@sonicjs-cms/core 2.10.0 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-CJYFSKH7.js → chunk-2MXF4RYZ.js} +3 -3
- package/dist/{chunk-CJYFSKH7.js.map → chunk-2MXF4RYZ.js.map} +1 -1
- package/dist/{chunk-MNFY6DWY.cjs → chunk-56GUBLJE.cjs} +7 -7
- package/dist/{chunk-MNFY6DWY.cjs.map → chunk-56GUBLJE.cjs.map} +1 -1
- package/dist/{chunk-IIBRG5S5.cjs → chunk-6BVLPACH.cjs} +408 -2
- package/dist/chunk-6BVLPACH.cjs.map +1 -0
- package/dist/{chunk-RCA6R6VE.cjs → chunk-ASAEJ4B7.cjs} +315 -162
- package/dist/chunk-ASAEJ4B7.cjs.map +1 -0
- package/dist/{chunk-IT2TC4ZD.cjs → chunk-B2ASV5RD.cjs} +13 -7
- package/dist/chunk-B2ASV5RD.cjs.map +1 -0
- package/dist/{chunk-IZWNIUJI.js → chunk-BUU2US2Z.js} +3 -3
- package/dist/{chunk-IZWNIUJI.js.map → chunk-BUU2US2Z.js.map} +1 -1
- package/dist/{chunk-ZMVWMJ3S.cjs → chunk-DE5YTNCD.cjs} +9 -2
- package/dist/chunk-DE5YTNCD.cjs.map +1 -0
- package/dist/{chunk-4TTMQQC7.js → chunk-GKRGDJGG.js} +10 -4
- package/dist/chunk-GKRGDJGG.js.map +1 -0
- package/dist/{chunk-6O3RJV3C.js → chunk-H55AYIRI.js} +9 -2
- package/dist/chunk-H55AYIRI.js.map +1 -0
- package/dist/{chunk-JTNUM7JE.js → chunk-JTQBNSZX.js} +187 -34
- package/dist/chunk-JTQBNSZX.js.map +1 -0
- package/dist/{chunk-64APW3DW.cjs → chunk-LFAQUR7P.cjs} +9 -2
- package/dist/chunk-LFAQUR7P.cjs.map +1 -0
- package/dist/{chunk-27AOVQTR.js → chunk-NMLFKXWW.js} +402 -3
- package/dist/chunk-NMLFKXWW.js.map +1 -0
- package/dist/{chunk-EKPLKUZT.cjs → chunk-QLPFENZ2.cjs} +3 -3
- package/dist/{chunk-EKPLKUZT.cjs.map → chunk-QLPFENZ2.cjs.map} +1 -1
- package/dist/{chunk-KYGRJCZM.cjs → chunk-QTFKZBLC.cjs} +3 -2
- package/dist/chunk-QTFKZBLC.cjs.map +1 -0
- package/dist/{chunk-LOUJRBXV.js → chunk-QXOZI5Q2.js} +3 -2
- package/dist/chunk-QXOZI5Q2.js.map +1 -0
- package/dist/{chunk-7JMMLHPQ.js → chunk-VJCLJH3X.js} +9 -2
- package/dist/chunk-VJCLJH3X.js.map +1 -0
- package/dist/index.cjs +751 -152
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +125 -5
- package/dist/index.d.ts +125 -5
- package/dist/index.js +582 -15
- package/dist/index.js.map +1 -1
- package/dist/middleware.cjs +29 -29
- package/dist/middleware.js +3 -3
- package/dist/migrations-UFVJTPVT.js +4 -0
- package/dist/{migrations-N2C2VPJU.js.map → migrations-UFVJTPVT.js.map} +1 -1
- package/dist/migrations-VNYOSUNE.cjs +13 -0
- package/dist/{migrations-ONIAY6GK.cjs.map → migrations-VNYOSUNE.cjs.map} +1 -1
- package/dist/{plugin-0Xogrln-.d.cts → plugin-DDYetMF-.d.cts} +1 -0
- package/dist/{plugin-0Xogrln-.d.ts → plugin-DDYetMF-.d.ts} +1 -0
- package/dist/{plugin-bootstrap-fpG98Otb.d.cts → plugin-bootstrap-DCXpeQVb.d.cts} +229 -1
- package/dist/{plugin-bootstrap-WmpvYM5w.d.ts → plugin-bootstrap-DXBAYaqM.d.ts} +229 -1
- package/dist/{plugin-manager-GcIeb226.d.cts → plugin-manager-BoM3Q7o7.d.cts} +1 -1
- package/dist/{plugin-manager-Clf2gXwj.d.ts → plugin-manager-Efx9RyDX.d.ts} +1 -1
- package/dist/plugins.cjs +10 -10
- package/dist/plugins.d.cts +2 -2
- package/dist/plugins.d.ts +2 -2
- package/dist/plugins.js +2 -2
- package/dist/routes.cjs +29 -29
- package/dist/routes.js +6 -6
- package/dist/services.cjs +60 -32
- package/dist/services.d.cts +1 -1
- package/dist/services.d.ts +1 -1
- package/dist/services.js +3 -3
- package/dist/types.cjs +2 -2
- package/dist/types.d.cts +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/utils.cjs +11 -11
- package/dist/utils.js +1 -1
- package/migrations/033_form_content_integration.sql +19 -0
- package/package.json +1 -1
- package/dist/chunk-27AOVQTR.js.map +0 -1
- package/dist/chunk-4TTMQQC7.js.map +0 -1
- package/dist/chunk-64APW3DW.cjs.map +0 -1
- package/dist/chunk-6O3RJV3C.js.map +0 -1
- package/dist/chunk-7JMMLHPQ.js.map +0 -1
- package/dist/chunk-IIBRG5S5.cjs.map +0 -1
- package/dist/chunk-IT2TC4ZD.cjs.map +0 -1
- package/dist/chunk-JTNUM7JE.js.map +0 -1
- package/dist/chunk-KYGRJCZM.cjs.map +0 -1
- package/dist/chunk-LOUJRBXV.js.map +0 -1
- package/dist/chunk-RCA6R6VE.cjs.map +0 -1
- package/dist/chunk-ZMVWMJ3S.cjs.map +0 -1
- package/dist/migrations-N2C2VPJU.js +0 -4
- package/dist/migrations-ONIAY6GK.cjs +0 -13
|
@@ -283,6 +283,370 @@ async function fullCollectionSync(db) {
|
|
|
283
283
|
return { results, removed };
|
|
284
284
|
}
|
|
285
285
|
|
|
286
|
+
// src/services/form-collection-sync.ts
|
|
287
|
+
var SYSTEM_FORM_USER_ID = "system-form-submission";
|
|
288
|
+
function mapFormioTypeToSchemaType(component) {
|
|
289
|
+
switch (component.type) {
|
|
290
|
+
case "textfield":
|
|
291
|
+
case "textarea":
|
|
292
|
+
case "password":
|
|
293
|
+
case "phoneNumber":
|
|
294
|
+
case "url":
|
|
295
|
+
return { type: "string", title: component.label || component.key };
|
|
296
|
+
case "email":
|
|
297
|
+
return { type: "string", format: "email", title: component.label || component.key };
|
|
298
|
+
case "number":
|
|
299
|
+
case "currency":
|
|
300
|
+
return { type: "number", title: component.label || component.key };
|
|
301
|
+
case "checkbox":
|
|
302
|
+
return { type: "boolean", title: component.label || component.key };
|
|
303
|
+
case "select":
|
|
304
|
+
case "radio": {
|
|
305
|
+
const enumValues = (component.data?.values || component.values || []).map((v) => v.value);
|
|
306
|
+
const enumLabels = (component.data?.values || component.values || []).map((v) => v.label);
|
|
307
|
+
return {
|
|
308
|
+
type: "select",
|
|
309
|
+
title: component.label || component.key,
|
|
310
|
+
enum: enumValues,
|
|
311
|
+
enumLabels
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
case "selectboxes":
|
|
315
|
+
return { type: "object", title: component.label || component.key };
|
|
316
|
+
case "datetime":
|
|
317
|
+
case "day":
|
|
318
|
+
case "time":
|
|
319
|
+
return { type: "string", format: "date-time", title: component.label || component.key };
|
|
320
|
+
case "file":
|
|
321
|
+
case "signature":
|
|
322
|
+
return { type: "string", title: component.label || component.key };
|
|
323
|
+
case "address":
|
|
324
|
+
return { type: "object", title: component.label || component.key };
|
|
325
|
+
case "hidden":
|
|
326
|
+
return { type: "string", title: component.label || component.key };
|
|
327
|
+
default:
|
|
328
|
+
return { type: "string", title: component.label || component.key };
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
function extractFieldComponents(components) {
|
|
332
|
+
const fields = [];
|
|
333
|
+
if (!components) return fields;
|
|
334
|
+
for (const comp of components) {
|
|
335
|
+
if (comp.type === "panel" || comp.type === "fieldset" || comp.type === "well" || comp.type === "tabs") {
|
|
336
|
+
if (comp.components) {
|
|
337
|
+
fields.push(...extractFieldComponents(comp.components));
|
|
338
|
+
}
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
if (comp.type === "columns" && comp.columns) {
|
|
342
|
+
for (const col of comp.columns) {
|
|
343
|
+
if (col.components) {
|
|
344
|
+
fields.push(...extractFieldComponents(col.components));
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
if (comp.type === "table" && comp.rows) {
|
|
350
|
+
for (const row of comp.rows) {
|
|
351
|
+
if (Array.isArray(row)) {
|
|
352
|
+
for (const cell of row) {
|
|
353
|
+
if (cell.components) {
|
|
354
|
+
fields.push(...extractFieldComponents(cell.components));
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
if (comp.type === "button" || comp.type === "htmlelement" || comp.type === "content") {
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
if (comp.type === "turnstile") {
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
if (comp.key) {
|
|
368
|
+
fields.push(comp);
|
|
369
|
+
}
|
|
370
|
+
if (comp.components) {
|
|
371
|
+
fields.push(...extractFieldComponents(comp.components));
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return fields;
|
|
375
|
+
}
|
|
376
|
+
function deriveCollectionSchemaFromFormio(formioSchema) {
|
|
377
|
+
const components = formioSchema?.components || [];
|
|
378
|
+
const fieldComponents = extractFieldComponents(components);
|
|
379
|
+
const properties = {
|
|
380
|
+
// Always include a title field for the content item
|
|
381
|
+
title: { type: "string", title: "Title", required: true }
|
|
382
|
+
};
|
|
383
|
+
const required = ["title"];
|
|
384
|
+
for (const comp of fieldComponents) {
|
|
385
|
+
const key = comp.key;
|
|
386
|
+
if (!key || key === "submit" || key === "title") continue;
|
|
387
|
+
const fieldDef = mapFormioTypeToSchemaType(comp);
|
|
388
|
+
if (comp.validate?.required) {
|
|
389
|
+
fieldDef.required = true;
|
|
390
|
+
required.push(key);
|
|
391
|
+
}
|
|
392
|
+
properties[key] = fieldDef;
|
|
393
|
+
}
|
|
394
|
+
return { type: "object", properties, required };
|
|
395
|
+
}
|
|
396
|
+
function deriveSubmissionTitle(data, formDisplayName) {
|
|
397
|
+
const candidates = ["name", "fullName", "full_name", "firstName", "first_name"];
|
|
398
|
+
for (const key of candidates) {
|
|
399
|
+
if (data[key] && typeof data[key] === "string" && data[key].trim()) {
|
|
400
|
+
if (key === "firstName" || key === "first_name") {
|
|
401
|
+
const last = data["lastName"] || data["last_name"] || data["lastname"] || "";
|
|
402
|
+
if (last) return `${data[key].trim()} ${last.trim()}`;
|
|
403
|
+
}
|
|
404
|
+
return data[key].trim();
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
if (data.email && typeof data.email === "string" && data.email.trim()) {
|
|
408
|
+
return data.email.trim();
|
|
409
|
+
}
|
|
410
|
+
if (data.subject && typeof data.subject === "string" && data.subject.trim()) {
|
|
411
|
+
return data.subject.trim();
|
|
412
|
+
}
|
|
413
|
+
const dateStr = (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", {
|
|
414
|
+
year: "numeric",
|
|
415
|
+
month: "short",
|
|
416
|
+
day: "numeric",
|
|
417
|
+
hour: "2-digit",
|
|
418
|
+
minute: "2-digit"
|
|
419
|
+
});
|
|
420
|
+
return `${formDisplayName} - ${dateStr}`;
|
|
421
|
+
}
|
|
422
|
+
function mapFormStatusToContentStatus(formStatus) {
|
|
423
|
+
switch (formStatus) {
|
|
424
|
+
case "pending":
|
|
425
|
+
return "published";
|
|
426
|
+
case "reviewed":
|
|
427
|
+
return "published";
|
|
428
|
+
case "approved":
|
|
429
|
+
return "published";
|
|
430
|
+
case "rejected":
|
|
431
|
+
return "archived";
|
|
432
|
+
case "spam":
|
|
433
|
+
return "deleted";
|
|
434
|
+
default:
|
|
435
|
+
return "published";
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
async function syncFormCollection(db, form) {
|
|
439
|
+
const collectionName = `form_${form.name}`;
|
|
440
|
+
const displayName = `${form.display_name} (Form)`;
|
|
441
|
+
const formioSchema = typeof form.formio_schema === "string" ? JSON.parse(form.formio_schema) : form.formio_schema;
|
|
442
|
+
const schema = deriveCollectionSchemaFromFormio(formioSchema);
|
|
443
|
+
const schemaJson = JSON.stringify(schema);
|
|
444
|
+
const now = Date.now();
|
|
445
|
+
const isActive = form.is_active ? 1 : 0;
|
|
446
|
+
const existing = await db.prepare(
|
|
447
|
+
"SELECT id, schema, display_name, description, is_active FROM collections WHERE source_type = ? AND source_id = ?"
|
|
448
|
+
).bind("form", form.id).first();
|
|
449
|
+
if (!existing) {
|
|
450
|
+
const collectionId = `col-form-${form.name}-${crypto.randomUUID().slice(0, 8)}`;
|
|
451
|
+
await db.prepare(`
|
|
452
|
+
INSERT INTO collections (id, name, display_name, description, schema, is_active, managed, source_type, source_id, created_at, updated_at)
|
|
453
|
+
VALUES (?, ?, ?, ?, ?, ?, 1, 'form', ?, ?, ?)
|
|
454
|
+
`).bind(
|
|
455
|
+
collectionId,
|
|
456
|
+
collectionName,
|
|
457
|
+
displayName,
|
|
458
|
+
form.description || null,
|
|
459
|
+
schemaJson,
|
|
460
|
+
isActive,
|
|
461
|
+
form.id,
|
|
462
|
+
now,
|
|
463
|
+
now
|
|
464
|
+
).run();
|
|
465
|
+
console.log(`[FormSync] Created shadow collection: ${collectionName}`);
|
|
466
|
+
return { collectionId, status: "created" };
|
|
467
|
+
}
|
|
468
|
+
const existingSchema = existing.schema ? JSON.stringify(typeof existing.schema === "string" ? JSON.parse(existing.schema) : existing.schema) : "{}";
|
|
469
|
+
const needsUpdate = schemaJson !== existingSchema || displayName !== existing.display_name || (form.description || null) !== existing.description || isActive !== existing.is_active;
|
|
470
|
+
if (!needsUpdate) {
|
|
471
|
+
return { collectionId: existing.id, status: "unchanged" };
|
|
472
|
+
}
|
|
473
|
+
await db.prepare(`
|
|
474
|
+
UPDATE collections SET display_name = ?, description = ?, schema = ?, is_active = ?, updated_at = ?
|
|
475
|
+
WHERE id = ?
|
|
476
|
+
`).bind(
|
|
477
|
+
displayName,
|
|
478
|
+
form.description || null,
|
|
479
|
+
schemaJson,
|
|
480
|
+
isActive,
|
|
481
|
+
now,
|
|
482
|
+
existing.id
|
|
483
|
+
).run();
|
|
484
|
+
console.log(`[FormSync] Updated shadow collection: ${collectionName}`);
|
|
485
|
+
return { collectionId: existing.id, status: "updated" };
|
|
486
|
+
}
|
|
487
|
+
async function syncAllFormCollections(db) {
|
|
488
|
+
try {
|
|
489
|
+
const tableCheck = await db.prepare(
|
|
490
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name='forms'"
|
|
491
|
+
).first();
|
|
492
|
+
if (!tableCheck) {
|
|
493
|
+
console.log("[FormSync] Forms table does not exist, skipping form sync");
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
const { results: forms } = await db.prepare(
|
|
497
|
+
"SELECT id, name, display_name, description, formio_schema, is_active FROM forms"
|
|
498
|
+
).all();
|
|
499
|
+
if (!forms || forms.length === 0) {
|
|
500
|
+
console.log("[FormSync] No forms found, skipping");
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
let created = 0;
|
|
504
|
+
let updated = 0;
|
|
505
|
+
for (const form of forms) {
|
|
506
|
+
try {
|
|
507
|
+
const result = await syncFormCollection(db, form);
|
|
508
|
+
if (result.status === "created") created++;
|
|
509
|
+
if (result.status === "updated") updated++;
|
|
510
|
+
await backfillFormSubmissions(db, form.id, result.collectionId);
|
|
511
|
+
} catch (error) {
|
|
512
|
+
console.error(`[FormSync] Error syncing form ${form.name}:`, error);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
console.log(`[FormSync] Sync complete: ${created} created, ${updated} updated out of ${forms.length} forms`);
|
|
516
|
+
} catch (error) {
|
|
517
|
+
console.error("[FormSync] Error syncing form collections:", error);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
async function createContentFromSubmission(db, submissionData, form, submissionId, metadata = {}) {
|
|
521
|
+
try {
|
|
522
|
+
let collection = await db.prepare(
|
|
523
|
+
"SELECT id FROM collections WHERE source_type = ? AND source_id = ?"
|
|
524
|
+
).bind("form", form.id).first();
|
|
525
|
+
if (!collection) {
|
|
526
|
+
console.warn(`[FormSync] No shadow collection found for form ${form.name}, attempting to create...`);
|
|
527
|
+
try {
|
|
528
|
+
const fullForm = await db.prepare(
|
|
529
|
+
"SELECT id, name, display_name, description, formio_schema, is_active FROM forms WHERE id = ?"
|
|
530
|
+
).bind(form.id).first();
|
|
531
|
+
if (fullForm) {
|
|
532
|
+
const schema = typeof fullForm.formio_schema === "string" ? JSON.parse(fullForm.formio_schema) : fullForm.formio_schema;
|
|
533
|
+
const result = await syncFormCollection(db, {
|
|
534
|
+
id: fullForm.id,
|
|
535
|
+
name: fullForm.name,
|
|
536
|
+
display_name: fullForm.display_name,
|
|
537
|
+
description: fullForm.description,
|
|
538
|
+
formio_schema: schema,
|
|
539
|
+
is_active: fullForm.is_active ?? 1
|
|
540
|
+
});
|
|
541
|
+
collection = await db.prepare(
|
|
542
|
+
"SELECT id FROM collections WHERE source_type = ? AND source_id = ?"
|
|
543
|
+
).bind("form", form.id).first();
|
|
544
|
+
console.log(`[FormSync] On-the-fly sync result: ${result.status}, collectionId: ${result.collectionId}`);
|
|
545
|
+
}
|
|
546
|
+
} catch (syncErr) {
|
|
547
|
+
console.error("[FormSync] On-the-fly shadow collection creation failed:", syncErr);
|
|
548
|
+
}
|
|
549
|
+
if (!collection) {
|
|
550
|
+
console.error(`[FormSync] Still no shadow collection for form ${form.name} after recovery attempt`);
|
|
551
|
+
return null;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
const contentId = crypto.randomUUID();
|
|
555
|
+
const now = Date.now();
|
|
556
|
+
const title = deriveSubmissionTitle(submissionData, form.display_name);
|
|
557
|
+
const slug = `submission-${submissionId.slice(0, 8)}`;
|
|
558
|
+
const contentData = {
|
|
559
|
+
title,
|
|
560
|
+
...submissionData,
|
|
561
|
+
_submission_metadata: {
|
|
562
|
+
submissionId,
|
|
563
|
+
formId: form.id,
|
|
564
|
+
formName: form.name,
|
|
565
|
+
email: metadata.userEmail || submissionData.email || null,
|
|
566
|
+
ipAddress: metadata.ipAddress || null,
|
|
567
|
+
userAgent: metadata.userAgent || null,
|
|
568
|
+
submittedAt: now
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
const authorId = metadata.userId || SYSTEM_FORM_USER_ID;
|
|
572
|
+
if (authorId === SYSTEM_FORM_USER_ID) {
|
|
573
|
+
const systemUser = await db.prepare("SELECT id FROM users WHERE id = ?").bind(SYSTEM_FORM_USER_ID).first();
|
|
574
|
+
if (!systemUser) {
|
|
575
|
+
console.log("[FormSync] System form user missing, creating...");
|
|
576
|
+
const sysNow = Date.now();
|
|
577
|
+
await db.prepare(`
|
|
578
|
+
INSERT OR IGNORE INTO users (id, email, username, first_name, last_name, password_hash, role, is_active, created_at, updated_at)
|
|
579
|
+
VALUES (?, ?, ?, ?, ?, NULL, 'viewer', 0, ?, ?)
|
|
580
|
+
`).bind(SYSTEM_FORM_USER_ID, "system-forms@sonicjs.internal", "system-forms", "Form", "Submission", sysNow, sysNow).run();
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
console.log(`[FormSync] Inserting content: id=${contentId}, collection=${collection.id}, slug=${slug}, title=${title}, author=${authorId}`);
|
|
584
|
+
await db.prepare(`
|
|
585
|
+
INSERT INTO content (id, collection_id, slug, title, data, status, author_id, created_at, updated_at)
|
|
586
|
+
VALUES (?, ?, ?, ?, ?, 'published', ?, ?, ?)
|
|
587
|
+
`).bind(
|
|
588
|
+
contentId,
|
|
589
|
+
collection.id,
|
|
590
|
+
slug,
|
|
591
|
+
title,
|
|
592
|
+
JSON.stringify(contentData),
|
|
593
|
+
authorId,
|
|
594
|
+
now,
|
|
595
|
+
now
|
|
596
|
+
).run();
|
|
597
|
+
await db.prepare(
|
|
598
|
+
"UPDATE form_submissions SET content_id = ? WHERE id = ?"
|
|
599
|
+
).bind(contentId, submissionId).run();
|
|
600
|
+
console.log(`[FormSync] Content created successfully: ${contentId}`);
|
|
601
|
+
return contentId;
|
|
602
|
+
} catch (error) {
|
|
603
|
+
console.error("[FormSync] Error creating content from submission:", error);
|
|
604
|
+
return null;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
async function backfillFormSubmissions(db, formId, collectionId) {
|
|
608
|
+
try {
|
|
609
|
+
const { results: submissions } = await db.prepare(
|
|
610
|
+
"SELECT id, submission_data, user_email, ip_address, user_agent, user_id, submitted_at FROM form_submissions WHERE form_id = ? AND content_id IS NULL"
|
|
611
|
+
).bind(formId).all();
|
|
612
|
+
if (!submissions || submissions.length === 0) {
|
|
613
|
+
return 0;
|
|
614
|
+
}
|
|
615
|
+
const form = await db.prepare(
|
|
616
|
+
"SELECT id, name, display_name FROM forms WHERE id = ?"
|
|
617
|
+
).bind(formId).first();
|
|
618
|
+
if (!form) return 0;
|
|
619
|
+
let count = 0;
|
|
620
|
+
for (const sub of submissions) {
|
|
621
|
+
try {
|
|
622
|
+
const submissionData = typeof sub.submission_data === "string" ? JSON.parse(sub.submission_data) : sub.submission_data;
|
|
623
|
+
const contentId = await createContentFromSubmission(
|
|
624
|
+
db,
|
|
625
|
+
submissionData,
|
|
626
|
+
{ id: form.id, name: form.name, display_name: form.display_name },
|
|
627
|
+
sub.id,
|
|
628
|
+
{
|
|
629
|
+
ipAddress: sub.ip_address,
|
|
630
|
+
userAgent: sub.user_agent,
|
|
631
|
+
userEmail: sub.user_email,
|
|
632
|
+
userId: sub.user_id
|
|
633
|
+
}
|
|
634
|
+
);
|
|
635
|
+
if (contentId) count++;
|
|
636
|
+
} catch (error) {
|
|
637
|
+
console.error(`[FormSync] Error backfilling submission ${sub.id}:`, error);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
if (count > 0) {
|
|
641
|
+
console.log(`[FormSync] Backfilled ${count} submissions for form ${formId}`);
|
|
642
|
+
}
|
|
643
|
+
return count;
|
|
644
|
+
} catch (error) {
|
|
645
|
+
console.error("[FormSync] Error backfilling submissions:", error);
|
|
646
|
+
return 0;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
286
650
|
// src/services/plugin-service.ts
|
|
287
651
|
var PluginService = class {
|
|
288
652
|
constructor(db) {
|
|
@@ -713,6 +1077,41 @@ var PluginBootstrapService = class {
|
|
|
713
1077
|
results_limit: 20,
|
|
714
1078
|
index_media: false
|
|
715
1079
|
}
|
|
1080
|
+
},
|
|
1081
|
+
{
|
|
1082
|
+
id: "oauth-providers",
|
|
1083
|
+
name: "oauth-providers",
|
|
1084
|
+
display_name: "OAuth Providers",
|
|
1085
|
+
description: "OAuth2/OIDC social login with GitHub, Google, and more",
|
|
1086
|
+
version: "1.0.0-beta.1",
|
|
1087
|
+
author: "SonicJS Team",
|
|
1088
|
+
category: "authentication",
|
|
1089
|
+
icon: "\u{1F511}",
|
|
1090
|
+
permissions: ["oauth:manage"],
|
|
1091
|
+
dependencies: [],
|
|
1092
|
+
settings: {
|
|
1093
|
+
providers: {
|
|
1094
|
+
github: { clientId: "", clientSecret: "", enabled: false },
|
|
1095
|
+
google: { clientId: "", clientSecret: "", enabled: false }
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
},
|
|
1099
|
+
{
|
|
1100
|
+
id: "global-variables",
|
|
1101
|
+
name: "global-variables",
|
|
1102
|
+
display_name: "Global Variables",
|
|
1103
|
+
description: "Dynamic content variables with inline token support. Use {variable_key} syntax in rich text fields for server-side resolution.",
|
|
1104
|
+
version: "1.0.0-beta.1",
|
|
1105
|
+
author: "SonicJS Team",
|
|
1106
|
+
category: "content",
|
|
1107
|
+
icon: "\u{1F524}",
|
|
1108
|
+
permissions: ["global-variables:manage", "global-variables:view"],
|
|
1109
|
+
dependencies: [],
|
|
1110
|
+
settings: {
|
|
1111
|
+
enableResolution: true,
|
|
1112
|
+
cacheEnabled: true,
|
|
1113
|
+
cacheTTL: 300
|
|
1114
|
+
}
|
|
716
1115
|
}
|
|
717
1116
|
];
|
|
718
1117
|
/**
|
|
@@ -826,16 +1225,23 @@ var PluginBootstrapService = class {
|
|
|
826
1225
|
|
|
827
1226
|
exports.PluginBootstrapService = PluginBootstrapService;
|
|
828
1227
|
exports.PluginService = PluginService;
|
|
1228
|
+
exports.backfillFormSubmissions = backfillFormSubmissions;
|
|
829
1229
|
exports.cleanupRemovedCollections = cleanupRemovedCollections;
|
|
1230
|
+
exports.createContentFromSubmission = createContentFromSubmission;
|
|
1231
|
+
exports.deriveCollectionSchemaFromFormio = deriveCollectionSchemaFromFormio;
|
|
1232
|
+
exports.deriveSubmissionTitle = deriveSubmissionTitle;
|
|
830
1233
|
exports.fullCollectionSync = fullCollectionSync;
|
|
831
1234
|
exports.getAvailableCollectionNames = getAvailableCollectionNames;
|
|
832
1235
|
exports.getManagedCollections = getManagedCollections;
|
|
833
1236
|
exports.isCollectionManaged = isCollectionManaged;
|
|
834
1237
|
exports.loadCollectionConfig = loadCollectionConfig;
|
|
835
1238
|
exports.loadCollectionConfigs = loadCollectionConfigs;
|
|
1239
|
+
exports.mapFormStatusToContentStatus = mapFormStatusToContentStatus;
|
|
836
1240
|
exports.registerCollections = registerCollections;
|
|
1241
|
+
exports.syncAllFormCollections = syncAllFormCollections;
|
|
837
1242
|
exports.syncCollection = syncCollection;
|
|
838
1243
|
exports.syncCollections = syncCollections;
|
|
1244
|
+
exports.syncFormCollection = syncFormCollection;
|
|
839
1245
|
exports.validateCollectionConfig = validateCollectionConfig;
|
|
840
|
-
//# sourceMappingURL=chunk-
|
|
841
|
-
//# sourceMappingURL=chunk-
|
|
1246
|
+
//# sourceMappingURL=chunk-6BVLPACH.cjs.map
|
|
1247
|
+
//# sourceMappingURL=chunk-6BVLPACH.cjs.map
|