karin-plugin-qgroup-file2openlist 0.0.1

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.
@@ -0,0 +1,756 @@
1
+ import {
2
+ dir
3
+ } from "./chunk-IZS467MR.js";
4
+
5
+ // src/web.config.ts
6
+ import fs from "fs";
7
+ import path from "path";
8
+ import { components, defineConfig, logger } from "node-karin";
9
+ var MAX_FILE_TIMEOUT_SEC = 3e3;
10
+ var readJsonSafe = (filePath) => {
11
+ try {
12
+ if (!fs.existsSync(filePath)) return {};
13
+ const raw = fs.readFileSync(filePath, "utf8");
14
+ return raw ? JSON.parse(raw) : {};
15
+ } catch (error) {
16
+ logger.error(error);
17
+ return {};
18
+ }
19
+ };
20
+ var writeJsonSafe = (filePath, data) => {
21
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
22
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf8");
23
+ };
24
+ var getConfigFilePath = () => path.join(dir.ConfigDir, "config.json");
25
+ var getDefaultConfigPath = () => path.join(dir.defConfigDir, "config.json");
26
+ var getMergedConfig = () => {
27
+ const cfg = readJsonSafe(getConfigFilePath());
28
+ const def = readJsonSafe(getDefaultConfigPath());
29
+ return { ...def, ...cfg };
30
+ };
31
+ var asString = (value) => typeof value === "string" ? value : value == null ? "" : String(value);
32
+ var asBoolean = (value) => {
33
+ if (typeof value === "boolean") return value;
34
+ if (typeof value === "number") return value !== 0;
35
+ if (typeof value === "string") {
36
+ const v = value.trim().toLowerCase();
37
+ if (!v) return false;
38
+ if (["1", "true", "yes", "y", "on", "\u5F00\u542F", "\u5F00", "\u662F"].includes(v)) return true;
39
+ if (["0", "false", "no", "n", "off", "\u5173\u95ED", "\u5173", "\u5426"].includes(v)) return false;
40
+ }
41
+ return false;
42
+ };
43
+ var asInt = (value, fallback) => {
44
+ if (typeof value === "number" && Number.isFinite(value)) return Math.floor(value);
45
+ if (typeof value === "string") {
46
+ const trimmed = value.trim();
47
+ if (!trimmed) return fallback;
48
+ const n = Number(trimmed);
49
+ if (Number.isFinite(n)) return Math.floor(n);
50
+ }
51
+ return fallback;
52
+ };
53
+ var asOptionalInt = (value) => {
54
+ if (typeof value === "number" && Number.isFinite(value)) return Math.floor(value);
55
+ if (typeof value === "string") {
56
+ const trimmed = value.trim();
57
+ if (!trimmed) return void 0;
58
+ const n = Number(trimmed);
59
+ if (Number.isFinite(n)) return Math.floor(n);
60
+ }
61
+ return void 0;
62
+ };
63
+ var unwrapAccordionValue = (value) => {
64
+ if (!value || typeof value !== "object") return value;
65
+ const v = value;
66
+ return Object.prototype.hasOwnProperty.call(v, "value") ? v.value : value;
67
+ };
68
+ var pickMode = (value, fallback) => {
69
+ const v = asString(value).trim().toLowerCase();
70
+ if (v === "full" || v === "\u5168\u91CF") return "full";
71
+ if (v === "incremental" || v === "\u589E\u91CF") return "incremental";
72
+ return fallback;
73
+ };
74
+ var clampInt = (value, min, max) => Math.min(max, Math.max(min, value));
75
+ var arrayFrom = (value) => {
76
+ if (Array.isArray(value)) return value;
77
+ if (value == null) return [];
78
+ return [value];
79
+ };
80
+ var getFormRowValue = (form, key, index) => {
81
+ const raw = form?.[key];
82
+ if (Array.isArray(raw)) return raw[index];
83
+ return index === 0 ? raw : void 0;
84
+ };
85
+ var extractAccordionRows = (value) => {
86
+ if (Array.isArray(value)) return value;
87
+ if (value && typeof value === "object") {
88
+ const obj = value;
89
+ const candidates = [obj.rows, obj.data, obj.value, obj.items];
90
+ for (const it of candidates) {
91
+ if (Array.isArray(it)) return it;
92
+ }
93
+ }
94
+ return [];
95
+ };
96
+ var web_config_default = defineConfig({
97
+ info: {
98
+ id: dir.name,
99
+ name: "\u7FA4\u6587\u4EF6\u5DE5\u5177",
100
+ version: dir.version,
101
+ description: dir.pkg?.description ?? ""
102
+ },
103
+ components: () => {
104
+ const cfg = getMergedConfig();
105
+ const defaults = cfg.groupSyncDefaults ?? {};
106
+ const targets = Array.isArray(cfg.groupSyncTargets) ? cfg.groupSyncTargets : [];
107
+ const targetsData = targets.map((t) => {
108
+ const groupId = asString(t?.groupId ?? "").trim();
109
+ const title = groupId ? `\u7FA4 ${groupId}` : "\u76EE\u6807\u7FA4";
110
+ return {
111
+ title,
112
+ subtitle: "\u200B",
113
+ gst_groupId: groupId,
114
+ gst_enabled: Boolean(t?.enabled ?? true),
115
+ gst_sourceFolderId: asString(t?.sourceFolderId ?? ""),
116
+ gst_targetDir: asString(t?.targetDir ?? ""),
117
+ gst_mode: pickMode(t?.mode, pickMode(defaults?.mode, "incremental")),
118
+ gst_flat: typeof t?.flat === "boolean" ? t.flat : Boolean(defaults?.flat ?? false),
119
+ gst_maxFiles: asString(t?.maxFiles ?? ""),
120
+ gst_urlConcurrency: asString(t?.urlConcurrency ?? ""),
121
+ gst_transferConcurrency: asString(t?.transferConcurrency ?? ""),
122
+ gst_fileTimeoutSec: asString(t?.fileTimeoutSec ?? ""),
123
+ gst_retryTimes: asString(t?.retryTimes ?? ""),
124
+ gst_retryDelayMs: asString(t?.retryDelayMs ?? ""),
125
+ gst_progressReportEvery: asString(t?.progressReportEvery ?? ""),
126
+ gst_downloadLimitKbps: asString(t?.downloadLimitKbps ?? ""),
127
+ gst_uploadLimitKbps: asString(t?.uploadLimitKbps ?? ""),
128
+ gst_timeWindows: asString(t?.timeWindows ?? ""),
129
+ gst_scheduleEnabled: Boolean(t?.schedule?.enabled ?? false),
130
+ gst_scheduleCron: asString(t?.schedule?.cron ?? "")
131
+ };
132
+ });
133
+ const compactInput = {
134
+ size: "sm",
135
+ variant: "bordered",
136
+ radius: "sm",
137
+ color: "default",
138
+ labelPlacement: "outside",
139
+ fullWidth: true,
140
+ isRequired: false,
141
+ disableAnimation: true,
142
+ className: "w-full",
143
+ componentClassName: "w-full"
144
+ };
145
+ const compactNumber = {
146
+ ...compactInput,
147
+ rules: []
148
+ };
149
+ const compactSelect = {
150
+ size: "sm",
151
+ variant: "bordered",
152
+ radius: "sm",
153
+ color: "default",
154
+ labelPlacement: "outside",
155
+ isRequired: false,
156
+ disableAnimation: true,
157
+ className: "!max-w-none w-full !p-0",
158
+ componentClassName: "w-full"
159
+ };
160
+ const compactSwitch = {
161
+ size: "sm",
162
+ color: "default",
163
+ disableAnimation: true,
164
+ className: "w-full"
165
+ };
166
+ const grid2 = "grid grid-cols-1 sm:grid-cols-2 gap-2 p-1";
167
+ return [
168
+ components.accordion.create("groupFilesTool", {
169
+ title: "\u7FA4\u6587\u4EF6\u5DE5\u5177\u914D\u7F6E",
170
+ variant: "bordered",
171
+ selectionMode: "multiple",
172
+ isCompact: true,
173
+ showDivider: true,
174
+ fullWidth: true,
175
+ children: [
176
+ components.accordionItem.create("openlist", {
177
+ title: "OpenList",
178
+ subtitle: "\u200B",
179
+ isCompact: true,
180
+ className: grid2,
181
+ children: [
182
+ components.input.string("openlistBaseUrl", {
183
+ ...compactInput,
184
+ className: `sm:col-span-2 ${compactInput.className}`,
185
+ label: "OpenList \u5730\u5740",
186
+ description: "\u4F8B\u5982\uFF1Ahttp://127.0.0.1:5244",
187
+ defaultValue: cfg.openlistBaseUrl ?? "http://127.0.0.1:5244",
188
+ placeholder: "http://127.0.0.1:5244",
189
+ isClearable: true
190
+ }),
191
+ components.input.string("openlistUsername", {
192
+ ...compactInput,
193
+ label: "OpenList \u7528\u6237\u540D",
194
+ description: "\u7528\u4E8E WebDAV BasicAuth \u767B\u5F55",
195
+ defaultValue: cfg.openlistUsername ?? "",
196
+ isClearable: true
197
+ }),
198
+ components.input.create("openlistPassword", {
199
+ ...compactInput,
200
+ label: "OpenList \u5BC6\u7801",
201
+ description: "\u7528\u4E8E WebDAV BasicAuth \u767B\u5F55\uFF08\u4F1A\u4FDD\u5B58\u5230\u914D\u7F6E\u6587\u4EF6\uFF09",
202
+ defaultValue: cfg.openlistPassword ?? "",
203
+ type: "password",
204
+ isClearable: true
205
+ }),
206
+ components.input.string("openlistTargetDir", {
207
+ ...compactInput,
208
+ className: `sm:col-span-2 ${compactInput.className}`,
209
+ label: "OpenList \u9ED8\u8BA4\u76EE\u6807\u76EE\u5F55",
210
+ description: "WebDAV \u76EE\u6807\u76EE\u5F55\uFF0C\u4F8B\u5982\uFF1A/\u6302\u8F7D\u76EE\u5F55/QQ\u7FA4\u6587\u4EF6",
211
+ defaultValue: cfg.openlistTargetDir ?? "/",
212
+ placeholder: "/\u6302\u8F7D\u76EE\u5F55/QQ\u7FA4\u6587\u4EF6",
213
+ isClearable: true
214
+ })
215
+ ]
216
+ }),
217
+ components.accordionItem.create("defaults", {
218
+ title: "\u7FA4\u540C\u6B65\u9ED8\u8BA4\u7B56\u7565",
219
+ subtitle: "\u200B",
220
+ isCompact: true,
221
+ className: grid2,
222
+ children: [
223
+ components.select.create("groupSyncDefaults_mode", {
224
+ ...compactSelect,
225
+ label: "\u9ED8\u8BA4\u540C\u6B65\u6A21\u5F0F",
226
+ description: "\u5168\u91CF=\u6BCF\u6B21\u90FD\u540C\u6B65\uFF1B\u589E\u91CF=\u8DF3\u8FC7\u5DF2\u540C\u6B65\u6587\u4EF6\uFF08\u4F9D\u8D56\u672C\u5730\u72B6\u6001\uFF09",
227
+ defaultValue: pickMode(defaults?.mode, "incremental"),
228
+ items: [
229
+ components.select.createItem("incremental", { value: "incremental", label: "\u589E\u91CF (incremental)" }),
230
+ components.select.createItem("full", { value: "full", label: "\u5168\u91CF (full)" })
231
+ ]
232
+ }),
233
+ components.switch.options("groupSyncDefaults_flat", {
234
+ ...compactSwitch,
235
+ label: "\u9ED8\u8BA4\u5E73\u94FA\u4E0A\u4F20",
236
+ startText: "\u4FDD\u7559\u76EE\u5F55\u7ED3\u6784",
237
+ endText: "\u5E73\u94FA\u4E0A\u4F20",
238
+ defaultSelected: Boolean(defaults?.flat ?? false)
239
+ }),
240
+ components.input.number("groupSyncDefaults_urlConcurrency", {
241
+ ...compactNumber,
242
+ label: "\u9ED8\u8BA4\u89E3\u6790URL\u5E76\u53D1",
243
+ defaultValue: asString(defaults?.urlConcurrency ?? 3),
244
+ isClearable: true,
245
+ rules: [{ min: 1, max: 5, error: "\u5E76\u53D1\u8303\u56F4 1-5" }]
246
+ }),
247
+ components.input.number("groupSyncDefaults_transferConcurrency", {
248
+ ...compactNumber,
249
+ label: "\u9ED8\u8BA4\u4E0B\u8F7D/\u4E0A\u4F20\u5E76\u53D1\uFF08\u591A\u7EBF\u7A0B\uFF09",
250
+ defaultValue: asString(defaults?.transferConcurrency ?? 3),
251
+ isClearable: true,
252
+ rules: [{ min: 1, max: 5, error: "\u5E76\u53D1\u8303\u56F4 1-5" }]
253
+ }),
254
+ components.input.number("groupSyncDefaults_fileTimeoutSec", {
255
+ ...compactNumber,
256
+ className: `sm:col-span-2 ${compactInput.className}`,
257
+ label: "\u9ED8\u8BA4\u5355\u6587\u4EF6\u8D85\u65F6\uFF08\u79D2\uFF09",
258
+ description: `\u4EC5\u5F71\u54CD\u5355\u4E2A\u6587\u4EF6\u7684\u4E0B\u8F7D+\u4E0A\u4F20\u603B\u8D85\u65F6\uFF08\u6700\u5927 ${MAX_FILE_TIMEOUT_SEC}s\uFF09`,
259
+ defaultValue: asString(defaults?.fileTimeoutSec ?? 600),
260
+ isClearable: true,
261
+ rules: [{ min: 10, max: MAX_FILE_TIMEOUT_SEC, error: `\u8303\u56F4 10-${MAX_FILE_TIMEOUT_SEC}` }]
262
+ }),
263
+ components.input.number("groupSyncDefaults_retryTimes", {
264
+ ...compactNumber,
265
+ label: "\u9ED8\u8BA4\u5931\u8D25\u91CD\u8BD5\u6B21\u6570",
266
+ defaultValue: asString(defaults?.retryTimes ?? 2),
267
+ isClearable: true,
268
+ rules: [{ min: 0, max: 1e3, error: "\u8303\u56F4 0-1000" }]
269
+ }),
270
+ components.input.number("groupSyncDefaults_retryDelayMs", {
271
+ ...compactNumber,
272
+ className: `sm:col-span-2 ${compactInput.className}`,
273
+ label: "\u9ED8\u8BA4\u91CD\u8BD5\u5EF6\u8FDF\uFF08ms\uFF09",
274
+ description: "\u6307\u6570\u9000\u907F\u7684\u57FA\u7840\u5EF6\u8FDF",
275
+ defaultValue: asString(defaults?.retryDelayMs ?? 1500),
276
+ isClearable: true,
277
+ rules: [{ min: 0, max: 36e5, error: "\u8303\u56F4 0-3600000" }]
278
+ }),
279
+ components.input.number("groupSyncDefaults_progressReportEvery", {
280
+ ...compactNumber,
281
+ className: `sm:col-span-2 ${compactInput.className}`,
282
+ label: "\u9ED8\u8BA4\u8FDB\u5EA6\u6D88\u606F\u95F4\u9694\uFF08\u6BCFN\u4E2A\u6587\u4EF6\uFF09",
283
+ description: "0=\u5173\u95ED\u8FDB\u5EA6\u6D88\u606F\uFF08\u4EC5\u5F71\u54CD\u804A\u5929\u56DE\u6267\uFF0C\u4E0D\u5F71\u54CD\u65E5\u5FD7\uFF09",
284
+ defaultValue: asString(defaults?.progressReportEvery ?? 10),
285
+ isClearable: true,
286
+ rules: [{ min: 0, max: 1e5, error: "\u8303\u56F4 0-100000" }]
287
+ }),
288
+ components.input.number("groupSyncDefaults_downloadLimitKbps", {
289
+ ...compactNumber,
290
+ className: `sm:col-span-2 ${compactInput.className}`,
291
+ label: "\u9ED8\u8BA4\u4E0B\u8F7D\u9650\u901F\uFF08KB/s\uFF09",
292
+ description: "0=\u4E0D\u9650\u5236\uFF1B\u4E0D\u914D\u7F6E\u9650\u901F\u65F6\u4F1A\u81EA\u52A8\u8C03\u6574\u5E76\u53D1\uFF08\u6700\u59275\uFF09\u4EE5\u5C3D\u91CF\u8DD1\u6EE1\u5E26\u5BBD",
293
+ defaultValue: asString(defaults?.downloadLimitKbps ?? 0),
294
+ isClearable: true,
295
+ rules: [{ min: 0, max: 1e7, error: "\u8303\u56F4 0-10000000" }]
296
+ }),
297
+ components.input.number("groupSyncDefaults_uploadLimitKbps", {
298
+ ...compactNumber,
299
+ className: `sm:col-span-2 ${compactInput.className}`,
300
+ label: "\u9ED8\u8BA4\u4E0A\u4F20\u9650\u901F\uFF08KB/s\uFF09",
301
+ description: "0=\u4E0D\u9650\u5236\uFF1B\u4E0D\u914D\u7F6E\u9650\u901F\u65F6\u4F1A\u81EA\u52A8\u8C03\u6574\u5E76\u53D1\uFF08\u6700\u59275\uFF09\u4EE5\u5C3D\u91CF\u8DD1\u6EE1\u5E26\u5BBD",
302
+ defaultValue: asString(defaults?.uploadLimitKbps ?? 0),
303
+ isClearable: true,
304
+ rules: [{ min: 0, max: 1e7, error: "\u8303\u56F4 0-10000000" }]
305
+ })
306
+ ]
307
+ }),
308
+ ...false ? [components.accordionItem.create("targets", {
309
+ title: "\u540C\u6B65\u5BF9\u8C61\u7FA4\u914D\u7F6E",
310
+ subtitle: "\u200B",
311
+ isCompact: true,
312
+ children: [
313
+ components.accordionPro.create("groupSyncTargets", targetsData, {
314
+ title: "\u540C\u6B65\u5BF9\u8C61\u7FA4",
315
+ variant: "bordered",
316
+ selectionMode: "single",
317
+ isCompact: true,
318
+ showDivider: true,
319
+ fullWidth: true,
320
+ children: {
321
+ title: "\u76EE\u6807\u7FA4",
322
+ subtitle: "\u200B",
323
+ isCompact: true,
324
+ className: grid2,
325
+ children: [
326
+ components.input.string("gst_groupId", {
327
+ ...compactInput,
328
+ className: `sm:col-span-2 ${compactInput.className}`,
329
+ label: "\u7FA4\u53F7",
330
+ placeholder: "123456",
331
+ isClearable: true
332
+ }),
333
+ components.switch.options("gst_enabled", {
334
+ ...compactSwitch,
335
+ label: "\u542F\u7528\u81EA\u52A8\u540C\u6B65",
336
+ defaultSelected: true
337
+ }),
338
+ components.select.create("gst_mode", {
339
+ ...compactSelect,
340
+ label: "\u540C\u6B65\u6A21\u5F0F",
341
+ defaultValue: pickMode(defaults?.mode, "incremental"),
342
+ items: [
343
+ components.select.createItem("incremental", { value: "incremental", label: "\u589E\u91CF" }),
344
+ components.select.createItem("full", { value: "full", label: "\u5168\u91CF" })
345
+ ]
346
+ }),
347
+ components.input.string("gst_targetDir", {
348
+ ...compactInput,
349
+ className: `sm:col-span-2 ${compactInput.className}`,
350
+ label: "\u76EE\u6807\u76EE\u5F55\uFF08\u53EF\u9009\uFF09",
351
+ description: "\u4E3A\u7A7A\u5219\u4F7F\u7528 openlistTargetDir/<\u7FA4\u53F7>",
352
+ placeholder: "/\u6302\u8F7D\u76EE\u5F55/QQ\u7FA4\u6587\u4EF6/123456",
353
+ isClearable: true
354
+ }),
355
+ components.input.string("gst_sourceFolderId", {
356
+ ...compactInput,
357
+ className: `sm:col-span-2 ${compactInput.className}`,
358
+ label: "\u8D77\u59CB\u6587\u4EF6\u5939ID\uFF08\u53EF\u9009\uFF09",
359
+ description: "\u4ECE\u7FA4\u6587\u4EF6\u7684\u6307\u5B9A\u6587\u4EF6\u5939\u5F00\u59CB\u5BFC\u51FA/\u540C\u6B65",
360
+ isClearable: true
361
+ }),
362
+ components.switch.options("gst_flat", {
363
+ ...compactSwitch,
364
+ label: "\u5E73\u94FA\u4E0A\u4F20",
365
+ startText: "\u4FDD\u7559\u7ED3\u6784",
366
+ endText: "\u5E73\u94FA",
367
+ defaultSelected: Boolean(defaults?.flat ?? false)
368
+ }),
369
+ components.input.number("gst_maxFiles", {
370
+ ...compactNumber,
371
+ label: "\u6700\u591A\u540C\u6B65\u6587\u4EF6\u6570\uFF080=\u4E0D\u9650\u5236\uFF09",
372
+ defaultValue: "",
373
+ isClearable: true,
374
+ rules: [{ min: 0, max: 1e6, error: "\u8303\u56F4 0-1000000" }]
375
+ }),
376
+ components.input.number("gst_urlConcurrency", {
377
+ ...compactNumber,
378
+ label: "\u89E3\u6790URL\u5E76\u53D1\uFF08\u53EF\u9009\uFF09",
379
+ defaultValue: "",
380
+ isClearable: true,
381
+ rules: [{ min: 0, max: 5, error: "\u8303\u56F4 0-5" }]
382
+ }),
383
+ components.input.number("gst_transferConcurrency", {
384
+ ...compactNumber,
385
+ label: "\u4E0B\u8F7D/\u4E0A\u4F20\u5E76\u53D1\uFF08\u53EF\u9009\uFF09",
386
+ defaultValue: "",
387
+ isClearable: true,
388
+ rules: [{ min: 0, max: 5, error: "\u8303\u56F4 0-5" }]
389
+ }),
390
+ components.input.number("gst_fileTimeoutSec", {
391
+ ...compactNumber,
392
+ label: "\u5355\u6587\u4EF6\u8D85\u65F6\uFF08\u79D2\uFF0C\u53EF\u9009\uFF09",
393
+ description: `\u6700\u5927 ${MAX_FILE_TIMEOUT_SEC}s`,
394
+ defaultValue: "",
395
+ isClearable: true,
396
+ rules: [{ min: 0, max: MAX_FILE_TIMEOUT_SEC, error: `\u8303\u56F4 0-${MAX_FILE_TIMEOUT_SEC}` }]
397
+ }),
398
+ components.input.number("gst_retryTimes", {
399
+ ...compactNumber,
400
+ label: "\u91CD\u8BD5\u6B21\u6570\uFF08\u53EF\u9009\uFF09",
401
+ defaultValue: "",
402
+ isClearable: true,
403
+ rules: [{ min: 0, max: 1e3, error: "\u8303\u56F4 0-1000" }]
404
+ }),
405
+ components.input.number("gst_retryDelayMs", {
406
+ ...compactNumber,
407
+ label: "\u91CD\u8BD5\u5EF6\u8FDF\uFF08ms\uFF0C\u53EF\u9009\uFF09",
408
+ defaultValue: "",
409
+ isClearable: true,
410
+ rules: [{ min: 0, max: 36e5, error: "\u8303\u56F4 0-3600000" }]
411
+ }),
412
+ components.input.number("gst_progressReportEvery", {
413
+ ...compactNumber,
414
+ label: "\u8FDB\u5EA6\u6D88\u606F\u95F4\u9694\uFF08\u6BCFN\u4E2A\u6587\u4EF6\uFF0C\u53EF\u9009\uFF09",
415
+ description: "0=\u5173\u95ED\uFF08\u4EC5\u5F71\u54CD\u804A\u5929\u56DE\u6267\uFF0C\u4E0D\u5F71\u54CD\u65E5\u5FD7\uFF09",
416
+ defaultValue: "",
417
+ isClearable: true,
418
+ rules: [{ min: 0, max: 1e5, error: "\u8303\u56F4 0-100000" }]
419
+ }),
420
+ components.input.number("gst_downloadLimitKbps", {
421
+ ...compactNumber,
422
+ label: "\u4E0B\u8F7D\u9650\u901F\uFF08KB/s\uFF0C\u53EF\u9009\uFF09",
423
+ description: "0=\u4E0D\u9650\u5236\uFF1B\u4E0D\u914D\u7F6E\u9650\u901F\u65F6\u4F1A\u81EA\u52A8\u8C03\u6574\u5E76\u53D1\uFF08\u6700\u59275\uFF09\u4EE5\u5C3D\u91CF\u8DD1\u6EE1\u5E26\u5BBD",
424
+ defaultValue: "",
425
+ isClearable: true,
426
+ rules: [{ min: 0, max: 1e7, error: "\u8303\u56F4 0-10000000" }]
427
+ }),
428
+ components.input.number("gst_uploadLimitKbps", {
429
+ ...compactNumber,
430
+ label: "\u4E0A\u4F20\u9650\u901F\uFF08KB/s\uFF0C\u53EF\u9009\uFF09",
431
+ description: "0=\u4E0D\u9650\u5236\uFF1B\u4E0D\u914D\u7F6E\u9650\u901F\u65F6\u4F1A\u81EA\u52A8\u8C03\u6574\u5E76\u53D1\uFF08\u6700\u59275\uFF09\u4EE5\u5C3D\u91CF\u8DD1\u6EE1\u5E26\u5BBD",
432
+ defaultValue: "",
433
+ isClearable: true,
434
+ rules: [{ min: 0, max: 1e7, error: "\u8303\u56F4 0-10000000" }]
435
+ }),
436
+ components.input.string("gst_timeWindows", {
437
+ ...compactInput,
438
+ className: `sm:col-span-2 ${compactInput.className}`,
439
+ label: "\u540C\u6B65\u65F6\u6BB5\uFF08\u5B9A\u65F6\u4EFB\u52A1\uFF09",
440
+ description: "\u4F8B\uFF1A00:00-06:00,23:00-23:59\uFF1B\u7A7A=\u4E0D\u9650\u5236",
441
+ placeholder: "00:00-06:00,23:00-23:59",
442
+ isClearable: true
443
+ }),
444
+ components.switch.options("gst_scheduleEnabled", {
445
+ ...compactSwitch,
446
+ label: "\u542F\u7528\u5B9A\u65F6\u540C\u6B65",
447
+ defaultSelected: false
448
+ }),
449
+ components.input.string("gst_scheduleCron", {
450
+ ...compactInput,
451
+ className: `sm:col-span-2 ${compactInput.className}`,
452
+ label: "\u5B9A\u65F6 Cron",
453
+ description: "6\u6BB5/5\u6BB5\u5747\u53EF\uFF0C\u4F8B\u5982\uFF1A0 */10 * * * *\uFF08\u6BCF10\u5206\u949F\uFF09",
454
+ placeholder: "0 */10 * * * *",
455
+ isClearable: true
456
+ })
457
+ ]
458
+ }
459
+ })
460
+ ]
461
+ })] : []
462
+ ]
463
+ }),
464
+ components.accordionPro.create("groupSyncTargets", targetsData, {
465
+ className: "mt-2",
466
+ title: "\u540C\u6B65\u5BF9\u8C61\u7FA4\u914D\u7F6E",
467
+ variant: "bordered",
468
+ selectionMode: "single",
469
+ isCompact: true,
470
+ showDivider: true,
471
+ fullWidth: true,
472
+ children: {
473
+ title: "\u76EE\u6807\u7FA4",
474
+ subtitle: "\u200B",
475
+ isCompact: true,
476
+ className: grid2,
477
+ children: [
478
+ components.input.string("gst_groupId", {
479
+ ...compactInput,
480
+ className: `sm:col-span-2 ${compactInput.className}`,
481
+ label: "\u7FA4\u53F7",
482
+ placeholder: "123456",
483
+ isClearable: true
484
+ }),
485
+ components.switch.options("gst_enabled", {
486
+ ...compactSwitch,
487
+ label: "\u542F\u7528\u81EA\u52A8\u540C\u6B65",
488
+ defaultSelected: true
489
+ }),
490
+ components.select.create("gst_mode", {
491
+ ...compactSelect,
492
+ label: "\u540C\u6B65\u6A21\u5F0F",
493
+ defaultValue: pickMode(defaults?.mode, "incremental"),
494
+ items: [
495
+ components.select.createItem("incremental", { value: "incremental", label: "\u589E\u91CF" }),
496
+ components.select.createItem("full", { value: "full", label: "\u5168\u91CF" })
497
+ ]
498
+ }),
499
+ components.input.string("gst_targetDir", {
500
+ ...compactInput,
501
+ className: `sm:col-span-2 ${compactInput.className}`,
502
+ label: "\u76EE\u6807\u76EE\u5F55\uFF08\u53EF\u9009\uFF09",
503
+ description: "\u4E3A\u7A7A\u5219\u4F7F\u7528 openlistTargetDir/<\u7FA4\u53F7>",
504
+ placeholder: "/\u6302\u8F7D\u76EE\u5F55/QQ\u7FA4\u6587\u4EF6/123456",
505
+ isClearable: true
506
+ }),
507
+ components.input.string("gst_sourceFolderId", {
508
+ ...compactInput,
509
+ className: `sm:col-span-2 ${compactInput.className}`,
510
+ label: "\u8D77\u59CB\u6587\u4EF6\u5939ID\uFF08\u53EF\u9009\uFF09",
511
+ description: "\u4ECE\u7FA4\u6587\u4EF6\u7684\u6307\u5B9A\u6587\u4EF6\u5939\u5F00\u59CB\u9012\u5F52\u540C\u6B65",
512
+ isClearable: true
513
+ }),
514
+ components.switch.options("gst_flat", {
515
+ ...compactSwitch,
516
+ label: "\u5E73\u94FA\u4E0A\u4F20",
517
+ startText: "\u4FDD\u7559\u7ED3\u6784",
518
+ endText: "\u5E73\u94FA",
519
+ defaultSelected: Boolean(defaults?.flat ?? false)
520
+ }),
521
+ components.input.number("gst_maxFiles", {
522
+ ...compactNumber,
523
+ label: "\u6700\u591A\u540C\u6B65\u6587\u4EF6\u6570\uFF080=\u4E0D\u9650\uFF09",
524
+ defaultValue: "",
525
+ isClearable: true,
526
+ rules: [{ min: 0, max: 1e6, error: "\u8303\u56F4 0-1000000" }]
527
+ }),
528
+ components.input.number("gst_urlConcurrency", {
529
+ ...compactNumber,
530
+ label: "\u89E3\u6790URL\u5E76\u53D1\uFF08\u53EF\u9009\uFF09",
531
+ defaultValue: "",
532
+ isClearable: true,
533
+ rules: [{ min: 0, max: 5, error: "\u8303\u56F4 0-5" }]
534
+ }),
535
+ components.input.number("gst_transferConcurrency", {
536
+ ...compactNumber,
537
+ label: "\u4E0B\u8F7D/\u4E0A\u4F20\u5E76\u53D1\uFF08\u53EF\u9009\uFF09",
538
+ defaultValue: "",
539
+ isClearable: true,
540
+ rules: [{ min: 0, max: 5, error: "\u8303\u56F4 0-5" }]
541
+ }),
542
+ components.input.number("gst_fileTimeoutSec", {
543
+ ...compactNumber,
544
+ label: "\u5355\u6587\u4EF6\u8D85\u65F6\uFF08\u79D2\uFF0C\u53EF\u9009\uFF09",
545
+ description: `\u6700\u591A ${MAX_FILE_TIMEOUT_SEC}s`,
546
+ defaultValue: "",
547
+ isClearable: true,
548
+ rules: [{ min: 0, max: MAX_FILE_TIMEOUT_SEC, error: `\u8303\u56F4 0-${MAX_FILE_TIMEOUT_SEC}` }]
549
+ }),
550
+ components.input.number("gst_retryTimes", {
551
+ ...compactNumber,
552
+ label: "\u91CD\u8BD5\u6B21\u6570\uFF08\u53EF\u9009\uFF09",
553
+ defaultValue: "",
554
+ isClearable: true,
555
+ rules: [{ min: 0, max: 1e3, error: "\u8303\u56F4 0-1000" }]
556
+ }),
557
+ components.input.number("gst_retryDelayMs", {
558
+ ...compactNumber,
559
+ label: "\u91CD\u8BD5\u5EF6\u8FDF\uFF08ms\uFF0C\u53EF\u9009\uFF09",
560
+ defaultValue: "",
561
+ isClearable: true,
562
+ rules: [{ min: 0, max: 36e5, error: "\u8303\u56F4 0-3600000" }]
563
+ }),
564
+ components.input.number("gst_progressReportEvery", {
565
+ ...compactNumber,
566
+ label: "\u8FDB\u5EA6\u6D88\u606F\u95F4\u9694\uFF08\u6BCFN\u4E2A\u6587\u4EF6\uFF0C\u53EF\u9009\uFF09",
567
+ description: "0=\u5173\u95ED\uFF08\u4EC5\u5F71\u54CD\u804A\u5929\u56DE\u6267\uFF0C\u4E0D\u5F71\u54CD\u65E5\u5FD7\uFF09",
568
+ defaultValue: "",
569
+ isClearable: true,
570
+ rules: [{ min: 0, max: 1e5, error: "\u8303\u56F4 0-100000" }]
571
+ }),
572
+ components.input.number("gst_downloadLimitKbps", {
573
+ ...compactNumber,
574
+ label: "\u4E0B\u8F7D\u9650\u901F\uFF08KB/s\uFF0C\u53EF\u9009\uFF09",
575
+ description: "0=\u4E0D\u9650\u5236\uFF1B\u4E0D\u914D\u7F6E\u9650\u901F\u65F6\u4F1A\u81EA\u52A8\u8C03\u6574\u5E76\u53D1\uFF08\u6700\u591A 5\uFF09\u4EE5\u5C3D\u91CF\u8DD1\u6EE1\u5E26\u5BBD",
576
+ defaultValue: "",
577
+ isClearable: true,
578
+ rules: [{ min: 0, max: 1e7, error: "\u8303\u56F4 0-10000000" }]
579
+ }),
580
+ components.input.number("gst_uploadLimitKbps", {
581
+ ...compactNumber,
582
+ label: "\u4E0A\u4F20\u9650\u901F\uFF08KB/s\uFF0C\u53EF\u9009\uFF09",
583
+ description: "0=\u4E0D\u9650\u5236\uFF1B\u4E0D\u914D\u7F6E\u9650\u901F\u65F6\u4F1A\u81EA\u52A8\u8C03\u6574\u5E76\u53D1\uFF08\u6700\u591A 5\uFF09\u4EE5\u5C3D\u91CF\u8DD1\u6EE1\u5E26\u5BBD",
584
+ defaultValue: "",
585
+ isClearable: true,
586
+ rules: [{ min: 0, max: 1e7, error: "\u8303\u56F4 0-10000000" }]
587
+ }),
588
+ components.input.string("gst_timeWindows", {
589
+ ...compactInput,
590
+ className: `sm:col-span-2 ${compactInput.className}`,
591
+ label: "\u540C\u6B65\u65F6\u6BB5\uFF08\u5B9A\u65F6\u4EFB\u52A1\uFF09",
592
+ description: "\u4F8B\uFF1A00:00-06:00,23:00-23:59\uFF1B\u7A7A=\u4E0D\u9650\u5236",
593
+ placeholder: "00:00-06:00,23:00-23:59",
594
+ isClearable: true
595
+ }),
596
+ components.switch.options("gst_scheduleEnabled", {
597
+ ...compactSwitch,
598
+ label: "\u542F\u7528\u5B9A\u65F6\u540C\u6B65",
599
+ defaultSelected: false
600
+ }),
601
+ components.input.string("gst_scheduleCron", {
602
+ ...compactInput,
603
+ className: `sm:col-span-2 ${compactInput.className}`,
604
+ label: "\u5B9A\u65F6 Cron",
605
+ description: "6\u6BB5/5\u6BB5\u5747\u53EF\uFF0C\u4F8B\uFF1A0 */10 * * * *\uFF08\u6BCF10\u5206\u949F\uFF09",
606
+ placeholder: "0 */10 * * * *",
607
+ isClearable: true
608
+ })
609
+ ]
610
+ }
611
+ })
612
+ ];
613
+ },
614
+ save: (form) => {
615
+ try {
616
+ const flattenedForm = (() => {
617
+ if (!form || typeof form !== "object") return form;
618
+ const groupFilesTool = form.groupFilesTool;
619
+ if (!Array.isArray(groupFilesTool)) return form;
620
+ const merged = {};
621
+ for (const item of groupFilesTool) {
622
+ if (!item || typeof item !== "object") continue;
623
+ Object.assign(merged, item);
624
+ }
625
+ return { ...merged, ...form };
626
+ })();
627
+ const configPath = getConfigFilePath();
628
+ const current = readJsonSafe(configPath);
629
+ const def = readJsonSafe(getDefaultConfigPath());
630
+ const next = {
631
+ ...def,
632
+ ...current
633
+ };
634
+ next.openlistBaseUrl = asString(flattenedForm.openlistBaseUrl ?? next.openlistBaseUrl);
635
+ next.openlistUsername = asString(flattenedForm.openlistUsername ?? next.openlistUsername);
636
+ next.openlistPassword = asString(flattenedForm.openlistPassword ?? next.openlistPassword);
637
+ next.openlistTargetDir = asString(flattenedForm.openlistTargetDir ?? next.openlistTargetDir);
638
+ const baseDefaults = next.groupSyncDefaults ?? {};
639
+ next.groupSyncDefaults = {
640
+ ...baseDefaults,
641
+ mode: pickMode(flattenedForm.groupSyncDefaults_mode, pickMode(baseDefaults?.mode, "incremental")),
642
+ flat: typeof flattenedForm.groupSyncDefaults_flat === "undefined" ? Boolean(baseDefaults?.flat ?? false) : asBoolean(flattenedForm.groupSyncDefaults_flat),
643
+ urlConcurrency: asInt(flattenedForm.groupSyncDefaults_urlConcurrency, asInt(baseDefaults?.urlConcurrency, 3)),
644
+ transferConcurrency: asInt(flattenedForm.groupSyncDefaults_transferConcurrency, asInt(baseDefaults?.transferConcurrency, 3)),
645
+ fileTimeoutSec: clampInt(asInt(flattenedForm.groupSyncDefaults_fileTimeoutSec, asInt(baseDefaults?.fileTimeoutSec, 600)), 10, MAX_FILE_TIMEOUT_SEC),
646
+ retryTimes: asInt(flattenedForm.groupSyncDefaults_retryTimes, asInt(baseDefaults?.retryTimes, 2)),
647
+ retryDelayMs: asInt(flattenedForm.groupSyncDefaults_retryDelayMs, asInt(baseDefaults?.retryDelayMs, 1500)),
648
+ progressReportEvery: clampInt(asInt(flattenedForm.groupSyncDefaults_progressReportEvery, asInt(baseDefaults?.progressReportEvery, 10)), 0, 1e5),
649
+ downloadLimitKbps: clampInt(asInt(flattenedForm.groupSyncDefaults_downloadLimitKbps, asInt(baseDefaults?.downloadLimitKbps, 0)), 0, 1e7),
650
+ uploadLimitKbps: clampInt(asInt(flattenedForm.groupSyncDefaults_uploadLimitKbps, asInt(baseDefaults?.uploadLimitKbps, 0)), 0, 1e7)
651
+ };
652
+ const normalizedTargets = [];
653
+ const rows = extractAccordionRows(flattenedForm.groupSyncTargets);
654
+ if (rows.length) {
655
+ for (const row of rows) {
656
+ const groupId = asString(unwrapAccordionValue(row?.gst_groupId) ?? "").trim();
657
+ if (!groupId) continue;
658
+ const enabled = asBoolean(unwrapAccordionValue(row?.gst_enabled) ?? true);
659
+ const mode = pickMode(unwrapAccordionValue(row?.gst_mode), next.groupSyncDefaults?.mode ?? "incremental");
660
+ const targetDir = asString(unwrapAccordionValue(row?.gst_targetDir) ?? "").trim();
661
+ const sourceFolderId = asString(unwrapAccordionValue(row?.gst_sourceFolderId) ?? "").trim();
662
+ const flat = asBoolean(unwrapAccordionValue(row?.gst_flat) ?? next.groupSyncDefaults?.flat ?? false);
663
+ const maxFiles = asInt(unwrapAccordionValue(row?.gst_maxFiles), 0);
664
+ const urlConcurrency = asInt(unwrapAccordionValue(row?.gst_urlConcurrency), 0);
665
+ const transferConcurrency = asInt(unwrapAccordionValue(row?.gst_transferConcurrency), 0);
666
+ const fileTimeoutSec = clampInt(asInt(unwrapAccordionValue(row?.gst_fileTimeoutSec), 0), 0, MAX_FILE_TIMEOUT_SEC);
667
+ const retryTimes = asOptionalInt(unwrapAccordionValue(row?.gst_retryTimes));
668
+ const retryDelayMs = asOptionalInt(unwrapAccordionValue(row?.gst_retryDelayMs));
669
+ const progressReportEvery = asOptionalInt(unwrapAccordionValue(row?.gst_progressReportEvery));
670
+ const downloadLimitKbps = asOptionalInt(unwrapAccordionValue(row?.gst_downloadLimitKbps));
671
+ const uploadLimitKbps = asOptionalInt(unwrapAccordionValue(row?.gst_uploadLimitKbps));
672
+ const timeWindows = asString(unwrapAccordionValue(row?.gst_timeWindows) ?? "").trim();
673
+ const scheduleEnabled = asBoolean(unwrapAccordionValue(row?.gst_scheduleEnabled) ?? false);
674
+ const scheduleCron = asString(unwrapAccordionValue(row?.gst_scheduleCron) ?? "").trim();
675
+ const target = {
676
+ groupId,
677
+ enabled,
678
+ mode,
679
+ flat
680
+ };
681
+ if (sourceFolderId) target.sourceFolderId = sourceFolderId;
682
+ if (targetDir) target.targetDir = targetDir;
683
+ if (maxFiles > 0) target.maxFiles = maxFiles;
684
+ if (urlConcurrency > 0) target.urlConcurrency = urlConcurrency;
685
+ if (transferConcurrency > 0) target.transferConcurrency = transferConcurrency;
686
+ if (fileTimeoutSec > 0) target.fileTimeoutSec = fileTimeoutSec;
687
+ if (typeof retryTimes === "number" && retryTimes >= 0) target.retryTimes = retryTimes;
688
+ if (typeof retryDelayMs === "number" && retryDelayMs >= 0) target.retryDelayMs = retryDelayMs;
689
+ if (typeof progressReportEvery === "number" && progressReportEvery >= 0) target.progressReportEvery = clampInt(progressReportEvery, 0, 1e5);
690
+ if (typeof downloadLimitKbps === "number" && downloadLimitKbps >= 0) target.downloadLimitKbps = clampInt(downloadLimitKbps, 0, 1e7);
691
+ if (typeof uploadLimitKbps === "number" && uploadLimitKbps >= 0) target.uploadLimitKbps = clampInt(uploadLimitKbps, 0, 1e7);
692
+ if (timeWindows) target.timeWindows = timeWindows;
693
+ if (scheduleEnabled || scheduleCron) target.schedule = { enabled: scheduleEnabled, cron: scheduleCron };
694
+ normalizedTargets.push(target);
695
+ }
696
+ } else {
697
+ const container = flattenedForm.groupSyncTargets && typeof flattenedForm.groupSyncTargets === "object" ? flattenedForm.groupSyncTargets : flattenedForm;
698
+ const groupIds = arrayFrom(container.gst_groupId).map((v) => asString(unwrapAccordionValue(v)).trim());
699
+ for (let index = 0; index < groupIds.length; index++) {
700
+ const groupId = groupIds[index];
701
+ if (!groupId) continue;
702
+ const enabled = asBoolean(unwrapAccordionValue(getFormRowValue(container, "gst_enabled", index)) ?? true);
703
+ const mode = pickMode(unwrapAccordionValue(getFormRowValue(container, "gst_mode", index)), next.groupSyncDefaults?.mode ?? "incremental");
704
+ const targetDir = asString(unwrapAccordionValue(getFormRowValue(container, "gst_targetDir", index)) ?? "").trim();
705
+ const sourceFolderId = asString(unwrapAccordionValue(getFormRowValue(container, "gst_sourceFolderId", index)) ?? "").trim();
706
+ const flat = asBoolean(unwrapAccordionValue(getFormRowValue(container, "gst_flat", index)) ?? next.groupSyncDefaults?.flat ?? false);
707
+ const maxFiles = asInt(unwrapAccordionValue(getFormRowValue(container, "gst_maxFiles", index)), 0);
708
+ const urlConcurrency = asInt(unwrapAccordionValue(getFormRowValue(container, "gst_urlConcurrency", index)), 0);
709
+ const transferConcurrency = asInt(unwrapAccordionValue(getFormRowValue(container, "gst_transferConcurrency", index)), 0);
710
+ const fileTimeoutSec = clampInt(asInt(unwrapAccordionValue(getFormRowValue(container, "gst_fileTimeoutSec", index)), 0), 0, MAX_FILE_TIMEOUT_SEC);
711
+ const retryTimes = asOptionalInt(unwrapAccordionValue(getFormRowValue(container, "gst_retryTimes", index)));
712
+ const retryDelayMs = asOptionalInt(unwrapAccordionValue(getFormRowValue(container, "gst_retryDelayMs", index)));
713
+ const progressReportEvery = asOptionalInt(unwrapAccordionValue(getFormRowValue(container, "gst_progressReportEvery", index)));
714
+ const downloadLimitKbps = asOptionalInt(unwrapAccordionValue(getFormRowValue(container, "gst_downloadLimitKbps", index)));
715
+ const uploadLimitKbps = asOptionalInt(unwrapAccordionValue(getFormRowValue(container, "gst_uploadLimitKbps", index)));
716
+ const timeWindows = asString(unwrapAccordionValue(getFormRowValue(container, "gst_timeWindows", index)) ?? "").trim();
717
+ const scheduleEnabled = asBoolean(unwrapAccordionValue(getFormRowValue(container, "gst_scheduleEnabled", index)) ?? false);
718
+ const scheduleCron = asString(unwrapAccordionValue(getFormRowValue(container, "gst_scheduleCron", index)) ?? "").trim();
719
+ const target = {
720
+ groupId,
721
+ enabled,
722
+ mode,
723
+ flat
724
+ };
725
+ if (sourceFolderId) target.sourceFolderId = sourceFolderId;
726
+ if (targetDir) target.targetDir = targetDir;
727
+ if (maxFiles > 0) target.maxFiles = maxFiles;
728
+ if (urlConcurrency > 0) target.urlConcurrency = urlConcurrency;
729
+ if (transferConcurrency > 0) target.transferConcurrency = transferConcurrency;
730
+ if (fileTimeoutSec > 0) target.fileTimeoutSec = fileTimeoutSec;
731
+ if (typeof retryTimes === "number" && retryTimes >= 0) target.retryTimes = retryTimes;
732
+ if (typeof retryDelayMs === "number" && retryDelayMs >= 0) target.retryDelayMs = retryDelayMs;
733
+ if (typeof progressReportEvery === "number" && progressReportEvery >= 0) target.progressReportEvery = clampInt(progressReportEvery, 0, 1e5);
734
+ if (typeof downloadLimitKbps === "number" && downloadLimitKbps >= 0) target.downloadLimitKbps = clampInt(downloadLimitKbps, 0, 1e7);
735
+ if (typeof uploadLimitKbps === "number" && uploadLimitKbps >= 0) target.uploadLimitKbps = clampInt(uploadLimitKbps, 0, 1e7);
736
+ if (timeWindows) target.timeWindows = timeWindows;
737
+ if (scheduleEnabled || scheduleCron) target.schedule = { enabled: scheduleEnabled, cron: scheduleCron };
738
+ normalizedTargets.push(target);
739
+ }
740
+ }
741
+ const unique = /* @__PURE__ */ new Map();
742
+ for (const item of normalizedTargets) {
743
+ unique.set(String(item.groupId), item);
744
+ }
745
+ next.groupSyncTargets = [...unique.values()];
746
+ writeJsonSafe(configPath, next);
747
+ return { success: true, message: "\u914D\u7F6E\u4FDD\u5B58\u6210\u529F" };
748
+ } catch (error) {
749
+ logger.error(error);
750
+ return { success: false, message: error?.message ?? String(error) };
751
+ }
752
+ }
753
+ });
754
+ export {
755
+ web_config_default as default
756
+ };