package main import ( "bytes" "fmt" "log" "net/http" "strings" "text/template" "time" "github.com/CalebQ42/darkstorm-server/internal/backend" "github.com/CalebQ42/darkstorm-server/internal/blog" "go.mongodb.org/mongo-driver/bson" ) const ( loginPage = `
` editorPage = `

{{.Editor}}
` editorForm = `

{{.Result}}

` ) func loginPageRequest(w http.ResponseWriter, r *http.Request) { sendContent(w, r, loginPage, "", "") } func trueLoginRequest(w http.ResponseWriter, r *http.Request) { if r.Header.Get("HX-Request") != "true" { sendContent(w, r, "

Bad request

", "", "") return } err := r.ParseForm() if err != nil { sendContent(w, r, "

Bad request

", "", "") return } u, err := back.TryLogin(r.Context(), r.FormValue("username"), r.FormValue("password")) if err != nil { if err == backend.ErrLoginTimeout { sendContent(w, r, fmt.Sprint("

Timed out for", time.Unix(u.Timeout, 0).Sub(time.Now()), "

"), "", "") } else if err == backend.ErrLoginIncorrect { sendContent(w, r, "

Username or password invalid

", "", "") } else { log.Println("error trying to login:", err) sendContent(w, r, "

Server error

", "", "") } return } tok, err := back.GenerateJWT(u.ToReqUser()) if err != nil { log.Println("error trying to generate JWT:", err) sendContent(w, r, "

Server error

", "", "") return } w.Header().Set("Set-Cookie", "blogAuthToken="+tok+"; Secure; Max-Age=43170; SameSite=Lax") // Max-Age is 11.5 hours. JWTs are valid for 12 hours. sendContent(w, r, "

Successful Login

", "", "") } var ( pageTmpl *template.Template formTmpl *template.Template ) type pageTmplStruct struct { Selected string Blogs []blog.BlogListResult Editor string } type formTmplStruct struct { Blog blog.Blog Result string } func setupEditorTemplates() error { var err error pageTmpl, err = template.New("page").Parse(editorPage) if err != nil { return err } formTmpl, err = template.New("form").Parse(editorForm) if err != nil { return err } return nil } func editorRequest(w http.ResponseWriter, r *http.Request) { if verifyEditorCookie(r) == nil { editorRedirect(w, r, "/login") return } blogs, err := blogApp.AllBlogsList(r.Context()) if err != nil { log.Println("error getting all blogs:", err) sendContent(w, r, "ERROR", "", "") return } buf := new(bytes.Buffer) err = pageTmpl.Execute(buf, pageTmplStruct{Blogs: blogs}) if err != nil { log.Println("error executing editor page template:", err) sendContent(w, r, "ERROR", "", "") return } sendContent(w, r, buf.String(), "", "") } func editorEdit(w http.ResponseWriter, r *http.Request) { if verifyEditorCookie(r) == nil { editorRedirect(w, r, "/login") return } var bl *blog.Blog blogID := r.URL.Query().Get("blog") if blogID == "" { sendContent(w, r, "

Select a blog!

", "", "") return } var err error if blogID == "new" { bl = &blog.Blog{} } else { bl, err = blogApp.AnyBlog(r.Context(), blogID) if err != nil { log.Println("error getting blog for editor:", err) log.Println(blogID) sendContent(w, r, "ERROR", "", "") return } } buf := new(bytes.Buffer) err = formTmpl.Execute(buf, formTmplStruct{Blog: *bl}) if err != nil { log.Println("error executing editor template:", err) sendContent(w, r, "ERROR", "", "") return } sendContent(w, r, buf.String(), "", "") } func editorPost(w http.ResponseWriter, r *http.Request) { usr := verifyEditorCookie(r) if usr == nil { editorRedirect(w, r, "/login") return } if usr.Perm["blog"] != "admin" { sendContent(w, r, "

You are not allowed to perform this action. Sorry, not sorry.

", "", "") return } err := r.ParseForm() if err != nil { sendContent(w, r, "

Bad request

", "", "") return } newBlog := blog.Blog{ ID: r.FormValue("id"), Title: r.FormValue("title"), RawBlog: r.FormValue("blog"), Draft: r.FormValue("draft") == "on", StaticPage: r.FormValue("staticPage") == "on", } if newBlog.Title == "" || newBlog.RawBlog == "" { sendContent(w, r, "

Title and Blog content required

", "", "") return } if newBlog.ID == "" { newBlog.ID = strings.TrimSpace(strings.ToLower(strings.ReplaceAll(newBlog.ID, " ", "-"))) if blogApp.Contains(r.Context(), newBlog.ID) { sendContent(w, r, "

Title is not unique!

", "", "") return } now := time.Now() newBlog.CreateTime = now.Unix() newBlog.Author = usr.Username err = blogApp.InsertBlog(r.Context(), newBlog) if err != nil { log.Println("error creating new blog ID:", err) sendContent(w, r, "

Error inserting into DB

", "", "") return } var blogs []blog.BlogListResult blogs, err = blogApp.AllBlogsList(r.Context()) if err != nil { log.Println("error getting all blogs list:", err) sendContent(w, r, "

Successfully save, but page reload failed

", "", "") return } w.Header().Set("HX-Retarget", "#content") newForm := new(bytes.Buffer) formTmpl.Execute(newForm, formTmplStruct{Blog: newBlog, Result: "

Successfully Created

"}) pageTmpl.Execute(w, pageTmplStruct{Selected: newBlog.ID, Blogs: blogs, Editor: newForm.String()}) return } err = blogApp.UpdateBlog(r.Context(), newBlog.ID, bson.M{ "updateTime": time.Now().Unix(), "title": newBlog.Title, "blog": newBlog.RawBlog, "draft": newBlog.Draft, "staticPage": newBlog.StaticPage}) if err != nil { log.Println("error updating blog:", err) sendContent(w, r, "

Server error updating blog

", "", "") return } old, err := blogApp.AnyBlog(r.Context(), newBlog.ID) if err != nil { log.Println("error getting old blog to be updated:", err) sendContent(w, r, "

Updated!

", "", "") return } if old.Title == newBlog.Title { sendContent(w, r, "

Updated!

", "", "") return } var blogs []blog.BlogListResult blogs, err = blogApp.AllBlogsList(r.Context()) if err != nil { log.Println("error getting all blogs list:", err) sendContent(w, r, "

Updated!

", "", "") return } w.Header().Set("HX-Retarget", "#content") newForm := new(bytes.Buffer) formTmpl.Execute(newForm, formTmplStruct{Blog: newBlog, Result: "

Successfully Created

"}) pageTmpl.Execute(w, pageTmplStruct{Selected: newBlog.ID, Blogs: blogs, Editor: newForm.String()}) } func verifyEditorCookie(r *http.Request) *backend.User { authCookie, err := r.Cookie("blogAuthToken") if err != nil { if err != http.ErrNoCookie { log.Println("error getting auth cookie:", err) } return nil } usr, err := back.VerifyUser(r.Context(), authCookie.Value) if err != nil { if err != backend.ErrTokenUnauthorized { log.Println("error authorizing JWT token:", err) } return nil } return usr } func editorRedirect(w http.ResponseWriter, r *http.Request, path string) { if r.Header.Get("HX-Request") == "true" { w.Header().Set("HX-Location", `{"path": "`+path+`", "target":"#content"}`) return } http.Redirect(w, r, "https://darkstorm.tech"+path, http.StatusFound) }