feat: implement workspace management and member invitations

- Added tenant API for managing workspaces, including listing, creating, and inviting members.
- Introduced new models for organizations, workspaces, and workspace memberships in the backend.
- Created frontend components for workspace administration, allowing users to manage workspaces and invite members.
- Updated document listing to support workspace-specific filtering.
- Implemented account settings page for user profile management and password changes.
- Added end-to-end tests for workspace administration and dashboard functionalities.
This commit is contained in:
Anurag Jangid 2026-10-23 01:19:11 +05:30
parent 809235e962
commit bf5cd44dc1
20 changed files with 928 additions and 27 deletions

View file

@ -2,8 +2,9 @@ from fastapi import APIRouter, Depends, HTTPException, Query, Response, status
from motor.motor_asyncio import AsyncIOMotorDatabase
from sqlalchemy.orm import Session
from app.database import get_db
from app.dependencies import get_current_user, get_mongo_db_dep
from app.database import get_db
from app.services.tenant_service import get_workspace
from app.models.user import User
from app.schemas.document import DocumentCreate, DocumentResponse, DocumentUpdate, DocumentVersionResponse, ShareRequest
from app.schemas.collaboration import (
@ -199,10 +200,19 @@ async def list_docs(
stead_id: str | None = Query(None, description="Filter by parent stead"),
parent_page_id: str | None = Query(None, description="Filter by parent page"),
starred: bool | None = Query(None, description="Filter starred projects"),
workspace_id: int | None = Query(None, description="Filter by workspace"),
sort: str = Query("updated_at", description="Sort field: updated_at or last_visited_at"),
current_user: User = Depends(get_current_user),
db: AsyncIOMotorDatabase = Depends(get_mongo_db_dep),
auth_db: Session = Depends(get_db),
) -> list[DocumentResponse]:
if workspace_id is not None:
if get_workspace(auth_db, workspace_id, current_user.id) is None:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Workspace access denied")
await db["documents"].update_many(
{"owner_id": current_user.id, "workspace_id": {"$exists": False}},
{"$set": {"workspace_id": workspace_id}},
)
sort_field = "last_visited_at" if sort == "last_visited_at" else "updated_at"
docs = await list_documents(
db,
@ -212,6 +222,7 @@ async def list_docs(
parent_page_id=parent_page_id,
starred=starred,
sort=sort_field,
workspace_id=workspace_id,
)
return [DocumentResponse(**doc) for doc in docs]
@ -243,7 +254,10 @@ async def create_doc(
data: DocumentCreate,
current_user: User = Depends(get_current_user),
db: AsyncIOMotorDatabase = Depends(get_mongo_db_dep),
auth_db: Session = Depends(get_db),
) -> DocumentResponse:
if data.workspace_id is not None and get_workspace(auth_db, data.workspace_id, current_user.id) is None:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Workspace access denied")
created_by = current_user.full_name or current_user.username
try:
doc = await create_document(db, current_user.id, created_by, data)