@kyro-cms/core 0.10.4 → 0.10.6

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 (301) hide show
  1. package/dist/api-handler-graphql.cjs +1 -49
  2. package/dist/api-handler-graphql.js +1 -46
  3. package/dist/api-handler-trpc.cjs +1 -38
  4. package/dist/api-handler-trpc.js +1 -36
  5. package/dist/api-handler.cjs +1 -44
  6. package/dist/api-handler.js +1 -27
  7. package/dist/bootstrap-HIAV5ZFT.cjs +1 -0
  8. package/dist/bootstrap-P7QPQYQP.js +1 -0
  9. package/dist/chunk-2BTRHVT3.cjs +20 -0
  10. package/dist/chunk-2W4FK6CJ.js +122 -0
  11. package/dist/chunk-4ORJMSJU.cjs +1 -0
  12. package/dist/chunk-65G73T7Z.cjs +2 -0
  13. package/dist/chunk-7N6NKCRT.js +1 -0
  14. package/dist/chunk-7WOX27LK.js +259 -0
  15. package/dist/chunk-AE6GXUT7.js +1 -0
  16. package/dist/chunk-AJAZ7D3W.cjs +259 -0
  17. package/dist/chunk-ATLSIYRH.cjs +1 -0
  18. package/dist/chunk-AU6IWTBO.js +2 -0
  19. package/dist/chunk-CDL7ARXT.js +133 -0
  20. package/dist/chunk-CUTFS6N2.cjs +1 -0
  21. package/dist/chunk-CVVW4LXZ.cjs +1 -0
  22. package/dist/chunk-DGJUBN33.js +1 -0
  23. package/dist/chunk-DV4LFLPL.cjs +1 -0
  24. package/dist/chunk-EEWCANFX.js +1 -0
  25. package/dist/chunk-ELICKLFM.cjs +3 -0
  26. package/dist/chunk-EQUQJBPP.cjs +2 -0
  27. package/dist/chunk-ET6NN2V2.cjs +122 -0
  28. package/dist/chunk-F7P36SLX.cjs +74 -0
  29. package/dist/chunk-F7SR5IDT.cjs +1 -0
  30. package/dist/chunk-FO3HLBC7.js +1 -0
  31. package/dist/chunk-FS7GL2BR.js +73 -0
  32. package/dist/chunk-G6A7OSIG.js +2 -0
  33. package/dist/chunk-GFU7AOI3.cjs +1 -0
  34. package/dist/chunk-GYQMNXXW.cjs +114 -0
  35. package/dist/chunk-HLSB2MCK.js +1 -0
  36. package/dist/chunk-HVXO3OFB.cjs +1 -0
  37. package/dist/chunk-IFJJ44OQ.cjs +133 -0
  38. package/dist/chunk-ISWMETHJ.cjs +1 -0
  39. package/dist/chunk-K6WUOU6P.js +1 -0
  40. package/dist/chunk-KDNYHLD2.js +1 -0
  41. package/dist/chunk-KIW36TG7.cjs +86 -0
  42. package/dist/chunk-L6CZYN3L.cjs +20 -0
  43. package/dist/chunk-LRGAPGG3.js +74 -0
  44. package/dist/chunk-LZ6YFYQM.js +20 -0
  45. package/dist/chunk-MIOMMB23.cjs +1 -0
  46. package/dist/chunk-NGBJKZMT.js +90 -0
  47. package/dist/chunk-NRLIGDYF.js +1 -0
  48. package/dist/chunk-NY47CFVP.cjs +1 -0
  49. package/dist/chunk-NZ35PV5R.js +2 -0
  50. package/dist/chunk-OG7CNQGU.js +1 -0
  51. package/dist/chunk-OQD3WG6Z.cjs +1 -0
  52. package/dist/chunk-P7CESLDJ.js +1 -0
  53. package/dist/chunk-PP5S2UBV.js +0 -0
  54. package/dist/chunk-Q3Q7BJVQ.cjs +1 -0
  55. package/dist/chunk-Q4CSLXCO.cjs +90 -0
  56. package/dist/chunk-QRXDCV6K.js +0 -0
  57. package/dist/chunk-R7DVVXXI.cjs +73 -0
  58. package/dist/chunk-RHCWCG3O.js +114 -0
  59. package/dist/chunk-SL3LZQCF.js +1 -0
  60. package/dist/chunk-UBHFUFD4.cjs +1 -0
  61. package/dist/chunk-UBRYJP2G.js +1 -0
  62. package/dist/chunk-UDSJGML2.cjs +2 -0
  63. package/dist/chunk-UHFDH3PG.js +20 -0
  64. package/dist/chunk-UJNSOOYZ.cjs +1 -0
  65. package/dist/chunk-URCLFIME.js +86 -0
  66. package/dist/chunk-UVTS2IWM.js +1 -0
  67. package/dist/chunk-V65NPSR5.cjs +1 -0
  68. package/dist/chunk-WIZ6C5QZ.cjs +1 -0
  69. package/dist/chunk-ZAQOSRP6.js +1 -0
  70. package/dist/chunk-ZELCS7IC.js +3 -0
  71. package/dist/cli/index.cjs +35 -405
  72. package/dist/cli/index.js +35 -402
  73. package/dist/client.cjs +1 -55
  74. package/dist/client.js +1 -6
  75. package/dist/drizzle/index.cjs +1 -50
  76. package/dist/drizzle/index.d.cts +1 -2
  77. package/dist/drizzle/index.d.ts +1 -2
  78. package/dist/drizzle/index.js +1 -9
  79. package/dist/fields/index.cjs +1 -89
  80. package/dist/fields/index.js +1 -4
  81. package/dist/graphql/index.cjs +1 -31
  82. package/dist/graphql/index.d.cts +1 -1
  83. package/dist/graphql/index.d.ts +1 -1
  84. package/dist/graphql/index.js +1 -6
  85. package/dist/{index-jLs0zlft.d.ts → index-DXr33e0x.d.ts} +1 -1
  86. package/dist/{index-DHbywDs-.d.cts → index-DuuUzWBq.d.cts} +1 -1
  87. package/dist/index.cjs +29 -2464
  88. package/dist/index.d.cts +2 -2
  89. package/dist/index.d.ts +2 -2
  90. package/dist/index.js +29 -1929
  91. package/dist/integration.cjs +1 -10
  92. package/dist/integration.js +1 -4
  93. package/dist/media-NSSMOB7N.cjs +1 -0
  94. package/dist/media-RNMCZYPZ.js +1 -0
  95. package/dist/mongo-auth-adapter-JYJ4GBZE.cjs +1 -0
  96. package/dist/mongo-auth-adapter-XI7EPURZ.js +1 -0
  97. package/dist/mongodb/index.cjs +1 -29
  98. package/dist/mongodb/index.d.cts +1 -2
  99. package/dist/mongodb/index.d.ts +1 -2
  100. package/dist/mongodb/index.js +1 -8
  101. package/dist/postgres-auth-adapter-E7OGMDD4.cjs +1 -0
  102. package/dist/postgres-auth-adapter-F4KLOGAG.js +1 -0
  103. package/dist/redis-adapter-AQJVABYT.cjs +1 -0
  104. package/dist/redis-adapter-AUKOV74G.js +1 -0
  105. package/dist/rest/index.cjs +1 -31
  106. package/dist/rest/index.d.cts +1 -1
  107. package/dist/rest/index.d.ts +1 -1
  108. package/dist/rest/index.js +1 -18
  109. package/dist/schema-5XNROI7I.js +1 -0
  110. package/dist/schema-6O4JTJRU.cjs +1 -0
  111. package/dist/sqlite-adapter-5XFLNJDH.js +1 -0
  112. package/dist/sqlite-adapter-W53Q32N5.cjs +1 -0
  113. package/dist/templates/index.cjs +1 -207
  114. package/dist/templates/index.js +1 -6
  115. package/dist/trpc/index.cjs +1 -48
  116. package/dist/trpc/index.js +1 -7
  117. package/dist/ws/index.cjs +1 -25
  118. package/dist/ws/index.js +1 -4
  119. package/package.json +44 -13
  120. package/dist/api-handler-graphql.cjs.map +0 -1
  121. package/dist/api-handler-graphql.js.map +0 -1
  122. package/dist/api-handler-trpc.cjs.map +0 -1
  123. package/dist/api-handler-trpc.js.map +0 -1
  124. package/dist/api-handler.cjs.map +0 -1
  125. package/dist/api-handler.js.map +0 -1
  126. package/dist/bootstrap-FUAPY2OF.cjs +0 -32
  127. package/dist/bootstrap-FUAPY2OF.cjs.map +0 -1
  128. package/dist/bootstrap-OXNVM7FP.js +0 -7
  129. package/dist/bootstrap-OXNVM7FP.js.map +0 -1
  130. package/dist/chunk-2HFJUUFZ.js +0 -250
  131. package/dist/chunk-2HFJUUFZ.js.map +0 -1
  132. package/dist/chunk-3AKCVYDB.js +0 -162
  133. package/dist/chunk-3AKCVYDB.js.map +0 -1
  134. package/dist/chunk-3KTWGODI.cjs +0 -178
  135. package/dist/chunk-3KTWGODI.cjs.map +0 -1
  136. package/dist/chunk-3YELQL7Z.cjs +0 -4
  137. package/dist/chunk-3YELQL7Z.cjs.map +0 -1
  138. package/dist/chunk-4CV4JOE5.js +0 -24
  139. package/dist/chunk-4CV4JOE5.js.map +0 -1
  140. package/dist/chunk-4OCMVGEY.cjs +0 -173
  141. package/dist/chunk-4OCMVGEY.cjs.map +0 -1
  142. package/dist/chunk-4PWRCMTQ.cjs +0 -15
  143. package/dist/chunk-4PWRCMTQ.cjs.map +0 -1
  144. package/dist/chunk-53NYVYVX.js +0 -3243
  145. package/dist/chunk-53NYVYVX.js.map +0 -1
  146. package/dist/chunk-5GFQPHGT.cjs +0 -156
  147. package/dist/chunk-5GFQPHGT.cjs.map +0 -1
  148. package/dist/chunk-5RPRLTTN.js +0 -861
  149. package/dist/chunk-5RPRLTTN.js.map +0 -1
  150. package/dist/chunk-74YSDAWE.cjs +0 -2749
  151. package/dist/chunk-74YSDAWE.cjs.map +0 -1
  152. package/dist/chunk-7OGPN7MP.js +0 -352
  153. package/dist/chunk-7OGPN7MP.js.map +0 -1
  154. package/dist/chunk-7OS7TX2Q.cjs +0 -495
  155. package/dist/chunk-7OS7TX2Q.cjs.map +0 -1
  156. package/dist/chunk-ADLJSJSN.cjs +0 -13
  157. package/dist/chunk-ADLJSJSN.cjs.map +0 -1
  158. package/dist/chunk-B6X3LN25.cjs +0 -6486
  159. package/dist/chunk-B6X3LN25.cjs.map +0 -1
  160. package/dist/chunk-BYBMTIMT.js +0 -419
  161. package/dist/chunk-BYBMTIMT.js.map +0 -1
  162. package/dist/chunk-CF7OL6HR.js +0 -355
  163. package/dist/chunk-CF7OL6HR.js.map +0 -1
  164. package/dist/chunk-CJX74IYK.js +0 -489
  165. package/dist/chunk-CJX74IYK.js.map +0 -1
  166. package/dist/chunk-CKVOU6MX.cjs +0 -52
  167. package/dist/chunk-CKVOU6MX.cjs.map +0 -1
  168. package/dist/chunk-CZLDE2OZ.cjs +0 -28
  169. package/dist/chunk-CZLDE2OZ.cjs.map +0 -1
  170. package/dist/chunk-DPA3KWPY.cjs +0 -205
  171. package/dist/chunk-DPA3KWPY.cjs.map +0 -1
  172. package/dist/chunk-EEJUFDMF.cjs +0 -424
  173. package/dist/chunk-EEJUFDMF.cjs.map +0 -1
  174. package/dist/chunk-FIAMXNTH.js +0 -220
  175. package/dist/chunk-FIAMXNTH.js.map +0 -1
  176. package/dist/chunk-GAOXD3XT.js +0 -175
  177. package/dist/chunk-GAOXD3XT.js.map +0 -1
  178. package/dist/chunk-GTGRLD4Y.js +0 -3
  179. package/dist/chunk-GTGRLD4Y.js.map +0 -1
  180. package/dist/chunk-GUUB5EAG.cjs +0 -223
  181. package/dist/chunk-GUUB5EAG.cjs.map +0 -1
  182. package/dist/chunk-GXFOGU7N.cjs +0 -359
  183. package/dist/chunk-GXFOGU7N.cjs.map +0 -1
  184. package/dist/chunk-GZS4LMR7.js +0 -422
  185. package/dist/chunk-GZS4LMR7.js.map +0 -1
  186. package/dist/chunk-HY6B7B5A.js +0 -150
  187. package/dist/chunk-HY6B7B5A.js.map +0 -1
  188. package/dist/chunk-HZOMOK5Y.js +0 -2728
  189. package/dist/chunk-HZOMOK5Y.js.map +0 -1
  190. package/dist/chunk-IDVRRRAK.cjs +0 -824
  191. package/dist/chunk-IDVRRRAK.cjs.map +0 -1
  192. package/dist/chunk-KC2GDBLS.cjs +0 -84
  193. package/dist/chunk-KC2GDBLS.cjs.map +0 -1
  194. package/dist/chunk-L4EZKIEX.js +0 -185
  195. package/dist/chunk-L4EZKIEX.js.map +0 -1
  196. package/dist/chunk-LNTFRAHF.cjs +0 -473
  197. package/dist/chunk-LNTFRAHF.cjs.map +0 -1
  198. package/dist/chunk-MTIRYI7F.cjs +0 -4
  199. package/dist/chunk-MTIRYI7F.cjs.map +0 -1
  200. package/dist/chunk-N4GN6Q3V.cjs +0 -869
  201. package/dist/chunk-N4GN6Q3V.cjs.map +0 -1
  202. package/dist/chunk-N4H37VN4.cjs +0 -259
  203. package/dist/chunk-N4H37VN4.cjs.map +0 -1
  204. package/dist/chunk-N5QSHUA6.js +0 -1135
  205. package/dist/chunk-N5QSHUA6.js.map +0 -1
  206. package/dist/chunk-NKPKR5BW.cjs +0 -188
  207. package/dist/chunk-NKPKR5BW.cjs.map +0 -1
  208. package/dist/chunk-OIRG6ZRV.cjs +0 -1073
  209. package/dist/chunk-OIRG6ZRV.cjs.map +0 -1
  210. package/dist/chunk-OJ2AM75W.cjs +0 -253
  211. package/dist/chunk-OJ2AM75W.cjs.map +0 -1
  212. package/dist/chunk-OLV7K22H.js +0 -6457
  213. package/dist/chunk-OLV7K22H.js.map +0 -1
  214. package/dist/chunk-OX6GOIPH.js +0 -516
  215. package/dist/chunk-OX6GOIPH.js.map +0 -1
  216. package/dist/chunk-P2YW545G.js +0 -11
  217. package/dist/chunk-P2YW545G.js.map +0 -1
  218. package/dist/chunk-PHJRNPHY.cjs +0 -3291
  219. package/dist/chunk-PHJRNPHY.cjs.map +0 -1
  220. package/dist/chunk-Q72BOAPK.js +0 -817
  221. package/dist/chunk-Q72BOAPK.js.map +0 -1
  222. package/dist/chunk-QU2RFFH4.js +0 -3
  223. package/dist/chunk-QU2RFFH4.js.map +0 -1
  224. package/dist/chunk-RP7VZUEL.js +0 -49
  225. package/dist/chunk-RP7VZUEL.js.map +0 -1
  226. package/dist/chunk-SHTTJMLT.cjs +0 -361
  227. package/dist/chunk-SHTTJMLT.cjs.map +0 -1
  228. package/dist/chunk-SX76DNOO.js +0 -103
  229. package/dist/chunk-SX76DNOO.js.map +0 -1
  230. package/dist/chunk-THBQG64D.cjs +0 -425
  231. package/dist/chunk-THBQG64D.cjs.map +0 -1
  232. package/dist/chunk-TQG7G7YI.cjs +0 -526
  233. package/dist/chunk-TQG7G7YI.cjs.map +0 -1
  234. package/dist/chunk-U3VVLYXG.cjs +0 -189
  235. package/dist/chunk-U3VVLYXG.cjs.map +0 -1
  236. package/dist/chunk-UUDTPZX6.js +0 -193
  237. package/dist/chunk-UUDTPZX6.js.map +0 -1
  238. package/dist/chunk-V5AWATUP.js +0 -1059
  239. package/dist/chunk-V5AWATUP.js.map +0 -1
  240. package/dist/chunk-VAXLFZCV.cjs +0 -110
  241. package/dist/chunk-VAXLFZCV.cjs.map +0 -1
  242. package/dist/chunk-VKREAIBA.cjs +0 -1139
  243. package/dist/chunk-VKREAIBA.cjs.map +0 -1
  244. package/dist/chunk-WDTTHKDS.js +0 -458
  245. package/dist/chunk-WDTTHKDS.js.map +0 -1
  246. package/dist/chunk-WXVB364T.js +0 -218
  247. package/dist/chunk-WXVB364T.js.map +0 -1
  248. package/dist/chunk-X4BPPD6K.js +0 -181
  249. package/dist/chunk-X4BPPD6K.js.map +0 -1
  250. package/dist/chunk-XEB7PH2E.js +0 -81
  251. package/dist/chunk-XEB7PH2E.js.map +0 -1
  252. package/dist/chunk-YT7HXXVN.js +0 -13
  253. package/dist/chunk-YT7HXXVN.js.map +0 -1
  254. package/dist/cli/index.cjs.map +0 -1
  255. package/dist/cli/index.js.map +0 -1
  256. package/dist/client.cjs.map +0 -1
  257. package/dist/client.js.map +0 -1
  258. package/dist/drizzle/index.cjs.map +0 -1
  259. package/dist/drizzle/index.js.map +0 -1
  260. package/dist/fields/index.cjs.map +0 -1
  261. package/dist/fields/index.js.map +0 -1
  262. package/dist/graphql/index.cjs.map +0 -1
  263. package/dist/graphql/index.js.map +0 -1
  264. package/dist/index.cjs.map +0 -1
  265. package/dist/index.js.map +0 -1
  266. package/dist/integration.cjs.map +0 -1
  267. package/dist/integration.js.map +0 -1
  268. package/dist/media-7WDX4BDJ.js +0 -4
  269. package/dist/media-7WDX4BDJ.js.map +0 -1
  270. package/dist/media-TUSLVRQ6.cjs +0 -17
  271. package/dist/media-TUSLVRQ6.cjs.map +0 -1
  272. package/dist/mongo-auth-adapter-GT4S7SCU.cjs +0 -17
  273. package/dist/mongo-auth-adapter-GT4S7SCU.cjs.map +0 -1
  274. package/dist/mongo-auth-adapter-M7VV4LNB.js +0 -4
  275. package/dist/mongo-auth-adapter-M7VV4LNB.js.map +0 -1
  276. package/dist/mongodb/index.cjs.map +0 -1
  277. package/dist/mongodb/index.js.map +0 -1
  278. package/dist/postgres-auth-adapter-AFAPISH7.js +0 -5
  279. package/dist/postgres-auth-adapter-AFAPISH7.js.map +0 -1
  280. package/dist/postgres-auth-adapter-SFDTLONT.cjs +0 -14
  281. package/dist/postgres-auth-adapter-SFDTLONT.cjs.map +0 -1
  282. package/dist/redis-adapter-UQX4EE3B.cjs +0 -13
  283. package/dist/redis-adapter-UQX4EE3B.cjs.map +0 -1
  284. package/dist/redis-adapter-XALOGWY3.js +0 -4
  285. package/dist/redis-adapter-XALOGWY3.js.map +0 -1
  286. package/dist/rest/index.cjs.map +0 -1
  287. package/dist/rest/index.js.map +0 -1
  288. package/dist/schema-6QL3USNB.cjs +0 -67
  289. package/dist/schema-6QL3USNB.cjs.map +0 -1
  290. package/dist/schema-FNNWEAAW.js +0 -6
  291. package/dist/schema-FNNWEAAW.js.map +0 -1
  292. package/dist/sqlite-adapter-AQB5TCGV.cjs +0 -13
  293. package/dist/sqlite-adapter-AQB5TCGV.cjs.map +0 -1
  294. package/dist/sqlite-adapter-N5H6IM2X.js +0 -4
  295. package/dist/sqlite-adapter-N5H6IM2X.js.map +0 -1
  296. package/dist/templates/index.cjs.map +0 -1
  297. package/dist/templates/index.js.map +0 -1
  298. package/dist/trpc/index.cjs.map +0 -1
  299. package/dist/trpc/index.js.map +0 -1
  300. package/dist/ws/index.cjs.map +0 -1
  301. package/dist/ws/index.js.map +0 -1
@@ -0,0 +1,259 @@
1
+ import {a}from'./chunk-K6WUOU6P.js';var u=false,p=class o{db;cache={};loaded=false;static SENSITIVE_KEYS=["storage.s3.secret_access_key","storage.r2.secret_access_key","storage.gcs.private_key","storage.backblaze.application_key","storage.wasabi.secret_access_key","storage.ftp.password","storage.bunny.api_key","storage.cloudinary.api_secret","storage.imgix.sign_key","email.smtp.pass","auth.jwt_secret","auth.github_secret","auth.google_secret","auth.app_secret","database.url","redis.url","auth.admin_password"];constructor(e){this.db=e;}async load(){if(!this.loaded){await this.ensureSettingsTable();try{if(typeof this.db?.select=="function"){let e=await this.db.select().from(a);this.cache=e.reduce((t,s)=>(t[s.key]=s.value,t),{});}await this.loadFromGlobals();}catch{console.warn("ConfigService: Could not load settings from database, using environment fallbacks.");}this.loaded=true;}}async ensureSettingsTable(){if(!u)try{if(typeof this.db?.execute=="function"){let{sql:e}=await import('drizzle-orm');await this.db.execute(e`
2
+ CREATE TABLE IF NOT EXISTS "settings" (
3
+ "key" VARCHAR(255) PRIMARY KEY,
4
+ "value" TEXT NOT NULL,
5
+ "description" TEXT,
6
+ "updated_at" TIMESTAMP DEFAULT NOW()
7
+ )
8
+ `),u=!0;}else typeof this.db?.exec=="function"&&(this.db.exec(`
9
+ CREATE TABLE IF NOT EXISTS settings (
10
+ key TEXT PRIMARY KEY,
11
+ value TEXT NOT NULL,
12
+ description TEXT,
13
+ updated_at TEXT
14
+ )
15
+ `),u=!0);}catch{}}async loadFromGlobals(){try{let e=null;if(typeof this.db?.execute=="function"){let{sql:r}=await import('drizzle-orm'),i=['"_globals_storage-settings"','"_globals_storage_settings"','"global_storage_settings"'];for(let a of i)try{let n=await this.db.execute(r`SELECT * FROM ${r.raw(a)} LIMIT 1`);if(e=Array.isArray(n)?n[0]:n?.rows?n.rows[0]:null,e)break}catch{}}else if(typeof this.db?.prepare=="function"){let r=['"_globals_storage-settings"','"_globals_storage_settings"','"global_storage_settings"'];for(let i of r)try{if(e=this.db.prepare(`SELECT * FROM ${i} LIMIT 1`).get(),e)break}catch{}}if(!e)return;let t=r=>{if(!r)return null;if(typeof r=="string")try{return JSON.parse(r)}catch{return null}return r},s=e.provider||"local";if(this.cache["storage.type"]=s==="aws"?"s3":s,s==="local"){let r=t(e.local);this.cache["storage.local.dir"]=r?.uploadDir||"./public/uploads",this.cache["storage.local.url"]=r?.baseUrl||"/uploads";}if(s==="aws"){let r=t(e.aws);this.cache["storage.s3.bucket"]=r?.bucket||"",this.cache["storage.s3.region"]=r?.region||"us-east-1",this.cache["storage.s3.access_key_id"]=r?.accessKeyId||"",this.cache["storage.s3.secret_access_key"]=r?.secretAccessKey||"",this.cache["storage.s3.endpoint"]=r?.endpoint||"",this.cache["storage.s3.cdn_url"]=r?.cdnUrl||"",this.cache["storage.s3.prefix"]=r?.prefix||"";}if(s==="r2"){let r=t(e.r2);this.cache["storage.r2.account_id"]=r?.accountId||"",this.cache["storage.r2.access_key_id"]=r?.accessKeyId||"",this.cache["storage.r2.secret_access_key"]=r?.secretAccessKey||"",this.cache["storage.r2.bucket"]=r?.bucket||"",this.cache["storage.r2.cdn_url"]=r?.cdnUrl||"",this.cache["storage.r2.prefix"]=r?.prefix||"";}if(s==="cloudinary"){let r=t(e.cloudinary);this.cache["storage.cloudinary.cloud_name"]=r?.cloudName||"",this.cache["storage.cloudinary.api_key"]=r?.apiKey||"",this.cache["storage.cloudinary.api_secret"]=r?.apiSecret||"",this.cache["storage.cloudinary.folder"]=r?.folder||"";}if(s==="ftp"){let r=t(e.ftp);this.cache["storage.ftp.host"]=r?.host||"",this.cache["storage.ftp.port"]=String(r?.port||"21"),this.cache["storage.ftp.user"]=r?.user||"",this.cache["storage.ftp.password"]=r?.password||"",this.cache["storage.ftp.secure"]=r?.secure?"true":"false",this.cache["storage.ftp.base_url"]=r?.baseUrl||"",this.cache["storage.ftp.prefix"]=r?.prefix||"";}}catch(e){console.warn("ConfigService: Could not load storage settings from globals table:",e);}try{let e=null;if(typeof this.db?.execute=="function"){let{sql:t}=await import('drizzle-orm'),s=['"_globals_email-settings"','"_globals_email_settings"','"global_email_settings"'];for(let r of s)try{let i=await this.db.execute(t`SELECT * FROM ${t.raw(r)} LIMIT 1`);if(e=Array.isArray(i)?i[0]:i?.rows?i.rows[0]:null,e)break}catch{}}else if(typeof this.db?.prepare=="function"){let t=['"_globals_email-settings"','"_globals_email_settings"','"global_email_settings"'];for(let s of t)try{if(e=this.db.prepare(`SELECT * FROM ${s} LIMIT 1`).get(),e)break}catch{}}if(e){let t=s=>{if(!s)return null;if(typeof s=="string")try{return JSON.parse(s)}catch{return null}return s};if(this.cache["email.provider"]=e.provider||"smtp",this.cache["email.smtp.from"]=e.fromEmail||"",this.cache["email.smtp.from_name"]=e.fromName||"",this.cache["email.smtp.reply_to"]=e.replyTo||"",e.provider==="smtp"){let s=t(e.smtp);this.cache["email.smtp.host"]=s?.host||"",this.cache["email.smtp.port"]=String(s?.port||"587"),this.cache["email.smtp.user"]=s?.username||"",this.cache["email.smtp.pass"]=s?.password||"",this.cache["email.smtp.secure"]=s?.secure?"true":"false";}else if(e.provider==="resend"){let s=t(e.resend);this.cache["email.smtp.pass"]=s?.apiKey||"";}else if(e.provider==="sendgrid"){let s=t(e.sendgrid);this.cache["email.smtp.pass"]=s?.apiKey||"";}else if(e.provider==="mailgun"){let s=t(e.mailgun);this.cache["email.smtp.pass"]=s?.apiKey||"";}else if(e.provider==="ses"){let s=t(e.ses);this.cache["email.smtp.user"]=s?.accessKeyId||"",this.cache["email.smtp.pass"]=s?.secretAccessKey||"";}}}catch(e){console.warn("ConfigService: Could not load email settings from globals table:",e);}}get(e,t,s){return this.cache[e]?this.cache[e]:t&&process.env[t]?process.env[t]:s}getStorageConfig(){return {type:this.get("storage.type","STORAGE_TYPE","local"),s3:{bucket:this.get("storage.s3.bucket","STORAGE_BUCKET"),region:this.get("storage.s3.region","STORAGE_REGION","us-east-1"),accessKeyId:this.get("storage.s3.access_key_id","STORAGE_ACCESS_KEY_ID"),secretAccessKey:this.get("storage.s3.secret_access_key","STORAGE_SECRET_ACCESS_KEY"),endpoint:this.get("storage.s3.endpoint","STORAGE_ENDPOINT"),cdnUrl:this.get("storage.s3.cdn_url","STORAGE_CDN_URL"),prefix:this.get("storage.s3.prefix","STORAGE_PREFIX")},r2:{accountId:this.get("storage.r2.account_id","R2_ACCOUNT_ID"),accessKeyId:this.get("storage.r2.access_key_id","R2_ACCESS_KEY_ID"),secretAccessKey:this.get("storage.r2.secret_access_key","R2_SECRET_ACCESS_KEY"),bucket:this.get("storage.r2.bucket","R2_BUCKET"),cdnUrl:this.get("storage.r2.cdn_url","R2_CDN_URL"),prefix:this.get("storage.r2.prefix","R2_PREFIX")},gcs:{bucket:this.get("storage.gcs.bucket","GCS_BUCKET"),projectId:this.get("storage.gcs.project_id","GCS_PROJECT_ID"),clientEmail:this.get("storage.gcs.client_email","GCS_CLIENT_EMAIL"),privateKey:this.get("storage.gcs.private_key","GCS_PRIVATE_KEY"),cdnUrl:this.get("storage.gcs.cdn_url","GCS_CDN_URL"),prefix:this.get("storage.gcs.prefix","GCS_PREFIX")},digitalocean:{bucket:this.get("storage.digitalocean.bucket","DO_BUCKET"),region:this.get("storage.digitalocean.region","DO_REGION","nyc3"),accessKeyId:this.get("storage.digitalocean.access_key_id","DO_ACCESS_KEY_ID"),secretAccessKey:this.get("storage.digitalocean.secret_access_key","DO_SECRET_ACCESS_KEY"),cdnUrl:this.get("storage.digitalocean.cdn_url","DO_CDN_URL"),prefix:this.get("storage.digitalocean.prefix","DO_PREFIX")},backblaze:{bucket:this.get("storage.backblaze.bucket","BB_BUCKET"),accountId:this.get("storage.backblaze.account_id","BB_ACCOUNT_ID"),applicationKeyId:this.get("storage.backblaze.application_key_id","BB_APPLICATION_KEY_ID"),applicationKey:this.get("storage.backblaze.application_key","BB_APPLICATION_KEY"),cdnUrl:this.get("storage.backblaze.cdn_url","BB_CDN_URL"),prefix:this.get("storage.backblaze.prefix","BB_PREFIX")},wasabi:{bucket:this.get("storage.wasabi.bucket","WASABI_BUCKET"),region:this.get("storage.wasabi.region","WASABI_REGION","us-east-1"),accessKeyId:this.get("storage.wasabi.access_key_id","WASABI_ACCESS_KEY_ID"),secretAccessKey:this.get("storage.wasabi.secret_access_key","WASABI_SECRET_ACCESS_KEY"),cdnUrl:this.get("storage.wasabi.cdn_url","WASABI_CDN_URL"),prefix:this.get("storage.wasabi.prefix","WASABI_PREFIX")},bunny:{storageZone:this.get("storage.bunny.storage_zone","BUNNY_STORAGE_ZONE"),apiKey:this.get("storage.bunny.api_key","BUNNY_API_KEY"),cdnUrl:this.get("storage.bunny.cdn_url","BUNNY_CDN_URL"),prefix:this.get("storage.bunny.prefix","BUNNY_PREFIX")},ftp:{host:this.get("storage.ftp.host","FTP_HOST"),port:parseInt(this.get("storage.ftp.port","FTP_PORT","21"),10),user:this.get("storage.ftp.user","FTP_USER"),password:this.get("storage.ftp.password","FTP_PASSWORD"),secure:this.get("storage.ftp.secure","FTP_SECURE")==="true",baseUrl:this.get("storage.ftp.base_url","FTP_BASE_URL"),prefix:this.get("storage.ftp.prefix","FTP_PREFIX")},cloudinary:{cloudName:this.get("storage.cloudinary.cloud_name","CLOUDINARY_CLOUD_NAME"),apiKey:this.get("storage.cloudinary.api_key","CLOUDINARY_API_KEY"),apiSecret:this.get("storage.cloudinary.api_secret","CLOUDINARY_API_SECRET"),folder:this.get("storage.cloudinary.folder","CLOUDINARY_FOLDER")},imgix:{domain:this.get("storage.imgix.domain","IMGIX_DOMAIN"),signKey:this.get("storage.imgix.sign_key","IMGIX_SIGN_KEY")},local:{uploadDir:this.get("storage.local.dir","STORAGE_LOCAL_DIR","./public/uploads"),baseUrl:this.get("storage.local.url","STORAGE_LOCAL_URL","/uploads")}}}getEmailConfig(){return {provider:this.get("email.provider","EMAIL_PROVIDER","smtp"),host:this.get("email.smtp.host","SMTP_HOST"),port:parseInt(this.get("email.smtp.port","SMTP_PORT","587"),10),secure:this.get("email.smtp.secure","SMTP_SECURE")==="true",user:this.get("email.smtp.user","SMTP_USER"),pass:this.get("email.smtp.pass","SMTP_PASS"),from:this.get("email.smtp.from","SMTP_FROM","noreply@example.com"),fromName:this.get("email.smtp.from_name","SMTP_FROM_NAME","Kyro CMS"),replyTo:this.get("email.smtp.reply_to","SMTP_REPLY_TO")}}maskSensitive(e,t){return t&&(o.SENSITIVE_KEYS.includes(e)?"********":t)}async set(e,t,s){await this.db.insert(a).values({key:e,value:t,description:s,updatedAt:new Date}).onConflictDoUpdate({target:[a.key],set:{value:t,description:s,updatedAt:new Date}}),this.cache[e]=t;}};var b={verifyEmail:(o,e="User")=>({subject:"Verify your email address",html:`
16
+ <!DOCTYPE html>
17
+ <html>
18
+ <head>
19
+ <meta charset="utf-8">
20
+ <meta name="viewport" content="width=device-width, initial-scale=1">
21
+ <title>Verify Email</title>
22
+ <style>
23
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; }
24
+ .container { max-width: 600px; margin: 0 auto; padding: 20px; }
25
+ .button { display: inline-block; padding: 12px 24px; background: #0b1222; color: white; text-decoration: none; border-radius: 6px; font-weight: 600; }
26
+ .footer { margin-top: 30px; font-size: 12px; color: #666; }
27
+ </style>
28
+ </head>
29
+ <body>
30
+ <div class="container">
31
+ <h1>Welcome, ${e}!</h1>
32
+ <p>Please verify your email address by clicking the button below:</p>
33
+ <p style="text-align: center; margin: 30px 0;">
34
+ <a href="${o}" class="button">Verify Email</a>
35
+ </p>
36
+ <p>Or copy and paste this link into your browser:</p>
37
+ <p style="word-break: break-all; color: #666;">${o}</p>
38
+ <p>This link will expire in 24 hours.</p>
39
+ <div class="footer">
40
+ <p>If you didn't create an account, you can safely ignore this email.</p>
41
+ </div>
42
+ </div>
43
+ </body>
44
+ </html>
45
+ `,text:`Welcome ${e}!
46
+
47
+ Please verify your email by clicking this link: ${o}
48
+
49
+ This link will expire in 24 hours.
50
+
51
+ If you didn't create an account, you can safely ignore this email.`}),resetPassword:(o,e="User")=>({subject:"Reset your password",html:`
52
+ <!DOCTYPE html>
53
+ <html>
54
+ <head>
55
+ <meta charset="utf-8">
56
+ <meta name="viewport" content="width=device-width, initial-scale=1">
57
+ <title>Reset Password</title>
58
+ <style>
59
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; }
60
+ .container { max-width: 600px; margin: 0 auto; padding: 20px; }
61
+ .button { display: inline-block; padding: 12px 24px; background: #dc2626; color: white; text-decoration: none; border-radius: 6px; font-weight: 600; }
62
+ .warning { background: #fef3c7; border: 1px solid #f59e0b; padding: 12px; border-radius: 6px; margin: 20px 0; }
63
+ .footer { margin-top: 30px; font-size: 12px; color: #666; }
64
+ </style>
65
+ </head>
66
+ <body>
67
+ <div class="container">
68
+ <h1>Password Reset Request</h1>
69
+ <p>Hello ${e},</p>
70
+ <p>We received a request to reset your password. Click the button below to create a new password:</p>
71
+ <p style="text-align: center; margin: 30px 0;">
72
+ <a href="${o}" class="button">Reset Password</a>
73
+ </p>
74
+ <p>Or copy and paste this link into your browser:</p>
75
+ <p style="word-break: break-all; color: #666;">${o}</p>
76
+ <div class="warning">
77
+ <strong>\u26A0\uFE0F Important:</strong> This link will expire in 1 hour. If you didn't request a password reset, please ignore this email or contact support if you have concerns.
78
+ </div>
79
+ <div class="footer">
80
+ <p>For security reasons, please don't share this email with anyone.</p>
81
+ </div>
82
+ </div>
83
+ </body>
84
+ </html>
85
+ `,text:`Password Reset Request
86
+
87
+ Hello ${e},
88
+
89
+ We received a request to reset your password. Click this link to create a new password: ${o}
90
+
91
+ This link will expire in 1 hour.
92
+
93
+ If you didn't request a password reset, please ignore this email.`}),welcome:(o="User")=>({subject:"Welcome to Kyro CMS",html:`
94
+ <!DOCTYPE html>
95
+ <html>
96
+ <head>
97
+ <meta charset="utf-8">
98
+ <meta name="viewport" content="width=device-width, initial-scale=1">
99
+ <title>Welcome</title>
100
+ <style>
101
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; }
102
+ .container { max-width: 600px; margin: 0 auto; padding: 20px; }
103
+ .button { display: inline-block; padding: 12px 24px; background: #0b1222; color: white; text-decoration: none; border-radius: 6px; font-weight: 600; }
104
+ </style>
105
+ </head>
106
+ <body>
107
+ <div class="container">
108
+ <h1>Welcome to Kyro CMS, ${o}!</h1>
109
+ <p>Your account has been created successfully.</p>
110
+ <p>You can now:</p>
111
+ <ul>
112
+ <li>Manage your content collections</li>
113
+ <li>Upload and organize media</li>
114
+ <li>Configure settings</li>
115
+ <li>And much more...</li>
116
+ </ul>
117
+ <p style="text-align: center; margin: 30px 0;">
118
+ <a href="#" class="button">Get Started</a>
119
+ </p>
120
+ <p>If you have any questions, feel free to reach out to our support team.</p>
121
+ </div>
122
+ </body>
123
+ </html>
124
+ `,text:`Welcome to Kyro CMS, ${o}!
125
+
126
+ Your account has been created successfully.
127
+
128
+ You can now:
129
+ - Manage your content collections
130
+ - Upload and organize media
131
+ - Configure settings
132
+ - And much more...
133
+
134
+ Get started by logging into your dashboard.`}),accountLocked:(o,e,t="User")=>({subject:"Account Security Alert - Account Locked",html:`
135
+ <!DOCTYPE html>
136
+ <html>
137
+ <head>
138
+ <meta charset="utf-8">
139
+ <meta name="viewport" content="width=device-width, initial-scale=1">
140
+ <title>Account Locked</title>
141
+ <style>
142
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; }
143
+ .container { max-width: 600px; margin: 0 auto; padding: 20px; }
144
+ .alert { background: #fef2f2; border: 1px solid #ef4444; padding: 16px; border-radius: 8px; margin: 20px 0; }
145
+ .footer { margin-top: 30px; font-size: 12px; color: #666; }
146
+ </style>
147
+ </head>
148
+ <body>
149
+ <div class="container">
150
+ <h1>Account Security Alert</h1>
151
+ <p>Hello ${t},</p>
152
+ <div class="alert">
153
+ <p><strong>\u26A0\uFE0F Your account has been temporarily locked due to multiple failed login attempts.</strong></p>
154
+ <p>Failed attempts: ${o}</p>
155
+ <p>Lockout duration: ${Math.round(e/6e4)} minutes</p>
156
+ </div>
157
+ <p>Your account will automatically unlock after the lockout period expires.</p>
158
+ <p>If this wasn't you, we recommend:</p>
159
+ <ul>
160
+ <li>Using a strong, unique password</li>
161
+ <li>Enabling two-factor authentication (coming soon)</li>
162
+ <li>Reviewing your recent account activity</li>
163
+ </ul>
164
+ <div class="footer">
165
+ <p>If you need immediate assistance, please contact support.</p>
166
+ </div>
167
+ </div>
168
+ </body>
169
+ </html>
170
+ `,text:`Account Security Alert
171
+
172
+ Hello ${t},
173
+
174
+ Your account has been temporarily locked due to multiple failed login attempts (${o}).
175
+
176
+ Lockout duration: ${Math.round(e/6e4)} minutes
177
+
178
+ Your account will automatically unlock after this period.
179
+
180
+ If this wasn't you, we recommend using a strong, unique password.`}),passwordChanged:(o="User")=>({subject:"Your password has been changed",html:`
181
+ <!DOCTYPE html>
182
+ <html>
183
+ <head>
184
+ <meta charset="utf-8">
185
+ <meta name="viewport" content="width=device-width, initial-scale=1">
186
+ <title>Password Changed</title>
187
+ <style>
188
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; }
189
+ .container { max-width: 600px; margin: 0 auto; padding: 20px; }
190
+ .info { background: #f0fdf4; border: 1px solid #22c55e; padding: 12px; border-radius: 6px; margin: 20px 0; }
191
+ </style>
192
+ </head>
193
+ <body>
194
+ <div class="container">
195
+ <h1>Password Changed</h1>
196
+ <p>Hello ${o},</p>
197
+ <div class="info">
198
+ <p>Your password was recently changed.</p>
199
+ </div>
200
+ <p>If you did this, you can safely ignore this email.</p>
201
+ <p><strong>If you didn't change your password</strong>, please contact our support team immediately as your account may have been compromised.</p>
202
+ </div>
203
+ </body>
204
+ </html>
205
+ `,text:`Password Changed
206
+
207
+ Hello ${o},
208
+
209
+ Your password was recently changed.
210
+
211
+ If you did this, you can safely ignore this email.
212
+
213
+ If you didn't change your password, please contact support immediately.`}),newLogin:(o,e,t="User")=>({subject:"New login to your account",html:`
214
+ <!DOCTYPE html>
215
+ <html>
216
+ <head>
217
+ <meta charset="utf-8">
218
+ <meta name="viewport" content="width=device-width, initial-scale=1">
219
+ <title>New Login</title>
220
+ <style>
221
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; }
222
+ .container { max-width: 600px; margin: 0 auto; padding: 20px; }
223
+ .info-box { background: #f8fafc; border: 1px solid #e2e8f0; padding: 16px; border-radius: 8px; margin: 20px 0; }
224
+ .footer { margin-top: 30px; font-size: 12px; color: #666; }
225
+ </style>
226
+ </head>
227
+ <body>
228
+ <div class="container">
229
+ <h1>New Login Detected</h1>
230
+ <p>Hello ${t},</p>
231
+ <p>We detected a new login to your account:</p>
232
+ <div class="info-box">
233
+ <p><strong>Location:</strong> ${o}</p>
234
+ <p><strong>Time:</strong> ${e}</p>
235
+ </div>
236
+ <p><strong>If this was you</strong>, no action is needed.</p>
237
+ <p><strong>If this wasn't you</strong>, your account may be compromised. Please:</p>
238
+ <ol>
239
+ <li>Change your password immediately</li>
240
+ <li>Review your recent account activity</li>
241
+ <li>Contact support if needed</li>
242
+ </ol>
243
+ <div class="footer">
244
+ <p>This is an automated security notification.</p>
245
+ </div>
246
+ </div>
247
+ </body>
248
+ </html>
249
+ `,text:`New Login Detected
250
+
251
+ Hello ${t},
252
+
253
+ We detected a new login to your account:
254
+
255
+ Location: ${o}
256
+ Time: ${e}
257
+
258
+ If this wasn't you, please change your password immediately and contact support.`})},h=class o{transporter;config;templates;transporterInitialized=false;constructor(e,t){this.config=e,this.templates={...b,...t};}async ensureTransporter(){if(this.transporterInitialized)return this.transporter;let{default:e}=await import('nodemailer');return this.config.provider==="smtp"&&this.config.smtp?this.transporter=e.createTransport({host:this.config.smtp.host,port:this.config.smtp.port,secure:this.config.smtp.secure,auth:this.config.smtp.auth}):this.config.provider==="ses"&&this.config.ses&&(this.transporter=e.createTransport({host:`email-smtp.${this.config.ses.region}.amazonaws.com`,port:587,secure:false,auth:{user:this.config.ses.accessKeyId,pass:this.config.ses.secretAccessKey}})),this.transporterInitialized=true,this.transporter}async send(e){let{provider:t,from:s,fromName:r,replyTo:i}=this.config,a=`"${r||"Kyro CMS"}" <${s}>`,n=e.replyTo||i;try{let c;switch(t){case "smtp":case "ses":{let l=await this.ensureTransporter();if(!l)throw new Error(`${t} transporter not initialized`);c=await l.sendMail({from:a,to:Array.isArray(e.to)?e.to.join(", "):e.to,subject:e.subject,html:e.html,text:e.text,replyTo:n});}break;case "resend":c=await this.sendViaResend(a,e,n);break;case "sendgrid":c=await this.sendViaSendGrid(a,e,n);break;case "mailgun":c=await this.sendViaMailgun(a,e,n);break;default:throw new Error(`Unsupported email provider: ${t}`)}return c}catch(c){throw console.error("[EmailTransport] FAILED to send email:",c.message),c.response&&console.error("[EmailTransport] Provider Error Detail:",JSON.stringify(c.response,null,2)),c}}async sendViaResend(e,t,s){let r=this.config.resend?.apiKey;if(!r)throw new Error("Resend API Key missing");let i={from:e,to:t.to,subject:t.subject,html:t.html,text:t.text,reply_to:s},a=await fetch("https://api.resend.com/emails",{method:"POST",headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},body:JSON.stringify(i)});if(!a.ok){let n=await a.json();throw new Error(`Resend Error: ${JSON.stringify(n)}`)}return a.json()}async sendViaSendGrid(e,t,s){let r=this.config.sendgrid?.apiKey;if(!r)throw new Error("SendGrid API Key missing");let i={personalizations:[{to:Array.isArray(t.to)?t.to.map(n=>({email:n})):[{email:t.to}]}],from:{email:e.match(/<(.+)>/)?.[1]||e,name:e.match(/"(.+)"/)?.[1]},subject:t.subject,content:[{type:"text/plain",value:t.text||""},{type:"text/html",value:t.html}],reply_to:s?{email:s}:void 0},a=await fetch("https://api.sendgrid.com/v3/mail/send",{method:"POST",headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},body:JSON.stringify(i)});if(!a.ok){let n=await a.json();throw new Error(`SendGrid Error: ${JSON.stringify(n)}`)}return {success:true}}async sendViaMailgun(e,t,s){let{apiKey:r,domain:i,region:a}=this.config.mailgun||{};if(!r||!i)throw new Error("Mailgun config missing");let n=a==="eu"?"api.eu.mailgun.net":"api.mailgun.net",c=btoa(`api:${r}`),l=new URLSearchParams;l.append("from",e);let f=Array.isArray(t.to)?t.to.join(", "):t.to;l.append("to",f),l.append("subject",t.subject),l.append("html",t.html),t.text&&l.append("text",t.text),s&&l.append("h:Reply-To",s);let d=await fetch(`https://${n}/v3/${i}/messages`,{method:"POST",headers:{Authorization:`Basic ${c}`,"Content-Type":"application/x-www-form-urlencoded"},body:l});if(!d.ok){let y=await d.json();throw new Error(`Mailgun Error: ${JSON.stringify(y)}`)}return d.json()}getTemplates(){return this.templates}async verifyConnection(){if(this.config.provider==="smtp"||this.config.provider==="ses")try{let e=await this.ensureTransporter();if(e)return await e.verify(),!0}catch{return false}return !!(this.config.resend?.apiKey||this.config.sendgrid?.apiKey||this.config.mailgun?.apiKey)}static async fromConfig(e){let t=new p(e);await t.load();let s=t.getEmailConfig();if(!s.provider)return this.fromEnv();let r={provider:s.provider||"smtp",from:s.from||"noreply@example.com",fromName:s.fromName,replyTo:s.replyTo,smtp:s.provider==="smtp"?{host:s.host||"",port:s.port||587,secure:s.secure||false,auth:{user:s.user||"",pass:s.pass||""}}:void 0,resend:s.provider==="resend"?{apiKey:s.pass||""}:void 0,sendgrid:s.provider==="sendgrid"?{apiKey:s.pass||""}:void 0,mailgun:s.provider==="mailgun"?{apiKey:s.pass||"",domain:s.host||"",region:s.secure?"eu":"us"}:void 0,ses:s.provider==="ses"?{accessKeyId:s.user||"",secretAccessKey:s.pass||"",region:s.host||"us-east-1"}:void 0};return new o(r)}static fromEnv(){let e=process.env.EMAIL_PROVIDER||"smtp",t=process.env.SMTP_FROM||process.env.DEFAULT_FROM||"noreply@example.com",s=process.env.SMTP_FROM_NAME||"Kyro CMS",r=process.env.SMTP_REPLY_TO;if(e==="smtp"){let i=process.env.SMTP_HOST,a=process.env.SMTP_USER,n=process.env.SMTP_PASS;return !i||!a||!n?null:new o({provider:"smtp",from:t,fromName:s,replyTo:r,smtp:{host:i,port:parseInt(process.env.SMTP_PORT||"587",10),secure:process.env.SMTP_SECURE==="true",auth:{user:a,pass:n}}})}if(e==="resend"){let i=process.env.RESEND_API_KEY||process.env.SMTP_PASS;return i?new o({provider:"resend",from:t,fromName:s,replyTo:r,resend:{apiKey:i}}):null}if(e==="sendgrid"){let i=process.env.SENDGRID_API_KEY||process.env.SMTP_PASS;return i?new o({provider:"sendgrid",from:t,fromName:s,replyTo:r,sendgrid:{apiKey:i}}):null}if(e==="mailgun"){let i=process.env.MAILGUN_API_KEY||process.env.SMTP_PASS,a=process.env.MAILGUN_DOMAIN||process.env.SMTP_HOST;return !i||!a?null:new o({provider:"mailgun",from:t,fromName:s,replyTo:r,mailgun:{apiKey:i,domain:a,region:process.env.MAILGUN_REGION||(process.env.SMTP_SECURE==="true"?"eu":"us")}})}if(e==="ses"){let i=process.env.AWS_ACCESS_KEY_ID||process.env.SMTP_USER,a=process.env.AWS_SECRET_ACCESS_KEY||process.env.SMTP_PASS,n=process.env.AWS_REGION||process.env.SMTP_HOST||"us-east-1";return !i||!a?null:new o({provider:"ses",from:t,fromName:s,replyTo:r,ses:{accessKeyId:i,secretAccessKey:a,region:n}})}return null}};var _={minLength:12,requireUppercase:true,requireLowercase:true,requireNumbers:true,requireSpecialChars:true,preventReuse:5,maxLength:128},m=class{config;constructor(e={}){this.config={..._,...e};}validate(e){let t=[];return this.config.maxLength&&e.length>this.config.maxLength&&t.push(`Password must not exceed ${this.config.maxLength} characters`),e.length<this.config.minLength&&t.push(`Password must be at least ${this.config.minLength} characters`),this.config.requireUppercase&&!/[A-Z]/.test(e)&&t.push("Password must contain at least one uppercase letter"),this.config.requireLowercase&&!/[a-z]/.test(e)&&t.push("Password must contain at least one lowercase letter"),this.config.requireNumbers&&!/[0-9]/.test(e)&&t.push("Password must contain at least one number"),this.config.requireSpecialChars&&!/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(e)&&t.push("Password must contain at least one special character"),["password","123456","12345678","qwerty","abc123","monkey","1234567","letmein","trustno1","dragon","baseball","iloveyou","master","sunshine","ashley","football","password1","shadow","123123","654321"].includes(e.toLowerCase())&&t.push("This password is too common. Please choose a more secure password"),(/^[a-zA-Z]+$/.test(e)||/^[0-9]+$/.test(e))&&t.push("Password must contain a mix of letters, numbers, and/or special characters"),/(.)\1{2,}/.test(e)&&t.push("Password must not contain more than 2 consecutive identical characters"),/^(012|123|234|345|456|567|678|789|890|098|987|876|765|654|543|432|321|210)+$/i.test(e)&&t.push("Password must not contain sequential numbers or letters"),{valid:t.length===0,errors:t}}async checkReuse(e,t,s){return {valid:true,errors:[]}}async isInHistory(e,t,s){for(let r of t)if(await s(e,r))return true;return false}generatePassword(e=16){let t="ABCDEFGHIJKLMNOPQRSTUVWXYZ",s="abcdefghijklmnopqrstuvwxyz",r="0123456789",i="!@#$%^&*()_+-=[]{}|;:,.<>?",a="";a+=t[Math.floor(Math.random()*t.length)],a+=s[Math.floor(Math.random()*s.length)],a+=r[Math.floor(Math.random()*r.length)],a+=i[Math.floor(Math.random()*i.length)];let n=t+s+r+i;for(let c=a.length;c<e;c++)a+=n[Math.floor(Math.random()*n.length)];return a.split("").sort(()=>Math.random()-.5).join("")}getStrength(e){let t=0,s=[];e.length>=8&&(t+=1),e.length>=12&&(t+=1),e.length>=16&&(t+=1),/[a-z]/.test(e)&&(t+=1),/[A-Z]/.test(e)&&(t+=1),/[0-9]/.test(e)&&(t+=1),/[!@#$%^&*()_+\-=\[\]{}|;:,.<>?]/.test(e)&&(t+=1),e.length>8&&(t+=1),e.length>12&&(t+=1);let r=new Set(e).size;r>6&&(t+=1),r>10&&(t+=1);let i;return t<=3?(i="Weak",s.push("Add more characters"),s.push("Include uppercase and lowercase letters")):t<=5?(i="Fair",s.push("Add special characters"),s.push("Consider making it longer")):t<=7?(i="Good",s.push("Consider making it longer for extra security")):i="Strong",{score:t,label:i,feedback:s}}setConfig(e){this.config={...this.config,...e};}getConfig(){return {...this.config}}};
259
+ export{p as a,h as b,m as c};
@@ -0,0 +1 @@
1
+ var e={slug:"users",label:"Users",fields:[{name:"name",type:"text",label:"Name"},{name:"email",type:"email",required:true},{name:"passwordHash",type:"text",label:"Password Hash",admin:{hidden:true}},{name:"role",type:"select",options:[{label:"Super Admin",value:"super_admin"},{label:"Admin",value:"admin"},{label:"Editor",value:"editor"},{label:"Author",value:"author"},{label:"Customer",value:"customer"},{label:"Guest",value:"guest"}],required:true},{name:"avatar",type:"upload",relationTo:"media",label:"Profile Picture"},{name:"tenantId",type:"text",label:"Tenant"},{name:"emailVerified",type:"checkbox",label:"Email Verified"},{name:"locked",type:"checkbox"},{name:"lastLogin",type:"date",time:true,label:"Last Login",admin:{readOnly:true}},{name:"failedLoginAttempts",type:"number",label:"Failed Login Attempts",admin:{readOnly:true}}],timestamps:true,admin:{useAsTitle:"email",defaultColumns:["email","role","tenantId","lastLogin"]}},t={slug:"audit_logs",label:"Audit Logs",fields:[{name:"action",type:"text",required:true},{name:"userId",type:"text",label:"User ID"},{name:"userEmail",type:"email",label:"User Email"},{name:"role",type:"text"},{name:"resource",type:"text",label:"Resource"},{name:"ipAddress",type:"text",label:"IP Address"},{name:"success",type:"checkbox"},{name:"timestamp",type:"date",time:true,required:true,admin:{readOnly:true}}],admin:{defaultColumns:["action","userEmail","resource","success","timestamp"]}},a={users:e,audit_logs:t};export{e as a,a as b};
@@ -0,0 +1,259 @@
1
+ 'use strict';var chunkWIZ6C5QZ_cjs=require('./chunk-WIZ6C5QZ.cjs');var u=false,p=class o{db;cache={};loaded=false;static SENSITIVE_KEYS=["storage.s3.secret_access_key","storage.r2.secret_access_key","storage.gcs.private_key","storage.backblaze.application_key","storage.wasabi.secret_access_key","storage.ftp.password","storage.bunny.api_key","storage.cloudinary.api_secret","storage.imgix.sign_key","email.smtp.pass","auth.jwt_secret","auth.github_secret","auth.google_secret","auth.app_secret","database.url","redis.url","auth.admin_password"];constructor(e){this.db=e;}async load(){if(!this.loaded){await this.ensureSettingsTable();try{if(typeof this.db?.select=="function"){let e=await this.db.select().from(chunkWIZ6C5QZ_cjs.a);this.cache=e.reduce((t,s)=>(t[s.key]=s.value,t),{});}await this.loadFromGlobals();}catch{console.warn("ConfigService: Could not load settings from database, using environment fallbacks.");}this.loaded=true;}}async ensureSettingsTable(){if(!u)try{if(typeof this.db?.execute=="function"){let{sql:e}=await import('drizzle-orm');await this.db.execute(e`
2
+ CREATE TABLE IF NOT EXISTS "settings" (
3
+ "key" VARCHAR(255) PRIMARY KEY,
4
+ "value" TEXT NOT NULL,
5
+ "description" TEXT,
6
+ "updated_at" TIMESTAMP DEFAULT NOW()
7
+ )
8
+ `),u=!0;}else typeof this.db?.exec=="function"&&(this.db.exec(`
9
+ CREATE TABLE IF NOT EXISTS settings (
10
+ key TEXT PRIMARY KEY,
11
+ value TEXT NOT NULL,
12
+ description TEXT,
13
+ updated_at TEXT
14
+ )
15
+ `),u=!0);}catch{}}async loadFromGlobals(){try{let e=null;if(typeof this.db?.execute=="function"){let{sql:r}=await import('drizzle-orm'),i=['"_globals_storage-settings"','"_globals_storage_settings"','"global_storage_settings"'];for(let a of i)try{let n=await this.db.execute(r`SELECT * FROM ${r.raw(a)} LIMIT 1`);if(e=Array.isArray(n)?n[0]:n?.rows?n.rows[0]:null,e)break}catch{}}else if(typeof this.db?.prepare=="function"){let r=['"_globals_storage-settings"','"_globals_storage_settings"','"global_storage_settings"'];for(let i of r)try{if(e=this.db.prepare(`SELECT * FROM ${i} LIMIT 1`).get(),e)break}catch{}}if(!e)return;let t=r=>{if(!r)return null;if(typeof r=="string")try{return JSON.parse(r)}catch{return null}return r},s=e.provider||"local";if(this.cache["storage.type"]=s==="aws"?"s3":s,s==="local"){let r=t(e.local);this.cache["storage.local.dir"]=r?.uploadDir||"./public/uploads",this.cache["storage.local.url"]=r?.baseUrl||"/uploads";}if(s==="aws"){let r=t(e.aws);this.cache["storage.s3.bucket"]=r?.bucket||"",this.cache["storage.s3.region"]=r?.region||"us-east-1",this.cache["storage.s3.access_key_id"]=r?.accessKeyId||"",this.cache["storage.s3.secret_access_key"]=r?.secretAccessKey||"",this.cache["storage.s3.endpoint"]=r?.endpoint||"",this.cache["storage.s3.cdn_url"]=r?.cdnUrl||"",this.cache["storage.s3.prefix"]=r?.prefix||"";}if(s==="r2"){let r=t(e.r2);this.cache["storage.r2.account_id"]=r?.accountId||"",this.cache["storage.r2.access_key_id"]=r?.accessKeyId||"",this.cache["storage.r2.secret_access_key"]=r?.secretAccessKey||"",this.cache["storage.r2.bucket"]=r?.bucket||"",this.cache["storage.r2.cdn_url"]=r?.cdnUrl||"",this.cache["storage.r2.prefix"]=r?.prefix||"";}if(s==="cloudinary"){let r=t(e.cloudinary);this.cache["storage.cloudinary.cloud_name"]=r?.cloudName||"",this.cache["storage.cloudinary.api_key"]=r?.apiKey||"",this.cache["storage.cloudinary.api_secret"]=r?.apiSecret||"",this.cache["storage.cloudinary.folder"]=r?.folder||"";}if(s==="ftp"){let r=t(e.ftp);this.cache["storage.ftp.host"]=r?.host||"",this.cache["storage.ftp.port"]=String(r?.port||"21"),this.cache["storage.ftp.user"]=r?.user||"",this.cache["storage.ftp.password"]=r?.password||"",this.cache["storage.ftp.secure"]=r?.secure?"true":"false",this.cache["storage.ftp.base_url"]=r?.baseUrl||"",this.cache["storage.ftp.prefix"]=r?.prefix||"";}}catch(e){console.warn("ConfigService: Could not load storage settings from globals table:",e);}try{let e=null;if(typeof this.db?.execute=="function"){let{sql:t}=await import('drizzle-orm'),s=['"_globals_email-settings"','"_globals_email_settings"','"global_email_settings"'];for(let r of s)try{let i=await this.db.execute(t`SELECT * FROM ${t.raw(r)} LIMIT 1`);if(e=Array.isArray(i)?i[0]:i?.rows?i.rows[0]:null,e)break}catch{}}else if(typeof this.db?.prepare=="function"){let t=['"_globals_email-settings"','"_globals_email_settings"','"global_email_settings"'];for(let s of t)try{if(e=this.db.prepare(`SELECT * FROM ${s} LIMIT 1`).get(),e)break}catch{}}if(e){let t=s=>{if(!s)return null;if(typeof s=="string")try{return JSON.parse(s)}catch{return null}return s};if(this.cache["email.provider"]=e.provider||"smtp",this.cache["email.smtp.from"]=e.fromEmail||"",this.cache["email.smtp.from_name"]=e.fromName||"",this.cache["email.smtp.reply_to"]=e.replyTo||"",e.provider==="smtp"){let s=t(e.smtp);this.cache["email.smtp.host"]=s?.host||"",this.cache["email.smtp.port"]=String(s?.port||"587"),this.cache["email.smtp.user"]=s?.username||"",this.cache["email.smtp.pass"]=s?.password||"",this.cache["email.smtp.secure"]=s?.secure?"true":"false";}else if(e.provider==="resend"){let s=t(e.resend);this.cache["email.smtp.pass"]=s?.apiKey||"";}else if(e.provider==="sendgrid"){let s=t(e.sendgrid);this.cache["email.smtp.pass"]=s?.apiKey||"";}else if(e.provider==="mailgun"){let s=t(e.mailgun);this.cache["email.smtp.pass"]=s?.apiKey||"";}else if(e.provider==="ses"){let s=t(e.ses);this.cache["email.smtp.user"]=s?.accessKeyId||"",this.cache["email.smtp.pass"]=s?.secretAccessKey||"";}}}catch(e){console.warn("ConfigService: Could not load email settings from globals table:",e);}}get(e,t,s){return this.cache[e]?this.cache[e]:t&&process.env[t]?process.env[t]:s}getStorageConfig(){return {type:this.get("storage.type","STORAGE_TYPE","local"),s3:{bucket:this.get("storage.s3.bucket","STORAGE_BUCKET"),region:this.get("storage.s3.region","STORAGE_REGION","us-east-1"),accessKeyId:this.get("storage.s3.access_key_id","STORAGE_ACCESS_KEY_ID"),secretAccessKey:this.get("storage.s3.secret_access_key","STORAGE_SECRET_ACCESS_KEY"),endpoint:this.get("storage.s3.endpoint","STORAGE_ENDPOINT"),cdnUrl:this.get("storage.s3.cdn_url","STORAGE_CDN_URL"),prefix:this.get("storage.s3.prefix","STORAGE_PREFIX")},r2:{accountId:this.get("storage.r2.account_id","R2_ACCOUNT_ID"),accessKeyId:this.get("storage.r2.access_key_id","R2_ACCESS_KEY_ID"),secretAccessKey:this.get("storage.r2.secret_access_key","R2_SECRET_ACCESS_KEY"),bucket:this.get("storage.r2.bucket","R2_BUCKET"),cdnUrl:this.get("storage.r2.cdn_url","R2_CDN_URL"),prefix:this.get("storage.r2.prefix","R2_PREFIX")},gcs:{bucket:this.get("storage.gcs.bucket","GCS_BUCKET"),projectId:this.get("storage.gcs.project_id","GCS_PROJECT_ID"),clientEmail:this.get("storage.gcs.client_email","GCS_CLIENT_EMAIL"),privateKey:this.get("storage.gcs.private_key","GCS_PRIVATE_KEY"),cdnUrl:this.get("storage.gcs.cdn_url","GCS_CDN_URL"),prefix:this.get("storage.gcs.prefix","GCS_PREFIX")},digitalocean:{bucket:this.get("storage.digitalocean.bucket","DO_BUCKET"),region:this.get("storage.digitalocean.region","DO_REGION","nyc3"),accessKeyId:this.get("storage.digitalocean.access_key_id","DO_ACCESS_KEY_ID"),secretAccessKey:this.get("storage.digitalocean.secret_access_key","DO_SECRET_ACCESS_KEY"),cdnUrl:this.get("storage.digitalocean.cdn_url","DO_CDN_URL"),prefix:this.get("storage.digitalocean.prefix","DO_PREFIX")},backblaze:{bucket:this.get("storage.backblaze.bucket","BB_BUCKET"),accountId:this.get("storage.backblaze.account_id","BB_ACCOUNT_ID"),applicationKeyId:this.get("storage.backblaze.application_key_id","BB_APPLICATION_KEY_ID"),applicationKey:this.get("storage.backblaze.application_key","BB_APPLICATION_KEY"),cdnUrl:this.get("storage.backblaze.cdn_url","BB_CDN_URL"),prefix:this.get("storage.backblaze.prefix","BB_PREFIX")},wasabi:{bucket:this.get("storage.wasabi.bucket","WASABI_BUCKET"),region:this.get("storage.wasabi.region","WASABI_REGION","us-east-1"),accessKeyId:this.get("storage.wasabi.access_key_id","WASABI_ACCESS_KEY_ID"),secretAccessKey:this.get("storage.wasabi.secret_access_key","WASABI_SECRET_ACCESS_KEY"),cdnUrl:this.get("storage.wasabi.cdn_url","WASABI_CDN_URL"),prefix:this.get("storage.wasabi.prefix","WASABI_PREFIX")},bunny:{storageZone:this.get("storage.bunny.storage_zone","BUNNY_STORAGE_ZONE"),apiKey:this.get("storage.bunny.api_key","BUNNY_API_KEY"),cdnUrl:this.get("storage.bunny.cdn_url","BUNNY_CDN_URL"),prefix:this.get("storage.bunny.prefix","BUNNY_PREFIX")},ftp:{host:this.get("storage.ftp.host","FTP_HOST"),port:parseInt(this.get("storage.ftp.port","FTP_PORT","21"),10),user:this.get("storage.ftp.user","FTP_USER"),password:this.get("storage.ftp.password","FTP_PASSWORD"),secure:this.get("storage.ftp.secure","FTP_SECURE")==="true",baseUrl:this.get("storage.ftp.base_url","FTP_BASE_URL"),prefix:this.get("storage.ftp.prefix","FTP_PREFIX")},cloudinary:{cloudName:this.get("storage.cloudinary.cloud_name","CLOUDINARY_CLOUD_NAME"),apiKey:this.get("storage.cloudinary.api_key","CLOUDINARY_API_KEY"),apiSecret:this.get("storage.cloudinary.api_secret","CLOUDINARY_API_SECRET"),folder:this.get("storage.cloudinary.folder","CLOUDINARY_FOLDER")},imgix:{domain:this.get("storage.imgix.domain","IMGIX_DOMAIN"),signKey:this.get("storage.imgix.sign_key","IMGIX_SIGN_KEY")},local:{uploadDir:this.get("storage.local.dir","STORAGE_LOCAL_DIR","./public/uploads"),baseUrl:this.get("storage.local.url","STORAGE_LOCAL_URL","/uploads")}}}getEmailConfig(){return {provider:this.get("email.provider","EMAIL_PROVIDER","smtp"),host:this.get("email.smtp.host","SMTP_HOST"),port:parseInt(this.get("email.smtp.port","SMTP_PORT","587"),10),secure:this.get("email.smtp.secure","SMTP_SECURE")==="true",user:this.get("email.smtp.user","SMTP_USER"),pass:this.get("email.smtp.pass","SMTP_PASS"),from:this.get("email.smtp.from","SMTP_FROM","noreply@example.com"),fromName:this.get("email.smtp.from_name","SMTP_FROM_NAME","Kyro CMS"),replyTo:this.get("email.smtp.reply_to","SMTP_REPLY_TO")}}maskSensitive(e,t){return t&&(o.SENSITIVE_KEYS.includes(e)?"********":t)}async set(e,t,s){await this.db.insert(chunkWIZ6C5QZ_cjs.a).values({key:e,value:t,description:s,updatedAt:new Date}).onConflictDoUpdate({target:[chunkWIZ6C5QZ_cjs.a.key],set:{value:t,description:s,updatedAt:new Date}}),this.cache[e]=t;}};var b={verifyEmail:(o,e="User")=>({subject:"Verify your email address",html:`
16
+ <!DOCTYPE html>
17
+ <html>
18
+ <head>
19
+ <meta charset="utf-8">
20
+ <meta name="viewport" content="width=device-width, initial-scale=1">
21
+ <title>Verify Email</title>
22
+ <style>
23
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; }
24
+ .container { max-width: 600px; margin: 0 auto; padding: 20px; }
25
+ .button { display: inline-block; padding: 12px 24px; background: #0b1222; color: white; text-decoration: none; border-radius: 6px; font-weight: 600; }
26
+ .footer { margin-top: 30px; font-size: 12px; color: #666; }
27
+ </style>
28
+ </head>
29
+ <body>
30
+ <div class="container">
31
+ <h1>Welcome, ${e}!</h1>
32
+ <p>Please verify your email address by clicking the button below:</p>
33
+ <p style="text-align: center; margin: 30px 0;">
34
+ <a href="${o}" class="button">Verify Email</a>
35
+ </p>
36
+ <p>Or copy and paste this link into your browser:</p>
37
+ <p style="word-break: break-all; color: #666;">${o}</p>
38
+ <p>This link will expire in 24 hours.</p>
39
+ <div class="footer">
40
+ <p>If you didn't create an account, you can safely ignore this email.</p>
41
+ </div>
42
+ </div>
43
+ </body>
44
+ </html>
45
+ `,text:`Welcome ${e}!
46
+
47
+ Please verify your email by clicking this link: ${o}
48
+
49
+ This link will expire in 24 hours.
50
+
51
+ If you didn't create an account, you can safely ignore this email.`}),resetPassword:(o,e="User")=>({subject:"Reset your password",html:`
52
+ <!DOCTYPE html>
53
+ <html>
54
+ <head>
55
+ <meta charset="utf-8">
56
+ <meta name="viewport" content="width=device-width, initial-scale=1">
57
+ <title>Reset Password</title>
58
+ <style>
59
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; }
60
+ .container { max-width: 600px; margin: 0 auto; padding: 20px; }
61
+ .button { display: inline-block; padding: 12px 24px; background: #dc2626; color: white; text-decoration: none; border-radius: 6px; font-weight: 600; }
62
+ .warning { background: #fef3c7; border: 1px solid #f59e0b; padding: 12px; border-radius: 6px; margin: 20px 0; }
63
+ .footer { margin-top: 30px; font-size: 12px; color: #666; }
64
+ </style>
65
+ </head>
66
+ <body>
67
+ <div class="container">
68
+ <h1>Password Reset Request</h1>
69
+ <p>Hello ${e},</p>
70
+ <p>We received a request to reset your password. Click the button below to create a new password:</p>
71
+ <p style="text-align: center; margin: 30px 0;">
72
+ <a href="${o}" class="button">Reset Password</a>
73
+ </p>
74
+ <p>Or copy and paste this link into your browser:</p>
75
+ <p style="word-break: break-all; color: #666;">${o}</p>
76
+ <div class="warning">
77
+ <strong>\u26A0\uFE0F Important:</strong> This link will expire in 1 hour. If you didn't request a password reset, please ignore this email or contact support if you have concerns.
78
+ </div>
79
+ <div class="footer">
80
+ <p>For security reasons, please don't share this email with anyone.</p>
81
+ </div>
82
+ </div>
83
+ </body>
84
+ </html>
85
+ `,text:`Password Reset Request
86
+
87
+ Hello ${e},
88
+
89
+ We received a request to reset your password. Click this link to create a new password: ${o}
90
+
91
+ This link will expire in 1 hour.
92
+
93
+ If you didn't request a password reset, please ignore this email.`}),welcome:(o="User")=>({subject:"Welcome to Kyro CMS",html:`
94
+ <!DOCTYPE html>
95
+ <html>
96
+ <head>
97
+ <meta charset="utf-8">
98
+ <meta name="viewport" content="width=device-width, initial-scale=1">
99
+ <title>Welcome</title>
100
+ <style>
101
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; }
102
+ .container { max-width: 600px; margin: 0 auto; padding: 20px; }
103
+ .button { display: inline-block; padding: 12px 24px; background: #0b1222; color: white; text-decoration: none; border-radius: 6px; font-weight: 600; }
104
+ </style>
105
+ </head>
106
+ <body>
107
+ <div class="container">
108
+ <h1>Welcome to Kyro CMS, ${o}!</h1>
109
+ <p>Your account has been created successfully.</p>
110
+ <p>You can now:</p>
111
+ <ul>
112
+ <li>Manage your content collections</li>
113
+ <li>Upload and organize media</li>
114
+ <li>Configure settings</li>
115
+ <li>And much more...</li>
116
+ </ul>
117
+ <p style="text-align: center; margin: 30px 0;">
118
+ <a href="#" class="button">Get Started</a>
119
+ </p>
120
+ <p>If you have any questions, feel free to reach out to our support team.</p>
121
+ </div>
122
+ </body>
123
+ </html>
124
+ `,text:`Welcome to Kyro CMS, ${o}!
125
+
126
+ Your account has been created successfully.
127
+
128
+ You can now:
129
+ - Manage your content collections
130
+ - Upload and organize media
131
+ - Configure settings
132
+ - And much more...
133
+
134
+ Get started by logging into your dashboard.`}),accountLocked:(o,e,t="User")=>({subject:"Account Security Alert - Account Locked",html:`
135
+ <!DOCTYPE html>
136
+ <html>
137
+ <head>
138
+ <meta charset="utf-8">
139
+ <meta name="viewport" content="width=device-width, initial-scale=1">
140
+ <title>Account Locked</title>
141
+ <style>
142
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; }
143
+ .container { max-width: 600px; margin: 0 auto; padding: 20px; }
144
+ .alert { background: #fef2f2; border: 1px solid #ef4444; padding: 16px; border-radius: 8px; margin: 20px 0; }
145
+ .footer { margin-top: 30px; font-size: 12px; color: #666; }
146
+ </style>
147
+ </head>
148
+ <body>
149
+ <div class="container">
150
+ <h1>Account Security Alert</h1>
151
+ <p>Hello ${t},</p>
152
+ <div class="alert">
153
+ <p><strong>\u26A0\uFE0F Your account has been temporarily locked due to multiple failed login attempts.</strong></p>
154
+ <p>Failed attempts: ${o}</p>
155
+ <p>Lockout duration: ${Math.round(e/6e4)} minutes</p>
156
+ </div>
157
+ <p>Your account will automatically unlock after the lockout period expires.</p>
158
+ <p>If this wasn't you, we recommend:</p>
159
+ <ul>
160
+ <li>Using a strong, unique password</li>
161
+ <li>Enabling two-factor authentication (coming soon)</li>
162
+ <li>Reviewing your recent account activity</li>
163
+ </ul>
164
+ <div class="footer">
165
+ <p>If you need immediate assistance, please contact support.</p>
166
+ </div>
167
+ </div>
168
+ </body>
169
+ </html>
170
+ `,text:`Account Security Alert
171
+
172
+ Hello ${t},
173
+
174
+ Your account has been temporarily locked due to multiple failed login attempts (${o}).
175
+
176
+ Lockout duration: ${Math.round(e/6e4)} minutes
177
+
178
+ Your account will automatically unlock after this period.
179
+
180
+ If this wasn't you, we recommend using a strong, unique password.`}),passwordChanged:(o="User")=>({subject:"Your password has been changed",html:`
181
+ <!DOCTYPE html>
182
+ <html>
183
+ <head>
184
+ <meta charset="utf-8">
185
+ <meta name="viewport" content="width=device-width, initial-scale=1">
186
+ <title>Password Changed</title>
187
+ <style>
188
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; }
189
+ .container { max-width: 600px; margin: 0 auto; padding: 20px; }
190
+ .info { background: #f0fdf4; border: 1px solid #22c55e; padding: 12px; border-radius: 6px; margin: 20px 0; }
191
+ </style>
192
+ </head>
193
+ <body>
194
+ <div class="container">
195
+ <h1>Password Changed</h1>
196
+ <p>Hello ${o},</p>
197
+ <div class="info">
198
+ <p>Your password was recently changed.</p>
199
+ </div>
200
+ <p>If you did this, you can safely ignore this email.</p>
201
+ <p><strong>If you didn't change your password</strong>, please contact our support team immediately as your account may have been compromised.</p>
202
+ </div>
203
+ </body>
204
+ </html>
205
+ `,text:`Password Changed
206
+
207
+ Hello ${o},
208
+
209
+ Your password was recently changed.
210
+
211
+ If you did this, you can safely ignore this email.
212
+
213
+ If you didn't change your password, please contact support immediately.`}),newLogin:(o,e,t="User")=>({subject:"New login to your account",html:`
214
+ <!DOCTYPE html>
215
+ <html>
216
+ <head>
217
+ <meta charset="utf-8">
218
+ <meta name="viewport" content="width=device-width, initial-scale=1">
219
+ <title>New Login</title>
220
+ <style>
221
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; }
222
+ .container { max-width: 600px; margin: 0 auto; padding: 20px; }
223
+ .info-box { background: #f8fafc; border: 1px solid #e2e8f0; padding: 16px; border-radius: 8px; margin: 20px 0; }
224
+ .footer { margin-top: 30px; font-size: 12px; color: #666; }
225
+ </style>
226
+ </head>
227
+ <body>
228
+ <div class="container">
229
+ <h1>New Login Detected</h1>
230
+ <p>Hello ${t},</p>
231
+ <p>We detected a new login to your account:</p>
232
+ <div class="info-box">
233
+ <p><strong>Location:</strong> ${o}</p>
234
+ <p><strong>Time:</strong> ${e}</p>
235
+ </div>
236
+ <p><strong>If this was you</strong>, no action is needed.</p>
237
+ <p><strong>If this wasn't you</strong>, your account may be compromised. Please:</p>
238
+ <ol>
239
+ <li>Change your password immediately</li>
240
+ <li>Review your recent account activity</li>
241
+ <li>Contact support if needed</li>
242
+ </ol>
243
+ <div class="footer">
244
+ <p>This is an automated security notification.</p>
245
+ </div>
246
+ </div>
247
+ </body>
248
+ </html>
249
+ `,text:`New Login Detected
250
+
251
+ Hello ${t},
252
+
253
+ We detected a new login to your account:
254
+
255
+ Location: ${o}
256
+ Time: ${e}
257
+
258
+ If this wasn't you, please change your password immediately and contact support.`})},h=class o{transporter;config;templates;transporterInitialized=false;constructor(e,t){this.config=e,this.templates={...b,...t};}async ensureTransporter(){if(this.transporterInitialized)return this.transporter;let{default:e}=await import('nodemailer');return this.config.provider==="smtp"&&this.config.smtp?this.transporter=e.createTransport({host:this.config.smtp.host,port:this.config.smtp.port,secure:this.config.smtp.secure,auth:this.config.smtp.auth}):this.config.provider==="ses"&&this.config.ses&&(this.transporter=e.createTransport({host:`email-smtp.${this.config.ses.region}.amazonaws.com`,port:587,secure:false,auth:{user:this.config.ses.accessKeyId,pass:this.config.ses.secretAccessKey}})),this.transporterInitialized=true,this.transporter}async send(e){let{provider:t,from:s,fromName:r,replyTo:i}=this.config,a=`"${r||"Kyro CMS"}" <${s}>`,n=e.replyTo||i;try{let c;switch(t){case "smtp":case "ses":{let l=await this.ensureTransporter();if(!l)throw new Error(`${t} transporter not initialized`);c=await l.sendMail({from:a,to:Array.isArray(e.to)?e.to.join(", "):e.to,subject:e.subject,html:e.html,text:e.text,replyTo:n});}break;case "resend":c=await this.sendViaResend(a,e,n);break;case "sendgrid":c=await this.sendViaSendGrid(a,e,n);break;case "mailgun":c=await this.sendViaMailgun(a,e,n);break;default:throw new Error(`Unsupported email provider: ${t}`)}return c}catch(c){throw console.error("[EmailTransport] FAILED to send email:",c.message),c.response&&console.error("[EmailTransport] Provider Error Detail:",JSON.stringify(c.response,null,2)),c}}async sendViaResend(e,t,s){let r=this.config.resend?.apiKey;if(!r)throw new Error("Resend API Key missing");let i={from:e,to:t.to,subject:t.subject,html:t.html,text:t.text,reply_to:s},a=await fetch("https://api.resend.com/emails",{method:"POST",headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},body:JSON.stringify(i)});if(!a.ok){let n=await a.json();throw new Error(`Resend Error: ${JSON.stringify(n)}`)}return a.json()}async sendViaSendGrid(e,t,s){let r=this.config.sendgrid?.apiKey;if(!r)throw new Error("SendGrid API Key missing");let i={personalizations:[{to:Array.isArray(t.to)?t.to.map(n=>({email:n})):[{email:t.to}]}],from:{email:e.match(/<(.+)>/)?.[1]||e,name:e.match(/"(.+)"/)?.[1]},subject:t.subject,content:[{type:"text/plain",value:t.text||""},{type:"text/html",value:t.html}],reply_to:s?{email:s}:void 0},a=await fetch("https://api.sendgrid.com/v3/mail/send",{method:"POST",headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},body:JSON.stringify(i)});if(!a.ok){let n=await a.json();throw new Error(`SendGrid Error: ${JSON.stringify(n)}`)}return {success:true}}async sendViaMailgun(e,t,s){let{apiKey:r,domain:i,region:a}=this.config.mailgun||{};if(!r||!i)throw new Error("Mailgun config missing");let n=a==="eu"?"api.eu.mailgun.net":"api.mailgun.net",c=btoa(`api:${r}`),l=new URLSearchParams;l.append("from",e);let f=Array.isArray(t.to)?t.to.join(", "):t.to;l.append("to",f),l.append("subject",t.subject),l.append("html",t.html),t.text&&l.append("text",t.text),s&&l.append("h:Reply-To",s);let d=await fetch(`https://${n}/v3/${i}/messages`,{method:"POST",headers:{Authorization:`Basic ${c}`,"Content-Type":"application/x-www-form-urlencoded"},body:l});if(!d.ok){let y=await d.json();throw new Error(`Mailgun Error: ${JSON.stringify(y)}`)}return d.json()}getTemplates(){return this.templates}async verifyConnection(){if(this.config.provider==="smtp"||this.config.provider==="ses")try{let e=await this.ensureTransporter();if(e)return await e.verify(),!0}catch{return false}return !!(this.config.resend?.apiKey||this.config.sendgrid?.apiKey||this.config.mailgun?.apiKey)}static async fromConfig(e){let t=new p(e);await t.load();let s=t.getEmailConfig();if(!s.provider)return this.fromEnv();let r={provider:s.provider||"smtp",from:s.from||"noreply@example.com",fromName:s.fromName,replyTo:s.replyTo,smtp:s.provider==="smtp"?{host:s.host||"",port:s.port||587,secure:s.secure||false,auth:{user:s.user||"",pass:s.pass||""}}:void 0,resend:s.provider==="resend"?{apiKey:s.pass||""}:void 0,sendgrid:s.provider==="sendgrid"?{apiKey:s.pass||""}:void 0,mailgun:s.provider==="mailgun"?{apiKey:s.pass||"",domain:s.host||"",region:s.secure?"eu":"us"}:void 0,ses:s.provider==="ses"?{accessKeyId:s.user||"",secretAccessKey:s.pass||"",region:s.host||"us-east-1"}:void 0};return new o(r)}static fromEnv(){let e=process.env.EMAIL_PROVIDER||"smtp",t=process.env.SMTP_FROM||process.env.DEFAULT_FROM||"noreply@example.com",s=process.env.SMTP_FROM_NAME||"Kyro CMS",r=process.env.SMTP_REPLY_TO;if(e==="smtp"){let i=process.env.SMTP_HOST,a=process.env.SMTP_USER,n=process.env.SMTP_PASS;return !i||!a||!n?null:new o({provider:"smtp",from:t,fromName:s,replyTo:r,smtp:{host:i,port:parseInt(process.env.SMTP_PORT||"587",10),secure:process.env.SMTP_SECURE==="true",auth:{user:a,pass:n}}})}if(e==="resend"){let i=process.env.RESEND_API_KEY||process.env.SMTP_PASS;return i?new o({provider:"resend",from:t,fromName:s,replyTo:r,resend:{apiKey:i}}):null}if(e==="sendgrid"){let i=process.env.SENDGRID_API_KEY||process.env.SMTP_PASS;return i?new o({provider:"sendgrid",from:t,fromName:s,replyTo:r,sendgrid:{apiKey:i}}):null}if(e==="mailgun"){let i=process.env.MAILGUN_API_KEY||process.env.SMTP_PASS,a=process.env.MAILGUN_DOMAIN||process.env.SMTP_HOST;return !i||!a?null:new o({provider:"mailgun",from:t,fromName:s,replyTo:r,mailgun:{apiKey:i,domain:a,region:process.env.MAILGUN_REGION||(process.env.SMTP_SECURE==="true"?"eu":"us")}})}if(e==="ses"){let i=process.env.AWS_ACCESS_KEY_ID||process.env.SMTP_USER,a=process.env.AWS_SECRET_ACCESS_KEY||process.env.SMTP_PASS,n=process.env.AWS_REGION||process.env.SMTP_HOST||"us-east-1";return !i||!a?null:new o({provider:"ses",from:t,fromName:s,replyTo:r,ses:{accessKeyId:i,secretAccessKey:a,region:n}})}return null}};var _={minLength:12,requireUppercase:true,requireLowercase:true,requireNumbers:true,requireSpecialChars:true,preventReuse:5,maxLength:128},m=class{config;constructor(e={}){this.config={..._,...e};}validate(e){let t=[];return this.config.maxLength&&e.length>this.config.maxLength&&t.push(`Password must not exceed ${this.config.maxLength} characters`),e.length<this.config.minLength&&t.push(`Password must be at least ${this.config.minLength} characters`),this.config.requireUppercase&&!/[A-Z]/.test(e)&&t.push("Password must contain at least one uppercase letter"),this.config.requireLowercase&&!/[a-z]/.test(e)&&t.push("Password must contain at least one lowercase letter"),this.config.requireNumbers&&!/[0-9]/.test(e)&&t.push("Password must contain at least one number"),this.config.requireSpecialChars&&!/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(e)&&t.push("Password must contain at least one special character"),["password","123456","12345678","qwerty","abc123","monkey","1234567","letmein","trustno1","dragon","baseball","iloveyou","master","sunshine","ashley","football","password1","shadow","123123","654321"].includes(e.toLowerCase())&&t.push("This password is too common. Please choose a more secure password"),(/^[a-zA-Z]+$/.test(e)||/^[0-9]+$/.test(e))&&t.push("Password must contain a mix of letters, numbers, and/or special characters"),/(.)\1{2,}/.test(e)&&t.push("Password must not contain more than 2 consecutive identical characters"),/^(012|123|234|345|456|567|678|789|890|098|987|876|765|654|543|432|321|210)+$/i.test(e)&&t.push("Password must not contain sequential numbers or letters"),{valid:t.length===0,errors:t}}async checkReuse(e,t,s){return {valid:true,errors:[]}}async isInHistory(e,t,s){for(let r of t)if(await s(e,r))return true;return false}generatePassword(e=16){let t="ABCDEFGHIJKLMNOPQRSTUVWXYZ",s="abcdefghijklmnopqrstuvwxyz",r="0123456789",i="!@#$%^&*()_+-=[]{}|;:,.<>?",a="";a+=t[Math.floor(Math.random()*t.length)],a+=s[Math.floor(Math.random()*s.length)],a+=r[Math.floor(Math.random()*r.length)],a+=i[Math.floor(Math.random()*i.length)];let n=t+s+r+i;for(let c=a.length;c<e;c++)a+=n[Math.floor(Math.random()*n.length)];return a.split("").sort(()=>Math.random()-.5).join("")}getStrength(e){let t=0,s=[];e.length>=8&&(t+=1),e.length>=12&&(t+=1),e.length>=16&&(t+=1),/[a-z]/.test(e)&&(t+=1),/[A-Z]/.test(e)&&(t+=1),/[0-9]/.test(e)&&(t+=1),/[!@#$%^&*()_+\-=\[\]{}|;:,.<>?]/.test(e)&&(t+=1),e.length>8&&(t+=1),e.length>12&&(t+=1);let r=new Set(e).size;r>6&&(t+=1),r>10&&(t+=1);let i;return t<=3?(i="Weak",s.push("Add more characters"),s.push("Include uppercase and lowercase letters")):t<=5?(i="Fair",s.push("Add special characters"),s.push("Consider making it longer")):t<=7?(i="Good",s.push("Consider making it longer for extra security")):i="Strong",{score:t,label:i,feedback:s}}setConfig(e){this.config={...this.config,...e};}getConfig(){return {...this.config}}};
259
+ exports.a=p;exports.b=h;exports.c=m;
@@ -0,0 +1 @@
1
+ 'use strict';var h=require('bcryptjs'),crypto=require('crypto');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var h__default=/*#__PURE__*/_interopDefault(h);var w=class{db;prefix;sessionTTL;refreshTokenTTL;indexesEnsured=false;constructor(e){this.db=e.db,this.prefix=e.collectionPrefix||"",this.sessionTTL=e.sessionTTL||86400,this.refreshTokenTTL=e.refreshTokenTTL||604800;}col(e){return this.db.collection(`${this.prefix}${e}`)}async connect(){this.indexesEnsured||await this.ensureIndexes();}async disconnect(){}async ensureIndexes(){await this.col("users").createIndex({email:1},{unique:true}),await this.col("users").createIndex({tenantId:1}),await this.col("users").createIndex({role:1}),await this.col("sessions").createIndex({token:1},{unique:true}),await this.col("sessions").createIndex({refreshToken:1}),await this.col("sessions").createIndex({userId:1}),await this.col("sessions").createIndex({expiresAt:1},{expireAfterSeconds:0}),await this.col("audit_logs").createIndex({timestamp:-1}),await this.col("audit_logs").createIndex({userId:1}),await this.col("audit_logs").createIndex({action:1}),await this.col("audit_logs").createIndex({resource:1}),await this.col("password_history").createIndex({userId:1,createdAt:-1}),await this.col("lockouts").createIndex({userId:1},{unique:true}),await this.col("lockouts").createIndex({lockedUntil:1}),this.indexesEnsured=true;}async createUser(e){let s=crypto.randomUUID(),t=new Date,i=await this.hashPassword(e.password),n={_id:s,name:e.name||null,email:e.email.toLowerCase(),passwordHash:i,role:e.role||"customer",avatar:e.avatar||null,tenantId:e.tenantId||null,emailVerified:false,locked:false,lastLogin:null,failedLoginAttempts:0,createdAt:t,updatedAt:t};return await this.col("users").insertOne(n),this.docToAuthUser(n)}async findUserByEmail(e){let s=await this.col("users").findOne({email:e.toLowerCase()});return s?this.docToAuthUser(s):null}async findUserById(e){let s=await this.col("users").findOne({_id:e});return s?this.docToAuthUser(s):null}async updateUser(e,s){let t={updatedAt:new Date};s.name!==void 0&&(t.name=s.name),s.email!==void 0&&(t.email=s.email),s.passwordHash!==void 0&&(t.passwordHash=s.passwordHash),s.role!==void 0&&(t.role=s.role),s.avatar!==void 0&&(t.avatar=s.avatar),s.tenantId!==void 0&&(t.tenantId=s.tenantId),s.emailVerified!==void 0&&(t.emailVerified=s.emailVerified),s.locked!==void 0&&(t.locked=s.locked),s.lastLogin!==void 0&&(t.lastLogin=s.lastLogin?new Date(s.lastLogin):null),s.failedLoginAttempts!==void 0&&(t.failedLoginAttempts=s.failedLoginAttempts);let i=await this.col("users").findOneAndUpdate({_id:e},{$set:t},{returnDocument:"after"});return i?this.docToAuthUser(i):null}async deleteUser(e){return (await this.col("users").deleteOne({_id:e})).deletedCount>0}async verifyPassword(e,s){let t=await this.findUserByEmail(e);if(!t)return null;let i=await this.col("users").findOne({email:e.toLowerCase()},{projection:{passwordHash:1}});return i?.passwordHash&&await h__default.default.compare(s,i.passwordHash)?t:null}async hashPassword(e){return h__default.default.hash(e,12)}async createSession(e,s){let t=crypto.randomUUID(),i=crypto.randomBytes(32).toString("base64url"),n=crypto.randomBytes(32).toString("base64url"),a=new Date,u=new Date(a.getTime()+this.sessionTTL*1e3),d={_id:t,userId:e,token:i,refreshToken:n,ipAddress:s?.ipAddress||null,userAgent:s?.userAgent||null,expiresAt:u,createdAt:a};return await this.col("sessions").insertOne(d),this.docToSession(d)}async findSessionByToken(e){let s=await this.col("sessions").findOne({token:e,expiresAt:{$gt:new Date}});return s?this.docToSession(s):null}async findSessionByRefreshToken(e){let s=await this.col("sessions").findOne({refreshToken:e,expiresAt:{$gt:new Date}});return s?this.docToSession(s):null}async deleteSession(e){return (await this.col("sessions").deleteOne({_id:e})).deletedCount>0}async deleteUserSessions(e){return (await this.col("sessions").deleteMany({userId:e})).deletedCount}async hasAnyUsers(){return await this.col("users").countDocuments()>0}async addPasswordToHistory(e,s){await this.col("password_history").insertOne({userId:e,passwordHash:s,createdAt:new Date});}async getPasswordHistory(e,s=5){return (await this.col("password_history").find({userId:e}).sort({createdAt:-1}).limit(s).toArray()).map(i=>i.passwordHash)}async isPasswordInHistory(e,s,t=5){let i=await this.getPasswordHistory(s,t);for(let n of i)if(await h__default.default.compare(e,n))return true;return false}async findAuditLogs(e){let{limit:s=50,offset:t=0,userId:i,action:n,resource:a,resourceId:u,success:d,startDate:c,endDate:f}=e,o={};i&&(o.userId=i),n&&(Array.isArray(n)?o.action={$in:n}:o.action=n),a&&(o.resource=a),u&&(o.resourceId=u),d!==void 0&&(o.success=d),(c||f)&&(o.timestamp={},c&&(o.timestamp.$gte=c),f&&(o.timestamp.$lte=f));let g=await this.col("audit_logs").countDocuments(o);return {logs:(await this.col("audit_logs").find(o).sort({timestamp:-1}).skip(t).limit(s).toArray()).map(r=>({id:r._id,timestamp:r.timestamp,action:r.action,userId:r.userId||void 0,userEmail:r.userEmail||void 0,role:r.role||void 0,resource:r.resource,resourceId:r.resourceId||void 0,changes:r.changes||void 0,ipAddress:r.ipAddress||void 0,userAgent:r.userAgent||void 0,success:r.success,error:r.error||void 0,metadata:r.metadata||void 0})),total:g}}async createAuditLog(e){let s=crypto.randomUUID(),t=new Date;return await this.col("audit_logs").insertOne({_id:s,action:e.action,userId:e.userId||null,userEmail:e.userEmail||null,role:e.role||null,resource:e.resource,resourceId:e.resourceId||null,changes:e.changes||null,ipAddress:e.ipAddress||null,userAgent:e.userAgent||null,success:e.success,error:e.error||null,metadata:e.metadata||null,timestamp:t}),{...e,id:s,timestamp:t}}docToAuthUser(e){return {id:e._id,name:e.name||void 0,email:e.email,passwordHash:e.passwordHash||void 0,role:e.role,tenantId:e.tenantId||void 0,avatar:e.avatar||void 0,emailVerified:e.emailVerified||false,locked:e.locked||false,lastLogin:e.lastLogin?.toISOString?.()||e.lastLogin||void 0,failedLoginAttempts:e.failedLoginAttempts||0,createdAt:e.createdAt?.toISOString?.()||e.createdAt,updatedAt:e.updatedAt?.toISOString?.()||e.updatedAt}}docToSession(e){return {id:e._id,userId:e.userId,token:e.token,refreshToken:e.refreshToken||void 0,expiresAt:e.expiresAt?.toISOString?.()||e.expiresAt,createdAt:e.createdAt?.toISOString?.()||e.createdAt,ipAddress:e.ipAddress||void 0,userAgent:e.userAgent||void 0}}async createEmailVerificationToken(e){let s=crypto.randomBytes(32).toString("hex"),t=new Date(Date.now()+1440*60*1e3);return await this.db.collection("email_verifications").insertOne({userId:new(await import('mongodb')).ObjectId(e),token:s,expiresAt:t,createdAt:new Date}),{token:s,expiresAt:t}}async verifyEmailToken(e){let s=await this.db.collection("email_verifications").findOne({token:e});return s?s.expiresAt<new Date?{success:false,error:"Verification token has expired"}:(await this.db.collection("users").updateOne({_id:s.userId},{$set:{emailVerified:true}}),await this.db.collection("email_verifications").deleteOne({_id:s._id}),{success:true,userId:s.userId.toString()}):{success:false,error:"Invalid verification token"}}async createPasswordResetToken(e){let s=await this.findUserByEmail(e);if(!s)return {token:"",expiresAt:new Date,error:"User not found"};let t=crypto.randomBytes(32).toString("hex"),i=new Date(Date.now()+3600*1e3);return await this.db.collection("password_resets").insertOne({userId:new(await import('mongodb')).ObjectId(s.id),token:t,expiresAt:i,createdAt:new Date}),{token:t,expiresAt:i}}async resetPasswordWithToken(e,s){let t=await this.db.collection("password_resets").findOne({token:e});if(!t)return {success:false,error:"Invalid reset token"};if(t.expiresAt<new Date)return {success:false,error:"Reset token has expired"};if(t.usedAt)return {success:false,error:"Reset token has already been used"};let i=await this.hashPassword(s);return await this.db.collection("users").updateOne({_id:t.userId},{$set:{passwordHash:i,updatedAt:new Date}}),await this.db.collection("password_resets").updateOne({_id:t._id},{$set:{usedAt:new Date}}),await this.db.collection("sessions").deleteMany({userId:t.userId}),{success:true}}};exports.a=w;