diff --git a/.vscode/tasks.json b/.vscode/tasks.json index ea6c6ac..0f2c52d 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -4,7 +4,7 @@ { "label": "Build", "type": "shell", - "command": "go build", + "command": "go build -o GoBlog", "options": { "env": { "GOFLAGS": "-tags=linux,libsqlite3,sqlite_fts5" diff --git a/activityPub.go b/activityPub.go index 18da9b3..f3ec005 100644 --- a/activityPub.go +++ b/activityPub.go @@ -15,10 +15,10 @@ import ( "strings" "time" - "git.jlel.se/jlelse/GoBlog/pkgs/contenttype" "github.com/go-chi/chi/v5" "github.com/go-fed/httpsig" "github.com/spf13/cast" + "go.goblog.app/app/pkgs/contenttype" ) func (a *goBlog) initActivityPub() error { diff --git a/activityPubSending.go b/activityPubSending.go index 30b2087..1a7af32 100644 --- a/activityPubSending.go +++ b/activityPubSending.go @@ -12,7 +12,7 @@ import ( "net/url" "time" - "git.jlel.se/jlelse/GoBlog/pkgs/contenttype" + "go.goblog.app/app/pkgs/contenttype" ) type apRequest struct { diff --git a/activityStreams.go b/activityStreams.go index 9680ced..b5064b0 100644 --- a/activityStreams.go +++ b/activityStreams.go @@ -8,9 +8,9 @@ import ( "fmt" "net/http" - "git.jlel.se/jlelse/GoBlog/pkgs/contenttype" "github.com/araddon/dateparse" ct "github.com/elnormous/contenttype" + "go.goblog.app/app/pkgs/contenttype" ) const asContext = "https://www.w3.org/ns/activitystreams" diff --git a/app.go b/app.go index 5196915..ee10db4 100644 --- a/app.go +++ b/app.go @@ -6,7 +6,6 @@ import ( "net/http" "sync" - "git.jlel.se/jlelse/GoBlog/pkgs/minify" shutdowner "git.jlel.se/jlelse/go-shutdowner" ts "git.jlel.se/jlelse/template-strings" ct "github.com/elnormous/contenttype" @@ -14,6 +13,7 @@ import ( "github.com/go-fed/httpsig" rotatelogs "github.com/lestrrat-go/file-rotatelogs" "github.com/yuin/goldmark" + "go.goblog.app/app/pkgs/minify" "golang.org/x/sync/singleflight" ) diff --git a/authentication.go b/authentication.go index 2496adc..080a59b 100644 --- a/authentication.go +++ b/authentication.go @@ -8,8 +8,8 @@ import ( "io" "net/http" - "git.jlel.se/jlelse/GoBlog/pkgs/contenttype" "github.com/pquerna/otp/totp" + "go.goblog.app/app/pkgs/contenttype" ) func (a *goBlog) checkCredentials(username, password, totpPasscode string) bool { diff --git a/blogroll.go b/blogroll.go index 5847f3d..4d0a060 100644 --- a/blogroll.go +++ b/blogroll.go @@ -10,10 +10,10 @@ import ( "strings" "time" - "git.jlel.se/jlelse/GoBlog/pkgs/contenttype" "github.com/kaorimatz/go-opml" servertiming "github.com/mitchellh/go-server-timing" "github.com/thoas/go-funk" + "go.goblog.app/app/pkgs/contenttype" ) func (a *goBlog) serveBlogroll(w http.ResponseWriter, r *http.Request) { diff --git a/captcha.go b/captcha.go index fe6c138..38be5f3 100644 --- a/captcha.go +++ b/captcha.go @@ -7,8 +7,8 @@ import ( "io" "net/http" - "git.jlel.se/jlelse/GoBlog/pkgs/contenttype" "github.com/dchest/captcha" + "go.goblog.app/app/pkgs/contenttype" ) func (a *goBlog) captchaMiddleware(next http.Handler) http.Handler { diff --git a/captcha_test.go b/captcha_test.go index 3769433..19d81e5 100644 --- a/captcha_test.go +++ b/captcha_test.go @@ -6,8 +6,8 @@ import ( "net/http/httptest" "testing" - "git.jlel.se/jlelse/GoBlog/pkgs/contenttype" "github.com/stretchr/testify/assert" + "go.goblog.app/app/pkgs/contenttype" ) func Test_captchaMiddleware(t *testing.T) { diff --git a/editor.go b/editor.go index 86803ed..ff74efd 100644 --- a/editor.go +++ b/editor.go @@ -8,7 +8,7 @@ import ( "net/http/httptest" "net/url" - "git.jlel.se/jlelse/GoBlog/pkgs/contenttype" + "go.goblog.app/app/pkgs/contenttype" ) const editorPath = "/editor" diff --git a/editorFiles.go b/editorFiles.go new file mode 100644 index 0000000..5b6d245 --- /dev/null +++ b/editorFiles.go @@ -0,0 +1,48 @@ +package main + +import ( + "net/http" + "sort" +) + +func (a *goBlog) serveEditorFiles(w http.ResponseWriter, r *http.Request) { + files, err := a.mediaFiles() + if err != nil { + a.serveError(w, r, err.Error(), http.StatusInternalServerError) + return + } + // Sort files time desc + sort.Slice(files, func(i, j int) bool { + return files[i].Time.After(files[j].Time) + }) + // Serve HTML + blog := r.Context().Value(blogContextKey).(string) + a.render(w, r, templateEditorFiles, &renderData{ + BlogString: blog, + Data: map[string]interface{}{ + "Files": files, + }, + }) +} + +func (a *goBlog) serveEditorFilesView(w http.ResponseWriter, r *http.Request) { + filename := r.FormValue("filename") + if filename == "" { + a.serveError(w, r, "No file selected", http.StatusBadRequest) + return + } + http.Redirect(w, r, a.mediaFileLocation(filename), http.StatusFound) +} + +func (a *goBlog) serveEditorFilesDelete(w http.ResponseWriter, r *http.Request) { + filename := r.FormValue("filename") + if filename == "" { + a.serveError(w, r, "No file selected", http.StatusBadRequest) + return + } + if err := a.deleteMediaFile(filename); err != nil { + a.serveError(w, r, err.Error(), http.StatusInternalServerError) + return + } + http.Redirect(w, r, a.getRelativePath(r.Context().Value(blogContextKey).(string), "/editor/files"), http.StatusFound) +} diff --git a/errors.go b/errors.go index c8b5f65..0769270 100644 --- a/errors.go +++ b/errors.go @@ -4,8 +4,8 @@ import ( "fmt" "net/http" - "git.jlel.se/jlelse/GoBlog/pkgs/contenttype" ct "github.com/elnormous/contenttype" + "go.goblog.app/app/pkgs/contenttype" ) type errorData struct { diff --git a/errors_test.go b/errors_test.go index 33ffabe..e54cb63 100644 --- a/errors_test.go +++ b/errors_test.go @@ -6,8 +6,8 @@ import ( "net/http/httptest" "testing" - "git.jlel.se/jlelse/GoBlog/pkgs/contenttype" "github.com/stretchr/testify/assert" + "go.goblog.app/app/pkgs/contenttype" ) func Test_errors(t *testing.T) { diff --git a/feeds.go b/feeds.go index 37ed41f..c2a89df 100644 --- a/feeds.go +++ b/feeds.go @@ -5,9 +5,9 @@ import ( "strings" "time" - "git.jlel.se/jlelse/GoBlog/pkgs/contenttype" "github.com/araddon/dateparse" "github.com/gorilla/feeds" + "go.goblog.app/app/pkgs/contenttype" ) type feedType string diff --git a/go.mod b/go.mod index a94dd9a..a2e4584 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module git.jlel.se/jlelse/GoBlog +module go.goblog.app/app go 1.16 @@ -11,6 +11,7 @@ require ( github.com/andybalholm/cascadia v1.2.0 // indirect github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/boombuler/barcode v1.0.1 // indirect + github.com/c2h5oh/datasize v0.0.0-20200825124411-48ed595a09d2 github.com/caddyserver/certmagic v0.14.0 github.com/cretz/bine v0.2.0 github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f @@ -45,17 +46,18 @@ require ( github.com/smartystreets/assertions v1.2.0 // indirect github.com/snabb/sitemap v1.0.0 github.com/spf13/cast v1.3.1 - github.com/spf13/viper v1.8.0 + github.com/spf13/viper v1.8.1 github.com/stretchr/testify v1.7.0 - github.com/tdewolff/minify/v2 v2.9.17 + github.com/tdewolff/minify/v2 v2.9.18 github.com/thoas/go-funk v0.8.0 github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 github.com/vcraescu/go-paginator v1.0.1-0.20201114172518-2cfc59fe05c2 - github.com/yuin/goldmark v1.3.8 + github.com/yuin/goldmark v1.3.9 // master github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38 go.uber.org/atomic v1.8.0 // indirect go.uber.org/multierr v1.7.0 // indirect + go.uber.org/zap v1.18.1 // indirect golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e // indirect golang.org/x/net v0.0.0-20210614182718-04defd469f4e golang.org/x/sync v0.0.0-20210220032951-036812b2e83c diff --git a/go.sum b/go.sum index 030c801..d4c53d9 100644 --- a/go.sum +++ b/go.sum @@ -63,12 +63,16 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs= github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/bradfitz/gomemcache v0.0.0-20170208213004-1952afaa557d/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60= +github.com/c2h5oh/datasize v0.0.0-20200825124411-48ed595a09d2 h1:t8KYCwSKsOEZBFELI4Pn/phbp38iJ1RRAkDFNin1aak= +github.com/c2h5oh/datasize v0.0.0-20200825124411-48ed595a09d2/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= github.com/caddyserver/certmagic v0.14.0 h1:XW1o32s7smIYEJSc6g+N8YXljpjRo5ZE2zi3CIYTs74= github.com/caddyserver/certmagic v0.14.0/go.mod h1:oRQOZmUVKwlpgNidslysHt05osM9uMrJ4YMk+Ot4P4Q= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -363,8 +367,8 @@ github.com/spf13/pflag v1.0.1-0.20170901120850-7aff26db30c1/go.mod h1:DYY7MBk1bd github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= -github.com/spf13/viper v1.8.0 h1:QRwDgoG8xX+kp69di68D+YYTCWfYEckbZRfUlEIAal0= -github.com/spf13/viper v1.8.0/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -375,8 +379,10 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/tdewolff/minify/v2 v2.9.17 h1:0RPKCBSz5plIKZkmkm/zeQdqPYf/9NJVwG63NHtViHQ= -github.com/tdewolff/minify/v2 v2.9.17/go.mod h1:OLHZpngMfp36EyqxkGGta1l3hB1c+sHhXNHk8WTrsQo= +github.com/tdewolff/minify/v2 v2.9.18 h1:j5Is0sOGp4cxm0o3HgvHCWCvTtmKnfB0qv0FCRbmgZY= +github.com/tdewolff/minify/v2 v2.9.18/go.mod h1:0y0mXZnisZm8HcgQvAV0btxa1IgecGam90zMuHqEZuc= +github.com/tdewolff/parse/v2 v2.5.18 h1:d67Ql/Pe36JcJZ7J2MY8upx6iTxbxGS9lzwyFGtMmd0= +github.com/tdewolff/parse/v2 v2.5.18/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho= github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4= github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= github.com/thoas/go-funk v0.8.0 h1:JP9tKSvnpFVclYgDM0Is7FD9M4fhPvqA0s0BsXmzSRQ= @@ -391,8 +397,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.3.8 h1:Nw158Q8QN+CPgTmVRByhVwapp8Mm1e2blinhmx4wx5E= -github.com/yuin/goldmark v1.3.8/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.3.9 h1:XsVHmzm4P6g84IBbAj+WYMF/IEZ3J9+3I1wlqCNa/SQ= +github.com/yuin/goldmark v1.3.9/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38 h1:XZjLcLoTPNZuxppY3gwhRqo/T2XF6JMGFFdkAjX3w1w= github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= @@ -409,14 +415,17 @@ go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.8.0 h1:CUhrE4N1rqSE6FM9ecihEjRkLQu8cDfgDyoOs83mEY4= go.uber.org/atomic v1.8.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= -go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.18.1 h1:CSUJ2mjFszzEWt4CdKISEuChVIXGBn3lAPwkRGyVrc4= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -450,6 +459,7 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= @@ -610,6 +620,7 @@ golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -644,6 +655,7 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/http.go b/http.go index e820135..f2a5ed0 100644 --- a/http.go +++ b/http.go @@ -194,6 +194,9 @@ func (a *goBlog) buildStaticHandlersRouters() error { a.editorRouter.Use(a.authMiddleware) a.editorRouter.Get("/", a.serveEditor) a.editorRouter.Post("/", a.serveEditorPost) + a.editorRouter.Get("/files", a.serveEditorFiles) + a.editorRouter.Post("/files/view", a.serveEditorFilesView) + a.editorRouter.Post("/files/delete", a.serveEditorFilesDelete) a.commentsRouter = chi.NewRouter() a.commentsRouter.Use(a.privateModeHandler...) diff --git a/indieAuthServer.go b/indieAuthServer.go index 814b7f6..3277a9b 100644 --- a/indieAuthServer.go +++ b/indieAuthServer.go @@ -11,8 +11,8 @@ import ( "strings" "time" - "git.jlel.se/jlelse/GoBlog/pkgs/contenttype" "github.com/spf13/cast" + "go.goblog.app/app/pkgs/contenttype" ) // https://www.w3.org/TR/indieauth/ diff --git a/mediaCompression.go b/mediaCompression.go index c06100b..b516f42 100644 --- a/mediaCompression.go +++ b/mediaCompression.go @@ -9,7 +9,7 @@ import ( "net/http" "os" - "git.jlel.se/jlelse/GoBlog/pkgs/contenttype" + "go.goblog.app/app/pkgs/contenttype" ) const defaultCompressionWidth = 2000 diff --git a/mediaStorage.go b/mediaStorage.go index 4f6d968..0a82bf6 100644 --- a/mediaStorage.go +++ b/mediaStorage.go @@ -12,9 +12,7 @@ import ( "github.com/jlaffaye/ftp" ) -type mediaStorageSaveFunc func(filename string, file io.Reader) (location string, err error) - -func (a *goBlog) saveMediaFile(filename string, f io.Reader) (string, error) { +func (a *goBlog) initMediaStorage() { a.mediaStorageInit.Do(func() { type initFunc func() mediaStorage for _, fc := range []initFunc{a.initBunnyCdnMediaStorage, a.initFtpMediaStorage, a.initLocalMediaStorage} { @@ -24,6 +22,12 @@ func (a *goBlog) saveMediaFile(filename string, f io.Reader) (string, error) { } } }) +} + +type mediaStorageSaveFunc func(filename string, file io.Reader) (location string, err error) + +func (a *goBlog) saveMediaFile(filename string, f io.Reader) (string, error) { + a.initMediaStorage() if a.mediaStorage == nil { return "", errors.New("no media storage configured") } @@ -34,8 +38,42 @@ func (a *goBlog) saveMediaFile(filename string, f io.Reader) (string, error) { return a.getFullAddress(loc), nil } +func (a *goBlog) deleteMediaFile(filename string) error { + a.initMediaStorage() + if a.mediaStorage == nil { + return errors.New("no media storage configured") + } + return a.mediaStorage.delete(filepath.Base(filename)) +} + +type mediaFile struct { + Name string + Location string + Time time.Time + Size int64 +} + +func (a *goBlog) mediaFiles() ([]*mediaFile, error) { + a.initMediaStorage() + if a.mediaStorage == nil { + return nil, errors.New("no media storage configured") + } + return a.mediaStorage.files() +} + +func (a *goBlog) mediaFileLocation(name string) string { + a.initMediaStorage() + if a.mediaStorage == nil { + return "" + } + return a.mediaStorage.location(name) +} + type mediaStorage interface { save(filename string, file io.Reader) (location string, err error) + delete(filename string) (err error) + files() (files []*mediaFile, err error) + location(filename string) (location string) } type localMediaStorage struct { @@ -64,10 +102,46 @@ func (l *localMediaStorage) save(filename string, file io.Reader) (location stri if _, err = io.Copy(newFile, file); err != nil { return "", err } - if l.mediaURL != "" { - return fmt.Sprintf("%s/%s", l.mediaURL, filename), nil + return l.location(filename), nil +} + +func (l *localMediaStorage) delete(filename string) (err error) { + if err = os.MkdirAll(l.path, 0644); err != nil { + return err } - return fmt.Sprintf("/m/%s", filename), nil + return os.Remove(filepath.Join(l.path, filename)) +} + +func (l *localMediaStorage) files() (files []*mediaFile, err error) { + if err = os.MkdirAll(l.path, 0644); err != nil { + return nil, err + } + entries, err := os.ReadDir(l.path) + if err != nil { + return nil, err + } + for _, e := range entries { + fi, er := e.Info() + if er != nil { + continue + } + if fi.Mode().IsRegular() { + files = append(files, &mediaFile{ + Name: fi.Name(), + Location: l.location(fi.Name()), + Time: fi.ModTime(), + Size: int64(fi.Size()), + }) + } + } + return files, nil +} + +func (l *localMediaStorage) location(name string) string { + if l.mediaURL != "" { + return fmt.Sprintf("%s/%s", l.mediaURL, name) + } + return fmt.Sprintf("/m/%s", name) } func (a *goBlog) initBunnyCdnMediaStorage() mediaStorage { @@ -108,21 +182,72 @@ func (a *goBlog) initFtpMediaStorage() mediaStorage { } func (f *ftpMediaStorage) save(filename string, file io.Reader) (location string, err error) { - if f.address == "" || f.user == "" || f.password == "" { - return "", errors.New("missing FTP config") - } - c, err := ftp.Dial(f.address, ftp.DialWithTimeout(5*time.Second)) - if err != nil { - return "", err - } + c, err := f.connection() defer func() { - _ = c.Quit() + if c != nil { + _ = c.Quit() + } }() - if err = c.Login(f.user, f.password); err != nil { + if err != nil { return "", err } if err = c.Stor(filename, file); err != nil { return "", err } - return fmt.Sprintf("%s/%s", f.mediaURL, filename), nil + return f.location(filename), nil +} + +func (f *ftpMediaStorage) delete(filename string) (err error) { + c, err := f.connection() + defer func() { + if c != nil { + _ = c.Quit() + } + }() + if err != nil { + return err + } + return c.Delete(filename) +} + +func (f *ftpMediaStorage) files() (files []*mediaFile, err error) { + c, err := f.connection() + defer func() { + if c != nil { + _ = c.Quit() + } + }() + if err != nil { + return nil, err + } + w := c.Walk("") + for w.Next() { + if s := w.Stat(); s.Type == ftp.EntryTypeFile { + files = append(files, &mediaFile{ + Name: s.Name, + Location: f.location(s.Name), + Time: s.Time, + Size: int64(s.Size), + }) + } + } + return files, nil +} + +func (f *ftpMediaStorage) location(name string) string { + return fmt.Sprintf("%s/%s", f.mediaURL, name) +} + +func (f *ftpMediaStorage) connection() (*ftp.ServerConn, error) { + if f.address == "" || f.user == "" || f.password == "" { + return nil, errors.New("missing FTP config") + } + c, err := ftp.Dial(f.address, ftp.DialWithTimeout(5*time.Second)) + if err != nil { + return nil, err + } + if err = c.Login(f.user, f.password); err != nil { + return nil, err + } + return c, nil } diff --git a/micropub.go b/micropub.go index bf51943..cbf0020 100644 --- a/micropub.go +++ b/micropub.go @@ -13,8 +13,8 @@ import ( "strings" "time" - "git.jlel.se/jlelse/GoBlog/pkgs/contenttype" "github.com/spf13/cast" + "go.goblog.app/app/pkgs/contenttype" "gopkg.in/yaml.v3" ) diff --git a/micropubMedia.go b/micropubMedia.go index f1cdbcf..c04a845 100644 --- a/micropubMedia.go +++ b/micropubMedia.go @@ -6,7 +6,7 @@ import ( "path/filepath" "strings" - "git.jlel.se/jlelse/GoBlog/pkgs/contenttype" + "go.goblog.app/app/pkgs/contenttype" ) const micropubMediaSubPath = "/media" diff --git a/nodeinfo.go b/nodeinfo.go index 0f3204c..27de9c4 100644 --- a/nodeinfo.go +++ b/nodeinfo.go @@ -4,7 +4,7 @@ import ( "encoding/json" "net/http" - "git.jlel.se/jlelse/GoBlog/pkgs/contenttype" + "go.goblog.app/app/pkgs/contenttype" ) func (a *goBlog) serveNodeInfoDiscover(w http.ResponseWriter, r *http.Request) { @@ -28,7 +28,7 @@ func (a *goBlog) serveNodeInfo(w http.ResponseWriter, r *http.Request) { "version": "2.1", "software": map[string]interface{}{ "name": "goblog", - "repository": "https://git.jlel.se/jlelse/GoBlog", + "repository": "https://go.goblog.app/app", }, "usage": map[string]interface{}{ "users": map[string]interface{}{ diff --git a/opensearch.go b/opensearch.go index feccc2a..77da779 100644 --- a/opensearch.go +++ b/opensearch.go @@ -4,7 +4,7 @@ import ( "fmt" "net/http" - "git.jlel.se/jlelse/GoBlog/pkgs/contenttype" + "go.goblog.app/app/pkgs/contenttype" ) func (a *goBlog) serveOpenSearch(w http.ResponseWriter, r *http.Request) { diff --git a/original-assets/styles/styles.scss b/original-assets/styles/styles.scss index ab887ce..34bd81d 100644 --- a/original-assets/styles/styles.scss +++ b/original-assets/styles/styles.scss @@ -118,7 +118,7 @@ form { .fw-form { @extend .fw; - input:not([type]), input[type="submit"], input[type="button"], input[type="text"], input[type="email"], input[type="url"], input[type="password"], textarea { + input:not([type]), input[type="submit"], input[type="button"], input[type="text"], input[type="email"], input[type="url"], input[type="password"], input[type="file"], textarea, select { @extend .fw; } } diff --git a/pkgs/minify/minify.go b/pkgs/minify/minify.go index 6c7989d..3da3c30 100644 --- a/pkgs/minify/minify.go +++ b/pkgs/minify/minify.go @@ -4,13 +4,13 @@ import ( "io" "sync" - "git.jlel.se/jlelse/GoBlog/pkgs/contenttype" "github.com/tdewolff/minify/v2" mCss "github.com/tdewolff/minify/v2/css" mHtml "github.com/tdewolff/minify/v2/html" mJs "github.com/tdewolff/minify/v2/js" mJson "github.com/tdewolff/minify/v2/json" mXml "github.com/tdewolff/minify/v2/xml" + "go.goblog.app/app/pkgs/contenttype" ) type Minifier struct { diff --git a/render.go b/render.go index f4ddd76..4d47235 100644 --- a/render.go +++ b/render.go @@ -11,8 +11,8 @@ import ( "path/filepath" "strings" - "git.jlel.se/jlelse/GoBlog/pkgs/contenttype" servertiming "github.com/mitchellh/go-server-timing" + "go.goblog.app/app/pkgs/contenttype" ) const ( @@ -28,6 +28,7 @@ const ( templateSummary = "summary" templatePhotosSummary = "photosummary" templateEditor = "editor" + templateEditorFiles = "editorfiles" templateLogin = "login" templateStaticHome = "statichome" templateBlogStats = "blogstats" @@ -68,6 +69,7 @@ func (a *goBlog) initRendering() error { "geotitle": a.geoTitle, "geolink": geoOSMLink, "opensearch": openSearchUrl, + "mbytes": mBytesString, } baseTemplate, err := template.New("base").Funcs(templateFunctions).ParseFiles(path.Join(templatesDir, templateBase+templatesExt)) if err != nil { diff --git a/templateAssets.go b/templateAssets.go index c107282..0e9a5f3 100644 --- a/templateAssets.go +++ b/templateAssets.go @@ -10,7 +10,7 @@ import ( "path/filepath" "strings" - "git.jlel.se/jlelse/GoBlog/pkgs/contenttype" + "go.goblog.app/app/pkgs/contenttype" ) const assetsFolder = "templates/assets" diff --git a/templates/assets/css/styles.css b/templates/assets/css/styles.css index 1f2a224..3d4efb3 100644 --- a/templates/assets/css/styles.css +++ b/templates/assets/css/styles.css @@ -163,7 +163,7 @@ footer * { display: inline; } -.fw, .fw-form, .fw-form input:not([type]), .fw-form input[type=submit], .fw-form input[type=button], .fw-form input[type=text], .fw-form input[type=email], .fw-form input[type=url], .fw-form input[type=password], .fw-form textarea { +.fw, .fw-form, .fw-form input:not([type]), .fw-form input[type=submit], .fw-form input[type=button], .fw-form input[type=text], .fw-form input[type=email], .fw-form input[type=url], .fw-form input[type=password], .fw-form input[type=file], .fw-form textarea, .fw-form select { width: 100%; } diff --git a/templates/assets/js/formconfirm.js b/templates/assets/js/formconfirm.js new file mode 100644 index 0000000..9833545 --- /dev/null +++ b/templates/assets/js/formconfirm.js @@ -0,0 +1,16 @@ +(function () { + Array.from(document.querySelectorAll('form input.confirm')).forEach(element => { + let showed = false + element.form.addEventListener('submit', event => { + if (!showed) { + event.preventDefault() + element.value = '…' + setTimeout(() => { + element.value = element.dataset.confirmmessage + showed = true + }, 1000) + return false + } + }) + }) +})() \ No newline at end of file diff --git a/templates/editor.gohtml b/templates/editor.gohtml index acb59f5..cbf5865 100644 --- a/templates/editor.gohtml +++ b/templates/editor.gohtml @@ -47,14 +47,15 @@ tags:

{{ string .Blog.Lang "upload" }}

- +
+

{{ string .Blog.Lang "mediafiles" }}

{{ if .Data.Drafts }}

{{ string .Blog.Lang "drafts" }}

- {{ range $i, $draft := .Data.Drafts }} {{ end }} @@ -64,7 +65,7 @@ tags: {{ end }}

{{ string .Blog.Lang "location" }}

- +
diff --git a/templates/editorfiles.gohtml b/templates/editorfiles.gohtml new file mode 100644 index 0000000..c6538ee --- /dev/null +++ b/templates/editorfiles.gohtml @@ -0,0 +1,28 @@ +{{ define "title" }} + {{ string .Blog.Lang "mediafiles" }} - {{ .Blog.Title }} +{{ end }} + +{{ define "main" }} +
+ {{ $blog := .Blog }} +

{{ string .Blog.Lang "mediafiles" }}

+ {{ if .Data.Files }} +
+ + + +
+ + {{ else }} +

{{ string .Blog.Lang "nofiles" }}

+ {{ end }} +
+{{ end }} + +{{ define "editorfiles" }} + {{ template "base" . }} +{{ end }} \ No newline at end of file diff --git a/templates/strings/de.yaml b/templates/strings/de.yaml index 584bf2d..1b3ca3b 100644 --- a/templates/strings/de.yaml +++ b/templates/strings/de.yaml @@ -19,7 +19,9 @@ location: "Standort" locationfailed: "Abfragen des Standorts fehlgeschlagen" locationget: "Standort abfragen" locationnotsupported: "Die Standort-API wird von diesem Browser nicht unterstützt" +mediafiles: "Medien-Dateien" next: "Weiter" +nofiles: "Keine Dateien" noposts: "Hier sind keine Posts." oldcontent: "⚠️ Dieser Eintrag ist bereits über ein Jahr alt. Er ist möglicherweise nicht mehr aktuell. Meinungen können sich geändert haben." posts: "Posts" diff --git a/templates/strings/default.yaml b/templates/strings/default.yaml index 66dd52d..651de92 100644 --- a/templates/strings/default.yaml +++ b/templates/strings/default.yaml @@ -28,8 +28,10 @@ locationget: "Request location" locationnotsupported: "The location API is not supported by this browser" login: "Login" logout: "Logout" +mediafiles: "Media files" nameopt: "Name (optional)" next: "Next" +nofiles: "No files" noposts: "There are no posts here." notifications: "Notifications" oldcontent: "⚠️ This entry is already over one year old. It may no longer be up to date. Opinions may have changed." diff --git a/utils.go b/utils.go index 9ecd098..de61695 100644 --- a/utils.go +++ b/utils.go @@ -15,6 +15,7 @@ import ( "github.com/PuerkitoBio/goquery" "github.com/araddon/dateparse" + "github.com/c2h5oh/datasize" "github.com/thoas/go-funk" ) @@ -226,3 +227,7 @@ func getSHA256(file io.ReadSeeker) (filename string, err error) { } return fmt.Sprintf("%x", h.Sum(nil)), nil } + +func mBytesString(size int64) string { + return fmt.Sprintf("%.2f MB", datasize.ByteSize(size).MBytes()) +} diff --git a/webmention.go b/webmention.go index dc2173a..41496d8 100644 --- a/webmention.go +++ b/webmention.go @@ -9,7 +9,7 @@ import ( "strings" "time" - "git.jlel.se/jlelse/GoBlog/pkgs/contenttype" + "go.goblog.app/app/pkgs/contenttype" ) type webmentionStatus string diff --git a/webmentionSending.go b/webmentionSending.go index 92468e2..2c77521 100644 --- a/webmentionSending.go +++ b/webmentionSending.go @@ -8,10 +8,10 @@ import ( "net/url" "strings" - "git.jlel.se/jlelse/GoBlog/pkgs/contenttype" "github.com/PuerkitoBio/goquery" "github.com/thoas/go-funk" "github.com/tomnomnom/linkheader" + "go.goblog.app/app/pkgs/contenttype" ) func (a *goBlog) sendWebmentions(p *post) error {