@tunghtml/strapi-plugin-export-import-clsx 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/README.md +93 -0
- package/admin/src/components/BulkActions/index.js +70 -0
- package/admin/src/components/ExportButton/index.js +48 -0
- package/admin/src/components/ExportImportButtons/index.js +245 -0
- package/admin/src/components/ImportButton/index.js +54 -0
- package/admin/src/components/Initializer/index.js +15 -0
- package/admin/src/components/PluginIcon/index.js +6 -0
- package/admin/src/pages/App/index.js +8 -0
- package/admin/src/pages/HomePage/index.js +297 -0
- package/admin/src/pluginId.js +3 -0
- package/admin/src/translations/en.json +14 -0
- package/package.json +60 -0
- package/server/bootstrap.js +3 -0
- package/server/config/index.js +4 -0
- package/server/content-types/index.js +1 -0
- package/server/controllers/export-controller.js +62 -0
- package/server/controllers/import-controller.js +42 -0
- package/server/controllers/index.js +7 -0
- package/server/destroy.js +3 -0
- package/server/middlewares/index.js +1 -0
- package/server/policies/index.js +1 -0
- package/server/register.js +3 -0
- package/server/routes/index.js +29 -0
- package/server/services/export-service.js +336 -0
- package/server/services/import-service.js +306 -0
- package/server/services/index.js +7 -0
- package/strapi-admin.js +88 -0
- package/strapi-server.js +34 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
const HomePage = () => {
|
|
4
|
+
const [isExporting, setIsExporting] = useState(false);
|
|
5
|
+
const [isImporting, setIsImporting] = useState(false);
|
|
6
|
+
const [contentTypes, setContentTypes] = useState([]);
|
|
7
|
+
const [selectedContentType, setSelectedContentType] = useState('');
|
|
8
|
+
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
// Fetch available content types
|
|
11
|
+
fetch('/admin/content-manager/content-types')
|
|
12
|
+
.then(res => res.json())
|
|
13
|
+
.then(data => {
|
|
14
|
+
const apiTypes = data.data.filter(ct => ct.uid.startsWith('api::'));
|
|
15
|
+
setContentTypes(apiTypes);
|
|
16
|
+
})
|
|
17
|
+
.catch(err => console.error('Failed to fetch content types:', err));
|
|
18
|
+
}, []);
|
|
19
|
+
|
|
20
|
+
const handleExportAll = async () => {
|
|
21
|
+
setIsExporting(true);
|
|
22
|
+
try {
|
|
23
|
+
const response = await fetch('/export-import-clsx/export?format=excel');
|
|
24
|
+
if (response.ok) {
|
|
25
|
+
const blob = await response.blob();
|
|
26
|
+
const url = window.URL.createObjectURL(blob);
|
|
27
|
+
const a = document.createElement('a');
|
|
28
|
+
a.href = url;
|
|
29
|
+
a.download = `strapi-all-export-${new Date().toISOString().split('T')[0]}.xlsx`;
|
|
30
|
+
document.body.appendChild(a);
|
|
31
|
+
a.click();
|
|
32
|
+
window.URL.revokeObjectURL(url);
|
|
33
|
+
document.body.removeChild(a);
|
|
34
|
+
alert('Export completed successfully');
|
|
35
|
+
} else {
|
|
36
|
+
throw new Error('Export failed');
|
|
37
|
+
}
|
|
38
|
+
} catch (error) {
|
|
39
|
+
alert('Export failed: ' + error.message);
|
|
40
|
+
} finally {
|
|
41
|
+
setIsExporting(false);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const handleExportSingle = async () => {
|
|
46
|
+
if (!selectedContentType) {
|
|
47
|
+
alert('Please select a content type');
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
setIsExporting(true);
|
|
52
|
+
try {
|
|
53
|
+
const response = await fetch(`/export-import-clsx/export?format=excel&contentType=${selectedContentType}`);
|
|
54
|
+
if (response.ok) {
|
|
55
|
+
const blob = await response.blob();
|
|
56
|
+
const url = window.URL.createObjectURL(blob);
|
|
57
|
+
const a = document.createElement('a');
|
|
58
|
+
a.href = url;
|
|
59
|
+
a.download = `${selectedContentType.replace('api::', '')}-export-${new Date().toISOString().split('T')[0]}.xlsx`;
|
|
60
|
+
document.body.appendChild(a);
|
|
61
|
+
a.click();
|
|
62
|
+
window.URL.revokeObjectURL(url);
|
|
63
|
+
document.body.removeChild(a);
|
|
64
|
+
alert('Export completed successfully');
|
|
65
|
+
} else {
|
|
66
|
+
throw new Error('Export failed');
|
|
67
|
+
}
|
|
68
|
+
} catch (error) {
|
|
69
|
+
alert('Export failed: ' + error.message);
|
|
70
|
+
} finally {
|
|
71
|
+
setIsExporting(false);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const handleImport = async (event) => {
|
|
76
|
+
const file = event.target.files[0];
|
|
77
|
+
if (!file) return;
|
|
78
|
+
|
|
79
|
+
setIsImporting(true);
|
|
80
|
+
const formData = new FormData();
|
|
81
|
+
formData.append('file', file);
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const response = await fetch('/export-import-clsx/import', {
|
|
85
|
+
method: 'POST',
|
|
86
|
+
body: formData,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
if (response.ok) {
|
|
90
|
+
const result = await response.json();
|
|
91
|
+
alert(`Import completed! Imported: ${result.result.imported}, Errors: ${result.result.errors.length}`);
|
|
92
|
+
} else {
|
|
93
|
+
throw new Error('Import failed');
|
|
94
|
+
}
|
|
95
|
+
} catch (error) {
|
|
96
|
+
alert('Import failed: ' + error.message);
|
|
97
|
+
} finally {
|
|
98
|
+
setIsImporting(false);
|
|
99
|
+
event.target.value = '';
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
return React.createElement('div', {
|
|
104
|
+
style: {
|
|
105
|
+
padding: '24px',
|
|
106
|
+
maxWidth: '1200px',
|
|
107
|
+
margin: '0 auto',
|
|
108
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
React.createElement('div', { style: { marginBottom: '32px' } },
|
|
112
|
+
React.createElement('h1', {
|
|
113
|
+
style: {
|
|
114
|
+
fontSize: '32px',
|
|
115
|
+
fontWeight: '600',
|
|
116
|
+
color: '#212134',
|
|
117
|
+
marginBottom: '8px'
|
|
118
|
+
}
|
|
119
|
+
}, 'Export Import CLSX'),
|
|
120
|
+
React.createElement('p', {
|
|
121
|
+
style: {
|
|
122
|
+
fontSize: '16px',
|
|
123
|
+
color: '#666687',
|
|
124
|
+
margin: '0'
|
|
125
|
+
}
|
|
126
|
+
}, 'Export and import your Strapi collections as Excel files')
|
|
127
|
+
),
|
|
128
|
+
|
|
129
|
+
React.createElement('div', {
|
|
130
|
+
style: {
|
|
131
|
+
display: 'grid',
|
|
132
|
+
gridTemplateColumns: 'repeat(auto-fit, minmax(400px, 1fr))',
|
|
133
|
+
gap: '24px'
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
// Export All Card
|
|
137
|
+
React.createElement('div', {
|
|
138
|
+
style: {
|
|
139
|
+
backgroundColor: 'white',
|
|
140
|
+
border: '1px solid #dcdce4',
|
|
141
|
+
borderRadius: '8px',
|
|
142
|
+
padding: '24px',
|
|
143
|
+
boxShadow: '0 1px 4px rgba(33, 33, 52, 0.1)'
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
React.createElement('h3', {
|
|
147
|
+
style: {
|
|
148
|
+
fontSize: '20px',
|
|
149
|
+
fontWeight: '600',
|
|
150
|
+
color: '#212134',
|
|
151
|
+
marginBottom: '12px'
|
|
152
|
+
}
|
|
153
|
+
}, 'Export All Collections'),
|
|
154
|
+
React.createElement('p', {
|
|
155
|
+
style: {
|
|
156
|
+
fontSize: '14px',
|
|
157
|
+
color: '#666687',
|
|
158
|
+
marginBottom: '20px',
|
|
159
|
+
lineHeight: '1.5'
|
|
160
|
+
}
|
|
161
|
+
}, 'Export all your API collections to a single Excel file with multiple sheets.'),
|
|
162
|
+
React.createElement('button', {
|
|
163
|
+
onClick: handleExportAll,
|
|
164
|
+
disabled: isExporting,
|
|
165
|
+
style: {
|
|
166
|
+
padding: '12px 24px',
|
|
167
|
+
backgroundColor: isExporting ? '#dcdce4' : '#4945ff',
|
|
168
|
+
color: 'white',
|
|
169
|
+
border: 'none',
|
|
170
|
+
borderRadius: '4px',
|
|
171
|
+
fontSize: '14px',
|
|
172
|
+
fontWeight: '600',
|
|
173
|
+
cursor: isExporting ? 'not-allowed' : 'pointer',
|
|
174
|
+
transition: 'background-color 0.2s'
|
|
175
|
+
}
|
|
176
|
+
}, isExporting ? 'Exporting...' : 'Export All Collections')
|
|
177
|
+
),
|
|
178
|
+
|
|
179
|
+
// Export Single Card
|
|
180
|
+
React.createElement('div', {
|
|
181
|
+
style: {
|
|
182
|
+
backgroundColor: 'white',
|
|
183
|
+
border: '1px solid #dcdce4',
|
|
184
|
+
borderRadius: '8px',
|
|
185
|
+
padding: '24px',
|
|
186
|
+
boxShadow: '0 1px 4px rgba(33, 33, 52, 0.1)'
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
React.createElement('h3', {
|
|
190
|
+
style: {
|
|
191
|
+
fontSize: '20px',
|
|
192
|
+
fontWeight: '600',
|
|
193
|
+
color: '#212134',
|
|
194
|
+
marginBottom: '12px'
|
|
195
|
+
}
|
|
196
|
+
}, 'Export Single Collection'),
|
|
197
|
+
React.createElement('p', {
|
|
198
|
+
style: {
|
|
199
|
+
fontSize: '14px',
|
|
200
|
+
color: '#666687',
|
|
201
|
+
marginBottom: '20px',
|
|
202
|
+
lineHeight: '1.5'
|
|
203
|
+
}
|
|
204
|
+
}, 'Export a specific collection to an Excel file.'),
|
|
205
|
+
React.createElement('select', {
|
|
206
|
+
value: selectedContentType,
|
|
207
|
+
onChange: (e) => setSelectedContentType(e.target.value),
|
|
208
|
+
style: {
|
|
209
|
+
width: '100%',
|
|
210
|
+
padding: '8px 12px',
|
|
211
|
+
border: '1px solid #dcdce4',
|
|
212
|
+
borderRadius: '4px',
|
|
213
|
+
fontSize: '14px',
|
|
214
|
+
marginBottom: '16px',
|
|
215
|
+
backgroundColor: 'white'
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
React.createElement('option', { value: '' }, 'Select a collection...'),
|
|
219
|
+
...contentTypes.map(ct =>
|
|
220
|
+
React.createElement('option', {
|
|
221
|
+
key: ct.uid,
|
|
222
|
+
value: ct.uid
|
|
223
|
+
}, ct.info.displayName || ct.info.singularName)
|
|
224
|
+
)
|
|
225
|
+
),
|
|
226
|
+
React.createElement('button', {
|
|
227
|
+
onClick: handleExportSingle,
|
|
228
|
+
disabled: isExporting || !selectedContentType,
|
|
229
|
+
style: {
|
|
230
|
+
padding: '12px 24px',
|
|
231
|
+
backgroundColor: (isExporting || !selectedContentType) ? '#dcdce4' : '#328048',
|
|
232
|
+
color: 'white',
|
|
233
|
+
border: 'none',
|
|
234
|
+
borderRadius: '4px',
|
|
235
|
+
fontSize: '14px',
|
|
236
|
+
fontWeight: '600',
|
|
237
|
+
cursor: (isExporting || !selectedContentType) ? 'not-allowed' : 'pointer',
|
|
238
|
+
transition: 'background-color 0.2s'
|
|
239
|
+
}
|
|
240
|
+
}, isExporting ? 'Exporting...' : 'Export Collection')
|
|
241
|
+
),
|
|
242
|
+
|
|
243
|
+
// Import Card
|
|
244
|
+
React.createElement('div', {
|
|
245
|
+
style: {
|
|
246
|
+
backgroundColor: 'white',
|
|
247
|
+
border: '1px solid #dcdce4',
|
|
248
|
+
borderRadius: '8px',
|
|
249
|
+
padding: '24px',
|
|
250
|
+
boxShadow: '0 1px 4px rgba(33, 33, 52, 0.1)'
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
React.createElement('h3', {
|
|
254
|
+
style: {
|
|
255
|
+
fontSize: '20px',
|
|
256
|
+
fontWeight: '600',
|
|
257
|
+
color: '#212134',
|
|
258
|
+
marginBottom: '12px'
|
|
259
|
+
}
|
|
260
|
+
}, 'Import Data'),
|
|
261
|
+
React.createElement('p', {
|
|
262
|
+
style: {
|
|
263
|
+
fontSize: '14px',
|
|
264
|
+
color: '#666687',
|
|
265
|
+
marginBottom: '20px',
|
|
266
|
+
lineHeight: '1.5'
|
|
267
|
+
}
|
|
268
|
+
}, 'Import data from Excel or JSON files. Supports multiple collections.'),
|
|
269
|
+
React.createElement('input', {
|
|
270
|
+
type: 'file',
|
|
271
|
+
accept: '.xlsx,.xls,.json',
|
|
272
|
+
onChange: handleImport,
|
|
273
|
+
disabled: isImporting,
|
|
274
|
+
style: { display: 'none' },
|
|
275
|
+
id: 'import-file'
|
|
276
|
+
}),
|
|
277
|
+
React.createElement('label', {
|
|
278
|
+
htmlFor: 'import-file',
|
|
279
|
+
style: {
|
|
280
|
+
display: 'inline-block',
|
|
281
|
+
padding: '12px 24px',
|
|
282
|
+
backgroundColor: isImporting ? '#dcdce4' : '#f6a609',
|
|
283
|
+
color: 'white',
|
|
284
|
+
border: 'none',
|
|
285
|
+
borderRadius: '4px',
|
|
286
|
+
fontSize: '14px',
|
|
287
|
+
fontWeight: '600',
|
|
288
|
+
cursor: isImporting ? 'not-allowed' : 'pointer',
|
|
289
|
+
transition: 'background-color 0.2s'
|
|
290
|
+
}
|
|
291
|
+
}, isImporting ? 'Importing...' : 'Choose File to Import')
|
|
292
|
+
)
|
|
293
|
+
)
|
|
294
|
+
);
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
export default HomePage;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"plugin.name": "Export Import CLSX",
|
|
3
|
+
"plugin.description": "Export and import data with enhanced functionality",
|
|
4
|
+
"export.title": "Export Data",
|
|
5
|
+
"export.description": "Export all your content types data to a JSON file",
|
|
6
|
+
"export.button": "Export Data",
|
|
7
|
+
"export.success": "Export completed successfully",
|
|
8
|
+
"export.error": "Export failed",
|
|
9
|
+
"import.title": "Import Data",
|
|
10
|
+
"import.description": "Import data from a JSON file to your Strapi instance",
|
|
11
|
+
"import.button": "Import Data",
|
|
12
|
+
"import.success": "Import completed successfully",
|
|
13
|
+
"import.error": "Import failed"
|
|
14
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tunghtml/strapi-plugin-export-import-clsx",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A powerful Strapi plugin for exporting and importing data with Excel support and advanced filtering",
|
|
5
|
+
"main": "./strapi-server.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"develop": "strapi plugin:watch",
|
|
8
|
+
"build": "strapi plugin:build"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"strapi",
|
|
12
|
+
"plugin",
|
|
13
|
+
"export",
|
|
14
|
+
"import",
|
|
15
|
+
"excel",
|
|
16
|
+
"xlsx",
|
|
17
|
+
"data",
|
|
18
|
+
"migration",
|
|
19
|
+
"bulk",
|
|
20
|
+
"filter",
|
|
21
|
+
"strapi-plugin"
|
|
22
|
+
],
|
|
23
|
+
"author": {
|
|
24
|
+
"name": "tunghtml",
|
|
25
|
+
"email": "tunghtml@example.com"
|
|
26
|
+
},
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/tunghtml/strapi-plugin-export-import-clsx.git"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/tunghtml/strapi-plugin-export-import-clsx#readme",
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/tunghtml/strapi-plugin-export-import-clsx/issues"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=16.0.0",
|
|
38
|
+
"npm": ">=7.0.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@strapi/strapi": "^4.0.0 || ^5.0.0"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"xlsx": "^0.18.5"
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"admin/",
|
|
48
|
+
"server/",
|
|
49
|
+
"strapi-admin.js",
|
|
50
|
+
"strapi-server.js",
|
|
51
|
+
"README.md"
|
|
52
|
+
],
|
|
53
|
+
"strapi": {
|
|
54
|
+
"name": "export-import-clsx",
|
|
55
|
+
"displayName": "Export Import CLSX",
|
|
56
|
+
"description": "Export and import data with enhanced functionality including Excel support and advanced filtering",
|
|
57
|
+
"kind": "plugin",
|
|
58
|
+
"category": "data-management"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module.exports = ({ strapi }) => ({
|
|
2
|
+
async export(ctx) {
|
|
3
|
+
try {
|
|
4
|
+
const { format = 'excel', contentType, selectedIds, ...filters } = ctx.query;
|
|
5
|
+
const exportService = strapi.plugin('export-import-clsx').service('export-service');
|
|
6
|
+
|
|
7
|
+
// Parse selectedIds if provided
|
|
8
|
+
let parsedSelectedIds = [];
|
|
9
|
+
if (selectedIds) {
|
|
10
|
+
try {
|
|
11
|
+
parsedSelectedIds = Array.isArray(selectedIds) ? selectedIds : JSON.parse(selectedIds);
|
|
12
|
+
} catch (error) {
|
|
13
|
+
strapi.log.warn('Failed to parse selectedIds:', error.message);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (format === 'excel') {
|
|
18
|
+
const buffer = await exportService.exportData('excel', contentType, filters, parsedSelectedIds);
|
|
19
|
+
|
|
20
|
+
const filename = parsedSelectedIds.length > 0
|
|
21
|
+
? `${contentType?.replace('api::', '') || 'strapi'}-selected-${parsedSelectedIds.length}-${new Date().toISOString().split('T')[0]}.xlsx`
|
|
22
|
+
: `${contentType?.replace('api::', '') || 'strapi'}-export-${new Date().toISOString().split('T')[0]}.xlsx`;
|
|
23
|
+
|
|
24
|
+
ctx.set('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
|
25
|
+
ctx.set('Content-Disposition', `attachment; filename="${filename}"`);
|
|
26
|
+
|
|
27
|
+
ctx.body = buffer;
|
|
28
|
+
} else {
|
|
29
|
+
const data = await exportService.exportData('json', contentType, filters, parsedSelectedIds);
|
|
30
|
+
|
|
31
|
+
const filename = parsedSelectedIds.length > 0
|
|
32
|
+
? `${contentType?.replace('api::', '') || 'strapi'}-selected-${parsedSelectedIds.length}-${new Date().toISOString().split('T')[0]}.json`
|
|
33
|
+
: `${contentType?.replace('api::', '') || 'strapi'}-export-${new Date().toISOString().split('T')[0]}.json`;
|
|
34
|
+
|
|
35
|
+
ctx.set('Content-Type', 'application/json');
|
|
36
|
+
ctx.set('Content-Disposition', `attachment; filename="${filename}"`);
|
|
37
|
+
|
|
38
|
+
ctx.body = JSON.stringify(data, null, 2);
|
|
39
|
+
}
|
|
40
|
+
} catch (error) {
|
|
41
|
+
strapi.log.error('Export error:', error);
|
|
42
|
+
ctx.throw(500, 'Export failed');
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
async exportSingle(ctx) {
|
|
47
|
+
try {
|
|
48
|
+
const { contentType, id } = ctx.params;
|
|
49
|
+
const exportService = strapi.plugin('export-import-clsx').service('export-service');
|
|
50
|
+
|
|
51
|
+
const buffer = await exportService.exportSingleEntry(contentType, id);
|
|
52
|
+
|
|
53
|
+
ctx.set('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
|
54
|
+
ctx.set('Content-Disposition', `attachment; filename="entry-${id}-${new Date().toISOString().split('T')[0]}.xlsx"`);
|
|
55
|
+
|
|
56
|
+
ctx.body = buffer;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
strapi.log.error('Export single error:', error);
|
|
59
|
+
ctx.throw(500, 'Export failed');
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module.exports = ({ strapi }) => ({
|
|
2
|
+
async import(ctx) {
|
|
3
|
+
try {
|
|
4
|
+
const { files, body } = ctx.request;
|
|
5
|
+
|
|
6
|
+
if (!files || !files.file) {
|
|
7
|
+
return ctx.throw(400, 'No file provided');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const file = Array.isArray(files.file) ? files.file[0] : files.file;
|
|
11
|
+
const targetContentType = body.contentType;
|
|
12
|
+
|
|
13
|
+
const importService = strapi.plugin('export-import-clsx').service('import-service');
|
|
14
|
+
|
|
15
|
+
const result = await importService.importData(file, targetContentType);
|
|
16
|
+
|
|
17
|
+
// Create appropriate message based on results
|
|
18
|
+
let message = 'Import completed successfully';
|
|
19
|
+
if (result.errors && result.errors.length > 0) {
|
|
20
|
+
message = `Import completed with ${result.errors.length} error(s). Please check the details below.`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
ctx.body = {
|
|
24
|
+
message,
|
|
25
|
+
result,
|
|
26
|
+
summary: {
|
|
27
|
+
total: result.created + result.updated,
|
|
28
|
+
created: result.created,
|
|
29
|
+
updated: result.updated,
|
|
30
|
+
errors: result.errors.length,
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
} catch (error) {
|
|
34
|
+
strapi.log.error('Import error:', error);
|
|
35
|
+
ctx.body = {
|
|
36
|
+
error: error.message,
|
|
37
|
+
details: error.stack
|
|
38
|
+
};
|
|
39
|
+
ctx.status = 500;
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module.exports = [
|
|
2
|
+
{
|
|
3
|
+
method: 'GET',
|
|
4
|
+
path: '/export',
|
|
5
|
+
handler: 'export-controller.export',
|
|
6
|
+
config: {
|
|
7
|
+
policies: [],
|
|
8
|
+
auth: false,
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
method: 'GET',
|
|
13
|
+
path: '/export/:contentType/:id',
|
|
14
|
+
handler: 'export-controller.exportSingle',
|
|
15
|
+
config: {
|
|
16
|
+
policies: [],
|
|
17
|
+
auth: false,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
method: 'POST',
|
|
22
|
+
path: '/import',
|
|
23
|
+
handler: 'import-controller.import',
|
|
24
|
+
config: {
|
|
25
|
+
policies: [],
|
|
26
|
+
auth: false,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
];
|