126 lines
2.9 KiB
Markdown
126 lines
2.9 KiB
Markdown
# 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
|