Agent Skills
› lncrawl/lightnovel-crawler
› add-api-endpoint
add-api-endpoint
GitHub指导在lncrawl项目中添加或修改FastAPI端点,涵盖路由聚合、用户/管理员安全认证、DTO/DAO模型使用、分页及错误处理规范。
Trigger Scenarios
需要新增或修改API接口
涉及lncrawl/server/api/目录的代码变更
需要配置路由安全依赖
Install
npx skills add lncrawl/lightnovel-crawler --skill add-api-endpoint -g -y
SKILL.md
Frontmatter
{
"name": "add-api-endpoint",
"description": "Add or modify a FastAPI endpoint in lncrawl — router aggregation, ensure_user\/ensure_admin security, DTO vs DAO models, pagination, ServerErrors. Use when touching lncrawl\/server\/api\/, security.py, or server\/models\/."
}
Server API (lncrawl/server/api/)
Each domain is one module with a module-level router = APIRouter() and relative paths.
server/api/__init__.py aggregates them: one include_router(alias, prefix, tags, dependencies=[Security(...)]) block per module; the composite router mounts at /api in
server/app.py.
Recipe: new endpoint / router
- Add the handler to the matching
server/api/<domain>.py(or create a new module +include_routerblock in__init__.py). - Auth: router-level
dependencies=[Security(ensure_user)]covers most modules; useSecurity(ensure_admin)for admin routers. To get the caller inside a handler, add auser: User = Security(ensure_user)parameter; to tighten one route inside a looser router, use per-routedependencies=[Security(ensure_admin)]. - Delegate all logic to a service singleton (
ctx.<service>) — handlers stay thin. Return DAO SQLModel objects orserver/models/Pydantic DTOs directly; FastAPI serializes them. - Request bodies are Pydantic models in
server/models/(re-exported from its__init__.py) viaBody(...); query/path params viaQuery(...)/Path(...). - Quota/permission checks go through
ctx.tier.<check>(user)— never inline tier logic.
Security model (server/security.py)
ensure_useraccepts either HTTP Basic (email+password) or a Bearer JWT; rejects inactive users. JWT scopes = provided scopes ∪ {user.role, user.tier} (services/users.py);verify_tokenrequires allSecurity(..., scopes=[...])scopes.ensure_admin=ensure_userwith the ADMIN scope plus an explicit role check (the role check matters because the Basic-auth path bypasses scope verification).ensure_localexists but is currently used by no endpoint — don't copy it as a live pattern without checking.- WebSocket routers must not inherit HTTP security dependencies — that's why the LSP
router is included first, without a parent dependency (see the comment in
api/__init__.py). The only WebSocket is/api/lsp. - Protected static files (
/static/novels|images|sources/...) authenticate via a?token=query param (StaticFilesGuardmiddleware), not headers — that's how the web UI loads images and downloads artifacts.
Conventions
- Paths: list endpoints extend the router prefix with a bare
"s"—@router.get("s")under prefix/novelservesGET /api/novelswhile@router.get("/{novel_id}")servesGET /api/novel/{id}. Follow it for new list routes. - Pagination:
Paginated[T](server/models/pagination.py) ={total, offset, limit, items}; conventional paramsoffset: int = Query(default=0),limit: Query(default=20, le=100). When adding filters, apply the same conditions to thetotalcount query — some existing services count unfiltered totals; don't copy that. - Errors: raise the pre-instantiated singletons from
ServerErrors(lncrawl/exceptions.py), optionally.with_extra(detail). Add new error kinds to that catalog; do not hand-rollHTTPException. Exception handlers registered inapp.pyproduce{"error": ..., "detail": ...}bodies. WebSocket errors use the separateWebSocketErrorclasses. - Swagger UI at
/docs, ReDoc at/redoc, raw spec at/openapi.json;/healthis the unauthenticated liveness probe. server/web/is committed build output synced from the lncrawl-web repo'sartifactsbranch by the "Sync Web" workflow — never hand-edit it.
Version History
- b76d44a Current 2026-07-25 09:00


