@minion-stack/db 0.3.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/dist/crypto.d.ts +19 -0
  2. package/dist/crypto.d.ts.map +1 -0
  3. package/dist/crypto.js +65 -0
  4. package/dist/crypto.js.map +1 -0
  5. package/dist/crypto.test.d.ts +2 -0
  6. package/dist/crypto.test.d.ts.map +1 -0
  7. package/dist/crypto.test.js +23 -0
  8. package/dist/crypto.test.js.map +1 -0
  9. package/dist/pg/crypto.d.ts +1 -7
  10. package/dist/pg/crypto.d.ts.map +1 -1
  11. package/dist/pg/crypto.js +4 -41
  12. package/dist/pg/crypto.js.map +1 -1
  13. package/dist/pg/schema/agent-groups.d.ts +169 -0
  14. package/dist/pg/schema/agent-groups.d.ts.map +1 -0
  15. package/dist/pg/schema/agent-groups.js +21 -0
  16. package/dist/pg/schema/agent-groups.js.map +1 -0
  17. package/dist/pg/schema/builder.d.ts +1432 -0
  18. package/dist/pg/schema/builder.d.ts.map +1 -0
  19. package/dist/pg/schema/builder.js +160 -0
  20. package/dist/pg/schema/builder.js.map +1 -0
  21. package/dist/pg/schema/channels.d.ts +441 -0
  22. package/dist/pg/schema/channels.d.ts.map +1 -0
  23. package/dist/pg/schema/channels.js +62 -0
  24. package/dist/pg/schema/channels.js.map +1 -0
  25. package/dist/pg/schema/chat-messages.d.ts +184 -0
  26. package/dist/pg/schema/chat-messages.d.ts.map +1 -0
  27. package/dist/pg/schema/chat-messages.js +26 -0
  28. package/dist/pg/schema/chat-messages.js.map +1 -0
  29. package/dist/pg/schema/device-identities.d.ts +111 -0
  30. package/dist/pg/schema/device-identities.d.ts.map +1 -0
  31. package/dist/pg/schema/device-identities.js +11 -0
  32. package/dist/pg/schema/device-identities.js.map +1 -0
  33. package/dist/pg/schema/files.d.ts +162 -0
  34. package/dist/pg/schema/files.d.ts.map +1 -0
  35. package/dist/pg/schema/files.js +15 -0
  36. package/dist/pg/schema/files.js.map +1 -0
  37. package/dist/pg/schema/index.d.ts +19 -0
  38. package/dist/pg/schema/index.d.ts.map +1 -1
  39. package/dist/pg/schema/index.js +20 -0
  40. package/dist/pg/schema/index.js.map +1 -1
  41. package/dist/pg/schema/marketplace.d.ts +459 -0
  42. package/dist/pg/schema/marketplace.d.ts.map +1 -0
  43. package/dist/pg/schema/marketplace.js +42 -0
  44. package/dist/pg/schema/marketplace.js.map +1 -0
  45. package/dist/pg/schema/messages.d.ts +391 -0
  46. package/dist/pg/schema/messages.d.ts.map +1 -0
  47. package/dist/pg/schema/messages.js +44 -0
  48. package/dist/pg/schema/messages.js.map +1 -0
  49. package/dist/pg/schema/missions.d.ts +361 -0
  50. package/dist/pg/schema/missions.d.ts.map +1 -0
  51. package/dist/pg/schema/missions.js +48 -0
  52. package/dist/pg/schema/missions.js.map +1 -0
  53. package/dist/pg/schema/personal-agents.d.ts +285 -0
  54. package/dist/pg/schema/personal-agents.d.ts.map +1 -0
  55. package/dist/pg/schema/personal-agents.js +40 -0
  56. package/dist/pg/schema/personal-agents.js.map +1 -0
  57. package/dist/pg/schema/profiles.d.ts +17 -0
  58. package/dist/pg/schema/profiles.d.ts.map +1 -1
  59. package/dist/pg/schema/profiles.js +2 -0
  60. package/dist/pg/schema/profiles.js.map +1 -1
  61. package/dist/pg/schema/server-ops.d.ts +836 -0
  62. package/dist/pg/schema/server-ops.d.ts.map +1 -0
  63. package/dist/pg/schema/server-ops.js +88 -0
  64. package/dist/pg/schema/server-ops.js.map +1 -0
  65. package/dist/pg/schema/sessions.d.ts +395 -0
  66. package/dist/pg/schema/sessions.d.ts.map +1 -0
  67. package/dist/pg/schema/sessions.js +50 -0
  68. package/dist/pg/schema/sessions.js.map +1 -0
  69. package/dist/pg/schema/settings.d.ts +111 -0
  70. package/dist/pg/schema/settings.d.ts.map +1 -0
  71. package/dist/pg/schema/settings.js +17 -0
  72. package/dist/pg/schema/settings.js.map +1 -0
  73. package/dist/pg/schema/skills.d.ts +395 -0
  74. package/dist/pg/schema/skills.d.ts.map +1 -0
  75. package/dist/pg/schema/skills.js +45 -0
  76. package/dist/pg/schema/skills.js.map +1 -0
  77. package/dist/pg/schema/user-agents.d.ts +80 -0
  78. package/dist/pg/schema/user-agents.d.ts.map +1 -0
  79. package/dist/pg/schema/user-agents.js +21 -0
  80. package/dist/pg/schema/user-agents.js.map +1 -0
  81. package/dist/pg/schema/user-identities.d.ts +1 -1
  82. package/dist/pg/schema/user-preferences.d.ts +97 -0
  83. package/dist/pg/schema/user-preferences.d.ts.map +1 -0
  84. package/dist/pg/schema/user-preferences.js +19 -0
  85. package/dist/pg/schema/user-preferences.js.map +1 -0
  86. package/dist/pg/schema/workshop-saves.d.ts +145 -0
  87. package/dist/pg/schema/workshop-saves.d.ts.map +1 -0
  88. package/dist/pg/schema/workshop-saves.js +13 -0
  89. package/dist/pg/schema/workshop-saves.js.map +1 -0
  90. package/dist/pg/schema/workspace-membership.d.ts +83 -0
  91. package/dist/pg/schema/workspace-membership.d.ts.map +1 -0
  92. package/dist/pg/schema/workspace-membership.js +19 -0
  93. package/dist/pg/schema/workspace-membership.js.map +1 -0
  94. package/dist/schema/flows.d.ts +36 -0
  95. package/dist/schema/flows.d.ts.map +1 -1
  96. package/dist/schema/flows.js +2 -0
  97. package/dist/schema/flows.js.map +1 -1
  98. package/dist/schema/index.d.ts +2 -0
  99. package/dist/schema/index.d.ts.map +1 -1
  100. package/dist/schema/index.js +1 -0
  101. package/dist/schema/index.js.map +1 -1
  102. package/dist/schema/join-requests.d.ts +188 -0
  103. package/dist/schema/join-requests.d.ts.map +1 -0
  104. package/dist/schema/join-requests.js +35 -0
  105. package/dist/schema/join-requests.js.map +1 -0
  106. package/dist/schema/personal-agents.d.ts +1 -1
  107. package/dist/schema/reliability-events.d.ts +1 -1
  108. package/dist/schema/skill-execution-stats.d.ts +1 -1
  109. package/package.json +15 -12
  110. package/src/crypto.test.ts +33 -0
  111. package/src/crypto.ts +73 -0
  112. package/src/pg/crypto.ts +4 -44
  113. package/src/pg/schema/agent-groups.ts +30 -0
  114. package/src/pg/schema/builder.ts +205 -0
  115. package/src/pg/schema/channels.ts +77 -0
  116. package/src/pg/schema/chat-messages.ts +30 -0
  117. package/src/pg/schema/device-identities.ts +11 -0
  118. package/src/pg/schema/files.ts +19 -0
  119. package/src/pg/schema/index.ts +36 -0
  120. package/src/pg/schema/marketplace.ts +47 -0
  121. package/src/pg/schema/messages.ts +48 -0
  122. package/src/pg/schema/missions.ts +58 -0
  123. package/src/pg/schema/personal-agents.ts +44 -0
  124. package/src/pg/schema/profiles.ts +2 -0
  125. package/src/pg/schema/server-ops.ts +126 -0
  126. package/src/pg/schema/sessions.ts +60 -0
  127. package/src/pg/schema/settings.ts +21 -0
  128. package/src/pg/schema/skills.ts +65 -0
  129. package/src/pg/schema/user-agents.ts +25 -0
  130. package/src/pg/schema/user-preferences.ts +23 -0
  131. package/src/pg/schema/workshop-saves.ts +13 -0
  132. package/src/pg/schema/workspace-membership.ts +26 -0
  133. package/src/schema/flows.ts +2 -0
  134. package/src/schema/index.ts +2 -0
  135. package/src/schema/join-requests.ts +42 -0
@@ -0,0 +1,126 @@
1
+ import {
2
+ pgTable,
3
+ uuid,
4
+ text,
5
+ integer,
6
+ bigint,
7
+ boolean,
8
+ timestamp,
9
+ index,
10
+ uniqueIndex,
11
+ } from 'drizzle-orm/pg-core';
12
+ import { gateway } from './gateway.js';
13
+
14
+ /**
15
+ * Server/gateway provisioning + backup operations.
16
+ * Mirrors Turso `server_backups` / `server_provision_configs` / `backup_configs` / `config_snapshots`.
17
+ * FK remap: server_id → gateway_id (gateway.id via legacy_server_id),
18
+ * tenant_id → organizations.id (plain uuid soft-ref, RLS-enforced).
19
+ * Type remap: integer epoch → timestamptz, integer boolean-flags → boolean,
20
+ * size_bytes integer → bigint.
21
+ */
22
+
23
+ export const serverBackups = pgTable(
24
+ 'server_backups',
25
+ {
26
+ id: text('id').primaryKey(),
27
+ gatewayId: uuid('gateway_id')
28
+ .notNull()
29
+ .references(() => gateway.id, { onDelete: 'cascade' }),
30
+ tenantId: uuid('tenant_id').notNull(),
31
+ snapshotPath: text('snapshot_path').notNull(),
32
+ timestamp: timestamp('timestamp', { withTimezone: true }).notNull(),
33
+ sizeBytes: bigint('size_bytes', { mode: 'number' }),
34
+ status: text('status', { enum: ['running', 'complete', 'failed'] })
35
+ .notNull()
36
+ .default('running'),
37
+ createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
38
+ },
39
+ (t) => [
40
+ index('idx_server_backups_gateway').on(t.gatewayId),
41
+ index('idx_server_backups_tenant').on(t.tenantId),
42
+ ],
43
+ );
44
+
45
+ export const serverProvisionConfigs = pgTable(
46
+ 'server_provision_configs',
47
+ {
48
+ id: text('id').primaryKey(),
49
+ gatewayId: uuid('gateway_id')
50
+ .notNull()
51
+ .references(() => gateway.id, { onDelete: 'cascade' }),
52
+ tenantId: uuid('tenant_id').notNull(),
53
+
54
+ // SSH connection
55
+ sshHost: text('ssh_host'),
56
+ sshUser: text('ssh_user').default('root'),
57
+ sshPort: integer('ssh_port').default(22),
58
+
59
+ // Credentials (encrypted)
60
+ apiKey: text('api_key'),
61
+ apiKeyIv: text('api_key_iv'),
62
+
63
+ // Agent config
64
+ agentName: text('agent_name'),
65
+ sandboxMode: text('sandbox_mode', { enum: ['non-main', 'always', 'never'] }).default('non-main'),
66
+ dmPolicy: text('dm_policy', { enum: ['pairing', 'solo', 'disabled'] }).default('pairing'),
67
+
68
+ // Install config
69
+ installMethod: text('install_method', { enum: ['package', 'source'] }).default('package'),
70
+ pkgManager: text('pkg_manager', { enum: ['npm', 'bun'] }).default('npm'),
71
+
72
+ // Gateway config
73
+ gatewayPort: integer('gateway_port').default(18789),
74
+ gatewayBind: text('gateway_bind', { enum: ['loopback', 'all'] }).default('loopback'),
75
+
76
+ // Channel toggles
77
+ enableWhatsapp: boolean('enable_whatsapp').default(false),
78
+ enableTelegram: boolean('enable_telegram').default(false),
79
+ enableDiscord: boolean('enable_discord').default(false),
80
+
81
+ // Provision state
82
+ phaseStatuses: text('phase_statuses').default('{}'),
83
+ lastProvisionAt: timestamp('last_provision_at', { withTimezone: true }),
84
+
85
+ // Timestamps
86
+ createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
87
+ updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
88
+ },
89
+ (t) => [
90
+ uniqueIndex('provision_configs_uniq_gateway').on(t.gatewayId),
91
+ index('idx_provision_configs_tenant').on(t.tenantId),
92
+ ],
93
+ );
94
+
95
+ export const backupConfigs = pgTable(
96
+ 'backup_configs',
97
+ {
98
+ id: text('id').primaryKey(),
99
+ tenantId: uuid('tenant_id').notNull(),
100
+ backupHost: text('backup_host'),
101
+ backupUser: text('backup_user').default('root'),
102
+ backupPort: integer('backup_port').default(22),
103
+ backupBasePath: text('backup_base_path').default('/mnt/agent-data/backups'),
104
+ schedule: text('schedule'),
105
+ retentionCount: integer('retention_count').default(7),
106
+ enabled: boolean('enabled').default(false),
107
+ createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
108
+ updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
109
+ },
110
+ (t) => [index('idx_backup_configs_tenant').on(t.tenantId)],
111
+ );
112
+
113
+ export const configSnapshots = pgTable(
114
+ 'config_snapshots',
115
+ {
116
+ id: text('id').primaryKey(),
117
+ tenantId: uuid('tenant_id').notNull(),
118
+ gatewayId: uuid('gateway_id')
119
+ .notNull()
120
+ .references(() => gateway.id, { onDelete: 'cascade' }),
121
+ configJson: text('config_json').notNull(),
122
+ configHash: text('config_hash').notNull(),
123
+ fetchedAt: timestamp('fetched_at', { withTimezone: true }).notNull(),
124
+ },
125
+ (t) => [uniqueIndex('idx_config_snapshots_gateway').on(t.gatewayId)],
126
+ );
@@ -0,0 +1,60 @@
1
+ import { pgTable, uuid, text, integer, timestamp, index, uniqueIndex } from 'drizzle-orm/pg-core';
2
+ import { gateway } from './gateway.js';
3
+
4
+ /**
5
+ * Agent sessions + per-session task boards.
6
+ * Mirrors Turso `sessions` / `session_tasks`.
7
+ * FK remap: server_id → gateway_id, tenant_id → organizations.id (soft-ref),
8
+ * integer epoch timestamps → timestamptz.
9
+ */
10
+
11
+ export const sessions = pgTable(
12
+ 'sessions',
13
+ {
14
+ id: text('id').primaryKey(),
15
+ tenantId: uuid('tenant_id').notNull(),
16
+ gatewayId: uuid('gateway_id')
17
+ .notNull()
18
+ .references(() => gateway.id, { onDelete: 'cascade' }),
19
+ agentId: text('agent_id').notNull(),
20
+ sessionKey: text('session_key').notNull(),
21
+ status: text('status', { enum: ['running', 'thinking', 'idle', 'aborted', 'completed'] })
22
+ .notNull()
23
+ .default('idle'),
24
+ metadata: text('metadata'),
25
+ startedAt: timestamp('started_at', { withTimezone: true }),
26
+ endedAt: timestamp('ended_at', { withTimezone: true }),
27
+ createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
28
+ updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
29
+ },
30
+ (t) => [
31
+ index('idx_sessions_tenant').on(t.tenantId),
32
+ index('idx_sessions_gateway').on(t.gatewayId),
33
+ uniqueIndex('sessions_uniq_key').on(t.tenantId, t.gatewayId, t.sessionKey),
34
+ ],
35
+ );
36
+
37
+ export const sessionTasks = pgTable(
38
+ 'session_tasks',
39
+ {
40
+ id: text('id').primaryKey(),
41
+ tenantId: uuid('tenant_id').notNull(),
42
+ gatewayId: uuid('gateway_id')
43
+ .notNull()
44
+ .references(() => gateway.id, { onDelete: 'cascade' }),
45
+ sessionKey: text('session_key').notNull(),
46
+ title: text('title').notNull(),
47
+ description: text('description'),
48
+ status: text('status', { enum: ['backlog', 'todo', 'in_progress', 'done'] })
49
+ .notNull()
50
+ .default('backlog'),
51
+ sortOrder: integer('sort_order').notNull().default(0),
52
+ metadata: text('metadata'),
53
+ createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
54
+ updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
55
+ },
56
+ (t) => [
57
+ index('idx_session_tasks_tenant').on(t.tenantId),
58
+ index('idx_session_tasks_gateway_session').on(t.gatewayId, t.sessionKey),
59
+ ],
60
+ );
@@ -0,0 +1,21 @@
1
+ import { pgTable, uuid, text, timestamp, index, uniqueIndex } from 'drizzle-orm/pg-core';
2
+ import { gateway } from './gateway.js';
3
+
4
+ /** Per-gateway config settings. Mirrors Turso `settings`. tenant_id → organizations.id. */
5
+ export const settings = pgTable(
6
+ 'settings',
7
+ {
8
+ id: uuid('id').primaryKey().defaultRandom(),
9
+ tenantId: uuid('tenant_id').notNull(),
10
+ gatewayId: uuid('gateway_id')
11
+ .notNull()
12
+ .references(() => gateway.id, { onDelete: 'cascade' }),
13
+ section: text('section').notNull(),
14
+ value: text('value').notNull(),
15
+ updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
16
+ },
17
+ (t) => [
18
+ uniqueIndex('uq_settings_gateway_section').on(t.gatewayId, t.section),
19
+ index('idx_settings_tenant').on(t.tenantId),
20
+ ],
21
+ );
@@ -0,0 +1,65 @@
1
+ import {
2
+ pgTable,
3
+ uuid,
4
+ text,
5
+ integer,
6
+ boolean,
7
+ bigserial,
8
+ timestamp,
9
+ index,
10
+ primaryKey,
11
+ } from 'drizzle-orm/pg-core';
12
+ import { gateway } from './gateway.js';
13
+
14
+ /**
15
+ * Gateway skill registry + execution telemetry.
16
+ * Mirrors Turso `skills` / `skill_execution_stats`.
17
+ * FK remap: server_id → gateway_id, tenant_id → organizations.id (soft-ref).
18
+ * boolean cols stay boolean; integer epoch → timestamptz; autoincrement id → bigserial.
19
+ */
20
+
21
+ export const skills = pgTable(
22
+ 'skills',
23
+ {
24
+ skillKey: text('skill_key').notNull(),
25
+ gatewayId: uuid('gateway_id')
26
+ .notNull()
27
+ .references(() => gateway.id, { onDelete: 'cascade' }),
28
+ tenantId: uuid('tenant_id').notNull(),
29
+ name: text('name').notNull(),
30
+ description: text('description'),
31
+ emoji: text('emoji'),
32
+ bundled: boolean('bundled').notNull().default(false),
33
+ disabled: boolean('disabled').notNull().default(false),
34
+ eligible: boolean('eligible').notNull().default(false),
35
+ rawJson: text('raw_json').notNull(),
36
+ lastSeenAt: timestamp('last_seen_at', { withTimezone: true }).notNull(),
37
+ },
38
+ (t) => [
39
+ primaryKey({ columns: [t.skillKey, t.gatewayId] }),
40
+ index('idx_skills_tenant').on(t.tenantId),
41
+ ],
42
+ );
43
+
44
+ export const skillExecutionStats = pgTable(
45
+ 'skill_execution_stats',
46
+ {
47
+ id: bigserial('id', { mode: 'number' }).primaryKey(),
48
+ tenantId: uuid('tenant_id').notNull(),
49
+ gatewayId: uuid('gateway_id')
50
+ .notNull()
51
+ .references(() => gateway.id, { onDelete: 'cascade' }),
52
+ agentId: text('agent_id'),
53
+ skillName: text('skill_name').notNull(),
54
+ sessionKey: text('session_key'),
55
+ status: text('status', { enum: ['ok', 'auth_error', 'timeout', 'error'] }).notNull(),
56
+ durationMs: integer('duration_ms'),
57
+ errorMessage: text('error_message'),
58
+ occurredAt: timestamp('occurred_at', { withTimezone: true }).notNull(),
59
+ createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
60
+ },
61
+ (t) => [
62
+ index('idx_skill_stats_gateway_skill_time').on(t.gatewayId, t.skillName, t.occurredAt),
63
+ index('idx_skill_stats_tenant').on(t.tenantId),
64
+ ],
65
+ );
@@ -0,0 +1,25 @@
1
+ import { pgTable, uuid, text, timestamp, index, primaryKey } from 'drizzle-orm/pg-core';
2
+ import { gateway } from './gateway.js';
3
+ import { profiles } from './profiles.js';
4
+
5
+ /**
6
+ * User ↔ agent access grants. Mirrors Turso `user_agents`.
7
+ * FK remap: user_id → profiles.id (via legacy_user_id), server_id → gateway_id.
8
+ */
9
+ export const userAgents = pgTable(
10
+ 'user_agents',
11
+ {
12
+ userId: uuid('user_id')
13
+ .notNull()
14
+ .references(() => profiles.id, { onDelete: 'cascade' }),
15
+ agentId: text('agent_id').notNull(),
16
+ gatewayId: uuid('gateway_id')
17
+ .notNull()
18
+ .references(() => gateway.id, { onDelete: 'cascade' }),
19
+ createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
20
+ },
21
+ (t) => [
22
+ primaryKey({ columns: [t.userId, t.agentId, t.gatewayId] }),
23
+ index('idx_user_agents_gateway').on(t.gatewayId),
24
+ ],
25
+ );
@@ -0,0 +1,23 @@
1
+ import { pgTable, uuid, text, timestamp, uniqueIndex, index } from 'drizzle-orm/pg-core';
2
+ import { profiles } from './profiles.js';
3
+
4
+ /**
5
+ * Per-user UI/feature preferences. Mirrors Turso `user_preferences`.
6
+ * userId (Turso text) is remapped to profile_id (uuid → profiles.id) on migration.
7
+ */
8
+ export const userPreferences = pgTable(
9
+ 'user_preferences',
10
+ {
11
+ id: uuid('id').primaryKey().defaultRandom(),
12
+ profileId: uuid('profile_id')
13
+ .notNull()
14
+ .references(() => profiles.id, { onDelete: 'cascade' }),
15
+ section: text('section').notNull(),
16
+ value: text('value').notNull(),
17
+ updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
18
+ },
19
+ (t) => [
20
+ uniqueIndex('uq_user_prefs_profile_section').on(t.profileId, t.section),
21
+ index('idx_user_prefs_profile').on(t.profileId),
22
+ ],
23
+ );
@@ -0,0 +1,13 @@
1
+ import { pgTable, uuid, text, timestamp } from 'drizzle-orm/pg-core';
2
+
3
+ /** Workshop canvas saves. Mirrors Turso `workshop_saves`. profile_id/tenant_id nullable (pre-migration shared). */
4
+ export const workshopSaves = pgTable('workshop_saves', {
5
+ id: text('id').primaryKey(),
6
+ name: text('name').notNull(),
7
+ state: text('state').notNull(),
8
+ thumbnail: text('thumbnail'),
9
+ profileId: uuid('profile_id'),
10
+ tenantId: uuid('tenant_id'),
11
+ createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
12
+ updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
13
+ });
@@ -0,0 +1,26 @@
1
+ import { pgTable, uuid, text, timestamp, index, primaryKey } from 'drizzle-orm/pg-core';
2
+ import { profiles } from './profiles.js';
3
+
4
+ /**
5
+ * workspace_membership — bridge between hub identity and Paperclip company tenancy.
6
+ * Mirrors Turso `workspace_membership`. FK remap: user_id → profiles.id (via legacy_user_id).
7
+ * Each row means "user X holds role Y in Paperclip company Z".
8
+ */
9
+ export const workspaceMembership = pgTable(
10
+ 'workspace_membership',
11
+ {
12
+ userId: uuid('user_id')
13
+ .notNull()
14
+ .references(() => profiles.id, { onDelete: 'cascade' }),
15
+ paperclipCompanyId: text('paperclip_company_id').notNull(),
16
+ role: text('role').notNull().default('admin'),
17
+ createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
18
+ },
19
+ (t) => [
20
+ primaryKey({ columns: [t.userId, t.paperclipCompanyId] }),
21
+ index('idx_workspace_membership_user').on(t.userId),
22
+ ],
23
+ );
24
+
25
+ export type WorkspaceMembership = typeof workspaceMembership.$inferSelect;
26
+ export type NewWorkspaceMembership = typeof workspaceMembership.$inferInsert;
@@ -9,4 +9,6 @@ export const flows = sqliteTable('flows', {
9
9
  tenantId: text('tenant_id'), // tenant scope — null for pre-migration rows
10
10
  createdAt: integer('created_at').notNull(),
11
11
  updatedAt: integer('updated_at').notNull(),
12
+ active: integer('active', { mode: 'boolean' }).notNull().default(false),
13
+ config: text('config').notNull().default('{}'),
12
14
  });
@@ -60,3 +60,5 @@ export {
60
60
  export { userPreferences } from './user-preferences.js';
61
61
  export { workspaceMembership } from './workspace-membership.js';
62
62
  export type { WorkspaceMembership, NewWorkspaceMembership } from './workspace-membership.js';
63
+ export { joinRequests } from './join-requests.js';
64
+ export type { JoinRequest, NewJoinRequest } from './join-requests.js';
@@ -0,0 +1,42 @@
1
+ import { sqliteTable, text, integer, index } from 'drizzle-orm/sqlite-core';
2
+ import { user } from './auth/index.js';
3
+ import { organization } from './auth/index.js';
4
+
5
+ /**
6
+ * join_requests — users requesting to join an organization.
7
+ *
8
+ * When a user not-yet-in-an-org wants access, they submit a join request.
9
+ * Org admins review (approve/deny). On approval the user is added as a member.
10
+ *
11
+ * Used by:
12
+ * - /join page (submission)
13
+ * - /api/join-requests/* (listing, counting, review)
14
+ * - notifications panel
15
+ */
16
+ export const joinRequests = sqliteTable(
17
+ 'join_requests',
18
+ {
19
+ id: text('id').primaryKey(),
20
+ userId: text('user_id')
21
+ .notNull()
22
+ .references(() => user.id, { onDelete: 'cascade' }),
23
+ orgId: text('org_id')
24
+ .notNull()
25
+ .references(() => organization.id, { onDelete: 'cascade' }),
26
+ email: text('email').notNull(),
27
+ message: text('message'),
28
+ status: text('status', { enum: ['pending', 'approved', 'denied'] })
29
+ .notNull()
30
+ .default('pending'),
31
+ reviewedBy: text('reviewed_by'),
32
+ reviewedAt: integer('reviewed_at'),
33
+ createdAt: integer('created_at').notNull(),
34
+ },
35
+ (t) => [
36
+ index('idx_join_requests_user').on(t.userId),
37
+ index('idx_join_requests_org_status').on(t.orgId, t.status),
38
+ ],
39
+ );
40
+
41
+ export type JoinRequest = typeof joinRequests.$inferSelect;
42
+ export type NewJoinRequest = typeof joinRequests.$inferInsert;