Remove multi-tenant company_id scoping from entire codebase

Drop company_id column from all 22 domain tables via migration.
Remove companyId from JWT payload, auth plugins, all service method
signatures (~215 occurrences), all route handlers (~105 occurrences),
test runner, test suites, and frontend auth store/types.

The company table stays as store settings (name, timezone). Tenant
isolation in a SaaS deployment would be at the database level (one
DB per customer) not the application level.

All 107 API tests pass. Zero TSC errors across all packages.
This commit is contained in:
Ryan Moon
2026-03-29 14:58:33 -05:00
parent 55f8591cf1
commit d36c6f7135
35 changed files with 353 additions and 511 deletions

View File

@@ -11,7 +11,6 @@ interface User {
interface AuthState {
token: string | null
user: User | null
companyId: string | null
permissions: Set<string>
permissionsLoaded: boolean
setAuth: (token: string, user: User) => void
@@ -49,12 +48,7 @@ function expandPermissions(slugs: string[]): Set<string> {
return expanded
}
function decodeJwtPayload(token: string): { id: string; companyId: string; role: string } {
const payload = token.split('.')[1]
return JSON.parse(atob(payload))
}
function loadSession(): { token: string; user: User; companyId: string; permissions?: string[] } | null {
function loadSession(): { token: string; user: User; permissions?: string[] } | null {
try {
const raw = sessionStorage.getItem('forte-auth')
if (!raw) return null
@@ -64,8 +58,8 @@ function loadSession(): { token: string; user: User; companyId: string; permissi
}
}
function saveSession(token: string, user: User, companyId: string, permissions?: string[]) {
sessionStorage.setItem('forte-auth', JSON.stringify({ token, user, companyId, permissions }))
function saveSession(token: string, user: User, permissions?: string[]) {
sessionStorage.setItem('forte-auth', JSON.stringify({ token, user, permissions }))
}
function clearSession() {
@@ -78,22 +72,19 @@ export const useAuthStore = create<AuthState>((set, get) => {
return {
token: initial?.token ?? null,
user: initial?.user ?? null,
companyId: initial?.companyId ?? null,
permissions: initialPerms,
permissionsLoaded: initialPerms.size > 0,
setAuth: (token, user) => {
const payload = decodeJwtPayload(token)
saveSession(token, user, payload.companyId)
set({ token, user, companyId: payload.companyId })
saveSession(token, user)
set({ token, user })
},
setPermissions: (slugs: string[]) => {
const expanded = expandPermissions(slugs)
// Update session storage to include permissions
const { token, user, companyId } = get()
if (token && user && companyId) {
saveSession(token, user, companyId, slugs)
const { token, user } = get()
if (token && user) {
saveSession(token, user, slugs)
}
set({ permissions: expanded, permissionsLoaded: true })
},
@@ -104,6 +95,6 @@ export const useAuthStore = create<AuthState>((set, get) => {
logout: () => {
clearSession()
set({ token: null, user: null, companyId: null, permissions: new Set(), permissionsLoaded: false })
set({ token: null, user: null, permissions: new Set(), permissionsLoaded: false })
},
}})

View File

@@ -1,6 +1,5 @@
export interface Account {
id: string
companyId: string
accountNumber: string | null
name: string
primaryMemberId: string | null
@@ -25,7 +24,6 @@ export interface Account {
export interface Member {
id: string
accountId: string
companyId: string
memberNumber: string | null
firstName: string
lastName: string
@@ -42,7 +40,6 @@ export interface Member {
export interface ProcessorLink {
id: string
accountId: string
companyId: string
processor: 'stripe' | 'global_payments'
processorCustomerId: string
isActive: boolean
@@ -52,7 +49,6 @@ export interface ProcessorLink {
export interface PaymentMethod {
id: string
accountId: string
companyId: string
processor: 'stripe' | 'global_payments'
processorPaymentMethodId: string
cardBrand: string | null
@@ -67,7 +63,6 @@ export interface PaymentMethod {
export interface MemberIdentifier {
id: string
memberId: string
companyId: string
type: 'drivers_license' | 'passport' | 'school_id'
label: string | null
value: string
@@ -85,7 +80,6 @@ export interface MemberIdentifier {
export interface TaxExemption {
id: string
accountId: string
companyId: string
status: 'none' | 'pending' | 'approved'
certificateNumber: string
certificateType: string | null

View File

@@ -1,6 +1,5 @@
export interface RepairTicket {
id: string
companyId: string
locationId: string | null
repairBatchId: string | null
ticketNumber: string | null
@@ -42,7 +41,6 @@ export interface RepairLineItem {
export interface RepairBatch {
id: string
companyId: string
locationId: string | null
batchNumber: string | null
accountId: string
@@ -80,7 +78,6 @@ export interface RepairNote {
export interface RepairServiceTemplate {
id: string
companyId: string
name: string
instrumentType: string | null
size: string | null