feat: add multi-tenant system with properties, organizations, and public booking

Implement complete multi-property architecture:
- Properties (groups of spaces) with public/private visibility
- Property managers (many-to-many) with role-based permissions
- Organizations with member management
- Anonymous/guest booking support via public API (/api/public/*)
- Property-scoped spaces, bookings, and settings
- Frontend: property selector, organization management, public booking views
- Migration script and updated seed data

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-02-15 00:17:21 +00:00
parent d637513d92
commit e21cf03a16
51 changed files with 6324 additions and 273 deletions

View File

@@ -5,7 +5,7 @@ from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import BaseModel
from sqlalchemy.orm import Session
from app.core.deps import get_current_admin, get_current_user, get_db
from app.core.deps import get_current_admin, get_current_manager_or_superadmin, get_current_user, get_db
from app.core.security import get_password_hash
from app.models.user import User
from app.schemas.user import (
@@ -65,12 +65,12 @@ def update_timezone(
@admin_router.get("", response_model=list[UserResponse])
def list_users(
db: Annotated[Session, Depends(get_db)],
_: Annotated[User, Depends(get_current_admin)],
_: Annotated[User, Depends(get_current_manager_or_superadmin)],
role: str | None = None,
organization: str | None = None,
) -> list[User]:
"""
Get list of users (admin only).
Get list of users (manager or admin).
Supports filtering by role and organization.
"""
@@ -109,10 +109,10 @@ def create_user(
)
# Validate role
if user_data.role not in ["admin", "user"]:
if user_data.role not in ["admin", "superadmin", "manager", "user"]:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Role must be 'admin' or 'user'",
detail="Role must be 'superadmin', 'manager', or 'user'",
)
user = User(
@@ -170,10 +170,10 @@ def update_user(
)
# Validate role
if user_data.role and user_data.role not in ["admin", "user"]:
if user_data.role and user_data.role not in ["admin", "superadmin", "manager", "user"]:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Role must be 'admin' or 'user'",
detail="Role must be 'superadmin', 'manager', or 'user'",
)
# Track what changed