From 778cb72c8a0902bd0b8159ebd3bb7eff93f28c83 Mon Sep 17 00:00:00 2001 From: grm Date: Mon, 14 Sep 2026 23:51:55 +0300 Subject: Turn the image library into a file library, with a per-blog upload limit Bloggers want to attach PDFs, archives, audio and other files to posts, not only images. The Images tab becomes Files: any type is accepted, listed by kind with search, paging, rename and multi-file upload, and the editor's paste/drop/"Insert file" takes anything (images are shown, everything else becomes a link). The default limit goes from 5 to 10 MB and the superadmin can override it per blog from /admin/. Files stay in Postgres so one pg_dump is still the whole blog. The bytea column is STORAGE EXTERNAL and /media streams it in substring() slices, so serving never holds a whole file in memory whatever limit a blog gets. Serving any type on the root domain, which carries the session cookie, needs a policy: uploads are typed by sniffing (the extension may only refine a generic sniff to an allowlisted type) and only images, PDF, plain text, audio and video render inline; HTML, SVG, XML, scripts, archives and binaries always go out as application/octet-stream with Content-Disposition: attachment. The body cap moves out of requireAuth into guardPOST, which runs after withBlog has resolved the blog and so knows its limit. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A --- internal/web/templates/dashboard/files.html | 95 +++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 internal/web/templates/dashboard/files.html (limited to 'internal/web/templates/dashboard/files.html') diff --git a/internal/web/templates/dashboard/files.html b/internal/web/templates/dashboard/files.html new file mode 100644 index 0000000..b42cec2 --- /dev/null +++ b/internal/web/templates/dashboard/files.html @@ -0,0 +1,95 @@ +{{define "title"}}{{t "Files"}} · {{.Blog.Title}}{{end}} +{{define "content"}} +{{$b := .Blog.Subdomain}} +
+

{{t "Files"}}

+

{{t "Files are stored with your blog. Put one in a post with"}} {{t "Insert file"}} {{t "in the editor, or copy its line from the table."}} {{tf "%d files, %s in use." .Data.count (size .Data.bytes)}}

+
+
+
+ + +

+
    +
    +
    +
    + + {{t "All"}} + {{range .Data.kinds}}{{t (index $.Data.kindNames .)}}{{end}} + + {{if .Data.kind}}{{end}} + +
    +
    +{{if .Data.files}} + + + {{range .Data.files}} + + + + + + + + {{end}} +
    {{t "Name"}}{{t "Kind"}}{{t "Size"}}{{t "Date"}}Markdown
    {{if eq .Kind "image"}}{{else}}{{.Badge}}{{end}}{{.Filename}} +
    {{t "rename"}} +
    + + + +
    {{t (index $.Data.kindNames .Kind)}}{{size .Size}}{{date .CreatedAt}}{{t "download"}} · +
    +{{else if or .Data.q .Data.kind}}

    {{t "No files match."}}

    +{{else}}

    {{t "No files yet."}}

    {{end}} +
    +{{if gt .Data.lastPage 1}} +
    + {{if gt .Data.pageNum 1}}← {{t "Newer"}}{{end}} + {{tf "Page %d of %d" .Data.pageNum .Data.lastPage}} + {{if lt .Data.pageNum .Data.lastPage}}{{t "Older"}} →{{end}} +
    +{{end}} + +{{end}} -- cgit v1.2.3