Compare commits
No commits in common. "master" and "generate" have entirely different histories.
|
@ -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 {
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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}})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
108
web/entry.go
108
web/entry.go
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue