Spaces:
Sleeping
User Profile with Project Context - API Update
Summary
Updated /api/v1/auth/me endpoint to include project context. The last_active_project_id is now stored in the user_preferences table (not users table) since it's a UI preference.
Database Changes
Migration: 012_add_user_last_active_project.sql
-- Adds last_active_project_id to user_preferences table
ALTER TABLE user_preferences
ADD COLUMN last_active_project_id UUID REFERENCES projects(id) ON DELETE SET NULL;
Updated API Endpoints
1. GET /api/v1/auth/me
Returns user profile with project context:
{
"id": "user_123",
"email": "pm@example.com",
"name": "John Doe",
"role": "project_manager",
"status": "active",
"is_active": true,
"client_id": null,
"contractor_id": "contractor_456",
"created_at": "2025-01-15T10:00:00Z",
"updated_at": "2025-11-19T14:30:00Z",
// NEW FIELDS:
"primary_project": {
"id": "proj_456",
"title": "Safaricom Fiber Rollout"
},
"assigned_projects": [
{ "id": "proj_456", "title": "Safaricom Fiber Rollout" },
{ "id": "proj_789", "title": "Nairobi West Maintenance" }
],
"last_active_project_id": "proj_456"
}
Project Selection Logic:
- last_active_project_id - User's last selected project (from preferences)
- primary_project - Determined by priority:
- Last active project (if set)
- First project where user is primary manager
- First assigned project (fallback)
- assigned_projects - All projects user is a member of (via
project_teamtable)
2. PUT /api/v1/auth/me/preferences
Update user preferences (e.g., switch active project):
PUT /api/v1/auth/me/preferences
Authorization: Bearer <token>
Content-Type: application/json
{
"last_active_project_id": "proj_789"
}
Response: Returns full UserProfile (same as GET /me) with updated context
Features:
- ✅ Validates user is actually assigned to the project
- ✅ Validates project exists and is active
- ✅ Prevents unauthorized project access
- ✅ Logs all changes in audit log
- ✅ Auto-creates user_preferences if missing
- ✅ Returns updated profile immediately
Clear preference (no active project):
{
"last_active_project_id": null
}
Frontend Implementation
On Login
// After successful login, call /auth/me
const userProfile = await fetch('/api/v1/auth/me', {
headers: { 'Authorization': `Bearer ${token}` }
}).then(r => r.json());
// Use primary_project for initial dashboard
const projectId = userProfile.primary_project?.id;
if (projectId) {
// Fetch dashboard data with this project context
const metrics = await fetch(`/api/v1/tickets/stats?project_id=${projectId}`);
}
// Store assigned projects for project switcher dropdown
localStorage.setItem('assigned_projects', JSON.stringify(userProfile.assigned_projects));
On Project Switch
// User selects different project from dropdown
async function switchProject(newProjectId: string) {
// Update preference on backend
const updatedProfile = await fetch('/api/v1/auth/me/preferences', {
method: 'PUT',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ last_active_project_id: newProjectId })
}).then(r => r.json());
// Reload dashboard with new project context
window.location.href = `/dashboard?project_id=${newProjectId}`;
}
Project Context Across All Pages
// In your app context/store
const { primary_project, assigned_projects } = userProfile;
// Use primary_project.id for all API calls
const tickets = await fetch(
`/api/v1/tickets?project_id=${primary_project.id}`
);
const finance = await fetch(
`/api/v1/finance/cash-flow?project_id=${primary_project.id}`
);
Error Handling
403 Forbidden - User not assigned to requested project
{
"detail": "You are not assigned to this project"
}
404 Not Found - Project doesn't exist
{
"detail": "Project not found"
}
400 Bad Request - Invalid UUID format
{
"detail": "Invalid project ID format"
}
Platform Admin Handling
Platform admins don't have project assignments - they oversee the entire platform:
// Platform admin's /auth/me response:
{
"id": "admin_123",
"email": "admin@platform.com",
"role": "platform_admin",
// ... other fields ...
"primary_project": null,
"assigned_projects": [],
"last_active_project_id": null
}
Attempting to set project preference as platform_admin returns error:
PUT /api/v1/auth/me/preferences
{ "last_active_project_id": "proj_456" }
// Response: 400 Bad Request
{
"detail": "Platform admins cannot set active project (no project assignments)"
}
Frontend should:
- Hide project switcher for platform_admin role
- Don't scope API calls to project_id for platform admins
- Show platform-wide dashboard instead of project dashboard
Notes
- Automatic Creation: If user_preferences doesn't exist, it's auto-created on first preference update
- Backwards Compatible: Existing code still works - just ignore new fields if not needed
- Security: All project access is validated against project_team membership
- Performance: Indexed queries, single DB roundtrip for profile data
- Audit Trail: All preference changes logged with old/new values
- Platform Admin: Returns null/empty project context, cannot set project preference
Migration
Run the migration:
# Apply migration
psql -U postgres -d your_database -f migrations/012_add_user_last_active_project.sql
# Rollback if needed
psql -U postgres -d your_database -f migrations/012_add_user_last_active_project_rollback.sql