2.9 KiB
2.9 KiB
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: Timestampupdated_at: Timestamp (auto-updated)
Models
-
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
-
Project
- id: UUID PK
- name: str
- description: str | None
- owner_id: UUID → User
- default_ssh_key_id: UUID → SSHKey | None
- created_at, updated_at
-
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
-
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
-
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
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