Compare commits

..

No commits in common. "master" and "generate" have entirely different histories.

8 changed files with 84 additions and 166 deletions

View File

@ -49,7 +49,7 @@ func makeKey() (string, error) {
} }
func New(db *db.Database, name, password string) (*User, error) { func New(db *db.Database, name, password string) (*User, error) {
q := `insert or replace into users values (null, ?, ?, ?, ?)` q := `insert into users values (null, ?, ?, ?, ?)`
key, err := makeKey() key, err := makeKey()
if err != nil { if err != nil {

View File

@ -1,4 +1,4 @@
FROM golang:alpine FROM alpine:edge
RUN apk add --no-cache git RUN apk add --no-cache git
RUN apk add --no-cache musl-dev RUN apk add --no-cache musl-dev
@ -27,7 +27,8 @@ ENV TZ America/New_York
# RUN yarn global add @vue/cli # RUN yarn global add @vue/cli
RUN cd $SRC_DIR/frontend; yarn && yarn build RUN cd $SRC_DIR/frontend; yarn && yarn build
RUN go get -u github.com/mjibson/esc RUN go get -u github.com/gobuffalo/packr/v2/packr2
RUN cd $SRC_DIR; go generate && go get ./... && go build -o /app/cabinet RUN cd $SRC_DIR; $HOME/go/bin/packr2
RUN cd $SRC_DIR; go get ./...; go build -o /app/cabinet
ENTRYPOINT ["/app/cabinet", "-httpAddr=0.0.0.0:5673", "-db=/app/var/cabinet.db"] ENTRYPOINT ["/app/cabinet", "-httpAddr=0.0.0.0:5673", "-db=/app/var/cabinet.db"]

View File

@ -2,16 +2,13 @@ package entry
import ( import (
"fmt" "fmt"
"io"
"os/exec"
"regexp" "regexp"
"strings" "strings"
"time" "time"
"code.chrissexton.org/cws/cabinet/db"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"code.chrissexton.org/cws/cabinet/db"
) )
type Entry struct { type Entry struct {
@ -55,7 +52,7 @@ func PrepareTable(tx *sqlx.Tx) error {
return nil return nil
} }
func NewFromAdoc(db *db.Database, body string) *Entry { func NewFromMd(db *db.Database, body string) *Entry {
e := New(db) e := New(db)
e.Content = body e.Content = body
e.Title = e.GenerateTitle() e.Title = e.GenerateTitle()
@ -63,33 +60,6 @@ func NewFromAdoc(db *db.Database, body string) *Entry {
return e return e
} }
func pandocMdToAdoc(body string) string {
log.Debug().Str("input", body).Msgf("converting md->adoc")
cmd := exec.Command("pandoc", "-f", "commonmark", "-t", "asciidoctor")
stdin, err := cmd.StdinPipe()
if err != nil {
log.Error().Err(err).Msgf("could not get stdin")
}
go func() {
defer stdin.Close()
io.WriteString(stdin, body)
}()
out, err := cmd.CombinedOutput()
if err != nil {
log.Error().Err(err).Msgf("could not get stdout")
}
log.Debug().Msgf("md->adoc: %s", out)
return string(out)
}
func NewFromMd(db *db.Database, body string) *Entry {
body = pandocMdToAdoc(body)
return NewFromAdoc(db, body)
}
func New(db *db.Database) *Entry { func New(db *db.Database) *Entry {
e := Entry{ e := Entry{
db: db, db: db,
@ -124,49 +94,27 @@ func GetByID(db *db.Database, id int64) (Entry, error) {
return e, e.populateTags() return e, e.populateTags()
} }
func SearchByTag(db *db.Database, query string, tags []string) ([]*Entry, error) { func Search(db *db.Database, query string) ([]*Entry, error) {
entries := []*Entry{} entries := []*Entry{}
query = fmt.Sprintf("%%%s%%", query) log.Debug().Str("query", query).Msg("searching")
log.Debug().Str("tag query", query).Int("len(tags)", len(tags)).Msg("searching") if query != "" {
q := `select * from entries where content like ? order by updated desc`
if len(tags) > 0 { err := db.Select(&entries, q, "%"+query+"%")
q := `select e.*
from entries e
inner join tags t
on e.id=t.entry_id
where
t.name in (?)
AND content like ?
order by updated desc`
q, args, err := sqlx.In(q, tags, query)
if err != nil {
return nil, err
}
err = db.Select(&entries, q, args...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} else { } else {
q := `select e.* q := `select * from entries order by updated desc`
from entries e err := db.Select(&entries, q)
where
content like ?
order by updated desc`
err := db.Select(&entries, q, query)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
for _, e := range entries { for _, e := range entries {
e.db = db e.db = db
e.Title = e.GenerateTitle() e.Title = e.GenerateTitle()
e.populateTags() e.populateTags()
} }
return entries, nil return entries, nil
} }
@ -329,12 +277,3 @@ func (e *Entry) Create() error {
tx.Commit() tx.Commit()
return nil return nil
} }
func (e *Entry) HasTag(tag string) bool {
for _, t := range e.Tags {
if strings.ToLower(tag) == strings.ToLower(t) {
return true
}
}
return false
}

View File

@ -1,2 +1,2 @@
//go:generate esc -prefix frontend/dist -o esc.go frontend/dist //go:generate esc -o esc.go -prefix="frontend/dist" frontend/dist
package main package main

View File

@ -33,15 +33,6 @@
components: { components: {
Error Error
}, },
created() {
if (!this.$store.state.key) {
let key = this.$cookies.get('key')
if (key) {
this.$store.commit('setKey', key)
return
}
}
},
methods: { methods: {
newFile: function() { newFile: function() {
this.$store.dispatch('newFile') this.$store.dispatch('newFile')

View File

@ -80,6 +80,11 @@ export default {
// because it has not been created yet when this guard is called! // because it has not been created yet when this guard is called!
next(vm => { next(vm => {
if (!vm.$store.state.key) { if (!vm.$store.state.key) {
let key = vm.$cookies.get('key')
if (key) {
vm.$store.commit('setKey', key)
return
}
vm.$router.push({name: "login", params: {returnTo: vm.$route.path}}) vm.$router.push({name: "login", params: {returnTo: vm.$route.path}})
} }
}) })

View File

@ -13,18 +13,6 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )
func (web *Web) writeJSON(w http.ResponseWriter, code int, data interface{}) error {
w.Header().Set("content-type", "application/json")
resp, err := json.Marshal(data)
if err != nil {
w.WriteHeader(500)
return err
}
w.WriteHeader(code)
w.Write(resp)
return nil
}
func (web *Web) editEntry(w http.ResponseWriter, r *http.Request) { func (web *Web) editEntry(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
slug := vars["slug"] slug := vars["slug"]
@ -55,87 +43,93 @@ func (web *Web) editEntry(w http.ResponseWriter, r *http.Request) {
err = oldEntry.Update() err = oldEntry.Update()
if err != nil { if err != nil {
web.writeJSON(w, 500, err) w.WriteHeader(500)
fmt.Fprint(w, err)
return return
} }
web.writeJSON(w, 200, oldEntry) resp, err := json.Marshal(oldEntry)
}
func (web *Web) newAdocEntry(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil { if err != nil {
web.writeJSON(w, 500, err) w.WriteHeader(500)
return fmt.Fprint(w, err)
}
newEntry := entry.NewFromAdoc(web.db, string(body))
err = newEntry.Create()
if err != nil {
web.writeJSON(w, 500, err)
return return
} }
web.writeJSON(w, 200, newEntry) w.Header().Set("content-type", "application/json")
fmt.Fprint(w, string(resp))
} }
func (web *Web) newMarkdownEntry(w http.ResponseWriter, r *http.Request) { func (web *Web) newMarkdownEntry(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body) body, err := ioutil.ReadAll(r.Body)
if err != nil { if err != nil {
log.Error().Err(err).Msgf("could not read message body") w.WriteHeader(500)
web.writeJSON(w, 500, err) fmt.Fprint(w, err)
return return
} }
newEntry := entry.NewFromMd(web.db, string(body)) newEntry := entry.NewFromMd(web.db, string(body))
err = newEntry.Create() err = newEntry.Create()
if err != nil { if err != nil {
log.Error().Err(err).Msgf("could not create entry") w.WriteHeader(500)
web.writeJSON(w, 500, err) fmt.Fprint(w, err)
return return
} }
web.writeJSON(w, 200, newEntry) resp, err := json.Marshal(newEntry)
if err != nil {
w.WriteHeader(500)
fmt.Fprint(w, err)
return
}
w.Header().Set("content-type", "application/json")
fmt.Fprint(w, string(resp))
} }
func (web *Web) newEntry(w http.ResponseWriter, r *http.Request) { func (web *Web) newEntry(w http.ResponseWriter, r *http.Request) {
log.Debug().
Str("content-type", r.Header.Get("Content-Type")).
Msgf("newEntry")
dec := json.NewDecoder(r.Body) dec := json.NewDecoder(r.Body)
newEntry := entry.New(web.db) newEntry := entry.New(web.db)
err := dec.Decode(&newEntry) err := dec.Decode(&newEntry)
if err != nil { if err != nil {
log.Error().Err(err).Msgf("could not decode entry") w.WriteHeader(500)
web.writeJSON(w, 500, err) fmt.Fprint(w, err)
return return
} }
err = newEntry.Create() err = newEntry.Create()
if err != nil { if err != nil {
log.Error().Err(err).Msgf("could not create raw entry") w.WriteHeader(500)
web.writeJSON(w, 500, err) fmt.Fprint(w, err)
return return
} }
resp, err := json.Marshal(newEntry)
web.writeJSON(w, 200, newEntry) if err != nil {
w.WriteHeader(500)
fmt.Fprint(w, err)
return
}
w.Header().Set("content-type", "application/json")
fmt.Fprint(w, string(resp))
} }
func (web *Web) allEntries(w http.ResponseWriter, r *http.Request) { func (web *Web) allEntries(w http.ResponseWriter, r *http.Request) {
query := "" query := ""
tags := []string{}
if !web.AuthCheck(r) {
tags = append(tags, "public")
}
items, ok := r.URL.Query()["query"] items, ok := r.URL.Query()["query"]
if ok { if ok {
query = items[0] query = items[0]
} }
entries, err := entry.SearchByTag(web.db, query, tags) entries, err := entry.Search(web.db, query)
if err != nil { if err != nil {
log.Error().Msgf("Error querying: %w", err) w.WriteHeader(500)
web.writeJSON(w, 500, err) fmt.Fprint(w, err)
return return
} }
web.writeJSON(w, 200, entries) resp, err := json.Marshal(entries)
if err != nil {
w.WriteHeader(500)
fmt.Fprint(w, err)
return
}
w.Header().Set("content-type", "application/json")
fmt.Fprint(w, string(resp))
} }
func (web *Web) getEntry(w http.ResponseWriter, r *http.Request) { func (web *Web) getEntry(w http.ResponseWriter, r *http.Request) {
@ -144,16 +138,19 @@ func (web *Web) getEntry(w http.ResponseWriter, r *http.Request) {
entry, err := entry.GetBySlug(web.db, slug) entry, err := entry.GetBySlug(web.db, slug)
if err != nil { if err != nil {
web.writeJSON(w, 500, err) w.WriteHeader(500)
fmt.Fprint(w, err)
return return
} }
if !web.AuthCheck(r) && !entry.HasTag("public") { resp, err := json.Marshal(entry)
web.writeJSON(w, 401, "not authorized") if err != nil {
w.WriteHeader(500)
fmt.Fprint(w, err)
return return
} }
w.Header().Set("content-type", "application/json")
web.writeJSON(w, 200, entry) fmt.Fprint(w, string(resp))
} }
func (web *Web) removeEntry(w http.ResponseWriter, r *http.Request) { func (web *Web) removeEntry(w http.ResponseWriter, r *http.Request) {
@ -163,8 +160,9 @@ func (web *Web) removeEntry(w http.ResponseWriter, r *http.Request) {
err := entry.RemoveBySlug(web.db, slug) err := entry.RemoveBySlug(web.db, slug)
if err != nil { if err != nil {
log.Error().Msgf("Error deleting: %s", err) log.Error().Msgf("Error deleting: %s", err)
web.writeJSON(w, 500, err) w.WriteHeader(500)
fmt.Fprint(w, err)
return return
} }
web.writeJSON(w, 200, "success") w.WriteHeader(200)
} }

View File

@ -38,42 +38,27 @@ func New(addr string, db *db.Database, static http.FileSystem) *Web {
} }
type AuthMiddleware struct { type AuthMiddleware struct {
web *Web
db *db.Database db *db.Database
} }
func NewAuthMiddleware(web *Web) AuthMiddleware {
return AuthMiddleware{
web: web,
db: web.db,
}
}
func (aw *AuthMiddleware) Middleware(next http.Handler) http.Handler { func (aw *AuthMiddleware) Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if aw.web.AuthCheck(r) { key := r.Header.Get("X-Auth-Key")
next.ServeHTTP(w, r) u, err := auth.GetByKey(aw.db, key)
return if key == "" || err != nil {
}
w.WriteHeader(401) w.WriteHeader(401)
fmt.Fprint(w, "invalid login") fmt.Fprint(w, "invalid login")
}) return
}
func (web *Web) AuthCheck(r *http.Request) bool {
key := r.Header.Get("X-Auth-Key")
u, err := auth.GetByKey(web.db, key)
if key == "" || err != nil {
return false
} }
log.Debug().Msgf("This shit is authed to user %s!", u.Name) log.Debug().Msgf("This shit is authed to user %s!", u.Name)
return true next.ServeHTTP(w, r)
})
} }
func (web *Web) routeSetup() http.Handler { func (web *Web) routeSetup() http.Handler {
r := mux.NewRouter() r := mux.NewRouter()
api := r.PathPrefix("/v1/").Subrouter() api := r.PathPrefix("/v1/").Subrouter()
auth := NewAuthMiddleware(web) auth := AuthMiddleware{web.db}
authedApi := r.PathPrefix("/v1/").Subrouter() authedApi := r.PathPrefix("/v1/").Subrouter()
authedApi.Use(auth.Middleware) authedApi.Use(auth.Middleware)
@ -88,12 +73,11 @@ func (web *Web) routeSetup() http.Handler {
// curl 'http://127.0.0.1:8080/v1/test' -X POST -H 'Accept: application/json, text/plain, */*' --compressed -H 'Content-Type: application/json;charset=utf-8' --data '{ "test": 1 }' // curl 'http://127.0.0.1:8080/v1/test' -X POST -H 'Accept: application/json, text/plain, */*' --compressed -H 'Content-Type: application/json;charset=utf-8' --data '{ "test": 1 }'
authedApi.HandleFunc("/entries", web.newEntry).Methods(http.MethodPost)
authedApi.HandleFunc("/entries", web.newEntry).Methods(http.MethodPost). authedApi.HandleFunc("/entries", web.newEntry).Methods(http.MethodPost).
HeadersRegexp("Content-Type", "application/(text|json).*") HeadersRegexp("Content-Type", "application/(text|json).*")
authedApi.HandleFunc("/entries", web.newMarkdownEntry).Methods(http.MethodPost). authedApi.HandleFunc("/entries", web.newMarkdownEntry).Methods(http.MethodPost).
HeadersRegexp("Content-Type", "application/markdown") HeadersRegexp("Content-Type", "application/markdown.*")
authedApi.HandleFunc("/entries", web.newAdocEntry).Methods(http.MethodPost).
HeadersRegexp("Content-Type", "application/asciidoc")
authedApi.HandleFunc("/entries/{slug}", web.removeEntry).Methods(http.MethodDelete) authedApi.HandleFunc("/entries/{slug}", web.removeEntry).Methods(http.MethodDelete)
authedApi.HandleFunc("/entries/{slug}", web.editEntry).Methods(http.MethodPut) authedApi.HandleFunc("/entries/{slug}", web.editEntry).Methods(http.MethodPut)