3.3 KiB
3.3 KiB
Architecture
Layers
molodetz/main.py: FastAPI object (/swagger,/openapi.json), lifespan (init lock,init_db, bootstrap, services with service lock), static mounts (uploads, versioned/static/v<STATIC_VERSION>/, fallback), middleware in template order, exception handlers, routers.molodetz/config.py: the only place forload_dotenv, paths, env bindings with prefixMOLODETZ_,APP_VERSIONviatomllib,BOOT_ID,STATIC_VERSION.molodetz/database/: synchronousdatasethandle (SQLAlchemy NullPool, WAL PRAGMAs), per-table modules, soft delete with registries,init_dbwith indexes andANALYZE. No async driver and no threadpool around database calls: the synchronous layer is called directly from async handlers (section 7.1). Only CPU and file work (PBKDF2, archive building, DNS) goes throughasyncio.to_thread.molodetz/routers/: one module per surface. Every HTML route returns JSON onAccept: application/jsonthroughresponses.respondand a Pydantic output schema fromschemas/.routers/legacy.pyanswers the old Dutch paths with 301 (308 for POST), hidden from the schema.molodetz/templating.py: the onlyJinja2Templates. Globalsstatic_url,local_dt,avatar_url,render_contentand more.molodetz/rendering.py: server-side markdown (mistune, escaping on), emoji, URL allowlist, dash normalisation. Published content is always rendered on the server.molodetz/stealth.py: the only factory for outbound HTTP clients (httpx with curl_cffi transport,net_guardSSRF protection).link_check.pyuses it for the repo link check on join requests.molodetz/services/: background services (presence, housekeeping, backup) under aServiceManager, visible and controllable at/admin/services.molodetz/gallery.py: flyers and memes fromstatic/media/, Pillow thumbnails (webp) as blobs underdata/uploads, perceptual hash (imagehash) against duplicates.molodetz/cli/: argparse CLImolodetz.molodetz/static/js/: ES6 modules without a bundler.Application.jsloadscomponents/index.js(custom elements in light DOM) and puts singletons onwindow.app. Vendor: marked, DOMPurify, highlight.js as ES modules instatic/vendor/.molodetz/static/css/: handwritten, tokens invariables.css, breakpoints exactly 360/480/768/1024.
Auth
Session cookie (HttpOnly, SameSite=Lax, Secure behind TLS), rows in sessions. Also X-API-KEY, Authorization: Bearer <api key> and Basic for scripts. Passwords with passlib PBKDF2-SHA256. No JWT, no OAuth. The first user is the administrator from .env; there is no public registration.
Data flows
- Writing: administrator ->
/admin/posts->content.create_post/edit_post-> SQLite -> server render on read (lru cache on text). - Joining: visitor ->
POST /join(honeypot, rate limit) ->join_requests-> notification to administrators ->/admin/joins. - Gallery: startup ->
sync_gallery->mediatable + thumbnails ->/flyers,/memeswith lightbox. - Language: startup ->
bootstrap.migrate_to_english(once, guarded by settingcontent_language) -> seeded posts, topics, tagline, maintenance message, notifications and admin bio translated in place. Old post slugs resolve through their uid tail and redirect with 301.