feat: zentrale selfhost-dienste bereitstellen

This commit is contained in:
2026-08-02 16:34:10 +02:00
parent d3ad53bcf0
commit 8b8e0c97d3
30 changed files with 1818 additions and 74 deletions

View File

@@ -1,5 +1,6 @@
import {
boolean,
bigint,
index,
integer,
jsonb,
@@ -19,6 +20,8 @@ export const deviceStatus = pgEnum("device_status", ["active", "disabled", "inva
export const deliveryStatus = pgEnum("delivery_status", ["accepted", "processing", "completed", "failed", "partial"]);
export const attemptStatus = pgEnum("attempt_status", ["pending", "sent", "failed", "skipped"]);
export const attemptProvider = pgEnum("attempt_provider", ["web_push", "apns", "fcm"]);
export const centralService = pgEnum("central_service", ["ai", "banking"]);
export const usageStatus = pgEnum("usage_status", ["succeeded", "failed"]);
export const pushInstances = pgTable(
"push_instances",
@@ -144,6 +147,51 @@ export const auditLogs = pgTable(
}),
);
export const serviceEntitlements = pgTable(
"service_entitlements",
{
id: uuid("id").primaryKey().defaultRandom(),
instanceId: uuid("instance_id")
.notNull()
.references(() => pushInstances.id, { onDelete: "cascade" }),
service: centralService("service").notNull(),
enabled: boolean("enabled").notNull().default(false),
monthlyLimit: bigint("monthly_limit", { mode: "number" }),
unitPriceMicros: bigint("unit_price_micros", { mode: "number" }).notNull().default(0),
metadata: jsonb("metadata").$type<Record<string, unknown>>().notNull().default(sql`'{}'::jsonb`),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
},
(table) => ({
instanceServiceIdx: uniqueIndex("service_entitlements_instance_service_idx").on(table.instanceId, table.service),
}),
);
export const serviceUsageEvents = pgTable(
"service_usage_events",
{
id: uuid("id").primaryKey().defaultRandom(),
requestId: text("request_id").notNull().unique(),
instanceId: uuid("instance_id")
.notNull()
.references(() => pushInstances.id, { onDelete: "cascade" }),
service: centralService("service").notNull(),
operation: text("operation").notNull(),
units: bigint("units", { mode: "number" }).notNull().default(1),
costMicros: bigint("cost_micros", { mode: "number" }).notNull().default(0),
status: usageStatus("status").notNull(),
provider: text("provider"),
providerRequestId: text("provider_request_id"),
errorCode: text("error_code"),
metadata: jsonb("metadata").$type<Record<string, unknown>>().notNull().default(sql`'{}'::jsonb`),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
},
(table) => ({
requestIdIdx: uniqueIndex("service_usage_events_request_id_idx").on(table.requestId),
instanceServiceCreatedIdx: index("service_usage_events_instance_service_created_idx").on(table.instanceId, table.service, table.createdAt),
}),
);
export type PushInstance = typeof pushInstances.$inferSelect;
export type NewPushInstance = typeof pushInstances.$inferInsert;
export type PushDevice = typeof pushDevices.$inferSelect;
@@ -152,3 +200,5 @@ export type DeliveryJob = typeof deliveryJobs.$inferSelect;
export type NewDeliveryJob = typeof deliveryJobs.$inferInsert;
export type DeliveryAttempt = typeof deliveryAttempts.$inferSelect;
export type NewDeliveryAttempt = typeof deliveryAttempts.$inferInsert;
export type ServiceEntitlement = typeof serviceEntitlements.$inferSelect;
export type ServiceUsageEvent = typeof serviceUsageEvents.$inferSelect;