@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,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging utilities for public API v1
|
|
3
|
+
* Handles ai_call_log inserts and api_keys.last_used_at updates
|
|
4
|
+
*/
|
|
5
|
+
import { getSupabaseServiceClient } from "@lastbrain/core/server";
|
|
6
|
+
import { logger } from "@lastbrain/core";
|
|
7
|
+
/**
|
|
8
|
+
* Insert a log entry in ai_call_log
|
|
9
|
+
*/
|
|
10
|
+
export async function logApiCall(params) {
|
|
11
|
+
try {
|
|
12
|
+
const supabase = await getSupabaseServiceClient();
|
|
13
|
+
const { error } = await supabase.from("ai_call_log").insert({
|
|
14
|
+
owner_id: params.ownerId,
|
|
15
|
+
api_key_id: params.apiKeyId,
|
|
16
|
+
endpoint: params.endpoint,
|
|
17
|
+
provider: params.provider,
|
|
18
|
+
model: params.model,
|
|
19
|
+
tokens_in: params.tokensIn,
|
|
20
|
+
tokens_out: params.tokensOut,
|
|
21
|
+
tokens_total: params.tokensTotal,
|
|
22
|
+
latency_ms: params.latencyMs,
|
|
23
|
+
status_code: params.statusCode,
|
|
24
|
+
error_code: params.errorCode,
|
|
25
|
+
meta: {
|
|
26
|
+
request_id: params.requestId,
|
|
27
|
+
...(params.meta || {}),
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
if (error) {
|
|
31
|
+
logger.error("[logApiCall] Failed to insert ai_call_log", {
|
|
32
|
+
error,
|
|
33
|
+
params,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
logger.debug("[logApiCall] Logged API call", {
|
|
38
|
+
endpoint: params.endpoint,
|
|
39
|
+
statusCode: params.statusCode,
|
|
40
|
+
requestId: params.requestId,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
logger.error("[logApiCall] Exception logging API call", error);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Update api_keys.last_used_at
|
|
50
|
+
*/
|
|
51
|
+
export async function updateApiKeyLastUsed(apiKey) {
|
|
52
|
+
try {
|
|
53
|
+
const supabase = await getSupabaseServiceClient();
|
|
54
|
+
const { error } = await supabase
|
|
55
|
+
.from("api_keys")
|
|
56
|
+
.update({ last_used_at: new Date().toISOString() })
|
|
57
|
+
.eq("id", apiKey.id);
|
|
58
|
+
if (error) {
|
|
59
|
+
logger.warn("[updateApiKeyLastUsed] Failed to update", {
|
|
60
|
+
error,
|
|
61
|
+
apiKeyId: apiKey.id,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
logger.warn("[updateApiKeyLastUsed] Exception", error);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quota and rate limit checking for public API v1
|
|
3
|
+
*/
|
|
4
|
+
import type { ApiKeyData } from "./auth";
|
|
5
|
+
export interface QuotaCheckResult {
|
|
6
|
+
success: boolean;
|
|
7
|
+
error?: string;
|
|
8
|
+
errorCode?: string;
|
|
9
|
+
balance?: number;
|
|
10
|
+
remainingQuota?: number;
|
|
11
|
+
remainingPurchased?: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Check if user has sufficient tokens and within limits
|
|
15
|
+
*/
|
|
16
|
+
export declare function checkQuota(apiKey: ApiKeyData, estimatedTokens: number): Promise<QuotaCheckResult>;
|
|
17
|
+
/**
|
|
18
|
+
* Check user_ai_settings for max_tokens_per_call limit
|
|
19
|
+
*/
|
|
20
|
+
export declare function checkMaxTokensPerCall(ownerId: string, requestedMaxTokens: number): Promise<{
|
|
21
|
+
allowed: boolean;
|
|
22
|
+
limit?: number;
|
|
23
|
+
}>;
|
|
24
|
+
//# sourceMappingURL=quota.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quota.d.ts","sourceRoot":"","sources":["../../../../../src/api/public/v1/_lib/quota.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEzC,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,UAAU,EAClB,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,gBAAgB,CAAC,CAwG3B;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,MAAM,EACf,kBAAkB,EAAE,MAAM,GACzB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAsB/C"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quota and rate limit checking for public API v1
|
|
3
|
+
*/
|
|
4
|
+
import { getSupabaseServiceClient } from "@lastbrain/core/server";
|
|
5
|
+
import { logger } from "@lastbrain/core";
|
|
6
|
+
/**
|
|
7
|
+
* Check if user has sufficient tokens and within limits
|
|
8
|
+
*/
|
|
9
|
+
export async function checkQuota(apiKey, estimatedTokens) {
|
|
10
|
+
try {
|
|
11
|
+
const supabase = await getSupabaseServiceClient();
|
|
12
|
+
// 1. Check token balance (purchased)
|
|
13
|
+
const { data: balanceData } = await supabase
|
|
14
|
+
.from("user_token_balance_v")
|
|
15
|
+
.select("balance")
|
|
16
|
+
.eq("owner_id", apiKey.owner_id)
|
|
17
|
+
.single();
|
|
18
|
+
const purchasedBalance = balanceData?.balance || 0;
|
|
19
|
+
// 2. Check quota balance (monthly included)
|
|
20
|
+
const { data: quotaData } = await supabase
|
|
21
|
+
.from("user_token_quota_monthly")
|
|
22
|
+
.select("effective_included_tokens, used_included_tokens, period_start, period_end")
|
|
23
|
+
.eq("owner_id", apiKey.owner_id)
|
|
24
|
+
.single();
|
|
25
|
+
let remainingQuota = 0;
|
|
26
|
+
if (quotaData) {
|
|
27
|
+
const now = new Date();
|
|
28
|
+
const periodStart = quotaData.period_start
|
|
29
|
+
? new Date(quotaData.period_start)
|
|
30
|
+
: null;
|
|
31
|
+
const periodEnd = quotaData.period_end
|
|
32
|
+
? new Date(quotaData.period_end)
|
|
33
|
+
: null;
|
|
34
|
+
const isActive = periodStart && periodEnd && now >= periodStart && now <= periodEnd;
|
|
35
|
+
if (isActive) {
|
|
36
|
+
remainingQuota = Math.max(0, quotaData.effective_included_tokens -
|
|
37
|
+
(quotaData.used_included_tokens || 0));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const totalBalance = purchasedBalance + remainingQuota;
|
|
41
|
+
// 3. Check daily limit (if set on API key)
|
|
42
|
+
if (apiKey.daily_token_limit !== null) {
|
|
43
|
+
const today = new Date();
|
|
44
|
+
today.setHours(0, 0, 0, 0);
|
|
45
|
+
const { data: todayUsage } = await supabase
|
|
46
|
+
.from("ai_call_log")
|
|
47
|
+
.select("tokens_total")
|
|
48
|
+
.eq("api_key_id", apiKey.id)
|
|
49
|
+
.gte("created_at", today.toISOString());
|
|
50
|
+
const usedToday = todayUsage?.reduce((sum, log) => sum + (log.tokens_total || 0), 0) || 0;
|
|
51
|
+
const dailyLimit = Number(apiKey.daily_token_limit);
|
|
52
|
+
if (usedToday + estimatedTokens > dailyLimit) {
|
|
53
|
+
return {
|
|
54
|
+
success: false,
|
|
55
|
+
error: `Daily token limit exceeded. Used: ${usedToday}, Limit: ${dailyLimit}`,
|
|
56
|
+
errorCode: "DAILY_LIMIT_EXCEEDED",
|
|
57
|
+
balance: totalBalance,
|
|
58
|
+
remainingQuota,
|
|
59
|
+
remainingPurchased: purchasedBalance,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// 4. Check if insufficient balance
|
|
64
|
+
if (totalBalance < estimatedTokens) {
|
|
65
|
+
// Block if purchased balance is already negative
|
|
66
|
+
if (purchasedBalance < 0) {
|
|
67
|
+
return {
|
|
68
|
+
success: false,
|
|
69
|
+
error: "Insufficient tokens. Please purchase more tokens.",
|
|
70
|
+
errorCode: "INSUFFICIENT_TOKENS",
|
|
71
|
+
balance: totalBalance,
|
|
72
|
+
remainingQuota,
|
|
73
|
+
remainingPurchased: purchasedBalance,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
// If positive but insufficient, allow one-time overdraft (debit will handle it)
|
|
77
|
+
logger.info("[checkQuota] Allowing overdraft for user", apiKey.owner_id);
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
success: true,
|
|
81
|
+
balance: totalBalance,
|
|
82
|
+
remainingQuota,
|
|
83
|
+
remainingPurchased: purchasedBalance,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
logger.error("[checkQuota] Error checking quota", error);
|
|
88
|
+
return {
|
|
89
|
+
success: false,
|
|
90
|
+
error: "Internal error checking quota",
|
|
91
|
+
errorCode: "INTERNAL_ERROR",
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Check user_ai_settings for max_tokens_per_call limit
|
|
97
|
+
*/
|
|
98
|
+
export async function checkMaxTokensPerCall(ownerId, requestedMaxTokens) {
|
|
99
|
+
try {
|
|
100
|
+
const supabase = await getSupabaseServiceClient();
|
|
101
|
+
const { data: settings } = await supabase
|
|
102
|
+
.from("user_ai_settings")
|
|
103
|
+
.select("max_tokens_per_call")
|
|
104
|
+
.eq("owner_id", ownerId)
|
|
105
|
+
.single();
|
|
106
|
+
if (settings?.max_tokens_per_call) {
|
|
107
|
+
const limit = settings.max_tokens_per_call;
|
|
108
|
+
if (requestedMaxTokens > limit) {
|
|
109
|
+
return { allowed: false, limit };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return { allowed: true };
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
logger.warn("[checkMaxTokensPerCall] Error, allowing by default", error);
|
|
116
|
+
return { allowed: true };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Provider router for public API v1
|
|
3
|
+
* DEPRECATED: Use ai-generation-service.ts instead
|
|
4
|
+
*
|
|
5
|
+
* This file is kept for backward compatibility only
|
|
6
|
+
* All functions now delegate to the centralized service
|
|
7
|
+
*/
|
|
8
|
+
export interface TextGenerationParams {
|
|
9
|
+
prompt: string;
|
|
10
|
+
model?: string;
|
|
11
|
+
maxTokens?: number;
|
|
12
|
+
temperature?: number;
|
|
13
|
+
systemPrompt?: string;
|
|
14
|
+
actionType?: "generate-text" | "generate-recipe-text" | "autocomplete";
|
|
15
|
+
}
|
|
16
|
+
export interface TextGenerationResult {
|
|
17
|
+
success: boolean;
|
|
18
|
+
text?: string;
|
|
19
|
+
tokensIn?: number;
|
|
20
|
+
tokensOut?: number;
|
|
21
|
+
tokensTotal?: number;
|
|
22
|
+
model?: string;
|
|
23
|
+
provider?: string;
|
|
24
|
+
error?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface ImageGenerationParams {
|
|
27
|
+
prompt: string;
|
|
28
|
+
model?: string;
|
|
29
|
+
size?: string;
|
|
30
|
+
quality?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface ImageGenerationResult {
|
|
33
|
+
success: boolean;
|
|
34
|
+
imageUrl?: string;
|
|
35
|
+
revisedPrompt?: string;
|
|
36
|
+
tokensUsed?: number;
|
|
37
|
+
model?: string;
|
|
38
|
+
provider?: string;
|
|
39
|
+
error?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Generate text using Vercel AI SDK with model filtering
|
|
43
|
+
*/
|
|
44
|
+
/**
|
|
45
|
+
* Generate text - delegates to centralized service
|
|
46
|
+
* @deprecated Use generateTextWithBilling from ai-generation-service directly
|
|
47
|
+
*/
|
|
48
|
+
export declare function generateText(ownerId: string, params: TextGenerationParams): Promise<TextGenerationResult>;
|
|
49
|
+
/**
|
|
50
|
+
* Generate images - delegates to centralized service
|
|
51
|
+
* @deprecated Use generateImageWithBilling from ai-generation-service directly
|
|
52
|
+
*/
|
|
53
|
+
export declare function generateImage(ownerId: string, params: ImageGenerationParams): Promise<ImageGenerationResult>;
|
|
54
|
+
//# sourceMappingURL=router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../../../../src/api/public/v1/_lib/router.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,eAAe,GAAG,sBAAsB,GAAG,cAAc,CAAC;CACxE;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA4ED;;GAEG;AACH;;;GAGG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,oBAAoB,CAAC,CAyB/B;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC,CA4BhC"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Provider router for public API v1
|
|
3
|
+
* DEPRECATED: Use ai-generation-service.ts instead
|
|
4
|
+
*
|
|
5
|
+
* This file is kept for backward compatibility only
|
|
6
|
+
* All functions now delegate to the centralized service
|
|
7
|
+
*/
|
|
8
|
+
import { logger } from "@lastbrain/core";
|
|
9
|
+
import { getSupabaseServiceClient } from "@lastbrain/core/server";
|
|
10
|
+
import { generateTextWithBilling, generateImageWithBilling, } from "../../../../server/ai-generation-service";
|
|
11
|
+
import { getGlobalAISettings } from "../../../../server/global-settings";
|
|
12
|
+
/**
|
|
13
|
+
* Get user's default text settings or use GLOBAL system defaults
|
|
14
|
+
*/
|
|
15
|
+
async function getTextSettings(ownerId) {
|
|
16
|
+
try {
|
|
17
|
+
const supabase = await getSupabaseServiceClient();
|
|
18
|
+
const { data: settings } = await supabase
|
|
19
|
+
.from("user_ai_settings")
|
|
20
|
+
.select("default_text_provider, default_text_model, mode")
|
|
21
|
+
.eq("owner_id", ownerId)
|
|
22
|
+
.single();
|
|
23
|
+
if (settings?.mode === "manual" && settings.default_text_provider) {
|
|
24
|
+
return {
|
|
25
|
+
provider: settings.default_text_provider,
|
|
26
|
+
model: settings.default_text_model || "gpt-4.1-mini",
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
logger.warn("[getTextSettings] Could not fetch user settings, using global defaults", error);
|
|
32
|
+
}
|
|
33
|
+
// Use GLOBAL defaults instead of hardcoded values
|
|
34
|
+
const globalSettings = await getGlobalAISettings();
|
|
35
|
+
return {
|
|
36
|
+
provider: globalSettings.default_text_provider,
|
|
37
|
+
model: globalSettings.default_text_model,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get user's default image settings or use GLOBAL system defaults
|
|
42
|
+
*/
|
|
43
|
+
async function getImageSettings(ownerId) {
|
|
44
|
+
try {
|
|
45
|
+
const supabase = await getSupabaseServiceClient();
|
|
46
|
+
const { data: settings } = await supabase
|
|
47
|
+
.from("user_ai_settings")
|
|
48
|
+
.select("default_image_provider, default_image_model, mode")
|
|
49
|
+
.eq("owner_id", ownerId)
|
|
50
|
+
.single();
|
|
51
|
+
if (settings?.mode === "manual" && settings.default_image_provider) {
|
|
52
|
+
return {
|
|
53
|
+
provider: settings.default_image_provider,
|
|
54
|
+
model: settings.default_image_model || "gemini-2.5-flash-image",
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
logger.warn("[getImageSettings] Could not fetch user settings, using global defaults", error);
|
|
60
|
+
}
|
|
61
|
+
// Use GLOBAL defaults instead of hardcoded values
|
|
62
|
+
const globalSettings = await getGlobalAISettings();
|
|
63
|
+
return {
|
|
64
|
+
provider: globalSettings.default_image_provider,
|
|
65
|
+
model: globalSettings.default_image_model,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Generate text using Vercel AI SDK with model filtering
|
|
70
|
+
*/
|
|
71
|
+
/**
|
|
72
|
+
* Generate text - delegates to centralized service
|
|
73
|
+
* @deprecated Use generateTextWithBilling from ai-generation-service directly
|
|
74
|
+
*/
|
|
75
|
+
export async function generateText(ownerId, params) {
|
|
76
|
+
logger.info(`[router] Delegating to generateTextWithBilling for user ${ownerId}`);
|
|
77
|
+
const result = await generateTextWithBilling(ownerId, {
|
|
78
|
+
prompt: params.prompt,
|
|
79
|
+
model: params.model,
|
|
80
|
+
systemPrompt: params.systemPrompt,
|
|
81
|
+
maxTokens: params.maxTokens,
|
|
82
|
+
temperature: params.temperature,
|
|
83
|
+
actionType: params.actionType || "generate-text",
|
|
84
|
+
});
|
|
85
|
+
// Map to legacy format for backward compatibility
|
|
86
|
+
return {
|
|
87
|
+
success: result.success,
|
|
88
|
+
text: result.text,
|
|
89
|
+
tokensIn: result.inputTokens,
|
|
90
|
+
tokensOut: result.outputTokens,
|
|
91
|
+
tokensTotal: 0, // Deprecated
|
|
92
|
+
model: result.model,
|
|
93
|
+
provider: result.provider,
|
|
94
|
+
error: result.error,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Generate images - delegates to centralized service
|
|
99
|
+
* @deprecated Use generateImageWithBilling from ai-generation-service directly
|
|
100
|
+
*/
|
|
101
|
+
export async function generateImage(ownerId, params) {
|
|
102
|
+
logger.info(`[router] Delegating to generateImageWithBilling for user ${ownerId}`);
|
|
103
|
+
const result = await generateImageWithBilling(ownerId, {
|
|
104
|
+
prompt: params.prompt,
|
|
105
|
+
model: params.model,
|
|
106
|
+
size: params.size,
|
|
107
|
+
quality: params.quality,
|
|
108
|
+
actionType: "generate-image",
|
|
109
|
+
});
|
|
110
|
+
// Map to legacy format
|
|
111
|
+
return {
|
|
112
|
+
success: result.success,
|
|
113
|
+
imageUrl: result.imageUrl,
|
|
114
|
+
revisedPrompt: result.revisedPrompt,
|
|
115
|
+
model: result.model,
|
|
116
|
+
provider: result.provider,
|
|
117
|
+
error: result.error,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /api/public/v1/connect
|
|
3
|
+
* Authenticate with email/password and receive a dev API key
|
|
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
|
+
message: string;
|
|
9
|
+
api_key_id: any;
|
|
10
|
+
prefix: any;
|
|
11
|
+
user_id: string;
|
|
12
|
+
dashboard_url: string;
|
|
13
|
+
}> | NextResponse<{
|
|
14
|
+
request_id: `${string}-${string}-${string}-${string}-${string}`;
|
|
15
|
+
api_key: string;
|
|
16
|
+
prefix: string;
|
|
17
|
+
user_id: string;
|
|
18
|
+
note: string;
|
|
19
|
+
}>>;
|
|
20
|
+
//# sourceMappingURL=connect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../../../src/api/public/v1/connect.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAsBxD,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;;;;;;;;;;IAwI9C"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /api/public/v1/connect
|
|
3
|
+
* Authenticate with email/password and receive a dev API key
|
|
4
|
+
*/
|
|
5
|
+
import { NextResponse } from "next/server";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { randomUUID, randomBytes } from "crypto";
|
|
8
|
+
import { logger } from "@lastbrain/core";
|
|
9
|
+
import { getSupabaseServiceClient } from "@lastbrain/core/server";
|
|
10
|
+
import { createErrorResponse } from "./_lib/errors";
|
|
11
|
+
import { hashApiKey } from "./_lib/auth";
|
|
12
|
+
const ConnectSchema = z.object({
|
|
13
|
+
email: z.string().email(),
|
|
14
|
+
password: z.string().min(1),
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* Generate a random API key
|
|
18
|
+
*/
|
|
19
|
+
function generateApiKey() {
|
|
20
|
+
const key = `lb_${randomBytes(32).toString("hex")}`;
|
|
21
|
+
const prefix = key.substring(0, 10);
|
|
22
|
+
return { key, prefix };
|
|
23
|
+
}
|
|
24
|
+
export async function POST(request) {
|
|
25
|
+
const requestId = randomUUID();
|
|
26
|
+
let statusCode = 200;
|
|
27
|
+
try {
|
|
28
|
+
// 1. Parse and validate request body
|
|
29
|
+
const body = await request.json();
|
|
30
|
+
const validation = ConnectSchema.safeParse(body);
|
|
31
|
+
if (!validation.success) {
|
|
32
|
+
const { response, status } = createErrorResponse("VALIDATION_ERROR", validation.error.errors.map((e) => e.message).join(", "), requestId, undefined, 400);
|
|
33
|
+
statusCode = status;
|
|
34
|
+
return NextResponse.json(response, { status });
|
|
35
|
+
}
|
|
36
|
+
const { email, password } = validation.data;
|
|
37
|
+
// 2. Authenticate user with Supabase Auth
|
|
38
|
+
const supabase = await getSupabaseServiceClient();
|
|
39
|
+
const { data: authData, error: authError } = await supabase.auth.signInWithPassword({
|
|
40
|
+
email,
|
|
41
|
+
password,
|
|
42
|
+
});
|
|
43
|
+
if (authError || !authData.user) {
|
|
44
|
+
logger.warn("[connect] Authentication failed", {
|
|
45
|
+
email,
|
|
46
|
+
error: authError?.message,
|
|
47
|
+
});
|
|
48
|
+
const { response, status } = createErrorResponse("INVALID_CREDENTIALS", "Invalid email or password", requestId, undefined, 401);
|
|
49
|
+
statusCode = status;
|
|
50
|
+
return NextResponse.json(response, { status });
|
|
51
|
+
}
|
|
52
|
+
const userId = authData.user.id;
|
|
53
|
+
// 3. Check if user already has a dev API key
|
|
54
|
+
const { data: existingKeys } = await supabase
|
|
55
|
+
.from("api_keys")
|
|
56
|
+
.select("*")
|
|
57
|
+
.eq("owner_id", userId)
|
|
58
|
+
.eq("env", "dev")
|
|
59
|
+
.eq("is_active", true)
|
|
60
|
+
.limit(1);
|
|
61
|
+
if (existingKeys && existingKeys.length > 0) {
|
|
62
|
+
// Return existing key info (but not the raw key)
|
|
63
|
+
const existingKey = existingKeys[0];
|
|
64
|
+
return NextResponse.json({
|
|
65
|
+
request_id: requestId,
|
|
66
|
+
message: "You already have an active dev API key. For security reasons, existing keys cannot be retrieved. Please create a new key or use the dashboard to manage your keys.",
|
|
67
|
+
api_key_id: existingKey.id,
|
|
68
|
+
prefix: existingKey.prefix,
|
|
69
|
+
user_id: userId,
|
|
70
|
+
dashboard_url: process.env.NEXT_PUBLIC_APP_URL + "/auth/module-ai/api-keys",
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
// 4. Generate new dev API key
|
|
74
|
+
const { key: rawKey, prefix } = generateApiKey();
|
|
75
|
+
const keyHash = hashApiKey(rawKey);
|
|
76
|
+
const { data: newKey, error: insertError } = await supabase
|
|
77
|
+
.from("api_keys")
|
|
78
|
+
.insert({
|
|
79
|
+
owner_id: userId,
|
|
80
|
+
name: "Dev API Key (auto-created)",
|
|
81
|
+
key_hash: keyHash,
|
|
82
|
+
prefix,
|
|
83
|
+
env: "dev",
|
|
84
|
+
scopes: ["text", "image", "status"],
|
|
85
|
+
rate_limit_rpm: 60,
|
|
86
|
+
daily_token_limit: null,
|
|
87
|
+
is_active: true,
|
|
88
|
+
})
|
|
89
|
+
.select()
|
|
90
|
+
.single();
|
|
91
|
+
if (insertError || !newKey) {
|
|
92
|
+
logger.error("[connect] Failed to create API key", {
|
|
93
|
+
userId,
|
|
94
|
+
error: insertError,
|
|
95
|
+
});
|
|
96
|
+
const { response, status } = createErrorResponse("INTERNAL_ERROR", "Failed to create API key", requestId, undefined, 500);
|
|
97
|
+
statusCode = status;
|
|
98
|
+
return NextResponse.json(response, { status });
|
|
99
|
+
}
|
|
100
|
+
// 5. Return the raw API key (ONLY TIME IT'S SHOWN)
|
|
101
|
+
logger.info("[connect] Created new dev API key", {
|
|
102
|
+
userId,
|
|
103
|
+
keyId: newKey.id,
|
|
104
|
+
});
|
|
105
|
+
return NextResponse.json({
|
|
106
|
+
request_id: requestId,
|
|
107
|
+
api_key: rawKey,
|
|
108
|
+
prefix,
|
|
109
|
+
user_id: userId,
|
|
110
|
+
note: "Store this API key securely. It will not be shown again.",
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
logger.error("[connect] Unexpected error", error);
|
|
115
|
+
statusCode = 500;
|
|
116
|
+
const { response, status } = createErrorResponse("INTERNAL_ERROR", error.message || "Internal server error", requestId, undefined, 500);
|
|
117
|
+
return NextResponse.json(response, { status });
|
|
118
|
+
}
|
|
119
|
+
}
|