@weirdfingers/baseboards 0.9.5 → 0.9.7

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 (237) hide show
  1. package/dist/index.js +561 -469
  2. package/dist/index.js.map +1 -1
  3. package/package.json +2 -5
  4. package/templates/README.md +0 -122
  5. package/templates/api/.env.example +0 -65
  6. package/templates/api/ARTIFACT_RESOLUTION_GUIDE.md +0 -148
  7. package/templates/api/Dockerfile +0 -32
  8. package/templates/api/README.md +0 -264
  9. package/templates/api/alembic/env.py +0 -114
  10. package/templates/api/alembic/script.py.mako +0 -28
  11. package/templates/api/alembic/versions/20250101_000000_initial_schema.py +0 -506
  12. package/templates/api/alembic/versions/20251022_174729_remove_provider_name_from_generations.py +0 -75
  13. package/templates/api/alembic/versions/20251023_165852_switch_to_declarative_base_and_mapping.py +0 -467
  14. package/templates/api/alembic/versions/20251202_000000_add_artifact_lineage.py +0 -134
  15. package/templates/api/alembic/versions/2025925_62735_add_seed_data_for_default_tenant.py +0 -88
  16. package/templates/api/alembic.ini +0 -36
  17. package/templates/api/config/generators.yaml +0 -237
  18. package/templates/api/config/storage_config.yaml +0 -26
  19. package/templates/api/docs/ADDING_GENERATORS.md +0 -409
  20. package/templates/api/docs/GENERATORS_API.md +0 -502
  21. package/templates/api/docs/MIGRATIONS.md +0 -472
  22. package/templates/api/docs/TESTING_LIVE_APIS.md +0 -417
  23. package/templates/api/docs/storage_providers.md +0 -337
  24. package/templates/api/pyproject.toml +0 -205
  25. package/templates/api/src/boards/__init__.py +0 -10
  26. package/templates/api/src/boards/api/app.py +0 -172
  27. package/templates/api/src/boards/api/auth.py +0 -75
  28. package/templates/api/src/boards/api/endpoints/__init__.py +0 -3
  29. package/templates/api/src/boards/api/endpoints/jobs.py +0 -76
  30. package/templates/api/src/boards/api/endpoints/setup.py +0 -505
  31. package/templates/api/src/boards/api/endpoints/sse.py +0 -129
  32. package/templates/api/src/boards/api/endpoints/storage.py +0 -155
  33. package/templates/api/src/boards/api/endpoints/tenant_registration.py +0 -296
  34. package/templates/api/src/boards/api/endpoints/uploads.py +0 -149
  35. package/templates/api/src/boards/api/endpoints/webhooks.py +0 -13
  36. package/templates/api/src/boards/auth/__init__.py +0 -15
  37. package/templates/api/src/boards/auth/adapters/__init__.py +0 -27
  38. package/templates/api/src/boards/auth/adapters/auth0.py +0 -220
  39. package/templates/api/src/boards/auth/adapters/base.py +0 -73
  40. package/templates/api/src/boards/auth/adapters/clerk.py +0 -172
  41. package/templates/api/src/boards/auth/adapters/jwt.py +0 -122
  42. package/templates/api/src/boards/auth/adapters/none.py +0 -102
  43. package/templates/api/src/boards/auth/adapters/oidc.py +0 -284
  44. package/templates/api/src/boards/auth/adapters/supabase.py +0 -110
  45. package/templates/api/src/boards/auth/context.py +0 -35
  46. package/templates/api/src/boards/auth/factory.py +0 -129
  47. package/templates/api/src/boards/auth/middleware.py +0 -221
  48. package/templates/api/src/boards/auth/provisioning.py +0 -129
  49. package/templates/api/src/boards/auth/tenant_extraction.py +0 -278
  50. package/templates/api/src/boards/cli.py +0 -354
  51. package/templates/api/src/boards/config.py +0 -131
  52. package/templates/api/src/boards/database/__init__.py +0 -7
  53. package/templates/api/src/boards/database/cli.py +0 -110
  54. package/templates/api/src/boards/database/connection.py +0 -292
  55. package/templates/api/src/boards/database/models.py +0 -19
  56. package/templates/api/src/boards/database/seed_data.py +0 -182
  57. package/templates/api/src/boards/dbmodels/__init__.py +0 -441
  58. package/templates/api/src/boards/generators/__init__.py +0 -57
  59. package/templates/api/src/boards/generators/artifact_resolution.py +0 -405
  60. package/templates/api/src/boards/generators/artifacts.py +0 -53
  61. package/templates/api/src/boards/generators/base.py +0 -144
  62. package/templates/api/src/boards/generators/implementations/__init__.py +0 -14
  63. package/templates/api/src/boards/generators/implementations/fal/__init__.py +0 -25
  64. package/templates/api/src/boards/generators/implementations/fal/audio/__init__.py +0 -23
  65. package/templates/api/src/boards/generators/implementations/fal/audio/beatoven_music_generation.py +0 -171
  66. package/templates/api/src/boards/generators/implementations/fal/audio/beatoven_sound_effect_generation.py +0 -167
  67. package/templates/api/src/boards/generators/implementations/fal/audio/chatterbox_text_to_speech.py +0 -176
  68. package/templates/api/src/boards/generators/implementations/fal/audio/chatterbox_tts_turbo.py +0 -195
  69. package/templates/api/src/boards/generators/implementations/fal/audio/elevenlabs_sound_effects_v2.py +0 -194
  70. package/templates/api/src/boards/generators/implementations/fal/audio/elevenlabs_tts_eleven_v3.py +0 -209
  71. package/templates/api/src/boards/generators/implementations/fal/audio/fal_elevenlabs_tts_turbo_v2_5.py +0 -206
  72. package/templates/api/src/boards/generators/implementations/fal/audio/fal_minimax_speech_26_hd.py +0 -237
  73. package/templates/api/src/boards/generators/implementations/fal/audio/minimax_music_v2.py +0 -173
  74. package/templates/api/src/boards/generators/implementations/fal/audio/minimax_speech_2_6_turbo.py +0 -221
  75. package/templates/api/src/boards/generators/implementations/fal/image/__init__.py +0 -63
  76. package/templates/api/src/boards/generators/implementations/fal/image/bytedance_seedream_v45_edit.py +0 -219
  77. package/templates/api/src/boards/generators/implementations/fal/image/clarity_upscaler.py +0 -220
  78. package/templates/api/src/boards/generators/implementations/fal/image/crystal_upscaler.py +0 -173
  79. package/templates/api/src/boards/generators/implementations/fal/image/fal_ideogram_character.py +0 -227
  80. package/templates/api/src/boards/generators/implementations/fal/image/flux_2.py +0 -203
  81. package/templates/api/src/boards/generators/implementations/fal/image/flux_2_edit.py +0 -230
  82. package/templates/api/src/boards/generators/implementations/fal/image/flux_2_pro.py +0 -204
  83. package/templates/api/src/boards/generators/implementations/fal/image/flux_2_pro_edit.py +0 -221
  84. package/templates/api/src/boards/generators/implementations/fal/image/flux_pro_kontext.py +0 -216
  85. package/templates/api/src/boards/generators/implementations/fal/image/flux_pro_ultra.py +0 -197
  86. package/templates/api/src/boards/generators/implementations/fal/image/gemini_25_flash_image.py +0 -177
  87. package/templates/api/src/boards/generators/implementations/fal/image/gemini_25_flash_image_edit.py +0 -208
  88. package/templates/api/src/boards/generators/implementations/fal/image/gpt_image_15_edit.py +0 -216
  89. package/templates/api/src/boards/generators/implementations/fal/image/gpt_image_1_5.py +0 -177
  90. package/templates/api/src/boards/generators/implementations/fal/image/gpt_image_1_edit_image.py +0 -182
  91. package/templates/api/src/boards/generators/implementations/fal/image/gpt_image_1_mini.py +0 -167
  92. package/templates/api/src/boards/generators/implementations/fal/image/ideogram_character_edit.py +0 -299
  93. package/templates/api/src/boards/generators/implementations/fal/image/ideogram_v2.py +0 -190
  94. package/templates/api/src/boards/generators/implementations/fal/image/imagen4_preview.py +0 -191
  95. package/templates/api/src/boards/generators/implementations/fal/image/imagen4_preview_fast.py +0 -179
  96. package/templates/api/src/boards/generators/implementations/fal/image/nano_banana.py +0 -183
  97. package/templates/api/src/boards/generators/implementations/fal/image/nano_banana_edit.py +0 -212
  98. package/templates/api/src/boards/generators/implementations/fal/image/nano_banana_pro.py +0 -179
  99. package/templates/api/src/boards/generators/implementations/fal/image/nano_banana_pro_edit.py +0 -226
  100. package/templates/api/src/boards/generators/implementations/fal/image/qwen_image.py +0 -249
  101. package/templates/api/src/boards/generators/implementations/fal/image/qwen_image_edit.py +0 -244
  102. package/templates/api/src/boards/generators/implementations/fal/image/reve_edit.py +0 -178
  103. package/templates/api/src/boards/generators/implementations/fal/image/reve_text_to_image.py +0 -155
  104. package/templates/api/src/boards/generators/implementations/fal/image/seedream_v45_text_to_image.py +0 -180
  105. package/templates/api/src/boards/generators/implementations/fal/utils.py +0 -61
  106. package/templates/api/src/boards/generators/implementations/fal/video/__init__.py +0 -77
  107. package/templates/api/src/boards/generators/implementations/fal/video/bytedance_seedance_v1_pro_text_to_video.py +0 -209
  108. package/templates/api/src/boards/generators/implementations/fal/video/creatify_lipsync.py +0 -161
  109. package/templates/api/src/boards/generators/implementations/fal/video/fal_bytedance_seedance_v1_pro_image_to_video.py +0 -222
  110. package/templates/api/src/boards/generators/implementations/fal/video/fal_minimax_hailuo_02_standard_text_to_video.py +0 -152
  111. package/templates/api/src/boards/generators/implementations/fal/video/fal_pixverse_lipsync.py +0 -197
  112. package/templates/api/src/boards/generators/implementations/fal/video/fal_sora_2_text_to_video.py +0 -173
  113. package/templates/api/src/boards/generators/implementations/fal/video/infinitalk.py +0 -221
  114. package/templates/api/src/boards/generators/implementations/fal/video/kling_video_ai_avatar_v2_pro.py +0 -168
  115. package/templates/api/src/boards/generators/implementations/fal/video/kling_video_ai_avatar_v2_standard.py +0 -159
  116. package/templates/api/src/boards/generators/implementations/fal/video/kling_video_v2_5_turbo_pro_image_to_video.py +0 -175
  117. package/templates/api/src/boards/generators/implementations/fal/video/kling_video_v2_5_turbo_pro_text_to_video.py +0 -168
  118. package/templates/api/src/boards/generators/implementations/fal/video/minimax_hailuo_2_3_pro_image_to_video.py +0 -153
  119. package/templates/api/src/boards/generators/implementations/fal/video/sora2_image_to_video.py +0 -172
  120. package/templates/api/src/boards/generators/implementations/fal/video/sora_2_image_to_video_pro.py +0 -175
  121. package/templates/api/src/boards/generators/implementations/fal/video/sora_2_text_to_video_pro.py +0 -163
  122. package/templates/api/src/boards/generators/implementations/fal/video/sync_lipsync_v2.py +0 -167
  123. package/templates/api/src/boards/generators/implementations/fal/video/sync_lipsync_v2_pro.py +0 -155
  124. package/templates/api/src/boards/generators/implementations/fal/video/veed_fabric_1_0.py +0 -180
  125. package/templates/api/src/boards/generators/implementations/fal/video/veed_lipsync.py +0 -174
  126. package/templates/api/src/boards/generators/implementations/fal/video/veo3.py +0 -194
  127. package/templates/api/src/boards/generators/implementations/fal/video/veo31.py +0 -190
  128. package/templates/api/src/boards/generators/implementations/fal/video/veo31_fast.py +0 -190
  129. package/templates/api/src/boards/generators/implementations/fal/video/veo31_fast_image_to_video.py +0 -191
  130. package/templates/api/src/boards/generators/implementations/fal/video/veo31_first_last_frame_to_video.py +0 -187
  131. package/templates/api/src/boards/generators/implementations/fal/video/veo31_image_to_video.py +0 -183
  132. package/templates/api/src/boards/generators/implementations/fal/video/veo31_reference_to_video.py +0 -172
  133. package/templates/api/src/boards/generators/implementations/fal/video/wan_25_preview_image_to_video.py +0 -212
  134. package/templates/api/src/boards/generators/implementations/fal/video/wan_25_preview_text_to_video.py +0 -208
  135. package/templates/api/src/boards/generators/implementations/fal/video/wan_pro_image_to_video.py +0 -158
  136. package/templates/api/src/boards/generators/implementations/kie/__init__.py +0 -11
  137. package/templates/api/src/boards/generators/implementations/kie/base.py +0 -316
  138. package/templates/api/src/boards/generators/implementations/kie/image/__init__.py +0 -3
  139. package/templates/api/src/boards/generators/implementations/kie/image/nano_banana_edit.py +0 -190
  140. package/templates/api/src/boards/generators/implementations/kie/utils.py +0 -98
  141. package/templates/api/src/boards/generators/implementations/kie/video/__init__.py +0 -8
  142. package/templates/api/src/boards/generators/implementations/kie/video/veo3.py +0 -161
  143. package/templates/api/src/boards/generators/implementations/openai/__init__.py +0 -1
  144. package/templates/api/src/boards/generators/implementations/openai/audio/__init__.py +0 -1
  145. package/templates/api/src/boards/generators/implementations/openai/audio/whisper.py +0 -69
  146. package/templates/api/src/boards/generators/implementations/openai/image/__init__.py +0 -1
  147. package/templates/api/src/boards/generators/implementations/openai/image/dalle3.py +0 -96
  148. package/templates/api/src/boards/generators/implementations/replicate/__init__.py +0 -1
  149. package/templates/api/src/boards/generators/implementations/replicate/image/__init__.py +0 -1
  150. package/templates/api/src/boards/generators/implementations/replicate/image/flux_pro.py +0 -88
  151. package/templates/api/src/boards/generators/implementations/replicate/video/__init__.py +0 -1
  152. package/templates/api/src/boards/generators/implementations/replicate/video/lipsync.py +0 -73
  153. package/templates/api/src/boards/generators/loader.py +0 -253
  154. package/templates/api/src/boards/generators/registry.py +0 -114
  155. package/templates/api/src/boards/generators/resolution.py +0 -632
  156. package/templates/api/src/boards/generators/testmods/class_gen.py +0 -34
  157. package/templates/api/src/boards/generators/testmods/import_side_effect.py +0 -35
  158. package/templates/api/src/boards/graphql/__init__.py +0 -7
  159. package/templates/api/src/boards/graphql/access_control.py +0 -136
  160. package/templates/api/src/boards/graphql/mutations/root.py +0 -148
  161. package/templates/api/src/boards/graphql/queries/root.py +0 -116
  162. package/templates/api/src/boards/graphql/resolvers/__init__.py +0 -8
  163. package/templates/api/src/boards/graphql/resolvers/auth.py +0 -12
  164. package/templates/api/src/boards/graphql/resolvers/board.py +0 -1053
  165. package/templates/api/src/boards/graphql/resolvers/generation.py +0 -666
  166. package/templates/api/src/boards/graphql/resolvers/generator.py +0 -50
  167. package/templates/api/src/boards/graphql/resolvers/lineage.py +0 -381
  168. package/templates/api/src/boards/graphql/resolvers/upload.py +0 -463
  169. package/templates/api/src/boards/graphql/resolvers/user.py +0 -25
  170. package/templates/api/src/boards/graphql/schema.py +0 -81
  171. package/templates/api/src/boards/graphql/types/board.py +0 -102
  172. package/templates/api/src/boards/graphql/types/generation.py +0 -166
  173. package/templates/api/src/boards/graphql/types/generator.py +0 -17
  174. package/templates/api/src/boards/graphql/types/user.py +0 -47
  175. package/templates/api/src/boards/jobs/repository.py +0 -153
  176. package/templates/api/src/boards/logging.py +0 -195
  177. package/templates/api/src/boards/middleware.py +0 -339
  178. package/templates/api/src/boards/progress/__init__.py +0 -4
  179. package/templates/api/src/boards/progress/models.py +0 -25
  180. package/templates/api/src/boards/progress/publisher.py +0 -64
  181. package/templates/api/src/boards/py.typed +0 -0
  182. package/templates/api/src/boards/redis_pool.py +0 -118
  183. package/templates/api/src/boards/storage/__init__.py +0 -52
  184. package/templates/api/src/boards/storage/base.py +0 -363
  185. package/templates/api/src/boards/storage/config.py +0 -187
  186. package/templates/api/src/boards/storage/factory.py +0 -288
  187. package/templates/api/src/boards/storage/implementations/__init__.py +0 -27
  188. package/templates/api/src/boards/storage/implementations/gcs.py +0 -340
  189. package/templates/api/src/boards/storage/implementations/local.py +0 -201
  190. package/templates/api/src/boards/storage/implementations/s3.py +0 -294
  191. package/templates/api/src/boards/storage/implementations/supabase.py +0 -218
  192. package/templates/api/src/boards/tenant_isolation.py +0 -446
  193. package/templates/api/src/boards/validation.py +0 -262
  194. package/templates/api/src/boards/workers/__init__.py +0 -1
  195. package/templates/api/src/boards/workers/actors.py +0 -274
  196. package/templates/api/src/boards/workers/cli.py +0 -125
  197. package/templates/api/src/boards/workers/context.py +0 -348
  198. package/templates/api/src/boards/workers/middleware.py +0 -58
  199. package/templates/api/src/py.typed +0 -0
  200. package/templates/compose.web.yaml +0 -35
  201. package/templates/compose.yaml +0 -116
  202. package/templates/docker/env.example +0 -23
  203. package/templates/web/.env.example +0 -28
  204. package/templates/web/Dockerfile +0 -51
  205. package/templates/web/components.json +0 -22
  206. package/templates/web/imageLoader.js +0 -18
  207. package/templates/web/next-env.d.ts +0 -5
  208. package/templates/web/next.config.js +0 -36
  209. package/templates/web/package.json +0 -41
  210. package/templates/web/postcss.config.mjs +0 -7
  211. package/templates/web/public/favicon.ico +0 -0
  212. package/templates/web/src/app/boards/[boardId]/page.tsx +0 -353
  213. package/templates/web/src/app/globals.css +0 -123
  214. package/templates/web/src/app/layout.tsx +0 -31
  215. package/templates/web/src/app/lineage/[generationId]/page.tsx +0 -235
  216. package/templates/web/src/app/page.tsx +0 -35
  217. package/templates/web/src/app/providers.tsx +0 -18
  218. package/templates/web/src/components/boards/ArtifactInputSlots.tsx +0 -206
  219. package/templates/web/src/components/boards/ArtifactPreview.tsx +0 -466
  220. package/templates/web/src/components/boards/GenerationGrid.tsx +0 -282
  221. package/templates/web/src/components/boards/GenerationInput.tsx +0 -370
  222. package/templates/web/src/components/boards/GeneratorSelector.tsx +0 -272
  223. package/templates/web/src/components/boards/UploadArtifact.tsx +0 -563
  224. package/templates/web/src/components/header.tsx +0 -32
  225. package/templates/web/src/components/theme-provider.tsx +0 -10
  226. package/templates/web/src/components/theme-toggle.tsx +0 -75
  227. package/templates/web/src/components/ui/alert-dialog.tsx +0 -157
  228. package/templates/web/src/components/ui/button.tsx +0 -58
  229. package/templates/web/src/components/ui/card.tsx +0 -92
  230. package/templates/web/src/components/ui/dropdown-menu.tsx +0 -200
  231. package/templates/web/src/components/ui/navigation-menu.tsx +0 -168
  232. package/templates/web/src/components/ui/toast.tsx +0 -128
  233. package/templates/web/src/components/ui/toaster.tsx +0 -35
  234. package/templates/web/src/components/ui/use-toast.ts +0 -187
  235. package/templates/web/src/hooks/useGeneratorMRU.ts +0 -57
  236. package/templates/web/src/lib/utils.ts +0 -6
  237. package/templates/web/tsconfig.json +0 -41
@@ -1,292 +0,0 @@
1
- """
2
- Database connection management
3
- """
4
-
5
- import os
6
- import threading
7
- from collections.abc import AsyncGenerator, Generator
8
- from contextlib import asynccontextmanager, contextmanager
9
-
10
- from sqlalchemy import create_engine, text
11
- from sqlalchemy.ext.asyncio import (
12
- AsyncEngine,
13
- AsyncSession,
14
- async_sessionmaker,
15
- create_async_engine,
16
- )
17
- from sqlalchemy.orm import Session, sessionmaker
18
-
19
- from ..config import settings
20
- from ..logging import get_logger
21
-
22
- logger = get_logger(__name__)
23
-
24
- # Global shared connection pools (proper FastAPI pattern)
25
- _engine = None
26
- _session_local = None
27
- _sync_initialized = False
28
- _sync_init_lock = threading.Lock()
29
-
30
-
31
- class AsyncDBContext(threading.local):
32
- engine: AsyncEngine | None
33
- initialized: bool
34
- session_local: async_sessionmaker[AsyncSession] | None
35
- lock: threading.Lock
36
-
37
- def __init__(self):
38
- self.engine = None
39
- self.initialized = False
40
- self.session_local = None
41
- self.lock = threading.Lock() # Per-thread lock for async initialization
42
-
43
-
44
- _async_db_ctx = AsyncDBContext()
45
-
46
-
47
- def get_database_url() -> str:
48
- """Get database URL, checking environment variables first for test compatibility."""
49
- # Always check environment first (for tests), then fall back to settings
50
- db_url = os.getenv("BOARDS_DATABASE_URL")
51
- if not db_url:
52
- # For tests that set env vars after settings are loaded
53
- if "BOARDS_DATABASE_URL" in os.environ:
54
- db_url = os.environ["BOARDS_DATABASE_URL"]
55
- else:
56
- db_url = settings.database_url
57
- return db_url
58
-
59
-
60
- def reset_database():
61
- """Reset database connections (for tests)."""
62
- global _engine, _session_local, _sync_initialized
63
-
64
- # Dispose of sync engine if it exists
65
- if _engine is not None:
66
- _engine.dispose()
67
-
68
- _engine = None
69
- _session_local = None
70
- _sync_initialized = False
71
-
72
- # Reset async context for current thread
73
- # Note: async engine disposal must be done with await, so we just clear the reference
74
- # The engine will be garbage collected when no sessions reference it
75
- _async_db_ctx.engine = None
76
- _async_db_ctx.session_local = None
77
- _async_db_ctx.initialized = False
78
-
79
-
80
- async def test_database_connection() -> tuple[bool, str | None]:
81
- """
82
- Test the database connection and return helpful error messages.
83
-
84
- Returns:
85
- tuple: (success: bool, error_message: str | None)
86
- """
87
- if _async_db_ctx.engine is None:
88
- return False, "Database engine not initialized"
89
-
90
- try:
91
- async with _async_db_ctx.engine.connect() as conn:
92
- await conn.execute(text("SELECT 1"))
93
- return True, None
94
- except Exception as e:
95
- error_str = str(e)
96
- error_type = type(e).__name__
97
-
98
- # Provide helpful error messages based on the error type
99
- if "does not exist" in error_str and "role" in error_str:
100
- # This is the confusing error - could be database server not running
101
- db_url = get_database_url()
102
- # Extract database name from URL
103
- db_name = db_url.split("/")[-1].split("?")[0]
104
- return False, (
105
- f"Cannot connect to database: {error_str}\n"
106
- f"This usually means:\n"
107
- f" 1. The database server is not running\n"
108
- f" 2. The database '{db_name}' doesn't exist\n"
109
- f" 3. The database user/role doesn't exist\n"
110
- f"Please check your database connection and run migrations if needed."
111
- )
112
- elif "Connection refused" in error_str or "could not connect" in error_str:
113
- return False, (
114
- f"Cannot connect to database server: {error_str}\n"
115
- f"The database server appears to be down or unreachable.\n"
116
- f"Please check that PostgreSQL is running and accessible."
117
- )
118
- elif "password authentication failed" in error_str:
119
- return False, (
120
- f"Database authentication failed: {error_str}\n"
121
- f"Please check your database credentials."
122
- )
123
- else:
124
- return False, f"Database connection error ({error_type}): {error_str}"
125
-
126
-
127
- def init_database(database_url: str | None = None, force_reinit: bool = False):
128
- """Initialize shared database connection pools.
129
-
130
- Thread-safe initialization using a lock to prevent race conditions
131
- when multiple threads attempt to initialize simultaneously.
132
- """
133
- global _engine, _session_local, _sync_initialized
134
-
135
- # Get the database URL
136
- db_url = database_url or get_database_url()
137
-
138
- # Initialize Sync Engine (Global)
139
- if not _sync_initialized or force_reinit:
140
- with _sync_init_lock:
141
- if not _sync_initialized or force_reinit:
142
- sync_db_url = db_url
143
- if db_url.startswith("postgresql://"):
144
- sync_db_url = db_url.replace("postgresql://", "postgresql+psycopg://")
145
- _engine = create_engine(
146
- url=sync_db_url,
147
- pool_size=settings.database_pool_size,
148
- max_overflow=settings.database_max_overflow,
149
- echo=settings.sql_echo,
150
- )
151
- _session_local = sessionmaker(autocommit=False, autoflush=False, bind=_engine)
152
- _sync_initialized = True
153
- logger.info("Sync database initialized", database_url=db_url)
154
-
155
- # Initialize Async Engine (Thread-Local)
156
- # Async engines must be thread-local because asyncpg connections are tied to the event loop
157
- # and cannot be shared across threads/loops.
158
- if not _async_db_ctx.initialized or force_reinit:
159
- with _async_db_ctx.lock:
160
- # Double-check after acquiring lock (another coroutine may have initialized)
161
- if not _async_db_ctx.initialized or force_reinit:
162
- if db_url.startswith("postgresql://"):
163
- async_db_url = db_url.replace("postgresql://", "postgresql+asyncpg://")
164
- _async_db_ctx.engine = create_async_engine(
165
- url=async_db_url,
166
- pool_size=settings.database_pool_size,
167
- max_overflow=settings.database_max_overflow,
168
- echo=settings.sql_echo,
169
- )
170
- _async_db_ctx.session_local = async_sessionmaker(
171
- _async_db_ctx.engine,
172
- class_=AsyncSession,
173
- autocommit=False,
174
- autoflush=False,
175
- )
176
- _async_db_ctx.initialized = True
177
- logger.info(
178
- "Async database initialized for thread",
179
- thread_id=threading.get_ident(),
180
- )
181
- else:
182
- logger.warning(
183
- "Non-PostgreSQL URL detected, async engine not initialized",
184
- url_prefix=db_url.split("://")[0] if "://" in db_url else "unknown",
185
- )
186
-
187
-
188
- def get_engine():
189
- """Get the shared SQLAlchemy engine."""
190
- if _engine is None:
191
- init_database()
192
- return _engine
193
-
194
-
195
- def get_async_engine():
196
- """Get the shared async SQLAlchemy engine."""
197
- if _async_db_ctx.engine is None:
198
- init_database()
199
- return _async_db_ctx.engine
200
-
201
-
202
- @contextmanager
203
- def get_session() -> Generator[Session, None, None]:
204
- """Get a database session (sync) from shared pool."""
205
- if _session_local is None:
206
- init_database()
207
-
208
- if _session_local is None:
209
- raise RuntimeError("Database not initialized")
210
-
211
- session = _session_local()
212
- try:
213
- yield session
214
- session.commit()
215
- except Exception:
216
- session.rollback()
217
- raise
218
- finally:
219
- session.close()
220
-
221
-
222
- @asynccontextmanager
223
- async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
224
- """Get a database session (async) from shared pool."""
225
- if _async_db_ctx.session_local is None:
226
- init_database()
227
-
228
- if _async_db_ctx.session_local is None:
229
- raise RuntimeError("Async database not available (PostgreSQL required)")
230
-
231
- async with _async_db_ctx.session_local() as session:
232
- try:
233
- yield session
234
- await session.commit()
235
- except Exception:
236
- await session.rollback()
237
- raise
238
- finally:
239
- await session.close()
240
-
241
-
242
- # Dependency for FastAPI
243
- async def get_db_session() -> AsyncGenerator[AsyncSession, None]:
244
- """FastAPI dependency for database sessions."""
245
- async with get_async_session() as session:
246
- yield session
247
-
248
-
249
- # Test-specific database session context
250
- @asynccontextmanager
251
- async def get_test_db_session(database_url: str) -> AsyncGenerator[AsyncSession, None]:
252
- """Get a database session for testing with explicit database URL."""
253
- from sqlalchemy.ext.asyncio import (
254
- AsyncSession,
255
- async_sessionmaker,
256
- create_async_engine,
257
- )
258
-
259
- from ..config import settings
260
-
261
- # Convert to async URL for PostgreSQL
262
- if database_url.startswith("postgresql://"):
263
- async_url = database_url.replace("postgresql://", "postgresql+asyncpg://")
264
- else:
265
- async_url = database_url
266
-
267
- # Create isolated engine for this session
268
- engine = create_async_engine(
269
- async_url,
270
- pool_size=settings.database_pool_size,
271
- max_overflow=settings.database_max_overflow,
272
- echo=settings.debug,
273
- isolation_level="AUTOCOMMIT", # Ensure we can see committed schema changes
274
- )
275
-
276
- session_local = async_sessionmaker(
277
- engine,
278
- class_=AsyncSession,
279
- autocommit=False,
280
- autoflush=False,
281
- )
282
-
283
- async with session_local() as session:
284
- try:
285
- yield session
286
- await session.commit()
287
- except Exception:
288
- await session.rollback()
289
- raise
290
- finally:
291
- await session.close()
292
- await engine.dispose()
@@ -1,19 +0,0 @@
1
- # type: ignore[reportMissingImports]
2
-
3
- """
4
- Compatibility shim: re-export models from boards.dbmodels
5
- This file remains to avoid breaking existing imports like `from boards.database.models import ...`.
6
- """
7
-
8
- from boards.dbmodels import ( # noqa: F401
9
- Base,
10
- BoardMembers,
11
- Boards,
12
- CreditTransactions,
13
- Generations,
14
- LoraModels,
15
- ProviderConfigs,
16
- Tenants,
17
- Users,
18
- target_metadata,
19
- )
@@ -1,182 +0,0 @@
1
- """
2
- Reusable seed data functions for database initialization.
3
-
4
- This module provides functions to seed initial data into the database,
5
- including tenants, users, and other setup-time functionality.
6
- """
7
-
8
- from __future__ import annotations
9
-
10
- import os
11
- from typing import Any
12
- from uuid import UUID
13
-
14
- from sqlalchemy import select
15
- from sqlalchemy.ext.asyncio import AsyncSession
16
-
17
- from ..config import settings
18
- from ..dbmodels import Tenants
19
- from ..logging import get_logger
20
-
21
- logger = get_logger(__name__)
22
-
23
-
24
- async def ensure_tenant(
25
- db: AsyncSession,
26
- *,
27
- tenant_id: UUID | None = None,
28
- name: str | None = None,
29
- slug: str | None = None,
30
- settings_dict: dict[str, Any] | None = None,
31
- ) -> UUID:
32
- """
33
- Ensure a tenant exists in the database.
34
-
35
- This function creates a tenant if it doesn't exist, or returns the
36
- existing tenant's ID if it does.
37
-
38
- Args:
39
- db: Database session
40
- tenant_id: UUID for the tenant (if None, auto-generated)
41
- name: Tenant name (defaults to env var or "Default Tenant")
42
- slug: Tenant slug (defaults to env var or "default")
43
- settings_dict: Optional tenant settings/metadata
44
-
45
- Returns:
46
- UUID of the tenant (existing or newly created)
47
- """
48
- # Use environment variables with fallbacks
49
- if name is None:
50
- name = os.getenv("BOARDS_TENANT_NAME", "Default Tenant")
51
-
52
- if slug is None:
53
- slug = os.getenv("BOARDS_TENANT_SLUG", settings.default_tenant_slug)
54
-
55
- if settings_dict is None:
56
- settings_dict = {}
57
-
58
- # Check if tenant already exists by slug
59
- stmt = select(Tenants).where(Tenants.slug == slug)
60
- result = await db.execute(stmt)
61
- existing_tenant = result.scalar_one_or_none()
62
-
63
- if existing_tenant:
64
- logger.debug(
65
- "Tenant already exists",
66
- tenant_id=str(existing_tenant.id),
67
- slug=slug,
68
- name=existing_tenant.name,
69
- )
70
- return existing_tenant.id
71
-
72
- # Create new tenant
73
- new_tenant = Tenants(
74
- name=name,
75
- slug=slug,
76
- settings=settings_dict,
77
- )
78
-
79
- # Set specific ID if provided
80
- if tenant_id:
81
- new_tenant.id = tenant_id
82
-
83
- db.add(new_tenant)
84
- await db.commit()
85
- await db.refresh(new_tenant)
86
-
87
- logger.info(
88
- "Created new tenant",
89
- tenant_id=str(new_tenant.id),
90
- slug=slug,
91
- name=name,
92
- )
93
-
94
- return new_tenant.id
95
-
96
-
97
- async def ensure_default_tenant(db: AsyncSession) -> UUID:
98
- """
99
- Ensure the default tenant exists for single-tenant or no-auth mode.
100
-
101
- This is a convenience function that uses environment variables
102
- or defaults to create/get the default tenant.
103
-
104
- Args:
105
- db: Database session
106
-
107
- Returns:
108
- UUID of the default tenant
109
- """
110
- return await ensure_tenant(db)
111
-
112
-
113
- async def seed_initial_data(db: AsyncSession) -> None:
114
- """
115
- Seed all initial data required for the application.
116
-
117
- This function can be extended to seed additional data like:
118
- - Default provider configurations
119
- - Initial admin users
120
- - Sample boards or generations
121
- - Default credit allocations
122
-
123
- Args:
124
- db: Database session
125
- """
126
- logger.info("Starting database seeding")
127
-
128
- # Always ensure default tenant exists in non-multi-tenant mode
129
- if not settings.multi_tenant_mode:
130
- tenant_id = await ensure_default_tenant(db)
131
- logger.info("Ensured default tenant exists", tenant_id=str(tenant_id))
132
-
133
- # Add more seed operations here as needed
134
- # For example:
135
- # await ensure_default_providers(db, tenant_id)
136
- # await ensure_admin_user(db, tenant_id)
137
-
138
- logger.info("Database seeding completed")
139
-
140
-
141
- async def seed_tenant_with_data(
142
- db: AsyncSession,
143
- *,
144
- tenant_name: str,
145
- tenant_slug: str,
146
- tenant_settings: dict[str, Any] | None = None,
147
- include_sample_data: bool = False,
148
- ) -> UUID:
149
- """
150
- Create a new tenant with optional sample data.
151
-
152
- This function is useful for:
153
- - Multi-tenant setup
154
- - Creating demo tenants
155
- - Testing different tenant configurations
156
-
157
- Args:
158
- db: Database session
159
- tenant_name: Display name for the tenant
160
- tenant_slug: Unique slug for the tenant
161
- tenant_settings: Optional tenant-specific settings
162
- include_sample_data: Whether to create sample boards/generations
163
-
164
- Returns:
165
- UUID of the created tenant
166
- """
167
- tenant_id = await ensure_tenant(
168
- db,
169
- name=tenant_name,
170
- slug=tenant_slug,
171
- settings_dict=tenant_settings,
172
- )
173
-
174
- if include_sample_data:
175
- # Future: Add sample boards, users, generations
176
- logger.info(
177
- "Would create sample data for tenant",
178
- tenant_id=str(tenant_id),
179
- note="Sample data creation not yet implemented",
180
- )
181
-
182
- return tenant_id