@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,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GET /api/ai/public/token-pricing
|
|
3
|
+
* Returns token pricing information for all models
|
|
4
|
+
* Single source of truth for client-side pricing display
|
|
5
|
+
*/
|
|
6
|
+
import { NextResponse } from "next/server";
|
|
7
|
+
import { logger } from "@lastbrain/core";
|
|
8
|
+
import { getSupabaseServiceClient } from "@lastbrain/core/server";
|
|
9
|
+
import { fetchGatewayModels } from "../../server/gateway-service";
|
|
10
|
+
import { computeProviderCostUsd, estimateDebitTokens, } from "../../server/billing";
|
|
11
|
+
export async function GET(request) {
|
|
12
|
+
try {
|
|
13
|
+
// Get pack ID from query params (optional)
|
|
14
|
+
const url = new URL(request.url);
|
|
15
|
+
const packId = url.searchParams.get("packId");
|
|
16
|
+
// Get pack info from database
|
|
17
|
+
const supabase = await getSupabaseServiceClient();
|
|
18
|
+
let packInfo;
|
|
19
|
+
if (packId) {
|
|
20
|
+
const { data: pack } = await supabase
|
|
21
|
+
.from("token_packs")
|
|
22
|
+
.select("tokens, price_cents, currency, pack_coef")
|
|
23
|
+
.eq("id", packId)
|
|
24
|
+
.eq("is_active", true)
|
|
25
|
+
.single();
|
|
26
|
+
if (pack) {
|
|
27
|
+
packInfo = {
|
|
28
|
+
tokens: pack.tokens,
|
|
29
|
+
price_usd: pack.price_cents / 100,
|
|
30
|
+
coef: pack.pack_coef || 1.35, // Use DB value or fallback
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
// Fallback to best pack
|
|
35
|
+
packInfo = await getBestPack(supabase);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
// Use best pack (lowest $/token)
|
|
40
|
+
packInfo = await getBestPack(supabase);
|
|
41
|
+
}
|
|
42
|
+
// Fetch models from gateway (internal, with pricing)
|
|
43
|
+
const gatewayResponse = await fetchGatewayModels();
|
|
44
|
+
// Calculate pricing for all models
|
|
45
|
+
const providersWithPricing = gatewayResponse.providers.map((provider) => {
|
|
46
|
+
const modelsWithPricing = provider.models.map((model) => {
|
|
47
|
+
// Map Gateway types to our internal types
|
|
48
|
+
const normalizedType = model.type === "language" ? "text" : model.type;
|
|
49
|
+
// Only calculate pricing for supported types
|
|
50
|
+
if (normalizedType !== "text" && normalizedType !== "image") {
|
|
51
|
+
return {
|
|
52
|
+
modelId: model.id,
|
|
53
|
+
modelName: model.name,
|
|
54
|
+
type: model.type,
|
|
55
|
+
provider: model.provider,
|
|
56
|
+
estimatedTokens: null,
|
|
57
|
+
displayPrice: "N/A",
|
|
58
|
+
available: false,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const providerPricing = {
|
|
62
|
+
// Gateway prices are in $/token (not $/M)
|
|
63
|
+
inputUsdPerToken: model.pricing?.input
|
|
64
|
+
? parseFloat(model.pricing.input.toString())
|
|
65
|
+
: undefined,
|
|
66
|
+
outputUsdPerToken: model.pricing?.output
|
|
67
|
+
? parseFloat(model.pricing.output.toString())
|
|
68
|
+
: undefined,
|
|
69
|
+
imageUsdPerImage: model.pricing?.image
|
|
70
|
+
? parseFloat(model.pricing.image.toString())
|
|
71
|
+
: undefined,
|
|
72
|
+
};
|
|
73
|
+
let estimatedTokens = null;
|
|
74
|
+
let displayPrice = "N/A";
|
|
75
|
+
let inputSellUsdPerMToken;
|
|
76
|
+
let outputSellUsdPerMToken;
|
|
77
|
+
if (normalizedType === "text") {
|
|
78
|
+
// Estimate for 500K input + 500K output
|
|
79
|
+
const usage = {
|
|
80
|
+
inputTokens: 500_000,
|
|
81
|
+
outputTokens: 500_000,
|
|
82
|
+
};
|
|
83
|
+
const providerCost = computeProviderCostUsd(usage, providerPricing);
|
|
84
|
+
if (providerCost > 0) {
|
|
85
|
+
estimatedTokens = estimateDebitTokens(providerCost, packInfo.coef, packInfo.tokens, packInfo.price_usd);
|
|
86
|
+
displayPrice = `~${estimatedTokens.toLocaleString()} tokens/1M`;
|
|
87
|
+
// Sell prices with markup
|
|
88
|
+
if (providerPricing.inputUsdPerToken) {
|
|
89
|
+
inputSellUsdPerMToken =
|
|
90
|
+
providerPricing.inputUsdPerToken * 1_000_000 * packInfo.coef;
|
|
91
|
+
}
|
|
92
|
+
if (providerPricing.outputUsdPerToken) {
|
|
93
|
+
outputSellUsdPerMToken =
|
|
94
|
+
providerPricing.outputUsdPerToken * 1_000_000 * packInfo.coef;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else if (normalizedType === "image") {
|
|
99
|
+
// Estimate for 1 image
|
|
100
|
+
const usage = { images: 1 };
|
|
101
|
+
const providerCost = computeProviderCostUsd(usage, providerPricing);
|
|
102
|
+
if (providerCost > 0) {
|
|
103
|
+
estimatedTokens = estimateDebitTokens(providerCost, packInfo.coef, packInfo.tokens, packInfo.price_usd);
|
|
104
|
+
displayPrice = `${estimatedTokens.toLocaleString()} tokens/img`;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
modelId: model.id,
|
|
109
|
+
modelName: model.name,
|
|
110
|
+
type: model.type,
|
|
111
|
+
provider: model.provider,
|
|
112
|
+
estimatedTokens,
|
|
113
|
+
displayPrice,
|
|
114
|
+
available: estimatedTokens !== null,
|
|
115
|
+
inputSellUsdPerMToken,
|
|
116
|
+
outputSellUsdPerMToken,
|
|
117
|
+
imageSellUsd: providerPricing.imageUsdPerImage
|
|
118
|
+
? providerPricing.imageUsdPerImage * packInfo.coef
|
|
119
|
+
: undefined,
|
|
120
|
+
};
|
|
121
|
+
});
|
|
122
|
+
return {
|
|
123
|
+
name: provider.name,
|
|
124
|
+
display_name: provider.display_name,
|
|
125
|
+
models: modelsWithPricing,
|
|
126
|
+
};
|
|
127
|
+
});
|
|
128
|
+
return NextResponse.json({
|
|
129
|
+
config: {
|
|
130
|
+
packTokens: packInfo.tokens,
|
|
131
|
+
packPriceUsd: packInfo.price_usd,
|
|
132
|
+
tokenPriceUsd: packInfo.price_usd / packInfo.tokens,
|
|
133
|
+
markupCoef: packInfo.coef,
|
|
134
|
+
},
|
|
135
|
+
providers: providersWithPricing,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
logger.error("[public/token-pricing] Error:", error);
|
|
140
|
+
return NextResponse.json({ error: error.message || "Internal server error" }, { status: 500 });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Helper to get best pack (lowest $/token)
|
|
144
|
+
async function getBestPack(supabase) {
|
|
145
|
+
const { data: packs } = await supabase
|
|
146
|
+
.from("token_packs")
|
|
147
|
+
.select("tokens, price_cents, pack_coef")
|
|
148
|
+
.eq("is_active", true);
|
|
149
|
+
if (!packs || packs.length === 0) {
|
|
150
|
+
// Fallback
|
|
151
|
+
return { tokens: 2_000_000, price_usd: 50, coef: 1.35 };
|
|
152
|
+
}
|
|
153
|
+
// Find pack with lowest $/token
|
|
154
|
+
let bestPack = packs[0];
|
|
155
|
+
let bestRate = packs[0].price_cents / 100 / packs[0].tokens;
|
|
156
|
+
for (const pack of packs) {
|
|
157
|
+
const rate = pack.price_cents / 100 / pack.tokens;
|
|
158
|
+
if (rate < bestRate) {
|
|
159
|
+
bestRate = rate;
|
|
160
|
+
bestPack = pack;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
tokens: bestPack.tokens,
|
|
165
|
+
price_usd: bestPack.price_cents / 100,
|
|
166
|
+
coef: bestPack.pack_coef || 1.35, // Use DB value or fallback
|
|
167
|
+
};
|
|
168
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper functions for AI artifacts storage
|
|
3
|
+
*/
|
|
4
|
+
interface ApiKey {
|
|
5
|
+
id: string;
|
|
6
|
+
owner_id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
}
|
|
9
|
+
interface ArtifactOptions {
|
|
10
|
+
apiKey: ApiKey;
|
|
11
|
+
kind: "text" | "image" | "pdf" | "file";
|
|
12
|
+
endpoint: string;
|
|
13
|
+
provider?: string | null;
|
|
14
|
+
model?: string | null;
|
|
15
|
+
tokensTotal?: number | null;
|
|
16
|
+
statusCode?: number;
|
|
17
|
+
textContent?: string;
|
|
18
|
+
fileBuffer?: Buffer;
|
|
19
|
+
mimeType?: string;
|
|
20
|
+
ext?: string;
|
|
21
|
+
meta?: Record<string, any>;
|
|
22
|
+
}
|
|
23
|
+
interface StorageResult {
|
|
24
|
+
success: boolean;
|
|
25
|
+
artifactId?: string;
|
|
26
|
+
storagePath?: string;
|
|
27
|
+
error?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Check if user has store_outputs enabled
|
|
31
|
+
*/
|
|
32
|
+
export declare function shouldStoreOutputs(ownerId: string, overrideStoreOutputs?: boolean): Promise<boolean>;
|
|
33
|
+
/**
|
|
34
|
+
* Create artifact record in database
|
|
35
|
+
*/
|
|
36
|
+
export declare function createArtifact(options: ArtifactOptions): Promise<StorageResult>;
|
|
37
|
+
/**
|
|
38
|
+
* Generate signed URL for file download
|
|
39
|
+
*/
|
|
40
|
+
export declare function getSignedDownloadUrl(storagePath: string, expiresIn?: number): Promise<{
|
|
41
|
+
url: string | null;
|
|
42
|
+
error?: string;
|
|
43
|
+
}>;
|
|
44
|
+
/**
|
|
45
|
+
* Delete artifact and its storage file
|
|
46
|
+
*/
|
|
47
|
+
export declare function deleteArtifact(artifactId: string, ownerId: string): Promise<{
|
|
48
|
+
success: boolean;
|
|
49
|
+
error?: string;
|
|
50
|
+
}>;
|
|
51
|
+
export {};
|
|
52
|
+
//# sourceMappingURL=artifacts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifacts.d.ts","sourceRoot":"","sources":["../../../../../src/api/public/v1/_lib/artifacts.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,UAAU,MAAM;IACd,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,eAAe;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC5B;AAED,UAAU,aAAa;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAuBD;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,EACf,oBAAoB,CAAC,EAAE,OAAO,GAC7B,OAAO,CAAC,OAAO,CAAC,CA8BlB;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,aAAa,CAAC,CA+GxB;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,WAAW,EAAE,MAAM,EACnB,SAAS,SAAK,GACb,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAqBjD;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAgD/C"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper functions for AI artifacts storage
|
|
3
|
+
*/
|
|
4
|
+
import { logger } from "@lastbrain/core";
|
|
5
|
+
import { getSupabaseServiceClient } from "@lastbrain/core/server";
|
|
6
|
+
/**
|
|
7
|
+
* Sanitize API key name for storage path
|
|
8
|
+
*/
|
|
9
|
+
function sanitizeApiKeyName(name) {
|
|
10
|
+
return name
|
|
11
|
+
.toLowerCase()
|
|
12
|
+
.replace(/[^a-z0-9-]/g, "-")
|
|
13
|
+
.replace(/-+/g, "-")
|
|
14
|
+
.substring(0, 50);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Get month-year folder (mm-yyyy)
|
|
18
|
+
*/
|
|
19
|
+
function getMonthYearFolder() {
|
|
20
|
+
const now = new Date();
|
|
21
|
+
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
22
|
+
const year = now.getFullYear();
|
|
23
|
+
return `${month}-${year}`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Check if user has store_outputs enabled
|
|
27
|
+
*/
|
|
28
|
+
export async function shouldStoreOutputs(ownerId, overrideStoreOutputs) {
|
|
29
|
+
// If explicitly provided in request, use that value
|
|
30
|
+
if (overrideStoreOutputs !== undefined) {
|
|
31
|
+
return overrideStoreOutputs;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const supabase = getSupabaseServiceClient();
|
|
35
|
+
const { data, error } = await supabase
|
|
36
|
+
.from("user_ai_settings")
|
|
37
|
+
.select("store_outputs")
|
|
38
|
+
.eq("owner_id", ownerId)
|
|
39
|
+
.single();
|
|
40
|
+
if (error || !data) {
|
|
41
|
+
logger.debug("[artifacts] No settings found for user, defaulting to false", {
|
|
42
|
+
ownerId,
|
|
43
|
+
});
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
return data.store_outputs === true;
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
logger.error("[artifacts] Error checking store_outputs", error);
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Create artifact record in database
|
|
55
|
+
*/
|
|
56
|
+
export async function createArtifact(options) {
|
|
57
|
+
const { apiKey, kind, endpoint, provider, model, tokensTotal, statusCode = 200, textContent, fileBuffer, mimeType, ext, meta = {}, } = options;
|
|
58
|
+
try {
|
|
59
|
+
const supabase = getSupabaseServiceClient();
|
|
60
|
+
const artifactId = crypto.randomUUID();
|
|
61
|
+
// Sanitize API key name for path
|
|
62
|
+
const apiKeyName = apiKey.name
|
|
63
|
+
? sanitizeApiKeyName(apiKey.name)
|
|
64
|
+
: "default";
|
|
65
|
+
const monthYear = getMonthYearFolder();
|
|
66
|
+
let storagePath = null;
|
|
67
|
+
let sizeBytes = null;
|
|
68
|
+
// Handle file storage (image, pdf, file)
|
|
69
|
+
if (kind !== "text" && fileBuffer) {
|
|
70
|
+
const extension = ext || "bin";
|
|
71
|
+
storagePath = `${apiKey.owner_id}/${apiKeyName}/${monthYear}/${artifactId}.${extension}`;
|
|
72
|
+
try {
|
|
73
|
+
const { error: uploadError } = await supabase.storage
|
|
74
|
+
.from("app")
|
|
75
|
+
.upload(storagePath, fileBuffer, {
|
|
76
|
+
contentType: mimeType || "application/octet-stream",
|
|
77
|
+
upsert: false,
|
|
78
|
+
});
|
|
79
|
+
if (uploadError) {
|
|
80
|
+
logger.error("[artifacts] Storage upload failed", {
|
|
81
|
+
error: uploadError,
|
|
82
|
+
path: storagePath,
|
|
83
|
+
});
|
|
84
|
+
// Continue anyway, will store metadata without file
|
|
85
|
+
storagePath = null;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
sizeBytes = fileBuffer.length;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch (uploadError) {
|
|
92
|
+
logger.error("[artifacts] Storage upload exception", uploadError);
|
|
93
|
+
storagePath = null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Insert artifact record
|
|
97
|
+
const { data, error } = await supabase
|
|
98
|
+
.from("ai_artifacts")
|
|
99
|
+
.insert({
|
|
100
|
+
id: artifactId,
|
|
101
|
+
owner_id: apiKey.owner_id,
|
|
102
|
+
api_key_id: apiKey.id,
|
|
103
|
+
api_key_name: apiKeyName,
|
|
104
|
+
kind,
|
|
105
|
+
provider,
|
|
106
|
+
model,
|
|
107
|
+
endpoint,
|
|
108
|
+
tokens_total: tokensTotal,
|
|
109
|
+
status_code: statusCode,
|
|
110
|
+
text_content: kind === "text" ? textContent : null,
|
|
111
|
+
storage_bucket: storagePath ? "app" : null,
|
|
112
|
+
storage_path: storagePath,
|
|
113
|
+
mime_type: mimeType || null,
|
|
114
|
+
size_bytes: sizeBytes,
|
|
115
|
+
meta,
|
|
116
|
+
})
|
|
117
|
+
.select("id, storage_path")
|
|
118
|
+
.single();
|
|
119
|
+
if (error) {
|
|
120
|
+
logger.error("[artifacts] Failed to insert artifact", {
|
|
121
|
+
error,
|
|
122
|
+
artifactId,
|
|
123
|
+
});
|
|
124
|
+
return {
|
|
125
|
+
success: false,
|
|
126
|
+
error: error.message,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
logger.info("[artifacts] Artifact created", {
|
|
130
|
+
artifactId,
|
|
131
|
+
kind,
|
|
132
|
+
storagePath,
|
|
133
|
+
});
|
|
134
|
+
return {
|
|
135
|
+
success: true,
|
|
136
|
+
artifactId: data.id,
|
|
137
|
+
storagePath: data.storage_path || undefined,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
logger.error("[artifacts] Unexpected error creating artifact", error);
|
|
142
|
+
return {
|
|
143
|
+
success: false,
|
|
144
|
+
error: error.message,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Generate signed URL for file download
|
|
150
|
+
*/
|
|
151
|
+
export async function getSignedDownloadUrl(storagePath, expiresIn = 60) {
|
|
152
|
+
try {
|
|
153
|
+
const supabase = getSupabaseServiceClient();
|
|
154
|
+
const { data, error } = await supabase.storage
|
|
155
|
+
.from("app")
|
|
156
|
+
.createSignedUrl(storagePath, expiresIn);
|
|
157
|
+
if (error) {
|
|
158
|
+
logger.error("[artifacts] Failed to generate signed URL", {
|
|
159
|
+
error,
|
|
160
|
+
path: storagePath,
|
|
161
|
+
});
|
|
162
|
+
return { url: null, error: error.message };
|
|
163
|
+
}
|
|
164
|
+
return { url: data.signedUrl };
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
logger.error("[artifacts] Exception generating signed URL", error);
|
|
168
|
+
return { url: null, error: error.message };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Delete artifact and its storage file
|
|
173
|
+
*/
|
|
174
|
+
export async function deleteArtifact(artifactId, ownerId) {
|
|
175
|
+
try {
|
|
176
|
+
const supabase = getSupabaseServiceClient();
|
|
177
|
+
// Get artifact first to check ownership and get storage path
|
|
178
|
+
const { data: artifact, error: fetchError } = await supabase
|
|
179
|
+
.from("ai_artifacts")
|
|
180
|
+
.select("owner_id, storage_path, storage_bucket")
|
|
181
|
+
.eq("id", artifactId)
|
|
182
|
+
.single();
|
|
183
|
+
if (fetchError || !artifact) {
|
|
184
|
+
return { success: false, error: "Artifact not found" };
|
|
185
|
+
}
|
|
186
|
+
if (artifact.owner_id !== ownerId) {
|
|
187
|
+
return { success: false, error: "Unauthorized" };
|
|
188
|
+
}
|
|
189
|
+
// Delete file from storage if exists
|
|
190
|
+
if (artifact.storage_path && artifact.storage_bucket) {
|
|
191
|
+
try {
|
|
192
|
+
await supabase.storage
|
|
193
|
+
.from(artifact.storage_bucket)
|
|
194
|
+
.remove([artifact.storage_path]);
|
|
195
|
+
}
|
|
196
|
+
catch (storageError) {
|
|
197
|
+
logger.error("[artifacts] Failed to delete storage file", storageError);
|
|
198
|
+
// Continue anyway to delete DB record
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Delete database record
|
|
202
|
+
const { error: deleteError } = await supabase
|
|
203
|
+
.from("ai_artifacts")
|
|
204
|
+
.delete()
|
|
205
|
+
.eq("id", artifactId);
|
|
206
|
+
if (deleteError) {
|
|
207
|
+
logger.error("[artifacts] Failed to delete artifact record", deleteError);
|
|
208
|
+
return { success: false, error: deleteError.message };
|
|
209
|
+
}
|
|
210
|
+
logger.info("[artifacts] Artifact deleted", { artifactId });
|
|
211
|
+
return { success: true };
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
logger.error("[artifacts] Exception deleting artifact", error);
|
|
215
|
+
return { success: false, error: error.message };
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication and authorization helpers for public API v1
|
|
3
|
+
*/
|
|
4
|
+
export interface ApiKeyData {
|
|
5
|
+
id: string;
|
|
6
|
+
owner_id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
prefix: string;
|
|
9
|
+
env: string;
|
|
10
|
+
scopes: string[];
|
|
11
|
+
rate_limit_rpm: number;
|
|
12
|
+
daily_token_limit: bigint | null;
|
|
13
|
+
is_active: boolean;
|
|
14
|
+
last_used_at: string | null;
|
|
15
|
+
created_at: string;
|
|
16
|
+
}
|
|
17
|
+
export interface ValidateApiKeyResult {
|
|
18
|
+
success: boolean;
|
|
19
|
+
error?: string;
|
|
20
|
+
errorCode?: string;
|
|
21
|
+
apiKey?: ApiKeyData;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Hash API key for lookup
|
|
25
|
+
*/
|
|
26
|
+
export declare function hashApiKey(apiKey: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Validate API key from Authorization header
|
|
29
|
+
*/
|
|
30
|
+
export declare function validateApiKey(authHeader: string | null): Promise<ValidateApiKeyResult>;
|
|
31
|
+
/**
|
|
32
|
+
* Check if API key has required scope
|
|
33
|
+
*/
|
|
34
|
+
export declare function hasScope(apiKey: ApiKeyData, requiredScope: string): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Verify API key has required scope, returns error if not
|
|
37
|
+
*/
|
|
38
|
+
export declare function verifyScope(apiKey: ApiKeyData, requiredScope: string): {
|
|
39
|
+
success: boolean;
|
|
40
|
+
error?: string;
|
|
41
|
+
errorCode?: string;
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../../../src/api/public/v1/_lib/auth.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,UAAU,EAAE,MAAM,GAAG,IAAI,GACxB,OAAO,CAAC,oBAAoB,CAAC,CAgF/B;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAE3E;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,UAAU,EAClB,aAAa,EAAE,MAAM,GACpB;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAS1D"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication and authorization helpers for public API v1
|
|
3
|
+
*/
|
|
4
|
+
import { getSupabaseServiceClient } from "@lastbrain/core/server";
|
|
5
|
+
import { logger } from "@lastbrain/core";
|
|
6
|
+
import { createHash } from "crypto";
|
|
7
|
+
/**
|
|
8
|
+
* Hash API key for lookup
|
|
9
|
+
*/
|
|
10
|
+
export function hashApiKey(apiKey) {
|
|
11
|
+
return createHash("sha256").update(apiKey).digest("hex");
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Validate API key from Authorization header
|
|
15
|
+
*/
|
|
16
|
+
export async function validateApiKey(authHeader) {
|
|
17
|
+
if (!authHeader) {
|
|
18
|
+
return {
|
|
19
|
+
success: false,
|
|
20
|
+
error: "Missing Authorization header",
|
|
21
|
+
errorCode: "MISSING_API_KEY",
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const parts = authHeader.split(" ");
|
|
25
|
+
if (parts.length !== 2 || parts[0] !== "Bearer") {
|
|
26
|
+
return {
|
|
27
|
+
success: false,
|
|
28
|
+
error: "Invalid Authorization header format. Expected: Bearer <api_key>",
|
|
29
|
+
errorCode: "INVALID_API_KEY",
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const rawKey = parts[1];
|
|
33
|
+
if (!rawKey || rawKey.length < 32) {
|
|
34
|
+
return {
|
|
35
|
+
success: false,
|
|
36
|
+
error: "Invalid API key format",
|
|
37
|
+
errorCode: "INVALID_API_KEY",
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
const keyHash = hashApiKey(rawKey);
|
|
42
|
+
const supabase = await getSupabaseServiceClient();
|
|
43
|
+
const { data: apiKey, error } = await supabase
|
|
44
|
+
.from("api_keys")
|
|
45
|
+
.select("*")
|
|
46
|
+
.eq("key_hash", keyHash)
|
|
47
|
+
.single();
|
|
48
|
+
if (error || !apiKey) {
|
|
49
|
+
logger.warn("[validateApiKey] API key not found", { keyHash });
|
|
50
|
+
return {
|
|
51
|
+
success: false,
|
|
52
|
+
error: "Invalid API key",
|
|
53
|
+
errorCode: "INVALID_API_KEY",
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if (!apiKey.is_active) {
|
|
57
|
+
logger.warn("[validateApiKey] API key is inactive", {
|
|
58
|
+
id: apiKey.id,
|
|
59
|
+
owner_id: apiKey.owner_id,
|
|
60
|
+
});
|
|
61
|
+
return {
|
|
62
|
+
success: false,
|
|
63
|
+
error: "API key is inactive",
|
|
64
|
+
errorCode: "INACTIVE_API_KEY",
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// Parse scopes from JSONB
|
|
68
|
+
const scopes = Array.isArray(apiKey.scopes)
|
|
69
|
+
? apiKey.scopes
|
|
70
|
+
: typeof apiKey.scopes === "string"
|
|
71
|
+
? JSON.parse(apiKey.scopes)
|
|
72
|
+
: [];
|
|
73
|
+
return {
|
|
74
|
+
success: true,
|
|
75
|
+
apiKey: {
|
|
76
|
+
...apiKey,
|
|
77
|
+
scopes,
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
logger.error("[validateApiKey] Error validating API key", error);
|
|
83
|
+
return {
|
|
84
|
+
success: false,
|
|
85
|
+
error: "Internal error validating API key",
|
|
86
|
+
errorCode: "INTERNAL_ERROR",
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Check if API key has required scope
|
|
92
|
+
*/
|
|
93
|
+
export function hasScope(apiKey, requiredScope) {
|
|
94
|
+
return apiKey.scopes.includes(requiredScope);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Verify API key has required scope, returns error if not
|
|
98
|
+
*/
|
|
99
|
+
export function verifyScope(apiKey, requiredScope) {
|
|
100
|
+
if (!hasScope(apiKey, requiredScope)) {
|
|
101
|
+
return {
|
|
102
|
+
success: false,
|
|
103
|
+
error: `Missing required scope: ${requiredScope}`,
|
|
104
|
+
errorCode: "INSUFFICIENT_SCOPE",
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return { success: true };
|
|
108
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard error codes and JSON error responses for public API v1
|
|
3
|
+
*/
|
|
4
|
+
export type ErrorCode = "MISSING_API_KEY" | "INVALID_API_KEY" | "INACTIVE_API_KEY" | "INSUFFICIENT_SCOPE" | "INSUFFICIENT_TOKENS" | "RATE_LIMIT_EXCEEDED" | "DAILY_LIMIT_EXCEEDED" | "VALIDATION_ERROR" | "PROVIDER_ERROR" | "INTERNAL_ERROR" | "INVALID_CREDENTIALS" | "USER_NOT_FOUND";
|
|
5
|
+
export interface ApiError {
|
|
6
|
+
error: {
|
|
7
|
+
code: ErrorCode;
|
|
8
|
+
message: string;
|
|
9
|
+
request_id: string;
|
|
10
|
+
action_url?: string;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export declare function createErrorResponse(code: ErrorCode, message: string, requestId: string, actionUrl?: string, status?: number): {
|
|
14
|
+
response: ApiError;
|
|
15
|
+
status: number;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../../../src/api/public/v1/_lib/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,SAAS,GACjB,iBAAiB,GACjB,iBAAiB,GACjB,kBAAkB,GAClB,oBAAoB,GACpB,qBAAqB,GACrB,qBAAqB,GACrB,sBAAsB,GACtB,kBAAkB,GAClB,gBAAgB,GAChB,gBAAgB,GAChB,qBAAqB,GACrB,gBAAgB,CAAC;AAErB,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE;QACL,IAAI,EAAE,SAAS,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,EAClB,MAAM,GAAE,MAAY,GACnB;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAYxC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard error codes and JSON error responses for public API v1
|
|
3
|
+
*/
|
|
4
|
+
export function createErrorResponse(code, message, requestId, actionUrl, status = 400) {
|
|
5
|
+
return {
|
|
6
|
+
response: {
|
|
7
|
+
error: {
|
|
8
|
+
code,
|
|
9
|
+
message,
|
|
10
|
+
request_id: requestId,
|
|
11
|
+
...(actionUrl && { action_url: actionUrl }),
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
status,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging utilities for public API v1
|
|
3
|
+
* Handles ai_call_log inserts and api_keys.last_used_at updates
|
|
4
|
+
*/
|
|
5
|
+
import type { ApiKeyData } from "./auth";
|
|
6
|
+
export interface LogCallParams {
|
|
7
|
+
apiKeyId: string;
|
|
8
|
+
ownerId: string;
|
|
9
|
+
endpoint: string;
|
|
10
|
+
provider: string | null;
|
|
11
|
+
model: string | null;
|
|
12
|
+
tokensIn: number | null;
|
|
13
|
+
tokensOut: number | null;
|
|
14
|
+
tokensTotal: number | null;
|
|
15
|
+
latencyMs: number | null;
|
|
16
|
+
statusCode: number;
|
|
17
|
+
errorCode: string | null;
|
|
18
|
+
requestId: string;
|
|
19
|
+
meta?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Insert a log entry in ai_call_log
|
|
23
|
+
*/
|
|
24
|
+
export declare function logApiCall(params: LogCallParams): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Update api_keys.last_used_at
|
|
27
|
+
*/
|
|
28
|
+
export declare function updateApiKeyLastUsed(apiKey: ApiKeyData): Promise<void>;
|
|
29
|
+
//# sourceMappingURL=log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../../../../src/api/public/v1/_lib/log.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEzC,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAqCrE;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB5E"}
|