158 lines
4 KiB
Go
158 lines
4 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
func writeJSON(w http.ResponseWriter, code int, v any) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(code)
|
|
_ = json.NewEncoder(w).Encode(v)
|
|
}
|
|
|
|
func jsonError(w http.ResponseWriter, code int, msg string) {
|
|
writeJSON(w, code, map[string]string{"error": msg})
|
|
}
|
|
|
|
func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) {
|
|
count, errors := s.store.FlushStats()
|
|
mem := s.store.MemUsage()
|
|
writeJSON(w, 200, map[string]any{
|
|
"status": "ok",
|
|
"flushes": count,
|
|
"flush_errors": errors,
|
|
"mem": mem,
|
|
})
|
|
}
|
|
|
|
func (s *Server) handleListPosts(w http.ResponseWriter, r *http.Request) {
|
|
channel := r.URL.Query().Get("channel")
|
|
posts := s.store.ListPosts(channel, 20)
|
|
if posts == nil {
|
|
posts = []*Post{}
|
|
}
|
|
writeJSON(w, 200, map[string]any{"posts": posts})
|
|
}
|
|
|
|
func (s *Server) handleGetPost(w http.ResponseWriter, r *http.Request) {
|
|
id := strings.TrimPrefix(r.URL.Path, "/api/posts/")
|
|
if id == "" || strings.Contains(id, "/") {
|
|
jsonError(w, 400, "bad id")
|
|
return
|
|
}
|
|
post, ok := s.store.GetPost(id)
|
|
if !ok {
|
|
jsonError(w, 404, "not found")
|
|
return
|
|
}
|
|
comments := s.store.ListComments(id)
|
|
if comments == nil {
|
|
comments = []*Comment{}
|
|
}
|
|
writeJSON(w, 200, PostWithComments{Post: *post, Comments: comments})
|
|
}
|
|
|
|
func (s *Server) handleCreatePost(w http.ResponseWriter, r *http.Request) {
|
|
author := r.Header.Get("X-Posts-Author")
|
|
if author == "" {
|
|
jsonError(w, 401, "X-Posts-Author required")
|
|
return
|
|
}
|
|
var req CreatePostRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
jsonError(w, 400, "bad json")
|
|
return
|
|
}
|
|
m := &CreatePost{
|
|
AuthorID: author,
|
|
Title: req.Title,
|
|
Body: req.Body,
|
|
Channels: req.Channels,
|
|
}
|
|
if err := Submit(r.Context(), s.store, m); err != nil {
|
|
jsonError(w, 400, err.Error())
|
|
return
|
|
}
|
|
post, _ := s.store.GetPost(m.ResolvedPostID())
|
|
writeJSON(w, 201, post)
|
|
}
|
|
|
|
func (s *Server) handleCreateComment(w http.ResponseWriter, r *http.Request) {
|
|
author := r.Header.Get("X-Posts-Author")
|
|
if author == "" {
|
|
jsonError(w, 401, "X-Posts-Author required")
|
|
return
|
|
}
|
|
parts := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
|
|
if len(parts) != 4 || parts[0] != "api" || parts[1] != "posts" || parts[3] != "comments" {
|
|
jsonError(w, 400, "bad path")
|
|
return
|
|
}
|
|
postID := parts[2]
|
|
var req CreateCommentRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
jsonError(w, 400, "bad json")
|
|
return
|
|
}
|
|
m := &AddComment{
|
|
PostID: postID,
|
|
ParentID: req.ParentID,
|
|
AuthorID: author,
|
|
Body: req.Body,
|
|
}
|
|
if err := Submit(r.Context(), s.store, m); err != nil {
|
|
jsonError(w, 400, err.Error())
|
|
return
|
|
}
|
|
comments := s.store.ListComments(postID)
|
|
var created *Comment
|
|
for _, c := range comments {
|
|
if c.ID == m.ResolvedCommentID() {
|
|
created = c
|
|
break
|
|
}
|
|
}
|
|
writeJSON(w, 201, created)
|
|
}
|
|
|
|
|
|
func (s *Server) handleUpsertAuthor(w http.ResponseWriter, r *http.Request) {
|
|
var req struct {
|
|
ID string `json:"id"`
|
|
Kind string `json:"kind"`
|
|
DisplayName string `json:"display_name"`
|
|
AvatarURL string `json:"avatar_url,omitempty"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
jsonError(w, 400, "bad json")
|
|
return
|
|
}
|
|
m := &UpsertAuthor{ID: req.ID, AuthorKind: req.Kind, DisplayName: req.DisplayName, AvatarURL: req.AvatarURL}
|
|
if err := Submit(r.Context(), s.store, m); err != nil {
|
|
jsonError(w, 400, err.Error())
|
|
return
|
|
}
|
|
a, _ := s.store.GetAuthor(m.ID)
|
|
writeJSON(w, 200, a)
|
|
}
|
|
|
|
func (s *Server) handleUpsertChannel(w http.ResponseWriter, r *http.Request) {
|
|
var req struct {
|
|
Slug string `json:"slug"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description,omitempty"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
jsonError(w, 400, "bad json")
|
|
return
|
|
}
|
|
m := &UpsertChannel{Slug: req.Slug, Name: req.Name, Description: req.Description}
|
|
if err := Submit(r.Context(), s.store, m); err != nil {
|
|
jsonError(w, 400, err.Error())
|
|
return
|
|
}
|
|
c, _ := s.store.GetChannel(m.Slug)
|
|
writeJSON(w, 200, c)
|
|
}
|