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