KI-AGENT: Zentralen Push-Server Stack ergänzen
This commit is contained in:
1
push-server/packages/db/src/index.ts
Normal file
1
push-server/packages/db/src/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./schema.js";
|
||||
154
push-server/packages/db/src/schema.ts
Normal file
154
push-server/packages/db/src/schema.ts
Normal file
@@ -0,0 +1,154 @@
|
||||
import {
|
||||
boolean,
|
||||
index,
|
||||
integer,
|
||||
jsonb,
|
||||
pgEnum,
|
||||
pgTable,
|
||||
text,
|
||||
timestamp,
|
||||
uniqueIndex,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { sql } from "drizzle-orm";
|
||||
|
||||
export const instanceStatus = pgEnum("instance_status", ["active", "blocked", "disabled"]);
|
||||
export const payloadMode = pgEnum("payload_mode", ["minimal", "rich"]);
|
||||
export const devicePlatform = pgEnum("device_platform", ["web", "ios", "android"]);
|
||||
export const deviceStatus = pgEnum("device_status", ["active", "disabled", "invalid"]);
|
||||
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 pushInstances = pgTable(
|
||||
"push_instances",
|
||||
{
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
instanceId: text("instance_id").notNull().unique(),
|
||||
name: text("name").notNull(),
|
||||
baseUrl: text("base_url").notNull(),
|
||||
status: instanceStatus("status").notNull().default("active"),
|
||||
mode: payloadMode("payload_mode").notNull().default("minimal"),
|
||||
capabilities: jsonb("capabilities").$type<string[]>().notNull().default(sql`'[]'::jsonb`),
|
||||
rateLimitPerMinute: integer("rate_limit_per_minute").notNull().default(120),
|
||||
dailyQuota: integer("daily_quota").notNull().default(10000),
|
||||
currentSecretEncrypted: text("current_secret_encrypted").notNull(),
|
||||
currentSecretPreview: text("current_secret_preview").notNull(),
|
||||
nextSecretEncrypted: text("next_secret_encrypted"),
|
||||
nextSecretPreview: text("next_secret_preview"),
|
||||
notes: text("notes"),
|
||||
lastHeartbeatAt: timestamp("last_heartbeat_at", { withTimezone: true }),
|
||||
lastHeartbeatVersion: text("last_heartbeat_version"),
|
||||
lastHeartbeatIp: text("last_heartbeat_ip"),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
instanceIdIdx: uniqueIndex("push_instances_instance_id_idx").on(table.instanceId),
|
||||
statusIdx: index("push_instances_status_idx").on(table.status),
|
||||
}),
|
||||
);
|
||||
|
||||
export const pushDevices = pgTable(
|
||||
"push_devices",
|
||||
{
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
centralDeviceId: text("central_device_id").notNull().unique(),
|
||||
instanceId: uuid("instance_id")
|
||||
.notNull()
|
||||
.references(() => pushInstances.id, { onDelete: "cascade" }),
|
||||
localDeviceId: text("local_device_id").notNull(),
|
||||
platform: devicePlatform("platform").notNull(),
|
||||
status: deviceStatus("status").notNull().default("active"),
|
||||
providerTokenEncrypted: text("provider_token_encrypted"),
|
||||
webPushSubscription: jsonb("web_push_subscription").$type<Record<string, unknown>>(),
|
||||
meta: jsonb("meta").$type<Record<string, unknown>>().notNull().default(sql`'{}'::jsonb`),
|
||||
lastSeenAt: timestamp("last_seen_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
disabledAt: timestamp("disabled_at", { withTimezone: true }),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
centralDeviceIdIdx: uniqueIndex("push_devices_central_device_id_idx").on(table.centralDeviceId),
|
||||
instanceLocalDeviceIdx: uniqueIndex("push_devices_instance_local_device_idx").on(table.instanceId, table.localDeviceId),
|
||||
instanceStatusIdx: index("push_devices_instance_status_idx").on(table.instanceId, table.status),
|
||||
}),
|
||||
);
|
||||
|
||||
export const deliveryJobs = pgTable(
|
||||
"delivery_jobs",
|
||||
{
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
deliveryJobId: text("delivery_job_id").notNull().unique(),
|
||||
instanceId: uuid("instance_id")
|
||||
.notNull()
|
||||
.references(() => pushInstances.id, { onDelete: "cascade" }),
|
||||
idempotencyKey: text("idempotency_key").notNull(),
|
||||
status: deliveryStatus("status").notNull().default("accepted"),
|
||||
priority: text("priority").notNull().default("normal"),
|
||||
collapseKey: text("collapse_key"),
|
||||
ttlSeconds: integer("ttl_seconds").notNull().default(3600),
|
||||
acceptedCount: integer("accepted_count").notNull().default(0),
|
||||
rejectedCount: integer("rejected_count").notNull().default(0),
|
||||
sentCount: integer("sent_count").notNull().default(0),
|
||||
failedCount: integer("failed_count").notNull().default(0),
|
||||
lastErrorCode: text("last_error_code"),
|
||||
lastErrorMessage: text("last_error_message"),
|
||||
completedAt: timestamp("completed_at", { withTimezone: true }),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
deliveryJobIdIdx: uniqueIndex("delivery_jobs_delivery_job_id_idx").on(table.deliveryJobId),
|
||||
instanceIdempotencyIdx: uniqueIndex("delivery_jobs_instance_idempotency_idx").on(table.instanceId, table.idempotencyKey),
|
||||
instanceCreatedIdx: index("delivery_jobs_instance_created_idx").on(table.instanceId, table.createdAt),
|
||||
}),
|
||||
);
|
||||
|
||||
export const deliveryAttempts = pgTable(
|
||||
"delivery_attempts",
|
||||
{
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
deliveryJobId: uuid("delivery_job_id")
|
||||
.notNull()
|
||||
.references(() => deliveryJobs.id, { onDelete: "cascade" }),
|
||||
deviceId: uuid("device_id")
|
||||
.notNull()
|
||||
.references(() => pushDevices.id, { onDelete: "cascade" }),
|
||||
provider: attemptProvider("provider").notNull(),
|
||||
status: attemptStatus("status").notNull().default("pending"),
|
||||
providerMessageId: text("provider_message_id"),
|
||||
errorCode: text("error_code"),
|
||||
errorMessage: text("error_message"),
|
||||
sentAt: timestamp("sent_at", { withTimezone: true }),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
jobIdx: index("delivery_attempts_job_idx").on(table.deliveryJobId),
|
||||
deviceIdx: index("delivery_attempts_device_idx").on(table.deviceId),
|
||||
}),
|
||||
);
|
||||
|
||||
export const auditLogs = pgTable(
|
||||
"audit_logs",
|
||||
{
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
actor: text("actor").notNull(),
|
||||
action: text("action").notNull(),
|
||||
instanceId: uuid("instance_id").references(() => pushInstances.id, { onDelete: "set null" }),
|
||||
meta: jsonb("meta").$type<Record<string, unknown>>().notNull().default(sql`'{}'::jsonb`),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
instanceCreatedIdx: index("audit_logs_instance_created_idx").on(table.instanceId, table.createdAt),
|
||||
}),
|
||||
);
|
||||
|
||||
export type PushInstance = typeof pushInstances.$inferSelect;
|
||||
export type NewPushInstance = typeof pushInstances.$inferInsert;
|
||||
export type PushDevice = typeof pushDevices.$inferSelect;
|
||||
export type NewPushDevice = typeof pushDevices.$inferInsert;
|
||||
export type DeliveryJob = typeof deliveryJobs.$inferSelect;
|
||||
export type NewDeliveryJob = typeof deliveryJobs.$inferInsert;
|
||||
export type DeliveryAttempt = typeof deliveryAttempts.$inferSelect;
|
||||
export type NewDeliveryAttempt = typeof deliveryAttempts.$inferInsert;
|
||||
Reference in New Issue
Block a user