package handlers import ( "bytes" "database/sql" "html/template" "io" "log" "mime/multipart" "net/http" "os" "strconv" "time" "paterissa.net/mblog/internal/models" ) type blogContext struct { err *log.Logger db *sql.DB Post models.Post Rows []models.Post Name string IsAuth bool PagePopulated bool Offset int } func (ctx *blogContext) viewPost(w http.ResponseWriter, r *http.Request) { ctx.Rows = []models.Post{} postId := r.URL.Query().Get("id") stmt, err := ctx.db.Prepare("SELECT p.id, u.name, p.time, p.brief, p.content FROM posts p INNER JOIN logins u ON p.user_id = u.id WHERE p.id = $1;") if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Internal Error")) ctx.err.Printf("Could not prepare statement for DB: %v\n", err) return } defer stmt.Close() row := stmt.QueryRow(postId) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Internal Error")) ctx.err.Printf("Could not load post from DB: %v\n", err) return } var p models.Post err = row.Scan(&p.Id, &p.Name, &p.Time, &p.Brief, &p.Content) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Internal Error")) ctx.err.Printf("Could not load post from DB: %v\n", err) return } p.FormattedTime = p.Time.Format(time.ANSIC) ctx.Post = p files := []string{ "ui/html/base.tmpl.html", "ui/html/music_player.tmpl.html", "ui/html/pages/post.tmpl.html", } compiled, err := template.ParseFiles(files...) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Internal Error")) ctx.err.Printf("Could not parse template: %v\n", err) return } err = compiled.ExecuteTemplate(w, "base", ctx) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Internal Error")) ctx.err.Printf("Could not execute template: %v\n", err) return } } func (ctx *blogContext) post(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { w.WriteHeader(http.StatusMethodNotAllowed) w.Write([]byte("Method Not Allowed")) return } cookie, err := r.Cookie("paterissa_session_token") if err != nil { w.WriteHeader(http.StatusUnauthorized) w.Write([]byte("Unauthorized")) return } stmt, err := ctx.db.Prepare("SELECT * FROM cookies WHERE content = $1;") if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Internal Error")) ctx.err.Printf("Could not load cookies from DB: %v\n", err) return } var id uint64 var content string var userId uint64 var expiration time.Time row := stmt.QueryRow(cookie.Value) err = row.Scan(&id, &content, &userId, &expiration) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Internal Error")) return } err = r.ParseMultipartForm(4 << 20) if err != nil { w.WriteHeader(http.StatusUnprocessableEntity) w.Write([]byte("Failed to retrieve form data")) ctx.err.Printf("Could not parse request form: %v\n", err) return } var buffer bytes.Buffer boundary := "----internal-parser-req" writer := multipart.NewWriter(&buffer) writer.SetBoundary(boundary) writer.WriteField("user_id", strconv.Itoa(int(userId))) part, err := writer.CreateFormField("raw") if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Internal Error")) ctx.err.Printf("Could not create form field: %v\n", err) return } _, err = part.Write([]byte(r.Form.Get("raw"))) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Internal Error")) ctx.err.Printf("Could not create form field: %v\n", err) return } writer.Close() proxyReq, err := http.NewRequest(r.Method, "http://127.0.0.1:"+os.Getenv("parser_port"), bytes.NewReader(buffer.Bytes())) proxyReq.Header = make(http.Header) for key, val := range r.Header { if key != "Content-Length" { proxyReq.Header[key] = val } } proxyReq.Header.Set("Content-Type", writer.FormDataContentType()) resp, err := http.DefaultClient.Do(proxyReq) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Internal Error")) ctx.err.Printf("Fail response from parser: %v\n", err) return } if resp.StatusCode != http.StatusOK { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Internal Error")) body, err := io.ReadAll(resp.Body) if err == nil { ctx.err.Printf("Fail response from parser: %s\n", body) } return } defer resp.Body.Close() return } func (ctx *blogContext) index(w http.ResponseWriter, r *http.Request) { ctx.Rows = []models.Post{} ctx.IsAuth = false ctx.PagePopulated = false if r.URL.Path != "/" { http.NotFound(w, r) return } _, err := r.Cookie("paterissa_session_token") if err == nil { ctx.IsAuth = true } ctx.Offset, _ = strconv.Atoi(r.URL.Query().Get("offset")) rows, err := ctx.db.Query("SELECT p.id, u.name, p.time, p.brief FROM posts p INNER JOIN logins u ON p.user_id = u.id WHERE p.id > " + strconv.Itoa(ctx.Offset) + " ORDER BY p.id DESC LIMIT 20;") if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Internal Error")) ctx.err.Printf("Could not load posts from DB: %v\n", err) return } defer rows.Close() for rows.Next() { var p models.Post if err = rows.Scan(&p.Id, &p.Name, &p.Time, &p.Brief); err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Internal Error")) ctx.err.Printf("Could not load row: %v\n", err) return } p.FormattedTime = p.Time.Format(time.ANSIC) ctx.Rows = append(ctx.Rows, p) ctx.PagePopulated = true } files := []string{ "ui/html/base.tmpl.html", "ui/html/music_player.tmpl.html", "ui/html/pages/index.tmpl.html", } funcMap := template.FuncMap{ "add": func(a int, b int) int { return a + b }, "sub": func(a int, b int) int { return a - b }, } compiled, err := template.New("blog").Funcs(funcMap).ParseFiles(files...) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Internal Error")) ctx.err.Printf("Could not parse template: %v\n", err) return } err = compiled.ExecuteTemplate(w, "base", ctx) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Internal Error")) ctx.err.Printf("Could not execute template: %v\n", err) return } }