diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..6e564a1 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,45 @@ +# Contributing + +Thanks for your interest in improving MAL. + +## Before you start + +- Open an issue first for large changes so scope is clear +- Keep pull requests focused and small when possible +- Prioritize user-facing clarity: cleaner flows, less friction, better defaults + +## Local setup + +```bash +# install templ CLI +go install github.com/a-h/templ/cmd/templ@latest + +# generate templates +templ generate + +# run tests +go test ./... + +# run app +go run ./cmd/server +``` + +## Development guidelines + +- Follow existing folder boundaries (`internal/features/*`, `internal/jikan`, `internal/templates`) +- Prefer simple, explicit solutions over broad abstractions +- Do not add dependencies unless there is a clear benefit +- Keep generated files in sync when changing `.templ` or SQL query definitions + +## Pull request checklist + +- Explain the user problem this change solves +- Describe tradeoffs or constraints +- Include before/after behavior notes +- Ensure `go test ./...` passes locally + +## Security + +- Never commit secrets, private tokens, or real credentials +- Keep `.env` values local +- Report security issues privately before public disclosure diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..edc23f3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 mkelvers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ffb44b1 --- /dev/null +++ b/README.md @@ -0,0 +1,262 @@ +# MAL + + + + + + +
+ MAL logo + + MAL
+ For everyone who wanted MyAnimeList-style tracking,
+ but with cleaner UI, better UX, and fewer compromises. +
+ +

+ A calmer anime tracker built for people who care about flow. +

+

+ Discover anime, track progress, follow sequels, and keep your watchlist clean without fighting noisy UI. +

+ +

+ Go + SQLite + templ + HTMX +

+ +--- + +## Why this project exists + +I built this because I genuinely disliked the UI and UX of the alternatives. + +Most anime tracking tools I tried felt cluttered, slow to navigate, or missing basic pieces I wanted in one place. I wanted a product that felt focused: search quickly, open details quickly, decide status quickly, and move on. + +This project is that answer. + +Under the hood, it is also an engineering exercise in building a reliable product on top of an unreliable upstream data source. Anime metadata APIs can rate-limit, timeout, or intermittently fail. Instead of pretending that never happens, this codebase is designed around that reality. + +## What you can do with it + +- Browse a catalog view of popular anime +- Discover airing and upcoming shows +- Search instantly from the header quick-search +- Open anime detail pages with related titles and recommendations +- Add, update, remove, import, and export watchlist entries +- Track statuses (`watching`, `completed`, `plan_to_watch`, etc.) +- Get notification-oriented views for tracking and upcoming sequels +- Register/login/logout, rotate account recovery keys, and recover accounts + +## Product philosophy + +- **Minimal friction:** fewer clicks and less visual noise +- **Practical over perfect:** fast, readable pages over heavy front-end complexity +- **Resilient by default:** graceful fallback behavior when upstream services fail +- **Honest constraints:** explicitly acknowledge tradeoffs and incomplete pieces + +## Technical overview + +This is a server-rendered Go web application with SQLite persistence, generated SQL accessors, background workers, and templ-based UI rendering. + +### Stack + +- Go `1.24` +- SQLite (`github.com/mattn/go-sqlite3`) +- `sqlc` for typed query generation +- `templ` for server-side HTML components +- HTMX + small vanilla JS modules for interactivity +- Jikan API (`https://api.jikan.moe/v4`) as primary anime data provider +- `goquery` for watch-order parsing/fallback extraction + +### Repository layout + +```text +cmd/server/ # app entrypoint +internal/server/ # route composition + middleware wiring +internal/features/anime/ # anime handlers + service logic +internal/features/watchlist/ # watchlist handlers + service logic +internal/features/auth/ # auth/session/recovery logic +internal/jikan/ # upstream API client + caching + retry paths +internal/worker/ # relation sync + retry processing + cache cleanup +internal/database/ # sqlc models/queries + migration runner +internal/templates/ # templ views and partials +migrations/ # schema evolution scripts +static/ # CSS/JS assets +``` + +## How the app works + +At startup, the server: + +1. Opens SQLite (`DATABASE_FILE`, default `mal.db`) +2. Runs migrations +3. Initializes typed DB queries, auth service, and Jikan client +4. Starts a background worker +5. Starts HTTP server on `PORT` (default `3000`) + +Request flow is intentionally straightforward: + +1. Request enters `http.ServeMux` +2. Middleware enforces origin checks and auth boundaries +3. Feature handlers call feature services +4. Services read/write SQLite and/or fetch from Jikan +5. Templ renders pages or partials + +## The hard parts (and how they are handled) + +### 1) Upstream instability + +Jikan can return `429`/`5xx`, intermittently timeout, or vary response behavior under load. + +Mitigations implemented in this codebase: + +- Request pacing to stay under known rate limits (base delay between requests) +- Retry with bounded backoff and `Retry-After` support +- Cache-first reads where possible +- Stale cache fallback if fresh fetch fails +- Persisted retry queue (`anime_fetch_retry`) for retryable failures +- Worker signaling so retries can be processed quickly after enqueue + +### 2) Keeping sequel graphs useful + +Finding upcoming sequels is not just a single lookup. The app keeps relation data updated using worker jobs and recursive SQL CTEs, then maps those relations back to your watchlist context. + +### 3) UI responsiveness without a heavy front-end framework + +The app uses server-rendered templates plus HTMX partial updates and focused JS files. The goal is immediate UI feedback with less complexity than a large SPA stack. + +### 4) Security basics done properly + +- Password rules enforce complexity and minimum length +- Password hashes use bcrypt +- Session cookies are `HttpOnly` and `SameSite=Strict` +- `Secure` cookies are enabled in production mode (`ENV=production`) +- Origin/Referer checks protect non-GET actions +- Auth routes are rate-limited per IP + +## Getting started + +### Prerequisites + +- Go `1.24+` +- SQLite +- `templ` CLI + +### Local development + +```bash +# install templ CLI +go install github.com/a-h/templ/cmd/templ@latest + +# generate templ code +templ generate + +# run server +go run ./cmd/server +``` + +Open `http://localhost:3000`. + +### Docker + +The repository includes a multi-stage `Dockerfile` that: + +- installs dependencies +- generates templ files +- builds `./cmd/server` +- ships a slim runtime image with SQLite support + +Example: + +```bash +docker build -t mal . +docker run --rm -p 3000:3000 mal +``` + +## Configuration + +| Variable | Default | Description | +| --- | --- | --- | +| `PORT` | `3000` | HTTP listen port | +| `DATABASE_FILE` | `mal.db` | SQLite database file path | +| `ENV` | _(empty)_ | Set to `production` to mark session cookies as secure | + +## Database and migrations + +Migrations run automatically on startup. + +Current migration history covers: + +- initial auth/session/watchlist schema +- anime title and airing metadata +- notifications data model +- relation graph support +- Jikan response caching +- query-performance indexes +- account recovery key support +- persisted anime fetch retry queue + +## Testing + +The repo includes tests around core behavior such as: + +- watchlist service logic +- watch-order parsing behavior +- relation helpers +- auth middleware behavior + +Run all tests: + +```bash +go test ./... +``` + +## Tradeoffs and known limitations + +- Watchlist sorting by `score` is currently a placeholder path +- External data quality and uptime still depend on Jikan/third-party sources +- There is no formal CI pipeline configured in this repository yet +- Project docs (contributing/license) are still lightweight and evolving + +## Roadmap direction + +- Complete score sorting semantics for watchlist +- Expand test coverage for handler + integration paths +- Add clearer contribution and governance docs +- Improve observability around worker retries and cache health +- Continue refining the UI for speed and clarity over visual noise + +## Development notes + +- Generated templ outputs (`*_templ.go`) are checked in +- SQL is authored in `internal/database/queries.sql` and generated through `sqlc` +- Static assets live in `static/` + +## Contributing + +Please read `CONTRIBUTING.md` before opening a pull request. + +If you want to contribute, open an issue or pull request with: + +- the user-facing problem you are solving +- the technical approach and tradeoffs +- before/after behavior notes + +Small, focused changes are preferred over broad rewrites. + +## Security and secrets + +- Do not commit real API keys or private credentials +- Keep local `.env` values out of documentation and screenshots +- If you discover a security issue, report it privately before public disclosure + +## License + +MIT. See `LICENSE`. + +--- + +If this project resonates with you, it is probably for the same reason it exists: you want anime tracking that gets out of your way and lets you focus on actually watching.