155 lines
3.9 KiB
Go
155 lines
3.9 KiB
Go
package web
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
"git.veillax.com/Veillax/mako/internal/store"
|
|
)
|
|
|
|
func (s *Server) pages(w http.ResponseWriter, r *http.Request, p principal) {
|
|
counts, tags := p.st.Summary()
|
|
writeJSON(w, map[string]any{"pages": p.st.Pages(), "counts": counts, "tags": tags})
|
|
}
|
|
|
|
func (s *Server) listNotes(w http.ResponseWriter, r *http.Request, p principal) {
|
|
q := r.URL.Query()
|
|
limit, _ := strconv.Atoi(q.Get("limit"))
|
|
if limit <= 0 || limit > 200 {
|
|
limit = 50
|
|
}
|
|
offset, _ := strconv.Atoi(q.Get("offset"))
|
|
text := q.Get("q")
|
|
if len(text) > 200 {
|
|
text = text[:200]
|
|
}
|
|
notes, total := p.st.List(store.Query{Text: text, Tag: q.Get("tag"), View: q.Get("view"), Offset: offset, Limit: limit})
|
|
writeJSON(w, map[string]any{"notes": notes, "total": total})
|
|
}
|
|
|
|
func (s *Server) find(w http.ResponseWriter, r *http.Request, p principal) {
|
|
q := r.URL.Query().Get("q")
|
|
if len(q) > 200 {
|
|
q = q[:200]
|
|
}
|
|
writeJSON(w, map[string]any{"hits": p.st.Find(q, 12)})
|
|
}
|
|
|
|
func (s *Server) createNote(w http.ResponseWriter, r *http.Request, p principal) {
|
|
var in struct{ Title, Body, Parent string }
|
|
if !decode(w, r, &in) {
|
|
return
|
|
}
|
|
n, err := p.st.Create(store.NewNote{Title: in.Title, Body: in.Body, Parent: in.Parent})
|
|
if err != nil {
|
|
storeFail(w, err)
|
|
return
|
|
}
|
|
writeJSONStatus(w, http.StatusCreated, n)
|
|
}
|
|
|
|
// page is a note with the pages that link to it.
|
|
type page struct {
|
|
store.Note
|
|
Backlinks []store.Ref `json:"backlinks"`
|
|
}
|
|
|
|
func (s *Server) getNote(w http.ResponseWriter, r *http.Request, p principal) {
|
|
n, err := p.st.Get(r.PathValue("id"))
|
|
if err != nil {
|
|
storeFail(w, err)
|
|
return
|
|
}
|
|
writeJSON(w, page{n, p.st.Backlinks(n.ID)})
|
|
}
|
|
|
|
func (s *Server) updateNote(w http.ResponseWriter, r *http.Request, p principal) {
|
|
var in struct {
|
|
Title *string `json:"title"`
|
|
Body *string `json:"body"`
|
|
Pinned *bool `json:"pinned"`
|
|
Parent *string `json:"parent"`
|
|
Base string `json:"base"`
|
|
}
|
|
if !decode(w, r, &in) {
|
|
return
|
|
}
|
|
var base time.Time
|
|
if in.Base != "" {
|
|
t, err := time.Parse(time.RFC3339Nano, in.Base)
|
|
if err != nil {
|
|
fail(w, http.StatusBadRequest, "base must be the page's updated time")
|
|
return
|
|
}
|
|
base = t
|
|
}
|
|
n, err := p.st.Update(r.PathValue("id"), store.Change{Title: in.Title, Body: in.Body, Pinned: in.Pinned, Parent: in.Parent}, base)
|
|
if errors.Is(err, store.ErrConflict) {
|
|
writeJSONStatus(w, http.StatusConflict, map[string]any{"error": "This page changed somewhere else", "note": n})
|
|
return
|
|
}
|
|
if err != nil {
|
|
storeFail(w, err)
|
|
return
|
|
}
|
|
writeJSON(w, page{n, p.st.Backlinks(n.ID)})
|
|
}
|
|
|
|
func (s *Server) deleteNote(w http.ResponseWriter, r *http.Request, p principal) {
|
|
id := r.PathValue("id")
|
|
var err error
|
|
if r.URL.Query().Get("purge") == "1" {
|
|
err = p.st.Purge(id)
|
|
} else {
|
|
_, err = p.st.Trash(id)
|
|
}
|
|
if err != nil {
|
|
storeFail(w, err)
|
|
return
|
|
}
|
|
writeJSON(w, map[string]bool{"ok": true})
|
|
}
|
|
|
|
func (s *Server) restoreNote(w http.ResponseWriter, r *http.Request, p principal) {
|
|
n, err := p.st.Restore(r.PathValue("id"))
|
|
if err != nil {
|
|
storeFail(w, err)
|
|
return
|
|
}
|
|
writeJSON(w, n)
|
|
}
|
|
|
|
func (s *Server) emptyTrash(w http.ResponseWriter, r *http.Request, p principal) {
|
|
n, err := p.st.EmptyTrash()
|
|
if err != nil {
|
|
storeFail(w, err)
|
|
return
|
|
}
|
|
writeJSON(w, map[string]int{"deleted": n})
|
|
}
|
|
|
|
func (s *Server) daily(w http.ResponseWriter, r *http.Request, p principal) {
|
|
var in struct{ Date string }
|
|
if !decode(w, r, &in) {
|
|
return
|
|
}
|
|
n, err := p.st.Daily(in.Date)
|
|
if err != nil {
|
|
storeFail(w, err)
|
|
return
|
|
}
|
|
writeJSON(w, page{n, p.st.Backlinks(n.ID)})
|
|
}
|
|
|
|
func (s *Server) export(w http.ResponseWriter, r *http.Request, p principal) {
|
|
w.Header().Set("Content-Type", "application/zip")
|
|
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="mako-%s.zip"`, time.Now().Format("2006-01-02")))
|
|
if err := p.st.WriteZip(w); err != nil {
|
|
// The download has started, so all that can be done is to cut it short.
|
|
panic(http.ErrAbortHandler)
|
|
}
|
|
}
|