@kyro-cms/core 0.5.5 → 0.7.0
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.cjs +75 -35
- package/dist/api-handler.cjs.map +1 -1
- package/dist/api-handler.d.cts +2 -5
- package/dist/api-handler.d.ts +2 -5
- package/dist/api-handler.js +75 -36
- package/dist/api-handler.js.map +1 -1
- package/dist/bootstrap-AKAUP6F6.cjs +32 -0
- package/dist/{bootstrap-EE6BJZWL.cjs.map → bootstrap-AKAUP6F6.cjs.map} +1 -1
- package/dist/bootstrap-JCML6NFO.js +7 -0
- package/dist/{bootstrap-4MH44YKG.js.map → bootstrap-JCML6NFO.js.map} +1 -1
- package/dist/{chunk-WVPOPOEQ.cjs → chunk-2KVHZE6O.cjs} +286 -126
- package/dist/chunk-2KVHZE6O.cjs.map +1 -0
- package/dist/{chunk-RALQO47U.cjs → chunk-2OL4O2TH.cjs} +55 -2
- package/dist/chunk-2OL4O2TH.cjs.map +1 -0
- package/dist/{chunk-XU7AFF6V.js → chunk-35U3FROB.js} +982 -4
- package/dist/chunk-35U3FROB.js.map +1 -0
- package/dist/{chunk-WSCJQI2B.js → chunk-3J4MFTI3.js} +27 -11
- package/dist/chunk-3J4MFTI3.js.map +1 -0
- package/dist/chunk-3ZFYL34R.js +391 -0
- package/dist/chunk-3ZFYL34R.js.map +1 -0
- package/dist/chunk-4DA7QPLA.cjs +356 -0
- package/dist/chunk-4DA7QPLA.cjs.map +1 -0
- package/dist/{chunk-TP5YQFIX.js → chunk-57P6MJKC.js} +3 -715
- package/dist/chunk-57P6MJKC.js.map +1 -0
- package/dist/{chunk-R2YHJN6W.cjs → chunk-5KVM3WEY.cjs} +34 -208
- package/dist/chunk-5KVM3WEY.cjs.map +1 -0
- package/dist/{chunk-Z2OVHWHB.cjs → chunk-6IMPH6WV.cjs} +28 -11
- package/dist/chunk-6IMPH6WV.cjs.map +1 -0
- package/dist/{chunk-QKVA2SOG.js → chunk-DXHRBMGB.js} +27 -284
- package/dist/chunk-DXHRBMGB.js.map +1 -0
- package/dist/{chunk-E3BZLMX6.js → chunk-ES5HNFFT.js} +43 -2
- package/dist/chunk-ES5HNFFT.js.map +1 -0
- package/dist/{chunk-QYZKIPSD.js → chunk-FXYP2HA6.js} +34 -3
- package/dist/chunk-FXYP2HA6.js.map +1 -0
- package/dist/chunk-H727JIG7.js +809 -0
- package/dist/chunk-H727JIG7.js.map +1 -0
- package/dist/{chunk-AM4JKIPP.js → chunk-HXRD4B37.js} +9 -183
- package/dist/chunk-HXRD4B37.js.map +1 -0
- package/dist/chunk-I7HHI6QV.cjs +816 -0
- package/dist/chunk-I7HHI6QV.cjs.map +1 -0
- package/dist/{chunk-RDRJVCL5.cjs → chunk-IA6AU5PI.cjs} +2 -720
- package/dist/chunk-IA6AU5PI.cjs.map +1 -0
- package/dist/{chunk-55BNRTLW.cjs → chunk-LINKCEG4.cjs} +985 -4
- package/dist/chunk-LINKCEG4.cjs.map +1 -0
- package/dist/{chunk-TVVYZ2TH.js → chunk-OHVB4AJ7.js} +56 -3
- package/dist/chunk-OHVB4AJ7.js.map +1 -0
- package/dist/{chunk-XAEBVZTI.cjs → chunk-PDYFVNUX.cjs} +26 -289
- package/dist/chunk-PDYFVNUX.cjs.map +1 -0
- package/dist/{chunk-6WXQRYTW.js → chunk-QPPDLRNR.js} +286 -126
- package/dist/chunk-QPPDLRNR.js.map +1 -0
- package/dist/{chunk-WBCIEYHC.cjs → chunk-QUW2RZTM.cjs} +35 -4
- package/dist/chunk-QUW2RZTM.cjs.map +1 -0
- package/dist/chunk-SA7NSSIQ.cjs +397 -0
- package/dist/chunk-SA7NSSIQ.cjs.map +1 -0
- package/dist/{chunk-H4XCAPA6.cjs → chunk-V3LKPM3O.cjs} +43 -2
- package/dist/chunk-V3LKPM3O.cjs.map +1 -0
- package/dist/chunk-Y3N7UUDO.js +349 -0
- package/dist/chunk-Y3N7UUDO.js.map +1 -0
- package/dist/{chunk-S3FG2NY7.js → chunk-Y3QQN7PN.js} +4 -3
- package/dist/chunk-Y3QQN7PN.js.map +1 -0
- package/dist/{chunk-5HA5OMFH.cjs → chunk-YVUJBEXE.cjs} +7 -6
- package/dist/chunk-YVUJBEXE.cjs.map +1 -0
- package/dist/cli/index.cjs +103 -20
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +103 -20
- package/dist/cli/index.js.map +1 -1
- package/dist/client.d.cts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/drizzle/index.cjs +12 -12
- package/dist/drizzle/index.d.cts +23 -2
- package/dist/drizzle/index.d.ts +23 -2
- package/dist/drizzle/index.js +3 -3
- package/dist/index.cjs +174 -1054
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +85 -7
- package/dist/index.d.ts +85 -7
- package/dist/index.js +91 -980
- package/dist/index.js.map +1 -1
- package/dist/integration.cjs +2 -2
- package/dist/integration.d.cts +3 -16
- package/dist/integration.d.ts +3 -16
- package/dist/integration.js +1 -1
- package/dist/mongo-auth-adapter-NHHUJHVH.cjs +17 -0
- package/dist/mongo-auth-adapter-NHHUJHVH.cjs.map +1 -0
- package/dist/mongo-auth-adapter-NJQUUCTP.js +4 -0
- package/dist/mongo-auth-adapter-NJQUUCTP.js.map +1 -0
- package/dist/mongodb/index.cjs +9 -8
- package/dist/mongodb/index.d.cts +86 -5
- package/dist/mongodb/index.d.ts +86 -5
- package/dist/mongodb/index.js +3 -2
- package/dist/postgres-auth-adapter-3T2NKTSE.js +5 -0
- package/dist/{postgres-auth-adapter-B65BULNS.js.map → postgres-auth-adapter-3T2NKTSE.js.map} +1 -1
- package/dist/postgres-auth-adapter-7IEENCKQ.cjs +14 -0
- package/dist/{postgres-auth-adapter-6742WDCF.cjs.map → postgres-auth-adapter-7IEENCKQ.cjs.map} +1 -1
- package/dist/redis-adapter-D2E2S3GB.cjs +13 -0
- package/dist/{redis-adapter-LPUWLE4Y.cjs.map → redis-adapter-D2E2S3GB.cjs.map} +1 -1
- package/dist/redis-adapter-VQXD7ESY.js +4 -0
- package/dist/{redis-adapter-THYDCGQR.js.map → redis-adapter-VQXD7ESY.js.map} +1 -1
- package/dist/rest/index.cjs +10 -8
- package/dist/rest/index.js +8 -6
- package/dist/sqlite-adapter-LVK5PS4T.cjs +13 -0
- package/dist/sqlite-adapter-LVK5PS4T.cjs.map +1 -0
- package/dist/sqlite-adapter-TR3U3W6Q.js +4 -0
- package/dist/sqlite-adapter-TR3U3W6Q.js.map +1 -0
- package/dist/templates/index.cjs +31 -27
- package/dist/templates/index.d.cts +8 -5
- package/dist/templates/index.d.ts +8 -5
- package/dist/templates/index.js +1 -1
- package/dist/{base-eVegJ_Pr.d.ts → tenant-B1YB0Jy8.d.ts} +10 -1
- package/dist/{base-DvvNqnM-.d.cts → tenant-Cpeveji6.d.cts} +10 -1
- package/dist/{types-DqN4ckOC.d.cts → types-D6ZLRGbH.d.cts} +19 -1
- package/dist/{types-DqN4ckOC.d.ts → types-D6ZLRGbH.d.ts} +19 -1
- package/package.json +56 -9
- package/dist/adapter-BSvBudTG.d.cts +0 -65
- package/dist/adapter-CXGB2Elb.d.ts +0 -65
- package/dist/bootstrap-4MH44YKG.js +0 -6
- package/dist/bootstrap-EE6BJZWL.cjs +0 -31
- package/dist/chunk-55BNRTLW.cjs.map +0 -1
- package/dist/chunk-5HA5OMFH.cjs.map +0 -1
- package/dist/chunk-6WXQRYTW.js.map +0 -1
- package/dist/chunk-A4USRVTQ.js +0 -115
- package/dist/chunk-A4USRVTQ.js.map +0 -1
- package/dist/chunk-AM4JKIPP.js.map +0 -1
- package/dist/chunk-E3BZLMX6.js.map +0 -1
- package/dist/chunk-H4XCAPA6.cjs.map +0 -1
- package/dist/chunk-KOCTZKPV.cjs +0 -117
- package/dist/chunk-KOCTZKPV.cjs.map +0 -1
- package/dist/chunk-QKVA2SOG.js.map +0 -1
- package/dist/chunk-QYZKIPSD.js.map +0 -1
- package/dist/chunk-R2YHJN6W.cjs.map +0 -1
- package/dist/chunk-RALQO47U.cjs.map +0 -1
- package/dist/chunk-RDRJVCL5.cjs.map +0 -1
- package/dist/chunk-S3FG2NY7.js.map +0 -1
- package/dist/chunk-TP5YQFIX.js.map +0 -1
- package/dist/chunk-TVVYZ2TH.js.map +0 -1
- package/dist/chunk-WBCIEYHC.cjs.map +0 -1
- package/dist/chunk-WSCJQI2B.js.map +0 -1
- package/dist/chunk-WVPOPOEQ.cjs.map +0 -1
- package/dist/chunk-XAEBVZTI.cjs.map +0 -1
- package/dist/chunk-XU7AFF6V.js.map +0 -1
- package/dist/chunk-Z2OVHWHB.cjs.map +0 -1
- package/dist/postgres-auth-adapter-6742WDCF.cjs +0 -14
- package/dist/postgres-auth-adapter-B65BULNS.js +0 -5
- package/dist/redis-adapter-LPUWLE4Y.cjs +0 -13
- package/dist/redis-adapter-THYDCGQR.js +0 -4
package/dist/index.js
CHANGED
|
@@ -1,33 +1,34 @@
|
|
|
1
|
-
export { RedisAuthAdapter } from './chunk-
|
|
2
|
-
export { allSettingsGlobals, blogCollections, blogGlobals, coreSettingsGlobals, createTemplateConfig, ecommerceCollections, ecommerceGlobals,
|
|
3
|
-
export { kyro } from './chunk-
|
|
4
|
-
export { ConfigValidationError, Kyro, Registry, collectionToCreateZod, collectionToUpdateZod, collectionToWhereZod, collectionToZod, createKyro, createRegistry, fieldToZod, getRegistry, globalToZod, resetRegistry, validateCollection, validateConfig, validateFields, validateGlobal } from './chunk-
|
|
5
|
-
export { autoBootstrap, bootstrapAdmin, getBootstrapFromEnv } from './chunk-
|
|
1
|
+
export { RedisAuthAdapter } from './chunk-ES5HNFFT.js';
|
|
2
|
+
export { allSettingsGlobals, blogCollections, blogGlobals, coreSettingsGlobals, createTemplateConfig, ecommerceCollections, ecommerceGlobals, kitchenSinkCollections, mediaCollections, minimalCollections } from './chunk-3J4MFTI3.js';
|
|
3
|
+
export { kyro } from './chunk-FXYP2HA6.js';
|
|
4
|
+
export { ConfigValidationError, Kyro, LocalAdapter, Registry, collectionToCreateZod, collectionToUpdateZod, collectionToWhereZod, collectionToZod, createKyro, createLocalAdapter, createRegistry, fieldToZod, getRegistry, globalToZod, resetRegistry, validateCollection, validateConfig, validateFields, validateGlobal } from './chunk-35U3FROB.js';
|
|
5
|
+
export { autoBootstrap, bootstrapAdmin, bootstrapWithRetry, getBootstrapFromEnv } from './chunk-Y3QQN7PN.js';
|
|
6
6
|
export { CSSGenerator, createAdminStyling, defaultDarkTheme, defaultFieldStyling, defaultLightTheme, ecommerce2026Theme, generateCSSVariables, generateTailwindConfig } from './chunk-2HFJUUFZ.js';
|
|
7
7
|
export { ALL_FIELD_TYPES, COMPLEX_FIELD_TYPES, LAYOUT_FIELD_TYPES, PRIMITIVE_FIELD_TYPES, RELATIONAL_FIELD_TYPES, createColumnsNode, isArrayField, isBlocksField, isGroupField, isImageField, isLayoutField, isNumberField, isRelationshipField, isRichTextField, isSelectField, isTextField, isUploadField, normalizeRichTextDocument, normalizeRichTextValue, renderRichText, richTextStyles } from './chunk-Q23JB3KL.js';
|
|
8
8
|
export { createContext, createCountProcedure, createCreateProcedure, createDeleteProcedure, createDynamicRouter, createFindByIDProcedure, createFindProcedure, createKyroServer, createUpdateProcedure } from './chunk-3AJE4SEG.js';
|
|
9
|
-
import { init_secret, AuditLogger, InMemoryRateLimiter, InMemoryAuditLogger, AuthRoutes } from './chunk-
|
|
10
|
-
export { AuditLogger, InMemoryAuditLogger, InMemoryRateLimiter, MediaService, createAuditContext, createHonoApp, createLocalStorage, createRESTAPI, getAppSecret, getEncryptionKey, getSessionConfig, loadSecrets, resolveProvider, setDbAdapter } from './chunk-
|
|
11
|
-
import { EmailTransport, PasswordPolicy
|
|
12
|
-
export { ConfigService, EmailTransport, PasswordPolicy
|
|
9
|
+
import { init_secret, AuditLogger, InMemoryRateLimiter, InMemoryAuditLogger, AuthRoutes } from './chunk-HXRD4B37.js';
|
|
10
|
+
export { AuditLogger, InMemoryAuditLogger, InMemoryRateLimiter, MediaService, createAuditContext, createHonoApp, createLocalStorage, createRESTAPI, getAppSecret, getEncryptionKey, getSessionConfig, loadSecrets, resolveProvider, setDbAdapter } from './chunk-HXRD4B37.js';
|
|
11
|
+
import { EmailTransport, PasswordPolicy } from './chunk-57P6MJKC.js';
|
|
12
|
+
export { ConfigService, EmailTransport, PasswordPolicy } from './chunk-57P6MJKC.js';
|
|
13
13
|
import './chunk-YT7HXXVN.js';
|
|
14
|
+
import { SQLiteAuthAdapter } from './chunk-H727JIG7.js';
|
|
15
|
+
export { SQLiteAuthAdapter } from './chunk-H727JIG7.js';
|
|
14
16
|
import './chunk-P2YW545G.js';
|
|
15
17
|
export { ALL_WEBHOOK_EVENTS, WEBHOOK_COLLECTION, WEBHOOK_DELIVERY_COLLECTION, WEBHOOK_EVENTS, WebhookService, buildDeliveryRecord, createTestPayload, createWebhookService, deliverWebhook, deliverWithRetry, generateWebhookSecret, signPayload } from './chunk-QXIQWPAP.js';
|
|
16
18
|
export { buildGraphQLSchema, createGraphQLSchema } from './chunk-REK7AYOC.js';
|
|
17
19
|
export { evaluateAccess, getWhereClause, mergeWhereClauses } from './chunk-SDMNUYVU.js';
|
|
18
20
|
export { KyroPubSub, KyroWSServer, PubSub, createWSServer } from './chunk-3TPQ2BU6.js';
|
|
19
21
|
import './chunk-QU2RFFH4.js';
|
|
20
|
-
export { DrizzleAdapter, collectionToDrizzleSchema, createDatabase, createDrizzleAdapter, fieldToDrizzleType, runMigrations, seedDefaultRoles } from './chunk-
|
|
21
|
-
import { PostgresAuthAdapter } from './chunk-
|
|
22
|
-
export { PostgresAuthAdapter } from './chunk-
|
|
22
|
+
export { DrizzleAdapter, collectionToDrizzleSchema, createDatabase, createDrizzleAdapter, fieldToDrizzleType, runMigrations, seedDefaultRoles } from './chunk-QPPDLRNR.js';
|
|
23
|
+
import { PostgresAuthAdapter } from './chunk-OHVB4AJ7.js';
|
|
24
|
+
export { PostgresAuthAdapter } from './chunk-OHVB4AJ7.js';
|
|
23
25
|
import './chunk-WOWUL7ZY.js';
|
|
24
26
|
import './chunk-GTGRLD4Y.js';
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
export { AbstractBaseAdapter } from './chunk-
|
|
27
|
+
export { MongoDBAdapter, createMongoDBAdapter } from './chunk-DXHRBMGB.js';
|
|
28
|
+
import { MongoDBAuthAdapter } from './chunk-Y3N7UUDO.js';
|
|
29
|
+
export { MongoDBAuthAdapter } from './chunk-Y3N7UUDO.js';
|
|
30
|
+
export { AbstractBaseAdapter } from './chunk-3ZFYL34R.js';
|
|
29
31
|
import './chunk-Z6ZWNWWR.js';
|
|
30
|
-
import { createRequire } from 'module';
|
|
31
32
|
import crypto2, { randomBytes } from 'crypto';
|
|
32
33
|
import { readFileSync } from 'fs';
|
|
33
34
|
import path, { join, resolve } from 'path';
|
|
@@ -53,967 +54,6 @@ async function runHooks(hooks, args) {
|
|
|
53
54
|
async function runFieldHooks(hooks, args) {
|
|
54
55
|
return runHooks(hooks, args);
|
|
55
56
|
}
|
|
56
|
-
var _require = createRequire(import.meta.url);
|
|
57
|
-
var modPath = "node:sqlite";
|
|
58
|
-
var { DatabaseSync } = _require(modPath);
|
|
59
|
-
function flattenFields(fields) {
|
|
60
|
-
const result = [];
|
|
61
|
-
for (const field of fields) {
|
|
62
|
-
if (field.type === "tabs" && "tabs" in field) {
|
|
63
|
-
for (const tab of field.tabs) {
|
|
64
|
-
result.push(...flattenFields(tab.fields));
|
|
65
|
-
}
|
|
66
|
-
} else if (field.type === "row" && "fields" in field) {
|
|
67
|
-
result.push(...flattenFields(field.fields));
|
|
68
|
-
} else if (field.type === "collapsible" && "fields" in field) {
|
|
69
|
-
result.push(...flattenFields(field.fields));
|
|
70
|
-
} else {
|
|
71
|
-
result.push(field);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return result;
|
|
75
|
-
}
|
|
76
|
-
function processFieldValue(row, field) {
|
|
77
|
-
const f = field;
|
|
78
|
-
let value = row[f.name];
|
|
79
|
-
if (f.type === "json" || f.type === "richtext" || f.type === "array" || f.type === "group" || f.type === "blocks" || f.type === "list" || f.type === "relationship-block") {
|
|
80
|
-
try {
|
|
81
|
-
value = value ? JSON.parse(value) : null;
|
|
82
|
-
} catch {
|
|
83
|
-
value = null;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
if (f.type === "checkbox") {
|
|
87
|
-
value = Boolean(value);
|
|
88
|
-
}
|
|
89
|
-
if (f.type === "date" && value) {
|
|
90
|
-
try {
|
|
91
|
-
const d = new Date(value);
|
|
92
|
-
if (isNaN(d.getTime())) {
|
|
93
|
-
value = null;
|
|
94
|
-
} else {
|
|
95
|
-
value = d.toISOString();
|
|
96
|
-
}
|
|
97
|
-
} catch {
|
|
98
|
-
value = null;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
if ((f.type === "upload" || f.type === "image") && value) {
|
|
102
|
-
try {
|
|
103
|
-
const parsed = JSON.parse(value);
|
|
104
|
-
if (Array.isArray(parsed)) {
|
|
105
|
-
value = parsed.map((item) => {
|
|
106
|
-
if (typeof item === "object" && item !== null) {
|
|
107
|
-
return item;
|
|
108
|
-
}
|
|
109
|
-
return { id: item };
|
|
110
|
-
});
|
|
111
|
-
} else {
|
|
112
|
-
value = typeof parsed === "object" ? parsed : { id: parsed };
|
|
113
|
-
}
|
|
114
|
-
} catch {
|
|
115
|
-
value = { id: value };
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
if (f.type === "relationship" && value) {
|
|
119
|
-
try {
|
|
120
|
-
const parsed = JSON.parse(value);
|
|
121
|
-
if (Array.isArray(parsed)) {
|
|
122
|
-
value = parsed;
|
|
123
|
-
} else {
|
|
124
|
-
value = parsed;
|
|
125
|
-
}
|
|
126
|
-
} catch {
|
|
127
|
-
value = { relationTo: Array.isArray(f.relationTo) ? f.relationTo[0] : f.relationTo, value };
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
if (f.type === "list" && value) {
|
|
131
|
-
try {
|
|
132
|
-
const parsed = JSON.parse(value);
|
|
133
|
-
value = Array.isArray(parsed) ? parsed : [];
|
|
134
|
-
} catch {
|
|
135
|
-
value = [];
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
if (f.type === "relationship-block" && value) {
|
|
139
|
-
try {
|
|
140
|
-
const parsed = JSON.parse(value);
|
|
141
|
-
value = Array.isArray(parsed) ? parsed : [];
|
|
142
|
-
} catch {
|
|
143
|
-
value = [];
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
return value;
|
|
147
|
-
}
|
|
148
|
-
function getTableColumns(db, tableName) {
|
|
149
|
-
try {
|
|
150
|
-
const rows = db.prepare(`PRAGMA table_info(${tableName})`).all();
|
|
151
|
-
return rows.map((r) => r.name);
|
|
152
|
-
} catch {
|
|
153
|
-
return [];
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
var LocalAdapter = class extends AbstractBaseAdapter {
|
|
157
|
-
db;
|
|
158
|
-
path;
|
|
159
|
-
migrations = /* @__PURE__ */ new Map();
|
|
160
|
-
draftsTableName = "kyro_drafts";
|
|
161
|
-
versionsTableName = "kyro_versions";
|
|
162
|
-
constructor(options) {
|
|
163
|
-
super();
|
|
164
|
-
this.path = options.path;
|
|
165
|
-
if (options.db) {
|
|
166
|
-
this.db = options.db;
|
|
167
|
-
} else {
|
|
168
|
-
this.db = null;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
async connect() {
|
|
172
|
-
if (!this.db) {
|
|
173
|
-
this.db = new DatabaseSync(this.path || ":memory:");
|
|
174
|
-
}
|
|
175
|
-
this.db.exec("PRAGMA journal_mode = WAL");
|
|
176
|
-
this.db.exec("PRAGMA foreign_keys = ON");
|
|
177
|
-
this.connected = true;
|
|
178
|
-
console.log(
|
|
179
|
-
`[LocalAdapter] Connected to SQLite (${this.path || "memory"})`
|
|
180
|
-
);
|
|
181
|
-
}
|
|
182
|
-
async disconnect() {
|
|
183
|
-
if (this.db) {
|
|
184
|
-
this.db.close();
|
|
185
|
-
}
|
|
186
|
-
this.connected = false;
|
|
187
|
-
console.log("[LocalAdapter] Disconnected");
|
|
188
|
-
}
|
|
189
|
-
// ========================================================================
|
|
190
|
-
// Schema Management
|
|
191
|
-
// ========================================================================
|
|
192
|
-
ensureTable(config, tableName) {
|
|
193
|
-
const name = tableName || this.getTableNameFor(config.slug);
|
|
194
|
-
const columns = [`id TEXT PRIMARY KEY`];
|
|
195
|
-
for (const field of flattenFields(config.fields)) {
|
|
196
|
-
if (!field.name || field.name === "id") continue;
|
|
197
|
-
const colDef = this.fieldToSQL(field);
|
|
198
|
-
if (colDef) columns.push(colDef);
|
|
199
|
-
}
|
|
200
|
-
columns.push(`${this.col("createdAt")} TEXT DEFAULT (datetime('now'))`);
|
|
201
|
-
columns.push(`${this.col("updatedAt")} TEXT DEFAULT (datetime('now'))`);
|
|
202
|
-
columns.push(`_status TEXT DEFAULT 'published'`);
|
|
203
|
-
columns.push(`_has_draft INTEGER DEFAULT 0`);
|
|
204
|
-
if (config.tenantScoped) {
|
|
205
|
-
columns.push(`tenant_id TEXT NOT NULL`);
|
|
206
|
-
}
|
|
207
|
-
const existingColumns = getTableColumns(this.db, name);
|
|
208
|
-
if (existingColumns.length === 0) {
|
|
209
|
-
const createSQL = `CREATE TABLE IF NOT EXISTS ${name} (${columns.join(", ")})`;
|
|
210
|
-
this.db.exec(createSQL);
|
|
211
|
-
this.db.exec(`CREATE INDEX IF NOT EXISTS idx_${name}__status ON ${name}(_status)`);
|
|
212
|
-
for (const field of flattenFields(config.fields)) {
|
|
213
|
-
if (field.name && field.indexed) {
|
|
214
|
-
this.db.exec(
|
|
215
|
-
`CREATE INDEX IF NOT EXISTS idx_${name}_${field.name} ON ${name}(${this.col(field.name)})`
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
if (field.name && field.unique) {
|
|
219
|
-
this.db.exec(
|
|
220
|
-
`CREATE UNIQUE INDEX IF NOT EXISTS idx_${name}_${field.name}_unique ON ${name}(${this.col(field.name)})`
|
|
221
|
-
);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
} else {
|
|
225
|
-
const existingSet = new Set(existingColumns);
|
|
226
|
-
for (const colDef of columns) {
|
|
227
|
-
const colName = colDef.split(" ")[0].replace(/^"/, "").replace(/"$/, "");
|
|
228
|
-
if (!existingSet.has(colName) && colName !== "id") {
|
|
229
|
-
try {
|
|
230
|
-
if (colName === "_status") {
|
|
231
|
-
this.db.exec(`ALTER TABLE ${name} ADD COLUMN ${this.col(colName)} TEXT DEFAULT 'published'`);
|
|
232
|
-
} else if (colName === "_has_draft") {
|
|
233
|
-
this.db.exec(`ALTER TABLE ${name} ADD COLUMN ${this.col(colName)} INTEGER DEFAULT 0`);
|
|
234
|
-
} else {
|
|
235
|
-
this.db.exec(`ALTER TABLE ${name} ADD COLUMN ${this.col(colName)} TEXT`);
|
|
236
|
-
}
|
|
237
|
-
} catch {
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
this.migrations.set(name, true);
|
|
243
|
-
}
|
|
244
|
-
ensureVersionsTable() {
|
|
245
|
-
this.db.exec(`
|
|
246
|
-
CREATE TABLE IF NOT EXISTS ${this.versionsTableName} (
|
|
247
|
-
id TEXT PRIMARY KEY,
|
|
248
|
-
collection_slug TEXT NOT NULL,
|
|
249
|
-
document_id TEXT NOT NULL,
|
|
250
|
-
tenant_id TEXT,
|
|
251
|
-
version INTEGER NOT NULL,
|
|
252
|
-
status TEXT NOT NULL DEFAULT 'draft',
|
|
253
|
-
data TEXT NOT NULL,
|
|
254
|
-
created_by TEXT,
|
|
255
|
-
change_description TEXT,
|
|
256
|
-
published_at TEXT,
|
|
257
|
-
created_at TEXT DEFAULT (datetime('now')),
|
|
258
|
-
updated_at TEXT DEFAULT (datetime('now'))
|
|
259
|
-
)
|
|
260
|
-
`);
|
|
261
|
-
this.db.exec(
|
|
262
|
-
`CREATE INDEX IF NOT EXISTS idx_${this.versionsTableName}_doc ON ${this.versionsTableName}(collection_slug, document_id)`
|
|
263
|
-
);
|
|
264
|
-
this.db.exec(
|
|
265
|
-
`CREATE INDEX IF NOT EXISTS idx_${this.versionsTableName}_status ON ${this.versionsTableName}(status)`
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
ensureDraftsTable() {
|
|
269
|
-
this.db.exec(`
|
|
270
|
-
CREATE TABLE IF NOT EXISTS ${this.draftsTableName} (
|
|
271
|
-
id TEXT PRIMARY KEY,
|
|
272
|
-
collection_slug TEXT NOT NULL,
|
|
273
|
-
document_id TEXT NOT NULL,
|
|
274
|
-
tenant_id TEXT,
|
|
275
|
-
data TEXT NOT NULL,
|
|
276
|
-
base_updated_at TEXT,
|
|
277
|
-
draft_updated_at TEXT NOT NULL,
|
|
278
|
-
created_at TEXT DEFAULT (datetime('now')),
|
|
279
|
-
updated_at TEXT DEFAULT (datetime('now'))
|
|
280
|
-
)
|
|
281
|
-
`);
|
|
282
|
-
this.db.exec(
|
|
283
|
-
`CREATE UNIQUE INDEX IF NOT EXISTS idx_${this.draftsTableName}_document ON ${this.draftsTableName}(collection_slug, document_id, tenant_id)`
|
|
284
|
-
);
|
|
285
|
-
}
|
|
286
|
-
// ========================================================================
|
|
287
|
-
// SQL Quoting
|
|
288
|
-
// ========================================================================
|
|
289
|
-
col(name) {
|
|
290
|
-
return `"${name}"`;
|
|
291
|
-
}
|
|
292
|
-
fieldToSQL(field) {
|
|
293
|
-
switch (field.type) {
|
|
294
|
-
case "text":
|
|
295
|
-
case "email":
|
|
296
|
-
case "password":
|
|
297
|
-
case "textarea":
|
|
298
|
-
case "color":
|
|
299
|
-
case "code":
|
|
300
|
-
case "markdown":
|
|
301
|
-
case "url":
|
|
302
|
-
return this.col(field.name) + " TEXT";
|
|
303
|
-
case "number":
|
|
304
|
-
return this.col(field.name) + " REAL";
|
|
305
|
-
case "checkbox":
|
|
306
|
-
return this.col(field.name) + " INTEGER DEFAULT 0";
|
|
307
|
-
case "date":
|
|
308
|
-
return this.col(field.name) + " TEXT";
|
|
309
|
-
case "select":
|
|
310
|
-
case "radio":
|
|
311
|
-
return this.col(field.name) + " TEXT";
|
|
312
|
-
case "relationship":
|
|
313
|
-
case "upload":
|
|
314
|
-
return this.col(field.name) + " TEXT";
|
|
315
|
-
case "json":
|
|
316
|
-
case "richtext":
|
|
317
|
-
case "array":
|
|
318
|
-
case "group":
|
|
319
|
-
case "blocks":
|
|
320
|
-
return this.col(field.name) + " TEXT";
|
|
321
|
-
default:
|
|
322
|
-
return null;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
// ========================================================================
|
|
326
|
-
// CRUD Operations
|
|
327
|
-
// ========================================================================
|
|
328
|
-
parseGlobalsSlug(slug) {
|
|
329
|
-
if (slug.startsWith("_globals_")) {
|
|
330
|
-
const globalSlug = slug.replace("_globals_", "");
|
|
331
|
-
return {
|
|
332
|
-
isGlobal: true,
|
|
333
|
-
globalSlug,
|
|
334
|
-
tableName: `global_${globalSlug.replace(/-/g, "_")}`
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
return {
|
|
338
|
-
isGlobal: false,
|
|
339
|
-
globalSlug: "",
|
|
340
|
-
tableName: this.getTableNameFor(slug)
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
async find(args) {
|
|
344
|
-
const {
|
|
345
|
-
collection: slug,
|
|
346
|
-
where = {},
|
|
347
|
-
sort,
|
|
348
|
-
limit = 10,
|
|
349
|
-
page = 1,
|
|
350
|
-
tenantID,
|
|
351
|
-
draft = false
|
|
352
|
-
} = args;
|
|
353
|
-
const parsed = this.parseGlobalsSlug(slug);
|
|
354
|
-
const config = parsed.isGlobal ? this.globals.get(parsed.globalSlug) : this.getCollection(slug);
|
|
355
|
-
this.ensureTable(config, parsed.tableName);
|
|
356
|
-
const tableName = parsed.tableName;
|
|
357
|
-
let sql = `SELECT * FROM ${tableName}`;
|
|
358
|
-
const params = [];
|
|
359
|
-
const conditions = [];
|
|
360
|
-
if (!draft && config.versions?.drafts) {
|
|
361
|
-
conditions.push(`_status = ?`);
|
|
362
|
-
params.push("published");
|
|
363
|
-
}
|
|
364
|
-
if (tenantID && config.tenantScoped) {
|
|
365
|
-
conditions.push(`tenant_id = ?`);
|
|
366
|
-
params.push(tenantID);
|
|
367
|
-
}
|
|
368
|
-
for (const [key, value] of Object.entries(where)) {
|
|
369
|
-
if (key === "AND" || key === "OR") continue;
|
|
370
|
-
if (typeof value === "object" && value !== null) {
|
|
371
|
-
if (value.equals !== void 0) {
|
|
372
|
-
conditions.push(`${this.col(key)} = ?`);
|
|
373
|
-
params.push(value.equals);
|
|
374
|
-
}
|
|
375
|
-
if (value.in !== void 0) {
|
|
376
|
-
conditions.push(`${this.col(key)} IN (${value.in.map(() => "?").join(", ")})`);
|
|
377
|
-
params.push(...value.in);
|
|
378
|
-
}
|
|
379
|
-
if (value.not_equals !== void 0) {
|
|
380
|
-
conditions.push(`${this.col(key)} != ?`);
|
|
381
|
-
params.push(value.not_equals);
|
|
382
|
-
}
|
|
383
|
-
} else {
|
|
384
|
-
conditions.push(`${this.col(key)} = ?`);
|
|
385
|
-
params.push(value);
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
if (conditions.length > 0) {
|
|
389
|
-
sql += ` WHERE ${conditions.join(" AND ")}`;
|
|
390
|
-
}
|
|
391
|
-
const sortField = this.col(sort?.replace("-", "") || "createdAt");
|
|
392
|
-
const sortDir = sort?.startsWith("-") ? "DESC" : "ASC";
|
|
393
|
-
sql += ` ORDER BY ${sortField} ${sortDir}`;
|
|
394
|
-
const countSql = sql.replace("SELECT *", "SELECT COUNT(*) as count");
|
|
395
|
-
const countResult = this.db.prepare(countSql).get(...params);
|
|
396
|
-
const totalDocs = countResult?.count || 0;
|
|
397
|
-
sql += ` LIMIT ? OFFSET ?`;
|
|
398
|
-
params.push(limit, (page - 1) * limit);
|
|
399
|
-
const rows = this.db.prepare(sql).all(...params);
|
|
400
|
-
let docs = rows.map((row) => this.rowToDoc(row, config));
|
|
401
|
-
if (draft) {
|
|
402
|
-
docs = await Promise.all(docs.map(async (doc) => {
|
|
403
|
-
if (doc._has_draft) {
|
|
404
|
-
const versions = await this.findVersions({
|
|
405
|
-
collection: slug,
|
|
406
|
-
documentId: doc.id,
|
|
407
|
-
limit: 1,
|
|
408
|
-
sort: "-createdAt"
|
|
409
|
-
});
|
|
410
|
-
if (versions.docs.length > 0 && versions.docs[0].status === "draft") {
|
|
411
|
-
return { ...doc, ...versions.docs[0].data, _has_draft: true, _status: doc._status };
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
return doc;
|
|
415
|
-
}));
|
|
416
|
-
}
|
|
417
|
-
return {
|
|
418
|
-
docs,
|
|
419
|
-
totalDocs,
|
|
420
|
-
limit,
|
|
421
|
-
totalPages: Math.ceil(totalDocs / limit),
|
|
422
|
-
page,
|
|
423
|
-
pagingCounter: (page - 1) * limit + 1,
|
|
424
|
-
hasPrevPage: page > 1,
|
|
425
|
-
hasNextPage: page < Math.ceil(totalDocs / limit),
|
|
426
|
-
prevPage: page > 1 ? page - 1 : null,
|
|
427
|
-
nextPage: page < Math.ceil(totalDocs / limit) ? page + 1 : null
|
|
428
|
-
};
|
|
429
|
-
}
|
|
430
|
-
async findByID(args) {
|
|
431
|
-
const { collection: slug, id, tenantID, draft = false } = args;
|
|
432
|
-
const parsed = this.parseGlobalsSlug(slug);
|
|
433
|
-
const config = parsed.isGlobal ? this.globals.get(parsed.globalSlug) : this.getCollection(slug);
|
|
434
|
-
this.ensureTable(config, parsed.tableName);
|
|
435
|
-
const tableName = parsed.tableName;
|
|
436
|
-
let sql = `SELECT * FROM ${tableName} WHERE id = ?`;
|
|
437
|
-
const params = [id];
|
|
438
|
-
if (!draft && config.versions?.drafts) {
|
|
439
|
-
sql += ` AND _status = ?`;
|
|
440
|
-
params.push("published");
|
|
441
|
-
}
|
|
442
|
-
if (tenantID && config.tenantScoped) {
|
|
443
|
-
sql += ` AND tenant_id = ?`;
|
|
444
|
-
params.push(tenantID);
|
|
445
|
-
}
|
|
446
|
-
const row = this.db.prepare(sql).get(...params);
|
|
447
|
-
if (!row) return null;
|
|
448
|
-
let doc = this.rowToDoc(row, config);
|
|
449
|
-
if (draft && doc._has_draft) {
|
|
450
|
-
const versions = await this.findVersions({
|
|
451
|
-
collection: slug,
|
|
452
|
-
documentId: doc.id,
|
|
453
|
-
limit: 1,
|
|
454
|
-
sort: "-createdAt"
|
|
455
|
-
});
|
|
456
|
-
if (versions.docs.length > 0 && versions.docs[0].status === "draft") {
|
|
457
|
-
doc = { ...doc, ...versions.docs[0].data, _has_draft: true, _status: doc._status };
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
return doc;
|
|
461
|
-
}
|
|
462
|
-
async create(args) {
|
|
463
|
-
const { collection: slug, data, tenantID } = args;
|
|
464
|
-
const parsed = this.parseGlobalsSlug(slug);
|
|
465
|
-
const config = parsed.isGlobal ? this.globals.get(parsed.globalSlug) : this.getCollection(slug);
|
|
466
|
-
this.ensureTable(config, parsed.tableName);
|
|
467
|
-
const tableName = parsed.tableName;
|
|
468
|
-
const id = parsed.isGlobal ? parsed.globalSlug : data.id || this.generateId();
|
|
469
|
-
const insertData = this.prepareData(data, config);
|
|
470
|
-
insertData.id = id;
|
|
471
|
-
insertData.created_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
472
|
-
insertData.updated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
473
|
-
if (tenantID && config.tenantScoped) {
|
|
474
|
-
insertData.tenant_id = tenantID;
|
|
475
|
-
}
|
|
476
|
-
const columns = Object.keys(insertData);
|
|
477
|
-
const validColumns = getTableColumns(this.db, tableName);
|
|
478
|
-
const filteredData = {};
|
|
479
|
-
for (const key of columns) {
|
|
480
|
-
if (validColumns.includes(key)) {
|
|
481
|
-
filteredData[key] = insertData[key];
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
const filteredColumns = Object.keys(filteredData);
|
|
485
|
-
const quotedColumns = filteredColumns.map((c) => this.col(c));
|
|
486
|
-
const placeholders = filteredColumns.map(() => "?").join(", ");
|
|
487
|
-
const values = Object.values(filteredData).map(
|
|
488
|
-
(v) => typeof v === "object" ? JSON.stringify(v) : v
|
|
489
|
-
);
|
|
490
|
-
this.db.prepare(
|
|
491
|
-
`INSERT OR REPLACE INTO ${tableName} (${quotedColumns.join(", ")}) VALUES (${placeholders})`
|
|
492
|
-
).run(...values);
|
|
493
|
-
return this.findByID({ collection: slug, id, tenantID });
|
|
494
|
-
}
|
|
495
|
-
async update(args) {
|
|
496
|
-
const { collection: slug, id, data, tenantID } = args;
|
|
497
|
-
const parsed = this.parseGlobalsSlug(slug);
|
|
498
|
-
const config = parsed.isGlobal ? this.globals.get(parsed.globalSlug) : this.getCollection(slug);
|
|
499
|
-
this.ensureTable(config, parsed.tableName);
|
|
500
|
-
const tableName = parsed.tableName;
|
|
501
|
-
const updateData = this.prepareData(data, config);
|
|
502
|
-
updateData.updated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
503
|
-
const validColumns = getTableColumns(this.db, tableName);
|
|
504
|
-
const filteredData = {};
|
|
505
|
-
for (const key of Object.keys(updateData)) {
|
|
506
|
-
if (validColumns.includes(key)) {
|
|
507
|
-
filteredData[key] = updateData[key];
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
const columns = Object.keys(filteredData);
|
|
511
|
-
const setClause = columns.map((c) => `${this.col(c)} = ?`).join(", ");
|
|
512
|
-
const values = Object.values(filteredData).map(
|
|
513
|
-
(v) => v !== null && typeof v === "object" ? JSON.stringify(v) : v
|
|
514
|
-
);
|
|
515
|
-
let sql = `UPDATE ${tableName} SET ${setClause} WHERE id = ?`;
|
|
516
|
-
const params = [...values, id];
|
|
517
|
-
console.log(`[LocalAdapter] update sql="${sql}", params=${JSON.stringify(params)}`);
|
|
518
|
-
if (tenantID && config.tenantScoped) {
|
|
519
|
-
sql += ` AND tenant_id = ?`;
|
|
520
|
-
params.push(tenantID);
|
|
521
|
-
}
|
|
522
|
-
this.db.prepare(sql).run(...params);
|
|
523
|
-
return this.findByID({ collection: slug, id, tenantID, draft: true });
|
|
524
|
-
}
|
|
525
|
-
async delete(args) {
|
|
526
|
-
const { collection: slug, id, tenantID } = args;
|
|
527
|
-
const parsed = this.parseGlobalsSlug(slug);
|
|
528
|
-
const config = parsed.isGlobal ? this.globals.get(parsed.globalSlug) : this.getCollection(slug);
|
|
529
|
-
this.ensureTable(config, parsed.tableName);
|
|
530
|
-
const doc = await this.findByID({ collection: slug, id, tenantID });
|
|
531
|
-
if (!doc) throw new Error(`Document not found: ${slug}/${id}`);
|
|
532
|
-
const tableName = parsed.tableName;
|
|
533
|
-
let sql = `DELETE FROM ${tableName} WHERE id = ?`;
|
|
534
|
-
const params = [id];
|
|
535
|
-
if (tenantID && config.tenantScoped) {
|
|
536
|
-
sql += ` AND tenant_id = ?`;
|
|
537
|
-
params.push(tenantID);
|
|
538
|
-
}
|
|
539
|
-
this.db.prepare(sql).run(...params);
|
|
540
|
-
return doc;
|
|
541
|
-
}
|
|
542
|
-
async count(args) {
|
|
543
|
-
const { collection: slug, tenantID } = args;
|
|
544
|
-
const parsed = this.parseGlobalsSlug(slug);
|
|
545
|
-
const config = parsed.isGlobal ? this.globals.get(parsed.globalSlug) : this.getCollection(slug);
|
|
546
|
-
this.ensureTable(config, parsed.tableName);
|
|
547
|
-
const tableName = parsed.tableName;
|
|
548
|
-
let sql = `SELECT COUNT(*) as count FROM ${tableName}`;
|
|
549
|
-
const params = [];
|
|
550
|
-
if (tenantID && config.tenantScoped) {
|
|
551
|
-
sql += ` WHERE tenant_id = ?`;
|
|
552
|
-
params.push(tenantID);
|
|
553
|
-
}
|
|
554
|
-
const result = this.db.prepare(sql).get(...params);
|
|
555
|
-
return result?.count || 0;
|
|
556
|
-
}
|
|
557
|
-
async findOne(args) {
|
|
558
|
-
const parsed = this.parseGlobalsSlug(args.collection);
|
|
559
|
-
if (parsed.isGlobal) {
|
|
560
|
-
const globalConfig = this.globals.get(parsed.globalSlug);
|
|
561
|
-
if (!globalConfig) {
|
|
562
|
-
throw new Error(`Global "${parsed.globalSlug}" not found in adapter`);
|
|
563
|
-
}
|
|
564
|
-
this.ensureTable(globalConfig, parsed.tableName);
|
|
565
|
-
let sql = `SELECT * FROM ${parsed.tableName}`;
|
|
566
|
-
const conditions = [];
|
|
567
|
-
const params = [];
|
|
568
|
-
if (!args.draft && globalConfig.versions) {
|
|
569
|
-
conditions.push("_status = 'published'");
|
|
570
|
-
}
|
|
571
|
-
if (conditions.length > 0) {
|
|
572
|
-
sql += ` WHERE ${conditions.join(" AND ")}`;
|
|
573
|
-
}
|
|
574
|
-
sql += " LIMIT 1";
|
|
575
|
-
const result2 = this.db.prepare(sql).get(...params);
|
|
576
|
-
if (result2) {
|
|
577
|
-
let doc = this.rowToDoc(result2, globalConfig);
|
|
578
|
-
if (args.draft && doc._has_draft) {
|
|
579
|
-
const versions = await this.findVersions({
|
|
580
|
-
collection: args.collection,
|
|
581
|
-
documentId: parsed.globalSlug,
|
|
582
|
-
limit: 1,
|
|
583
|
-
sort: "-createdAt"
|
|
584
|
-
});
|
|
585
|
-
if (versions.docs.length > 0 && versions.docs[0].status === "draft") {
|
|
586
|
-
doc = { ...doc, ...versions.docs[0].data, _has_draft: true, _status: doc._status };
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
return doc;
|
|
590
|
-
}
|
|
591
|
-
return null;
|
|
592
|
-
}
|
|
593
|
-
const result = await this.find({ ...args, limit: 1 });
|
|
594
|
-
return result.docs[0] || null;
|
|
595
|
-
}
|
|
596
|
-
// ========================================================================
|
|
597
|
-
// Version History
|
|
598
|
-
// ========================================================================
|
|
599
|
-
async findVersions(args) {
|
|
600
|
-
this.ensureVersionsTable();
|
|
601
|
-
const { collection, documentId, tenantID, limit = 20, page = 1 } = args;
|
|
602
|
-
const conditions = [`collection_slug = ?`, `document_id = ?`];
|
|
603
|
-
const params = [collection, documentId];
|
|
604
|
-
if (tenantID) {
|
|
605
|
-
conditions.push(`tenant_id = ?`);
|
|
606
|
-
params.push(tenantID);
|
|
607
|
-
} else {
|
|
608
|
-
conditions.push(`tenant_id IS NULL`);
|
|
609
|
-
}
|
|
610
|
-
const where = `WHERE ${conditions.join(" AND ")}`;
|
|
611
|
-
const countResult = this.db.prepare(`SELECT COUNT(*) as count FROM ${this.versionsTableName} ${where}`).get(...params);
|
|
612
|
-
const totalDocs = countResult?.count || 0;
|
|
613
|
-
const offset = (page - 1) * limit;
|
|
614
|
-
const rows = this.db.prepare(`SELECT * FROM ${this.versionsTableName} ${where} ORDER BY version DESC LIMIT ? OFFSET ?`).all(...params, limit, offset);
|
|
615
|
-
const docs = rows.map((r) => this.rowToVersion(r));
|
|
616
|
-
return {
|
|
617
|
-
docs,
|
|
618
|
-
totalDocs,
|
|
619
|
-
limit,
|
|
620
|
-
totalPages: Math.ceil(totalDocs / limit),
|
|
621
|
-
page,
|
|
622
|
-
pagingCounter: (page - 1) * limit + 1,
|
|
623
|
-
hasPrevPage: page > 1,
|
|
624
|
-
hasNextPage: page < Math.ceil(totalDocs / limit),
|
|
625
|
-
prevPage: page > 1 ? page - 1 : null,
|
|
626
|
-
nextPage: page < Math.ceil(totalDocs / limit) ? page + 1 : null
|
|
627
|
-
};
|
|
628
|
-
}
|
|
629
|
-
async findVersionByID(args) {
|
|
630
|
-
this.ensureVersionsTable();
|
|
631
|
-
const row = this.db.prepare(`SELECT * FROM ${this.versionsTableName} WHERE id = ? AND collection_slug = ? LIMIT 1`).get(args.versionId, args.collection);
|
|
632
|
-
return row ? this.rowToVersion(row) : null;
|
|
633
|
-
}
|
|
634
|
-
async createVersion(args) {
|
|
635
|
-
this.ensureVersionsTable();
|
|
636
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
637
|
-
const id = this.generateId();
|
|
638
|
-
const latestRow = this.db.prepare(`SELECT version FROM ${this.versionsTableName} WHERE collection_slug = ? AND document_id = ? ORDER BY version DESC LIMIT 1`).get(args.collection, args.documentId);
|
|
639
|
-
const nextVersion = (latestRow?.version ?? 0) + 1;
|
|
640
|
-
this.db.prepare(
|
|
641
|
-
`INSERT INTO ${this.versionsTableName} (
|
|
642
|
-
id, collection_slug, document_id, tenant_id, version, status, data, created_by, change_description, published_at, created_at, updated_at
|
|
643
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
644
|
-
).run(
|
|
645
|
-
id,
|
|
646
|
-
args.collection,
|
|
647
|
-
args.documentId,
|
|
648
|
-
args.tenantID ?? null,
|
|
649
|
-
nextVersion,
|
|
650
|
-
args.status,
|
|
651
|
-
JSON.stringify(args.data),
|
|
652
|
-
args.createdBy ?? null,
|
|
653
|
-
args.changeDescription ?? null,
|
|
654
|
-
args.status === "published" ? now : null,
|
|
655
|
-
now,
|
|
656
|
-
now
|
|
657
|
-
);
|
|
658
|
-
const collectionConfig = this.collections.get(args.collection);
|
|
659
|
-
const maxPerDoc = collectionConfig?.versions?.maxPerDoc;
|
|
660
|
-
if (maxPerDoc && maxPerDoc > 0) {
|
|
661
|
-
await this.deleteVersions({ collection: args.collection, documentId: args.documentId, keepLatest: maxPerDoc, tenantID: args.tenantID });
|
|
662
|
-
}
|
|
663
|
-
const saved = await this.findVersionByID({ collection: args.collection, versionId: id });
|
|
664
|
-
return saved;
|
|
665
|
-
}
|
|
666
|
-
async deleteVersions(args) {
|
|
667
|
-
this.ensureVersionsTable();
|
|
668
|
-
const { collection, documentId, keepLatest, tenantID } = args;
|
|
669
|
-
if (keepLatest && keepLatest > 0) {
|
|
670
|
-
const rows = this.db.prepare(`SELECT id, status FROM ${this.versionsTableName} WHERE collection_slug = ? AND document_id = ? ORDER BY version DESC`).all(collection, documentId);
|
|
671
|
-
let draftCount = 0;
|
|
672
|
-
const toDelete = [];
|
|
673
|
-
for (const row of rows) {
|
|
674
|
-
if (row.status === "published") continue;
|
|
675
|
-
draftCount++;
|
|
676
|
-
if (draftCount > keepLatest) toDelete.push(row.id);
|
|
677
|
-
}
|
|
678
|
-
for (const vid of toDelete) {
|
|
679
|
-
this.db.prepare(`DELETE FROM ${this.versionsTableName} WHERE id = ?`).run(vid);
|
|
680
|
-
}
|
|
681
|
-
} else {
|
|
682
|
-
let sql = `DELETE FROM ${this.versionsTableName} WHERE collection_slug = ? AND document_id = ?`;
|
|
683
|
-
const params = [collection, documentId];
|
|
684
|
-
if (tenantID) {
|
|
685
|
-
sql += ` AND tenant_id = ?`;
|
|
686
|
-
params.push(tenantID);
|
|
687
|
-
}
|
|
688
|
-
this.db.prepare(sql).run(...params);
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
rowToVersion(row) {
|
|
692
|
-
return {
|
|
693
|
-
id: String(row.id),
|
|
694
|
-
collection: row.collection_slug,
|
|
695
|
-
documentId: row.document_id,
|
|
696
|
-
version: row.version,
|
|
697
|
-
status: row.status,
|
|
698
|
-
data: row.data ? JSON.parse(row.data) : {},
|
|
699
|
-
createdBy: row.created_by ?? void 0,
|
|
700
|
-
changeDescription: row.change_description ?? void 0,
|
|
701
|
-
publishedAt: row.published_at ?? null,
|
|
702
|
-
createdAt: row.created_at,
|
|
703
|
-
updatedAt: row.updated_at
|
|
704
|
-
};
|
|
705
|
-
}
|
|
706
|
-
async findDraft(args) {
|
|
707
|
-
this.ensureDraftsTable();
|
|
708
|
-
let sql = `SELECT * FROM ${this.draftsTableName} WHERE collection_slug = ? AND document_id = ?`;
|
|
709
|
-
const params = [args.collection, args.documentId];
|
|
710
|
-
if (args.tenantID) {
|
|
711
|
-
sql += ` AND tenant_id = ?`;
|
|
712
|
-
params.push(args.tenantID);
|
|
713
|
-
} else {
|
|
714
|
-
sql += ` AND tenant_id IS NULL`;
|
|
715
|
-
}
|
|
716
|
-
sql += ` LIMIT 1`;
|
|
717
|
-
const row = this.db.prepare(sql).get(...params);
|
|
718
|
-
if (!row) return null;
|
|
719
|
-
return this.rowToDraft(row);
|
|
720
|
-
}
|
|
721
|
-
async upsertDraft(args) {
|
|
722
|
-
this.ensureDraftsTable();
|
|
723
|
-
const existing = await this.findDraft(args);
|
|
724
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
725
|
-
const draftUpdatedAt = args.draftUpdatedAt || now;
|
|
726
|
-
const id = existing?.id || this.generateId();
|
|
727
|
-
this.db.prepare(
|
|
728
|
-
`INSERT OR REPLACE INTO ${this.draftsTableName} (
|
|
729
|
-
id, collection_slug, document_id, tenant_id, data, base_updated_at, draft_updated_at, created_at, updated_at
|
|
730
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
731
|
-
).run(
|
|
732
|
-
id,
|
|
733
|
-
args.collection,
|
|
734
|
-
args.documentId,
|
|
735
|
-
args.tenantID ?? null,
|
|
736
|
-
JSON.stringify(args.data),
|
|
737
|
-
args.baseUpdatedAt ?? null,
|
|
738
|
-
draftUpdatedAt,
|
|
739
|
-
existing?.createdAt || now,
|
|
740
|
-
now
|
|
741
|
-
);
|
|
742
|
-
const saved = await this.findDraft(args);
|
|
743
|
-
if (!saved) {
|
|
744
|
-
throw new Error("Failed to persist draft snapshot");
|
|
745
|
-
}
|
|
746
|
-
return saved;
|
|
747
|
-
}
|
|
748
|
-
async deleteDraft(args) {
|
|
749
|
-
this.ensureDraftsTable();
|
|
750
|
-
let sql = `DELETE FROM ${this.draftsTableName} WHERE collection_slug = ? AND document_id = ?`;
|
|
751
|
-
const params = [args.collection, args.documentId];
|
|
752
|
-
if (args.tenantID) {
|
|
753
|
-
sql += ` AND tenant_id = ?`;
|
|
754
|
-
params.push(args.tenantID);
|
|
755
|
-
} else {
|
|
756
|
-
sql += ` AND tenant_id IS NULL`;
|
|
757
|
-
}
|
|
758
|
-
this.db.prepare(sql).run(...params);
|
|
759
|
-
}
|
|
760
|
-
// ========================================================================
|
|
761
|
-
// Helpers
|
|
762
|
-
// ========================================================================
|
|
763
|
-
prepareData(data, config) {
|
|
764
|
-
const result = {};
|
|
765
|
-
const fields = flattenFields(config.fields);
|
|
766
|
-
const processValue = (field, value) => {
|
|
767
|
-
const f = field;
|
|
768
|
-
if (f.type === "json" || f.type === "richtext" || f.type === "array" || f.type === "group" || f.type === "blocks" || f.type === "list" || f.type === "relationship-block") {
|
|
769
|
-
if (f.name === "content" && Array.isArray(value)) {
|
|
770
|
-
console.log("[processValue] content blocks before stringify - first block id:", value[0]?.id, "first block:", JSON.stringify(value[0]));
|
|
771
|
-
}
|
|
772
|
-
return value !== null && value !== void 0 ? JSON.stringify(value) : null;
|
|
773
|
-
} else if (f.type === "checkbox") {
|
|
774
|
-
return value ? 1 : 0;
|
|
775
|
-
} else if (f.type === "number") {
|
|
776
|
-
return value !== null && value !== "" ? Number(value) : null;
|
|
777
|
-
} else if (f.type === "upload" || f.type === "image") {
|
|
778
|
-
if (value === null || value === void 0) return null;
|
|
779
|
-
if (Array.isArray(value)) {
|
|
780
|
-
const items = value.map((v) => {
|
|
781
|
-
if (typeof v === "string") return v;
|
|
782
|
-
if (typeof v === "object") return v.id || v._id || v;
|
|
783
|
-
return String(v);
|
|
784
|
-
});
|
|
785
|
-
return JSON.stringify(items);
|
|
786
|
-
}
|
|
787
|
-
if (typeof value === "string") return value;
|
|
788
|
-
if (typeof value === "object") return JSON.stringify(value);
|
|
789
|
-
return String(value);
|
|
790
|
-
} else if (field.type === "relationship") {
|
|
791
|
-
if (value === null || value === void 0) return null;
|
|
792
|
-
if (Array.isArray(value)) {
|
|
793
|
-
const rels = value.map((v) => {
|
|
794
|
-
if (typeof v === "string") return v;
|
|
795
|
-
if (typeof v === "object") return JSON.stringify({ relationTo: field.relationTo, value: v.id || v });
|
|
796
|
-
return String(v);
|
|
797
|
-
});
|
|
798
|
-
return JSON.stringify(rels);
|
|
799
|
-
}
|
|
800
|
-
if (typeof value === "string") return value;
|
|
801
|
-
if (typeof value === "object") return JSON.stringify({ relationTo: field.relationTo, value: value.id || value });
|
|
802
|
-
return String(value);
|
|
803
|
-
}
|
|
804
|
-
return value;
|
|
805
|
-
};
|
|
806
|
-
for (const field of fields) {
|
|
807
|
-
if (!field.name || field.name === "id") continue;
|
|
808
|
-
const isInTab = config.fields.some(
|
|
809
|
-
(f) => f.type === "tabs" && "tabs" in f && f.tabs.some((t) => t.fields.some((tf) => tf.name === field.name))
|
|
810
|
-
);
|
|
811
|
-
if (isInTab) continue;
|
|
812
|
-
const value = data[field.name];
|
|
813
|
-
if (value !== void 0) {
|
|
814
|
-
result[field.name] = processValue(field, value);
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
for (const field of config.fields) {
|
|
818
|
-
if (field.type === "tabs" && "tabs" in field && field.name) {
|
|
819
|
-
const tabData = data[field.name];
|
|
820
|
-
if (tabData && typeof tabData === "object") {
|
|
821
|
-
for (const tab of field.tabs) {
|
|
822
|
-
for (const tabField of tab.fields) {
|
|
823
|
-
if (tabField.name && tabField.name !== "id") {
|
|
824
|
-
const value = tabData[tabField.name];
|
|
825
|
-
if (value !== void 0) {
|
|
826
|
-
result[tabField.name] = processValue(tabField, value);
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
return result;
|
|
835
|
-
}
|
|
836
|
-
rowToDoc(row, config) {
|
|
837
|
-
const doc = { id: row.id };
|
|
838
|
-
for (const field of flattenFields(config.fields)) {
|
|
839
|
-
if (!field.name || field.name === "id") continue;
|
|
840
|
-
const f = field;
|
|
841
|
-
let value = row[f.name];
|
|
842
|
-
if (f.type === "json" || f.type === "richtext" || f.type === "array" || f.type === "group" || f.type === "blocks" || f.type === "list" || f.type === "relationship-block") {
|
|
843
|
-
if (f.name === "content" && value) {
|
|
844
|
-
console.log("[rowToDoc] RAW content from DB (first 300 chars):", JSON.stringify(value).slice(0, 300));
|
|
845
|
-
}
|
|
846
|
-
try {
|
|
847
|
-
value = value ? JSON.parse(value) : null;
|
|
848
|
-
} catch {
|
|
849
|
-
value = null;
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
if (field.type === "checkbox") {
|
|
853
|
-
value = Boolean(value);
|
|
854
|
-
}
|
|
855
|
-
if (field.type === "date" && value) {
|
|
856
|
-
try {
|
|
857
|
-
const d = new Date(value);
|
|
858
|
-
if (isNaN(d.getTime())) {
|
|
859
|
-
console.warn(`[LocalAdapter] Invalid date value for field "${field.name}":`, value);
|
|
860
|
-
value = null;
|
|
861
|
-
} else {
|
|
862
|
-
value = d.toISOString();
|
|
863
|
-
}
|
|
864
|
-
} catch {
|
|
865
|
-
value = null;
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
if ((field.type === "upload" || field.type === "image") && value) {
|
|
869
|
-
try {
|
|
870
|
-
const parsed = JSON.parse(value);
|
|
871
|
-
if (Array.isArray(parsed)) {
|
|
872
|
-
value = parsed.map((item) => {
|
|
873
|
-
if (typeof item === "object" && item !== null) {
|
|
874
|
-
return item;
|
|
875
|
-
}
|
|
876
|
-
return { id: item };
|
|
877
|
-
});
|
|
878
|
-
} else {
|
|
879
|
-
value = typeof parsed === "object" ? parsed : { id: parsed };
|
|
880
|
-
}
|
|
881
|
-
} catch {
|
|
882
|
-
value = { id: value };
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
if (field.type === "relationship" && value) {
|
|
886
|
-
try {
|
|
887
|
-
const parsed = JSON.parse(value);
|
|
888
|
-
if (Array.isArray(parsed)) {
|
|
889
|
-
value = parsed;
|
|
890
|
-
} else {
|
|
891
|
-
value = parsed;
|
|
892
|
-
}
|
|
893
|
-
} catch {
|
|
894
|
-
value = { relationTo: Array.isArray(field.relationTo) ? field.relationTo[0] : field.relationTo, value };
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
doc[field.name] = value;
|
|
898
|
-
}
|
|
899
|
-
for (const field of config.fields) {
|
|
900
|
-
if (!field.name || field.name === "id" || field.name === "row" || field.name === "collapsible") continue;
|
|
901
|
-
if (field.type === "tabs" && "tabs" in field) {
|
|
902
|
-
const tabData = {};
|
|
903
|
-
for (const tab of field.tabs) {
|
|
904
|
-
for (const tabField of tab.fields) {
|
|
905
|
-
if (tabField.name && tabField.name !== "id") {
|
|
906
|
-
tabData[tabField.name] = processFieldValue(row, tabField);
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
doc[field.name] = tabData;
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
if (config.timestamps) {
|
|
914
|
-
const cAt = row.createdAt || row.created_at;
|
|
915
|
-
const uAt = row.updatedAt || row.updated_at;
|
|
916
|
-
if (cAt) {
|
|
917
|
-
try {
|
|
918
|
-
const d = new Date(cAt);
|
|
919
|
-
if (!isNaN(d.getTime())) doc.createdAt = d.toISOString();
|
|
920
|
-
} catch {
|
|
921
|
-
}
|
|
922
|
-
}
|
|
923
|
-
if (uAt) {
|
|
924
|
-
try {
|
|
925
|
-
const d = new Date(uAt);
|
|
926
|
-
if (!isNaN(d.getTime())) doc.updatedAt = d.toISOString();
|
|
927
|
-
} catch {
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
if (config.tenantScoped) {
|
|
932
|
-
doc.tenantID = row.tenant_id;
|
|
933
|
-
}
|
|
934
|
-
return doc;
|
|
935
|
-
}
|
|
936
|
-
generateId() {
|
|
937
|
-
const timestamp = Date.now().toString(16).padStart(12, "0");
|
|
938
|
-
const random = randomBytes(6).toString("hex");
|
|
939
|
-
return timestamp + random;
|
|
940
|
-
}
|
|
941
|
-
getMediaById(mediaId) {
|
|
942
|
-
try {
|
|
943
|
-
const tableName = this.getTableNameFor("media");
|
|
944
|
-
const row = this.db.prepare(`SELECT id, url, thumbnail_url FROM ${tableName} WHERE id = ?`).get(mediaId);
|
|
945
|
-
if (row) {
|
|
946
|
-
return {
|
|
947
|
-
id: row.id,
|
|
948
|
-
url: row.url,
|
|
949
|
-
thumbnailUrl: row.thumbnail_url || row.url
|
|
950
|
-
};
|
|
951
|
-
}
|
|
952
|
-
} catch (err) {
|
|
953
|
-
}
|
|
954
|
-
return null;
|
|
955
|
-
}
|
|
956
|
-
getTableNameFor(slug) {
|
|
957
|
-
return slug.replace(/-/g, "_");
|
|
958
|
-
}
|
|
959
|
-
rowToDraft(row) {
|
|
960
|
-
return {
|
|
961
|
-
id: row.id,
|
|
962
|
-
collection: row.collection_slug,
|
|
963
|
-
documentId: row.document_id,
|
|
964
|
-
tenantID: row.tenant_id ?? void 0,
|
|
965
|
-
data: row.data ? JSON.parse(row.data) : {},
|
|
966
|
-
baseUpdatedAt: row.base_updated_at ?? null,
|
|
967
|
-
draftUpdatedAt: row.draft_updated_at,
|
|
968
|
-
createdAt: row.created_at,
|
|
969
|
-
updatedAt: row.updated_at
|
|
970
|
-
};
|
|
971
|
-
}
|
|
972
|
-
// ========================================================================
|
|
973
|
-
// Migrations
|
|
974
|
-
// ========================================================================
|
|
975
|
-
async migrate() {
|
|
976
|
-
for (const config of this.collections.values()) {
|
|
977
|
-
this.ensureTable(config);
|
|
978
|
-
}
|
|
979
|
-
this.ensureDraftsTable();
|
|
980
|
-
console.log("[LocalAdapter] Migrations complete");
|
|
981
|
-
}
|
|
982
|
-
async rollback() {
|
|
983
|
-
console.log("[LocalAdapter] Rollback not supported for schema changes");
|
|
984
|
-
}
|
|
985
|
-
// ========================================================================
|
|
986
|
-
// Transaction Support
|
|
987
|
-
// ========================================================================
|
|
988
|
-
async transaction(fn) {
|
|
989
|
-
return new Promise((resolve2, reject) => {
|
|
990
|
-
const tx = this.db.transaction(async () => {
|
|
991
|
-
return fn({ db: this.db });
|
|
992
|
-
});
|
|
993
|
-
try {
|
|
994
|
-
const result = tx();
|
|
995
|
-
resolve2(result);
|
|
996
|
-
} catch (error) {
|
|
997
|
-
reject(error);
|
|
998
|
-
}
|
|
999
|
-
});
|
|
1000
|
-
}
|
|
1001
|
-
// ========================================================================
|
|
1002
|
-
// Direct DB Access
|
|
1003
|
-
// ========================================================================
|
|
1004
|
-
getDatabase() {
|
|
1005
|
-
return this.db;
|
|
1006
|
-
}
|
|
1007
|
-
exec(sql) {
|
|
1008
|
-
this.db.exec(sql);
|
|
1009
|
-
}
|
|
1010
|
-
prepare(sql) {
|
|
1011
|
-
return this.db.prepare(sql);
|
|
1012
|
-
}
|
|
1013
|
-
};
|
|
1014
|
-
function createLocalAdapter(options) {
|
|
1015
|
-
return new LocalAdapter(options || {});
|
|
1016
|
-
}
|
|
1017
57
|
|
|
1018
58
|
// src/plugins/index.ts
|
|
1019
59
|
var KyroPlugin = class {
|
|
@@ -1587,6 +627,8 @@ var InMemoryAuthAdapter = class {
|
|
|
1587
627
|
refreshTokens = /* @__PURE__ */ new Map();
|
|
1588
628
|
emailToUserId = /* @__PURE__ */ new Map();
|
|
1589
629
|
passwordHistory = /* @__PURE__ */ new Map();
|
|
630
|
+
emailVerificationTokens = /* @__PURE__ */ new Map();
|
|
631
|
+
passwordResetTokens = /* @__PURE__ */ new Map();
|
|
1590
632
|
auditLogs = [];
|
|
1591
633
|
externalDb = false;
|
|
1592
634
|
constructor() {
|
|
@@ -1744,6 +786,42 @@ var InMemoryAuthAdapter = class {
|
|
|
1744
786
|
}
|
|
1745
787
|
return false;
|
|
1746
788
|
}
|
|
789
|
+
async createEmailVerificationToken(userId) {
|
|
790
|
+
const token = randomBytes(32).toString("hex");
|
|
791
|
+
const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1e3);
|
|
792
|
+
this.emailVerificationTokens.set(token, { userId, expiresAt });
|
|
793
|
+
return { token, expiresAt };
|
|
794
|
+
}
|
|
795
|
+
async verifyEmailToken(token) {
|
|
796
|
+
const data = this.emailVerificationTokens.get(token);
|
|
797
|
+
if (!data || data.expiresAt < /* @__PURE__ */ new Date()) {
|
|
798
|
+
this.emailVerificationTokens.delete(token);
|
|
799
|
+
return { success: false, error: "Invalid or expired token" };
|
|
800
|
+
}
|
|
801
|
+
this.emailVerificationTokens.delete(token);
|
|
802
|
+
return { success: true, userId: data.userId };
|
|
803
|
+
}
|
|
804
|
+
async createPasswordResetToken(email) {
|
|
805
|
+
const user = await this.findUserByEmail(email);
|
|
806
|
+
if (!user) {
|
|
807
|
+
return { token: "", expiresAt: /* @__PURE__ */ new Date(), error: "User not found" };
|
|
808
|
+
}
|
|
809
|
+
const token = randomBytes(32).toString("hex");
|
|
810
|
+
const expiresAt = new Date(Date.now() + 60 * 60 * 1e3);
|
|
811
|
+
this.passwordResetTokens.set(token, { userId: user.id, expiresAt });
|
|
812
|
+
return { token, expiresAt };
|
|
813
|
+
}
|
|
814
|
+
async resetPasswordWithToken(token, newPassword) {
|
|
815
|
+
const data = this.passwordResetTokens.get(token);
|
|
816
|
+
if (!data || data.expiresAt < /* @__PURE__ */ new Date()) {
|
|
817
|
+
this.passwordResetTokens.delete(token);
|
|
818
|
+
return { success: false, error: "Invalid or expired token" };
|
|
819
|
+
}
|
|
820
|
+
const passwordHash = await this.hashPassword(newPassword);
|
|
821
|
+
await this.updateUser(data.userId, { passwordHash });
|
|
822
|
+
this.passwordResetTokens.delete(token);
|
|
823
|
+
return { success: true };
|
|
824
|
+
}
|
|
1747
825
|
async hasAnyUsers() {
|
|
1748
826
|
return this.users.size > 0;
|
|
1749
827
|
}
|
|
@@ -2009,7 +1087,7 @@ async function createAuthConfig(databaseType) {
|
|
|
2009
1087
|
const distributed = getEnvBool("KYRO_DISTRIBUTED", false);
|
|
2010
1088
|
let authAdapter;
|
|
2011
1089
|
if (distributed) {
|
|
2012
|
-
const { RedisAuthAdapter: RedisAuthAdapter2 } = await import('./redis-adapter-
|
|
1090
|
+
const { RedisAuthAdapter: RedisAuthAdapter2 } = await import('./redis-adapter-VQXD7ESY.js');
|
|
2013
1091
|
const redisUrl = getEnv("REDIS_URL", "redis://localhost:6379");
|
|
2014
1092
|
const redisTls = getEnvBool("REDIS_TLS", false);
|
|
2015
1093
|
const redisAdapter = new RedisAuthAdapter2({ url: redisUrl, tls: redisTls });
|
|
@@ -2222,6 +1300,39 @@ var Auth = class {
|
|
|
2222
1300
|
return { success: false, error: String(error) };
|
|
2223
1301
|
}
|
|
2224
1302
|
}
|
|
1303
|
+
async sendEmailVerification(userId) {
|
|
1304
|
+
try {
|
|
1305
|
+
const { token, expiresAt } = await this.adapter.createEmailVerificationToken(userId);
|
|
1306
|
+
return { success: true };
|
|
1307
|
+
} catch (error) {
|
|
1308
|
+
return { success: false, error: String(error) };
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
async verifyEmail(token) {
|
|
1312
|
+
try {
|
|
1313
|
+
return await this.adapter.verifyEmailToken(token);
|
|
1314
|
+
} catch (error) {
|
|
1315
|
+
return { success: false, error: String(error) };
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
async requestPasswordReset(email) {
|
|
1319
|
+
try {
|
|
1320
|
+
const result = await this.adapter.createPasswordResetToken(email);
|
|
1321
|
+
if (result.error) {
|
|
1322
|
+
return { success: false, error: result.error };
|
|
1323
|
+
}
|
|
1324
|
+
return { success: true };
|
|
1325
|
+
} catch (error) {
|
|
1326
|
+
return { success: false, error: String(error) };
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
async resetPasswordWithToken(token, newPassword) {
|
|
1330
|
+
try {
|
|
1331
|
+
return await this.adapter.resetPasswordWithToken(token, newPassword);
|
|
1332
|
+
} catch (error) {
|
|
1333
|
+
return { success: false, error: String(error) };
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
2225
1336
|
async deleteAccount(userId) {
|
|
2226
1337
|
try {
|
|
2227
1338
|
const user = await this.adapter.findUserById(userId);
|
|
@@ -2690,6 +1801,6 @@ async function createAuthStorage(config) {
|
|
|
2690
1801
|
// src/index.ts
|
|
2691
1802
|
init_secret();
|
|
2692
1803
|
|
|
2693
|
-
export { AccountLockout, AnalyticsPlugin, Auth, CommentsPlugin, InMemoryAccountLockout, InMemoryAuthAdapter, KyroPlugin,
|
|
1804
|
+
export { AccountLockout, AnalyticsPlugin, Auth, CommentsPlugin, InMemoryAccountLockout, InMemoryAuthAdapter, KyroPlugin, PluginManager, RateLimiter, ReviewsPlugin, SEOPLugin, VersionManager, WishlistPlugin, authConfig, createAuth, createAuthConfig, createAuthStorage, createStorage3 as createStorage, createVersionManager, defineConfig, generateAnalyticsTags, generateSeoTags, getDefaultDraftPublishConfig, isArchived, isDraft, isPublished, presetPlugins, runFieldHooks, runHooks };
|
|
2694
1805
|
//# sourceMappingURL=index.js.map
|
|
2695
1806
|
//# sourceMappingURL=index.js.map
|