Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/stardag-api/src/stardag_api/limits.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ class LimitsSettings(BaseSettings):
max_dependency_ids_per_task: Annotated[int, Field(ge=1)] | None = None
max_artifacts_per_task: Annotated[int, Field(ge=1)] | None = None

# Tenancy quotas (enforced in routes/workspaces.py)
max_workspaces_per_user: Annotated[int, Field(ge=1)] | None = None
max_environments_per_workspace: Annotated[int, Field(ge=1)] | None = None

# Cache TTL for DB count queries (seconds)
entity_count_cache_ttl: Annotated[int, Field(ge=1)] = 60

Expand Down
4 changes: 0 additions & 4 deletions app/stardag-api/src/stardag_api/models/workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@ class Workspace(Base, TimestampMixin):

__tablename__ = "workspaces"

# Limits
MAX_WORKSPACES_PER_USER = 3
MAX_ENVIRONMENTS_PER_WORKSPACE = 6

id: Mapped[UUID] = mapped_column(
Uuid,
primary_key=True,
Expand Down
11 changes: 7 additions & 4 deletions app/stardag-api/src/stardag_api/routes/workspaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from sqlalchemy.orm import selectinload

from stardag_api.auth import get_current_user, get_current_user_flexible
from stardag_api.config import limits_settings
from stardag_api.db import get_db
from stardag_api.models import (
Invite,
Expand Down Expand Up @@ -231,10 +232,11 @@ async def create_workspace(
)
)
workspace_count = workspace_count_result.scalar() or 0
if workspace_count >= Workspace.MAX_WORKSPACES_PER_USER:
max_workspaces = limits_settings.max_workspaces_per_user
if max_workspaces is not None and workspace_count >= max_workspaces:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=f"You can create at most {Workspace.MAX_WORKSPACES_PER_USER} workspaces",
detail=f"You can create at most {max_workspaces} workspaces",
)
Comment on lines 234 to 240

# Check if slug is taken
Expand Down Expand Up @@ -760,10 +762,11 @@ async def create_environment(
)
)
environment_count = environment_count_result.scalar() or 0
if environment_count >= Workspace.MAX_ENVIRONMENTS_PER_WORKSPACE:
max_environments = limits_settings.max_environments_per_workspace
if max_environments is not None and environment_count >= max_environments:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=f"Workspace can have at most {Workspace.MAX_ENVIRONMENTS_PER_WORKSPACE} environments",
detail=f"Workspace can have at most {max_environments} environments",
)
Comment on lines 764 to 770

# Check if slug exists in this workspace
Expand Down
2 changes: 2 additions & 0 deletions infra/aws-cdk/lib/api-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ export class ApiStack extends cdk.Stack {
LIMITS_MAX_ASSETS_PER_WORKSPACE_24H: "1000",
LIMITS_MAX_DEPENDENCY_IDS_PER_TASK: "500",
LIMITS_MAX_ASSETS_PER_TASK: "10",
LIMITS_MAX_WORKSPACES_PER_USER: "3",
LIMITS_MAX_ENVIRONMENTS_PER_WORKSPACE: "15",
Comment on lines 209 to +213
// Worker count is read by the Dockerfile CMD; keep undefined here
// to fall through to the image's default (sized for 1 vCPU).
...(apiGunicornWorkers ? { GUNICORN_WORKERS: apiGunicornWorkers } : {}),
Expand Down
Loading