Repository navigation
feat: cookie auth, generated client with Pinia Colada, hybrid SSR and agent tooling - #4
Merged
Merged
Conversation
…ests Login, refresh, logout and logout-all set and clear the HttpOnly `access_token` (path /) and `refresh_token` (path /api/v1/auth) cookies; no token is ever returned in a body. Login returns the user, their organizations and the cookie expiry; refresh reads only the refresh cookie and returns the expiry; logout and logout-all answer 204; `GET /auth/me` returns the user with their organizations. Credentials resolve in order: access cookie, `Authorization: Token`, `Authorization: Bearer` (kept for tests and server-side scripts). Unsafe requests that rely on the auth cookies need a trusted Origin (or Referer): the API host or CORS_ORIGINS, else 403. Header-authenticated and safe requests are exempt, so SSR can forward the Cookie header on GETs. Websockets share the trusted-origin check. Access tokens now live 15 minutes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`POST /organization-users` required the target organization to come from the `Organization-Key` header, so adding a member from an organization's page used the active organization instead of the one being viewed. The body now carries a required `organization_id`; the actor must belong to it (or be a superuser) and an unknown organization is 404. The read schema exposes `organization_id` as well. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- operationIds `<tag>_<route name>` (organizations_list, auth_login, ...) - every API operation documents the error envelope (ErrorResponse) for 400/401/403/404/409/422/429/500 - security schemes: access cookie, `Authorization: Token`, Bearer JWT; public auth routes declare none - `Organization-Key` declared as an optional header on viewsets that use it (organization-scoped models, organization assigned from the header) - list responses named `<ReadSchema>Page`; user token create answers 201 - temporary split of ronin's combined PUT/PATCH route into `update` and `partial_update` (fixed upstream on fix/split-put-patch-operations) - `manage.py openapi [--output]` dumps the schema offline; `make openapi` writes frontend/openapi.json; a test keeps the committed file current Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tions Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hey-api/openapi-ts (pinned) writes types, SDK functions and the fetch client into app/shared/api/generated. `pnpm gen:api` regenerates and formats it, `pnpm gen:api:check` fails when the committed output is stale and runs in `pnpm check`; `make gen-client` chains `make openapi` and `gen:api`. Generated code is excluded from oxlint, knip and the architecture guard but still typechecked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…a layer The browser talks to FastAPI on its own origin (Caddy in production, a nuxt dev proxy for /api/v1 and /ws in development), so the HttpOnly session cookies and the CSRF Origin check work unchanged. plugins/api.ts builds the generated fetch client per app instance: Organization-Key on every request, the request cookie forwarded to NUXT_API_INTERNAL_URL during SSR, and in the browser a single-flight POST /auth/refresh plus one replay on 401; a failed refresh signs out and sends app routes to /auth?redirect=. Features describe each entity once with defineResource (generated SDK functions, form mapping typed against the request schemas, query-key root). AppListTemplate, AppDetailTemplate, AppSelect and filter fields run on Pinia Colada: infinite lists under [key, 'list', params], records under [key, id], mutations invalidate the resource prefix. useSession replaces the base-auth module and the Nitro auth proxy; useSetupStore restores the organization from the session. App routes render client-only via routeRules. Members are added to the organization being viewed (organization_id in the body). Removed: modules/base-auth, server/, the repo toolkit, useRepo, codecs, useRepoData, user-context plugin, NUXT_PUBLIC_BASE_URL. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/ is the template's public page placeholder: product name from app.config, placeholder copy, a sign-in link (or a link into the app for a signed-in visitor) and useSeoMeta title/description/og tags. It renders on the server with the forwarded session cookie; the session is serialized in the payload, so it hydrates without a mismatch with or without a session. The image smoke test checks the og tags and no longer sets NUXT_PUBLIC_BASE_URL. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
frontend-concept: generated client, resource descriptor, query keys and invalidation, session and page meta guards, SSR rules for public pages, realtime, adding a feature end to end. architecture: frontend tree and request flow without base-auth and the Nitro proxy. README/deployment: make gen-client, NUXT_API_INTERNAL_URL instead of NUXT_PUBLIC_BASE_URL. product: public page, silent session renewal, sign out everywhere. roadmap: stage 5 done, resolved follow-ups removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Models come from app/domains/*/models.py and routers from the `routers` (or `router`) export of each views.py. Kernel domains keep their order so openapi.json is unchanged; import errors raise DomainDiscoveryError. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scripts/scaffold/new_domain.py generates an organization-scoped CRUD entity that mirrors the organization code: backend models/schemas/filters/views/ tests and the frontend resource, list/detail views, settings pages. The settings drawer becomes data (app/navigation/settings.ts) so the scaffolder can append its entry. Tooling tests run in check-backend. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scripts/bootstrap/init.py renames the project across the greppable surface, writes .env with fresh SECRET_KEY/ENCRYPTION_KEY, and on the first run resets versions to 0.1.0, empties the changelog, resets docs/product.md to the product skeleton and records the template version/commit in .template-version. Idempotent; refuses a dirty tree unless --force. Also adds make createsuperuser. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
.claude/settings.json wires a PreToolUse hook that blocks make m/mm/migrate/ make-migrations and tortoise migrate/makemigrations unless prefixed with ALLOW_MIGRATIONS=1, and a PostToolUse hook that formats only the edited backend/frontend file (ruff, vp fmt) without ever failing the edit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/template-core.md lists the capabilities every product inherits; docs/product.md becomes the product-owned spec (purpose, personas, scenarios, non-goals) and wins on conflict. docs/template-ownership.md says which paths forks own and how a template sync resolves conflicts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Skills: new-domain, verify, design-system, add-shadcn-component, sync-template, release — short checklists that link to the docs. Subagents: domain-reviewer (backend rules) and ui-reviewer (browser pass). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ion hook Writing a README through a here-document that mentions `make m` was blocked; here-document bodies are data, and newlines inside quotes do not start a new command. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…admap README gains "Start a new product", "Adding a domain" and "Agent tooling"; architecture.md describes the .claude tooling, scaffolder and bootstrap. Stage 7 is done, so the roadmap moves to docs/archive/roadmap-2026.md and the open items to docs/follow-ups.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ness probe A readiness probe timing out while the first query lazily created the asyncpg pool left the pool half-built, so every later query hung until the process restarted. Dev Makefile targets now run through uv, so they no longer pick up a system Python. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stages 5 and 7 of the template modernization (plan archived in docs/archive/roadmap-2026.md; open items in docs/follow-ups.md).
access_tokenpath/15 min,refresh_tokenpath/api/v1/auth30 days); CSRF Origin check for cookie-authenticated unsafe requests; websocket auth from the cookie; stable OpenAPI (<tag>_<action>operationIds, security schemes, documented error envelope,make openapi→frontend/openapi.jsonwith a staleness test); members are added to the organization named in the body. Temporary split of ronin's combined PUT/PATCH route until the fixedfastapi-roninis released.@hey-api/openapi-tsclient (make gen-client,gen:api:checkinpnpm check), Pinia Colada with typed resource descriptors behindAppListTemplate/AppDetailTemplate,useSession()with single-flight refresh on 401, Nitro auth proxy and the repo toolkit removed, hybrid rendering (routeRules: app routes client-only,/is an SSR public page with SEO/OG), same-origin dev proxy for/api/v1and/ws.make new-domainscaffolder (backend domain + tests, frontend feature, pages, data-driven settings nav);make initbootstrap for new products (rename, fresh secrets, version reset,.template-version);docs/template-core.mdvsdocs/product.md;docs/template-ownership.md+/sync-template;.claude/skills (new-domain,verify,design-system,add-shadcn-component,sync-template,release), review subagents and hooks (block migrations unlessALLOW_MIGRATIONS=1, format edited files); VS Code settings for Oxc/Ruff.uv.Breaking changes for forks
Authorization: Token)./to/home, setup to/home/setup.POST /organization-usersrequiresorganization_idin the body;user-tokenscreate returns 201; logout returns 204.Test plan
make checkagainst PostgreSQL: 186 backend tests, 65 tooling tests, frontend oxfmt/oxlint/ESLint/typecheck/67 vitest/knip/architecture guard/generated client up to datepnpm buildmake initverified on a scratch clone🤖 Generated with Claude Code