package admin import ( "bytes" "encoding/base64" "encoding/json" "fmt" "log" "net/http" "strings" "time" "github.com/bosssauce/ponzu/content" "github.com/bosssauce/ponzu/management/editor" "github.com/bosssauce/ponzu/management/manager" "github.com/bosssauce/ponzu/system/admin/config" "github.com/bosssauce/ponzu/system/admin/user" "github.com/bosssauce/ponzu/system/db" "github.com/nilslice/jwt" ) func adminHandler(res http.ResponseWriter, req *http.Request) { view, err := Admin(nil) if err != nil { fmt.Println(err) res.WriteHeader(http.StatusInternalServerError) return } res.Header().Set("Content-Type", "text/html") res.Write(view) } func initHandler(res http.ResponseWriter, req *http.Request) { if db.SystemInitComplete() { http.Redirect(res, req, req.URL.Scheme+req.URL.Host+"/admin", http.StatusFound) return } switch req.Method { case http.MethodGet: view, err := Init() if err != nil { fmt.Println(err) res.WriteHeader(http.StatusInternalServerError) return } res.Header().Set("Content-Type", "text/html") res.Write(view) case http.MethodPost: err := req.ParseForm() if err != nil { fmt.Println(err) res.WriteHeader(http.StatusInternalServerError) return } // get the site name from post to encode and use as secret name := []byte(req.FormValue("name")) secret := base64.StdEncoding.EncodeToString(name) req.Form.Set("client_secret", secret) // generate an Etag to use for response caching etag := db.NewEtag() req.Form.Set("etag", etag) email := strings.ToLower(req.FormValue("email")) password := req.FormValue("password") usr := user.NewUser(email, password) _, err = db.SetUser(usr) if err != nil { fmt.Println(err) res.WriteHeader(http.StatusInternalServerError) return } // set initial user email as admin_email and make config req.Form.Set("admin_email", email) err = db.SetConfig(req.Form) if err != nil { fmt.Println(err) res.WriteHeader(http.StatusInternalServerError) return } // add _token cookie for login persistence week := time.Now().Add(time.Hour * 24 * 7) claims := map[string]interface{}{ "exp": week.Unix(), "user": usr.Email, } jwt.Secret([]byte(secret)) token, err := jwt.New(claims) http.SetCookie(res, &http.Cookie{ Name: "_token", Value: token, Expires: week, }) redir := strings.TrimSuffix(req.URL.String(), "/init") http.Redirect(res, req, redir, http.StatusFound) default: res.WriteHeader(http.StatusMethodNotAllowed) } } func configHandler(res http.ResponseWriter, req *http.Request) { switch req.Method { case http.MethodGet: data, err := db.ConfigAll() if err != nil { fmt.Println(err) res.WriteHeader(http.StatusInternalServerError) return } c := &config.Config{} err = json.Unmarshal(data, c) if err != nil { fmt.Println(err) res.WriteHeader(http.StatusInternalServerError) return } cfg, err := c.MarshalEditor() if err != nil { fmt.Println(err) res.WriteHeader(http.StatusInternalServerError) return } adminView, err := Admin(cfg) if err != nil { fmt.Println(err) res.WriteHeader(http.StatusInternalServerError) return } res.Header().Set("Content-Type", "text/html") res.Write(adminView) case http.MethodPost: err := req.ParseForm() if err != nil { fmt.Println(err) res.WriteHeader(http.StatusInternalServerError) return } err = db.SetConfig(req.Form) if err != nil { fmt.Println(err) res.WriteHeader(http.StatusInternalServerError) return } http.Redirect(res, req, req.URL.String(), http.StatusFound) default: res.WriteHeader(http.StatusMethodNotAllowed) } } func configUsersHandler(res http.ResponseWriter, req *http.Request) { switch req.Method { case http.MethodGet: view, err := UsersList(req) if err != nil { fmt.Println(err) res.WriteHeader(http.StatusInternalServerError) errView, err := Error500() if err != nil { return } res.Write(errView) return } res.Write(view) case http.MethodPost: // create new user err := req.ParseMultipartForm(1024 * 1024 * 4) // maxMemory 4MB if err != nil { fmt.Println(err) res.WriteHeader(http.StatusInternalServerError) errView, err := Error500() if err != nil { return } res.Write(errView) return } email := strings.ToLower(req.FormValue("email")) password := req.PostFormValue("password") if email == "" || password == "" { fmt.Println(err) res.WriteHeader(http.StatusInternalServerError) errView, err := Error500() if err != nil { return } res.Write(errView) return } usr := user.NewUser(email, password) _, err = db.SetUser(usr) if err != nil { fmt.Println(err) res.WriteHeader(http.StatusInternalServerError) errView, err := Error500() if err != nil { return } res.Write(errView) return } http.Redirect(res, req, req.URL.String(), http.StatusFound) default: res.WriteHeader(http.StatusMethodNotAllowed) } } func configUsersEditHandler(res http.ResponseWriter, req *http.Request) { switch req.Method { case http.MethodPost: err := req.ParseMultipartForm(1024 * 1024 * 4) // maxMemory 4MB // check if user to be edited is current user j, err := db.CurrentUser(req) if err != nil { fmt.Println(err) res.WriteHeader(http.StatusInternalServerError) errView, err := Error500() if err != nil { return } res.Write(errView) return } usr := &user.User{} err = json.Unmarshal(j, usr) if err != nil { fmt.Println(err) res.WriteHeader(http.StatusInternalServerError) errView, err := Error500() if err != nil { return } res.Write(errView) return } // check if password matches password := req.PostFormValue("password") if !user.IsUser(usr, password) { fmt.Println("Unexpected user/password combination for", usr.Email) res.WriteHeader(http.StatusBadRequest) errView, err := Error405() if err != nil { return } res.Write(errView) return } email := strings.ToLower(req.PostFormValue("email")) newPassword := req.PostFormValue("new_password") var updatedUser *user.User if newPassword != "" { updatedUser = user.NewUser(email, newPassword) } else { updatedUser = user.NewUser(email, password) } // set the ID to the same ID as current user updatedUser.ID = usr.ID // set user in db err = db.UpdateUser(usr, updatedUser) if err != nil { fmt.Println(err) res.WriteHeader(http.StatusInternalServerError) errView, err := Error500() if err != nil { return } res.Write(errView) return } // create new token week := time.Now().Add(time.Hour * 24 * 7) claims := map[string]interface{}{ "exp": week, "user": updatedUser.Email, } token, err := jwt.New(claims) if err != nil { fmt.Println(err) res.WriteHeader(http.StatusInternalServerError) errView, err := Error500() if err != nil { return } res.Write(errView) return } // add token to cookie +1 week expiration cookie := &http.Cookie{ Name: "_token", Value: token, Expires: week, } http.SetCookie(res, cookie) // add new token cookie to the request req.AddCookie(cookie) http.Redirect(res, req, strings.TrimSuffix(req.URL.String(), "/edit"), http.StatusFound) default: res.WriteHeader(http.StatusMethodNotAllowed) } } func configUsersDeleteHandler(res http.ResponseWriter, req *http.Request) { switch req.Method { case http.MethodPost: err := req.ParseMultipartForm(1024 * 1024 * 4) // maxMemory 4MB // do not allow current user to delete themselves j, err := db.CurrentUser(req) if err != nil { fmt.Println(err) res.WriteHeader(http.StatusInternalServerError) errView, err := Error500() if err != nil { return } res.Write(errView) return } usr := &user.User{} err = json.Unmarshal(j, &usr) if err != nil { fmt.Println(err) res.WriteHeader(http.StatusInternalServerError) errView, err := Error500() if err != nil { return } res.Write(errView) return } email := strings.ToLower(req.PostFormValue("email")) if usr.Email == email { fmt.Println(err) res.WriteHeader(http.StatusBadRequest) errView, err := Error405() if err != nil { return } res.Write(errView) return } // delete existing user err = db.DeleteUser(email) if err != nil { fmt.Println(err) res.WriteHeader(http.StatusInternalServerError) errView, err := Error500() if err != nil { return } res.Write(errView) return } http.Redirect(res, req, strings.TrimSuffix(req.URL.String(), "/delete"), http.StatusFound) default: res.WriteHeader(http.StatusMethodNotAllowed) } } func loginHandler(res http.ResponseWriter, req *http.Request) { if !db.SystemInitComplete() { redir := req.URL.Scheme + req.URL.Host + "/admin/init" http.Redirect(res, req, redir, http.StatusFound) return } switch req.Method { case http.MethodGet: if user.IsValid(req) { http.Redirect(res, req, req.URL.Scheme+req.URL.Host+"/admin", http.StatusFound) return } view, err := Login() if err != nil { fmt.Println(err) res.WriteHeader(http.StatusInternalServerError) return } res.Header().Set("Content-Type", "text/html") res.Write(view) case http.MethodPost: if user.IsValid(req) { http.Redirect(res, req, req.URL.Scheme+req.URL.Host+"/admin", http.StatusFound) return } err := req.ParseForm() if err != nil { fmt.Println(err) http.Redirect(res, req, req.URL.String(), http.StatusFound) return } // check email & password j, err := db.User(strings.ToLower(req.FormValue("email"))) if err != nil { fmt.Println(err) http.Redirect(res, req, req.URL.String(), http.StatusFound) return } if j == nil { http.Redirect(res, req, req.URL.String(), http.StatusFound) return } usr := &user.User{} err = json.Unmarshal(j, usr) if err != nil { fmt.Println(err) http.Redirect(res, req, req.URL.String(), http.StatusFound) return } if !user.IsUser(usr, req.FormValue("password")) { http.Redirect(res, req, req.URL.String(), http.StatusFound) return } // create new token week := time.Now().Add(time.Hour * 24 * 7) claims := map[string]interface{}{ "exp": week, "user": usr.Email, } token, err := jwt.New(claims) if err != nil { fmt.Println(err) http.Redirect(res, req, req.URL.String(), http.StatusFound) return } // add it to cookie +1 week expiration http.SetCookie(res, &http.Cookie{ Name: "_token", Value: token, Expires: week, }) http.Redirect(res, req, strings.TrimSuffix(req.URL.String(), "/login"), http.StatusFound) } } func logoutHandler(res http.ResponseWriter, req *http.Request) { http.SetCookie(res, &http.Cookie{ Name: "_token", Expires: time.Unix(0, 0), Value: "", }) http.Redirect(res, req, req.URL.Scheme+req.URL.Host+"/admin/login", http.StatusFound) } func postsHandler(res http.ResponseWriter, req *http.Request) { q := req.URL.Query() t := q.Get("type") if t == "" { res.WriteHeader(http.StatusBadRequest) errView, err := Error405() if err != nil { return } res.Write(errView) return } order := strings.ToLower(q.Get("order")) posts := db.ContentAll(t + "_sorted") b := &bytes.Buffer{} p, ok := content.Types[t]().(editor.Editable) if !ok { res.WriteHeader(http.StatusInternalServerError) errView, err := Error500() if err != nil { return } res.Write(errView) return } html := `