From e457f58b6d9f658c5d33c5e4d4984d680df4369f Mon Sep 17 00:00:00 2001 From: gmemstr Date: Wed, 25 Oct 2017 21:12:40 -0700 Subject: [PATCH 01/24] Login moved to using SQLite3 storage SQLite3 should make future features like multiple publishers and user permissions _much_ easier to manage - JSON in Go is a pain sometimes. Updated godep file as required. --- Godeps/Godeps.json | 9 +++++ admin/admin.go | 55 ++++++++++++++++++++++++++ assets/config/users.db | Bin 0 -> 20480 bytes assets/config/users.json | 12 +++++- router/router.go | 83 ++++++++++++++++++++++++--------------- 5 files changed, 125 insertions(+), 34 deletions(-) create mode 100644 assets/config/users.db diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 709de63..76f57e8 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -17,6 +17,11 @@ "Comment": "v1.4.0-10-g18fca31", "Rev": "18fca31550181693b3a834a15b74b564b3605876" }, + { + "ImportPath": "github.com/mattn/go-sqlite3", + "Comment": "v1.3.0", + "Rev": "5160b48509cf5c877bc22c11c373f8c7738cdb38" + }, { "ImportPath": "golang.org/x/crypto/bcrypt", "Rev": "9419663f5a44be8b34ca85f08abc5fe1be11f8a3" @@ -25,6 +30,10 @@ "ImportPath": "golang.org/x/crypto/blowfish", "Rev": "9419663f5a44be8b34ca85f08abc5fe1be11f8a3" }, + { + "ImportPath": "golang.org/x/net/context", + "Rev": "3da985ce5951d99de868be4385f21ea6c2b22f24" + }, { "ImportPath": "golang.org/x/sys/unix", "Rev": "0b25a408a50076fbbcae6b7ac0ea5fbb0b085e79" diff --git a/admin/admin.go b/admin/admin.go index 4c27746..15ff500 100644 --- a/admin/admin.go +++ b/admin/admin.go @@ -13,10 +13,65 @@ import ( "net/http" "os" "strings" + "encoding/json" "github.com/gmemstr/pogo/common" ) +type Users struct { + Username UserOpts `json:u` +} + +type UserOpts struct { + Password string `json:password` + Realname string `json:realname` + Email string `json:email` +} + +func AddUser() common.Handler { + + return func(rc *common.RouterContext, w http.ResponseWriter, r *http.Request) *common.HTTPError { + + err := r.ParseMultipartForm(32 << 20) + if err != nil { + return &common.HTTPError{ + Message: err.Error(), + StatusCode: http.StatusBadRequest, + } + } + + d, err := ioutil.ReadFile("assets/config/users.json") + if err != nil { + return &common.HTTPError{ + Message: err.Error(), + StatusCode: http.StatusBadRequest, + } + } + var u []Users + err = json.Unmarshal(d, &u) + + // username := strings.Join(r.Form["username"], "") + password := strings.Join(r.Form["password"], "") + realname := strings.Join(r.Form["realname"], "") + email := strings.Join(r.Form["email"], "") + + // newuseropts := &UserOpts { + // Password: password, + // Realname: realname, + // Email: email, + // } + + u = append(u, Users{UserOpts{Password: password,Realname: realname,Email: email,}}) + json.Marshal(u) + fmt.Println(u) + + w.Write([]byte("")) + return nil + + } + +} + // Write custom CSS to disk or send it back to the client if GET func CustomCss() common.Handler { diff --git a/assets/config/users.db b/assets/config/users.db new file mode 100644 index 0000000000000000000000000000000000000000..05beb0c5754e6249736affb514afa570b5b355df GIT binary patch literal 20480 zcmeI)J#U*p7zgmXU?;{>LO0eMEH##+zNAs&4v~T_Qdzd~OR*AXv9AUfBrrALx*gMx z(V;`XPrpm2q(g@u=SEE;Royx`{}Tsy$8!hIZ#xO`y4UhkmrbKMLsnB7{-vn+l55!h=l(_SSJDc3^=IedauuWzgwU){5#qQ2{=3MiJ#%BFs- zQ{vAY_rveJ_#%yDez{}fB*y_009U<00Izz00bVk xKta(}`6a;W`~NGVT|I0NWDfxdKmY;|fB*y_009U<00IzLKY;=j`+xZ(fM1x>sC)nb literal 0 HcmV?d00001 diff --git a/assets/config/users.json b/assets/config/users.json index 80ff945..ec73880 100644 --- a/assets/config/users.json +++ b/assets/config/users.json @@ -1,4 +1,12 @@ { - "admin": "$2a$04$ZAf88Bao4Q768vKfCaKBlOqtPumwKwFhrcpBCdfMWWFX69wyhgTqi", - "gabriel": "$2a$04$KrhZ1q6FpOGqs0FVKMYhQ.BTYeVXztnjrM9RbK.0buI1OHfmyNEAy" + "admin": { + "password": "$2a$04$ZAf88Bao4Q768vKfCaKBlOqtPumwKwFhrcpBCdfMWWFX69wyhgTqi", + "realname": "Administrator", + "email": "admin@localhost.com" + }, + "gabriel": { + "password": "$2a$04$KrhZ1q6FpOGqs0FVKMYhQ.BTYeVXztnjrM9RbK.0buI1OHfmyNEAy", + "realname": "Gabriel Simmer", + "email": "gabriel@localhost.com" + } } \ No newline at end of file diff --git a/router/router.go b/router/router.go index fc88522..09ca50e 100644 --- a/router/router.go +++ b/router/router.go @@ -8,6 +8,9 @@ import ( "net/http" "strings" "golang.org/x/crypto/bcrypt" + "database/sql" + + _ "github.com/mattn/go-sqlite3" "github.com/gorilla/mux" "github.com/gmemstr/pogo/admin" @@ -80,6 +83,11 @@ func Init() *mux.Router { admin.CreateEpisode(), )).Methods("POST") + r.Handle("/admin/newuser", Handle( + auth.RequireAuthorization(), + admin.AddUser(), + )).Methods("POST") + r.Handle("/admin/edit", Handle( auth.RequireAuthorization(), admin.EditEpisode(), @@ -104,6 +112,16 @@ func Init() *mux.Router { func loginHandler() common.Handler { return func(rc *common.RouterContext, w http.ResponseWriter, r *http.Request) *common.HTTPError { + db, err := sql.Open("sqlite3", "assets/config/users.db") + + if err != nil { + return &common.HTTPError{ + Message: fmt.Sprintf("error in reading user database: %v", err), + StatusCode: http.StatusInternalServerError, + } + } + + stmt, err := db.Prepare("SELECT * FROM users WHERE username=?") if _, err := auth.DecryptCookie(r); err == nil { http.Redirect(w, r, "/admin", http.StatusTemporaryRedirect) @@ -115,17 +133,7 @@ func loginHandler() common.Handler { return common.ReadAndServeFile("assets/web/login.html", w) } - d, err := ioutil.ReadFile("assets/config/users.json") - if err != nil { - - return &common.HTTPError{ - Message: fmt.Sprintf("error in reading users.json: %v", err), - StatusCode: http.StatusInternalServerError, - } - } - err = r.ParseForm() - if err != nil { return &common.HTTPError{ Message: fmt.Sprintf("error in parsing form: %v", err), @@ -135,37 +143,48 @@ func loginHandler() common.Handler { username := r.Form.Get("username") password := r.Form.Get("password") + rows, err := stmt.Query(username) + if username == "" || password == "" { return &common.HTTPError{ Message: "username or password is empty", StatusCode: http.StatusBadRequest, } } - - var u map[string]string - err = json.Unmarshal(d, &u) // Unmarshal into interface - - // Iterate through map until we find matching username - for k, v := range u { - if k == username && bcrypt.CompareHashAndPassword([]byte(v), []byte(password)) == nil { - // Create a cookie here because the credentials are correct - c, err := auth.CreateSession(&common.User{ - Username: k, - }) - if err != nil { - return &common.HTTPError{ - Message: err.Error(), - StatusCode: http.StatusInternalServerError, - } + var id int + var dbun string + var dbhsh string + var dbrn string + var dbem string + for rows.Next() { + err := rows.Scan(&id,&dbun,&dbhsh,&dbrn,&dbem) + if err != nil { + return &common.HTTPError{ + Message: fmt.Sprintf("error in decoding sql data", err), + StatusCode: http.StatusBadRequest, } - - // r.AddCookie(c) - w.Header().Add("Set-Cookie", c.String()) - // And now redirect the user to admin page - http.Redirect(w, r, "/admin", http.StatusTemporaryRedirect) - return nil } + } + // Create a cookie here because the credentials are correct + if dbun == username && bcrypt.CompareHashAndPassword([]byte(dbhsh), []byte(password)) == nil { + c, err := auth.CreateSession(&common.User{ + Username: username, + }) + if err != nil { + return &common.HTTPError{ + Message: err.Error(), + StatusCode: http.StatusInternalServerError, + } + } + + // r.AddCookie(c) + w.Header().Add("Set-Cookie", c.String()) + // And now redirect the user to admin page + http.Redirect(w, r, "/admin", http.StatusTemporaryRedirect) + return nil + } + return &common.HTTPError{ Message: "Invalid credentials!", From ba9c7c7a5dadd43c440ca916f8401f5a122c5378 Mon Sep 17 00:00:00 2001 From: gmemstr Date: Wed, 25 Oct 2017 21:34:15 -0700 Subject: [PATCH 02/24] Reimplemented AddUser() function to use SQLite3 Aha! Screw you JSON headaches! I have banished thee to the realm of impossibility, and turn to the promised land of SQLite3, where the sky is blue and the grass long. ...or something. Rewrite AddUser() to insert into database, frontend soon. --- admin/admin.go | 58 +++++++++++++++++++++--------------------------- router/router.go | 5 +++-- 2 files changed, 28 insertions(+), 35 deletions(-) diff --git a/admin/admin.go b/admin/admin.go index 15ff500..19e2c55 100644 --- a/admin/admin.go +++ b/admin/admin.go @@ -13,25 +13,34 @@ import ( "net/http" "os" "strings" - "encoding/json" + "golang.org/x/crypto/bcrypt" + "database/sql" + + _ "github.com/mattn/go-sqlite3" "github.com/gmemstr/pogo/common" ) -type Users struct { - Username UserOpts `json:u` -} - -type UserOpts struct { - Password string `json:password` - Realname string `json:realname` - Email string `json:email` -} - +// Add user to the SQLite3 database func AddUser() common.Handler { return func(rc *common.RouterContext, w http.ResponseWriter, r *http.Request) *common.HTTPError { + db, err := sql.Open("sqlite3", "assets/config/users.db") + if err != nil { + return &common.HTTPError{ + Message: fmt.Sprintf("error opening sqlite3 file: %v", err), + StatusCode: http.StatusInternalServerError, + } + } + statement, err := db.Prepare("INSERT INTO users(username,hash,realname,email) VALUES (?,?,?,?)") + if err != nil { + return &common.HTTPError{ + Message: fmt.Sprintf("error preparing sqlite3 statement: %v", err), + StatusCode: http.StatusInternalServerError, + } + } + err := r.ParseMultipartForm(32 << 20) if err != nil { return &common.HTTPError{ @@ -40,34 +49,17 @@ func AddUser() common.Handler { } } - d, err := ioutil.ReadFile("assets/config/users.json") - if err != nil { - return &common.HTTPError{ - Message: err.Error(), - StatusCode: http.StatusBadRequest, - } - } - var u []Users - err = json.Unmarshal(d, &u) - - // username := strings.Join(r.Form["username"], "") + username := strings.Join(r.Form["username"], "") password := strings.Join(r.Form["password"], "") realname := strings.Join(r.Form["realname"], "") email := strings.Join(r.Form["email"], "") - // newuseropts := &UserOpts { - // Password: password, - // Realname: realname, - // Email: email, - // } + hash, err := bcrypt.GenerateFromPassword(password, 4) - u = append(u, Users{UserOpts{Password: password,Realname: realname,Email: email,}}) - json.Marshal(u) - fmt.Println(u) - - w.Write([]byte("")) + result, err := statement.Exec(username,hash,realname,email) + w.Write([]byte("")) + db.Close() return nil - } } diff --git a/router/router.go b/router/router.go index 09ca50e..61d8483 100644 --- a/router/router.go +++ b/router/router.go @@ -121,7 +121,7 @@ func loginHandler() common.Handler { } } - stmt, err := db.Prepare("SELECT * FROM users WHERE username=?") + statement, err := db.Prepare("SELECT * FROM users WHERE username=?") if _, err := auth.DecryptCookie(r); err == nil { http.Redirect(w, r, "/admin", http.StatusTemporaryRedirect) @@ -143,7 +143,7 @@ func loginHandler() common.Handler { username := r.Form.Get("username") password := r.Form.Get("password") - rows, err := stmt.Query(username) + rows, err := statement.Query(username) if username == "" || password == "" { return &common.HTTPError{ @@ -182,6 +182,7 @@ func loginHandler() common.Handler { w.Header().Add("Set-Cookie", c.String()) // And now redirect the user to admin page http.Redirect(w, r, "/admin", http.StatusTemporaryRedirect) + db.Close() return nil } From 7491d1c86e2ec179b10361aa6e8fb7bf8e257e3d Mon Sep 17 00:00:00 2001 From: gmemstr Date: Thu, 26 Oct 2017 09:03:54 -0700 Subject: [PATCH 03/24] Fixed non-byte value in hashing function Also fixed some other misc. build errors, & added err catch to exec result. --- admin/admin.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/admin/admin.go b/admin/admin.go index 19e2c55..0776029 100644 --- a/admin/admin.go +++ b/admin/admin.go @@ -41,7 +41,7 @@ func AddUser() common.Handler { } } - err := r.ParseMultipartForm(32 << 20) + err = r.ParseMultipartForm(32 << 20) if err != nil { return &common.HTTPError{ Message: err.Error(), @@ -54,9 +54,15 @@ func AddUser() common.Handler { realname := strings.Join(r.Form["realname"], "") email := strings.Join(r.Form["email"], "") - hash, err := bcrypt.GenerateFromPassword(password, 4) + hash, err := bcrypt.GenerateFromPassword([]byte(password), 4) - result, err := statement.Exec(username,hash,realname,email) + _, err = statement.Exec(username,hash,realname,email) + if err != nil { + return &common.HTTPError{ + Message: fmt.Sprintf("error executing sqlite3 statement: %v", err), + StatusCode: http.StatusInternalServerError, + } + } w.Write([]byte("")) db.Close() return nil From af8611bb379d8002707a236d3d81f00721bc994d Mon Sep 17 00:00:00 2001 From: gmemstr Date: Fri, 27 Oct 2017 17:41:28 -0700 Subject: [PATCH 04/24] Added /admin/listusers route for user management frontend This will allow basic listing on the frontend. Does require authentication for route. Also apparently added route for adding user properly (was that not in the last commit? Apparently not). --- admin/admin.go | 58 ++++++++++++++++++++++++++++++++++++++++++++++++ router/router.go | 10 +++++++++ 2 files changed, 68 insertions(+) diff --git a/admin/admin.go b/admin/admin.go index 0776029..3f6b0dc 100644 --- a/admin/admin.go +++ b/admin/admin.go @@ -13,6 +13,7 @@ import ( "net/http" "os" "strings" + "encoding/json" "golang.org/x/crypto/bcrypt" "database/sql" @@ -20,6 +21,15 @@ import ( "github.com/gmemstr/pogo/common" ) +type User struct { + Id int `json:"id"` + Dbun string `json:"username"` + Dbrn string `json:"realname"` + Dbem string `json:"email"` +} +type UserList struct { + Users []User +} // Add user to the SQLite3 database func AddUser() common.Handler { @@ -70,6 +80,54 @@ func AddUser() common.Handler { } +func ListUsers() common.Handler { + + return func(rc *common.RouterContext, w http.ResponseWriter, r *http.Request) *common.HTTPError { + + db, err := sql.Open("sqlite3", "assets/config/users.db") + + if err != nil { + return &common.HTTPError{ + Message: fmt.Sprintf("error in reading user database: %v", err), + StatusCode: http.StatusInternalServerError, + } + } + // NEVER SELECT hash ENTRY + statement, err := db.Prepare("SELECT id,username,realname,email FROM users") + if err != nil { + return &common.HTTPError{ + Message: fmt.Sprintf("error in reading user database: %v", err), + StatusCode: http.StatusInternalServerError, + } + } + + rows, err := statement.Query() + if err != nil { + return &common.HTTPError{ + Message: fmt.Sprintf("error in executing user SELECT: %v", err), + StatusCode: http.StatusInternalServerError, + } + } + res := []User{} + + for rows.Next() { + var u User + err := rows.Scan(&u.Id, &u.Dbun, &u.Dbrn, &u.Dbem) + if err != nil { + return &common.HTTPError{ + Message: fmt.Sprintf("error in decoding sql data", err), + StatusCode: http.StatusBadRequest, + } + } + res = append(res, u) + } + fin, err := json.Marshal(res) + w.Write(fin) + + return nil + } +} + // Write custom CSS to disk or send it back to the client if GET func CustomCss() common.Handler { diff --git a/router/router.go b/router/router.go index 61d8483..9d7226e 100644 --- a/router/router.go +++ b/router/router.go @@ -103,6 +103,16 @@ func Init() *mux.Router { admin.CustomCss(), )).Methods("GET", "POST") + r.Handle("/admin/adduser", Handle( + auth.RequireAuthorization(), + admin.AddUser(), + )).Methods("POST") + + r.Handle("/admin/listusers", Handle( + auth.RequireAuthorization(), + admin.ListUsers(), + )).Methods("GET") + r.Handle("/setup", Handle( serveSetup(), )).Methods("GET", "POST") From d32d7417a5e19d78a69a38d9218078ab73b63eb2 Mon Sep 17 00:00:00 2001 From: gmemstr Date: Fri, 27 Oct 2017 17:45:24 -0700 Subject: [PATCH 05/24] *Close DB when user lite route function ends --- admin/admin.go | 1 + 1 file changed, 1 insertion(+) diff --git a/admin/admin.go b/admin/admin.go index 3f6b0dc..50c8e3d 100644 --- a/admin/admin.go +++ b/admin/admin.go @@ -123,6 +123,7 @@ func ListUsers() common.Handler { } fin, err := json.Marshal(res) w.Write(fin) + db.Close() return nil } From 103e0b8f6e05f26c25362941a39c352fe423c1fd Mon Sep 17 00:00:00 2001 From: gmemstr Date: Fri, 27 Oct 2017 18:39:29 -0700 Subject: [PATCH 06/24] Format app.js templates Just some cleanup, also ignoring users database. --- .gitignore | 2 + assets/config/users.db | Bin 20480 -> 20480 bytes assets/web/static/app.js | 130 +++++++++++++++++++++++++++++++++------ 3 files changed, 112 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index bdd0bbe..0586dba 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ assets/static/custom\.css config\.json vendor/ + +assets/config/users\.db diff --git a/assets/config/users.db b/assets/config/users.db index 05beb0c5754e6249736affb514afa570b5b355df..91ef3eb15bc9150eb58485a0235e4cbc7498241d 100644 GIT binary patch delta 256 zcmZozz}T>Wae_1>%S0JxMwX2UOZYjM`1Kk1XYuQA78FqCug_#+XOM1W)znWypdI1J253UGY?|XWae_1>^F$eEM&^wPOZeFs`DZck&)O`gFoA#aEcq-T{|^KIA0YoJ|HJ@Z zK4xZ4PA&!p21fqx4E*1r!W_aJ%;KDhDY=<>ykNO+4E*1qa_n3jj6liK;?$yI0JL2l AqyPW_ diff --git a/assets/web/static/app.js b/assets/web/static/app.js index 7427f35..68d79d7 100644 --- a/assets/web/static/app.js +++ b/assets/web/static/app.js @@ -1,9 +1,37 @@ const episodepublishform = { - template: '

Publish Episode

' + template: `
+

Publish Episode

+
+ + + + + + + + + +
+
` } -const episodemanagement = { - template: '
TitleURLActions
{{ item.id }}: {{ item.title }}{{ item.url }}Edit
', +const usermanagement = { + template: `
+ + + + + + + + + + + +
TitleURLActions
{{ item.id }}: {{ item.title }}{{ item.url }} + Edit +
+
`, data() { return { loading: false, @@ -25,7 +53,7 @@ const episodemanagement = { this.error = this.items = [] this.loading = true - getEpisodes((err, items) => { + get("/admin/listusers", (err, items) => { this.loading = false if (err) { this.error = err.toString() @@ -45,8 +73,72 @@ const episodemanagement = { } } +const episodemanagement = { + template: `
+ + + + + + + +
TitleURLActions
{{ item.id }}: {{ item.title }}{{ item.url }}Edit
+
`, + data() { + return { + loading: false, + items: null, + error: null + } + }, + created() { + // fetch the data when the view is created and the data is + // already being observed + this.fetchData() + }, + watch: { + // call again the method if the route changes + '$route': 'fetchData' + }, + methods: { + fetchData() { + this.error = this.items = [] + this.loading = true + + get("/json", (err, items) => { + this.loading = false + if (err) { + this.error = err.toString() + } else { + var t = JSON.parse(items).items + for (var i = t.length - 1; i >= 0; i--) { + this.items.push({ + title: t[i].title, + url: t[i].url, + id: t[i].id + }) + } + } + }) + } + } +} + const episodeedit = { - template: '

Edit Episode

', + template: `
+
+

Edit Episode

+
+ + + + + + + +
+
+
`, data() { return { loading: false, @@ -68,7 +160,7 @@ const episodeedit = { this.error = this.episode = {} this.loading = true - getEpisodes((err, items) => { + get("/json", (err, items) => { this.loading = false if (err) { this.error = err.toString() @@ -96,7 +188,15 @@ const episodeedit = { } const customcss = { - template: '

Edit CSS


', + template: `
+

Edit CSS

+
+ + +
+ +
+
`, data() { return { loading: false, @@ -118,7 +218,7 @@ const customcss = { this.error = this.css = null this.loading = true - getCss((err, css) => { + get("/admin/css", (err, css) => { this.loading = false if (err) { this.error = err.toString() @@ -148,22 +248,12 @@ const app = new Vue({ } }).$mount('#app') -function getCss(callback) { +function get(url,callback) { var xmlHttp = new XMLHttpRequest(); xmlHttp.onreadystatechange = function() { if (xmlHttp.readyState == 4 && xmlHttp.status == 200) callback(null, xmlHttp.responseText) } - xmlHttp.open("GET", "/admin/css", true); - xmlHttp.send(null); -} - -function getEpisodes(callback) { - var xmlHttp = new XMLHttpRequest(); - xmlHttp.onreadystatechange = function() { - if (xmlHttp.readyState == 4 && xmlHttp.status == 200) - callback(null, xmlHttp.responseText) - } - xmlHttp.open("GET", "/json", true); + xmlHttp.open("GET", url, true); xmlHttp.send(null); } \ No newline at end of file From 1cf9e5c9dc5b9090d51a7318811e9098e3774715 Mon Sep 17 00:00:00 2001 From: gmemstr Date: Fri, 27 Oct 2017 19:04:11 -0700 Subject: [PATCH 07/24] Add user management frontend V. basic, but lists users and offers interface to edit (editing users path not implemented yet). --- assets/web/admin.html | 2 +- assets/web/static/app.js | 93 ++++++++++++++++++++++++++++++++++------ 2 files changed, 81 insertions(+), 14 deletions(-) diff --git a/assets/web/admin.html b/assets/web/admin.html index 9f9975b..b3453ba 100644 --- a/assets/web/admin.html +++ b/assets/web/admin.html @@ -9,7 +9,7 @@
- Publish Theme Manage + Publish Theme Manage Users

{{ header }}

diff --git a/assets/web/static/app.js b/assets/web/static/app.js index 68d79d7..ecfbc5f 100644 --- a/assets/web/static/app.js +++ b/assets/web/static/app.js @@ -15,19 +15,19 @@ const episodepublishform = {
` } -const usermanagement = { +const userlist = { template: `
- - - + + + - - + +
TitleURLActionsUsernameEmailEdit
{{ item.id }}: {{ item.title }}{{ item.url }}{{ item.username }}{{ item.email }} - Edit + Edit
@@ -58,13 +58,12 @@ const usermanagement = { if (err) { this.error = err.toString() } else { - console.log(items); - var t = JSON.parse(items).items + var t = JSON.parse(items).reverse(); for (var i = t.length - 1; i >= 0; i--) { this.items.push({ - title: t[i].title, - url: t[i].url, - id: t[i].id + id: t[i].id, + username: t[i].username, + email: t[i].email, }) } } @@ -73,6 +72,72 @@ const usermanagement = { } } +const useredit = { + template: `
+
+

Edit User

+
+ + + + + + + + + + + + + + +
+
+
+
`, + data() { + return { + loading: false, + user: null, + error: null + } + }, + created() { + // fetch the data when the view is created and the data is + // already being observed + this.fetchData() + }, + watch: { + // call again the method if the route changes + '$route': 'fetchData' + }, + methods: { + fetchData() { + this.error = this.user = [] + this.loading = true + + get("/admin/listusers", (err, items) => { + this.loading = false + if (err) { + this.error = err.toString() + } else { + var t = JSON.parse(items) + for (var i = t.length - 1; i >= 0; i--) { + if (t[i].id == this.$route.params.id) { + this.user = { + id: t[i].id, + username: t[i].username, + email: t[i].email, + realname: t[i].realname + } + } + } + } + }) + } + } +} + const episodemanagement = { template: `
@@ -234,7 +299,9 @@ const routes = [ { path: '/publish', component: episodepublishform }, { path: '/manage', component: episodemanagement }, { path: '/theme', component: customcss }, - { path: '/edit/:id', component: episodeedit } + { path: '/edit/:id', component: episodeedit }, + { path: '/users/', component: userlist }, + { path: '/user/:id', component: useredit } ] const router = new VueRouter({ From 4ef5d671f37f9690f69eaeca532e2be4a053e03f Mon Sep 17 00:00:00 2001 From: gmemstr Date: Fri, 17 Nov 2017 13:59:53 -0800 Subject: [PATCH 08/24] Admin: Added user editing Added user editing route both backend and frontend, fully functional. Currently working on new user frontend route, then setup process. Also changed to use gorilla/feeds now that code has been upstreamed, so please update your dependencies. --- README.md | 2 +- admin/admin.go | 89 +++++++++++++++++++++++++++++++++++++++ assets/config/users.db | Bin 20480 -> 20480 bytes assets/config/users.json | 12 ------ assets/web/static/app.js | 4 +- generate_rss.go | 2 +- router/router.go | 5 +++ 7 files changed, 98 insertions(+), 16 deletions(-) delete mode 100644 assets/config/users.json diff --git a/README.md b/README.md index 051a1ea..2b959a5 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ To produce a product that is easy to deploy and easier to use when hosting a pod ## Requirements -[github.com/gmemstr/feeds](https://github.com/gmemstr/feeds) _this branch contains some fixes for "podcast specific" tags_ +[github.com/gorilla/feeds](https://github.com/gorilla/feeds) [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify) diff --git a/admin/admin.go b/admin/admin.go index 50c8e3d..cfdc821 100644 --- a/admin/admin.go +++ b/admin/admin.go @@ -80,6 +80,95 @@ func AddUser() common.Handler { } +func EditUser() common.Handler { + + return func(rc *common.RouterContext, w http.ResponseWriter, r *http.Request) *common.HTTPError { + db, err := sql.Open("sqlite3", "assets/config/users.db") + + if err != nil { + return &common.HTTPError{ + Message: fmt.Sprintf("error in reading user database: %v", err), + StatusCode: http.StatusInternalServerError, + } + } + + err = r.ParseMultipartForm(32 << 20) + if err != nil { + return &common.HTTPError{ + Message: err.Error(), + StatusCode: http.StatusBadRequest, + } + } + id := strings.Join(r.Form["id"], "") + username := strings.Join(r.Form["username"], "") + password := strings.Join(r.Form["oldpw"], "") + newpassword := strings.Join(r.Form["newpw1"], "") + realname := strings.Join(r.Form["realname"], "") + email := strings.Join(r.Form["email"], "") + pwhash, err := bcrypt.GenerateFromPassword([]byte(password), 4) + + statement, err := db.Prepare("UPDATE users SET username=?, hash=?, realname=?, email=? WHERE id=?") + if err != nil { + return &common.HTTPError{ + Message: fmt.Sprintf("error preparing sqlite3 statement: %v", err), + StatusCode: http.StatusInternalServerError, + } + } + + pwstatement, err := db.Prepare("SELECT hash FROM users WHERE id=?") + if err != nil { + return &common.HTTPError{ + Message: fmt.Sprintf("error preparing sqlite3 statement: %v", err), + StatusCode: http.StatusInternalServerError, + } + } + + tmp, err := pwstatement.Query(id) + if err != nil { + return &common.HTTPError{ + Message: fmt.Sprintf("error executing sqlite3 statement: %v", err), + StatusCode: http.StatusInternalServerError, + } + } + + var hash []byte + + for tmp.Next() { + err = tmp.Scan(&hash) + if err != nil { + return &common.HTTPError{ + Message: fmt.Sprintf("error executing sqlite3 statement: %v", err), + StatusCode: http.StatusInternalServerError, + } + } + } + fmt.Println(hash) + if bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) != nil { + fmt.Println("Passwords do not match") + w.Write([]byte("")) + db.Close() + + return nil + } + + if newpassword != "" { + pwhash, err = bcrypt.GenerateFromPassword([]byte(newpassword), 4) + } + + _, err = statement.Exec(username,pwhash,realname,email,id) + if err != nil { + return &common.HTTPError{ + Message: fmt.Sprintf("error executing sqlite3 statement: %v", err), + StatusCode: http.StatusInternalServerError, + } + } + w.Write([]byte("")) + db.Close() + + return nil + } +} + func ListUsers() common.Handler { return func(rc *common.RouterContext, w http.ResponseWriter, r *http.Request) *common.HTTPError { diff --git a/assets/config/users.db b/assets/config/users.db index 91ef3eb15bc9150eb58485a0235e4cbc7498241d..e5f311a92d5c18a7a717bd1c557ecab73e8f6090 100644 GIT binary patch delta 137 zcmZozz}T>Wae_1>+e8^>RyGDbPK}KzbNRXXXE8AG>of2#;@96SsG!V0SzkUooPmLX zL%fkyJ%*8;LA;SgTstu(H#1MgC{e}0L?taN$JbLYCEq;Lpdut9yxh#Zs?4`E$;aO$ nH6yaXq|`7gGbbb@)63ns*gwxJw6fI4$+^@KWWeT+^85w>F`y=W delta 137 zcmZozz}T>Wae_1>%S0JxRu%?5#;A=cbNRU$7#NuN^%?kQ@#}9ER8Z!htS_G(p2En^ zAl}HTuAP{Yo0+F#l&E50q7vnpW?|u!m~RqjZe~&Do#veA?UdtRSQ1d0Tkc)%mQj>k n;N+Z=<{KXF7GY*tUYU^|QkdxoG9a_Kq$sf@zi9JEd42-`h}$TV diff --git a/assets/config/users.json b/assets/config/users.json deleted file mode 100644 index ec73880..0000000 --- a/assets/config/users.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "admin": { - "password": "$2a$04$ZAf88Bao4Q768vKfCaKBlOqtPumwKwFhrcpBCdfMWWFX69wyhgTqi", - "realname": "Administrator", - "email": "admin@localhost.com" - }, - "gabriel": { - "password": "$2a$04$KrhZ1q6FpOGqs0FVKMYhQ.BTYeVXztnjrM9RbK.0buI1OHfmyNEAy", - "realname": "Gabriel Simmer", - "email": "gabriel@localhost.com" - } -} \ No newline at end of file diff --git a/assets/web/static/app.js b/assets/web/static/app.js index ecfbc5f..59d2e1d 100644 --- a/assets/web/static/app.js +++ b/assets/web/static/app.js @@ -77,8 +77,8 @@ const useredit = {

Edit User

- - + + diff --git a/generate_rss.go b/generate_rss.go index 082bcad..c0f0a5d 100644 --- a/generate_rss.go +++ b/generate_rss.go @@ -17,7 +17,7 @@ import ( "encoding/json" "github.com/fsnotify/fsnotify" - "github.com/gmemstr/feeds" + "github.com/gorilla/feeds" ) type Config struct { diff --git a/router/router.go b/router/router.go index 9d7226e..9fdaf72 100644 --- a/router/router.go +++ b/router/router.go @@ -83,6 +83,11 @@ func Init() *mux.Router { admin.CreateEpisode(), )).Methods("POST") + r.Handle("/admin/edituser", Handle( + auth.RequireAuthorization(), + admin.EditUser(), + )).Methods("POST") + r.Handle("/admin/newuser", Handle( auth.RequireAuthorization(), admin.AddUser(), From 8c5b8d8e74e87768aaf1a18fdbd3426226637cf6 Mon Sep 17 00:00:00 2001 From: gmemstr Date: Fri, 17 Nov 2017 14:09:55 -0800 Subject: [PATCH 09/24] Add contributing.md --- CONTRIBUTING.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..e585e85 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,30 @@ +# Contributing + +Generally, any contributions to Pogo are more than welcome, but we'd like it if you follow a couple guidelines. We'll also point out a couple of tricks for ease of use. + +First, fork the repository and clone it locally + + +``` +git clone git@github.com:your-username/pogo.git +``` + +Set up Go and install the dependencies + +``` +cd pogo +go get github.com/tools/godep +godep restore +``` + +Then make your changes. If you use Sublime Text 3, please check out [our snippets](https://gist.github.com/gmemstr/60831109f0ae6c40861c1751a367524e) to add some shortcuts to make your life easier. + +The platform is divided into two main parts: The main Go app, which does everything from generating RSS to serving up webpages, and the Vue.js app portion, which currently handles the admin interface but will be implemented into the main frontend as well. + +Once you've made your changes, please make sure the app can build + +``` +go build +``` + +Once you've verified your addition works, push to your repository and [create a pull request](https://github.com/gmemstr/pogo/compare). During the review your PR will also pass through our TravisCI testing as well. \ No newline at end of file From 516b09dbb080ec5e9363e5048243432f2e1244d1 Mon Sep 17 00:00:00 2001 From: gmemstr Date: Fri, 17 Nov 2017 14:19:22 -0800 Subject: [PATCH 10/24] Update godeps --- Godeps/Godeps.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 76f57e8..f0b066d 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -9,8 +9,9 @@ "Rev": "4da3e2cfbabc9f751898f250b49f2439785783a1" }, { - "ImportPath": "github.com/gmemstr/feeds", - "Rev": "c8f8657f3a1f60cff88624dc069d369d77c658a1" + "ImportPath": "github.com/gorilla/feeds", + "Comment": "v1.0.0", + "Rev": "4b936b5221c53c99fcd4b15ac0b8c38ff490ab89" }, { "ImportPath": "github.com/gorilla/mux", From 614ceabce8b643e86b53550c45d3b76bc4e28868 Mon Sep 17 00:00:00 2001 From: gmemstr Date: Sat, 18 Nov 2017 20:21:15 -0800 Subject: [PATCH 11/24] Commit logos --- assets/web/static/logo-large.png | Bin 0 -> 80753 bytes assets/web/static/logo-med.png | Bin 0 -> 18341 bytes assets/web/static/logo-small.png | Bin 0 -> 7102 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/web/static/logo-large.png create mode 100644 assets/web/static/logo-med.png create mode 100644 assets/web/static/logo-small.png diff --git a/assets/web/static/logo-large.png b/assets/web/static/logo-large.png new file mode 100644 index 0000000000000000000000000000000000000000..b4132517fc6840c1b071dbf9756bb9fec9ab0a39 GIT binary patch literal 80753 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 literal 0 HcmV?d00001 diff --git a/assets/web/static/logo-med.png b/assets/web/static/logo-med.png new file mode 100644 index 0000000000000000000000000000000000000000..a38fa6811efc278c0712fb2743e4742b3d7dd25e GIT binary patch literal 18341 zcmeIZXH=BUvoAV;1VKQNBw2!>Ac#s%Lk5*#B1jeiLDImGVFpk@B#DShMkT0ZkQ^n8 zFd#W2aX?^@G)SD=&)|RW`{8`K=bkTTt?gRx>$0Ej>Z)IbuBz?{*VomgrDmgs!Ctc)avU0P5DL-;~Xv3rJWMyY_$HwZB58}Ix zJPf9(sja4b&wF%b!pGapF5uVtjt~t$39ry*kK(5Mbg0?Y>(nNT$f9v{R*L5Z-R#SZ zTo>H3UltB{nvfz#ABf6*E$vg0lr6n#hE1?*?f9C4`2?J?(F@xZ+ z9ma1hp6}wT-1!@J`~Kc^hHq@Nk6MaxLu-DRlO%xA|M~NO5P?7@tw@;VVJ-ek68;gr z>W_f^t=9!c(*+i-``U)<(S{rLnM>44J4mPcdL((%jklXJnK#jLI&arcW<5z>adzII#UAW1P9(y)7q&+WqjAPz_iQtfYFxcYfX?66&q3MKS7tf}{)*iDc zC80@@6IEwmusP;+6Fc)FnN~kLxkEKJ!*RKswX@v&PSbQeJtVN<$A=T%X(#{wv92F5 z_i)LyXZWa)U(sfn_V=$GWp)%z`h(=BbcH0zPjMVu>K3aUTbELw*6mFVF>njhZ0!~} zcvvf#H{|bzd(r9s9b--(&rK(Ty}rvQr(uiw!&fhJ?uR19{x?=!n)i@p-*Z?;3$<(m zMPaZrdB9IY6Xo*Yy3Ux=2I@Vxg|S=J-;T7uK|Gy?#HHC9Ck?>o%*ZTvtuToXw<+!7 zzP+hWAvqx%2xmE-5JkVW%gslz=k3XH&|gj2cBn%OR-dRk{*FDnSkjf7KXv-!;oUVv zh&L<<&?YB-REUA65!?~#wVcao%*z00csdO-NVJFC*EvWr$%t~8CqM%RYhWm|ZL3?U zz%tbzE&i_m>#P1mJ7S8pv`(F5_UZTy;J<{G&DnmRf5!g1SpNIhSRr zBZ3l^Za8Z4t3=Bs1oveRJ85^H)3OpmwC71wkJ)|n?wH_?P20;@7!?u*+mLw08CI;N z^k<`7Cz_SAVaAl7&XTM`D>v-)-K$%9%z6?s5S}T0?u3)uZ_Us5 zlYs9qpC@6y%u6bqr$I#I=?D6N!Q@5zE!QdeBXW#E8YIL7m<>S12o$B}<)WMq8? zc0_tLBgvpuFVUaYe()Z)M@-4eIC2ESDPe*B62-G{=B4$=tdzt(Et*CUb1n6fm4b4~ zX>^dZ?IY37pJG_ak3X+&_LXQUxlR}8(4G756+#hP3M`}eR*ER| z%X06K9X{iX>Rve_FLns>00Mda)k-nB+$=IuP8iHhlBgK&tN+=2Ld%Qf7#Nb3@4Nv7 z`q7PJ9?kYi-IN~xI40zUAV2J($9V0e=$QSnc=f#_mQ`1CSt5@vtDC*RsQ!cA`Ugaqc|}vp zEOD|=#Mn6fqgcg-{NV4Sgbje@x8k;cOemr)$`8gbq@pMo6S zbL8=tP&ML4>C&+%{s?5b6&Yj}bGZTRlCeZdPcK1hV8^rI8AOqcgnIi<)6qJoaflxS zb%duqD%PTemLAiE39f$Uose0AWC%RR-n-BM;vqiKT<&{x7x#u$JdrXRMFc zyQFDRLbkxz1WaRs@0d)9x4M_1=kd5eZyLe&n2+|Pv4S8-Lh6TeyDq705)=@6@FP!~ z*t&sZ%DZE4)vJW7pF*?T2B)HsEKy_-`MFN@S>o2@;u|$mdaGCdEyc@s?=BK0R~aVM zNXc;jlS9LE<=Vd~Wr}M$7|mm5zU7Pbro<3IX*b?KTKtc2J?>N2=Z}sFkq*bI?-hy@ zRk2Y{sC2HSfIw_d+5CBoYteS4OX8@*DdM&VtA03kCl4a|xh?}QqD0D$0sl3~7?g)2 z&Ln+~sB8NtCA$xfJv%gt7#ChDu|yJOWQ5PkxFr9-QJMUYn2yIAFgE;gA7>|us88jW zIy-sH@wh~>b(lC@k%13pgf6K4ofV6VOSRF~+kL@wv}aS3hGbgv7DzKl%>U!AH-z86 zX?P_!WD;Lj^6IAER!HjxIwr;CvmNKnHjF<%hZ!DQ8=N3&7kaZx%&e)V_p2tM%lEI( zWxPygPQscsnhw(y{oc4>Ep2!GhSJ;nhjqOzXhog4RE(6_G&p|tkh^KBRgF974&$sR z`z|S2Pu@*kLR;kQj%u^`Pkm2Zh|ix}OTB&zoa57m1zeAJ=^$me?er=o$-h)MRWS3` zdABi#GUPbSDVq%$?wzN9h!36hW7~p;|WyBzD^hE{K6zSX<{~G z`e#$q`;xQP?nV>Qr`r4SchOl;*xO6K;}Gqfcy_g^`h;BO)q@qOTT$a~+YRq-1A_=W zAT@hm!u73gzsPc`Ca3l?>WS3g>_-!m+iD0Vv-Bnn1;Q0ln0JlgLCbG3hMfe%N-YlI zx8(bSFGC~x)E`c$&jd#_=UJKORA`N_46WA8{|>6Hy8)~UE{a-2=PULo9o`7Sj2;ZT zaB_X;5~WKq0*AQ58a!smNE-O8y+ul4-CiUN#wBLlMDk?2XlnBM8_u`QLLJd5R4H)-auh8;sve$INr@^sG=6KM4tWCmIf(ZBMmX#5wZyZ(3%PPXKpq`J_k()dtLvnP zVSt^25pm`ohr#LJc`MHi17gbUlCGTbZNDT7*dK0ydvPN#&BU#-Ioh^`eA`{xdUGEM ze5+77h$}b!ZlykV7uUm2QaVq$o+A_=Lau0eNH#$&I}o`wC^)6ST3*wZw`S01oU^I{ z#jn89XM5F@vn{ixYSE<*^2-~akK@;J2ea1~+}$;AUcq8MbGUPXeIYf5D8q6awQT9B?VVGttGKb?%WJuv zgPUS$4n#c|rNQYj%GqVHip;VmlO|c;sE_4dW?VGr`COz|Z+9g%2A8^&+uXkUR&Sdf z(%oV}I;HSpzhZB8315CjOajG^)3g3L#DEk+x6JZ;J$cLN!ndo#3y`L^WOCgzVlqX! z+CI~!phx;&{%79ewHPyb-HZ~(94JV@)0!yM_MT~B8Ou`92@x$&7!IOa=3ZK3S`zu* zG6UQL9z!m6?p_{V%nzBMFtjDq z^VGe^Dj`-eR#q^j&7Dg&cVm|lcCX_}L6tqf-{ai*0QrsAuJ)sEbN>b;AL+|_j`B?} zvY~0Zlu)xYNk=#DT!3;rp^RzxqA7pwo_ zJopfTau%pBK!#7P+(20@@#>BBNuV${(VY?msj9j5QnJ}PSxfS6zDQUgb=7sb(4h@F zOwHQ8b48zGxemEb$YySg?9l0&ZsEUIyiyeG{amo((i2ev%vIPjLJl_Icjw(3Lf{>Z zz5P*^=rMZ+Asvm4n_=ia!QdqAW6Ib4HM)r{XfIc&MT#VfHcHio4?0ikXGzR z3N|REK^h{$8ut$NDR(G#Gz)}t7G0?^5S<`ra<&(u&K^pyIS(~%3$M#>-L?*FA#MRa zaZ;~tu5mls&{ykx@C;=u_-E+985c3iVOQ0v@^IVN9ZwWWgFOG!qja*&)8$p%;-L>l zk~ytgVKgPdbntl&y&q^e=kRayo1%=A06f{E7S0d%M8Cqi9$ogv?B>MWba4#k=q4@54w5xoDrD=+zB&pFmCM4_~t8_u(l$I zx8}<@8>RH%SgiQM@9} znfqZ#S)RU@=qjHo&e{XzIf&+e&|_Df2eW}&U&4U7N%yx#D;lUQMX`Vl8GzUg)*sPk zt@`$WUv~&%Xq#P+f<;AJhWG{Z#Vav!<1hzYutfrg?koJjtFTuXuMqb!vF<%l@y(4+ zsi;STUNMhi%M&*7H){%H#LA~Esul|p4x^r6uvgZwF$!#!e?;ou;-JZT@HTepTAOFnw`)niX)dM|EGkhW_D zvDeV;=+WFynL|t9YeOKW{8zbek?b%01 zV>-?H-CS*on_ra6KYh4r4r;mB(_RNg$`*tBVk6YPn~NS~CI=V8=vczZKE3>Ak;=g} zuIR(#C$5+Wa@GvUc&JN2r8FscxX0Po?D?B z;y6{X+_oCMBK;P4Y}~#%-cBW@m;GAsZ{LOGY|}0f9N@dPzb@a6S~{M{#&{viFgW?ggU4 zi#_v~aIkrr&A)24X@b{tRY0|E@mDZz)}F0Qr)Dr@PAGmIHx+QN+mcQt$eb+T2oKATkd4Yxg7DrDe>H!dk_uS7aVSkv#;Q!e|j ze23^;(X8YGZcoqLdxaJ`5k#l|N*XuWGdD6eksQ4A_CT*KFwRPYUIDv z_SqDyYnBmVTq4`QFd-hu`-Dw-{(2;c*YKas@i%j*7{5ic9Q^EmF0QXn83j)V+*wXQ zdBo2Zs~l~89=}wArgMPWx<4S9KWDCtt#!VU!PgT7OFxZJOjcOAdXhRzDZ^@N^JxF| zT~VRre#xCn42x8tDb+r-`?h?xO}$d+OGPk_J4T3?P)($YynM#ERi{pcxmVP9GI?h=^6jGuiy~#&DO3J)#Qv zlSxmfYe#=M?$hfo4UoR_uwX3)>q6pJ{a=5_+o7AuXN4HC??IUvSbEdUJ7OaofqQ-G z#heD;r|~IqfXA>=6y~>26Ti^=)$ZaKx(FlX0_N(D(~BCgmoz!p*YWp}P@jqNQ)uh* zn4z=FyAzr5p{t?rGyY3YpI*NE>CL9OT^Ptw0;2?+g{qG>3!bE9k*HfT)jn`^*GwlX z6i?mV!*?_o#HEvpN|I7P9EC(^aEN3jVJa#`tln#46$*T+0d;WN{6vqERC$I!&2q9en-mXzspNjgEKu5?v!aGW>12zUR|N-YZ(2Hq(i)tZ;z9K}14 zo!0~I4ShX%?w~T1YrmJ0GUX{$b{llontlF26A4>oK|PNS0N6&~#S4=X!9* zV`w*+o^(T+6AbtiFGa_4&6BL$4`e`gAzEzzZJR^T2m^gc5jKrLrKv(^)9=LQ1ZfIs z#ZO2k&{+k%Fd(8uiPK=F^K3B4AYqq5PZDHafC(ru0G*;9lECf3tE6t8L1&<}m4xib zKTgT31N!>Iv!Ciyp*>guRx)4aOaJ0r?Qhx0cue4r9XoN7`?CywhWBVaw-geJUauK*_FKI(eg}( zgcCN~mTuALM;}g`9}H?E%*D$Cnm=Cy=txdF^QC9Z#9?b&;mW`(A^O_Oqj>zUH`@9t zhLd};!lsO&z%YtxIfFn|xT}@>;jW@V_r*k-$aFV!8zkc_=ThzmDV^YAZ{NT8_;MqK zY$K2g0M1fJ>`%P@CNuqQ&(vse4jX2!d27RN#J$SrhSUpI}CB@4SMg) zdS3FfNd|WyqG+IQ^W56tlTnk2lo;LhR1ra|F~g%Anx@+#Vm`}{&<`X0m`Wjl;kS7e zQX@bfz}SuvBg_O|&Sv~sc;;TW6Zg5(#K<9JaGD(Z>-MQ?!ZyoTIq5TAwx z7PlV*t9i{PYO*ryNu!d|4yPE03(rtN`Jm&yj5d=8GHY*5XZ+p9(oMeHHWFJJCyei6 z2WGjmKvBooXIS8zwo|R1QSuXk@>WZ}>unO1{t71nohL0(TREe#D67IRfzn|k$!K#O z9b#yGEM+lJ_aV={*Vs@`XKloCt( z^SUa-_@Y?uNqffE&!h`JjVaE&i3YZCV@{ZxYnYjVCFY~u=#Wgsdi|;BbKu}8I&%-d z#s_@mwMeg0f1OwQxbU`Hp-of1!OhkozZ@PFlJ;~7bq$UzSe9DROuhVN5u))`>HpT0I3mKo~I*5-ND zxwbuu-0FH*+j|=o5V@gO+v^}Ru-PkoLGunvQ|?u?3#jfs#U$bBS|i$9tg#WEWrl-c z)!!5ullzP{x}0BgM~b0muoQW(?|QstQ_idmmg{`QN&>Gn`y(8us@-dzlIRnO(Ro-S zSW;)q9~020WgD=g#q5(Lc2R%QbZRx# zAH{}|7JPB zP&^95hZ1=!+j%S5CftV!I+xK8KgYc_7e7Zhv)*%1Fhq{7=#LZ`{x%)ZRH9LG(i{3KBX%g>ROR34Q!wSWRTY$_vK}kXS1=o zD6W2mo*f%{KGNE%pwyO1t{`J@osjOP>wfn3CzWKW;E1OCW_?t^%y@c&X2xtz#AERe z5S=Q;Ai7LEKzMfhN?D@Xd?lO%`2QE&*Sh}G>EoU!$_;X!619{*1M+tt55w;AB(-@R z?po(`8>n{vB#i~tLyLQNUaFx5aRYragP!-vRff(}Rt%-CJ^%I$zVWJ1*yCh))nhiO zWN`TPwfR)KBXsrf{#PB=<4m`bSA+!crv4x%q)?`UhCVyd|A2W=FWR^yMefLRONNiS z9?5{kzZ>t5zrTmzDj0U&t}#!9`XGUci#JVesBiF_mx4m2bou}a7X6oc^3qi=BE?RX zgYwH}{6FFsmDZw?(a`z*fV^rfS9!X2#VHQ)WWP|mKl-?|Brg0pTXV+Iso-Pypa)qW z)K3kRC9eY9RRxUvbU9V({uDCabZ27axD0sYpe-vKieDwF$1L3h6urJaJ6~3vpBdfE zdoz{;OgB9gn*4$li@3Ruiu;sz3~V%UQ1GOm3L6{6Z4-vMm|x7zySPl< zacbswcv9uD8o1dF;+Vc>S%vER8TnMNx;B0Xon+u^>G=#M#IsKgJfc2grg53Kh)o3@{~qw zCJ2{h$J8pO5JLYqS|o>34xE*qzx*vY3$KE(XGtP@o$|-#3+Lo)=j>L~vT}epc`axA_j}_$W7J-KG5zmQUH*`_BaO5ch34O ztov`C@3TB-%0UNolY2K^82OtME8ekVr`dGfD65EXs6v_}H{Kmc`kvT;OFc5fK6DLh zmJxpBC$|*{N_Qino_I+QnoWY_2zPIxp!8I7JiC%x*RhzeLcDa0$>?m$uVfdD!|~Qo zn@{4!Cg(^KSDa=11?O7Zbo=ARBY@-+>YbE|-GKk_btAGIY>%oa10{@x+_!j->;*1N zE@uJk{G`8XJ26PyI&cy+8esZwuMncXYiQ*q_Fb!vg8BY{#BFdI{A8INV@t2uETh1% z=A-C*s>cLU*L%3(=sm$s>z8f!H$&}vR1GJ6y8y7jvjVs2k$mZMs5vQFmY|z?@A~}2 zJ9pTilv01O5czH=4CBBEOKXdk=n%*%LBIAT-W z8nVv4;*f+Zarfdwug#8c=}x&E4P!2N!rQvuJay>GMRl)pB+J5LY>k( ze06j(e{zvxmt4G~@7I^**C4k%rq;N-KC!yGblddI+FGc$U%b4>eW@>YD{pcr6I)@a zXxKfBPeBsf$%%olAPMJwUY-c)NY-s3fEDM;`cv#CQ= zz3G-PYr(z|Jt$l1Ys{Sfno%?}!b#KR>LOjS((Oln%?xwuZK6e0s86PS%u&+zpUnp= z?l%mtO<~fWx;M>s({QKmstX^Q8f@=Bn6b{Z@9o>o5Yd!#4x1L;J_ z>qF9Rn#`{~k+6_k3SN^l?yKpIKbVLsbII}i&m{{xu&;*J_El7?m%rf2DMG-T9fM1i z`$gHgOBY}8em{DPp_ARor?_#*Vp*|~V;JtMb-)#*iKx zG`tt5`Nt9ET9lT`qNkw7U)0ui^zATBW5XTG6q7-%P>Ej z-Y6yP5q{Et0Nw}1pqFtb_0yA!uxAFIE5xS!+~VKx({)EqQ=~8lXKa@t?JZ&--Ho|m z5k*ift0L>~|J#N0fsV47!U4|oH#1*$B_Vr&2Q{1IkNl?7mm70*f65-MZGiuaOy`s(ywGbXnVwf|yj!9Mr;Cq*zx#6$2^ z#VV%mPy5FOn7;T~rf#YFX5v&bdWGa?W8m^s|K3|@?qwo?I{w%?e*a}I zlL-=PTRUc~4so<=9A%gm(MrPDX2gv3BVKb#cx=&uyIdg{O9;5^gio|Yv`p+pDT1MP zJz=>r;$;a{j67Rom&K;j2_DK-;WEN9_o-X#knPu9$(`F24)i z*5;gDi$(od<>j=b9o>;l+Y3NIL2OXS*_~wxmJ_{s**Lj866&*WN%qDhkvCA!`Zo%J zUEnT!hq0$0c>VPxSQ>r7RS{-MxIqRWJhBPJWU<-q9GWCNw4Ykpo%crx@9 z;J^Q!?u|%omae}84F{5{?d46mrXkZ)5viOebP?{5UK#08Qs8w!GIS2S_%&^Pp5x3C zpa$79PcQeGCHQ=cP`y5J{T;Qv%jG+z+$5Jk!ZlxG!w98oj9k0sRCzAXk?F4xicX9T zpOZmV&8R)Tz{Pi1W0x_#BA7)%`g+8Od0{M)#0Yc}VT{*$>l2C1L)BQ&M3_W@SLPqdiBf> zMF@9IL_eT-!HzX4miA(oupE%@B1vbo#l?en6~IY_lq`tEM!mQE&X0R)T6q6DZ!#Iw z2zu$43B^NB{eh(=;QruFxS@Cbp^3J4YoChu@4MrnwCw{J^vJb}Ay&xw^Co=s7`RZ^ zpV`Sv&<5b2lCb9lh*tmy)*#t>m3z zaX_EXQbQ${~246->oH%T8l&+(}ukL0XzQ{%8d52AwfK{;c}N z{pl&^viO!s{riUKkT}0Nq#^*y0i< z2^kpjxxKHl{r62JrX<&^pU!HKFopn@fA4l41o8W?>gFuz-(9`$j-X?V0>ci=KWEST zAC9FAY-KBsR4##Xs4GZi^WKTT(dwNb{_&%{x<9{~4Eo+Tw#GGok7>yLY>k{#x{Yxd&zq7$;jD9V z^;>e7Ymf`xyJM=228Y*{k_@e$%$S2L2Smf>nQvlz^Wpl@5<*>p7Aybr zmSxnap)FyC@|g_u{r+>7SIegBW}liaMYTv18YxW^pf)BvM&&S7q<^hjWR{-*DskYqHaU$1m>rzLOAE_*s$k4p3crF&CNK=dwl%0(D@uN6q^TEqT3fWBF{ zF3c5UY`=Jo`B~)^C=R`hxlX(4v_`M^`m14In!hKT?%m)0n!<9PcDF%DfFd;YdGqbV z_hBWoM$EtMLR~Ru`&<{!EvVQ?SieK#4xj3LgxY}mM)XPyAcJ`2s)0}0T@mT;_6<(Z zKPf17fUn%C{KF1q$P7MEl-P%3JJ!T|yA^-ny>47=^{xrq^=4kch(XNgnnYy~pyeTnVb+k>YH)!NA4W;XRdX`+x1G zo|JMx6KI=r>XneW?zbzi=`KIpJBy(@kI?gwG(WWt(Ydz9nS6x~iUFnDwX^F6PTCbp z5u2<_deS9lmkTksxy1f@sxr^dwvs@o`2bst$*6q=qHX83Ue~O(KU=zT9ka7`lh!nH zKqQ#%MdzTjN;8wl?Q1Qv3ePw6BHSR_xt9mAxw&>z>v~EGPE&RhGRq>r=J4BkwQYD0 z;&;E?^7>u7sl)EbBGdz_fP4xIyY(68F2La3HP81_&>Y8#wE!_L<6OZMc+7j$p1r-N zky$~-lJm>9yyY&D4igPhh{IrM5a%>!ewvxZ>=?$3{nA^ekjrE#&*6?#KKkxuEC8Vj zx98}7c=(rZJyW+Z4dFW)jqjWB{M$_J_pK}UWgAc0-{xwHq+@a~p6{5%XnKF$ob}B0 zim6k?7GfPg_#uN2QViOm$13{lh!6$m2sCo=#@=@$JubgT6Auqs|NdR~dcX;71*1{( zlehKqiNt3Vln9_~pVX|b|LFbAMOhsK9`UZ~7_>rE8h(FN$cQ=g&gz<__Vbs~Uf`05 z`s2+(n-af1_-#T3QX|aqWrf1gqp#gGoBT_m{-z#jhIoM&e-r*h(_!d07I2_5MGe_q z&pzy9Snt>B#Z=4#e+!;YZm(#+;>9F<$2T9PXv3RnwWAZVS1#3nw*s)jpZUOn1KLV#d>MviOe)pbKc~(#X;Bv7CMDE%m#4Nr^`pBtHA!>*dGWk9?hxM+-EnBR|tT z(LDiF?kITzwu&G{aN^!s9#zt1_E(~*n?f$_5Gsbmc2;fCId&pHkwPn^?n6LN3Zbl*0SD5DS$BDOsM|;^)I!UK{ZYpgXhJ{s;OtybOxcfWS zJ6>l8MKKjJoj;V@j5f15-TB9i6t<4a;r1L{vqQtW7{a6^mJ!Fvi zO)eUI?25Mc_>p32?M5g&>Q1g|iqm0I6QbHTa^t3|-M%a|kG5aw?D9z5ra|Pk$ghHN zLR{H5)u=}KBTiR@e^J1T$irnY>+{!>d_p1MP44qH0knHXg2L^oT7sY{C)bz7SM>_x zQ%AmfV>$DpI0G)xy^}mW-_E`m)gHwr(-AtS$_xj*2x)1<*KLLLQ6oc0J|ymoIB&N-0YT$*5xF!hN_}qUE{Bj>380ve%S#^-q$0^UI_2A zMOkqfc6ud*u4RJ&^|*RSgd*`ChuXmu4 z=CWJ-7(!8i%Vxm0)xO{DpC5w2;M;M@SAkZ*IHUeXMpNP=3)TJN0)g!4e(>%B8247M zGT*1Xic!H{D_TUVLgYKO0M1%RV2f19#fy5PYE@d4i0HL6KxbpYf{+ltWpR3`P^6v* zb8^)rel&=Jr^m|dIZ{pbxx4?`iPMYvFez3NLB7K@HCYi>EP)>*V_#)M$p*0YxaxeO z6TGR92(CmZUcw}(L#3|~H@K4CQFTc;QNnjPz{@#WToA&#P<<=dctsxk#-cDk_k;T# z29@!Jx^Di{SgG9y4Li30XzaKfe3q$s1vtm$kg8T6KdEr-D@quXw@_ON+Ny72LD7n# zvqU)JbzUo6XO%wXuA*hxGNiaBjlvfg6}(L>c)9&MquTG$F#o%3kGXBKvU0%B@ac8! zFekrG7Q@piPj5QaEPRw|x#D$Oni(`_!P-{ojXk`KhXI9AyDRf$xyT?w_hgDxIbw)} zW75W(1Vx_iI(D*unjd$fYu$c$>kk6FAO1@M)sLeJuYNpT~$vcKf#y@EpjY0XHQcm_$e$mAbY1~S% zi=E_L^fT|ztYkLm=zK~w3n5B=gW@FzMLY@GKB{kYJ zuVYfC6R)=Tuq{yP1fMFk%P;l?zdf=Flx63W)M|Ho+;MVND7@k@IIdQueTE!bRe9yf z3CG6uD#pqFwdL7q=T`pq!oMy#Sjc>M9AD>Z!^ON_JQw!Z-9-?W{vkw#h$JNZq_CjG0qW1!tQ; z(bry7SX9h5GLYiv$&_^^>&@>afq~rAz8I`5kRl`;SzQ%p(@G^YJUiggy<~^CuPy^H z3cZZrt*+`5j@AGRcvC(Uwox8-Dg|Vkje*>iQES-Scv($$tEz{pz4~+Jo_9sgyAuiy zdS)`d{j9*#7QC!ncf!fL4IKxO^F5n>g{#G9^m?7RhAAOq{ zgtYeZuL?LR+|Gy9P9HlsYZ!T<6^atqQ!5@GW*~H`DT^`AIB&;S?p^%yXift zj4!}&PIjrO!$E*6=9DRiX2}ec)~{+=)a&kju&3hv%?N^-uS3=Fw0+9B-Sv{gn!j7*O|G2Zfb+Aio zu`E(|bPip=Ob}S^b~>`VhVpJXM8C8_p<0LNFVsmDWiR~fP6w#I{gA)OdtL8vBRSRU zqwV0DRQJK|MB3QOAshRVf6aO~-d}erW_$NqSJ#o>RaE`R+F@jU{n}LR^m21RJ;s(m zZ)|~XYU{>tCgv%W+Y)>%JGo3@M#^ER#?U)#i!AEVH@Pn`@ikb-a=yV8NdUY~FS;&h z=}9HyLruD_tDW%{DGupnL1T~BQ;Q-R-J@87p&62mt$hv^nQfWfNg7KsadKo*wfDRZ ze0EFoIT%t+m?KGn_PU#9*X&m}>ZIJ>>|A9RCzYogvE7W(i&>@ZzL3VU-JChCkdHc@mMj}=bdq$ikgB!ig(0h`Ne1DxPfOsVYAp;)RgRT5WS^T#J#kB-XFB2k;8%WIDJnhs-Ka}sZy zAL&X_&S6!yYC3r1YC_db`GErn=ksTAepMgcloEv$z*|;yy`zTP=V`1mZMOW`w0H;Z z|If#8Rn~Z%Oc{|nDij|I-y(}Gc}DhHnQbOw+Ow_cZK?TdazIA?wB%$jZRJ+(8jZ`| z?I-sYXt%$&JLkgG<1}^;mkw3m8egUaKKNb%X%9ZucGd2-|?ZwMprMjYTC-Lg5X45ITr!4LA2&O0=I_0BZ0{JF% z0J8U6zpBRs-YyPmY6MCs)shZCoUIj8Y~;*Q_z=W*!nC#7Di=90`H*Z*Js5qdRD1sm=h zQ}l6@*A-i3(TsORD_3xz+~Grv*Nt0@?+Qd$ry9-R1F+Vmn6@`fhlIv9hw0h@i}4!2 z0VE0VcUVBp{A|?qx}52*fb$uB`|Hxqsmv1!>j9EMsJ*fE&vxmhe-_-lm1=Qbt9DYW z`MaP_1}*OQ+Y2$0z47bc|3td-m@z30bY}GK|{ykpF!NPu?WsFm6}afVVa8>O%318*`fuF^~ONf|Vax$ktYBCg6nnfn#**`d}`z`cPj3OdQjv&snq;g!Xnl$ zsK5&g40i5@>EA0zc*6MvRpW7av&*34<3>Tc7iUuHX-@PAhe?4aMpL+UG!d`q6#lgtGV)bYBE9A~N^JT@_%a5nl&b`^I zxJ^{l>hsOnY1}*QWaDuZiS^PeljZw;#KO_UcITTxa`(Gt8GH4;%<{o5T1ZjJ_2LLr zU33HzlF=1~II=>B_ClC6z@uJlUS?|hx&EW>--wL&K$t=EE?%0McVc9MILWwWH&S{^ zGVg3#h*kkBIV3^*RtJ6ly`-+i3T;Q1jJDgznT@gE`2zHi#wC4QN`>QHy&CyF)vA)J zpL7tFT-PgI{-xU8RLsCK*VQ1H9BBpM=21`&R4j_SI>wxD_IAky?P(#ez$g~$Idp;w?mQXh#@e|U9{srFd9q_~Gbh1;u5e4Sw=)IWY zgmR%@hy!NaKCuO@aYKBt)_@n8!yI8m9^F`_NT*aG=y-6_XnZhaD)89a#VG!@6Hv^&TKTWkjf1_(e`51?i z3xz}G{DxMHuMnkU>_^wjRdL`hsQFIM`h#2qlE9U)tNVP*D1+tp-DF68>81wt3RN5? z+G2@w`!gI6+4;?t^1KX|OPB&CED=_h4D=82CGZglKG%4SSD`%dcxKc5)D-fTFP(tW zY2r*)MZ#WIJQ6_%!MAZ{sXSuKw0W1ZMTV%`$v$2);+}!YYI{;@dpt7-x?n1>T3-c$ z-s4H_VS+B~X3^HMb*qS@^T%H?zrqVlw5KrAg!j_Kki#3`ny7R9Ej29Oq)lyaP#d*n z^=~9w1ClSo9pd(ix1dYWD^U!jyg)56*JOgxF$Z}Dpa`?eNod_wK3)dc5%3BpE-IWj z^11boz)Ko}^uh`bR0uDR_m*2(FEG|#i{7k0X882d(>_L`@I3Ty>SfC~+Em$>zALyN z12oq>Re$xhFoYcd_W71_+ivZ@F9X)9rADemrE`uR+Nll|H-=a9C;G8#jegIp&3eVJXEL zB%^|Wdzhu`yJ@e%{ahdG6<@;vn-cb006*Qbu|@T z0Dvyx^ap07^`LPwy|llJ9uH6+dTviWyscl@0q)znJ+XtSyIR}Z>DpP_`nrF&g989O zR_ZGE^?k-xh`wo;?E{6^8ar!ZL%t+bjV_GBA$%Maj!~!vdXS8-^w`mrgOzz-aq?41 zHZSSguS|#p5aP?1M?f1FHAD8+zSaC+$~B=bsMy+j-R!rou<^K5b;7m*h#?sPXaz5j z+ftW&u=A$i(;Wp;`cmm}0N=ajg~j0^Q^Iz+Ws8AZ7)|j1<3hf^{rF3Ona}v~#9oy( zX?HXKBy;LOFDSKtO+)Il^z8nhlZS||<;{SK5xY%4jN{4i8A^Kp!6bZVyKX_xLUFKP zxMjC`!C(0EQ-{OvYd;%O@`v?P2Us3|fqQn8s7lp^M*XnrV7rD zfpi6ao9b_^)E%@1L#W=EUymey}jiTFgqRa=f?`ptCA|9C;%PGCn4*l z=eAYWup=8CuqpvI`f`Ur_$pgId>IJJ{YOCna)56wZ8|E$mS$%tqXE)RdRc6pP4Hn=V1TpBv4ejrTZriDF+Ckz2*;04ES+CE#Zhd8XAbIq2M}z#Mq!SA)#07%&vwNjP{AdP*9J zheKYA-EFc^K?B5LD${ks;cAsdP#A!3==+l)#wK!15?NpfJ_r~)z+ndk*xSQt%$kxqN>K-|zG@(}d+jNgKo!wSOywPA}`QVNcMG>bP z?HNQKrWesPT(GgOx%TXq|Aq}u|MOYj9D9z2lBq>V+)+UIh~`lXvts7q1V_Wj{`yJm zr-|J~ie^B+RHHq_V|su@qDHy9s>}vK;Q|j}VXXk(V>6 zFU=8#C3XJjA>H%r%CyOYKbT2myVEi2;nnkj+BqOQr> zuK^*QhAS@fCFTJ;fnrS4LtL1mTk1y$auZkUy3r)5&VmjYTlSk*N}D|Yl2ZAm)6qG= z?_nmRda2QR`iv@m zim^IB;Jm~NO|xehm3kddob`x#ZsnA$3ANAXAH2K9vM@_@z^o z{lpFElrsIOqw`vqCy(wLFiy84)t`1*mvPi7ap>sx|6Y`M*Ei!&d7ua?0R9v5vz6{W z#5TP&!6e|f@hjscxufaCn;QD)pW!({MV~m%hIJ3`>4<$T`=F$SRz`8kAyI})!ylVj z5D;gj{fKI#kS|++?-_Ex(jJ`;Fm0#5M|k~h-`DAd*LD%FzcI|6W_T&?>^==xu23D1Sj4b(QoVu zCwkPCIv@S=DL%$Gph8^guksW_LW_I}DOZ-1eQs%GDy>(ZlNIb!b<3!Hd0fzRw4zuc zH+0ARm-dnZQIHcRo@``>sG+79y;))HYrdTT~q)tRA{HBaoFu_lknSx=Y#;y z&PTQ*JwXDuzv?8O?7cIajrO2xOWG$i#uY@{_{%rmXGvT&H}J3gWrQ$OkEn7As}Mg2 z6K{QIAvch|;A-rv2}y*HZ$_U?wXzF+Py$5)tfO{5Hy(W9HBihdyhvK^u%Ht}TJ30$ z3SCjKUa2#TGv2K^v2c6mb+?Qe1n#wqxt3;4ksrAUd9(eRlW@to8en}cc2fRp&Q7l% z{-g7^ZGloK9k6zh0D3u=%lyi{o%T|J}_*qZB7lV($mnanVD+*fOzHpg|?ma=X-m&oyD8ndm_tP8t-@BaAmyB z1j7K?2tOQjN4}-vMt3fiEPx=?Sa4=vx(!&JbhkliN8K}wRja&>ekAH zKF1RX9($$TSI%LPG|1A___9%PrE5FHlEh@F&N-NiQT=`5ne1sE%W6v_29GKG! zA|1{X;)m`w9ARY)nmd(edvAHgB8_5F1Y84>; zTJ8r6za`o}Q&p($c6V!$m*`yY)J~^s$+++HYF72cdrP2~S{o2PnVz7c7c4wGT1H9N z_W9*PQ5aw5YsQXy=`biY-_=FSo z0f|zo{M0a(z7ST4VCP)~hI)C_)O+7^mAJp12RsL>b7Po>4&OH5UwYFt!QR`mX5)SF zfiXH4Xe~k({T}cE0Rq!JT83x{-}tjT^Xzn!te|_5B!hZX+0qcy_4S6(z2~wf%~(?g znj0orRQS7-!n)*_M*>!u=NwWV9OyYJw)8dDmAR0uvXSL!Ylh~P;5M9}+yz@+YX2CiP?|~ts z9+6QTZ1Fld%L>;@nIUvQplx;AOcSYsov-0iau_kS3J~&%zMx&CHI^2^ZtHnAF60$V zNfu2eU4Z2R9pB$IGa~m4dh*T5jXRZwy!PfkC@sNAJw_VS+ZIJ#Y?~nxJlCx)zqel* zb#%lckqiT%6DpB_4{$;4Ovt1Wr&KAoJZM2H-HYj|^&FJv>~?LL+_-J30Y4A~>WGk?uIx%{LIfZr+)jusA-MKNcn8WZQ8f0Bh~g_&ihYeShxo>VCdVy)iBAp%cM&eK zHP#5K>@m)zCtO~k_1RpWy8XW9c;}oH2K07eFvxX&H+^bb)$$;#N{*5i5bcz6+=Joc zISy>tP2ZvP-%(l-SHvtWr#~#s-lKkcSQ9(BnB}7#2glg(U5i!8>^pAM3_3~ZU%$)s zTzVoT!i{1R%e$DeY?k_)LQpq7{I zuYiRJV;|B*@|UFX{WaIl&+IVHu{>n~_^~;bTch+syAAd!6!!MZ$$`2d#wu#d5lE*L z1*gXIZaaZ4jK)Pgld^gb$kS)zjT6}5#a@ajycWI`dfOUX?d$VJ1_Z>6 zGFhQx-*D4C9Bfrdc(#*hiy8aW>H-u-n@8 zemm91XXPi&qD(5j+i%fSDKz{{;L-fEQ#SB+J8JScN7=YnHAjm;aJ-59o5p{1l{Nk( z8EUOnQXAk}hVaOs1(585Cs|>^)I_ND&KmcpPl9mTNwdR5F}0o;E!Ian z1<0n8Qit-%V)3PGP}nWY6POBjB|Mb&aWI5+L(Ix2hEuvivfGr=Ux0;(#NfSFsH0SS zTfVHJ1JK0JNSe2&M|m$LIMMly3^-n#)p?ilydi}Jlrh_-*w^_iW;uHMHji3L#F|bB za!_I40~{aNc;yhw#m`op^IqZ6Q`IwZeHr%|fHCtW+=B64(ETM2>Vrpu20Ve5<2PxF z#X~%}m&Ygj+aRi8YF`P{bu!_qI9l3NRWKmZx|Aaru^q$t4~ZhXj2+K*H1MDzua^nu zuuT2+mTSWucEI5$()Nz$k#e^6?B|3kt6Ho1TQg2fiAz~MCOM#8-sLMchP zx@+x&BMTj{yx#d>Q2cOZ!FX?uYi{aZap^;oGScsRhpH*%8A@p655IXG@Ci3I(bDG` z7`N4iid~#CeWvVAikZqIL18*)6r)nQ`*V8QBwCP@-M0;SGk1^jq{@2mh)acUsSks0 zWrR_VKU{(D*Tp3Y)!ne2dczbN!2*iDY5p~5)no5)P|4@(<_wG5JICsRFTZ!bMDSLy z!*p&dK8~o*IWy(kA9a7@GrUYzJyZI-P8PLQP!^l zEO{`xs6!vPxD&=7H|Tl}_FRx>nE2zIXo>ECKw`4bUIysViF+rpwz*(C2Bv?>q!c*yyXaEcepe5>H=zrcpU;q#VpacH<{;vS+6!JfTGzbm;UqBj! z2LEp$jqAS|{|)*70BPp_C*%L34v96+W4;qTq{*~#>E ze-2#E8ei?n%HpO;7GF&Npbfb)$bK%NP*3w@N&@0BCs4-bi^}d2UdP>8T3`FUnV=m2 zzui&)w|lc)FGz;GEK-Tt+n$1FuGdF&wN4!E6%vn9p}+!PKa=%%YBo+T;gy|i_;=#E z``^`I+>77uogOb7Z-?Obs%}0j=WPlCbio zWB|7?S$>%y{lo33m~HD@3pG{*K41|fb9`hr2tztZn!AtXw9P68k-p%?e|QIMG#W7{ z*{xxYy@JogxoDKi+zz?V^DD~Yx%+EEx}CK~fZv+;iC1vt1!K9B{gYKnv@3!eQ&dun zuDxX|B8Uw5kc3aW);#^tbuHGt{i)t-PyqVCS@{}y*G|S5(;~q62_!)3zF?l-?;D@_fid;KC$I}Fvxu7;E}H95Ff72 z6mt<+@IAaIIn3JV&my@-E%O3jNo79vYHa}(iIyI>dFKI1aY^;-=Mog2J?y`HHXhGT zHv7m3qM2uW#3hJvmvt5hqDXCXuI$YO*7ICr0 zPmKu??`Uw-J)xAH^Y6SH-K*WC^U{-f!MX8448ZuRJ4HM@d%&{ISc8jqwK_DO(ie;i zHg~~Ju;WdPM_fT_%iT)94F`;u)p=AkeDG_Fmn#uHA5Q*uXiYvqic#wavIAUY%yGPHKBY#8oR?fc&u0R^n_QX5WKH532$U+_RfGGoKRSK zsBC%t7#1VdmnLfaij{FW#H5pCGp_6=LIksd6Q1nN^Ls$LV#)I~`Tb$9!-FtY`Olv* z+dE&cz?t4K2X)`3b_Or4LTpmhMr8{dKf6j^R(5achz2;)S@N?c9w+q;U(%2XMMb6D zgwu|fidw1y6juJEpfag6;WjUxRIR_|S{MI#Rz+_jf zqtgg8{5R&NnoC@DJjPoa@@fdh$%V8aoH>$zm=k6nHf@<(KQ$*+J4A~-(wttJo#6e}+M-Q|1d-8BlME?SOcQ3tnQ{`!QLESq zwwss5|HL*pS-V7LOYwcoed5EO8jDD{cCe8tCISz)I9PWw;^w(!>f-O1KBRCA}%SVvT2YX%U|8iG5Uuf zh#dQ+ut12{)|Pun-26_TeS`CAy^N*GcFd}Yo$<;MG35^C5+JvZR`7nYIQjxc!~}X~ zDXC`F#I1KhA1ZFIrr@sw>3}vC!RDTxXCgf%uMdCg$`Sw31+;NOU(O7l8EIc%U)`Jg zX=zqFkV2p>ey-Dm=Au#6L|j~zB;);&+1xaFWKrmMheJOYg7|4cl)wyzJ|eGRv8FQ* zgKe_pY|y<>&P_;yx-~mCac`_uyY=X!jW-=IRM7Y+&Pn^vPl}wb`%v*y#_m1mnf3<7 z$`gZ>iVPnSYoAvXG6F*lKOi=$imZY+l7-d=oT{`$}ho#=2tupc2v1~~dC2OSuN_-d- zO?X$nHz!PGgPZRiD}<&qi`X_C=mSTF(AES0A9`{L$#u8$D#?|y*IlH=YO)!brMjtIW2of(2ZYCq`Q^UT> ok`K51! Date: Sat, 18 Nov 2017 20:22:13 -0800 Subject: [PATCH 12/24] Add logo to readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2b959a5..58e5441 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Pogo +![Pogo Logo](https://cdn.rawgit.com/gmemstr/pogo/users/assets/web/static/logo-small.png) ## Podcast RSS feed generator and CMS in Go. [![Build Status](https://travis-ci.org/gmemstr/pogo.svg?branch=master)](https://travis-ci.org/gmemstr/pogo) [![gitgalaxy](https://img.shields.io/badge/website-gitgalaxy.com-blue.svg)](https://gitgalaxy.com) [![live branch](https://img.shields.io/badge/live-podcast.gitgalaxy.com-green.svg)](https://podcast.gitgalaxy.com) [![follow](https://img.shields.io/twitter/follow/gitgalaxy.svg?style=social&label=Follow)](https://twitter.com/gitgalaxy) From 262c1d4d7d350a993faa5233976b2e756e20c960 Mon Sep 17 00:00:00 2001 From: gmemstr Date: Sat, 18 Nov 2017 20:24:07 -0800 Subject: [PATCH 13/24] Add XS logo --- .../{logo-small.png => Pogo-Logo-Small.png} | Bin assets/web/static/Pogo-Logo-Xsmall.png | Bin 0 -> 2223 bytes 2 files changed, 0 insertions(+), 0 deletions(-) rename assets/web/static/{logo-small.png => Pogo-Logo-Small.png} (100%) create mode 100644 assets/web/static/Pogo-Logo-Xsmall.png diff --git a/assets/web/static/logo-small.png b/assets/web/static/Pogo-Logo-Small.png similarity index 100% rename from assets/web/static/logo-small.png rename to assets/web/static/Pogo-Logo-Small.png diff --git a/assets/web/static/Pogo-Logo-Xsmall.png b/assets/web/static/Pogo-Logo-Xsmall.png new file mode 100644 index 0000000000000000000000000000000000000000..2bd7945c8a2095f7fe481760d0eb0028029e083e GIT binary patch literal 2223 zcmV;g2vGNlP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGqB>(^xB>_oNB=7(L02y>eSaefwW^{L9 za%BK;VQFr3E^cLXAT%y8E-^XO*0N*(00`OUfKabIpKDk>@} zDk>@}v+}c!d+YK}2)0}nIqibYc-$UYZGvq}t6J%Yi7lPQh8AKo93r;V z1H`ueZDLE=Pi)D1i7m02*y5XrEp`tXkJ&>%jxv#VB6gFCh#Db)@5(h{@(gcu5X?J4 zFt?px4h!RPg4t~Zvswve9wWHZ<5h!4+a@W+uMC zcdvY&rNEGCz~D;2z;gJ|f7ghHS3%j#i@*xNRb}wC?=BL@tD#)xQ6T#5gb8gar17dK zlX(`1KBe$by93tn>L`x|AP`rSz^JC^?oW7?l*K|2_*#jG-aCdb@+v8d?5?3#B#BZO zrD(OsmgzVm7~;D~U)(7=u%SgTNRjroJyN9DD;N@aBs7uNW6h)>rjc|-ndk$@1d6zP zJ0$UHxfapA)WYH<|5FyeX7as=2C*=_o_-})pjQ$6iKn|3DgD&4uHP|%)2y!l_|>pF z`jISw+eOqA3`g=**MRkf16dv9Z&@9}I6JiO@XZbpSv&Nyz6OXKfy)ZTE}rHZBs7yT zX@HoDW@_V`9VWDz`h`@%#H<1f#0PGO?mSId_*dUw99%`C!fM1A)_=uBn}wK=2G)l) zvp%ee9fD2pF#bJQ;pR5CFUZ!y?XwB=EEI9cHtNSyl!5>D&tKNQ34Q)$bW&GkA2p*) zfU$ch8h%MJXOK-}2H3Ra5}Ts*Qq*67zpU&Y((-iK1Xd7VIRcj!&^(@^47`fO%i1?Z z^k-j6ra<)VhR=R_a`Z+15wZ!YgwJOcm=BG-_`=6?8?4Y2iRiGvGk~65j<*yha9}op z9^1s@y!gUrS&^7LSAm+7j<=Mc3TmCT56p)-O_}C}CHeQg<}9!ijQnF{6I4k*n6(Aw zLmMxy3|Jr7>!?7#Gw_K=_w66>kC07pCH8xq=4a1&cbuc9NY|OIakuaX>I?UyC zmd-r-VuT5Hh1b)6N`sOdftn)tBTrWjiwj3y^(X|RrWlNdgT$EK&Z5m8nNl1j#*DM% z^2T$*;6@7=r7#?{$NK&DNM@1H4955-k`rSlyEz=uAiG@e&QnLJ6LbXWzH!<3}|mq7L-Jy^L@!FMD~^IZa0 z?y^f{s}J1tv;~&KZNGA139o{(nMt5@m`SZ=O+8>Lll2q?j_RuER+n0;$q^V_1sGZb zObTEM6XvMEuv+?&zD_KRHh=2y>8}NkEP<7DGNhW0>1*iNnmRfbv73&?y+y_no5@(x zK6@lKk+FETEp`w2G}=u6u+~Jo*<`vXyg~5dyK+rr3Y_y<7?(#C=p?E@Cs74Di7L=Z zRDn*S3Um@xpp&QqokSJrB&t9sQ3X1QDv;yR39FZ%0*lWXumqh0i~a&wqApTP{Cm`r z)KB&qE`u@U>ir+`;{U}Nff+YOG^v+JNz(h|P%PU|A&{1&j7qV9|7fWqIqj z_qiZh+9~EPKP5`Hong>Z~JYxGgtWs1q~Q)B`$vIj8q0${*7K%efZ`v)gqX}P zO@gunc8LW%O&NHzR_NPJ$7Kk_oLj*-P6yAk|;E%-2j zcV`ordrKIR%763T7;hHZ5_|aW1fzdB7z0`8s%WNEHunzk&AUZ#MuAp38F&u5btnJq z@FNflhD`R$uekteQSZ_taeaa{rC+e7T@kG51A=wSb-`-9DOmGvlM}qSY~J{t;FEWY z<=+sjY!I<-xGGpvFA3J9-wM{4UUD|FhnQEtBl`2=&ljsIzFefQ6YHZI$%l!1$#B{M zawWZmZ07$~nF;A}d;c;ZdVc9rx Date: Sat, 18 Nov 2017 20:25:16 -0800 Subject: [PATCH 14/24] Rename logo files --- .../web/static/{Pogo-Logo-Small.png => logo-sm.png} | Bin .../static/{Pogo-Logo-Xsmall.png => logo-xs.png} | Bin 2 files changed, 0 insertions(+), 0 deletions(-) rename assets/web/static/{Pogo-Logo-Small.png => logo-sm.png} (100%) rename assets/web/static/{Pogo-Logo-Xsmall.png => logo-xs.png} (100%) diff --git a/assets/web/static/Pogo-Logo-Small.png b/assets/web/static/logo-sm.png similarity index 100% rename from assets/web/static/Pogo-Logo-Small.png rename to assets/web/static/logo-sm.png diff --git a/assets/web/static/Pogo-Logo-Xsmall.png b/assets/web/static/logo-xs.png similarity index 100% rename from assets/web/static/Pogo-Logo-Xsmall.png rename to assets/web/static/logo-xs.png From f8315b6f8b9913be0017a9e23b91199c779e8f0e Mon Sep 17 00:00:00 2001 From: gmemstr Date: Sat, 18 Nov 2017 20:26:16 -0800 Subject: [PATCH 15/24] Use XS logo --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 58e5441..54ff2e1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -![Pogo Logo](https://cdn.rawgit.com/gmemstr/pogo/users/assets/web/static/logo-small.png) -## Podcast RSS feed generator and CMS in Go. +## ![Pogo Logo](https://cdn.rawgit.com/gmemstr/pogo/users/assets/web/static/logo-xs.png) Podcast RSS feed generator and CMS in Go. [![Build Status](https://travis-ci.org/gmemstr/pogo.svg?branch=master)](https://travis-ci.org/gmemstr/pogo) [![gitgalaxy](https://img.shields.io/badge/website-gitgalaxy.com-blue.svg)](https://gitgalaxy.com) [![live branch](https://img.shields.io/badge/live-podcast.gitgalaxy.com-green.svg)](https://podcast.gitgalaxy.com) [![follow](https://img.shields.io/twitter/follow/gitgalaxy.svg?style=social&label=Follow)](https://twitter.com/gitgalaxy) From 27f561c3ce5e233b96da0fb554f9c48c3b0ffd31 Mon Sep 17 00:00:00 2001 From: gmemstr Date: Sun, 19 Nov 2017 11:11:46 -0800 Subject: [PATCH 16/24] Added form for adding users --- admin/admin.go | 2 +- assets/config/users.db | Bin 20480 -> 20480 bytes assets/web/static/app.js | 29 ++++++++++++++++++++++++++++- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/admin/admin.go b/admin/admin.go index cfdc821..7dbb640 100644 --- a/admin/admin.go +++ b/admin/admin.go @@ -140,7 +140,7 @@ func EditUser() common.Handler { Message: fmt.Sprintf("error executing sqlite3 statement: %v", err), StatusCode: http.StatusInternalServerError, } - } + } } fmt.Println(hash) if bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) != nil { diff --git a/assets/config/users.db b/assets/config/users.db index e5f311a92d5c18a7a717bd1c557ecab73e8f6090..ef24a396931772c748c003255b3edb359b121dec 100644 GIT binary patch delta 205 zcmZozz}T>Wae_1>$3z)tMvjdMOZd5%`7Sf?FXGqdyS!OY;2>W@95XvZ6cCFxvWRPz zq!yQ`7$vG0n5gKbrFpqkrkJD#6{nUt6(_qyRHWyY7Q2~eCg&x3yO>*sItH19`j;66 zpeTnr2h%>0iS_`mbN=6?iKbA_LigISajWEAt}*Y+X;jLZNa1v;Pr delta 89 zcmZozz}T>Wae_1>+e8^>Mz)O!OZYjM`1Kk17xC+F7F1B?pR6x`6)5tJf&UNxx6OhM tPxvPWuucA;&mzRc#LUde$;H6Hz{LNWf&V-IYoLbv{F`6fiwH0>0RTvh7;pdp diff --git a/assets/web/static/app.js b/assets/web/static/app.js index 59d2e1d..bf81fbd 100644 --- a/assets/web/static/app.js +++ b/assets/web/static/app.js @@ -15,8 +15,13 @@ const episodepublishform = {
` } +const message = { + template: `

{{ this.$route.params.message }}

` +} + const userlist = { template: `
+ New
@@ -72,6 +77,26 @@ const userlist = { } } +const usernew = { + template: `
+
+

New User

+ + + + + + + + + + +
+ +
+
` +} + const useredit = { template: `
@@ -301,7 +326,9 @@ const routes = [ { path: '/theme', component: customcss }, { path: '/edit/:id', component: episodeedit }, { path: '/users/', component: userlist }, - { path: '/user/:id', component: useredit } + { path: '/msg/:message', component: message }, + { path: '/user/:id', component: useredit }, + { path: '/users/new', component: usernew } ] const router = new VueRouter({ From d605d2bea24acc9deab84a5549d50a9884b5e5f1 Mon Sep 17 00:00:00 2001 From: gmemstr Date: Sun, 19 Nov 2017 15:58:38 -0800 Subject: [PATCH 17/24] Readme rewritten Rewrote readme taking inspiration from Bootstrap's readme. --- README.md | 64 +++++++++++++++++++++++-------------------------------- 1 file changed, 27 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 54ff2e1..358a9f1 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,37 @@ -## ![Pogo Logo](https://cdn.rawgit.com/gmemstr/pogo/users/assets/web/static/logo-xs.png) Podcast RSS feed generator and CMS in Go. +

+Pogo logo +

+

Pogo

+

+ Podcast RSS feed generator and CMS in Go. +

+ +## Getting Started + +There are a couple options for getting Pogo up and running. + +- [Download the latest release](https://github.com/gmemstr/pogo/releases/latest) +- [Clone the repo and build](#building) + +## Status [![Build Status](https://travis-ci.org/gmemstr/pogo.svg?branch=master)](https://travis-ci.org/gmemstr/pogo) [![gitgalaxy](https://img.shields.io/badge/website-gitgalaxy.com-blue.svg)](https://gitgalaxy.com) [![live branch](https://img.shields.io/badge/live-podcast.gitgalaxy.com-green.svg)](https://podcast.gitgalaxy.com) [![follow](https://img.shields.io/twitter/follow/gitgalaxy.svg?style=social&label=Follow)](https://twitter.com/gitgalaxy) -## Goal +## Features -To produce a product that is easy to deploy and easier to use when hosting a podcast from ones own servers. - -## Features - - * Auto-generate rss feed - * Basic frontend for listening to episodes - * Flat-file directory structure - * Human readable files - * Self publishing interface w/ password protection - * Custom CSS and themeing capabilities - * JSON feed generation for easier parsing - * Docker support - -## Requirements - -[github.com/gorilla/feeds](https://github.com/gorilla/feeds) - -[github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify) - -[github.com/gorilla/mux](https://github.com/gorilla/mux) +- Automatic RSS and JSON feed generation +- Frontend for listening and publishing episodes +- Multiple user support +- Custom CSS themes +- Docker support ## Building ``` +git clone https://github.com/gmemstr/pogo +cd pogo +go get github.com/tools/godep godep restore go build -# Set environment variable -export POGO_SECRET=secret -# Windows -# set POGO_SECRET=secret -./podcast -``` - -## File format - -Pogo uses a flat file structure for managing podcast episodes. As such, files have a special naming convention. - -For podcast audio files, filenames take the form of YEAR-MONTH-DAY followed by the title. The two values are -separated by underscores (`YYYY-MM-DD_TITLE.mp3`). - -"Shownote" files are markdown formatted and simply append `_SHOWNOTES.md` to the existing filename (sans .mp3 of course). +./pogo +``` \ No newline at end of file From 107530ef1ab8d8e7f4dc1d2173ac537150207218 Mon Sep 17 00:00:00 2001 From: gmemstr Date: Sun, 19 Nov 2017 16:10:17 -0800 Subject: [PATCH 18/24] Resize XS logo --- assets/web/static/logo-xs.png | Bin 2223 -> 9600 bytes assets/web/static/podcast_image.png | 0 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 assets/web/static/podcast_image.png diff --git a/assets/web/static/logo-xs.png b/assets/web/static/logo-xs.png index 2bd7945c8a2095f7fe481760d0eb0028029e083e..fd67a3d81aa016488f6e9f1ef2a1a34e030ffbcc 100644 GIT binary patch literal 9600 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 ;9QY zYF2tkYkXns73aX=9H}h>5z@MGXg&=)!^K}?@SmW%tOb48t_u#zVto}Dh!%MlOn91= zOD;a*;BvhvIP3Mf-Hq?@V9?LY-?fzJt)kvjo-0Gn%8W|C7uh_wUv#NcGQC=NLOlk% zRzs#qS7b>Cn1n9P&pQ1q8VikvHhsPIvE&vWTW2%6azamd) zr>2JZiyx!ya&W}*g6+Vexj4IV>YeN!Ys0jotS1I%1{bFF_M{;7 zppI(p!-MIc6L0O~D}F5m(b@qUB@fZ%P42QM2Dc%54pR&#(Od>u)&H4|IgD#jJ@@w{ z$%q|M_K5v8V%DpTl}(gScj!kFS#uuRtN04@`n1mq z-Jx-gIv;6?bc8w?54 zna5jL`!Sc#SgSTst@~86a$d&^kS{ef~s*2+avOrPB=px%+6x5Yg3N&p+QJCpw3$>}gAUE`P;QVYgHKN2JQ>6|n)vcKnbKFgyz5vD*|b z%E@nI7H!-oOIt(XsLc0Hzl2{B8d^_wB%gWPwWF^M6Mr`pA$A`f`F)Y4;628T$b5a+6Rk`x^Xh1mqo@i@s-rx={_Gweb$_UQjGqdbmi5qlWayf0_tQ1Z!-40 zl&&+b$ECc@S*-A`-H%=~JxqS!Nc^8)*y4Ax6UH}<6+L%I&ORF}0Z-XC2sq?Bqy;q9 z%|JpC&Jk**%e(O<*pAm=bIX4@YFS;j-yGdUKu}Zh^QnE>8;oCi)4)Abowp}F>vbmM z9B_yQcZTr8-;(dls=otbm9IF}Cua74x9aIb5VPhW~Ivjm9`B zuYOn`x72SOQgg!=rTIW&nzJW$%taaqXuEG@*3b{>SzbN=31KsbJx!3hzMGVsLz6?- z=NE1=pKm>J3TmcZd{pLx_^%)T4lGbGs)Lz_dH8o1=*s0g0Z@4-=qQeQZq3V%QEigp z#0j_mWs*~6z9A1!I_%vrZN@(+xZ|0hWe4lKTsdM5$G*e3HDz;di9!(~7@WE1UL^-r zEuFTq*l&SB#m^kfj>oFA5j)^Z?*%l=-aD}0wl5^Mfqv59BlbBKVqq7Vg5)<(|46<^Ir5X+Nk1Khw~z5JXfb+=(@k@cBLi`WLaAR0ULA{cF=fp@^Nd zYLUatNFsI$`~%zPl5S0LKzwq=AeU(MI%nh)0{?AS{V(6#tR=^lhr^PbVCEMTXQz>0 z3KF9*w?{cwRuI&zYB=3}n)}G6=Jz$GTlxb&7a6tSo8E-7Fy8xvV(>&C`oZbsI&%skV)&IxT7gy(JK;J*)U zmp`))pnGr$YX_0bj5*@V%%}e^mJKa`%vX-NBit$*we&m;jmIsJ0#ubW6-(vKL;nYV CYQkXv delta 2159 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 Date: Sun, 19 Nov 2017 16:12:16 -0800 Subject: [PATCH 19/24] Reformatted header / image --- README.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 358a9f1..86f9734 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,8 @@ -

-Pogo logo -

-

Pogo

-

- Podcast RSS feed generator and CMS in Go. -

+Pogo logo + +## Pogo + +Podcast RSS feed generator and CMS in Go. ## Getting Started From ed9a7fa518fcfae35a1f8594006ae3aa8150400c Mon Sep 17 00:00:00 2001 From: gmemstr Date: Sun, 19 Nov 2017 16:32:18 -0800 Subject: [PATCH 20/24] Reformatted Readme (again) --- README.md | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 86f9734..b8fe1bb 100644 --- a/README.md +++ b/README.md @@ -23,13 +23,41 @@ There are a couple options for getting Pogo up and running. - Custom CSS themes - Docker support +## Running + +1. [Download the latest release](https://github.com/gmemstr/pogo/releases/latest) +2. Unzip somewhere safe +3. [Edit the config](https://github.com/gmemstr/pogo/wiki/Configuration) +4. Run `pogo`. + ## Building ``` +# Clone the repository git clone https://github.com/gmemstr/pogo + +# Go to directory cd pogo + +# Get godep go get github.com/tools/godep + +# Install Go dependencies godep restore + +# Build go build + +# Run ./pogo -``` \ No newline at end of file +``` + +## Credits + +Pogo depends on several other open source projects to function. + + - [Golang](https://golang.org/) + - [gorilla/mux](http://github.com/gorilla/mux) + - [gorilla/feeds](http://github.com/gorilla/feeds) + - [fsnotify/fsnotify](http://github.com/fsnotify/fsnotify) + - [mattn/go-sqlite3](http://github.com/mattn/go-sqlite3) \ No newline at end of file From 6c613594da004495a06e58b0803aee0eed051200 Mon Sep 17 00:00:00 2001 From: gmemstr Date: Sun, 19 Nov 2017 16:35:39 -0800 Subject: [PATCH 21/24] Add additional "running" steps. --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b8fe1bb..b2e849a 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,10 @@ There are a couple options for getting Pogo up and running. 1. [Download the latest release](https://github.com/gmemstr/pogo/releases/latest) 2. Unzip somewhere safe 3. [Edit the config](https://github.com/gmemstr/pogo/wiki/Configuration) -4. Run `pogo`. +4. Run `pogo` +5. Navigate to your instance (`localhost:3000` by default) +6. Login to the admin interface (default: **admin**, **password1**) +7. **CHANGE YOUR PASSWORD** ## Building From b812b8be26ae8ec516f5d9423556e63e7b469b15 Mon Sep 17 00:00:00 2001 From: gmemstr Date: Sun, 19 Nov 2017 16:54:51 -0800 Subject: [PATCH 22/24] Un-ignore default config --- .gitignore | 3 --- assets/config/config.json | 8 ++++++++ 2 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 assets/config/config.json diff --git a/.gitignore b/.gitignore index 0586dba..ef02ecd 100644 --- a/.gitignore +++ b/.gitignore @@ -19,9 +19,6 @@ podcasts/ feed\.rss -assets/static/custom\.css - -config\.json vendor/ assets/config/users\.db diff --git a/assets/config/config.json b/assets/config/config.json new file mode 100644 index 0000000..b07fbc7 --- /dev/null +++ b/assets/config/config.json @@ -0,0 +1,8 @@ +{ + "Name": "Pogo Test Feed", + "Host": "Gabriel Simmer", + "Email": "admin@localhost", + "Description": "Discussion about open source projects on the internet.", + "Image": "localhost:8000/assets/logo-xs.png", + "PodcastUrl": "http://localhost:8000" +} \ No newline at end of file From f3779aa4bd4a9bfa917e1bbfc9e78ae4cf18dabb Mon Sep 17 00:00:00 2001 From: gmemstr Date: Mon, 20 Nov 2017 21:47:29 -0800 Subject: [PATCH 23/24] Admin: Added user deletion route and button --- README.md | 8 ++++--- admin/admin.go | 47 +++++++++++++++++++++++++++++++++++++++ assets/config/users.db | Bin 20480 -> 20480 bytes assets/web/static/app.js | 1 + router/router.go | 5 ++++- webserver.go | 2 +- 6 files changed, 58 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b2e849a..d828b14 100644 --- a/README.md +++ b/README.md @@ -35,12 +35,14 @@ There are a couple options for getting Pogo up and running. ## Building +_Note: [This requires a valid Go enviornment setup!](https://golang.org/doc/install)_ + ``` -# Clone the repository -git clone https://github.com/gmemstr/pogo +# Go get the repository +go get github.com/gmemstr/pogo # Go to directory -cd pogo +cd $GOPATH/src/github.com/gmemstr/pogo # Get godep go get github.com/tools/godep diff --git a/admin/admin.go b/admin/admin.go index 7dbb640..72fff1f 100644 --- a/admin/admin.go +++ b/admin/admin.go @@ -18,6 +18,7 @@ import ( "database/sql" _ "github.com/mattn/go-sqlite3" + "github.com/gorilla/mux" "github.com/gmemstr/pogo/common" ) @@ -169,6 +170,52 @@ func EditUser() common.Handler { } } +func DeleteUser() common.Handler { + + return func(rc *common.RouterContext, w http.ResponseWriter, r *http.Request) *common.HTTPError { + + db, err := sql.Open("sqlite3", "assets/config/users.db") + if err != nil { + return &common.HTTPError{ + Message: fmt.Sprintf("error opening sqlite3 file: %v", err), + StatusCode: http.StatusInternalServerError, + } + } + statement, err := db.Prepare("DELETE FROM users WHERE id=?") + if err != nil { + return &common.HTTPError{ + Message: fmt.Sprintf("error preparing sqlite3 statement: %v", err), + StatusCode: http.StatusInternalServerError, + } + } + + if err != nil { + return &common.HTTPError{ + Message: err.Error(), + StatusCode: http.StatusBadRequest, + } + } + vars := mux.Vars(r) + id := vars["id"] + if id == "1" { + w.Write([]byte("")) + db.Close() + return nil + } + + _, err = statement.Exec(id) + if err != nil { + return &common.HTTPError{ + Message: fmt.Sprintf("error executing sqlite3 statement: %v", err), + StatusCode: http.StatusInternalServerError, + } + } + w.Write([]byte("")) + db.Close() + return nil + } +} + func ListUsers() common.Handler { return func(rc *common.RouterContext, w http.ResponseWriter, r *http.Request) *common.HTTPError { diff --git a/assets/config/users.db b/assets/config/users.db index ef24a396931772c748c003255b3edb359b121dec..ce23faea5360ab881878ef9a58f8666f86973ee8 100644 GIT binary patch delta 138 zcmZozz}T>Wae_1>*F+g-My`zsOZd5%`1Kk17xC-!UEVCHaFB2EdAS#+<$;0b<#|rV zAtffJnPJ|J<%X#d-l@T%rh)po`Dvj=CW)ox6)wiU6-9bMdU~ZLIYFt(;gjFUO8`y# q#=!rF{~P~@&4Lb(_$PnR7XgaBX5jzM|C;|1(1t7gn;+TB2><{a%P(C3 delta 138 zcmV;50CoR>paFoO0gxL32$38^0SK{Rqz?)M4$}Y+q7N?))3XsE!48wp9_Te@YHUqO zE@W>vX)t(HSXXy8H+ptWbz)3UG-YU6a5Qx>YH4g#RB1{_GjmUFN>zDvOhQ9-lk6WD s3Ih-D01x~R_7Cv05kTS(lkhJT3Ih+~01x*M>ks0y5g^nLv*JG<5RF?fasU7T diff --git a/assets/web/static/app.js b/assets/web/static/app.js index bf81fbd..c7be3ed 100644 --- a/assets/web/static/app.js +++ b/assets/web/static/app.js @@ -118,6 +118,7 @@ const useredit = {
+ Delete User
`, data() { diff --git a/router/router.go b/router/router.go index 9fdaf72..0fe53aa 100644 --- a/router/router.go +++ b/router/router.go @@ -92,7 +92,10 @@ func Init() *mux.Router { auth.RequireAuthorization(), admin.AddUser(), )).Methods("POST") - + r.Handle("/admin/deleteuser/{id}", Handle( + auth.RequireAuthorization(), + admin.DeleteUser(), + )).Methods("GET") r.Handle("/admin/edit", Handle( auth.RequireAuthorization(), admin.EditEpisode(), diff --git a/webserver.go b/webserver.go index 750df5d..5d3fc2e 100644 --- a/webserver.go +++ b/webserver.go @@ -23,6 +23,6 @@ func main() { // Define routes // We're live r := router.Init() - fmt.Println("Listening on port :3000") + fmt.Println("Your Pogo instance is live on port :3000") log.Fatal(http.ListenAndServe(":3000", r)) } From 01465fd4a7a5f86002a81952b8ab0760a4552d88 Mon Sep 17 00:00:00 2001 From: gmemstr Date: Tue, 21 Nov 2017 10:38:58 -0800 Subject: [PATCH 24/24] New frontend styling! --- admin/admin.go | 12 ++++- assets/config/config.json | 4 +- assets/web/admin.html | 5 +- assets/web/index.html | 2 + assets/web/static/app.js | 44 ++++++++--------- assets/web/static/custom.css | 22 ++++++++- assets/web/static/styles.css | 91 +++++++++++++++++++++++++++++++++--- 7 files changed, 146 insertions(+), 34 deletions(-) diff --git a/admin/admin.go b/admin/admin.go index 72fff1f..d667096 100644 --- a/admin/admin.go +++ b/admin/admin.go @@ -31,8 +31,12 @@ type User struct { type UserList struct { Users []User } +/* + * The following is a set of admin commands + * that the average user probably shouldn't be + * able to have access to, mostly user management. + */ -// Add user to the SQLite3 database func AddUser() common.Handler { return func(rc *common.RouterContext, w http.ResponseWriter, r *http.Request) *common.HTTPError { @@ -265,6 +269,10 @@ func ListUsers() common.Handler { } } +/************************************* + * End of "sensitive" admin functions + * ***********************************/ + // Write custom CSS to disk or send it back to the client if GET func CustomCss() common.Handler { @@ -343,7 +351,7 @@ func EditEpisode() common.Handler { StatusCode: http.StatusBadRequest, } } - w.Write([]byte("")) + w.Write([]byte("")) return nil } } diff --git a/assets/config/config.json b/assets/config/config.json index b07fbc7..d367062 100644 --- a/assets/config/config.json +++ b/assets/config/config.json @@ -3,6 +3,6 @@ "Host": "Gabriel Simmer", "Email": "admin@localhost", "Description": "Discussion about open source projects on the internet.", - "Image": "localhost:8000/assets/logo-xs.png", - "PodcastUrl": "http://localhost:8000" + "Image": "localhost:3000/assets/logo-xs.png", + "PodcastUrl": "http://localhost:3000" } \ No newline at end of file diff --git a/assets/web/admin.html b/assets/web/admin.html index b3453ba..2b9a5bb 100644 --- a/assets/web/admin.html +++ b/assets/web/admin.html @@ -9,14 +9,15 @@
- Publish Theme Manage Users +

{{ header }}

+

Pogo licensed under the GPLv3

- diff --git a/assets/web/index.html b/assets/web/index.html index 9980f28..18636a3 100644 --- a/assets/web/index.html +++ b/assets/web/index.html @@ -12,12 +12,14 @@
+

Loading

Admin

+

Pogo licensed under the GPLv3 | RSS Feed

diff --git a/assets/web/static/app.js b/assets/web/static/app.js index c7be3ed..806cab8 100644 --- a/assets/web/static/app.js +++ b/assets/web/static/app.js @@ -1,16 +1,16 @@ const episodepublishform = { template: `

Publish Episode

-
+ - + - - +

+
` } @@ -21,18 +21,18 @@ const message = { const userlist = { template: `
- New -
Username
+ New +
- +
Username EmailEdit
{{ item.username }} {{ item.email }} - Edit + Edit
@@ -91,8 +91,8 @@ const usernew = { -
- +

+
` } @@ -116,9 +116,9 @@ const useredit = { -
- - Delete User +

+ + Delete User `, data() { @@ -168,10 +168,12 @@ const episodemanagement = { template: `
- + + + - +
TitleURLActionsTitleURL
{{ item.id }}: {{ item.title }}{{ item.url }}Edit{{ item.id }}: {{ item.title }}{{ item.url }}Edit
`, @@ -227,7 +229,7 @@ const episodeedit = { - + `, data() { @@ -280,12 +282,11 @@ const episodeedit = { const customcss = { template: `
-

Edit CSS

+

Theme

- - -
- + +

+
`, data() { @@ -322,6 +323,7 @@ const customcss = { } const routes = [ + {path: '/', redirect: '/publish'}, { path: '/publish', component: episodepublishform }, { path: '/manage', component: episodemanagement }, { path: '/theme', component: customcss }, diff --git a/assets/web/static/custom.css b/assets/web/static/custom.css index a0512fd..6202346 100644 --- a/assets/web/static/custom.css +++ b/assets/web/static/custom.css @@ -8,7 +8,27 @@ .container {} /* Basic container from styles.css */ .title {} /* Page title */ -.adminlink {} /* Link to admin interface */ +.adminlink { /* Link to admin interface */ + margin-left:100%; + display:inline-block; + margin-bottom: 10px; + background-color: #397AD6; + border: none; + color: white; + padding: 5px 32px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + -webkit-transition:.52s; + -moz-transition: .5s; + -o-transition: .5s; + -ms-transition: .5s; + transition:.5s; +} +.adminlink:hover { + background-color: #50B7D5; +} .podcastlist {} /* Chronological podcast list */ diff --git a/assets/web/static/styles.css b/assets/web/static/styles.css index 342294f..8050c86 100644 --- a/assets/web/static/styles.css +++ b/assets/web/static/styles.css @@ -12,13 +12,68 @@ body { h1,h2,h3,h4,h5 { font-weight: 400; } -.container { - margin: 0 auto; - padding: 0 2.0rem; - position: relative; - width: 60vw; +.podcastlist { + padding-bottom: 10%; +} +.container { + margin: 0 auto; + padding: 0 2.0rem; + position: relative; + width: 60vw; + height: 100%; +} +button, .button { + margin-bottom: 10px; + background-color: #397AD6; + border: none; + color: white; + padding: 5px 32px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + -webkit-transition:.52s; + -moz-transition: .5s; + -o-transition: .5s; + -ms-transition: .5s; + transition:.5s; +} +button:hover, .button:hover { + background-color: #50B7D5; +} +footer { + position: absolute; + right: 0; + bottom: 0; + left: 0; + padding: .25rem; + color: #f9f9f9; + background-color: #397AD6; + text-align: center; +} +table { + text-align: left; + width: 100%; +} +nav a { + margin-bottom: 10px; + background-color: #397AD6; + border: none; + color: white; + padding: 5px 32px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + -webkit-transition:.52s; + -moz-transition: .5s; + -o-transition: .5s; + -ms-transition: .5s; + transition:.5s; +} +nav a:hover { + background-color: #50B7D5; } - .podcast { width:70%; } @@ -39,3 +94,27 @@ hr { text-align: center; } +.css { + font-family: Monospace; +} + +input[type=text], input[type=date], input[type=file], input[type=password],textarea { + padding:10px; + border-radius: 4px; + box-sizing: border-box; + border: 1px solid #397AD6; +} +.publish [type=text], +.publish input[type=date], +.publish input[type=file], +.publish input[type=password], +.publish textarea { + width: 100%; +} +.publish textarea { + height: 30vh; +} + +.epdesc { + font-family: 'Muli', sans-serif; +}