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:
menzelj
2026-06-07 16:04:58 +00:00
co-authored by Claude Opus 4.8
commit f732cb080b
59 changed files with 3677 additions and 0 deletions
+6
View File
@@ -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"]
+20
View File
@@ -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)
+49
View File
@@ -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
+50
View File
@@ -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