feat: implement auth, projects, and frontend foundation

This commit is contained in:
2026-05-17 20:21:55 +00:00
parent e7819bfc82
commit 71d9fe6406
88 changed files with 10936 additions and 47 deletions
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-05-17
@@ -0,0 +1,3 @@
# database-models
Implement SQLAlchemy models and Alembic migrations for all core entities
@@ -0,0 +1,125 @@
# Design: Database Models
## Technology Choices
- **SQLAlchemy 2.0**: Modern async ORM with type annotations
- **asyncpg**: High-performance async PostgreSQL driver
- **Alembic**: Database migration tool
- **UUID**: All primary keys use UUID for distributed safety
## Architecture
### Base Model
All models inherit from a common base with:
- `id`: UUID primary key (default=uuid4)
- `created_at`: Timestamp
- `updated_at`: Timestamp (auto-updated)
### Models
1. **User**
- id: UUID PK
- email: str, unique, indexed
- name: str
- authentik_id: str, unique (external auth reference)
- avatar_url: str | None
- created_at, updated_at
2. **Project**
- id: UUID PK
- name: str
- description: str | None
- owner_id: UUID → User
- default_ssh_key_id: UUID → SSHKey | None
- created_at, updated_at
3. **GitRepository**
- id: UUID PK
- name: str
- path: str (filesystem path to bare repo)
- project_id: UUID → Project
- owner_id: UUID → User
- is_mirror: bool
- remote_url: str | None
- last_push: datetime | None
- created_at
4. **SSHKey**
- id: UUID PK
- name: str
- public_key: str
- private_key_encrypted: str (Fernet encrypted)
- user_id: UUID → User
- project_id: UUID → Project | None
- created_at
5. **UserConfig**
- id: UUID PK
- user_id: UUID → User
- config: JSONB (PostgreSQL native JSON)
- created_at, updated_at
### Relationships
```
User 1--N Project
User 1--N SSHKey
User 1--1 UserConfig
Project 1--N GitRepository
Project N--1 SSHKey (default_ssh_key)
```
### File Structure
```
apps/api/
├── src/
│ ├── models/
│ │ ├── __init__.py
│ │ ├── base.py # DeclarativeBase + common columns
│ │ ├── user.py
│ │ ├── project.py
│ │ ├── git_repository.py
│ │ ├── ssh_key.py
│ │ └── user_config.py
│ ├── database.py # Async engine + session
│ └── config.py # Settings with pydantic-settings
├── alembic/
│ ├── env.py
│ ├── script.py.mako
│ └── versions/
│ └── 001_initial.py
├── tests/
│ └── test_models.py
└── scripts/
└── seed.py
```
## Async Pattern
```python
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker
engine = create_async_engine(DATABASE_URL)
AsyncSessionLocal = sessionmaker(engine, class_=AsyncSession)
```
## Migration Strategy
- Single initial migration creating all tables
- Future migrations use `alembic revision --autogenerate`
- Run with `make migrate` (docker compose exec api alembic upgrade head)
## Seed Data
- Create a test user with sample data
- Run via `docker compose exec api python scripts/seed.py`
## Quality Gates
- pytest with async test support
- mypy strict mode
- ruff for linting
- All models have type annotations
@@ -0,0 +1,28 @@
# Proposal: Database Models
## What
Implement SQLAlchemy 2.0 async models and Alembic migrations for all core entities in the Headquarter platform.
## Why
All other features (auth, git repos, projects, SSH keys, etc.) depend on a solid database foundation. We need models that:
- Use SQLAlchemy 2.0 async style for performance
- Support all entity relationships defined in the specs
- Have proper migrations for schema versioning
- Include seed data for development
## Scope
- User, Project, GitRepository, SSHKey, UserConfig models
- Alembic setup with asyncpg support
- Initial migration creating all tables
- Database seeding script
## Success Criteria
- All models defined with correct relationships
- Initial migration runs successfully
- `make migrate` works
- Seed script creates test data
- Quality gates pass (pytest, mypy, ruff)
@@ -0,0 +1,64 @@
# Tasks: Database Models
## Task 1: Create project structure and configuration
- [x] Create `apps/api/src/models/__init__.py`
- [x] Create `apps/api/src/config.py` with pydantic-settings for database URL
- [x] Create `apps/api/src/database.py` with async engine and session
## Task 2: Create base model
- [x] Create `apps/api/src/models/base.py` with DeclarativeBase
- [x] Add UUID primary key mixin
- [x] Add timestamp mixin (created_at, updated_at)
## Task 3: Create User model
- [x] Create `apps/api/src/models/user.py`
- [x] Define User with all fields from spec
- [x] Add relationships to Project, SSHKey, UserConfig
## Task 4: Create Project model
- [x] Create `apps/api/src/models/project.py`
- [x] Define Project with all fields
- [x] Add relationships to User, GitRepository, SSHKey
## Task 5: Create GitRepository model
- [x] Create `apps/api/src/models/git_repository.py`
- [x] Define GitRepository with all fields
- [x] Add relationships to Project, User
## Task 6: Create SSHKey model
- [x] Create `apps/api/src/models/ssh_key.py`
- [x] Define SSHKey with all fields
- [x] Add relationships to User, Project
## Task 7: Create UserConfig model
- [x] Create `apps/api/src/models/user_config.py`
- [x] Define UserConfig with JSONB config field
- [x] Add relationship to User
## Task 8: Initialize Alembic
- [x] Create Alembic scaffolding equivalent to `alembic init`
- [x] Configure `alembic/env.py` for async
- [x] Update `alembic.ini` with correct URL
## Task 9: Create initial migration
- [x] Generate migration creating all tables
- [x] Verify migration is correct
## Task 10: Create seed script
- [x] Create `apps/api/src/scripts/seed.py`
- [x] Add test user and sample data payload helper
- [x] Make script runnable
## Task 11: Create tests
- [x] Create `apps/api/tests/test_models.py`
- [x] Test model creation and relationships
- [x] Test async database operations
## Task 12: Run quality gates
- [x] Run `pytest` - all tests pass
- [x] Run `mypy .` - no type errors
- [x] Run `ruff check .` - no lint errors
## Runtime Verification
- [x] Run migrations against a live PostgreSQL instance to verify end-to-end database execution.