@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.
- package/dist/api-handler-graphql.cjs +1 -49
- package/dist/api-handler-graphql.js +1 -46
- package/dist/api-handler-trpc.cjs +1 -38
- package/dist/api-handler-trpc.js +1 -36
- package/dist/api-handler.cjs +1 -44
- package/dist/api-handler.js +1 -27
- package/dist/bootstrap-HIAV5ZFT.cjs +1 -0
- package/dist/bootstrap-P7QPQYQP.js +1 -0
- package/dist/chunk-2BTRHVT3.cjs +20 -0
- package/dist/chunk-2W4FK6CJ.js +122 -0
- package/dist/chunk-4ORJMSJU.cjs +1 -0
- package/dist/chunk-65G73T7Z.cjs +2 -0
- package/dist/chunk-7N6NKCRT.js +1 -0
- package/dist/chunk-7WOX27LK.js +259 -0
- package/dist/chunk-AE6GXUT7.js +1 -0
- package/dist/chunk-AJAZ7D3W.cjs +259 -0
- package/dist/chunk-ATLSIYRH.cjs +1 -0
- package/dist/chunk-AU6IWTBO.js +2 -0
- package/dist/chunk-CDL7ARXT.js +133 -0
- package/dist/chunk-CUTFS6N2.cjs +1 -0
- package/dist/chunk-CVVW4LXZ.cjs +1 -0
- package/dist/chunk-DGJUBN33.js +1 -0
- package/dist/chunk-DV4LFLPL.cjs +1 -0
- package/dist/chunk-EEWCANFX.js +1 -0
- package/dist/chunk-ELICKLFM.cjs +3 -0
- package/dist/chunk-EQUQJBPP.cjs +2 -0
- package/dist/chunk-ET6NN2V2.cjs +122 -0
- package/dist/chunk-F7P36SLX.cjs +74 -0
- package/dist/chunk-F7SR5IDT.cjs +1 -0
- package/dist/chunk-FO3HLBC7.js +1 -0
- package/dist/chunk-FS7GL2BR.js +73 -0
- package/dist/chunk-G6A7OSIG.js +2 -0
- package/dist/chunk-GFU7AOI3.cjs +1 -0
- package/dist/chunk-GYQMNXXW.cjs +114 -0
- package/dist/chunk-HLSB2MCK.js +1 -0
- package/dist/chunk-HVXO3OFB.cjs +1 -0
- package/dist/chunk-IFJJ44OQ.cjs +133 -0
- package/dist/chunk-ISWMETHJ.cjs +1 -0
- package/dist/chunk-K6WUOU6P.js +1 -0
- package/dist/chunk-KDNYHLD2.js +1 -0
- package/dist/chunk-KIW36TG7.cjs +86 -0
- package/dist/chunk-L6CZYN3L.cjs +20 -0
- package/dist/chunk-LRGAPGG3.js +74 -0
- package/dist/chunk-LZ6YFYQM.js +20 -0
- package/dist/chunk-MIOMMB23.cjs +1 -0
- package/dist/chunk-NGBJKZMT.js +90 -0
- package/dist/chunk-NRLIGDYF.js +1 -0
- package/dist/chunk-NY47CFVP.cjs +1 -0
- package/dist/chunk-NZ35PV5R.js +2 -0
- package/dist/chunk-OG7CNQGU.js +1 -0
- package/dist/chunk-OQD3WG6Z.cjs +1 -0
- package/dist/chunk-P7CESLDJ.js +1 -0
- package/dist/chunk-PP5S2UBV.js +0 -0
- package/dist/chunk-Q3Q7BJVQ.cjs +1 -0
- package/dist/chunk-Q4CSLXCO.cjs +90 -0
- package/dist/chunk-QRXDCV6K.js +0 -0
- package/dist/chunk-R7DVVXXI.cjs +73 -0
- package/dist/chunk-RHCWCG3O.js +114 -0
- package/dist/chunk-SL3LZQCF.js +1 -0
- package/dist/chunk-UBHFUFD4.cjs +1 -0
- package/dist/chunk-UBRYJP2G.js +1 -0
- package/dist/chunk-UDSJGML2.cjs +2 -0
- package/dist/chunk-UHFDH3PG.js +20 -0
- package/dist/chunk-UJNSOOYZ.cjs +1 -0
- package/dist/chunk-URCLFIME.js +86 -0
- package/dist/chunk-UVTS2IWM.js +1 -0
- package/dist/chunk-V65NPSR5.cjs +1 -0
- package/dist/chunk-WIZ6C5QZ.cjs +1 -0
- package/dist/chunk-ZAQOSRP6.js +1 -0
- package/dist/chunk-ZELCS7IC.js +3 -0
- package/dist/cli/index.cjs +35 -405
- package/dist/cli/index.js +35 -402
- package/dist/client.cjs +1 -55
- package/dist/client.js +1 -6
- package/dist/drizzle/index.cjs +1 -50
- package/dist/drizzle/index.d.cts +1 -2
- package/dist/drizzle/index.d.ts +1 -2
- package/dist/drizzle/index.js +1 -9
- package/dist/fields/index.cjs +1 -89
- package/dist/fields/index.js +1 -4
- package/dist/graphql/index.cjs +1 -31
- package/dist/graphql/index.d.cts +1 -1
- package/dist/graphql/index.d.ts +1 -1
- package/dist/graphql/index.js +1 -6
- package/dist/{index-jLs0zlft.d.ts → index-DXr33e0x.d.ts} +1 -1
- package/dist/{index-DHbywDs-.d.cts → index-DuuUzWBq.d.cts} +1 -1
- package/dist/index.cjs +29 -2464
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +29 -1929
- package/dist/integration.cjs +1 -10
- package/dist/integration.js +1 -4
- package/dist/media-NSSMOB7N.cjs +1 -0
- package/dist/media-RNMCZYPZ.js +1 -0
- package/dist/mongo-auth-adapter-JYJ4GBZE.cjs +1 -0
- package/dist/mongo-auth-adapter-XI7EPURZ.js +1 -0
- package/dist/mongodb/index.cjs +1 -29
- package/dist/mongodb/index.d.cts +1 -2
- package/dist/mongodb/index.d.ts +1 -2
- package/dist/mongodb/index.js +1 -8
- package/dist/postgres-auth-adapter-E7OGMDD4.cjs +1 -0
- package/dist/postgres-auth-adapter-F4KLOGAG.js +1 -0
- package/dist/redis-adapter-AQJVABYT.cjs +1 -0
- package/dist/redis-adapter-AUKOV74G.js +1 -0
- package/dist/rest/index.cjs +1 -31
- package/dist/rest/index.d.cts +1 -1
- package/dist/rest/index.d.ts +1 -1
- package/dist/rest/index.js +1 -18
- package/dist/schema-5XNROI7I.js +1 -0
- package/dist/schema-6O4JTJRU.cjs +1 -0
- package/dist/sqlite-adapter-5XFLNJDH.js +1 -0
- package/dist/sqlite-adapter-W53Q32N5.cjs +1 -0
- package/dist/templates/index.cjs +1 -207
- package/dist/templates/index.js +1 -6
- package/dist/trpc/index.cjs +1 -48
- package/dist/trpc/index.js +1 -7
- package/dist/ws/index.cjs +1 -25
- package/dist/ws/index.js +1 -4
- package/package.json +44 -13
- package/dist/api-handler-graphql.cjs.map +0 -1
- package/dist/api-handler-graphql.js.map +0 -1
- package/dist/api-handler-trpc.cjs.map +0 -1
- package/dist/api-handler-trpc.js.map +0 -1
- package/dist/api-handler.cjs.map +0 -1
- package/dist/api-handler.js.map +0 -1
- package/dist/bootstrap-FUAPY2OF.cjs +0 -32
- package/dist/bootstrap-FUAPY2OF.cjs.map +0 -1
- package/dist/bootstrap-OXNVM7FP.js +0 -7
- package/dist/bootstrap-OXNVM7FP.js.map +0 -1
- package/dist/chunk-2HFJUUFZ.js +0 -250
- package/dist/chunk-2HFJUUFZ.js.map +0 -1
- package/dist/chunk-3AKCVYDB.js +0 -162
- package/dist/chunk-3AKCVYDB.js.map +0 -1
- package/dist/chunk-3KTWGODI.cjs +0 -178
- package/dist/chunk-3KTWGODI.cjs.map +0 -1
- package/dist/chunk-3YELQL7Z.cjs +0 -4
- package/dist/chunk-3YELQL7Z.cjs.map +0 -1
- package/dist/chunk-4CV4JOE5.js +0 -24
- package/dist/chunk-4CV4JOE5.js.map +0 -1
- package/dist/chunk-4OCMVGEY.cjs +0 -173
- package/dist/chunk-4OCMVGEY.cjs.map +0 -1
- package/dist/chunk-4PWRCMTQ.cjs +0 -15
- package/dist/chunk-4PWRCMTQ.cjs.map +0 -1
- package/dist/chunk-53NYVYVX.js +0 -3243
- package/dist/chunk-53NYVYVX.js.map +0 -1
- package/dist/chunk-5GFQPHGT.cjs +0 -156
- package/dist/chunk-5GFQPHGT.cjs.map +0 -1
- package/dist/chunk-5RPRLTTN.js +0 -861
- package/dist/chunk-5RPRLTTN.js.map +0 -1
- package/dist/chunk-74YSDAWE.cjs +0 -2749
- package/dist/chunk-74YSDAWE.cjs.map +0 -1
- package/dist/chunk-7OGPN7MP.js +0 -352
- package/dist/chunk-7OGPN7MP.js.map +0 -1
- package/dist/chunk-7OS7TX2Q.cjs +0 -495
- package/dist/chunk-7OS7TX2Q.cjs.map +0 -1
- package/dist/chunk-ADLJSJSN.cjs +0 -13
- package/dist/chunk-ADLJSJSN.cjs.map +0 -1
- package/dist/chunk-B6X3LN25.cjs +0 -6486
- package/dist/chunk-B6X3LN25.cjs.map +0 -1
- package/dist/chunk-BYBMTIMT.js +0 -419
- package/dist/chunk-BYBMTIMT.js.map +0 -1
- package/dist/chunk-CF7OL6HR.js +0 -355
- package/dist/chunk-CF7OL6HR.js.map +0 -1
- package/dist/chunk-CJX74IYK.js +0 -489
- package/dist/chunk-CJX74IYK.js.map +0 -1
- package/dist/chunk-CKVOU6MX.cjs +0 -52
- package/dist/chunk-CKVOU6MX.cjs.map +0 -1
- package/dist/chunk-CZLDE2OZ.cjs +0 -28
- package/dist/chunk-CZLDE2OZ.cjs.map +0 -1
- package/dist/chunk-DPA3KWPY.cjs +0 -205
- package/dist/chunk-DPA3KWPY.cjs.map +0 -1
- package/dist/chunk-EEJUFDMF.cjs +0 -424
- package/dist/chunk-EEJUFDMF.cjs.map +0 -1
- package/dist/chunk-FIAMXNTH.js +0 -220
- package/dist/chunk-FIAMXNTH.js.map +0 -1
- package/dist/chunk-GAOXD3XT.js +0 -175
- package/dist/chunk-GAOXD3XT.js.map +0 -1
- package/dist/chunk-GTGRLD4Y.js +0 -3
- package/dist/chunk-GTGRLD4Y.js.map +0 -1
- package/dist/chunk-GUUB5EAG.cjs +0 -223
- package/dist/chunk-GUUB5EAG.cjs.map +0 -1
- package/dist/chunk-GXFOGU7N.cjs +0 -359
- package/dist/chunk-GXFOGU7N.cjs.map +0 -1
- package/dist/chunk-GZS4LMR7.js +0 -422
- package/dist/chunk-GZS4LMR7.js.map +0 -1
- package/dist/chunk-HY6B7B5A.js +0 -150
- package/dist/chunk-HY6B7B5A.js.map +0 -1
- package/dist/chunk-HZOMOK5Y.js +0 -2728
- package/dist/chunk-HZOMOK5Y.js.map +0 -1
- package/dist/chunk-IDVRRRAK.cjs +0 -824
- package/dist/chunk-IDVRRRAK.cjs.map +0 -1
- package/dist/chunk-KC2GDBLS.cjs +0 -84
- package/dist/chunk-KC2GDBLS.cjs.map +0 -1
- package/dist/chunk-L4EZKIEX.js +0 -185
- package/dist/chunk-L4EZKIEX.js.map +0 -1
- package/dist/chunk-LNTFRAHF.cjs +0 -473
- package/dist/chunk-LNTFRAHF.cjs.map +0 -1
- package/dist/chunk-MTIRYI7F.cjs +0 -4
- package/dist/chunk-MTIRYI7F.cjs.map +0 -1
- package/dist/chunk-N4GN6Q3V.cjs +0 -869
- package/dist/chunk-N4GN6Q3V.cjs.map +0 -1
- package/dist/chunk-N4H37VN4.cjs +0 -259
- package/dist/chunk-N4H37VN4.cjs.map +0 -1
- package/dist/chunk-N5QSHUA6.js +0 -1135
- package/dist/chunk-N5QSHUA6.js.map +0 -1
- package/dist/chunk-NKPKR5BW.cjs +0 -188
- package/dist/chunk-NKPKR5BW.cjs.map +0 -1
- package/dist/chunk-OIRG6ZRV.cjs +0 -1073
- package/dist/chunk-OIRG6ZRV.cjs.map +0 -1
- package/dist/chunk-OJ2AM75W.cjs +0 -253
- package/dist/chunk-OJ2AM75W.cjs.map +0 -1
- package/dist/chunk-OLV7K22H.js +0 -6457
- package/dist/chunk-OLV7K22H.js.map +0 -1
- package/dist/chunk-OX6GOIPH.js +0 -516
- package/dist/chunk-OX6GOIPH.js.map +0 -1
- package/dist/chunk-P2YW545G.js +0 -11
- package/dist/chunk-P2YW545G.js.map +0 -1
- package/dist/chunk-PHJRNPHY.cjs +0 -3291
- package/dist/chunk-PHJRNPHY.cjs.map +0 -1
- package/dist/chunk-Q72BOAPK.js +0 -817
- package/dist/chunk-Q72BOAPK.js.map +0 -1
- package/dist/chunk-QU2RFFH4.js +0 -3
- package/dist/chunk-QU2RFFH4.js.map +0 -1
- package/dist/chunk-RP7VZUEL.js +0 -49
- package/dist/chunk-RP7VZUEL.js.map +0 -1
- package/dist/chunk-SHTTJMLT.cjs +0 -361
- package/dist/chunk-SHTTJMLT.cjs.map +0 -1
- package/dist/chunk-SX76DNOO.js +0 -103
- package/dist/chunk-SX76DNOO.js.map +0 -1
- package/dist/chunk-THBQG64D.cjs +0 -425
- package/dist/chunk-THBQG64D.cjs.map +0 -1
- package/dist/chunk-TQG7G7YI.cjs +0 -526
- package/dist/chunk-TQG7G7YI.cjs.map +0 -1
- package/dist/chunk-U3VVLYXG.cjs +0 -189
- package/dist/chunk-U3VVLYXG.cjs.map +0 -1
- package/dist/chunk-UUDTPZX6.js +0 -193
- package/dist/chunk-UUDTPZX6.js.map +0 -1
- package/dist/chunk-V5AWATUP.js +0 -1059
- package/dist/chunk-V5AWATUP.js.map +0 -1
- package/dist/chunk-VAXLFZCV.cjs +0 -110
- package/dist/chunk-VAXLFZCV.cjs.map +0 -1
- package/dist/chunk-VKREAIBA.cjs +0 -1139
- package/dist/chunk-VKREAIBA.cjs.map +0 -1
- package/dist/chunk-WDTTHKDS.js +0 -458
- package/dist/chunk-WDTTHKDS.js.map +0 -1
- package/dist/chunk-WXVB364T.js +0 -218
- package/dist/chunk-WXVB364T.js.map +0 -1
- package/dist/chunk-X4BPPD6K.js +0 -181
- package/dist/chunk-X4BPPD6K.js.map +0 -1
- package/dist/chunk-XEB7PH2E.js +0 -81
- package/dist/chunk-XEB7PH2E.js.map +0 -1
- package/dist/chunk-YT7HXXVN.js +0 -13
- package/dist/chunk-YT7HXXVN.js.map +0 -1
- package/dist/cli/index.cjs.map +0 -1
- package/dist/cli/index.js.map +0 -1
- package/dist/client.cjs.map +0 -1
- package/dist/client.js.map +0 -1
- package/dist/drizzle/index.cjs.map +0 -1
- package/dist/drizzle/index.js.map +0 -1
- package/dist/fields/index.cjs.map +0 -1
- package/dist/fields/index.js.map +0 -1
- package/dist/graphql/index.cjs.map +0 -1
- package/dist/graphql/index.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/integration.cjs.map +0 -1
- package/dist/integration.js.map +0 -1
- package/dist/media-7WDX4BDJ.js +0 -4
- package/dist/media-7WDX4BDJ.js.map +0 -1
- package/dist/media-TUSLVRQ6.cjs +0 -17
- package/dist/media-TUSLVRQ6.cjs.map +0 -1
- package/dist/mongo-auth-adapter-GT4S7SCU.cjs +0 -17
- package/dist/mongo-auth-adapter-GT4S7SCU.cjs.map +0 -1
- package/dist/mongo-auth-adapter-M7VV4LNB.js +0 -4
- package/dist/mongo-auth-adapter-M7VV4LNB.js.map +0 -1
- package/dist/mongodb/index.cjs.map +0 -1
- package/dist/mongodb/index.js.map +0 -1
- package/dist/postgres-auth-adapter-AFAPISH7.js +0 -5
- package/dist/postgres-auth-adapter-AFAPISH7.js.map +0 -1
- package/dist/postgres-auth-adapter-SFDTLONT.cjs +0 -14
- package/dist/postgres-auth-adapter-SFDTLONT.cjs.map +0 -1
- package/dist/redis-adapter-UQX4EE3B.cjs +0 -13
- package/dist/redis-adapter-UQX4EE3B.cjs.map +0 -1
- package/dist/redis-adapter-XALOGWY3.js +0 -4
- package/dist/redis-adapter-XALOGWY3.js.map +0 -1
- package/dist/rest/index.cjs.map +0 -1
- package/dist/rest/index.js.map +0 -1
- package/dist/schema-6QL3USNB.cjs +0 -67
- package/dist/schema-6QL3USNB.cjs.map +0 -1
- package/dist/schema-FNNWEAAW.js +0 -6
- package/dist/schema-FNNWEAAW.js.map +0 -1
- package/dist/sqlite-adapter-AQB5TCGV.cjs +0 -13
- package/dist/sqlite-adapter-AQB5TCGV.cjs.map +0 -1
- package/dist/sqlite-adapter-N5H6IM2X.js +0 -4
- package/dist/sqlite-adapter-N5H6IM2X.js.map +0 -1
- package/dist/templates/index.cjs.map +0 -1
- package/dist/templates/index.js.map +0 -1
- package/dist/trpc/index.cjs.map +0 -1
- package/dist/trpc/index.js.map +0 -1
- package/dist/ws/index.cjs.map +0 -1
- package/dist/ws/index.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,1943 +1,43 @@
|
|
|
1
|
-
export { RedisAuthAdapter } from './chunk-CF7OL6HR.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
import './chunk-QU2RFFH4.js';
|
|
24
|
-
export { DrizzleAdapter, collectionToDrizzleSchema, createDatabase, createDrizzleAdapter, fieldToDrizzleType, runMigrations, seedDefaultRoles } from './chunk-V5AWATUP.js';
|
|
25
|
-
import { PostgresAuthAdapter } from './chunk-CJX74IYK.js';
|
|
26
|
-
export { PostgresAuthAdapter } from './chunk-CJX74IYK.js';
|
|
27
|
-
import './chunk-UUDTPZX6.js';
|
|
28
|
-
import './chunk-GTGRLD4Y.js';
|
|
29
|
-
export { MongoDBAdapter, createMongoDBAdapter } from './chunk-GZS4LMR7.js';
|
|
30
|
-
import { MongoDBAuthAdapter } from './chunk-7OGPN7MP.js';
|
|
31
|
-
export { MongoDBAuthAdapter } from './chunk-7OGPN7MP.js';
|
|
32
|
-
export { AbstractBaseAdapter } from './chunk-WXVB364T.js';
|
|
33
|
-
import './chunk-L4EZKIEX.js';
|
|
34
|
-
import './chunk-4CV4JOE5.js';
|
|
35
|
-
import crypto2, { randomBytes } from 'crypto';
|
|
36
|
-
import { readFileSync } from 'fs';
|
|
37
|
-
import path, { join, resolve } from 'path';
|
|
38
|
-
export { z } from 'zod';
|
|
39
|
-
import { createStorage } from 'unstorage';
|
|
40
|
-
import indexedbDriver from 'unstorage/drivers/indexedb';
|
|
41
|
-
import fsDriver from 'unstorage/drivers/fs';
|
|
42
|
-
|
|
43
|
-
// src/hooks/types.ts
|
|
44
|
-
async function runHooks(hooks, args) {
|
|
45
|
-
let result = args.data;
|
|
46
|
-
for (const hook of hooks) {
|
|
47
|
-
const hookResult = await hook({
|
|
48
|
-
...args,
|
|
49
|
-
data: result
|
|
50
|
-
});
|
|
51
|
-
if (hookResult !== void 0) {
|
|
52
|
-
result = hookResult;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return result;
|
|
56
|
-
}
|
|
57
|
-
async function runFieldHooks(hooks, args) {
|
|
58
|
-
return runHooks(hooks, args);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// src/plugins/index.ts
|
|
62
|
-
var KyroPlugin = class {
|
|
63
|
-
name;
|
|
64
|
-
version;
|
|
65
|
-
description;
|
|
66
|
-
hooks = {};
|
|
67
|
-
collections = [];
|
|
68
|
-
globals = [];
|
|
69
|
-
fields = [];
|
|
70
|
-
extensions = { collections: [], globals: [] };
|
|
71
|
-
adminComponents = {};
|
|
72
|
-
adminStyles = [];
|
|
73
|
-
serverMiddleware;
|
|
74
|
-
clientMiddleware;
|
|
75
|
-
constructor(name) {
|
|
76
|
-
this.name = name;
|
|
77
|
-
}
|
|
78
|
-
async init(api) {
|
|
79
|
-
}
|
|
80
|
-
async beforeInit(api) {
|
|
81
|
-
}
|
|
82
|
-
async afterInit(api) {
|
|
83
|
-
}
|
|
84
|
-
getCollections() {
|
|
85
|
-
return this.collections;
|
|
86
|
-
}
|
|
87
|
-
getGlobals() {
|
|
88
|
-
return this.globals;
|
|
89
|
-
}
|
|
90
|
-
getHooks() {
|
|
91
|
-
return this.hooks;
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
var PluginManager = class {
|
|
95
|
-
plugins = /* @__PURE__ */ new Map();
|
|
96
|
-
hooks = /* @__PURE__ */ new Map();
|
|
97
|
-
register(plugin) {
|
|
98
|
-
if (this.plugins.has(plugin.name)) {
|
|
99
|
-
throw new Error(`Plugin "${plugin.name}" is already registered`);
|
|
100
|
-
}
|
|
101
|
-
this.plugins.set(plugin.name, plugin);
|
|
102
|
-
const pluginHooks = plugin.getHooks?.() || {};
|
|
103
|
-
for (const [event, handlers] of Object.entries(pluginHooks)) {
|
|
104
|
-
if (Array.isArray(handlers)) {
|
|
105
|
-
for (const handler of handlers) {
|
|
106
|
-
this.registerHook(event, handler);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
unregister(name) {
|
|
112
|
-
const plugin = this.plugins.get(name);
|
|
113
|
-
if (!plugin) return;
|
|
114
|
-
const pluginHooks = plugin.getHooks?.() || {};
|
|
115
|
-
for (const [event, handlers] of Object.entries(pluginHooks)) {
|
|
116
|
-
if (Array.isArray(handlers)) {
|
|
117
|
-
for (const handler of handlers) {
|
|
118
|
-
this.unregisterHook(event, handler);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
this.plugins.delete(name);
|
|
123
|
-
}
|
|
124
|
-
get(name) {
|
|
125
|
-
return this.plugins.get(name);
|
|
126
|
-
}
|
|
127
|
-
getAll() {
|
|
128
|
-
return Array.from(this.plugins.values());
|
|
129
|
-
}
|
|
130
|
-
has(name) {
|
|
131
|
-
return this.plugins.has(name);
|
|
132
|
-
}
|
|
133
|
-
registerHook(event, handler) {
|
|
134
|
-
if (!this.hooks.has(event)) {
|
|
135
|
-
this.hooks.set(event, []);
|
|
136
|
-
}
|
|
137
|
-
this.hooks.get(event).push(handler);
|
|
138
|
-
}
|
|
139
|
-
unregisterHook(event, handler) {
|
|
140
|
-
const handlers = this.hooks.get(event);
|
|
141
|
-
if (handlers) {
|
|
142
|
-
const index = handlers.indexOf(handler);
|
|
143
|
-
if (index > -1) {
|
|
144
|
-
handlers.splice(index, 1);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
async executeHook(event, args) {
|
|
149
|
-
const handlers = this.hooks.get(event) || [];
|
|
150
|
-
let result = args;
|
|
151
|
-
for (const handler of handlers) {
|
|
152
|
-
try {
|
|
153
|
-
const hookResult = await handler({ ...args, data: result });
|
|
154
|
-
if (hookResult !== void 0) {
|
|
155
|
-
result = hookResult;
|
|
156
|
-
}
|
|
157
|
-
} catch (error) {
|
|
158
|
-
console.error(`[PluginManager] Error in hook "${event}":`, error);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
return result;
|
|
162
|
-
}
|
|
163
|
-
// ========================================================================
|
|
164
|
-
// Collection/Field Extensions
|
|
165
|
-
// ========================================================================
|
|
166
|
-
getAllCollections() {
|
|
167
|
-
const collections = [];
|
|
168
|
-
for (const plugin of this.plugins.values()) {
|
|
169
|
-
const pluginCollections = plugin.getCollections?.() || [];
|
|
170
|
-
collections.push(...pluginCollections);
|
|
171
|
-
}
|
|
172
|
-
return collections;
|
|
173
|
-
}
|
|
174
|
-
getAllGlobals() {
|
|
175
|
-
const globals = [];
|
|
176
|
-
for (const plugin of this.plugins.values()) {
|
|
177
|
-
const pluginGlobals = plugin.getGlobals?.() || [];
|
|
178
|
-
globals.push(...pluginGlobals);
|
|
179
|
-
}
|
|
180
|
-
return globals;
|
|
181
|
-
}
|
|
182
|
-
getAllFields() {
|
|
183
|
-
const fields = [];
|
|
184
|
-
for (const plugin of this.plugins.values()) {
|
|
185
|
-
fields.push(...plugin.fields);
|
|
186
|
-
}
|
|
187
|
-
return fields;
|
|
188
|
-
}
|
|
189
|
-
getAdminComponents() {
|
|
190
|
-
const components = {};
|
|
191
|
-
for (const plugin of this.plugins.values()) {
|
|
192
|
-
Object.assign(components, plugin.adminComponents);
|
|
193
|
-
}
|
|
194
|
-
return components;
|
|
195
|
-
}
|
|
196
|
-
getAdminStyles() {
|
|
197
|
-
const styles = [];
|
|
198
|
-
for (const plugin of this.plugins.values()) {
|
|
199
|
-
styles.push(...plugin.adminStyles);
|
|
200
|
-
}
|
|
201
|
-
return styles;
|
|
202
|
-
}
|
|
203
|
-
};
|
|
204
|
-
var SEOPLugin = class extends KyroPlugin {
|
|
205
|
-
constructor() {
|
|
206
|
-
super("seo");
|
|
207
|
-
this.description = "Advanced SEO features including sitemaps, robots.txt, and structured data";
|
|
208
|
-
this.collections.push({
|
|
209
|
-
slug: "seo-settings",
|
|
210
|
-
label: "SEO Settings",
|
|
211
|
-
fields: [
|
|
212
|
-
{
|
|
213
|
-
name: "sitemap",
|
|
214
|
-
type: "checkbox",
|
|
215
|
-
label: "Enable Sitemap",
|
|
216
|
-
defaultValue: true
|
|
217
|
-
},
|
|
218
|
-
{ name: "robotsTxt", type: "richtext", label: "robots.txt Content" },
|
|
219
|
-
{
|
|
220
|
-
name: "canonicalUrl",
|
|
221
|
-
type: "text",
|
|
222
|
-
variant: "url",
|
|
223
|
-
label: "Canonical URL"
|
|
224
|
-
},
|
|
225
|
-
{ name: "ogImage", type: "text", label: "Default OG Image URL" }
|
|
226
|
-
]
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
|
-
var AnalyticsPlugin = class extends KyroPlugin {
|
|
231
|
-
constructor() {
|
|
232
|
-
super("analytics");
|
|
233
|
-
this.description = "Analytics integration for tracking page views and events";
|
|
234
|
-
this.collections.push({
|
|
235
|
-
slug: "analytics-events",
|
|
236
|
-
label: "Analytics Events",
|
|
237
|
-
fields: [
|
|
238
|
-
{ name: "name", type: "text", required: true },
|
|
239
|
-
{ name: "properties", type: "json", label: "Event Properties" },
|
|
240
|
-
{ name: "timestamp", type: "date", required: true },
|
|
241
|
-
{ name: "userId", type: "text", label: "User ID" },
|
|
242
|
-
{ name: "sessionId", type: "text", label: "Session ID" }
|
|
243
|
-
]
|
|
244
|
-
});
|
|
245
|
-
this.adminComponents["AnalyticsDashboard"] = {};
|
|
246
|
-
}
|
|
247
|
-
};
|
|
248
|
-
var CommentsPlugin = class extends KyroPlugin {
|
|
249
|
-
constructor() {
|
|
250
|
-
super("comments");
|
|
251
|
-
this.description = "Commenting system for products and posts";
|
|
252
|
-
this.collections.push({
|
|
253
|
-
slug: "comments",
|
|
254
|
-
label: "Comments",
|
|
255
|
-
fields: [
|
|
256
|
-
{ name: "content", type: "richtext", required: true },
|
|
257
|
-
{ name: "author", type: "text", required: true },
|
|
258
|
-
{ name: "email", type: "email" },
|
|
259
|
-
{ name: "approved", type: "checkbox", defaultValue: false },
|
|
260
|
-
{ name: "parent", type: "text", label: "Parent Comment ID" },
|
|
261
|
-
{ name: "resourceType", type: "text", required: true },
|
|
262
|
-
{ name: "resourceId", type: "text", required: true }
|
|
263
|
-
]
|
|
264
|
-
});
|
|
265
|
-
this.adminComponents["CommentModeration"] = {};
|
|
266
|
-
}
|
|
267
|
-
};
|
|
268
|
-
var ReviewsPlugin = class extends KyroPlugin {
|
|
269
|
-
constructor() {
|
|
270
|
-
super("reviews");
|
|
271
|
-
this.description = "Product reviews and ratings";
|
|
272
|
-
this.collections.push({
|
|
273
|
-
slug: "reviews",
|
|
274
|
-
label: "Reviews",
|
|
275
|
-
fields: [
|
|
276
|
-
{ name: "rating", type: "number", required: true, min: 1, max: 5 },
|
|
277
|
-
{ name: "title", type: "text" },
|
|
278
|
-
{ name: "content", type: "richtext", required: true },
|
|
279
|
-
{ name: "author", type: "relationship", relationTo: "customers" },
|
|
280
|
-
{
|
|
281
|
-
name: "product",
|
|
282
|
-
type: "relationship",
|
|
283
|
-
relationTo: "products",
|
|
284
|
-
required: true
|
|
285
|
-
},
|
|
286
|
-
{ name: "approved", type: "checkbox", defaultValue: false },
|
|
287
|
-
{ name: "verified", type: "checkbox", label: "Verified Purchase" },
|
|
288
|
-
{
|
|
289
|
-
name: "helpful",
|
|
290
|
-
type: "number",
|
|
291
|
-
label: "Helpful Count",
|
|
292
|
-
defaultValue: 0
|
|
293
|
-
}
|
|
294
|
-
]
|
|
295
|
-
});
|
|
296
|
-
this.adminComponents["ReviewModeration"] = {};
|
|
297
|
-
}
|
|
298
|
-
};
|
|
299
|
-
var WishlistPlugin = class extends KyroPlugin {
|
|
300
|
-
constructor() {
|
|
301
|
-
super("wishlist");
|
|
302
|
-
this.description = "Customer wishlists";
|
|
303
|
-
this.collections.push({
|
|
304
|
-
slug: "wishlists",
|
|
305
|
-
label: "Wishlists",
|
|
306
|
-
fields: [
|
|
307
|
-
{
|
|
308
|
-
name: "customer",
|
|
309
|
-
type: "relationship",
|
|
310
|
-
relationTo: "customers",
|
|
311
|
-
required: true
|
|
312
|
-
},
|
|
313
|
-
{
|
|
314
|
-
name: "name",
|
|
315
|
-
type: "text",
|
|
316
|
-
label: "Wishlist Name",
|
|
317
|
-
defaultValue: "My Wishlist"
|
|
318
|
-
},
|
|
319
|
-
{
|
|
320
|
-
name: "items",
|
|
321
|
-
type: "blocks",
|
|
322
|
-
label: "Items",
|
|
323
|
-
blocks: [
|
|
324
|
-
{
|
|
325
|
-
slug: "wishlist-item",
|
|
326
|
-
label: "Item",
|
|
327
|
-
fields: [
|
|
328
|
-
{
|
|
329
|
-
name: "product",
|
|
330
|
-
type: "relationship",
|
|
331
|
-
relationTo: "products"
|
|
332
|
-
},
|
|
333
|
-
{ name: "quantity", type: "number", defaultValue: 1 },
|
|
334
|
-
{ name: "addedAt", type: "date" },
|
|
335
|
-
{
|
|
336
|
-
name: "priority",
|
|
337
|
-
type: "select",
|
|
338
|
-
options: [
|
|
339
|
-
{ label: "Low", value: "low" },
|
|
340
|
-
{ label: "Medium", value: "medium" },
|
|
341
|
-
{ label: "High", value: "high" }
|
|
342
|
-
]
|
|
343
|
-
}
|
|
344
|
-
]
|
|
345
|
-
}
|
|
346
|
-
]
|
|
347
|
-
}
|
|
348
|
-
]
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
};
|
|
352
|
-
var presetPlugins = {
|
|
353
|
-
SEO: SEOPLugin,
|
|
354
|
-
Analytics: AnalyticsPlugin,
|
|
355
|
-
Comments: CommentsPlugin,
|
|
356
|
-
Reviews: ReviewsPlugin,
|
|
357
|
-
Wishlist: WishlistPlugin
|
|
358
|
-
};
|
|
359
|
-
|
|
360
|
-
// src/auth/security/lockout.ts
|
|
361
|
-
var DEFAULT_LOCKOUT_CONFIG = {
|
|
362
|
-
maxAttempts: 5,
|
|
363
|
-
lockDuration: 9e5,
|
|
364
|
-
notifyUser: true,
|
|
365
|
-
notifyAdmin: true,
|
|
366
|
-
adminNotifyAfter: 3
|
|
367
|
-
};
|
|
368
|
-
var AccountLockout = class {
|
|
369
|
-
redis;
|
|
370
|
-
prefix;
|
|
371
|
-
config;
|
|
372
|
-
constructor(redis, config = {}, prefix = "kyro:lockout:") {
|
|
373
|
-
this.redis = redis;
|
|
374
|
-
this.prefix = prefix;
|
|
375
|
-
this.config = { ...DEFAULT_LOCKOUT_CONFIG, ...config };
|
|
376
|
-
}
|
|
377
|
-
lockKey(userId) {
|
|
378
|
-
return `${this.prefix}${userId}`;
|
|
379
|
-
}
|
|
380
|
-
historyKey(userId) {
|
|
381
|
-
return `${this.prefix}${userId}:history`;
|
|
382
|
-
}
|
|
383
|
-
async checkLockout(userId) {
|
|
384
|
-
const key = this.lockKey(userId);
|
|
385
|
-
const data = await this.redis.hgetall(key);
|
|
386
|
-
if (!data || Object.keys(data).length === 0) {
|
|
387
|
-
return {
|
|
388
|
-
locked: false,
|
|
389
|
-
attemptsRemaining: this.config.maxAttempts,
|
|
390
|
-
totalAttempts: 0
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
const attempts = parseInt(data.attempts, 10);
|
|
394
|
-
const lockedUntil = data.lockedUntil ? new Date(parseInt(data.lockedUntil, 10)) : void 0;
|
|
395
|
-
if (lockedUntil && lockedUntil > /* @__PURE__ */ new Date()) {
|
|
396
|
-
return {
|
|
397
|
-
locked: true,
|
|
398
|
-
attemptsRemaining: 0,
|
|
399
|
-
lockedUntil,
|
|
400
|
-
totalAttempts: attempts
|
|
401
|
-
};
|
|
402
|
-
}
|
|
403
|
-
if (lockedUntil && lockedUntil <= /* @__PURE__ */ new Date()) {
|
|
404
|
-
await this.unlockAccount(userId);
|
|
405
|
-
return {
|
|
406
|
-
locked: false,
|
|
407
|
-
attemptsRemaining: this.config.maxAttempts,
|
|
408
|
-
totalAttempts: 0
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
return {
|
|
412
|
-
locked: false,
|
|
413
|
-
attemptsRemaining: Math.max(0, this.config.maxAttempts - attempts),
|
|
414
|
-
totalAttempts: attempts
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
async recordFailedAttempt(userId) {
|
|
418
|
-
const key = this.lockKey(userId);
|
|
419
|
-
const historyKey = this.historyKey(userId);
|
|
420
|
-
const now = Date.now();
|
|
421
|
-
const current = await this.redis.hincrby(key, "attempts", 1);
|
|
422
|
-
await this.redis.hset(key, "lastAttempt", now.toString());
|
|
423
|
-
await this.redis.lpush(historyKey, now.toString());
|
|
424
|
-
await this.redis.ltrim(historyKey, 0, 99);
|
|
425
|
-
if (current >= this.config.maxAttempts) {
|
|
426
|
-
const lockedUntil = new Date(now + this.config.lockDuration);
|
|
427
|
-
await this.redis.hset(key, {
|
|
428
|
-
lockedAt: now.toString(),
|
|
429
|
-
lockedUntil: lockedUntil.getTime().toString()
|
|
430
|
-
});
|
|
431
|
-
await this.redis.expire(
|
|
432
|
-
key,
|
|
433
|
-
Math.ceil(this.config.lockDuration / 1e3) + 3600
|
|
434
|
-
);
|
|
435
|
-
return {
|
|
436
|
-
locked: true,
|
|
437
|
-
attemptsRemaining: 0,
|
|
438
|
-
lockedUntil,
|
|
439
|
-
totalAttempts: current
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
return {
|
|
443
|
-
locked: false,
|
|
444
|
-
attemptsRemaining: Math.max(0, this.config.maxAttempts - current),
|
|
445
|
-
totalAttempts: current
|
|
446
|
-
};
|
|
447
|
-
}
|
|
448
|
-
async lockAccount(userId, duration) {
|
|
449
|
-
const key = this.lockKey(userId);
|
|
450
|
-
const now = Date.now();
|
|
451
|
-
const lockDuration = duration || this.config.lockDuration;
|
|
452
|
-
const lockedUntil = new Date(now + lockDuration);
|
|
453
|
-
const pipeline = this.redis.pipeline();
|
|
454
|
-
pipeline.hset(key, {
|
|
455
|
-
attempts: this.config.maxAttempts.toString(),
|
|
456
|
-
lockedAt: now.toString(),
|
|
457
|
-
lockedUntil: lockedUntil.getTime().toString()
|
|
458
|
-
});
|
|
459
|
-
pipeline.expire(key, Math.ceil(lockDuration / 1e3) + 3600);
|
|
460
|
-
await pipeline.exec();
|
|
461
|
-
}
|
|
462
|
-
async unlockAccount(userId) {
|
|
463
|
-
const key = this.lockKey(userId);
|
|
464
|
-
await this.redis.del(key);
|
|
465
|
-
}
|
|
466
|
-
async resetAttempts(userId) {
|
|
467
|
-
const key = this.lockKey(userId);
|
|
468
|
-
const data = await this.redis.hgetall(key);
|
|
469
|
-
if (data.lockedAt) {
|
|
470
|
-
await this.redis.hset(key, {
|
|
471
|
-
attempts: "0",
|
|
472
|
-
lockedAt: "",
|
|
473
|
-
lockedUntil: ""
|
|
474
|
-
});
|
|
475
|
-
} else {
|
|
476
|
-
await this.redis.del(key);
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
async getLockoutHistory(userId, limit = 10) {
|
|
480
|
-
const historyKey = this.historyKey(userId);
|
|
481
|
-
const timestamps = await this.redis.lrange(historyKey, 0, limit - 1);
|
|
482
|
-
return timestamps.map((ts) => new Date(parseInt(ts, 10)));
|
|
483
|
-
}
|
|
484
|
-
async getLockoutStats(userId) {
|
|
485
|
-
const historyKey = this.historyKey(userId);
|
|
486
|
-
const timestamps = await this.redis.lrange(historyKey, 0, -1);
|
|
487
|
-
const lockouts = timestamps.filter((_, i) => {
|
|
488
|
-
const attemptNum = i + 1;
|
|
489
|
-
return attemptNum % this.config.maxAttempts === 0;
|
|
490
|
-
}).length;
|
|
491
|
-
const lastLockoutData = await this.redis.hget(
|
|
492
|
-
this.lockKey(userId),
|
|
493
|
-
"lockedAt"
|
|
494
|
-
);
|
|
495
|
-
return {
|
|
496
|
-
totalFailedAttempts: timestamps.length,
|
|
497
|
-
lockoutCount: lockouts,
|
|
498
|
-
lastLockout: lastLockoutData ? new Date(parseInt(lastLockoutData, 10)) : null,
|
|
499
|
-
averageAttemptsBeforeLockout: lockouts > 0 ? this.config.maxAttempts : 0
|
|
500
|
-
};
|
|
501
|
-
}
|
|
502
|
-
shouldNotifyAdmin(currentAttempts) {
|
|
503
|
-
return this.config.notifyAdmin && currentAttempts >= this.config.adminNotifyAfter;
|
|
504
|
-
}
|
|
505
|
-
getConfig() {
|
|
506
|
-
return { ...this.config };
|
|
507
|
-
}
|
|
508
|
-
setConfig(config) {
|
|
509
|
-
this.config = { ...this.config, ...config };
|
|
510
|
-
}
|
|
511
|
-
};
|
|
512
|
-
|
|
513
|
-
// src/auth/security/rate-limit.ts
|
|
514
|
-
var DEFAULT_RATE_LIMITS = {
|
|
515
|
-
"auth:login": { window: 9e5, max: 5 },
|
|
516
|
-
"auth:register": { window: 36e5, max: 3 },
|
|
517
|
-
"auth:forgot": { window: 36e5, max: 3 },
|
|
518
|
-
"auth:reset": { window: 36e5, max: 5 },
|
|
519
|
-
"auth:verify": { window: 36e5, max: 5 },
|
|
520
|
-
"api:general": { window: 6e4, max: 100 },
|
|
521
|
-
"api:authenticated": { window: 6e4, max: 200 }
|
|
522
|
-
};
|
|
523
|
-
var RateLimiter = class {
|
|
524
|
-
redis;
|
|
525
|
-
prefix;
|
|
526
|
-
limits;
|
|
527
|
-
userLimits;
|
|
528
|
-
constructor(redis, limits, userLimits, prefix = "kyro:ratelimit:") {
|
|
529
|
-
this.redis = redis;
|
|
530
|
-
this.prefix = prefix;
|
|
531
|
-
this.limits = { ...DEFAULT_RATE_LIMITS, ...limits };
|
|
532
|
-
this.userLimits = userLimits || {
|
|
533
|
-
"user:api": { window: 6e4, max: 500 },
|
|
534
|
-
"user:write": { window: 36e5, max: 100 }
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
getKey(type, identifier) {
|
|
538
|
-
return `${this.prefix}${type}:${identifier}`;
|
|
539
|
-
}
|
|
540
|
-
async check(type, identifier) {
|
|
541
|
-
const config = this.limits[type] || this.limits["api:general"];
|
|
542
|
-
const key = this.getKey(type, identifier);
|
|
543
|
-
const now = Date.now();
|
|
544
|
-
const windowStart = now - config.window;
|
|
545
|
-
const pipeline = this.redis.pipeline();
|
|
546
|
-
pipeline.zremrangebyscore(key, 0, windowStart);
|
|
547
|
-
pipeline.zcard(key);
|
|
548
|
-
pipeline.zadd(key, now, `${now}:${Math.random()}`);
|
|
549
|
-
pipeline.expire(key, Math.ceil(config.window / 1e3) + 1);
|
|
550
|
-
const results = await pipeline.exec();
|
|
551
|
-
const count = results?.[1]?.[1] || 0;
|
|
552
|
-
if (count >= config.max) {
|
|
553
|
-
const oldestTimestamp = await this.redis.zrange(key, 0, 0, "WITHSCORES");
|
|
554
|
-
const resetAt = oldestTimestamp.length > 1 ? parseInt(oldestTimestamp[1], 10) + config.window : now + config.window;
|
|
555
|
-
return {
|
|
556
|
-
allowed: false,
|
|
557
|
-
remaining: 0,
|
|
558
|
-
resetAt,
|
|
559
|
-
retryAfter: Math.ceil((resetAt - now) / 1e3)
|
|
560
|
-
};
|
|
561
|
-
}
|
|
562
|
-
return {
|
|
563
|
-
allowed: true,
|
|
564
|
-
remaining: config.max - count - 1,
|
|
565
|
-
resetAt: now + config.window
|
|
566
|
-
};
|
|
567
|
-
}
|
|
568
|
-
async checkUser(type, userId, identifier) {
|
|
569
|
-
const config = this.userLimits[type] || this.userLimits["user:api"];
|
|
570
|
-
const key = this.getKey(`user:${type}:${userId}`, identifier);
|
|
571
|
-
const now = Date.now();
|
|
572
|
-
const windowStart = now - config.window;
|
|
573
|
-
const pipeline = this.redis.pipeline();
|
|
574
|
-
pipeline.zremrangebyscore(key, 0, windowStart);
|
|
575
|
-
pipeline.zcard(key);
|
|
576
|
-
pipeline.zadd(key, now, `${now}:${Math.random()}`);
|
|
577
|
-
pipeline.expire(key, Math.ceil(config.window / 1e3) + 1);
|
|
578
|
-
const results = await pipeline.exec();
|
|
579
|
-
const count = results?.[1]?.[1] || 0;
|
|
580
|
-
if (count >= config.max) {
|
|
581
|
-
const oldestTimestamp = await this.redis.zrange(key, 0, 0, "WITHSCORES");
|
|
582
|
-
const resetAt = oldestTimestamp.length > 1 ? parseInt(oldestTimestamp[1], 10) + config.window : now + config.window;
|
|
583
|
-
return {
|
|
584
|
-
allowed: false,
|
|
585
|
-
remaining: 0,
|
|
586
|
-
resetAt,
|
|
587
|
-
retryAfter: Math.ceil((resetAt - now) / 1e3)
|
|
588
|
-
};
|
|
589
|
-
}
|
|
590
|
-
return {
|
|
591
|
-
allowed: true,
|
|
592
|
-
remaining: config.max - count - 1,
|
|
593
|
-
resetAt: now + config.window
|
|
594
|
-
};
|
|
595
|
-
}
|
|
596
|
-
async reset(type, identifier) {
|
|
597
|
-
const key = this.getKey(type, identifier);
|
|
598
|
-
await this.redis.del(key);
|
|
599
|
-
}
|
|
600
|
-
async resetUser(type, userId, identifier) {
|
|
601
|
-
const key = this.getKey(`user:${type}:${userId}`, identifier);
|
|
602
|
-
await this.redis.del(key);
|
|
603
|
-
}
|
|
604
|
-
async getStatus(type, identifier) {
|
|
605
|
-
const config = this.limits[type] || this.limits["api:general"];
|
|
606
|
-
const key = this.getKey(type, identifier);
|
|
607
|
-
const now = Date.now();
|
|
608
|
-
const windowStart = now - config.window;
|
|
609
|
-
await this.redis.zremrangebyscore(key, 0, windowStart);
|
|
610
|
-
const count = await this.redis.zcard(key);
|
|
611
|
-
return {
|
|
612
|
-
count,
|
|
613
|
-
limit: config.max,
|
|
614
|
-
remaining: Math.max(0, config.max - count),
|
|
615
|
-
resetAt: now + config.window
|
|
616
|
-
};
|
|
617
|
-
}
|
|
618
|
-
setLimit(type, config) {
|
|
619
|
-
this.limits[type] = config;
|
|
620
|
-
}
|
|
621
|
-
setUserLimit(type, config) {
|
|
622
|
-
this.userLimits[type] = config;
|
|
623
|
-
}
|
|
624
|
-
};
|
|
625
|
-
var InMemoryAuthAdapter = class {
|
|
626
|
-
users = /* @__PURE__ */ new Map();
|
|
627
|
-
sessions = /* @__PURE__ */ new Map();
|
|
628
|
-
refreshTokens = /* @__PURE__ */ new Map();
|
|
629
|
-
emailToUserId = /* @__PURE__ */ new Map();
|
|
630
|
-
passwordHistory = /* @__PURE__ */ new Map();
|
|
631
|
-
emailVerificationTokens = /* @__PURE__ */ new Map();
|
|
632
|
-
passwordResetTokens = /* @__PURE__ */ new Map();
|
|
633
|
-
auditLogs = [];
|
|
634
|
-
externalDb = false;
|
|
635
|
-
constructor() {
|
|
636
|
-
}
|
|
637
|
-
async connect() {
|
|
638
|
-
}
|
|
639
|
-
async disconnect() {
|
|
640
|
-
this.users.clear();
|
|
641
|
-
this.sessions.clear();
|
|
642
|
-
this.refreshTokens.clear();
|
|
643
|
-
this.emailToUserId.clear();
|
|
644
|
-
this.passwordHistory.clear();
|
|
645
|
-
}
|
|
646
|
-
async createUser(data) {
|
|
647
|
-
const userId = randomBytes(16).toString("hex");
|
|
648
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
649
|
-
const passwordHash = await this.hashPassword(data.password);
|
|
650
|
-
const user = {
|
|
651
|
-
id: userId,
|
|
652
|
-
email: data.email.toLowerCase(),
|
|
653
|
-
passwordHash,
|
|
654
|
-
role: data.role || "customer",
|
|
655
|
-
tenantId: data.tenantId,
|
|
656
|
-
createdAt: now,
|
|
657
|
-
updatedAt: now
|
|
658
|
-
};
|
|
659
|
-
this.users.set(userId, user);
|
|
660
|
-
this.emailToUserId.set(data.email.toLowerCase(), userId);
|
|
661
|
-
this.passwordHistory.set(userId, []);
|
|
662
|
-
return user;
|
|
663
|
-
}
|
|
664
|
-
async findUserByEmail(email) {
|
|
665
|
-
const userId = this.emailToUserId.get(email.toLowerCase());
|
|
666
|
-
if (!userId) return null;
|
|
667
|
-
return this.findUserById(userId);
|
|
668
|
-
}
|
|
669
|
-
async findUserById(userId) {
|
|
670
|
-
return this.users.get(userId) || null;
|
|
671
|
-
}
|
|
672
|
-
async updateUser(userId, data) {
|
|
673
|
-
const existing = await this.findUserById(userId);
|
|
674
|
-
if (!existing) return null;
|
|
675
|
-
const updated = {
|
|
676
|
-
...existing,
|
|
677
|
-
...data,
|
|
678
|
-
id: userId,
|
|
679
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
680
|
-
};
|
|
681
|
-
if (data.email && data.email !== existing.email) {
|
|
682
|
-
this.emailToUserId.delete(existing.email.toLowerCase());
|
|
683
|
-
this.emailToUserId.set(data.email.toLowerCase(), userId);
|
|
684
|
-
}
|
|
685
|
-
this.users.set(userId, updated);
|
|
686
|
-
return updated;
|
|
687
|
-
}
|
|
688
|
-
async deleteUser(userId) {
|
|
689
|
-
const user = await this.findUserById(userId);
|
|
690
|
-
if (!user) return false;
|
|
691
|
-
this.users.delete(userId);
|
|
692
|
-
this.emailToUserId.delete(user.email.toLowerCase());
|
|
693
|
-
this.refreshTokens.forEach((sessionId, refreshToken) => {
|
|
694
|
-
if (this.sessions.get(sessionId)?.userId === userId) {
|
|
695
|
-
this.refreshTokens.delete(refreshToken);
|
|
696
|
-
this.sessions.delete(sessionId);
|
|
697
|
-
}
|
|
698
|
-
});
|
|
699
|
-
this.passwordHistory.delete(userId);
|
|
700
|
-
this.sessions.forEach((session, sessionId) => {
|
|
701
|
-
if (session.userId === userId) {
|
|
702
|
-
this.sessions.delete(sessionId);
|
|
703
|
-
}
|
|
704
|
-
});
|
|
705
|
-
return true;
|
|
706
|
-
}
|
|
707
|
-
async hashPassword(password) {
|
|
708
|
-
const bcrypt = (await import('bcryptjs')).default;
|
|
709
|
-
return bcrypt.hash(password, 12);
|
|
710
|
-
}
|
|
711
|
-
async verifyPassword(email, password) {
|
|
712
|
-
const user = await this.findUserByEmail(email);
|
|
713
|
-
if (!user || !user.passwordHash) return null;
|
|
714
|
-
const bcrypt = (await import('bcryptjs')).default;
|
|
715
|
-
const valid = await bcrypt.compare(password, user.passwordHash);
|
|
716
|
-
return valid ? user : null;
|
|
717
|
-
}
|
|
718
|
-
async createSession(userId, data = {}) {
|
|
719
|
-
const sessionId = randomBytes(32).toString("hex");
|
|
720
|
-
const token = randomBytes(32).toString("base64url");
|
|
721
|
-
const refreshToken = randomBytes(32).toString("base64url");
|
|
722
|
-
const now = /* @__PURE__ */ new Date();
|
|
723
|
-
const session = {
|
|
724
|
-
id: sessionId,
|
|
725
|
-
userId,
|
|
726
|
-
token,
|
|
727
|
-
refreshToken,
|
|
728
|
-
expiresAt: new Date(now.getTime() + 86400 * 1e3).toISOString(),
|
|
729
|
-
// 24 hours
|
|
730
|
-
createdAt: now.toISOString(),
|
|
731
|
-
ipAddress: data.ipAddress,
|
|
732
|
-
userAgent: data.userAgent
|
|
733
|
-
};
|
|
734
|
-
this.sessions.set(sessionId, session);
|
|
735
|
-
this.refreshTokens.set(refreshToken, sessionId);
|
|
736
|
-
return session;
|
|
737
|
-
}
|
|
738
|
-
async findSessionByToken(token) {
|
|
739
|
-
return this.sessions.get(token) || null;
|
|
740
|
-
}
|
|
741
|
-
async findSessionByRefreshToken(refreshToken) {
|
|
742
|
-
const sessionId = this.refreshTokens.get(refreshToken);
|
|
743
|
-
if (!sessionId) return null;
|
|
744
|
-
const session = this.sessions.get(sessionId);
|
|
745
|
-
return session || null;
|
|
746
|
-
}
|
|
747
|
-
async deleteSession(sessionId) {
|
|
748
|
-
const session = this.sessions.get(sessionId);
|
|
749
|
-
if (!session) return false;
|
|
750
|
-
if (session.refreshToken) {
|
|
751
|
-
this.refreshTokens.delete(session.refreshToken);
|
|
752
|
-
}
|
|
753
|
-
this.sessions.delete(sessionId);
|
|
754
|
-
return true;
|
|
755
|
-
}
|
|
756
|
-
async deleteUserSessions(userId) {
|
|
757
|
-
let deleted = 0;
|
|
758
|
-
this.sessions.forEach((session, sessionId) => {
|
|
759
|
-
if (session.userId === userId) {
|
|
760
|
-
if (session.refreshToken) {
|
|
761
|
-
this.refreshTokens.delete(session.refreshToken);
|
|
762
|
-
}
|
|
763
|
-
this.sessions.delete(sessionId);
|
|
764
|
-
deleted++;
|
|
765
|
-
}
|
|
766
|
-
});
|
|
767
|
-
return deleted;
|
|
768
|
-
}
|
|
769
|
-
async addPasswordToHistory(userId, passwordHash) {
|
|
770
|
-
const history = this.passwordHistory.get(userId) || [];
|
|
771
|
-
history.push(passwordHash);
|
|
772
|
-
if (history.length > 5) {
|
|
773
|
-
history.splice(0, history.length - 5);
|
|
774
|
-
}
|
|
775
|
-
this.passwordHistory.set(userId, history);
|
|
776
|
-
}
|
|
777
|
-
async getPasswordHistory(userId, count = 5) {
|
|
778
|
-
return this.passwordHistory.get(userId) || [];
|
|
779
|
-
}
|
|
780
|
-
async isPasswordInHistory(password, userId, historyCount = 5) {
|
|
781
|
-
const history = await this.getPasswordHistory(userId, historyCount);
|
|
782
|
-
const bcrypt = (await import('bcryptjs')).default;
|
|
783
|
-
for (const hash of history) {
|
|
784
|
-
if (await bcrypt.compare(password, hash)) {
|
|
785
|
-
return true;
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
return false;
|
|
789
|
-
}
|
|
790
|
-
async createEmailVerificationToken(userId) {
|
|
791
|
-
const token = randomBytes(32).toString("hex");
|
|
792
|
-
const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1e3);
|
|
793
|
-
this.emailVerificationTokens.set(token, { userId, expiresAt });
|
|
794
|
-
return { token, expiresAt };
|
|
795
|
-
}
|
|
796
|
-
async verifyEmailToken(token) {
|
|
797
|
-
const data = this.emailVerificationTokens.get(token);
|
|
798
|
-
if (!data || data.expiresAt < /* @__PURE__ */ new Date()) {
|
|
799
|
-
this.emailVerificationTokens.delete(token);
|
|
800
|
-
return { success: false, error: "Invalid or expired token" };
|
|
801
|
-
}
|
|
802
|
-
this.emailVerificationTokens.delete(token);
|
|
803
|
-
return { success: true, userId: data.userId };
|
|
804
|
-
}
|
|
805
|
-
async createPasswordResetToken(email) {
|
|
806
|
-
const user = await this.findUserByEmail(email);
|
|
807
|
-
if (!user) {
|
|
808
|
-
return { token: "", expiresAt: /* @__PURE__ */ new Date(), error: "User not found" };
|
|
809
|
-
}
|
|
810
|
-
const token = randomBytes(32).toString("hex");
|
|
811
|
-
const expiresAt = new Date(Date.now() + 60 * 60 * 1e3);
|
|
812
|
-
this.passwordResetTokens.set(token, { userId: user.id, expiresAt });
|
|
813
|
-
return { token, expiresAt };
|
|
814
|
-
}
|
|
815
|
-
async resetPasswordWithToken(token, newPassword) {
|
|
816
|
-
const data = this.passwordResetTokens.get(token);
|
|
817
|
-
if (!data || data.expiresAt < /* @__PURE__ */ new Date()) {
|
|
818
|
-
this.passwordResetTokens.delete(token);
|
|
819
|
-
return { success: false, error: "Invalid or expired token" };
|
|
820
|
-
}
|
|
821
|
-
const passwordHash = await this.hashPassword(newPassword);
|
|
822
|
-
await this.updateUser(data.userId, { passwordHash });
|
|
823
|
-
this.passwordResetTokens.delete(token);
|
|
824
|
-
return { success: true };
|
|
825
|
-
}
|
|
826
|
-
async hasAnyUsers() {
|
|
827
|
-
return this.users.size > 0;
|
|
828
|
-
}
|
|
829
|
-
async findAuditLogs(filter) {
|
|
830
|
-
const { limit = 50, offset = 0 } = filter;
|
|
831
|
-
let logs = this.auditLogs.slice().reverse();
|
|
832
|
-
if (filter.userId) logs = logs.filter((l) => l.userId === filter.userId);
|
|
833
|
-
if (filter.action) {
|
|
834
|
-
if (Array.isArray(filter.action)) {
|
|
835
|
-
logs = logs.filter((l) => filter.action.includes(String(l.action)));
|
|
836
|
-
} else {
|
|
837
|
-
logs = logs.filter((l) => l.action === filter.action);
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
if (filter.resource)
|
|
841
|
-
logs = logs.filter((l) => l.resource === filter.resource);
|
|
842
|
-
if (filter.success !== void 0)
|
|
843
|
-
logs = logs.filter((l) => l.success === filter.success);
|
|
844
|
-
return { logs: logs.slice(offset, offset + limit), total: logs.length };
|
|
845
|
-
}
|
|
846
|
-
async createAuditLog(data) {
|
|
847
|
-
const id = randomBytes(16).toString("hex");
|
|
848
|
-
const timestamp = /* @__PURE__ */ new Date();
|
|
849
|
-
const log = { ...data, id, timestamp };
|
|
850
|
-
this.auditLogs.push(log);
|
|
851
|
-
return log;
|
|
852
|
-
}
|
|
853
|
-
};
|
|
854
|
-
|
|
855
|
-
// src/auth/security/in-memory-lockout.ts
|
|
856
|
-
var InMemoryAccountLockout = class {
|
|
857
|
-
storage = /* @__PURE__ */ new Map();
|
|
858
|
-
history = /* @__PURE__ */ new Map();
|
|
859
|
-
// userId -> attempt timestamps
|
|
860
|
-
config;
|
|
861
|
-
constructor(config = {}) {
|
|
862
|
-
this.config = {
|
|
863
|
-
maxAttempts: 5,
|
|
864
|
-
lockDuration: 9e5,
|
|
865
|
-
// 15 minutes
|
|
866
|
-
notifyUser: true,
|
|
867
|
-
notifyAdmin: true,
|
|
868
|
-
adminNotifyAfter: 3,
|
|
869
|
-
...config
|
|
870
|
-
};
|
|
871
|
-
}
|
|
872
|
-
async checkLockout(userId) {
|
|
873
|
-
const now = Date.now();
|
|
874
|
-
const record = this.storage.get(userId);
|
|
875
|
-
if (record && record.lockedUntil !== null && record.lockedUntil <= now) {
|
|
876
|
-
await this.resetAttempts(userId);
|
|
877
|
-
return {
|
|
878
|
-
locked: false,
|
|
879
|
-
attemptsRemaining: this.config.maxAttempts,
|
|
880
|
-
totalAttempts: 0
|
|
881
|
-
};
|
|
882
|
-
}
|
|
883
|
-
if (!record) {
|
|
884
|
-
return {
|
|
885
|
-
locked: false,
|
|
886
|
-
attemptsRemaining: this.config.maxAttempts,
|
|
887
|
-
totalAttempts: 0
|
|
888
|
-
};
|
|
889
|
-
}
|
|
890
|
-
const { attempts, lockedUntil } = record;
|
|
891
|
-
if (lockedUntil !== null && lockedUntil > now) {
|
|
892
|
-
return {
|
|
893
|
-
locked: true,
|
|
894
|
-
attemptsRemaining: 0,
|
|
895
|
-
lockedUntil: new Date(lockedUntil),
|
|
896
|
-
totalAttempts: attempts
|
|
897
|
-
};
|
|
898
|
-
}
|
|
899
|
-
return {
|
|
900
|
-
locked: false,
|
|
901
|
-
attemptsRemaining: Math.max(0, this.config.maxAttempts - attempts),
|
|
902
|
-
totalAttempts: attempts
|
|
903
|
-
};
|
|
904
|
-
}
|
|
905
|
-
async recordFailedAttempt(userId) {
|
|
906
|
-
const now = Date.now();
|
|
907
|
-
const record = this.storage.get(userId) || {
|
|
908
|
-
attempts: 0,
|
|
909
|
-
lastAttempt: null,
|
|
910
|
-
lockedAt: null,
|
|
911
|
-
lockedUntil: null
|
|
912
|
-
};
|
|
913
|
-
record.attempts += 1;
|
|
914
|
-
record.lastAttempt = now;
|
|
915
|
-
let history = this.history.get(userId) || [];
|
|
916
|
-
history.push(now);
|
|
917
|
-
if (history.length > 100) {
|
|
918
|
-
history.splice(0, history.length - 100);
|
|
919
|
-
}
|
|
920
|
-
this.history.set(userId, history);
|
|
921
|
-
this.storage.set(userId, record);
|
|
922
|
-
if (record.attempts >= this.config.maxAttempts) {
|
|
923
|
-
const lockedUntil = new Date(now + this.config.lockDuration);
|
|
924
|
-
record.lockedAt = now;
|
|
925
|
-
record.lockedUntil = lockedUntil.getTime();
|
|
926
|
-
this.storage.set(userId, record);
|
|
927
|
-
return {
|
|
928
|
-
locked: true,
|
|
929
|
-
attemptsRemaining: 0,
|
|
930
|
-
lockedUntil,
|
|
931
|
-
totalAttempts: record.attempts
|
|
932
|
-
};
|
|
933
|
-
}
|
|
934
|
-
return {
|
|
935
|
-
locked: false,
|
|
936
|
-
attemptsRemaining: Math.max(0, this.config.maxAttempts - record.attempts),
|
|
937
|
-
totalAttempts: record.attempts
|
|
938
|
-
};
|
|
939
|
-
}
|
|
940
|
-
async lockAccount(userId, duration) {
|
|
941
|
-
const now = Date.now();
|
|
942
|
-
const lockDuration = duration || this.config.lockDuration;
|
|
943
|
-
const lockedUntil = new Date(now + lockDuration);
|
|
944
|
-
const record = this.storage.get(userId) || {
|
|
945
|
-
attempts: 0,
|
|
946
|
-
lastAttempt: null,
|
|
947
|
-
lockedAt: null,
|
|
948
|
-
lockedUntil: null
|
|
949
|
-
};
|
|
950
|
-
record.attempts = this.config.maxAttempts;
|
|
951
|
-
record.lockedAt = now;
|
|
952
|
-
record.lockedUntil = lockedUntil.getTime();
|
|
953
|
-
this.storage.set(userId, record);
|
|
954
|
-
}
|
|
955
|
-
async unlockAccount(userId) {
|
|
956
|
-
await this.resetAttempts(userId);
|
|
957
|
-
}
|
|
958
|
-
async resetAttempts(userId) {
|
|
959
|
-
const record = this.storage.get(userId);
|
|
960
|
-
if (record) {
|
|
961
|
-
record.attempts = 0;
|
|
962
|
-
record.lockedAt = null;
|
|
963
|
-
record.lockedUntil = null;
|
|
964
|
-
this.storage.set(userId, record);
|
|
965
|
-
}
|
|
966
|
-
this.history.delete(userId);
|
|
967
|
-
}
|
|
968
|
-
async getLockoutHistory(userId, limit = 10) {
|
|
969
|
-
const history = this.history.get(userId) || [];
|
|
970
|
-
return history.slice(-limit).reverse().map((timestamp) => new Date(timestamp));
|
|
971
|
-
}
|
|
972
|
-
async getLockoutStats(userId) {
|
|
973
|
-
const history = this.history.get(userId) || [];
|
|
974
|
-
const totalFailedAttempts = history.length;
|
|
975
|
-
const lockoutCount = Math.floor(
|
|
976
|
-
totalFailedAttempts / this.config.maxAttempts
|
|
977
|
-
);
|
|
978
|
-
let lastLockout = null;
|
|
979
|
-
const record = this.storage.get(userId);
|
|
980
|
-
if (record && record.lockedAt !== null) {
|
|
981
|
-
lastLockout = new Date(record.lockedAt);
|
|
982
|
-
}
|
|
983
|
-
const averageAttemptsBeforeLockout = lockoutCount > 0 ? this.config.maxAttempts : 0;
|
|
984
|
-
return {
|
|
985
|
-
totalFailedAttempts,
|
|
986
|
-
lockoutCount,
|
|
987
|
-
lastLockout,
|
|
988
|
-
averageAttemptsBeforeLockout
|
|
989
|
-
};
|
|
990
|
-
}
|
|
991
|
-
shouldNotifyAdmin(currentAttempts) {
|
|
992
|
-
return this.config.notifyAdmin && currentAttempts >= this.config.adminNotifyAfter;
|
|
993
|
-
}
|
|
994
|
-
getConfig() {
|
|
995
|
-
return { ...this.config };
|
|
996
|
-
}
|
|
997
|
-
setConfig(config) {
|
|
998
|
-
this.config = { ...this.config, ...config };
|
|
999
|
-
}
|
|
1000
|
-
};
|
|
1001
|
-
function getEnv(key, fallback = "") {
|
|
1002
|
-
return process.env[key] || fallback;
|
|
1003
|
-
}
|
|
1004
|
-
function getEnvBool(key, fallback = false) {
|
|
1005
|
-
const val = process.env[key];
|
|
1006
|
-
if (!val) return fallback;
|
|
1007
|
-
return val.toLowerCase() === "true";
|
|
1008
|
-
}
|
|
1009
|
-
function getEnvNum(key, fallback = 0) {
|
|
1010
|
-
const val = process.env[key];
|
|
1011
|
-
if (!val) return fallback;
|
|
1012
|
-
return parseInt(val, 10);
|
|
1013
|
-
}
|
|
1014
|
-
function detectDatabaseType() {
|
|
1015
|
-
const envDb = process.env.KYRO_AUTH_DATABASE?.toLowerCase();
|
|
1016
|
-
if (envDb && ["sqlite", "postgres", "mongodb", "memory"].includes(envDb)) {
|
|
1017
|
-
return envDb;
|
|
1018
|
-
}
|
|
1019
|
-
try {
|
|
1020
|
-
const configPath = join(process.cwd(), "kyro.config.ts");
|
|
1021
|
-
const configContent = readFileSync(configPath, "utf8");
|
|
1022
|
-
if (configContent.includes("createLocalAdapter")) {
|
|
1023
|
-
return "sqlite";
|
|
1024
|
-
} else if (configContent.includes("createDrizzleAdapter")) {
|
|
1025
|
-
if (configContent.includes("postgres") || configContent.includes("postgresql")) {
|
|
1026
|
-
return "postgres";
|
|
1027
|
-
}
|
|
1028
|
-
return "postgres";
|
|
1029
|
-
} else if (configContent.includes("createMongoDBAdapter")) {
|
|
1030
|
-
return "mongodb";
|
|
1031
|
-
}
|
|
1032
|
-
} catch {
|
|
1033
|
-
}
|
|
1034
|
-
return "memory";
|
|
1035
|
-
}
|
|
1036
|
-
async function createAuthAdapter(databaseType) {
|
|
1037
|
-
const cwd = process.cwd();
|
|
1038
|
-
const rootDir = cwd.endsWith("admin") ? join(cwd, "..") : cwd;
|
|
1039
|
-
const defaultAuthDbPath = resolve(rootDir, "data", "auth.db");
|
|
1040
|
-
switch (databaseType) {
|
|
1041
|
-
case "sqlite":
|
|
1042
|
-
return new SQLiteAuthAdapter({
|
|
1043
|
-
path: getEnv("KYRO_AUTH_DB_PATH", defaultAuthDbPath)
|
|
1044
|
-
});
|
|
1045
|
-
case "postgres": {
|
|
1046
|
-
try {
|
|
1047
|
-
const databaseUrl = getEnv("DATABASE_URL", "");
|
|
1048
|
-
if (databaseUrl) {
|
|
1049
|
-
const { drizzle } = await import('drizzle-orm/postgres-js');
|
|
1050
|
-
const postgres = await import('postgres');
|
|
1051
|
-
const sql = postgres.default(databaseUrl, { onnotice: () => {
|
|
1052
|
-
} });
|
|
1053
|
-
const drizzleDb = drizzle(sql);
|
|
1054
|
-
return new PostgresAuthAdapter({ db: drizzleDb });
|
|
1055
|
-
}
|
|
1056
|
-
} catch (e) {
|
|
1057
|
-
console.warn("[Auth] Could not create PostgresAuthAdapter, falling back to SQLite auth adapter.");
|
|
1058
|
-
}
|
|
1059
|
-
return new SQLiteAuthAdapter({
|
|
1060
|
-
path: getEnv("KYRO_AUTH_DB_PATH", defaultAuthDbPath)
|
|
1061
|
-
});
|
|
1062
|
-
}
|
|
1063
|
-
case "mongodb": {
|
|
1064
|
-
try {
|
|
1065
|
-
const mongoUri = getEnv("MONGODB_URI", "");
|
|
1066
|
-
if (mongoUri) {
|
|
1067
|
-
const { MongoClient } = await import('mongodb');
|
|
1068
|
-
const client = new MongoClient(mongoUri);
|
|
1069
|
-
await client.connect();
|
|
1070
|
-
const url = new URL(mongoUri);
|
|
1071
|
-
const dbName = url.pathname.replace(/^\//, "") || "kyro_cms";
|
|
1072
|
-
const mongoDb = client.db(dbName);
|
|
1073
|
-
return new MongoDBAuthAdapter({ db: mongoDb });
|
|
1074
|
-
}
|
|
1075
|
-
} catch (e) {
|
|
1076
|
-
console.warn("[Auth] Could not create MongoDBAuthAdapter, falling back to SQLite auth adapter.");
|
|
1077
|
-
}
|
|
1078
|
-
return new SQLiteAuthAdapter({
|
|
1079
|
-
path: getEnv("KYRO_AUTH_DB_PATH", defaultAuthDbPath)
|
|
1080
|
-
});
|
|
1081
|
-
}
|
|
1082
|
-
case "memory":
|
|
1083
|
-
default:
|
|
1084
|
-
return new InMemoryAuthAdapter();
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
async function createAuthConfig(databaseType, db) {
|
|
1088
|
-
const distributed = getEnvBool("KYRO_DISTRIBUTED", false);
|
|
1089
|
-
let authAdapter;
|
|
1090
|
-
if (distributed) {
|
|
1091
|
-
const { RedisAuthAdapter: RedisAuthAdapter2 } = await import('./redis-adapter-XALOGWY3.js');
|
|
1092
|
-
const redisUrl = getEnv("REDIS_URL", "redis://localhost:6379");
|
|
1093
|
-
const redisTls = getEnvBool("REDIS_TLS", false);
|
|
1094
|
-
const redisAdapter = new RedisAuthAdapter2({ url: redisUrl, tls: redisTls });
|
|
1095
|
-
await redisAdapter.connect?.();
|
|
1096
|
-
authAdapter = redisAdapter;
|
|
1097
|
-
} else {
|
|
1098
|
-
const initialDbType = databaseType || detectDatabaseType();
|
|
1099
|
-
authAdapter = await createAuthAdapter(initialDbType);
|
|
1100
|
-
if (authAdapter.connect) {
|
|
1101
|
-
await authAdapter.connect();
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
const email = db ? await EmailTransport.fromConfig(db).catch(() => null) || EmailTransport.fromEnv() || void 0 : EmailTransport.fromEnv() || void 0;
|
|
1105
|
-
const passwordPolicy = new PasswordPolicy({
|
|
1106
|
-
minLength: getEnvNum("PASSWORD_MIN_LENGTH", 12),
|
|
1107
|
-
requireUppercase: getEnvBool("PASSWORD_REQUIRE_UPPERCASE", true),
|
|
1108
|
-
requireLowercase: getEnvBool("PASSWORD_REQUIRE_LOWERCASE", true),
|
|
1109
|
-
requireNumbers: getEnvBool("PASSWORD_REQUIRE_NUMBERS", true),
|
|
1110
|
-
requireSpecialChars: getEnvBool("PASSWORD_REQUIRE_SPECIAL", true),
|
|
1111
|
-
preventReuse: getEnvNum("PASSWORD_PREVENT_REUSE", 5),
|
|
1112
|
-
maxLength: getEnvNum("PASSWORD_MAX_LENGTH", 128)
|
|
1113
|
-
});
|
|
1114
|
-
let lockout;
|
|
1115
|
-
let rateLimiter;
|
|
1116
|
-
let auditLogger;
|
|
1117
|
-
if (distributed) {
|
|
1118
|
-
const redis = authAdapter;
|
|
1119
|
-
const redisClient = redis.redis;
|
|
1120
|
-
lockout = new AccountLockout(redisClient, {
|
|
1121
|
-
maxAttempts: getEnvNum("LOCKOUT_MAX_ATTEMPTS", 5),
|
|
1122
|
-
lockDuration: getEnvNum("LOCKOUT_DURATION_MINUTES", 15) * 60 * 1e3
|
|
1123
|
-
});
|
|
1124
|
-
rateLimiter = new RateLimiter(redisClient, {
|
|
1125
|
-
"auth:login": {
|
|
1126
|
-
window: getEnvNum("RATE_LIMIT_AUTH_WINDOW_MS", 9e5),
|
|
1127
|
-
max: getEnvNum("RATE_LIMIT_AUTH_MAX_REQUESTS", 10)
|
|
1128
|
-
},
|
|
1129
|
-
"api:general": {
|
|
1130
|
-
window: getEnvNum("RATE_LIMIT_WINDOW_MS", 6e4),
|
|
1131
|
-
max: getEnvNum("RATE_LIMIT_MAX_REQUESTS", 100)
|
|
1132
|
-
}
|
|
1133
|
-
});
|
|
1134
|
-
auditLogger = new AuditLogger(
|
|
1135
|
-
redisClient,
|
|
1136
|
-
getEnvNum("AUDIT_LOG_RETENTION_DAYS", 30)
|
|
1137
|
-
);
|
|
1138
|
-
} else {
|
|
1139
|
-
lockout = new InMemoryAccountLockout({
|
|
1140
|
-
maxAttempts: getEnvNum("LOCKOUT_MAX_ATTEMPTS", 5),
|
|
1141
|
-
lockDuration: getEnvNum("LOCKOUT_DURATION_MINUTES", 15) * 60 * 1e3
|
|
1142
|
-
});
|
|
1143
|
-
rateLimiter = new InMemoryRateLimiter({
|
|
1144
|
-
"auth:login": {
|
|
1145
|
-
window: getEnvNum("RATE_LIMIT_AUTH_WINDOW_MS", 9e5),
|
|
1146
|
-
max: getEnvNum("RATE_LIMIT_AUTH_MAX_REQUESTS", 10)
|
|
1147
|
-
},
|
|
1148
|
-
"api:general": {
|
|
1149
|
-
window: getEnvNum("RATE_LIMIT_WINDOW_MS", 6e4),
|
|
1150
|
-
max: getEnvNum("RATE_LIMIT_MAX_REQUESTS", 100)
|
|
1151
|
-
}
|
|
1152
|
-
});
|
|
1153
|
-
auditLogger = getEnvBool("AUDIT_LOG_ENABLED", true) ? new InMemoryAuditLogger(getEnvNum("AUDIT_LOG_RETENTION_DAYS", 30)) : void 0;
|
|
1154
|
-
}
|
|
1155
|
-
const routes = new AuthRoutes({
|
|
1156
|
-
redis: authAdapter,
|
|
1157
|
-
email,
|
|
1158
|
-
jwtSecret: getEnv("APP_SECRET", "change-me"),
|
|
1159
|
-
jwtExpiresIn: getEnv("JWT_EXPIRES_IN", "24h"),
|
|
1160
|
-
jwtIssuer: getEnv("JWT_ISSUER", "kyro-cms"),
|
|
1161
|
-
jwtAudience: getEnv("JWT_AUDIENCE", "kyro-cms-client"),
|
|
1162
|
-
passwordPolicy,
|
|
1163
|
-
lockout,
|
|
1164
|
-
rateLimiter,
|
|
1165
|
-
auditLogger,
|
|
1166
|
-
baseUrl: getEnv("EMAIL_BASE_URL", "http://localhost:4321"),
|
|
1167
|
-
emailVerificationRequired: getEnvBool("EMAIL_VERIFICATION_REQUIRED", true)
|
|
1168
|
-
});
|
|
1169
|
-
const actualDbType = distributed ? "distributed" : databaseType || detectDatabaseType();
|
|
1170
|
-
return {
|
|
1171
|
-
authAdapter,
|
|
1172
|
-
databaseType: actualDbType,
|
|
1173
|
-
email,
|
|
1174
|
-
passwordPolicy,
|
|
1175
|
-
lockout,
|
|
1176
|
-
rateLimiter,
|
|
1177
|
-
auditLogger,
|
|
1178
|
-
routes
|
|
1179
|
-
};
|
|
1180
|
-
}
|
|
1181
|
-
var authConfig = createAuthConfig().catch((err) => {
|
|
1182
|
-
console.warn("[AuthConfig] Failed to initialize auth config:", err.message);
|
|
1183
|
-
return null;
|
|
1184
|
-
});
|
|
1185
|
-
|
|
1186
|
-
// src/auth/index.ts
|
|
1187
|
-
var DEFAULT_SALT_ROUNDS = 12;
|
|
1188
|
-
var DEFAULT_EXPIRES_IN = "24h";
|
|
1189
|
-
var DEFAULT_REFRESH_EXPIRES_IN = "7d";
|
|
1190
|
-
var Auth = class {
|
|
1191
|
-
adapter;
|
|
1192
|
-
config;
|
|
1193
|
-
constructor(adapter, config) {
|
|
1194
|
-
this.adapter = adapter;
|
|
1195
|
-
this.config = {
|
|
1196
|
-
secret: config.secret,
|
|
1197
|
-
expiresIn: config.expiresIn ?? DEFAULT_EXPIRES_IN,
|
|
1198
|
-
refreshExpiresIn: config.refreshExpiresIn ?? DEFAULT_REFRESH_EXPIRES_IN,
|
|
1199
|
-
issuer: config.issuer ?? "kyro-cms",
|
|
1200
|
-
audience: config.audience ?? [],
|
|
1201
|
-
saltRounds: config.saltRounds ?? DEFAULT_SALT_ROUNDS
|
|
1202
|
-
};
|
|
1203
|
-
}
|
|
1204
|
-
async register(data) {
|
|
1205
|
-
try {
|
|
1206
|
-
const existing = await this.adapter.findUserByEmail(data.email);
|
|
1207
|
-
if (existing) {
|
|
1208
|
-
return { success: false, error: "Email already registered" };
|
|
1209
|
-
}
|
|
1210
|
-
const user = await this.adapter.createUser({
|
|
1211
|
-
email: data.email,
|
|
1212
|
-
password: data.password,
|
|
1213
|
-
role: data.role ?? "customer",
|
|
1214
|
-
tenantId: data.tenantId
|
|
1215
|
-
});
|
|
1216
|
-
return this.createSessionForUser(user);
|
|
1217
|
-
} catch (error) {
|
|
1218
|
-
return { success: false, error: String(error) };
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
async login(credentials) {
|
|
1222
|
-
try {
|
|
1223
|
-
const user = await this.adapter.verifyPassword(
|
|
1224
|
-
credentials.email,
|
|
1225
|
-
credentials.password
|
|
1226
|
-
);
|
|
1227
|
-
if (!user) {
|
|
1228
|
-
return { success: false, error: "Invalid credentials" };
|
|
1229
|
-
}
|
|
1230
|
-
return this.createSessionForUser(user);
|
|
1231
|
-
} catch (error) {
|
|
1232
|
-
return { success: false, error: String(error) };
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1235
|
-
async logout(token) {
|
|
1236
|
-
await this.adapter.deleteSession(token);
|
|
1237
|
-
}
|
|
1238
|
-
async refreshToken(refreshToken) {
|
|
1239
|
-
try {
|
|
1240
|
-
const session = await this.adapter.findSessionByToken(refreshToken);
|
|
1241
|
-
if (!session || new Date(session.expiresAt) < /* @__PURE__ */ new Date()) {
|
|
1242
|
-
return { success: false, error: "Invalid or expired refresh token" };
|
|
1243
|
-
}
|
|
1244
|
-
const user = await this.adapter.findUserById(session.userId);
|
|
1245
|
-
if (!user) {
|
|
1246
|
-
return { success: false, error: "User not found" };
|
|
1247
|
-
}
|
|
1248
|
-
await this.adapter.deleteSession(refreshToken);
|
|
1249
|
-
return this.createSessionForUser(user);
|
|
1250
|
-
} catch (error) {
|
|
1251
|
-
return { success: false, error: String(error) };
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
async verifyToken(token) {
|
|
1255
|
-
try {
|
|
1256
|
-
const { default: jwtModule } = await import('jsonwebtoken');
|
|
1257
|
-
const decoded = jwtModule.verify(token, this.config.secret, {
|
|
1258
|
-
issuer: this.config.issuer,
|
|
1259
|
-
audience: this.config.audience.length > 0 ? this.config.audience[0] : void 0
|
|
1260
|
-
});
|
|
1261
|
-
return decoded;
|
|
1262
|
-
} catch {
|
|
1263
|
-
return null;
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
async getUserFromToken(token) {
|
|
1267
|
-
const payload = await this.verifyToken(token);
|
|
1268
|
-
if (!payload) return null;
|
|
1269
|
-
return this.adapter.findUserById(payload.sub);
|
|
1270
|
-
}
|
|
1271
|
-
async changePassword(userId, currentPassword, newPassword) {
|
|
1272
|
-
try {
|
|
1273
|
-
const user = await this.adapter.findUserById(userId);
|
|
1274
|
-
if (!user) {
|
|
1275
|
-
return { success: false, error: "User not found" };
|
|
1276
|
-
}
|
|
1277
|
-
const valid = await this.adapter.verifyPassword(
|
|
1278
|
-
user.email,
|
|
1279
|
-
currentPassword
|
|
1280
|
-
);
|
|
1281
|
-
if (!valid) {
|
|
1282
|
-
return { success: false, error: "Current password is incorrect" };
|
|
1283
|
-
}
|
|
1284
|
-
await this.adapter.updateUser(userId, { password: newPassword });
|
|
1285
|
-
await this.adapter.deleteUserSessions(userId);
|
|
1286
|
-
return { success: true, user };
|
|
1287
|
-
} catch (error) {
|
|
1288
|
-
return { success: false, error: String(error) };
|
|
1289
|
-
}
|
|
1290
|
-
}
|
|
1291
|
-
async resetPassword(email, newPassword) {
|
|
1292
|
-
try {
|
|
1293
|
-
const user = await this.adapter.findUserByEmail(email);
|
|
1294
|
-
if (!user) {
|
|
1295
|
-
return { success: false, error: "User not found" };
|
|
1296
|
-
}
|
|
1297
|
-
await this.adapter.updateUser(user.id, { password: newPassword });
|
|
1298
|
-
await this.adapter.deleteUserSessions(user.id);
|
|
1299
|
-
return { success: true, user };
|
|
1300
|
-
} catch (error) {
|
|
1301
|
-
return { success: false, error: String(error) };
|
|
1302
|
-
}
|
|
1303
|
-
}
|
|
1304
|
-
async sendEmailVerification(userId) {
|
|
1305
|
-
try {
|
|
1306
|
-
const { token, expiresAt } = await this.adapter.createEmailVerificationToken(userId);
|
|
1307
|
-
return { success: true };
|
|
1308
|
-
} catch (error) {
|
|
1309
|
-
return { success: false, error: String(error) };
|
|
1310
|
-
}
|
|
1311
|
-
}
|
|
1312
|
-
async verifyEmail(token) {
|
|
1313
|
-
try {
|
|
1314
|
-
return await this.adapter.verifyEmailToken(token);
|
|
1315
|
-
} catch (error) {
|
|
1316
|
-
return { success: false, error: String(error) };
|
|
1317
|
-
}
|
|
1318
|
-
}
|
|
1319
|
-
async requestPasswordReset(email) {
|
|
1320
|
-
try {
|
|
1321
|
-
const result = await this.adapter.createPasswordResetToken(email);
|
|
1322
|
-
if (result.error) {
|
|
1323
|
-
return { success: false, error: result.error };
|
|
1324
|
-
}
|
|
1325
|
-
return { success: true };
|
|
1326
|
-
} catch (error) {
|
|
1327
|
-
return { success: false, error: String(error) };
|
|
1328
|
-
}
|
|
1329
|
-
}
|
|
1330
|
-
async resetPasswordWithToken(token, newPassword) {
|
|
1331
|
-
try {
|
|
1332
|
-
return await this.adapter.resetPasswordWithToken(token, newPassword);
|
|
1333
|
-
} catch (error) {
|
|
1334
|
-
return { success: false, error: String(error) };
|
|
1335
|
-
}
|
|
1336
|
-
}
|
|
1337
|
-
async deleteAccount(userId) {
|
|
1338
|
-
try {
|
|
1339
|
-
const user = await this.adapter.findUserById(userId);
|
|
1340
|
-
if (!user) {
|
|
1341
|
-
return { success: false, error: "User not found" };
|
|
1342
|
-
}
|
|
1343
|
-
await this.adapter.deleteUserSessions(userId);
|
|
1344
|
-
await this.adapter.deleteUser(userId);
|
|
1345
|
-
return { success: true };
|
|
1346
|
-
} catch (error) {
|
|
1347
|
-
return { success: false, error: String(error) };
|
|
1348
|
-
}
|
|
1349
|
-
}
|
|
1350
|
-
async createSessionForUser(user) {
|
|
1351
|
-
const token = await this.generateToken(user);
|
|
1352
|
-
const session = await this.adapter.createSession(user.id);
|
|
1353
|
-
return {
|
|
1354
|
-
success: true,
|
|
1355
|
-
user,
|
|
1356
|
-
session,
|
|
1357
|
-
token
|
|
1358
|
-
};
|
|
1359
|
-
}
|
|
1360
|
-
async generateToken(user) {
|
|
1361
|
-
const { default: jwtModule } = await import('jsonwebtoken');
|
|
1362
|
-
const payload = {
|
|
1363
|
-
sub: user.id,
|
|
1364
|
-
email: user.email,
|
|
1365
|
-
role: user.role,
|
|
1366
|
-
tenantId: user.tenantId
|
|
1367
|
-
};
|
|
1368
|
-
const signOptions = {
|
|
1369
|
-
expiresIn: this.parseExpiresIn(this.config.expiresIn) / 1e3,
|
|
1370
|
-
issuer: this.config.issuer
|
|
1371
|
-
};
|
|
1372
|
-
if (this.config.audience.length > 0) {
|
|
1373
|
-
signOptions.audience = this.config.audience[0];
|
|
1374
|
-
}
|
|
1375
|
-
return jwtModule.sign(payload, this.config.secret, signOptions);
|
|
1376
|
-
}
|
|
1377
|
-
async hashPassword(password) {
|
|
1378
|
-
const { default: bcrypt } = await import('bcryptjs');
|
|
1379
|
-
return bcrypt.hash(password, this.config.saltRounds);
|
|
1380
|
-
}
|
|
1381
|
-
parseExpiresIn(value) {
|
|
1382
|
-
if (typeof value === "number") return value;
|
|
1383
|
-
const match = value.match(/^(\d+)([smhd])$/);
|
|
1384
|
-
if (!match) return 864e5;
|
|
1385
|
-
const num = parseInt(match[1], 10);
|
|
1386
|
-
switch (match[2]) {
|
|
1387
|
-
case "s":
|
|
1388
|
-
return num * 1e3;
|
|
1389
|
-
case "m":
|
|
1390
|
-
return num * 6e4;
|
|
1391
|
-
case "h":
|
|
1392
|
-
return num * 36e5;
|
|
1393
|
-
case "d":
|
|
1394
|
-
return num * 864e5;
|
|
1395
|
-
default:
|
|
1396
|
-
return 864e5;
|
|
1397
|
-
}
|
|
1398
|
-
}
|
|
1399
|
-
};
|
|
1400
|
-
function createAuth(adapter, config) {
|
|
1401
|
-
return new Auth(adapter, config);
|
|
1402
|
-
}
|
|
1403
|
-
|
|
1404
|
-
// src/versions/types.ts
|
|
1405
|
-
function getDefaultDraftPublishConfig() {
|
|
1406
|
-
return {
|
|
1407
|
-
enabled: true,
|
|
1408
|
-
draftsEnabled: true,
|
|
1409
|
-
publishEnabled: true,
|
|
1410
|
-
scheduleEnabled: false,
|
|
1411
|
-
versioningEnabled: true,
|
|
1412
|
-
maxVersionsPerDocument: 50,
|
|
1413
|
-
autoPublish: false,
|
|
1414
|
-
requirePublishPermission: true
|
|
1415
|
-
};
|
|
1416
|
-
}
|
|
1417
|
-
|
|
1418
|
-
// src/versions/index.ts
|
|
1419
|
-
var VersionManager = class {
|
|
1420
|
-
adapter;
|
|
1421
|
-
config;
|
|
1422
|
-
constructor(adapter, config) {
|
|
1423
|
-
this.adapter = adapter;
|
|
1424
|
-
this.config = { ...getDefaultDraftPublishConfig(), ...config };
|
|
1425
|
-
}
|
|
1426
|
-
async createVersion(options) {
|
|
1427
|
-
const latestVersion = await this.adapter.getLatestVersion(options.collection, options.documentId);
|
|
1428
|
-
const nextVersion = (latestVersion?.version ?? 0) + 1;
|
|
1429
|
-
const versionOptions = {
|
|
1430
|
-
...options,
|
|
1431
|
-
version: nextVersion
|
|
1432
|
-
};
|
|
1433
|
-
const version = await this.adapter.createVersion(versionOptions);
|
|
1434
|
-
if (this.config.maxVersionsPerDocument > 0) {
|
|
1435
|
-
await this.pruneOldVersions(options.collection, options.documentId);
|
|
1436
|
-
}
|
|
1437
|
-
return version;
|
|
1438
|
-
}
|
|
1439
|
-
async publishVersion(options) {
|
|
1440
|
-
const version = await this.adapter.getVersion(options.collection, options.versionId);
|
|
1441
|
-
if (!version) {
|
|
1442
|
-
throw new Error("Version not found");
|
|
1443
|
-
}
|
|
1444
|
-
if (version.status === "published") {
|
|
1445
|
-
throw new Error("Version is already published");
|
|
1446
|
-
}
|
|
1447
|
-
await this.adapter.publishVersion(options);
|
|
1448
|
-
}
|
|
1449
|
-
async unpublishDocument(collection, documentId) {
|
|
1450
|
-
const versions = await this.adapter.getVersions({
|
|
1451
|
-
collection,
|
|
1452
|
-
documentId,
|
|
1453
|
-
limit: 1e3
|
|
1454
|
-
});
|
|
1455
|
-
for (const version of versions) {
|
|
1456
|
-
if (version.status === "published") {
|
|
1457
|
-
await this.createVersion({
|
|
1458
|
-
collection,
|
|
1459
|
-
documentId,
|
|
1460
|
-
data: version.data,
|
|
1461
|
-
status: "draft",
|
|
1462
|
-
createdBy: "system",
|
|
1463
|
-
changeDescription: "Unpublished document"
|
|
1464
|
-
});
|
|
1465
|
-
break;
|
|
1466
|
-
}
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
async revertToVersion(collection, documentId, versionId, userId) {
|
|
1470
|
-
const targetVersion = await this.adapter.getVersion(collection, versionId);
|
|
1471
|
-
if (!targetVersion) {
|
|
1472
|
-
throw new Error("Version not found");
|
|
1473
|
-
}
|
|
1474
|
-
const newVersion = await this.adapter.revertToVersion({
|
|
1475
|
-
collection,
|
|
1476
|
-
documentId,
|
|
1477
|
-
versionId,
|
|
1478
|
-
userId
|
|
1479
|
-
});
|
|
1480
|
-
return newVersion;
|
|
1481
|
-
}
|
|
1482
|
-
async getVersionHistory(collection, documentId, limit = 20, offset = 0) {
|
|
1483
|
-
return this.adapter.getVersions({
|
|
1484
|
-
collection,
|
|
1485
|
-
documentId,
|
|
1486
|
-
limit,
|
|
1487
|
-
offset
|
|
1488
|
-
});
|
|
1489
|
-
}
|
|
1490
|
-
async compareTwoVersions(collection, documentId, versionA, versionB) {
|
|
1491
|
-
return this.adapter.compareVersions({
|
|
1492
|
-
collection,
|
|
1493
|
-
documentId,
|
|
1494
|
-
versionA,
|
|
1495
|
-
versionB
|
|
1496
|
-
});
|
|
1497
|
-
}
|
|
1498
|
-
async getLatestDraft(collection, documentId) {
|
|
1499
|
-
return this.adapter.getLatestVersion(collection, documentId);
|
|
1500
|
-
}
|
|
1501
|
-
async getPublishedVersion(collection, documentId) {
|
|
1502
|
-
return this.adapter.getPublishedVersion(collection, documentId);
|
|
1503
|
-
}
|
|
1504
|
-
async getVersion(collection, versionId) {
|
|
1505
|
-
return this.adapter.getVersion(collection, versionId);
|
|
1506
|
-
}
|
|
1507
|
-
async schedulePublish(collection, documentId, versionId, scheduledFor) {
|
|
1508
|
-
if (!this.config.scheduleEnabled) {
|
|
1509
|
-
throw new Error("Scheduled publishing is not enabled");
|
|
1510
|
-
}
|
|
1511
|
-
const version = await this.adapter.getVersion(collection, versionId);
|
|
1512
|
-
if (!version) {
|
|
1513
|
-
throw new Error("Version not found");
|
|
1514
|
-
}
|
|
1515
|
-
}
|
|
1516
|
-
async deleteVersionHistory(collection, documentId) {
|
|
1517
|
-
await this.adapter.deleteVersions(collection, documentId);
|
|
1518
|
-
}
|
|
1519
|
-
async pruneOldVersions(collection, documentId) {
|
|
1520
|
-
const versions = await this.adapter.getVersions({
|
|
1521
|
-
collection,
|
|
1522
|
-
documentId,
|
|
1523
|
-
limit: this.config.maxVersionsPerDocument + 100
|
|
1524
|
-
});
|
|
1525
|
-
if (versions.length <= this.config.maxVersionsPerDocument) {
|
|
1526
|
-
return;
|
|
1527
|
-
}
|
|
1528
|
-
versions.slice(0, this.config.maxVersionsPerDocument);
|
|
1529
|
-
const versionsToDelete = versions.slice(this.config.maxVersionsPerDocument);
|
|
1530
|
-
for (const version of versionsToDelete) {
|
|
1531
|
-
if (version.status !== "published") {
|
|
1532
|
-
await this.adapter.deleteVersions(collection, documentId);
|
|
1533
|
-
break;
|
|
1534
|
-
}
|
|
1535
|
-
}
|
|
1536
|
-
}
|
|
1537
|
-
};
|
|
1538
|
-
function createVersionManager(adapter, config) {
|
|
1539
|
-
return new VersionManager(adapter, config);
|
|
1540
|
-
}
|
|
1541
|
-
function isPublished(status) {
|
|
1542
|
-
return status === "published";
|
|
1543
|
-
}
|
|
1544
|
-
function isDraft(status) {
|
|
1545
|
-
return status === "draft";
|
|
1546
|
-
}
|
|
1547
|
-
function isArchived(status) {
|
|
1548
|
-
return status === "archived";
|
|
1549
|
-
}
|
|
1550
|
-
|
|
1551
|
-
// src/registry/config.ts
|
|
1552
|
-
function normalizeCollections(collections) {
|
|
1553
|
-
if (!collections) return [];
|
|
1554
|
-
if (Array.isArray(collections)) return collections;
|
|
1555
|
-
return Object.values(collections);
|
|
1556
|
-
}
|
|
1557
|
-
function normalizeGlobals(globals) {
|
|
1558
|
-
if (!globals) return [];
|
|
1559
|
-
if (Array.isArray(globals)) return globals;
|
|
1560
|
-
return Object.values(globals);
|
|
1561
|
-
}
|
|
1562
|
-
function defineConfig(config) {
|
|
1563
|
-
return {
|
|
1564
|
-
collections: normalizeCollections(config.collections),
|
|
1565
|
-
globals: normalizeGlobals(config.globals),
|
|
1566
|
-
adapter: config.adapter,
|
|
1567
|
-
plugins: config.plugins,
|
|
1568
|
-
auth: config.auth,
|
|
1569
|
-
cors: config.cors,
|
|
1570
|
-
admin: config.admin,
|
|
1571
|
-
upload: config.upload,
|
|
1572
|
-
graphQL: config.graphQL,
|
|
1573
|
-
typescript: config.typescript,
|
|
1574
|
-
localization: config.localization,
|
|
1575
|
-
rateLimit: config.rateLimit,
|
|
1576
|
-
debug: config.debug
|
|
1577
|
-
};
|
|
1578
|
-
}
|
|
1579
|
-
var defineKyroConfig = defineConfig;
|
|
1580
|
-
|
|
1581
|
-
// src/lib/seo.ts
|
|
1582
|
-
function generateSeoTags(options) {
|
|
1583
|
-
const { siteSettings, seoSettings, title, description, image, url } = options;
|
|
1584
|
-
if (!siteSettings) return "";
|
|
1585
|
-
const siteName = siteSettings.siteName || "";
|
|
1586
|
-
const rawTitle = title || seoSettings?.defaultTitle || siteName;
|
|
1587
|
-
const titleTemplate = seoSettings?.titleTemplate;
|
|
1588
|
-
const separator = seoSettings?.separator || " | ";
|
|
1589
|
-
const siteNameInTitle = seoSettings?.siteNameInTitle !== false;
|
|
1590
|
-
const finalTitle = titleTemplate ? titleTemplate.replace(/\{\{title\}\}/g, rawTitle).replace(/\{\{siteName\}\}/g, siteNameInTitle ? siteName : "").replace(/\{\{separator\}\}/g, separator).replace(/\s+/g, " ").trim() : rawTitle;
|
|
1591
|
-
const finalDesc = description || seoSettings?.defaultDescription || siteSettings.siteDescription || "";
|
|
1592
|
-
const finalImage = image || siteSettings.siteOgImage?.url || "";
|
|
1593
|
-
const finalUrl = url || siteSettings.siteUrl || "";
|
|
1594
|
-
let tags = `
|
|
1595
|
-
<title>${finalTitle}</title>
|
|
1596
|
-
<meta name="description" content="${finalDesc}">
|
|
1597
|
-
`;
|
|
1598
|
-
if (siteSettings.siteFavicon?.url) {
|
|
1599
|
-
tags += `
|
|
1600
|
-
<link rel="icon" type="${siteSettings.siteFavicon.mimeType || "image/x-icon"}" href="${siteSettings.siteFavicon.url}">`;
|
|
1601
|
-
}
|
|
1602
|
-
const robots = seoSettings?.meta?.robots;
|
|
1603
|
-
if (robots) {
|
|
1604
|
-
tags += `
|
|
1605
|
-
<meta name="robots" content="${robots}">`;
|
|
1606
|
-
}
|
|
1607
|
-
const canonical = seoSettings?.meta?.canonicalUrl || finalUrl;
|
|
1608
|
-
if (canonical) {
|
|
1609
|
-
tags += `
|
|
1610
|
-
<link rel="canonical" href="${canonical}">`;
|
|
1611
|
-
}
|
|
1612
|
-
const ogType = seoSettings?.meta?.ogType || "website";
|
|
1613
|
-
tags += `
|
|
1614
|
-
<meta property="og:type" content="${ogType}">
|
|
1615
|
-
<meta property="og:title" content="${finalTitle}">
|
|
1616
|
-
<meta property="og:description" content="${finalDesc}">
|
|
1617
|
-
<meta property="og:site_name" content="${siteName}">
|
|
1618
|
-
`;
|
|
1619
|
-
if (finalUrl) tags += `
|
|
1620
|
-
<meta property="og:url" content="${finalUrl}">`;
|
|
1621
|
-
if (finalImage) tags += `
|
|
1622
|
-
<meta property="og:image" content="${finalImage}">`;
|
|
1623
|
-
if (seoSettings?.social?.fbAppId) {
|
|
1624
|
-
tags += `
|
|
1625
|
-
<meta property="fb:app_id" content="${seoSettings.social.fbAppId}">`;
|
|
1626
|
-
}
|
|
1627
|
-
const twitterCard = seoSettings?.social?.twitterCardType || "summary_large_image";
|
|
1628
|
-
tags += `
|
|
1629
|
-
<meta name="twitter:card" content="${twitterCard}">
|
|
1630
|
-
<meta name="twitter:title" content="${finalTitle}">
|
|
1631
|
-
<meta name="twitter:description" content="${finalDesc}">
|
|
1632
|
-
`;
|
|
1633
|
-
if (seoSettings?.social?.twitterHandle) {
|
|
1634
|
-
tags += `
|
|
1635
|
-
<meta name="twitter:site" content="${seoSettings.social.twitterHandle}">`;
|
|
1636
|
-
}
|
|
1637
|
-
if (finalImage) tags += `
|
|
1638
|
-
<meta name="twitter:image" content="${finalImage}">`;
|
|
1639
|
-
if (siteSettings.enableI18n && siteSettings.i18n?.language) {
|
|
1640
|
-
tags += `
|
|
1641
|
-
<meta http-equiv="content-language" content="${siteSettings.i18n.language}">`;
|
|
1642
|
-
}
|
|
1643
|
-
return tags;
|
|
1644
|
-
}
|
|
1645
|
-
function generateAnalyticsTags(siteSettings) {
|
|
1646
|
-
if (!siteSettings || !siteSettings.analyticsEnabled || !siteSettings.analytics) {
|
|
1647
|
-
return "";
|
|
1648
|
-
}
|
|
1649
|
-
const { googleAnalyticsId, googleTagManagerId, plausibleDomain } = siteSettings.analytics;
|
|
1650
|
-
let scripts = "";
|
|
1651
|
-
if (googleAnalyticsId) {
|
|
1652
|
-
scripts += `
|
|
1
|
+
export{a as RedisAuthAdapter}from'./chunk-AU6IWTBO.js';export{P as allSettingsGlobals,c as blogCollections,d as blogGlobals,Q as coreSettingsGlobals,U as createTemplateConfig,e as ecommerceCollections,f as ecommerceGlobals,g as kitchenSinkCollections,i as mediaCollections,a as minimalCollections,T as templateCollections}from'./chunk-ZELCS7IC.js';export{a as kyro}from'./chunk-LZ6YFYQM.js';export{a as ConfigValidationError,p as Kyro,r as LocalAdapter,l as Registry,h as collectionToCreateZod,i as collectionToUpdateZod,j as collectionToWhereZod,g as collectionToZod,q as createKyro,s as createLocalAdapter,o as createRegistry,f as fieldToZod,m as getRegistry,k as globalToZod,n as resetRegistry,b as validateCollection,e as validateConfig,d as validateFields,c as validateGlobal}from'./chunk-FS7GL2BR.js';export{d as autoBootstrap,a as bootstrapAdmin,e as bootstrapWithRetry,c as getBootstrapFromEnv}from'./chunk-FO3HLBC7.js';import'./chunk-NZ35PV5R.js';export{a as CSSGenerator,g as createAdminStyling,d as defaultDarkTheme,h as defaultFieldStyling,c as defaultLightTheme,e as ecommerce2026Theme,f as generateCSSVariables,b as generateTailwindConfig}from'./chunk-UHFDH3PG.js';export{p as ALL_FIELD_TYPES,m as COMPLEX_FIELD_TYPES,o as LAYOUT_FIELD_TYPES,l as PRIMITIVE_FIELD_TYPES,n as RELATIONAL_FIELD_TYPES,d as isArrayField,f as isBlocksField,e as isGroupField,h as isImageField,k as isLayoutField,b as isNumberField,c as isRelationshipField,i as isRichTextField,j as isSelectField,a as isTextField,g as isUploadField,s as normalizeRichTextValue,t as renderRichText,r as richTextStyles}from'./chunk-NGBJKZMT.js';export{a as createContext,g as createCountProcedure,d as createCreateProcedure,f as createDeleteProcedure,h as createDynamicRouter,c as createFindByIDProcedure,b as createFindProcedure,i as createKyroServer,e as createUpdateProcedure}from'./chunk-7N6NKCRT.js';export{I as buildGraphQLSchema,J as createGraphQLSchema}from'./chunk-LRGAPGG3.js';import {l,n,m,p as p$1,q}from'./chunk-URCLFIME.js';export{n as AuditLogger,p as InMemoryAuditLogger,m as InMemoryRateLimiter,r as MediaService,o as createAuditContext,s as createHonoApp,e as createLocalStorage,t as createRESTAPI,i as getAppSecret,j as getEncryptionKey,k as getSessionConfig,h as loadSecrets,d as resolveProvider,g as setDbAdapter}from'./chunk-URCLFIME.js';import {b as b$1,c}from'./chunk-7WOX27LK.js';export{a as ConfigService,b as EmailTransport,c as PasswordPolicy}from'./chunk-7WOX27LK.js';import'./chunk-K6WUOU6P.js';import'./chunk-AE6GXUT7.js';import {a as a$1}from'./chunk-2W4FK6CJ.js';export{a as SQLiteAuthAdapter}from'./chunk-2W4FK6CJ.js';import'./chunk-NRLIGDYF.js';export{d as ALL_WEBHOOK_EVENTS,e as WEBHOOK_COLLECTION,f as WEBHOOK_DELIVERY_COLLECTION,c as WEBHOOK_EVENTS,m as WebhookService,k as buildDeliveryRecord,l as createTestPayload,n as createWebhookService,i as deliverWebhook,j as deliverWithRetry,h as generateWebhookSecret,g as signPayload}from'./chunk-UVTS2IWM.js';export{a as evaluateAccess,c as getWhereClause,b as mergeWhereClauses}from'./chunk-HLSB2MCK.js';export{b as KyroPubSub,c as KyroWSServer,a as PubSub,d as createWSServer}from'./chunk-SL3LZQCF.js';import'./chunk-PP5S2UBV.js';export{c as DrizzleAdapter,b as collectionToDrizzleSchema,f as createDatabase,d as createDrizzleAdapter,a as fieldToDrizzleType,g as runMigrations,h as seedDefaultRoles}from'./chunk-CDL7ARXT.js';import {a as a$2}from'./chunk-RHCWCG3O.js';export{a as PostgresAuthAdapter}from'./chunk-RHCWCG3O.js';import'./chunk-OG7CNQGU.js';import'./chunk-QRXDCV6K.js';export{a as MongoDBAdapter,b as createMongoDBAdapter}from'./chunk-G6A7OSIG.js';import {a}from'./chunk-P7CESLDJ.js';export{a as MongoDBAuthAdapter}from'./chunk-P7CESLDJ.js';export{a as AbstractBaseAdapter}from'./chunk-ZAQOSRP6.js';import'./chunk-KDNYHLD2.js';import'./chunk-DGJUBN33.js';import q$1,{randomBytes}from'crypto';import {readFileSync}from'fs';import Me,{join,resolve}from'path';export{z}from'zod';import {createStorage}from'unstorage';import $r from'unstorage/drivers/indexedb';import zr from'unstorage/drivers/fs';async function Ie(s,e){let t=e.data;for(let r of s){let o=await r({...e,data:t});o!==void 0&&(t=o);}return t}async function br(s,e){return Ie(s,e)}var b=class{name;version;description;hooks={};collections=[];globals=[];fields=[];extensions={collections:[],globals:[]};adminComponents={};adminStyles=[];serverMiddleware;clientMiddleware;constructor(e){this.name=e;}async init(e){}async beforeInit(e){}async afterInit(e){}getCollections(){return this.collections}getGlobals(){return this.globals}getHooks(){return this.hooks}},$=class{plugins=new Map;hooks=new Map;register(e){if(this.plugins.has(e.name))throw new Error(`Plugin "${e.name}" is already registered`);this.plugins.set(e.name,e);let t=e.getHooks?.()||{};for(let[r,o]of Object.entries(t))if(Array.isArray(o))for(let i of o)this.registerHook(r,i);}unregister(e){let t=this.plugins.get(e);if(!t)return;let r=t.getHooks?.()||{};for(let[o,i]of Object.entries(r))if(Array.isArray(i))for(let n of i)this.unregisterHook(o,n);this.plugins.delete(e);}get(e){return this.plugins.get(e)}getAll(){return Array.from(this.plugins.values())}has(e){return this.plugins.has(e)}registerHook(e,t){this.hooks.has(e)||this.hooks.set(e,[]),this.hooks.get(e).push(t);}unregisterHook(e,t){let r=this.hooks.get(e);if(r){let o=r.indexOf(t);o>-1&&r.splice(o,1);}}async executeHook(e,t){let r=this.hooks.get(e)||[],o=t;for(let i of r)try{let n=await i({...t,data:o});n!==void 0&&(o=n);}catch(n){console.error(`[PluginManager] Error in hook "${e}":`,n);}return o}getAllCollections(){let e=[];for(let t of this.plugins.values()){let r=t.getCollections?.()||[];e.push(...r);}return e}getAllGlobals(){let e=[];for(let t of this.plugins.values()){let r=t.getGlobals?.()||[];e.push(...r);}return e}getAllFields(){let e=[];for(let t of this.plugins.values())e.push(...t.fields);return e}getAdminComponents(){let e={};for(let t of this.plugins.values())Object.assign(e,t.adminComponents);return e}getAdminStyles(){let e=[];for(let t of this.plugins.values())e.push(...t.adminStyles);return e}},L=class extends b{constructor(){super("seo"),this.description="Advanced SEO features including sitemaps, robots.txt, and structured data",this.collections.push({slug:"seo-settings",label:"SEO Settings",fields:[{name:"sitemap",type:"checkbox",label:"Enable Sitemap",defaultValue:true},{name:"robotsTxt",type:"richtext",label:"robots.txt Content"},{name:"canonicalUrl",type:"text",variant:"url",label:"Canonical URL"},{name:"ogImage",type:"text",label:"Default OG Image URL"}]});}},E=class extends b{constructor(){super("analytics"),this.description="Analytics integration for tracking page views and events",this.collections.push({slug:"analytics-events",label:"Analytics Events",fields:[{name:"name",type:"text",required:true},{name:"properties",type:"json",label:"Event Properties"},{name:"timestamp",type:"date",required:true},{name:"userId",type:"text",label:"User ID"},{name:"sessionId",type:"text",label:"Session ID"}]}),this.adminComponents.AnalyticsDashboard={};}},U=class extends b{constructor(){super("comments"),this.description="Commenting system for products and posts",this.collections.push({slug:"comments",label:"Comments",fields:[{name:"content",type:"richtext",required:true},{name:"author",type:"text",required:true},{name:"email",type:"email"},{name:"approved",type:"checkbox",defaultValue:false},{name:"parent",type:"text",label:"Parent Comment ID"},{name:"resourceType",type:"text",required:true},{name:"resourceId",type:"text",required:true}]}),this.adminComponents.CommentModeration={};}},V=class extends b{constructor(){super("reviews"),this.description="Product reviews and ratings",this.collections.push({slug:"reviews",label:"Reviews",fields:[{name:"rating",type:"number",required:true,min:1,max:5},{name:"title",type:"text"},{name:"content",type:"richtext",required:true},{name:"author",type:"relationship",relationTo:"customers"},{name:"product",type:"relationship",relationTo:"products",required:true},{name:"approved",type:"checkbox",defaultValue:false},{name:"verified",type:"checkbox",label:"Verified Purchase"},{name:"helpful",type:"number",label:"Helpful Count",defaultValue:0}]}),this.adminComponents.ReviewModeration={};}},O=class extends b{constructor(){super("wishlist"),this.description="Customer wishlists",this.collections.push({slug:"wishlists",label:"Wishlists",fields:[{name:"customer",type:"relationship",relationTo:"customers",required:true},{name:"name",type:"text",label:"Wishlist Name",defaultValue:"My Wishlist"},{name:"items",type:"blocks",label:"Items",blocks:[{slug:"wishlist-item",label:"Item",fields:[{name:"product",type:"relationship",relationTo:"products"},{name:"quantity",type:"number",defaultValue:1},{name:"addedAt",type:"date"},{name:"priority",type:"select",options:[{label:"Low",value:"low"},{label:"Medium",value:"medium"},{label:"High",value:"high"}]}]}]}]});}},Ar={SEO:L,Analytics:E,Comments:U,Reviews:V,Wishlist:O};var wr={maxAttempts:5,lockDuration:9e5,notifyUser:true,notifyAdmin:true,adminNotifyAfter:3},x=class{redis;prefix;config;constructor(e,t={},r="kyro:lockout:"){this.redis=e,this.prefix=r,this.config={...wr,...t};}lockKey(e){return `${this.prefix}${e}`}historyKey(e){return `${this.prefix}${e}:history`}async checkLockout(e){let t=this.lockKey(e),r=await this.redis.hgetall(t);if(!r||Object.keys(r).length===0)return {locked:false,attemptsRemaining:this.config.maxAttempts,totalAttempts:0};let o=parseInt(r.attempts,10),i=r.lockedUntil?new Date(parseInt(r.lockedUntil,10)):void 0;return i&&i>new Date?{locked:true,attemptsRemaining:0,lockedUntil:i,totalAttempts:o}:i&&i<=new Date?(await this.unlockAccount(e),{locked:false,attemptsRemaining:this.config.maxAttempts,totalAttempts:0}):{locked:false,attemptsRemaining:Math.max(0,this.config.maxAttempts-o),totalAttempts:o}}async recordFailedAttempt(e){let t=this.lockKey(e),r=this.historyKey(e),o=Date.now(),i=await this.redis.hincrby(t,"attempts",1);if(await this.redis.hset(t,"lastAttempt",o.toString()),await this.redis.lpush(r,o.toString()),await this.redis.ltrim(r,0,99),i>=this.config.maxAttempts){let n=new Date(o+this.config.lockDuration);return await this.redis.hset(t,{lockedAt:o.toString(),lockedUntil:n.getTime().toString()}),await this.redis.expire(t,Math.ceil(this.config.lockDuration/1e3)+3600),{locked:true,attemptsRemaining:0,lockedUntil:n,totalAttempts:i}}return {locked:false,attemptsRemaining:Math.max(0,this.config.maxAttempts-i),totalAttempts:i}}async lockAccount(e,t){let r=this.lockKey(e),o=Date.now(),i=t||this.config.lockDuration,n=new Date(o+i),a=this.redis.pipeline();a.hset(r,{attempts:this.config.maxAttempts.toString(),lockedAt:o.toString(),lockedUntil:n.getTime().toString()}),a.expire(r,Math.ceil(i/1e3)+3600),await a.exec();}async unlockAccount(e){let t=this.lockKey(e);await this.redis.del(t);}async resetAttempts(e){let t=this.lockKey(e);(await this.redis.hgetall(t)).lockedAt?await this.redis.hset(t,{attempts:"0",lockedAt:"",lockedUntil:""}):await this.redis.del(t);}async getLockoutHistory(e,t=10){let r=this.historyKey(e);return (await this.redis.lrange(r,0,t-1)).map(i=>new Date(parseInt(i,10)))}async getLockoutStats(e){let t=this.historyKey(e),r=await this.redis.lrange(t,0,-1),o=r.filter((n,a)=>(a+1)%this.config.maxAttempts===0).length,i=await this.redis.hget(this.lockKey(e),"lockedAt");return {totalFailedAttempts:r.length,lockoutCount:o,lastLockout:i?new Date(parseInt(i,10)):null,averageAttemptsBeforeLockout:o>0?this.config.maxAttempts:0}}shouldNotifyAdmin(e){return this.config.notifyAdmin&&e>=this.config.adminNotifyAfter}getConfig(){return {...this.config}}setConfig(e){this.config={...this.config,...e};}};var kr={"auth:login":{window:9e5,max:5},"auth:register":{window:36e5,max:3},"auth:forgot":{window:36e5,max:3},"auth:reset":{window:36e5,max:5},"auth:verify":{window:36e5,max:5},"api:general":{window:6e4,max:100},"api:authenticated":{window:6e4,max:200}},S=class{redis;prefix;limits;userLimits;constructor(e,t,r,o="kyro:ratelimit:"){this.redis=e,this.prefix=o,this.limits={...kr,...t},this.userLimits=r||{"user:api":{window:6e4,max:500},"user:write":{window:36e5,max:100}};}getKey(e,t){return `${this.prefix}${e}:${t}`}async check(e,t){let r=this.limits[e]||this.limits["api:general"],o=this.getKey(e,t),i=Date.now(),n=i-r.window,a=this.redis.pipeline();a.zremrangebyscore(o,0,n),a.zcard(o),a.zadd(o,i,`${i}:${Math.random()}`),a.expire(o,Math.ceil(r.window/1e3)+1);let c=(await a.exec())?.[1]?.[1]||0;if(c>=r.max){let g=await this.redis.zrange(o,0,0,"WITHSCORES"),d=g.length>1?parseInt(g[1],10)+r.window:i+r.window;return {allowed:false,remaining:0,resetAt:d,retryAfter:Math.ceil((d-i)/1e3)}}return {allowed:true,remaining:r.max-c-1,resetAt:i+r.window}}async checkUser(e,t,r){let o=this.userLimits[e]||this.userLimits["user:api"],i=this.getKey(`user:${e}:${t}`,r),n=Date.now(),a=n-o.window,l=this.redis.pipeline();l.zremrangebyscore(i,0,a),l.zcard(i),l.zadd(i,n,`${n}:${Math.random()}`),l.expire(i,Math.ceil(o.window/1e3)+1);let g=(await l.exec())?.[1]?.[1]||0;if(g>=o.max){let d=await this.redis.zrange(i,0,0,"WITHSCORES"),u=d.length>1?parseInt(d[1],10)+o.window:n+o.window;return {allowed:false,remaining:0,resetAt:u,retryAfter:Math.ceil((u-n)/1e3)}}return {allowed:true,remaining:o.max-g-1,resetAt:n+o.window}}async reset(e,t){let r=this.getKey(e,t);await this.redis.del(r);}async resetUser(e,t,r){let o=this.getKey(`user:${e}:${t}`,r);await this.redis.del(o);}async getStatus(e,t){let r=this.limits[e]||this.limits["api:general"],o=this.getKey(e,t),i=Date.now(),n=i-r.window;await this.redis.zremrangebyscore(o,0,n);let a=await this.redis.zcard(o);return {count:a,limit:r.max,remaining:Math.max(0,r.max-a),resetAt:i+r.window}}setLimit(e,t){this.limits[e]=t;}setUserLimit(e,t){this.userLimits[e]=t;}};var P=class{users=new Map;sessions=new Map;refreshTokens=new Map;emailToUserId=new Map;passwordHistory=new Map;emailVerificationTokens=new Map;passwordResetTokens=new Map;auditLogs=[];externalDb=false;constructor(){}async connect(){}async disconnect(){this.users.clear(),this.sessions.clear(),this.refreshTokens.clear(),this.emailToUserId.clear(),this.passwordHistory.clear();}async createUser(e){let t=randomBytes(16).toString("hex"),r=new Date().toISOString(),o=await this.hashPassword(e.password),i={id:t,email:e.email.toLowerCase(),passwordHash:o,role:e.role||"customer",tenantId:e.tenantId,createdAt:r,updatedAt:r};return this.users.set(t,i),this.emailToUserId.set(e.email.toLowerCase(),t),this.passwordHistory.set(t,[]),i}async findUserByEmail(e){let t=this.emailToUserId.get(e.toLowerCase());return t?this.findUserById(t):null}async findUserById(e){return this.users.get(e)||null}async updateUser(e,t){let r=await this.findUserById(e);if(!r)return null;let o={...r,...t,id:e,updatedAt:new Date().toISOString()};return t.email&&t.email!==r.email&&(this.emailToUserId.delete(r.email.toLowerCase()),this.emailToUserId.set(t.email.toLowerCase(),e)),this.users.set(e,o),o}async deleteUser(e){let t=await this.findUserById(e);return t?(this.users.delete(e),this.emailToUserId.delete(t.email.toLowerCase()),this.refreshTokens.forEach((r,o)=>{this.sessions.get(r)?.userId===e&&(this.refreshTokens.delete(o),this.sessions.delete(r));}),this.passwordHistory.delete(e),this.sessions.forEach((r,o)=>{r.userId===e&&this.sessions.delete(o);}),true):false}async hashPassword(e){return (await import('bcryptjs')).default.hash(e,12)}async verifyPassword(e,t){let r=await this.findUserByEmail(e);return !r||!r.passwordHash?null:await(await import('bcryptjs')).default.compare(t,r.passwordHash)?r:null}async createSession(e,t={}){let r=randomBytes(32).toString("hex"),o=randomBytes(32).toString("base64url"),i=randomBytes(32).toString("base64url"),n=new Date,a={id:r,userId:e,token:o,refreshToken:i,expiresAt:new Date(n.getTime()+86400*1e3).toISOString(),createdAt:n.toISOString(),ipAddress:t.ipAddress,userAgent:t.userAgent};return this.sessions.set(r,a),this.refreshTokens.set(i,r),a}async findSessionByToken(e){return this.sessions.get(e)||null}async findSessionByRefreshToken(e){let t=this.refreshTokens.get(e);return t&&this.sessions.get(t)||null}async deleteSession(e){let t=this.sessions.get(e);return t?(t.refreshToken&&this.refreshTokens.delete(t.refreshToken),this.sessions.delete(e),true):false}async deleteUserSessions(e){let t=0;return this.sessions.forEach((r,o)=>{r.userId===e&&(r.refreshToken&&this.refreshTokens.delete(r.refreshToken),this.sessions.delete(o),t++);}),t}async addPasswordToHistory(e,t){let r=this.passwordHistory.get(e)||[];r.push(t),r.length>5&&r.splice(0,r.length-5),this.passwordHistory.set(e,r);}async getPasswordHistory(e,t=5){return this.passwordHistory.get(e)||[]}async isPasswordInHistory(e,t,r=5){let o=await this.getPasswordHistory(t,r),i=(await import('bcryptjs')).default;for(let n of o)if(await i.compare(e,n))return true;return false}async createEmailVerificationToken(e){let t=randomBytes(32).toString("hex"),r=new Date(Date.now()+1440*60*1e3);return this.emailVerificationTokens.set(t,{userId:e,expiresAt:r}),{token:t,expiresAt:r}}async verifyEmailToken(e){let t=this.emailVerificationTokens.get(e);return !t||t.expiresAt<new Date?(this.emailVerificationTokens.delete(e),{success:false,error:"Invalid or expired token"}):(this.emailVerificationTokens.delete(e),{success:true,userId:t.userId})}async createPasswordResetToken(e){let t=await this.findUserByEmail(e);if(!t)return {token:"",expiresAt:new Date,error:"User not found"};let r=randomBytes(32).toString("hex"),o=new Date(Date.now()+3600*1e3);return this.passwordResetTokens.set(r,{userId:t.id,expiresAt:o}),{token:r,expiresAt:o}}async resetPasswordWithToken(e,t){let r=this.passwordResetTokens.get(e);if(!r||r.expiresAt<new Date)return this.passwordResetTokens.delete(e),{success:false,error:"Invalid or expired token"};let o=await this.hashPassword(t);return await this.updateUser(r.userId,{passwordHash:o}),this.passwordResetTokens.delete(e),{success:true}}async hasAnyUsers(){return this.users.size>0}async findAuditLogs(e){let{limit:t=50,offset:r=0}=e,o=this.auditLogs.slice().reverse();return e.userId&&(o=o.filter(i=>i.userId===e.userId)),e.action&&(Array.isArray(e.action)?o=o.filter(i=>e.action.includes(String(i.action))):o=o.filter(i=>i.action===e.action)),e.resource&&(o=o.filter(i=>i.resource===e.resource)),e.success!==void 0&&(o=o.filter(i=>i.success===e.success)),{logs:o.slice(r,r+t),total:o.length}}async createAuditLog(e){let t=randomBytes(16).toString("hex"),o={...e,id:t,timestamp:new Date};return this.auditLogs.push(o),o}};var C=class{storage=new Map;history=new Map;config;constructor(e={}){this.config={maxAttempts:5,lockDuration:9e5,notifyUser:true,notifyAdmin:true,adminNotifyAfter:3,...e};}async checkLockout(e){let t=Date.now(),r=this.storage.get(e);if(r&&r.lockedUntil!==null&&r.lockedUntil<=t)return await this.resetAttempts(e),{locked:false,attemptsRemaining:this.config.maxAttempts,totalAttempts:0};if(!r)return {locked:false,attemptsRemaining:this.config.maxAttempts,totalAttempts:0};let{attempts:o,lockedUntil:i}=r;return i!==null&&i>t?{locked:true,attemptsRemaining:0,lockedUntil:new Date(i),totalAttempts:o}:{locked:false,attemptsRemaining:Math.max(0,this.config.maxAttempts-o),totalAttempts:o}}async recordFailedAttempt(e){let t=Date.now(),r=this.storage.get(e)||{attempts:0,lastAttempt:null,lockedAt:null,lockedUntil:null};r.attempts+=1,r.lastAttempt=t;let o=this.history.get(e)||[];if(o.push(t),o.length>100&&o.splice(0,o.length-100),this.history.set(e,o),this.storage.set(e,r),r.attempts>=this.config.maxAttempts){let i=new Date(t+this.config.lockDuration);return r.lockedAt=t,r.lockedUntil=i.getTime(),this.storage.set(e,r),{locked:true,attemptsRemaining:0,lockedUntil:i,totalAttempts:r.attempts}}return {locked:false,attemptsRemaining:Math.max(0,this.config.maxAttempts-r.attempts),totalAttempts:r.attempts}}async lockAccount(e,t){let r=Date.now(),o=t||this.config.lockDuration,i=new Date(r+o),n=this.storage.get(e)||{attempts:0,lastAttempt:null,lockedAt:null,lockedUntil:null};n.attempts=this.config.maxAttempts,n.lockedAt=r,n.lockedUntil=i.getTime(),this.storage.set(e,n);}async unlockAccount(e){await this.resetAttempts(e);}async resetAttempts(e){let t=this.storage.get(e);t&&(t.attempts=0,t.lockedAt=null,t.lockedUntil=null,this.storage.set(e,t)),this.history.delete(e);}async getLockoutHistory(e,t=10){return (this.history.get(e)||[]).slice(-t).reverse().map(o=>new Date(o))}async getLockoutStats(e){let r=(this.history.get(e)||[]).length,o=Math.floor(r/this.config.maxAttempts),i=null,n=this.storage.get(e);n&&n.lockedAt!==null&&(i=new Date(n.lockedAt));let a=o>0?this.config.maxAttempts:0;return {totalFailedAttempts:r,lockoutCount:o,lastLockout:i,averageAttemptsBeforeLockout:a}}shouldNotifyAdmin(e){return this.config.notifyAdmin&&e>=this.config.adminNotifyAfter}getConfig(){return {...this.config}}setConfig(e){this.config={...this.config,...e};}};function h(s,e=""){return process.env[s]||e}function A(s,e=false){let t=process.env[s];return t?t.toLowerCase()==="true":e}function p(s,e=0){let t=process.env[s];return t?parseInt(t,10):e}function Le(){let s=process.env.KYRO_AUTH_DATABASE?.toLowerCase();if(s&&["sqlite","postgres","mongodb","memory"].includes(s))return s;try{let e=join(process.cwd(),"kyro.config.ts"),t=readFileSync(e,"utf8");if(t.includes("createLocalAdapter"))return "sqlite";if(t.includes("createDrizzleAdapter"))return t.includes("postgres")||t.includes("postgresql"),"postgres";if(t.includes("createMongoDBAdapter"))return "mongodb"}catch{}return "memory"}async function Pr(s){let e=process.cwd(),t=e.endsWith("admin")?join(e,".."):e,r=resolve(t,"data","auth.db");switch(s){case "sqlite":return new a$1({path:h("KYRO_AUTH_DB_PATH",r)});case "postgres":{try{let o=h("DATABASE_URL","");if(o){let{drizzle:i}=await import('drizzle-orm/postgres-js'),a=(await import('postgres')).default(o,{onnotice:()=>{}}),l=i(a);return new a$2({db:l})}}catch{console.warn("[Auth] Could not create PostgresAuthAdapter, falling back to SQLite auth adapter.");}return new a$1({path:h("KYRO_AUTH_DB_PATH",r)})}case "mongodb":{try{let o=h("MONGODB_URI","");if(o){let{MongoClient:i}=await import('mongodb'),n=new i(o);await n.connect();let l=new URL(o).pathname.replace(/^\//,"")||"kyro_cms",c=n.db(l);return new a({db:c})}}catch{console.warn("[Auth] Could not create MongoDBAuthAdapter, falling back to SQLite auth adapter.");}return new a$1({path:h("KYRO_AUTH_DB_PATH",r)})}default:return new P}}async function W(s,e){let t=A("KYRO_DISTRIBUTED",false),r;if(t){let{RedisAuthAdapter:d}=await import('./redis-adapter-AUKOV74G.js'),u=h("REDIS_URL","redis://localhost:6379"),y=A("REDIS_TLS",false),f=new d({url:u,tls:y});await f.connect?.(),r=f;}else {let d=s||Le();r=await Pr(d),r.connect&&await r.connect();}let o=e?await b$1.fromConfig(e).catch(()=>null)||b$1.fromEnv()||void 0:b$1.fromEnv()||void 0,i=new c({minLength:p("PASSWORD_MIN_LENGTH",12),requireUppercase:A("PASSWORD_REQUIRE_UPPERCASE",true),requireLowercase:A("PASSWORD_REQUIRE_LOWERCASE",true),requireNumbers:A("PASSWORD_REQUIRE_NUMBERS",true),requireSpecialChars:A("PASSWORD_REQUIRE_SPECIAL",true),preventReuse:p("PASSWORD_PREVENT_REUSE",5),maxLength:p("PASSWORD_MAX_LENGTH",128)}),n$1,a,l;if(t){let u=r.redis;n$1=new x(u,{maxAttempts:p("LOCKOUT_MAX_ATTEMPTS",5),lockDuration:p("LOCKOUT_DURATION_MINUTES",15)*60*1e3}),a=new S(u,{"auth:login":{window:p("RATE_LIMIT_AUTH_WINDOW_MS",9e5),max:p("RATE_LIMIT_AUTH_MAX_REQUESTS",10)},"api:general":{window:p("RATE_LIMIT_WINDOW_MS",6e4),max:p("RATE_LIMIT_MAX_REQUESTS",100)}}),l=new n(u,p("AUDIT_LOG_RETENTION_DAYS",30));}else n$1=new C({maxAttempts:p("LOCKOUT_MAX_ATTEMPTS",5),lockDuration:p("LOCKOUT_DURATION_MINUTES",15)*60*1e3}),a=new m({"auth:login":{window:p("RATE_LIMIT_AUTH_WINDOW_MS",9e5),max:p("RATE_LIMIT_AUTH_MAX_REQUESTS",10)},"api:general":{window:p("RATE_LIMIT_WINDOW_MS",6e4),max:p("RATE_LIMIT_MAX_REQUESTS",100)}}),l=A("AUDIT_LOG_ENABLED",true)?new p$1(p("AUDIT_LOG_RETENTION_DAYS",30)):void 0;let c$1=new q({redis:r,email:o,jwtSecret:h("APP_SECRET","change-me"),jwtExpiresIn:h("JWT_EXPIRES_IN","24h"),jwtIssuer:h("JWT_ISSUER","kyro-cms"),jwtAudience:h("JWT_AUDIENCE","kyro-cms-client"),passwordPolicy:i,lockout:n$1,rateLimiter:a,auditLogger:l,baseUrl:h("EMAIL_BASE_URL","http://localhost:4321"),emailVerificationRequired:A("EMAIL_VERIFICATION_REQUIRED",true)}),g=t?"distributed":s||Le();return {authAdapter:r,databaseType:g,email:o,passwordPolicy:i,lockout:n$1,rateLimiter:a,auditLogger:l,routes:c$1}}var Ue=W().catch(s=>(console.warn("[AuthConfig] Failed to initialize auth config:",s.message),null));var Cr=12,Tr="24h",vr="7d",H=class{adapter;config;constructor(e,t){this.adapter=e,this.config={secret:t.secret,expiresIn:t.expiresIn??Tr,refreshExpiresIn:t.refreshExpiresIn??vr,issuer:t.issuer??"kyro-cms",audience:t.audience??[],saltRounds:t.saltRounds??Cr};}async register(e){try{if(await this.adapter.findUserByEmail(e.email))return {success:!1,error:"Email already registered"};let r=await this.adapter.createUser({email:e.email,password:e.password,role:e.role??"customer",tenantId:e.tenantId});return this.createSessionForUser(r)}catch(t){return {success:false,error:String(t)}}}async login(e){try{let t=await this.adapter.verifyPassword(e.email,e.password);return t?this.createSessionForUser(t):{success:!1,error:"Invalid credentials"}}catch(t){return {success:false,error:String(t)}}}async logout(e){await this.adapter.deleteSession(e);}async refreshToken(e){try{let t=await this.adapter.findSessionByToken(e);if(!t||new Date(t.expiresAt)<new Date)return {success:!1,error:"Invalid or expired refresh token"};let r=await this.adapter.findUserById(t.userId);return r?(await this.adapter.deleteSession(e),this.createSessionForUser(r)):{success:!1,error:"User not found"}}catch(t){return {success:false,error:String(t)}}}async verifyToken(e){try{let{default:t}=await import('jsonwebtoken');return t.verify(e,this.config.secret,{issuer:this.config.issuer,audience:this.config.audience.length>0?this.config.audience[0]:void 0})}catch{return null}}async getUserFromToken(e){let t=await this.verifyToken(e);return t?this.adapter.findUserById(t.sub):null}async changePassword(e,t,r){try{let o=await this.adapter.findUserById(e);return o?await this.adapter.verifyPassword(o.email,t)?(await this.adapter.updateUser(e,{password:r}),await this.adapter.deleteUserSessions(e),{success:!0,user:o}):{success:!1,error:"Current password is incorrect"}:{success:!1,error:"User not found"}}catch(o){return {success:false,error:String(o)}}}async resetPassword(e,t){try{let r=await this.adapter.findUserByEmail(e);return r?(await this.adapter.updateUser(r.id,{password:t}),await this.adapter.deleteUserSessions(r.id),{success:!0,user:r}):{success:!1,error:"User not found"}}catch(r){return {success:false,error:String(r)}}}async sendEmailVerification(e){try{let{token:t,expiresAt:r}=await this.adapter.createEmailVerificationToken(e);return {success:!0}}catch(t){return {success:false,error:String(t)}}}async verifyEmail(e){try{return await this.adapter.verifyEmailToken(e)}catch(t){return {success:false,error:String(t)}}}async requestPasswordReset(e){try{let t=await this.adapter.createPasswordResetToken(e);return t.error?{success:!1,error:t.error}:{success:!0}}catch(t){return {success:false,error:String(t)}}}async resetPasswordWithToken(e,t){try{return await this.adapter.resetPasswordWithToken(e,t)}catch(r){return {success:false,error:String(r)}}}async deleteAccount(e){try{return await this.adapter.findUserById(e)?(await this.adapter.deleteUserSessions(e),await this.adapter.deleteUser(e),{success:!0}):{success:!1,error:"User not found"}}catch(t){return {success:false,error:String(t)}}}async createSessionForUser(e){let t=await this.generateToken(e),r=await this.adapter.createSession(e.id);return {success:true,user:e,session:r,token:t}}async generateToken(e){let{default:t}=await import('jsonwebtoken'),r={sub:e.id,email:e.email,role:e.role,tenantId:e.tenantId},o={expiresIn:this.parseExpiresIn(this.config.expiresIn)/1e3,issuer:this.config.issuer};return this.config.audience.length>0&&(o.audience=this.config.audience[0]),t.sign(r,this.config.secret,o)}async hashPassword(e){let{default:t}=await import('bcryptjs');return t.hash(e,this.config.saltRounds)}parseExpiresIn(e){if(typeof e=="number")return e;let t=e.match(/^(\d+)([smhd])$/);if(!t)return 864e5;let r=parseInt(t[1],10);switch(t[2]){case "s":return r*1e3;case "m":return r*6e4;case "h":return r*36e5;case "d":return r*864e5;default:return 864e5}}};function Rr(s,e){return new H(s,e)}function _(){return {enabled:true,draftsEnabled:true,publishEnabled:true,scheduleEnabled:false,versioningEnabled:true,maxVersionsPerDocument:50,autoPublish:false,requirePublishPermission:true}}var M=class{adapter;config;constructor(e,t){this.adapter=e,this.config={..._(),...t};}async createVersion(e){let r=((await this.adapter.getLatestVersion(e.collection,e.documentId))?.version??0)+1,o={...e,version:r},i=await this.adapter.createVersion(o);return this.config.maxVersionsPerDocument>0&&await this.pruneOldVersions(e.collection,e.documentId),i}async publishVersion(e){let t=await this.adapter.getVersion(e.collection,e.versionId);if(!t)throw new Error("Version not found");if(t.status==="published")throw new Error("Version is already published");await this.adapter.publishVersion(e);}async unpublishDocument(e,t){let r=await this.adapter.getVersions({collection:e,documentId:t,limit:1e3});for(let o of r)if(o.status==="published"){await this.createVersion({collection:e,documentId:t,data:o.data,status:"draft",createdBy:"system",changeDescription:"Unpublished document"});break}}async revertToVersion(e,t,r,o){if(!await this.adapter.getVersion(e,r))throw new Error("Version not found");return await this.adapter.revertToVersion({collection:e,documentId:t,versionId:r,userId:o})}async getVersionHistory(e,t,r=20,o=0){return this.adapter.getVersions({collection:e,documentId:t,limit:r,offset:o})}async compareTwoVersions(e,t,r,o){return this.adapter.compareVersions({collection:e,documentId:t,versionA:r,versionB:o})}async getLatestDraft(e,t){return this.adapter.getLatestVersion(e,t)}async getPublishedVersion(e,t){return this.adapter.getPublishedVersion(e,t)}async getVersion(e,t){return this.adapter.getVersion(e,t)}async schedulePublish(e,t,r,o){if(!this.config.scheduleEnabled)throw new Error("Scheduled publishing is not enabled");if(!await this.adapter.getVersion(e,r))throw new Error("Version not found")}async deleteVersionHistory(e,t){await this.adapter.deleteVersions(e,t);}async pruneOldVersions(e,t){let r=await this.adapter.getVersions({collection:e,documentId:t,limit:this.config.maxVersionsPerDocument+100});if(r.length<=this.config.maxVersionsPerDocument)return;r.slice(0,this.config.maxVersionsPerDocument);let i=r.slice(this.config.maxVersionsPerDocument);for(let n of i)if(n.status!=="published"){await this.adapter.deleteVersions(e,t);break}}};function Dr(s,e){return new M(s,e)}function Ir(s){return s==="published"}function Lr(s){return s==="draft"}function Er(s){return s==="archived"}function Ur(s){return s?Array.isArray(s)?s:Object.values(s):[]}function Vr(s){return s?Array.isArray(s)?s:Object.values(s):[]}function Ve(s){return {collections:Ur(s.collections),globals:Vr(s.globals),adapter:s.adapter,plugins:s.plugins,auth:s.auth,cors:s.cors,admin:s.admin,upload:s.upload,graphQL:s.graphQL,typescript:s.typescript,localization:s.localization,rateLimit:s.rateLimit,debug:s.debug}}var Or=Ve;function Hr(s){let{siteSettings:e,seoSettings:t,title:r,description:o,image:i,url:n}=s;if(!e)return "";let a=e.siteName||"",l=r||t?.defaultTitle||a,c=t?.titleTemplate,g=t?.separator||" | ",d=t?.siteNameInTitle!==false,u=c?c.replace(/\{\{title\}\}/g,l).replace(/\{\{siteName\}\}/g,d?a:"").replace(/\{\{separator\}\}/g,g).replace(/\s+/g," ").trim():l,y=o||t?.defaultDescription||e.siteDescription||"",f=i||e.siteOgImage?.url||"",F=n||e.siteUrl||"",m=`
|
|
2
|
+
<title>${u}</title>
|
|
3
|
+
<meta name="description" content="${y}">
|
|
4
|
+
`;e.siteFavicon?.url&&(m+=`
|
|
5
|
+
<link rel="icon" type="${e.siteFavicon.mimeType||"image/x-icon"}" href="${e.siteFavicon.url}">`);let X=t?.meta?.robots;X&&(m+=`
|
|
6
|
+
<meta name="robots" content="${X}">`);let J=t?.meta?.canonicalUrl||F;J&&(m+=`
|
|
7
|
+
<link rel="canonical" href="${J}">`);let Be=t?.meta?.ogType||"website";m+=`
|
|
8
|
+
<meta property="og:type" content="${Be}">
|
|
9
|
+
<meta property="og:title" content="${u}">
|
|
10
|
+
<meta property="og:description" content="${y}">
|
|
11
|
+
<meta property="og:site_name" content="${a}">
|
|
12
|
+
`,F&&(m+=`
|
|
13
|
+
<meta property="og:url" content="${F}">`),f&&(m+=`
|
|
14
|
+
<meta property="og:image" content="${f}">`),t?.social?.fbAppId&&(m+=`
|
|
15
|
+
<meta property="fb:app_id" content="${t.social.fbAppId}">`);let Ne=t?.social?.twitterCardType||"summary_large_image";return m+=`
|
|
16
|
+
<meta name="twitter:card" content="${Ne}">
|
|
17
|
+
<meta name="twitter:title" content="${u}">
|
|
18
|
+
<meta name="twitter:description" content="${y}">
|
|
19
|
+
`,t?.social?.twitterHandle&&(m+=`
|
|
20
|
+
<meta name="twitter:site" content="${t.social.twitterHandle}">`),f&&(m+=`
|
|
21
|
+
<meta name="twitter:image" content="${f}">`),e.enableI18n&&e.i18n?.language&&(m+=`
|
|
22
|
+
<meta http-equiv="content-language" content="${e.i18n.language}">`),m}function _r(s){if(!s||!s.analyticsEnabled||!s.analytics)return "";let{googleAnalyticsId:e,googleTagManagerId:t,plausibleDomain:r}=s.analytics,o="";return e&&(o+=`
|
|
1653
23
|
<!-- Google Analytics -->
|
|
1654
|
-
<script async src="https://www.googletagmanager.com/gtag/js?id=${
|
|
24
|
+
<script async src="https://www.googletagmanager.com/gtag/js?id=${e}"></script>
|
|
1655
25
|
<script>
|
|
1656
26
|
window.dataLayer = window.dataLayer || [];
|
|
1657
27
|
function gtag(){dataLayer.push(arguments);}
|
|
1658
28
|
gtag('js', new Date());
|
|
1659
|
-
gtag('config', '${
|
|
29
|
+
gtag('config', '${e}');
|
|
1660
30
|
</script>
|
|
1661
|
-
|
|
1662
|
-
}
|
|
1663
|
-
if (googleTagManagerId) {
|
|
1664
|
-
scripts += `
|
|
31
|
+
`),t&&(o+=`
|
|
1665
32
|
<!-- Google Tag Manager -->
|
|
1666
33
|
<script>
|
|
1667
34
|
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
|
1668
35
|
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
|
1669
36
|
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
|
1670
37
|
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
|
1671
|
-
})(window,document,'script','dataLayer','${
|
|
38
|
+
})(window,document,'script','dataLayer','${t}');
|
|
1672
39
|
</script>
|
|
1673
|
-
|
|
1674
|
-
}
|
|
1675
|
-
if (plausibleDomain) {
|
|
1676
|
-
scripts += `
|
|
40
|
+
`),r&&(o+=`
|
|
1677
41
|
<!-- Plausible Analytics -->
|
|
1678
|
-
<script defer data-domain="${
|
|
1679
|
-
|
|
1680
|
-
}
|
|
1681
|
-
return scripts;
|
|
1682
|
-
}
|
|
1683
|
-
|
|
1684
|
-
// src/lib/social.ts
|
|
1685
|
-
var PLATFORM_META = {
|
|
1686
|
-
facebook: { label: "Facebook" },
|
|
1687
|
-
twitter: { label: "Twitter / X" },
|
|
1688
|
-
instagram: { label: "Instagram" },
|
|
1689
|
-
linkedin: { label: "LinkedIn" },
|
|
1690
|
-
youtube: { label: "YouTube" },
|
|
1691
|
-
tiktok: { label: "TikTok" },
|
|
1692
|
-
pinterest: { label: "Pinterest" },
|
|
1693
|
-
discord: { label: "Discord" },
|
|
1694
|
-
twitch: { label: "Twitch" },
|
|
1695
|
-
github: { label: "GitHub" },
|
|
1696
|
-
mastodon: { label: "Mastodon" }
|
|
1697
|
-
};
|
|
1698
|
-
var ALL_PLATFORMS = [
|
|
1699
|
-
"facebook",
|
|
1700
|
-
"twitter",
|
|
1701
|
-
"instagram",
|
|
1702
|
-
"linkedin",
|
|
1703
|
-
"youtube",
|
|
1704
|
-
"tiktok",
|
|
1705
|
-
"pinterest",
|
|
1706
|
-
"discord",
|
|
1707
|
-
"twitch",
|
|
1708
|
-
"github",
|
|
1709
|
-
"mastodon"
|
|
1710
|
-
];
|
|
1711
|
-
function getSocialLinksFromSettings(socialSettings) {
|
|
1712
|
-
if (!socialSettings) return [];
|
|
1713
|
-
const showAll = socialSettings.showAll === true;
|
|
1714
|
-
const links = [];
|
|
1715
|
-
for (const platform of ALL_PLATFORMS) {
|
|
1716
|
-
const url = socialSettings[platform];
|
|
1717
|
-
if (!url) continue;
|
|
1718
|
-
const isNiche = ["tiktok", "pinterest", "discord", "twitch", "github", "mastodon"].includes(platform);
|
|
1719
|
-
if (isNiche && !showAll) continue;
|
|
1720
|
-
const meta = PLATFORM_META[platform];
|
|
1721
|
-
links.push({
|
|
1722
|
-
platform,
|
|
1723
|
-
url,
|
|
1724
|
-
label: meta?.label || platform
|
|
1725
|
-
});
|
|
1726
|
-
}
|
|
1727
|
-
return links;
|
|
1728
|
-
}
|
|
1729
|
-
async function getSocialLinks(db, options) {
|
|
1730
|
-
try {
|
|
1731
|
-
const doc = await db.findOne({
|
|
1732
|
-
collection: "_globals_social-settings",
|
|
1733
|
-
where: {},
|
|
1734
|
-
draft: options?.draft ?? false
|
|
1735
|
-
});
|
|
1736
|
-
return getSocialLinksFromSettings(doc);
|
|
1737
|
-
} catch {
|
|
1738
|
-
return [];
|
|
1739
|
-
}
|
|
1740
|
-
}
|
|
1741
|
-
|
|
1742
|
-
// src/lib/store.ts
|
|
1743
|
-
function getStoreConfigFromSettings(settings) {
|
|
1744
|
-
if (!settings) return {};
|
|
1745
|
-
return {
|
|
1746
|
-
storeName: settings.storeName,
|
|
1747
|
-
storeEmail: settings.storeEmail,
|
|
1748
|
-
storePhone: settings.storePhone,
|
|
1749
|
-
address: settings.address,
|
|
1750
|
-
currency: settings.currency,
|
|
1751
|
-
tax: settings.tax,
|
|
1752
|
-
shipping: settings.shipping,
|
|
1753
|
-
orders: settings.orders
|
|
1754
|
-
};
|
|
1755
|
-
}
|
|
1756
|
-
async function getStoreConfig(db, options) {
|
|
1757
|
-
try {
|
|
1758
|
-
const doc = await db.findOne({
|
|
1759
|
-
collection: "_globals_store-settings",
|
|
1760
|
-
where: {},
|
|
1761
|
-
draft: options?.draft ?? false
|
|
1762
|
-
});
|
|
1763
|
-
return getStoreConfigFromSettings(doc);
|
|
1764
|
-
} catch {
|
|
1765
|
-
return {};
|
|
1766
|
-
}
|
|
1767
|
-
}
|
|
1768
|
-
|
|
1769
|
-
// src/lib/payment.ts
|
|
1770
|
-
function getPaymentConfigFromSettings(settings) {
|
|
1771
|
-
if (!settings) return {};
|
|
1772
|
-
return {
|
|
1773
|
-
testMode: settings.testMode,
|
|
1774
|
-
provider: settings.provider,
|
|
1775
|
-
stripe: settings.stripe,
|
|
1776
|
-
paypal: settings.paypal,
|
|
1777
|
-
square: settings.square,
|
|
1778
|
-
methods: settings.methods,
|
|
1779
|
-
bankTransfer: settings.bankTransfer
|
|
1780
|
-
};
|
|
1781
|
-
}
|
|
1782
|
-
async function getPaymentConfig(db, options) {
|
|
1783
|
-
try {
|
|
1784
|
-
const doc = await db.findOne({
|
|
1785
|
-
collection: "_globals_payment-settings",
|
|
1786
|
-
where: {},
|
|
1787
|
-
draft: options?.draft ?? false
|
|
1788
|
-
});
|
|
1789
|
-
return getPaymentConfigFromSettings(doc);
|
|
1790
|
-
} catch {
|
|
1791
|
-
return {};
|
|
1792
|
-
}
|
|
1793
|
-
}
|
|
1794
|
-
async function createBrowserStorage(options = {}) {
|
|
1795
|
-
const { namespace = "kyro", ttl } = options;
|
|
1796
|
-
const store = createStorage({
|
|
1797
|
-
driver: indexedbDriver({
|
|
1798
|
-
dbName: "kyro-cms",
|
|
1799
|
-
storeName: namespace
|
|
1800
|
-
})
|
|
1801
|
-
});
|
|
1802
|
-
const prefix = namespace ? `${namespace}:` : "";
|
|
1803
|
-
const storage = {
|
|
1804
|
-
getItem: async (name) => {
|
|
1805
|
-
const key = `${prefix}${name}`;
|
|
1806
|
-
const raw = await store.getItem(key);
|
|
1807
|
-
if (raw == null) return null;
|
|
1808
|
-
try {
|
|
1809
|
-
const parsed = JSON.parse(raw);
|
|
1810
|
-
if (parsed.expiry && Date.now() > parsed.expiry) {
|
|
1811
|
-
await store.removeItem(key);
|
|
1812
|
-
return null;
|
|
1813
|
-
}
|
|
1814
|
-
return parsed.value;
|
|
1815
|
-
} catch {
|
|
1816
|
-
return raw;
|
|
1817
|
-
}
|
|
1818
|
-
},
|
|
1819
|
-
setItem: async (name, value) => {
|
|
1820
|
-
const key = `${prefix}${name}`;
|
|
1821
|
-
const data = JSON.stringify(
|
|
1822
|
-
ttl ? { value, expiry: Date.now() + ttl } : { value }
|
|
1823
|
-
);
|
|
1824
|
-
await store.setItem(key, data);
|
|
1825
|
-
},
|
|
1826
|
-
removeItem: async (name) => {
|
|
1827
|
-
await store.removeItem(`${prefix}${name}`);
|
|
1828
|
-
}
|
|
1829
|
-
};
|
|
1830
|
-
return {
|
|
1831
|
-
storage,
|
|
1832
|
-
cleanup: async () => {
|
|
1833
|
-
await store.dispose?.();
|
|
1834
|
-
}
|
|
1835
|
-
};
|
|
1836
|
-
}
|
|
1837
|
-
function getEncryptionKey2(secret) {
|
|
1838
|
-
const appSecret = secret || process.env.APP_SECRET || process.env.AUTH_SECRET || "development-secret-key";
|
|
1839
|
-
return crypto2.createHash("sha256").update(appSecret).digest("hex");
|
|
1840
|
-
}
|
|
1841
|
-
function encrypt(text, key) {
|
|
1842
|
-
const iv = crypto2.randomBytes(16);
|
|
1843
|
-
const cipher = crypto2.createCipheriv("aes-256-gcm", Buffer.from(key, "hex").subarray(0, 32), iv);
|
|
1844
|
-
let encrypted = cipher.update(text, "utf8", "hex");
|
|
1845
|
-
encrypted += cipher.final("hex");
|
|
1846
|
-
const authTag = cipher.getAuthTag().toString("hex");
|
|
1847
|
-
return `${iv.toString("hex")}:${authTag}:${encrypted}`;
|
|
1848
|
-
}
|
|
1849
|
-
function decrypt(encryptedText, key) {
|
|
1850
|
-
const [ivHex, authTagHex, encrypted] = encryptedText.split(":");
|
|
1851
|
-
const iv = Buffer.from(ivHex, "hex");
|
|
1852
|
-
const authTag = Buffer.from(authTagHex, "hex");
|
|
1853
|
-
const decipher = crypto2.createDecipheriv("aes-256-gcm", Buffer.from(key, "hex").subarray(0, 32), iv);
|
|
1854
|
-
decipher.setAuthTag(authTag);
|
|
1855
|
-
let decrypted = decipher.update(encrypted, "hex", "utf8");
|
|
1856
|
-
decrypted += decipher.final("utf8");
|
|
1857
|
-
return decrypted;
|
|
1858
|
-
}
|
|
1859
|
-
async function createNodeStorage(options = {}) {
|
|
1860
|
-
const {
|
|
1861
|
-
namespace = "kyro",
|
|
1862
|
-
ttl,
|
|
1863
|
-
encryption = true,
|
|
1864
|
-
secret,
|
|
1865
|
-
basePath
|
|
1866
|
-
} = options;
|
|
1867
|
-
const defaultPath = basePath ? path.join(basePath, ".astro", "kyro.json") : path.join(process.cwd(), ".astro", "kyro.json");
|
|
1868
|
-
const store = createStorage({
|
|
1869
|
-
driver: fsDriver({ base: defaultPath })
|
|
1870
|
-
});
|
|
1871
|
-
const encKey = encryption ? getEncryptionKey2(secret) : null;
|
|
1872
|
-
const prefix = namespace ? `${namespace}:` : "";
|
|
1873
|
-
return {
|
|
1874
|
-
storage: {
|
|
1875
|
-
getItem: async (name) => {
|
|
1876
|
-
const key = `${prefix}${name}`;
|
|
1877
|
-
const raw = await store.getItem(key);
|
|
1878
|
-
if (raw == null) return null;
|
|
1879
|
-
try {
|
|
1880
|
-
const data = encKey ? decrypt(raw, encKey) : raw;
|
|
1881
|
-
const parsed = JSON.parse(data);
|
|
1882
|
-
if (parsed.expiry && Date.now() > parsed.expiry) {
|
|
1883
|
-
await store.removeItem(key);
|
|
1884
|
-
return null;
|
|
1885
|
-
}
|
|
1886
|
-
return parsed.value;
|
|
1887
|
-
} catch {
|
|
1888
|
-
return raw;
|
|
1889
|
-
}
|
|
1890
|
-
},
|
|
1891
|
-
setItem: async (name, value) => {
|
|
1892
|
-
const key = `${prefix}${name}`;
|
|
1893
|
-
const data = JSON.stringify(
|
|
1894
|
-
ttl ? { value, expiry: Date.now() + ttl } : { value }
|
|
1895
|
-
);
|
|
1896
|
-
await store.setItem(key, encKey ? encrypt(data, encKey) : data);
|
|
1897
|
-
},
|
|
1898
|
-
removeItem: async (name) => {
|
|
1899
|
-
await store.removeItem(`${prefix}${name}`);
|
|
1900
|
-
}
|
|
1901
|
-
},
|
|
1902
|
-
cleanup: async () => {
|
|
1903
|
-
await store.dispose?.();
|
|
1904
|
-
}
|
|
1905
|
-
};
|
|
1906
|
-
}
|
|
1907
|
-
|
|
1908
|
-
// src/lib/storage/factory.ts
|
|
1909
|
-
async function createStorage3(config) {
|
|
1910
|
-
const { environment, adapter, connectionString, encryption } = config;
|
|
1911
|
-
const secret = process.env.APP_SECRET || process.env.AUTH_SECRET;
|
|
1912
|
-
const encryptionOptions = {
|
|
1913
|
-
enabled: encryption?.enabled ?? true,
|
|
1914
|
-
algorithm: encryption?.algorithm
|
|
1915
|
-
};
|
|
1916
|
-
if (environment === "browser") {
|
|
1917
|
-
return createBrowserStorage({
|
|
1918
|
-
namespace: "kyro"
|
|
1919
|
-
});
|
|
1920
|
-
}
|
|
1921
|
-
return createNodeStorage({
|
|
1922
|
-
namespace: "kyro",
|
|
1923
|
-
encryption: encryptionOptions.enabled,
|
|
1924
|
-
secret
|
|
1925
|
-
});
|
|
1926
|
-
}
|
|
1927
|
-
async function createAuthStorage(config) {
|
|
1928
|
-
const storageConfig = {
|
|
1929
|
-
...config,
|
|
1930
|
-
encryption: {
|
|
1931
|
-
enabled: true,
|
|
1932
|
-
algorithm: config.encryption?.algorithm
|
|
1933
|
-
}
|
|
1934
|
-
};
|
|
1935
|
-
return createStorage3(storageConfig);
|
|
1936
|
-
}
|
|
1937
|
-
|
|
1938
|
-
// src/index.ts
|
|
1939
|
-
init_secret();
|
|
1940
|
-
|
|
1941
|
-
export { AccountLockout, AnalyticsPlugin, Auth, CommentsPlugin, InMemoryAccountLockout, InMemoryAuthAdapter, KyroPlugin, PluginManager, RateLimiter, ReviewsPlugin, SEOPLugin, VersionManager, WishlistPlugin, authConfig, createAuth, createAuthConfig, createAuthStorage, createStorage3 as createStorage, createVersionManager, defineConfig, defineKyroConfig, generateAnalyticsTags, generateSeoTags, getDefaultDraftPublishConfig, getPaymentConfig, getPaymentConfigFromSettings, getSocialLinks, getSocialLinksFromSettings, getStoreConfig, getStoreConfigFromSettings, isArchived, isDraft, isPublished, presetPlugins, runFieldHooks, runHooks };
|
|
1942
|
-
//# sourceMappingURL=index.js.map
|
|
1943
|
-
//# sourceMappingURL=index.js.map
|
|
42
|
+
<script defer data-domain="${r}" src="https://plausible.io/js/script.js"></script>
|
|
43
|
+
`),o}var Mr={facebook:{label:"Facebook"},twitter:{label:"Twitter / X"},instagram:{label:"Instagram"},linkedin:{label:"LinkedIn"},youtube:{label:"YouTube"},tiktok:{label:"TikTok"},pinterest:{label:"Pinterest"},discord:{label:"Discord"},twitch:{label:"Twitch"},github:{label:"GitHub"},mastodon:{label:"Mastodon"}},Fr=["facebook","twitter","instagram","linkedin","youtube","tiktok","pinterest","discord","twitch","github","mastodon"];function Oe(s){if(!s)return [];let e=s.showAll===true,t=[];for(let r of Fr){let o=s[r];if(!o||["tiktok","pinterest","discord","twitch","github","mastodon"].includes(r)&&!e)continue;let n=Mr[r];t.push({platform:r,url:o,label:n?.label||r});}return t}async function Br(s,e){try{let t=await s.findOne({collection:"_globals_social-settings",where:{},draft:e?.draft??!1});return Oe(t)}catch{return []}}function He(s){return s?{storeName:s.storeName,storeEmail:s.storeEmail,storePhone:s.storePhone,address:s.address,currency:s.currency,tax:s.tax,shipping:s.shipping,orders:s.orders}:{}}async function Nr(s,e){try{let t=await s.findOne({collection:"_globals_store-settings",where:{},draft:e?.draft??!1});return He(t)}catch{return {}}}function _e(s){return s?{testMode:s.testMode,provider:s.provider,stripe:s.stripe,paypal:s.paypal,square:s.square,methods:s.methods,bankTransfer:s.bankTransfer}:{}}async function Kr(s,e){try{let t=await s.findOne({collection:"_globals_payment-settings",where:{},draft:e?.draft??!1});return _e(t)}catch{return {}}}async function G(s={}){let{namespace:e="kyro",ttl:t}=s,r=createStorage({driver:$r({dbName:"kyro-cms",storeName:e})}),o=e?`${e}:`:"";return {storage:{getItem:async n=>{let a=`${o}${n}`,l=await r.getItem(a);if(l==null)return null;try{let c=JSON.parse(l);return c.expiry&&Date.now()>c.expiry?(await r.removeItem(a),null):c.value}catch{return l}},setItem:async(n,a)=>{let l=`${o}${n}`,c=JSON.stringify(t?{value:a,expiry:Date.now()+t}:{value:a});await r.setItem(l,c);},removeItem:async n=>{await r.removeItem(`${o}${n}`);}},cleanup:async()=>{await r.dispose?.();}}}function z(s){let e=s||process.env.APP_SECRET||process.env.AUTH_SECRET||"development-secret-key";return q$1.createHash("sha256").update(e).digest("hex")}function qr(s,e){let t=q$1.randomBytes(16),r=q$1.createCipheriv("aes-256-gcm",Buffer.from(e,"hex").subarray(0,32),t),o=r.update(s,"utf8","hex");o+=r.final("hex");let i=r.getAuthTag().toString("hex");return `${t.toString("hex")}:${i}:${o}`}function Qr(s,e){let[t,r,o]=s.split(":"),i=Buffer.from(t,"hex"),n=Buffer.from(r,"hex"),a=q$1.createDecipheriv("aes-256-gcm",Buffer.from(e,"hex").subarray(0,32),i);a.setAuthTag(n);let l=a.update(o,"hex","utf8");return l+=a.final("utf8"),l}async function Q(s={}){let{namespace:e="kyro",ttl:t,encryption:r=true,secret:o,basePath:i}=s,n=i?Me.join(i,".astro","kyro.json"):Me.join(process.cwd(),".astro","kyro.json"),a=createStorage({driver:zr({base:n})}),l=r?z(o):null,c=e?`${e}:`:"";return {storage:{getItem:async g=>{let d=`${c}${g}`,u=await a.getItem(d);if(u==null)return null;try{let y=l?Qr(u,l):u,f=JSON.parse(y);return f.expiry&&Date.now()>f.expiry?(await a.removeItem(d),null):f.value}catch{return u}},setItem:async(g,d)=>{let u=`${c}${g}`,y=JSON.stringify(t?{value:d,expiry:Date.now()+t}:{value:d});await a.setItem(u,l?qr(y,l):y);},removeItem:async g=>{await a.removeItem(`${c}${g}`);}},cleanup:async()=>{await a.dispose?.();}}}async function Y(s){let{environment:e,adapter:t,connectionString:r,encryption:o}=s,i=process.env.APP_SECRET||process.env.AUTH_SECRET,n={enabled:o?.enabled??true,algorithm:o?.algorithm};return e==="browser"?G({namespace:"kyro"}):Q({namespace:"kyro",encryption:n.enabled,secret:i})}async function Fe(s){let e={...s,encryption:{enabled:true,algorithm:s.encryption?.algorithm}};return Y(e)}l();export{x as AccountLockout,E as AnalyticsPlugin,H as Auth,U as CommentsPlugin,C as InMemoryAccountLockout,P as InMemoryAuthAdapter,b as KyroPlugin,$ as PluginManager,S as RateLimiter,V as ReviewsPlugin,L as SEOPLugin,M as VersionManager,O as WishlistPlugin,Ue as authConfig,Rr as createAuth,W as createAuthConfig,Fe as createAuthStorage,Y as createStorage,Dr as createVersionManager,Ve as defineConfig,Or as defineKyroConfig,_r as generateAnalyticsTags,Hr as generateSeoTags,_ as getDefaultDraftPublishConfig,Kr as getPaymentConfig,_e as getPaymentConfigFromSettings,Br as getSocialLinks,Oe as getSocialLinksFromSettings,Nr as getStoreConfig,He as getStoreConfigFromSettings,Er as isArchived,Lr as isDraft,Ir as isPublished,Ar as presetPlugins,br as runFieldHooks,Ie as runHooks};
|