yufan.me stores every piece of runtime configuration in theDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/syhily/yufan.me/llms.txt
Use this file to discover all available pages before exploring further.
setting table as one JSONB row per section, identified by a scope string of the form blog.<section>. Sections save independently — when you click Save on the navigation settings page, only the blog.navigation row is written. Concurrent admin tabs editing different sections cannot overwrite each other’s changes, and a failed save in one section never rolls back another.
All sections are edited from the admin console at /admin/settings/<section>. No environment variable change or server restart is required to apply updates.
general and assets — have no auto-generated defaults and require user input at install time. The two-stage install gate at /admin/setup/settings collects these values and writes all 14 rows atomically before the public site becomes available.Sections
general — site identity
general — site identity
blog.generalThe core identity of the blog. Collected during the install gate and editable at /admin/settings/general.| Field | Description |
|---|---|
title | Site name, up to 120 characters |
description | Short site description, up to 240 characters |
website | Canonical public URL (used in feeds and SEO meta) |
keywords | Up to 20 keyword tags for SEO |
author.name | Author display name |
author.email | Author contact email |
author.url | Author profile URL |
locale | BCP 47 language tag (e.g. zh-CN, en-US) |
timeZone | IANA time zone name (e.g. Asia/Shanghai, UTC) |
timeFormat | Date format token string consumed by formatLocalDate |
initialYear | Year the blog launched, shown in copyright notices |
icpNo | Optional ICP filing number (China regulatory) |
moeIcpNo | Optional Moe ICP filing number |
defaults: null — it must be filled in during setup.assets — CDN host, S3 storage, upload limits
assets — CDN host, S3 storage, upload limits
blog.assetsControls the public asset CDN host, S3-compatible storage credentials, and image upload policy. Editable at /admin/settings/assets.| Field | Description |
|---|---|
asset.host | CDN hostname (e.g. cdn.example.com). Used to construct public image and music URLs |
asset.scheme | http or https |
storage.enabled | Toggle S3 uploads on or off. Defaults to false on fresh installs |
storage.endpoint | S3-compatible endpoint URL |
storage.region | Bucket region |
storage.bucket | Bucket name |
storage.accessKeyId | S3 access key |
storage.secretAccessKey | S3 secret key (send undefined to keep the stored value) |
storage.forcePathStyle | Set true for MinIO and other path-style endpoints |
storage.urlTemplate | Custom public URL template for the bucket |
upload.maxBytes | Maximum upload size in bytes (1 KB – 50 MB) |
upload.jpegQuality | JPEG re-encode quality (40–100) |
storage.enabled is false, uploads are refused with a 503 response and the admin image library is read-only. Previously uploaded images still resolve against the stored publicBaseUrl. Flipping the toggle back on does not require re-entering credentials.This section has defaults: null — asset.host must be supplied during setup.navigation — side nav and footer nav
navigation — side nav and footer nav
socials — social profile links
socials — social profile links
content — pagination, feed, post sort order
content — pagination, feed, post sort order
blog.contentControls how content is paginated, how the RSS/Atom feed is generated, and how posts are ordered.| Field | Description |
|---|---|
pagination.posts | Posts per page on the home listing (1–100, default 10) |
pagination.category | Posts per page on category listings (default 10) |
pagination.tags | Posts per page on tag listings (default 10) |
pagination.search | Results per page in search (default 10) |
feed.full | true to include full post body in feeds; false for excerpt only |
feed.size | Number of posts in the feed (1–100, default 20) |
post.sort | Sort direction: asc or desc (default desc) |
post.sortBy | Sort field: publishedAt or updatedAt |
post.featureEnabled | Enable featured post pinning on the home page |
footnotes.sectionTitle | Heading text for the footnotes section (default 尾声礼记) |
sidebar — sidebar widgets
sidebar — sidebar widgets
comments — page size, Gravatar, token TTL
comments — page size, Gravatar, token TTL
seo — TOC levels, OG image dimensions
seo — TOC levels, OG image dimensions
blog.seoControls which heading levels appear in the post table of contents and the pixel dimensions of generated Open Graph images.| Field | Description |
|---|---|
toc.minHeadingLevel | Minimum heading level included in the TOC (1–6, default 2) |
toc.maxHeadingLevel | Maximum heading level included in the TOC (1–6, default 4) |
og.width | OG image canvas width in pixels (600–4096, default 1200) |
og.height | OG image canvas height in pixels (315–4096, default 630) |
mail — outgoing email via Zeabur ZSend
mail — outgoing email via Zeabur ZSend
blog.mailConfigures the outgoing mail integration. yufan.me uses Zeabur ZSend as its mail transport.| Field | Description |
|---|---|
mail.enabled | Toggle outgoing mail on or off (default false) |
mail.host | ZSend API hostname, without scheme (default api.zeabur.com) |
mail.apiKey | ZSend API key. Send undefined to keep the stored value without re-pasting |
mail.sender | Verified sender email address (must be a valid email) |
enabled to true. Use the Send test email button on the settings page to verify the configuration before enabling it in production.cache — Redis prefix and TTL per bucket
cache — Redis prefix and TTL per bucket
blog.cacheConfigures the Redis key prefix and TTL for each cache bucket. Each bucket’s prefix must end with : and must not collide with another bucket’s prefix or with the reserved system prefixes (session:, rate-limit:, avatar-status:).| Bucket | Default prefix | Default TTL | Caches |
|---|---|---|---|
og | og: | 24 hours | Generated Open Graph images |
calendar | calendar: | 24 hours | Generated calendar SVGs |
avatar | avatar: | 24 hours | Proxied Gravatar/QQ avatars |
imageMeta | image-meta: | 1 hour | Image row lookups (storagePath → ImageRow) |
embeddingSearch | embedding-search: | 7 days | OpenAI embedding vectors for semantic search |
searchResult | search-result: | 1 hour | Search result pages |
rateLimit — per-bucket request caps
rateLimit — per-bucket request caps
blog.rateLimitConfigures the sliding-window rate limit for each protected surface. Every bucket has a windowSeconds (60 s – 24 h) and a maxAttempts (1–1000) field.| Bucket | Default window | Default max attempts | Protects |
|---|---|---|---|
signInIp | 30 min | 5 | Login form, per source IP |
commentPostIp | 1 hour | 12 | Anonymous comment submission, per source IP |
commentPostEmail | 1 hour | 8 | Anonymous comment submission, per email address |
likeIncreaseIp | 1 hour | 30 | Post like button, per source IP |
inviteIp | 1 hour | 10 | Admin invite flow, per source IP |
inviteEmail | 1 hour | 5 | Admin invite flow, per email address |
passwordResetIp | 1 hour | 5 | Password reset, per source IP |
passwordResetEmail | 1 hour | 5 | Password reset, per email address |
passwordResetTarget | 1 hour | 3 | Password reset, per target user |
search — mode, OpenAI endpoint, model
search — mode, OpenAI endpoint, model
blog.searchControls whether search is enabled and how it operates.| Field | Description |
|---|---|
search.enabled | Toggle site search on or off (default false) |
search.mode | like for SQL LIKE full-text search, vector for OpenAI vector/embedding-based semantic search |
search.endpoint | OpenAI-compatible API endpoint. Leave empty to use the official OpenAI endpoint |
search.apiKey | API key for the embedding provider. Send undefined to keep the stored value |
search.model | Embedding model name (default text-embedding-3-small) |
search.similarityThreshold | Cosine similarity threshold for vector search results (0–1, default 0.5) |
like mode requires no external service and works on a fresh install. vector mode requires a valid API key and pre-computed embeddings; the embedding pipeline runs automatically when posts are published or updated while vector mode is active.fonts — OG image, calendar, and CSS font URLs
fonts — OG image, calendar, and CSS font URLs
blog.fontsConfigures the font URLs used by the OG image renderer, the calendar SVG generator, and the public site CSS.| Field | Description |
|---|---|
og.url | Font file URL for the OG image canvas renderer. Empty string disables custom font |
calendar.url | Font file URL for the calendar SVG renderer. Empty string disables custom font |
globalCss | Array of up to 8 CSS stylesheet URLs loaded on every public page |
postCss | Array of up to 8 CSS stylesheet URLs loaded only on post detail pages |
/admin/settings/fonts when you are ready to apply custom typography.backup — scheduled database backups
backup — scheduled database backups
blog.backupConfigures automated pg_dump backups uploaded to the configured S3 bucket.| Field | Description |
|---|---|
scheduled.enabled | Toggle scheduled backups on or off (default false) |
scheduled.frequency | daily, weekly, or monthly |
scheduled.hour | UTC hour to run the backup (0–23, default 3) |
scheduled.minute | Minute past the hour: 0 or 30 (default 0) |
scheduled.dayOfWeek | Required when frequency is weekly (1 = Monday, 7 = Sunday) |
scheduled.dayOfMonth | Required when frequency is monthly (1–28) |
retention.enabled | Auto-delete old backups from S3 (default true) |
retention.days | Days to keep backups before deletion (1–365, default 30) |
audit_log table is excluded from pg_dump backups; audit archive files are managed separately.general and assets — have defaults: null in the section registry. They have no auto-generated seed values and require user input at install time. The install gate at /admin/setup/settings collects the site name, description, author details, and asset CDN host before writing all 14 rows atomically. Every other section ships with conservative defaults (empty arrays, disabled integrations, minimal TTLs) so the public site renders immediately after setup without requiring the admin to visit each settings tab.
blog.commentsControls comment display and the anonymous commenter token lifecycle.comments.sizecomments.avatar.mirrorhttps://www.gravatar.com/avatar)comments.avatar.sizecomments.tokenTtlSecondscomments.avatar.mirrorto the mirror hostname such ashttps://gravatar.loli.net/avatar.