@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,151 @@
|
|
|
1
|
+
-- ============================================================================
|
|
2
|
+
-- AI User Prompts System
|
|
3
|
+
-- ============================================================================
|
|
4
|
+
|
|
5
|
+
-- Table: user_prompt
|
|
6
|
+
-- Store user's saved prompts (private or public)
|
|
7
|
+
CREATE TABLE IF NOT EXISTS user_prompt (
|
|
8
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
9
|
+
owner_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
10
|
+
title TEXT NOT NULL,
|
|
11
|
+
content TEXT NOT NULL,
|
|
12
|
+
type TEXT NOT NULL CHECK (type IN ('text', 'image')),
|
|
13
|
+
is_public BOOLEAN NOT NULL DEFAULT false,
|
|
14
|
+
favorite BOOLEAN NOT NULL DEFAULT false,
|
|
15
|
+
tags TEXT[] DEFAULT '{}',
|
|
16
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
17
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
-- Indexes
|
|
21
|
+
CREATE INDEX idx_user_prompt_owner ON user_prompt(owner_id);
|
|
22
|
+
CREATE INDEX idx_user_prompt_type ON user_prompt(type);
|
|
23
|
+
CREATE INDEX idx_user_prompt_public ON user_prompt(is_public) WHERE is_public = true;
|
|
24
|
+
CREATE INDEX idx_user_prompt_favorite ON user_prompt(owner_id, favorite) WHERE favorite = true;
|
|
25
|
+
CREATE INDEX idx_user_prompt_tags ON user_prompt USING GIN(tags);
|
|
26
|
+
|
|
27
|
+
-- RLS
|
|
28
|
+
ALTER TABLE user_prompt ENABLE ROW LEVEL SECURITY;
|
|
29
|
+
|
|
30
|
+
-- Policy: Superadmin can do everything
|
|
31
|
+
CREATE POLICY "superadmin_all_user_prompt" ON user_prompt
|
|
32
|
+
FOR ALL
|
|
33
|
+
USING (is_superadmin(auth.uid()));
|
|
34
|
+
|
|
35
|
+
-- Policy: Users can view their own prompts
|
|
36
|
+
CREATE POLICY "user_select_own_prompts" ON user_prompt
|
|
37
|
+
FOR SELECT
|
|
38
|
+
USING (owner_id = auth.uid());
|
|
39
|
+
|
|
40
|
+
-- Policy: Users can view public prompts
|
|
41
|
+
CREATE POLICY "user_select_public_prompts" ON user_prompt
|
|
42
|
+
FOR SELECT
|
|
43
|
+
USING (is_public = true);
|
|
44
|
+
|
|
45
|
+
-- Policy: Users can insert their own prompts
|
|
46
|
+
CREATE POLICY "user_insert_own_prompts" ON user_prompt
|
|
47
|
+
FOR INSERT
|
|
48
|
+
WITH CHECK (owner_id = auth.uid());
|
|
49
|
+
|
|
50
|
+
-- Policy: Users can update their own prompts
|
|
51
|
+
CREATE POLICY "user_update_own_prompts" ON user_prompt
|
|
52
|
+
FOR UPDATE
|
|
53
|
+
USING (owner_id = auth.uid())
|
|
54
|
+
WITH CHECK (owner_id = auth.uid());
|
|
55
|
+
|
|
56
|
+
-- Policy: Users can delete their own prompts
|
|
57
|
+
CREATE POLICY "user_delete_own_prompts" ON user_prompt
|
|
58
|
+
FOR DELETE
|
|
59
|
+
USING (owner_id = auth.uid());
|
|
60
|
+
|
|
61
|
+
-- Table: prompt_stats
|
|
62
|
+
-- Statistics for public prompts
|
|
63
|
+
CREATE TABLE IF NOT EXISTS prompt_stats (
|
|
64
|
+
prompt_id UUID PRIMARY KEY REFERENCES user_prompt(id) ON DELETE CASCADE,
|
|
65
|
+
views INTEGER NOT NULL DEFAULT 0,
|
|
66
|
+
used_count INTEGER NOT NULL DEFAULT 0,
|
|
67
|
+
picked_count INTEGER NOT NULL DEFAULT 0,
|
|
68
|
+
last_used_at TIMESTAMPTZ,
|
|
69
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
70
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
-- RLS for prompt_stats (read-only for everyone, write for superadmin)
|
|
74
|
+
ALTER TABLE prompt_stats ENABLE ROW LEVEL SECURITY;
|
|
75
|
+
|
|
76
|
+
CREATE POLICY "anyone_select_prompt_stats" ON prompt_stats
|
|
77
|
+
FOR SELECT
|
|
78
|
+
USING (true);
|
|
79
|
+
|
|
80
|
+
CREATE POLICY "superadmin_all_prompt_stats" ON prompt_stats
|
|
81
|
+
FOR ALL
|
|
82
|
+
USING (is_superadmin(auth.uid()));
|
|
83
|
+
|
|
84
|
+
-- View: public_prompts_with_stats
|
|
85
|
+
-- Public prompts with their stats
|
|
86
|
+
CREATE OR REPLACE VIEW public_prompts_with_stats AS
|
|
87
|
+
SELECT
|
|
88
|
+
p.id,
|
|
89
|
+
p.owner_id,
|
|
90
|
+
p.title,
|
|
91
|
+
p.content,
|
|
92
|
+
p.type,
|
|
93
|
+
p.tags,
|
|
94
|
+
p.created_at,
|
|
95
|
+
COALESCE(s.views, 0) as views,
|
|
96
|
+
COALESCE(s.used_count, 0) as used_count,
|
|
97
|
+
COALESCE(s.picked_count, 0) as picked_count,
|
|
98
|
+
s.last_used_at
|
|
99
|
+
FROM user_prompt p
|
|
100
|
+
LEFT JOIN prompt_stats s ON s.prompt_id = p.id
|
|
101
|
+
WHERE p.is_public = true
|
|
102
|
+
ORDER BY s.picked_count DESC NULLS LAST, p.created_at DESC;
|
|
103
|
+
|
|
104
|
+
-- Grant access to the view
|
|
105
|
+
GRANT SELECT ON public_prompts_with_stats TO authenticated, anon;
|
|
106
|
+
|
|
107
|
+
-- Function: increment_prompt_stat
|
|
108
|
+
-- Increment a specific stat counter
|
|
109
|
+
CREATE OR REPLACE FUNCTION increment_prompt_stat(
|
|
110
|
+
p_prompt_id UUID,
|
|
111
|
+
p_stat_type TEXT
|
|
112
|
+
)
|
|
113
|
+
RETURNS void
|
|
114
|
+
LANGUAGE plpgsql
|
|
115
|
+
SECURITY DEFINER
|
|
116
|
+
AS $$
|
|
117
|
+
BEGIN
|
|
118
|
+
-- Insert or update stats
|
|
119
|
+
INSERT INTO prompt_stats (prompt_id, views, used_count, picked_count, last_used_at)
|
|
120
|
+
VALUES (
|
|
121
|
+
p_prompt_id,
|
|
122
|
+
CASE WHEN p_stat_type = 'view' THEN 1 ELSE 0 END,
|
|
123
|
+
CASE WHEN p_stat_type = 'used' THEN 1 ELSE 0 END,
|
|
124
|
+
CASE WHEN p_stat_type = 'picked' THEN 1 ELSE 0 END,
|
|
125
|
+
CASE WHEN p_stat_type IN ('used', 'picked') THEN now() ELSE NULL END
|
|
126
|
+
)
|
|
127
|
+
ON CONFLICT (prompt_id) DO UPDATE SET
|
|
128
|
+
views = prompt_stats.views + CASE WHEN p_stat_type = 'view' THEN 1 ELSE 0 END,
|
|
129
|
+
used_count = prompt_stats.used_count + CASE WHEN p_stat_type = 'used' THEN 1 ELSE 0 END,
|
|
130
|
+
picked_count = prompt_stats.picked_count + CASE WHEN p_stat_type = 'picked' THEN 1 ELSE 0 END,
|
|
131
|
+
last_used_at = CASE WHEN p_stat_type IN ('used', 'picked') THEN now() ELSE prompt_stats.last_used_at END,
|
|
132
|
+
updated_at = now();
|
|
133
|
+
END;
|
|
134
|
+
$$;
|
|
135
|
+
|
|
136
|
+
-- Grant execute to authenticated and anon users
|
|
137
|
+
GRANT EXECUTE ON FUNCTION increment_prompt_stat TO authenticated, anon;
|
|
138
|
+
|
|
139
|
+
-- Trigger: update_user_prompt_updated_at
|
|
140
|
+
CREATE OR REPLACE FUNCTION update_user_prompt_updated_at()
|
|
141
|
+
RETURNS TRIGGER AS $$
|
|
142
|
+
BEGIN
|
|
143
|
+
NEW.updated_at = now();
|
|
144
|
+
RETURN NEW;
|
|
145
|
+
END;
|
|
146
|
+
$$ LANGUAGE plpgsql;
|
|
147
|
+
|
|
148
|
+
CREATE TRIGGER trigger_update_user_prompt_updated_at
|
|
149
|
+
BEFORE UPDATE ON user_prompt
|
|
150
|
+
FOR EACH ROW
|
|
151
|
+
EXECUTE FUNCTION update_user_prompt_updated_at();
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
-- ============================================================================
|
|
2
|
+
-- AI Prompts IP Tracking System
|
|
3
|
+
-- Add IP-based tracking to prevent duplicate stat increments
|
|
4
|
+
-- ============================================================================
|
|
5
|
+
|
|
6
|
+
-- Table: prompt_stat_tracking
|
|
7
|
+
-- Track which IPs have already incremented specific stats
|
|
8
|
+
CREATE TABLE IF NOT EXISTS prompt_stat_tracking (
|
|
9
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
10
|
+
prompt_id UUID NOT NULL REFERENCES user_prompt(id) ON DELETE CASCADE,
|
|
11
|
+
ip_address TEXT NOT NULL,
|
|
12
|
+
stat_type TEXT NOT NULL CHECK (stat_type IN ('views', 'picked', 'used')),
|
|
13
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
-- Indexes
|
|
17
|
+
CREATE INDEX idx_prompt_stat_tracking_lookup ON prompt_stat_tracking(prompt_id, ip_address, stat_type);
|
|
18
|
+
CREATE INDEX idx_prompt_stat_tracking_cleanup ON prompt_stat_tracking(created_at);
|
|
19
|
+
|
|
20
|
+
-- RLS (service role only)
|
|
21
|
+
ALTER TABLE prompt_stat_tracking ENABLE ROW LEVEL SECURITY;
|
|
22
|
+
|
|
23
|
+
CREATE POLICY "service_all_prompt_stat_tracking" ON prompt_stat_tracking
|
|
24
|
+
FOR ALL
|
|
25
|
+
USING (is_superadmin(auth.uid()));
|
|
26
|
+
|
|
27
|
+
-- Function: increment_prompt_stat_with_ip
|
|
28
|
+
-- Increment stat only if IP hasn't already done so (with 24h cooldown)
|
|
29
|
+
CREATE OR REPLACE FUNCTION increment_prompt_stat_with_ip(
|
|
30
|
+
p_prompt_id UUID,
|
|
31
|
+
p_stat_type TEXT,
|
|
32
|
+
p_ip_address TEXT
|
|
33
|
+
)
|
|
34
|
+
RETURNS BOOLEAN
|
|
35
|
+
LANGUAGE plpgsql
|
|
36
|
+
SECURITY DEFINER
|
|
37
|
+
AS $$
|
|
38
|
+
DECLARE
|
|
39
|
+
v_already_tracked BOOLEAN;
|
|
40
|
+
BEGIN
|
|
41
|
+
-- Check if this IP has already incremented this stat in the last 24 hours
|
|
42
|
+
SELECT EXISTS (
|
|
43
|
+
SELECT 1 FROM prompt_stat_tracking
|
|
44
|
+
WHERE prompt_id = p_prompt_id
|
|
45
|
+
AND ip_address = p_ip_address
|
|
46
|
+
AND stat_type = p_stat_type
|
|
47
|
+
AND created_at > now() - INTERVAL '24 hours'
|
|
48
|
+
) INTO v_already_tracked;
|
|
49
|
+
|
|
50
|
+
-- If already tracked, return false (no increment)
|
|
51
|
+
IF v_already_tracked THEN
|
|
52
|
+
RETURN FALSE;
|
|
53
|
+
END IF;
|
|
54
|
+
|
|
55
|
+
-- Record this tracking
|
|
56
|
+
INSERT INTO prompt_stat_tracking (prompt_id, ip_address, stat_type)
|
|
57
|
+
VALUES (p_prompt_id, p_ip_address, p_stat_type);
|
|
58
|
+
|
|
59
|
+
-- Increment the actual stat
|
|
60
|
+
INSERT INTO prompt_stats (prompt_id, views, used_count, picked_count, last_used_at)
|
|
61
|
+
VALUES (
|
|
62
|
+
p_prompt_id,
|
|
63
|
+
CASE WHEN p_stat_type = 'views' THEN 1 ELSE 0 END,
|
|
64
|
+
CASE WHEN p_stat_type = 'used' THEN 1 ELSE 0 END,
|
|
65
|
+
CASE WHEN p_stat_type = 'picked' THEN 1 ELSE 0 END,
|
|
66
|
+
CASE WHEN p_stat_type IN ('used', 'picked') THEN now() ELSE NULL END
|
|
67
|
+
)
|
|
68
|
+
ON CONFLICT (prompt_id) DO UPDATE SET
|
|
69
|
+
views = prompt_stats.views + CASE WHEN p_stat_type = 'views' THEN 1 ELSE 0 END,
|
|
70
|
+
used_count = prompt_stats.used_count + CASE WHEN p_stat_type = 'used' THEN 1 ELSE 0 END,
|
|
71
|
+
picked_count = prompt_stats.picked_count + CASE WHEN p_stat_type = 'picked' THEN 1 ELSE 0 END,
|
|
72
|
+
last_used_at = CASE WHEN p_stat_type IN ('used', 'picked') THEN now() ELSE prompt_stats.last_used_at END,
|
|
73
|
+
updated_at = now();
|
|
74
|
+
|
|
75
|
+
RETURN TRUE;
|
|
76
|
+
END;
|
|
77
|
+
$$;
|
|
78
|
+
|
|
79
|
+
-- Grant execute to authenticated and anon users
|
|
80
|
+
GRANT EXECUTE ON FUNCTION increment_prompt_stat_with_ip TO authenticated, anon;
|
|
81
|
+
|
|
82
|
+
-- Cleanup function: Remove tracking older than 30 days
|
|
83
|
+
CREATE OR REPLACE FUNCTION cleanup_old_prompt_stat_tracking()
|
|
84
|
+
RETURNS void
|
|
85
|
+
LANGUAGE plpgsql
|
|
86
|
+
SECURITY DEFINER
|
|
87
|
+
AS $$
|
|
88
|
+
BEGIN
|
|
89
|
+
DELETE FROM prompt_stat_tracking
|
|
90
|
+
WHERE created_at < now() - INTERVAL '30 days';
|
|
91
|
+
END;
|
|
92
|
+
$$;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
-- ============================================================================
|
|
2
|
+
-- Add slug column to user_prompt for SEO-friendly URLs
|
|
3
|
+
-- ============================================================================
|
|
4
|
+
|
|
5
|
+
-- Add slug column
|
|
6
|
+
ALTER TABLE user_prompt ADD COLUMN IF NOT EXISTS slug TEXT;
|
|
7
|
+
|
|
8
|
+
-- Create unique index on slug for public prompts
|
|
9
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_user_prompt_slug ON user_prompt(slug) WHERE is_public = true AND slug IS NOT NULL;
|
|
10
|
+
|
|
11
|
+
-- Function to generate slug from title
|
|
12
|
+
CREATE OR REPLACE FUNCTION generate_prompt_slug(p_title TEXT)
|
|
13
|
+
RETURNS TEXT
|
|
14
|
+
LANGUAGE plpgsql
|
|
15
|
+
AS $$
|
|
16
|
+
DECLARE
|
|
17
|
+
v_slug TEXT;
|
|
18
|
+
v_short_id TEXT;
|
|
19
|
+
v_counter INTEGER := 0;
|
|
20
|
+
v_final_slug TEXT;
|
|
21
|
+
BEGIN
|
|
22
|
+
-- Slugify title: lowercase, replace spaces with hyphens, remove special chars
|
|
23
|
+
v_slug := lower(regexp_replace(p_title, '[^a-zA-Z0-9\s-]', '', 'g'));
|
|
24
|
+
v_slug := regexp_replace(v_slug, '\s+', '-', 'g');
|
|
25
|
+
v_slug := regexp_replace(v_slug, '-+', '-', 'g');
|
|
26
|
+
v_slug := trim(both '-' from v_slug);
|
|
27
|
+
|
|
28
|
+
-- Limit to 50 characters
|
|
29
|
+
v_slug := substring(v_slug from 1 for 50);
|
|
30
|
+
|
|
31
|
+
-- Generate short random ID (6 chars)
|
|
32
|
+
v_short_id := substring(md5(random()::text || clock_timestamp()::text) from 1 for 6);
|
|
33
|
+
|
|
34
|
+
-- Combine slug with short ID
|
|
35
|
+
v_final_slug := v_slug || '-' || v_short_id;
|
|
36
|
+
|
|
37
|
+
RETURN v_final_slug;
|
|
38
|
+
END;
|
|
39
|
+
$$;
|
|
40
|
+
|
|
41
|
+
-- Function to auto-generate slug on insert/update if missing
|
|
42
|
+
CREATE OR REPLACE FUNCTION auto_generate_prompt_slug()
|
|
43
|
+
RETURNS TRIGGER AS $$
|
|
44
|
+
BEGIN
|
|
45
|
+
-- Only generate slug for public prompts if not already set
|
|
46
|
+
IF NEW.is_public = true AND (NEW.slug IS NULL OR NEW.slug = '') THEN
|
|
47
|
+
NEW.slug := generate_prompt_slug(NEW.title);
|
|
48
|
+
END IF;
|
|
49
|
+
|
|
50
|
+
RETURN NEW;
|
|
51
|
+
END;
|
|
52
|
+
$$ LANGUAGE plpgsql;
|
|
53
|
+
|
|
54
|
+
-- Create trigger
|
|
55
|
+
DROP TRIGGER IF EXISTS trigger_auto_generate_prompt_slug ON user_prompt;
|
|
56
|
+
CREATE TRIGGER trigger_auto_generate_prompt_slug
|
|
57
|
+
BEFORE INSERT OR UPDATE ON user_prompt
|
|
58
|
+
FOR EACH ROW
|
|
59
|
+
EXECUTE FUNCTION auto_generate_prompt_slug();
|
|
60
|
+
|
|
61
|
+
-- Backfill existing public prompts with slugs
|
|
62
|
+
UPDATE user_prompt
|
|
63
|
+
SET slug = generate_prompt_slug(title)
|
|
64
|
+
WHERE is_public = true AND (slug IS NULL OR slug = '');
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
-- ============================================================================
|
|
2
|
+
-- Update public_prompts_with_stats view to include slug
|
|
3
|
+
-- ============================================================================
|
|
4
|
+
|
|
5
|
+
-- Drop existing view to allow adding new column
|
|
6
|
+
DROP VIEW IF EXISTS public_prompts_with_stats CASCADE;
|
|
7
|
+
|
|
8
|
+
-- Recreate view with slug column
|
|
9
|
+
CREATE VIEW public_prompts_with_stats AS
|
|
10
|
+
SELECT
|
|
11
|
+
p.id,
|
|
12
|
+
p.owner_id,
|
|
13
|
+
p.slug,
|
|
14
|
+
p.title,
|
|
15
|
+
p.content,
|
|
16
|
+
p.type,
|
|
17
|
+
p.tags,
|
|
18
|
+
p.created_at,
|
|
19
|
+
COALESCE(s.views, 0) as views,
|
|
20
|
+
COALESCE(s.used_count, 0) as used_count,
|
|
21
|
+
COALESCE(s.picked_count, 0) as picked_count,
|
|
22
|
+
s.last_used_at
|
|
23
|
+
FROM user_prompt p
|
|
24
|
+
LEFT JOIN prompt_stats s ON s.prompt_id = p.id
|
|
25
|
+
WHERE p.is_public = true
|
|
26
|
+
ORDER BY s.picked_count DESC NULLS LAST, p.created_at DESC;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
-- ============================================================================
|
|
2
|
+
-- Update public_prompts_with_stats view to include username
|
|
3
|
+
-- ============================================================================
|
|
4
|
+
|
|
5
|
+
-- Drop existing view to allow adding username column
|
|
6
|
+
DROP VIEW IF EXISTS public_prompts_with_stats CASCADE;
|
|
7
|
+
|
|
8
|
+
-- Recreate view with slug, username and avatar
|
|
9
|
+
CREATE VIEW public_prompts_with_stats AS
|
|
10
|
+
SELECT
|
|
11
|
+
p.id,
|
|
12
|
+
p.owner_id,
|
|
13
|
+
p.slug,
|
|
14
|
+
p.title,
|
|
15
|
+
p.content,
|
|
16
|
+
p.type,
|
|
17
|
+
p.tags,
|
|
18
|
+
p.created_at,
|
|
19
|
+
up.username,
|
|
20
|
+
up.avatar_url,
|
|
21
|
+
COALESCE(s.views, 0) as views,
|
|
22
|
+
COALESCE(s.used_count, 0) as used_count,
|
|
23
|
+
COALESCE(s.picked_count, 0) as picked_count,
|
|
24
|
+
s.last_used_at
|
|
25
|
+
FROM user_prompt p
|
|
26
|
+
LEFT JOIN prompt_stats s ON s.prompt_id = p.id
|
|
27
|
+
LEFT JOIN user_profil up ON up.owner_id = p.owner_id
|
|
28
|
+
WHERE p.is_public = true
|
|
29
|
+
ORDER BY s.picked_count DESC NULLS LAST, p.created_at DESC;
|
|
30
|
+
|
|
31
|
+
-- Grant permissions
|
|
32
|
+
GRANT SELECT ON public_prompts_with_stats TO authenticated;
|
|
33
|
+
GRANT SELECT ON public_prompts_with_stats TO anon;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
-- ============================================================================
|
|
2
|
+
-- Add lang column to user_prompt and update view
|
|
3
|
+
-- ============================================================================
|
|
4
|
+
|
|
5
|
+
-- Add lang column to user_prompt
|
|
6
|
+
ALTER TABLE public.user_prompt
|
|
7
|
+
ADD COLUMN IF NOT EXISTS lang TEXT DEFAULT 'en';
|
|
8
|
+
|
|
9
|
+
-- Add comment
|
|
10
|
+
COMMENT ON COLUMN public.user_prompt.lang IS 'Language of the prompt (en, fr, etc.)';
|
|
11
|
+
|
|
12
|
+
-- Update view to include lang
|
|
13
|
+
DROP VIEW IF EXISTS public_prompts_with_stats CASCADE;
|
|
14
|
+
|
|
15
|
+
CREATE VIEW public_prompts_with_stats AS
|
|
16
|
+
SELECT
|
|
17
|
+
p.id,
|
|
18
|
+
p.owner_id,
|
|
19
|
+
p.slug,
|
|
20
|
+
p.title,
|
|
21
|
+
p.content,
|
|
22
|
+
p.type,
|
|
23
|
+
p.tags,
|
|
24
|
+
p.lang,
|
|
25
|
+
p.created_at,
|
|
26
|
+
up.username,
|
|
27
|
+
up.avatar_url,
|
|
28
|
+
COALESCE(s.views, 0) as views,
|
|
29
|
+
COALESCE(s.used_count, 0) as used_count,
|
|
30
|
+
COALESCE(s.picked_count, 0) as picked_count,
|
|
31
|
+
s.last_used_at
|
|
32
|
+
FROM user_prompt p
|
|
33
|
+
LEFT JOIN prompt_stats s ON s.prompt_id = p.id
|
|
34
|
+
LEFT JOIN user_profil up ON up.owner_id = p.owner_id
|
|
35
|
+
WHERE p.is_public = true
|
|
36
|
+
ORDER BY s.picked_count DESC NULLS LAST, p.created_at DESC;
|
|
37
|
+
|
|
38
|
+
-- Grant permissions
|
|
39
|
+
GRANT SELECT ON public_prompts_with_stats TO authenticated;
|
|
40
|
+
GRANT SELECT ON public_prompts_with_stats TO anon;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
-- Migration: Extract model and prompt from metadata to dedicated columns in user_token_ledger
|
|
2
|
+
-- Modifies record_token_consumption RPC to populate model and prompt columns from p_meta JSONB
|
|
3
|
+
|
|
4
|
+
CREATE OR REPLACE FUNCTION public.record_token_consumption(
|
|
5
|
+
p_user_id uuid,
|
|
6
|
+
p_debit_tokens bigint,
|
|
7
|
+
p_provider_cost_usd numeric,
|
|
8
|
+
p_sell_usd numeric,
|
|
9
|
+
p_margin_usd numeric,
|
|
10
|
+
p_meta jsonb DEFAULT '{}'::jsonb
|
|
11
|
+
)
|
|
12
|
+
RETURNS jsonb AS $$
|
|
13
|
+
DECLARE
|
|
14
|
+
v_current_balance bigint;
|
|
15
|
+
v_wallet_provider_budget numeric;
|
|
16
|
+
v_wallet_sell_value numeric;
|
|
17
|
+
v_new_balance bigint;
|
|
18
|
+
v_allow_overdraft boolean;
|
|
19
|
+
BEGIN
|
|
20
|
+
-- 1. Get current balance
|
|
21
|
+
SELECT COALESCE(SUM(amount), 0) INTO v_current_balance
|
|
22
|
+
FROM public.user_token_ledger
|
|
23
|
+
WHERE owner_id = p_user_id;
|
|
24
|
+
|
|
25
|
+
-- 2. Get wallet values
|
|
26
|
+
SELECT
|
|
27
|
+
wallet_provider_budget_usd,
|
|
28
|
+
wallet_sell_value_usd
|
|
29
|
+
INTO v_wallet_provider_budget, v_wallet_sell_value
|
|
30
|
+
FROM public.user_token_wallet
|
|
31
|
+
WHERE user_id = p_user_id;
|
|
32
|
+
|
|
33
|
+
-- 3. Check overdraft
|
|
34
|
+
v_allow_overdraft := COALESCE((p_meta->>'allowOverdraft')::boolean, false);
|
|
35
|
+
|
|
36
|
+
IF NOT v_allow_overdraft AND v_current_balance < p_debit_tokens THEN
|
|
37
|
+
RAISE EXCEPTION 'Insufficient token balance: have %, need %', v_current_balance, p_debit_tokens;
|
|
38
|
+
END IF;
|
|
39
|
+
|
|
40
|
+
IF v_wallet_provider_budget < p_provider_cost_usd THEN
|
|
41
|
+
RAISE EXCEPTION 'Insufficient provider budget: have $%, need $%', v_wallet_provider_budget, p_provider_cost_usd;
|
|
42
|
+
END IF;
|
|
43
|
+
|
|
44
|
+
IF v_wallet_sell_value < p_sell_usd THEN
|
|
45
|
+
RAISE EXCEPTION 'Insufficient sell value: have $%, need $%', v_wallet_sell_value, p_sell_usd;
|
|
46
|
+
END IF;
|
|
47
|
+
|
|
48
|
+
-- 4. Insert ledger entry (negative amount) with model and prompt extracted from metadata
|
|
49
|
+
INSERT INTO public.user_token_ledger (
|
|
50
|
+
owner_id,
|
|
51
|
+
amount,
|
|
52
|
+
type,
|
|
53
|
+
debit_tokens,
|
|
54
|
+
provider_cost_usd,
|
|
55
|
+
sell_usd,
|
|
56
|
+
margin_usd,
|
|
57
|
+
model,
|
|
58
|
+
prompt,
|
|
59
|
+
meta
|
|
60
|
+
) VALUES (
|
|
61
|
+
p_user_id,
|
|
62
|
+
-p_debit_tokens,
|
|
63
|
+
'use',
|
|
64
|
+
p_debit_tokens,
|
|
65
|
+
p_provider_cost_usd,
|
|
66
|
+
p_sell_usd,
|
|
67
|
+
p_margin_usd,
|
|
68
|
+
p_meta->>'model',
|
|
69
|
+
substring(p_meta->>'prompt', 1, 32),
|
|
70
|
+
p_meta
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
-- 5. Update wallet (subtract USD values)
|
|
74
|
+
UPDATE public.user_token_wallet
|
|
75
|
+
SET
|
|
76
|
+
wallet_provider_budget_usd = wallet_provider_budget_usd - p_provider_cost_usd,
|
|
77
|
+
wallet_sell_value_usd = wallet_sell_value_usd - p_sell_usd,
|
|
78
|
+
updated_at = now()
|
|
79
|
+
WHERE user_id = p_user_id;
|
|
80
|
+
|
|
81
|
+
-- 6. Get new balance
|
|
82
|
+
v_new_balance := v_current_balance - p_debit_tokens;
|
|
83
|
+
|
|
84
|
+
RETURN jsonb_build_object(
|
|
85
|
+
'success', true,
|
|
86
|
+
'new_balance', v_new_balance
|
|
87
|
+
);
|
|
88
|
+
END;
|
|
89
|
+
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
90
|
+
|
|
91
|
+
COMMENT ON FUNCTION public.record_token_consumption IS
|
|
92
|
+
'Records AI consumption: debits tokens, updates wallet USD values, and extracts model/prompt from metadata to dedicated columns for efficient querying';
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
-- Fix duplicate purchases in ledger
|
|
2
|
+
-- This migration removes duplicate purchase entries with same payment_id
|
|
3
|
+
-- and recalculates wallet from clean ledger
|
|
4
|
+
|
|
5
|
+
BEGIN;
|
|
6
|
+
|
|
7
|
+
-- Step 1: Delete duplicate purchases (keep oldest entry per payment_id)
|
|
8
|
+
WITH duplicates AS (
|
|
9
|
+
SELECT
|
|
10
|
+
id,
|
|
11
|
+
owner_id,
|
|
12
|
+
meta->>'payment_id' as payment_id,
|
|
13
|
+
ts,
|
|
14
|
+
ROW_NUMBER() OVER (
|
|
15
|
+
PARTITION BY owner_id, meta->>'payment_id'
|
|
16
|
+
ORDER BY ts ASC
|
|
17
|
+
) as rn
|
|
18
|
+
FROM user_token_ledger
|
|
19
|
+
WHERE
|
|
20
|
+
type = 'purchase'
|
|
21
|
+
AND meta->>'payment_id' IS NOT NULL
|
|
22
|
+
)
|
|
23
|
+
DELETE FROM user_token_ledger
|
|
24
|
+
WHERE id IN (
|
|
25
|
+
SELECT id FROM duplicates WHERE rn > 1
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
-- Step 2: Recalculate ALL wallets from clean ledger
|
|
29
|
+
WITH wallet_totals AS (
|
|
30
|
+
SELECT
|
|
31
|
+
owner_id,
|
|
32
|
+
SUM(CASE WHEN type = 'purchase' THEN provider_budget_added_usd ELSE 0 END) as total_provider_added,
|
|
33
|
+
SUM(CASE WHEN type = 'purchase' THEN sell_value_added_usd ELSE 0 END) as total_sell_added,
|
|
34
|
+
SUM(CASE WHEN type = 'use' THEN provider_cost_usd ELSE 0 END) as total_provider_used,
|
|
35
|
+
SUM(CASE WHEN type = 'use' THEN sell_usd ELSE 0 END) as total_sell_used
|
|
36
|
+
FROM user_token_ledger
|
|
37
|
+
GROUP BY owner_id
|
|
38
|
+
)
|
|
39
|
+
INSERT INTO user_token_wallet (
|
|
40
|
+
user_id,
|
|
41
|
+
wallet_provider_budget_usd,
|
|
42
|
+
wallet_sell_value_usd,
|
|
43
|
+
updated_at
|
|
44
|
+
)
|
|
45
|
+
SELECT
|
|
46
|
+
owner_id,
|
|
47
|
+
total_provider_added - COALESCE(total_provider_used, 0),
|
|
48
|
+
total_sell_added - COALESCE(total_sell_used, 0),
|
|
49
|
+
NOW()
|
|
50
|
+
FROM wallet_totals
|
|
51
|
+
ON CONFLICT (user_id) DO UPDATE SET
|
|
52
|
+
wallet_provider_budget_usd = EXCLUDED.wallet_provider_budget_usd,
|
|
53
|
+
wallet_sell_value_usd = EXCLUDED.wallet_sell_value_usd,
|
|
54
|
+
updated_at = NOW();
|
|
55
|
+
|
|
56
|
+
-- Step 3: Add unique constraint to prevent future duplicates
|
|
57
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_ledger_unique_payment
|
|
58
|
+
ON user_token_ledger (owner_id, (meta->>'payment_id'))
|
|
59
|
+
WHERE type = 'purchase' AND meta->>'payment_id' IS NOT NULL;
|
|
60
|
+
|
|
61
|
+
COMMIT;
|
|
62
|
+
|
|
63
|
+
COMMENT ON INDEX idx_ledger_unique_payment IS
|
|
64
|
+
'Prevents duplicate purchase entries for same payment_id';
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
-- module-ai: Set default text and image models GLOBALLY
|
|
2
|
+
-- This migration creates a system-wide config table for default models
|
|
3
|
+
-- All API calls (recipes, images, etc.) will use these defaults unless user overrides
|
|
4
|
+
|
|
5
|
+
begin;
|
|
6
|
+
|
|
7
|
+
-- -----------------------------------------------------------------------------
|
|
8
|
+
-- 1) Create global AI configuration table (single row)
|
|
9
|
+
-- -----------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
create table if not exists public.ai_global_settings (
|
|
12
|
+
id integer primary key default 1 check (id = 1), -- Only 1 row allowed
|
|
13
|
+
|
|
14
|
+
-- Default models for all users
|
|
15
|
+
default_text_provider text not null default 'openai',
|
|
16
|
+
default_text_model text not null default 'gpt-4.1-mini',
|
|
17
|
+
|
|
18
|
+
default_image_provider text not null default 'google',
|
|
19
|
+
default_image_model text not null default 'gemini-2.5-flash-image',
|
|
20
|
+
|
|
21
|
+
-- Optional: default max tokens
|
|
22
|
+
max_tokens_per_call integer not null default 16384,
|
|
23
|
+
|
|
24
|
+
-- Metadata
|
|
25
|
+
updated_at timestamptz not null default now(),
|
|
26
|
+
updated_by uuid null references auth.users(id)
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
-- Insert default values
|
|
30
|
+
insert into public.ai_global_settings (id)
|
|
31
|
+
values (1)
|
|
32
|
+
on conflict (id) do update
|
|
33
|
+
set
|
|
34
|
+
default_text_provider = excluded.default_text_provider,
|
|
35
|
+
default_text_model = excluded.default_text_model,
|
|
36
|
+
default_image_provider = excluded.default_image_provider,
|
|
37
|
+
default_image_model = excluded.default_image_model,
|
|
38
|
+
max_tokens_per_call = excluded.max_tokens_per_call;
|
|
39
|
+
|
|
40
|
+
-- -----------------------------------------------------------------------------
|
|
41
|
+
-- 2) RLS: Public read for all, only superadmin can write
|
|
42
|
+
-- -----------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
alter table public.ai_global_settings enable row level security;
|
|
45
|
+
|
|
46
|
+
drop policy if exists ai_global_settings_select on public.ai_global_settings;
|
|
47
|
+
create policy ai_global_settings_select
|
|
48
|
+
on public.ai_global_settings for select
|
|
49
|
+
using (true); -- Everyone can read
|
|
50
|
+
|
|
51
|
+
drop policy if exists ai_global_settings_update on public.ai_global_settings;
|
|
52
|
+
create policy ai_global_settings_update
|
|
53
|
+
on public.ai_global_settings for update
|
|
54
|
+
using (is_superadmin(auth.uid()))
|
|
55
|
+
with check (is_superadmin(auth.uid()));
|
|
56
|
+
|
|
57
|
+
-- Updated trigger
|
|
58
|
+
drop trigger if exists trg_ai_global_settings_updated_at on public.ai_global_settings;
|
|
59
|
+
create trigger trg_ai_global_settings_updated_at
|
|
60
|
+
before update on public.ai_global_settings
|
|
61
|
+
for each row execute function public.set_updated_at();
|
|
62
|
+
|
|
63
|
+
commit;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
-- module-ai: Remove deprecated tables ai_providers and ai_provider_models
|
|
2
|
+
-- These tables are no longer used - we query Vercel AI Gateway directly for available models
|
|
3
|
+
-- Global defaults are now stored in ai_global_settings table
|
|
4
|
+
|
|
5
|
+
begin;
|
|
6
|
+
|
|
7
|
+
-- Drop dependent tables/views first (if any)
|
|
8
|
+
drop view if exists ai_active_models_v cascade;
|
|
9
|
+
|
|
10
|
+
-- Drop the tables
|
|
11
|
+
drop table if exists public.ai_provider_models cascade;
|
|
12
|
+
drop table if exists public.ai_providers cascade;
|
|
13
|
+
|
|
14
|
+
-- Note: No need to update ai_global_settings as it doesn't have foreign keys to these tables
|
|
15
|
+
-- The global settings table uses simple text fields for provider/model names
|
|
16
|
+
|
|
17
|
+
commit;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
-- ===========================================================================
|
|
2
|
+
-- Module: @lastbrain/module-ai
|
|
3
|
+
-- Migration DOWN: 20251217120000_user_token_quota_monthly.sql
|
|
4
|
+
-- Description: Rollback monthly token quota tables
|
|
5
|
+
-- ===========================================================================
|
|
6
|
+
|
|
7
|
+
-- Drop helper function
|
|
8
|
+
DROP FUNCTION IF EXISTS public.get_plan_default_tokens(text);
|
|
9
|
+
|
|
10
|
+
-- Drop triggers
|
|
11
|
+
DROP TRIGGER IF EXISTS set_user_token_quota_monthly_updated_at ON public.user_token_quota_monthly;
|
|
12
|
+
|
|
13
|
+
-- Drop policies - user_token_quota_ledger
|
|
14
|
+
DROP POLICY IF EXISTS user_token_quota_ledger_admin_all ON public.user_token_quota_ledger;
|
|
15
|
+
DROP POLICY IF EXISTS user_token_quota_ledger_select_own ON public.user_token_quota_ledger;
|
|
16
|
+
|
|
17
|
+
-- Drop policies - user_token_quota_monthly
|
|
18
|
+
DROP POLICY IF EXISTS user_token_quota_monthly_admin_all ON public.user_token_quota_monthly;
|
|
19
|
+
DROP POLICY IF EXISTS user_token_quota_monthly_select_own ON public.user_token_quota_monthly;
|
|
20
|
+
|
|
21
|
+
-- Drop indexes - user_token_quota_ledger
|
|
22
|
+
DROP INDEX IF EXISTS public.idx_user_token_quota_ledger_period;
|
|
23
|
+
DROP INDEX IF EXISTS public.idx_user_token_quota_ledger_owner_created;
|
|
24
|
+
DROP INDEX IF EXISTS public.idx_user_token_quota_ledger_owner;
|
|
25
|
+
|
|
26
|
+
-- Drop indexes - user_token_quota_monthly
|
|
27
|
+
DROP INDEX IF EXISTS public.idx_user_token_quota_monthly_subscription;
|
|
28
|
+
DROP INDEX IF EXISTS public.idx_user_token_quota_monthly_period;
|
|
29
|
+
DROP INDEX IF EXISTS public.idx_user_token_quota_monthly_plan;
|
|
30
|
+
DROP INDEX IF EXISTS public.idx_user_token_quota_monthly_owner;
|
|
31
|
+
|
|
32
|
+
-- Drop tables
|
|
33
|
+
DROP TABLE IF EXISTS public.user_token_quota_ledger;
|
|
34
|
+
DROP TABLE IF EXISTS public.user_token_quota_monthly;
|