docs: update features and technical stack

This commit is contained in:
2026-04-25 22:20:46 +02:00
parent c46d53b430
commit 8859c36d4b

View File

@@ -36,13 +36,13 @@ Technically, I also wanted to prove that a small, server-rendered Go app could s
## What the application offers ## What the application offers
For my own workflow, MyAnimeList combines catalog browsing, seasonal discovery, quick search, detail pages with recommendations and relations, full watchlist management, continue-watching, and in-app playback in one server-rendered interface. For my own workflow, MyAnimeList combines catalog browsing, seasonal discovery, quick search, detail pages with recommendations and relations, watchlist management, continue-watching, and in-app playback in one server-rendered interface.
Authentication in the web UI is login-only. The interface is minimal and functional, featuring a dark theme and quick access to tracking tools.
## Technical approach ## Technical approach
The application is written in Go and rendered on the server with `templ`, with SQLite as the primary datastore and `sqlc` for typed query generation. HTMX and small JavaScript modules handle incremental interactions, which keeps the interface responsive without moving the entire product into a heavy client-side architecture. The application is written in Go and rendered on the server with `templ`, with SQLite as the primary datastore and `sqlc` for typed query generation. HTMX and small TypeScript modules handle incremental interactions, which keeps the interface responsive without moving the entire product into a heavy client-side architecture.
The external anime data source is Jikan (`https://api.jikan.moe/v4`). Because reliability is a first-class concern, the client layer includes request pacing, bounded retries, backoff behavior, stale-cache fallback, and a persisted retry queue for failed fetches that should be retried later. The external anime data source is Jikan (`https://api.jikan.moe/v4`). Because reliability is a first-class concern, the client layer includes request pacing, bounded retries, backoff behavior, stale-cache fallback, and a persisted retry queue for failed fetches that should be retried later.
@@ -55,9 +55,9 @@ The codebase follows standard Go project layout conventions with clear separatio
| Path | Purpose | | Path | Purpose |
| --- | --- | | --- | --- |
| `api/anime` | Catalog, discovery, search, details, recommendations, and relations | | `api/anime` | Catalog, discovery, search, details, recommendations, and relations |
| `api/auth` | Login/session handling and auth service logic | | `api/auth` | Login and session handling logic |
| `api/playback` | Watch page, stream/subtitle proxying, and watch progress APIs | | `api/playback` | Watch page, stream/subtitle proxying, and watch progress APIs |
| `api/watchlist` | Watchlist updates, retrieval, import/export, and continue-watching | | `api/watchlist` | Watchlist updates, retrieval, and continue-watching |
### External Integrations ### External Integrations
@@ -72,7 +72,7 @@ The codebase follows standard Go project layout conventions with clear separatio
| --- | --- | | --- | --- |
| `cmd/server` | Application entrypoint and process lifecycle setup | | `cmd/server` | Application entrypoint and process lifecycle setup |
| `internal/db` | Migration runner, generated query layer, and DB models | | `internal/db` | Migration runner, generated query layer, and DB models |
| `internal/middleware` | App-specific auth and access control middleware | | `internal/middleware` | App-specific auth and access policy middleware |
| `internal/server` | Route registration and middleware composition | | `internal/server` | Route registration and middleware composition |
| `internal/worker` | Background relation sync, retry processing, and cache cleanup | | `internal/worker` | Background relation sync, retry processing, and cache cleanup |
@@ -103,7 +103,7 @@ The codebase follows standard Go project layout conventions with clear separatio
On startup, the server opens SQLite using `DATABASE_FILE` (defaulting to `mal.db`), runs migrations automatically, initializes core services, starts the background worker, and then serves HTTP traffic on `PORT` (defaulting to `3000`). A request enters the router, passes through global middleware for origin and auth boundaries, reaches a feature handler, and then resolves through service logic that combines database access with upstream data where needed before rendering HTML. On startup, the server opens SQLite using `DATABASE_FILE` (defaulting to `mal.db`), runs migrations automatically, initializes core services, starts the background worker, and then serves HTTP traffic on `PORT` (defaulting to `3000`). A request enters the router, passes through global middleware for origin and auth boundaries, reaches a feature handler, and then resolves through service logic that combines database access with upstream data where needed before rendering HTML.
Public access is intentionally limited. Only `/login` and static asset routes (`/static/*`, `/dist/*`) are available without authentication; all other routes require a valid session. Public access is restricted. Only `/login` and static asset routes (`/static/*`, `/dist/*`) are available without authentication; all other routes require a valid session. There is no public registration; users must be seeded directly in the database.
The background worker continuously maintains relation data for sequel awareness, processes queued retryable anime fetches, and periodically removes expired cache records. This keeps user-facing pages stable even when data collection has to happen in multiple phases. The background worker continuously maintains relation data for sequel awareness, processes queued retryable anime fetches, and periodically removes expired cache records. This keeps user-facing pages stable even when data collection has to happen in multiple phases.
@@ -111,7 +111,7 @@ The background worker continuously maintains relation data for sequel awareness,
The hardest part has been balancing freshness and resilience. Upstream APIs can fail transiently with `429` and `5xx` responses, so the app favors graceful degradation over hard failure. Cached values are used when fresh requests fail, retryable failures are persisted and replayed in the worker, and relation synchronization is incremental so one bad fetch does not block the rest of the graph. The hardest part has been balancing freshness and resilience. Upstream APIs can fail transiently with `429` and `5xx` responses, so the app favors graceful degradation over hard failure. Cached values are used when fresh requests fail, retryable failures are persisted and replayed in the worker, and relation synchronization is incremental so one bad fetch does not block the rest of the graph.
There are still honest limits. Metadata quality still depends partly on external providers, and there is also no formal CI pipeline yet, so local validation is currently the main quality gate. There are still honest limits. Metadata quality depends on external providers, and there is no formal CI pipeline yet, so local validation is the primary quality gate.
## Getting started ## Getting started