Add /api/block/ API
This commit is contained in:
parent
8f65d459ff
commit
35b8c45169
4
TODO.md
4
TODO.md
|
@ -1,5 +1,9 @@
|
||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
|
## Remove old/normal page editor
|
||||||
|
|
||||||
|
- The normal page editor is still available. It should be removed.
|
||||||
|
|
||||||
## API for blocks
|
## API for blocks
|
||||||
|
|
||||||
### POST /api/block/append
|
### POST /api/block/append
|
||||||
|
|
29
file.go
29
file.go
|
@ -145,16 +145,26 @@ func (fp *FilePages) Get(name string) Page {
|
||||||
t, err := time.Parse("2006-01-02", name)
|
t, err := time.Parse("2006-01-02", name)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
date = true
|
date = true
|
||||||
names = append(names, formatDateTitle(t), name)
|
names = append(names, formatDatePageName(t), name)
|
||||||
|
} else {
|
||||||
|
names = append(names, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
|
log.Printf("Trying %q", name)
|
||||||
blocks, err := loadBlocks(fp.dirname, name)
|
blocks, err := loadBlocks(fp.dirname, name)
|
||||||
if err != nil && errors.Is(err, BlockNotFound) {
|
if err != nil && errors.Is(err, BlockNotFound) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return fp.blocksBackendGet(name, blocks, titleOption{date, t})
|
return fp.blocksBackendGet(name, blocks, titleOption{date, t})
|
||||||
}
|
}
|
||||||
return fp.oldPagesBackend(name)
|
|
||||||
|
page, err := fp.oldPagesBackend(name)
|
||||||
|
if err != nil {
|
||||||
|
return fp.blocksBackendGet(name, BlockResponse{ParentID: "root", PageID: name}, titleOption{date, t})
|
||||||
|
}
|
||||||
|
|
||||||
|
return page
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fp *FilePages) blocksBackendGet(name string, blocks BlockResponse, option titleOption) Page {
|
func (fp *FilePages) blocksBackendGet(name string, blocks BlockResponse, option titleOption) Page {
|
||||||
|
@ -176,6 +186,9 @@ func (fp *FilePages) blocksBackendGet(name string, blocks BlockResponse, option
|
||||||
}
|
}
|
||||||
|
|
||||||
title := formatTitle(blocks.Texts[name], option)
|
title := formatTitle(blocks.Texts[name], option)
|
||||||
|
if title == "" {
|
||||||
|
title = cleanTitle(name)
|
||||||
|
}
|
||||||
|
|
||||||
return Page{
|
return Page{
|
||||||
Name: name,
|
Name: name,
|
||||||
|
@ -194,7 +207,7 @@ func formatTitle(title string, option titleOption) string {
|
||||||
return title
|
return title
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fp *FilePages) oldPagesBackend(title string) Page {
|
func (fp *FilePages) oldPagesBackend(title string) (Page, error) {
|
||||||
name := strings.Replace(title, " ", "_", -1)
|
name := strings.Replace(title, " ", "_", -1)
|
||||||
title = strings.Replace(title, "_", " ", -1)
|
title = strings.Replace(title, "_", " ", -1)
|
||||||
|
|
||||||
|
@ -211,7 +224,7 @@ func (fp *FilePages) oldPagesBackend(title string) Page {
|
||||||
Name: name,
|
Name: name,
|
||||||
Content: "",
|
Content: "",
|
||||||
Refs: refs,
|
Refs: refs,
|
||||||
}
|
}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
@ -223,7 +236,7 @@ func (fp *FilePages) oldPagesBackend(title string) Page {
|
||||||
Title: title,
|
Title: title,
|
||||||
Content: "",
|
Content: "",
|
||||||
Refs: refs,
|
Refs: refs,
|
||||||
}
|
}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return Page{
|
return Page{
|
||||||
|
@ -231,7 +244,7 @@ func (fp *FilePages) oldPagesBackend(title string) Page {
|
||||||
Title: title,
|
Title: title,
|
||||||
Content: string(body),
|
Content: string(body),
|
||||||
Refs: refs,
|
Refs: refs,
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fp *FilePages) Save(p string, page Page, summary, author string) error {
|
func (fp *FilePages) Save(p string, page Page, summary, author string) error {
|
||||||
|
@ -506,13 +519,13 @@ func loadBlocks(dirname, rootBlockID string) (BlockResponse, error) {
|
||||||
func loadBlock(dirname, blockID string) (Block, error) {
|
func loadBlock(dirname, blockID string) (Block, error) {
|
||||||
f, err := os.Open(filepath.Join(dirname, BlocksDirectory, blockID))
|
f, err := os.Open(filepath.Join(dirname, BlocksDirectory, blockID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Block{}, fmt.Errorf("%q: %w", BlockNotFound)
|
return Block{}, fmt.Errorf("%q: %w", blockID, BlockNotFound)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
var block Block
|
var block Block
|
||||||
err = json.NewDecoder(f).Decode(&block)
|
err = json.NewDecoder(f).Decode(&block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Block{}, fmt.Errorf("%q: %v", err)
|
return Block{}, fmt.Errorf("%q: %v", blockID, err)
|
||||||
}
|
}
|
||||||
return block, nil
|
return block, nil
|
||||||
}
|
}
|
||||||
|
|
98
main.go
98
main.go
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -26,12 +27,19 @@ func init() {
|
||||||
log.SetFlags(log.Lshortfile)
|
log.SetFlags(log.Lshortfile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type authorizedKey string
|
||||||
|
|
||||||
|
var (
|
||||||
|
authKey = authorizedKey("authorizedKey")
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
mp PagesRepository
|
mp PagesRepository
|
||||||
|
|
||||||
port = flag.Int("port", 8080, "listen port")
|
port = flag.Int("port", 8080, "listen port")
|
||||||
baseurl = flag.String("baseurl", "", "baseurl")
|
baseurl = flag.String("baseurl", "", "baseurl")
|
||||||
redirectURI string = ""
|
redirectURI = ""
|
||||||
|
authToken = "XVlBzgbaiCMRAjWw"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Backref struct {
|
type Backref struct {
|
||||||
|
@ -620,6 +628,10 @@ func (h *indexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
page := r.URL.Path[1:]
|
page := r.URL.Path[1:]
|
||||||
|
if page == "favicon.ico" {
|
||||||
|
http.Error(w, "Not Found", 404)
|
||||||
|
return
|
||||||
|
}
|
||||||
page = resolvePageName(page)
|
page = resolvePageName(page)
|
||||||
|
|
||||||
mpPage := mp.Get(page)
|
mpPage := mp.Get(page)
|
||||||
|
@ -636,7 +648,7 @@ func (h *indexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
var rawMsg json.RawMessage
|
var rawMsg json.RawMessage
|
||||||
err = json.NewDecoder(strings.NewReader(pageText)).Decode(&rawMsg)
|
err = json.NewDecoder(strings.NewReader(pageText)).Decode(&rawMsg)
|
||||||
|
|
||||||
title := cleanTitle(page)
|
title := cleanTitle(mpPage.Title)
|
||||||
|
|
||||||
jsonPage := pageText != "" && err == nil
|
jsonPage := pageText != "" && err == nil
|
||||||
if jsonPage {
|
if jsonPage {
|
||||||
|
@ -889,7 +901,65 @@ func main() {
|
||||||
mp = NewFilePages(dataDir, searchIndex)
|
mp = NewFilePages(dataDir, searchIndex)
|
||||||
|
|
||||||
http.Handle("/auth/", &authHandler{})
|
http.Handle("/auth/", &authHandler{})
|
||||||
http.HandleFunc("/api/document/append", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/api/block/", wrapAuth(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
defer r.Body.Close()
|
||||||
|
|
||||||
|
if !r.Context().Value(authKey).(bool) {
|
||||||
|
http.Error(w, "Unauthorized", 401)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Method != "GET" {
|
||||||
|
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
format := r.URL.Query().Get("format")
|
||||||
|
if format == "" {
|
||||||
|
format = "json"
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(format == "json" || format == "metakv") {
|
||||||
|
http.Error(w, "unknown format", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
page := r.URL.Path
|
||||||
|
page = strings.TrimPrefix(page, "/api/block/")
|
||||||
|
page = resolvePageName(page)
|
||||||
|
|
||||||
|
mpPage := mp.Get(page)
|
||||||
|
pageText := mpPage.Content
|
||||||
|
if pageText == "" {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if format == "json" {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.Header().Set("Cache-Control", "no-store")
|
||||||
|
// Shortcut for json output
|
||||||
|
_, err := io.WriteString(w, pageText)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), 500)
|
||||||
|
}
|
||||||
|
} else if format == "metakv" {
|
||||||
|
so, err := createStructuredFormat(mpPage)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.Header().Set("Cache-Control", "no-store")
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
enc.SetIndent("", " ")
|
||||||
|
err = enc.Encode(so)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), 500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
http.HandleFunc("/api/block/append", wrapAuth(func(w http.ResponseWriter, r *http.Request) {
|
||||||
err := r.ParseForm()
|
err := r.ParseForm()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), 400)
|
http.Error(w, err.Error(), 400)
|
||||||
|
@ -904,6 +974,7 @@ func main() {
|
||||||
page := mp.Get(id)
|
page := mp.Get(id)
|
||||||
log.Println(page.Content)
|
log.Println(page.Content)
|
||||||
var listItems []ListItem
|
var listItems []ListItem
|
||||||
|
id = page.Name // Use the name that was actually loaded
|
||||||
|
|
||||||
err = json.NewDecoder(strings.NewReader(page.Content)).Decode(&listItems)
|
err = json.NewDecoder(strings.NewReader(page.Content)).Decode(&listItems)
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
|
@ -912,8 +983,9 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
newId := &ID{"1", true}
|
newId := &ID{"1", true}
|
||||||
|
generatedID := newId.NewID()
|
||||||
listItems = append(listItems, ListItem{
|
listItems = append(listItems, ListItem{
|
||||||
ID: newId.NewID(),
|
ID: generatedID,
|
||||||
Indented: 0,
|
Indented: 0,
|
||||||
Text: r.Form.Get("text"),
|
Text: r.Form.Get("text"),
|
||||||
Fleeting: false,
|
Fleeting: false,
|
||||||
|
@ -936,8 +1008,10 @@ func main() {
|
||||||
http.Error(w, fmt.Sprintf("while saving: %s", err.Error()), 500)
|
http.Error(w, fmt.Sprintf("while saving: %s", err.Error()), 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Println(generatedID)
|
||||||
return
|
return
|
||||||
})
|
}))
|
||||||
http.HandleFunc("/links.json", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/links.json", func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
http.ServeFile(w, r, filepath.Join(dataDir, LinksFile))
|
http.ServeFile(w, r, filepath.Join(dataDir, LinksFile))
|
||||||
|
@ -997,6 +1071,20 @@ func main() {
|
||||||
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), nil))
|
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func wrapAuth(handler http.HandlerFunc) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
auth := r.Header.Get("Authorization")
|
||||||
|
if auth == "" || auth == "Token "+authToken {
|
||||||
|
r = r.WithContext(context.WithValue(r.Context(), authKey, auth != ""))
|
||||||
|
// auth == "", require cookie in handler
|
||||||
|
handler.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
http.Error(w, "Authorization Required", 401)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func createSearchIndex(dataDir, indexName string) (bleve.Index, error) {
|
func createSearchIndex(dataDir, indexName string) (bleve.Index, error) {
|
||||||
indexDir := filepath.Join(dataDir, indexName)
|
indexDir := filepath.Join(dataDir, indexName)
|
||||||
if _, err := os.Stat(indexDir); os.IsNotExist(err) {
|
if _, err := os.Stat(indexDir); os.IsNotExist(err) {
|
||||||
|
|
15
session.go
15
session.go
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
@ -71,24 +72,20 @@ func getSessionCookie(w http.ResponseWriter, r *http.Request) (string, error) {
|
||||||
c, err := r.Cookie("session")
|
c, err := r.Cookie("session")
|
||||||
var sessionVar string
|
var sessionVar string
|
||||||
|
|
||||||
if err != nil {
|
if err != nil && errors.Is(err, http.ErrNoCookie) {
|
||||||
if err == http.ErrNoCookie {
|
|
||||||
sessionVar = RandStringBytes(16)
|
sessionVar = RandStringBytes(16)
|
||||||
newCookie := &http.Cookie{
|
c = &http.Cookie{
|
||||||
Name: "session",
|
Name: "session",
|
||||||
Value: sessionVar,
|
Value: sessionVar,
|
||||||
Expires: time.Now().Add(24 * time.Hour),
|
Expires: time.Now().Add(24 * time.Hour),
|
||||||
Path: "/",
|
Path: "/",
|
||||||
}
|
}
|
||||||
|
|
||||||
http.SetCookie(w, newCookie)
|
|
||||||
return sessionVar, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", err
|
|
||||||
} else {
|
} else {
|
||||||
sessionVar = c.Value
|
sessionVar = c.Value
|
||||||
|
c.Expires = time.Now().Add(24 * time.Hour)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
http.SetCookie(w, c)
|
||||||
|
|
||||||
return sessionVar, nil
|
return sessionVar, nil
|
||||||
}
|
}
|
||||||
|
|
20
util.go
20
util.go
|
@ -99,10 +99,28 @@ func (sw *stopwatch) Stop() {
|
||||||
|
|
||||||
func todayPage() string {
|
func todayPage() string {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
return formatDateTitle(now)
|
return formatDatePageName(now)
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatDateTitle(date time.Time) string {
|
func formatDateTitle(date time.Time) string {
|
||||||
|
months := []string{
|
||||||
|
"",
|
||||||
|
"januari",
|
||||||
|
"februari",
|
||||||
|
"maart",
|
||||||
|
"april",
|
||||||
|
"mei",
|
||||||
|
"juni",
|
||||||
|
"juli",
|
||||||
|
"augustus",
|
||||||
|
"september",
|
||||||
|
"oktober",
|
||||||
|
"november",
|
||||||
|
"december",
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%d %s %d", date.Day(), months[date.Month()], date.Year())
|
||||||
|
}
|
||||||
|
func formatDatePageName(date time.Time) string {
|
||||||
months := []string{
|
months := []string{
|
||||||
"",
|
"",
|
||||||
"januari",
|
"januari",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user