strapi-content-sync-pro 1.0.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/LICENSE +21 -0
- package/README.md +206 -0
- package/admin/src/components/ConfigTab.jsx +1038 -0
- package/admin/src/components/ContentTypesTab.jsx +160 -0
- package/admin/src/components/HelpTab.jsx +945 -0
- package/admin/src/components/LogsTab.jsx +136 -0
- package/admin/src/components/MediaTab.jsx +557 -0
- package/admin/src/components/SyncProfilesTab.jsx +715 -0
- package/admin/src/components/SyncTab.jsx +988 -0
- package/admin/src/index.js +31 -0
- package/admin/src/pages/App/index.jsx +129 -0
- package/admin/src/pluginId.js +3 -0
- package/package.json +84 -0
- package/server/src/bootstrap.js +151 -0
- package/server/src/config/index.js +5 -0
- package/server/src/content-types/index.js +7 -0
- package/server/src/content-types/sync-log/schema.json +24 -0
- package/server/src/controllers/alerts.js +59 -0
- package/server/src/controllers/config.js +292 -0
- package/server/src/controllers/content-type-discovery.js +9 -0
- package/server/src/controllers/dependencies.js +109 -0
- package/server/src/controllers/index.js +29 -0
- package/server/src/controllers/ping.js +7 -0
- package/server/src/controllers/sync-config.js +26 -0
- package/server/src/controllers/sync-enforcement.js +323 -0
- package/server/src/controllers/sync-execution.js +134 -0
- package/server/src/controllers/sync-log.js +18 -0
- package/server/src/controllers/sync-media.js +158 -0
- package/server/src/controllers/sync-profiles.js +182 -0
- package/server/src/controllers/sync.js +31 -0
- package/server/src/destroy.js +7 -0
- package/server/src/index.js +21 -0
- package/server/src/middlewares/verify-signature.js +32 -0
- package/server/src/register.js +7 -0
- package/server/src/routes/index.js +111 -0
- package/server/src/services/alerts.js +437 -0
- package/server/src/services/config.js +68 -0
- package/server/src/services/content-type-discovery.js +41 -0
- package/server/src/services/dependency-resolver.js +284 -0
- package/server/src/services/index.js +30 -0
- package/server/src/services/ping.js +7 -0
- package/server/src/services/sync-config.js +45 -0
- package/server/src/services/sync-enforcement.js +362 -0
- package/server/src/services/sync-execution.js +541 -0
- package/server/src/services/sync-log.js +56 -0
- package/server/src/services/sync-media.js +963 -0
- package/server/src/services/sync-profiles.js +380 -0
- package/server/src/services/sync.js +248 -0
- package/server/src/utils/applier.js +89 -0
- package/server/src/utils/comparator.js +83 -0
- package/server/src/utils/fetcher.js +142 -0
- package/server/src/utils/hmac.js +37 -0
- package/server/src/utils/pagination.js +51 -0
- package/server/src/utils/sync-guard.js +29 -0
- package/server/src/utils/sync-id.js +16 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const PLUGIN_ID = 'strapi-content-sync-pro';
|
|
4
|
+
|
|
5
|
+
module.exports = ({ strapi }) => ({
|
|
6
|
+
/**
|
|
7
|
+
* GET /enforcement/settings
|
|
8
|
+
* Get enforcement settings
|
|
9
|
+
*/
|
|
10
|
+
async getSettings(ctx) {
|
|
11
|
+
try {
|
|
12
|
+
const settings = await strapi.plugin(PLUGIN_ID).service('syncEnforcement').getSettings();
|
|
13
|
+
ctx.body = { data: settings };
|
|
14
|
+
} catch (err) {
|
|
15
|
+
ctx.throw(500, err.message);
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* PUT /enforcement/settings
|
|
21
|
+
* Update enforcement settings
|
|
22
|
+
*/
|
|
23
|
+
async updateSettings(ctx) {
|
|
24
|
+
const body = ctx.request.body;
|
|
25
|
+
try {
|
|
26
|
+
const settings = await strapi.plugin(PLUGIN_ID).service('syncEnforcement').updateSettings(body);
|
|
27
|
+
ctx.body = { data: settings };
|
|
28
|
+
} catch (err) {
|
|
29
|
+
ctx.throw(400, err.message);
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* GET /enforcement/local-info
|
|
35
|
+
* Get local version and schema info
|
|
36
|
+
*/
|
|
37
|
+
async getLocalInfo(ctx) {
|
|
38
|
+
try {
|
|
39
|
+
const service = strapi.plugin(PLUGIN_ID).service('syncEnforcement');
|
|
40
|
+
const versionInfo = service.getLocalVersionInfo();
|
|
41
|
+
ctx.body = { data: versionInfo };
|
|
42
|
+
} catch (err) {
|
|
43
|
+
ctx.throw(500, err.message);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* GET /enforcement/remote-info
|
|
49
|
+
* Get remote server info for connection test
|
|
50
|
+
*/
|
|
51
|
+
async getRemoteInfo(ctx) {
|
|
52
|
+
try {
|
|
53
|
+
const configService = strapi.plugin(PLUGIN_ID).service('config');
|
|
54
|
+
const config = await configService.getConfig({ safe: false });
|
|
55
|
+
|
|
56
|
+
if (!config || !config.baseUrl) {
|
|
57
|
+
return ctx.throw(400, 'Remote server not configured');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const url = `${config.baseUrl}/api/${PLUGIN_ID}/enforcement/local-info`;
|
|
61
|
+
const response = await fetch(url, {
|
|
62
|
+
headers: {
|
|
63
|
+
Authorization: `Bearer ${config.apiToken}`,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
let errorDetail = '';
|
|
69
|
+
try {
|
|
70
|
+
const errorBody = await response.json();
|
|
71
|
+
errorDetail = errorBody?.error?.message || JSON.stringify(errorBody);
|
|
72
|
+
} catch {
|
|
73
|
+
errorDetail = await response.text();
|
|
74
|
+
}
|
|
75
|
+
return ctx.throw(response.status, `Remote server returned ${response.status}: ${errorDetail}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const data = await response.json();
|
|
79
|
+
ctx.body = { data: data.data };
|
|
80
|
+
} catch (err) {
|
|
81
|
+
if (err.status) {
|
|
82
|
+
throw err; // Re-throw Koa errors
|
|
83
|
+
}
|
|
84
|
+
ctx.throw(500, err.message || 'Failed to get remote info');
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* GET /enforcement/check/:type
|
|
90
|
+
* Run individual diagnostic check (schema, version, time)
|
|
91
|
+
*/
|
|
92
|
+
async runDiagnosticCheck(ctx) {
|
|
93
|
+
const { type } = ctx.params;
|
|
94
|
+
const validTypes = ['schema', 'version', 'time'];
|
|
95
|
+
|
|
96
|
+
if (!validTypes.includes(type)) {
|
|
97
|
+
return ctx.throw(400, `Invalid check type. Must be one of: ${validTypes.join(', ')}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
const configService = strapi.plugin(PLUGIN_ID).service('config');
|
|
102
|
+
const enforcementService = strapi.plugin(PLUGIN_ID).service('syncEnforcement');
|
|
103
|
+
const syncConfigService = strapi.plugin(PLUGIN_ID).service('syncConfig');
|
|
104
|
+
|
|
105
|
+
const config = await configService.getConfig({ safe: false });
|
|
106
|
+
|
|
107
|
+
if (!config || !config.baseUrl) {
|
|
108
|
+
return ctx.body = {
|
|
109
|
+
data: {
|
|
110
|
+
passed: false,
|
|
111
|
+
error: 'Remote server not configured',
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Get remote info
|
|
117
|
+
let remoteInfo;
|
|
118
|
+
try {
|
|
119
|
+
const response = await fetch(`${config.baseUrl}/api/${PLUGIN_ID}/enforcement/local-info`, {
|
|
120
|
+
headers: {
|
|
121
|
+
Authorization: `Bearer ${config.apiToken}`,
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
if (!response.ok) {
|
|
126
|
+
return ctx.body = {
|
|
127
|
+
data: {
|
|
128
|
+
passed: false,
|
|
129
|
+
error: `Cannot reach remote server (${response.status})`,
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
remoteInfo = (await response.json()).data;
|
|
135
|
+
} catch (err) {
|
|
136
|
+
return ctx.body = {
|
|
137
|
+
data: {
|
|
138
|
+
passed: false,
|
|
139
|
+
error: `Connection failed: ${err.message}`,
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const localInfo = enforcementService.getLocalVersionInfo();
|
|
145
|
+
const settings = await enforcementService.getSettings();
|
|
146
|
+
|
|
147
|
+
let result = { passed: true, details: {} };
|
|
148
|
+
|
|
149
|
+
switch (type) {
|
|
150
|
+
case 'version': {
|
|
151
|
+
const versionResult = enforcementService.compareVersions(
|
|
152
|
+
localInfo.strapiVersion,
|
|
153
|
+
remoteInfo.strapiVersion,
|
|
154
|
+
settings.allowedVersionDrift
|
|
155
|
+
);
|
|
156
|
+
result = {
|
|
157
|
+
passed: versionResult.compatible,
|
|
158
|
+
details: {
|
|
159
|
+
localVersion: localInfo.strapiVersion,
|
|
160
|
+
remoteVersion: remoteInfo.strapiVersion,
|
|
161
|
+
driftLevel: versionResult.driftLevel,
|
|
162
|
+
message: versionResult.message,
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
case 'time': {
|
|
169
|
+
const localTime = new Date();
|
|
170
|
+
const remoteTime = new Date(remoteInfo.serverTime);
|
|
171
|
+
const driftMs = Math.abs(localTime - remoteTime);
|
|
172
|
+
const passed = driftMs <= settings.maxTimeDriftMs;
|
|
173
|
+
|
|
174
|
+
result = {
|
|
175
|
+
passed,
|
|
176
|
+
details: {
|
|
177
|
+
localTime: localTime.toISOString(),
|
|
178
|
+
remoteTime: remoteInfo.serverTime,
|
|
179
|
+
driftMs,
|
|
180
|
+
maxAllowed: settings.maxTimeDriftMs,
|
|
181
|
+
message: passed
|
|
182
|
+
? `Time drift of ${driftMs}ms is within allowed limit`
|
|
183
|
+
: `Time drift of ${driftMs}ms exceeds limit of ${settings.maxTimeDriftMs}ms`,
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
case 'schema': {
|
|
190
|
+
const syncConfig = await syncConfigService.getSyncConfig();
|
|
191
|
+
const enabledTypes = (syncConfig.contentTypes || [])
|
|
192
|
+
.filter((ct) => ct.enabled !== false)
|
|
193
|
+
.map((ct) => ct.uid);
|
|
194
|
+
const mismatches = [];
|
|
195
|
+
|
|
196
|
+
if (enabledTypes.length === 0) {
|
|
197
|
+
result = {
|
|
198
|
+
passed: true,
|
|
199
|
+
details: {
|
|
200
|
+
checkedTypes: [],
|
|
201
|
+
mismatches: [],
|
|
202
|
+
matchMode: settings.schemaMatchMode,
|
|
203
|
+
message: 'No content types enabled for sync',
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
for (const uid of enabledTypes) {
|
|
210
|
+
try {
|
|
211
|
+
// Get remote schema
|
|
212
|
+
const schemaResponse = await fetch(
|
|
213
|
+
`${config.baseUrl}/api/${PLUGIN_ID}/enforcement/schema/${encodeURIComponent(uid)}`,
|
|
214
|
+
{
|
|
215
|
+
headers: {
|
|
216
|
+
Authorization: `Bearer ${config.apiToken}`,
|
|
217
|
+
},
|
|
218
|
+
}
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
if (!schemaResponse.ok) {
|
|
222
|
+
mismatches.push({
|
|
223
|
+
type: uid,
|
|
224
|
+
reason: `Not found on remote (${schemaResponse.status})`,
|
|
225
|
+
});
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const remoteSchema = (await schemaResponse.json()).data?.schema;
|
|
230
|
+
const localSchema = enforcementService.getLocalSchema(uid);
|
|
231
|
+
|
|
232
|
+
if (!localSchema) {
|
|
233
|
+
mismatches.push({
|
|
234
|
+
type: uid,
|
|
235
|
+
reason: 'Not found locally',
|
|
236
|
+
});
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Compare schemas
|
|
241
|
+
const comparison = enforcementService.compareSchemas(localSchema, remoteSchema, settings.schemaMatchMode);
|
|
242
|
+
if (!comparison.compatible) {
|
|
243
|
+
mismatches.push({
|
|
244
|
+
type: uid,
|
|
245
|
+
reason: comparison.differences?.join(', ') || 'Schema mismatch',
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
} catch (err) {
|
|
249
|
+
mismatches.push({
|
|
250
|
+
type: uid,
|
|
251
|
+
reason: err.message,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
result = {
|
|
257
|
+
passed: mismatches.length === 0,
|
|
258
|
+
details: {
|
|
259
|
+
checkedTypes: enabledTypes,
|
|
260
|
+
mismatches,
|
|
261
|
+
matchMode: settings.schemaMatchMode,
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
ctx.body = { data: result };
|
|
269
|
+
} catch (err) {
|
|
270
|
+
ctx.throw(500, err.message);
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* GET /enforcement/schema/:uid
|
|
276
|
+
* Get local schema for a content type
|
|
277
|
+
*/
|
|
278
|
+
async getLocalSchema(ctx) {
|
|
279
|
+
const { uid } = ctx.params;
|
|
280
|
+
try {
|
|
281
|
+
const schema = strapi.plugin(PLUGIN_ID).service('syncEnforcement').getLocalSchema(uid);
|
|
282
|
+
if (!schema) {
|
|
283
|
+
return ctx.throw(404, `Content type "${uid}" not found`);
|
|
284
|
+
}
|
|
285
|
+
ctx.body = { data: { uid, schema } };
|
|
286
|
+
} catch (err) {
|
|
287
|
+
ctx.throw(500, err.message);
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* POST /enforcement/check
|
|
293
|
+
* Run pre-sync enforcement checks
|
|
294
|
+
*/
|
|
295
|
+
async runChecks(ctx) {
|
|
296
|
+
const { contentType, remoteInfo } = ctx.request.body;
|
|
297
|
+
if (!contentType) {
|
|
298
|
+
return ctx.throw(400, 'contentType is required');
|
|
299
|
+
}
|
|
300
|
+
if (!remoteInfo) {
|
|
301
|
+
return ctx.throw(400, 'remoteInfo is required');
|
|
302
|
+
}
|
|
303
|
+
try {
|
|
304
|
+
const results = await strapi.plugin(PLUGIN_ID).service('syncEnforcement').runPreSyncChecks(contentType, remoteInfo);
|
|
305
|
+
ctx.body = { data: results };
|
|
306
|
+
} catch (err) {
|
|
307
|
+
ctx.throw(400, err.message);
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* GET /enforcement/summary
|
|
313
|
+
* Get enforcement summary for UI
|
|
314
|
+
*/
|
|
315
|
+
async getSummary(ctx) {
|
|
316
|
+
try {
|
|
317
|
+
const summary = await strapi.plugin(PLUGIN_ID).service('syncEnforcement').getEnforcementSummary();
|
|
318
|
+
ctx.body = { data: summary };
|
|
319
|
+
} catch (err) {
|
|
320
|
+
ctx.throw(500, err.message);
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
});
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const PLUGIN_ID = 'strapi-content-sync-pro';
|
|
4
|
+
|
|
5
|
+
module.exports = ({ strapi }) => ({
|
|
6
|
+
/**
|
|
7
|
+
* GET /sync-execution/settings
|
|
8
|
+
* Get all execution settings
|
|
9
|
+
*/
|
|
10
|
+
async getSettings(ctx) {
|
|
11
|
+
try {
|
|
12
|
+
const settings = await strapi.plugin(PLUGIN_ID).service('syncExecution').getExecutionSettings();
|
|
13
|
+
ctx.body = { data: settings };
|
|
14
|
+
} catch (err) {
|
|
15
|
+
ctx.throw(500, err.message);
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* GET /sync-execution/settings/:profileId
|
|
21
|
+
* Get execution settings for a profile
|
|
22
|
+
*/
|
|
23
|
+
async getProfileSettings(ctx) {
|
|
24
|
+
const { profileId } = ctx.params;
|
|
25
|
+
try {
|
|
26
|
+
const settings = await strapi.plugin(PLUGIN_ID).service('syncExecution').getProfileExecutionSettings(profileId);
|
|
27
|
+
ctx.body = { data: settings };
|
|
28
|
+
} catch (err) {
|
|
29
|
+
ctx.throw(500, err.message);
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* PUT /sync-execution/settings/:profileId
|
|
35
|
+
* Update execution settings for a profile
|
|
36
|
+
*/
|
|
37
|
+
async updateProfileSettings(ctx) {
|
|
38
|
+
const { profileId } = ctx.params;
|
|
39
|
+
const body = ctx.request.body;
|
|
40
|
+
try {
|
|
41
|
+
const settings = await strapi.plugin(PLUGIN_ID).service('syncExecution').setProfileExecutionSettings(profileId, body);
|
|
42
|
+
ctx.body = { data: settings };
|
|
43
|
+
} catch (err) {
|
|
44
|
+
ctx.throw(400, err.message);
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* GET /sync-execution/global-settings
|
|
50
|
+
* Get global execution settings
|
|
51
|
+
*/
|
|
52
|
+
async getGlobalSettings(ctx) {
|
|
53
|
+
try {
|
|
54
|
+
const settings = await strapi.plugin(PLUGIN_ID).service('syncExecution').getGlobalSettings();
|
|
55
|
+
ctx.body = { data: settings };
|
|
56
|
+
} catch (err) {
|
|
57
|
+
ctx.throw(500, err.message);
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* PUT /sync-execution/global-settings
|
|
63
|
+
* Update global execution settings
|
|
64
|
+
*/
|
|
65
|
+
async updateGlobalSettings(ctx) {
|
|
66
|
+
const body = ctx.request.body;
|
|
67
|
+
try {
|
|
68
|
+
const settings = await strapi.plugin(PLUGIN_ID).service('syncExecution').setGlobalSettings(body);
|
|
69
|
+
ctx.body = { data: settings };
|
|
70
|
+
} catch (err) {
|
|
71
|
+
ctx.throw(400, err.message);
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* POST /sync-execution/execute/:profileId
|
|
77
|
+
* Execute a profile on-demand
|
|
78
|
+
*/
|
|
79
|
+
async executeProfile(ctx) {
|
|
80
|
+
const { profileId } = ctx.params;
|
|
81
|
+
const options = ctx.request.body || {};
|
|
82
|
+
try {
|
|
83
|
+
const result = await strapi.plugin(PLUGIN_ID).service('syncExecution').executeProfile(profileId, options);
|
|
84
|
+
ctx.body = { data: result };
|
|
85
|
+
} catch (err) {
|
|
86
|
+
ctx.throw(400, err.message);
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* POST /sync-execution/execute-batch
|
|
92
|
+
* Execute multiple profiles
|
|
93
|
+
*/
|
|
94
|
+
async executeProfiles(ctx) {
|
|
95
|
+
const { profileIds, options } = ctx.request.body;
|
|
96
|
+
if (!profileIds || !Array.isArray(profileIds)) {
|
|
97
|
+
return ctx.throw(400, 'profileIds array is required');
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
const result = await strapi.plugin(PLUGIN_ID).service('syncExecution').executeProfiles(profileIds, options || {});
|
|
101
|
+
ctx.body = { data: result };
|
|
102
|
+
} catch (err) {
|
|
103
|
+
ctx.throw(400, err.message);
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* POST /sync-execution/execute-content-type/:uid
|
|
109
|
+
* Execute active profile for a content type
|
|
110
|
+
*/
|
|
111
|
+
async executeContentType(ctx) {
|
|
112
|
+
const { uid } = ctx.params;
|
|
113
|
+
const options = ctx.request.body || {};
|
|
114
|
+
try {
|
|
115
|
+
const result = await strapi.plugin(PLUGIN_ID).service('syncExecution').executeContentType(uid, options);
|
|
116
|
+
ctx.body = { data: result };
|
|
117
|
+
} catch (err) {
|
|
118
|
+
ctx.throw(400, err.message);
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* GET /sync-execution/status
|
|
124
|
+
* Get execution status for all profiles
|
|
125
|
+
*/
|
|
126
|
+
async getStatus(ctx) {
|
|
127
|
+
try {
|
|
128
|
+
const status = await strapi.plugin(PLUGIN_ID).service('syncExecution').getExecutionStatus();
|
|
129
|
+
ctx.body = { data: status };
|
|
130
|
+
} catch (err) {
|
|
131
|
+
ctx.throw(500, err.message);
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
async find(ctx) {
|
|
5
|
+
const { page, pageSize, status, contentType, action } = ctx.query;
|
|
6
|
+
|
|
7
|
+
const service = strapi.plugin('strapi-content-sync-pro').service('syncLog');
|
|
8
|
+
const result = await service.getLogs({
|
|
9
|
+
page: page ? parseInt(page, 10) : 1,
|
|
10
|
+
pageSize: pageSize ? parseInt(pageSize, 10) : 25,
|
|
11
|
+
status,
|
|
12
|
+
contentType,
|
|
13
|
+
action,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
ctx.body = result;
|
|
17
|
+
},
|
|
18
|
+
};
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const PLUGIN_ID = 'strapi-content-sync-pro';
|
|
4
|
+
|
|
5
|
+
function service(strapi) {
|
|
6
|
+
return strapi.plugin(PLUGIN_ID).service('syncMedia');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
module.exports = ({ strapi }) => ({
|
|
10
|
+
// ── Profile CRUD ──────────────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
async getProfiles(ctx) {
|
|
13
|
+
try {
|
|
14
|
+
ctx.body = { data: await service(strapi).getProfiles() };
|
|
15
|
+
} catch (err) {
|
|
16
|
+
ctx.throw(500, err.message);
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
async getProfile(ctx) {
|
|
21
|
+
try {
|
|
22
|
+
const profile = await service(strapi).getProfile(ctx.params.id);
|
|
23
|
+
if (!profile) return ctx.throw(404, 'Media profile not found');
|
|
24
|
+
ctx.body = { data: profile };
|
|
25
|
+
} catch (err) {
|
|
26
|
+
ctx.throw(500, err.message);
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
async createProfile(ctx) {
|
|
31
|
+
try {
|
|
32
|
+
const body = ctx.request.body || {};
|
|
33
|
+
ctx.body = { data: await service(strapi).createProfile(body) };
|
|
34
|
+
} catch (err) {
|
|
35
|
+
ctx.throw(400, err.message);
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
async updateProfile(ctx) {
|
|
40
|
+
try {
|
|
41
|
+
const body = ctx.request.body || {};
|
|
42
|
+
ctx.body = { data: await service(strapi).updateProfile(ctx.params.id, body) };
|
|
43
|
+
} catch (err) {
|
|
44
|
+
ctx.throw(400, err.message);
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
async deleteProfile(ctx) {
|
|
49
|
+
try {
|
|
50
|
+
ctx.body = { data: await service(strapi).deleteProfile(ctx.params.id) };
|
|
51
|
+
} catch (err) {
|
|
52
|
+
ctx.throw(400, err.message);
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
async activateProfile(ctx) {
|
|
57
|
+
try {
|
|
58
|
+
ctx.body = { data: await service(strapi).activateProfile(ctx.params.id) };
|
|
59
|
+
} catch (err) {
|
|
60
|
+
ctx.throw(400, err.message);
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
// ── Global settings ───────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
async getGlobalSettings(ctx) {
|
|
67
|
+
try {
|
|
68
|
+
ctx.body = { data: await service(strapi).getGlobalSettings() };
|
|
69
|
+
} catch (err) {
|
|
70
|
+
ctx.throw(500, err.message);
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
async updateGlobalSettings(ctx) {
|
|
75
|
+
try {
|
|
76
|
+
const body = ctx.request.body || {};
|
|
77
|
+
ctx.body = { data: await service(strapi).setGlobalSettings(body) };
|
|
78
|
+
} catch (err) {
|
|
79
|
+
ctx.throw(400, err.message);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
// ── Defaults / constants ──────────────────────────────────────────────────
|
|
84
|
+
|
|
85
|
+
async getDefaults(ctx) {
|
|
86
|
+
try {
|
|
87
|
+
ctx.body = { data: service(strapi).getDefaults() };
|
|
88
|
+
} catch (err) {
|
|
89
|
+
ctx.throw(500, err.message);
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
// ── Execution ─────────────────────────────────────────────────────────────
|
|
94
|
+
|
|
95
|
+
async runProfile(ctx) {
|
|
96
|
+
try {
|
|
97
|
+
const options = ctx.request.body || {};
|
|
98
|
+
const result = await service(strapi).runProfile(ctx.params.id, options);
|
|
99
|
+
ctx.body = { data: result };
|
|
100
|
+
} catch (err) {
|
|
101
|
+
ctx.throw(400, err.message);
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
async runActiveProfiles(ctx) {
|
|
106
|
+
try {
|
|
107
|
+
const results = await service(strapi).runActiveProfiles();
|
|
108
|
+
ctx.body = { data: results };
|
|
109
|
+
} catch (err) {
|
|
110
|
+
ctx.throw(400, err.message);
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
// ── Back-compat (old flat endpoints) ──────────────────────────────────────
|
|
115
|
+
|
|
116
|
+
async getSettings(ctx) {
|
|
117
|
+
try {
|
|
118
|
+
ctx.body = { data: await service(strapi).getSettings() };
|
|
119
|
+
} catch (err) {
|
|
120
|
+
ctx.throw(500, err.message);
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
async updateSettings(ctx) {
|
|
125
|
+
try {
|
|
126
|
+
const body = ctx.request.body || {};
|
|
127
|
+
ctx.body = { data: await service(strapi).setSettings(body) };
|
|
128
|
+
} catch (err) {
|
|
129
|
+
ctx.throw(400, err.message);
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
async getStatus(ctx) {
|
|
134
|
+
try {
|
|
135
|
+
ctx.body = { data: await service(strapi).getStatus() };
|
|
136
|
+
} catch (err) {
|
|
137
|
+
ctx.throw(500, err.message);
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
async test(ctx) {
|
|
142
|
+
try {
|
|
143
|
+
ctx.body = { data: await service(strapi).testConnection() };
|
|
144
|
+
} catch (err) {
|
|
145
|
+
ctx.throw(500, err.message);
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
async run(ctx) {
|
|
150
|
+
try {
|
|
151
|
+
const options = ctx.request.body || {};
|
|
152
|
+
const result = await service(strapi).run(options);
|
|
153
|
+
ctx.body = { data: result };
|
|
154
|
+
} catch (err) {
|
|
155
|
+
ctx.throw(400, err.message);
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
});
|