yufan.me requires two data stores: Postgres for all content, settings, sessions, and analytics, and Redis for session storage, rate limiting, and generated-image caching. Both must be reachable before the server starts. Schema management is handled by Drizzle Kit — migrations live underDocumentation 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.
drizzle/ and are applied independently of application boot.
Postgres
yufan.me targets Postgres 16 or later. Postgres 17 is recommended. Thepgvector extension is used for embedding-based search but is optional — the application falls back to SQL LIKE search when the extension is unavailable.
Connection strings follow the standard libpq URI format:
?sslmode=require (or verify-full for certificate verification):
pg driver through Drizzle ORM. All queries go through the pool configured in src/server/infra/db/.
Redis
Redis is used for three purposes:- Session storage — signed session tokens are persisted in Redis so they survive server restarts.
- Rate limiting — per-IP and per-user counters enforce the limits configured in
blog.rateLimitsettings. - Caching — generated OG images and other computed artifacts are cached to avoid redundant work.
ioredis URI format:
Running migrations
Migrations are managed with Drizzle Kit. Thedrizzle/ directory contains one subdirectory per migration, each with a SQL file and snapshot metadata. The directory is copied into the Docker runtime image so migrations can be applied from within a container without access to the source tree.
Set DATABASE_URL
Ensure Drizzle Kit reads this variable automatically. No separate
DATABASE_URL is exported in your shell or present in your .env file before running any migration commands:drizzle.config.ts override is needed for the standard deployment path.Generate migrations after schema changes (development only)
When you modify This runs
src/server/infra/db/schema.ts, generate a new migration file with:drizzle-kit generate and writes a new timestamped directory under drizzle/. Commit the generated files alongside your schema changes. You do not need to run this step when deploying an existing release.Apply migrations
Apply all pending migrations against your database using When deploying with Docker, run migrations from a temporary container before starting the application container:Drizzle Kit tracks applied migrations in a
drizzle-kit migrate:__drizzle_migrations table and skips files that have already been applied. It is safe to run on every deployment.Migration files
Thedrizzle/ directory contains ten migrations applied in timestamp order:
| Migration | Description |
|---|---|
20260514000000_enable_pgvector | Enables the pgvector extension for embedding-based search. Must be the first migration to run. |
20260514000001_init_schema | Initial schema: post, page, category, tag, user, setting, image, music, friend, and junction tables. |
20260514000002_access_log | Analytics visit ingestion table (access_log) used by the first-party analytics dashboard. |
20260514000003_access_log_timescale | Converts access_log to a TimescaleDB hypertable for time-series performance. Falls back gracefully when TimescaleDB is not installed. |
20260515153336_audit_log | Audit log table that records admin events (content changes, settings updates, user actions). |
20260517143442_sidebar_widgets | Sidebar widget configuration stored as JSONB under blog.sidebar settings. |
20260517150000_x_rebrand_and_footer_nav | Updates social link keys to reflect the X (formerly Twitter) rebrand and adds footer navigation columns. |
20260517160000_merge_footer_nav_into_navigation | Consolidates footer navigation configuration into the blog.navigation settings section. |
20260517170000_merge_footer_into_general | Merges footer display settings into the blog.general settings section. |
20260521000000_audit_log_indexes | Adds performance indexes to audit_log to speed up admin dashboard queries. |
The
audit_log table is excluded from standard pg_dump backups because it uses S3 archival for long-term retention. If you are running manual backups, add --exclude-table=audit_log to your pg_dump command to avoid backing up data that is already archived to object storage.