@lastbrain/module-ai 2.0.26 → 2.0.30
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/README.md +52 -1
- package/dist/ai.build.config.d.ts.map +1 -1
- package/dist/ai.build.config.js +508 -9
- package/dist/api/admin/ai-provider-models/[id].d.ts +18 -0
- package/dist/api/admin/ai-provider-models/[id].d.ts.map +1 -0
- package/dist/api/admin/ai-provider-models/[id].js +58 -0
- package/dist/api/admin/ai-provider-models.d.ts +20 -0
- package/dist/api/admin/ai-provider-models.d.ts.map +1 -0
- package/dist/api/admin/ai-provider-models.js +26 -0
- package/dist/api/admin/ai-providers/[key].d.ts +18 -0
- package/dist/api/admin/ai-providers/[key].d.ts.map +1 -0
- package/dist/api/admin/ai-providers/[key].js +55 -0
- package/dist/api/admin/ai-providers.d.ts +20 -0
- package/dist/api/admin/ai-providers.d.ts.map +1 -0
- package/dist/api/admin/ai-providers.js +26 -0
- package/dist/api/admin/billing-analytics.d.ts +43 -0
- package/dist/api/admin/billing-analytics.d.ts.map +1 -0
- package/dist/api/admin/billing-analytics.js +144 -0
- package/dist/api/admin/global-ai-settings.d.ts +14 -0
- package/dist/api/admin/global-ai-settings.d.ts.map +1 -0
- package/dist/api/admin/global-ai-settings.js +63 -0
- package/dist/api/admin/token-packs/[id].d.ts.map +1 -1
- package/dist/api/admin/token-packs/[id].js +3 -2
- package/dist/api/admin/token-packs.d.ts.map +1 -1
- package/dist/api/admin/token-packs.js +3 -2
- package/dist/api/admin/user-monthly-details.d.ts +49 -0
- package/dist/api/admin/user-monthly-details.d.ts.map +1 -0
- package/dist/api/admin/user-monthly-details.js +140 -0
- package/dist/api/admin/user-quota.d.ts +21 -0
- package/dist/api/admin/user-quota.d.ts.map +1 -0
- package/dist/api/admin/user-quota.js +59 -0
- package/dist/api/admin/user-token/[id].d.ts.map +1 -1
- package/dist/api/admin/user-token/[id].js +2 -1
- package/dist/api/admin/user-token.d.ts +5 -2
- package/dist/api/admin/user-token.d.ts.map +1 -1
- package/dist/api/admin/user-token.js +91 -17
- package/dist/api/admin/user-usage-by-model.d.ts +22 -0
- package/dist/api/admin/user-usage-by-model.d.ts.map +1 -0
- package/dist/api/admin/user-usage-by-model.js +78 -0
- package/dist/api/admin/user-wallet-analytics.d.ts +15 -0
- package/dist/api/admin/user-wallet-analytics.d.ts.map +1 -0
- package/dist/api/admin/user-wallet-analytics.js +67 -0
- package/dist/api/admin/wallet-repair/route.d.ts +30 -0
- package/dist/api/admin/wallet-repair/route.d.ts.map +1 -0
- package/dist/api/admin/wallet-repair/route.js +63 -0
- package/dist/api/auth/ai-model-settings.d.ts +21 -0
- package/dist/api/auth/ai-model-settings.d.ts.map +1 -0
- package/dist/api/auth/ai-model-settings.js +86 -0
- package/dist/api/auth/ai-settings.d.ts +17 -0
- package/dist/api/auth/ai-settings.d.ts.map +1 -0
- package/dist/api/auth/ai-settings.js +87 -0
- package/dist/api/auth/api-keys/[id].d.ts +17 -0
- package/dist/api/auth/api-keys/[id].d.ts.map +1 -0
- package/dist/api/auth/api-keys/[id].js +66 -0
- package/dist/api/auth/api-keys.d.ts +19 -0
- package/dist/api/auth/api-keys.d.ts.map +1 -0
- package/dist/api/auth/api-keys.js +94 -0
- package/dist/api/auth/create-checkout.d.ts +1 -1
- package/dist/api/auth/create-checkout.d.ts.map +1 -1
- package/dist/api/auth/create-checkout.js +8 -6
- package/dist/api/auth/generate-image.d.ts +2 -2
- package/dist/api/auth/generate-image.d.ts.map +1 -1
- package/dist/api/auth/generate-image.js +404 -104
- package/dist/api/auth/generate-text.d.ts +3 -2
- package/dist/api/auth/generate-text.d.ts.map +1 -1
- package/dist/api/auth/generate-text.js +130 -58
- package/dist/api/auth/process-ocr.d.ts +9 -0
- package/dist/api/auth/process-ocr.d.ts.map +1 -0
- package/dist/api/auth/process-ocr.js +43 -0
- package/dist/api/auth/prompts/stats.d.ts +14 -0
- package/dist/api/auth/prompts/stats.d.ts.map +1 -0
- package/dist/api/auth/prompts/stats.js +46 -0
- package/dist/api/auth/prompts.d.ts +26 -0
- package/dist/api/auth/prompts.d.ts.map +1 -0
- package/dist/api/auth/prompts.js +175 -0
- package/dist/api/auth/token-balance.d.ts +26 -0
- package/dist/api/auth/token-balance.d.ts.map +1 -0
- package/dist/api/auth/token-balance.js +47 -0
- package/dist/api/auth/token-checkout.d.ts.map +1 -1
- package/dist/api/auth/token-checkout.js +22 -4
- package/dist/api/auth/token-packs.d.ts.map +1 -1
- package/dist/api/auth/token-packs.js +2 -1
- package/dist/api/auth/usage-by-model.d.ts +25 -0
- package/dist/api/auth/usage-by-model.d.ts.map +1 -0
- package/dist/api/auth/usage-by-model.js +95 -0
- package/dist/api/auth/usage.d.ts +26 -0
- package/dist/api/auth/usage.d.ts.map +1 -0
- package/dist/api/auth/usage.js +127 -0
- package/dist/api/auth/user-tokens.d.ts.map +1 -1
- package/dist/api/auth/user-tokens.js +36 -2
- package/dist/api/auth/wallet/route.d.ts +17 -0
- package/dist/api/auth/wallet/route.d.ts.map +1 -0
- package/dist/api/auth/wallet/route.js +68 -0
- package/dist/api/auth/wallet.d.ts +16 -0
- package/dist/api/auth/wallet.d.ts.map +1 -0
- package/dist/api/auth/wallet.js +71 -0
- package/dist/api/public/gateway-models.d.ts +25 -0
- package/dist/api/public/gateway-models.d.ts.map +1 -0
- package/dist/api/public/gateway-models.js +49 -0
- package/dist/api/public/pricing-summary.d.ts +46 -0
- package/dist/api/public/pricing-summary.d.ts.map +1 -0
- package/dist/api/public/pricing-summary.js +70 -0
- package/dist/api/public/prompts/[id]/stats.d.ts +15 -0
- package/dist/api/public/prompts/[id]/stats.d.ts.map +1 -0
- package/dist/api/public/prompts/[id]/stats.js +37 -0
- package/dist/api/public/prompts/[id]/view.d.ts +15 -0
- package/dist/api/public/prompts/[id]/view.d.ts.map +1 -0
- package/dist/api/public/prompts/[id]/view.js +41 -0
- package/dist/api/public/prompts/slug/[slug].d.ts +15 -0
- package/dist/api/public/prompts/slug/[slug].d.ts.map +1 -0
- package/dist/api/public/prompts/slug/[slug].js +40 -0
- package/dist/api/public/prompts/user/[username].d.ts +17 -0
- package/dist/api/public/prompts/user/[username].d.ts.map +1 -0
- package/dist/api/public/prompts/user/[username].js +51 -0
- package/dist/api/public/prompts.d.ts +15 -0
- package/dist/api/public/prompts.d.ts.map +1 -0
- package/dist/api/public/prompts.js +45 -0
- package/dist/api/public/token-packs.d.ts +11 -0
- package/dist/api/public/token-packs.d.ts.map +1 -0
- package/dist/api/public/token-packs.js +25 -0
- package/dist/api/public/token-pricing.d.ts +44 -0
- package/dist/api/public/token-pricing.d.ts.map +1 -0
- package/dist/api/public/token-pricing.js +168 -0
- package/dist/api/public/v1/_lib/artifacts.d.ts +52 -0
- package/dist/api/public/v1/_lib/artifacts.d.ts.map +1 -0
- package/dist/api/public/v1/_lib/artifacts.js +217 -0
- package/dist/api/public/v1/_lib/auth.d.ts +43 -0
- package/dist/api/public/v1/_lib/auth.d.ts.map +1 -0
- package/dist/api/public/v1/_lib/auth.js +108 -0
- package/dist/api/public/v1/_lib/errors.d.ts +17 -0
- package/dist/api/public/v1/_lib/errors.d.ts.map +1 -0
- package/dist/api/public/v1/_lib/errors.js +16 -0
- package/dist/api/public/v1/_lib/log.d.ts +29 -0
- package/dist/api/public/v1/_lib/log.d.ts.map +1 -0
- package/dist/api/public/v1/_lib/log.js +68 -0
- package/dist/api/public/v1/_lib/quota.d.ts +24 -0
- package/dist/api/public/v1/_lib/quota.d.ts.map +1 -0
- package/dist/api/public/v1/_lib/quota.js +118 -0
- package/dist/api/public/v1/_lib/router.d.ts +54 -0
- package/dist/api/public/v1/_lib/router.d.ts.map +1 -0
- package/dist/api/public/v1/_lib/router.js +119 -0
- package/dist/api/public/v1/connect.d.ts +20 -0
- package/dist/api/public/v1/connect.d.ts.map +1 -0
- package/dist/api/public/v1/connect.js +119 -0
- package/dist/api/public/v1/doc.d.ts +239 -0
- package/dist/api/public/v1/doc.d.ts.map +1 -0
- package/dist/api/public/v1/doc.js +253 -0
- package/dist/api/public/v1/history/[id].d.ts +92 -0
- package/dist/api/public/v1/history/[id].d.ts.map +1 -0
- package/dist/api/public/v1/history/[id].js +176 -0
- package/dist/api/public/v1/history.d.ts +30 -0
- package/dist/api/public/v1/history.d.ts.map +1 -0
- package/dist/api/public/v1/history.js +142 -0
- package/dist/api/public/v1/image-ai.d.ts +24 -0
- package/dist/api/public/v1/image-ai.d.ts.map +1 -0
- package/dist/api/public/v1/image-ai.js +233 -0
- package/dist/api/public/v1/prompts.d.ts +19 -0
- package/dist/api/public/v1/prompts.d.ts.map +1 -0
- package/dist/api/public/v1/prompts.js +107 -0
- package/dist/api/public/v1/provider.d.ts +16 -0
- package/dist/api/public/v1/provider.d.ts.map +1 -0
- package/dist/api/public/v1/provider.js +130 -0
- package/dist/api/public/v1/purchase.d.ts +11 -0
- package/dist/api/public/v1/purchase.d.ts.map +1 -0
- package/dist/api/public/v1/purchase.js +18 -0
- package/dist/api/public/v1/status.d.ts +35 -0
- package/dist/api/public/v1/status.d.ts.map +1 -0
- package/dist/api/public/v1/status.js +163 -0
- package/dist/api/public/v1/text-ai.d.ts +26 -0
- package/dist/api/public/v1/text-ai.d.ts.map +1 -0
- package/dist/api/public/v1/text-ai.js +239 -0
- package/dist/api/public/webhook.d.ts.map +1 -1
- package/dist/api/public/webhook.js +50 -39
- package/dist/api/track-usage.d.ts +12 -0
- package/dist/api/track-usage.d.ts.map +1 -0
- package/dist/api/track-usage.js +37 -0
- package/dist/components/Doc.d.ts.map +1 -1
- package/dist/components/Doc.js +1 -1
- package/dist/components/DocUsageCustom.js +6 -6
- package/dist/components/admin/UserTokenTab.d.ts.map +1 -1
- package/dist/components/admin/UserTokenTab.js +170 -23
- package/dist/components/auth/AuthDashboardAi.d.ts +2 -0
- package/dist/components/auth/AuthDashboardAi.d.ts.map +1 -0
- package/dist/components/auth/AuthDashboardAi.js +53 -0
- package/dist/docs/REFACTORING_BILLING_GUIDE.d.ts +277 -0
- package/dist/docs/REFACTORING_BILLING_GUIDE.d.ts.map +1 -0
- package/dist/docs/REFACTORING_BILLING_GUIDE.js +276 -0
- package/dist/index.d.ts +15 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -1
- package/dist/scripts/migrate-tokens-to-wallet.d.ts +13 -0
- package/dist/scripts/migrate-tokens-to-wallet.d.ts.map +1 -0
- package/dist/scripts/migrate-tokens-to-wallet.js +165 -0
- package/dist/server/__tests__/billing.test.d.ts +5 -0
- package/dist/server/__tests__/billing.test.d.ts.map +1 -0
- package/dist/server/__tests__/billing.test.js +223 -0
- package/dist/server/ai-client.d.ts +59 -0
- package/dist/server/ai-client.d.ts.map +1 -0
- package/dist/server/ai-client.js +111 -0
- package/dist/server/ai-generation-service.d.ts +66 -0
- package/dist/server/ai-generation-service.d.ts.map +1 -0
- package/dist/server/ai-generation-service.js +274 -0
- package/dist/server/billing.d.ts +200 -0
- package/dist/server/billing.d.ts.map +1 -0
- package/dist/server/billing.js +488 -0
- package/dist/server/gateway-service.d.ts +13 -0
- package/dist/server/gateway-service.d.ts.map +1 -0
- package/dist/server/gateway-service.js +161 -0
- package/dist/server/global-settings.d.ts +16 -0
- package/dist/server/global-settings.d.ts.map +1 -0
- package/dist/server/global-settings.js +42 -0
- package/dist/server/model-filter.d.ts +25 -0
- package/dist/server/model-filter.d.ts.map +1 -0
- package/dist/server/model-filter.js +240 -0
- package/dist/server/ocr.d.ts +39 -0
- package/dist/server/ocr.d.ts.map +1 -0
- package/dist/server/ocr.js +280 -0
- package/dist/server/openai-client.d.ts +19 -0
- package/dist/server/openai-client.d.ts.map +1 -0
- package/dist/server/openai-client.js +26 -0
- package/dist/server/pricing-config.d.ts +18 -0
- package/dist/server/pricing-config.d.ts.map +1 -0
- package/dist/server/pricing-config.js +94 -0
- package/dist/server/pricing-validator.d.ts +41 -0
- package/dist/server/pricing-validator.d.ts.map +1 -0
- package/dist/server/pricing-validator.js +113 -0
- package/dist/server/pricing.d.ts +121 -0
- package/dist/server/pricing.d.ts.map +1 -0
- package/dist/server/pricing.js +225 -0
- package/dist/server/quota.d.ts +66 -0
- package/dist/server/quota.d.ts.map +1 -0
- package/dist/server/quota.js +538 -0
- package/dist/server/wallet-repair.d.ts +32 -0
- package/dist/server/wallet-repair.d.ts.map +1 -0
- package/dist/server/wallet-repair.js +189 -0
- package/dist/server.d.ts +13 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +87 -16
- package/dist/sitemap/handlers/prompts.d.ts +6 -0
- package/dist/sitemap/handlers/prompts.d.ts.map +1 -0
- package/dist/sitemap/handlers/prompts.js +72 -0
- package/dist/sitemap/handlers/users.d.ts +6 -0
- package/dist/sitemap/handlers/users.d.ts.map +1 -0
- package/dist/sitemap/handlers/users.js +80 -0
- package/dist/sitemap/manifest.d.ts +8 -0
- package/dist/sitemap/manifest.d.ts.map +1 -0
- package/dist/sitemap/manifest.js +27 -0
- package/dist/types/gateway.d.ts +40 -0
- package/dist/types/gateway.d.ts.map +1 -0
- package/dist/types/gateway.js +4 -0
- package/dist/types/quota.d.ts +74 -0
- package/dist/types/quota.d.ts.map +1 -0
- package/dist/types/quota.js +11 -0
- package/dist/utils/date.d.ts +7 -0
- package/dist/utils/date.d.ts.map +1 -0
- package/dist/utils/date.js +17 -0
- package/dist/web/admin/AdminTokenPacksPage.js +1 -1
- package/dist/web/admin/BillingAnalyticsPage.d.ts +2 -0
- package/dist/web/admin/BillingAnalyticsPage.d.ts.map +1 -0
- package/dist/web/admin/BillingAnalyticsPage.js +141 -0
- package/dist/web/admin/GlobalAISettingsPage.d.ts +2 -0
- package/dist/web/admin/GlobalAISettingsPage.d.ts.map +1 -0
- package/dist/web/admin/GlobalAISettingsPage.js +93 -0
- package/dist/web/admin/UserTokenPage.d.ts.map +1 -1
- package/dist/web/admin/UserTokenPage.js +20 -7
- package/dist/web/auth/AISettingsPage.d.ts +2 -0
- package/dist/web/auth/AISettingsPage.d.ts.map +1 -0
- package/dist/web/auth/AISettingsPage.js +258 -0
- package/dist/web/auth/APIKeysPage.d.ts +2 -0
- package/dist/web/auth/APIKeysPage.d.ts.map +1 -0
- package/dist/web/auth/APIKeysPage.js +154 -0
- package/dist/web/auth/HistoryPage.d.ts +2 -0
- package/dist/web/auth/HistoryPage.d.ts.map +1 -0
- package/dist/web/auth/HistoryPage.js +279 -0
- package/dist/web/auth/PromptsPage.d.ts +5 -0
- package/dist/web/auth/PromptsPage.d.ts.map +1 -0
- package/dist/web/auth/PromptsPage.js +137 -0
- package/dist/web/auth/TokenPage.d.ts.map +1 -1
- package/dist/web/auth/TokenPage.js +88 -31
- package/dist/web/auth/UsageAndTokensPage.d.ts +2 -0
- package/dist/web/auth/UsageAndTokensPage.d.ts.map +1 -0
- package/dist/web/auth/UsageAndTokensPage.js +157 -0
- package/dist/web/auth/UsagePage.d.ts +2 -0
- package/dist/web/auth/UsagePage.d.ts.map +1 -0
- package/dist/web/auth/UsagePage.js +62 -0
- package/dist/web/auth/components/ApiKeyFilterSelect.d.ts +13 -0
- package/dist/web/auth/components/ApiKeyFilterSelect.d.ts.map +1 -0
- package/dist/web/auth/components/ApiKeyFilterSelect.js +16 -0
- package/dist/web/auth/components/ModelUsageTable.d.ts +19 -0
- package/dist/web/auth/components/ModelUsageTable.d.ts.map +1 -0
- package/dist/web/auth/components/ModelUsageTable.js +37 -0
- package/dist/web/auth/components/PurchaseButton.d.ts +7 -0
- package/dist/web/auth/components/PurchaseButton.d.ts.map +1 -0
- package/dist/web/auth/components/PurchaseButton.js +13 -0
- package/dist/web/auth/components/TokenHistoryCard.d.ts +20 -0
- package/dist/web/auth/components/TokenHistoryCard.d.ts.map +1 -0
- package/dist/web/auth/components/TokenHistoryCard.js +76 -0
- package/dist/web/auth/components/TokenKpiGrid.d.ts +24 -0
- package/dist/web/auth/components/TokenKpiGrid.d.ts.map +1 -0
- package/dist/web/auth/components/TokenKpiGrid.js +38 -0
- package/dist/web/auth/components/UsageByDayChart.d.ts +11 -0
- package/dist/web/auth/components/UsageByDayChart.d.ts.map +1 -0
- package/dist/web/auth/components/UsageByDayChart.js +32 -0
- package/dist/web/auth/components/UsageByModelBarChart.d.ts +12 -0
- package/dist/web/auth/components/UsageByModelBarChart.d.ts.map +1 -0
- package/dist/web/auth/components/UsageByModelBarChart.js +32 -0
- package/dist/web/auth/components/WalletStatusCard.d.ts +9 -0
- package/dist/web/auth/components/WalletStatusCard.d.ts.map +1 -0
- package/dist/web/auth/components/WalletStatusCard.js +50 -0
- package/dist/web/components/ImageGenerative.d.ts +3 -1
- package/dist/web/components/ImageGenerative.d.ts.map +1 -1
- package/dist/web/components/ImageGenerative.js +139 -52
- package/dist/web/components/TextareaGenerative.d.ts +3 -1
- package/dist/web/components/TextareaGenerative.d.ts.map +1 -1
- package/dist/web/components/TextareaGenerative.js +10 -5
- package/dist/web/public/PromptDetailPage.d.ts +25 -0
- package/dist/web/public/PromptDetailPage.d.ts.map +1 -0
- package/dist/web/public/PromptDetailPage.js +71 -0
- package/dist/web/public/PromptDetailPageServer.d.ts +15 -0
- package/dist/web/public/PromptDetailPageServer.d.ts.map +1 -0
- package/dist/web/public/PromptDetailPageServer.js +68 -0
- package/dist/web/public/PublicPromptsPage.d.ts +5 -0
- package/dist/web/public/PublicPromptsPage.d.ts.map +1 -0
- package/dist/web/public/PublicPromptsPage.js +110 -0
- package/dist/web/public/PurchaseTokensPage.d.ts +2 -0
- package/dist/web/public/PurchaseTokensPage.d.ts.map +1 -0
- package/dist/web/public/PurchaseTokensPage.js +98 -0
- package/dist/web/public/UserAvatar.d.ts +13 -0
- package/dist/web/public/UserAvatar.d.ts.map +1 -0
- package/dist/web/public/UserAvatar.js +13 -0
- package/dist/web/public/UserDetailPageServer.d.ts +15 -0
- package/dist/web/public/UserDetailPageServer.d.ts.map +1 -0
- package/dist/web/public/UserDetailPageServer.js +31 -0
- package/dist/web/public/UserPromptsPage.d.ts +9 -0
- package/dist/web/public/UserPromptsPage.d.ts.map +1 -0
- package/dist/web/public/UserPromptsPage.js +112 -0
- package/dist/web/public/UserPromptsPageServer.d.ts +15 -0
- package/dist/web/public/UserPromptsPageServer.d.ts.map +1 -0
- package/dist/web/public/UserPromptsPageServer.js +31 -0
- package/package.json +18 -9
- package/supabase/migrations/20251125000000_ai_tokens.sql +7 -0
- package/supabase/migrations/20260123100002_user_token_quota_monthly copy.sql +173 -0
- package/supabase/migrations/20260128100003_update_and_add_table.sql +368 -0
- package/supabase/migrations/20260128120000_seed_providers_models.sql +78 -0
- package/supabase/migrations/20260128131405_add_api_key_id_to_ledgers.sql +41 -0
- package/supabase/migrations/20260128140000_ai_artifacts_storage.sql +99 -0
- package/supabase/migrations/20260128140002_ai_user_settings.sql +57 -0
- package/supabase/migrations/20260128150000_drop_ai_user_settings.sql +21 -0
- package/supabase/migrations/20260128160000_wallet_billing_system.sql +192 -0
- package/supabase/migrations/20260128160001_wallet_rpc_functions.sql +165 -0
- package/supabase/migrations/20260128170000_add_pack_coef_to_token_packs.sql +30 -0
- package/supabase/migrations/20260129120000_wallet_view_rpc.sql +41 -0
- package/supabase/migrations/20260129220003_update_pack_margins.sql +31 -0
- package/supabase/migrations/20260129330004_ai_user_prompts.sql +151 -0
- package/supabase/migrations/20260129330005_ai_prompts_ip_tracking.sql +92 -0
- package/supabase/migrations/20260129330006_ai_prompts_slug.sql +64 -0
- package/supabase/migrations/20260129330007_ai_prompts_view_slug.sql +26 -0
- package/supabase/migrations/20260129440000_ai_prompts_view_username.sql +33 -0
- package/supabase/migrations/20260129450000_ai_prompts_add_lang.sql +40 -0
- package/supabase/migrations/20260131000000_extract_model_prompt_in_ledger.sql +92 -0
- package/supabase/migrations/20260131140000_fix_duplicate_purchases.sql +64 -0
- package/supabase/migrations/20260201120000_module-ai_default_models.sql +63 -0
- package/supabase/migrations/20260201130000_module-ai_remove_provider_tables.sql +17 -0
- package/supabase/migrations-down/20251217120000_user_token_quota_monthly.sql +34 -0
- package/supabase/migrations-down/20260128131405_add_api_key_id_to_ledgers.sql +25 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GET /api/public/v1/history/[id]
|
|
3
|
+
* Get detailed artifact with download URL for files
|
|
4
|
+
*
|
|
5
|
+
* DELETE /api/public/v1/history/[id]
|
|
6
|
+
* Delete artifact and its storage file
|
|
7
|
+
*/
|
|
8
|
+
import { NextResponse } from "next/server";
|
|
9
|
+
import { randomUUID } from "crypto";
|
|
10
|
+
import { logger } from "@lastbrain/core";
|
|
11
|
+
import { getSupabaseServiceClient } from "@lastbrain/core/server";
|
|
12
|
+
import { validateApiKey } from "../_lib/auth";
|
|
13
|
+
import { createErrorResponse } from "../_lib/errors";
|
|
14
|
+
import { getSignedDownloadUrl, deleteArtifact } from "../_lib/artifacts";
|
|
15
|
+
export async function GET(request, { params }) {
|
|
16
|
+
const requestId = randomUUID();
|
|
17
|
+
try {
|
|
18
|
+
const { id: artifactId } = await params;
|
|
19
|
+
const authHeader = request.headers.get("Authorization");
|
|
20
|
+
// 1. Validate API key OR session auth
|
|
21
|
+
let ownerId = null;
|
|
22
|
+
if (authHeader) {
|
|
23
|
+
// Try API key authentication
|
|
24
|
+
const authResult = await validateApiKey(authHeader);
|
|
25
|
+
if (authResult.success && authResult.apiKey) {
|
|
26
|
+
ownerId = authResult.apiKey.owner_id;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// If no API key, try session authentication
|
|
30
|
+
if (!ownerId) {
|
|
31
|
+
const { getSupabaseServerClient } = await import("@lastbrain/core/server");
|
|
32
|
+
const supabaseAuth = await getSupabaseServerClient();
|
|
33
|
+
const { data: { user }, } = await supabaseAuth.auth.getUser();
|
|
34
|
+
if (user) {
|
|
35
|
+
ownerId = user.id;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// If still no authentication, return error
|
|
39
|
+
if (!ownerId) {
|
|
40
|
+
const { response, status } = createErrorResponse("UNAUTHORIZED", "Authentication required (API key or session)", requestId, undefined, 401);
|
|
41
|
+
return NextResponse.json(response, { status });
|
|
42
|
+
}
|
|
43
|
+
// 2. Fetch artifact from database
|
|
44
|
+
const supabase = getSupabaseServiceClient();
|
|
45
|
+
const { data: artifact, error } = await supabase
|
|
46
|
+
.from("ai_artifacts")
|
|
47
|
+
.select("*")
|
|
48
|
+
.eq("id", artifactId)
|
|
49
|
+
.single();
|
|
50
|
+
if (error || !artifact) {
|
|
51
|
+
const { response, status } = createErrorResponse("INTERNAL_ERROR", "Artifact not found", requestId, undefined, 404);
|
|
52
|
+
return NextResponse.json(response, { status });
|
|
53
|
+
}
|
|
54
|
+
// 3. Verify ownership
|
|
55
|
+
if (artifact.owner_id !== ownerId) {
|
|
56
|
+
const { response, status } = createErrorResponse("INTERNAL_ERROR", "You don't have access to this artifact", requestId, undefined, 403);
|
|
57
|
+
return NextResponse.json(response, { status });
|
|
58
|
+
}
|
|
59
|
+
// 4. Build response based on artifact kind
|
|
60
|
+
const baseResponse = {
|
|
61
|
+
id: artifact.id,
|
|
62
|
+
kind: artifact.kind,
|
|
63
|
+
provider: artifact.provider,
|
|
64
|
+
model: artifact.model,
|
|
65
|
+
endpoint: artifact.endpoint,
|
|
66
|
+
tokens_total: artifact.tokens_total,
|
|
67
|
+
status_code: artifact.status_code,
|
|
68
|
+
mime_type: artifact.mime_type,
|
|
69
|
+
storage_path: artifact.storage_path,
|
|
70
|
+
size_bytes: artifact.size_bytes,
|
|
71
|
+
created_at: artifact.created_at,
|
|
72
|
+
meta: artifact.meta,
|
|
73
|
+
};
|
|
74
|
+
// For text artifacts, return full text_content
|
|
75
|
+
if (artifact.kind === "text") {
|
|
76
|
+
return NextResponse.json({
|
|
77
|
+
request_id: requestId,
|
|
78
|
+
artifact: {
|
|
79
|
+
...baseResponse,
|
|
80
|
+
text_content: artifact.text_content,
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
// For file-based artifacts (image, pdf, file), generate signed URL
|
|
85
|
+
if (artifact.storage_path && artifact.storage_bucket) {
|
|
86
|
+
const { url, error: urlError } = await getSignedDownloadUrl(artifact.storage_path, 60 // 60 seconds TTL
|
|
87
|
+
);
|
|
88
|
+
if (urlError || !url) {
|
|
89
|
+
logger.error("[history/[id]] Failed to generate signed URL", {
|
|
90
|
+
artifactId,
|
|
91
|
+
error: urlError,
|
|
92
|
+
});
|
|
93
|
+
return NextResponse.json({
|
|
94
|
+
request_id: requestId,
|
|
95
|
+
artifact: {
|
|
96
|
+
...baseResponse,
|
|
97
|
+
download_error: "Failed to generate download URL",
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return NextResponse.json({
|
|
102
|
+
request_id: requestId,
|
|
103
|
+
artifact: {
|
|
104
|
+
...baseResponse,
|
|
105
|
+
download_url: url,
|
|
106
|
+
expires_in: 60,
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
// Artifact exists but has no storage
|
|
111
|
+
return NextResponse.json({
|
|
112
|
+
request_id: requestId,
|
|
113
|
+
artifact: {
|
|
114
|
+
...baseResponse,
|
|
115
|
+
note: "No storage file available for this artifact",
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
const errorMessage = error instanceof Error ? error.message : "Internal server error";
|
|
121
|
+
logger.error("[history/[id]] Unexpected error", error);
|
|
122
|
+
const { response, status } = createErrorResponse("INTERNAL_ERROR", errorMessage, requestId, undefined, 500);
|
|
123
|
+
return NextResponse.json(response, { status });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
export async function DELETE(request, { params }) {
|
|
127
|
+
const requestId = randomUUID();
|
|
128
|
+
try {
|
|
129
|
+
const { id: artifactId } = await params;
|
|
130
|
+
const authHeader = request.headers.get("Authorization");
|
|
131
|
+
// 1. Validate API key OR session auth
|
|
132
|
+
let ownerId = null;
|
|
133
|
+
if (authHeader) {
|
|
134
|
+
// Try API key authentication
|
|
135
|
+
const authResult = await validateApiKey(authHeader);
|
|
136
|
+
if (authResult.success && authResult.apiKey) {
|
|
137
|
+
ownerId = authResult.apiKey.owner_id;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// If no API key, try session authentication
|
|
141
|
+
if (!ownerId) {
|
|
142
|
+
const { getSupabaseServerClient } = await import("@lastbrain/core/server");
|
|
143
|
+
const supabaseAuth = await getSupabaseServerClient();
|
|
144
|
+
const { data: { user }, } = await supabaseAuth.auth.getUser();
|
|
145
|
+
if (user) {
|
|
146
|
+
ownerId = user.id;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// If still no authentication, return error
|
|
150
|
+
if (!ownerId) {
|
|
151
|
+
const { response, status } = createErrorResponse("UNAUTHORIZED", "Authentication required (API key or session)", requestId, undefined, 401);
|
|
152
|
+
return NextResponse.json(response, { status });
|
|
153
|
+
}
|
|
154
|
+
// 2. Delete artifact (includes ownership check and storage cleanup)
|
|
155
|
+
const deleteResult = await deleteArtifact(artifactId, ownerId);
|
|
156
|
+
if (!deleteResult.success) {
|
|
157
|
+
const { response, status } = createErrorResponse("INTERNAL_ERROR", deleteResult.error || "Failed to delete artifact", requestId, undefined, deleteResult.error === "Artifact not found"
|
|
158
|
+
? 404
|
|
159
|
+
: deleteResult.error === "Unauthorized"
|
|
160
|
+
? 403
|
|
161
|
+
: 500);
|
|
162
|
+
return NextResponse.json(response, { status });
|
|
163
|
+
}
|
|
164
|
+
return NextResponse.json({
|
|
165
|
+
request_id: requestId,
|
|
166
|
+
deleted: true,
|
|
167
|
+
artifact_id: artifactId,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
const errorMessage = error instanceof Error ? error.message : "Internal server error";
|
|
172
|
+
logger.error("[history/[id]] DELETE unexpected error", error);
|
|
173
|
+
const { response, status } = createErrorResponse("INTERNAL_ERROR", errorMessage, requestId, undefined, 500);
|
|
174
|
+
return NextResponse.json(response, { status });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GET /api/public/v1/history
|
|
3
|
+
* List AI artifacts history with pagination
|
|
4
|
+
*/
|
|
5
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
6
|
+
export declare function GET(request: NextRequest): Promise<NextResponse<import("./_lib/errors").ApiError> | NextResponse<{
|
|
7
|
+
request_id: `${string}-${string}-${string}-${string}-${string}`;
|
|
8
|
+
items: {
|
|
9
|
+
id: any;
|
|
10
|
+
kind: any;
|
|
11
|
+
provider: any;
|
|
12
|
+
model: any;
|
|
13
|
+
endpoint: any;
|
|
14
|
+
tokens_total: any;
|
|
15
|
+
status_code: any;
|
|
16
|
+
has_storage: boolean;
|
|
17
|
+
mime_type: any;
|
|
18
|
+
storage_path: any;
|
|
19
|
+
size_bytes: any;
|
|
20
|
+
created_at: any;
|
|
21
|
+
meta: any;
|
|
22
|
+
text_excerpt: string | undefined;
|
|
23
|
+
}[];
|
|
24
|
+
pagination: {
|
|
25
|
+
limit: number;
|
|
26
|
+
has_more: boolean;
|
|
27
|
+
next_cursor: string | undefined;
|
|
28
|
+
};
|
|
29
|
+
}>>;
|
|
30
|
+
//# sourceMappingURL=history.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../../../../src/api/public/v1/history.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAiBxD,wBAAsB,GAAG,CAAC,OAAO,EAAE,WAAW;;;;;;;;;;;;;;;;;;;;;;;IA0K7C"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GET /api/public/v1/history
|
|
3
|
+
* List AI artifacts history with pagination
|
|
4
|
+
*/
|
|
5
|
+
import { NextResponse } from "next/server";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { randomUUID } from "crypto";
|
|
8
|
+
import { logger } from "@lastbrain/core";
|
|
9
|
+
import { getSupabaseServiceClient } from "@lastbrain/core/server";
|
|
10
|
+
import { validateApiKey } from "./_lib/auth";
|
|
11
|
+
import { createErrorResponse } from "./_lib/errors";
|
|
12
|
+
const HistoryQuerySchema = z.object({
|
|
13
|
+
kind: z
|
|
14
|
+
.enum(["all", "text", "image", "pdf", "file"])
|
|
15
|
+
.optional()
|
|
16
|
+
.default("all"),
|
|
17
|
+
limit: z.coerce.number().int().min(1).max(100).optional().default(20),
|
|
18
|
+
cursor: z.string().optional(),
|
|
19
|
+
});
|
|
20
|
+
export async function GET(request) {
|
|
21
|
+
const requestId = randomUUID();
|
|
22
|
+
try {
|
|
23
|
+
const authHeader = request.headers.get("Authorization");
|
|
24
|
+
// 1. Validate API key OR session auth
|
|
25
|
+
let ownerId = null;
|
|
26
|
+
if (authHeader) {
|
|
27
|
+
// Try API key authentication
|
|
28
|
+
const authResult = await validateApiKey(authHeader);
|
|
29
|
+
if (authResult.success && authResult.apiKey) {
|
|
30
|
+
ownerId = authResult.apiKey.owner_id;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// If no API key, try session authentication
|
|
34
|
+
if (!ownerId) {
|
|
35
|
+
const { getSupabaseServerClient } = await import("@lastbrain/core/server");
|
|
36
|
+
const supabaseAuth = await getSupabaseServerClient();
|
|
37
|
+
const { data: { user }, } = await supabaseAuth.auth.getUser();
|
|
38
|
+
if (user) {
|
|
39
|
+
ownerId = user.id;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// If still no authentication, return error
|
|
43
|
+
if (!ownerId) {
|
|
44
|
+
const { response, status } = createErrorResponse("UNAUTHORIZED", "Authentication required (API key or session)", requestId, undefined, 401);
|
|
45
|
+
return NextResponse.json(response, { status });
|
|
46
|
+
}
|
|
47
|
+
// 2. Parse query params
|
|
48
|
+
const { searchParams } = new URL(request.url);
|
|
49
|
+
const queryData = {
|
|
50
|
+
kind: searchParams.get("kind") || "all",
|
|
51
|
+
limit: searchParams.get("limit") || undefined,
|
|
52
|
+
cursor: searchParams.get("cursor") || undefined,
|
|
53
|
+
};
|
|
54
|
+
const validation = HistoryQuerySchema.safeParse(queryData);
|
|
55
|
+
if (!validation.success) {
|
|
56
|
+
const { response, status } = createErrorResponse("VALIDATION_ERROR", validation.error.errors.map((e) => e.message).join(", "), requestId, undefined, 400);
|
|
57
|
+
return NextResponse.json(response, { status });
|
|
58
|
+
}
|
|
59
|
+
const { kind, limit, cursor } = validation.data;
|
|
60
|
+
// 3. Query artifacts from database
|
|
61
|
+
const supabase = getSupabaseServiceClient();
|
|
62
|
+
let query = supabase
|
|
63
|
+
.from("ai_artifacts")
|
|
64
|
+
.select("id, kind, provider, model, endpoint,storage_path, tokens_total, status_code, storage_path, mime_type, size_bytes, created_at, meta")
|
|
65
|
+
.eq("owner_id", ownerId)
|
|
66
|
+
.order("created_at", { ascending: false })
|
|
67
|
+
.limit(limit + 1); // Fetch one extra to determine if there are more
|
|
68
|
+
// Filter by kind if not "all"
|
|
69
|
+
if (kind !== "all") {
|
|
70
|
+
query = query.eq("kind", kind);
|
|
71
|
+
}
|
|
72
|
+
// Handle cursor-based pagination
|
|
73
|
+
if (cursor) {
|
|
74
|
+
try {
|
|
75
|
+
const [cursorDate, cursorId] = cursor.split("_");
|
|
76
|
+
query = query.or(`created_at.lt.${cursorDate},and(created_at.eq.${cursorDate},id.lt.${cursorId})`);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
logger.warn("[history] Invalid cursor format", { cursor });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const { data: artifacts, error } = await query;
|
|
83
|
+
if (error) {
|
|
84
|
+
logger.error("[history] Database query failed", error);
|
|
85
|
+
const { response, status } = createErrorResponse("INTERNAL_ERROR", "Failed to fetch history", requestId, undefined, 500);
|
|
86
|
+
return NextResponse.json(response, { status });
|
|
87
|
+
}
|
|
88
|
+
// 4. Determine if there are more results
|
|
89
|
+
const hasMore = artifacts.length > limit;
|
|
90
|
+
const items = hasMore ? artifacts.slice(0, limit) : artifacts;
|
|
91
|
+
// 5. Format response
|
|
92
|
+
const formattedItems = items.map((artifact) => {
|
|
93
|
+
// For text artifacts, include excerpt
|
|
94
|
+
let textExcerpt;
|
|
95
|
+
if (artifact.kind === "text") {
|
|
96
|
+
const fullText = artifact.text_content;
|
|
97
|
+
if (fullText && typeof fullText === "string") {
|
|
98
|
+
textExcerpt = fullText.substring(0, 200);
|
|
99
|
+
if (fullText.length > 200) {
|
|
100
|
+
textExcerpt += "...";
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
id: artifact.id,
|
|
106
|
+
kind: artifact.kind,
|
|
107
|
+
provider: artifact.provider,
|
|
108
|
+
model: artifact.model,
|
|
109
|
+
endpoint: artifact.endpoint,
|
|
110
|
+
tokens_total: artifact.tokens_total,
|
|
111
|
+
status_code: artifact.status_code,
|
|
112
|
+
has_storage: !!artifact.storage_path,
|
|
113
|
+
mime_type: artifact.mime_type,
|
|
114
|
+
storage_path: artifact.storage_path,
|
|
115
|
+
size_bytes: artifact.size_bytes,
|
|
116
|
+
created_at: artifact.created_at,
|
|
117
|
+
meta: artifact.meta,
|
|
118
|
+
text_excerpt: textExcerpt,
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
// 6. Generate next cursor
|
|
122
|
+
let nextCursor;
|
|
123
|
+
if (hasMore && items.length > 0) {
|
|
124
|
+
const lastItem = items[items.length - 1];
|
|
125
|
+
nextCursor = `${lastItem.created_at}_${lastItem.id}`;
|
|
126
|
+
}
|
|
127
|
+
return NextResponse.json({
|
|
128
|
+
request_id: requestId,
|
|
129
|
+
items: formattedItems,
|
|
130
|
+
pagination: {
|
|
131
|
+
limit,
|
|
132
|
+
has_more: hasMore,
|
|
133
|
+
next_cursor: nextCursor,
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
logger.error("[history] Unexpected error", error);
|
|
139
|
+
const { response, status } = createErrorResponse("INTERNAL_ERROR", error.message || "Internal server error", requestId, undefined, 500);
|
|
140
|
+
return NextResponse.json(response, { status });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /api/public/v1/image-ai
|
|
3
|
+
* Generate images using AI
|
|
4
|
+
*/
|
|
5
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
6
|
+
export declare function POST(request: NextRequest): Promise<NextResponse<import("./_lib/errors").ApiError> | NextResponse<{
|
|
7
|
+
request_id: `${string}-${string}-${string}-${string}-${string}`;
|
|
8
|
+
image_url: string | undefined;
|
|
9
|
+
model: string | undefined;
|
|
10
|
+
provider: string | undefined;
|
|
11
|
+
tokens: {
|
|
12
|
+
used: number;
|
|
13
|
+
};
|
|
14
|
+
balance: {
|
|
15
|
+
remaining_quota: number;
|
|
16
|
+
remaining_purchased: any;
|
|
17
|
+
total: any;
|
|
18
|
+
};
|
|
19
|
+
artifact: {
|
|
20
|
+
id: string | undefined;
|
|
21
|
+
stored: boolean;
|
|
22
|
+
} | undefined;
|
|
23
|
+
}>>;
|
|
24
|
+
//# sourceMappingURL=image-ai.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-ai.d.ts","sourceRoot":"","sources":["../../../../src/api/public/v1/image-ai.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAsBxD,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;;;;;;;;;;;;;;IA8R9C"}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /api/public/v1/image-ai
|
|
3
|
+
* Generate images using AI
|
|
4
|
+
*/
|
|
5
|
+
import { NextResponse } from "next/server";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { randomUUID } from "crypto";
|
|
8
|
+
import { logger } from "@lastbrain/core";
|
|
9
|
+
import { getSupabaseServiceClient } from "@lastbrain/core/server";
|
|
10
|
+
import { validateApiKey, verifyScope } from "./_lib/auth";
|
|
11
|
+
import { createErrorResponse } from "./_lib/errors";
|
|
12
|
+
import { logApiCall, updateApiKeyLastUsed } from "./_lib/log";
|
|
13
|
+
import { generateImage } from "./_lib/router";
|
|
14
|
+
import { shouldStoreOutputs, createArtifact } from "./_lib/artifacts";
|
|
15
|
+
const ImageGenerationSchema = z.object({
|
|
16
|
+
prompt: z.string().min(1).max(5000),
|
|
17
|
+
model: z.string().optional(),
|
|
18
|
+
size: z
|
|
19
|
+
.enum(["256x256", "512x512", "1024x1024", "1024x1792", "1792x1024"])
|
|
20
|
+
.optional(),
|
|
21
|
+
quality: z.enum(["standard", "hd"]).optional(),
|
|
22
|
+
store_outputs: z.boolean().optional(),
|
|
23
|
+
artifact_title: z.string().max(200).optional(),
|
|
24
|
+
});
|
|
25
|
+
export async function POST(request) {
|
|
26
|
+
const startTime = Date.now();
|
|
27
|
+
const requestId = randomUUID();
|
|
28
|
+
let apiKeyId = null;
|
|
29
|
+
let ownerId = null;
|
|
30
|
+
let statusCode = 200;
|
|
31
|
+
let errorCode = null;
|
|
32
|
+
try {
|
|
33
|
+
const authHeader = request.headers.get("Authorization");
|
|
34
|
+
// 1. Validate API key
|
|
35
|
+
const authResult = await validateApiKey(authHeader);
|
|
36
|
+
if (!authResult.success || !authResult.apiKey) {
|
|
37
|
+
const { response, status } = createErrorResponse(authResult.errorCode, authResult.error || "Authentication failed", requestId, undefined, 401);
|
|
38
|
+
statusCode = status;
|
|
39
|
+
errorCode = authResult.errorCode || null;
|
|
40
|
+
return NextResponse.json(response, { status });
|
|
41
|
+
}
|
|
42
|
+
const apiKey = authResult.apiKey;
|
|
43
|
+
apiKeyId = apiKey.id;
|
|
44
|
+
ownerId = apiKey.owner_id;
|
|
45
|
+
// 2. Verify scope
|
|
46
|
+
const scopeResult = verifyScope(apiKey, "image");
|
|
47
|
+
if (!scopeResult.success) {
|
|
48
|
+
const { response, status } = createErrorResponse("INSUFFICIENT_SCOPE", scopeResult.error || "Missing required scope", requestId, undefined, 403);
|
|
49
|
+
statusCode = status;
|
|
50
|
+
errorCode = "INSUFFICIENT_SCOPE";
|
|
51
|
+
return NextResponse.json(response, { status });
|
|
52
|
+
}
|
|
53
|
+
// 3. Parse and validate request body
|
|
54
|
+
let body;
|
|
55
|
+
try {
|
|
56
|
+
body = await request.json();
|
|
57
|
+
}
|
|
58
|
+
catch (parseError) {
|
|
59
|
+
const { response, status } = createErrorResponse("VALIDATION_ERROR", "Invalid JSON in request body", requestId, undefined, 400);
|
|
60
|
+
statusCode = status;
|
|
61
|
+
errorCode = "VALIDATION_ERROR";
|
|
62
|
+
return NextResponse.json(response, { status });
|
|
63
|
+
}
|
|
64
|
+
if (!body || typeof body !== "object") {
|
|
65
|
+
const { response, status } = createErrorResponse("VALIDATION_ERROR", "Request body must be a valid JSON object", requestId, undefined, 400);
|
|
66
|
+
statusCode = status;
|
|
67
|
+
errorCode = "VALIDATION_ERROR";
|
|
68
|
+
return NextResponse.json(response, { status });
|
|
69
|
+
}
|
|
70
|
+
const validation = ImageGenerationSchema.safeParse(body);
|
|
71
|
+
if (!validation.success) {
|
|
72
|
+
const { response, status } = createErrorResponse("VALIDATION_ERROR", validation.error.errors.map((e) => e.message).join(", "), requestId, undefined, 400);
|
|
73
|
+
statusCode = status;
|
|
74
|
+
errorCode = "VALIDATION_ERROR";
|
|
75
|
+
return NextResponse.json(response, { status });
|
|
76
|
+
}
|
|
77
|
+
const data = validation.data;
|
|
78
|
+
// 4. Check token balance BEFORE calling model (prevent free consumption)
|
|
79
|
+
const estimatedTokens = 6000; // Conservative estimate for images
|
|
80
|
+
const supabase = getSupabaseServiceClient();
|
|
81
|
+
const { data: balanceData } = await supabase
|
|
82
|
+
.from("user_token_balance_v")
|
|
83
|
+
.select("balance")
|
|
84
|
+
.eq("owner_id", ownerId)
|
|
85
|
+
.single();
|
|
86
|
+
const currentBalance = balanceData?.balance || 0;
|
|
87
|
+
if (currentBalance < estimatedTokens) {
|
|
88
|
+
logger.warn(`[image-ai] Insufficient balance: have ${currentBalance}, need ~${estimatedTokens}`);
|
|
89
|
+
const purchaseUrl = process.env.NEXT_PUBLIC_APP_URL + "/auth/module-ai/tokens";
|
|
90
|
+
const { response, status } = createErrorResponse("INSUFFICIENT_TOKENS", `Insufficient token balance. You have ${currentBalance} tokens, estimated ${estimatedTokens} needed.`, requestId, purchaseUrl, 402);
|
|
91
|
+
statusCode = status;
|
|
92
|
+
errorCode = "INSUFFICIENT_TOKENS";
|
|
93
|
+
return NextResponse.json(response, { status });
|
|
94
|
+
}
|
|
95
|
+
// 5. Generate image via router (handles model validation and token estimation)
|
|
96
|
+
const generateResult = await generateImage(ownerId, {
|
|
97
|
+
prompt: data.prompt,
|
|
98
|
+
model: data.model, // Can be provider/model or just model name
|
|
99
|
+
size: data.size,
|
|
100
|
+
quality: data.quality,
|
|
101
|
+
});
|
|
102
|
+
if (!generateResult.success) {
|
|
103
|
+
const { response, status } = createErrorResponse("PROVIDER_ERROR", generateResult.error || "Image generation failed", requestId, undefined, 500);
|
|
104
|
+
statusCode = status;
|
|
105
|
+
errorCode = "PROVIDER_ERROR";
|
|
106
|
+
return NextResponse.json(response, { status });
|
|
107
|
+
}
|
|
108
|
+
// Token debit already handled by computeAndDebitAIUsage() in generateImage()
|
|
109
|
+
const tokensUsed = generateResult.tokensUsed || 0;
|
|
110
|
+
logger.info(`[image-ai] ✅ Success: billing handled via computeAndDebitAIUsage()`);
|
|
111
|
+
// 6. Log API call
|
|
112
|
+
const latencyMs = Date.now() - startTime;
|
|
113
|
+
await logApiCall({
|
|
114
|
+
apiKeyId,
|
|
115
|
+
ownerId,
|
|
116
|
+
endpoint: "image",
|
|
117
|
+
provider: generateResult.provider || null,
|
|
118
|
+
model: generateResult.model || null,
|
|
119
|
+
tokensIn: null,
|
|
120
|
+
tokensOut: null,
|
|
121
|
+
tokensTotal: tokensUsed,
|
|
122
|
+
latencyMs,
|
|
123
|
+
statusCode,
|
|
124
|
+
errorCode: null,
|
|
125
|
+
requestId,
|
|
126
|
+
meta: {
|
|
127
|
+
prompt_length: data.prompt.length,
|
|
128
|
+
size: data.size,
|
|
129
|
+
quality: data.quality,
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
// 8. Update API key last used
|
|
133
|
+
await updateApiKeyLastUsed(apiKey);
|
|
134
|
+
// 9. Store artifact if enabled
|
|
135
|
+
let artifactId;
|
|
136
|
+
let artifactStored = false;
|
|
137
|
+
const shouldStore = await shouldStoreOutputs(ownerId, body.store_outputs);
|
|
138
|
+
if (shouldStore && generateResult.imageUrl) {
|
|
139
|
+
try {
|
|
140
|
+
// Download image from URL
|
|
141
|
+
const imageResponse = await fetch(generateResult.imageUrl);
|
|
142
|
+
if (imageResponse.ok) {
|
|
143
|
+
const arrayBuffer = await imageResponse.arrayBuffer();
|
|
144
|
+
const imageBuffer = Buffer.from(arrayBuffer);
|
|
145
|
+
const artifactResult = await createArtifact({
|
|
146
|
+
apiKey,
|
|
147
|
+
kind: "image",
|
|
148
|
+
endpoint: "image-ai",
|
|
149
|
+
provider: generateResult.provider,
|
|
150
|
+
model: generateResult.model,
|
|
151
|
+
tokensTotal: tokensUsed,
|
|
152
|
+
statusCode: 200,
|
|
153
|
+
fileBuffer: imageBuffer,
|
|
154
|
+
mimeType: "image/png",
|
|
155
|
+
ext: "png",
|
|
156
|
+
meta: {
|
|
157
|
+
request_id: requestId,
|
|
158
|
+
title: body.artifact_title,
|
|
159
|
+
size: data.size,
|
|
160
|
+
quality: data.quality,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
if (artifactResult.success) {
|
|
164
|
+
artifactId = artifactResult.artifactId;
|
|
165
|
+
artifactStored = true;
|
|
166
|
+
logger.debug("[image-ai] Artifact stored", { artifactId });
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
logger.warn("[image-ai] Failed to store artifact", {
|
|
170
|
+
error: artifactResult.error,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch (downloadError) {
|
|
176
|
+
logger.error("[image-ai] Failed to download image for storage", downloadError);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// 7. Get updated balance for response
|
|
180
|
+
const { data: finalBalanceData } = await supabase
|
|
181
|
+
.from("user_token_balance_v")
|
|
182
|
+
.select("balance")
|
|
183
|
+
.eq("owner_id", ownerId)
|
|
184
|
+
.single();
|
|
185
|
+
const totalBalance = finalBalanceData?.balance || 0;
|
|
186
|
+
// 8. Return response
|
|
187
|
+
return NextResponse.json({
|
|
188
|
+
request_id: requestId,
|
|
189
|
+
image_url: generateResult.imageUrl,
|
|
190
|
+
model: generateResult.model,
|
|
191
|
+
provider: generateResult.provider,
|
|
192
|
+
tokens: {
|
|
193
|
+
used: tokensUsed,
|
|
194
|
+
},
|
|
195
|
+
balance: {
|
|
196
|
+
remaining_quota: 0,
|
|
197
|
+
remaining_purchased: totalBalance,
|
|
198
|
+
total: totalBalance,
|
|
199
|
+
},
|
|
200
|
+
artifact: artifactStored
|
|
201
|
+
? {
|
|
202
|
+
id: artifactId,
|
|
203
|
+
stored: true,
|
|
204
|
+
}
|
|
205
|
+
: undefined,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
logger.error("[image-ai] Unexpected error", error);
|
|
210
|
+
statusCode = 500;
|
|
211
|
+
errorCode = "INTERNAL_ERROR";
|
|
212
|
+
// Log error even in catch
|
|
213
|
+
if (apiKeyId && ownerId) {
|
|
214
|
+
const latencyMs = Date.now() - startTime;
|
|
215
|
+
await logApiCall({
|
|
216
|
+
apiKeyId,
|
|
217
|
+
ownerId,
|
|
218
|
+
endpoint: "image",
|
|
219
|
+
provider: null,
|
|
220
|
+
model: null,
|
|
221
|
+
tokensIn: null,
|
|
222
|
+
tokensOut: null,
|
|
223
|
+
tokensTotal: null,
|
|
224
|
+
latencyMs,
|
|
225
|
+
statusCode,
|
|
226
|
+
errorCode,
|
|
227
|
+
requestId,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
const { response, status } = createErrorResponse("INTERNAL_ERROR", error.message || "Internal server error", requestId, undefined, 500);
|
|
231
|
+
return NextResponse.json(response, { status });
|
|
232
|
+
}
|
|
233
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GET /api/public/v1/prompts
|
|
3
|
+
* Get user's prompts via Bearer token authentication
|
|
4
|
+
*/
|
|
5
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
6
|
+
export declare function GET(request: NextRequest): Promise<NextResponse<import("./_lib/errors").ApiError> | NextResponse<{
|
|
7
|
+
request_id: `${string}-${string}-${string}-${string}-${string}`;
|
|
8
|
+
prompts: {
|
|
9
|
+
id: any;
|
|
10
|
+
title: any;
|
|
11
|
+
content: any;
|
|
12
|
+
type: any;
|
|
13
|
+
tags: any;
|
|
14
|
+
is_public: any;
|
|
15
|
+
favorite: any;
|
|
16
|
+
created_at: any;
|
|
17
|
+
}[];
|
|
18
|
+
}>>;
|
|
19
|
+
//# sourceMappingURL=prompts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../../../src/api/public/v1/prompts.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAQxD,wBAAsB,GAAG,CAAC,OAAO,EAAE,WAAW;;;;;;;;;;;;IAkI7C"}
|