@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.
Files changed (365) hide show
  1. package/README.md +52 -1
  2. package/dist/ai.build.config.d.ts.map +1 -1
  3. package/dist/ai.build.config.js +508 -9
  4. package/dist/api/admin/ai-provider-models/[id].d.ts +18 -0
  5. package/dist/api/admin/ai-provider-models/[id].d.ts.map +1 -0
  6. package/dist/api/admin/ai-provider-models/[id].js +58 -0
  7. package/dist/api/admin/ai-provider-models.d.ts +20 -0
  8. package/dist/api/admin/ai-provider-models.d.ts.map +1 -0
  9. package/dist/api/admin/ai-provider-models.js +26 -0
  10. package/dist/api/admin/ai-providers/[key].d.ts +18 -0
  11. package/dist/api/admin/ai-providers/[key].d.ts.map +1 -0
  12. package/dist/api/admin/ai-providers/[key].js +55 -0
  13. package/dist/api/admin/ai-providers.d.ts +20 -0
  14. package/dist/api/admin/ai-providers.d.ts.map +1 -0
  15. package/dist/api/admin/ai-providers.js +26 -0
  16. package/dist/api/admin/billing-analytics.d.ts +43 -0
  17. package/dist/api/admin/billing-analytics.d.ts.map +1 -0
  18. package/dist/api/admin/billing-analytics.js +144 -0
  19. package/dist/api/admin/global-ai-settings.d.ts +14 -0
  20. package/dist/api/admin/global-ai-settings.d.ts.map +1 -0
  21. package/dist/api/admin/global-ai-settings.js +63 -0
  22. package/dist/api/admin/token-packs/[id].d.ts.map +1 -1
  23. package/dist/api/admin/token-packs/[id].js +3 -2
  24. package/dist/api/admin/token-packs.d.ts.map +1 -1
  25. package/dist/api/admin/token-packs.js +3 -2
  26. package/dist/api/admin/user-monthly-details.d.ts +49 -0
  27. package/dist/api/admin/user-monthly-details.d.ts.map +1 -0
  28. package/dist/api/admin/user-monthly-details.js +140 -0
  29. package/dist/api/admin/user-quota.d.ts +21 -0
  30. package/dist/api/admin/user-quota.d.ts.map +1 -0
  31. package/dist/api/admin/user-quota.js +59 -0
  32. package/dist/api/admin/user-token/[id].d.ts.map +1 -1
  33. package/dist/api/admin/user-token/[id].js +2 -1
  34. package/dist/api/admin/user-token.d.ts +5 -2
  35. package/dist/api/admin/user-token.d.ts.map +1 -1
  36. package/dist/api/admin/user-token.js +91 -17
  37. package/dist/api/admin/user-usage-by-model.d.ts +22 -0
  38. package/dist/api/admin/user-usage-by-model.d.ts.map +1 -0
  39. package/dist/api/admin/user-usage-by-model.js +78 -0
  40. package/dist/api/admin/user-wallet-analytics.d.ts +15 -0
  41. package/dist/api/admin/user-wallet-analytics.d.ts.map +1 -0
  42. package/dist/api/admin/user-wallet-analytics.js +67 -0
  43. package/dist/api/admin/wallet-repair/route.d.ts +30 -0
  44. package/dist/api/admin/wallet-repair/route.d.ts.map +1 -0
  45. package/dist/api/admin/wallet-repair/route.js +63 -0
  46. package/dist/api/auth/ai-model-settings.d.ts +21 -0
  47. package/dist/api/auth/ai-model-settings.d.ts.map +1 -0
  48. package/dist/api/auth/ai-model-settings.js +86 -0
  49. package/dist/api/auth/ai-settings.d.ts +17 -0
  50. package/dist/api/auth/ai-settings.d.ts.map +1 -0
  51. package/dist/api/auth/ai-settings.js +87 -0
  52. package/dist/api/auth/api-keys/[id].d.ts +17 -0
  53. package/dist/api/auth/api-keys/[id].d.ts.map +1 -0
  54. package/dist/api/auth/api-keys/[id].js +66 -0
  55. package/dist/api/auth/api-keys.d.ts +19 -0
  56. package/dist/api/auth/api-keys.d.ts.map +1 -0
  57. package/dist/api/auth/api-keys.js +94 -0
  58. package/dist/api/auth/create-checkout.d.ts +1 -1
  59. package/dist/api/auth/create-checkout.d.ts.map +1 -1
  60. package/dist/api/auth/create-checkout.js +8 -6
  61. package/dist/api/auth/generate-image.d.ts +2 -2
  62. package/dist/api/auth/generate-image.d.ts.map +1 -1
  63. package/dist/api/auth/generate-image.js +404 -104
  64. package/dist/api/auth/generate-text.d.ts +3 -2
  65. package/dist/api/auth/generate-text.d.ts.map +1 -1
  66. package/dist/api/auth/generate-text.js +130 -58
  67. package/dist/api/auth/process-ocr.d.ts +9 -0
  68. package/dist/api/auth/process-ocr.d.ts.map +1 -0
  69. package/dist/api/auth/process-ocr.js +43 -0
  70. package/dist/api/auth/prompts/stats.d.ts +14 -0
  71. package/dist/api/auth/prompts/stats.d.ts.map +1 -0
  72. package/dist/api/auth/prompts/stats.js +46 -0
  73. package/dist/api/auth/prompts.d.ts +26 -0
  74. package/dist/api/auth/prompts.d.ts.map +1 -0
  75. package/dist/api/auth/prompts.js +175 -0
  76. package/dist/api/auth/token-balance.d.ts +26 -0
  77. package/dist/api/auth/token-balance.d.ts.map +1 -0
  78. package/dist/api/auth/token-balance.js +47 -0
  79. package/dist/api/auth/token-checkout.d.ts.map +1 -1
  80. package/dist/api/auth/token-checkout.js +22 -4
  81. package/dist/api/auth/token-packs.d.ts.map +1 -1
  82. package/dist/api/auth/token-packs.js +2 -1
  83. package/dist/api/auth/usage-by-model.d.ts +25 -0
  84. package/dist/api/auth/usage-by-model.d.ts.map +1 -0
  85. package/dist/api/auth/usage-by-model.js +95 -0
  86. package/dist/api/auth/usage.d.ts +26 -0
  87. package/dist/api/auth/usage.d.ts.map +1 -0
  88. package/dist/api/auth/usage.js +127 -0
  89. package/dist/api/auth/user-tokens.d.ts.map +1 -1
  90. package/dist/api/auth/user-tokens.js +36 -2
  91. package/dist/api/auth/wallet/route.d.ts +17 -0
  92. package/dist/api/auth/wallet/route.d.ts.map +1 -0
  93. package/dist/api/auth/wallet/route.js +68 -0
  94. package/dist/api/auth/wallet.d.ts +16 -0
  95. package/dist/api/auth/wallet.d.ts.map +1 -0
  96. package/dist/api/auth/wallet.js +71 -0
  97. package/dist/api/public/gateway-models.d.ts +25 -0
  98. package/dist/api/public/gateway-models.d.ts.map +1 -0
  99. package/dist/api/public/gateway-models.js +49 -0
  100. package/dist/api/public/pricing-summary.d.ts +46 -0
  101. package/dist/api/public/pricing-summary.d.ts.map +1 -0
  102. package/dist/api/public/pricing-summary.js +70 -0
  103. package/dist/api/public/prompts/[id]/stats.d.ts +15 -0
  104. package/dist/api/public/prompts/[id]/stats.d.ts.map +1 -0
  105. package/dist/api/public/prompts/[id]/stats.js +37 -0
  106. package/dist/api/public/prompts/[id]/view.d.ts +15 -0
  107. package/dist/api/public/prompts/[id]/view.d.ts.map +1 -0
  108. package/dist/api/public/prompts/[id]/view.js +41 -0
  109. package/dist/api/public/prompts/slug/[slug].d.ts +15 -0
  110. package/dist/api/public/prompts/slug/[slug].d.ts.map +1 -0
  111. package/dist/api/public/prompts/slug/[slug].js +40 -0
  112. package/dist/api/public/prompts/user/[username].d.ts +17 -0
  113. package/dist/api/public/prompts/user/[username].d.ts.map +1 -0
  114. package/dist/api/public/prompts/user/[username].js +51 -0
  115. package/dist/api/public/prompts.d.ts +15 -0
  116. package/dist/api/public/prompts.d.ts.map +1 -0
  117. package/dist/api/public/prompts.js +45 -0
  118. package/dist/api/public/token-packs.d.ts +11 -0
  119. package/dist/api/public/token-packs.d.ts.map +1 -0
  120. package/dist/api/public/token-packs.js +25 -0
  121. package/dist/api/public/token-pricing.d.ts +44 -0
  122. package/dist/api/public/token-pricing.d.ts.map +1 -0
  123. package/dist/api/public/token-pricing.js +168 -0
  124. package/dist/api/public/v1/_lib/artifacts.d.ts +52 -0
  125. package/dist/api/public/v1/_lib/artifacts.d.ts.map +1 -0
  126. package/dist/api/public/v1/_lib/artifacts.js +217 -0
  127. package/dist/api/public/v1/_lib/auth.d.ts +43 -0
  128. package/dist/api/public/v1/_lib/auth.d.ts.map +1 -0
  129. package/dist/api/public/v1/_lib/auth.js +108 -0
  130. package/dist/api/public/v1/_lib/errors.d.ts +17 -0
  131. package/dist/api/public/v1/_lib/errors.d.ts.map +1 -0
  132. package/dist/api/public/v1/_lib/errors.js +16 -0
  133. package/dist/api/public/v1/_lib/log.d.ts +29 -0
  134. package/dist/api/public/v1/_lib/log.d.ts.map +1 -0
  135. package/dist/api/public/v1/_lib/log.js +68 -0
  136. package/dist/api/public/v1/_lib/quota.d.ts +24 -0
  137. package/dist/api/public/v1/_lib/quota.d.ts.map +1 -0
  138. package/dist/api/public/v1/_lib/quota.js +118 -0
  139. package/dist/api/public/v1/_lib/router.d.ts +54 -0
  140. package/dist/api/public/v1/_lib/router.d.ts.map +1 -0
  141. package/dist/api/public/v1/_lib/router.js +119 -0
  142. package/dist/api/public/v1/connect.d.ts +20 -0
  143. package/dist/api/public/v1/connect.d.ts.map +1 -0
  144. package/dist/api/public/v1/connect.js +119 -0
  145. package/dist/api/public/v1/doc.d.ts +239 -0
  146. package/dist/api/public/v1/doc.d.ts.map +1 -0
  147. package/dist/api/public/v1/doc.js +253 -0
  148. package/dist/api/public/v1/history/[id].d.ts +92 -0
  149. package/dist/api/public/v1/history/[id].d.ts.map +1 -0
  150. package/dist/api/public/v1/history/[id].js +176 -0
  151. package/dist/api/public/v1/history.d.ts +30 -0
  152. package/dist/api/public/v1/history.d.ts.map +1 -0
  153. package/dist/api/public/v1/history.js +142 -0
  154. package/dist/api/public/v1/image-ai.d.ts +24 -0
  155. package/dist/api/public/v1/image-ai.d.ts.map +1 -0
  156. package/dist/api/public/v1/image-ai.js +233 -0
  157. package/dist/api/public/v1/prompts.d.ts +19 -0
  158. package/dist/api/public/v1/prompts.d.ts.map +1 -0
  159. package/dist/api/public/v1/prompts.js +107 -0
  160. package/dist/api/public/v1/provider.d.ts +16 -0
  161. package/dist/api/public/v1/provider.d.ts.map +1 -0
  162. package/dist/api/public/v1/provider.js +130 -0
  163. package/dist/api/public/v1/purchase.d.ts +11 -0
  164. package/dist/api/public/v1/purchase.d.ts.map +1 -0
  165. package/dist/api/public/v1/purchase.js +18 -0
  166. package/dist/api/public/v1/status.d.ts +35 -0
  167. package/dist/api/public/v1/status.d.ts.map +1 -0
  168. package/dist/api/public/v1/status.js +163 -0
  169. package/dist/api/public/v1/text-ai.d.ts +26 -0
  170. package/dist/api/public/v1/text-ai.d.ts.map +1 -0
  171. package/dist/api/public/v1/text-ai.js +239 -0
  172. package/dist/api/public/webhook.d.ts.map +1 -1
  173. package/dist/api/public/webhook.js +50 -39
  174. package/dist/api/track-usage.d.ts +12 -0
  175. package/dist/api/track-usage.d.ts.map +1 -0
  176. package/dist/api/track-usage.js +37 -0
  177. package/dist/components/Doc.d.ts.map +1 -1
  178. package/dist/components/Doc.js +1 -1
  179. package/dist/components/DocUsageCustom.js +6 -6
  180. package/dist/components/admin/UserTokenTab.d.ts.map +1 -1
  181. package/dist/components/admin/UserTokenTab.js +170 -23
  182. package/dist/components/auth/AuthDashboardAi.d.ts +2 -0
  183. package/dist/components/auth/AuthDashboardAi.d.ts.map +1 -0
  184. package/dist/components/auth/AuthDashboardAi.js +53 -0
  185. package/dist/docs/REFACTORING_BILLING_GUIDE.d.ts +277 -0
  186. package/dist/docs/REFACTORING_BILLING_GUIDE.d.ts.map +1 -0
  187. package/dist/docs/REFACTORING_BILLING_GUIDE.js +276 -0
  188. package/dist/index.d.ts +15 -1
  189. package/dist/index.d.ts.map +1 -1
  190. package/dist/index.js +18 -1
  191. package/dist/scripts/migrate-tokens-to-wallet.d.ts +13 -0
  192. package/dist/scripts/migrate-tokens-to-wallet.d.ts.map +1 -0
  193. package/dist/scripts/migrate-tokens-to-wallet.js +165 -0
  194. package/dist/server/__tests__/billing.test.d.ts +5 -0
  195. package/dist/server/__tests__/billing.test.d.ts.map +1 -0
  196. package/dist/server/__tests__/billing.test.js +223 -0
  197. package/dist/server/ai-client.d.ts +59 -0
  198. package/dist/server/ai-client.d.ts.map +1 -0
  199. package/dist/server/ai-client.js +111 -0
  200. package/dist/server/ai-generation-service.d.ts +66 -0
  201. package/dist/server/ai-generation-service.d.ts.map +1 -0
  202. package/dist/server/ai-generation-service.js +274 -0
  203. package/dist/server/billing.d.ts +200 -0
  204. package/dist/server/billing.d.ts.map +1 -0
  205. package/dist/server/billing.js +488 -0
  206. package/dist/server/gateway-service.d.ts +13 -0
  207. package/dist/server/gateway-service.d.ts.map +1 -0
  208. package/dist/server/gateway-service.js +161 -0
  209. package/dist/server/global-settings.d.ts +16 -0
  210. package/dist/server/global-settings.d.ts.map +1 -0
  211. package/dist/server/global-settings.js +42 -0
  212. package/dist/server/model-filter.d.ts +25 -0
  213. package/dist/server/model-filter.d.ts.map +1 -0
  214. package/dist/server/model-filter.js +240 -0
  215. package/dist/server/ocr.d.ts +39 -0
  216. package/dist/server/ocr.d.ts.map +1 -0
  217. package/dist/server/ocr.js +280 -0
  218. package/dist/server/openai-client.d.ts +19 -0
  219. package/dist/server/openai-client.d.ts.map +1 -0
  220. package/dist/server/openai-client.js +26 -0
  221. package/dist/server/pricing-config.d.ts +18 -0
  222. package/dist/server/pricing-config.d.ts.map +1 -0
  223. package/dist/server/pricing-config.js +94 -0
  224. package/dist/server/pricing-validator.d.ts +41 -0
  225. package/dist/server/pricing-validator.d.ts.map +1 -0
  226. package/dist/server/pricing-validator.js +113 -0
  227. package/dist/server/pricing.d.ts +121 -0
  228. package/dist/server/pricing.d.ts.map +1 -0
  229. package/dist/server/pricing.js +225 -0
  230. package/dist/server/quota.d.ts +66 -0
  231. package/dist/server/quota.d.ts.map +1 -0
  232. package/dist/server/quota.js +538 -0
  233. package/dist/server/wallet-repair.d.ts +32 -0
  234. package/dist/server/wallet-repair.d.ts.map +1 -0
  235. package/dist/server/wallet-repair.js +189 -0
  236. package/dist/server.d.ts +13 -1
  237. package/dist/server.d.ts.map +1 -1
  238. package/dist/server.js +87 -16
  239. package/dist/sitemap/handlers/prompts.d.ts +6 -0
  240. package/dist/sitemap/handlers/prompts.d.ts.map +1 -0
  241. package/dist/sitemap/handlers/prompts.js +72 -0
  242. package/dist/sitemap/handlers/users.d.ts +6 -0
  243. package/dist/sitemap/handlers/users.d.ts.map +1 -0
  244. package/dist/sitemap/handlers/users.js +80 -0
  245. package/dist/sitemap/manifest.d.ts +8 -0
  246. package/dist/sitemap/manifest.d.ts.map +1 -0
  247. package/dist/sitemap/manifest.js +27 -0
  248. package/dist/types/gateway.d.ts +40 -0
  249. package/dist/types/gateway.d.ts.map +1 -0
  250. package/dist/types/gateway.js +4 -0
  251. package/dist/types/quota.d.ts +74 -0
  252. package/dist/types/quota.d.ts.map +1 -0
  253. package/dist/types/quota.js +11 -0
  254. package/dist/utils/date.d.ts +7 -0
  255. package/dist/utils/date.d.ts.map +1 -0
  256. package/dist/utils/date.js +17 -0
  257. package/dist/web/admin/AdminTokenPacksPage.js +1 -1
  258. package/dist/web/admin/BillingAnalyticsPage.d.ts +2 -0
  259. package/dist/web/admin/BillingAnalyticsPage.d.ts.map +1 -0
  260. package/dist/web/admin/BillingAnalyticsPage.js +141 -0
  261. package/dist/web/admin/GlobalAISettingsPage.d.ts +2 -0
  262. package/dist/web/admin/GlobalAISettingsPage.d.ts.map +1 -0
  263. package/dist/web/admin/GlobalAISettingsPage.js +93 -0
  264. package/dist/web/admin/UserTokenPage.d.ts.map +1 -1
  265. package/dist/web/admin/UserTokenPage.js +20 -7
  266. package/dist/web/auth/AISettingsPage.d.ts +2 -0
  267. package/dist/web/auth/AISettingsPage.d.ts.map +1 -0
  268. package/dist/web/auth/AISettingsPage.js +258 -0
  269. package/dist/web/auth/APIKeysPage.d.ts +2 -0
  270. package/dist/web/auth/APIKeysPage.d.ts.map +1 -0
  271. package/dist/web/auth/APIKeysPage.js +154 -0
  272. package/dist/web/auth/HistoryPage.d.ts +2 -0
  273. package/dist/web/auth/HistoryPage.d.ts.map +1 -0
  274. package/dist/web/auth/HistoryPage.js +279 -0
  275. package/dist/web/auth/PromptsPage.d.ts +5 -0
  276. package/dist/web/auth/PromptsPage.d.ts.map +1 -0
  277. package/dist/web/auth/PromptsPage.js +137 -0
  278. package/dist/web/auth/TokenPage.d.ts.map +1 -1
  279. package/dist/web/auth/TokenPage.js +88 -31
  280. package/dist/web/auth/UsageAndTokensPage.d.ts +2 -0
  281. package/dist/web/auth/UsageAndTokensPage.d.ts.map +1 -0
  282. package/dist/web/auth/UsageAndTokensPage.js +157 -0
  283. package/dist/web/auth/UsagePage.d.ts +2 -0
  284. package/dist/web/auth/UsagePage.d.ts.map +1 -0
  285. package/dist/web/auth/UsagePage.js +62 -0
  286. package/dist/web/auth/components/ApiKeyFilterSelect.d.ts +13 -0
  287. package/dist/web/auth/components/ApiKeyFilterSelect.d.ts.map +1 -0
  288. package/dist/web/auth/components/ApiKeyFilterSelect.js +16 -0
  289. package/dist/web/auth/components/ModelUsageTable.d.ts +19 -0
  290. package/dist/web/auth/components/ModelUsageTable.d.ts.map +1 -0
  291. package/dist/web/auth/components/ModelUsageTable.js +37 -0
  292. package/dist/web/auth/components/PurchaseButton.d.ts +7 -0
  293. package/dist/web/auth/components/PurchaseButton.d.ts.map +1 -0
  294. package/dist/web/auth/components/PurchaseButton.js +13 -0
  295. package/dist/web/auth/components/TokenHistoryCard.d.ts +20 -0
  296. package/dist/web/auth/components/TokenHistoryCard.d.ts.map +1 -0
  297. package/dist/web/auth/components/TokenHistoryCard.js +76 -0
  298. package/dist/web/auth/components/TokenKpiGrid.d.ts +24 -0
  299. package/dist/web/auth/components/TokenKpiGrid.d.ts.map +1 -0
  300. package/dist/web/auth/components/TokenKpiGrid.js +38 -0
  301. package/dist/web/auth/components/UsageByDayChart.d.ts +11 -0
  302. package/dist/web/auth/components/UsageByDayChart.d.ts.map +1 -0
  303. package/dist/web/auth/components/UsageByDayChart.js +32 -0
  304. package/dist/web/auth/components/UsageByModelBarChart.d.ts +12 -0
  305. package/dist/web/auth/components/UsageByModelBarChart.d.ts.map +1 -0
  306. package/dist/web/auth/components/UsageByModelBarChart.js +32 -0
  307. package/dist/web/auth/components/WalletStatusCard.d.ts +9 -0
  308. package/dist/web/auth/components/WalletStatusCard.d.ts.map +1 -0
  309. package/dist/web/auth/components/WalletStatusCard.js +50 -0
  310. package/dist/web/components/ImageGenerative.d.ts +3 -1
  311. package/dist/web/components/ImageGenerative.d.ts.map +1 -1
  312. package/dist/web/components/ImageGenerative.js +139 -52
  313. package/dist/web/components/TextareaGenerative.d.ts +3 -1
  314. package/dist/web/components/TextareaGenerative.d.ts.map +1 -1
  315. package/dist/web/components/TextareaGenerative.js +10 -5
  316. package/dist/web/public/PromptDetailPage.d.ts +25 -0
  317. package/dist/web/public/PromptDetailPage.d.ts.map +1 -0
  318. package/dist/web/public/PromptDetailPage.js +71 -0
  319. package/dist/web/public/PromptDetailPageServer.d.ts +15 -0
  320. package/dist/web/public/PromptDetailPageServer.d.ts.map +1 -0
  321. package/dist/web/public/PromptDetailPageServer.js +68 -0
  322. package/dist/web/public/PublicPromptsPage.d.ts +5 -0
  323. package/dist/web/public/PublicPromptsPage.d.ts.map +1 -0
  324. package/dist/web/public/PublicPromptsPage.js +110 -0
  325. package/dist/web/public/PurchaseTokensPage.d.ts +2 -0
  326. package/dist/web/public/PurchaseTokensPage.d.ts.map +1 -0
  327. package/dist/web/public/PurchaseTokensPage.js +98 -0
  328. package/dist/web/public/UserAvatar.d.ts +13 -0
  329. package/dist/web/public/UserAvatar.d.ts.map +1 -0
  330. package/dist/web/public/UserAvatar.js +13 -0
  331. package/dist/web/public/UserDetailPageServer.d.ts +15 -0
  332. package/dist/web/public/UserDetailPageServer.d.ts.map +1 -0
  333. package/dist/web/public/UserDetailPageServer.js +31 -0
  334. package/dist/web/public/UserPromptsPage.d.ts +9 -0
  335. package/dist/web/public/UserPromptsPage.d.ts.map +1 -0
  336. package/dist/web/public/UserPromptsPage.js +112 -0
  337. package/dist/web/public/UserPromptsPageServer.d.ts +15 -0
  338. package/dist/web/public/UserPromptsPageServer.d.ts.map +1 -0
  339. package/dist/web/public/UserPromptsPageServer.js +31 -0
  340. package/package.json +18 -9
  341. package/supabase/migrations/20251125000000_ai_tokens.sql +7 -0
  342. package/supabase/migrations/20260123100002_user_token_quota_monthly copy.sql +173 -0
  343. package/supabase/migrations/20260128100003_update_and_add_table.sql +368 -0
  344. package/supabase/migrations/20260128120000_seed_providers_models.sql +78 -0
  345. package/supabase/migrations/20260128131405_add_api_key_id_to_ledgers.sql +41 -0
  346. package/supabase/migrations/20260128140000_ai_artifacts_storage.sql +99 -0
  347. package/supabase/migrations/20260128140002_ai_user_settings.sql +57 -0
  348. package/supabase/migrations/20260128150000_drop_ai_user_settings.sql +21 -0
  349. package/supabase/migrations/20260128160000_wallet_billing_system.sql +192 -0
  350. package/supabase/migrations/20260128160001_wallet_rpc_functions.sql +165 -0
  351. package/supabase/migrations/20260128170000_add_pack_coef_to_token_packs.sql +30 -0
  352. package/supabase/migrations/20260129120000_wallet_view_rpc.sql +41 -0
  353. package/supabase/migrations/20260129220003_update_pack_margins.sql +31 -0
  354. package/supabase/migrations/20260129330004_ai_user_prompts.sql +151 -0
  355. package/supabase/migrations/20260129330005_ai_prompts_ip_tracking.sql +92 -0
  356. package/supabase/migrations/20260129330006_ai_prompts_slug.sql +64 -0
  357. package/supabase/migrations/20260129330007_ai_prompts_view_slug.sql +26 -0
  358. package/supabase/migrations/20260129440000_ai_prompts_view_username.sql +33 -0
  359. package/supabase/migrations/20260129450000_ai_prompts_add_lang.sql +40 -0
  360. package/supabase/migrations/20260131000000_extract_model_prompt_in_ledger.sql +92 -0
  361. package/supabase/migrations/20260131140000_fix_duplicate_purchases.sql +64 -0
  362. package/supabase/migrations/20260201120000_module-ai_default_models.sql +63 -0
  363. package/supabase/migrations/20260201130000_module-ai_remove_provider_tables.sql +17 -0
  364. package/supabase/migrations-down/20251217120000_user_token_quota_monthly.sql +34 -0
  365. package/supabase/migrations-down/20260128131405_add_api_key_id_to_ledgers.sql +25 -0
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Logging utilities for public API v1
3
+ * Handles ai_call_log inserts and api_keys.last_used_at updates
4
+ */
5
+ import { getSupabaseServiceClient } from "@lastbrain/core/server";
6
+ import { logger } from "@lastbrain/core";
7
+ /**
8
+ * Insert a log entry in ai_call_log
9
+ */
10
+ export async function logApiCall(params) {
11
+ try {
12
+ const supabase = await getSupabaseServiceClient();
13
+ const { error } = await supabase.from("ai_call_log").insert({
14
+ owner_id: params.ownerId,
15
+ api_key_id: params.apiKeyId,
16
+ endpoint: params.endpoint,
17
+ provider: params.provider,
18
+ model: params.model,
19
+ tokens_in: params.tokensIn,
20
+ tokens_out: params.tokensOut,
21
+ tokens_total: params.tokensTotal,
22
+ latency_ms: params.latencyMs,
23
+ status_code: params.statusCode,
24
+ error_code: params.errorCode,
25
+ meta: {
26
+ request_id: params.requestId,
27
+ ...(params.meta || {}),
28
+ },
29
+ });
30
+ if (error) {
31
+ logger.error("[logApiCall] Failed to insert ai_call_log", {
32
+ error,
33
+ params,
34
+ });
35
+ }
36
+ else {
37
+ logger.debug("[logApiCall] Logged API call", {
38
+ endpoint: params.endpoint,
39
+ statusCode: params.statusCode,
40
+ requestId: params.requestId,
41
+ });
42
+ }
43
+ }
44
+ catch (error) {
45
+ logger.error("[logApiCall] Exception logging API call", error);
46
+ }
47
+ }
48
+ /**
49
+ * Update api_keys.last_used_at
50
+ */
51
+ export async function updateApiKeyLastUsed(apiKey) {
52
+ try {
53
+ const supabase = await getSupabaseServiceClient();
54
+ const { error } = await supabase
55
+ .from("api_keys")
56
+ .update({ last_used_at: new Date().toISOString() })
57
+ .eq("id", apiKey.id);
58
+ if (error) {
59
+ logger.warn("[updateApiKeyLastUsed] Failed to update", {
60
+ error,
61
+ apiKeyId: apiKey.id,
62
+ });
63
+ }
64
+ }
65
+ catch (error) {
66
+ logger.warn("[updateApiKeyLastUsed] Exception", error);
67
+ }
68
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Quota and rate limit checking for public API v1
3
+ */
4
+ import type { ApiKeyData } from "./auth";
5
+ export interface QuotaCheckResult {
6
+ success: boolean;
7
+ error?: string;
8
+ errorCode?: string;
9
+ balance?: number;
10
+ remainingQuota?: number;
11
+ remainingPurchased?: number;
12
+ }
13
+ /**
14
+ * Check if user has sufficient tokens and within limits
15
+ */
16
+ export declare function checkQuota(apiKey: ApiKeyData, estimatedTokens: number): Promise<QuotaCheckResult>;
17
+ /**
18
+ * Check user_ai_settings for max_tokens_per_call limit
19
+ */
20
+ export declare function checkMaxTokensPerCall(ownerId: string, requestedMaxTokens: number): Promise<{
21
+ allowed: boolean;
22
+ limit?: number;
23
+ }>;
24
+ //# sourceMappingURL=quota.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quota.d.ts","sourceRoot":"","sources":["../../../../../src/api/public/v1/_lib/quota.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEzC,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,UAAU,EAClB,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,gBAAgB,CAAC,CAwG3B;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,MAAM,EACf,kBAAkB,EAAE,MAAM,GACzB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAsB/C"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Quota and rate limit checking for public API v1
3
+ */
4
+ import { getSupabaseServiceClient } from "@lastbrain/core/server";
5
+ import { logger } from "@lastbrain/core";
6
+ /**
7
+ * Check if user has sufficient tokens and within limits
8
+ */
9
+ export async function checkQuota(apiKey, estimatedTokens) {
10
+ try {
11
+ const supabase = await getSupabaseServiceClient();
12
+ // 1. Check token balance (purchased)
13
+ const { data: balanceData } = await supabase
14
+ .from("user_token_balance_v")
15
+ .select("balance")
16
+ .eq("owner_id", apiKey.owner_id)
17
+ .single();
18
+ const purchasedBalance = balanceData?.balance || 0;
19
+ // 2. Check quota balance (monthly included)
20
+ const { data: quotaData } = await supabase
21
+ .from("user_token_quota_monthly")
22
+ .select("effective_included_tokens, used_included_tokens, period_start, period_end")
23
+ .eq("owner_id", apiKey.owner_id)
24
+ .single();
25
+ let remainingQuota = 0;
26
+ if (quotaData) {
27
+ const now = new Date();
28
+ const periodStart = quotaData.period_start
29
+ ? new Date(quotaData.period_start)
30
+ : null;
31
+ const periodEnd = quotaData.period_end
32
+ ? new Date(quotaData.period_end)
33
+ : null;
34
+ const isActive = periodStart && periodEnd && now >= periodStart && now <= periodEnd;
35
+ if (isActive) {
36
+ remainingQuota = Math.max(0, quotaData.effective_included_tokens -
37
+ (quotaData.used_included_tokens || 0));
38
+ }
39
+ }
40
+ const totalBalance = purchasedBalance + remainingQuota;
41
+ // 3. Check daily limit (if set on API key)
42
+ if (apiKey.daily_token_limit !== null) {
43
+ const today = new Date();
44
+ today.setHours(0, 0, 0, 0);
45
+ const { data: todayUsage } = await supabase
46
+ .from("ai_call_log")
47
+ .select("tokens_total")
48
+ .eq("api_key_id", apiKey.id)
49
+ .gte("created_at", today.toISOString());
50
+ const usedToday = todayUsage?.reduce((sum, log) => sum + (log.tokens_total || 0), 0) || 0;
51
+ const dailyLimit = Number(apiKey.daily_token_limit);
52
+ if (usedToday + estimatedTokens > dailyLimit) {
53
+ return {
54
+ success: false,
55
+ error: `Daily token limit exceeded. Used: ${usedToday}, Limit: ${dailyLimit}`,
56
+ errorCode: "DAILY_LIMIT_EXCEEDED",
57
+ balance: totalBalance,
58
+ remainingQuota,
59
+ remainingPurchased: purchasedBalance,
60
+ };
61
+ }
62
+ }
63
+ // 4. Check if insufficient balance
64
+ if (totalBalance < estimatedTokens) {
65
+ // Block if purchased balance is already negative
66
+ if (purchasedBalance < 0) {
67
+ return {
68
+ success: false,
69
+ error: "Insufficient tokens. Please purchase more tokens.",
70
+ errorCode: "INSUFFICIENT_TOKENS",
71
+ balance: totalBalance,
72
+ remainingQuota,
73
+ remainingPurchased: purchasedBalance,
74
+ };
75
+ }
76
+ // If positive but insufficient, allow one-time overdraft (debit will handle it)
77
+ logger.info("[checkQuota] Allowing overdraft for user", apiKey.owner_id);
78
+ }
79
+ return {
80
+ success: true,
81
+ balance: totalBalance,
82
+ remainingQuota,
83
+ remainingPurchased: purchasedBalance,
84
+ };
85
+ }
86
+ catch (error) {
87
+ logger.error("[checkQuota] Error checking quota", error);
88
+ return {
89
+ success: false,
90
+ error: "Internal error checking quota",
91
+ errorCode: "INTERNAL_ERROR",
92
+ };
93
+ }
94
+ }
95
+ /**
96
+ * Check user_ai_settings for max_tokens_per_call limit
97
+ */
98
+ export async function checkMaxTokensPerCall(ownerId, requestedMaxTokens) {
99
+ try {
100
+ const supabase = await getSupabaseServiceClient();
101
+ const { data: settings } = await supabase
102
+ .from("user_ai_settings")
103
+ .select("max_tokens_per_call")
104
+ .eq("owner_id", ownerId)
105
+ .single();
106
+ if (settings?.max_tokens_per_call) {
107
+ const limit = settings.max_tokens_per_call;
108
+ if (requestedMaxTokens > limit) {
109
+ return { allowed: false, limit };
110
+ }
111
+ }
112
+ return { allowed: true };
113
+ }
114
+ catch (error) {
115
+ logger.warn("[checkMaxTokensPerCall] Error, allowing by default", error);
116
+ return { allowed: true };
117
+ }
118
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * AI Provider router for public API v1
3
+ * DEPRECATED: Use ai-generation-service.ts instead
4
+ *
5
+ * This file is kept for backward compatibility only
6
+ * All functions now delegate to the centralized service
7
+ */
8
+ export interface TextGenerationParams {
9
+ prompt: string;
10
+ model?: string;
11
+ maxTokens?: number;
12
+ temperature?: number;
13
+ systemPrompt?: string;
14
+ actionType?: "generate-text" | "generate-recipe-text" | "autocomplete";
15
+ }
16
+ export interface TextGenerationResult {
17
+ success: boolean;
18
+ text?: string;
19
+ tokensIn?: number;
20
+ tokensOut?: number;
21
+ tokensTotal?: number;
22
+ model?: string;
23
+ provider?: string;
24
+ error?: string;
25
+ }
26
+ export interface ImageGenerationParams {
27
+ prompt: string;
28
+ model?: string;
29
+ size?: string;
30
+ quality?: string;
31
+ }
32
+ export interface ImageGenerationResult {
33
+ success: boolean;
34
+ imageUrl?: string;
35
+ revisedPrompt?: string;
36
+ tokensUsed?: number;
37
+ model?: string;
38
+ provider?: string;
39
+ error?: string;
40
+ }
41
+ /**
42
+ * Generate text using Vercel AI SDK with model filtering
43
+ */
44
+ /**
45
+ * Generate text - delegates to centralized service
46
+ * @deprecated Use generateTextWithBilling from ai-generation-service directly
47
+ */
48
+ export declare function generateText(ownerId: string, params: TextGenerationParams): Promise<TextGenerationResult>;
49
+ /**
50
+ * Generate images - delegates to centralized service
51
+ * @deprecated Use generateImageWithBilling from ai-generation-service directly
52
+ */
53
+ export declare function generateImage(ownerId: string, params: ImageGenerationParams): Promise<ImageGenerationResult>;
54
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../../../../src/api/public/v1/_lib/router.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,eAAe,GAAG,sBAAsB,GAAG,cAAc,CAAC;CACxE;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA4ED;;GAEG;AACH;;;GAGG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,oBAAoB,CAAC,CAyB/B;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC,CA4BhC"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * AI Provider router for public API v1
3
+ * DEPRECATED: Use ai-generation-service.ts instead
4
+ *
5
+ * This file is kept for backward compatibility only
6
+ * All functions now delegate to the centralized service
7
+ */
8
+ import { logger } from "@lastbrain/core";
9
+ import { getSupabaseServiceClient } from "@lastbrain/core/server";
10
+ import { generateTextWithBilling, generateImageWithBilling, } from "../../../../server/ai-generation-service";
11
+ import { getGlobalAISettings } from "../../../../server/global-settings";
12
+ /**
13
+ * Get user's default text settings or use GLOBAL system defaults
14
+ */
15
+ async function getTextSettings(ownerId) {
16
+ try {
17
+ const supabase = await getSupabaseServiceClient();
18
+ const { data: settings } = await supabase
19
+ .from("user_ai_settings")
20
+ .select("default_text_provider, default_text_model, mode")
21
+ .eq("owner_id", ownerId)
22
+ .single();
23
+ if (settings?.mode === "manual" && settings.default_text_provider) {
24
+ return {
25
+ provider: settings.default_text_provider,
26
+ model: settings.default_text_model || "gpt-4.1-mini",
27
+ };
28
+ }
29
+ }
30
+ catch (error) {
31
+ logger.warn("[getTextSettings] Could not fetch user settings, using global defaults", error);
32
+ }
33
+ // Use GLOBAL defaults instead of hardcoded values
34
+ const globalSettings = await getGlobalAISettings();
35
+ return {
36
+ provider: globalSettings.default_text_provider,
37
+ model: globalSettings.default_text_model,
38
+ };
39
+ }
40
+ /**
41
+ * Get user's default image settings or use GLOBAL system defaults
42
+ */
43
+ async function getImageSettings(ownerId) {
44
+ try {
45
+ const supabase = await getSupabaseServiceClient();
46
+ const { data: settings } = await supabase
47
+ .from("user_ai_settings")
48
+ .select("default_image_provider, default_image_model, mode")
49
+ .eq("owner_id", ownerId)
50
+ .single();
51
+ if (settings?.mode === "manual" && settings.default_image_provider) {
52
+ return {
53
+ provider: settings.default_image_provider,
54
+ model: settings.default_image_model || "gemini-2.5-flash-image",
55
+ };
56
+ }
57
+ }
58
+ catch (error) {
59
+ logger.warn("[getImageSettings] Could not fetch user settings, using global defaults", error);
60
+ }
61
+ // Use GLOBAL defaults instead of hardcoded values
62
+ const globalSettings = await getGlobalAISettings();
63
+ return {
64
+ provider: globalSettings.default_image_provider,
65
+ model: globalSettings.default_image_model,
66
+ };
67
+ }
68
+ /**
69
+ * Generate text using Vercel AI SDK with model filtering
70
+ */
71
+ /**
72
+ * Generate text - delegates to centralized service
73
+ * @deprecated Use generateTextWithBilling from ai-generation-service directly
74
+ */
75
+ export async function generateText(ownerId, params) {
76
+ logger.info(`[router] Delegating to generateTextWithBilling for user ${ownerId}`);
77
+ const result = await generateTextWithBilling(ownerId, {
78
+ prompt: params.prompt,
79
+ model: params.model,
80
+ systemPrompt: params.systemPrompt,
81
+ maxTokens: params.maxTokens,
82
+ temperature: params.temperature,
83
+ actionType: params.actionType || "generate-text",
84
+ });
85
+ // Map to legacy format for backward compatibility
86
+ return {
87
+ success: result.success,
88
+ text: result.text,
89
+ tokensIn: result.inputTokens,
90
+ tokensOut: result.outputTokens,
91
+ tokensTotal: 0, // Deprecated
92
+ model: result.model,
93
+ provider: result.provider,
94
+ error: result.error,
95
+ };
96
+ }
97
+ /**
98
+ * Generate images - delegates to centralized service
99
+ * @deprecated Use generateImageWithBilling from ai-generation-service directly
100
+ */
101
+ export async function generateImage(ownerId, params) {
102
+ logger.info(`[router] Delegating to generateImageWithBilling for user ${ownerId}`);
103
+ const result = await generateImageWithBilling(ownerId, {
104
+ prompt: params.prompt,
105
+ model: params.model,
106
+ size: params.size,
107
+ quality: params.quality,
108
+ actionType: "generate-image",
109
+ });
110
+ // Map to legacy format
111
+ return {
112
+ success: result.success,
113
+ imageUrl: result.imageUrl,
114
+ revisedPrompt: result.revisedPrompt,
115
+ model: result.model,
116
+ provider: result.provider,
117
+ error: result.error,
118
+ };
119
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * POST /api/public/v1/connect
3
+ * Authenticate with email/password and receive a dev API key
4
+ */
5
+ import { NextRequest, NextResponse } from "next/server";
6
+ export declare function POST(request: NextRequest): Promise<NextResponse<import("./_lib/errors").ApiError> | NextResponse<{
7
+ request_id: `${string}-${string}-${string}-${string}-${string}`;
8
+ message: string;
9
+ api_key_id: any;
10
+ prefix: any;
11
+ user_id: string;
12
+ dashboard_url: string;
13
+ }> | NextResponse<{
14
+ request_id: `${string}-${string}-${string}-${string}-${string}`;
15
+ api_key: string;
16
+ prefix: string;
17
+ user_id: string;
18
+ note: string;
19
+ }>>;
20
+ //# sourceMappingURL=connect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../../../src/api/public/v1/connect.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAsBxD,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;;;;;;;;;;IAwI9C"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * POST /api/public/v1/connect
3
+ * Authenticate with email/password and receive a dev API key
4
+ */
5
+ import { NextResponse } from "next/server";
6
+ import { z } from "zod";
7
+ import { randomUUID, randomBytes } from "crypto";
8
+ import { logger } from "@lastbrain/core";
9
+ import { getSupabaseServiceClient } from "@lastbrain/core/server";
10
+ import { createErrorResponse } from "./_lib/errors";
11
+ import { hashApiKey } from "./_lib/auth";
12
+ const ConnectSchema = z.object({
13
+ email: z.string().email(),
14
+ password: z.string().min(1),
15
+ });
16
+ /**
17
+ * Generate a random API key
18
+ */
19
+ function generateApiKey() {
20
+ const key = `lb_${randomBytes(32).toString("hex")}`;
21
+ const prefix = key.substring(0, 10);
22
+ return { key, prefix };
23
+ }
24
+ export async function POST(request) {
25
+ const requestId = randomUUID();
26
+ let statusCode = 200;
27
+ try {
28
+ // 1. Parse and validate request body
29
+ const body = await request.json();
30
+ const validation = ConnectSchema.safeParse(body);
31
+ if (!validation.success) {
32
+ const { response, status } = createErrorResponse("VALIDATION_ERROR", validation.error.errors.map((e) => e.message).join(", "), requestId, undefined, 400);
33
+ statusCode = status;
34
+ return NextResponse.json(response, { status });
35
+ }
36
+ const { email, password } = validation.data;
37
+ // 2. Authenticate user with Supabase Auth
38
+ const supabase = await getSupabaseServiceClient();
39
+ const { data: authData, error: authError } = await supabase.auth.signInWithPassword({
40
+ email,
41
+ password,
42
+ });
43
+ if (authError || !authData.user) {
44
+ logger.warn("[connect] Authentication failed", {
45
+ email,
46
+ error: authError?.message,
47
+ });
48
+ const { response, status } = createErrorResponse("INVALID_CREDENTIALS", "Invalid email or password", requestId, undefined, 401);
49
+ statusCode = status;
50
+ return NextResponse.json(response, { status });
51
+ }
52
+ const userId = authData.user.id;
53
+ // 3. Check if user already has a dev API key
54
+ const { data: existingKeys } = await supabase
55
+ .from("api_keys")
56
+ .select("*")
57
+ .eq("owner_id", userId)
58
+ .eq("env", "dev")
59
+ .eq("is_active", true)
60
+ .limit(1);
61
+ if (existingKeys && existingKeys.length > 0) {
62
+ // Return existing key info (but not the raw key)
63
+ const existingKey = existingKeys[0];
64
+ return NextResponse.json({
65
+ request_id: requestId,
66
+ message: "You already have an active dev API key. For security reasons, existing keys cannot be retrieved. Please create a new key or use the dashboard to manage your keys.",
67
+ api_key_id: existingKey.id,
68
+ prefix: existingKey.prefix,
69
+ user_id: userId,
70
+ dashboard_url: process.env.NEXT_PUBLIC_APP_URL + "/auth/module-ai/api-keys",
71
+ });
72
+ }
73
+ // 4. Generate new dev API key
74
+ const { key: rawKey, prefix } = generateApiKey();
75
+ const keyHash = hashApiKey(rawKey);
76
+ const { data: newKey, error: insertError } = await supabase
77
+ .from("api_keys")
78
+ .insert({
79
+ owner_id: userId,
80
+ name: "Dev API Key (auto-created)",
81
+ key_hash: keyHash,
82
+ prefix,
83
+ env: "dev",
84
+ scopes: ["text", "image", "status"],
85
+ rate_limit_rpm: 60,
86
+ daily_token_limit: null,
87
+ is_active: true,
88
+ })
89
+ .select()
90
+ .single();
91
+ if (insertError || !newKey) {
92
+ logger.error("[connect] Failed to create API key", {
93
+ userId,
94
+ error: insertError,
95
+ });
96
+ const { response, status } = createErrorResponse("INTERNAL_ERROR", "Failed to create API key", requestId, undefined, 500);
97
+ statusCode = status;
98
+ return NextResponse.json(response, { status });
99
+ }
100
+ // 5. Return the raw API key (ONLY TIME IT'S SHOWN)
101
+ logger.info("[connect] Created new dev API key", {
102
+ userId,
103
+ keyId: newKey.id,
104
+ });
105
+ return NextResponse.json({
106
+ request_id: requestId,
107
+ api_key: rawKey,
108
+ prefix,
109
+ user_id: userId,
110
+ note: "Store this API key securely. It will not be shown again.",
111
+ });
112
+ }
113
+ catch (error) {
114
+ logger.error("[connect] Unexpected error", error);
115
+ statusCode = 500;
116
+ const { response, status } = createErrorResponse("INTERNAL_ERROR", error.message || "Internal server error", requestId, undefined, 500);
117
+ return NextResponse.json(response, { status });
118
+ }
119
+ }