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.
114 lines
3.1 KiB
TypeScript
114 lines
3.1 KiB
TypeScript
import { eq } from 'drizzle-orm'
|
|
import type { PostgresJsDatabase } from 'drizzle-orm/postgres-js'
|
|
import { ForbiddenError } from '../lib/errors.js'
|
|
import {
|
|
inventoryUnitStatuses,
|
|
itemConditions,
|
|
SYSTEM_UNIT_STATUSES,
|
|
SYSTEM_ITEM_CONDITIONS,
|
|
} from '../db/schema/lookups.js'
|
|
import type { LookupCreateInput, LookupUpdateInput } from '@forte/shared/schemas'
|
|
|
|
function createLookupService(
|
|
table: typeof inventoryUnitStatuses | typeof itemConditions,
|
|
systemSeeds: ReadonlyArray<{ slug: string; name: string; description: string; sortOrder: number }>,
|
|
) {
|
|
return {
|
|
async seedDefaults(db: PostgresJsDatabase<any>) {
|
|
const existing = await db
|
|
.select()
|
|
.from(table)
|
|
.where(eq(table.isSystem, true))
|
|
.limit(1)
|
|
|
|
if (existing.length > 0) return // already seeded
|
|
|
|
await db.insert(table).values(
|
|
systemSeeds.map((seed) => ({
|
|
...seed,
|
|
isSystem: true,
|
|
})),
|
|
)
|
|
},
|
|
|
|
async list(db: PostgresJsDatabase<any>) {
|
|
return db
|
|
.select()
|
|
.from(table)
|
|
.where(eq(table.isActive, true))
|
|
.orderBy(table.sortOrder)
|
|
},
|
|
|
|
async getBySlug(db: PostgresJsDatabase<any>, slug: string) {
|
|
const [row] = await db
|
|
.select()
|
|
.from(table)
|
|
.where(eq(table.slug, slug))
|
|
.limit(1)
|
|
return row ?? null
|
|
},
|
|
|
|
async create(db: PostgresJsDatabase<any>, input: LookupCreateInput) {
|
|
const [row] = await db
|
|
.insert(table)
|
|
.values({
|
|
name: input.name,
|
|
slug: input.slug,
|
|
description: input.description,
|
|
sortOrder: input.sortOrder,
|
|
isSystem: false,
|
|
})
|
|
.returning()
|
|
return row
|
|
},
|
|
|
|
async update(db: PostgresJsDatabase<any>, id: string, input: LookupUpdateInput) {
|
|
// Prevent modifying system rows' slug or system flag
|
|
const existing = await db
|
|
.select()
|
|
.from(table)
|
|
.where(eq(table.id, id))
|
|
.limit(1)
|
|
|
|
if (!existing[0]) return null
|
|
if (existing[0].isSystem && input.isActive === false) {
|
|
throw new ForbiddenError('Cannot deactivate a system status')
|
|
}
|
|
|
|
const [row] = await db
|
|
.update(table)
|
|
.set(input)
|
|
.where(eq(table.id, id))
|
|
.returning()
|
|
return row ?? null
|
|
},
|
|
|
|
async delete(db: PostgresJsDatabase<any>, id: string) {
|
|
const existing = await db
|
|
.select()
|
|
.from(table)
|
|
.where(eq(table.id, id))
|
|
.limit(1)
|
|
|
|
if (!existing[0]) return null
|
|
if (existing[0].isSystem) {
|
|
throw new ForbiddenError('Cannot delete a system status')
|
|
}
|
|
|
|
const [row] = await db
|
|
.delete(table)
|
|
.where(eq(table.id, id))
|
|
.returning()
|
|
return row ?? null
|
|
},
|
|
|
|
async validateSlug(db: PostgresJsDatabase<any>, slug: string): Promise<boolean> {
|
|
const row = await this.getBySlug(db, slug)
|
|
return row !== null && row.isActive
|
|
},
|
|
}
|
|
}
|
|
|
|
export const UnitStatusService = createLookupService(inventoryUnitStatuses, SYSTEM_UNIT_STATUSES)
|
|
export const ItemConditionService = createLookupService(itemConditions, SYSTEM_ITEM_CONDITIONS)
|