mirror of
https://github.com/AeThex-Corporation/AeThex-OS.git
synced 2026-04-21 15:37:21 +00:00
300 lines
9.4 KiB
Markdown
300 lines
9.4 KiB
Markdown
# Multi-Tenancy Implementation Summary
|
|
|
|
## 🎯 Overview
|
|
|
|
This implementation adds full multi-tenancy support to AeThex-OS, enabling organizations, team collaboration, and project-based ownership.
|
|
|
|
---
|
|
|
|
## ✅ Deliverables Completed
|
|
|
|
### 1. Database Schema Changes (`shared/schema.ts`)
|
|
|
|
#### New Tables Added:
|
|
- ✅ **organizations** - Workspace/team containers
|
|
- `id`, `name`, `slug`, `owner_user_id`, `plan`, timestamps
|
|
- ✅ **organization_members** - Team membership
|
|
- `id`, `organization_id`, `user_id`, `role` (owner/admin/member/viewer)
|
|
- Unique constraint on (organization_id, user_id)
|
|
- ✅ **project_collaborators** - Project-level permissions
|
|
- `id`, `project_id`, `user_id`, `role`, `permissions` (jsonb)
|
|
- Unique constraint on (project_id, user_id)
|
|
- CASCADE on project deletion
|
|
|
|
#### Existing Tables Updated:
|
|
Added nullable `organization_id` column to:
|
|
- ✅ `projects` (also added `owner_user_id` for standardization)
|
|
- ✅ `aethex_projects`
|
|
- ✅ `marketplace_listings`
|
|
- ✅ `marketplace_transactions`
|
|
- ✅ `files`
|
|
- ✅ `custom_apps`
|
|
- ✅ `aethex_sites`
|
|
- ✅ `aethex_opportunities`
|
|
- ✅ `aethex_events`
|
|
|
|
All with foreign key constraints (ON DELETE RESTRICT) and indexes.
|
|
|
|
---
|
|
|
|
### 2. SQL Migrations
|
|
|
|
#### Migration 0004: Organizations & Collaborators
|
|
File: `/migrations/0004_multi_tenancy_organizations.sql`
|
|
- Creates `organizations`, `organization_members`, `project_collaborators` tables
|
|
- Adds foreign key constraints
|
|
- Creates indexes for common queries
|
|
|
|
#### Migration 0005: Organization FKs
|
|
File: `/migrations/0005_add_organization_fks.sql`
|
|
- Adds `organization_id` columns to all entity tables
|
|
- Creates foreign keys with ON DELETE RESTRICT
|
|
- Adds indexes for org-scoped queries
|
|
- Backfills `projects.owner_user_id` from existing data
|
|
|
|
#### Backfill Script
|
|
File: `/script/backfill-organizations.ts`
|
|
- Creates default organization for each existing user
|
|
- Format: `"<display_name>'s Workspace"`
|
|
- Generates unique slugs
|
|
- Adds user as organization owner
|
|
- Backfills `organization_id` for user's existing entities
|
|
|
|
---
|
|
|
|
### 3. Server Middleware (`server/org-middleware.ts`)
|
|
|
|
#### Middleware Functions:
|
|
- ✅ **attachOrgContext** - Non-blocking middleware that:
|
|
- Reads org ID from `x-org-id` header or session
|
|
- Falls back to user's first/default org
|
|
- Verifies membership and attaches `req.orgId`, `req.orgRole`
|
|
- ✅ **requireOrgMember** - Blocks requests without org membership
|
|
- ✅ **requireOrgRole(minRole)** - Enforces role hierarchy (viewer < member < admin < owner)
|
|
|
|
#### Helper Functions:
|
|
- ✅ **assertProjectAccess(projectId, userId, minRole)** - Checks:
|
|
- Project ownership
|
|
- Collaborator role
|
|
- Organization membership (if project is in an org)
|
|
|
|
---
|
|
|
|
### 4. Server API Routes (`server/routes.ts`)
|
|
|
|
#### Organization Routes:
|
|
- ✅ `GET /api/orgs` - List user's organizations
|
|
- ✅ `POST /api/orgs` - Create new organization (auto-adds creator as owner)
|
|
- ✅ `GET /api/orgs/:slug` - Get organization details (requires membership)
|
|
- ✅ `GET /api/orgs/:slug/members` - List organization members (requires membership)
|
|
|
|
#### Project Routes (Updated):
|
|
- ✅ `GET /api/projects` - Org-scoped list (admin sees all, users see org projects)
|
|
- ✅ `GET /api/projects/:id` - Access-controlled project fetch
|
|
- ✅ `GET /api/projects/:id/collaborators` - List collaborators (requires contributor role)
|
|
- ✅ `POST /api/projects/:id/collaborators` - Add collaborator (requires admin role)
|
|
- ✅ `PATCH /api/projects/:id/collaborators/:collabId` - Update role/permissions (requires admin)
|
|
- ✅ `DELETE /api/projects/:id/collaborators/:collabId` - Remove collaborator (requires admin)
|
|
|
|
#### Middleware Application:
|
|
```typescript
|
|
app.use("/api/orgs", requireAuth, attachOrgContext);
|
|
app.use("/api/projects", attachOrgContext);
|
|
app.use("/api/files", attachOrgContext);
|
|
app.use("/api/marketplace", attachOrgContext);
|
|
```
|
|
|
|
---
|
|
|
|
### 5. Client Components
|
|
|
|
#### OrgSwitcher Component (`client/src/components/OrgSwitcher.tsx`)
|
|
- Dropdown menu in top nav
|
|
- Lists user's organizations
|
|
- Shows current org with checkmark
|
|
- Stores selection in localStorage
|
|
- Provides hooks:
|
|
- `useCurrentOrgId()` - Get active org ID
|
|
- `useOrgHeaders()` - Get headers for API calls
|
|
|
|
#### Organizations List Page (`client/src/pages/orgs.tsx`)
|
|
- View all user's organizations
|
|
- Create new organization with name + slug
|
|
- Auto-generates slug from name
|
|
- Shows user's role per org
|
|
- Navigation to settings
|
|
|
|
#### Organization Settings Page (`client/src/pages/orgs/settings.tsx`)
|
|
- Tabbed interface: General | Members
|
|
- **General Tab:**
|
|
- Display org name, slug, plan
|
|
- (Edit capabilities noted as "coming soon")
|
|
- **Members Tab:**
|
|
- List all members with avatars
|
|
- Show roles with colored badges + icons
|
|
- Owner (purple/crown), Admin (cyan/shield), Member (slate/user), Viewer (slate/eye)
|
|
|
|
#### Routes Added to App.tsx:
|
|
```tsx
|
|
<Route path="/orgs">{() => <ProtectedRoute><Orgs /></ProtectedRoute>}</Route>
|
|
<Route path="/orgs/:slug/settings">{() => <ProtectedRoute><OrgSettings /></ProtectedRoute>}</Route>
|
|
```
|
|
|
|
---
|
|
|
|
### 6. Documentation
|
|
|
|
#### README_EXPANSION.md Updated
|
|
- Added section: "Multi-Tenancy & Project Ownership"
|
|
- Documented difference between `projects` and `aethex_projects`:
|
|
- **projects**: Canonical internal project graph, org-scoped, full collaboration
|
|
- **aethex_projects**: Public showcase/portfolio, creator-focused
|
|
- Outlined future migration plan to link the two
|
|
|
|
---
|
|
|
|
## 🔧 Usage Guide
|
|
|
|
### For Developers
|
|
|
|
#### Running Migrations:
|
|
```bash
|
|
# Apply migrations
|
|
npx drizzle-kit push
|
|
|
|
# Run backfill script
|
|
npx tsx script/backfill-organizations.ts
|
|
```
|
|
|
|
#### Making Org-Scoped API Calls (Client):
|
|
```tsx
|
|
import { useOrgHeaders } from "@/components/OrgSwitcher";
|
|
|
|
function MyComponent() {
|
|
const orgHeaders = useOrgHeaders();
|
|
|
|
const { data } = useQuery({
|
|
queryKey: ["/api/projects"],
|
|
queryFn: async () => {
|
|
const res = await fetch("/api/projects", {
|
|
credentials: "include",
|
|
headers: orgHeaders, // Adds x-org-id header
|
|
});
|
|
return res.json();
|
|
},
|
|
});
|
|
}
|
|
```
|
|
|
|
#### Checking Project Access (Server):
|
|
```typescript
|
|
import { assertProjectAccess } from "./org-middleware.js";
|
|
|
|
app.get("/api/projects/:id/some-action", requireAuth, async (req, res) => {
|
|
const accessCheck = await assertProjectAccess(
|
|
req.params.id,
|
|
req.session.userId!,
|
|
'contributor' // minimum required role
|
|
);
|
|
|
|
if (!accessCheck.hasAccess) {
|
|
return res.status(403).json({ error: accessCheck.reason });
|
|
}
|
|
|
|
// Proceed with action
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 🚀 What's Next (Future Work)
|
|
|
|
### Phase 2: Full Org Scoping
|
|
- Scope `aethex_sites`, `aethex_opportunities`, `aethex_events` list endpoints
|
|
- Add org filtering to admin dashboards
|
|
- Implement org-wide analytics
|
|
|
|
### Phase 3: Advanced Permissions
|
|
- Granular permissions matrix (read/write/delete per resource)
|
|
- Project templates and cloning
|
|
- Org-level roles (billing admin, content moderator, etc.)
|
|
|
|
### Phase 4: Billing & Plans
|
|
- Integrate Stripe for org subscriptions
|
|
- Enforce feature limits per plan (free/pro/enterprise)
|
|
- Usage metering and billing dashboard
|
|
|
|
### Phase 5: Invitations & Discovery
|
|
- Email-based invitations to join organizations
|
|
- Invite links with tokens
|
|
- Public org directory (for discoverability)
|
|
- Transfer ownership flows
|
|
|
|
---
|
|
|
|
## 📊 Architecture Decisions
|
|
|
|
### Why Nullable `organization_id` First?
|
|
- **Safety**: Existing data remains intact
|
|
- **Gradual Migration**: Users can operate without orgs initially
|
|
- **Backfill-Ready**: Script can populate later without breaking changes
|
|
|
|
### Why RESTRICT on Delete?
|
|
- **Data Safety**: Accidental org deletion won't cascade delete all projects
|
|
- **Audit Trail**: Forces manual cleanup or archive before deletion
|
|
- **Future Proof**: Can implement "soft delete" or "archive" later
|
|
|
|
### Why Separate `project_collaborators`?
|
|
- **Flexibility**: Collaborators can differ from org members
|
|
- **Granular Control**: Project-level permissions independent of org roles
|
|
- **Cross-Org Collaboration**: Future support for external collaborators
|
|
|
|
### Why Keep Legacy Columns (`user_id`, `owner_id`)?
|
|
- **Backwards Compatibility**: Existing code paths still work
|
|
- **Migration Safety**: Can validate new columns before removing old ones
|
|
- **Rollback Path**: Easy to revert if issues found
|
|
|
|
---
|
|
|
|
## ✅ Testing Checklist
|
|
|
|
- [ ] Run migrations on clean database
|
|
- [ ] Run backfill script with existing user data
|
|
- [ ] Create organization via UI
|
|
- [ ] Invite member to organization (manual DB insert for now)
|
|
- [ ] Switch between orgs using OrgSwitcher
|
|
- [ ] Verify projects are scoped to selected org
|
|
- [ ] Add collaborator to project
|
|
- [ ] Verify access control (viewer vs admin)
|
|
- [ ] Check that legacy `projects` queries still work (admin routes)
|
|
|
|
---
|
|
|
|
## 📝 Migration Checklist for Production
|
|
|
|
1. **Pre-Migration:**
|
|
- [ ] Backup database
|
|
- [ ] Review all foreign key constraints
|
|
- [ ] Test migrations on staging
|
|
|
|
2. **Migration:**
|
|
- [ ] Run 0004_multi_tenancy_organizations.sql
|
|
- [ ] Run 0005_add_organization_fks.sql
|
|
- [ ] Run backfill-organizations.ts script
|
|
- [ ] Verify all users have default org
|
|
|
|
3. **Post-Migration:**
|
|
- [ ] Verify existing projects still accessible
|
|
- [ ] Check org member counts
|
|
- [ ] Test org switching in UI
|
|
- [ ] Monitor for access control issues
|
|
|
|
4. **Cleanup (Later):**
|
|
- [ ] Once validated, make `organization_id` NOT NULL
|
|
- [ ] Drop legacy columns (`user_id`, `owner_id` from projects)
|
|
- [ ] Update all queries to use new columns
|
|
|
|
---
|
|
|
|
**Status:** ✅ Multi-tenancy foundation complete and ready for use.
|
|
|