@kyro-cms/core 0.1.6 → 0.1.7
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/WebhookService-BCgL1bLF.d.cts +112 -0
- package/dist/WebhookService-BPVJUgTl.d.ts +112 -0
- package/dist/{base-DlhVlwnN.d.cts → base-B0Y6isUJ.d.cts} +1 -1
- package/dist/{base-CQkFzqQl.d.ts → base-DaP-5PPG.d.ts} +1 -1
- package/dist/bootstrap-BMWVB2T6.cjs +31 -0
- package/dist/{bootstrap-WMWQ4DBX.cjs.map → bootstrap-BMWVB2T6.cjs.map} +1 -1
- package/dist/bootstrap-LL6O7PWO.js +6 -0
- package/dist/{bootstrap-WOVGAKZP.js.map → bootstrap-LL6O7PWO.js.map} +1 -1
- package/dist/{chunk-3VZCX4DF.cjs → chunk-42JPONZU.cjs} +77 -14
- package/dist/chunk-42JPONZU.cjs.map +1 -0
- package/dist/{chunk-3EVLFWH2.cjs → chunk-4M5PHMUE.cjs} +60 -346
- package/dist/chunk-4M5PHMUE.cjs.map +1 -0
- package/dist/chunk-4PWRCMTQ.cjs +15 -0
- package/dist/chunk-4PWRCMTQ.cjs.map +1 -0
- package/dist/chunk-6COM32WF.js +47 -0
- package/dist/chunk-6COM32WF.js.map +1 -0
- package/dist/chunk-6MSSF46R.js +941 -0
- package/dist/chunk-6MSSF46R.js.map +1 -0
- package/dist/{chunk-TZFJMPCH.cjs → chunk-7YITG2US.cjs} +9 -18
- package/dist/chunk-7YITG2US.cjs.map +1 -0
- package/dist/{chunk-A3RQWHKD.cjs → chunk-BLMFBDBG.cjs} +56 -6
- package/dist/chunk-BLMFBDBG.cjs.map +1 -0
- package/dist/{chunk-EINVJPFM.js → chunk-BTOE3VUK.js} +65 -3
- package/dist/chunk-BTOE3VUK.js.map +1 -0
- package/dist/chunk-E5X75WNB.js +497 -0
- package/dist/chunk-E5X75WNB.js.map +1 -0
- package/dist/chunk-E63IF3MD.cjs +951 -0
- package/dist/chunk-E63IF3MD.cjs.map +1 -0
- package/dist/{chunk-K7QF2QCM.cjs → chunk-FTSSDDZQ.cjs} +7 -3
- package/dist/chunk-FTSSDDZQ.cjs.map +1 -0
- package/dist/chunk-G7VZBCD6.cjs +35 -0
- package/dist/{chunk-5BLDMQED.cjs.map → chunk-G7VZBCD6.cjs.map} +1 -1
- package/dist/{chunk-VMSRTAH7.js → chunk-GLCPGZPM.js} +56 -6
- package/dist/chunk-GLCPGZPM.js.map +1 -0
- package/dist/{chunk-V3B25QOK.cjs → chunk-GVFB5C6O.cjs} +74 -2
- package/dist/chunk-GVFB5C6O.cjs.map +1 -0
- package/dist/chunk-HVSQDZZJ.cjs +765 -0
- package/dist/chunk-HVSQDZZJ.cjs.map +1 -0
- package/dist/chunk-HYC4GNHX.js +758 -0
- package/dist/chunk-HYC4GNHX.js.map +1 -0
- package/dist/chunk-KDVDIZ4Y.cjs +3479 -0
- package/dist/chunk-KDVDIZ4Y.cjs.map +1 -0
- package/dist/{chunk-OG3KX56O.js → chunk-KWGNR4HM.js} +7 -3
- package/dist/chunk-KWGNR4HM.js.map +1 -0
- package/dist/chunk-LIJVWQKU.cjs +256 -0
- package/dist/chunk-LIJVWQKU.cjs.map +1 -0
- package/dist/{chunk-XTZSUDSI.js → chunk-LTRCYJAG.js} +3 -18
- package/dist/chunk-LTRCYJAG.js.map +1 -0
- package/dist/{chunk-UEYC46RL.js → chunk-OUGKLCYF.js} +71 -8
- package/dist/chunk-OUGKLCYF.js.map +1 -0
- package/dist/chunk-RONAX6UU.js +3456 -0
- package/dist/chunk-RONAX6UU.js.map +1 -0
- package/dist/{chunk-5Y7QGIHD.js → chunk-RRYXQMZG.js} +60 -344
- package/dist/chunk-RRYXQMZG.js.map +1 -0
- package/dist/{chunk-QUJ4OLSC.js → chunk-U74F3YZU.js} +87 -7
- package/dist/chunk-U74F3YZU.js.map +1 -0
- package/dist/chunk-VIONYQ2K.cjs +517 -0
- package/dist/chunk-VIONYQ2K.cjs.map +1 -0
- package/dist/chunk-VSTRLXMQ.cjs +50 -0
- package/dist/chunk-VSTRLXMQ.cjs.map +1 -0
- package/dist/chunk-YT7HXXVN.js +13 -0
- package/dist/chunk-YT7HXXVN.js.map +1 -0
- package/dist/chunk-Z6ZWNWWR.js +30 -0
- package/dist/{chunk-NSBPE2FW.js.map → chunk-Z6ZWNWWR.js.map} +1 -1
- package/dist/cli/index.cjs +11 -7
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +11 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/drizzle/index.cjs +20 -17
- package/dist/drizzle/index.d.cts +4 -4
- package/dist/drizzle/index.d.ts +4 -4
- package/dist/drizzle/index.js +4 -5
- package/dist/graphql/index.cjs +4 -4
- package/dist/graphql/index.d.cts +3 -2
- package/dist/graphql/index.d.ts +3 -2
- package/dist/graphql/index.js +2 -2
- package/dist/{index-DI0DRPNv.d.cts → index-BwE4NueJ.d.cts} +1 -1
- package/dist/{index-CMUNCIWQ.d.ts → index-DUKmDSeC.d.cts} +96 -24
- package/dist/{index-BMySjW6o.d.cts → index-DtBi3zP0.d.ts} +96 -24
- package/dist/{index-4fJKLFK2.d.ts → index-DupWTmW6.d.ts} +1 -1
- package/dist/index.cjs +3317 -352
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +379 -105
- package/dist/index.d.ts +379 -105
- package/dist/index.js +3211 -310
- package/dist/index.js.map +1 -1
- package/dist/media-HOT3O7RW.js +4 -0
- package/dist/media-HOT3O7RW.js.map +1 -0
- package/dist/media-WKP5AOX2.cjs +17 -0
- package/dist/media-WKP5AOX2.cjs.map +1 -0
- package/dist/mongodb/index.cjs +1 -1
- package/dist/mongodb/index.d.cts +2 -2
- package/dist/mongodb/index.d.ts +2 -2
- package/dist/mongodb/index.js +1 -1
- package/dist/mysql-media-AI6YK767.cjs +48 -0
- package/dist/mysql-media-AI6YK767.cjs.map +1 -0
- package/dist/mysql-media-CDZUS7YX.js +45 -0
- package/dist/mysql-media-CDZUS7YX.js.map +1 -0
- package/dist/postgres-auth-adapter-EVRPO7BQ.cjs +14 -0
- package/dist/{postgres-auth-adapter-VK6GY7LX.cjs.map → postgres-auth-adapter-EVRPO7BQ.cjs.map} +1 -1
- package/dist/postgres-auth-adapter-OTRWSTT5.js +5 -0
- package/dist/{postgres-auth-adapter-REJFUMP7.js.map → postgres-auth-adapter-OTRWSTT5.js.map} +1 -1
- package/dist/redis-adapter-2N6VA7BI.cjs +13 -0
- package/dist/{redis-adapter-LBLNKGNS.cjs.map → redis-adapter-2N6VA7BI.cjs.map} +1 -1
- package/dist/redis-adapter-RA24FNCX.js +4 -0
- package/dist/{redis-adapter-4YDY4LWE.js.map → redis-adapter-RA24FNCX.js.map} +1 -1
- package/dist/rest/index.cjs +7 -5
- package/dist/rest/index.d.cts +29 -3
- package/dist/rest/index.d.ts +29 -3
- package/dist/rest/index.js +5 -3
- package/dist/schema-CNB2DDTX.js +6 -0
- package/dist/schema-CNB2DDTX.js.map +1 -0
- package/dist/schema-Y777CQQS.cjs +67 -0
- package/dist/schema-Y777CQQS.cjs.map +1 -0
- package/dist/templates/index.cjs +24 -28
- package/dist/templates/index.d.cts +2 -4
- package/dist/templates/index.d.ts +2 -4
- package/dist/templates/index.js +2 -2
- package/dist/trpc/index.cjs +12 -12
- package/dist/trpc/index.d.cts +19 -14
- package/dist/trpc/index.d.ts +19 -14
- package/dist/trpc/index.js +3 -3
- package/dist/{types-BGM5MV_K.d.cts → types-BM0s_YOy.d.cts} +67 -35
- package/dist/{types-BGM5MV_K.d.ts → types-BM0s_YOy.d.ts} +67 -35
- package/dist/ws/index.cjs +1 -1
- package/dist/ws/index.js +1 -1
- package/package.json +11 -1
- package/dist/bootstrap-WMWQ4DBX.cjs +0 -29
- package/dist/bootstrap-WOVGAKZP.js +0 -4
- package/dist/chunk-3EVLFWH2.cjs.map +0 -1
- package/dist/chunk-3QX6KG2S.js +0 -2125
- package/dist/chunk-3QX6KG2S.js.map +0 -1
- package/dist/chunk-3VZCX4DF.cjs.map +0 -1
- package/dist/chunk-5BLDMQED.cjs +0 -18
- package/dist/chunk-5Y7QGIHD.js.map +0 -1
- package/dist/chunk-7G6EVYCU.cjs +0 -94
- package/dist/chunk-7G6EVYCU.cjs.map +0 -1
- package/dist/chunk-A3RQWHKD.cjs.map +0 -1
- package/dist/chunk-EINVJPFM.js.map +0 -1
- package/dist/chunk-F5B64H5S.cjs +0 -2149
- package/dist/chunk-F5B64H5S.cjs.map +0 -1
- package/dist/chunk-K7QF2QCM.cjs.map +0 -1
- package/dist/chunk-LRTZJJPD.js +0 -86
- package/dist/chunk-LRTZJJPD.js.map +0 -1
- package/dist/chunk-NSBPE2FW.js +0 -15
- package/dist/chunk-OG3KX56O.js.map +0 -1
- package/dist/chunk-QUJ4OLSC.js.map +0 -1
- package/dist/chunk-R3XIBBAW.cjs +0 -34
- package/dist/chunk-R3XIBBAW.cjs.map +0 -1
- package/dist/chunk-SDMNUYVU.js +0 -30
- package/dist/chunk-SDMNUYVU.js.map +0 -1
- package/dist/chunk-TZFJMPCH.cjs.map +0 -1
- package/dist/chunk-UEG7KMKC.cjs +0 -228
- package/dist/chunk-UEG7KMKC.cjs.map +0 -1
- package/dist/chunk-UEYC46RL.js.map +0 -1
- package/dist/chunk-V3B25QOK.cjs.map +0 -1
- package/dist/chunk-VMSRTAH7.js.map +0 -1
- package/dist/chunk-XTZSUDSI.js.map +0 -1
- package/dist/chunk-YD7Y25W7.cjs +0 -176
- package/dist/chunk-YD7Y25W7.cjs.map +0 -1
- package/dist/chunk-YPAFJ7EV.js +0 -225
- package/dist/chunk-YPAFJ7EV.js.map +0 -1
- package/dist/database-7CJOXEZR.js +0 -5
- package/dist/database-7CJOXEZR.js.map +0 -1
- package/dist/database-QOIV44GT.cjs +0 -22
- package/dist/database-QOIV44GT.cjs.map +0 -1
- package/dist/postgres-auth-adapter-REJFUMP7.js +0 -5
- package/dist/postgres-auth-adapter-VK6GY7LX.cjs +0 -14
- package/dist/redis-adapter-4YDY4LWE.js +0 -4
- package/dist/redis-adapter-LBLNKGNS.cjs +0 -13
package/dist/index.js
CHANGED
|
@@ -1,29 +1,1023 @@
|
|
|
1
|
-
export { allSettingsGlobals, blogCollections, blogGlobals, coreSettingsGlobals, createTemplateConfig, ecommerceCollections, ecommerceGlobals, ecommerceSettingsGlobals, kitchenSinkCollections, mediaCollections, minimalCollections } from './chunk-
|
|
2
|
-
export { RedisAuthAdapter } from './chunk-
|
|
3
|
-
import {
|
|
4
|
-
export {
|
|
5
|
-
import { createKyroServer } from './chunk-
|
|
6
|
-
export { createContext, createCountProcedure, createCreateProcedure, createDeleteProcedure, createDynamicRouter, createFindByIDProcedure, createFindProcedure, createKyroServer, createUpdateProcedure } from './chunk-
|
|
7
|
-
import { buildGraphQLSchema } from './chunk-
|
|
8
|
-
export { buildGraphQLSchema, createGraphQLSchema } from './chunk-
|
|
9
|
-
import { createHonoApp } from './chunk-
|
|
10
|
-
export { createHonoApp, createRESTAPI } from './chunk-
|
|
11
|
-
|
|
1
|
+
export { allSettingsGlobals, blogCollections, blogGlobals, coreSettingsGlobals, createTemplateConfig, ecommerceCollections, ecommerceGlobals, ecommerceSettingsGlobals, kitchenSinkCollections, mediaCollections, minimalCollections } from './chunk-RONAX6UU.js';
|
|
2
|
+
export { RedisAuthAdapter } from './chunk-GLCPGZPM.js';
|
|
3
|
+
import { PasswordPolicy, SQLiteAuthAdapter } from './chunk-RRYXQMZG.js';
|
|
4
|
+
export { PasswordPolicy, SQLiteAuthAdapter, autoBootstrap, bootstrapAdmin, getBootstrapFromEnv } from './chunk-RRYXQMZG.js';
|
|
5
|
+
import { createKyroServer } from './chunk-OUGKLCYF.js';
|
|
6
|
+
export { createContext, createCountProcedure, createCreateProcedure, createDeleteProcedure, createDynamicRouter, createFindByIDProcedure, createFindProcedure, createKyroServer, createUpdateProcedure } from './chunk-OUGKLCYF.js';
|
|
7
|
+
import { buildGraphQLSchema } from './chunk-KWGNR4HM.js';
|
|
8
|
+
export { buildGraphQLSchema, createGraphQLSchema } from './chunk-KWGNR4HM.js';
|
|
9
|
+
import { InMemoryRateLimiter, generateToken, defaultExtractToken, createHonoApp } from './chunk-6MSSF46R.js';
|
|
10
|
+
export { InMemoryRateLimiter, createHonoApp, createRESTAPI } from './chunk-6MSSF46R.js';
|
|
11
|
+
import { EmailTransport, ConfigService } from './chunk-HYC4GNHX.js';
|
|
12
|
+
export { ConfigService, EmailTransport } from './chunk-HYC4GNHX.js';
|
|
13
|
+
import './chunk-YT7HXXVN.js';
|
|
14
|
+
export { ALL_WEBHOOK_EVENTS, WEBHOOK_COLLECTION, WEBHOOK_DELIVERY_COLLECTION, WEBHOOK_EVENTS, WebhookService, buildDeliveryRecord, createTestPayload, createWebhookService, deliverWebhook, deliverWithRetry, evaluateAccess, generateWebhookSecret, getWhereClause, mergeWhereClauses, signPayload } from './chunk-E5X75WNB.js';
|
|
12
15
|
import { KyroPubSub, createWSServer } from './chunk-3TPQ2BU6.js';
|
|
13
16
|
export { KyroPubSub, KyroWSServer, PubSub, createWSServer } from './chunk-3TPQ2BU6.js';
|
|
14
|
-
|
|
15
|
-
export {
|
|
16
|
-
export {
|
|
17
|
-
import './chunk-
|
|
17
|
+
import { genId } from './chunk-BTOE3VUK.js';
|
|
18
|
+
export { DrizzleAdapter, collectionToDrizzleSchema, createDatabase, createDrizzleAdapter, fieldToDrizzleType, runMigrations, seedDefaultRoles } from './chunk-BTOE3VUK.js';
|
|
19
|
+
export { PostgresAuthAdapter } from './chunk-U74F3YZU.js';
|
|
20
|
+
import './chunk-LTRCYJAG.js';
|
|
18
21
|
export { MongoDBAdapter, createMongoDBAdapter } from './chunk-DIC236EW.js';
|
|
19
22
|
import { AbstractBaseAdapter } from './chunk-BXMWDUED.js';
|
|
20
23
|
export { AbstractBaseAdapter } from './chunk-BXMWDUED.js';
|
|
21
|
-
import { __require } from './chunk-
|
|
24
|
+
import { __esm, __export, __require, __toCommonJS } from './chunk-Z6ZWNWWR.js';
|
|
25
|
+
import { Readable, Writable } from 'stream';
|
|
26
|
+
import { Agent, request } from 'https';
|
|
27
|
+
import http2, { constants } from 'http2';
|
|
22
28
|
import { z } from 'zod';
|
|
23
29
|
export { z } from 'zod';
|
|
24
30
|
import bcrypt from 'bcrypt';
|
|
25
|
-
import
|
|
26
|
-
import { randomBytes } from 'crypto';
|
|
31
|
+
import jwt from 'jsonwebtoken';
|
|
32
|
+
import { randomBytes, createHash } from 'crypto';
|
|
33
|
+
import path, { join, basename, extname } from 'path';
|
|
34
|
+
import { readdir, stat, rename, unlink, writeFile, mkdir } from 'fs/promises';
|
|
35
|
+
import { existsSync } from 'fs';
|
|
36
|
+
import { S3Client, HeadObjectCommand, ListObjectsV2Command, CopyObjectCommand, DeleteObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';
|
|
37
|
+
import { Client } from 'basic-ftp';
|
|
38
|
+
import sharp from 'sharp';
|
|
39
|
+
|
|
40
|
+
// node_modules/@smithy/protocol-http/dist-es/extensions/httpExtensionConfiguration.js
|
|
41
|
+
var init_httpExtensionConfiguration = __esm({
|
|
42
|
+
"node_modules/@smithy/protocol-http/dist-es/extensions/httpExtensionConfiguration.js"() {
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// node_modules/@smithy/protocol-http/dist-es/extensions/index.js
|
|
47
|
+
var init_extensions = __esm({
|
|
48
|
+
"node_modules/@smithy/protocol-http/dist-es/extensions/index.js"() {
|
|
49
|
+
init_httpExtensionConfiguration();
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// node_modules/@smithy/protocol-http/dist-es/Field.js
|
|
54
|
+
var init_Field = __esm({
|
|
55
|
+
"node_modules/@smithy/protocol-http/dist-es/Field.js"() {
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// node_modules/@smithy/protocol-http/dist-es/Fields.js
|
|
60
|
+
var init_Fields = __esm({
|
|
61
|
+
"node_modules/@smithy/protocol-http/dist-es/Fields.js"() {
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// node_modules/@smithy/protocol-http/dist-es/httpHandler.js
|
|
66
|
+
var init_httpHandler = __esm({
|
|
67
|
+
"node_modules/@smithy/protocol-http/dist-es/httpHandler.js"() {
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// node_modules/@smithy/protocol-http/dist-es/httpRequest.js
|
|
72
|
+
var init_httpRequest = __esm({
|
|
73
|
+
"node_modules/@smithy/protocol-http/dist-es/httpRequest.js"() {
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// node_modules/@smithy/protocol-http/dist-es/httpResponse.js
|
|
78
|
+
var HttpResponse;
|
|
79
|
+
var init_httpResponse = __esm({
|
|
80
|
+
"node_modules/@smithy/protocol-http/dist-es/httpResponse.js"() {
|
|
81
|
+
HttpResponse = class {
|
|
82
|
+
statusCode;
|
|
83
|
+
reason;
|
|
84
|
+
headers;
|
|
85
|
+
body;
|
|
86
|
+
constructor(options) {
|
|
87
|
+
this.statusCode = options.statusCode;
|
|
88
|
+
this.reason = options.reason;
|
|
89
|
+
this.headers = options.headers || {};
|
|
90
|
+
this.body = options.body;
|
|
91
|
+
}
|
|
92
|
+
static isInstance(response) {
|
|
93
|
+
if (!response)
|
|
94
|
+
return false;
|
|
95
|
+
const resp = response;
|
|
96
|
+
return typeof resp.statusCode === "number" && typeof resp.headers === "object";
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// node_modules/@smithy/protocol-http/dist-es/isValidHostname.js
|
|
103
|
+
var init_isValidHostname = __esm({
|
|
104
|
+
"node_modules/@smithy/protocol-http/dist-es/isValidHostname.js"() {
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// node_modules/@smithy/protocol-http/dist-es/types.js
|
|
109
|
+
var init_types = __esm({
|
|
110
|
+
"node_modules/@smithy/protocol-http/dist-es/types.js"() {
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// node_modules/@smithy/protocol-http/dist-es/index.js
|
|
115
|
+
var init_dist_es = __esm({
|
|
116
|
+
"node_modules/@smithy/protocol-http/dist-es/index.js"() {
|
|
117
|
+
init_extensions();
|
|
118
|
+
init_Field();
|
|
119
|
+
init_Fields();
|
|
120
|
+
init_httpHandler();
|
|
121
|
+
init_httpRequest();
|
|
122
|
+
init_httpResponse();
|
|
123
|
+
init_isValidHostname();
|
|
124
|
+
init_types();
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// node_modules/@smithy/util-uri-escape/dist-es/escape-uri.js
|
|
129
|
+
var escapeUri, hexEncode;
|
|
130
|
+
var init_escape_uri = __esm({
|
|
131
|
+
"node_modules/@smithy/util-uri-escape/dist-es/escape-uri.js"() {
|
|
132
|
+
escapeUri = (uri) => encodeURIComponent(uri).replace(/[!'()*]/g, hexEncode);
|
|
133
|
+
hexEncode = (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`;
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// node_modules/@smithy/util-uri-escape/dist-es/escape-uri-path.js
|
|
138
|
+
var init_escape_uri_path = __esm({
|
|
139
|
+
"node_modules/@smithy/util-uri-escape/dist-es/escape-uri-path.js"() {
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// node_modules/@smithy/util-uri-escape/dist-es/index.js
|
|
144
|
+
var init_dist_es2 = __esm({
|
|
145
|
+
"node_modules/@smithy/util-uri-escape/dist-es/index.js"() {
|
|
146
|
+
init_escape_uri();
|
|
147
|
+
init_escape_uri_path();
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// node_modules/@smithy/querystring-builder/dist-es/index.js
|
|
152
|
+
function buildQueryString(query) {
|
|
153
|
+
const parts = [];
|
|
154
|
+
for (let key of Object.keys(query).sort()) {
|
|
155
|
+
const value = query[key];
|
|
156
|
+
key = escapeUri(key);
|
|
157
|
+
if (Array.isArray(value)) {
|
|
158
|
+
for (let i = 0, iLen = value.length; i < iLen; i++) {
|
|
159
|
+
parts.push(`${key}=${escapeUri(value[i])}`);
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
let qsEntry = key;
|
|
163
|
+
if (value || typeof value === "string") {
|
|
164
|
+
qsEntry += `=${escapeUri(value)}`;
|
|
165
|
+
}
|
|
166
|
+
parts.push(qsEntry);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return parts.join("&");
|
|
170
|
+
}
|
|
171
|
+
var init_dist_es3 = __esm({
|
|
172
|
+
"node_modules/@smithy/querystring-builder/dist-es/index.js"() {
|
|
173
|
+
init_dist_es2();
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// node_modules/@smithy/node-http-handler/dist-es/build-abort-error.js
|
|
178
|
+
function buildAbortError(abortSignal) {
|
|
179
|
+
const reason = abortSignal && typeof abortSignal === "object" && "reason" in abortSignal ? abortSignal.reason : void 0;
|
|
180
|
+
if (reason) {
|
|
181
|
+
if (reason instanceof Error) {
|
|
182
|
+
const abortError3 = new Error("Request aborted");
|
|
183
|
+
abortError3.name = "AbortError";
|
|
184
|
+
abortError3.cause = reason;
|
|
185
|
+
return abortError3;
|
|
186
|
+
}
|
|
187
|
+
const abortError2 = new Error(String(reason));
|
|
188
|
+
abortError2.name = "AbortError";
|
|
189
|
+
return abortError2;
|
|
190
|
+
}
|
|
191
|
+
const abortError = new Error("Request aborted");
|
|
192
|
+
abortError.name = "AbortError";
|
|
193
|
+
return abortError;
|
|
194
|
+
}
|
|
195
|
+
var init_build_abort_error = __esm({
|
|
196
|
+
"node_modules/@smithy/node-http-handler/dist-es/build-abort-error.js"() {
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// node_modules/@smithy/node-http-handler/dist-es/constants.js
|
|
201
|
+
var NODEJS_TIMEOUT_ERROR_CODES;
|
|
202
|
+
var init_constants = __esm({
|
|
203
|
+
"node_modules/@smithy/node-http-handler/dist-es/constants.js"() {
|
|
204
|
+
NODEJS_TIMEOUT_ERROR_CODES = ["ECONNRESET", "EPIPE", "ETIMEDOUT"];
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// node_modules/@smithy/node-http-handler/dist-es/get-transformed-headers.js
|
|
209
|
+
var getTransformedHeaders;
|
|
210
|
+
var init_get_transformed_headers = __esm({
|
|
211
|
+
"node_modules/@smithy/node-http-handler/dist-es/get-transformed-headers.js"() {
|
|
212
|
+
getTransformedHeaders = (headers) => {
|
|
213
|
+
const transformedHeaders = {};
|
|
214
|
+
for (const name of Object.keys(headers)) {
|
|
215
|
+
const headerValues = headers[name];
|
|
216
|
+
transformedHeaders[name] = Array.isArray(headerValues) ? headerValues.join(",") : headerValues;
|
|
217
|
+
}
|
|
218
|
+
return transformedHeaders;
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// node_modules/@smithy/node-http-handler/dist-es/timing.js
|
|
224
|
+
var timing;
|
|
225
|
+
var init_timing = __esm({
|
|
226
|
+
"node_modules/@smithy/node-http-handler/dist-es/timing.js"() {
|
|
227
|
+
timing = {
|
|
228
|
+
setTimeout: (cb, ms) => setTimeout(cb, ms),
|
|
229
|
+
clearTimeout: (timeoutId) => clearTimeout(timeoutId)
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// node_modules/@smithy/node-http-handler/dist-es/set-connection-timeout.js
|
|
235
|
+
var DEFER_EVENT_LISTENER_TIME, setConnectionTimeout;
|
|
236
|
+
var init_set_connection_timeout = __esm({
|
|
237
|
+
"node_modules/@smithy/node-http-handler/dist-es/set-connection-timeout.js"() {
|
|
238
|
+
init_timing();
|
|
239
|
+
DEFER_EVENT_LISTENER_TIME = 1e3;
|
|
240
|
+
setConnectionTimeout = (request, reject, timeoutInMs = 0) => {
|
|
241
|
+
if (!timeoutInMs) {
|
|
242
|
+
return -1;
|
|
243
|
+
}
|
|
244
|
+
const registerTimeout = (offset) => {
|
|
245
|
+
const timeoutId = timing.setTimeout(() => {
|
|
246
|
+
request.destroy();
|
|
247
|
+
reject(Object.assign(new Error(`@smithy/node-http-handler - the request socket did not establish a connection with the server within the configured timeout of ${timeoutInMs} ms.`), {
|
|
248
|
+
name: "TimeoutError"
|
|
249
|
+
}));
|
|
250
|
+
}, timeoutInMs - offset);
|
|
251
|
+
const doWithSocket = (socket) => {
|
|
252
|
+
if (socket?.connecting) {
|
|
253
|
+
socket.on("connect", () => {
|
|
254
|
+
timing.clearTimeout(timeoutId);
|
|
255
|
+
});
|
|
256
|
+
} else {
|
|
257
|
+
timing.clearTimeout(timeoutId);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
if (request.socket) {
|
|
261
|
+
doWithSocket(request.socket);
|
|
262
|
+
} else {
|
|
263
|
+
request.on("socket", doWithSocket);
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
if (timeoutInMs < 2e3) {
|
|
267
|
+
registerTimeout(0);
|
|
268
|
+
return 0;
|
|
269
|
+
}
|
|
270
|
+
return timing.setTimeout(registerTimeout.bind(null, DEFER_EVENT_LISTENER_TIME), DEFER_EVENT_LISTENER_TIME);
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// node_modules/@smithy/node-http-handler/dist-es/set-request-timeout.js
|
|
276
|
+
var setRequestTimeout;
|
|
277
|
+
var init_set_request_timeout = __esm({
|
|
278
|
+
"node_modules/@smithy/node-http-handler/dist-es/set-request-timeout.js"() {
|
|
279
|
+
init_timing();
|
|
280
|
+
setRequestTimeout = (req, reject, timeoutInMs = 0, throwOnRequestTimeout, logger) => {
|
|
281
|
+
if (timeoutInMs) {
|
|
282
|
+
return timing.setTimeout(() => {
|
|
283
|
+
let msg = `@smithy/node-http-handler - [${throwOnRequestTimeout ? "ERROR" : "WARN"}] a request has exceeded the configured ${timeoutInMs} ms requestTimeout.`;
|
|
284
|
+
if (throwOnRequestTimeout) {
|
|
285
|
+
const error = Object.assign(new Error(msg), {
|
|
286
|
+
name: "TimeoutError",
|
|
287
|
+
code: "ETIMEDOUT"
|
|
288
|
+
});
|
|
289
|
+
req.destroy(error);
|
|
290
|
+
reject(error);
|
|
291
|
+
} else {
|
|
292
|
+
msg += ` Init client requestHandler with throwOnRequestTimeout=true to turn this into an error.`;
|
|
293
|
+
logger?.warn?.(msg);
|
|
294
|
+
}
|
|
295
|
+
}, timeoutInMs);
|
|
296
|
+
}
|
|
297
|
+
return -1;
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// node_modules/@smithy/node-http-handler/dist-es/set-socket-keep-alive.js
|
|
303
|
+
var DEFER_EVENT_LISTENER_TIME2, setSocketKeepAlive;
|
|
304
|
+
var init_set_socket_keep_alive = __esm({
|
|
305
|
+
"node_modules/@smithy/node-http-handler/dist-es/set-socket-keep-alive.js"() {
|
|
306
|
+
init_timing();
|
|
307
|
+
DEFER_EVENT_LISTENER_TIME2 = 3e3;
|
|
308
|
+
setSocketKeepAlive = (request, { keepAlive, keepAliveMsecs }, deferTimeMs = DEFER_EVENT_LISTENER_TIME2) => {
|
|
309
|
+
if (keepAlive !== true) {
|
|
310
|
+
return -1;
|
|
311
|
+
}
|
|
312
|
+
const registerListener = () => {
|
|
313
|
+
if (request.socket) {
|
|
314
|
+
request.socket.setKeepAlive(keepAlive, keepAliveMsecs || 0);
|
|
315
|
+
} else {
|
|
316
|
+
request.on("socket", (socket) => {
|
|
317
|
+
socket.setKeepAlive(keepAlive, keepAliveMsecs || 0);
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
if (deferTimeMs === 0) {
|
|
322
|
+
registerListener();
|
|
323
|
+
return 0;
|
|
324
|
+
}
|
|
325
|
+
return timing.setTimeout(registerListener, deferTimeMs);
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// node_modules/@smithy/node-http-handler/dist-es/set-socket-timeout.js
|
|
331
|
+
var DEFER_EVENT_LISTENER_TIME3, setSocketTimeout;
|
|
332
|
+
var init_set_socket_timeout = __esm({
|
|
333
|
+
"node_modules/@smithy/node-http-handler/dist-es/set-socket-timeout.js"() {
|
|
334
|
+
init_timing();
|
|
335
|
+
DEFER_EVENT_LISTENER_TIME3 = 3e3;
|
|
336
|
+
setSocketTimeout = (request, reject, timeoutInMs = 0) => {
|
|
337
|
+
const registerTimeout = (offset) => {
|
|
338
|
+
const timeout = timeoutInMs - offset;
|
|
339
|
+
const onTimeout = () => {
|
|
340
|
+
request.destroy();
|
|
341
|
+
reject(Object.assign(new Error(`@smithy/node-http-handler - the request socket timed out after ${timeoutInMs} ms of inactivity (configured by client requestHandler).`), { name: "TimeoutError" }));
|
|
342
|
+
};
|
|
343
|
+
if (request.socket) {
|
|
344
|
+
request.socket.setTimeout(timeout, onTimeout);
|
|
345
|
+
request.on("close", () => request.socket?.removeListener("timeout", onTimeout));
|
|
346
|
+
} else {
|
|
347
|
+
request.setTimeout(timeout, onTimeout);
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
if (0 < timeoutInMs && timeoutInMs < 6e3) {
|
|
351
|
+
registerTimeout(0);
|
|
352
|
+
return 0;
|
|
353
|
+
}
|
|
354
|
+
return timing.setTimeout(registerTimeout.bind(null, timeoutInMs === 0 ? 0 : DEFER_EVENT_LISTENER_TIME3), DEFER_EVENT_LISTENER_TIME3);
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
async function writeRequestBody(httpRequest, request, maxContinueTimeoutMs = MIN_WAIT_TIME, externalAgent = false) {
|
|
359
|
+
const headers = request.headers ?? {};
|
|
360
|
+
const expect = headers.Expect || headers.expect;
|
|
361
|
+
let timeoutId = -1;
|
|
362
|
+
let sendBody = true;
|
|
363
|
+
if (!externalAgent && expect === "100-continue") {
|
|
364
|
+
sendBody = await Promise.race([
|
|
365
|
+
new Promise((resolve) => {
|
|
366
|
+
timeoutId = Number(timing.setTimeout(() => resolve(true), Math.max(MIN_WAIT_TIME, maxContinueTimeoutMs)));
|
|
367
|
+
}),
|
|
368
|
+
new Promise((resolve) => {
|
|
369
|
+
httpRequest.on("continue", () => {
|
|
370
|
+
timing.clearTimeout(timeoutId);
|
|
371
|
+
resolve(true);
|
|
372
|
+
});
|
|
373
|
+
httpRequest.on("response", () => {
|
|
374
|
+
timing.clearTimeout(timeoutId);
|
|
375
|
+
resolve(false);
|
|
376
|
+
});
|
|
377
|
+
httpRequest.on("error", () => {
|
|
378
|
+
timing.clearTimeout(timeoutId);
|
|
379
|
+
resolve(false);
|
|
380
|
+
});
|
|
381
|
+
})
|
|
382
|
+
]);
|
|
383
|
+
}
|
|
384
|
+
if (sendBody) {
|
|
385
|
+
writeBody(httpRequest, request.body);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
function writeBody(httpRequest, body) {
|
|
389
|
+
if (body instanceof Readable) {
|
|
390
|
+
body.pipe(httpRequest);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if (body) {
|
|
394
|
+
const isBuffer = Buffer.isBuffer(body);
|
|
395
|
+
const isString = typeof body === "string";
|
|
396
|
+
if (isBuffer || isString) {
|
|
397
|
+
if (isBuffer && body.byteLength === 0) {
|
|
398
|
+
httpRequest.end();
|
|
399
|
+
} else {
|
|
400
|
+
httpRequest.end(body);
|
|
401
|
+
}
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
const uint8 = body;
|
|
405
|
+
if (typeof uint8 === "object" && uint8.buffer && typeof uint8.byteOffset === "number" && typeof uint8.byteLength === "number") {
|
|
406
|
+
httpRequest.end(Buffer.from(uint8.buffer, uint8.byteOffset, uint8.byteLength));
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
httpRequest.end(Buffer.from(body));
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
httpRequest.end();
|
|
413
|
+
}
|
|
414
|
+
var MIN_WAIT_TIME;
|
|
415
|
+
var init_write_request_body = __esm({
|
|
416
|
+
"node_modules/@smithy/node-http-handler/dist-es/write-request-body.js"() {
|
|
417
|
+
init_timing();
|
|
418
|
+
MIN_WAIT_TIME = 6e3;
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
var DEFAULT_REQUEST_TIMEOUT, hAgent, hRequest, NodeHttpHandler;
|
|
422
|
+
var init_node_http_handler = __esm({
|
|
423
|
+
"node_modules/@smithy/node-http-handler/dist-es/node-http-handler.js"() {
|
|
424
|
+
init_dist_es();
|
|
425
|
+
init_dist_es3();
|
|
426
|
+
init_build_abort_error();
|
|
427
|
+
init_constants();
|
|
428
|
+
init_get_transformed_headers();
|
|
429
|
+
init_set_connection_timeout();
|
|
430
|
+
init_set_request_timeout();
|
|
431
|
+
init_set_socket_keep_alive();
|
|
432
|
+
init_set_socket_timeout();
|
|
433
|
+
init_timing();
|
|
434
|
+
init_write_request_body();
|
|
435
|
+
DEFAULT_REQUEST_TIMEOUT = 0;
|
|
436
|
+
hAgent = void 0;
|
|
437
|
+
hRequest = void 0;
|
|
438
|
+
NodeHttpHandler = class _NodeHttpHandler {
|
|
439
|
+
config;
|
|
440
|
+
configProvider;
|
|
441
|
+
socketWarningTimestamp = 0;
|
|
442
|
+
externalAgent = false;
|
|
443
|
+
metadata = { handlerProtocol: "http/1.1" };
|
|
444
|
+
static create(instanceOrOptions) {
|
|
445
|
+
if (typeof instanceOrOptions?.handle === "function") {
|
|
446
|
+
return instanceOrOptions;
|
|
447
|
+
}
|
|
448
|
+
return new _NodeHttpHandler(instanceOrOptions);
|
|
449
|
+
}
|
|
450
|
+
static checkSocketUsage(agent, socketWarningTimestamp, logger = console) {
|
|
451
|
+
const { sockets, requests, maxSockets } = agent;
|
|
452
|
+
if (typeof maxSockets !== "number" || maxSockets === Infinity) {
|
|
453
|
+
return socketWarningTimestamp;
|
|
454
|
+
}
|
|
455
|
+
const interval = 15e3;
|
|
456
|
+
if (Date.now() - interval < socketWarningTimestamp) {
|
|
457
|
+
return socketWarningTimestamp;
|
|
458
|
+
}
|
|
459
|
+
if (sockets && requests) {
|
|
460
|
+
for (const origin in sockets) {
|
|
461
|
+
const socketsInUse = sockets[origin]?.length ?? 0;
|
|
462
|
+
const requestsEnqueued = requests[origin]?.length ?? 0;
|
|
463
|
+
if (socketsInUse >= maxSockets && requestsEnqueued >= 2 * maxSockets) {
|
|
464
|
+
logger?.warn?.(`@smithy/node-http-handler:WARN - socket usage at capacity=${socketsInUse} and ${requestsEnqueued} additional requests are enqueued.
|
|
465
|
+
See https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-configuring-maxsockets.html
|
|
466
|
+
or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler config.`);
|
|
467
|
+
return Date.now();
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
return socketWarningTimestamp;
|
|
472
|
+
}
|
|
473
|
+
constructor(options) {
|
|
474
|
+
this.configProvider = new Promise((resolve, reject) => {
|
|
475
|
+
if (typeof options === "function") {
|
|
476
|
+
options().then((_options) => {
|
|
477
|
+
resolve(this.resolveDefaultConfig(_options));
|
|
478
|
+
}).catch(reject);
|
|
479
|
+
} else {
|
|
480
|
+
resolve(this.resolveDefaultConfig(options));
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
destroy() {
|
|
485
|
+
this.config?.httpAgent?.destroy();
|
|
486
|
+
this.config?.httpsAgent?.destroy();
|
|
487
|
+
}
|
|
488
|
+
async handle(request$1, { abortSignal, requestTimeout } = {}) {
|
|
489
|
+
if (!this.config) {
|
|
490
|
+
this.config = await this.configProvider;
|
|
491
|
+
}
|
|
492
|
+
const config = this.config;
|
|
493
|
+
const isSSL = request$1.protocol === "https:";
|
|
494
|
+
if (!isSSL && !this.config.httpAgent) {
|
|
495
|
+
this.config.httpAgent = await this.config.httpAgentProvider();
|
|
496
|
+
}
|
|
497
|
+
return new Promise((_resolve, _reject) => {
|
|
498
|
+
let writeRequestBodyPromise = void 0;
|
|
499
|
+
const timeouts = [];
|
|
500
|
+
const resolve = async (arg) => {
|
|
501
|
+
await writeRequestBodyPromise;
|
|
502
|
+
timeouts.forEach(timing.clearTimeout);
|
|
503
|
+
_resolve(arg);
|
|
504
|
+
};
|
|
505
|
+
const reject = async (arg) => {
|
|
506
|
+
await writeRequestBodyPromise;
|
|
507
|
+
timeouts.forEach(timing.clearTimeout);
|
|
508
|
+
_reject(arg);
|
|
509
|
+
};
|
|
510
|
+
if (abortSignal?.aborted) {
|
|
511
|
+
const abortError = buildAbortError(abortSignal);
|
|
512
|
+
reject(abortError);
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
const headers = request$1.headers ?? {};
|
|
516
|
+
const expectContinue = (headers.Expect ?? headers.expect) === "100-continue";
|
|
517
|
+
let agent = isSSL ? config.httpsAgent : config.httpAgent;
|
|
518
|
+
if (expectContinue && !this.externalAgent) {
|
|
519
|
+
agent = new (isSSL ? Agent : hAgent)({
|
|
520
|
+
keepAlive: false,
|
|
521
|
+
maxSockets: Infinity
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
timeouts.push(timing.setTimeout(() => {
|
|
525
|
+
this.socketWarningTimestamp = _NodeHttpHandler.checkSocketUsage(agent, this.socketWarningTimestamp, config.logger);
|
|
526
|
+
}, config.socketAcquisitionWarningTimeout ?? (config.requestTimeout ?? 2e3) + (config.connectionTimeout ?? 1e3)));
|
|
527
|
+
const queryString = buildQueryString(request$1.query || {});
|
|
528
|
+
let auth = void 0;
|
|
529
|
+
if (request$1.username != null || request$1.password != null) {
|
|
530
|
+
const username = request$1.username ?? "";
|
|
531
|
+
const password = request$1.password ?? "";
|
|
532
|
+
auth = `${username}:${password}`;
|
|
533
|
+
}
|
|
534
|
+
let path2 = request$1.path;
|
|
535
|
+
if (queryString) {
|
|
536
|
+
path2 += `?${queryString}`;
|
|
537
|
+
}
|
|
538
|
+
if (request$1.fragment) {
|
|
539
|
+
path2 += `#${request$1.fragment}`;
|
|
540
|
+
}
|
|
541
|
+
let hostname = request$1.hostname ?? "";
|
|
542
|
+
if (hostname[0] === "[" && hostname.endsWith("]")) {
|
|
543
|
+
hostname = request$1.hostname.slice(1, -1);
|
|
544
|
+
} else {
|
|
545
|
+
hostname = request$1.hostname;
|
|
546
|
+
}
|
|
547
|
+
const nodeHttpsOptions = {
|
|
548
|
+
headers: request$1.headers,
|
|
549
|
+
host: hostname,
|
|
550
|
+
method: request$1.method,
|
|
551
|
+
path: path2,
|
|
552
|
+
port: request$1.port,
|
|
553
|
+
agent,
|
|
554
|
+
auth
|
|
555
|
+
};
|
|
556
|
+
const requestFunc = isSSL ? request : hRequest;
|
|
557
|
+
const req = requestFunc(nodeHttpsOptions, (res) => {
|
|
558
|
+
const httpResponse = new HttpResponse({
|
|
559
|
+
statusCode: res.statusCode || -1,
|
|
560
|
+
reason: res.statusMessage,
|
|
561
|
+
headers: getTransformedHeaders(res.headers),
|
|
562
|
+
body: res
|
|
563
|
+
});
|
|
564
|
+
resolve({ response: httpResponse });
|
|
565
|
+
});
|
|
566
|
+
req.on("error", (err) => {
|
|
567
|
+
if (NODEJS_TIMEOUT_ERROR_CODES.includes(err.code)) {
|
|
568
|
+
reject(Object.assign(err, { name: "TimeoutError" }));
|
|
569
|
+
} else {
|
|
570
|
+
reject(err);
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
if (abortSignal) {
|
|
574
|
+
const onAbort = () => {
|
|
575
|
+
req.destroy();
|
|
576
|
+
const abortError = buildAbortError(abortSignal);
|
|
577
|
+
reject(abortError);
|
|
578
|
+
};
|
|
579
|
+
if (typeof abortSignal.addEventListener === "function") {
|
|
580
|
+
const signal = abortSignal;
|
|
581
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
582
|
+
req.once("close", () => signal.removeEventListener("abort", onAbort));
|
|
583
|
+
} else {
|
|
584
|
+
abortSignal.onabort = onAbort;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
const effectiveRequestTimeout = requestTimeout ?? config.requestTimeout;
|
|
588
|
+
timeouts.push(setConnectionTimeout(req, reject, config.connectionTimeout));
|
|
589
|
+
timeouts.push(setRequestTimeout(req, reject, effectiveRequestTimeout, config.throwOnRequestTimeout, config.logger ?? console));
|
|
590
|
+
timeouts.push(setSocketTimeout(req, reject, config.socketTimeout));
|
|
591
|
+
const httpAgent = nodeHttpsOptions.agent;
|
|
592
|
+
if (typeof httpAgent === "object" && "keepAlive" in httpAgent) {
|
|
593
|
+
timeouts.push(setSocketKeepAlive(req, {
|
|
594
|
+
keepAlive: httpAgent.keepAlive,
|
|
595
|
+
keepAliveMsecs: httpAgent.keepAliveMsecs
|
|
596
|
+
}));
|
|
597
|
+
}
|
|
598
|
+
writeRequestBodyPromise = writeRequestBody(req, request$1, effectiveRequestTimeout, this.externalAgent).catch((e) => {
|
|
599
|
+
timeouts.forEach(timing.clearTimeout);
|
|
600
|
+
return _reject(e);
|
|
601
|
+
});
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
updateHttpClientConfig(key, value) {
|
|
605
|
+
this.config = void 0;
|
|
606
|
+
this.configProvider = this.configProvider.then((config) => {
|
|
607
|
+
return {
|
|
608
|
+
...config,
|
|
609
|
+
[key]: value
|
|
610
|
+
};
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
httpHandlerConfigs() {
|
|
614
|
+
return this.config ?? {};
|
|
615
|
+
}
|
|
616
|
+
resolveDefaultConfig(options) {
|
|
617
|
+
const { requestTimeout, connectionTimeout, socketTimeout, socketAcquisitionWarningTimeout, httpAgent, httpsAgent, throwOnRequestTimeout, logger } = options || {};
|
|
618
|
+
const keepAlive = true;
|
|
619
|
+
const maxSockets = 50;
|
|
620
|
+
return {
|
|
621
|
+
connectionTimeout,
|
|
622
|
+
requestTimeout,
|
|
623
|
+
socketTimeout,
|
|
624
|
+
socketAcquisitionWarningTimeout,
|
|
625
|
+
throwOnRequestTimeout,
|
|
626
|
+
httpAgentProvider: async () => {
|
|
627
|
+
const { Agent, request } = await import('http');
|
|
628
|
+
hRequest = request;
|
|
629
|
+
hAgent = Agent;
|
|
630
|
+
if (httpAgent instanceof hAgent || typeof httpAgent?.destroy === "function") {
|
|
631
|
+
this.externalAgent = true;
|
|
632
|
+
return httpAgent;
|
|
633
|
+
}
|
|
634
|
+
return new hAgent({ keepAlive, maxSockets, ...httpAgent });
|
|
635
|
+
},
|
|
636
|
+
httpsAgent: (() => {
|
|
637
|
+
if (httpsAgent instanceof Agent || typeof httpsAgent?.destroy === "function") {
|
|
638
|
+
this.externalAgent = true;
|
|
639
|
+
return httpsAgent;
|
|
640
|
+
}
|
|
641
|
+
return new Agent({ keepAlive, maxSockets, ...httpsAgent });
|
|
642
|
+
})(),
|
|
643
|
+
logger
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
// node_modules/@smithy/node-http-handler/dist-es/node-http2-connection-pool.js
|
|
651
|
+
var NodeHttp2ConnectionPool;
|
|
652
|
+
var init_node_http2_connection_pool = __esm({
|
|
653
|
+
"node_modules/@smithy/node-http-handler/dist-es/node-http2-connection-pool.js"() {
|
|
654
|
+
NodeHttp2ConnectionPool = class {
|
|
655
|
+
sessions = [];
|
|
656
|
+
constructor(sessions) {
|
|
657
|
+
this.sessions = sessions ?? [];
|
|
658
|
+
}
|
|
659
|
+
poll() {
|
|
660
|
+
if (this.sessions.length > 0) {
|
|
661
|
+
return this.sessions.shift();
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
offerLast(session) {
|
|
665
|
+
this.sessions.push(session);
|
|
666
|
+
}
|
|
667
|
+
contains(session) {
|
|
668
|
+
return this.sessions.includes(session);
|
|
669
|
+
}
|
|
670
|
+
remove(session) {
|
|
671
|
+
this.sessions = this.sessions.filter((s) => s !== session);
|
|
672
|
+
}
|
|
673
|
+
[Symbol.iterator]() {
|
|
674
|
+
return this.sessions[Symbol.iterator]();
|
|
675
|
+
}
|
|
676
|
+
destroy(connection) {
|
|
677
|
+
for (const session of this.sessions) {
|
|
678
|
+
if (session === connection) {
|
|
679
|
+
if (!session.destroyed) {
|
|
680
|
+
session.destroy();
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
var NodeHttp2ConnectionManager;
|
|
689
|
+
var init_node_http2_connection_manager = __esm({
|
|
690
|
+
"node_modules/@smithy/node-http-handler/dist-es/node-http2-connection-manager.js"() {
|
|
691
|
+
init_node_http2_connection_pool();
|
|
692
|
+
NodeHttp2ConnectionManager = class {
|
|
693
|
+
constructor(config) {
|
|
694
|
+
this.config = config;
|
|
695
|
+
if (this.config.maxConcurrency && this.config.maxConcurrency <= 0) {
|
|
696
|
+
throw new RangeError("maxConcurrency must be greater than zero.");
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
config;
|
|
700
|
+
sessionCache = /* @__PURE__ */ new Map();
|
|
701
|
+
lease(requestContext, connectionConfiguration) {
|
|
702
|
+
const url = this.getUrlString(requestContext);
|
|
703
|
+
const existingPool = this.sessionCache.get(url);
|
|
704
|
+
if (existingPool) {
|
|
705
|
+
const existingSession = existingPool.poll();
|
|
706
|
+
if (existingSession && !this.config.disableConcurrency) {
|
|
707
|
+
return existingSession;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
const session = http2.connect(url);
|
|
711
|
+
if (this.config.maxConcurrency) {
|
|
712
|
+
session.settings({ maxConcurrentStreams: this.config.maxConcurrency }, (err) => {
|
|
713
|
+
if (err) {
|
|
714
|
+
throw new Error("Fail to set maxConcurrentStreams to " + this.config.maxConcurrency + "when creating new session for " + requestContext.destination.toString());
|
|
715
|
+
}
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
session.unref();
|
|
719
|
+
const destroySessionCb = () => {
|
|
720
|
+
session.destroy();
|
|
721
|
+
this.deleteSession(url, session);
|
|
722
|
+
};
|
|
723
|
+
session.on("goaway", destroySessionCb);
|
|
724
|
+
session.on("error", destroySessionCb);
|
|
725
|
+
session.on("frameError", destroySessionCb);
|
|
726
|
+
session.on("close", () => this.deleteSession(url, session));
|
|
727
|
+
if (connectionConfiguration.requestTimeout) {
|
|
728
|
+
session.setTimeout(connectionConfiguration.requestTimeout, destroySessionCb);
|
|
729
|
+
}
|
|
730
|
+
const connectionPool = this.sessionCache.get(url) || new NodeHttp2ConnectionPool();
|
|
731
|
+
connectionPool.offerLast(session);
|
|
732
|
+
this.sessionCache.set(url, connectionPool);
|
|
733
|
+
return session;
|
|
734
|
+
}
|
|
735
|
+
deleteSession(authority, session) {
|
|
736
|
+
const existingConnectionPool = this.sessionCache.get(authority);
|
|
737
|
+
if (!existingConnectionPool) {
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
if (!existingConnectionPool.contains(session)) {
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
existingConnectionPool.remove(session);
|
|
744
|
+
this.sessionCache.set(authority, existingConnectionPool);
|
|
745
|
+
}
|
|
746
|
+
release(requestContext, session) {
|
|
747
|
+
const cacheKey = this.getUrlString(requestContext);
|
|
748
|
+
this.sessionCache.get(cacheKey)?.offerLast(session);
|
|
749
|
+
}
|
|
750
|
+
destroy() {
|
|
751
|
+
for (const [key, connectionPool] of this.sessionCache) {
|
|
752
|
+
for (const session of connectionPool) {
|
|
753
|
+
if (!session.destroyed) {
|
|
754
|
+
session.destroy();
|
|
755
|
+
}
|
|
756
|
+
connectionPool.remove(session);
|
|
757
|
+
}
|
|
758
|
+
this.sessionCache.delete(key);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
setMaxConcurrentStreams(maxConcurrentStreams) {
|
|
762
|
+
if (maxConcurrentStreams && maxConcurrentStreams <= 0) {
|
|
763
|
+
throw new RangeError("maxConcurrentStreams must be greater than zero.");
|
|
764
|
+
}
|
|
765
|
+
this.config.maxConcurrency = maxConcurrentStreams;
|
|
766
|
+
}
|
|
767
|
+
setDisableConcurrentStreams(disableConcurrentStreams) {
|
|
768
|
+
this.config.disableConcurrency = disableConcurrentStreams;
|
|
769
|
+
}
|
|
770
|
+
getUrlString(request) {
|
|
771
|
+
return request.destination.toString();
|
|
772
|
+
}
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
});
|
|
776
|
+
var NodeHttp2Handler;
|
|
777
|
+
var init_node_http2_handler = __esm({
|
|
778
|
+
"node_modules/@smithy/node-http-handler/dist-es/node-http2-handler.js"() {
|
|
779
|
+
init_dist_es();
|
|
780
|
+
init_dist_es3();
|
|
781
|
+
init_build_abort_error();
|
|
782
|
+
init_get_transformed_headers();
|
|
783
|
+
init_node_http2_connection_manager();
|
|
784
|
+
init_write_request_body();
|
|
785
|
+
NodeHttp2Handler = class _NodeHttp2Handler {
|
|
786
|
+
config;
|
|
787
|
+
configProvider;
|
|
788
|
+
metadata = { handlerProtocol: "h2" };
|
|
789
|
+
connectionManager = new NodeHttp2ConnectionManager({});
|
|
790
|
+
static create(instanceOrOptions) {
|
|
791
|
+
if (typeof instanceOrOptions?.handle === "function") {
|
|
792
|
+
return instanceOrOptions;
|
|
793
|
+
}
|
|
794
|
+
return new _NodeHttp2Handler(instanceOrOptions);
|
|
795
|
+
}
|
|
796
|
+
constructor(options) {
|
|
797
|
+
this.configProvider = new Promise((resolve, reject) => {
|
|
798
|
+
if (typeof options === "function") {
|
|
799
|
+
options().then((opts) => {
|
|
800
|
+
resolve(opts || {});
|
|
801
|
+
}).catch(reject);
|
|
802
|
+
} else {
|
|
803
|
+
resolve(options || {});
|
|
804
|
+
}
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
destroy() {
|
|
808
|
+
this.connectionManager.destroy();
|
|
809
|
+
}
|
|
810
|
+
async handle(request, { abortSignal, requestTimeout } = {}) {
|
|
811
|
+
if (!this.config) {
|
|
812
|
+
this.config = await this.configProvider;
|
|
813
|
+
this.connectionManager.setDisableConcurrentStreams(this.config.disableConcurrentStreams || false);
|
|
814
|
+
if (this.config.maxConcurrentStreams) {
|
|
815
|
+
this.connectionManager.setMaxConcurrentStreams(this.config.maxConcurrentStreams);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
const { requestTimeout: configRequestTimeout, disableConcurrentStreams } = this.config;
|
|
819
|
+
const effectiveRequestTimeout = requestTimeout ?? configRequestTimeout;
|
|
820
|
+
return new Promise((_resolve, _reject) => {
|
|
821
|
+
let fulfilled = false;
|
|
822
|
+
let writeRequestBodyPromise = void 0;
|
|
823
|
+
const resolve = async (arg) => {
|
|
824
|
+
await writeRequestBodyPromise;
|
|
825
|
+
_resolve(arg);
|
|
826
|
+
};
|
|
827
|
+
const reject = async (arg) => {
|
|
828
|
+
await writeRequestBodyPromise;
|
|
829
|
+
_reject(arg);
|
|
830
|
+
};
|
|
831
|
+
if (abortSignal?.aborted) {
|
|
832
|
+
fulfilled = true;
|
|
833
|
+
const abortError = buildAbortError(abortSignal);
|
|
834
|
+
reject(abortError);
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
const { hostname, method, port, protocol, query } = request;
|
|
838
|
+
let auth = "";
|
|
839
|
+
if (request.username != null || request.password != null) {
|
|
840
|
+
const username = request.username ?? "";
|
|
841
|
+
const password = request.password ?? "";
|
|
842
|
+
auth = `${username}:${password}@`;
|
|
843
|
+
}
|
|
844
|
+
const authority = `${protocol}//${auth}${hostname}${port ? `:${port}` : ""}`;
|
|
845
|
+
const requestContext = { destination: new URL(authority) };
|
|
846
|
+
const session = this.connectionManager.lease(requestContext, {
|
|
847
|
+
requestTimeout: this.config?.sessionTimeout,
|
|
848
|
+
disableConcurrentStreams: disableConcurrentStreams || false
|
|
849
|
+
});
|
|
850
|
+
const rejectWithDestroy = (err) => {
|
|
851
|
+
if (disableConcurrentStreams) {
|
|
852
|
+
this.destroySession(session);
|
|
853
|
+
}
|
|
854
|
+
fulfilled = true;
|
|
855
|
+
reject(err);
|
|
856
|
+
};
|
|
857
|
+
const queryString = buildQueryString(query || {});
|
|
858
|
+
let path2 = request.path;
|
|
859
|
+
if (queryString) {
|
|
860
|
+
path2 += `?${queryString}`;
|
|
861
|
+
}
|
|
862
|
+
if (request.fragment) {
|
|
863
|
+
path2 += `#${request.fragment}`;
|
|
864
|
+
}
|
|
865
|
+
const req = session.request({
|
|
866
|
+
...request.headers,
|
|
867
|
+
[constants.HTTP2_HEADER_PATH]: path2,
|
|
868
|
+
[constants.HTTP2_HEADER_METHOD]: method
|
|
869
|
+
});
|
|
870
|
+
session.ref();
|
|
871
|
+
req.on("response", (headers) => {
|
|
872
|
+
const httpResponse = new HttpResponse({
|
|
873
|
+
statusCode: headers[":status"] || -1,
|
|
874
|
+
headers: getTransformedHeaders(headers),
|
|
875
|
+
body: req
|
|
876
|
+
});
|
|
877
|
+
fulfilled = true;
|
|
878
|
+
resolve({ response: httpResponse });
|
|
879
|
+
if (disableConcurrentStreams) {
|
|
880
|
+
session.close();
|
|
881
|
+
this.connectionManager.deleteSession(authority, session);
|
|
882
|
+
}
|
|
883
|
+
});
|
|
884
|
+
if (effectiveRequestTimeout) {
|
|
885
|
+
req.setTimeout(effectiveRequestTimeout, () => {
|
|
886
|
+
req.close();
|
|
887
|
+
const timeoutError = new Error(`Stream timed out because of no activity for ${effectiveRequestTimeout} ms`);
|
|
888
|
+
timeoutError.name = "TimeoutError";
|
|
889
|
+
rejectWithDestroy(timeoutError);
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
if (abortSignal) {
|
|
893
|
+
const onAbort = () => {
|
|
894
|
+
req.close();
|
|
895
|
+
const abortError = buildAbortError(abortSignal);
|
|
896
|
+
rejectWithDestroy(abortError);
|
|
897
|
+
};
|
|
898
|
+
if (typeof abortSignal.addEventListener === "function") {
|
|
899
|
+
const signal = abortSignal;
|
|
900
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
901
|
+
req.once("close", () => signal.removeEventListener("abort", onAbort));
|
|
902
|
+
} else {
|
|
903
|
+
abortSignal.onabort = onAbort;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
req.on("frameError", (type, code, id) => {
|
|
907
|
+
rejectWithDestroy(new Error(`Frame type id ${type} in stream id ${id} has failed with code ${code}.`));
|
|
908
|
+
});
|
|
909
|
+
req.on("error", rejectWithDestroy);
|
|
910
|
+
req.on("aborted", () => {
|
|
911
|
+
rejectWithDestroy(new Error(`HTTP/2 stream is abnormally aborted in mid-communication with result code ${req.rstCode}.`));
|
|
912
|
+
});
|
|
913
|
+
req.on("close", () => {
|
|
914
|
+
session.unref();
|
|
915
|
+
if (disableConcurrentStreams) {
|
|
916
|
+
session.destroy();
|
|
917
|
+
}
|
|
918
|
+
if (!fulfilled) {
|
|
919
|
+
rejectWithDestroy(new Error("Unexpected error: http2 request did not get a response"));
|
|
920
|
+
}
|
|
921
|
+
});
|
|
922
|
+
writeRequestBodyPromise = writeRequestBody(req, request, effectiveRequestTimeout);
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
updateHttpClientConfig(key, value) {
|
|
926
|
+
this.config = void 0;
|
|
927
|
+
this.configProvider = this.configProvider.then((config) => {
|
|
928
|
+
return {
|
|
929
|
+
...config,
|
|
930
|
+
[key]: value
|
|
931
|
+
};
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
httpHandlerConfigs() {
|
|
935
|
+
return this.config ?? {};
|
|
936
|
+
}
|
|
937
|
+
destroySession(session) {
|
|
938
|
+
if (!session.destroyed) {
|
|
939
|
+
session.destroy();
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
});
|
|
945
|
+
var Collector;
|
|
946
|
+
var init_collector = __esm({
|
|
947
|
+
"node_modules/@smithy/node-http-handler/dist-es/stream-collector/collector.js"() {
|
|
948
|
+
Collector = class extends Writable {
|
|
949
|
+
bufferedBytes = [];
|
|
950
|
+
_write(chunk, encoding, callback) {
|
|
951
|
+
this.bufferedBytes.push(chunk);
|
|
952
|
+
callback();
|
|
953
|
+
}
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
});
|
|
957
|
+
|
|
958
|
+
// node_modules/@smithy/node-http-handler/dist-es/stream-collector/index.js
|
|
959
|
+
async function collectReadableStream(stream) {
|
|
960
|
+
const chunks = [];
|
|
961
|
+
const reader = stream.getReader();
|
|
962
|
+
let isDone = false;
|
|
963
|
+
let length = 0;
|
|
964
|
+
while (!isDone) {
|
|
965
|
+
const { done, value } = await reader.read();
|
|
966
|
+
if (value) {
|
|
967
|
+
chunks.push(value);
|
|
968
|
+
length += value.length;
|
|
969
|
+
}
|
|
970
|
+
isDone = done;
|
|
971
|
+
}
|
|
972
|
+
const collected = new Uint8Array(length);
|
|
973
|
+
let offset = 0;
|
|
974
|
+
for (const chunk of chunks) {
|
|
975
|
+
collected.set(chunk, offset);
|
|
976
|
+
offset += chunk.length;
|
|
977
|
+
}
|
|
978
|
+
return collected;
|
|
979
|
+
}
|
|
980
|
+
var streamCollector, isReadableStreamInstance;
|
|
981
|
+
var init_stream_collector = __esm({
|
|
982
|
+
"node_modules/@smithy/node-http-handler/dist-es/stream-collector/index.js"() {
|
|
983
|
+
init_collector();
|
|
984
|
+
streamCollector = (stream) => {
|
|
985
|
+
if (isReadableStreamInstance(stream)) {
|
|
986
|
+
return collectReadableStream(stream);
|
|
987
|
+
}
|
|
988
|
+
return new Promise((resolve, reject) => {
|
|
989
|
+
const collector = new Collector();
|
|
990
|
+
stream.pipe(collector);
|
|
991
|
+
stream.on("error", (err) => {
|
|
992
|
+
collector.end();
|
|
993
|
+
reject(err);
|
|
994
|
+
});
|
|
995
|
+
collector.on("error", reject);
|
|
996
|
+
collector.on("finish", function() {
|
|
997
|
+
const bytes = new Uint8Array(Buffer.concat(this.bufferedBytes));
|
|
998
|
+
resolve(bytes);
|
|
999
|
+
});
|
|
1000
|
+
});
|
|
1001
|
+
};
|
|
1002
|
+
isReadableStreamInstance = (stream) => typeof ReadableStream === "function" && stream instanceof ReadableStream;
|
|
1003
|
+
}
|
|
1004
|
+
});
|
|
1005
|
+
|
|
1006
|
+
// node_modules/@smithy/node-http-handler/dist-es/index.js
|
|
1007
|
+
var dist_es_exports = {};
|
|
1008
|
+
__export(dist_es_exports, {
|
|
1009
|
+
DEFAULT_REQUEST_TIMEOUT: () => DEFAULT_REQUEST_TIMEOUT,
|
|
1010
|
+
NodeHttp2Handler: () => NodeHttp2Handler,
|
|
1011
|
+
NodeHttpHandler: () => NodeHttpHandler,
|
|
1012
|
+
streamCollector: () => streamCollector
|
|
1013
|
+
});
|
|
1014
|
+
var init_dist_es4 = __esm({
|
|
1015
|
+
"node_modules/@smithy/node-http-handler/dist-es/index.js"() {
|
|
1016
|
+
init_node_http_handler();
|
|
1017
|
+
init_node_http2_handler();
|
|
1018
|
+
init_stream_collector();
|
|
1019
|
+
}
|
|
1020
|
+
});
|
|
27
1021
|
|
|
28
1022
|
// src/registry/validator.ts
|
|
29
1023
|
var ConfigValidationError = class extends Error {
|
|
@@ -377,10 +1371,7 @@ function checkboxToZod(field) {
|
|
|
377
1371
|
return schema;
|
|
378
1372
|
}
|
|
379
1373
|
function dateToZod(field) {
|
|
380
|
-
let schema = z.string().refine(
|
|
381
|
-
(val) => !isNaN(Date.parse(val)),
|
|
382
|
-
"Invalid date format"
|
|
383
|
-
);
|
|
1374
|
+
let schema = z.string().refine((val) => !isNaN(Date.parse(val)), "Invalid date format");
|
|
384
1375
|
if (field.minDate) {
|
|
385
1376
|
schema = schema.refine(
|
|
386
1377
|
(val) => new Date(val) >= new Date(field.minDate),
|
|
@@ -438,19 +1429,28 @@ function radioToZod(field) {
|
|
|
438
1429
|
}
|
|
439
1430
|
function colorToZod(field) {
|
|
440
1431
|
let schema = z.string();
|
|
441
|
-
if (field.format === "hex")
|
|
442
|
-
|
|
443
|
-
if (field.format === "
|
|
1432
|
+
if (field.format === "hex")
|
|
1433
|
+
schema = schema.regex(/^#[0-9A-Fa-f]{6}$/);
|
|
1434
|
+
if (field.format === "rgb")
|
|
1435
|
+
schema = schema.regex(
|
|
1436
|
+
/^rgb\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\)$/
|
|
1437
|
+
);
|
|
1438
|
+
if (field.format === "hsl")
|
|
1439
|
+
schema = schema.regex(
|
|
1440
|
+
/^hsl\(\s*\d{1,3}\s*,\s*\d{1,3}%\s*,\s*\d{1,3}%\s*\)$/
|
|
1441
|
+
);
|
|
444
1442
|
if (!field.required) schema = schema.optional();
|
|
445
1443
|
if (field.validate) schema = addCustomValidation(schema, field.validate);
|
|
446
1444
|
return schema;
|
|
447
1445
|
}
|
|
448
1446
|
function richTextToZod(field) {
|
|
449
|
-
let schema = z.array(
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
1447
|
+
let schema = z.array(
|
|
1448
|
+
z.object({
|
|
1449
|
+
type: z.string(),
|
|
1450
|
+
data: z.record(z.any()),
|
|
1451
|
+
children: z.array(z.any()).optional()
|
|
1452
|
+
})
|
|
1453
|
+
);
|
|
454
1454
|
if (!field.required) schema = schema.optional();
|
|
455
1455
|
if (field.validate) schema = addCustomValidation(schema, field.validate);
|
|
456
1456
|
return schema;
|
|
@@ -516,7 +1516,8 @@ function groupToZod(field) {
|
|
|
516
1516
|
return schema;
|
|
517
1517
|
}
|
|
518
1518
|
function blocksToZod(field) {
|
|
519
|
-
const
|
|
1519
|
+
const blocks = field.blocks || [];
|
|
1520
|
+
const blockSchemas = blocks.map((block) => {
|
|
520
1521
|
return z.object({
|
|
521
1522
|
blockType: z.literal(block.slug),
|
|
522
1523
|
...Object.fromEntries(
|
|
@@ -524,7 +1525,9 @@ function blocksToZod(field) {
|
|
|
524
1525
|
)
|
|
525
1526
|
});
|
|
526
1527
|
});
|
|
527
|
-
let schema = z.array(
|
|
1528
|
+
let schema = z.array(
|
|
1529
|
+
z.discriminatedUnion("blockType", blockSchemas)
|
|
1530
|
+
);
|
|
528
1531
|
if (field.minRows) schema = schema.min(field.minRows);
|
|
529
1532
|
if (field.maxRows) schema = schema.max(field.maxRows);
|
|
530
1533
|
if (!field.required) schema = schema.optional();
|
|
@@ -984,6 +1987,7 @@ var Kyro = class {
|
|
|
984
1987
|
registry;
|
|
985
1988
|
db;
|
|
986
1989
|
pubsub;
|
|
1990
|
+
settings;
|
|
987
1991
|
wsServer;
|
|
988
1992
|
config;
|
|
989
1993
|
constructor(config) {
|
|
@@ -1005,7 +2009,10 @@ var Kyro = class {
|
|
|
1005
2009
|
}
|
|
1006
2010
|
async init() {
|
|
1007
2011
|
await this.registry.init();
|
|
1008
|
-
await this.db.init(
|
|
2012
|
+
await this.db.init(
|
|
2013
|
+
this.registry.getCollections(),
|
|
2014
|
+
this.registry.getGlobals()
|
|
2015
|
+
);
|
|
1009
2016
|
this.pubsub.autoRegisterHooks();
|
|
1010
2017
|
console.log("\u2705 Kyro CMS initialized");
|
|
1011
2018
|
console.log(` Collections: ${this.registry.getCollections().length}`);
|
|
@@ -1014,19 +2021,37 @@ var Kyro = class {
|
|
|
1014
2021
|
// ============================================================================
|
|
1015
2022
|
// API Methods
|
|
1016
2023
|
// ============================================================================
|
|
2024
|
+
// Load settings from globals if not already loaded
|
|
2025
|
+
async loadSettings() {
|
|
2026
|
+
if (this.settings) return this.settings;
|
|
2027
|
+
try {
|
|
2028
|
+
const accessSettings = await this.db.findOne({
|
|
2029
|
+
collection: "_globals",
|
|
2030
|
+
where: { slug: "access-settings" }
|
|
2031
|
+
});
|
|
2032
|
+
if (accessSettings) {
|
|
2033
|
+
this.settings = accessSettings.data;
|
|
2034
|
+
}
|
|
2035
|
+
} catch (e) {
|
|
2036
|
+
console.log("\u26A0\uFE0F No access-settings found, using defaults");
|
|
2037
|
+
}
|
|
2038
|
+
return this.settings || {};
|
|
2039
|
+
}
|
|
1017
2040
|
getREST(options) {
|
|
1018
2041
|
return createHonoApp({
|
|
1019
2042
|
registry: this.registry,
|
|
1020
2043
|
db: this.db,
|
|
1021
2044
|
...options,
|
|
1022
|
-
cors: this.config.cors
|
|
2045
|
+
cors: this.config.cors,
|
|
2046
|
+
settings: this.settings
|
|
1023
2047
|
});
|
|
1024
2048
|
}
|
|
1025
2049
|
getGraphQL(options) {
|
|
1026
2050
|
return buildGraphQLSchema({
|
|
1027
2051
|
registry: this.registry,
|
|
1028
2052
|
db: this.db,
|
|
1029
|
-
...options
|
|
2053
|
+
...options,
|
|
2054
|
+
settings: this.settings
|
|
1030
2055
|
});
|
|
1031
2056
|
}
|
|
1032
2057
|
getTRPC(options) {
|
|
@@ -1034,14 +2059,20 @@ var Kyro = class {
|
|
|
1034
2059
|
registry: this.registry,
|
|
1035
2060
|
db: this.db,
|
|
1036
2061
|
req: options?.req || { headers: {} },
|
|
1037
|
-
...options
|
|
2062
|
+
...options,
|
|
2063
|
+
settings: this.settings
|
|
1038
2064
|
});
|
|
1039
2065
|
}
|
|
1040
2066
|
async startWebSocket(options) {
|
|
2067
|
+
const apiAccess = this.settings?.access?.apiAccess;
|
|
2068
|
+
if (apiAccess?.websocketEnabled === false) {
|
|
2069
|
+
console.log("\u26A0\uFE0F WebSocket is disabled in settings");
|
|
2070
|
+
return null;
|
|
2071
|
+
}
|
|
1041
2072
|
this.wsServer = createWSServer({
|
|
1042
2073
|
pubsub: this.pubsub,
|
|
1043
2074
|
port: options?.port || 8080,
|
|
1044
|
-
requireAuth: options?.requireAuth,
|
|
2075
|
+
requireAuth: options?.requireAuth ?? apiAccess?.requireAuth,
|
|
1045
2076
|
verifyToken: options?.verifyToken
|
|
1046
2077
|
});
|
|
1047
2078
|
console.log(`\u{1F50C} WebSocket server started on port ${options?.port || 8080}`);
|
|
@@ -1118,11 +2149,7 @@ var RELATIONAL_FIELD_TYPES = [
|
|
|
1118
2149
|
"group",
|
|
1119
2150
|
"blocks"
|
|
1120
2151
|
];
|
|
1121
|
-
var LAYOUT_FIELD_TYPES = [
|
|
1122
|
-
"row",
|
|
1123
|
-
"collapsible",
|
|
1124
|
-
"tabs"
|
|
1125
|
-
];
|
|
2152
|
+
var LAYOUT_FIELD_TYPES = ["row", "collapsible", "tabs"];
|
|
1126
2153
|
var ALL_FIELD_TYPES = [
|
|
1127
2154
|
...PRIMITIVE_FIELD_TYPES,
|
|
1128
2155
|
...COMPLEX_FIELD_TYPES,
|
|
@@ -1130,42 +2157,450 @@ var ALL_FIELD_TYPES = [
|
|
|
1130
2157
|
...LAYOUT_FIELD_TYPES
|
|
1131
2158
|
];
|
|
1132
2159
|
|
|
1133
|
-
// src/
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
});
|
|
1141
|
-
if (hookResult !== void 0) {
|
|
1142
|
-
result = hookResult;
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
return result;
|
|
2160
|
+
// src/fields/richtext.ts
|
|
2161
|
+
var MIN_COLUMNS = 1;
|
|
2162
|
+
var MAX_COLUMNS = 6;
|
|
2163
|
+
var richTextStyles = `
|
|
2164
|
+
.kyro-richtext {
|
|
2165
|
+
color: inherit;
|
|
2166
|
+
line-height: 1.7;
|
|
1146
2167
|
}
|
|
1147
|
-
|
|
1148
|
-
|
|
2168
|
+
|
|
2169
|
+
.kyro-richtext > *:first-child {
|
|
2170
|
+
margin-top: 0;
|
|
1149
2171
|
}
|
|
1150
2172
|
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
2173
|
+
.kyro-richtext > *:last-child {
|
|
2174
|
+
margin-bottom: 0;
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2177
|
+
.kyro-richtext p,
|
|
2178
|
+
.kyro-richtext ul,
|
|
2179
|
+
.kyro-richtext ol,
|
|
2180
|
+
.kyro-richtext blockquote,
|
|
2181
|
+
.kyro-richtext pre,
|
|
2182
|
+
.kyro-richtext kyro-callout,
|
|
2183
|
+
.kyro-richtext kyro-hero,
|
|
2184
|
+
.kyro-richtext kyro-heading,
|
|
2185
|
+
.kyro-richtext kyro-stack,
|
|
2186
|
+
.kyro-richtext kyro-columns {
|
|
2187
|
+
margin: 0 0 1rem;
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
.kyro-richtext h1,
|
|
2191
|
+
.kyro-richtext h2,
|
|
2192
|
+
.kyro-richtext h3,
|
|
2193
|
+
.kyro-richtext h4,
|
|
2194
|
+
.kyro-richtext h5,
|
|
2195
|
+
.kyro-richtext h6 {
|
|
2196
|
+
margin: 0 0 0.75rem;
|
|
2197
|
+
line-height: 1.2;
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2200
|
+
.kyro-richtext ul,
|
|
2201
|
+
.kyro-richtext ol {
|
|
2202
|
+
padding-left: 1.5rem;
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2205
|
+
.kyro-richtext blockquote {
|
|
2206
|
+
border-left: 4px solid rgba(148, 163, 184, 0.5);
|
|
2207
|
+
margin-left: 0;
|
|
2208
|
+
padding-left: 1rem;
|
|
2209
|
+
font-style: italic;
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
.kyro-richtext pre {
|
|
2213
|
+
overflow-x: auto;
|
|
2214
|
+
border-radius: 0.75rem;
|
|
2215
|
+
background: rgba(15, 23, 42, 0.92);
|
|
2216
|
+
color: #f8fafc;
|
|
2217
|
+
padding: 1rem;
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
.kyro-richtext code {
|
|
2221
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
.kyro-richtext img {
|
|
2225
|
+
display: block;
|
|
2226
|
+
max-width: 100%;
|
|
2227
|
+
height: auto;
|
|
2228
|
+
border-radius: 0.75rem;
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2231
|
+
.kyro-richtext kyro-columns {
|
|
2232
|
+
display: grid;
|
|
2233
|
+
grid-template-columns: repeat(var(--kyro-columns-count, 1), minmax(0, 1fr));
|
|
2234
|
+
gap: 1rem;
|
|
2235
|
+
}
|
|
2236
|
+
|
|
2237
|
+
.kyro-richtext kyro-column {
|
|
2238
|
+
display: block;
|
|
2239
|
+
min-width: 0;
|
|
2240
|
+
}
|
|
2241
|
+
|
|
2242
|
+
.kyro-richtext kyro-column > *:last-child {
|
|
2243
|
+
margin-bottom: 0;
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
.kyro-richtext kyro-hero,
|
|
2247
|
+
.kyro-richtext kyro-callout,
|
|
2248
|
+
.kyro-richtext kyro-stack,
|
|
2249
|
+
.kyro-richtext kyro-heading {
|
|
2250
|
+
display: block;
|
|
2251
|
+
}
|
|
2252
|
+
|
|
2253
|
+
.kyro-richtext kyro-hero {
|
|
2254
|
+
border: 1px solid rgba(148, 163, 184, 0.25);
|
|
2255
|
+
border-radius: 1rem;
|
|
2256
|
+
padding: 1.5rem;
|
|
2257
|
+
text-align: center;
|
|
2258
|
+
background: linear-gradient(to bottom, rgba(148, 163, 184, 0.12), transparent);
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
.kyro-richtext kyro-heading {
|
|
2262
|
+
font-size: 1.125rem;
|
|
2263
|
+
font-weight: 700;
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
.kyro-richtext kyro-callout {
|
|
2267
|
+
border: 1px solid rgba(59, 130, 246, 0.25);
|
|
2268
|
+
border-radius: 0.875rem;
|
|
2269
|
+
padding: 0.875rem 1rem;
|
|
2270
|
+
background: rgba(59, 130, 246, 0.06);
|
|
2271
|
+
}
|
|
2272
|
+
|
|
2273
|
+
@media (max-width: 720px) {
|
|
2274
|
+
.kyro-richtext kyro-columns {
|
|
2275
|
+
grid-template-columns: 1fr;
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
`.trim();
|
|
2279
|
+
function createEmptyParagraphNode() {
|
|
2280
|
+
return {
|
|
2281
|
+
type: "paragraph",
|
|
2282
|
+
content: []
|
|
2283
|
+
};
|
|
2284
|
+
}
|
|
2285
|
+
function createColumnNode(content = [createEmptyParagraphNode()]) {
|
|
2286
|
+
return {
|
|
2287
|
+
type: "kyroColumn",
|
|
2288
|
+
content: content.length > 0 ? content : [createEmptyParagraphNode()]
|
|
2289
|
+
};
|
|
2290
|
+
}
|
|
2291
|
+
function createColumnsNode(columns = 2) {
|
|
2292
|
+
const count = clampColumns(columns);
|
|
2293
|
+
return {
|
|
2294
|
+
type: "kyroColumns",
|
|
2295
|
+
attrs: { columns: count },
|
|
2296
|
+
content: Array.from({ length: count }, () => createColumnNode())
|
|
2297
|
+
};
|
|
2298
|
+
}
|
|
2299
|
+
function normalizeRichTextDocument(value) {
|
|
2300
|
+
const documentNode = toRichTextDocument(value);
|
|
2301
|
+
return {
|
|
2302
|
+
type: "doc",
|
|
2303
|
+
content: normalizeNodes(documentNode.content)
|
|
2304
|
+
};
|
|
2305
|
+
}
|
|
2306
|
+
function normalizeRichTextValue(value) {
|
|
2307
|
+
if (Array.isArray(value)) {
|
|
2308
|
+
return normalizeRichTextDocument(value).content;
|
|
2309
|
+
}
|
|
2310
|
+
if (isObject(value) && value.type === "doc") {
|
|
2311
|
+
return normalizeRichTextDocument(value);
|
|
2312
|
+
}
|
|
2313
|
+
return value;
|
|
2314
|
+
}
|
|
2315
|
+
function renderRichText(value) {
|
|
2316
|
+
if (typeof value === "string") {
|
|
2317
|
+
return `<div class="kyro-richtext">${value}</div>`;
|
|
2318
|
+
}
|
|
2319
|
+
const documentNode = normalizeRichTextDocument(value);
|
|
2320
|
+
return `<div class="kyro-richtext">${renderNodes(documentNode.content)}</div>`;
|
|
2321
|
+
}
|
|
2322
|
+
function toRichTextDocument(value) {
|
|
2323
|
+
if (Array.isArray(value)) {
|
|
2324
|
+
return {
|
|
2325
|
+
type: "doc",
|
|
2326
|
+
content: normalizeNodes(value)
|
|
2327
|
+
};
|
|
2328
|
+
}
|
|
2329
|
+
if (isObject(value) && value.type === "doc") {
|
|
2330
|
+
const doc = value;
|
|
2331
|
+
return {
|
|
2332
|
+
type: "doc",
|
|
2333
|
+
content: normalizeNodes(doc.content ?? [])
|
|
2334
|
+
};
|
|
2335
|
+
}
|
|
2336
|
+
return {
|
|
2337
|
+
type: "doc",
|
|
2338
|
+
content: []
|
|
2339
|
+
};
|
|
2340
|
+
}
|
|
2341
|
+
function normalizeNodes(nodes) {
|
|
2342
|
+
return nodes.filter((node) => isObject(node) && typeof node.type === "string").map((node) => normalizeNode(node));
|
|
2343
|
+
}
|
|
2344
|
+
function normalizeNode(node) {
|
|
2345
|
+
if (node.type === "kyroColumns") {
|
|
2346
|
+
return normalizeColumnsNode(node);
|
|
2347
|
+
}
|
|
2348
|
+
if (node.type === "kyroColumn") {
|
|
2349
|
+
const normalizedContent = normalizeNodes(
|
|
2350
|
+
Array.isArray(node.content) ? node.content : []
|
|
2351
|
+
).filter((child) => child.type !== "kyroColumns");
|
|
2352
|
+
return {
|
|
2353
|
+
...node,
|
|
2354
|
+
content: normalizedContent.length > 0 ? normalizedContent : [createEmptyParagraphNode()]
|
|
2355
|
+
};
|
|
2356
|
+
}
|
|
2357
|
+
if (Array.isArray(node.content)) {
|
|
2358
|
+
return {
|
|
2359
|
+
...node,
|
|
2360
|
+
content: normalizeNodes(node.content)
|
|
2361
|
+
};
|
|
2362
|
+
}
|
|
2363
|
+
return {
|
|
2364
|
+
...node
|
|
2365
|
+
};
|
|
2366
|
+
}
|
|
2367
|
+
function normalizeColumnsNode(node) {
|
|
2368
|
+
const rawChildren = Array.isArray(node.content) ? node.content : [];
|
|
2369
|
+
const hasStructuredColumns = rawChildren.every((child) => child?.type === "kyroColumn");
|
|
2370
|
+
const targetColumns = clampColumns(
|
|
2371
|
+
typeof node.attrs?.columns === "number" ? node.attrs.columns : hasStructuredColumns && rawChildren.length > 0 ? rawChildren.length : 2
|
|
2372
|
+
);
|
|
2373
|
+
const content = hasStructuredColumns ? normalizeStructuredColumns(rawChildren, targetColumns) : normalizeLegacyColumns(rawChildren, targetColumns);
|
|
2374
|
+
return {
|
|
2375
|
+
...node,
|
|
2376
|
+
attrs: {
|
|
2377
|
+
...node.attrs ?? {},
|
|
2378
|
+
columns: content.length
|
|
2379
|
+
},
|
|
2380
|
+
content
|
|
2381
|
+
};
|
|
2382
|
+
}
|
|
2383
|
+
function normalizeStructuredColumns(columns, targetColumns) {
|
|
2384
|
+
const normalized = columns.slice(0, MAX_COLUMNS).map((column) => normalizeNode(column)).filter((column) => column.type === "kyroColumn");
|
|
2385
|
+
if (normalized.length === 0) {
|
|
2386
|
+
return Array.from({ length: targetColumns }, () => createColumnNode());
|
|
2387
|
+
}
|
|
2388
|
+
if (normalized.length > targetColumns) {
|
|
2389
|
+
const kept = normalized.slice(0, targetColumns);
|
|
2390
|
+
const extras = normalized.slice(targetColumns);
|
|
2391
|
+
const mergedTail = extras.flatMap(
|
|
2392
|
+
(column) => normalizeColumnChildren(column.content)
|
|
2393
|
+
);
|
|
2394
|
+
const lastIndex = kept.length - 1;
|
|
2395
|
+
kept[lastIndex] = createColumnNode([
|
|
2396
|
+
...normalizeColumnChildren(kept[lastIndex].content),
|
|
2397
|
+
...mergedTail
|
|
2398
|
+
]);
|
|
2399
|
+
return kept;
|
|
2400
|
+
}
|
|
2401
|
+
if (normalized.length < targetColumns) {
|
|
2402
|
+
return [
|
|
2403
|
+
...normalized,
|
|
2404
|
+
...Array.from(
|
|
2405
|
+
{ length: targetColumns - normalized.length },
|
|
2406
|
+
() => createColumnNode()
|
|
2407
|
+
)
|
|
2408
|
+
];
|
|
2409
|
+
}
|
|
2410
|
+
return normalized;
|
|
2411
|
+
}
|
|
2412
|
+
function normalizeLegacyColumns(blocks, targetColumns) {
|
|
2413
|
+
const normalizedBlocks = normalizeNodes(blocks);
|
|
2414
|
+
return Array.from({ length: targetColumns }, (_, index) => {
|
|
2415
|
+
if (index < targetColumns - 1) {
|
|
2416
|
+
const block = normalizedBlocks[index];
|
|
2417
|
+
return block ? createColumnNode([block]) : createColumnNode();
|
|
2418
|
+
}
|
|
2419
|
+
const tail = normalizedBlocks.slice(index);
|
|
2420
|
+
return createColumnNode(tail.length > 0 ? tail : [createEmptyParagraphNode()]);
|
|
2421
|
+
});
|
|
2422
|
+
}
|
|
2423
|
+
function normalizeColumnChildren(content) {
|
|
2424
|
+
const children = Array.isArray(content) ? normalizeNodes(content) : [];
|
|
2425
|
+
const filtered = children.filter((child) => child.type !== "kyroColumns");
|
|
2426
|
+
return filtered.length > 0 ? filtered : [createEmptyParagraphNode()];
|
|
2427
|
+
}
|
|
2428
|
+
function renderNodes(nodes) {
|
|
2429
|
+
return nodes.map((node) => renderNode(node)).join("");
|
|
2430
|
+
}
|
|
2431
|
+
function renderNode(node) {
|
|
2432
|
+
switch (node.type) {
|
|
2433
|
+
case "paragraph":
|
|
2434
|
+
return `<p>${renderInlineContent(node.content)}</p>`;
|
|
2435
|
+
case "text":
|
|
2436
|
+
return renderTextNode(node);
|
|
2437
|
+
case "heading": {
|
|
2438
|
+
const level = clampHeadingLevel(node.attrs?.level);
|
|
2439
|
+
return `<h${level}>${renderInlineContent(node.content)}</h${level}>`;
|
|
2440
|
+
}
|
|
2441
|
+
case "bulletList":
|
|
2442
|
+
return `<ul>${renderNodes(node.content ?? [])}</ul>`;
|
|
2443
|
+
case "orderedList":
|
|
2444
|
+
return `<ol>${renderNodes(node.content ?? [])}</ol>`;
|
|
2445
|
+
case "listItem":
|
|
2446
|
+
return `<li>${renderNodes(node.content ?? [])}</li>`;
|
|
2447
|
+
case "blockquote":
|
|
2448
|
+
return `<blockquote>${renderNodes(node.content ?? [])}</blockquote>`;
|
|
2449
|
+
case "codeBlock":
|
|
2450
|
+
return `<pre><code>${escapeHtml(extractText(node.content ?? []))}</code></pre>`;
|
|
2451
|
+
case "horizontalRule":
|
|
2452
|
+
return "<hr />";
|
|
2453
|
+
case "hardBreak":
|
|
2454
|
+
return "<br />";
|
|
2455
|
+
case "image":
|
|
2456
|
+
return renderImageNode(node);
|
|
2457
|
+
case "kyroColumns": {
|
|
2458
|
+
const columns = clampColumns(
|
|
2459
|
+
Array.isArray(node.content) ? node.content.length : 1
|
|
2460
|
+
);
|
|
2461
|
+
return `<kyro-columns data-columns="${columns}" style="--kyro-columns-count:${columns}">${renderNodes(node.content ?? [])}</kyro-columns>`;
|
|
2462
|
+
}
|
|
2463
|
+
case "kyroColumn":
|
|
2464
|
+
return `<kyro-column>${renderNodes(node.content ?? [])}</kyro-column>`;
|
|
2465
|
+
case "kyroHero":
|
|
2466
|
+
return `<kyro-hero>${renderInlineContent(node.content)}</kyro-hero>`;
|
|
2467
|
+
case "kyroHeading":
|
|
2468
|
+
return `<kyro-heading>${renderInlineContent(node.content)}</kyro-heading>`;
|
|
2469
|
+
case "kyroDivider":
|
|
2470
|
+
return '<hr class="kyro-divider" />';
|
|
2471
|
+
case "kyroCallout":
|
|
2472
|
+
return `<kyro-callout>${renderInlineContent(node.content)}</kyro-callout>`;
|
|
2473
|
+
case "kyroStack":
|
|
2474
|
+
return `<kyro-stack>${renderNodes(node.content ?? [])}</kyro-stack>`;
|
|
2475
|
+
case "kyroImage":
|
|
2476
|
+
return renderCustomMediaNode("kyro-image", node);
|
|
2477
|
+
case "kyroVideo":
|
|
2478
|
+
return renderCustomMediaNode("kyro-video", node);
|
|
2479
|
+
default:
|
|
2480
|
+
return renderNodes(node.content ?? []);
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
function renderInlineContent(content) {
|
|
2484
|
+
const nodes = content ?? [];
|
|
2485
|
+
if (nodes.length === 0) {
|
|
2486
|
+
return "<br />";
|
|
2487
|
+
}
|
|
2488
|
+
return nodes.map((node) => renderNode(node)).join("");
|
|
2489
|
+
}
|
|
2490
|
+
function renderTextNode(node) {
|
|
2491
|
+
const text = typeof node.text === "string" ? escapeHtml(node.text) : "";
|
|
2492
|
+
return (node.marks ?? []).reduce((acc, mark) => applyMark(acc, mark), text);
|
|
2493
|
+
}
|
|
2494
|
+
function renderImageNode(node) {
|
|
2495
|
+
const src = typeof node.attrs?.src === "string" ? node.attrs.src : "";
|
|
2496
|
+
if (!src) {
|
|
2497
|
+
return "";
|
|
2498
|
+
}
|
|
2499
|
+
const alt = typeof node.attrs?.alt === "string" ? node.attrs.alt : "";
|
|
2500
|
+
return `<img src="${escapeAttribute(src)}" alt="${escapeAttribute(alt)}" />`;
|
|
2501
|
+
}
|
|
2502
|
+
function renderCustomMediaNode(tagName, node) {
|
|
2503
|
+
const attrs = renderAttributes(node.attrs);
|
|
2504
|
+
return `<${tagName}${attrs}></${tagName}>`;
|
|
2505
|
+
}
|
|
2506
|
+
function renderAttributes(attrs, keysToSkip = []) {
|
|
2507
|
+
if (!attrs) {
|
|
2508
|
+
return "";
|
|
2509
|
+
}
|
|
2510
|
+
const entries = Object.entries(attrs).filter(([key, value]) => !keysToSkip.includes(key) && value !== void 0 && value !== null).map(([key, value]) => `${key}="${escapeAttribute(String(value))}"`);
|
|
2511
|
+
return entries.length > 0 ? ` ${entries.join(" ")}` : "";
|
|
2512
|
+
}
|
|
2513
|
+
function applyMark(content, mark) {
|
|
2514
|
+
switch (mark.type) {
|
|
2515
|
+
case "bold":
|
|
2516
|
+
return `<strong>${content}</strong>`;
|
|
2517
|
+
case "italic":
|
|
2518
|
+
return `<em>${content}</em>`;
|
|
2519
|
+
case "underline":
|
|
2520
|
+
return `<u>${content}</u>`;
|
|
2521
|
+
case "strike":
|
|
2522
|
+
return `<s>${content}</s>`;
|
|
2523
|
+
case "code":
|
|
2524
|
+
return `<code>${content}</code>`;
|
|
2525
|
+
case "link": {
|
|
2526
|
+
const href = typeof mark.attrs?.href === "string" ? escapeAttribute(mark.attrs.href) : "#";
|
|
2527
|
+
return `<a href="${href}" target="_blank" rel="noopener noreferrer">${content}</a>`;
|
|
2528
|
+
}
|
|
2529
|
+
default:
|
|
2530
|
+
return content;
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
function extractText(nodes) {
|
|
2534
|
+
return nodes.map((node) => {
|
|
2535
|
+
if (node.type === "text") {
|
|
2536
|
+
return typeof node.text === "string" ? node.text : "";
|
|
2537
|
+
}
|
|
2538
|
+
if (node.type === "hardBreak") {
|
|
2539
|
+
return "\n";
|
|
2540
|
+
}
|
|
2541
|
+
return extractText(node.content ?? []);
|
|
2542
|
+
}).join("");
|
|
2543
|
+
}
|
|
2544
|
+
function clampColumns(value) {
|
|
2545
|
+
const numericValue = typeof value === "number" ? value : Number(value);
|
|
2546
|
+
if (!Number.isFinite(numericValue)) {
|
|
2547
|
+
return 2;
|
|
2548
|
+
}
|
|
2549
|
+
return Math.max(MIN_COLUMNS, Math.min(MAX_COLUMNS, Math.round(numericValue)));
|
|
2550
|
+
}
|
|
2551
|
+
function clampHeadingLevel(level) {
|
|
2552
|
+
const numericLevel = typeof level === "number" ? level : Number(level);
|
|
2553
|
+
if (!Number.isFinite(numericLevel)) {
|
|
2554
|
+
return 2;
|
|
2555
|
+
}
|
|
2556
|
+
return Math.max(1, Math.min(6, Math.round(numericLevel)));
|
|
2557
|
+
}
|
|
2558
|
+
function escapeHtml(value) {
|
|
2559
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
2560
|
+
}
|
|
2561
|
+
function escapeAttribute(value) {
|
|
2562
|
+
return escapeHtml(value).replaceAll('"', """);
|
|
2563
|
+
}
|
|
2564
|
+
function isObject(value) {
|
|
2565
|
+
return typeof value === "object" && value !== null;
|
|
2566
|
+
}
|
|
2567
|
+
|
|
2568
|
+
// src/hooks/types.ts
|
|
2569
|
+
async function runHooks(hooks, args) {
|
|
2570
|
+
let result = args.data;
|
|
2571
|
+
for (const hook of hooks) {
|
|
2572
|
+
const hookResult = await hook({
|
|
2573
|
+
...args,
|
|
2574
|
+
data: result
|
|
2575
|
+
});
|
|
2576
|
+
if (hookResult !== void 0) {
|
|
2577
|
+
result = hookResult;
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
return result;
|
|
2581
|
+
}
|
|
2582
|
+
async function runFieldHooks(hooks, args) {
|
|
2583
|
+
return runHooks(hooks, args);
|
|
2584
|
+
}
|
|
2585
|
+
|
|
2586
|
+
// src/database/local/adapter.ts
|
|
2587
|
+
var LocalAdapter = class extends AbstractBaseAdapter {
|
|
2588
|
+
db;
|
|
2589
|
+
path;
|
|
2590
|
+
migrations = /* @__PURE__ */ new Map();
|
|
2591
|
+
constructor(options) {
|
|
2592
|
+
super();
|
|
2593
|
+
this.path = options.path;
|
|
2594
|
+
if (options.db) {
|
|
2595
|
+
this.db = options.db;
|
|
2596
|
+
} else {
|
|
2597
|
+
this.db = null;
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
async connect() {
|
|
2601
|
+
if (!this.db) {
|
|
2602
|
+
const Database = (await import('better-sqlite3')).default;
|
|
2603
|
+
this.db = new Database(this.path || ":memory:");
|
|
1169
2604
|
}
|
|
1170
2605
|
this.db.pragma("journal_mode = WAL");
|
|
1171
2606
|
this.db.pragma("foreign_keys = ON");
|
|
@@ -1649,9 +3084,19 @@ var SEOPLugin = class extends KyroPlugin {
|
|
|
1649
3084
|
slug: "seo-settings",
|
|
1650
3085
|
label: "SEO Settings",
|
|
1651
3086
|
fields: [
|
|
1652
|
-
{
|
|
1653
|
-
|
|
1654
|
-
|
|
3087
|
+
{
|
|
3088
|
+
name: "sitemap",
|
|
3089
|
+
type: "checkbox",
|
|
3090
|
+
label: "Enable Sitemap",
|
|
3091
|
+
defaultValue: true
|
|
3092
|
+
},
|
|
3093
|
+
{ name: "robotsTxt", type: "richtext", label: "robots.txt Content" },
|
|
3094
|
+
{
|
|
3095
|
+
name: "canonicalUrl",
|
|
3096
|
+
type: "text",
|
|
3097
|
+
variant: "url",
|
|
3098
|
+
label: "Canonical URL"
|
|
3099
|
+
},
|
|
1655
3100
|
{ name: "ogImage", type: "text", label: "Default OG Image URL" }
|
|
1656
3101
|
]
|
|
1657
3102
|
});
|
|
@@ -1683,7 +3128,7 @@ var CommentsPlugin = class extends KyroPlugin {
|
|
|
1683
3128
|
slug: "comments",
|
|
1684
3129
|
label: "Comments",
|
|
1685
3130
|
fields: [
|
|
1686
|
-
{ name: "content", type: "
|
|
3131
|
+
{ name: "content", type: "richtext", required: true },
|
|
1687
3132
|
{ name: "author", type: "text", required: true },
|
|
1688
3133
|
{ name: "email", type: "email" },
|
|
1689
3134
|
{ name: "approved", type: "checkbox", defaultValue: false },
|
|
@@ -1705,12 +3150,22 @@ var ReviewsPlugin = class extends KyroPlugin {
|
|
|
1705
3150
|
fields: [
|
|
1706
3151
|
{ name: "rating", type: "number", required: true, min: 1, max: 5 },
|
|
1707
3152
|
{ name: "title", type: "text" },
|
|
1708
|
-
{ name: "content", type: "
|
|
3153
|
+
{ name: "content", type: "richtext", required: true },
|
|
1709
3154
|
{ name: "author", type: "relationship", relationTo: "customers" },
|
|
1710
|
-
{
|
|
3155
|
+
{
|
|
3156
|
+
name: "product",
|
|
3157
|
+
type: "relationship",
|
|
3158
|
+
relationTo: "products",
|
|
3159
|
+
required: true
|
|
3160
|
+
},
|
|
1711
3161
|
{ name: "approved", type: "checkbox", defaultValue: false },
|
|
1712
3162
|
{ name: "verified", type: "checkbox", label: "Verified Purchase" },
|
|
1713
|
-
{
|
|
3163
|
+
{
|
|
3164
|
+
name: "helpful",
|
|
3165
|
+
type: "number",
|
|
3166
|
+
label: "Helpful Count",
|
|
3167
|
+
defaultValue: 0
|
|
3168
|
+
}
|
|
1714
3169
|
]
|
|
1715
3170
|
});
|
|
1716
3171
|
this.adminComponents["ReviewModeration"] = {};
|
|
@@ -1724,24 +3179,47 @@ var WishlistPlugin = class extends KyroPlugin {
|
|
|
1724
3179
|
slug: "wishlists",
|
|
1725
3180
|
label: "Wishlists",
|
|
1726
3181
|
fields: [
|
|
1727
|
-
{
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
3182
|
+
{
|
|
3183
|
+
name: "customer",
|
|
3184
|
+
type: "relationship",
|
|
3185
|
+
relationTo: "customers",
|
|
3186
|
+
required: true
|
|
3187
|
+
},
|
|
3188
|
+
{
|
|
3189
|
+
name: "name",
|
|
3190
|
+
type: "text",
|
|
3191
|
+
label: "Wishlist Name",
|
|
3192
|
+
defaultValue: "My Wishlist"
|
|
3193
|
+
},
|
|
3194
|
+
{
|
|
3195
|
+
name: "items",
|
|
3196
|
+
type: "blocks",
|
|
3197
|
+
label: "Items",
|
|
3198
|
+
blocks: [
|
|
3199
|
+
{
|
|
3200
|
+
slug: "wishlist-item",
|
|
3201
|
+
label: "Item",
|
|
3202
|
+
fields: [
|
|
3203
|
+
{
|
|
3204
|
+
name: "product",
|
|
3205
|
+
type: "relationship",
|
|
3206
|
+
relationTo: "products"
|
|
3207
|
+
},
|
|
3208
|
+
{ name: "quantity", type: "number", defaultValue: 1 },
|
|
3209
|
+
{ name: "addedAt", type: "date" },
|
|
3210
|
+
{
|
|
3211
|
+
name: "priority",
|
|
3212
|
+
type: "select",
|
|
3213
|
+
options: [
|
|
3214
|
+
{ label: "Low", value: "low" },
|
|
3215
|
+
{ label: "Medium", value: "medium" },
|
|
3216
|
+
{ label: "High", value: "high" }
|
|
3217
|
+
]
|
|
3218
|
+
}
|
|
3219
|
+
]
|
|
3220
|
+
}
|
|
3221
|
+
]
|
|
3222
|
+
}
|
|
1745
3223
|
]
|
|
1746
3224
|
});
|
|
1747
3225
|
}
|
|
@@ -2502,10 +3980,9 @@ var InMemoryAuthAdapter = class {
|
|
|
2502
3980
|
users = /* @__PURE__ */ new Map();
|
|
2503
3981
|
sessions = /* @__PURE__ */ new Map();
|
|
2504
3982
|
refreshTokens = /* @__PURE__ */ new Map();
|
|
2505
|
-
// refreshToken -> sessionId
|
|
2506
3983
|
emailToUserId = /* @__PURE__ */ new Map();
|
|
2507
3984
|
passwordHistory = /* @__PURE__ */ new Map();
|
|
2508
|
-
|
|
3985
|
+
auditLogs = [];
|
|
2509
3986
|
externalDb = false;
|
|
2510
3987
|
constructor() {
|
|
2511
3988
|
}
|
|
@@ -2521,10 +3998,11 @@ var InMemoryAuthAdapter = class {
|
|
|
2521
3998
|
async createUser(data) {
|
|
2522
3999
|
const userId = randomBytes(16).toString("hex");
|
|
2523
4000
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4001
|
+
const passwordHash = await this.hashPassword(data.password);
|
|
2524
4002
|
const user = {
|
|
2525
4003
|
id: userId,
|
|
2526
4004
|
email: data.email.toLowerCase(),
|
|
2527
|
-
passwordHash
|
|
4005
|
+
passwordHash,
|
|
2528
4006
|
role: data.role || "customer",
|
|
2529
4007
|
tenantId: data.tenantId,
|
|
2530
4008
|
createdAt: now,
|
|
@@ -2582,9 +4060,12 @@ var InMemoryAuthAdapter = class {
|
|
|
2582
4060
|
const bcrypt2 = (await import('bcryptjs')).default;
|
|
2583
4061
|
return bcrypt2.hash(password, 12);
|
|
2584
4062
|
}
|
|
2585
|
-
async verifyPassword(
|
|
4063
|
+
async verifyPassword(email, password) {
|
|
4064
|
+
const user = await this.findUserByEmail(email);
|
|
4065
|
+
if (!user || !user.passwordHash) return null;
|
|
2586
4066
|
const bcrypt2 = (await import('bcryptjs')).default;
|
|
2587
|
-
|
|
4067
|
+
const valid = await bcrypt2.compare(password, user.passwordHash);
|
|
4068
|
+
return valid ? user : null;
|
|
2588
4069
|
}
|
|
2589
4070
|
async createSession(userId, data = {}) {
|
|
2590
4071
|
const sessionId = randomBytes(32).toString("hex");
|
|
@@ -2655,145 +4136,31 @@ var InMemoryAuthAdapter = class {
|
|
|
2655
4136
|
async hasAnyUsers() {
|
|
2656
4137
|
return this.users.size > 0;
|
|
2657
4138
|
}
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
this.limits = { ...DEFAULT_RATE_LIMITS2, ...limits };
|
|
2668
|
-
this.userLimits = userLimits || {
|
|
2669
|
-
"user:api": { window: 6e4, max: 500 },
|
|
2670
|
-
"user:write": { window: 36e5, max: 100 }
|
|
2671
|
-
};
|
|
2672
|
-
}
|
|
2673
|
-
getKey(type, identifier) {
|
|
2674
|
-
return `${type}:${identifier}`;
|
|
2675
|
-
}
|
|
2676
|
-
getUserKey(type, userId, identifier) {
|
|
2677
|
-
return `user:${type}:${userId}:${identifier}`;
|
|
2678
|
-
}
|
|
2679
|
-
cleanupOldEntries(entries, window) {
|
|
2680
|
-
const now = Date.now();
|
|
2681
|
-
const windowStart = now - window;
|
|
2682
|
-
while (entries.length > 0 && entries[0].timestamp < windowStart) {
|
|
2683
|
-
entries.shift();
|
|
2684
|
-
}
|
|
2685
|
-
}
|
|
2686
|
-
async check(type, identifier) {
|
|
2687
|
-
const config = this.limits[type] || this.limits["api:general"];
|
|
2688
|
-
const key = this.getKey(type, identifier);
|
|
2689
|
-
let entries = this.storage.get(key);
|
|
2690
|
-
if (!entries) {
|
|
2691
|
-
entries = [];
|
|
2692
|
-
this.storage.set(key, entries);
|
|
2693
|
-
}
|
|
2694
|
-
this.cleanupOldEntries(entries, config.window);
|
|
2695
|
-
const now = Date.now();
|
|
2696
|
-
const count = entries.reduce((sum, entry) => sum + entry.count, 0);
|
|
2697
|
-
entries.push({ timestamp: now, count: 1 });
|
|
2698
|
-
if (count >= config.max) {
|
|
2699
|
-
const oldestEntry = entries.reduce(
|
|
2700
|
-
(oldest, current) => oldest.timestamp < current.timestamp ? oldest : current,
|
|
2701
|
-
entries[0]
|
|
2702
|
-
);
|
|
2703
|
-
const resetAt = oldestEntry.timestamp + config.window;
|
|
2704
|
-
return {
|
|
2705
|
-
allowed: false,
|
|
2706
|
-
remaining: 0,
|
|
2707
|
-
resetAt,
|
|
2708
|
-
retryAfter: Math.ceil((resetAt - now) / 1e3)
|
|
2709
|
-
};
|
|
2710
|
-
}
|
|
2711
|
-
return {
|
|
2712
|
-
allowed: true,
|
|
2713
|
-
remaining: config.max - count - 1,
|
|
2714
|
-
resetAt: now + config.window
|
|
2715
|
-
};
|
|
2716
|
-
}
|
|
2717
|
-
async checkUser(type, userId, identifier) {
|
|
2718
|
-
const config = this.userLimits[type] || this.userLimits["user:api"];
|
|
2719
|
-
const userMap = this.userStorage.get(userId);
|
|
2720
|
-
let entries = [];
|
|
2721
|
-
if (userMap) {
|
|
2722
|
-
entries = userMap.get(this.getKey(type, identifier)) || [];
|
|
2723
|
-
} else {
|
|
2724
|
-
if (!this.userStorage.has(userId)) {
|
|
2725
|
-
this.userStorage.set(userId, /* @__PURE__ */ new Map());
|
|
4139
|
+
async findAuditLogs(filter) {
|
|
4140
|
+
const { limit = 50, offset = 0 } = filter;
|
|
4141
|
+
let logs = this.auditLogs.slice().reverse();
|
|
4142
|
+
if (filter.userId) logs = logs.filter((l) => l.userId === filter.userId);
|
|
4143
|
+
if (filter.action) {
|
|
4144
|
+
if (Array.isArray(filter.action)) {
|
|
4145
|
+
logs = logs.filter((l) => filter.action.includes(String(l.action)));
|
|
4146
|
+
} else {
|
|
4147
|
+
logs = logs.filter((l) => l.action === filter.action);
|
|
2726
4148
|
}
|
|
2727
|
-
this.userStorage.get(userId).set(this.getKey(type, identifier), entries);
|
|
2728
|
-
}
|
|
2729
|
-
this.cleanupOldEntries(entries, config.window);
|
|
2730
|
-
const now = Date.now();
|
|
2731
|
-
const count = entries.reduce((sum, entry) => sum + entry.count, 0);
|
|
2732
|
-
entries.push({ timestamp: now, count: 1 });
|
|
2733
|
-
if (count >= config.max) {
|
|
2734
|
-
const oldestEntry = entries.reduce(
|
|
2735
|
-
(oldest, current) => oldest.timestamp < current.timestamp ? oldest : current,
|
|
2736
|
-
entries[0]
|
|
2737
|
-
);
|
|
2738
|
-
const resetAt = oldestEntry.timestamp + config.window;
|
|
2739
|
-
return {
|
|
2740
|
-
allowed: false,
|
|
2741
|
-
remaining: 0,
|
|
2742
|
-
resetAt,
|
|
2743
|
-
retryAfter: Math.ceil((resetAt - now) / 1e3)
|
|
2744
|
-
};
|
|
2745
|
-
}
|
|
2746
|
-
return {
|
|
2747
|
-
allowed: true,
|
|
2748
|
-
remaining: config.max - count - 1,
|
|
2749
|
-
resetAt: now + config.window
|
|
2750
|
-
};
|
|
2751
|
-
}
|
|
2752
|
-
async reset(type, identifier) {
|
|
2753
|
-
const key = this.getKey(type, identifier);
|
|
2754
|
-
this.storage.delete(key);
|
|
2755
|
-
}
|
|
2756
|
-
async resetUser(type, userId, identifier) {
|
|
2757
|
-
const userMap = this.userStorage.get(userId);
|
|
2758
|
-
if (userMap) {
|
|
2759
|
-
const key = this.getKey(type, identifier);
|
|
2760
|
-
userMap.delete(key);
|
|
2761
|
-
}
|
|
2762
|
-
}
|
|
2763
|
-
async getStatus(type, identifier) {
|
|
2764
|
-
const config = this.limits[type] || this.limits["api:general"];
|
|
2765
|
-
const key = this.getKey(type, identifier);
|
|
2766
|
-
let entries = this.storage.get(key);
|
|
2767
|
-
if (!entries) {
|
|
2768
|
-
entries = [];
|
|
2769
|
-
this.storage.set(key, entries);
|
|
2770
4149
|
}
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
limit: config.max,
|
|
2777
|
-
remaining: Math.max(0, config.max - count),
|
|
2778
|
-
resetAt: now + config.window
|
|
2779
|
-
};
|
|
2780
|
-
}
|
|
2781
|
-
setLimit(type, config) {
|
|
2782
|
-
this.limits[type] = config;
|
|
4150
|
+
if (filter.resource)
|
|
4151
|
+
logs = logs.filter((l) => l.resource === filter.resource);
|
|
4152
|
+
if (filter.success !== void 0)
|
|
4153
|
+
logs = logs.filter((l) => l.success === filter.success);
|
|
4154
|
+
return { logs: logs.slice(offset, offset + limit), total: logs.length };
|
|
2783
4155
|
}
|
|
2784
|
-
|
|
2785
|
-
|
|
4156
|
+
async createAuditLog(data) {
|
|
4157
|
+
const id = randomBytes(16).toString("hex");
|
|
4158
|
+
const timestamp = /* @__PURE__ */ new Date();
|
|
4159
|
+
const log = { ...data, id, timestamp };
|
|
4160
|
+
this.auditLogs.push(log);
|
|
4161
|
+
return log;
|
|
2786
4162
|
}
|
|
2787
4163
|
};
|
|
2788
|
-
var DEFAULT_RATE_LIMITS2 = {
|
|
2789
|
-
"auth:login": { window: 9e5, max: 5 },
|
|
2790
|
-
"auth:register": { window: 36e5, max: 3 },
|
|
2791
|
-
"auth:forgot": { window: 36e5, max: 3 },
|
|
2792
|
-
"auth:reset": { window: 36e5, max: 5 },
|
|
2793
|
-
"auth:verify": { window: 36e5, max: 5 },
|
|
2794
|
-
"api:general": { window: 6e4, max: 100 },
|
|
2795
|
-
"api:authenticated": { window: 6e4, max: 200 }
|
|
2796
|
-
};
|
|
2797
4164
|
|
|
2798
4165
|
// src/auth/security/in-memory-lockout.ts
|
|
2799
4166
|
var InMemoryAccountLockout = class {
|
|
@@ -3064,32 +4431,6 @@ function createAuditContext2(req) {
|
|
|
3064
4431
|
userAgent: req.headers.get("user-agent") || "unknown"
|
|
3065
4432
|
};
|
|
3066
4433
|
}
|
|
3067
|
-
function defaultExtractToken(req) {
|
|
3068
|
-
const authHeader = req.headers.get("Authorization");
|
|
3069
|
-
if (authHeader?.startsWith("Bearer ")) {
|
|
3070
|
-
return authHeader.slice(7);
|
|
3071
|
-
}
|
|
3072
|
-
const cookieHeader = req.headers.get("Cookie");
|
|
3073
|
-
if (cookieHeader) {
|
|
3074
|
-
const cookies = Object.fromEntries(
|
|
3075
|
-
cookieHeader.split("; ").map((c) => {
|
|
3076
|
-
const [key, ...val] = c.split("=");
|
|
3077
|
-
return [key.trim(), val.join("=")];
|
|
3078
|
-
})
|
|
3079
|
-
);
|
|
3080
|
-
return cookies["auth_token"] || null;
|
|
3081
|
-
}
|
|
3082
|
-
return null;
|
|
3083
|
-
}
|
|
3084
|
-
function generateToken(payload, secret, options = {}) {
|
|
3085
|
-
return jwt2.sign(payload, secret, {
|
|
3086
|
-
expiresIn: options.expiresIn || "24h",
|
|
3087
|
-
issuer: options.issuer,
|
|
3088
|
-
audience: options.audience
|
|
3089
|
-
});
|
|
3090
|
-
}
|
|
3091
|
-
|
|
3092
|
-
// src/api/rest/auth-routes.ts
|
|
3093
4434
|
var AuthRoutes = class {
|
|
3094
4435
|
authAdapter;
|
|
3095
4436
|
email;
|
|
@@ -3141,10 +4482,9 @@ var AuthRoutes = class {
|
|
|
3141
4482
|
if (existingUser) {
|
|
3142
4483
|
return this.errorResponse("Email already registered", 400);
|
|
3143
4484
|
}
|
|
3144
|
-
const passwordHash = await this.authAdapter.hashPassword(body.password);
|
|
3145
4485
|
const user = await this.authAdapter.createUser({
|
|
3146
4486
|
email: body.email,
|
|
3147
|
-
|
|
4487
|
+
password: body.password,
|
|
3148
4488
|
role: body.role || "customer",
|
|
3149
4489
|
tenantId: body.tenantId
|
|
3150
4490
|
});
|
|
@@ -3279,7 +4619,7 @@ var AuthRoutes = class {
|
|
|
3279
4619
|
}
|
|
3280
4620
|
const { ipAddress, userAgent } = createAuditContext2(req);
|
|
3281
4621
|
try {
|
|
3282
|
-
const payload =
|
|
4622
|
+
const payload = jwt.decode(token);
|
|
3283
4623
|
if (payload && payload.sub) {
|
|
3284
4624
|
await this.authAdapter.deleteUserSessions(payload.sub);
|
|
3285
4625
|
if (this.auditLogger) {
|
|
@@ -3320,7 +4660,7 @@ var AuthRoutes = class {
|
|
|
3320
4660
|
return this.errorResponse("Not authenticated", 401);
|
|
3321
4661
|
}
|
|
3322
4662
|
try {
|
|
3323
|
-
const payload =
|
|
4663
|
+
const payload = jwt.verify(token, this.jwtSecret, {
|
|
3324
4664
|
issuer: this.jwtIssuer,
|
|
3325
4665
|
audience: this.jwtAudience
|
|
3326
4666
|
});
|
|
@@ -3343,7 +4683,7 @@ var AuthRoutes = class {
|
|
|
3343
4683
|
}
|
|
3344
4684
|
const { ipAddress, userAgent } = createAuditContext2(req);
|
|
3345
4685
|
try {
|
|
3346
|
-
const payload =
|
|
4686
|
+
const payload = jwt.verify(token, this.jwtSecret);
|
|
3347
4687
|
const body = await req.json();
|
|
3348
4688
|
const { currentPassword, newPassword, confirmPassword } = body;
|
|
3349
4689
|
if (!currentPassword || !newPassword) {
|
|
@@ -3550,8 +4890,8 @@ function detectDatabaseType() {
|
|
|
3550
4890
|
}
|
|
3551
4891
|
try {
|
|
3552
4892
|
const { readFileSync } = __require("fs");
|
|
3553
|
-
const { join } = __require("path");
|
|
3554
|
-
const configPath =
|
|
4893
|
+
const { join: join2 } = __require("path");
|
|
4894
|
+
const configPath = join2(process.cwd(), "kyro.config.ts");
|
|
3555
4895
|
const configContent = readFileSync(configPath, "utf8");
|
|
3556
4896
|
if (configContent.includes("createLocalAdapter")) {
|
|
3557
4897
|
return "sqlite";
|
|
@@ -3593,7 +4933,7 @@ async function createAuthConfig(databaseType) {
|
|
|
3593
4933
|
const distributed = getEnvBool("KYRO_DISTRIBUTED", false);
|
|
3594
4934
|
let authAdapter;
|
|
3595
4935
|
if (distributed) {
|
|
3596
|
-
const { RedisAuthAdapter: RedisAuthAdapter2 } = await import('./redis-adapter-
|
|
4936
|
+
const { RedisAuthAdapter: RedisAuthAdapter2 } = await import('./redis-adapter-RA24FNCX.js');
|
|
3597
4937
|
const redisUrl = getEnv("REDIS_URL", "redis://localhost:6379");
|
|
3598
4938
|
const redisTls = getEnvBool("REDIS_TLS", false);
|
|
3599
4939
|
const redisAdapter = new RedisAuthAdapter2({ url: redisUrl, tls: redisTls });
|
|
@@ -3606,8 +4946,7 @@ async function createAuthConfig(databaseType) {
|
|
|
3606
4946
|
await authAdapter.connect();
|
|
3607
4947
|
}
|
|
3608
4948
|
}
|
|
3609
|
-
const
|
|
3610
|
-
const email = emailConfig ? new EmailTransport(emailConfig) : void 0;
|
|
4949
|
+
const email = EmailTransport.fromEnv() || void 0;
|
|
3611
4950
|
const passwordPolicy = new PasswordPolicy({
|
|
3612
4951
|
minLength: getEnvNum("PASSWORD_MIN_LENGTH", 12),
|
|
3613
4952
|
requireUppercase: getEnvBool("PASSWORD_REQUIRE_UPPERCASE", true),
|
|
@@ -3684,21 +5023,6 @@ async function createAuthConfig(databaseType) {
|
|
|
3684
5023
|
routes
|
|
3685
5024
|
};
|
|
3686
5025
|
}
|
|
3687
|
-
function getEmailConfig() {
|
|
3688
|
-
const host = getEnv("SMTP_HOST");
|
|
3689
|
-
if (!host) return void 0;
|
|
3690
|
-
return {
|
|
3691
|
-
host,
|
|
3692
|
-
port: getEnvNum("SMTP_PORT", 587),
|
|
3693
|
-
secure: getEnvBool("SMTP_SECURE", false),
|
|
3694
|
-
auth: {
|
|
3695
|
-
user: getEnv("SMTP_USER"),
|
|
3696
|
-
pass: getEnv("SMTP_PASS")
|
|
3697
|
-
},
|
|
3698
|
-
from: getEnv("SMTP_FROM", "noreply@example.com"),
|
|
3699
|
-
fromName: getEnv("SMTP_FROM_NAME", "Kyro CMS")
|
|
3700
|
-
};
|
|
3701
|
-
}
|
|
3702
5026
|
var authConfig = createAuthConfig();
|
|
3703
5027
|
|
|
3704
5028
|
// src/auth/index.ts
|
|
@@ -3725,10 +5049,9 @@ var Auth = class {
|
|
|
3725
5049
|
if (existing) {
|
|
3726
5050
|
return { success: false, error: "Email already registered" };
|
|
3727
5051
|
}
|
|
3728
|
-
const passwordHash = await this.hashPassword(data.password);
|
|
3729
5052
|
const user = await this.adapter.createUser({
|
|
3730
5053
|
email: data.email,
|
|
3731
|
-
|
|
5054
|
+
password: data.password,
|
|
3732
5055
|
role: data.role ?? "customer",
|
|
3733
5056
|
tenantId: data.tenantId
|
|
3734
5057
|
});
|
|
@@ -3739,15 +5062,11 @@ var Auth = class {
|
|
|
3739
5062
|
}
|
|
3740
5063
|
async login(credentials) {
|
|
3741
5064
|
try {
|
|
3742
|
-
const user = await this.adapter.
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
}
|
|
3746
|
-
const valid = await this.adapter.verifyPassword(
|
|
3747
|
-
credentials.password,
|
|
3748
|
-
user.passwordHash
|
|
5065
|
+
const user = await this.adapter.verifyPassword(
|
|
5066
|
+
credentials.email,
|
|
5067
|
+
credentials.password
|
|
3749
5068
|
);
|
|
3750
|
-
if (!
|
|
5069
|
+
if (!user) {
|
|
3751
5070
|
return { success: false, error: "Invalid credentials" };
|
|
3752
5071
|
}
|
|
3753
5072
|
return this.createSessionForUser(user);
|
|
@@ -3776,7 +5095,7 @@ var Auth = class {
|
|
|
3776
5095
|
}
|
|
3777
5096
|
async verifyToken(token) {
|
|
3778
5097
|
try {
|
|
3779
|
-
const decoded =
|
|
5098
|
+
const decoded = jwt.verify(token, this.config.secret, {
|
|
3780
5099
|
issuer: this.config.issuer,
|
|
3781
5100
|
audience: this.config.audience.length > 0 ? this.config.audience[0] : void 0
|
|
3782
5101
|
});
|
|
@@ -3793,18 +5112,17 @@ var Auth = class {
|
|
|
3793
5112
|
async changePassword(userId, currentPassword, newPassword) {
|
|
3794
5113
|
try {
|
|
3795
5114
|
const user = await this.adapter.findUserById(userId);
|
|
3796
|
-
if (!user
|
|
5115
|
+
if (!user) {
|
|
3797
5116
|
return { success: false, error: "User not found" };
|
|
3798
5117
|
}
|
|
3799
5118
|
const valid = await this.adapter.verifyPassword(
|
|
3800
|
-
|
|
3801
|
-
|
|
5119
|
+
user.email,
|
|
5120
|
+
currentPassword
|
|
3802
5121
|
);
|
|
3803
5122
|
if (!valid) {
|
|
3804
5123
|
return { success: false, error: "Current password is incorrect" };
|
|
3805
5124
|
}
|
|
3806
|
-
|
|
3807
|
-
await this.adapter.updateUser(userId, { passwordHash: newHash });
|
|
5125
|
+
await this.adapter.updateUser(userId, { password: newPassword });
|
|
3808
5126
|
await this.adapter.deleteUserSessions(userId);
|
|
3809
5127
|
return { success: true, user };
|
|
3810
5128
|
} catch (error) {
|
|
@@ -3817,8 +5135,7 @@ var Auth = class {
|
|
|
3817
5135
|
if (!user) {
|
|
3818
5136
|
return { success: false, error: "User not found" };
|
|
3819
5137
|
}
|
|
3820
|
-
|
|
3821
|
-
await this.adapter.updateUser(user.id, { passwordHash });
|
|
5138
|
+
await this.adapter.updateUser(user.id, { password: newPassword });
|
|
3822
5139
|
await this.adapter.deleteUserSessions(user.id);
|
|
3823
5140
|
return { success: true, user };
|
|
3824
5141
|
} catch (error) {
|
|
@@ -3862,7 +5179,7 @@ var Auth = class {
|
|
|
3862
5179
|
if (this.config.audience.length > 0) {
|
|
3863
5180
|
signOptions.audience = this.config.audience[0];
|
|
3864
5181
|
}
|
|
3865
|
-
return
|
|
5182
|
+
return jwt.sign(payload, this.config.secret, signOptions);
|
|
3866
5183
|
}
|
|
3867
5184
|
async hashPassword(password) {
|
|
3868
5185
|
return bcrypt.hash(password, this.config.saltRounds);
|
|
@@ -4036,6 +5353,1590 @@ function isDraft(status) {
|
|
|
4036
5353
|
function isArchived(status) {
|
|
4037
5354
|
return status === "archived";
|
|
4038
5355
|
}
|
|
5356
|
+
function createLocalStorage(config) {
|
|
5357
|
+
const { uploadDir, baseUrl = "/uploads" } = config;
|
|
5358
|
+
async function ensureDir(dir) {
|
|
5359
|
+
if (!existsSync(dir)) {
|
|
5360
|
+
await mkdir(dir, { recursive: true });
|
|
5361
|
+
}
|
|
5362
|
+
}
|
|
5363
|
+
function getMimeType(filename) {
|
|
5364
|
+
const ext = extname(filename).toLowerCase();
|
|
5365
|
+
const mimeTypes = {
|
|
5366
|
+
".jpg": "image/jpeg",
|
|
5367
|
+
".jpeg": "image/jpeg",
|
|
5368
|
+
".png": "image/png",
|
|
5369
|
+
".gif": "image/gif",
|
|
5370
|
+
".webp": "image/webp",
|
|
5371
|
+
".svg": "image/svg+xml",
|
|
5372
|
+
".mp4": "video/mp4",
|
|
5373
|
+
".webm": "video/webm",
|
|
5374
|
+
".pdf": "application/pdf",
|
|
5375
|
+
".doc": "application/msword",
|
|
5376
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
5377
|
+
".xls": "application/vnd.ms-excel",
|
|
5378
|
+
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
5379
|
+
".zip": "application/zip"
|
|
5380
|
+
};
|
|
5381
|
+
return mimeTypes[ext] || "application/octet-stream";
|
|
5382
|
+
}
|
|
5383
|
+
async function getImageDimensions(buffer) {
|
|
5384
|
+
try {
|
|
5385
|
+
const header = buffer.toString("hex", 0, 8);
|
|
5386
|
+
if (header.startsWith("89504e47")) {
|
|
5387
|
+
const width = buffer.readUInt32BE(16);
|
|
5388
|
+
const height = buffer.readUInt32BE(20);
|
|
5389
|
+
return { width, height };
|
|
5390
|
+
}
|
|
5391
|
+
if (header.startsWith("ffd8")) {
|
|
5392
|
+
let offset = 2;
|
|
5393
|
+
while (offset < buffer.length) {
|
|
5394
|
+
if (buffer[offset] !== 255) break;
|
|
5395
|
+
const marker = buffer[offset + 1];
|
|
5396
|
+
if (marker === 192 || marker === 194) {
|
|
5397
|
+
const height = buffer.readUInt16BE(offset + 5);
|
|
5398
|
+
const width = buffer.readUInt16BE(offset + 7);
|
|
5399
|
+
return { width, height };
|
|
5400
|
+
}
|
|
5401
|
+
offset += 2 + buffer.readUInt16BE(offset + 2);
|
|
5402
|
+
}
|
|
5403
|
+
}
|
|
5404
|
+
} catch {
|
|
5405
|
+
}
|
|
5406
|
+
return {};
|
|
5407
|
+
}
|
|
5408
|
+
return {
|
|
5409
|
+
name: "local",
|
|
5410
|
+
displayName: "Local Storage",
|
|
5411
|
+
supportsDynamicResize: true,
|
|
5412
|
+
async upload(file, options) {
|
|
5413
|
+
await ensureDir(uploadDir);
|
|
5414
|
+
const buffer = Buffer.from(await file.arrayBuffer());
|
|
5415
|
+
const hash = createHash("md5").update(buffer).digest("hex");
|
|
5416
|
+
const ext = extname(file.name);
|
|
5417
|
+
const filename = options?.filename || `${hash}${ext}`;
|
|
5418
|
+
const folder = options?.folder || "";
|
|
5419
|
+
const targetDir = folder ? join(uploadDir, folder) : uploadDir;
|
|
5420
|
+
await ensureDir(targetDir);
|
|
5421
|
+
const filepath = join(targetDir, filename);
|
|
5422
|
+
await writeFile(filepath, buffer);
|
|
5423
|
+
const dimensions = file.type.startsWith("image/") ? await getImageDimensions(buffer) : {};
|
|
5424
|
+
const normalizedBaseUrl = baseUrl || "/uploads";
|
|
5425
|
+
const urlPath = folder ? `/${folder}/${filename}` : `/${filename}`;
|
|
5426
|
+
const url = normalizedBaseUrl + urlPath;
|
|
5427
|
+
return {
|
|
5428
|
+
id: hash,
|
|
5429
|
+
filename,
|
|
5430
|
+
originalName: file.name,
|
|
5431
|
+
mimeType: file.type || getMimeType(file.name),
|
|
5432
|
+
size: buffer.length,
|
|
5433
|
+
url,
|
|
5434
|
+
thumbnailUrl: file.type.startsWith("image/") ? url : void 0,
|
|
5435
|
+
folder: folder || void 0,
|
|
5436
|
+
provider: "local",
|
|
5437
|
+
metadata: {
|
|
5438
|
+
...dimensions,
|
|
5439
|
+
...options?.metadata
|
|
5440
|
+
},
|
|
5441
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5442
|
+
};
|
|
5443
|
+
},
|
|
5444
|
+
async uploadFromUrl(url, options) {
|
|
5445
|
+
const response = await fetch(url);
|
|
5446
|
+
if (!response.ok) {
|
|
5447
|
+
throw new Error(`Failed to fetch file: ${response.statusText}`);
|
|
5448
|
+
}
|
|
5449
|
+
const blob = await response.blob();
|
|
5450
|
+
const contentDisposition = response.headers.get("content-disposition");
|
|
5451
|
+
let filename = options?.filename;
|
|
5452
|
+
if (!filename && contentDisposition) {
|
|
5453
|
+
const match = contentDisposition.match(
|
|
5454
|
+
/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/
|
|
5455
|
+
);
|
|
5456
|
+
if (match) {
|
|
5457
|
+
filename = match[1].replace(/['"]/g, "");
|
|
5458
|
+
}
|
|
5459
|
+
}
|
|
5460
|
+
if (!filename) {
|
|
5461
|
+
filename = basename(new URL(url).pathname);
|
|
5462
|
+
}
|
|
5463
|
+
const file = new File([blob], filename, {
|
|
5464
|
+
type: blob.type || getMimeType(filename)
|
|
5465
|
+
});
|
|
5466
|
+
return this.upload(file, options);
|
|
5467
|
+
},
|
|
5468
|
+
async delete(url) {
|
|
5469
|
+
const filepath = join(uploadDir, url.replace(baseUrl + "/", ""));
|
|
5470
|
+
try {
|
|
5471
|
+
await unlink(filepath);
|
|
5472
|
+
} catch {
|
|
5473
|
+
}
|
|
5474
|
+
},
|
|
5475
|
+
async rename(oldUrl, newFilename) {
|
|
5476
|
+
const oldPath = join(uploadDir, oldUrl.replace(baseUrl + "/", ""));
|
|
5477
|
+
const newPath = join(uploadDir, newFilename);
|
|
5478
|
+
await rename(oldPath, newPath);
|
|
5479
|
+
return `${baseUrl}/${newFilename}`;
|
|
5480
|
+
},
|
|
5481
|
+
getImageUrl(url, transforms) {
|
|
5482
|
+
if (!transforms || Object.keys(transforms).length === 0) return url;
|
|
5483
|
+
const params = new URLSearchParams({ url });
|
|
5484
|
+
if (transforms.width) params.set("w", String(transforms.width));
|
|
5485
|
+
if (transforms.height) params.set("h", String(transforms.height));
|
|
5486
|
+
if (transforms.quality) params.set("q", String(transforms.quality));
|
|
5487
|
+
if (transforms.format) params.set("f", transforms.format);
|
|
5488
|
+
return `/api/media/resize?${params.toString()}`;
|
|
5489
|
+
},
|
|
5490
|
+
async generateThumbnail(file) {
|
|
5491
|
+
if (!file.mimeType.startsWith("image/")) return "";
|
|
5492
|
+
return this.getImageUrl(file.url, { width: 400, height: 400 });
|
|
5493
|
+
},
|
|
5494
|
+
async list(prefix) {
|
|
5495
|
+
const dir = prefix ? join(uploadDir, prefix) : uploadDir;
|
|
5496
|
+
if (!existsSync(dir)) return [];
|
|
5497
|
+
const files = [];
|
|
5498
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
5499
|
+
for (const entry of entries) {
|
|
5500
|
+
if (entry.isFile()) {
|
|
5501
|
+
const filepath = join(dir, entry.name);
|
|
5502
|
+
const stats = await stat(filepath);
|
|
5503
|
+
const url = `${baseUrl}${prefix ? `/${prefix}` : ""}/${entry.name}`;
|
|
5504
|
+
files.push({
|
|
5505
|
+
id: createHash("md5").update(entry.name).digest("hex"),
|
|
5506
|
+
filename: entry.name,
|
|
5507
|
+
originalName: entry.name,
|
|
5508
|
+
mimeType: getMimeType(entry.name),
|
|
5509
|
+
size: stats.size,
|
|
5510
|
+
url,
|
|
5511
|
+
provider: "local",
|
|
5512
|
+
createdAt: stats.birthtime.toISOString()
|
|
5513
|
+
});
|
|
5514
|
+
}
|
|
5515
|
+
}
|
|
5516
|
+
return files;
|
|
5517
|
+
},
|
|
5518
|
+
async exists(url) {
|
|
5519
|
+
const filepath = join(uploadDir, url.replace(baseUrl + "/", ""));
|
|
5520
|
+
return existsSync(filepath);
|
|
5521
|
+
}
|
|
5522
|
+
};
|
|
5523
|
+
}
|
|
5524
|
+
function extractPublicDevUrlId(url) {
|
|
5525
|
+
if (!url) return "";
|
|
5526
|
+
if (url.startsWith("pub-")) return url;
|
|
5527
|
+
const match = url.match(/pub-[a-zA-Z0-9]+/i);
|
|
5528
|
+
return match ? match[0] : "";
|
|
5529
|
+
}
|
|
5530
|
+
function getPublicUrl(key, config) {
|
|
5531
|
+
const normalizedKey = key.startsWith("/") ? key.slice(1) : key;
|
|
5532
|
+
if (config.cdnUrl) {
|
|
5533
|
+
const cdn = config.cdnUrl.replace(/\/$/, "");
|
|
5534
|
+
return `${cdn}/${normalizedKey}`;
|
|
5535
|
+
}
|
|
5536
|
+
switch (config.provider) {
|
|
5537
|
+
case "r2": {
|
|
5538
|
+
const pubId = extractPublicDevUrlId(config.publicDevUrl);
|
|
5539
|
+
if (pubId) {
|
|
5540
|
+
return `https://${pubId}.r2.dev/${normalizedKey}`;
|
|
5541
|
+
}
|
|
5542
|
+
return `https://${config.bucket}.${config.accountId}.r2.cloudflarestorage.com/${normalizedKey}`;
|
|
5543
|
+
}
|
|
5544
|
+
case "gcs":
|
|
5545
|
+
return `https://storage.googleapis.com/${config.bucket}/${normalizedKey}`;
|
|
5546
|
+
case "digitalocean":
|
|
5547
|
+
return `https://${config.bucket}.${config.region}.cdn.digitaloceanspaces.com/${normalizedKey}`;
|
|
5548
|
+
case "backblaze":
|
|
5549
|
+
return `https://${config.bucket}.s3.backblazeb2.com/${normalizedKey}`;
|
|
5550
|
+
case "wasabi":
|
|
5551
|
+
return `https://${config.bucket}.s3.wasabisys.com/${normalizedKey}`;
|
|
5552
|
+
case "aws":
|
|
5553
|
+
default:
|
|
5554
|
+
return `https://${config.bucket}.s3.${config.region}.amazonaws.com/${normalizedKey}`;
|
|
5555
|
+
}
|
|
5556
|
+
}
|
|
5557
|
+
function getUrlPrefix(config) {
|
|
5558
|
+
if (config.cdnUrl) {
|
|
5559
|
+
return config.cdnUrl.replace(/\/$/, "") + "/";
|
|
5560
|
+
}
|
|
5561
|
+
switch (config.provider) {
|
|
5562
|
+
case "r2": {
|
|
5563
|
+
const pubId = extractPublicDevUrlId(config.publicDevUrl);
|
|
5564
|
+
if (pubId) {
|
|
5565
|
+
return `https://${pubId}.r2.dev/`;
|
|
5566
|
+
}
|
|
5567
|
+
return `https://${config.bucket}.${config.accountId}.r2.cloudflarestorage.com/`;
|
|
5568
|
+
}
|
|
5569
|
+
case "gcs":
|
|
5570
|
+
return `https://storage.googleapis.com/${config.bucket}/`;
|
|
5571
|
+
case "digitalocean":
|
|
5572
|
+
return `https://${config.bucket}.${config.region}.cdn.digitaloceanspaces.com/`;
|
|
5573
|
+
case "backblaze":
|
|
5574
|
+
return `https://${config.bucket}.s3.backblazeb2.com/`;
|
|
5575
|
+
case "wasabi":
|
|
5576
|
+
return `https://${config.bucket}.s3.wasabisys.com/`;
|
|
5577
|
+
case "aws":
|
|
5578
|
+
default:
|
|
5579
|
+
return `https://${config.bucket}.s3.${config.region}.amazonaws.com/`;
|
|
5580
|
+
}
|
|
5581
|
+
}
|
|
5582
|
+
function getDisplayName(provider) {
|
|
5583
|
+
switch (provider) {
|
|
5584
|
+
case "r2":
|
|
5585
|
+
return "Cloudflare R2";
|
|
5586
|
+
case "gcs":
|
|
5587
|
+
return "Google Cloud Storage";
|
|
5588
|
+
case "digitalocean":
|
|
5589
|
+
return "DigitalOcean Spaces";
|
|
5590
|
+
case "backblaze":
|
|
5591
|
+
return "Backblaze B2";
|
|
5592
|
+
case "wasabi":
|
|
5593
|
+
return "Wasabi";
|
|
5594
|
+
case "aws":
|
|
5595
|
+
default:
|
|
5596
|
+
return "AWS S3";
|
|
5597
|
+
}
|
|
5598
|
+
}
|
|
5599
|
+
function createS3Storage(config) {
|
|
5600
|
+
console.log("[createS3Storage] Creating provider:", config.provider);
|
|
5601
|
+
console.log("[createS3Storage] Credentials:", {
|
|
5602
|
+
accessKeyId: config.accessKeyId ? "SET" : "UNDEFINED",
|
|
5603
|
+
secretAccessKey: config.secretAccessKey ? "SET" : "UNDEFINED",
|
|
5604
|
+
bucket: config.bucket,
|
|
5605
|
+
accountId: config.accountId,
|
|
5606
|
+
endpoint: config.endpoint
|
|
5607
|
+
});
|
|
5608
|
+
const client = new S3Client({
|
|
5609
|
+
region: config.region || "auto",
|
|
5610
|
+
endpoint: config.endpoint,
|
|
5611
|
+
credentials: {
|
|
5612
|
+
accessKeyId: config.accessKeyId,
|
|
5613
|
+
secretAccessKey: config.secretAccessKey
|
|
5614
|
+
},
|
|
5615
|
+
forcePathStyle: true,
|
|
5616
|
+
tls: true,
|
|
5617
|
+
// R2 requires specific SSL configuration
|
|
5618
|
+
...config.provider === "r2" && {
|
|
5619
|
+
requestHandler: new (init_dist_es4(), __toCommonJS(dist_es_exports)).NodeHttpHandler({
|
|
5620
|
+
connectionTimeout: 1e4,
|
|
5621
|
+
socketTimeout: 1e4
|
|
5622
|
+
})
|
|
5623
|
+
}
|
|
5624
|
+
});
|
|
5625
|
+
const getKey = (path2) => {
|
|
5626
|
+
const prefix = config.prefix ? `${config.prefix}/` : "";
|
|
5627
|
+
return `${prefix}${path2}`.replace(/\/+/g, "/");
|
|
5628
|
+
};
|
|
5629
|
+
const getUrl = (key) => getPublicUrl(key, config);
|
|
5630
|
+
return {
|
|
5631
|
+
name: config.provider,
|
|
5632
|
+
displayName: getDisplayName(config.provider),
|
|
5633
|
+
supportsDynamicResize: true,
|
|
5634
|
+
async upload(file, options) {
|
|
5635
|
+
const key = getKey(
|
|
5636
|
+
`${options?.folder ? `${options.folder}/` : ""}${options?.filename || file.name}`
|
|
5637
|
+
);
|
|
5638
|
+
const buffer = Buffer.from(await file.arrayBuffer());
|
|
5639
|
+
await client.send(
|
|
5640
|
+
new PutObjectCommand({
|
|
5641
|
+
Bucket: config.bucket,
|
|
5642
|
+
Key: key,
|
|
5643
|
+
Body: buffer,
|
|
5644
|
+
ContentType: file.type,
|
|
5645
|
+
Metadata: options?.metadata
|
|
5646
|
+
})
|
|
5647
|
+
);
|
|
5648
|
+
const head = await client.send(
|
|
5649
|
+
new HeadObjectCommand({
|
|
5650
|
+
Bucket: config.bucket,
|
|
5651
|
+
Key: key
|
|
5652
|
+
})
|
|
5653
|
+
);
|
|
5654
|
+
return {
|
|
5655
|
+
id: Buffer.from(key).toString("base64url"),
|
|
5656
|
+
filename: options?.filename || file.name,
|
|
5657
|
+
originalName: file.name,
|
|
5658
|
+
mimeType: file.type,
|
|
5659
|
+
size: buffer.length,
|
|
5660
|
+
url: getUrl(key),
|
|
5661
|
+
thumbnailUrl: file.type.startsWith("image/") ? getUrl(key) : void 0,
|
|
5662
|
+
folder: options?.folder,
|
|
5663
|
+
provider: config.provider,
|
|
5664
|
+
metadata: {
|
|
5665
|
+
...options?.metadata,
|
|
5666
|
+
etag: head.ETag
|
|
5667
|
+
},
|
|
5668
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5669
|
+
};
|
|
5670
|
+
},
|
|
5671
|
+
async uploadFromUrl(url) {
|
|
5672
|
+
const response = await fetch(url);
|
|
5673
|
+
if (!response.ok) {
|
|
5674
|
+
throw new Error(`Failed to fetch: ${response.statusText}`);
|
|
5675
|
+
}
|
|
5676
|
+
const blob = await response.blob();
|
|
5677
|
+
const filename = url.split("/").pop() || "file";
|
|
5678
|
+
const file = new File([blob], filename, { type: blob.type });
|
|
5679
|
+
return this.upload(file);
|
|
5680
|
+
},
|
|
5681
|
+
async delete(url) {
|
|
5682
|
+
const key = url.replace(getUrlPrefix(config), "");
|
|
5683
|
+
await client.send(
|
|
5684
|
+
new DeleteObjectCommand({
|
|
5685
|
+
Bucket: config.bucket,
|
|
5686
|
+
Key: key
|
|
5687
|
+
})
|
|
5688
|
+
);
|
|
5689
|
+
},
|
|
5690
|
+
async rename(oldUrl, newKey) {
|
|
5691
|
+
const oldKey = oldUrl.replace(getUrlPrefix(config), "");
|
|
5692
|
+
const newKeyWithPrefix = config.prefix ? `${config.prefix}/${newKey}` : newKey;
|
|
5693
|
+
await client.send(
|
|
5694
|
+
new CopyObjectCommand({
|
|
5695
|
+
Bucket: config.bucket,
|
|
5696
|
+
CopySource: `${config.bucket}/${oldKey}`,
|
|
5697
|
+
Key: newKeyWithPrefix
|
|
5698
|
+
})
|
|
5699
|
+
);
|
|
5700
|
+
await client.send(
|
|
5701
|
+
new DeleteObjectCommand({
|
|
5702
|
+
Bucket: config.bucket,
|
|
5703
|
+
Key: oldKey
|
|
5704
|
+
})
|
|
5705
|
+
);
|
|
5706
|
+
return getUrl(newKeyWithPrefix);
|
|
5707
|
+
},
|
|
5708
|
+
getImageUrl(url, transforms) {
|
|
5709
|
+
if (!transforms || Object.keys(transforms).length === 0) return url;
|
|
5710
|
+
const params = new URLSearchParams({ url });
|
|
5711
|
+
if (transforms.width) params.set("w", String(transforms.width));
|
|
5712
|
+
if (transforms.height) params.set("h", String(transforms.height));
|
|
5713
|
+
if (transforms.quality) params.set("q", String(transforms.quality));
|
|
5714
|
+
if (transforms.format) params.set("f", transforms.format);
|
|
5715
|
+
return `/api/media/resize?${params.toString()}`;
|
|
5716
|
+
},
|
|
5717
|
+
async generateThumbnail(file) {
|
|
5718
|
+
return this.getImageUrl(file.url, { width: 400, height: 400 });
|
|
5719
|
+
},
|
|
5720
|
+
async list(prefix) {
|
|
5721
|
+
const key = getKey(prefix || "");
|
|
5722
|
+
const response = await client.send(
|
|
5723
|
+
new ListObjectsV2Command({
|
|
5724
|
+
Bucket: config.bucket,
|
|
5725
|
+
Prefix: key
|
|
5726
|
+
})
|
|
5727
|
+
);
|
|
5728
|
+
return (response.Contents || []).map((item) => ({
|
|
5729
|
+
id: Buffer.from(item.Key || "").toString("base64url"),
|
|
5730
|
+
filename: item.Key?.split("/").pop() || "",
|
|
5731
|
+
originalName: item.Key?.split("/").pop() || "",
|
|
5732
|
+
mimeType: "application/octet-stream",
|
|
5733
|
+
size: item.Size || 0,
|
|
5734
|
+
url: getUrl(item.Key || ""),
|
|
5735
|
+
provider: config.provider,
|
|
5736
|
+
createdAt: item.LastModified?.toISOString() || (/* @__PURE__ */ new Date()).toISOString()
|
|
5737
|
+
}));
|
|
5738
|
+
},
|
|
5739
|
+
async exists(url) {
|
|
5740
|
+
try {
|
|
5741
|
+
const key = url.replace(getUrlPrefix(config), "");
|
|
5742
|
+
await client.send(
|
|
5743
|
+
new HeadObjectCommand({
|
|
5744
|
+
Bucket: config.bucket,
|
|
5745
|
+
Key: key
|
|
5746
|
+
})
|
|
5747
|
+
);
|
|
5748
|
+
return true;
|
|
5749
|
+
} catch {
|
|
5750
|
+
return false;
|
|
5751
|
+
}
|
|
5752
|
+
}
|
|
5753
|
+
};
|
|
5754
|
+
}
|
|
5755
|
+
|
|
5756
|
+
// src/storage/cloudinary.ts
|
|
5757
|
+
function createCloudinaryStorage(config) {
|
|
5758
|
+
const getBaseUrl = () => `https://api.cloudinary.com/v1_1/${config.cloudName}/upload`;
|
|
5759
|
+
const generateSignature = async (params) => {
|
|
5760
|
+
const crypto = await import('crypto');
|
|
5761
|
+
const sortedParams = Object.keys(params).sort().map((key) => `${key}=${params[key]}`).join("&");
|
|
5762
|
+
return crypto.createHash("sha256").update(sortedParams + config.apiSecret).digest("hex");
|
|
5763
|
+
};
|
|
5764
|
+
return {
|
|
5765
|
+
name: "cloudinary",
|
|
5766
|
+
displayName: "Cloudinary",
|
|
5767
|
+
supportsDynamicResize: true,
|
|
5768
|
+
async upload(file, options) {
|
|
5769
|
+
const formData = new FormData();
|
|
5770
|
+
formData.append("file", file);
|
|
5771
|
+
if (config.uploadPreset) {
|
|
5772
|
+
formData.append("upload_preset", config.uploadPreset);
|
|
5773
|
+
} else {
|
|
5774
|
+
const timestamp = Math.round(Date.now() / 1e3);
|
|
5775
|
+
const signatureParams = {
|
|
5776
|
+
timestamp: String(timestamp)
|
|
5777
|
+
};
|
|
5778
|
+
if (options?.folder || config.folder) {
|
|
5779
|
+
signatureParams.folder = options?.folder || config.folder || "";
|
|
5780
|
+
}
|
|
5781
|
+
const signature = await generateSignature(signatureParams);
|
|
5782
|
+
formData.append("timestamp", String(timestamp));
|
|
5783
|
+
formData.append("signature", signature);
|
|
5784
|
+
formData.append("api_key", config.apiKey);
|
|
5785
|
+
}
|
|
5786
|
+
if (options?.folder || config.folder) {
|
|
5787
|
+
formData.append("folder", options?.folder || config.folder || "");
|
|
5788
|
+
}
|
|
5789
|
+
const response = await fetch(getBaseUrl(), {
|
|
5790
|
+
method: "POST",
|
|
5791
|
+
body: formData
|
|
5792
|
+
});
|
|
5793
|
+
if (!response.ok) {
|
|
5794
|
+
const error = await response.json();
|
|
5795
|
+
throw new Error(
|
|
5796
|
+
`Cloudinary upload failed: ${error.error?.message || response.statusText}`
|
|
5797
|
+
);
|
|
5798
|
+
}
|
|
5799
|
+
const data = await response.json();
|
|
5800
|
+
return {
|
|
5801
|
+
id: data.public_id,
|
|
5802
|
+
filename: `${data.public_id}.${data.format}`,
|
|
5803
|
+
originalName: data.original_filename || file.name,
|
|
5804
|
+
mimeType: file.type || `image/${data.format}`,
|
|
5805
|
+
size: data.bytes,
|
|
5806
|
+
url: data.secure_url,
|
|
5807
|
+
thumbnailUrl: this.getImageUrl(data.secure_url, {
|
|
5808
|
+
width: 200,
|
|
5809
|
+
height: 200,
|
|
5810
|
+
fit: "crop"
|
|
5811
|
+
}),
|
|
5812
|
+
width: data.width,
|
|
5813
|
+
height: data.height,
|
|
5814
|
+
folder: options?.folder || config.folder,
|
|
5815
|
+
provider: "cloudinary",
|
|
5816
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5817
|
+
};
|
|
5818
|
+
},
|
|
5819
|
+
async uploadFromUrl(url, options) {
|
|
5820
|
+
const formData = new FormData();
|
|
5821
|
+
formData.append("file", url);
|
|
5822
|
+
formData.append("upload_preset", "ml_default");
|
|
5823
|
+
if (options?.folder || config.folder) {
|
|
5824
|
+
formData.append("folder", options?.folder || config.folder || "");
|
|
5825
|
+
}
|
|
5826
|
+
const response = await fetch(getBaseUrl(), {
|
|
5827
|
+
method: "POST",
|
|
5828
|
+
body: formData
|
|
5829
|
+
});
|
|
5830
|
+
if (!response.ok) throw new Error("Cloudinary upload failed");
|
|
5831
|
+
const data = await response.json();
|
|
5832
|
+
return {
|
|
5833
|
+
id: data.public_id,
|
|
5834
|
+
filename: `${data.public_id}.${data.format}`,
|
|
5835
|
+
originalName: data.original_filename || url.split("/").pop() || "file",
|
|
5836
|
+
mimeType: `image/${data.format}`,
|
|
5837
|
+
size: data.bytes,
|
|
5838
|
+
url: data.secure_url,
|
|
5839
|
+
thumbnailUrl: this.getImageUrl(data.secure_url, {
|
|
5840
|
+
width: 200,
|
|
5841
|
+
height: 200,
|
|
5842
|
+
fit: "crop"
|
|
5843
|
+
}),
|
|
5844
|
+
width: data.width,
|
|
5845
|
+
height: data.height,
|
|
5846
|
+
provider: "cloudinary",
|
|
5847
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5848
|
+
};
|
|
5849
|
+
},
|
|
5850
|
+
async delete(id) {
|
|
5851
|
+
try {
|
|
5852
|
+
const parts = id.split("/image/upload/");
|
|
5853
|
+
if (parts.length !== 2) {
|
|
5854
|
+
console.warn("[Cloudinary delete] Could not parse URL:", id);
|
|
5855
|
+
return;
|
|
5856
|
+
}
|
|
5857
|
+
const publicId = parts[1].replace(/^v\d+\//, "").replace(/\.[^.]+$/, "");
|
|
5858
|
+
const timestamp = Math.round(Date.now() / 1e3);
|
|
5859
|
+
const signatureParams = {
|
|
5860
|
+
timestamp: String(timestamp),
|
|
5861
|
+
public_id: publicId
|
|
5862
|
+
};
|
|
5863
|
+
const sortedParams = Object.keys(signatureParams).sort().map((key) => `${key}=${signatureParams[key]}`).join("&");
|
|
5864
|
+
const crypto = await import('crypto');
|
|
5865
|
+
const signature = crypto.createHash("sha256").update(sortedParams + config.apiSecret).digest("hex");
|
|
5866
|
+
const deleteUrl = `https://api.cloudinary.com/v1_1/${config.cloudName}/image/destroy`;
|
|
5867
|
+
const formData = new FormData();
|
|
5868
|
+
formData.append("public_id", publicId);
|
|
5869
|
+
formData.append("timestamp", String(timestamp));
|
|
5870
|
+
formData.append("signature", signature);
|
|
5871
|
+
formData.append("api_key", config.apiKey);
|
|
5872
|
+
const response = await fetch(deleteUrl, {
|
|
5873
|
+
method: "POST",
|
|
5874
|
+
body: formData
|
|
5875
|
+
});
|
|
5876
|
+
if (!response.ok) {
|
|
5877
|
+
const error = await response.json();
|
|
5878
|
+
console.warn("[Cloudinary delete] Failed:", error);
|
|
5879
|
+
}
|
|
5880
|
+
} catch (e) {
|
|
5881
|
+
console.warn("[Cloudinary delete] Error:", e.message);
|
|
5882
|
+
}
|
|
5883
|
+
},
|
|
5884
|
+
async rename(oldUrl, newKey) {
|
|
5885
|
+
let version = "";
|
|
5886
|
+
let newPublicId = newKey.replace(/\.[^.]+$/, "");
|
|
5887
|
+
let folder = "";
|
|
5888
|
+
try {
|
|
5889
|
+
const parts = oldUrl.split("/image/upload/");
|
|
5890
|
+
if (parts.length !== 2) {
|
|
5891
|
+
console.warn("[Cloudinary rename] Could not parse old URL:", oldUrl);
|
|
5892
|
+
return oldUrl;
|
|
5893
|
+
}
|
|
5894
|
+
const urlPath = parts[1];
|
|
5895
|
+
const versionMatch = urlPath.match(/^(v\d+)\//);
|
|
5896
|
+
version = versionMatch ? versionMatch[1] : "";
|
|
5897
|
+
const publicIdWithoutVersion = urlPath.replace(/^v\d+\//, "").replace(/\.[^.]+$/, "");
|
|
5898
|
+
const folderParts = publicIdWithoutVersion.split("/");
|
|
5899
|
+
folder = folderParts.length > 1 ? folderParts.slice(0, -1).join("/") : "";
|
|
5900
|
+
const newFilename = newKey.replace(/\.[^.]+$/, "");
|
|
5901
|
+
newPublicId = folder ? `${folder}/${newFilename}` : newFilename;
|
|
5902
|
+
console.log("[Cloudinary rename]", {
|
|
5903
|
+
urlPath,
|
|
5904
|
+
version,
|
|
5905
|
+
publicIdWithoutVersion,
|
|
5906
|
+
folder,
|
|
5907
|
+
newPublicId
|
|
5908
|
+
});
|
|
5909
|
+
const timestamp = Math.round(Date.now() / 1e3);
|
|
5910
|
+
const signatureParams = {
|
|
5911
|
+
from_public_id: publicIdWithoutVersion,
|
|
5912
|
+
to_public_id: newPublicId,
|
|
5913
|
+
timestamp: String(timestamp)
|
|
5914
|
+
};
|
|
5915
|
+
const sortedParams = Object.keys(signatureParams).sort().map((key) => `${key}=${signatureParams[key]}`).join("&");
|
|
5916
|
+
const crypto = await import('crypto');
|
|
5917
|
+
const signature = crypto.createHash("sha256").update(sortedParams + config.apiSecret).digest("hex");
|
|
5918
|
+
const formData = new FormData();
|
|
5919
|
+
formData.append("from_public_id", publicIdWithoutVersion);
|
|
5920
|
+
formData.append("to_public_id", newPublicId);
|
|
5921
|
+
formData.append("timestamp", String(timestamp));
|
|
5922
|
+
formData.append("signature", signature);
|
|
5923
|
+
formData.append("api_key", config.apiKey);
|
|
5924
|
+
const response = await fetch(
|
|
5925
|
+
`https://api.cloudinary.com/v1_1/${config.cloudName}/image/rename`,
|
|
5926
|
+
{
|
|
5927
|
+
method: "POST",
|
|
5928
|
+
body: formData
|
|
5929
|
+
}
|
|
5930
|
+
);
|
|
5931
|
+
if (response.ok) {
|
|
5932
|
+
const data = await response.json();
|
|
5933
|
+
console.log("[Cloudinary rename] Success:", data.secure_url);
|
|
5934
|
+
return data.secure_url;
|
|
5935
|
+
} else {
|
|
5936
|
+
const error = await response.json();
|
|
5937
|
+
console.warn("[Cloudinary rename] Failed:", error);
|
|
5938
|
+
const versionStr = version ? `/${version}/` : "/";
|
|
5939
|
+
return `https://res.cloudinary.com/${config.cloudName}/image/upload${versionStr}${newPublicId}.${newKey.split(".").pop()}`;
|
|
5940
|
+
}
|
|
5941
|
+
} catch (e) {
|
|
5942
|
+
console.warn("[Cloudinary rename] Error:", e.message);
|
|
5943
|
+
const versionStr = version ? `/${version}/` : "/";
|
|
5944
|
+
return `https://res.cloudinary.com/${config.cloudName}/image/upload${versionStr}${newPublicId}.${newKey.split(".").pop()}`;
|
|
5945
|
+
}
|
|
5946
|
+
},
|
|
5947
|
+
getImageUrl(url, transforms) {
|
|
5948
|
+
if (!transforms) return url;
|
|
5949
|
+
const parts = url.split("/upload/");
|
|
5950
|
+
if (parts.length !== 2) return url;
|
|
5951
|
+
const transformationArr = [];
|
|
5952
|
+
if (transforms.width) transformationArr.push(`w_${transforms.width}`);
|
|
5953
|
+
if (transforms.height) transformationArr.push(`h_${transforms.height}`);
|
|
5954
|
+
if (transforms.fit) {
|
|
5955
|
+
const fitMap = {
|
|
5956
|
+
crop: "c_fill",
|
|
5957
|
+
clip: "c_fit",
|
|
5958
|
+
scale: "c_scale",
|
|
5959
|
+
fill: "c_fill"
|
|
5960
|
+
};
|
|
5961
|
+
transformationArr.push(fitMap[transforms.fit] || "c_limit");
|
|
5962
|
+
}
|
|
5963
|
+
if (transforms.quality) transformationArr.push(`q_${transforms.quality}`);
|
|
5964
|
+
if (transforms.format) transformationArr.push(`f_${transforms.format}`);
|
|
5965
|
+
const transformationStr = transformationArr.join(",");
|
|
5966
|
+
return `${parts[0]}/upload/${transformationStr}/${parts[1]}`;
|
|
5967
|
+
},
|
|
5968
|
+
async generateThumbnail(file) {
|
|
5969
|
+
return this.getImageUrl(file.url, {
|
|
5970
|
+
width: 200,
|
|
5971
|
+
height: 200,
|
|
5972
|
+
fit: "crop"
|
|
5973
|
+
});
|
|
5974
|
+
},
|
|
5975
|
+
async list() {
|
|
5976
|
+
return [];
|
|
5977
|
+
},
|
|
5978
|
+
async exists(url) {
|
|
5979
|
+
const response = await fetch(url, { method: "HEAD" });
|
|
5980
|
+
return response.ok;
|
|
5981
|
+
}
|
|
5982
|
+
};
|
|
5983
|
+
}
|
|
5984
|
+
|
|
5985
|
+
// src/storage/imgix.ts
|
|
5986
|
+
function createImgixStorage(config) {
|
|
5987
|
+
const signUrl = (path2, params) => {
|
|
5988
|
+
if (!config.signKey) return path2;
|
|
5989
|
+
const signer = new TextEncoder();
|
|
5990
|
+
const key = signer.encode(config.signKey);
|
|
5991
|
+
const data = signer.encode(path2 + params.toString());
|
|
5992
|
+
let hash = 0;
|
|
5993
|
+
const combined = new Uint8Array(key.length + data.length);
|
|
5994
|
+
combined.set(key);
|
|
5995
|
+
combined.set(data, key.length);
|
|
5996
|
+
for (let i = 0; i < combined.length; i++) {
|
|
5997
|
+
hash = (hash << 5) - hash + combined[i] | 0;
|
|
5998
|
+
}
|
|
5999
|
+
params.set("s", Math.abs(hash).toString(16));
|
|
6000
|
+
return path2;
|
|
6001
|
+
};
|
|
6002
|
+
return {
|
|
6003
|
+
name: "imgix",
|
|
6004
|
+
displayName: "Imgix",
|
|
6005
|
+
supportsDynamicResize: true,
|
|
6006
|
+
async upload(_file, _options) {
|
|
6007
|
+
throw new Error(
|
|
6008
|
+
"Imgix is a transformation service. Use another provider for uploads."
|
|
6009
|
+
);
|
|
6010
|
+
},
|
|
6011
|
+
async uploadFromUrl(url, options) {
|
|
6012
|
+
const filename = options?.filename || url.split("/").pop() || "file";
|
|
6013
|
+
const response = await fetch(url);
|
|
6014
|
+
if (!response.ok) {
|
|
6015
|
+
throw new Error(`Failed to fetch: ${response.statusText}`);
|
|
6016
|
+
}
|
|
6017
|
+
const blob = await response.blob();
|
|
6018
|
+
new File([blob], filename, { type: blob.type });
|
|
6019
|
+
return {
|
|
6020
|
+
id: Buffer.from(url).toString("base64").slice(0, 20),
|
|
6021
|
+
filename,
|
|
6022
|
+
originalName: filename,
|
|
6023
|
+
mimeType: blob.type,
|
|
6024
|
+
size: blob.size,
|
|
6025
|
+
url: this.getImageUrl(url),
|
|
6026
|
+
thumbnailUrl: this.getImageUrl(url, {
|
|
6027
|
+
width: 200,
|
|
6028
|
+
height: 200,
|
|
6029
|
+
fit: "crop"
|
|
6030
|
+
}),
|
|
6031
|
+
provider: "imgix",
|
|
6032
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6033
|
+
};
|
|
6034
|
+
},
|
|
6035
|
+
async delete(_url) {
|
|
6036
|
+
},
|
|
6037
|
+
async rename(_oldUrl, newKey) {
|
|
6038
|
+
return `https://${config.domain}/${newKey}`;
|
|
6039
|
+
},
|
|
6040
|
+
getImageUrl(url, transforms) {
|
|
6041
|
+
const parsed = new URL(url);
|
|
6042
|
+
const params = new URLSearchParams(parsed.search);
|
|
6043
|
+
if (config.defaultParameters) {
|
|
6044
|
+
Object.entries(config.defaultParameters).forEach(([key, value]) => {
|
|
6045
|
+
if (!params.has(key)) {
|
|
6046
|
+
params.set(key, value);
|
|
6047
|
+
}
|
|
6048
|
+
});
|
|
6049
|
+
}
|
|
6050
|
+
if (transforms) {
|
|
6051
|
+
if (transforms.width) params.set("w", String(transforms.width));
|
|
6052
|
+
if (transforms.height) params.set("h", String(transforms.height));
|
|
6053
|
+
if (transforms.quality) params.set("q", String(transforms.quality));
|
|
6054
|
+
if (transforms.format) params.set("fm", transforms.format);
|
|
6055
|
+
if (transforms.fit) params.set("fit", transforms.fit);
|
|
6056
|
+
if (transforms.blur) params.set("blur", String(transforms.blur));
|
|
6057
|
+
if (transforms.sharpen) params.set("sharp", String(transforms.sharpen));
|
|
6058
|
+
}
|
|
6059
|
+
params.set("auto", "compress,format");
|
|
6060
|
+
parsed.pathname + "?" + params.toString();
|
|
6061
|
+
if (config.signKey) {
|
|
6062
|
+
signUrl(parsed.pathname + params.toString(), params);
|
|
6063
|
+
}
|
|
6064
|
+
return `https://${config.domain}${parsed.pathname}${params.toString() ? "?" + params.toString() : ""}`;
|
|
6065
|
+
},
|
|
6066
|
+
async generateThumbnail(file) {
|
|
6067
|
+
return this.getImageUrl(file.url, {
|
|
6068
|
+
width: 200,
|
|
6069
|
+
height: 200,
|
|
6070
|
+
fit: "crop"
|
|
6071
|
+
});
|
|
6072
|
+
},
|
|
6073
|
+
async list() {
|
|
6074
|
+
return [];
|
|
6075
|
+
},
|
|
6076
|
+
async exists(url) {
|
|
6077
|
+
try {
|
|
6078
|
+
const response = await fetch(url, { method: "HEAD" });
|
|
6079
|
+
return response.ok;
|
|
6080
|
+
} catch {
|
|
6081
|
+
return false;
|
|
6082
|
+
}
|
|
6083
|
+
}
|
|
6084
|
+
};
|
|
6085
|
+
}
|
|
6086
|
+
function createFtpStorage(config) {
|
|
6087
|
+
let client = null;
|
|
6088
|
+
async function getClient() {
|
|
6089
|
+
if (!client) {
|
|
6090
|
+
client = new Client(6e4, { allowSeparateTransferHost: true });
|
|
6091
|
+
client.ftp.verbose = false;
|
|
6092
|
+
await client.access({
|
|
6093
|
+
host: config.host,
|
|
6094
|
+
port: config.port || 21,
|
|
6095
|
+
user: config.user,
|
|
6096
|
+
password: config.password,
|
|
6097
|
+
secure: config.secure,
|
|
6098
|
+
secureOptions: {}
|
|
6099
|
+
});
|
|
6100
|
+
}
|
|
6101
|
+
return client;
|
|
6102
|
+
}
|
|
6103
|
+
const getKey = (path2) => {
|
|
6104
|
+
const prefix = config.prefix ? `${config.prefix}/` : "";
|
|
6105
|
+
return `${prefix}${path2}`.replace(/\/+/g, "/");
|
|
6106
|
+
};
|
|
6107
|
+
const getUrl = (key) => {
|
|
6108
|
+
const base = config.baseUrl.replace(/\/$/, "");
|
|
6109
|
+
return `${base}/${key}`;
|
|
6110
|
+
};
|
|
6111
|
+
const getUrlPrefix2 = () => {
|
|
6112
|
+
const base = config.baseUrl.replace(/\/$/, "");
|
|
6113
|
+
return base + "/";
|
|
6114
|
+
};
|
|
6115
|
+
return {
|
|
6116
|
+
name: config.type,
|
|
6117
|
+
displayName: config.type === "sftp" ? "SFTP Storage" : "FTP Storage",
|
|
6118
|
+
supportsDynamicResize: false,
|
|
6119
|
+
async upload(file, options) {
|
|
6120
|
+
const ftp = await getClient();
|
|
6121
|
+
const key = getKey(
|
|
6122
|
+
`${options?.folder ? `${options.folder}/` : ""}${options?.filename || file.name}`
|
|
6123
|
+
);
|
|
6124
|
+
const buffer = Buffer.from(await file.arrayBuffer());
|
|
6125
|
+
const parts = key.split("/").slice(0, -1);
|
|
6126
|
+
let currentPath = "";
|
|
6127
|
+
for (const part of parts) {
|
|
6128
|
+
currentPath = currentPath ? `${currentPath}/${part}` : part;
|
|
6129
|
+
try {
|
|
6130
|
+
await ftp.ensureDir(currentPath);
|
|
6131
|
+
} catch {
|
|
6132
|
+
}
|
|
6133
|
+
}
|
|
6134
|
+
const readable = Readable.from(buffer);
|
|
6135
|
+
await ftp.uploadFrom(readable, key);
|
|
6136
|
+
return {
|
|
6137
|
+
id: Buffer.from(key).toString("base64url"),
|
|
6138
|
+
filename: options?.filename || file.name,
|
|
6139
|
+
originalName: file.name,
|
|
6140
|
+
mimeType: file.type,
|
|
6141
|
+
size: buffer.length,
|
|
6142
|
+
url: getUrl(key),
|
|
6143
|
+
thumbnailUrl: file.type.startsWith("image/") ? getUrl(key) : void 0,
|
|
6144
|
+
folder: options?.folder,
|
|
6145
|
+
provider: config.type,
|
|
6146
|
+
metadata: options?.metadata,
|
|
6147
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6148
|
+
};
|
|
6149
|
+
},
|
|
6150
|
+
async uploadFromUrl(url) {
|
|
6151
|
+
const response = await fetch(url);
|
|
6152
|
+
if (!response.ok) {
|
|
6153
|
+
throw new Error(`Failed to fetch: ${response.statusText}`);
|
|
6154
|
+
}
|
|
6155
|
+
const blob = await response.blob();
|
|
6156
|
+
const filename = url.split("/").pop() || "file";
|
|
6157
|
+
const file = new File([blob], filename, { type: blob.type });
|
|
6158
|
+
return this.upload(file);
|
|
6159
|
+
},
|
|
6160
|
+
async delete(url) {
|
|
6161
|
+
const ftp = await getClient();
|
|
6162
|
+
const key = url.replace(getUrlPrefix2(), "");
|
|
6163
|
+
await ftp.remove(key);
|
|
6164
|
+
},
|
|
6165
|
+
async rename(oldUrl, newKey) {
|
|
6166
|
+
const ftp = await getClient();
|
|
6167
|
+
const oldKey = oldUrl.replace(getUrlPrefix2(), "");
|
|
6168
|
+
const fullPath = config.prefix ? `${config.prefix}/${newKey}` : newKey;
|
|
6169
|
+
await ftp.rename(oldKey, fullPath);
|
|
6170
|
+
return getUrl(fullPath);
|
|
6171
|
+
},
|
|
6172
|
+
getImageUrl(url, transforms) {
|
|
6173
|
+
if (!transforms || Object.keys(transforms).length === 0) return url;
|
|
6174
|
+
const params = new URLSearchParams({ url });
|
|
6175
|
+
if (transforms.width) params.set("w", String(transforms.width));
|
|
6176
|
+
if (transforms.height) params.set("h", String(transforms.height));
|
|
6177
|
+
if (transforms.quality) params.set("q", String(transforms.quality));
|
|
6178
|
+
if (transforms.format) params.set("f", transforms.format);
|
|
6179
|
+
return `/api/media/resize?${params.toString()}`;
|
|
6180
|
+
},
|
|
6181
|
+
async generateThumbnail(file) {
|
|
6182
|
+
return file.url;
|
|
6183
|
+
},
|
|
6184
|
+
async list(prefix) {
|
|
6185
|
+
const ftp = await getClient();
|
|
6186
|
+
const key = getKey(prefix || "");
|
|
6187
|
+
let items;
|
|
6188
|
+
try {
|
|
6189
|
+
items = await ftp.list(key);
|
|
6190
|
+
} catch {
|
|
6191
|
+
return [];
|
|
6192
|
+
}
|
|
6193
|
+
return items.filter((item) => item.type === 0).map((item) => ({
|
|
6194
|
+
id: Buffer.from(`${key}/${item.name}`).toString("base64url"),
|
|
6195
|
+
filename: item.name,
|
|
6196
|
+
originalName: item.name,
|
|
6197
|
+
mimeType: "application/octet-stream",
|
|
6198
|
+
size: Number(item.size) || 0,
|
|
6199
|
+
url: getUrl(`${key}/${item.name}`.replace(/\/+/g, "/")),
|
|
6200
|
+
provider: config.type,
|
|
6201
|
+
createdAt: item.modifiedAt ? item.modifiedAt.toISOString() : (/* @__PURE__ */ new Date()).toISOString()
|
|
6202
|
+
}));
|
|
6203
|
+
},
|
|
6204
|
+
async exists(url) {
|
|
6205
|
+
const ftp = await getClient();
|
|
6206
|
+
const key = url.replace(getUrlPrefix2(), "");
|
|
6207
|
+
try {
|
|
6208
|
+
await ftp.size(key);
|
|
6209
|
+
return true;
|
|
6210
|
+
} catch {
|
|
6211
|
+
return false;
|
|
6212
|
+
}
|
|
6213
|
+
}
|
|
6214
|
+
};
|
|
6215
|
+
}
|
|
6216
|
+
|
|
6217
|
+
// src/storage/index.ts
|
|
6218
|
+
async function resolveProvider(configService) {
|
|
6219
|
+
const config = configService.getStorageConfig();
|
|
6220
|
+
switch (config.type) {
|
|
6221
|
+
case "aws":
|
|
6222
|
+
return createS3Storage({
|
|
6223
|
+
provider: "aws",
|
|
6224
|
+
bucket: config.s3.bucket || "",
|
|
6225
|
+
region: config.s3.region || "us-east-1",
|
|
6226
|
+
accessKeyId: config.s3.accessKeyId || "",
|
|
6227
|
+
secretAccessKey: config.s3.secretAccessKey || "",
|
|
6228
|
+
endpoint: config.s3.endpoint,
|
|
6229
|
+
cdnUrl: config.s3.cdnUrl,
|
|
6230
|
+
prefix: config.s3.prefix
|
|
6231
|
+
});
|
|
6232
|
+
case "r2":
|
|
6233
|
+
return createS3Storage({
|
|
6234
|
+
provider: "r2",
|
|
6235
|
+
bucket: config.r2.bucket || "",
|
|
6236
|
+
region: "auto",
|
|
6237
|
+
accessKeyId: config.r2.accessKeyId || "",
|
|
6238
|
+
secretAccessKey: config.r2.secretAccessKey || "",
|
|
6239
|
+
accountId: config.r2.accountId || "",
|
|
6240
|
+
publicDevUrl: config.r2.publicDevUrl,
|
|
6241
|
+
endpoint: `https://${config.r2.accountId || ""}.r2.cloudflarestorage.com`,
|
|
6242
|
+
cdnUrl: config.r2.cdnUrl,
|
|
6243
|
+
prefix: config.r2.prefix
|
|
6244
|
+
});
|
|
6245
|
+
case "gcs":
|
|
6246
|
+
return createS3Storage({
|
|
6247
|
+
provider: "gcs",
|
|
6248
|
+
bucket: config.gcs.bucket || "",
|
|
6249
|
+
region: config.gcs.projectId || "auto",
|
|
6250
|
+
accessKeyId: config.gcs.clientEmail || "",
|
|
6251
|
+
secretAccessKey: config.gcs.privateKey || "",
|
|
6252
|
+
cdnUrl: config.gcs.cdnUrl,
|
|
6253
|
+
prefix: config.gcs.prefix
|
|
6254
|
+
});
|
|
6255
|
+
case "digitalocean":
|
|
6256
|
+
return createS3Storage({
|
|
6257
|
+
provider: "digitalocean",
|
|
6258
|
+
bucket: config.digitalocean.bucket || "",
|
|
6259
|
+
region: config.digitalocean.region || "nyc3",
|
|
6260
|
+
accessKeyId: config.digitalocean.accessKeyId || "",
|
|
6261
|
+
secretAccessKey: config.digitalocean.secretAccessKey || "",
|
|
6262
|
+
endpoint: `https://${config.digitalocean.region || "nyc3"}.digitaloceanspaces.com`,
|
|
6263
|
+
cdnUrl: config.digitalocean.cdnUrl,
|
|
6264
|
+
prefix: config.digitalocean.prefix
|
|
6265
|
+
});
|
|
6266
|
+
case "backblaze":
|
|
6267
|
+
return createS3Storage({
|
|
6268
|
+
provider: "backblaze",
|
|
6269
|
+
bucket: config.backblaze.bucket || "",
|
|
6270
|
+
region: "auto",
|
|
6271
|
+
accessKeyId: config.backblaze.applicationKeyId || "",
|
|
6272
|
+
secretAccessKey: config.backblaze.applicationKey || "",
|
|
6273
|
+
accountId: config.backblaze.accountId || "",
|
|
6274
|
+
endpoint: `https://s3.backblazeb2.com`,
|
|
6275
|
+
cdnUrl: config.backblaze.cdnUrl,
|
|
6276
|
+
prefix: config.backblaze.prefix
|
|
6277
|
+
});
|
|
6278
|
+
case "wasabi":
|
|
6279
|
+
return createS3Storage({
|
|
6280
|
+
provider: "wasabi",
|
|
6281
|
+
bucket: config.wasabi.bucket || "",
|
|
6282
|
+
region: config.wasabi.region || "us-east-1",
|
|
6283
|
+
accessKeyId: config.wasabi.accessKeyId || "",
|
|
6284
|
+
secretAccessKey: config.wasabi.secretAccessKey || "",
|
|
6285
|
+
endpoint: `https://s3.${config.wasabi.region || "us-east-1"}.wasabisys.com`,
|
|
6286
|
+
cdnUrl: config.wasabi.cdnUrl,
|
|
6287
|
+
prefix: config.wasabi.prefix
|
|
6288
|
+
});
|
|
6289
|
+
case "ftp":
|
|
6290
|
+
case "sftp":
|
|
6291
|
+
return createFtpStorage({
|
|
6292
|
+
host: config.ftp?.host || "",
|
|
6293
|
+
port: config.ftp?.port || 21,
|
|
6294
|
+
user: config.ftp?.user || "",
|
|
6295
|
+
password: config.ftp?.password || "",
|
|
6296
|
+
secure: config.ftp?.secure || false,
|
|
6297
|
+
baseUrl: config.ftp?.baseUrl || "",
|
|
6298
|
+
prefix: config.ftp?.prefix,
|
|
6299
|
+
type: "ftp"
|
|
6300
|
+
});
|
|
6301
|
+
case "cloudinary":
|
|
6302
|
+
return createCloudinaryStorage({
|
|
6303
|
+
cloudName: config.cloudinary.cloudName || "",
|
|
6304
|
+
apiKey: config.cloudinary.apiKey || "",
|
|
6305
|
+
apiSecret: config.cloudinary.apiSecret || "",
|
|
6306
|
+
folder: config.cloudinary.folder
|
|
6307
|
+
});
|
|
6308
|
+
case "imgix":
|
|
6309
|
+
return createImgixStorage({
|
|
6310
|
+
domain: config.imgix.domain || "",
|
|
6311
|
+
signKey: config.imgix.signKey
|
|
6312
|
+
});
|
|
6313
|
+
case "local":
|
|
6314
|
+
default:
|
|
6315
|
+
return createLocalStorage({
|
|
6316
|
+
uploadDir: config.local.uploadDir || path.join(process.cwd(), "public", "uploads"),
|
|
6317
|
+
baseUrl: config.local.baseUrl || "/uploads"
|
|
6318
|
+
});
|
|
6319
|
+
}
|
|
6320
|
+
}
|
|
6321
|
+
async function resolveProviderWithConfig(config) {
|
|
6322
|
+
if (!config) {
|
|
6323
|
+
console.warn("[resolveProviderWithConfig] No config, using local");
|
|
6324
|
+
return createLocalStorage({
|
|
6325
|
+
uploadDir: path.join(process.cwd(), "public", "uploads"),
|
|
6326
|
+
baseUrl: "/uploads"
|
|
6327
|
+
});
|
|
6328
|
+
}
|
|
6329
|
+
console.log("[resolveProviderWithConfig] Creating provider:", config.type);
|
|
6330
|
+
switch (config.type) {
|
|
6331
|
+
case "aws":
|
|
6332
|
+
return createS3Storage({
|
|
6333
|
+
provider: "aws",
|
|
6334
|
+
bucket: config.s3?.bucket || "",
|
|
6335
|
+
region: config.s3?.region || "us-east-1",
|
|
6336
|
+
accessKeyId: config.s3?.accessKeyId || "",
|
|
6337
|
+
secretAccessKey: config.s3?.secretAccessKey || "",
|
|
6338
|
+
endpoint: config.s3?.endpoint,
|
|
6339
|
+
cdnUrl: config.s3?.cdnUrl,
|
|
6340
|
+
prefix: config.s3?.prefix
|
|
6341
|
+
});
|
|
6342
|
+
case "r2":
|
|
6343
|
+
return createS3Storage({
|
|
6344
|
+
provider: "r2",
|
|
6345
|
+
bucket: config.r2?.bucket || "",
|
|
6346
|
+
region: "auto",
|
|
6347
|
+
accessKeyId: config.r2?.accessKeyId || "",
|
|
6348
|
+
secretAccessKey: config.r2?.secretAccessKey || "",
|
|
6349
|
+
accountId: config.r2?.accountId || "",
|
|
6350
|
+
publicDevUrl: config.r2?.publicDevUrl,
|
|
6351
|
+
endpoint: `https://${config.r2?.accountId || ""}.r2.cloudflarestorage.com`,
|
|
6352
|
+
cdnUrl: config.r2?.cdnUrl,
|
|
6353
|
+
prefix: config.r2?.prefix
|
|
6354
|
+
});
|
|
6355
|
+
case "gcs":
|
|
6356
|
+
return createS3Storage({
|
|
6357
|
+
provider: "gcs",
|
|
6358
|
+
bucket: config.gcs?.bucket || "",
|
|
6359
|
+
region: config.gcs?.projectId || "auto",
|
|
6360
|
+
accessKeyId: config.gcs?.clientEmail || "",
|
|
6361
|
+
secretAccessKey: config.gcs?.privateKey || "",
|
|
6362
|
+
cdnUrl: config.gcs?.cdnUrl,
|
|
6363
|
+
prefix: config.gcs?.prefix
|
|
6364
|
+
});
|
|
6365
|
+
case "digitalocean":
|
|
6366
|
+
return createS3Storage({
|
|
6367
|
+
provider: "digitalocean",
|
|
6368
|
+
bucket: config.digitalocean?.bucket || "",
|
|
6369
|
+
region: config.digitalocean?.region || "nyc3",
|
|
6370
|
+
accessKeyId: config.digitalocean?.accessKeyId || "",
|
|
6371
|
+
secretAccessKey: config.digitalocean?.secretAccessKey || "",
|
|
6372
|
+
cdnUrl: config.digitalocean?.cdnUrl,
|
|
6373
|
+
prefix: config.digitalocean?.prefix
|
|
6374
|
+
});
|
|
6375
|
+
case "backblaze":
|
|
6376
|
+
return createS3Storage({
|
|
6377
|
+
provider: "backblaze",
|
|
6378
|
+
bucket: config.backblaze?.bucket || "",
|
|
6379
|
+
region: "auto",
|
|
6380
|
+
accessKeyId: config.backblaze?.applicationKeyId || "",
|
|
6381
|
+
secretAccessKey: config.backblaze?.applicationKey || "",
|
|
6382
|
+
cdnUrl: config.backblaze?.cdnUrl,
|
|
6383
|
+
prefix: config.backblaze?.prefix
|
|
6384
|
+
});
|
|
6385
|
+
case "wasabi":
|
|
6386
|
+
return createS3Storage({
|
|
6387
|
+
provider: "wasabi",
|
|
6388
|
+
bucket: config.wasabi?.bucket || "",
|
|
6389
|
+
region: config.wasabi?.region || "us-east-1",
|
|
6390
|
+
accessKeyId: config.wasabi?.accessKeyId || "",
|
|
6391
|
+
secretAccessKey: config.wasabi?.secretAccessKey || "",
|
|
6392
|
+
cdnUrl: config.wasabi?.cdnUrl,
|
|
6393
|
+
prefix: config.wasabi?.prefix
|
|
6394
|
+
});
|
|
6395
|
+
case "cloudinary":
|
|
6396
|
+
return createCloudinaryStorage({
|
|
6397
|
+
cloudName: config.cloudinary?.cloudName || "",
|
|
6398
|
+
apiKey: config.cloudinary?.apiKey || "",
|
|
6399
|
+
apiSecret: config.cloudinary?.apiSecret || "",
|
|
6400
|
+
folder: config.cloudinary?.folder
|
|
6401
|
+
});
|
|
6402
|
+
case "ftp":
|
|
6403
|
+
case "sftp": {
|
|
6404
|
+
const ftpConf = config.ftp || config;
|
|
6405
|
+
return createFtpStorage({
|
|
6406
|
+
type: "ftp",
|
|
6407
|
+
host: ftpConf.host || "",
|
|
6408
|
+
port: ftpConf.port || 21,
|
|
6409
|
+
user: ftpConf.user || "",
|
|
6410
|
+
password: ftpConf.password || "",
|
|
6411
|
+
secure: ftpConf.secure || false,
|
|
6412
|
+
baseUrl: ftpConf.baseUrl || "",
|
|
6413
|
+
prefix: ftpConf.prefix
|
|
6414
|
+
});
|
|
6415
|
+
}
|
|
6416
|
+
case "local":
|
|
6417
|
+
default: {
|
|
6418
|
+
const localConfig = config.local || {
|
|
6419
|
+
uploadDir: config["local.uploadDir"],
|
|
6420
|
+
baseUrl: config["local.baseUrl"]
|
|
6421
|
+
};
|
|
6422
|
+
const savedUploadDir = (localConfig?.uploadDir || "").trim();
|
|
6423
|
+
let uploadDir;
|
|
6424
|
+
if (savedUploadDir) {
|
|
6425
|
+
if (path.isAbsolute(savedUploadDir)) {
|
|
6426
|
+
uploadDir = savedUploadDir;
|
|
6427
|
+
} else if (savedUploadDir.includes("/") || savedUploadDir.includes("\\")) {
|
|
6428
|
+
uploadDir = path.resolve(process.cwd(), savedUploadDir);
|
|
6429
|
+
} else {
|
|
6430
|
+
uploadDir = path.join(process.cwd(), "public", savedUploadDir);
|
|
6431
|
+
}
|
|
6432
|
+
} else {
|
|
6433
|
+
uploadDir = path.join(process.cwd(), "public", "uploads");
|
|
6434
|
+
}
|
|
6435
|
+
const savedBaseUrl = (localConfig?.baseUrl || "").trim();
|
|
6436
|
+
let baseUrl;
|
|
6437
|
+
if (savedBaseUrl) {
|
|
6438
|
+
baseUrl = savedBaseUrl.startsWith("/") ? savedBaseUrl : `/${savedBaseUrl}`;
|
|
6439
|
+
} else {
|
|
6440
|
+
baseUrl = "/uploads";
|
|
6441
|
+
}
|
|
6442
|
+
return createLocalStorage({ uploadDir, baseUrl });
|
|
6443
|
+
}
|
|
6444
|
+
}
|
|
6445
|
+
}
|
|
6446
|
+
async function processImage(buffer) {
|
|
6447
|
+
const metadata = await sharp(buffer).metadata();
|
|
6448
|
+
const mainImage = sharp(buffer).webp({ quality: 85 });
|
|
6449
|
+
const thumbnail = sharp(buffer).resize({ width: 500, withoutEnlargement: true }).webp({ quality: 80 });
|
|
6450
|
+
return {
|
|
6451
|
+
buffer: await mainImage.toBuffer(),
|
|
6452
|
+
thumbnailBuffer: await thumbnail.toBuffer(),
|
|
6453
|
+
width: metadata.width,
|
|
6454
|
+
height: metadata.height,
|
|
6455
|
+
format: "webp"
|
|
6456
|
+
};
|
|
6457
|
+
}
|
|
6458
|
+
|
|
6459
|
+
// src/storage/MediaService.ts
|
|
6460
|
+
var MediaService = class _MediaService {
|
|
6461
|
+
db;
|
|
6462
|
+
storage;
|
|
6463
|
+
dialect;
|
|
6464
|
+
genId;
|
|
6465
|
+
mediaTable = "media";
|
|
6466
|
+
foldersTable = "media_folders";
|
|
6467
|
+
constructor(db, storage, options) {
|
|
6468
|
+
this.db = db;
|
|
6469
|
+
this.storage = storage;
|
|
6470
|
+
this.dialect = options?.dialect || "sqlite";
|
|
6471
|
+
this.genId = options?.genId || genId;
|
|
6472
|
+
}
|
|
6473
|
+
static async init(db, options) {
|
|
6474
|
+
let storage;
|
|
6475
|
+
if (options?.storageConfig) {
|
|
6476
|
+
console.log(
|
|
6477
|
+
"[MediaService.init] Using provided storageConfig:",
|
|
6478
|
+
options.storageConfig.type
|
|
6479
|
+
);
|
|
6480
|
+
storage = await resolveProviderWithConfig(options.storageConfig);
|
|
6481
|
+
} else {
|
|
6482
|
+
const configService = new ConfigService(db);
|
|
6483
|
+
await configService.load();
|
|
6484
|
+
storage = await resolveProvider(configService);
|
|
6485
|
+
}
|
|
6486
|
+
return new _MediaService(db, storage, options);
|
|
6487
|
+
}
|
|
6488
|
+
now() {
|
|
6489
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
6490
|
+
}
|
|
6491
|
+
buildFindConditions(params) {
|
|
6492
|
+
const conditions = [];
|
|
6493
|
+
const p = [];
|
|
6494
|
+
const sortCol = params.sortBy === "name" ? "title" : params.sortBy || "created_at";
|
|
6495
|
+
const sortDir = params.sortDir === "asc" ? "ASC" : "DESC";
|
|
6496
|
+
if (params.search) {
|
|
6497
|
+
conditions.push(
|
|
6498
|
+
`(title LIKE ? OR filename LIKE ? OR original_name LIKE ? OR alt LIKE ?)`
|
|
6499
|
+
);
|
|
6500
|
+
const s = `%${params.search}%`;
|
|
6501
|
+
p.push(s, s, s, s);
|
|
6502
|
+
}
|
|
6503
|
+
if (params.type && params.type !== "all") {
|
|
6504
|
+
conditions.push(`mime_type LIKE ?`);
|
|
6505
|
+
p.push(`${params.type}/%`);
|
|
6506
|
+
}
|
|
6507
|
+
if (params.folder) {
|
|
6508
|
+
conditions.push(`(folder = ? OR folder LIKE ?)`);
|
|
6509
|
+
p.push(params.folder, `${params.folder}/%`);
|
|
6510
|
+
}
|
|
6511
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
6512
|
+
return { where, params: p, orderBy: sortDir, sortCol };
|
|
6513
|
+
}
|
|
6514
|
+
rowToMedia(row) {
|
|
6515
|
+
return {
|
|
6516
|
+
id: row.id,
|
|
6517
|
+
filename: row.filename,
|
|
6518
|
+
title: row.title ?? null,
|
|
6519
|
+
originalName: row.original_name ?? row.originalName,
|
|
6520
|
+
mimeType: row.mime_type ?? row.mimeType,
|
|
6521
|
+
fileSize: row.file_size ?? row.fileSize,
|
|
6522
|
+
width: row.width ?? null,
|
|
6523
|
+
height: row.height ?? null,
|
|
6524
|
+
url: row.url,
|
|
6525
|
+
thumbnailUrl: row.thumbnail_url ?? row.thumbnailUrl ?? null,
|
|
6526
|
+
folder: row.folder ?? null,
|
|
6527
|
+
provider: row.provider,
|
|
6528
|
+
alt: row.alt ?? null,
|
|
6529
|
+
caption: row.caption ?? null,
|
|
6530
|
+
metadata: row.metadata ?? null,
|
|
6531
|
+
createdAt: row.created_at ?? row.createdAt,
|
|
6532
|
+
updatedAt: row.updated_at ?? row.updatedAt
|
|
6533
|
+
};
|
|
6534
|
+
}
|
|
6535
|
+
async sqliteRun(sql, params = []) {
|
|
6536
|
+
const stmt = this.db.prepare(sql);
|
|
6537
|
+
const sqlLower = sql.trim().toLowerCase();
|
|
6538
|
+
if (sqlLower.startsWith("insert") || sqlLower.startsWith("update") || sqlLower.startsWith("delete")) {
|
|
6539
|
+
return stmt.run(...params);
|
|
6540
|
+
}
|
|
6541
|
+
return stmt.all(...params);
|
|
6542
|
+
}
|
|
6543
|
+
sqliteGet(sql, params = []) {
|
|
6544
|
+
const stmt = this.db.prepare(sql);
|
|
6545
|
+
return stmt.get(...params);
|
|
6546
|
+
}
|
|
6547
|
+
async upload(file, folder = "") {
|
|
6548
|
+
const isImage = file.type.startsWith("image/");
|
|
6549
|
+
let processed;
|
|
6550
|
+
let uploadFile = file;
|
|
6551
|
+
let filename = file.name;
|
|
6552
|
+
let width = null;
|
|
6553
|
+
let height = null;
|
|
6554
|
+
if (isImage && !file.type.includes("svg")) {
|
|
6555
|
+
const buffer = Buffer.from(await file.arrayBuffer());
|
|
6556
|
+
processed = await processImage(buffer);
|
|
6557
|
+
const originalName = file.name.replace(/\.[^/.]+$/, "");
|
|
6558
|
+
const safeName = originalName.toLowerCase().replace(/[^a-z0-9]/g, "-").replace(/-+/g, "-").substring(0, 50);
|
|
6559
|
+
filename = `${safeName}.webp`;
|
|
6560
|
+
width = processed.width ?? null;
|
|
6561
|
+
height = processed.height ?? null;
|
|
6562
|
+
uploadFile = new File([processed.buffer], filename, {
|
|
6563
|
+
type: "image/webp"
|
|
6564
|
+
});
|
|
6565
|
+
}
|
|
6566
|
+
const storageResult = await this.storage.upload(uploadFile, {
|
|
6567
|
+
folder,
|
|
6568
|
+
filename
|
|
6569
|
+
});
|
|
6570
|
+
const thumbnailUrl = await this.storage.generateThumbnail(
|
|
6571
|
+
{
|
|
6572
|
+
...storageResult,
|
|
6573
|
+
id: "",
|
|
6574
|
+
provider: this.storage.name,
|
|
6575
|
+
createdAt: this.now()
|
|
6576
|
+
},
|
|
6577
|
+
{ width: 400, height: 400 }
|
|
6578
|
+
);
|
|
6579
|
+
const id = this.genId();
|
|
6580
|
+
const now = this.now();
|
|
6581
|
+
if (this.dialect === "sqlite") {
|
|
6582
|
+
await this.sqliteRun(
|
|
6583
|
+
`INSERT INTO ${this.mediaTable}
|
|
6584
|
+
(id, filename, title, original_name, mime_type, file_size, width, height, url, thumbnail_url, folder, provider, alt, caption, metadata, created_at, updated_at)
|
|
6585
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
6586
|
+
[
|
|
6587
|
+
id,
|
|
6588
|
+
storageResult.filename,
|
|
6589
|
+
file.name.replace(/\.[^/.]+$/, ""),
|
|
6590
|
+
file.name,
|
|
6591
|
+
storageResult.mimeType,
|
|
6592
|
+
storageResult.size,
|
|
6593
|
+
width,
|
|
6594
|
+
height,
|
|
6595
|
+
storageResult.url,
|
|
6596
|
+
thumbnailUrl,
|
|
6597
|
+
folder || null,
|
|
6598
|
+
this.storage.name,
|
|
6599
|
+
null,
|
|
6600
|
+
null,
|
|
6601
|
+
null,
|
|
6602
|
+
now,
|
|
6603
|
+
now
|
|
6604
|
+
]
|
|
6605
|
+
);
|
|
6606
|
+
} else {
|
|
6607
|
+
const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-CDZUS7YX.js') : import('./media-HOT3O7RW.js'));
|
|
6608
|
+
await this.db.insert(mediaSchema).values({
|
|
6609
|
+
id,
|
|
6610
|
+
filename: storageResult.filename,
|
|
6611
|
+
title: file.name.replace(/\.[^/.]+$/, ""),
|
|
6612
|
+
originalName: file.name,
|
|
6613
|
+
mimeType: storageResult.mimeType,
|
|
6614
|
+
fileSize: storageResult.size,
|
|
6615
|
+
width,
|
|
6616
|
+
height,
|
|
6617
|
+
url: storageResult.url,
|
|
6618
|
+
thumbnailUrl,
|
|
6619
|
+
folder: folder || "",
|
|
6620
|
+
provider: this.storage.name,
|
|
6621
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
6622
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
6623
|
+
}).returning();
|
|
6624
|
+
}
|
|
6625
|
+
return {
|
|
6626
|
+
id,
|
|
6627
|
+
filename: storageResult.filename,
|
|
6628
|
+
title: file.name.replace(/\.[^/.]+$/, ""),
|
|
6629
|
+
originalName: file.name,
|
|
6630
|
+
mimeType: storageResult.mimeType,
|
|
6631
|
+
fileSize: storageResult.size,
|
|
6632
|
+
width,
|
|
6633
|
+
height,
|
|
6634
|
+
url: storageResult.url,
|
|
6635
|
+
thumbnailUrl,
|
|
6636
|
+
folder: folder || null,
|
|
6637
|
+
provider: this.storage.name,
|
|
6638
|
+
alt: null,
|
|
6639
|
+
caption: null,
|
|
6640
|
+
metadata: null,
|
|
6641
|
+
createdAt: now,
|
|
6642
|
+
updatedAt: now
|
|
6643
|
+
};
|
|
6644
|
+
}
|
|
6645
|
+
async delete(id) {
|
|
6646
|
+
let item = null;
|
|
6647
|
+
if (this.dialect === "sqlite") {
|
|
6648
|
+
item = this.sqliteGet(`SELECT * FROM ${this.mediaTable} WHERE id = ?`, [
|
|
6649
|
+
id
|
|
6650
|
+
]);
|
|
6651
|
+
} else {
|
|
6652
|
+
const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-CDZUS7YX.js') : import('./media-HOT3O7RW.js'));
|
|
6653
|
+
const [row] = await this.db.select().from(mediaSchema).where(mediaSchema.id.equals(id));
|
|
6654
|
+
if (row) item = this.rowToMedia(row);
|
|
6655
|
+
}
|
|
6656
|
+
if (!item) return;
|
|
6657
|
+
await this.deleteFile(item.url);
|
|
6658
|
+
if (item.thumbnailUrl && item.thumbnailUrl !== item.url && item.thumbnailUrl !== item.url + "?thumb") {
|
|
6659
|
+
try {
|
|
6660
|
+
await this.deleteFile(item.thumbnailUrl);
|
|
6661
|
+
} catch {
|
|
6662
|
+
}
|
|
6663
|
+
}
|
|
6664
|
+
if (this.dialect === "sqlite") {
|
|
6665
|
+
await this.sqliteRun(`DELETE FROM ${this.mediaTable} WHERE id = ?`, [id]);
|
|
6666
|
+
} else {
|
|
6667
|
+
const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-CDZUS7YX.js') : import('./media-HOT3O7RW.js'));
|
|
6668
|
+
await this.db.delete(mediaSchema).where(mediaSchema.id.equals(id));
|
|
6669
|
+
}
|
|
6670
|
+
}
|
|
6671
|
+
async deleteFile(url) {
|
|
6672
|
+
await this.storage.delete(url);
|
|
6673
|
+
}
|
|
6674
|
+
async rename(id, newKey) {
|
|
6675
|
+
let item = null;
|
|
6676
|
+
if (this.dialect === "sqlite") {
|
|
6677
|
+
item = this.sqliteGet(`SELECT * FROM ${this.mediaTable} WHERE id = ?`, [
|
|
6678
|
+
id
|
|
6679
|
+
]);
|
|
6680
|
+
} else {
|
|
6681
|
+
const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-CDZUS7YX.js') : import('./media-HOT3O7RW.js'));
|
|
6682
|
+
const [row] = await this.db.select().from(mediaSchema).where(mediaSchema.id.equals(id));
|
|
6683
|
+
if (row) item = this.rowToMedia(row);
|
|
6684
|
+
}
|
|
6685
|
+
if (!item) return null;
|
|
6686
|
+
const newUrl = await this.storage.rename(item.url, newKey);
|
|
6687
|
+
let newThumbnailUrl;
|
|
6688
|
+
if (item.thumbnailUrl || this.storage.name === "cloudinary") {
|
|
6689
|
+
const versionMatch = newUrl.match(/\/upload\/(v\d+)\//);
|
|
6690
|
+
const version = versionMatch ? versionMatch[1] + "/" : "";
|
|
6691
|
+
const baseUrlMatch = newUrl.match(/(.+?)\/upload\//);
|
|
6692
|
+
const baseUrl = baseUrlMatch ? baseUrlMatch[1] : "https://res.cloudinary.com/" + this.storage.config?.cloudName || "unknown";
|
|
6693
|
+
newThumbnailUrl = `${baseUrl}/upload/w_200,h_200,c_fill/${version}${newKey}`;
|
|
6694
|
+
}
|
|
6695
|
+
const ext = item.filename.split(".").pop();
|
|
6696
|
+
const newFilename = newKey.includes(".") ? newKey : `${newKey}.${ext}`;
|
|
6697
|
+
const updateData = {
|
|
6698
|
+
url: newUrl,
|
|
6699
|
+
filename: newFilename,
|
|
6700
|
+
thumbnailUrl: newThumbnailUrl
|
|
6701
|
+
};
|
|
6702
|
+
if (this.dialect === "sqlite") {
|
|
6703
|
+
const sqliteUpdateData = { ...updateData };
|
|
6704
|
+
if ("thumbnailUrl" in sqliteUpdateData) {
|
|
6705
|
+
sqliteUpdateData.thumbnail_url = sqliteUpdateData.thumbnailUrl;
|
|
6706
|
+
delete sqliteUpdateData.thumbnailUrl;
|
|
6707
|
+
}
|
|
6708
|
+
const sets = Object.keys(sqliteUpdateData).map((k) => `${k} = ?`).join(", ");
|
|
6709
|
+
const vals = Object.values(sqliteUpdateData);
|
|
6710
|
+
await this.sqliteRun(
|
|
6711
|
+
`UPDATE ${this.mediaTable} SET ${sets}, updated_at = ? WHERE id = ?`,
|
|
6712
|
+
[...vals, this.now(), id]
|
|
6713
|
+
);
|
|
6714
|
+
} else {
|
|
6715
|
+
const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-CDZUS7YX.js') : import('./media-HOT3O7RW.js'));
|
|
6716
|
+
await this.db.update(mediaSchema).set({ ...updateData, updatedAt: this.now() }).where(mediaSchema.id.equals(id));
|
|
6717
|
+
}
|
|
6718
|
+
return {
|
|
6719
|
+
...item,
|
|
6720
|
+
...updateData,
|
|
6721
|
+
updatedAt: this.now(),
|
|
6722
|
+
thumbnailUrl: updateData.thumbnailUrl ?? null
|
|
6723
|
+
};
|
|
6724
|
+
}
|
|
6725
|
+
async find(params = {}) {
|
|
6726
|
+
const {
|
|
6727
|
+
page = 1,
|
|
6728
|
+
limit = 30,
|
|
6729
|
+
search = "",
|
|
6730
|
+
type = "",
|
|
6731
|
+
folder = "",
|
|
6732
|
+
sortBy = "createdAt",
|
|
6733
|
+
sortDir = "desc"
|
|
6734
|
+
} = params;
|
|
6735
|
+
const {
|
|
6736
|
+
where,
|
|
6737
|
+
params: p,
|
|
6738
|
+
orderBy,
|
|
6739
|
+
sortCol
|
|
6740
|
+
} = this.buildFindConditions({
|
|
6741
|
+
page,
|
|
6742
|
+
limit,
|
|
6743
|
+
search,
|
|
6744
|
+
type,
|
|
6745
|
+
folder,
|
|
6746
|
+
sortBy,
|
|
6747
|
+
sortDir
|
|
6748
|
+
});
|
|
6749
|
+
const offset = (page - 1) * limit;
|
|
6750
|
+
if (this.dialect === "sqlite") {
|
|
6751
|
+
const countRow = this.sqliteGet(
|
|
6752
|
+
`SELECT COUNT(*) as cnt FROM ${this.mediaTable} ${where}`,
|
|
6753
|
+
p
|
|
6754
|
+
);
|
|
6755
|
+
const totalDocs2 = countRow?.cnt ?? 0;
|
|
6756
|
+
const rows = await this.sqliteRun(
|
|
6757
|
+
`SELECT * FROM ${this.mediaTable} ${where} ORDER BY ${sortCol} ${orderBy} LIMIT ? OFFSET ?`,
|
|
6758
|
+
[...p, limit, offset]
|
|
6759
|
+
);
|
|
6760
|
+
return {
|
|
6761
|
+
docs: rows.map((r) => this.rowToMedia(r)),
|
|
6762
|
+
totalDocs: totalDocs2,
|
|
6763
|
+
page,
|
|
6764
|
+
limit,
|
|
6765
|
+
totalPages: Math.ceil(totalDocs2 / limit)
|
|
6766
|
+
};
|
|
6767
|
+
}
|
|
6768
|
+
const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-CDZUS7YX.js') : import('./media-HOT3O7RW.js'));
|
|
6769
|
+
const { like, or, and, asc, desc, eq, sql } = await (this.dialect === "mysql" ? import('drizzle-orm/mysql-core') : import('drizzle-orm/pg-core'));
|
|
6770
|
+
const conditions = [];
|
|
6771
|
+
if (search) {
|
|
6772
|
+
conditions.push(
|
|
6773
|
+
or(
|
|
6774
|
+
like(mediaSchema.title, `%${search}%`),
|
|
6775
|
+
like(mediaSchema.filename, `%${search}%`),
|
|
6776
|
+
like(mediaSchema.originalName, `%${search}%`),
|
|
6777
|
+
like(mediaSchema.alt, `%${search}%`)
|
|
6778
|
+
)
|
|
6779
|
+
);
|
|
6780
|
+
}
|
|
6781
|
+
if (type && type !== "all") {
|
|
6782
|
+
conditions.push(like(mediaSchema.mimeType, `${type}/%`));
|
|
6783
|
+
}
|
|
6784
|
+
if (folder) {
|
|
6785
|
+
conditions.push(
|
|
6786
|
+
or(
|
|
6787
|
+
eq(mediaSchema.folder, folder),
|
|
6788
|
+
like(mediaSchema.folder, `${folder}/%`)
|
|
6789
|
+
)
|
|
6790
|
+
);
|
|
6791
|
+
}
|
|
6792
|
+
const whereClause = conditions.length > 0 ? and(...conditions) : void 0;
|
|
6793
|
+
const order = sortDir === "asc" ? asc(mediaSchema[sortCol]) : desc(mediaSchema[sortCol]);
|
|
6794
|
+
const docs = await this.db.select().from(mediaSchema).where(whereClause).orderBy(order).limit(limit).offset(offset);
|
|
6795
|
+
const [{ count }] = await this.db.select({ count: sql`count(*)` }).from(mediaSchema).where(whereClause);
|
|
6796
|
+
const totalDocs = Number(count);
|
|
6797
|
+
return {
|
|
6798
|
+
docs: docs.map((r) => this.rowToMedia(r)),
|
|
6799
|
+
totalDocs,
|
|
6800
|
+
page,
|
|
6801
|
+
limit,
|
|
6802
|
+
totalPages: Math.ceil(totalDocs / limit)
|
|
6803
|
+
};
|
|
6804
|
+
}
|
|
6805
|
+
async update(id, data) {
|
|
6806
|
+
const now = this.now();
|
|
6807
|
+
if (this.dialect === "sqlite") {
|
|
6808
|
+
const sets = ["updated_at = ?"];
|
|
6809
|
+
const p = [now];
|
|
6810
|
+
if (data.title !== void 0) {
|
|
6811
|
+
sets.push("title = ?");
|
|
6812
|
+
p.push(data.title);
|
|
6813
|
+
}
|
|
6814
|
+
if (data.alt !== void 0) {
|
|
6815
|
+
sets.push("alt = ?");
|
|
6816
|
+
p.push(data.alt);
|
|
6817
|
+
}
|
|
6818
|
+
if (data.caption !== void 0) {
|
|
6819
|
+
sets.push("caption = ?");
|
|
6820
|
+
p.push(data.caption);
|
|
6821
|
+
}
|
|
6822
|
+
if (data.folder !== void 0) {
|
|
6823
|
+
sets.push("folder = ?");
|
|
6824
|
+
p.push(data.folder || null);
|
|
6825
|
+
}
|
|
6826
|
+
p.push(id);
|
|
6827
|
+
await this.sqliteRun(
|
|
6828
|
+
`UPDATE ${this.mediaTable} SET ${sets.join(", ")} WHERE id = ?`,
|
|
6829
|
+
p
|
|
6830
|
+
);
|
|
6831
|
+
const row = this.sqliteGet(
|
|
6832
|
+
`SELECT * FROM ${this.mediaTable} WHERE id = ?`,
|
|
6833
|
+
[id]
|
|
6834
|
+
);
|
|
6835
|
+
return row ? this.rowToMedia(row) : null;
|
|
6836
|
+
}
|
|
6837
|
+
const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-CDZUS7YX.js') : import('./media-HOT3O7RW.js'));
|
|
6838
|
+
const [updated] = await this.db.update(mediaSchema).set({ ...data, updatedAt: /* @__PURE__ */ new Date() }).where(mediaSchema.id.equals(id)).returning();
|
|
6839
|
+
return updated ? this.rowToMedia(updated) : null;
|
|
6840
|
+
}
|
|
6841
|
+
async updateMany(ids, data) {
|
|
6842
|
+
const now = this.now();
|
|
6843
|
+
if (this.dialect === "sqlite") {
|
|
6844
|
+
for (const id of ids) {
|
|
6845
|
+
const sets = ["updated_at = ?"];
|
|
6846
|
+
const p = [now];
|
|
6847
|
+
if (data.folder !== void 0) {
|
|
6848
|
+
sets.push("folder = ?");
|
|
6849
|
+
p.push(data.folder || null);
|
|
6850
|
+
}
|
|
6851
|
+
p.push(id);
|
|
6852
|
+
await this.sqliteRun(
|
|
6853
|
+
`UPDATE ${this.mediaTable} SET ${sets.join(", ")} WHERE id = ?`,
|
|
6854
|
+
p
|
|
6855
|
+
);
|
|
6856
|
+
}
|
|
6857
|
+
} else {
|
|
6858
|
+
const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-CDZUS7YX.js') : import('./media-HOT3O7RW.js'));
|
|
6859
|
+
for (const id of ids) {
|
|
6860
|
+
await this.db.update(mediaSchema).set({ ...data, updatedAt: /* @__PURE__ */ new Date() }).where(mediaSchema.id.equals(id));
|
|
6861
|
+
}
|
|
6862
|
+
}
|
|
6863
|
+
}
|
|
6864
|
+
async listFolders() {
|
|
6865
|
+
if (this.dialect === "sqlite") {
|
|
6866
|
+
const rows = await this.sqliteRun(
|
|
6867
|
+
`SELECT path FROM ${this.foldersTable} UNION
|
|
6868
|
+
SELECT folder as path FROM ${this.mediaTable} WHERE folder IS NOT NULL AND folder != ''`
|
|
6869
|
+
);
|
|
6870
|
+
return rows.map((r) => r.path).filter((f) => f && f !== "").sort();
|
|
6871
|
+
}
|
|
6872
|
+
const { media: mediaSchema, mediaFolders: folderSchema } = await (this.dialect === "mysql" ? import('./mysql-media-CDZUS7YX.js') : import('./media-HOT3O7RW.js'));
|
|
6873
|
+
const { eq, sql } = await (this.dialect === "mysql" ? import('drizzle-orm/mysql-core') : import('drizzle-orm/pg-core'));
|
|
6874
|
+
const fromMedia = await this.db.select({ folder: mediaSchema.folder }).from(mediaSchema).groupBy(mediaSchema.folder);
|
|
6875
|
+
const fromFolders = await this.db.select({ path: folderSchema.path }).from(folderSchema);
|
|
6876
|
+
const allPaths = /* @__PURE__ */ new Set([
|
|
6877
|
+
...fromMedia.map((r) => r.folder),
|
|
6878
|
+
...fromFolders.map((r) => r.path)
|
|
6879
|
+
]);
|
|
6880
|
+
return Array.from(allPaths).filter((f) => f && f !== "").sort();
|
|
6881
|
+
}
|
|
6882
|
+
async createFolder(name, parentPath = "") {
|
|
6883
|
+
const fullPath = parentPath ? `${parentPath}/${name}` : name;
|
|
6884
|
+
if (this.storage.name === "local") {
|
|
6885
|
+
const { mkdir: mkdir2 } = await import('fs/promises');
|
|
6886
|
+
const { join: join2 } = await import('path');
|
|
6887
|
+
await mkdir2(join2(process.cwd(), "public", "uploads", fullPath), {
|
|
6888
|
+
recursive: true
|
|
6889
|
+
});
|
|
6890
|
+
}
|
|
6891
|
+
const now = this.now();
|
|
6892
|
+
if (this.dialect === "sqlite") {
|
|
6893
|
+
await this.sqliteRun(
|
|
6894
|
+
`INSERT OR IGNORE INTO ${this.foldersTable} (path, name, parent_path, created_at) VALUES (?, ?, ?, ?)`,
|
|
6895
|
+
[fullPath, name, parentPath || null, now]
|
|
6896
|
+
);
|
|
6897
|
+
} else {
|
|
6898
|
+
const { mediaFolders: folderSchema } = await (this.dialect === "mysql" ? import('./mysql-media-CDZUS7YX.js') : import('./media-HOT3O7RW.js'));
|
|
6899
|
+
await this.db.insert(folderSchema).values({
|
|
6900
|
+
path: fullPath,
|
|
6901
|
+
name,
|
|
6902
|
+
parentPath: parentPath || null,
|
|
6903
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
6904
|
+
}).onConflictDoNothing();
|
|
6905
|
+
}
|
|
6906
|
+
}
|
|
6907
|
+
async deleteFolder(folder) {
|
|
6908
|
+
const result = await this.find({ folder, limit: 1e4 });
|
|
6909
|
+
for (const item of result.docs) {
|
|
6910
|
+
await this.delete(item.id);
|
|
6911
|
+
}
|
|
6912
|
+
if (this.dialect === "sqlite") {
|
|
6913
|
+
await this.sqliteRun(
|
|
6914
|
+
`DELETE FROM ${this.foldersTable} WHERE path = ? OR path LIKE ?`,
|
|
6915
|
+
[folder, `${folder}/%`]
|
|
6916
|
+
);
|
|
6917
|
+
} else {
|
|
6918
|
+
const { mediaFolders: folderSchema } = await (this.dialect === "mysql" ? import('./mysql-media-CDZUS7YX.js') : import('./media-HOT3O7RW.js'));
|
|
6919
|
+
const { like, or, eq } = await (this.dialect === "mysql" ? import('drizzle-orm/mysql-core') : import('drizzle-orm/pg-core'));
|
|
6920
|
+
await this.db.delete(folderSchema).where(
|
|
6921
|
+
or(
|
|
6922
|
+
eq(folderSchema.path, folder),
|
|
6923
|
+
like(folderSchema.path, `${folder}/%`)
|
|
6924
|
+
)
|
|
6925
|
+
);
|
|
6926
|
+
}
|
|
6927
|
+
if (this.storage.name === "local") {
|
|
6928
|
+
const { rm } = await import('fs/promises');
|
|
6929
|
+
const { join: join2 } = await import('path');
|
|
6930
|
+
try {
|
|
6931
|
+
await rm(join2(process.cwd(), "public", "uploads", folder), {
|
|
6932
|
+
recursive: true,
|
|
6933
|
+
force: true
|
|
6934
|
+
});
|
|
6935
|
+
} catch {
|
|
6936
|
+
}
|
|
6937
|
+
}
|
|
6938
|
+
}
|
|
6939
|
+
};
|
|
4039
6940
|
|
|
4040
6941
|
// src/registry/config.ts
|
|
4041
6942
|
function normalizeCollections(collections) {
|
|
@@ -4066,6 +6967,6 @@ function defineConfig(config) {
|
|
|
4066
6967
|
};
|
|
4067
6968
|
}
|
|
4068
6969
|
|
|
4069
|
-
export { ALL_FIELD_TYPES, AccountLockout, AnalyticsPlugin, AuditLogger, Auth, COMPLEX_FIELD_TYPES, CSSGenerator, CommentsPlugin, ConfigValidationError, InMemoryAccountLockout, InMemoryAuditLogger, InMemoryAuthAdapter,
|
|
6970
|
+
export { ALL_FIELD_TYPES, AccountLockout, AnalyticsPlugin, AuditLogger, Auth, COMPLEX_FIELD_TYPES, CSSGenerator, CommentsPlugin, ConfigValidationError, InMemoryAccountLockout, InMemoryAuditLogger, InMemoryAuthAdapter, Kyro, KyroPlugin, LAYOUT_FIELD_TYPES, LocalAdapter, MediaService, PRIMITIVE_FIELD_TYPES, PluginManager, RELATIONAL_FIELD_TYPES, RateLimiter, Registry, ReviewsPlugin, SEOPLugin, VersionManager, WishlistPlugin, authConfig, collectionToCreateZod, collectionToUpdateZod, collectionToWhereZod, collectionToZod, createAdminStyling, createAuditContext, createAuth, createAuthConfig, createColumnsNode, createKyro, createLocalAdapter, createLocalStorage, createRegistry, createVersionManager, defaultDarkTheme, defaultFieldStyling, defaultLightTheme, defineConfig, ecommerce2026Theme, fieldToZod, generateCSSVariables, generateTailwindConfig, getDefaultDraftPublishConfig, getRegistry, globalToZod, isArchived, isArrayField, isBlocksField, isDraft, isGroupField, isLayoutField, isNumberField, isPublished, isRelationshipField, isRichTextField, isSelectField, isTextField, isUploadField, normalizeRichTextDocument, normalizeRichTextValue, presetPlugins, renderRichText, resetRegistry, resolveProvider, richTextStyles, runFieldHooks, runHooks, validateCollection, validateConfig, validateFields, validateGlobal };
|
|
4070
6971
|
//# sourceMappingURL=index.js.map
|
|
4071
6972
|
//# sourceMappingURL=index.js.map
|