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,31 @@
|
|
|
1
|
+
import { ArrowsCounterClockwise } from '@strapi/icons';
|
|
2
|
+
import pluginId from './pluginId';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
register(app) {
|
|
6
|
+
app.addMenuLink({
|
|
7
|
+
to: `plugins/${pluginId}`,
|
|
8
|
+
icon: ArrowsCounterClockwise,
|
|
9
|
+
intlLabel: {
|
|
10
|
+
id: `${pluginId}.plugin.name`,
|
|
11
|
+
defaultMessage: 'Data Sync',
|
|
12
|
+
},
|
|
13
|
+
Component: async () => {
|
|
14
|
+
const { App } = await import('./pages/App');
|
|
15
|
+
return App;
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
app.registerPlugin({
|
|
20
|
+
id: pluginId,
|
|
21
|
+
name: pluginId,
|
|
22
|
+
});
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
async registerTrads({ locales }) {
|
|
26
|
+
return locales.map((locale) => ({
|
|
27
|
+
data: {},
|
|
28
|
+
locale,
|
|
29
|
+
}));
|
|
30
|
+
},
|
|
31
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { Routes, Route } from 'react-router-dom';
|
|
3
|
+
import { Page } from '@strapi/strapi/admin';
|
|
4
|
+
import { Box, Flex, Typography, Button, Main } from '@strapi/design-system';
|
|
5
|
+
import { ConfigTab } from '../../components/ConfigTab';
|
|
6
|
+
import { ContentTypesTab } from '../../components/ContentTypesTab';
|
|
7
|
+
import { SyncTab } from '../../components/SyncTab';
|
|
8
|
+
import { LogsTab } from '../../components/LogsTab';
|
|
9
|
+
import { HelpTab } from '../../components/HelpTab';
|
|
10
|
+
import { SyncProfilesTab } from '../../components/SyncProfilesTab';
|
|
11
|
+
import { MediaTab } from '../../components/MediaTab';
|
|
12
|
+
|
|
13
|
+
const TABS = [
|
|
14
|
+
{ key: 'config', label: 'Configuration' },
|
|
15
|
+
{ key: 'content-types', label: 'Content Types' },
|
|
16
|
+
{ key: 'sync-profiles', label: 'Sync Profiles' },
|
|
17
|
+
{ key: 'sync', label: 'Sync' },
|
|
18
|
+
{ key: 'media', label: 'Media' },
|
|
19
|
+
{ key: 'logs', label: 'Logs' },
|
|
20
|
+
{ key: 'help', label: 'Help' },
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const HomePage = () => {
|
|
24
|
+
const [activeTab, setActiveTab] = useState('config');
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<Main>
|
|
28
|
+
<Box padding={8} background="neutral100">
|
|
29
|
+
<Typography variant="alpha" tag="h1">
|
|
30
|
+
Content Sync Pro Plugin - Reliable Data Synchronization for Strapi Environments
|
|
31
|
+
</Typography>
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
<Box paddingTop={4} paddingBottom={6}>
|
|
35
|
+
<Flex gap={2}>
|
|
36
|
+
{TABS.map((tab) => (
|
|
37
|
+
<Button
|
|
38
|
+
key={tab.key}
|
|
39
|
+
variant={activeTab === tab.key ? 'default' : 'tertiary'}
|
|
40
|
+
onClick={() => setActiveTab(tab.key)}
|
|
41
|
+
>
|
|
42
|
+
{tab.label}
|
|
43
|
+
</Button>
|
|
44
|
+
))}
|
|
45
|
+
</Flex>
|
|
46
|
+
</Box>
|
|
47
|
+
|
|
48
|
+
{activeTab === 'config' && <ConfigTab />}
|
|
49
|
+
{activeTab === 'content-types' && <ContentTypesTab />}
|
|
50
|
+
{activeTab === 'sync-profiles' && <SyncProfilesTab />}
|
|
51
|
+
{activeTab === 'sync' && <SyncTab />}
|
|
52
|
+
{activeTab === 'media' && <MediaTab />}
|
|
53
|
+
{activeTab === 'logs' && <LogsTab />}
|
|
54
|
+
{activeTab === 'help' && <HelpTab />}
|
|
55
|
+
|
|
56
|
+
<Box paddingTop={8} borderColor="neutral200" borderStyle="solid" borderWidth="1px 0 0 0">
|
|
57
|
+
<Box paddingTop={4}>
|
|
58
|
+
<Typography variant="sigma" textColor="neutral600">
|
|
59
|
+
Need help with this plugin? Feel free to reach out or explore the resources below.
|
|
60
|
+
</Typography>
|
|
61
|
+
<Box paddingTop={2}>
|
|
62
|
+
<Typography variant="pi" textColor="neutral500">
|
|
63
|
+
Contact: {' Eja Arain '}
|
|
64
|
+
<Typography
|
|
65
|
+
variant="pi"
|
|
66
|
+
textColor="primary600"
|
|
67
|
+
tag="a"
|
|
68
|
+
href="mailto:eharain@yahoo.com"
|
|
69
|
+
>
|
|
70
|
+
eharain@yahoo.com
|
|
71
|
+
</Typography>
|
|
72
|
+
{' · '}
|
|
73
|
+
<Typography
|
|
74
|
+
variant="pi"
|
|
75
|
+
textColor="primary600"
|
|
76
|
+
tag="a"
|
|
77
|
+
href="https://github.com/eharain/strapi-content-sync-pro"
|
|
78
|
+
target="_blank"
|
|
79
|
+
rel="noopener noreferrer"
|
|
80
|
+
>
|
|
81
|
+
GitHub
|
|
82
|
+
</Typography>
|
|
83
|
+
{' · '}
|
|
84
|
+
<Typography
|
|
85
|
+
variant="pi"
|
|
86
|
+
textColor="primary600"
|
|
87
|
+
tag="a"
|
|
88
|
+
href="https://www.linkedin.com/in/ejazarain/"
|
|
89
|
+
target="_blank"
|
|
90
|
+
rel="noopener noreferrer"
|
|
91
|
+
>
|
|
92
|
+
LinkedIn
|
|
93
|
+
</Typography>
|
|
94
|
+
</Typography>
|
|
95
|
+
</Box>
|
|
96
|
+
<Box paddingTop={2}>
|
|
97
|
+
<Typography variant="pi" textColor="neutral500">
|
|
98
|
+
This plugin is a core component of {' '}
|
|
99
|
+
<Typography
|
|
100
|
+
variant="pi"
|
|
101
|
+
textColor="primary600"
|
|
102
|
+
tag="a"
|
|
103
|
+
href="https://github.com/eharain/Rutba-ERP/"
|
|
104
|
+
target="_blank"
|
|
105
|
+
rel="noopener noreferrer"
|
|
106
|
+
>
|
|
107
|
+
Rutba ERP
|
|
108
|
+
</Typography>
|
|
109
|
+
, powering reliable data synchronization across multi-environment installations.
|
|
110
|
+
</Typography>
|
|
111
|
+
</Box>
|
|
112
|
+
</Box>
|
|
113
|
+
</Box>
|
|
114
|
+
</Box>
|
|
115
|
+
</Main>
|
|
116
|
+
);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const App = () => {
|
|
120
|
+
return (
|
|
121
|
+
<Routes>
|
|
122
|
+
<Route index element={<HomePage />} />
|
|
123
|
+
<Route path="*" element={<Page.Error />} />
|
|
124
|
+
</Routes>
|
|
125
|
+
);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export { App };
|
|
129
|
+
export default App;
|
package/package.json
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "strapi-content-sync-pro",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Strapi v5 plugin to copy, migrate, and live-sync content, media, and data between multiple Strapi environments with bi-directional sync, field-level policies, scheduling, and alerts.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Ejaz Husain Arain",
|
|
8
|
+
"email": "eharain@yahoo.com",
|
|
9
|
+
"url": "https://github.com/eharain"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/eharain/strapi-content-sync-pro.git"
|
|
14
|
+
},
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/eharain/strapi-content-sync-pro/issues"
|
|
17
|
+
},
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public",
|
|
20
|
+
"registry": "https://registry.npmjs.org/"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://github.com/eharain/strapi-content-sync-pro#readme",
|
|
23
|
+
"keywords": [
|
|
24
|
+
"strapi",
|
|
25
|
+
"strapi-plugin",
|
|
26
|
+
"strapi-v5",
|
|
27
|
+
"sync",
|
|
28
|
+
"live-sync",
|
|
29
|
+
"data-sync",
|
|
30
|
+
"content-sync",
|
|
31
|
+
"copy",
|
|
32
|
+
"migrate",
|
|
33
|
+
"migration",
|
|
34
|
+
"mirror",
|
|
35
|
+
"replicate",
|
|
36
|
+
"replication",
|
|
37
|
+
"media-sync",
|
|
38
|
+
"synchronization",
|
|
39
|
+
"cross-environment",
|
|
40
|
+
"data-transfer",
|
|
41
|
+
"headless-cms",
|
|
42
|
+
"content-management"
|
|
43
|
+
],
|
|
44
|
+
"exports": {
|
|
45
|
+
"./strapi-admin": {
|
|
46
|
+
"source": "./admin/src/index.js",
|
|
47
|
+
"import": "./admin/src/index.js",
|
|
48
|
+
"require": "./admin/src/index.js",
|
|
49
|
+
"default": "./admin/src/index.js"
|
|
50
|
+
},
|
|
51
|
+
"./strapi-server": {
|
|
52
|
+
"source": "./server/src/index.js",
|
|
53
|
+
"import": "./server/src/index.js",
|
|
54
|
+
"require": "./server/src/index.js",
|
|
55
|
+
"default": "./server/src/index.js"
|
|
56
|
+
},
|
|
57
|
+
"./package.json": "./package.json"
|
|
58
|
+
},
|
|
59
|
+
"files": [
|
|
60
|
+
"admin/",
|
|
61
|
+
"server/",
|
|
62
|
+
"README.md",
|
|
63
|
+
"LICENSE"
|
|
64
|
+
],
|
|
65
|
+
"scripts": {
|
|
66
|
+
"test": "echo \"No tests yet\" && exit 0"
|
|
67
|
+
},
|
|
68
|
+
"peerDependencies": {
|
|
69
|
+
"@strapi/strapi": "^5.0.0",
|
|
70
|
+
"react": "^17.0.0 || ^18.0.0",
|
|
71
|
+
"react-dom": "^17.0.0 || ^18.0.0",
|
|
72
|
+
"react-router-dom": "^6.0.0"
|
|
73
|
+
},
|
|
74
|
+
"engines": {
|
|
75
|
+
"node": ">=20.0.0",
|
|
76
|
+
"npm": ">=6.0.0"
|
|
77
|
+
},
|
|
78
|
+
"strapi": {
|
|
79
|
+
"name": "strapi-content-sync-pro",
|
|
80
|
+
"description": "Copy, migrate, and live-sync content, media, and data between Strapi environments",
|
|
81
|
+
"kind": "plugin",
|
|
82
|
+
"displayName": "Content Sync Pro"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { ensureSyncId } = require('./utils/sync-id');
|
|
4
|
+
const { isRemoteUpdate } = require('./utils/sync-guard');
|
|
5
|
+
|
|
6
|
+
const bootstrap = ({ strapi }) => {
|
|
7
|
+
/**
|
|
8
|
+
* Check if live sync is enabled for a content type
|
|
9
|
+
*/
|
|
10
|
+
const isLiveSyncEnabled = async (contentTypeUid) => {
|
|
11
|
+
try {
|
|
12
|
+
const executionService = strapi.plugin('strapi-content-sync-pro').service('syncExecution');
|
|
13
|
+
const profilesService = strapi.plugin('strapi-content-sync-pro').service('syncProfiles');
|
|
14
|
+
|
|
15
|
+
// Get active profile for this content type
|
|
16
|
+
const profile = await profilesService.getActiveProfileForContentType(contentTypeUid);
|
|
17
|
+
if (!profile) return false;
|
|
18
|
+
|
|
19
|
+
// Get execution settings
|
|
20
|
+
const execSettings = await executionService.getProfileExecutionSettings(profile.id);
|
|
21
|
+
|
|
22
|
+
// Check if live mode is enabled
|
|
23
|
+
return execSettings.executionMode === 'live' && execSettings.enabled;
|
|
24
|
+
} catch (err) {
|
|
25
|
+
strapi.log.error(`[data-sync] Error checking live sync: ${err.message}`);
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Execute live sync for a record
|
|
32
|
+
*/
|
|
33
|
+
const executeLiveSync = async (contentTypeUid, record, action) => {
|
|
34
|
+
try {
|
|
35
|
+
const executionService = strapi.plugin('strapi-content-sync-pro').service('syncExecution');
|
|
36
|
+
const profilesService = strapi.plugin('strapi-content-sync-pro').service('syncProfiles');
|
|
37
|
+
const syncService = strapi.plugin('strapi-content-sync-pro').service('sync');
|
|
38
|
+
const logService = strapi.plugin('strapi-content-sync-pro').service('syncLog');
|
|
39
|
+
|
|
40
|
+
const profile = await profilesService.getActiveProfileForContentType(contentTypeUid);
|
|
41
|
+
if (!profile) return;
|
|
42
|
+
|
|
43
|
+
// Log the live sync trigger
|
|
44
|
+
await logService.log({
|
|
45
|
+
action: `live_${action}`,
|
|
46
|
+
contentType: contentTypeUid,
|
|
47
|
+
syncId: record.syncId,
|
|
48
|
+
direction: profile.direction,
|
|
49
|
+
status: 'info',
|
|
50
|
+
message: `Live sync triggered: ${action} on ${contentTypeUid}`,
|
|
51
|
+
details: { profileId: profile.id, recordId: record.id },
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Push the record based on profile direction
|
|
55
|
+
if (profile.direction === 'push' || profile.direction === 'both') {
|
|
56
|
+
await syncService.pushRecord(contentTypeUid, record);
|
|
57
|
+
}
|
|
58
|
+
} catch (err) {
|
|
59
|
+
strapi.log.error(`[data-sync] Live sync failed: ${err.message}`);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Subscribe to DB lifecycle events for all content types
|
|
64
|
+
strapi.db.lifecycles.subscribe({
|
|
65
|
+
/**
|
|
66
|
+
* Automatically generate a syncId (UUID) for every new
|
|
67
|
+
* record in an api:: content type that does not already have one.
|
|
68
|
+
*/
|
|
69
|
+
async beforeCreate(event) {
|
|
70
|
+
const { model, params } = event;
|
|
71
|
+
if (!model.uid.startsWith('api::')) return;
|
|
72
|
+
|
|
73
|
+
if (params.data) {
|
|
74
|
+
ensureSyncId(params.data);
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* After a local record is created, push it to the remote
|
|
80
|
+
* instance if live sync is enabled.
|
|
81
|
+
*/
|
|
82
|
+
async afterCreate(event) {
|
|
83
|
+
const { model, result } = event;
|
|
84
|
+
if (!model.uid.startsWith('api::')) return;
|
|
85
|
+
|
|
86
|
+
const key = `${model.uid}:${result.syncId}`;
|
|
87
|
+
if (isRemoteUpdate(key)) return;
|
|
88
|
+
|
|
89
|
+
// Check if live sync is enabled
|
|
90
|
+
const liveEnabled = await isLiveSyncEnabled(model.uid);
|
|
91
|
+
if (!liveEnabled) return;
|
|
92
|
+
|
|
93
|
+
await executeLiveSync(model.uid, result, 'create');
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* After a local record is updated, push it to the remote
|
|
98
|
+
* instance if live sync is enabled.
|
|
99
|
+
*/
|
|
100
|
+
async afterUpdate(event) {
|
|
101
|
+
const { model, result } = event;
|
|
102
|
+
if (!model.uid.startsWith('api::')) return;
|
|
103
|
+
|
|
104
|
+
const key = `${model.uid}:${result.syncId}`;
|
|
105
|
+
if (isRemoteUpdate(key)) return;
|
|
106
|
+
|
|
107
|
+
// Check if live sync is enabled
|
|
108
|
+
const liveEnabled = await isLiveSyncEnabled(model.uid);
|
|
109
|
+
if (!liveEnabled) return;
|
|
110
|
+
|
|
111
|
+
await executeLiveSync(model.uid, result, 'update');
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* After a local record is deleted, notify the remote
|
|
116
|
+
* instance if live sync is enabled.
|
|
117
|
+
*/
|
|
118
|
+
async afterDelete(event) {
|
|
119
|
+
const { model, result } = event;
|
|
120
|
+
if (!model.uid.startsWith('api::')) return;
|
|
121
|
+
if (!result?.syncId) return;
|
|
122
|
+
|
|
123
|
+
const key = `${model.uid}:${result.syncId}`;
|
|
124
|
+
if (isRemoteUpdate(key)) return;
|
|
125
|
+
|
|
126
|
+
// Check if live sync is enabled
|
|
127
|
+
const liveEnabled = await isLiveSyncEnabled(model.uid);
|
|
128
|
+
if (!liveEnabled) return;
|
|
129
|
+
|
|
130
|
+
await executeLiveSync(model.uid, result, 'delete');
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Initialize scheduled syncs after Strapi is ready
|
|
135
|
+
strapi.server.use(async (ctx, next) => {
|
|
136
|
+
await next();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Defer scheduler initialization
|
|
140
|
+
setImmediate(async () => {
|
|
141
|
+
try {
|
|
142
|
+
const executionService = strapi.plugin('strapi-content-sync-pro').service('syncExecution');
|
|
143
|
+
await executionService.initializeSchedulers();
|
|
144
|
+
strapi.log.info('[data-sync] Scheduled sync jobs initialized');
|
|
145
|
+
} catch (err) {
|
|
146
|
+
strapi.log.error(`[data-sync] Failed to initialize schedulers: ${err.message}`);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
module.exports = bootstrap;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "collectionType",
|
|
3
|
+
"collectionName": "sync_logs",
|
|
4
|
+
"info": {
|
|
5
|
+
"singularName": "sync-log",
|
|
6
|
+
"pluralName": "sync-logs",
|
|
7
|
+
"displayName": "Sync Log"
|
|
8
|
+
},
|
|
9
|
+
"options": {},
|
|
10
|
+
"pluginOptions": {
|
|
11
|
+
"content-manager": { "visible": false },
|
|
12
|
+
"content-type-builder": { "visible": false }
|
|
13
|
+
},
|
|
14
|
+
"attributes": {
|
|
15
|
+
"action": { "type": "string" },
|
|
16
|
+
"contentType": { "type": "string" },
|
|
17
|
+
"recordId": { "type": "string" },
|
|
18
|
+
"syncId": { "type": "string" },
|
|
19
|
+
"direction": { "type": "string" },
|
|
20
|
+
"status": { "type": "string" },
|
|
21
|
+
"message": { "type": "text" },
|
|
22
|
+
"details": { "type": "json" }
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const PLUGIN_ID = 'strapi-content-sync-pro';
|
|
4
|
+
|
|
5
|
+
module.exports = ({ strapi }) => ({
|
|
6
|
+
/**
|
|
7
|
+
* GET /alerts/settings
|
|
8
|
+
* Get alert settings
|
|
9
|
+
*/
|
|
10
|
+
async getSettings(ctx) {
|
|
11
|
+
try {
|
|
12
|
+
const settings = await strapi.plugin(PLUGIN_ID).service('alerts').getSettings();
|
|
13
|
+
ctx.body = { data: settings };
|
|
14
|
+
} catch (err) {
|
|
15
|
+
ctx.throw(500, err.message);
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* PUT /alerts/settings
|
|
21
|
+
* Update alert settings
|
|
22
|
+
*/
|
|
23
|
+
async updateSettings(ctx) {
|
|
24
|
+
const body = ctx.request.body;
|
|
25
|
+
try {
|
|
26
|
+
const settings = await strapi.plugin(PLUGIN_ID).service('alerts').updateSettings(body);
|
|
27
|
+
ctx.body = { data: settings };
|
|
28
|
+
} catch (err) {
|
|
29
|
+
ctx.throw(400, err.message);
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* POST /alerts/test/:channel
|
|
35
|
+
* Test an alert channel
|
|
36
|
+
*/
|
|
37
|
+
async testChannel(ctx) {
|
|
38
|
+
const { channel } = ctx.params;
|
|
39
|
+
try {
|
|
40
|
+
const result = await strapi.plugin(PLUGIN_ID).service('alerts').testChannel(channel);
|
|
41
|
+
ctx.body = { data: result };
|
|
42
|
+
} catch (err) {
|
|
43
|
+
ctx.throw(400, err.message);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* GET /alerts/stats
|
|
49
|
+
* Get alert statistics
|
|
50
|
+
*/
|
|
51
|
+
async getStats(ctx) {
|
|
52
|
+
try {
|
|
53
|
+
const stats = strapi.plugin(PLUGIN_ID).service('alerts').getAlertStats();
|
|
54
|
+
ctx.body = { data: stats };
|
|
55
|
+
} catch (err) {
|
|
56
|
+
ctx.throw(500, err.message);
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
});
|