1import type {
2 RelayAgentActivityAggregateState,
3 RelayAgentActivityState,
4 RelayAgentAwarenessPreferences,
5 RelayManagedEndpointOrigin,
6} from "@t3tools/contracts/relay";
7import {
8 boolean,
9 index,
10 integer,
11 jsonb,
12 pgTable,
13 primaryKey,
14 text,
15 uniqueIndex,
16 varchar,
17} from "drizzle-orm/pg-core";
18
19export const relayMobileDevices = pgTable(
20 "relay_mobile_devices",
21 {
22 userId: varchar("user_id", { length: 255 }).notNull(),
23 deviceId: varchar("device_id", { length: 255 }).notNull(),
24 label: text("label").notNull().default("iOS device"),
25 platform: varchar("platform", { length: 16 }).notNull().$type<"ios" | "android">(),
26 iosMajorVersion: integer("ios_major_version"),
27 androidApiLevel: integer("android_api_level"),
28 appVersion: varchar("app_version", { length: 64 }),
29 bundleId: varchar("bundle_id", { length: 255 }),
30 apsEnvironment: varchar("aps_environment", { length: 16 }).$type<"sandbox" | "production">(),
31 pushToken: text("push_token"),
32 pushToStartToken: text("push_to_start_token"),
33 preferencesJson: jsonb("preferences_json").notNull().$type<RelayAgentAwarenessPreferences>(),
34 createdAt: varchar("created_at", { length: 64 }).notNull(),
35 updatedAt: varchar("updated_at", { length: 64 }).notNull(),
36 },
37 (table) => [
38 primaryKey({ columns: [table.userId, table.deviceId] }),
39 uniqueIndex("idx_relay_mobile_devices_push_token").on(table.pushToken),
40 uniqueIndex("idx_relay_mobile_devices_push_to_start_token").on(table.pushToStartToken),
41 ],
42);
43
44export const relayLiveActivities = pgTable(
45 "relay_live_activities",
46 {
47 userId: varchar("user_id", { length: 255 }).notNull(),
48 deviceId: varchar("device_id", { length: 255 }).notNull(),
49 activityPushToken: text("activity_push_token"),
50 remoteStartQueuedAt: varchar("remote_start_queued_at", { length: 64 }),
51 remoteStartedAt: varchar("remote_started_at", { length: 64 }),
52 endedAt: varchar("ended_at", { length: 64 }),
53 lastAggregateJson: jsonb("last_aggregate_json").$type<RelayAgentActivityAggregateState>(),
54 lastLiveActivityDeliveryAt: varchar("last_live_activity_delivery_at", { length: 64 }),
55 createdAt: varchar("created_at", { length: 64 }).notNull(),
56 updatedAt: varchar("updated_at", { length: 64 }).notNull(),
57 },
58 (table) => [
59 primaryKey({ columns: [table.userId, table.deviceId] }),
60 uniqueIndex("idx_relay_live_activities_activity_push_token").on(table.activityPushToken),
61 ],
62);
63
64export const relayEnvironmentLinks = pgTable(
65 "relay_environment_links",
66 {
67 userId: varchar("user_id", { length: 191 }).notNull(),
68 environmentId: varchar("environment_id", { length: 191 }).notNull(),
69 environmentLabel: text("environment_label").notNull().default("T3 Environment"),
70 environmentPublicKey: text("environment_public_key").notNull(),
71 endpointHttpBaseUrl: text("endpoint_http_base_url").notNull(),
72 endpointWsBaseUrl: text("endpoint_ws_base_url").notNull(),
73 endpointProviderKind: varchar("endpoint_provider_kind", { length: 32 }).notNull(),
74 notificationsEnabled: boolean("notifications_enabled").notNull().default(true),
75 liveActivitiesEnabled: boolean("live_activities_enabled").notNull().default(true),
76 managedTunnelsEnabled: boolean("managed_tunnels_enabled").notNull().default(false),
77 // Opt-in: hold webhook requests while the environment is offline.
78 holdWebhooksWhileOffline: boolean("hold_webhooks_while_offline").notNull().default(false),
79 createdByDeviceId: varchar("created_by_device_id", { length: 191 }),
80 revokedAt: varchar("revoked_at", { length: 64 }),
81 createdAt: varchar("created_at", { length: 64 }).notNull(),
82 updatedAt: varchar("updated_at", { length: 64 }).notNull(),
83 },
84 (table) => [
85 primaryKey({ columns: [table.userId, table.environmentId] }),
86 index("idx_relay_environment_links_environment").on(table.environmentId, table.revokedAt),
87 ],
88);
89
90export const relayManagedEndpointAllocations = pgTable(
91 "relay_managed_endpoint_allocations",
92 {
93 userId: varchar("user_id", { length: 191 }).notNull(),
94 environmentId: varchar("environment_id", { length: 191 }).notNull(),
95 hostname: text("hostname").notNull(),
96 tunnelId: varchar("tunnel_id", { length: 191 }),
97 tunnelName: text("tunnel_name").notNull(),
98 dnsRecordId: varchar("dns_record_id", { length: 191 }),
99 readyAt: varchar("ready_at", { length: 64 }),
100 recoveryEnabledAt: varchar("recovery_enabled_at", { length: 64 }),
101 recoveryEnvironmentPublicKey: text("recovery_environment_public_key"),
102 // Set when cleanup deletes the recorded tunnel; cleared when a tunnel is recorded again.
103 tunnelReleasedAt: varchar("tunnel_released_at", { length: 64 }),
104 origin: jsonb("origin").$type<RelayManagedEndpointOrigin>(),
105 generation: integer("generation").notNull().default(0),
106 createdAt: varchar("created_at", { length: 64 }).notNull(),
107 updatedAt: varchar("updated_at", { length: 64 }).notNull(),
108 },
109 (table) => [
110 primaryKey({ columns: [table.userId, table.environmentId] }),
111 uniqueIndex("idx_relay_managed_endpoint_allocations_hostname").on(table.hostname),
112 uniqueIndex("idx_relay_managed_endpoint_allocations_tunnel_name").on(table.tunnelName),
113 ],
114);
115
116export const relayManagedTunnelLimits = pgTable("relay_managed_tunnel_limits", {
117 userId: varchar("user_id", { length: 191 }).primaryKey(),
118 maxTunnels: integer("max_tunnels").notNull(),
119 createdAt: varchar("created_at", { length: 64 }).notNull(),
120 updatedAt: varchar("updated_at", { length: 64 }).notNull(),
121});
122
123export const relayEnvironmentCredentials = pgTable(
124 "relay_environment_credentials",
125 {
126 credentialId: varchar("credential_id", { length: 64 }).primaryKey(),
127 environmentId: varchar("environment_id", { length: 191 }).notNull(),
128 environmentPublicKey: text("environment_public_key").notNull(),
129 credentialHash: varchar("credential_hash", { length: 191 }).notNull(),
130 revokedAt: varchar("revoked_at", { length: 64 }),
131 createdAt: varchar("created_at", { length: 64 }).notNull(),
132 updatedAt: varchar("updated_at", { length: 64 }).notNull(),
133 },
134 (table) => [
135 uniqueIndex("idx_relay_environment_credentials_hash").on(table.credentialHash),
136 index("idx_relay_environment_credentials_environment").on(table.environmentId, table.revokedAt),
137 index("idx_relay_environment_credentials_environment_key").on(
138 table.environmentId,
139 table.environmentPublicKey,
140 table.revokedAt,
141 ),
142 ],
143);
144
145export const relayAgentActivityRows = pgTable(
146 "relay_agent_activity_rows",
147 {
148 environmentId: varchar("environment_id", { length: 191 }).notNull(),
149 environmentPublicKey: text("environment_public_key").notNull(),
150 threadId: varchar("thread_id", { length: 512 }).notNull(),
151 stateJson: jsonb("state_json").notNull().$type<RelayAgentActivityState>(),
152 updatedAt: varchar("updated_at", { length: 64 }).notNull(),
153 createdAt: varchar("created_at", { length: 64 }).notNull(),
154 },
155 (table) => [
156 primaryKey({ columns: [table.environmentId, table.environmentPublicKey, table.threadId] }),
157 index("idx_relay_agent_activity_rows_updated").on(table.updatedAt),
158 ],
159);
160
161export const relayDeliveryAttempts = pgTable(
162 "relay_delivery_attempts",
163 {
164 id: varchar("id", { length: 36 }).primaryKey(),
165 createdAt: varchar("created_at", { length: 64 }).notNull(),
166 userId: varchar("user_id", { length: 255 }),
167 environmentId: varchar("environment_id", { length: 191 }),
168 threadId: varchar("thread_id", { length: 512 }),
169 deviceId: varchar("device_id", { length: 255 }),
170 kind: varchar("kind", { length: 64 }).notNull(),
171 sourceJobId: varchar("source_job_id", { length: 64 }),
172 tokenSuffix: varchar("token_suffix", { length: 16 }),
173 apnsStatus: integer("apns_status"),
174 apnsReason: text("apns_reason"),
175 apnsId: varchar("apns_id", { length: 128 }),
176 transportError: text("transport_error"),
177 },
178 (table) => [
179 index("idx_relay_delivery_attempts_environment").on(
180 table.environmentId,
181 table.threadId,
182 table.createdAt,
183 ),
184 uniqueIndex("idx_relay_delivery_attempts_source_job").on(table.sourceJobId),
185 ],
186);
187
188export const relayDpopProofs = pgTable(
189 "relay_dpop_proofs",
190 {
191 thumbprint: varchar("thumbprint", { length: 128 }).notNull(),
192 jti: varchar("jti", { length: 255 }).notNull(),
193 iat: integer("iat").notNull(),
194 expiresAt: varchar("expires_at", { length: 64 }).notNull(),
195 createdAt: varchar("created_at", { length: 64 }).notNull(),
196 },
197 (table) => [
198 primaryKey({ columns: [table.thumbprint, table.jti] }),
199 index("idx_relay_dpop_proofs_expires_at").on(table.expiresAt),
200 ],
201);