256 lines
12 KiB
Python
256 lines
12 KiB
Python
from __future__ import annotations
|
|
import enum
|
|
from datetime import datetime, timezone
|
|
from typing import List
|
|
|
|
from sqlalchemy import (
|
|
String, Text, Integer, DateTime, ForeignKey, Enum, JSON, Boolean
|
|
)
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
|
|
from app.database import Base
|
|
|
|
|
|
# ─── Enums ─────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
class ProjectStatus(str, enum.Enum):
|
|
PENDING = "PENDING"
|
|
AWAITING_CONDUCTOR = "AWAITING_CONDUCTOR"
|
|
CONDUCTOR_RUNNING = "CONDUCTOR_RUNNING"
|
|
AWAITING_ARCHITECT = "AWAITING_ARCHITECT"
|
|
ARCHITECT_RUNNING = "ARCHITECT_RUNNING"
|
|
AWAITING_DEV = "AWAITING_DEV"
|
|
DEV_RUNNING = "DEV_RUNNING"
|
|
AWAITING_UIUX = "AWAITING_UIUX"
|
|
UIUX_RUNNING = "UIUX_RUNNING"
|
|
AWAITING_QA = "AWAITING_QA"
|
|
QA_RUNNING = "QA_RUNNING"
|
|
AWAITING_DEPLOY = "AWAITING_DEPLOY"
|
|
DEPLOY_RUNNING = "DEPLOY_RUNNING"
|
|
COMPLETED = "COMPLETED"
|
|
FAILED = "FAILED"
|
|
PAUSED = "PAUSED"
|
|
|
|
|
|
class WorkflowType(str, enum.Enum):
|
|
SOFTWARE_DESIGN = "SOFTWARE_DESIGN"
|
|
SYSADMIN_DEBUG = "SYSADMIN_DEBUG"
|
|
DEVOPS_SETUP = "DEVOPS_SETUP"
|
|
SYSADMIN_ADJUST = "SYSADMIN_ADJUST"
|
|
|
|
|
|
class ProjectType(str, enum.Enum):
|
|
SYSTEMD_SERVICE = "SYSTEMD_SERVICE"
|
|
DOCKER_WEBAPP = "DOCKER_WEBAPP"
|
|
DOCKER_API = "DOCKER_API"
|
|
DOCKER_COMPOSE = "DOCKER_COMPOSE"
|
|
CLI_TOOL = "CLI_TOOL"
|
|
CRON_JOB = "CRON_JOB"
|
|
SHELL_SCRIPT = "SHELL_SCRIPT"
|
|
WORKER_MQ = "WORKER_MQ"
|
|
SOCKET_IPC = "SOCKET_IPC"
|
|
WEBHOOK_EVENT = "WEBHOOK_EVENT"
|
|
LIBRARY_PLUGIN = "LIBRARY_PLUGIN"
|
|
AGENT_BOT = "AGENT_BOT"
|
|
TUI_APP = "TUI_APP"
|
|
MCP_SERVER = "MCP_SERVER"
|
|
WASM_EDGE = "WASM_EDGE"
|
|
GRPC_SERVICE = "GRPC_SERVICE"
|
|
KERNEL_EBPF = "KERNEL_EBPF"
|
|
SERVERLESS_FAAS = "SERVERLESS_FAAS"
|
|
DESKTOP_GUI = "DESKTOP_GUI"
|
|
STATIC_SITE = "STATIC_SITE"
|
|
|
|
|
|
# Project type metadata (emoji, label, description)
|
|
PROJECT_TYPE_INFO = {
|
|
ProjectType.SYSTEMD_SERVICE: ("⚙️", "Service système (systemd)", "Démon long-running, démarrage automatique au boot, redémarrage sur erreur."),
|
|
ProjectType.DOCKER_WEBAPP: ("🌐", "Docker — Web app", "Application frontend ou backend HTTP/HTTPS containerisée."),
|
|
ProjectType.DOCKER_API: ("🔌", "Docker — API (REST / gRPC)", "Service HTTP headless containerisé, sans interface utilisateur."),
|
|
ProjectType.DOCKER_COMPOSE: ("🐳", "Docker Compose", "Orchestration de plusieurs conteneurs avec réseaux et volumes partagés."),
|
|
ProjectType.CLI_TOOL: ("💻", "Outil CLI", "Programme en ligne de commande avec sous-commandes, flags et stdin/stdout."),
|
|
ProjectType.CRON_JOB: ("⏰", "Jobs planifiés (cron / timer)", "Exécution périodique ou batch, anacron pour machines non 24/7."),
|
|
ProjectType.SHELL_SCRIPT: ("📜", "Script shell (Bash / Zsh)", "Automatisation légère, glue entre outils, pipelines de commandes."),
|
|
ProjectType.WORKER_MQ: ("📨", "Worker + Message queue", "Traitement asynchrone via Redis, RabbitMQ ou Kafka."),
|
|
ProjectType.SOCKET_IPC: ("🔗", "Socket / IPC local", "Communication inter-processus via Unix socket, FIFO ou D-Bus."),
|
|
ProjectType.WEBHOOK_EVENT: ("🪝", "Webhook / Event-driven", "Réception d'événements HTTP entrants, architecture pub/sub."),
|
|
ProjectType.LIBRARY_PLUGIN: ("📦", "Bibliothèque / Plugin", "Code réutilisable sous forme de .so, package Python ou module Go."),
|
|
ProjectType.AGENT_BOT: ("🤖", "Agent / Bot autonome", "Service IA ou bot (Telegram, Discord) tournant en continu."),
|
|
ProjectType.TUI_APP: ("🖥️", "TUI (interface texte)", "Interface terminal interactive via curses, Textual ou Bubble Tea."),
|
|
ProjectType.MCP_SERVER: ("🧠", "Serveur MCP", "Exposition d'outils IA via Model Context Protocol (stdio ou HTTP SSE)."),
|
|
ProjectType.WASM_EDGE: ("🧊", "WebAssembly / Edge", "Modules wasm sandboxés, portables, via wasi et wasmtime."),
|
|
ProjectType.GRPC_SERVICE: ("⚡", "Microservice gRPC / Thrift", "RPC binaire performant avec contrat IDL (protobuf)."),
|
|
ProjectType.KERNEL_EBPF: ("🔬", "Module kernel / eBPF", "Driver, hook réseau ou sonde d'observabilité au niveau noyau."),
|
|
ProjectType.SERVERLESS_FAAS: ("☁️", "Serverless / FaaS", "Fonctions déclenchées à la demande via OpenFaaS ou Knative."),
|
|
ProjectType.DESKTOP_GUI: ("🖼️", "Application desktop (GUI)", "Interface graphique native via GTK, Qt, ou Tauri/Electron."),
|
|
ProjectType.STATIC_SITE: ("📄", "Site statique / SSG", "Génération de HTML pur au build time via Hugo, Astro ou Jekyll."),
|
|
}
|
|
|
|
|
|
class TaskStatus(str, enum.Enum):
|
|
PENDING = "PENDING"
|
|
IN_PROGRESS = "IN_PROGRESS"
|
|
IN_REVIEW = "IN_REVIEW"
|
|
REJECTED = "REJECTED"
|
|
READY_FOR_DEPLOY = "READY_FOR_DEPLOY"
|
|
DONE = "DONE"
|
|
BLOCKED = "BLOCKED"
|
|
|
|
|
|
class TaskType(str, enum.Enum):
|
|
BACKEND = "BACKEND"
|
|
FRONTEND = "FRONTEND"
|
|
INFRA = "INFRA"
|
|
TEST = "TEST"
|
|
DESIGN = "DESIGN"
|
|
|
|
|
|
class TaskPriority(str, enum.Enum):
|
|
P1 = "P1"
|
|
P2 = "P2"
|
|
P3 = "P3"
|
|
|
|
|
|
class AgentExecutionStatus(str, enum.Enum):
|
|
RUNNING = "RUNNING"
|
|
SUCCESS = "SUCCESS"
|
|
FAILED = "FAILED"
|
|
TIMEOUT = "TIMEOUT"
|
|
|
|
|
|
def _utcnow() -> datetime:
|
|
return datetime.now(timezone.utc)
|
|
|
|
|
|
# ─── Infrastructure Models ─────────────────────────────────────────────────────
|
|
|
|
|
|
class DeployServer(Base):
|
|
__tablename__ = "deploy_servers"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
name: Mapped[str] = mapped_column(String(200), unique=True, nullable=False)
|
|
host: Mapped[str] = mapped_column(String(500), nullable=False)
|
|
user: Mapped[str] = mapped_column(String(100), default="deploy")
|
|
password: Mapped[str] = mapped_column(String(500), nullable=True)
|
|
ssh_port: Mapped[int] = mapped_column(Integer, default=22)
|
|
description: Mapped[str] = mapped_column(Text, default="")
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
|
|
|
|
|
|
class GitServer(Base):
|
|
__tablename__ = "git_servers"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
name: Mapped[str] = mapped_column(String(200), unique=True, nullable=False)
|
|
url: Mapped[str] = mapped_column(String(500), nullable=False)
|
|
token: Mapped[str] = mapped_column(String(500), nullable=True)
|
|
org: Mapped[str] = mapped_column(String(200), default="openclaw")
|
|
description: Mapped[str] = mapped_column(Text, default="")
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
|
|
|
|
|
|
# ─── Core Models ───────────────────────────────────────────────────────────────
|
|
|
|
|
|
class Project(Base):
|
|
__tablename__ = "projects"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
name: Mapped[str] = mapped_column(String(200), nullable=False)
|
|
slug: Mapped[str] = mapped_column(String(200), unique=True, nullable=False, index=True)
|
|
description: Mapped[str] = mapped_column(Text, default="")
|
|
status: Mapped[ProjectStatus] = mapped_column(
|
|
Enum(ProjectStatus), default=ProjectStatus.PENDING, nullable=False
|
|
)
|
|
workflow_type: Mapped[WorkflowType] = mapped_column(
|
|
Enum(WorkflowType), default=WorkflowType.SOFTWARE_DESIGN, nullable=False
|
|
)
|
|
project_type: Mapped[ProjectType] = mapped_column(
|
|
Enum(ProjectType), default=ProjectType.DOCKER_WEBAPP, nullable=False
|
|
)
|
|
test_mode: Mapped[bool] = mapped_column(default=False)
|
|
install_path: Mapped[str] = mapped_column(String(500), nullable=True)
|
|
gitea_repo: Mapped[str] = mapped_column(String(500), nullable=True)
|
|
deployment_target: Mapped[str] = mapped_column(String(200), nullable=True)
|
|
deploy_server_id: Mapped[int] = mapped_column(ForeignKey("deploy_servers.id"), nullable=True)
|
|
git_server_id: Mapped[int] = mapped_column(ForeignKey("git_servers.id"), nullable=True)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
|
|
updated_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), default=_utcnow, onupdate=_utcnow
|
|
)
|
|
|
|
# Relationships
|
|
tasks: Mapped[List["Task"]] = relationship(
|
|
back_populates="project", cascade="all, delete-orphan", lazy="selectin"
|
|
)
|
|
audit_logs: Mapped[List["AuditLog"]] = relationship(
|
|
back_populates="project", cascade="all, delete-orphan", lazy="selectin"
|
|
)
|
|
agent_executions: Mapped[List["AgentExecution"]] = relationship(
|
|
back_populates="project", cascade="all, delete-orphan", lazy="selectin"
|
|
)
|
|
deploy_server: Mapped["DeployServer"] = relationship(lazy="selectin")
|
|
git_server: Mapped["GitServer"] = relationship(lazy="selectin")
|
|
|
|
|
|
class Task(Base):
|
|
__tablename__ = "tasks"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
project_id: Mapped[int] = mapped_column(ForeignKey("projects.id"), nullable=False)
|
|
task_id: Mapped[str] = mapped_column(String(50), nullable=False) # e.g. TASK-001
|
|
type: Mapped[TaskType] = mapped_column(Enum(TaskType), default=TaskType.BACKEND)
|
|
title: Mapped[str] = mapped_column(String(500), nullable=False)
|
|
priority: Mapped[TaskPriority] = mapped_column(Enum(TaskPriority), default=TaskPriority.P3)
|
|
assigned_to: Mapped[str] = mapped_column(String(100), nullable=True)
|
|
status: Mapped[TaskStatus] = mapped_column(
|
|
Enum(TaskStatus), default=TaskStatus.PENDING, nullable=False
|
|
)
|
|
dependencies: Mapped[dict] = mapped_column(JSON, default=list)
|
|
acceptance_criteria: Mapped[str] = mapped_column(Text, nullable=True)
|
|
agent_payloads: Mapped[dict] = mapped_column(JSON, default=dict)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
|
|
updated_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), default=_utcnow, onupdate=_utcnow
|
|
)
|
|
|
|
# Relationships
|
|
project: Mapped["Project"] = relationship(back_populates="tasks")
|
|
|
|
|
|
class AgentExecution(Base):
|
|
__tablename__ = "agent_executions"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
project_id: Mapped[int] = mapped_column(ForeignKey("projects.id"), nullable=False)
|
|
agent_name: Mapped[str] = mapped_column(String(100), nullable=False)
|
|
status: Mapped[AgentExecutionStatus] = mapped_column(
|
|
Enum(AgentExecutionStatus), default=AgentExecutionStatus.RUNNING
|
|
)
|
|
pid: Mapped[int] = mapped_column(Integer, nullable=True)
|
|
started_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
|
|
finished_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
exit_code: Mapped[int] = mapped_column(Integer, nullable=True)
|
|
error_output: Mapped[str] = mapped_column(Text, nullable=True)
|
|
|
|
# Relationships
|
|
project: Mapped["Project"] = relationship(back_populates="agent_executions")
|
|
|
|
|
|
class AuditLog(Base):
|
|
__tablename__ = "audit_logs"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
project_id: Mapped[int] = mapped_column(ForeignKey("projects.id"), nullable=False)
|
|
timestamp: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
|
|
agent: Mapped[str] = mapped_column(String(100), nullable=False)
|
|
action: Mapped[str] = mapped_column(String(100), nullable=False)
|
|
target: Mapped[str] = mapped_column(String(100), nullable=True)
|
|
message: Mapped[str] = mapped_column(Text, nullable=True)
|
|
source: Mapped[str] = mapped_column(String(100), default="api")
|
|
|
|
# Relationships
|
|
project: Mapped["Project"] = relationship(back_populates="audit_logs")
|