跳到正文
cccskills
免费在 GitHub 公开

add-route

Add or modify a Flask endpoint in the Lyrion Dashboard. Use this whenever a task adds a new URL/route, a JSON API endpoint, or a page — anything that registers a handler in `routes/`. Covers the repo's route → service → test layering, input validation, caching headers, security headers, i18n wiring, and the exact test pattern, so a new endpoint matches how `routes/` already works instead of reinventing the structure.

中文简介正在准备中。当前显示原文说明。

查看安装方法

包含的文件(1)

  • SKILL.md5.8 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

Adding a Flask endpoint

This app layers strictly: routes parse the request and shape the response, services do the work (Lyrion calls, DB reads, lyrics), tests cover the route's contract. A route should read like a thin adapter — if a handler grows real logic, that logic belongs in a services/ module.

Where things go

  • routes/nowplaying.py — the dashboard and its JSON endpoints (/, /now-playing.json, /cover/..., /stats.json, /lyrics.json, ...). Registered as nowplaying_bp.
  • routes/custom.py — the /files/ static server. Registered as custom_bp.
  • Blueprints are created in the route module and registered in app.py's create_app(). A brand-new blueprint must be both created and register_blueprint-ed there; adding a route to an existing blueprint needs no app.py change.

The pattern

Handlers import service functions and translations at the top of the module — they do not build Lyrion payloads or open the DB inline:

from services.lyrion import get_active_now_playing
from services.database import get_stats

@nowplaying_bp.route("/stats.json")
def stats_json():
    return jsonify(get_stats())

For anything with new domain logic, add a function to the relevant services/ module first (see the lyrion-api skill for Lyrion calls, services/database.py for reads), then call it from the route.

Validate untrusted input, always

Every value from the client is validated before it reaches a service or an upstream URL. The module keeps compiled regexes for this and clamps numbers:

COVERID_RE = re.compile(r"[0-9a-fA-F]+")

@nowplaying_bp.route("/cover/<coverid>.jpg")
def cover(coverid):
    if not COVERID_RE.fullmatch(coverid):
        abort(404)
    size = request.args.get("size", type=int)
    if size is not None:
        size = min(max(size, 16), 512)   # clamp, don't trust
  • Match path/query params against a fullmatch regex; abort(404) on junk.
  • Clamp numeric ?limit= / ?size= params with min(max(...)) so a hostile value can't blow up a query or a fetch (see mosaic_covers_json).
  • Anything that fans out to a third party (the web lyrics search) sits behind services.ratelimit — RateLimiter per IP and Cooldown per track. Reuse those, don't roll your own throttle.

Response conventions

  • JSON endpoints return jsonify(...). Keep the endpoint name suffix .json.
  • Cacheable binary responses set Cache-Control explicitly, e.g. covers use headers={"Cache-Control": "public, max-age=86400"}.
  • Security headers are applied app-wide by set_security_headers in app.py via setdefault (CSP default-src 'self', nosniff, SAMEORIGIN). You get them for free. Only set a header on the response yourself to override the default for one route — as /files/ does with Content-Security-Policy: sandbox for untrusted files.

i18n for pages (not JSON)

HTML pages pick the language per-request and hand the whole translation dict to the template. JSON endpoints don't localize. See the i18n skill; the wiring is:

from i18n import pick_lang, TRANSLATIONS

lang = pick_lang(request.accept_languages)
return render_template("nowplaying.html", lang=lang, t=TRANSLATIONS[lang], ...)

Any new user-facing string added to a template needs a key in both fr and en in i18n.py.

Test the route's contract

Every endpoint gets a tests/test_<name>_route.py using unittest + unittest.mock.patch. The setup is deliberately duplicated per file so each stands alone (# pylint: disable=duplicate-code):

  1. Set LYRION_HOST, DB_DIR, DB_PERSIST_DIR env vars via os.environ.setdefault before importing app (config reads them at import).
  2. Build a client with create_app().test_client().
  3. Patch the service functions the route imports — patch them at the route module path, e.g. @patch("routes.nowplaying.get_track_lyrics"), never the real Lyrion server or DB.
  4. Assert on status, JSON body, and that services were called with the right args (mock.assert_called_with(...)).

tests/test_now_playing_route.py is the reference example — mirror its shape.

Keep the docs in sync

The public surface is documented in the READMEs and under docs/, and they must not drift from the app. Each page is an EN/FR pair edited together (see the i18n skill's documentation parity rule):

  • New/changed route → the Endpoints table in docs/endpoints.md (Method / Route / Description).
  • New config env var the route relies on → the table in docs/configuration.md, the single place operators discover it.
  • User-visible feature → the Features list in the README.

Checklist

  1. Put domain logic in a services/ function; keep the handler thin.
  2. Register the route on the right blueprint (new blueprint → wire it in app.py).
  3. Validate/clamp every client input; abort() on bad input.
  4. Set Cache-Control on cacheable responses; rely on the global security headers unless overriding.
  5. Localize page strings in both fr and en; leave JSON unlocalized.
  6. Add tests/test_<name>_route.py patching the imported services.
  7. Public surface changed? Update docs/endpoints.md, docs/configuration.md or the README Features list — each in both languages.
  8. Run python -m unittest discover and pylint app.py config.py i18n.py routes services scripts tests — both gate CI (.github/workflows/python-ci.yml).

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

android

免费

Work on the Android app — the thin WebView wrapper in `android/`. Use this whenever a task touches the Kotlin sources, the Gradle build, the WebView shell, the native settings screen, LMS server auto-discovery, the JS↔native bridge, app strings/i18n, or the Android CI/lint. Covers the "shell only, UI lives on the web" principle, the debug/release signing split, the static versioning F-Droid needs, and the min/target SDKs, so changes fit the wrapper.

中文简介正在准备中。当前显示原文说明。

werdeil/lyrion-dashboard32026年10月20日 更新

covers

免费

Work on album artwork — reading an image's dimensions, and embedding a folder's cover file into the tags of its tracks. Use this whenever a task touches `services/artwork.py`, the cover half of `services/tags.py`, or `scripts/embed_covers.py` / `embed_covers_cron.sh`. Covers why Lyrion shows the embedded artwork and never the folder file, how covers are compared, why images are never re-encoded, and what embedding costs in rewritten files.

中文简介正在准备中。当前显示原文说明。

werdeil/lyrion-dashboard32026年10月20日 更新

database

免费

Read data from Lyrion's SQLite databases — library stats, stored lyrics, album covers, recent plays. Use this whenever a task queries `library.db` or `persist.db`, adds or changes a statistic, or touches `services/database.py`. Covers the read-only connection model (RO open + `ATTACH persist.db`), the key tables, the `play_counts` view over the Alternative Play Count plugin, the single-flight stats cache, and why the app must never write to these DBs. Reach for it before writing any SQL against the Lyrion databases.

中文简介正在准备中。当前显示原文说明。

werdeil/lyrion-dashboard32026年10月20日 更新

frontend

免费

Work on the dashboard's frontend — the vanilla JS/CSS page with no framework or bundler. Use this whenever a task touches `static/nowplaying.js`, `static/style.css`, the Jinja templates (`templates/nowplaying.html`, `_icons.html`), the accent-color tinting, the karaoke lyrics sync, the now-playing poll, or the empty-state mosaic / recent-plays pile. Covers how the page is wired, how it reads i18n and data from the server, the ESLint gate, and the Android bridge, so changes match the existing patterns.

中文简介正在准备中。当前显示原文说明。

werdeil/lyrion-dashboard32026年10月20日 更新

i18n

免费

Add, change, or translate user-facing text in the Lyrion Dashboard, which ships bilingual FR/EN. Use this whenever a task introduces a new visible string (a label, tooltip, aria-label, empty state, stat name), changes wording, or touches the README or a `docs/` page — so every string lands in both languages and the EN/FR documentation pairs stay in sync. Covers `i18n.py`, how templates and JS consume translations, and the parity rule.

中文简介正在准备中。当前显示原文说明。

werdeil/lyrion-dashboard32026年10月20日 更新

lyrics

免费

Work on lyrics — the web fallback (LRCLIB, Musixmatch, NetEase, Genius), the in-memory cache, verification, and embedding lyrics into audio file tags. Use this whenever a task touches `services/lyrics.py`, `services/tags.py`, the `/lyrics.json` route, or `scripts/embed_lyrics.py` / `embed_lyrics_cron.sh` — adding or fixing a provider, changing caching/verification, or the batch tagger. Covers the provider contract, the synced-vs-plain rule, the read-only cache, and why writes only ever store plain text.

中文简介正在准备中。当前显示原文说明。

werdeil/lyrion-dashboard32026年10月20日 更新

查看 werdeil 的全部技能

报告此技能的问题