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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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>
- 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).
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.
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.
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.
- Enforce SLUG_REGEX (alphanumeric + dot/dash/underscore only) in creation_error
to prevent path traversal (C-1) and stored XSS (C-3).
- Add File.expand_path containment check in default_path_for as defense-in-depth.
- Escape slug in form action attributes (list, form views).
- Validate username in change_username using the same regex as create_user (C-4).
- Escape username in settings form action attributes.
- Treat submitting the current username as a no-op success instead of 'taken'.
Keep server.rb focused on configuration, session setup, and shared
helpers. Extract route definitions into registered Sinatra extension
modules. Exclude the registered methods from AbcSize/MethodLength
metrics — large registered methods are the idiomatic Sinatra pattern.
The admin.session_ttl value was defined in defaults, template, and
documentation but never read — sessions had no expiration. Pass it as
expire_after to Rack::Session::Cookie. A value <= 0 means persistent
(until browser close), matching the documented behaviour.