Initial commit: StackPilot Phase 1 (Core)
Self-hosted Docker Compose manager. - Backend: FastAPI + docker-py + SQLite (JWT auth, file-first stacks, lifecycle, live status, WebSocket logs, docker-run converter, audit log) - Frontend: React + Vite + Tailwind (login/setup, dashboard, stacks, stack detail, Monaco editor, dark/light theme) - Deployment: docker-compose.yml, Dockerfiles, nginx reverse proxy Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
"""SQLModel table models. Importing this package registers all tables."""
|
||||
from models.audit import AuditLog
|
||||
from models.stack import Stack
|
||||
from models.user import User
|
||||
|
||||
__all__ = ["User", "Stack", "AuditLog"]
|
||||
@@ -0,0 +1,20 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from typing import Optional
|
||||
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
|
||||
def _now() -> datetime:
|
||||
return datetime.now(timezone.utc)
|
||||
|
||||
|
||||
class AuditLog(SQLModel, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
user: str
|
||||
action: str # e.g. "stack.start"
|
||||
target: str # stack id / resource id
|
||||
detail: Optional[str] = None
|
||||
ip: Optional[str] = None
|
||||
timestamp: datetime = Field(default_factory=_now, index=True)
|
||||
@@ -0,0 +1,49 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from typing import Optional
|
||||
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
|
||||
def _now() -> datetime:
|
||||
return datetime.now(timezone.utc)
|
||||
|
||||
|
||||
class Stack(SQLModel, table=True):
|
||||
# id is the stack directory name (slug)
|
||||
id: str = Field(primary_key=True)
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
stacks_dir_override: Optional[str] = None
|
||||
created_at: datetime = Field(default_factory=_now)
|
||||
updated_at: datetime = Field(default_factory=_now)
|
||||
|
||||
|
||||
# --- API schemas ---
|
||||
|
||||
|
||||
class StackCreate(SQLModel):
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
yaml: Optional[str] = None # initial compose content
|
||||
env: Optional[str] = None
|
||||
|
||||
|
||||
class StackUpdate(SQLModel):
|
||||
name: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
yaml: Optional[str] = None
|
||||
env: Optional[str] = None
|
||||
|
||||
|
||||
class StackCloneRequest(SQLModel):
|
||||
name: str
|
||||
|
||||
|
||||
class ConvertRequest(SQLModel):
|
||||
command: str # a `docker run ...` string
|
||||
|
||||
|
||||
class ConvertResponse(SQLModel):
|
||||
yaml: str
|
||||
@@ -0,0 +1,50 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from typing import Optional
|
||||
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
|
||||
def _now() -> datetime:
|
||||
return datetime.now(timezone.utc)
|
||||
|
||||
|
||||
class User(SQLModel, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
username: str = Field(index=True, unique=True)
|
||||
hashed_password: str
|
||||
role: str = Field(default="user") # "admin" | "user"
|
||||
is_active: bool = Field(default=True)
|
||||
created_at: datetime = Field(default_factory=_now)
|
||||
|
||||
|
||||
# --- API schemas ---
|
||||
|
||||
|
||||
class UserRead(SQLModel):
|
||||
id: int
|
||||
username: str
|
||||
role: str
|
||||
is_active: bool
|
||||
|
||||
|
||||
class UserCreate(SQLModel):
|
||||
username: str
|
||||
password: str
|
||||
role: str = "admin"
|
||||
|
||||
|
||||
class LoginRequest(SQLModel):
|
||||
username: str
|
||||
password: str
|
||||
|
||||
|
||||
class TokenPair(SQLModel):
|
||||
access_token: str
|
||||
refresh_token: str
|
||||
token_type: str = "bearer"
|
||||
|
||||
|
||||
class RefreshRequest(SQLModel):
|
||||
refresh_token: str
|
||||
Reference in New Issue
Block a user