63 Commits
Author SHA1 Message Date
petrbalvin bce4f5e223 chore: merge development into main (v0.4.2)
Release / release (push) Successful in 1m11s
v0.4.2
2026-07-27 10:37:47 +02:00
petrbalvin a66a36abbc chore: prepare release v0.4.2
Test / test (push) Successful in 59s
2026-07-27 10:36:44 +02:00
petrbalvin 38544b84cf fix(admin): ship templates in wheel, fix CSP and bootstrap login
Test / test (push) Successful in 1m1s
2026-07-27 10:27:41 +02:00
petrbalvin 98f7f3c9e3 fix: add missing CSP style-src nonce and include static SVGs in wheel
Test / test (push) Successful in 57s
Release / release (push) Successful in 1m11s
v0.4.1
2026-07-27 09:03:44 +02:00
petrbalvin d9b7a53ac2 ci(release): fix tag env, upgrade actions, add release permissions
Test / test (push) Successful in 1m10s
2026-07-26 23:35:30 +02:00
petrbalvin a5c1b519ed fix(ci): use gitea.ref_name and map PYPI_TOKEN env for publish step
Test / test (push) Successful in 1m0s
Release / release (push) Successful in 1m10s
v0.4.0
2026-07-26 18:19:11 +02:00
petrbalvin 3b8ed31f67 chore: prepare release v0.4.0
Test / test (push) Successful in 1m7s
Release / release (push) Successful in 1m11s
2026-07-26 18:15:29 +02:00
petrbalvin 19c6161ae0 fix: Markdown import in admin preview route 2026-07-22 21:25:27 +02:00
petrbalvin 3ac1ef78c2 fix: icon path and config.toml structure 2026-07-22 21:22:08 +02:00
petrbalvin 36855afc70 fix: hash-password command import name 2026-07-22 21:17:21 +02:00
petrbalvin 51d744e0ea fix: default bind to :: for IPv6 dual-stack 2026-07-22 21:14:41 +02:00
petrbalvin 44b879371e refactor: rewrite from Ruby/Sinatra to Python/FastAPI 2026-07-22 21:12:28 +02:00
petrbalvin 6f62d3e3f3 chore: prepare release v0.3.0 v0.3.0 2026-07-12 14:03:14 +02:00
petrbalvin 1dfbf67921 style(web): polish native date, time and file inputs 2026-07-12 12:55:29 +02:00
petrbalvin 0e8f013bbb feat(web): show version in sidebar footer 2026-07-12 11:25:22 +02:00
petrbalvin 2f90c6da4d feat(admin): add volumen icon and use it in sidebar and login 2026-07-12 11:18:59 +02:00
petrbalvin f40ace64a8 feat(settings): add user profile photo upload and tabbed layout 2026-07-12 11:12:37 +02:00
petrbalvin ff0452bcc3 feat(uploads): restrict to webp/avif with conversion hint 2026-07-12 11:03:49 +02:00
petrbalvin 34dc67ce99 feat(admin): add user fediverse handle setting 2026-07-12 11:00:01 +02:00
petrbalvin e028db6447 feat(users): add display name field for users 2026-07-12 10:57:14 +02:00
petrbalvin cbae03014d fix(form): default to today when date is missing 2026-07-12 10:52:17 +02:00
petrbalvin 10ef258e84 feat(admin): redesign admin UI with modern layout 2026-07-12 10:50:49 +02:00
petrbalvin 5fdf8e5ee8 feat(post): add cover caption metadata 2026-07-12 10:31:21 +02:00
petrbalvin 6ee8e066aa feat(markdown): wrap titled images in figures with caption 2026-07-12 10:24:45 +02:00
petrbalvin 1dbeb66be0 fix(admin): clear posts cache after save/delete 2026-07-12 09:33:30 +02:00
petrbalvin d1295c6633 docs: document all development changes in changelog
Add entries for pagination flags, draft error code, just lint/fmt,
config.toml.example, published_posts memoization, Store mtime
invalidation, Cache-Control, FeedHelpers extraction, CSP header,
upload size limit, and MIME whitelist.
2026-06-26 20:49:48 +02:00
petrbalvin 5cc3a1aabe feat: return distinct 'draft' error for draft post detail requests
Previously the API returned 'not_found' for both missing and draft
posts. Now draft posts return a specific 'draft' error code so API
consumers can distinguish between the two cases and show an
appropriate message.
2026-06-26 20:44:42 +02:00
petrbalvin 781707a3fd chore: bump ClassLength limit to 440
Server sits at 423 lines after extracting FeedHelpers; allow a small
headroom for upcoming changes.
2026-06-26 20:44:03 +02:00
petrbalvin 2da9ec9bba refactor: extract feed and sitemap helpers into FeedHelpers module
Move feed_xml, feed_item, feed_json, json_feed_item, rfc822_date,
iso_date, and sitemap_xml out of Server into a dedicated FeedHelpers
module included via helpers. Reduces Server from ~500 to ~400 lines
and separates feed generation concerns from routing.
2026-06-26 20:43:27 +02:00
petrbalvin 0ba6e379d7 chore: gitignore config.toml, add config.toml.example
The dev config.toml contains a password hash and should not be
committed. Add a config.toml.example template instead and document
the copy step.
2026-06-26 20:41:49 +02:00
petrbalvin db6da86c93 perf: add Cache-Control header to public API responses
Set Cache-Control: public, max-age=60 on all /api/volumen/* endpoints
so browsers and CDNs can cache responses for 60 seconds, reducing
unnecessary re-fetches.
2026-06-26 20:41:30 +02:00
petrbalvin a2f42c6a12 security: add Content-Security-Policy header for admin routes
Set a strict CSP on all /admin/* responses: default-src 'self',
script/style 'unsafe-inline' (needed for the inline editor JS and
layout CSS), img-src 'self' data:, frame-ancestors 'none'. Public
API routes are unaffected. Bump ClassLength limit to 420 to
accommodate the new helper.
2026-06-26 20:41:10 +02:00
petrbalvin 223405dcb9 perf: invalidate Store cache on individual file mtime changes
Previously the store only checked the content directory's own mtime,
which does not change when an existing file is modified in place. Now
track the newest mtime across all .md files as well, so edits picked
up from disk (manual changes, git pull) are detected without a
restart.
2026-06-26 20:40:21 +02:00
petrbalvin 95452477d8 chore: add just lint and just fmt recipes
Add standalone lint and fmt (auto-fix) recipes to the justfile so
developers can check style without running the full build. Also
extract upload validation into a private Server helper to keep
the admin routes method under the complexity threshold.
2026-06-26 20:37:21 +02:00
petrbalvin bc2e056b68 feat: add has_next and has_prev to paginated posts response
Clients no longer need to compute page boundaries themselves; the API
now returns boolean flags indicating whether more pages exist in each
direction. Includes tests for first, middle, and last page.
2026-06-26 20:36:16 +02:00
petrbalvin e11c7d24a4 security: whitelist allowed image MIME types for uploads
Only accept common image formats (JPEG, PNG, GIF, WebP, AVIF, SVG)
on the upload endpoint. Non-image uploads now return HTTP 415 with a
structured JSON error listing the allowed types. Includes a test.
2026-06-26 20:35:46 +02:00
petrbalvin 25ebf691f7 security: reject file uploads exceeding 10 MB
Add a MAX_UPLOAD_BYTES constant (10 MB) to Server and check the
uploaded tempfile size before writing it to disk. Exceeding the limit
returns HTTP 413 with a structured JSON error. Includes a test.
2026-06-26 20:35:23 +02:00
petrbalvin 4abb5585df perf: memoize published_posts within a single request
Store the filtered and sorted list of published posts in an instance
variable so repeated calls during the same request (posts list, tags,
feeds, sitemap) reuse the cached result instead of re-filtering and
re-sorting from scratch each time. Sinatra resets instance variables
between requests, so the cache stays fresh.
2026-06-26 20:34:37 +02:00
petrbalvinandQwen-Coder 5437cef1cd feat: add fediverse_creator handle for Mastodon attribution
Expose a per-post fediverse_creator frontmatter field plus a site-wide
default in config.toml ([site].fediverse_creator). The handle is returned
in the post and site payloads, rendered as <dc:creator> in the RSS feed
and as authors[].name in the JSON feed, so a front-end can drop it into
<meta name="fediverse:creator"> on rendered pages. Per-post value
overrides the site default. Validated against a simple @user@host regex.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-06-26 17:40:48 +02:00
petrbalvin 1440e9a1fd release: bump version to 0.2.0 v0.2.0 2026-06-25 22:12:55 +02:00
petrbalvin b24a8cc377 feat: add fulltext search with ?q= parameter
Filter posts by case-insensitive match in title and body. Extracted
filter_by_lang and search_posts helpers to keep complexity in check.
2026-06-25 22:06:49 +02:00
petrbalvin 599c63e5bc feat: add JSON Feed endpoint at /api/volumen/feed.json
Implements jsonfeed.org version 1.1 with title, home_page_url, feed_url,
description, language, and items with content_html and date_published.
2026-06-25 22:06:49 +02:00
petrbalvin 73be893bf8 feat: add scheduled publishing with publish_at frontmatter field
Posts with a future publish_at date are hidden from the public API
and feeds, same as drafts. The admin form exposes a Publish at field.
2026-06-25 22:06:49 +02:00
petrbalvin 52be2bb996 security: CORS preflight, session invalidation, CSRF on preview, symlink containment, and hardening
- Add OPTIONS /api/volumen/* for CORS preflight (S-22).
- Invalidate session in require_login! when user no longer exists (S-8).
- Add check_csrf! to POST /admin/preview (S-1).
- Use File.realpath for symlink-safe containment in media_file (S-2).
- Memoize Users#all with mtime-based invalidation (S-12).
- Document that empty permitted_hosts means allow-all (S-5).
- Warn and fall back to random when session_key is too short (S-20).
2026-06-25 22:06:49 +02:00
petrbalvin 5b8236c0a4 test: add sitemap.xml endpoint test
Verify content type, urlset structure, post URL presence, and draft exclusion.
2026-06-25 22:06:49 +02:00
petrbalvin 51e425e6dd test: add CLI test coverage
Cover version, help, unknown command, parse_serve_options defaults
and overrides, prepare_config template generation and no-op.
2026-06-25 22:06:49 +02:00
petrbalvin ce57410044 fix: Store#save writes non-default language posts into lang subdirectory
default_path_for now routes posts whose lang differs from default_lang
into content_dir/<lang>/<slug>.md, matching the documented multi-language
layout and preventing same-slug posts in different languages from
overwriting each other.
2026-06-25 22:06:49 +02:00
petrbalvin afd8ab4bfb fix: atomic writes for posts and users.toml
Write to a .tmp file and then File.rename into place, so a concurrent
read or a mid-write crash never sees a torn file.
2026-06-25 22:06:49 +02:00
petrbalvin ad00872895 perf: memoize Store#all with mtime-based invalidation
Cache the parsed post list per Store instance, invalidated on save,
delete, or content_dir mtime change. Eliminates re-parsing every .md
file on every API and admin request.
2026-06-25 22:06:49 +02:00
petrbalvin d7f9813484 fix: prevent Config DEFAULTS mutation through deep_merge
Freeze inner hashes in DEFAULTS and rewrite deep_merge to build fresh
hashes with {}.merge for uncopied inner maps. Previously, inner hashes
shared objects with the frozen-looking DEFAULTS, so mutating a loaded
config silently poisoned all later loads.
2026-06-25 22:06:49 +02:00