Files
personal-ai-assistant/plans/phase-1-foundation.md
dolgolyov.alexei 7c752cae6b Phase 1: Foundation — backend auth, frontend shell, Docker setup
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>
2026-03-19 12:25:02 +03:00

14 KiB
Raw Blame History

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, .gitignore already in repo (done)

Database Schema (Phase 1)

users table

Column Type Constraints
id UUID PK, default gen_random_uuid()
email 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 16)

  • 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.conf with 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). Create backend/pyproject.toml with 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). Create frontend/package.json with 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 build succeeds for all services and docker compose up -d starts postgres, backend, frontend, nginx. Backend health endpoint responds at /api/v1/health. Frontend serves through nginx.

B. Backend Core Infrastructure (Tasks 713)

  • B. Create backend/app/__init__.py, backend/app/config.py using pydantic-settings BaseSettings. 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_db async generator, Base = declarative_base() with UUID pk mixin.

  • B. Create backend/app/models/__init__.py, backend/app/models/user.py, backend/app/models/session.py matching the schema above. SQLAlchemy 2.0 mapped_column style. updated_at with onupdate=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 Alembic upgrade head on startup. CORS middleware. Include API routers. /api/v1/health endpoint.

  • 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 (with remember_me), RefreshRequest, TokenResponse, UserResponse. Create backend/app/schemas/common.py: MessageResponse.

C. Backend Auth Logic (Tasks 1418)

  • 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_user dependency (JWT → User), require_admin dependency.

  • C6. Create backend/app/api/v1/__init__.py, backend/app/api/v1/auth.py router with 5 auth endpoints.

  • C7. Create backend/app/api/v1/router.py to aggregate v1 sub-routers under /api/v1. Include in main.py.

  • C8. Create backend/scripts/seed_admin.py: standalone async script, reads env, creates admin user if not exists. Idempotent.

D. Frontend Infrastructure (Tasks 1927)

  • D9. Create frontend/vite.config.ts (React plugin, proxy /api to backend in dev), frontend/tsconfig.json, frontend/tsconfig.node.json with @/ 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 in frontend/src/components/ui/.

  • D2. Set up i18next: frontend/src/i18n.ts, frontend/public/locales/en/translation.json, frontend/public/locales/ru/translation.json with 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). Create frontend/src/api/auth.ts with typed API functions.

  • D4. Create frontend/src/stores/auth-store.ts (Zustand, persisted): user, tokens, isAuthenticated, actions. Create frontend/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.tsx entry point.

  • D7. Set up TanStack Query: frontend/src/lib/query-client.ts. Wire QueryClientProvider in App.

E. Frontend Auth Pages & Layout (Tasks 2833)

  • 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.tsx and frontend/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 3436)

  • F3. Create frontend/nginx.conf for 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 with pytest.


Files to Create

Root

  • .env.example
  • docker-compose.yml
  • docker-compose.dev.yml

nginx/

  • nginx/Dockerfile
  • nginx/nginx.conf

backend/

  • backend/Dockerfile
  • backend/pyproject.toml
  • backend/alembic.ini
  • backend/alembic/env.py
  • backend/alembic/script.py.mako
  • backend/alembic/versions/<initial_migration>.py
  • backend/app/__init__.py
  • backend/app/main.py
  • backend/app/config.py
  • backend/app/database.py
  • backend/app/models/__init__.py
  • backend/app/models/user.py
  • backend/app/models/session.py
  • backend/app/schemas/__init__.py
  • backend/app/schemas/auth.py
  • backend/app/schemas/common.py
  • backend/app/core/__init__.py
  • backend/app/core/security.py
  • backend/app/services/__init__.py
  • backend/app/services/auth_service.py
  • backend/app/api/__init__.py
  • backend/app/api/deps.py
  • backend/app/api/v1/__init__.py
  • backend/app/api/v1/router.py
  • backend/app/api/v1/auth.py
  • backend/scripts/__init__.py
  • backend/scripts/seed_admin.py
  • backend/tests/__init__.py
  • backend/tests/conftest.py
  • backend/tests/test_auth.py

frontend/

  • frontend/Dockerfile
  • frontend/nginx.conf
  • frontend/package.json
  • frontend/vite.config.ts
  • frontend/tsconfig.json
  • frontend/tsconfig.node.json
  • frontend/tailwind.config.ts
  • frontend/postcss.config.js
  • frontend/components.json
  • frontend/index.html
  • frontend/src/main.tsx
  • frontend/src/App.tsx
  • frontend/src/routes.tsx
  • frontend/src/index.css
  • frontend/src/i18n.ts
  • frontend/src/vite-env.d.ts
  • frontend/src/lib/query-client.ts
  • frontend/src/lib/utils.ts
  • frontend/src/api/client.ts
  • frontend/src/api/auth.ts
  • frontend/src/stores/auth-store.ts
  • frontend/src/stores/ui-store.ts
  • frontend/src/components/ui/ (shadcn components)
  • frontend/src/components/shared/theme-provider.tsx
  • frontend/src/components/shared/language-toggle.tsx
  • frontend/src/components/shared/protected-route.tsx
  • frontend/src/components/auth/login-form.tsx
  • frontend/src/components/auth/register-form.tsx
  • frontend/src/components/layout/app-layout.tsx
  • frontend/src/components/layout/sidebar.tsx
  • frontend/src/components/layout/header.tsx
  • frontend/src/pages/login.tsx
  • frontend/src/pages/register.tsx
  • frontend/src/pages/dashboard.tsx
  • frontend/src/pages/not-found.tsx
  • frontend/public/locales/en/translation.json
  • frontend/public/locales/ru/translation.json

Acceptance Criteria

  1. docker compose up --build starts all 4 services (postgres, backend, frontend, nginx) without errors
  2. GET /api/v1/health returns {"status": "ok"}
  3. Alembic migration creates users and sessions tables with correct schema
  4. seed_admin.py creates an admin user; re-running is idempotent
  5. POST /api/v1/auth/register creates a new user and returns tokens
  6. POST /api/v1/auth/login with valid credentials returns tokens; remember_me=true → 30-day refresh, false → 24-hour
  7. POST /api/v1/auth/refresh rotates refresh token and returns new pair
  8. POST /api/v1/auth/logout invalidates the session
  9. GET /api/v1/auth/me with valid token returns user data; expired/invalid → 401
  10. Frontend loads at http://localhost, unauthenticated users see login page
  11. User can register, log in, and see dashboard with their name
  12. Sidebar renders with placeholder navigation; header has theme toggle, language toggle, user menu with logout
  13. Dark/light theme toggle works (persisted across reload)
  14. Language toggle switches between English and Russian (persisted across reload)
  15. Axios interceptor auto-refreshes access token on 401 and retries the request
  16. 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.