Backend (FastAPI): - App factory with async SQLAlchemy 2.0 + PostgreSQL - Alembic migration for users and sessions tables - JWT auth (access + refresh tokens, bcrypt passwords) - Auth endpoints: register, login, refresh, logout, me - Admin seed script, role-based access deps Frontend (React + TypeScript): - Vite + Tailwind CSS + shadcn/ui theme (health-oriented palette) - i18n with English and Russian translations - Zustand auth/UI stores with localStorage persistence - Axios client with automatic token refresh on 401 - Login/register pages, protected routing - App layout: collapsible sidebar, header with theme/language toggles - Dashboard with placeholder stats Infrastructure: - Docker Compose (postgres, backend, frontend, nginx) - Nginx reverse proxy with WebSocket support - Dev override with hot reload Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
14 KiB
Phase 1: Foundation — Subplan
Goal
Stand up the monorepo skeleton with Docker Compose orchestration, a working FastAPI backend with database migrations and JWT authentication, a React frontend shell with auth pages and protected routing, and an admin seed script — so that a user can register, log in, see a dashboard shell, and an admin account exists.
Prerequisites
- Git repository initialized (done)
- Docker and Docker Compose installed on the development machine
- Node.js 20+ and Python 3.12+ available locally for development outside containers
GeneralPlan.md,README.md,.gitignorealready in repo (done)
Database Schema (Phase 1)
users table
| Column | Type | Constraints |
|---|---|---|
| id | UUID | PK, default gen_random_uuid() |
| VARCHAR(255) | UNIQUE, NOT NULL | |
| username | VARCHAR(100) | UNIQUE, NOT NULL |
| hashed_password | VARCHAR(255) | NOT NULL |
| full_name | VARCHAR(255) | NULL |
| role | VARCHAR(20) | NOT NULL, default 'user', CHECK IN ('user','admin') |
| is_active | BOOLEAN | NOT NULL, default true |
| max_chats | INTEGER | NOT NULL, default 10 |
| oauth_provider | VARCHAR(50) | NULL |
| oauth_provider_id | VARCHAR(255) | NULL |
| telegram_chat_id | BIGINT | NULL |
| avatar_url | VARCHAR(500) | NULL |
| created_at | TIMESTAMPTZ | NOT NULL, default now() |
| updated_at | TIMESTAMPTZ | NOT NULL, default now(), on-update trigger |
Indexes: ix_users_email (unique), ix_users_username (unique).
sessions table
| Column | Type | Constraints |
|---|---|---|
| id | UUID | PK, default gen_random_uuid() |
| user_id | UUID | FK -> users.id ON DELETE CASCADE, NOT NULL |
| refresh_token_hash | VARCHAR(255) | NOT NULL |
| device_info | VARCHAR(500) | NULL |
| ip_address | VARCHAR(45) | NULL |
| expires_at | TIMESTAMPTZ | NOT NULL |
| created_at | TIMESTAMPTZ | NOT NULL, default now() |
Indexes: ix_sessions_user_id, ix_sessions_refresh_token_hash.
API Endpoint Signatures (Phase 1 Auth)
All under /api/v1/auth/.
| Method | Path | Request Body | Response | Notes |
|---|---|---|---|---|
| POST | /register |
{ email, username, password, full_name? } |
{ user, access_token, refresh_token } |
Creates user + session. 201 |
| POST | /login |
{ email, password, remember_me?: bool } |
{ user, access_token, refresh_token } |
remember_me controls refresh expiry (30d vs 24h). 200 |
| POST | /refresh |
{ refresh_token } |
{ access_token, refresh_token } |
Rotates refresh token (old invalidated). 200 |
| POST | /logout |
{ refresh_token } |
204 No Content |
Deletes session row |
| GET | /me |
— | UserResponse |
Requires valid access token. 200 |
Token details:
- Access token: JWT, HS256, 15-minute expiry, payload:
{ sub: user_id, role, exp, iat, jti } - Refresh token: opaque 64-byte hex string; only its SHA-256 hash is stored in
sessions.refresh_token_hash - Password hashing: bcrypt via
passlib[bcrypt]
Frontend Route Structure (Phase 1)
| Path | Component | Auth Required | Notes |
|---|---|---|---|
/login |
LoginPage | No | Redirects to / if already authenticated |
/register |
RegisterPage | No | Redirects to / if already authenticated |
/ |
DashboardPage | Yes | Wrapped in AppLayout (sidebar + header) |
* |
NotFoundPage | No | 404 fallback |
Tasks
A. Project Infrastructure & Docker (Tasks 1–6)
-
A1. Create root-level config files:
.env.example(all env vars:POSTGRES_USER,POSTGRES_PASSWORD,POSTGRES_DB,SECRET_KEY,BACKEND_CORS_ORIGINS,FIRST_ADMIN_EMAIL,FIRST_ADMIN_PASSWORD,FIRST_ADMIN_USERNAME),docker-compose.yml,docker-compose.dev.yml(volume mounts + hot reload). -
A2. Create
nginx/nginx.confwith reverse proxy rules:/api/*→backend:8000,/→frontend:80. Include WebSocket upgrade headers for future/ws/*. Add gzip, security headers,client_max_body_size 50M. -
A. Create
nginx/Dockerfile(FROM nginx:1.25-alpine, copy nginx.conf). -
A. Create
backend/Dockerfile(Python 3.12-slim, install deps from pyproject.toml, run uvicorn). Createbackend/pyproject.tomlwith dependencies: fastapi, uvicorn[standard], sqlalchemy[asyncio], asyncpg, alembic, pydantic-settings, passlib[bcrypt], python-jose[cryptography], python-multipart, httpx. Dev deps: pytest, pytest-asyncio, httpx. -
A. Create
frontend/Dockerfile(multi-stage: Node 20-alpine build stage + nginx:alpine serve stage). Createfrontend/package.jsonwith dependencies: react, react-dom, react-router-dom, axios, zustand, @tanstack/react-query, i18next, react-i18next, i18next-http-backend, i18next-browser-languagedetector, lucide-react, clsx, tailwind-merge, class-variance-authority. Dev deps: vite, @vitejs/plugin-react, typescript, tailwindcss, postcss, autoprefixer, @types/react, @types/react-dom. -
A. Verify
docker compose buildsucceeds for all services anddocker compose up -dstarts postgres, backend, frontend, nginx. Backend health endpoint responds at/api/v1/health. Frontend serves through nginx.
B. Backend Core Infrastructure (Tasks 7–13)
-
B. Create
backend/app/__init__.py,backend/app/config.pyusing pydantic-settingsBaseSettings. Fields:DATABASE_URL,SECRET_KEY,ACCESS_TOKEN_EXPIRE_MINUTES(15),REFRESH_TOKEN_EXPIRE_DAYS(30),REFRESH_TOKEN_EXPIRE_HOURS(24),CORS_ORIGINS,ENVIRONMENT. -
B. Create
backend/app/database.py: async engine,async_sessionmaker,get_dbasync generator,Base = declarative_base()with UUID pk mixin. -
B. Create
backend/app/models/__init__.py,backend/app/models/user.py,backend/app/models/session.pymatching the schema above. SQLAlchemy 2.0mapped_columnstyle.updated_atwithonupdate=func.now(). -
B0. Set up Alembic:
backend/alembic.ini,backend/alembic/env.py(async-compatible),backend/alembic/script.py.mako. Generate initial migration for users + sessions. -
B1. Create
backend/app/main.py:create_app()factory. Lifespan runs Alembicupgrade headon startup. CORS middleware. Include API routers./api/v1/healthendpoint. -
B2. Create
backend/app/core/__init__.py,backend/app/core/security.py:hash_password,verify_password(bcrypt),create_access_token,decode_access_token,generate_refresh_token,hash_refresh_token(SHA-256). -
B3. Create
backend/app/schemas/__init__.py,backend/app/schemas/auth.py:RegisterRequest,LoginRequest(withremember_me),RefreshRequest,TokenResponse,UserResponse. Createbackend/app/schemas/common.py:MessageResponse.
C. Backend Auth Logic (Tasks 14–18)
-
C4. Create
backend/app/services/__init__.py,backend/app/services/auth_service.py:register_user,login_user,refresh_tokens,logout_user. Each manages session rows. Raises HTTPException on errors. -
C5. Create
backend/app/api/__init__.py,backend/app/api/deps.py:get_current_userdependency (JWT → User),require_admindependency. -
C6. Create
backend/app/api/v1/__init__.py,backend/app/api/v1/auth.pyrouter with 5 auth endpoints. -
C7. Create
backend/app/api/v1/router.pyto aggregate v1 sub-routers under/api/v1. Include inmain.py. -
C8. Create
backend/scripts/seed_admin.py: standalone async script, reads env, creates admin user if not exists. Idempotent.
D. Frontend Infrastructure (Tasks 19–27)
-
D9. Create
frontend/vite.config.ts(React plugin, proxy/apito backend in dev),frontend/tsconfig.json,frontend/tsconfig.node.jsonwith@/path alias. -
D0. Set up Tailwind CSS:
frontend/tailwind.config.ts,frontend/postcss.config.js,frontend/src/index.css(Tailwind directives + shadcn CSS variables for dark mode). -
D1. Initialize shadcn/ui:
frontend/components.json. Install components: Button, Input, Label, Card, DropdownMenu, Avatar, Sheet, Separator, Sonner. Place infrontend/src/components/ui/. -
D2. Set up i18next:
frontend/src/i18n.ts,frontend/public/locales/en/translation.json,frontend/public/locales/ru/translation.jsonwith keys for auth, layout, and common strings. -
D3. Create
frontend/src/api/client.ts: Axios instance, request interceptor (attach token), response interceptor (401 → refresh → retry). Createfrontend/src/api/auth.tswith typed API functions. -
D4. Create
frontend/src/stores/auth-store.ts(Zustand, persisted): user, tokens, isAuthenticated, actions. Createfrontend/src/stores/ui-store.ts: sidebarOpen, theme, persisted. -
D5. Create
frontend/src/components/shared/theme-provider.tsx,language-toggle.tsx,protected-route.tsx. -
D6. Create
frontend/src/App.tsx,frontend/src/routes.tsx(React Router v6),frontend/src/main.tsxentry point. -
D7. Set up TanStack Query:
frontend/src/lib/query-client.ts. WireQueryClientProviderin App.
E. Frontend Auth Pages & Layout (Tasks 28–33)
-
E8. Create
frontend/src/components/auth/login-form.tsx: email + password + remember me + submit. Calls API, stores tokens, navigates to/. -
E9. Create
frontend/src/components/auth/register-form.tsx: email, username, password, confirm password, full_name. Validation: email format, password min 8, passwords match, username 3-50 chars. -
E0. Create
frontend/src/pages/login.tsxandfrontend/src/pages/register.tsx: centered card layout, redirect if authenticated. -
E1. Create
frontend/src/components/layout/app-layout.tsx(sidebar + main content),frontend/src/components/layout/sidebar.tsx(collapsible nav, placeholder items, mobile Sheet drawer). -
E2. Create
frontend/src/components/layout/header.tsx: sidebar toggle, page title, language toggle, theme toggle, user dropdown with logout. -
E3. Create
frontend/src/pages/dashboard.tsx(welcome card, placeholder stats),frontend/src/pages/not-found.tsx(404).
F. Integration & Verification (Tasks 34–36)
-
F3. Create
frontend/nginx.conffor SPA fallback. Ensure multi-stage Dockerfile copies build output and nginx config. -
F35. End-to-end smoke test:
docker compose up --build. Verify: postgres + migrations, health endpoint, seed_admin, frontend loads, register, login, dashboard, logout, language toggle, theme toggle. -
F3. Write backend tests:
backend/tests/conftest.py(async test client),backend/tests/test_auth.py(register, login, refresh, logout, invalid credentials, duplicate email, expired token). All pass withpytest.
Files to Create
Root
.env.exampledocker-compose.ymldocker-compose.dev.yml
nginx/
nginx/Dockerfilenginx/nginx.conf
backend/
backend/Dockerfilebackend/pyproject.tomlbackend/alembic.inibackend/alembic/env.pybackend/alembic/script.py.makobackend/alembic/versions/<initial_migration>.pybackend/app/__init__.pybackend/app/main.pybackend/app/config.pybackend/app/database.pybackend/app/models/__init__.pybackend/app/models/user.pybackend/app/models/session.pybackend/app/schemas/__init__.pybackend/app/schemas/auth.pybackend/app/schemas/common.pybackend/app/core/__init__.pybackend/app/core/security.pybackend/app/services/__init__.pybackend/app/services/auth_service.pybackend/app/api/__init__.pybackend/app/api/deps.pybackend/app/api/v1/__init__.pybackend/app/api/v1/router.pybackend/app/api/v1/auth.pybackend/scripts/__init__.pybackend/scripts/seed_admin.pybackend/tests/__init__.pybackend/tests/conftest.pybackend/tests/test_auth.py
frontend/
frontend/Dockerfilefrontend/nginx.conffrontend/package.jsonfrontend/vite.config.tsfrontend/tsconfig.jsonfrontend/tsconfig.node.jsonfrontend/tailwind.config.tsfrontend/postcss.config.jsfrontend/components.jsonfrontend/index.htmlfrontend/src/main.tsxfrontend/src/App.tsxfrontend/src/routes.tsxfrontend/src/index.cssfrontend/src/i18n.tsfrontend/src/vite-env.d.tsfrontend/src/lib/query-client.tsfrontend/src/lib/utils.tsfrontend/src/api/client.tsfrontend/src/api/auth.tsfrontend/src/stores/auth-store.tsfrontend/src/stores/ui-store.tsfrontend/src/components/ui/(shadcn components)frontend/src/components/shared/theme-provider.tsxfrontend/src/components/shared/language-toggle.tsxfrontend/src/components/shared/protected-route.tsxfrontend/src/components/auth/login-form.tsxfrontend/src/components/auth/register-form.tsxfrontend/src/components/layout/app-layout.tsxfrontend/src/components/layout/sidebar.tsxfrontend/src/components/layout/header.tsxfrontend/src/pages/login.tsxfrontend/src/pages/register.tsxfrontend/src/pages/dashboard.tsxfrontend/src/pages/not-found.tsxfrontend/public/locales/en/translation.jsonfrontend/public/locales/ru/translation.json
Acceptance Criteria
docker compose up --buildstarts all 4 services (postgres, backend, frontend, nginx) without errorsGET /api/v1/healthreturns{"status": "ok"}- Alembic migration creates
usersandsessionstables with correct schema seed_admin.pycreates an admin user; re-running is idempotentPOST /api/v1/auth/registercreates a new user and returns tokensPOST /api/v1/auth/loginwith valid credentials returns tokens;remember_me=true→ 30-day refresh,false→ 24-hourPOST /api/v1/auth/refreshrotates refresh token and returns new pairPOST /api/v1/auth/logoutinvalidates the sessionGET /api/v1/auth/mewith valid token returns user data; expired/invalid → 401- Frontend loads at
http://localhost, unauthenticated users see login page - User can register, log in, and see dashboard with their name
- Sidebar renders with placeholder navigation; header has theme toggle, language toggle, user menu with logout
- Dark/light theme toggle works (persisted across reload)
- Language toggle switches between English and Russian (persisted across reload)
- Axios interceptor auto-refreshes access token on 401 and retries the request
- All backend auth tests pass (
pytest)
Status
IN PROGRESS — All code written. F35 (Docker smoke test) blocked: Docker not installed on this machine. Install Docker Desktop to complete end-to-end verification.