strapi-content-sync-pro 1.0.2 → 1.0.4
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/README.md +67 -18
- package/admin/src/components/BulkTransferTab.jsx +880 -0
- package/admin/src/components/ConfigTab.jsx +25 -4
- package/admin/src/components/HelpTab.jsx +201 -15
- package/admin/src/components/MediaTab.jsx +7 -0
- package/admin/src/components/StatsTab.jsx +470 -0
- package/admin/src/components/SyncProfilesTab.jsx +63 -5
- package/admin/src/components/SyncTab.jsx +53 -7
- package/admin/src/pages/App/index.jsx +15 -1
- package/docs/clipchamp-screen-recording-script.md +0 -0
- package/docs/production-readiness-status.md +34 -0
- package/docs/production-readiness-test-matrix.md +151 -0
- package/docs/test-environments-setup-legacy.txt +60 -0
- package/package.json +13 -4
- package/server/src/content-types/index.js +2 -0
- package/server/src/content-types/sync-run-report/schema.json +26 -0
- package/server/src/controllers/bulk-transfer.js +141 -0
- package/server/src/controllers/config.js +48 -5
- package/server/src/controllers/index.js +4 -0
- package/server/src/controllers/sync-log.js +6 -0
- package/server/src/controllers/sync-media.js +19 -0
- package/server/src/controllers/sync-stats.js +51 -0
- package/server/src/controllers/sync.js +9 -3
- package/server/src/routes/index.js +28 -0
- package/server/src/services/bulk-transfer.js +837 -0
- package/server/src/services/config.js +18 -2
- package/server/src/services/index.js +4 -0
- package/server/src/services/sync-execution.js +102 -5
- package/server/src/services/sync-log.js +36 -0
- package/server/src/services/sync-media.js +224 -1
- package/server/src/services/sync-profiles.js +92 -4
- package/server/src/services/sync-stats.js +353 -0
- package/server/src/services/sync.js +323 -101
- package/server/src/utils/applier.js +120 -13
- package/server/src/utils/comparator.js +22 -6
- package/server/src/utils/fetcher.js +11 -2
|
@@ -30,6 +30,7 @@ const ConfigTab = () => {
|
|
|
30
30
|
apiToken: '',
|
|
31
31
|
instanceId: '',
|
|
32
32
|
sharedSecret: '',
|
|
33
|
+
syncMode: 'paired',
|
|
33
34
|
});
|
|
34
35
|
|
|
35
36
|
// Login modal state
|
|
@@ -131,6 +132,7 @@ const ConfigTab = () => {
|
|
|
131
132
|
if (config.apiToken && config.apiToken !== '••••••••') payload.apiToken = config.apiToken;
|
|
132
133
|
if (config.instanceId) payload.instanceId = config.instanceId;
|
|
133
134
|
if (config.sharedSecret && config.sharedSecret !== '••••••••') payload.sharedSecret = config.sharedSecret;
|
|
135
|
+
if (config.syncMode) payload.syncMode = config.syncMode;
|
|
134
136
|
|
|
135
137
|
await post(`/${PLUGIN_ID}/config`, payload);
|
|
136
138
|
setMessage({ type: 'success', text: 'Connection configuration saved' });
|
|
@@ -349,6 +351,11 @@ const ConfigTab = () => {
|
|
|
349
351
|
{/* Connection Tab */}
|
|
350
352
|
<Tabs.Content value="connection">
|
|
351
353
|
<Box>
|
|
354
|
+
<Box paddingBottom={4}>
|
|
355
|
+
<Alert variant="info" title="Deployment mode">
|
|
356
|
+
In <strong>Paired</strong> mode, install Content Sync Pro on both local and remote servers. In <strong>Single-side</strong> mode, install on local only; remote plugin routes are not required. Connection test behavior and allowed sync/execution options follow the selected mode.
|
|
357
|
+
</Alert>
|
|
358
|
+
</Box>
|
|
352
359
|
<Flex gap={6}>
|
|
353
360
|
{/* LEFT COLUMN: Remote Server */}
|
|
354
361
|
<Box flex="1">
|
|
@@ -362,7 +369,7 @@ const ConfigTab = () => {
|
|
|
362
369
|
value={config.baseUrl}
|
|
363
370
|
onChange={(e) => setConfig((p) => ({ ...p, baseUrl: e.target.value }))}
|
|
364
371
|
/>
|
|
365
|
-
<Field.Hint>URL of the Strapi server
|
|
372
|
+
<Field.Hint>URL of the remote Strapi server where this plugin is also installed</Field.Hint>
|
|
366
373
|
</Field.Root>
|
|
367
374
|
|
|
368
375
|
<Field.Root>
|
|
@@ -378,7 +385,7 @@ const ConfigTab = () => {
|
|
|
378
385
|
</Box>
|
|
379
386
|
|
|
380
387
|
</Flex>
|
|
381
|
-
<Field.Hint>
|
|
388
|
+
<Field.Hint>Remote API token with permissions for this plugin routes and synced content types</Field.Hint>
|
|
382
389
|
</Field.Root>
|
|
383
390
|
<Field.Root>
|
|
384
391
|
<Button
|
|
@@ -404,7 +411,7 @@ const ConfigTab = () => {
|
|
|
404
411
|
value={config.instanceId}
|
|
405
412
|
onChange={(e) => setConfig((p) => ({ ...p, instanceId: e.target.value }))}
|
|
406
413
|
/>
|
|
407
|
-
<Field.Hint>
|
|
414
|
+
<Field.Hint>Unique local instance name used in logs and sync traceability</Field.Hint>
|
|
408
415
|
</Field.Root>
|
|
409
416
|
|
|
410
417
|
<Field.Root>
|
|
@@ -415,7 +422,21 @@ const ConfigTab = () => {
|
|
|
415
422
|
value={config.sharedSecret}
|
|
416
423
|
onChange={(e) => setConfig((p) => ({ ...p, sharedSecret: e.target.value }))}
|
|
417
424
|
/>
|
|
418
|
-
<Field.Hint>Must match on both
|
|
425
|
+
<Field.Hint>Must match exactly on both local and remote plugin configurations</Field.Hint>
|
|
426
|
+
</Field.Root>
|
|
427
|
+
|
|
428
|
+
<Field.Root>
|
|
429
|
+
<Field.Label>Sync Mode</Field.Label>
|
|
430
|
+
<SingleSelect
|
|
431
|
+
value={config.syncMode || 'paired'}
|
|
432
|
+
onChange={(value) => setConfig((p) => ({ ...p, syncMode: value }))}
|
|
433
|
+
>
|
|
434
|
+
<SingleSelectOption value="paired">Paired (plugin on both servers)</SingleSelectOption>
|
|
435
|
+
<SingleSelectOption value="single_side">Single-side (plugin only on local)</SingleSelectOption>
|
|
436
|
+
</SingleSelect>
|
|
437
|
+
<Field.Hint>
|
|
438
|
+
Paired mode supports push/pull/bidirectional and remote plugin validation. Single-side mode is pull-focused and does not require plugin routes on remote.
|
|
439
|
+
</Field.Hint>
|
|
419
440
|
</Field.Root>
|
|
420
441
|
</Flex>
|
|
421
442
|
</Box>
|
|
@@ -38,7 +38,7 @@ export const HelpTab = () => {
|
|
|
38
38
|
<Box paddingBottom={4}>
|
|
39
39
|
<Typography variant="beta" tag="h2">Plugin Documentation</Typography>
|
|
40
40
|
<Typography variant="omega" textColor="neutral600">
|
|
41
|
-
|
|
41
|
+
End-to-end guide for configuring, securing, and operating Content Sync Pro across Strapi environments.
|
|
42
42
|
</Typography>
|
|
43
43
|
</Box>
|
|
44
44
|
|
|
@@ -49,7 +49,9 @@ export const HelpTab = () => {
|
|
|
49
49
|
<Tabs.Trigger value="content-types">Content Types</Tabs.Trigger>
|
|
50
50
|
<Tabs.Trigger value="sync-profiles">Sync Profiles</Tabs.Trigger>
|
|
51
51
|
<Tabs.Trigger value="execution">Sync Execution</Tabs.Trigger>
|
|
52
|
+
<Tabs.Trigger value="bulk-transfer">Bulk Transfer</Tabs.Trigger>
|
|
52
53
|
<Tabs.Trigger value="media">Media</Tabs.Trigger>
|
|
54
|
+
<Tabs.Trigger value="stats">Stats</Tabs.Trigger>
|
|
53
55
|
<Tabs.Trigger value="enforcement">Enforcement</Tabs.Trigger>
|
|
54
56
|
<Tabs.Trigger value="alerts">Alerts</Tabs.Trigger>
|
|
55
57
|
<Tabs.Trigger value="troubleshooting">Troubleshooting</Tabs.Trigger>
|
|
@@ -124,10 +126,13 @@ export const HelpTab = () => {
|
|
|
124
126
|
<ul style={{ paddingLeft: '20px', marginTop: '8px', lineHeight: '1.8' }}>
|
|
125
127
|
<li><Typography variant="omega">Bi-directional sync (push, pull, or both)</Typography></li>
|
|
126
128
|
<li><Typography variant="omega">Sync Profiles for defining WHAT to sync</Typography></li>
|
|
127
|
-
<li><Typography variant="omega">Execution modes: On-demand, Scheduled, or
|
|
129
|
+
<li><Typography variant="omega">Execution modes: On-demand, Scheduled, Live, or External scheduler</Typography></li>
|
|
128
130
|
<li><Typography variant="omega">Field-level sync policies (Advanced mode)</Typography></li>
|
|
129
131
|
<li><Typography variant="omega">Conflict resolution strategies (Latest, Local, Remote wins)</Typography></li>
|
|
132
|
+
<li><Typography variant="omega">Pagination support for large datasets with bounded memory usage</Typography></li>
|
|
130
133
|
<li><Typography variant="omega">Dependency resolution - sync related entities automatically</Typography></li>
|
|
134
|
+
<li><Typography variant="omega"><strong>Bulk Transfer</strong> - top-level tab for one-click full push / full pull with selectable chunks, page-level progress, pause / resume / cancel, and persisted run history (restart or resume any previous run)</Typography></li>
|
|
135
|
+
<li><Typography variant="omega">Media sync (URL or rsync) with MIME filters and morph-link remapping</Typography></li>
|
|
131
136
|
<li><Typography variant="omega">Enforcement checks - schema, version, and time validation</Typography></li>
|
|
132
137
|
<li><Typography variant="omega">Configurable alerts via email, webhook, or Strapi logs</Typography></li>
|
|
133
138
|
<li><Typography variant="omega">Secure communication via API tokens and HMAC signatures</Typography></li>
|
|
@@ -141,6 +146,7 @@ export const HelpTab = () => {
|
|
|
141
146
|
<ul style={{ paddingLeft: '20px', marginTop: '8px', lineHeight: '1.8' }}>
|
|
142
147
|
<li><Typography variant="omega"><strong>Sync Profiles</strong> - Define WHAT to sync (direction, conflict strategy, field policies)</Typography></li>
|
|
143
148
|
<li><Typography variant="omega"><strong>Sync Execution</strong> - Define WHEN to sync (on-demand, scheduled, live) and dependency handling</Typography></li>
|
|
149
|
+
<li><Typography variant="omega"><strong>Bulk Transfer</strong> - One-click full pull / push across selectable chunks with pause / resume and persisted history (separate top-level tab)</Typography></li>
|
|
144
150
|
<li><Typography variant="omega"><strong>Enforcement</strong> - Pre-sync validation (schema match, version check, time sync)</Typography></li>
|
|
145
151
|
<li><Typography variant="omega"><strong>Alerts</strong> - Notifications for sync success/failure</Typography></li>
|
|
146
152
|
</ul>
|
|
@@ -148,10 +154,11 @@ export const HelpTab = () => {
|
|
|
148
154
|
|
|
149
155
|
<HelpSection title="Quick Start">
|
|
150
156
|
<ol style={{ paddingLeft: '20px', lineHeight: '2' }}>
|
|
151
|
-
<li><Typography variant="omega"><strong>Configuration Tab</strong> - Set up remote server URL, API token, and shared secret</Typography></li>
|
|
157
|
+
<li><Typography variant="omega"><strong>Configuration Tab</strong> - Set up remote server URL, API token, instance ID, and shared secret</Typography></li>
|
|
152
158
|
<li><Typography variant="omega"><strong>Content Types Tab</strong> - Enable content types for sync (auto-generates default profiles)</Typography></li>
|
|
153
159
|
<li><Typography variant="omega"><strong>Sync Profiles Tab</strong> - Customize sync behavior or use defaults</Typography></li>
|
|
154
|
-
<li><Typography variant="omega"><strong>Sync Tab</strong> - Configure execution settings and run sync operations</Typography></li>
|
|
160
|
+
<li><Typography variant="omega"><strong>Sync Tab</strong> - Configure execution settings, page size, and run sync operations</Typography></li>
|
|
161
|
+
<li><Typography variant="omega"><strong>Bulk Transfer Tab</strong> - For a first-time migration or full refresh, run a one-click Full Pull or Full Push with selectable chunks, pause / resume, and persisted run history</Typography></li>
|
|
155
162
|
</ol>
|
|
156
163
|
</HelpSection>
|
|
157
164
|
|
|
@@ -170,6 +177,14 @@ export const HelpTab = () => {
|
|
|
170
177
|
<Typography variant="omega" paddingBottom={2}>
|
|
171
178
|
Configure the connection to the remote Strapi instance in the <strong>Connection</strong> sub-tab.
|
|
172
179
|
</Typography>
|
|
180
|
+
<Typography variant="omega" paddingBottom={2}>
|
|
181
|
+
<strong>Deployment modes:</strong> Use <strong>Paired</strong> mode when the plugin is installed on both servers,
|
|
182
|
+
or <strong>Single-side</strong> mode when the plugin is installed only on local server.
|
|
183
|
+
</Typography>
|
|
184
|
+
<Typography variant="omega" paddingBottom={2}>
|
|
185
|
+
In paired mode, connection test validates remote plugin reachability and token access. In single-side mode,
|
|
186
|
+
test validates remote reachability and content API token access without requiring remote plugin endpoints.
|
|
187
|
+
</Typography>
|
|
173
188
|
|
|
174
189
|
<Box background="neutral100" padding={4} hasRadius marginBottom={4}>
|
|
175
190
|
<Typography variant="sigma" textColor="neutral800">Base URL</Typography>
|
|
@@ -295,6 +310,9 @@ http://localhost:1337</CodeBlock>
|
|
|
295
310
|
Sync Profiles define <strong>WHAT</strong> to sync and <strong>HOW</strong> conflicts are resolved.
|
|
296
311
|
They do NOT control when sync runs - that's configured in the Sync tab (Execution).
|
|
297
312
|
</Typography>
|
|
313
|
+
<Typography variant="omega" paddingTop={2}>
|
|
314
|
+
In <strong>Single-side</strong> mode, profiles are automatically restricted to <strong>Pull Only</strong>.
|
|
315
|
+
</Typography>
|
|
298
316
|
<Typography variant="omega" paddingTop={2}>
|
|
299
317
|
Each profile specifies:
|
|
300
318
|
</Typography>
|
|
@@ -525,7 +543,7 @@ http://localhost:1337</CodeBlock>
|
|
|
525
543
|
Uses lifecycle hooks to detect changes.
|
|
526
544
|
</Typography>
|
|
527
545
|
<Typography variant="pi" textColor="warning600" paddingTop={2}>
|
|
528
|
-
Note: Increases server load. Use for critical content only.
|
|
546
|
+
Note: Increases server load. Use for critical content only. Live mode is available in paired mode and disabled in single-side mode.
|
|
529
547
|
</Typography>
|
|
530
548
|
</Box>
|
|
531
549
|
</HelpSection>
|
|
@@ -555,6 +573,18 @@ http://localhost:1337</CodeBlock>
|
|
|
555
573
|
</ul>
|
|
556
574
|
</HelpSection>
|
|
557
575
|
|
|
576
|
+
<HelpSection title="Bulk Transfer (Full Pull / Full Push)">
|
|
577
|
+
<Typography variant="omega">
|
|
578
|
+
Bulk Transfer is now its own <strong>top-level tab</strong> (separate from Sync). It is a
|
|
579
|
+
one-click helper for moving everything between local and remote, with selectable chunks,
|
|
580
|
+
page-level progress, pause / resume / cancel, and a persisted run history you can restart
|
|
581
|
+
or resume from.
|
|
582
|
+
</Typography>
|
|
583
|
+
<Typography variant="omega" paddingTop={2}>
|
|
584
|
+
See the <strong>Bulk Transfer</strong> help tab for the full guide.
|
|
585
|
+
</Typography>
|
|
586
|
+
</HelpSection>
|
|
587
|
+
|
|
558
588
|
<HelpSection title="Execution Status">
|
|
559
589
|
<Typography variant="omega">
|
|
560
590
|
The Status tab in Sync shows the current state of all profiles:
|
|
@@ -566,11 +596,103 @@ http://localhost:1337</CodeBlock>
|
|
|
566
596
|
<li><Typography variant="omega"><strong>Next Run</strong> - When scheduled sync will run next</Typography></li>
|
|
567
597
|
<li><Typography variant="omega"><strong>Status</strong> - Running or Idle</Typography></li>
|
|
568
598
|
</ul>
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
599
|
+
</HelpSection>
|
|
600
|
+
</Box>
|
|
601
|
+
</Tabs.Content>
|
|
602
|
+
|
|
603
|
+
{/* Bulk Transfer Tab */}
|
|
604
|
+
<Tabs.Content value="bulk-transfer">
|
|
605
|
+
<Box paddingTop={4}>
|
|
606
|
+
<HelpSection title="What is Bulk Transfer?">
|
|
607
|
+
<Typography variant="omega">
|
|
608
|
+
<strong>Bulk Transfer</strong> is a dedicated top-level tab for one-click, full-scope data
|
|
609
|
+
movement between the local and remote Strapi instances. It is the fastest way to perform an
|
|
610
|
+
initial migration, a full refresh, or a disaster-recovery style mirror, without having to
|
|
611
|
+
orchestrate individual Sync Profiles.
|
|
612
|
+
</Typography>
|
|
613
|
+
<Typography variant="omega" paddingTop={2}>
|
|
614
|
+
Unlike the Sync tab — which is profile-driven and can be scheduled or live — Bulk Transfer is
|
|
615
|
+
an explicit, user-driven run: pick a direction, pick the scopes, pick (or deselect) the chunks,
|
|
616
|
+
and start. It is designed to be safely pausable, resumable, and inspectable.
|
|
617
|
+
</Typography>
|
|
618
|
+
</HelpSection>
|
|
619
|
+
|
|
620
|
+
<HelpSection title="Direction & Scopes">
|
|
621
|
+
<ul style={{ paddingLeft: '20px', marginTop: '8px', lineHeight: '1.8' }}>
|
|
622
|
+
<li><Typography variant="omega"><strong>Direction</strong> — <em>Full Pull</em> (remote → local) or <em>Full Push</em> (local → remote). In <strong>Single-side</strong> mode only Full Pull is available.</Typography></li>
|
|
623
|
+
<li><Typography variant="omega"><strong>Content</strong> — All user-defined <code>api::*</code> content types.</Typography></li>
|
|
624
|
+
<li><Typography variant="omega"><strong>Media</strong> — Files and morph links via the active media profiles.</Typography></li>
|
|
625
|
+
<li><Typography variant="omega"><strong>Strapi Users</strong> — <code>plugin::users-permissions.user</code>.</Typography></li>
|
|
626
|
+
<li><Typography variant="omega"><strong>Admin Users</strong> — <code>admin::user</code> (experimental; use with care).</Typography></li>
|
|
627
|
+
<li><Typography variant="omega"><strong>Apply deletions</strong> — Destination removes items missing on source. Use with care, especially with user scopes.</Typography></li>
|
|
628
|
+
<li><Typography variant="omega"><strong>Conflict strategy</strong> — Latest updated wins (default), Local wins, or Remote wins.</Typography></li>
|
|
629
|
+
<li><Typography variant="omega"><strong>Auto-continue</strong> — Run all selected chunks back-to-back, or pause between chunks to review.</Typography></li>
|
|
630
|
+
</ul>
|
|
631
|
+
</HelpSection>
|
|
632
|
+
|
|
633
|
+
<HelpSection title="Selectable Chunks & Page-level Progress">
|
|
634
|
+
<Typography variant="omega">
|
|
635
|
+
A bulk job expands into one <strong>chunk</strong> per content type and one chunk per active
|
|
636
|
+
media profile (plus any selected user scopes). In the Run Transfer view you can:
|
|
637
|
+
</Typography>
|
|
638
|
+
<ul style={{ paddingLeft: '20px', marginTop: '8px', lineHeight: '1.8' }}>
|
|
639
|
+
<li><Typography variant="omega">Toggle individual chunks, or use <strong>Select / Deselect All</strong> to run only a subset.</Typography></li>
|
|
640
|
+
<li><Typography variant="omega">Watch per-chunk <strong>page progress</strong> (<code>page X of Y</code>, records pushed / pulled, errors) updated in near real-time.</Typography></li>
|
|
641
|
+
<li><Typography variant="omega">See consolidated run stats (total pushed, pulled, errors, pages done) and a spinner while the job is actively processing.</Typography></li>
|
|
642
|
+
<li><Typography variant="omega">Use <strong>Pause</strong> to stop after the current page, <strong>Resume</strong> to continue from the exact saved cursor, <strong>Run Next Chunk</strong> in manual mode, or <strong>Cancel</strong> to stop the run without losing the progress already made.</Typography></li>
|
|
643
|
+
</ul>
|
|
644
|
+
<Typography variant="pi" textColor="neutral600" paddingTop={2}>
|
|
645
|
+
Because transfer is paginated end-to-end using the global <code>syncPageSize</code>, memory
|
|
646
|
+
stays bounded even on very large datasets.
|
|
647
|
+
</Typography>
|
|
648
|
+
</HelpSection>
|
|
649
|
+
|
|
650
|
+
<HelpSection title="Pause, Resume & Cancel">
|
|
651
|
+
<Typography variant="omega">
|
|
652
|
+
Bulk Transfer treats <strong>pause</strong>, <strong>cancel</strong>, and <strong>resume</strong> as
|
|
653
|
+
first-class operations:
|
|
654
|
+
</Typography>
|
|
655
|
+
<ul style={{ paddingLeft: '20px', marginTop: '8px', lineHeight: '1.8' }}>
|
|
656
|
+
<li><Typography variant="omega"><strong>Pause</strong> — Stops after the current page finishes. The chunk cursor, page number, and counters are persisted, so resume continues from the same place.</Typography></li>
|
|
657
|
+
<li><Typography variant="omega"><strong>Cancel</strong> — Distinct from pause: the run is marked cancelled and the in-flight chunk is preserved as <em>paused</em> with accumulated progress, not falsely reported as successful.</Typography></li>
|
|
658
|
+
<li><Typography variant="omega"><strong>Resume</strong> — Re-opens the run exactly where it stopped: same direction, scopes, deletion flag, auto-continue, conflict strategy, and the same chunk selection, with cursor/page restored.</Typography></li>
|
|
659
|
+
</ul>
|
|
660
|
+
</HelpSection>
|
|
661
|
+
|
|
662
|
+
<HelpSection title="Previous Runs & History">
|
|
663
|
+
<Typography variant="omega">
|
|
664
|
+
Every bulk run — completed, paused, cancelled, or failed — is recorded in the
|
|
665
|
+
<strong> Previous Runs</strong> sub-tab. History is persisted in the plugin store (not just
|
|
666
|
+
in memory), with serialized writes so concurrent updates from the polling loop and control
|
|
667
|
+
actions never clobber each other.
|
|
668
|
+
</Typography>
|
|
669
|
+
<ul style={{ paddingLeft: '20px', marginTop: '8px', lineHeight: '1.8' }}>
|
|
670
|
+
<li><Typography variant="omega"><strong>Expand</strong> a row to inspect per-chunk status, page progress, record counts, and error messages for that run.</Typography></li>
|
|
671
|
+
<li><Typography variant="omega"><strong>Restart</strong> — Re-runs the same configuration from scratch (fresh cursor) while keeping the original entry in history.</Typography></li>
|
|
672
|
+
<li><Typography variant="omega"><strong>Resume</strong> — For paused / cancelled / incomplete runs, rehydrates the Run Transfer tab with the exact prior state (direction, scopes, options, chunk selection) and continues from the saved cursor and page.</Typography></li>
|
|
673
|
+
<li><Typography variant="omega"><strong>Clear history</strong> — Removes the stored history entries.</Typography></li>
|
|
674
|
+
</ul>
|
|
675
|
+
<Typography variant="omega" paddingTop={2}>
|
|
676
|
+
Each run is also recorded as a Stats report with <code>runType = bulk_transfer</code>, so the
|
|
677
|
+
before/after snapshots are available in the Stats tab.
|
|
678
|
+
</Typography>
|
|
679
|
+
</HelpSection>
|
|
680
|
+
|
|
681
|
+
<HelpSection title="Bulk Transfer vs Sync">
|
|
682
|
+
<ul style={{ paddingLeft: '20px', marginTop: '8px', lineHeight: '1.8' }}>
|
|
683
|
+
<li><Typography variant="omega"><strong>Sync</strong> is <em>profile-driven</em>: on-demand, scheduled, or live; intended for ongoing incremental synchronization with field-level policies.</Typography></li>
|
|
684
|
+
<li><Typography variant="omega"><strong>Bulk Transfer</strong> is <em>operation-driven</em>: an explicit full pull/push, chunked and resumable, intended for migrations, backfills, and full refreshes.</Typography></li>
|
|
685
|
+
</ul>
|
|
686
|
+
<Typography variant="pi" textColor="warning600" paddingTop={2}>
|
|
687
|
+
Note: The active in-memory job registry is cleared on Strapi restart, but the run history
|
|
688
|
+
(and its saved cursor/page/selection) is persisted — so after a restart you can open Previous
|
|
689
|
+
Runs and <strong>Resume</strong> the interrupted job.
|
|
690
|
+
</Typography>
|
|
691
|
+
</HelpSection>
|
|
692
|
+
</Box>
|
|
693
|
+
</Tabs.Content>
|
|
572
694
|
|
|
573
|
-
|
|
695
|
+
{/* Media Tab */}
|
|
574
696
|
<Tabs.Content value="media">
|
|
575
697
|
<Box paddingTop={4}>
|
|
576
698
|
<HelpSection title="Pagination for Large Datasets">
|
|
@@ -632,6 +754,10 @@ http://localhost:1337</CodeBlock>
|
|
|
632
754
|
<Typography variant="omega">
|
|
633
755
|
Each profile can sync two distinct aspects of media:
|
|
634
756
|
</Typography>
|
|
757
|
+
<Typography variant="omega" paddingTop={2}>
|
|
758
|
+
For entity-linked media consistency, this plugin also syncs media morph links using <strong>documentId-based remapping</strong>
|
|
759
|
+
(file documentId and related entity documentId are resolved to local numeric ids before insert).
|
|
760
|
+
</Typography>
|
|
635
761
|
<Box background="neutral100" padding={4} hasRadius marginTop={2} marginBottom={2}>
|
|
636
762
|
<Typography variant="sigma" textColor="neutral800">DB Rows (Metadata)</Typography>
|
|
637
763
|
<Typography variant="omega" paddingTop={1}>
|
|
@@ -652,6 +778,11 @@ http://localhost:1337</CodeBlock>
|
|
|
652
778
|
If you only need metadata references (e.g., both sides use the same S3 bucket), you can
|
|
653
779
|
sync DB rows only.
|
|
654
780
|
</Typography>
|
|
781
|
+
<Typography variant="pi" textColor="neutral600" paddingTop={2}>
|
|
782
|
+
Note: Strapi components, repeatable components, and dynamic zones are already tracked by
|
|
783
|
+
document service sync using stable documentIds, so they do not require id-to-documentId remapping
|
|
784
|
+
like upload morph tables do.
|
|
785
|
+
</Typography>
|
|
655
786
|
</HelpSection>
|
|
656
787
|
|
|
657
788
|
<HelpSection title="URL strategy (HTTP)">
|
|
@@ -720,6 +851,61 @@ http://localhost:1337</CodeBlock>
|
|
|
720
851
|
</Box>
|
|
721
852
|
</Tabs.Content>
|
|
722
853
|
|
|
854
|
+
{/* Stats Tab */}
|
|
855
|
+
<Tabs.Content value="stats">
|
|
856
|
+
<Box paddingTop={4}>
|
|
857
|
+
<HelpSection title="Database Stats Overview">
|
|
858
|
+
<Typography variant="omega">
|
|
859
|
+
The Stats tab compares local and remote data state per content type, including content
|
|
860
|
+
entries, media files, and media morph (relation) links. The view is split into two
|
|
861
|
+
sub-tabs: <strong>Current Snapshot</strong> and <strong>Run Reports</strong>.
|
|
862
|
+
</Typography>
|
|
863
|
+
</HelpSection>
|
|
864
|
+
|
|
865
|
+
<HelpSection title="Current Snapshot Tab">
|
|
866
|
+
<Typography variant="omega">
|
|
867
|
+
Shows the latest live counts and newest timestamps per content type, with the newest
|
|
868
|
+
side (local / remote / equal) highlighted. Use the controls above the table to drill in:
|
|
869
|
+
</Typography>
|
|
870
|
+
<ul style={{ paddingLeft: '20px', marginTop: '8px', lineHeight: '1.8' }}>
|
|
871
|
+
<li><Typography variant="omega"><strong>Search</strong> - Filter rows by UID substring.</Typography></li>
|
|
872
|
+
<li><Typography variant="omega"><strong>Type</strong> - Filter by Content, Media, or Media Morph.</Typography></li>
|
|
873
|
+
<li><Typography variant="omega"><strong>Newest side</strong> - Show only rows where local, remote, or both are newest.</Typography></li>
|
|
874
|
+
<li><Typography variant="omega"><strong>Page size / pagination</strong> - Browse large snapshots without scrolling.</Typography></li>
|
|
875
|
+
</ul>
|
|
876
|
+
</HelpSection>
|
|
877
|
+
|
|
878
|
+
<HelpSection title="Run Reports Tab (Before vs After)">
|
|
879
|
+
<Typography variant="omega">
|
|
880
|
+
Before every sync run, the plugin captures a pre-run snapshot. After the run completes,
|
|
881
|
+
it captures a post-run snapshot and stores both in a report so you can review sync impact
|
|
882
|
+
and trends over time.
|
|
883
|
+
</Typography>
|
|
884
|
+
<ul style={{ paddingLeft: '20px', marginTop: '8px', lineHeight: '1.8' }}>
|
|
885
|
+
<li><Typography variant="omega"><strong>Status filter</strong> - Show all runs, or only success / failed.</Typography></li>
|
|
886
|
+
<li><Typography variant="omega"><strong>Page size / pagination</strong> - Reports are paginated server-side.</Typography></li>
|
|
887
|
+
<li><Typography variant="omega"><strong>Show details</strong> - Expand a report card to see the before/after row tables (first {25} rows per side).</Typography></li>
|
|
888
|
+
</ul>
|
|
889
|
+
</HelpSection>
|
|
890
|
+
|
|
891
|
+
<HelpSection title="Top Action Row: Refresh, Clear & Retention">
|
|
892
|
+
<Typography variant="omega">
|
|
893
|
+
The top of the Stats tab exposes the controls that apply to both sub-tabs:
|
|
894
|
+
</Typography>
|
|
895
|
+
<ul style={{ paddingLeft: '20px', marginTop: '8px', lineHeight: '1.8' }}>
|
|
896
|
+
<li><Typography variant="omega"><strong>Refresh Stats</strong> - Reload the snapshot and reports.</Typography></li>
|
|
897
|
+
<li><Typography variant="omega"><strong>Clear Logs</strong> - Remove stored sync logs.</Typography></li>
|
|
898
|
+
<li><Typography variant="omega"><strong>Clear Stats Reports</strong> - Remove all before/after run reports.</Typography></li>
|
|
899
|
+
<li><Typography variant="omega"><strong>Max Logs / Max Reports</strong> - Retention limits; older entries are pruned when exceeded.</Typography></li>
|
|
900
|
+
<li><Typography variant="omega"><strong>Save & Apply Retention</strong> - Persists the limits and immediately prunes old data.</Typography></li>
|
|
901
|
+
</ul>
|
|
902
|
+
<Typography variant="pi" textColor="neutral600" paddingTop={2}>
|
|
903
|
+
Retention is also enforced automatically after each sync run.
|
|
904
|
+
</Typography>
|
|
905
|
+
</HelpSection>
|
|
906
|
+
</Box>
|
|
907
|
+
</Tabs.Content>
|
|
908
|
+
|
|
723
909
|
{/* Enforcement Tab */}
|
|
724
910
|
<Tabs.Content value="enforcement">
|
|
725
911
|
<Box paddingTop={4}>
|
|
@@ -897,10 +1083,10 @@ http://localhost:1337</CodeBlock>
|
|
|
897
1083
|
</Box>
|
|
898
1084
|
|
|
899
1085
|
<Box background="danger100" padding={4} hasRadius marginBottom={4}>
|
|
900
|
-
<Typography variant="sigma" textColor="danger700">401 Unauthorized</Typography>
|
|
1086
|
+
<Typography variant="sigma" textColor="danger700">401 Unauthorized / 403 Forbidden</Typography>
|
|
901
1087
|
<Typography variant="omega" paddingTop={1}>
|
|
902
|
-
The API token is invalid or
|
|
903
|
-
|
|
1088
|
+
The API token is invalid, expired, or missing required permissions. Generate a new token on the remote
|
|
1089
|
+
server and ensure it can access the synced content types (and Upload permissions for media sync).
|
|
904
1090
|
</Typography>
|
|
905
1091
|
</Box>
|
|
906
1092
|
|
|
@@ -913,10 +1099,10 @@ http://localhost:1337</CodeBlock>
|
|
|
913
1099
|
</Box>
|
|
914
1100
|
|
|
915
1101
|
<Box background="danger100" padding={4} hasRadius marginBottom={4}>
|
|
916
|
-
<Typography variant="sigma" textColor="danger700">Content type not found on remote</Typography>
|
|
1102
|
+
<Typography variant="sigma" textColor="danger700">Content type endpoint not found on remote</Typography>
|
|
917
1103
|
<Typography variant="omega" paddingTop={1}>
|
|
918
|
-
The content type exists locally but
|
|
919
|
-
have matching content type definitions.
|
|
1104
|
+
The content type exists locally but the remote REST endpoint is missing or named differently.
|
|
1105
|
+
Ensure both instances have matching content type definitions and API routes are enabled.
|
|
920
1106
|
</Typography>
|
|
921
1107
|
</Box>
|
|
922
1108
|
|
|
@@ -400,6 +400,13 @@ const MediaTab = () => {
|
|
|
400
400
|
{status?.lastResult && (
|
|
401
401
|
<Box paddingTop={3} background="neutral0" padding={4} hasRadius shadow="tableShadow">
|
|
402
402
|
<Typography variant="sigma">Last Run Result</Typography>
|
|
403
|
+
{(status.lastResult.morphLinksApplied !== undefined || status.lastResult.morphLinksSkipped !== undefined) && (
|
|
404
|
+
<Box paddingTop={2} paddingBottom={2}>
|
|
405
|
+
<Typography variant="omega" textColor="neutral700">
|
|
406
|
+
Morph links synced: applied {status.lastResult.morphLinksApplied || 0}, skipped {status.lastResult.morphLinksSkipped || 0}
|
|
407
|
+
</Typography>
|
|
408
|
+
</Box>
|
|
409
|
+
)}
|
|
403
410
|
<Typography variant="pi" style={{ fontFamily: 'monospace', whiteSpace: 'pre-wrap' }}>
|
|
404
411
|
{JSON.stringify(status.lastResult, null, 2)}
|
|
405
412
|
</Typography>
|