Initial commit: add .gitignore and README
Some checks failed
CI / lint-and-test (push) Has been cancelled
Some checks failed
CI / lint-and-test (push) Has been cancelled
This commit is contained in:
12
packages/schema/src/db/client.ts
Normal file
12
packages/schema/src/db/client.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { drizzle } from "drizzle-orm/postgres-js";
|
||||
import postgres from "postgres";
|
||||
import * as schema from "./schema.js";
|
||||
|
||||
const connectionString = process.env.DATABASE_URL ?? "postgres://sankofa:sankofa_dev@localhost:5432/sankofa";
|
||||
|
||||
export function getDb() {
|
||||
const client = postgres(connectionString, { max: 10 });
|
||||
return drizzle(client, { schema });
|
||||
}
|
||||
|
||||
export type Db = ReturnType<typeof getDb>;
|
||||
10
packages/schema/src/db/schema.test.ts
Normal file
10
packages/schema/src/db/schema.test.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { vendors, assets, auditEvents } from "./schema";
|
||||
|
||||
describe("schema", () => {
|
||||
it("exports core tables", () => {
|
||||
expect(vendors).toBeDefined();
|
||||
expect(assets).toBeDefined();
|
||||
expect(auditEvents).toBeDefined();
|
||||
});
|
||||
});
|
||||
417
packages/schema/src/db/schema.ts
Normal file
417
packages/schema/src/db/schema.ts
Normal file
@@ -0,0 +1,417 @@
|
||||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
text,
|
||||
timestamp,
|
||||
decimal,
|
||||
integer,
|
||||
jsonb,
|
||||
boolean,
|
||||
primaryKey,
|
||||
index,
|
||||
} from "drizzle-orm/pg-core";
|
||||
|
||||
// --- Org & users ---
|
||||
export const orgUnits = pgTable("org_units", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
name: text("name").notNull(),
|
||||
parentId: uuid("parent_id"),
|
||||
orgId: text("org_id").notNull(), // tenancy
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
});
|
||||
|
||||
export const users = pgTable("users", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
email: text("email").notNull().unique(),
|
||||
name: text("name"),
|
||||
orgUnitId: uuid("org_unit_id").references((): any => orgUnits.id),
|
||||
orgId: text("org_id").notNull(),
|
||||
vendorId: uuid("vendor_id").references((): any => vendors.id), // when set, user is a vendor user (login for that vendor)
|
||||
externalId: text("external_id"), // SSO
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
});
|
||||
|
||||
// --- Vendors ---
|
||||
export const vendors = pgTable("vendors", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
orgId: text("org_id").notNull(),
|
||||
legalName: text("legal_name").notNull(),
|
||||
contacts: jsonb("contacts").$type<{ email?: string; phone?: string; name?: string }[]>(),
|
||||
trustTier: text("trust_tier").notNull().default("unknown"), // unknown | low | medium | high
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
});
|
||||
|
||||
export const vendorBankDetails = pgTable("vendor_bank_details", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
vendorId: uuid("vendor_id").notNull().references((): any => vendors.id),
|
||||
version: integer("version").notNull().default(1),
|
||||
instructions: jsonb("instructions").$type<Record<string, unknown>>(),
|
||||
approvedBy1: uuid("approved_by_1"),
|
||||
approvedBy2: uuid("approved_by_2"),
|
||||
approvedAt: timestamp("approved_at"),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
});
|
||||
|
||||
// --- Offers ---
|
||||
export const offers = pgTable(
|
||||
"offers",
|
||||
{
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
orgId: text("org_id").notNull(),
|
||||
vendorId: uuid("vendor_id").references((): any => vendors.id), // null when ingested and not yet assigned
|
||||
sku: text("sku"),
|
||||
mpn: text("mpn"),
|
||||
quantity: integer("quantity").notNull(),
|
||||
unitPrice: decimal("unit_price", { precision: 20, scale: 4 }).notNull(),
|
||||
incoterms: text("incoterms"),
|
||||
leadTimeDays: integer("lead_time_days"),
|
||||
countryOfOrigin: text("country_of_origin"),
|
||||
condition: text("condition"),
|
||||
warranty: text("warranty"),
|
||||
evidenceRefs: jsonb("evidence_refs").$type<{ key: string; hash?: string }[]>(),
|
||||
riskScore: decimal("risk_score", { precision: 5, scale: 2 }),
|
||||
riskFactors: jsonb("risk_factors").$type<Record<string, unknown>>(),
|
||||
status: text("status").notNull().default("draft"), // draft | rejected | clarification | advanced
|
||||
source: text("source").notNull().default("manual"), // manual | scraped | email
|
||||
sourceRef: text("source_ref"), // URL or email message id
|
||||
sourceMetadata: jsonb("source_metadata").$type<Record<string, unknown>>(), // parsed sender, subject, page url, etc.
|
||||
ingestedAt: timestamp("ingested_at"),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
},
|
||||
(t) => [index("offers_vendor_idx").on(t.vendorId), index("offers_org_idx").on(t.orgId), index("offers_source_idx").on(t.source)]
|
||||
);
|
||||
|
||||
// --- Purchase orders ---
|
||||
export const purchaseOrders = pgTable(
|
||||
"purchase_orders",
|
||||
{
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
orgId: text("org_id").notNull(),
|
||||
vendorId: uuid("vendor_id").notNull().references((): any => vendors.id),
|
||||
lineItems: jsonb("line_items").$type<{ offerId?: string; sku?: string; quantity: number; unitPrice: string }[]>().notNull(),
|
||||
status: text("status").notNull().default("draft"), // draft | pending_approval | approved | rejected | ordered | received
|
||||
approvalStage: text("approval_stage"),
|
||||
escrowTerms: text("escrow_terms"),
|
||||
inspectionSiteId: uuid("inspection_site_id"),
|
||||
deliverySiteId: uuid("delivery_site_id"),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
},
|
||||
(t) => [index("po_vendor_idx").on(t.vendorId), index("po_org_idx").on(t.orgId)]
|
||||
);
|
||||
|
||||
// --- Shipments ---
|
||||
export const shipments = pgTable("shipments", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
orgId: text("org_id").notNull(),
|
||||
purchaseOrderId: uuid("purchase_order_id").notNull().references((): any => purchaseOrders.id),
|
||||
tracking: text("tracking"),
|
||||
cartonsPallets: jsonb("cartons_pallets").$type<Record<string, unknown>>(),
|
||||
customsDocsRefs: jsonb("customs_docs_refs").$type<{ key: string }[]>(),
|
||||
status: text("status").notNull().default("pending"),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
});
|
||||
|
||||
// --- Sites & racks ---
|
||||
export const regions = pgTable("regions", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
orgId: text("org_id").notNull(),
|
||||
name: text("name").notNull(),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
});
|
||||
|
||||
export const sites = pgTable(
|
||||
"sites",
|
||||
{
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
orgId: text("org_id").notNull(),
|
||||
regionId: uuid("region_id").references((): any => regions.id),
|
||||
name: text("name").notNull(),
|
||||
address: text("address"),
|
||||
networkMetadata: jsonb("network_metadata").$type<{ uplinks?: string[]; vlans?: string[]; portProfiles?: string[]; ipRanges?: string[] }>(),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
},
|
||||
(t) => [index("sites_org_idx").on(t.orgId)]
|
||||
);
|
||||
|
||||
export const rooms = pgTable("rooms", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
siteId: uuid("site_id").notNull().references((): any => sites.id),
|
||||
name: text("name").notNull(),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
});
|
||||
|
||||
export const rows = pgTable("rows", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
roomId: uuid("room_id").notNull().references((): any => rooms.id),
|
||||
name: text("name").notNull(),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
});
|
||||
|
||||
export const racks = pgTable(
|
||||
"racks",
|
||||
{
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
rowId: uuid("row_id").notNull().references((): any => rows.id),
|
||||
name: text("name").notNull(),
|
||||
ruTotal: integer("ru_total").notNull(),
|
||||
powerFeeds: jsonb("power_feeds").$type<{ feed: string; pduModel?: string; circuitLimitWatts?: number }[]>(),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
},
|
||||
(t) => [index("racks_row_idx").on(t.rowId)]
|
||||
);
|
||||
|
||||
export const positions = pgTable(
|
||||
"positions",
|
||||
{
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
rackId: uuid("rack_id").notNull().references((): any => racks.id),
|
||||
ruStart: integer("ru_start").notNull(),
|
||||
ruEnd: integer("ru_end").notNull(),
|
||||
assetId: uuid("asset_id"), // FK to assets.id set in migration if needed
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
},
|
||||
(t) => [index("positions_rack_idx").on(t.rackId)]
|
||||
);
|
||||
|
||||
// --- Assets ---
|
||||
export const assets = pgTable(
|
||||
"assets",
|
||||
{
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
orgId: text("org_id").notNull(),
|
||||
assetId: text("asset_id").notNull().unique(), // business identifier
|
||||
category: text("category").notNull(), // server | gpu | cpu | dimm | nic | ssd
|
||||
manufacturerSerial: text("manufacturer_serial"),
|
||||
serviceTag: text("service_tag"),
|
||||
macs: jsonb("macs").$type<string[]>(),
|
||||
wwns: jsonb("wwns").$type<string[]>(),
|
||||
partNumber: text("part_number"),
|
||||
condition: text("condition"),
|
||||
warranty: text("warranty"),
|
||||
proofArtifactRefs: jsonb("proof_artifact_refs").$type<{ key: string; hash?: string }[]>(),
|
||||
siteId: uuid("site_id").references((): any => sites.id),
|
||||
positionId: uuid("position_id").references((): any => positions.id),
|
||||
ownerId: uuid("owner_id").references((): any => users.id),
|
||||
projectId: text("project_id"),
|
||||
sensitivityTier: text("sensitivity_tier"),
|
||||
status: text("status").notNull().default("pending"), // pending | received | staged | provisioned | in_use | maintenance | decommissioned
|
||||
chainOfCustody: jsonb("chain_of_custody").$type<{ who: string; where?: string; when: string }[]>(),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
},
|
||||
(t) => [
|
||||
index("assets_org_idx").on(t.orgId),
|
||||
index("assets_site_idx").on(t.siteId),
|
||||
index("assets_category_idx").on(t.category),
|
||||
index("assets_status_idx").on(t.status),
|
||||
]
|
||||
);
|
||||
|
||||
// Asset -> components (server has many GPUs/CPUs/DIMMs/NICs)
|
||||
export const assetComponents = pgTable(
|
||||
"asset_components",
|
||||
{
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
parentAssetId: uuid("parent_asset_id").notNull().references((): any => assets.id),
|
||||
childAssetId: uuid("child_asset_id").notNull().references((): any => assets.id),
|
||||
role: text("role").notNull(), // gpu | cpu | dimm | nic | ssd
|
||||
slotIndex: integer("slot_index"),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
},
|
||||
(t) => [
|
||||
index("asset_components_parent_idx").on(t.parentAssetId),
|
||||
index("asset_components_child_idx").on(t.childAssetId),
|
||||
]
|
||||
);
|
||||
|
||||
// Provisioning (OS image, hypervisor, cluster)
|
||||
export const provisioningRecords = pgTable("provisioning_records", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
assetId: uuid("asset_id").notNull().references((): any => assets.id),
|
||||
osImage: text("os_image"),
|
||||
hypervisorNode: text("hypervisor_node"),
|
||||
clusterId: text("cluster_id"),
|
||||
metadata: jsonb("metadata").$type<Record<string, unknown>>(),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
});
|
||||
|
||||
// Inspection templates and runs
|
||||
export const inspectionTemplates = pgTable("inspection_templates", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
orgId: text("org_id").notNull(),
|
||||
category: text("category").notNull(), // server | gpu | memory | nic
|
||||
name: text("name").notNull(),
|
||||
steps: jsonb("steps").$type<{ id: string; label: string; required?: boolean }[]>().notNull(),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
});
|
||||
|
||||
export const inspectionRuns = pgTable("inspection_runs", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
orgId: text("org_id").notNull(),
|
||||
templateId: uuid("template_id").notNull().references((): any => inspectionTemplates.id),
|
||||
offerId: uuid("offer_id").references((): any => offers.id),
|
||||
assetId: uuid("asset_id").references((): any => assets.id),
|
||||
status: text("status").notNull().default("in_progress"), // in_progress | pass | fail
|
||||
evidenceRefs: jsonb("evidence_refs").$type<{ key: string; hash?: string }[]>(),
|
||||
resultNotes: text("result_notes"),
|
||||
completedAt: timestamp("completed_at"),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
});
|
||||
|
||||
// Compliance profiles (firmware freeze, hardware class, approved SKUs per sovereign)
|
||||
export const complianceProfiles = pgTable(
|
||||
"compliance_profiles",
|
||||
{
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
orgId: text("org_id").notNull(),
|
||||
name: text("name").notNull(),
|
||||
firmwareFreezePolicy: jsonb("firmware_freeze_policy").$type<{ lockedVersion?: string; minVersion?: string; maxVersion?: string }>(),
|
||||
allowedGenerations: jsonb("allowed_generations").$type<string[]>(),
|
||||
approvedSkus: jsonb("approved_skus").$type<string[]>(),
|
||||
siteId: uuid("site_id").references((): any => sites.id),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
},
|
||||
(t) => [index("compliance_profiles_org_idx").on(t.orgId)]
|
||||
);
|
||||
|
||||
// UniFi Product Intelligence (SKU -> generation, EoL, support horizon)
|
||||
export const unifiProductCatalog = pgTable(
|
||||
"unifi_product_catalog",
|
||||
{
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
sku: text("sku").notNull().unique(),
|
||||
modelName: text("model_name").notNull(),
|
||||
generation: text("generation").notNull(), // Gen1 | Gen2 | Enterprise
|
||||
performanceClass: text("performance_class"),
|
||||
eolDate: text("eol_date"), // ISO date or null
|
||||
supportHorizon: text("support_horizon"),
|
||||
approvedSovereignDefault: boolean("approved_sovereign_default").notNull().default(false),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
},
|
||||
(t) => [index("unifi_catalog_sku_idx").on(t.sku), index("unifi_catalog_generation_idx").on(t.generation)]
|
||||
);
|
||||
|
||||
// UniFi controller registry (topology and role; credentials in Vault)
|
||||
export const unifiControllers = pgTable("unifi_controllers", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
orgId: text("org_id").notNull(),
|
||||
siteId: uuid("site_id").references((): any => sites.id),
|
||||
baseUrl: text("base_url").notNull(),
|
||||
role: text("role").notNull(), // sovereign_write | oversight_read_only
|
||||
region: text("region"),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
});
|
||||
|
||||
// Integration mappings (Asset <-> external system ID)
|
||||
export const integrationMappings = pgTable("integration_mappings", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
orgId: text("org_id").notNull(),
|
||||
assetId: uuid("asset_id").references((): any => assets.id),
|
||||
siteId: uuid("site_id").references((): any => sites.id),
|
||||
provider: text("provider").notNull(), // unifi | proxmox | redfish
|
||||
externalId: text("external_id").notNull(),
|
||||
metadata: jsonb("metadata").$type<Record<string, unknown>>(),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
});
|
||||
|
||||
// Maintenance / RMA
|
||||
export const maintenances = pgTable("maintenances", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
orgId: text("org_id").notNull(),
|
||||
assetId: uuid("asset_id").notNull().references((): any => assets.id),
|
||||
type: text("type").notNull(), // incident | rma | part_swap
|
||||
vendorTicketRef: text("vendor_ticket_ref"),
|
||||
description: text("description"),
|
||||
status: text("status").notNull().default("open"),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
});
|
||||
|
||||
// Audit log (append-only, who/when/what/before/after)
|
||||
export const auditEvents = pgTable(
|
||||
"audit_events",
|
||||
{
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
orgId: text("org_id").notNull(),
|
||||
actorId: uuid("actor_id").references((): any => users.id),
|
||||
actorEmail: text("actor_email"),
|
||||
action: text("action").notNull(),
|
||||
resourceType: text("resource_type").notNull(),
|
||||
resourceId: text("resource_id").notNull(),
|
||||
beforeState: jsonb("before_state").$type<Record<string, unknown>>(),
|
||||
afterState: jsonb("after_state").$type<Record<string, unknown>>(),
|
||||
occurredAt: timestamp("occurred_at").defaultNow().notNull(),
|
||||
},
|
||||
(t) => [
|
||||
index("audit_events_org_idx").on(t.orgId),
|
||||
index("audit_events_resource_idx").on(t.resourceType, t.resourceId),
|
||||
index("audit_events_occurred_idx").on(t.occurredAt),
|
||||
]
|
||||
);
|
||||
|
||||
// Roles and permissions (RBAC)
|
||||
export const roles = pgTable("roles", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
name: text("name").notNull().unique(),
|
||||
description: text("description"),
|
||||
permissions: jsonb("permissions").$type<string[]>().notNull().default([]),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
});
|
||||
|
||||
export const userRoles = pgTable(
|
||||
"user_roles",
|
||||
{
|
||||
userId: uuid("user_id").notNull().references((): any => users.id),
|
||||
roleId: uuid("role_id").notNull().references((): any => roles.id),
|
||||
scopeSiteId: uuid("scope_site_id"),
|
||||
scopeProjectId: text("scope_project_id"),
|
||||
assignedAt: timestamp("assigned_at").defaultNow().notNull(),
|
||||
},
|
||||
(t) => [primaryKey({ columns: [t.userId, t.roleId] })]
|
||||
);
|
||||
|
||||
export type OrgUnit = typeof orgUnits.$inferSelect;
|
||||
export type User = typeof users.$inferSelect;
|
||||
export type Vendor = typeof vendors.$inferSelect;
|
||||
export type Offer = typeof offers.$inferSelect;
|
||||
export type PurchaseOrder = typeof purchaseOrders.$inferSelect;
|
||||
export type Shipment = typeof shipments.$inferSelect;
|
||||
export type Region = typeof regions.$inferSelect;
|
||||
export type Site = typeof sites.$inferSelect;
|
||||
export type Room = typeof rooms.$inferSelect;
|
||||
export type Row = typeof rows.$inferSelect;
|
||||
export type Rack = typeof racks.$inferSelect;
|
||||
export type Position = typeof positions.$inferSelect;
|
||||
export type Asset = typeof assets.$inferSelect;
|
||||
export type AssetComponent = typeof assetComponents.$inferSelect;
|
||||
export type ProvisioningRecord = typeof provisioningRecords.$inferSelect;
|
||||
export type InspectionTemplate = typeof inspectionTemplates.$inferSelect;
|
||||
export type InspectionRun = typeof inspectionRuns.$inferSelect;
|
||||
export type ComplianceProfile = typeof complianceProfiles.$inferSelect;
|
||||
export type UnifiController = typeof unifiControllers.$inferSelect;
|
||||
export type UnifiProductCatalog = typeof unifiProductCatalog.$inferSelect;
|
||||
export type IntegrationMapping = typeof integrationMappings.$inferSelect;
|
||||
export type Maintenance = typeof maintenances.$inferSelect;
|
||||
export type AuditEvent = typeof auditEvents.$inferSelect;
|
||||
export type Role = typeof roles.$inferSelect;
|
||||
export type UserRole = typeof userRoles.$inferSelect;
|
||||
2
packages/schema/src/index.ts
Normal file
2
packages/schema/src/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./db/schema.js";
|
||||
export { getDb } from "./db/client.js";
|
||||
Reference in New Issue
Block a user