@weirdfingers/baseboards 0.2.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 (139) hide show
  1. package/README.md +191 -0
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.js +887 -0
  4. package/dist/index.js.map +1 -0
  5. package/package.json +64 -0
  6. package/templates/README.md +120 -0
  7. package/templates/api/.env.example +62 -0
  8. package/templates/api/Dockerfile +32 -0
  9. package/templates/api/README.md +132 -0
  10. package/templates/api/alembic/env.py +106 -0
  11. package/templates/api/alembic/script.py.mako +28 -0
  12. package/templates/api/alembic/versions/20250101_000000_initial_schema.py +448 -0
  13. package/templates/api/alembic/versions/20251022_174729_remove_provider_name_from_generations.py +71 -0
  14. package/templates/api/alembic/versions/20251023_165852_switch_to_declarative_base_and_mapping.py +411 -0
  15. package/templates/api/alembic/versions/2025925_62735_add_seed_data_for_default_tenant.py +85 -0
  16. package/templates/api/alembic.ini +36 -0
  17. package/templates/api/config/generators.yaml +25 -0
  18. package/templates/api/config/storage_config.yaml +26 -0
  19. package/templates/api/docs/ADDING_GENERATORS.md +409 -0
  20. package/templates/api/docs/GENERATORS_API.md +502 -0
  21. package/templates/api/docs/MIGRATIONS.md +472 -0
  22. package/templates/api/docs/storage_providers.md +337 -0
  23. package/templates/api/pyproject.toml +165 -0
  24. package/templates/api/src/boards/__init__.py +10 -0
  25. package/templates/api/src/boards/api/app.py +171 -0
  26. package/templates/api/src/boards/api/auth.py +75 -0
  27. package/templates/api/src/boards/api/endpoints/__init__.py +3 -0
  28. package/templates/api/src/boards/api/endpoints/jobs.py +76 -0
  29. package/templates/api/src/boards/api/endpoints/setup.py +505 -0
  30. package/templates/api/src/boards/api/endpoints/sse.py +129 -0
  31. package/templates/api/src/boards/api/endpoints/storage.py +74 -0
  32. package/templates/api/src/boards/api/endpoints/tenant_registration.py +296 -0
  33. package/templates/api/src/boards/api/endpoints/webhooks.py +13 -0
  34. package/templates/api/src/boards/auth/__init__.py +15 -0
  35. package/templates/api/src/boards/auth/adapters/__init__.py +20 -0
  36. package/templates/api/src/boards/auth/adapters/auth0.py +220 -0
  37. package/templates/api/src/boards/auth/adapters/base.py +73 -0
  38. package/templates/api/src/boards/auth/adapters/clerk.py +172 -0
  39. package/templates/api/src/boards/auth/adapters/jwt.py +122 -0
  40. package/templates/api/src/boards/auth/adapters/none.py +102 -0
  41. package/templates/api/src/boards/auth/adapters/oidc.py +284 -0
  42. package/templates/api/src/boards/auth/adapters/supabase.py +110 -0
  43. package/templates/api/src/boards/auth/context.py +35 -0
  44. package/templates/api/src/boards/auth/factory.py +115 -0
  45. package/templates/api/src/boards/auth/middleware.py +221 -0
  46. package/templates/api/src/boards/auth/provisioning.py +129 -0
  47. package/templates/api/src/boards/auth/tenant_extraction.py +278 -0
  48. package/templates/api/src/boards/cli.py +354 -0
  49. package/templates/api/src/boards/config.py +116 -0
  50. package/templates/api/src/boards/database/__init__.py +7 -0
  51. package/templates/api/src/boards/database/cli.py +110 -0
  52. package/templates/api/src/boards/database/connection.py +252 -0
  53. package/templates/api/src/boards/database/models.py +19 -0
  54. package/templates/api/src/boards/database/seed_data.py +182 -0
  55. package/templates/api/src/boards/dbmodels/__init__.py +455 -0
  56. package/templates/api/src/boards/generators/__init__.py +57 -0
  57. package/templates/api/src/boards/generators/artifacts.py +53 -0
  58. package/templates/api/src/boards/generators/base.py +140 -0
  59. package/templates/api/src/boards/generators/implementations/__init__.py +12 -0
  60. package/templates/api/src/boards/generators/implementations/audio/__init__.py +3 -0
  61. package/templates/api/src/boards/generators/implementations/audio/whisper.py +66 -0
  62. package/templates/api/src/boards/generators/implementations/image/__init__.py +3 -0
  63. package/templates/api/src/boards/generators/implementations/image/dalle3.py +93 -0
  64. package/templates/api/src/boards/generators/implementations/image/flux_pro.py +85 -0
  65. package/templates/api/src/boards/generators/implementations/video/__init__.py +3 -0
  66. package/templates/api/src/boards/generators/implementations/video/lipsync.py +70 -0
  67. package/templates/api/src/boards/generators/loader.py +253 -0
  68. package/templates/api/src/boards/generators/registry.py +114 -0
  69. package/templates/api/src/boards/generators/resolution.py +515 -0
  70. package/templates/api/src/boards/generators/testmods/class_gen.py +34 -0
  71. package/templates/api/src/boards/generators/testmods/import_side_effect.py +35 -0
  72. package/templates/api/src/boards/graphql/__init__.py +7 -0
  73. package/templates/api/src/boards/graphql/access_control.py +136 -0
  74. package/templates/api/src/boards/graphql/mutations/root.py +136 -0
  75. package/templates/api/src/boards/graphql/queries/root.py +116 -0
  76. package/templates/api/src/boards/graphql/resolvers/__init__.py +8 -0
  77. package/templates/api/src/boards/graphql/resolvers/auth.py +12 -0
  78. package/templates/api/src/boards/graphql/resolvers/board.py +1055 -0
  79. package/templates/api/src/boards/graphql/resolvers/generation.py +889 -0
  80. package/templates/api/src/boards/graphql/resolvers/generator.py +50 -0
  81. package/templates/api/src/boards/graphql/resolvers/user.py +25 -0
  82. package/templates/api/src/boards/graphql/schema.py +81 -0
  83. package/templates/api/src/boards/graphql/types/board.py +102 -0
  84. package/templates/api/src/boards/graphql/types/generation.py +130 -0
  85. package/templates/api/src/boards/graphql/types/generator.py +17 -0
  86. package/templates/api/src/boards/graphql/types/user.py +47 -0
  87. package/templates/api/src/boards/jobs/repository.py +104 -0
  88. package/templates/api/src/boards/logging.py +195 -0
  89. package/templates/api/src/boards/middleware.py +339 -0
  90. package/templates/api/src/boards/progress/__init__.py +4 -0
  91. package/templates/api/src/boards/progress/models.py +25 -0
  92. package/templates/api/src/boards/progress/publisher.py +64 -0
  93. package/templates/api/src/boards/py.typed +0 -0
  94. package/templates/api/src/boards/redis_pool.py +118 -0
  95. package/templates/api/src/boards/storage/__init__.py +52 -0
  96. package/templates/api/src/boards/storage/base.py +363 -0
  97. package/templates/api/src/boards/storage/config.py +187 -0
  98. package/templates/api/src/boards/storage/factory.py +278 -0
  99. package/templates/api/src/boards/storage/implementations/__init__.py +27 -0
  100. package/templates/api/src/boards/storage/implementations/gcs.py +340 -0
  101. package/templates/api/src/boards/storage/implementations/local.py +201 -0
  102. package/templates/api/src/boards/storage/implementations/s3.py +294 -0
  103. package/templates/api/src/boards/storage/implementations/supabase.py +218 -0
  104. package/templates/api/src/boards/tenant_isolation.py +446 -0
  105. package/templates/api/src/boards/validation.py +262 -0
  106. package/templates/api/src/boards/workers/__init__.py +1 -0
  107. package/templates/api/src/boards/workers/actors.py +201 -0
  108. package/templates/api/src/boards/workers/cli.py +125 -0
  109. package/templates/api/src/boards/workers/context.py +188 -0
  110. package/templates/api/src/boards/workers/middleware.py +58 -0
  111. package/templates/api/src/py.typed +0 -0
  112. package/templates/compose.dev.yaml +39 -0
  113. package/templates/compose.yaml +109 -0
  114. package/templates/docker/env.example +23 -0
  115. package/templates/web/.env.example +28 -0
  116. package/templates/web/Dockerfile +51 -0
  117. package/templates/web/components.json +22 -0
  118. package/templates/web/imageLoader.js +18 -0
  119. package/templates/web/next-env.d.ts +5 -0
  120. package/templates/web/next.config.js +36 -0
  121. package/templates/web/package.json +37 -0
  122. package/templates/web/postcss.config.mjs +7 -0
  123. package/templates/web/public/favicon.ico +0 -0
  124. package/templates/web/src/app/boards/[boardId]/page.tsx +232 -0
  125. package/templates/web/src/app/globals.css +120 -0
  126. package/templates/web/src/app/layout.tsx +21 -0
  127. package/templates/web/src/app/page.tsx +35 -0
  128. package/templates/web/src/app/providers.tsx +18 -0
  129. package/templates/web/src/components/boards/ArtifactInputSlots.tsx +142 -0
  130. package/templates/web/src/components/boards/ArtifactPreview.tsx +125 -0
  131. package/templates/web/src/components/boards/GenerationGrid.tsx +45 -0
  132. package/templates/web/src/components/boards/GenerationInput.tsx +251 -0
  133. package/templates/web/src/components/boards/GeneratorSelector.tsx +89 -0
  134. package/templates/web/src/components/header.tsx +30 -0
  135. package/templates/web/src/components/ui/button.tsx +58 -0
  136. package/templates/web/src/components/ui/card.tsx +92 -0
  137. package/templates/web/src/components/ui/navigation-menu.tsx +168 -0
  138. package/templates/web/src/lib/utils.ts +6 -0
  139. package/templates/web/tsconfig.json +47 -0
@@ -0,0 +1,28 @@
1
+ """${message}
2
+
3
+ Revision ID: ${up_revision}
4
+ Revises: ${down_revision | comma,n}
5
+ Create Date: ${create_date}
6
+
7
+ """
8
+ from typing import Sequence, Union
9
+
10
+ from alembic import op
11
+ import sqlalchemy as sa
12
+ ${imports if imports else ""}
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision: str = ${repr(up_revision)}
16
+ down_revision: Union[str, Sequence[str], None] = ${repr(down_revision)}
17
+ branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
18
+ depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
19
+
20
+
21
+ def upgrade() -> None:
22
+ """Upgrade schema."""
23
+ ${upgrades if upgrades else "pass"}
24
+
25
+
26
+ def downgrade() -> None:
27
+ """Downgrade schema."""
28
+ ${downgrades if downgrades else "pass"}
@@ -0,0 +1,448 @@
1
+ """
2
+ Initial schema with uuid-ossp extension and core tables.
3
+
4
+ Revision ID: 20250101_000000_initial_schema
5
+ Revises:
6
+ Create Date: 2025-01-01 00:00:00
7
+ """
8
+
9
+ import sqlalchemy as sa
10
+ from sqlalchemy.dialects import postgresql
11
+
12
+ from alembic import op # type: ignore[reportMissingImports]
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision = "20250101_000000_initial_schema"
16
+ down_revision = None
17
+ branch_labels = None
18
+ depends_on = None
19
+
20
+
21
+ def upgrade() -> None:
22
+ # Required extension for uuid_generate_v4
23
+ op.execute('CREATE EXTENSION IF NOT EXISTS "uuid-ossp";')
24
+
25
+ # tenants
26
+ op.create_table(
27
+ "tenants",
28
+ sa.Column(
29
+ "id",
30
+ postgresql.UUID(as_uuid=False),
31
+ server_default=sa.text("uuid_generate_v4()"),
32
+ ),
33
+ sa.Column("name", sa.String(length=255), nullable=False),
34
+ sa.Column("slug", sa.String(length=255), nullable=False),
35
+ sa.Column(
36
+ "settings",
37
+ postgresql.JSONB(astext_type=sa.Text()),
38
+ server_default=sa.text("'{}'::jsonb"),
39
+ ),
40
+ sa.Column(
41
+ "created_at",
42
+ sa.DateTime(timezone=True),
43
+ server_default=sa.text("CURRENT_TIMESTAMP"),
44
+ ),
45
+ sa.Column(
46
+ "updated_at",
47
+ sa.DateTime(timezone=True),
48
+ server_default=sa.text("CURRENT_TIMESTAMP"),
49
+ ),
50
+ sa.PrimaryKeyConstraint("id", name="tenants_pkey"),
51
+ sa.UniqueConstraint("slug", name="tenants_slug_key"),
52
+ )
53
+
54
+ # provider_configs
55
+ op.create_table(
56
+ "provider_configs",
57
+ sa.Column(
58
+ "id",
59
+ postgresql.UUID(as_uuid=False),
60
+ server_default=sa.text("uuid_generate_v4()"),
61
+ ),
62
+ sa.Column("tenant_id", postgresql.UUID(as_uuid=False), nullable=False),
63
+ sa.Column("provider_name", sa.String(length=100), nullable=False),
64
+ sa.Column("config", postgresql.JSONB(astext_type=sa.Text()), nullable=False),
65
+ sa.Column("is_enabled", sa.Boolean(), server_default=sa.text("true")),
66
+ sa.Column(
67
+ "created_at",
68
+ sa.DateTime(timezone=True),
69
+ server_default=sa.text("CURRENT_TIMESTAMP"),
70
+ ),
71
+ sa.Column(
72
+ "updated_at",
73
+ sa.DateTime(timezone=True),
74
+ server_default=sa.text("CURRENT_TIMESTAMP"),
75
+ ),
76
+ sa.PrimaryKeyConstraint("id", name="provider_configs_pkey"),
77
+ sa.ForeignKeyConstraint(
78
+ ["tenant_id"],
79
+ ["tenants.id"],
80
+ ondelete="CASCADE",
81
+ name="provider_configs_tenant_id_fkey",
82
+ ),
83
+ sa.UniqueConstraint(
84
+ "tenant_id",
85
+ "provider_name",
86
+ name="provider_configs_tenant_id_provider_name_key",
87
+ ),
88
+ )
89
+
90
+ # users
91
+ op.create_table(
92
+ "users",
93
+ sa.Column(
94
+ "id",
95
+ postgresql.UUID(as_uuid=False),
96
+ server_default=sa.text("uuid_generate_v4()"),
97
+ ),
98
+ sa.Column("tenant_id", postgresql.UUID(as_uuid=False), nullable=False),
99
+ sa.Column("auth_provider", sa.String(length=50), nullable=False),
100
+ sa.Column("auth_subject", sa.String(length=255), nullable=False),
101
+ sa.Column("email", sa.String(length=255)),
102
+ sa.Column("display_name", sa.String(length=255)),
103
+ sa.Column("avatar_url", sa.Text()),
104
+ sa.Column(
105
+ "metadata",
106
+ postgresql.JSONB(astext_type=sa.Text()),
107
+ server_default=sa.text("'{}'::jsonb"),
108
+ ),
109
+ sa.Column(
110
+ "created_at",
111
+ sa.DateTime(timezone=True),
112
+ server_default=sa.text("CURRENT_TIMESTAMP"),
113
+ ),
114
+ sa.Column(
115
+ "updated_at",
116
+ sa.DateTime(timezone=True),
117
+ server_default=sa.text("CURRENT_TIMESTAMP"),
118
+ ),
119
+ sa.PrimaryKeyConstraint("id", name="users_pkey"),
120
+ sa.ForeignKeyConstraint(
121
+ ["tenant_id"],
122
+ ["tenants.id"],
123
+ ondelete="CASCADE",
124
+ name="users_tenant_id_fkey",
125
+ ),
126
+ sa.UniqueConstraint(
127
+ "tenant_id",
128
+ "auth_provider",
129
+ "auth_subject",
130
+ name="users_tenant_id_auth_provider_auth_subject_key",
131
+ ),
132
+ )
133
+ op.create_index(
134
+ "idx_users_auth", "users", ["auth_provider", "auth_subject"], unique=False
135
+ )
136
+ op.create_index("idx_users_tenant", "users", ["tenant_id"], unique=False)
137
+
138
+ # boards
139
+ op.create_table(
140
+ "boards",
141
+ sa.Column(
142
+ "id",
143
+ postgresql.UUID(as_uuid=False),
144
+ server_default=sa.text("uuid_generate_v4()"),
145
+ ),
146
+ sa.Column("tenant_id", postgresql.UUID(as_uuid=False), nullable=False),
147
+ sa.Column("owner_id", postgresql.UUID(as_uuid=False), nullable=False),
148
+ sa.Column("title", sa.String(length=255), nullable=False),
149
+ sa.Column("description", sa.Text()),
150
+ sa.Column("is_public", sa.Boolean(), server_default=sa.text("false")),
151
+ sa.Column(
152
+ "settings",
153
+ postgresql.JSONB(astext_type=sa.Text()),
154
+ server_default=sa.text("'{}'::jsonb"),
155
+ ),
156
+ sa.Column(
157
+ "metadata",
158
+ postgresql.JSONB(astext_type=sa.Text()),
159
+ server_default=sa.text("'{}'::jsonb"),
160
+ ),
161
+ sa.Column(
162
+ "created_at",
163
+ sa.DateTime(timezone=True),
164
+ server_default=sa.text("CURRENT_TIMESTAMP"),
165
+ ),
166
+ sa.Column(
167
+ "updated_at",
168
+ sa.DateTime(timezone=True),
169
+ server_default=sa.text("CURRENT_TIMESTAMP"),
170
+ ),
171
+ sa.PrimaryKeyConstraint("id", name="boards_pkey"),
172
+ sa.ForeignKeyConstraint(
173
+ ["owner_id"], ["users.id"], ondelete="CASCADE", name="boards_owner_id_fkey"
174
+ ),
175
+ sa.ForeignKeyConstraint(
176
+ ["tenant_id"],
177
+ ["tenants.id"],
178
+ ondelete="CASCADE",
179
+ name="boards_tenant_id_fkey",
180
+ ),
181
+ )
182
+ op.create_index("idx_boards_owner", "boards", ["owner_id"], unique=False)
183
+ op.create_index("idx_boards_tenant", "boards", ["tenant_id"], unique=False)
184
+
185
+ # lora_models
186
+ op.create_table(
187
+ "lora_models",
188
+ sa.Column(
189
+ "id",
190
+ postgresql.UUID(as_uuid=False),
191
+ server_default=sa.text("uuid_generate_v4()"),
192
+ ),
193
+ sa.Column("tenant_id", postgresql.UUID(as_uuid=False), nullable=False),
194
+ sa.Column("user_id", postgresql.UUID(as_uuid=False), nullable=False),
195
+ sa.Column("name", sa.String(length=255), nullable=False),
196
+ sa.Column("base_model", sa.String(length=100), nullable=False),
197
+ sa.Column("storage_url", sa.Text(), nullable=False),
198
+ sa.Column("config", postgresql.JSONB(astext_type=sa.Text()), nullable=False),
199
+ sa.Column("trigger_word", sa.String(length=100)),
200
+ sa.Column(
201
+ "metadata",
202
+ postgresql.JSONB(astext_type=sa.Text()),
203
+ server_default=sa.text("'{}'::jsonb"),
204
+ ),
205
+ sa.Column("is_public", sa.Boolean(), server_default=sa.text("false")),
206
+ sa.Column(
207
+ "created_at",
208
+ sa.DateTime(timezone=True),
209
+ server_default=sa.text("CURRENT_TIMESTAMP"),
210
+ ),
211
+ sa.Column(
212
+ "updated_at",
213
+ sa.DateTime(timezone=True),
214
+ server_default=sa.text("CURRENT_TIMESTAMP"),
215
+ ),
216
+ sa.PrimaryKeyConstraint("id", name="lora_models_pkey"),
217
+ sa.ForeignKeyConstraint(
218
+ ["tenant_id"],
219
+ ["tenants.id"],
220
+ ondelete="CASCADE",
221
+ name="lora_models_tenant_id_fkey",
222
+ ),
223
+ sa.ForeignKeyConstraint(
224
+ ["user_id"],
225
+ ["users.id"],
226
+ ondelete="CASCADE",
227
+ name="lora_models_user_id_fkey",
228
+ ),
229
+ )
230
+
231
+ # board_members
232
+ op.create_table(
233
+ "board_members",
234
+ sa.Column(
235
+ "id",
236
+ postgresql.UUID(as_uuid=False),
237
+ server_default=sa.text("uuid_generate_v4()"),
238
+ ),
239
+ sa.Column("board_id", postgresql.UUID(as_uuid=False), nullable=False),
240
+ sa.Column("user_id", postgresql.UUID(as_uuid=False), nullable=False),
241
+ sa.Column("role", sa.String(length=20), nullable=False),
242
+ sa.Column("invited_by", postgresql.UUID(as_uuid=False)),
243
+ sa.Column(
244
+ "joined_at",
245
+ sa.DateTime(timezone=True),
246
+ server_default=sa.text("CURRENT_TIMESTAMP"),
247
+ ),
248
+ sa.PrimaryKeyConstraint("id", name="board_members_pkey"),
249
+ sa.UniqueConstraint(
250
+ "board_id", "user_id", name="board_members_board_id_user_id_key"
251
+ ),
252
+ sa.CheckConstraint(
253
+ "role::text = ANY (ARRAY['viewer'::character varying, "
254
+ "'editor'::character varying, 'admin'::character varying]::text[])",
255
+ name="board_members_role_check",
256
+ ),
257
+ sa.ForeignKeyConstraint(
258
+ ["board_id"],
259
+ ["boards.id"],
260
+ ondelete="CASCADE",
261
+ name="board_members_board_id_fkey",
262
+ ),
263
+ sa.ForeignKeyConstraint(
264
+ ["invited_by"], ["users.id"], name="board_members_invited_by_fkey"
265
+ ),
266
+ sa.ForeignKeyConstraint(
267
+ ["user_id"],
268
+ ["users.id"],
269
+ ondelete="CASCADE",
270
+ name="board_members_user_id_fkey",
271
+ ),
272
+ )
273
+ op.create_index(
274
+ "idx_board_members_board", "board_members", ["board_id"], unique=False
275
+ )
276
+ op.create_index(
277
+ "idx_board_members_user", "board_members", ["user_id"], unique=False
278
+ )
279
+
280
+ # generations
281
+ op.create_table(
282
+ "generations",
283
+ sa.Column(
284
+ "id",
285
+ postgresql.UUID(as_uuid=False),
286
+ server_default=sa.text("uuid_generate_v4()"),
287
+ ),
288
+ sa.Column("tenant_id", postgresql.UUID(as_uuid=False), nullable=False),
289
+ sa.Column("board_id", postgresql.UUID(as_uuid=False), nullable=False),
290
+ sa.Column("user_id", postgresql.UUID(as_uuid=False), nullable=False),
291
+ sa.Column("generator_name", sa.String(length=100), nullable=False),
292
+ sa.Column("provider_name", sa.String(length=100), nullable=False),
293
+ sa.Column("artifact_type", sa.String(length=50), nullable=False),
294
+ sa.Column(
295
+ "input_params", postgresql.JSONB(astext_type=sa.Text()), nullable=False
296
+ ),
297
+ sa.Column(
298
+ "status",
299
+ sa.String(length=50),
300
+ nullable=False,
301
+ server_default=sa.text("'pending'::character varying"),
302
+ ),
303
+ sa.Column("storage_url", sa.Text()),
304
+ sa.Column("thumbnail_url", sa.Text()),
305
+ sa.Column(
306
+ "additional_files",
307
+ postgresql.JSONB(astext_type=sa.Text()),
308
+ server_default=sa.text("'[]'::jsonb"),
309
+ ),
310
+ sa.Column(
311
+ "output_metadata",
312
+ postgresql.JSONB(astext_type=sa.Text()),
313
+ server_default=sa.text("'{}'::jsonb"),
314
+ ),
315
+ sa.Column("parent_generation_id", postgresql.UUID(as_uuid=False)),
316
+ sa.Column(
317
+ "input_generation_ids",
318
+ postgresql.ARRAY(postgresql.UUID()),
319
+ server_default=sa.text("'{}'::uuid[]"),
320
+ ),
321
+ sa.Column("external_job_id", sa.String(length=255)),
322
+ sa.Column("progress", sa.Numeric(5, 2), server_default=sa.text("0.0")),
323
+ sa.Column("error_message", sa.Text()),
324
+ sa.Column("started_at", sa.DateTime(timezone=True)),
325
+ sa.Column("completed_at", sa.DateTime(timezone=True)),
326
+ sa.Column(
327
+ "created_at",
328
+ sa.DateTime(timezone=True),
329
+ server_default=sa.text("CURRENT_TIMESTAMP"),
330
+ ),
331
+ sa.Column(
332
+ "updated_at",
333
+ sa.DateTime(timezone=True),
334
+ server_default=sa.text("CURRENT_TIMESTAMP"),
335
+ ),
336
+ sa.PrimaryKeyConstraint("id", name="generations_pkey"),
337
+ sa.ForeignKeyConstraint(
338
+ ["board_id"],
339
+ ["boards.id"],
340
+ ondelete="CASCADE",
341
+ name="generations_board_id_fkey",
342
+ ),
343
+ sa.ForeignKeyConstraint(
344
+ ["parent_generation_id"],
345
+ ["generations.id"],
346
+ name="generations_parent_generation_id_fkey",
347
+ ),
348
+ sa.ForeignKeyConstraint(
349
+ ["tenant_id"],
350
+ ["tenants.id"],
351
+ ondelete="CASCADE",
352
+ name="generations_tenant_id_fkey",
353
+ ),
354
+ sa.ForeignKeyConstraint(
355
+ ["user_id"],
356
+ ["users.id"],
357
+ ondelete="CASCADE",
358
+ name="generations_user_id_fkey",
359
+ ),
360
+ )
361
+ op.create_index("idx_generations_board", "generations", ["board_id"], unique=False)
362
+ op.create_index(
363
+ "idx_generations_lineage", "generations", ["parent_generation_id"], unique=False
364
+ )
365
+ op.create_index("idx_generations_status", "generations", ["status"], unique=False)
366
+ op.create_index(
367
+ "idx_generations_tenant", "generations", ["tenant_id"], unique=False
368
+ )
369
+ op.create_index("idx_generations_user", "generations", ["user_id"], unique=False)
370
+
371
+ # credit_transactions
372
+ op.create_table(
373
+ "credit_transactions",
374
+ sa.Column(
375
+ "id",
376
+ postgresql.UUID(as_uuid=False),
377
+ server_default=sa.text("uuid_generate_v4()"),
378
+ ),
379
+ sa.Column("tenant_id", postgresql.UUID(as_uuid=False), nullable=False),
380
+ sa.Column("user_id", postgresql.UUID(as_uuid=False), nullable=False),
381
+ sa.Column("transaction_type", sa.String(length=20), nullable=False),
382
+ sa.Column("amount", sa.Numeric(10, 4), nullable=False),
383
+ sa.Column("balance_after", sa.Numeric(10, 4), nullable=False),
384
+ sa.Column("generation_id", postgresql.UUID(as_uuid=False)),
385
+ sa.Column("description", sa.Text()),
386
+ sa.Column(
387
+ "metadata",
388
+ postgresql.JSONB(astext_type=sa.Text()),
389
+ server_default=sa.text("'{}'::jsonb"),
390
+ ),
391
+ sa.Column(
392
+ "created_at",
393
+ sa.DateTime(timezone=True),
394
+ server_default=sa.text("CURRENT_TIMESTAMP"),
395
+ ),
396
+ sa.PrimaryKeyConstraint("id", name="credit_transactions_pkey"),
397
+ sa.ForeignKeyConstraint(
398
+ ["generation_id"],
399
+ ["generations.id"],
400
+ name="credit_transactions_generation_id_fkey",
401
+ ),
402
+ sa.ForeignKeyConstraint(
403
+ ["tenant_id"],
404
+ ["tenants.id"],
405
+ ondelete="CASCADE",
406
+ name="credit_transactions_tenant_id_fkey",
407
+ ),
408
+ sa.ForeignKeyConstraint(
409
+ ["user_id"],
410
+ ["users.id"],
411
+ ondelete="CASCADE",
412
+ name="credit_transactions_user_id_fkey",
413
+ ),
414
+ )
415
+ op.create_index(
416
+ "idx_credit_transactions_user", "credit_transactions", ["user_id"], unique=False
417
+ )
418
+
419
+
420
+ def downgrade() -> None:
421
+ # drop in reverse dependency order
422
+ op.drop_index("idx_credit_transactions_user", table_name="credit_transactions")
423
+ op.drop_table("credit_transactions")
424
+
425
+ op.drop_index("idx_generations_user", table_name="generations")
426
+ op.drop_index("idx_generations_tenant", table_name="generations")
427
+ op.drop_index("idx_generations_status", table_name="generations")
428
+ op.drop_index("idx_generations_lineage", table_name="generations")
429
+ op.drop_index("idx_generations_board", table_name="generations")
430
+ op.drop_table("generations")
431
+
432
+ op.drop_index("idx_board_members_user", table_name="board_members")
433
+ op.drop_index("idx_board_members_board", table_name="board_members")
434
+ op.drop_table("board_members")
435
+
436
+ op.drop_table("lora_models")
437
+
438
+ op.drop_index("idx_boards_tenant", table_name="boards")
439
+ op.drop_index("idx_boards_owner", table_name="boards")
440
+ op.drop_table("boards")
441
+
442
+ op.drop_index("idx_users_tenant", table_name="users")
443
+ op.drop_index("idx_users_auth", table_name="users")
444
+ op.drop_table("users")
445
+
446
+ op.drop_table("provider_configs")
447
+
448
+ op.drop_table("tenants")
@@ -0,0 +1,71 @@
1
+ """remove provider_name from generations
2
+
3
+ Revision ID: d7c16af77c9e
4
+ Revises: 553dc6a50a20
5
+ Create Date: 2025-10-22 17:47:29.312285
6
+
7
+ """
8
+
9
+ from typing import Sequence, Union
10
+
11
+ from alembic import op
12
+ import sqlalchemy as sa
13
+ from sqlalchemy.dialects import postgresql
14
+
15
+ # revision identifiers, used by Alembic.
16
+ revision: str = "d7c16af77c9e"
17
+ down_revision: Union[str, Sequence[str], None] = "553dc6a50a20"
18
+ branch_labels: Union[str, Sequence[str], None] = None
19
+ depends_on: Union[str, Sequence[str], None] = None
20
+
21
+
22
+ def upgrade() -> None:
23
+ """Upgrade schema."""
24
+ # ### commands auto generated by Alembic - please adjust! ###
25
+ # Drop schema_migrations table if it exists (from legacy migration system)
26
+ op.execute("DROP TABLE IF EXISTS schema_migrations")
27
+ op.drop_column("generations", "provider_name")
28
+ # ### end Alembic commands ###
29
+
30
+
31
+ def downgrade() -> None:
32
+ """Downgrade schema."""
33
+ # ### commands auto generated by Alembic - please adjust! ###
34
+ # Add column as nullable first, then update existing rows, then make it NOT NULL
35
+ op.add_column(
36
+ "generations",
37
+ sa.Column(
38
+ "provider_name", sa.VARCHAR(length=100), autoincrement=False, nullable=True
39
+ ),
40
+ )
41
+ # Set a default value for existing rows (though this downgrade should rarely be used)
42
+ op.execute(
43
+ "UPDATE generations SET provider_name = 'unknown' WHERE provider_name IS NULL"
44
+ )
45
+ # Now make it NOT NULL
46
+ op.alter_column("generations", "provider_name", nullable=False)
47
+ op.create_table(
48
+ "schema_migrations",
49
+ sa.Column(
50
+ "version", sa.VARCHAR(length=255), autoincrement=False, nullable=False
51
+ ),
52
+ sa.Column("name", sa.VARCHAR(length=255), autoincrement=False, nullable=False),
53
+ sa.Column(
54
+ "applied_at",
55
+ postgresql.TIMESTAMP(timezone=True),
56
+ server_default=sa.text("CURRENT_TIMESTAMP"),
57
+ autoincrement=False,
58
+ nullable=True,
59
+ ),
60
+ sa.Column(
61
+ "execution_time_ms", sa.INTEGER(), autoincrement=False, nullable=True
62
+ ),
63
+ sa.Column(
64
+ "checksum", sa.VARCHAR(length=64), autoincrement=False, nullable=True
65
+ ),
66
+ sa.Column(
67
+ "applied_by", sa.VARCHAR(length=255), autoincrement=False, nullable=True
68
+ ),
69
+ sa.PrimaryKeyConstraint("version", name=op.f("schema_migrations_pkey")),
70
+ )
71
+ # ### end Alembic commands ###