"""Projects domain: projects, their Git repositories and per-project credentials.""" from __future__ import annotations from dataclasses import dataclass, field from datetime import datetime from app.domain.base import new_id, utcnow @dataclass(slots=True, kw_only=True) class Project: organization_id: str name: str slug: str description: str = "" created_by_user_id: str | None = None default_branch: str = "main" id: str = field(default_factory=new_id) created_at: datetime = field(default_factory=utcnow) @dataclass(slots=True, kw_only=True) class ProjectGitRepo: """The Git repository attached to a project. Named ``ProjectGitRepo`` rather than ``ProjectRepository`` on purpose: the latter collided with the *infrastructure* repository that manages ``Project`` rows, and the shadowing silently broke every Git endpoint at runtime. """ project_id: str provider: str = "gitea" owner: str = "" repo_name: str = "" clone_url: str = "" web_url: str = "" default_branch: str = "main" id: str = field(default_factory=new_id) created_at: datetime = field(default_factory=utcnow) @property def full_name(self) -> str: return f"{self.owner}/{self.repo_name}" @dataclass(slots=True, kw_only=True) class ProjectGitCredential: """Per-project Git identity. The password is encrypted at rest and never returned to the frontend.""" project_id: str username: str encrypted_password: str id: str = field(default_factory=new_id) created_at: datetime = field(default_factory=utcnow) @dataclass(slots=True, kw_only=True) class ProjectExternalGit: """Credentials for a *user-supplied* git remote (v2 ``git_source=external``). Separate from :class:`ProjectGitCredential`, which is the platform's own Gitea identity. Keeping them apart means switching a project to an external remote can never accidentally reuse — or expose — the platform token. """ project_id: str auth_type: str = "none" # none | token | ssh_key username: str | None = None secret_encrypted: str | None = None known_hosts: str | None = None updated_at: datetime = field(default_factory=utcnow)