@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,411 @@
1
+ """switch to declarative base and mapping
2
+
3
+ Revision ID: cdad231052d5
4
+ Revises: d7c16af77c9e
5
+ Create Date: 2025-10-23 16:58:52.004927
6
+
7
+ """
8
+
9
+ from collections.abc import Sequence
10
+ from typing import Union
11
+
12
+ import sqlalchemy as sa
13
+ from sqlalchemy.dialects import postgresql
14
+
15
+ from alembic import op
16
+
17
+ # revision identifiers, used by Alembic.
18
+ revision: str = "cdad231052d5"
19
+ down_revision: str | Sequence[str] | None = "d7c16af77c9e"
20
+ branch_labels: str | Sequence[str] | None = None
21
+ depends_on: str | Sequence[str] | None = None
22
+
23
+
24
+ def upgrade() -> None:
25
+ """Upgrade schema."""
26
+ # ### commands auto generated by Alembic - please adjust! ###
27
+ op.alter_column(
28
+ "board_members",
29
+ "joined_at",
30
+ existing_type=postgresql.TIMESTAMP(timezone=True),
31
+ nullable=False,
32
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
33
+ )
34
+ op.alter_column(
35
+ "boards",
36
+ "is_public",
37
+ existing_type=sa.BOOLEAN(),
38
+ nullable=False,
39
+ existing_server_default=sa.text("false"),
40
+ )
41
+ op.alter_column(
42
+ "boards",
43
+ "settings",
44
+ existing_type=postgresql.JSONB(astext_type=sa.Text()),
45
+ nullable=False,
46
+ existing_server_default=sa.text("'{}'::jsonb"),
47
+ )
48
+ op.alter_column(
49
+ "boards",
50
+ "metadata",
51
+ existing_type=postgresql.JSONB(astext_type=sa.Text()),
52
+ nullable=False,
53
+ existing_server_default=sa.text("'{}'::jsonb"),
54
+ )
55
+ op.alter_column(
56
+ "boards",
57
+ "created_at",
58
+ existing_type=postgresql.TIMESTAMP(timezone=True),
59
+ nullable=False,
60
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
61
+ )
62
+ op.alter_column(
63
+ "boards",
64
+ "updated_at",
65
+ existing_type=postgresql.TIMESTAMP(timezone=True),
66
+ nullable=False,
67
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
68
+ )
69
+ op.alter_column(
70
+ "credit_transactions",
71
+ "metadata",
72
+ existing_type=postgresql.JSONB(astext_type=sa.Text()),
73
+ nullable=False,
74
+ existing_server_default=sa.text("'{}'::jsonb"),
75
+ )
76
+ op.alter_column(
77
+ "credit_transactions",
78
+ "created_at",
79
+ existing_type=postgresql.TIMESTAMP(timezone=True),
80
+ nullable=False,
81
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
82
+ )
83
+ op.alter_column(
84
+ "generations",
85
+ "additional_files",
86
+ existing_type=postgresql.JSONB(astext_type=sa.Text()),
87
+ nullable=False,
88
+ existing_server_default=sa.text("'[]'::jsonb"),
89
+ )
90
+ op.alter_column(
91
+ "generations",
92
+ "output_metadata",
93
+ existing_type=postgresql.JSONB(astext_type=sa.Text()),
94
+ nullable=False,
95
+ existing_server_default=sa.text("'{}'::jsonb"),
96
+ )
97
+ op.alter_column(
98
+ "generations",
99
+ "input_generation_ids",
100
+ existing_type=postgresql.ARRAY(sa.UUID()),
101
+ nullable=False,
102
+ existing_server_default=sa.text("'{}'::uuid[]"),
103
+ )
104
+ op.alter_column(
105
+ "generations",
106
+ "progress",
107
+ existing_type=sa.NUMERIC(precision=5, scale=2),
108
+ nullable=False,
109
+ existing_server_default=sa.text("0.0"),
110
+ )
111
+ op.alter_column(
112
+ "generations",
113
+ "created_at",
114
+ existing_type=postgresql.TIMESTAMP(timezone=True),
115
+ nullable=False,
116
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
117
+ )
118
+ op.alter_column(
119
+ "generations",
120
+ "updated_at",
121
+ existing_type=postgresql.TIMESTAMP(timezone=True),
122
+ nullable=False,
123
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
124
+ )
125
+ op.alter_column(
126
+ "lora_models",
127
+ "metadata",
128
+ existing_type=postgresql.JSONB(astext_type=sa.Text()),
129
+ nullable=False,
130
+ existing_server_default=sa.text("'{}'::jsonb"),
131
+ )
132
+ op.alter_column(
133
+ "lora_models",
134
+ "is_public",
135
+ existing_type=sa.BOOLEAN(),
136
+ nullable=False,
137
+ existing_server_default=sa.text("false"),
138
+ )
139
+ op.alter_column(
140
+ "lora_models",
141
+ "created_at",
142
+ existing_type=postgresql.TIMESTAMP(timezone=True),
143
+ nullable=False,
144
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
145
+ )
146
+ op.alter_column(
147
+ "lora_models",
148
+ "updated_at",
149
+ existing_type=postgresql.TIMESTAMP(timezone=True),
150
+ nullable=False,
151
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
152
+ )
153
+ op.alter_column(
154
+ "provider_configs",
155
+ "is_enabled",
156
+ existing_type=sa.BOOLEAN(),
157
+ nullable=False,
158
+ existing_server_default=sa.text("true"),
159
+ )
160
+ op.alter_column(
161
+ "provider_configs",
162
+ "created_at",
163
+ existing_type=postgresql.TIMESTAMP(timezone=True),
164
+ nullable=False,
165
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
166
+ )
167
+ op.alter_column(
168
+ "provider_configs",
169
+ "updated_at",
170
+ existing_type=postgresql.TIMESTAMP(timezone=True),
171
+ nullable=False,
172
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
173
+ )
174
+ op.alter_column(
175
+ "tenants",
176
+ "settings",
177
+ existing_type=postgresql.JSONB(astext_type=sa.Text()),
178
+ nullable=False,
179
+ existing_server_default=sa.text("'{}'::jsonb"),
180
+ )
181
+ op.alter_column(
182
+ "tenants",
183
+ "created_at",
184
+ existing_type=postgresql.TIMESTAMP(timezone=True),
185
+ nullable=False,
186
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
187
+ )
188
+ op.alter_column(
189
+ "tenants",
190
+ "updated_at",
191
+ existing_type=postgresql.TIMESTAMP(timezone=True),
192
+ nullable=False,
193
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
194
+ )
195
+ op.alter_column(
196
+ "users",
197
+ "metadata",
198
+ existing_type=postgresql.JSONB(astext_type=sa.Text()),
199
+ nullable=False,
200
+ existing_server_default=sa.text("'{}'::jsonb"),
201
+ )
202
+ op.alter_column(
203
+ "users",
204
+ "created_at",
205
+ existing_type=postgresql.TIMESTAMP(timezone=True),
206
+ nullable=False,
207
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
208
+ )
209
+ op.alter_column(
210
+ "users",
211
+ "updated_at",
212
+ existing_type=postgresql.TIMESTAMP(timezone=True),
213
+ nullable=False,
214
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
215
+ )
216
+ # ### end Alembic commands ###
217
+
218
+
219
+ def downgrade() -> None:
220
+ """Downgrade schema."""
221
+ # ### commands auto generated by Alembic - please adjust! ###
222
+ op.alter_column(
223
+ "users",
224
+ "updated_at",
225
+ existing_type=postgresql.TIMESTAMP(timezone=True),
226
+ nullable=True,
227
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
228
+ )
229
+ op.alter_column(
230
+ "users",
231
+ "created_at",
232
+ existing_type=postgresql.TIMESTAMP(timezone=True),
233
+ nullable=True,
234
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
235
+ )
236
+ op.alter_column(
237
+ "users",
238
+ "metadata",
239
+ existing_type=postgresql.JSONB(astext_type=sa.Text()),
240
+ nullable=True,
241
+ existing_server_default=sa.text("'{}'::jsonb"),
242
+ )
243
+ op.alter_column(
244
+ "tenants",
245
+ "updated_at",
246
+ existing_type=postgresql.TIMESTAMP(timezone=True),
247
+ nullable=True,
248
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
249
+ )
250
+ op.alter_column(
251
+ "tenants",
252
+ "created_at",
253
+ existing_type=postgresql.TIMESTAMP(timezone=True),
254
+ nullable=True,
255
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
256
+ )
257
+ op.alter_column(
258
+ "tenants",
259
+ "settings",
260
+ existing_type=postgresql.JSONB(astext_type=sa.Text()),
261
+ nullable=True,
262
+ existing_server_default=sa.text("'{}'::jsonb"),
263
+ )
264
+ op.alter_column(
265
+ "provider_configs",
266
+ "updated_at",
267
+ existing_type=postgresql.TIMESTAMP(timezone=True),
268
+ nullable=True,
269
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
270
+ )
271
+ op.alter_column(
272
+ "provider_configs",
273
+ "created_at",
274
+ existing_type=postgresql.TIMESTAMP(timezone=True),
275
+ nullable=True,
276
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
277
+ )
278
+ op.alter_column(
279
+ "provider_configs",
280
+ "is_enabled",
281
+ existing_type=sa.BOOLEAN(),
282
+ nullable=True,
283
+ existing_server_default=sa.text("true"),
284
+ )
285
+ op.alter_column(
286
+ "lora_models",
287
+ "updated_at",
288
+ existing_type=postgresql.TIMESTAMP(timezone=True),
289
+ nullable=True,
290
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
291
+ )
292
+ op.alter_column(
293
+ "lora_models",
294
+ "created_at",
295
+ existing_type=postgresql.TIMESTAMP(timezone=True),
296
+ nullable=True,
297
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
298
+ )
299
+ op.alter_column(
300
+ "lora_models",
301
+ "is_public",
302
+ existing_type=sa.BOOLEAN(),
303
+ nullable=True,
304
+ existing_server_default=sa.text("false"),
305
+ )
306
+ op.alter_column(
307
+ "lora_models",
308
+ "metadata",
309
+ existing_type=postgresql.JSONB(astext_type=sa.Text()),
310
+ nullable=True,
311
+ existing_server_default=sa.text("'{}'::jsonb"),
312
+ )
313
+ op.alter_column(
314
+ "generations",
315
+ "updated_at",
316
+ existing_type=postgresql.TIMESTAMP(timezone=True),
317
+ nullable=True,
318
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
319
+ )
320
+ op.alter_column(
321
+ "generations",
322
+ "created_at",
323
+ existing_type=postgresql.TIMESTAMP(timezone=True),
324
+ nullable=True,
325
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
326
+ )
327
+ op.alter_column(
328
+ "generations",
329
+ "progress",
330
+ existing_type=sa.NUMERIC(precision=5, scale=2),
331
+ nullable=True,
332
+ existing_server_default=sa.text("0.0"),
333
+ )
334
+ op.alter_column(
335
+ "generations",
336
+ "input_generation_ids",
337
+ existing_type=postgresql.ARRAY(sa.UUID()),
338
+ nullable=True,
339
+ existing_server_default=sa.text("'{}'::uuid[]"),
340
+ )
341
+ op.alter_column(
342
+ "generations",
343
+ "output_metadata",
344
+ existing_type=postgresql.JSONB(astext_type=sa.Text()),
345
+ nullable=True,
346
+ existing_server_default=sa.text("'{}'::jsonb"),
347
+ )
348
+ op.alter_column(
349
+ "generations",
350
+ "additional_files",
351
+ existing_type=postgresql.JSONB(astext_type=sa.Text()),
352
+ nullable=True,
353
+ existing_server_default=sa.text("'[]'::jsonb"),
354
+ )
355
+ op.alter_column(
356
+ "credit_transactions",
357
+ "created_at",
358
+ existing_type=postgresql.TIMESTAMP(timezone=True),
359
+ nullable=True,
360
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
361
+ )
362
+ op.alter_column(
363
+ "credit_transactions",
364
+ "metadata",
365
+ existing_type=postgresql.JSONB(astext_type=sa.Text()),
366
+ nullable=True,
367
+ existing_server_default=sa.text("'{}'::jsonb"),
368
+ )
369
+ op.alter_column(
370
+ "boards",
371
+ "updated_at",
372
+ existing_type=postgresql.TIMESTAMP(timezone=True),
373
+ nullable=True,
374
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
375
+ )
376
+ op.alter_column(
377
+ "boards",
378
+ "created_at",
379
+ existing_type=postgresql.TIMESTAMP(timezone=True),
380
+ nullable=True,
381
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
382
+ )
383
+ op.alter_column(
384
+ "boards",
385
+ "metadata",
386
+ existing_type=postgresql.JSONB(astext_type=sa.Text()),
387
+ nullable=True,
388
+ existing_server_default=sa.text("'{}'::jsonb"),
389
+ )
390
+ op.alter_column(
391
+ "boards",
392
+ "settings",
393
+ existing_type=postgresql.JSONB(astext_type=sa.Text()),
394
+ nullable=True,
395
+ existing_server_default=sa.text("'{}'::jsonb"),
396
+ )
397
+ op.alter_column(
398
+ "boards",
399
+ "is_public",
400
+ existing_type=sa.BOOLEAN(),
401
+ nullable=True,
402
+ existing_server_default=sa.text("false"),
403
+ )
404
+ op.alter_column(
405
+ "board_members",
406
+ "joined_at",
407
+ existing_type=postgresql.TIMESTAMP(timezone=True),
408
+ nullable=True,
409
+ existing_server_default=sa.text("CURRENT_TIMESTAMP"),
410
+ )
411
+ # ### end Alembic commands ###
@@ -0,0 +1,85 @@
1
+ """Add seed data for default tenant
2
+
3
+ This migration creates a default tenant for single-tenant deployments.
4
+ It can be customized via environment variables:
5
+
6
+ - BOARDS_TENANT_NAME: Display name for the default tenant (default: "Default Tenant")
7
+ - BOARDS_TENANT_SLUG: Slug for the default tenant (default: "default")
8
+
9
+ Revision ID: 553dc6a50a20
10
+ Revises: 20250101_000000_initial_schema
11
+ Create Date: 2025-09-25 06:27:35.976189
12
+
13
+ """
14
+ import os
15
+ from collections.abc import Sequence
16
+
17
+ import sqlalchemy as sa
18
+
19
+ from alembic import op
20
+
21
+ # revision identifiers, used by Alembic.
22
+ revision: str = '553dc6a50a20'
23
+ down_revision: str | Sequence[str] | None = '20250101_000000_initial_schema'
24
+ branch_labels: str | Sequence[str] | None = None
25
+ depends_on: str | Sequence[str] | None = None
26
+
27
+
28
+ def upgrade() -> None:
29
+ """Upgrade schema and seed default tenant."""
30
+ # Get configuration from environment variables
31
+ tenant_name = os.getenv("BOARDS_TENANT_NAME", "Default Tenant")
32
+ tenant_slug = os.getenv("BOARDS_TENANT_SLUG", "default")
33
+
34
+ # Only create the default tenant if it doesn't exist
35
+ # This allows the migration to be run multiple times safely
36
+ connection = op.get_bind()
37
+
38
+ # Check if tenant already exists
39
+ existing_tenant = connection.execute(
40
+ sa.text("SELECT id FROM tenants WHERE slug = :slug"),
41
+ {"slug": tenant_slug}
42
+ ).fetchone()
43
+
44
+ if not existing_tenant:
45
+ # Insert the default tenant
46
+ connection.execute(
47
+ sa.text("""
48
+ INSERT INTO tenants (name, slug, settings, created_at, updated_at)
49
+ VALUES (:name, :slug, '{}', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
50
+ """),
51
+ {
52
+ "name": tenant_name,
53
+ "slug": tenant_slug,
54
+ }
55
+ )
56
+ print(f"Created default tenant: {tenant_name} (slug: {tenant_slug})")
57
+ else:
58
+ print(f"Default tenant already exists: {tenant_slug}")
59
+
60
+
61
+ def downgrade() -> None:
62
+ """Downgrade schema and remove seeded data."""
63
+ # Get configuration from environment variables (same as upgrade)
64
+ tenant_slug = os.getenv("BOARDS_TENANT_SLUG", "default")
65
+
66
+ # Remove the default tenant
67
+ # WARNING: This will cascade delete all related data!
68
+ connection = op.get_bind()
69
+
70
+ result = connection.execute(
71
+ sa.text("DELETE FROM tenants WHERE slug = :slug"),
72
+ {"slug": tenant_slug}
73
+ )
74
+
75
+ if result.rowcount > 0:
76
+ print(f"Removed default tenant: {tenant_slug}")
77
+ else:
78
+ print(f"Default tenant not found: {tenant_slug}")
79
+
80
+ # Note: Due to CASCADE constraints, this will also remove:
81
+ # - All users in this tenant
82
+ # - All boards in this tenant
83
+ # - All generations in this tenant
84
+ # - All provider configs for this tenant
85
+ print("WARNING: All tenant data has been removed due to CASCADE constraints")
@@ -0,0 +1,36 @@
1
+ [alembic]
2
+ script_location = alembic
3
+ sqlalchemy.url = postgresql+asyncpg://user:pass@localhost:5433/placeholder
4
+ file_template = %%(year)s%%(month)s%%(day)s_%%(hour)s%%(minute)s%%(second)s_%%(slug)s
5
+
6
+ [loggers]
7
+ keys = root,sqlalchemy,alembic
8
+
9
+ [handlers]
10
+ keys = console
11
+
12
+ [formatters]
13
+ keys = generic
14
+
15
+ [logger_root]
16
+ level = WARN
17
+ handlers = console
18
+
19
+ [logger_sqlalchemy]
20
+ level = WARN
21
+ handlers =
22
+ qualname = sqlalchemy.engine
23
+
24
+ [logger_alembic]
25
+ level = INFO
26
+ handlers =
27
+ qualname = alembic
28
+
29
+ [handler_console]
30
+ class = StreamHandler
31
+ args = (sys.stderr,)
32
+ level = NOTSET
33
+ formatter = generic
34
+
35
+ [formatter_generic]
36
+ format = %(levelname)-5.5s [%(name)s] %(message)s
@@ -0,0 +1,25 @@
1
+ # Baseline generators configuration for Boards
2
+ #
3
+ # Set the path to this file via environment variable:
4
+ # export BOARDS_GENERATORS_CONFIG_PATH=/path/to/generators.yaml
5
+ #
6
+ # If not set, no generators will be loaded.
7
+
8
+ strict_mode: true
9
+ allow_unlisted: false
10
+
11
+ generators:
12
+ # Image generators
13
+ - class: "boards.generators.implementations.image.flux_pro.FluxProGenerator"
14
+ enabled: true
15
+
16
+ # Audio generators
17
+ - class: "boards.generators.implementations.audio.whisper.WhisperGenerator"
18
+ enabled: true
19
+
20
+ - class: "boards.generators.implementations.image.dalle3.DallE3Generator"
21
+ enabled: true
22
+
23
+ # Video generators
24
+ - class: "boards.generators.implementations.video.lipsync.LipsyncGenerator"
25
+ enabled: true
@@ -0,0 +1,26 @@
1
+ storage:
2
+ default_provider: local
3
+ max_file_size: 104857600
4
+ allowed_content_types:
5
+ - image/jpeg
6
+ - image/png
7
+ - image/webp
8
+ - image/gif
9
+ - video/mp4
10
+ - video/webm
11
+ - video/quicktime
12
+ - audio/mpeg
13
+ - audio/wav
14
+ - audio/ogg
15
+ - text/plain
16
+ - application/json
17
+ - text/markdown
18
+ - application/octet-stream
19
+ providers:
20
+ local:
21
+ type: local
22
+ config:
23
+ base_path: ./data/storage
24
+ public_url_base: http://localhost:8800/api/storage
25
+ routing_rules:
26
+ - provider: local