@weirdfingers/baseboards 0.9.6 → 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.
- package/dist/index.js +560 -469
- package/dist/index.js.map +1 -1
- package/package.json +2 -5
- package/templates/README.md +0 -122
- package/templates/api/.env.example +0 -65
- package/templates/api/ARTIFACT_RESOLUTION_GUIDE.md +0 -148
- package/templates/api/Dockerfile +0 -32
- package/templates/api/README.md +0 -264
- package/templates/api/alembic/env.py +0 -114
- package/templates/api/alembic/script.py.mako +0 -28
- package/templates/api/alembic/versions/20250101_000000_initial_schema.py +0 -506
- package/templates/api/alembic/versions/20251022_174729_remove_provider_name_from_generations.py +0 -75
- package/templates/api/alembic/versions/20251023_165852_switch_to_declarative_base_and_mapping.py +0 -467
- package/templates/api/alembic/versions/20251202_000000_add_artifact_lineage.py +0 -134
- package/templates/api/alembic/versions/2025925_62735_add_seed_data_for_default_tenant.py +0 -88
- package/templates/api/alembic.ini +0 -36
- package/templates/api/config/generators.yaml +0 -237
- package/templates/api/config/storage_config.yaml +0 -26
- package/templates/api/docs/ADDING_GENERATORS.md +0 -409
- package/templates/api/docs/GENERATORS_API.md +0 -502
- package/templates/api/docs/MIGRATIONS.md +0 -472
- package/templates/api/docs/TESTING_LIVE_APIS.md +0 -417
- package/templates/api/docs/storage_providers.md +0 -337
- package/templates/api/pyproject.toml +0 -205
- package/templates/api/src/boards/__init__.py +0 -10
- package/templates/api/src/boards/api/app.py +0 -172
- package/templates/api/src/boards/api/auth.py +0 -75
- package/templates/api/src/boards/api/endpoints/__init__.py +0 -3
- package/templates/api/src/boards/api/endpoints/jobs.py +0 -76
- package/templates/api/src/boards/api/endpoints/setup.py +0 -505
- package/templates/api/src/boards/api/endpoints/sse.py +0 -129
- package/templates/api/src/boards/api/endpoints/storage.py +0 -155
- package/templates/api/src/boards/api/endpoints/tenant_registration.py +0 -296
- package/templates/api/src/boards/api/endpoints/uploads.py +0 -149
- package/templates/api/src/boards/api/endpoints/webhooks.py +0 -13
- package/templates/api/src/boards/auth/__init__.py +0 -15
- package/templates/api/src/boards/auth/adapters/__init__.py +0 -27
- package/templates/api/src/boards/auth/adapters/auth0.py +0 -220
- package/templates/api/src/boards/auth/adapters/base.py +0 -73
- package/templates/api/src/boards/auth/adapters/clerk.py +0 -172
- package/templates/api/src/boards/auth/adapters/jwt.py +0 -122
- package/templates/api/src/boards/auth/adapters/none.py +0 -102
- package/templates/api/src/boards/auth/adapters/oidc.py +0 -284
- package/templates/api/src/boards/auth/adapters/supabase.py +0 -110
- package/templates/api/src/boards/auth/context.py +0 -35
- package/templates/api/src/boards/auth/factory.py +0 -129
- package/templates/api/src/boards/auth/middleware.py +0 -221
- package/templates/api/src/boards/auth/provisioning.py +0 -129
- package/templates/api/src/boards/auth/tenant_extraction.py +0 -278
- package/templates/api/src/boards/cli.py +0 -354
- package/templates/api/src/boards/config.py +0 -131
- package/templates/api/src/boards/database/__init__.py +0 -7
- package/templates/api/src/boards/database/cli.py +0 -110
- package/templates/api/src/boards/database/connection.py +0 -292
- package/templates/api/src/boards/database/models.py +0 -19
- package/templates/api/src/boards/database/seed_data.py +0 -182
- package/templates/api/src/boards/dbmodels/__init__.py +0 -441
- package/templates/api/src/boards/generators/__init__.py +0 -57
- package/templates/api/src/boards/generators/artifact_resolution.py +0 -405
- package/templates/api/src/boards/generators/artifacts.py +0 -53
- package/templates/api/src/boards/generators/base.py +0 -144
- package/templates/api/src/boards/generators/implementations/__init__.py +0 -14
- package/templates/api/src/boards/generators/implementations/fal/__init__.py +0 -25
- package/templates/api/src/boards/generators/implementations/fal/audio/__init__.py +0 -23
- package/templates/api/src/boards/generators/implementations/fal/audio/beatoven_music_generation.py +0 -171
- package/templates/api/src/boards/generators/implementations/fal/audio/beatoven_sound_effect_generation.py +0 -167
- package/templates/api/src/boards/generators/implementations/fal/audio/chatterbox_text_to_speech.py +0 -176
- package/templates/api/src/boards/generators/implementations/fal/audio/chatterbox_tts_turbo.py +0 -195
- package/templates/api/src/boards/generators/implementations/fal/audio/elevenlabs_sound_effects_v2.py +0 -194
- package/templates/api/src/boards/generators/implementations/fal/audio/elevenlabs_tts_eleven_v3.py +0 -209
- package/templates/api/src/boards/generators/implementations/fal/audio/fal_elevenlabs_tts_turbo_v2_5.py +0 -206
- package/templates/api/src/boards/generators/implementations/fal/audio/fal_minimax_speech_26_hd.py +0 -237
- package/templates/api/src/boards/generators/implementations/fal/audio/minimax_music_v2.py +0 -173
- package/templates/api/src/boards/generators/implementations/fal/audio/minimax_speech_2_6_turbo.py +0 -221
- package/templates/api/src/boards/generators/implementations/fal/image/__init__.py +0 -63
- package/templates/api/src/boards/generators/implementations/fal/image/bytedance_seedream_v45_edit.py +0 -219
- package/templates/api/src/boards/generators/implementations/fal/image/clarity_upscaler.py +0 -220
- package/templates/api/src/boards/generators/implementations/fal/image/crystal_upscaler.py +0 -173
- package/templates/api/src/boards/generators/implementations/fal/image/fal_ideogram_character.py +0 -227
- package/templates/api/src/boards/generators/implementations/fal/image/flux_2.py +0 -203
- package/templates/api/src/boards/generators/implementations/fal/image/flux_2_edit.py +0 -230
- package/templates/api/src/boards/generators/implementations/fal/image/flux_2_pro.py +0 -204
- package/templates/api/src/boards/generators/implementations/fal/image/flux_2_pro_edit.py +0 -221
- package/templates/api/src/boards/generators/implementations/fal/image/flux_pro_kontext.py +0 -216
- package/templates/api/src/boards/generators/implementations/fal/image/flux_pro_ultra.py +0 -197
- package/templates/api/src/boards/generators/implementations/fal/image/gemini_25_flash_image.py +0 -177
- package/templates/api/src/boards/generators/implementations/fal/image/gemini_25_flash_image_edit.py +0 -208
- package/templates/api/src/boards/generators/implementations/fal/image/gpt_image_15_edit.py +0 -216
- package/templates/api/src/boards/generators/implementations/fal/image/gpt_image_1_5.py +0 -177
- package/templates/api/src/boards/generators/implementations/fal/image/gpt_image_1_edit_image.py +0 -182
- package/templates/api/src/boards/generators/implementations/fal/image/gpt_image_1_mini.py +0 -167
- package/templates/api/src/boards/generators/implementations/fal/image/ideogram_character_edit.py +0 -299
- package/templates/api/src/boards/generators/implementations/fal/image/ideogram_v2.py +0 -190
- package/templates/api/src/boards/generators/implementations/fal/image/imagen4_preview.py +0 -191
- package/templates/api/src/boards/generators/implementations/fal/image/imagen4_preview_fast.py +0 -179
- package/templates/api/src/boards/generators/implementations/fal/image/nano_banana.py +0 -183
- package/templates/api/src/boards/generators/implementations/fal/image/nano_banana_edit.py +0 -212
- package/templates/api/src/boards/generators/implementations/fal/image/nano_banana_pro.py +0 -179
- package/templates/api/src/boards/generators/implementations/fal/image/nano_banana_pro_edit.py +0 -226
- package/templates/api/src/boards/generators/implementations/fal/image/qwen_image.py +0 -249
- package/templates/api/src/boards/generators/implementations/fal/image/qwen_image_edit.py +0 -244
- package/templates/api/src/boards/generators/implementations/fal/image/reve_edit.py +0 -178
- package/templates/api/src/boards/generators/implementations/fal/image/reve_text_to_image.py +0 -155
- package/templates/api/src/boards/generators/implementations/fal/image/seedream_v45_text_to_image.py +0 -180
- package/templates/api/src/boards/generators/implementations/fal/utils.py +0 -61
- package/templates/api/src/boards/generators/implementations/fal/video/__init__.py +0 -77
- package/templates/api/src/boards/generators/implementations/fal/video/bytedance_seedance_v1_pro_text_to_video.py +0 -209
- package/templates/api/src/boards/generators/implementations/fal/video/creatify_lipsync.py +0 -161
- package/templates/api/src/boards/generators/implementations/fal/video/fal_bytedance_seedance_v1_pro_image_to_video.py +0 -222
- package/templates/api/src/boards/generators/implementations/fal/video/fal_minimax_hailuo_02_standard_text_to_video.py +0 -152
- package/templates/api/src/boards/generators/implementations/fal/video/fal_pixverse_lipsync.py +0 -197
- package/templates/api/src/boards/generators/implementations/fal/video/fal_sora_2_text_to_video.py +0 -173
- package/templates/api/src/boards/generators/implementations/fal/video/infinitalk.py +0 -221
- package/templates/api/src/boards/generators/implementations/fal/video/kling_video_ai_avatar_v2_pro.py +0 -168
- package/templates/api/src/boards/generators/implementations/fal/video/kling_video_ai_avatar_v2_standard.py +0 -159
- package/templates/api/src/boards/generators/implementations/fal/video/kling_video_v2_5_turbo_pro_image_to_video.py +0 -175
- package/templates/api/src/boards/generators/implementations/fal/video/kling_video_v2_5_turbo_pro_text_to_video.py +0 -168
- package/templates/api/src/boards/generators/implementations/fal/video/minimax_hailuo_2_3_pro_image_to_video.py +0 -153
- package/templates/api/src/boards/generators/implementations/fal/video/sora2_image_to_video.py +0 -172
- package/templates/api/src/boards/generators/implementations/fal/video/sora_2_image_to_video_pro.py +0 -175
- package/templates/api/src/boards/generators/implementations/fal/video/sora_2_text_to_video_pro.py +0 -163
- package/templates/api/src/boards/generators/implementations/fal/video/sync_lipsync_v2.py +0 -167
- package/templates/api/src/boards/generators/implementations/fal/video/sync_lipsync_v2_pro.py +0 -155
- package/templates/api/src/boards/generators/implementations/fal/video/veed_fabric_1_0.py +0 -180
- package/templates/api/src/boards/generators/implementations/fal/video/veed_lipsync.py +0 -174
- package/templates/api/src/boards/generators/implementations/fal/video/veo3.py +0 -194
- package/templates/api/src/boards/generators/implementations/fal/video/veo31.py +0 -190
- package/templates/api/src/boards/generators/implementations/fal/video/veo31_fast.py +0 -190
- package/templates/api/src/boards/generators/implementations/fal/video/veo31_fast_image_to_video.py +0 -191
- package/templates/api/src/boards/generators/implementations/fal/video/veo31_first_last_frame_to_video.py +0 -187
- package/templates/api/src/boards/generators/implementations/fal/video/veo31_image_to_video.py +0 -183
- package/templates/api/src/boards/generators/implementations/fal/video/veo31_reference_to_video.py +0 -172
- package/templates/api/src/boards/generators/implementations/fal/video/wan_25_preview_image_to_video.py +0 -212
- package/templates/api/src/boards/generators/implementations/fal/video/wan_25_preview_text_to_video.py +0 -208
- package/templates/api/src/boards/generators/implementations/fal/video/wan_pro_image_to_video.py +0 -158
- package/templates/api/src/boards/generators/implementations/kie/__init__.py +0 -11
- package/templates/api/src/boards/generators/implementations/kie/base.py +0 -316
- package/templates/api/src/boards/generators/implementations/kie/image/__init__.py +0 -3
- package/templates/api/src/boards/generators/implementations/kie/image/nano_banana_edit.py +0 -190
- package/templates/api/src/boards/generators/implementations/kie/utils.py +0 -98
- package/templates/api/src/boards/generators/implementations/kie/video/__init__.py +0 -8
- package/templates/api/src/boards/generators/implementations/kie/video/veo3.py +0 -161
- package/templates/api/src/boards/generators/implementations/openai/__init__.py +0 -1
- package/templates/api/src/boards/generators/implementations/openai/audio/__init__.py +0 -1
- package/templates/api/src/boards/generators/implementations/openai/audio/whisper.py +0 -69
- package/templates/api/src/boards/generators/implementations/openai/image/__init__.py +0 -1
- package/templates/api/src/boards/generators/implementations/openai/image/dalle3.py +0 -96
- package/templates/api/src/boards/generators/implementations/replicate/__init__.py +0 -1
- package/templates/api/src/boards/generators/implementations/replicate/image/__init__.py +0 -1
- package/templates/api/src/boards/generators/implementations/replicate/image/flux_pro.py +0 -88
- package/templates/api/src/boards/generators/implementations/replicate/video/__init__.py +0 -1
- package/templates/api/src/boards/generators/implementations/replicate/video/lipsync.py +0 -73
- package/templates/api/src/boards/generators/loader.py +0 -253
- package/templates/api/src/boards/generators/registry.py +0 -114
- package/templates/api/src/boards/generators/resolution.py +0 -632
- package/templates/api/src/boards/generators/testmods/class_gen.py +0 -34
- package/templates/api/src/boards/generators/testmods/import_side_effect.py +0 -35
- package/templates/api/src/boards/graphql/__init__.py +0 -7
- package/templates/api/src/boards/graphql/access_control.py +0 -136
- package/templates/api/src/boards/graphql/mutations/root.py +0 -148
- package/templates/api/src/boards/graphql/queries/root.py +0 -116
- package/templates/api/src/boards/graphql/resolvers/__init__.py +0 -8
- package/templates/api/src/boards/graphql/resolvers/auth.py +0 -12
- package/templates/api/src/boards/graphql/resolvers/board.py +0 -1053
- package/templates/api/src/boards/graphql/resolvers/generation.py +0 -666
- package/templates/api/src/boards/graphql/resolvers/generator.py +0 -50
- package/templates/api/src/boards/graphql/resolvers/lineage.py +0 -381
- package/templates/api/src/boards/graphql/resolvers/upload.py +0 -463
- package/templates/api/src/boards/graphql/resolvers/user.py +0 -25
- package/templates/api/src/boards/graphql/schema.py +0 -81
- package/templates/api/src/boards/graphql/types/board.py +0 -102
- package/templates/api/src/boards/graphql/types/generation.py +0 -166
- package/templates/api/src/boards/graphql/types/generator.py +0 -17
- package/templates/api/src/boards/graphql/types/user.py +0 -47
- package/templates/api/src/boards/jobs/repository.py +0 -153
- package/templates/api/src/boards/logging.py +0 -195
- package/templates/api/src/boards/middleware.py +0 -339
- package/templates/api/src/boards/progress/__init__.py +0 -4
- package/templates/api/src/boards/progress/models.py +0 -25
- package/templates/api/src/boards/progress/publisher.py +0 -64
- package/templates/api/src/boards/py.typed +0 -0
- package/templates/api/src/boards/redis_pool.py +0 -118
- package/templates/api/src/boards/storage/__init__.py +0 -52
- package/templates/api/src/boards/storage/base.py +0 -363
- package/templates/api/src/boards/storage/config.py +0 -187
- package/templates/api/src/boards/storage/factory.py +0 -288
- package/templates/api/src/boards/storage/implementations/__init__.py +0 -27
- package/templates/api/src/boards/storage/implementations/gcs.py +0 -340
- package/templates/api/src/boards/storage/implementations/local.py +0 -201
- package/templates/api/src/boards/storage/implementations/s3.py +0 -294
- package/templates/api/src/boards/storage/implementations/supabase.py +0 -218
- package/templates/api/src/boards/tenant_isolation.py +0 -446
- package/templates/api/src/boards/validation.py +0 -262
- package/templates/api/src/boards/workers/__init__.py +0 -1
- package/templates/api/src/boards/workers/actors.py +0 -274
- package/templates/api/src/boards/workers/cli.py +0 -125
- package/templates/api/src/boards/workers/context.py +0 -348
- package/templates/api/src/boards/workers/middleware.py +0 -58
- package/templates/api/src/py.typed +0 -0
- package/templates/compose.web.yaml +0 -35
- package/templates/compose.yaml +0 -116
- package/templates/docker/env.example +0 -23
- package/templates/web/.env.example +0 -28
- package/templates/web/Dockerfile +0 -51
- package/templates/web/components.json +0 -22
- package/templates/web/imageLoader.js +0 -18
- package/templates/web/next-env.d.ts +0 -5
- package/templates/web/next.config.js +0 -36
- package/templates/web/package.json +0 -41
- package/templates/web/postcss.config.mjs +0 -7
- package/templates/web/public/favicon.ico +0 -0
- package/templates/web/src/app/boards/[boardId]/page.tsx +0 -353
- package/templates/web/src/app/globals.css +0 -123
- package/templates/web/src/app/layout.tsx +0 -31
- package/templates/web/src/app/lineage/[generationId]/page.tsx +0 -235
- package/templates/web/src/app/page.tsx +0 -35
- package/templates/web/src/app/providers.tsx +0 -18
- package/templates/web/src/components/boards/ArtifactInputSlots.tsx +0 -206
- package/templates/web/src/components/boards/ArtifactPreview.tsx +0 -466
- package/templates/web/src/components/boards/GenerationGrid.tsx +0 -282
- package/templates/web/src/components/boards/GenerationInput.tsx +0 -370
- package/templates/web/src/components/boards/GeneratorSelector.tsx +0 -272
- package/templates/web/src/components/boards/UploadArtifact.tsx +0 -563
- package/templates/web/src/components/header.tsx +0 -32
- package/templates/web/src/components/theme-provider.tsx +0 -10
- package/templates/web/src/components/theme-toggle.tsx +0 -75
- package/templates/web/src/components/ui/alert-dialog.tsx +0 -157
- package/templates/web/src/components/ui/button.tsx +0 -58
- package/templates/web/src/components/ui/card.tsx +0 -92
- package/templates/web/src/components/ui/dropdown-menu.tsx +0 -200
- package/templates/web/src/components/ui/navigation-menu.tsx +0 -168
- package/templates/web/src/components/ui/toast.tsx +0 -128
- package/templates/web/src/components/ui/toaster.tsx +0 -35
- package/templates/web/src/components/ui/use-toast.ts +0 -187
- package/templates/web/src/hooks/useGeneratorMRU.ts +0 -57
- package/templates/web/src/lib/utils.ts +0 -6
- package/templates/web/tsconfig.json +0 -41
|
@@ -1,354 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Main CLI entry point for Boards backend server.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
import os
|
|
7
|
-
import sys
|
|
8
|
-
|
|
9
|
-
import click
|
|
10
|
-
import uvicorn
|
|
11
|
-
|
|
12
|
-
from boards import __version__
|
|
13
|
-
from boards.logging import configure_logging, get_logger
|
|
14
|
-
|
|
15
|
-
logger = get_logger(__name__)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
@click.group()
|
|
19
|
-
@click.version_option(version=__version__, prog_name="boards")
|
|
20
|
-
def cli() -> None:
|
|
21
|
-
"""Boards CLI - manage server, database, and tenants."""
|
|
22
|
-
pass
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
@cli.command()
|
|
26
|
-
@click.option(
|
|
27
|
-
"--host",
|
|
28
|
-
default="0.0.0.0",
|
|
29
|
-
help="Host to bind to (default: 0.0.0.0)",
|
|
30
|
-
)
|
|
31
|
-
@click.option(
|
|
32
|
-
"--port",
|
|
33
|
-
default=8088,
|
|
34
|
-
type=int,
|
|
35
|
-
help="Port to bind to (default: 8088)",
|
|
36
|
-
)
|
|
37
|
-
@click.option(
|
|
38
|
-
"--reload",
|
|
39
|
-
is_flag=True,
|
|
40
|
-
default=False,
|
|
41
|
-
help="Enable auto-reload for development",
|
|
42
|
-
)
|
|
43
|
-
@click.option(
|
|
44
|
-
"--workers",
|
|
45
|
-
default=1,
|
|
46
|
-
type=int,
|
|
47
|
-
help="Number of worker processes (default: 1)",
|
|
48
|
-
)
|
|
49
|
-
@click.option(
|
|
50
|
-
"--log-level",
|
|
51
|
-
default="info",
|
|
52
|
-
type=click.Choice(["debug", "info", "warning", "error"]),
|
|
53
|
-
help="Log level (default: info)",
|
|
54
|
-
)
|
|
55
|
-
def serve(
|
|
56
|
-
host: str,
|
|
57
|
-
port: int,
|
|
58
|
-
reload: bool,
|
|
59
|
-
workers: int,
|
|
60
|
-
log_level: str,
|
|
61
|
-
) -> None:
|
|
62
|
-
"""Start the Boards API server."""
|
|
63
|
-
|
|
64
|
-
# Configure logging
|
|
65
|
-
configure_logging(debug=(log_level == "debug"))
|
|
66
|
-
|
|
67
|
-
logger.info(
|
|
68
|
-
"Starting Boards API server",
|
|
69
|
-
host=host,
|
|
70
|
-
port=port,
|
|
71
|
-
reload=reload,
|
|
72
|
-
workers=workers,
|
|
73
|
-
log_level=log_level,
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
# Set environment variables for app configuration when using reload/workers
|
|
77
|
-
# This ensures the app imports with the correct settings
|
|
78
|
-
if log_level == "debug":
|
|
79
|
-
os.environ["BOARDS_DEBUG"] = "true"
|
|
80
|
-
os.environ["BOARDS_LOG_LEVEL"] = "debug"
|
|
81
|
-
else:
|
|
82
|
-
os.environ.setdefault("BOARDS_DEBUG", "false")
|
|
83
|
-
os.environ.setdefault("BOARDS_LOG_LEVEL", log_level)
|
|
84
|
-
|
|
85
|
-
try:
|
|
86
|
-
# When using reload or multiple workers, pass app as import string
|
|
87
|
-
if reload or workers > 1:
|
|
88
|
-
uvicorn.run(
|
|
89
|
-
"boards.api.app:app",
|
|
90
|
-
host=host,
|
|
91
|
-
port=port,
|
|
92
|
-
reload=reload,
|
|
93
|
-
workers=(workers if not reload else 1), # reload doesn't work with multiple workers
|
|
94
|
-
log_level=log_level,
|
|
95
|
-
access_log=True,
|
|
96
|
-
)
|
|
97
|
-
else:
|
|
98
|
-
# Import app directly when not using reload/workers
|
|
99
|
-
from boards.api.app import app
|
|
100
|
-
|
|
101
|
-
uvicorn.run(
|
|
102
|
-
app,
|
|
103
|
-
host=host,
|
|
104
|
-
port=port,
|
|
105
|
-
reload=reload,
|
|
106
|
-
workers=workers,
|
|
107
|
-
log_level=log_level,
|
|
108
|
-
access_log=True,
|
|
109
|
-
)
|
|
110
|
-
except KeyboardInterrupt:
|
|
111
|
-
logger.info("Server shutdown requested by user")
|
|
112
|
-
except Exception as e:
|
|
113
|
-
logger.error("Server startup failed", error=str(e))
|
|
114
|
-
sys.exit(1)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
@cli.group()
|
|
118
|
-
def tenant() -> None:
|
|
119
|
-
"""Manage tenants in the database."""
|
|
120
|
-
pass
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
@tenant.command("create")
|
|
124
|
-
@click.option(
|
|
125
|
-
"--name",
|
|
126
|
-
required=True,
|
|
127
|
-
help="Display name for the tenant",
|
|
128
|
-
)
|
|
129
|
-
@click.option(
|
|
130
|
-
"--slug",
|
|
131
|
-
required=True,
|
|
132
|
-
help="Unique slug for the tenant (used in URLs)",
|
|
133
|
-
)
|
|
134
|
-
@click.option(
|
|
135
|
-
"--sample-data",
|
|
136
|
-
is_flag=True,
|
|
137
|
-
default=False,
|
|
138
|
-
help="Include sample data for the tenant",
|
|
139
|
-
)
|
|
140
|
-
def create_tenant(
|
|
141
|
-
name: str,
|
|
142
|
-
slug: str,
|
|
143
|
-
sample_data: bool,
|
|
144
|
-
) -> None:
|
|
145
|
-
"""Create a new tenant in the database."""
|
|
146
|
-
import asyncio
|
|
147
|
-
|
|
148
|
-
from boards.database.connection import get_async_session
|
|
149
|
-
from boards.database.seed_data import seed_tenant_with_data
|
|
150
|
-
|
|
151
|
-
configure_logging()
|
|
152
|
-
|
|
153
|
-
async def do_create():
|
|
154
|
-
async with get_async_session() as db:
|
|
155
|
-
try:
|
|
156
|
-
tenant_id = await seed_tenant_with_data(
|
|
157
|
-
db,
|
|
158
|
-
tenant_name=name,
|
|
159
|
-
tenant_slug=slug,
|
|
160
|
-
include_sample_data=sample_data,
|
|
161
|
-
)
|
|
162
|
-
logger.info(
|
|
163
|
-
"Tenant created successfully",
|
|
164
|
-
tenant_id=str(tenant_id),
|
|
165
|
-
name=name,
|
|
166
|
-
slug=slug,
|
|
167
|
-
)
|
|
168
|
-
click.echo(f"✓ Tenant created: {tenant_id}")
|
|
169
|
-
click.echo(f" Name: {name}")
|
|
170
|
-
click.echo(f" Slug: {slug}")
|
|
171
|
-
if sample_data:
|
|
172
|
-
click.echo(" Sample data: included")
|
|
173
|
-
except Exception as e:
|
|
174
|
-
logger.error("Failed to create tenant", error=str(e))
|
|
175
|
-
click.echo(f"✗ Error creating tenant: {e}", err=True)
|
|
176
|
-
sys.exit(1)
|
|
177
|
-
|
|
178
|
-
asyncio.run(do_create())
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
@tenant.command("list")
|
|
182
|
-
def list_tenants() -> None:
|
|
183
|
-
"""List all tenants in the database."""
|
|
184
|
-
import asyncio
|
|
185
|
-
|
|
186
|
-
from sqlalchemy import select
|
|
187
|
-
|
|
188
|
-
from boards.database.connection import get_async_session
|
|
189
|
-
from boards.dbmodels import Tenants
|
|
190
|
-
|
|
191
|
-
configure_logging()
|
|
192
|
-
|
|
193
|
-
async def do_list():
|
|
194
|
-
async with get_async_session() as db:
|
|
195
|
-
try:
|
|
196
|
-
stmt = select(Tenants).order_by(Tenants.created_at)
|
|
197
|
-
result = await db.execute(stmt)
|
|
198
|
-
tenants = result.scalars().all()
|
|
199
|
-
|
|
200
|
-
if not tenants:
|
|
201
|
-
click.echo("No tenants found.")
|
|
202
|
-
else:
|
|
203
|
-
click.echo(f"Found {len(tenants)} tenant(s):")
|
|
204
|
-
click.echo()
|
|
205
|
-
for t in tenants:
|
|
206
|
-
click.echo(f" ID: {t.id}")
|
|
207
|
-
click.echo(f" Name: {t.name}")
|
|
208
|
-
click.echo(f" Slug: {t.slug}")
|
|
209
|
-
click.echo(f" Created: {t.created_at}")
|
|
210
|
-
click.echo()
|
|
211
|
-
except Exception as e:
|
|
212
|
-
logger.error("Failed to list tenants", error=str(e))
|
|
213
|
-
click.echo(f"✗ Error listing tenants: {e}", err=True)
|
|
214
|
-
sys.exit(1)
|
|
215
|
-
|
|
216
|
-
asyncio.run(do_list())
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
@tenant.command("audit")
|
|
220
|
-
@click.option(
|
|
221
|
-
"--tenant-slug",
|
|
222
|
-
help="Slug of specific tenant to audit (audits all if not specified)",
|
|
223
|
-
)
|
|
224
|
-
@click.option(
|
|
225
|
-
"--output-format",
|
|
226
|
-
default="table",
|
|
227
|
-
type=click.Choice(["table", "json"]),
|
|
228
|
-
help="Output format (default: table)",
|
|
229
|
-
)
|
|
230
|
-
def audit_tenant_isolation(tenant_slug: str | None, output_format: str) -> None:
|
|
231
|
-
"""Audit tenant isolation for security validation."""
|
|
232
|
-
import asyncio
|
|
233
|
-
import json
|
|
234
|
-
|
|
235
|
-
from sqlalchemy import select
|
|
236
|
-
|
|
237
|
-
from boards.database.connection import get_async_session
|
|
238
|
-
from boards.dbmodels import Tenants
|
|
239
|
-
from boards.tenant_isolation import TenantIsolationValidator
|
|
240
|
-
|
|
241
|
-
configure_logging()
|
|
242
|
-
|
|
243
|
-
async def do_audit():
|
|
244
|
-
async with get_async_session() as db:
|
|
245
|
-
try:
|
|
246
|
-
validator = TenantIsolationValidator(db)
|
|
247
|
-
|
|
248
|
-
# Determine which tenants to audit
|
|
249
|
-
if tenant_slug:
|
|
250
|
-
stmt = select(Tenants).where(Tenants.slug == tenant_slug)
|
|
251
|
-
result = await db.execute(stmt)
|
|
252
|
-
tenant = result.scalar_one_or_none()
|
|
253
|
-
if not tenant:
|
|
254
|
-
click.echo(f"✗ Tenant '{tenant_slug}' not found", err=True)
|
|
255
|
-
sys.exit(1)
|
|
256
|
-
tenants_to_audit = [tenant]
|
|
257
|
-
else:
|
|
258
|
-
stmt = select(Tenants).order_by(Tenants.created_at)
|
|
259
|
-
result = await db.execute(stmt)
|
|
260
|
-
tenants_to_audit = result.scalars().all()
|
|
261
|
-
|
|
262
|
-
if not tenants_to_audit:
|
|
263
|
-
click.echo("No tenants found to audit.")
|
|
264
|
-
return
|
|
265
|
-
|
|
266
|
-
# Perform audits
|
|
267
|
-
audit_results = []
|
|
268
|
-
for tenant in tenants_to_audit:
|
|
269
|
-
click.echo(f"🔍 Auditing tenant: {tenant.slug}")
|
|
270
|
-
audit_result = await validator.audit_tenant_isolation(tenant.id)
|
|
271
|
-
audit_results.append(audit_result)
|
|
272
|
-
|
|
273
|
-
# Output results
|
|
274
|
-
if output_format == "json":
|
|
275
|
-
click.echo(json.dumps(audit_results, indent=2))
|
|
276
|
-
else:
|
|
277
|
-
_display_audit_results_table(audit_results)
|
|
278
|
-
|
|
279
|
-
except Exception as e:
|
|
280
|
-
logger.error("Failed to audit tenant isolation", error=str(e))
|
|
281
|
-
click.echo(f"✗ Error auditing tenants: {e}", err=True)
|
|
282
|
-
sys.exit(1)
|
|
283
|
-
|
|
284
|
-
def _display_audit_results_table(results):
|
|
285
|
-
"""Display audit results in table format."""
|
|
286
|
-
click.echo("\n" + "=" * 80)
|
|
287
|
-
click.echo("TENANT ISOLATION AUDIT RESULTS")
|
|
288
|
-
click.echo("=" * 80)
|
|
289
|
-
|
|
290
|
-
for result in results:
|
|
291
|
-
violations = result["isolation_violations"]
|
|
292
|
-
stats = result["statistics"]
|
|
293
|
-
|
|
294
|
-
click.echo(f"\n📋 Tenant: {result['tenant_id']}")
|
|
295
|
-
click.echo(f" Audit Time: {result['audit_timestamp']}")
|
|
296
|
-
|
|
297
|
-
# Statistics
|
|
298
|
-
click.echo("\n📊 Statistics:")
|
|
299
|
-
click.echo(f" Users: {stats.get('users_count', 0)}")
|
|
300
|
-
click.echo(f" Boards: {stats.get('boards_count', 0)}")
|
|
301
|
-
click.echo(f" Generations: {stats.get('generations_count', 0)}")
|
|
302
|
-
click.echo(f" Board Memberships: {stats.get('board_memberships_count', 0)}")
|
|
303
|
-
|
|
304
|
-
# Violations
|
|
305
|
-
if violations:
|
|
306
|
-
click.echo(f"\n⚠️ Isolation Violations ({len(violations)}):")
|
|
307
|
-
for i, violation in enumerate(violations, 1):
|
|
308
|
-
click.echo(f" {i}. {violation['type']}: {violation['description']}")
|
|
309
|
-
else:
|
|
310
|
-
click.echo("\n✅ No isolation violations found")
|
|
311
|
-
|
|
312
|
-
# Recommendations
|
|
313
|
-
click.echo("\n💡 Recommendations:")
|
|
314
|
-
for rec in result["recommendations"]:
|
|
315
|
-
click.echo(f" • {rec}")
|
|
316
|
-
|
|
317
|
-
if result != results[-1]: # Not the last result
|
|
318
|
-
click.echo("\n" + "-" * 60)
|
|
319
|
-
|
|
320
|
-
click.echo("\n" + "=" * 80)
|
|
321
|
-
|
|
322
|
-
asyncio.run(do_audit())
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
@cli.command()
|
|
326
|
-
def seed() -> None:
|
|
327
|
-
"""Seed the database with initial data."""
|
|
328
|
-
import asyncio
|
|
329
|
-
|
|
330
|
-
from boards.database.connection import get_async_session
|
|
331
|
-
from boards.database.seed_data import seed_initial_data
|
|
332
|
-
|
|
333
|
-
configure_logging()
|
|
334
|
-
|
|
335
|
-
async def do_seed():
|
|
336
|
-
async with get_async_session() as db:
|
|
337
|
-
try:
|
|
338
|
-
await seed_initial_data(db)
|
|
339
|
-
click.echo("✓ Database seeded successfully")
|
|
340
|
-
except Exception as e:
|
|
341
|
-
logger.error("Failed to seed database", error=str(e))
|
|
342
|
-
click.echo(f"✗ Error seeding database: {e}", err=True)
|
|
343
|
-
sys.exit(1)
|
|
344
|
-
|
|
345
|
-
asyncio.run(do_seed())
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
def main():
|
|
349
|
-
"""Entry point for the CLI."""
|
|
350
|
-
cli()
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
if __name__ == "__main__":
|
|
354
|
-
cli()
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Configuration management for Boards backend
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import os
|
|
6
|
-
|
|
7
|
-
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class Settings(BaseSettings):
|
|
11
|
-
"""Application settings loaded from environment variables."""
|
|
12
|
-
|
|
13
|
-
# Database
|
|
14
|
-
database_url: str = "postgresql://boards:boards_dev@localhost:5433/boards_dev"
|
|
15
|
-
database_pool_size: int = 10
|
|
16
|
-
database_max_overflow: int = 20
|
|
17
|
-
|
|
18
|
-
# Redis (for job queue)
|
|
19
|
-
redis_url: str = "redis://localhost:6380"
|
|
20
|
-
|
|
21
|
-
# Storage
|
|
22
|
-
storage_config_path: str | None = None
|
|
23
|
-
|
|
24
|
-
# Auth
|
|
25
|
-
auth_provider: str = "none" # 'none', 'supabase', 'clerk', 'auth0', 'jwt'
|
|
26
|
-
auth_config: dict = {}
|
|
27
|
-
jwt_secret: str | None = None
|
|
28
|
-
jwt_algorithm: str = "HS256"
|
|
29
|
-
jwt_tenant_claim: str | None = None # Custom JWT claim for tenant extraction
|
|
30
|
-
|
|
31
|
-
# API Settings
|
|
32
|
-
api_host: str = "0.0.0.0"
|
|
33
|
-
api_port: int = 8088
|
|
34
|
-
api_reload: bool = False
|
|
35
|
-
cors_origins: list[str] = ["http://localhost:3033"]
|
|
36
|
-
|
|
37
|
-
# Generators Configuration
|
|
38
|
-
generators_config_path: str | None = None
|
|
39
|
-
generator_api_keys: dict[str, str] = {}
|
|
40
|
-
|
|
41
|
-
# Environment
|
|
42
|
-
environment: str = "development" # 'development', 'staging', 'production'
|
|
43
|
-
debug: bool = True
|
|
44
|
-
sql_echo: bool = False
|
|
45
|
-
log_level: str = "INFO"
|
|
46
|
-
|
|
47
|
-
# Tenant Settings (for multi-tenant mode)
|
|
48
|
-
multi_tenant_mode: bool = False
|
|
49
|
-
default_tenant_slug: str = "default"
|
|
50
|
-
|
|
51
|
-
# Tenant Registration Settings
|
|
52
|
-
tenant_registration_requires_approval: bool = False
|
|
53
|
-
tenant_registration_allowed_domains: list[str] | None = None
|
|
54
|
-
max_tenants_per_user: int | None = None
|
|
55
|
-
|
|
56
|
-
# Frontend Integration
|
|
57
|
-
frontend_base_url: str | None = None
|
|
58
|
-
|
|
59
|
-
# Internal API URL (for Docker environments where worker needs to reach API)
|
|
60
|
-
# This allows workers to reach the API using Docker internal networking
|
|
61
|
-
internal_api_url: str | None = None
|
|
62
|
-
|
|
63
|
-
# Job Queue Settings
|
|
64
|
-
job_queue_name: str = "boards-jobs"
|
|
65
|
-
job_timeout: int = 3600 # 1 hour default timeout
|
|
66
|
-
|
|
67
|
-
# File Upload Settings
|
|
68
|
-
max_upload_size: int = 100 * 1024 * 1024 # 100MB
|
|
69
|
-
allowed_upload_extensions: list[str] = [
|
|
70
|
-
".jpg",
|
|
71
|
-
".jpeg",
|
|
72
|
-
".png",
|
|
73
|
-
".gif",
|
|
74
|
-
".webp", # Images
|
|
75
|
-
".mp4",
|
|
76
|
-
".mov",
|
|
77
|
-
".avi",
|
|
78
|
-
".webm", # Videos
|
|
79
|
-
".mp3",
|
|
80
|
-
".wav",
|
|
81
|
-
".ogg",
|
|
82
|
-
".m4a", # Audio
|
|
83
|
-
".txt",
|
|
84
|
-
".md",
|
|
85
|
-
".json", # Text
|
|
86
|
-
]
|
|
87
|
-
|
|
88
|
-
model_config = SettingsConfigDict(
|
|
89
|
-
env_file=".env",
|
|
90
|
-
env_prefix="BOARDS_",
|
|
91
|
-
case_sensitive=False,
|
|
92
|
-
# Allow extra fields for provider-specific configs
|
|
93
|
-
extra="allow",
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
# Global settings instance
|
|
98
|
-
settings = Settings()
|
|
99
|
-
|
|
100
|
-
# Debug: Log settings initialization (only in debug mode)
|
|
101
|
-
if settings.debug:
|
|
102
|
-
from .logging import get_logger
|
|
103
|
-
|
|
104
|
-
_logger = get_logger(__name__)
|
|
105
|
-
_logger.debug(
|
|
106
|
-
"Settings initialized",
|
|
107
|
-
internal_api_url=settings.internal_api_url,
|
|
108
|
-
environment=settings.environment,
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
def initialize_generator_api_keys() -> None:
|
|
113
|
-
"""
|
|
114
|
-
Sync generator API keys from settings to os.environ.
|
|
115
|
-
|
|
116
|
-
This allows third-party packages (like replicate) that expect
|
|
117
|
-
environment variables to work correctly with our Pydantic settings.
|
|
118
|
-
"""
|
|
119
|
-
for key, value in settings.generator_api_keys.items():
|
|
120
|
-
if value: # Only set non-empty values
|
|
121
|
-
os.environ[key] = value
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
# Helper functions
|
|
125
|
-
def get_database_url(tenant_slug: str | None = None) -> str:
|
|
126
|
-
"""Get database URL, optionally with tenant-specific schema."""
|
|
127
|
-
if settings.multi_tenant_mode and tenant_slug:
|
|
128
|
-
# In multi-tenant mode, could use schemas or separate databases
|
|
129
|
-
# For now, we'll use the same database with tenant isolation via queries
|
|
130
|
-
return settings.database_url
|
|
131
|
-
return settings.database_url
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
CLI entry point for Boards database migrations.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
import sys
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
|
|
9
|
-
import click
|
|
10
|
-
|
|
11
|
-
from alembic import command
|
|
12
|
-
from alembic.config import Config
|
|
13
|
-
from boards import __version__
|
|
14
|
-
from boards.logging import configure_logging, get_logger
|
|
15
|
-
|
|
16
|
-
logger = get_logger(__name__)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def get_alembic_config() -> Config:
|
|
20
|
-
"""Get Alembic configuration."""
|
|
21
|
-
# Find alembic.ini in the package directory
|
|
22
|
-
package_dir = Path(__file__).parent.parent.parent.parent
|
|
23
|
-
alembic_ini = package_dir / "alembic.ini"
|
|
24
|
-
|
|
25
|
-
if not alembic_ini.exists():
|
|
26
|
-
raise FileNotFoundError(f"alembic.ini not found at {alembic_ini}")
|
|
27
|
-
|
|
28
|
-
return Config(str(alembic_ini))
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
@click.group()
|
|
32
|
-
@click.option(
|
|
33
|
-
"--log-level",
|
|
34
|
-
default="info",
|
|
35
|
-
type=click.Choice(["debug", "info", "warning", "error"]),
|
|
36
|
-
help="Log level (default: info)",
|
|
37
|
-
)
|
|
38
|
-
@click.version_option(version=__version__, prog_name="boards-migrate")
|
|
39
|
-
def main(log_level: str) -> None:
|
|
40
|
-
"""Boards database migration management."""
|
|
41
|
-
configure_logging(debug=(log_level == "debug"))
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
@main.command()
|
|
45
|
-
@click.argument("revision", default="head")
|
|
46
|
-
def upgrade(revision: str) -> None:
|
|
47
|
-
"""Upgrade database to a revision (default: head)."""
|
|
48
|
-
try:
|
|
49
|
-
config = get_alembic_config()
|
|
50
|
-
logger.info("Upgrading database", revision=revision)
|
|
51
|
-
command.upgrade(config, revision)
|
|
52
|
-
logger.info("Database upgrade completed successfully")
|
|
53
|
-
except Exception as e:
|
|
54
|
-
logger.error("Database upgrade failed", error=str(e))
|
|
55
|
-
sys.exit(1)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
@main.command()
|
|
59
|
-
@click.argument("revision", default="-1")
|
|
60
|
-
def downgrade(revision: str) -> None:
|
|
61
|
-
"""Downgrade database to a revision (default: -1)."""
|
|
62
|
-
try:
|
|
63
|
-
config = get_alembic_config()
|
|
64
|
-
logger.info("Downgrading database", revision=revision)
|
|
65
|
-
command.downgrade(config, revision)
|
|
66
|
-
logger.info("Database downgrade completed successfully")
|
|
67
|
-
except Exception as e:
|
|
68
|
-
logger.error("Database downgrade failed", error=str(e))
|
|
69
|
-
sys.exit(1)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
@main.command()
|
|
73
|
-
@click.option("-m", "--message", required=True, help="Revision message")
|
|
74
|
-
@click.option("--autogenerate/--no-autogenerate", default=True, help="Auto-generate migration")
|
|
75
|
-
def revision(message: str, autogenerate: bool) -> None:
|
|
76
|
-
"""Create a new migration revision."""
|
|
77
|
-
try:
|
|
78
|
-
config = get_alembic_config()
|
|
79
|
-
logger.info("Creating new migration", message=message, autogenerate=autogenerate)
|
|
80
|
-
command.revision(config, message=message, autogenerate=autogenerate)
|
|
81
|
-
logger.info("Migration created successfully")
|
|
82
|
-
except Exception as e:
|
|
83
|
-
logger.error("Migration creation failed", error=str(e))
|
|
84
|
-
sys.exit(1)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
@main.command()
|
|
88
|
-
def current() -> None:
|
|
89
|
-
"""Show current database revision."""
|
|
90
|
-
try:
|
|
91
|
-
config = get_alembic_config()
|
|
92
|
-
command.current(config)
|
|
93
|
-
except Exception as e:
|
|
94
|
-
logger.error("Failed to get current revision", error=str(e))
|
|
95
|
-
sys.exit(1)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
@main.command()
|
|
99
|
-
def history() -> None:
|
|
100
|
-
"""Show migration history."""
|
|
101
|
-
try:
|
|
102
|
-
config = get_alembic_config()
|
|
103
|
-
command.history(config)
|
|
104
|
-
except Exception as e:
|
|
105
|
-
logger.error("Failed to get migration history", error=str(e))
|
|
106
|
-
sys.exit(1)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if __name__ == "__main__":
|
|
110
|
-
main()
|