diff options
-rw-r--r-- | app/Controllers/Http/DashboardController.js | 106 | ||||
-rw-r--r-- | public/js/transfer.js | 14 | ||||
-rw-r--r-- | resources/views/dashboard/account.edge | 3 | ||||
-rw-r--r-- | resources/views/dashboard/transfer.edge | 52 | ||||
-rw-r--r-- | start/routes.js | 3 |
5 files changed, 178 insertions, 0 deletions
diff --git a/app/Controllers/Http/DashboardController.js b/app/Controllers/Http/DashboardController.js index 49f6cc0..fe179c9 100644 --- a/app/Controllers/Http/DashboardController.js +++ b/app/Controllers/Http/DashboardController.js | |||
@@ -3,7 +3,11 @@ const { | |||
3 | validateAll, | 3 | validateAll, |
4 | } = use('Validator'); | 4 | } = use('Validator'); |
5 | 5 | ||
6 | const Service = use('App/Models/Service'); | ||
7 | const Workspace = use('App/Models/Workspace'); | ||
8 | |||
6 | const crypto = require('crypto'); | 9 | const crypto = require('crypto'); |
10 | const uuid = require('uuid/v4'); | ||
7 | 11 | ||
8 | class DashboardController { | 12 | class DashboardController { |
9 | async login({ | 13 | async login({ |
@@ -135,6 +139,108 @@ class DashboardController { | |||
135 | }); | 139 | }); |
136 | } | 140 | } |
137 | 141 | ||
142 | async export({ | ||
143 | auth, | ||
144 | response, | ||
145 | }) { | ||
146 | const general = auth.user; | ||
147 | const services = (await auth.user.services().fetch()).toJSON(); | ||
148 | const workspaces = (await auth.user.workspaces().fetch()).toJSON(); | ||
149 | |||
150 | const exportData = { | ||
151 | username: general.username, | ||
152 | mail: general.email, | ||
153 | services, | ||
154 | workspaces, | ||
155 | }; | ||
156 | |||
157 | return response | ||
158 | .header('Content-Type', 'application/force-download') | ||
159 | .header('Content-disposition', 'attachment; filename=export.ferdi-data') | ||
160 | .send(exportData); | ||
161 | } | ||
162 | |||
163 | async import({ | ||
164 | auth, | ||
165 | request, | ||
166 | session, | ||
167 | response, | ||
168 | }) { | ||
169 | let validation = await validateAll(request.all(), { | ||
170 | file: 'required', | ||
171 | }); | ||
172 | if (validation.fails()) { | ||
173 | session.withErrors(validation.messages()).flashExcept(['password']); | ||
174 | return response.redirect('back'); | ||
175 | } | ||
176 | |||
177 | let file; | ||
178 | try { | ||
179 | file = JSON.parse(request.input('file')); | ||
180 | } catch(e) { | ||
181 | session.flash({ type: 'danger', message: 'Invalid Ferdi account file' }) | ||
182 | return response.redirect('back'); | ||
183 | } | ||
184 | console.log(file); | ||
185 | |||
186 | if(!file || !file.services || !file.workspaces) { | ||
187 | session.flash({ type: 'danger', message: 'Invalid Ferdi account file (2)' }) | ||
188 | return response.redirect('back'); | ||
189 | } | ||
190 | |||
191 | const serviceIdTranslation = {}; | ||
192 | |||
193 | // Import services | ||
194 | try { | ||
195 | for (const service of file.services) { | ||
196 | // Get new, unused uuid | ||
197 | let serviceId; | ||
198 | do { | ||
199 | serviceId = uuid(); | ||
200 | } while ((await Service.query().where('serviceId', serviceId).fetch()).rows.length > 0); // eslint-disable-line no-await-in-loop | ||
201 | |||
202 | await Service.create({ // eslint-disable-line no-await-in-loop | ||
203 | userId: auth.user.id, | ||
204 | serviceId, | ||
205 | name: service.name, | ||
206 | recipeId: service.recipeId, | ||
207 | settings: JSON.stringify(service.settings), | ||
208 | }); | ||
209 | |||
210 | serviceIdTranslation[service.id] = serviceId; | ||
211 | } | ||
212 | } catch (e) { | ||
213 | const errorMessage = `Could not import your services into our system.\nError: ${e}`; | ||
214 | return response.status(401).send(errorMessage); | ||
215 | } | ||
216 | |||
217 | // Import workspaces | ||
218 | try { | ||
219 | for (const workspace of file.workspaces) { | ||
220 | let workspaceId; | ||
221 | do { | ||
222 | workspaceId = uuid(); | ||
223 | } while ((await Workspace.query().where('workspaceId', workspaceId).fetch()).rows.length > 0); // eslint-disable-line no-await-in-loop | ||
224 | |||
225 | const services = workspace.services.map((service) => serviceIdTranslation[service]); | ||
226 | |||
227 | await Workspace.create({ // eslint-disable-line no-await-in-loop | ||
228 | userId: auth.user.id, | ||
229 | workspaceId, | ||
230 | name: workspace.name, | ||
231 | order: workspace.order, | ||
232 | services: JSON.stringify(services), | ||
233 | data: JSON.stringify(workspace.data), | ||
234 | }); | ||
235 | } | ||
236 | } catch (e) { | ||
237 | const errorMessage = `Could not import your workspaces into our system.\nError: ${e}`; | ||
238 | return response.status(401).send(errorMessage); | ||
239 | } | ||
240 | |||
241 | return response.send('Your account has been imported.'); | ||
242 | } | ||
243 | |||
138 | logout({ | 244 | logout({ |
139 | auth, | 245 | auth, |
140 | response, | 246 | response, |
diff --git a/public/js/transfer.js b/public/js/transfer.js new file mode 100644 index 0000000..d3d23b3 --- /dev/null +++ b/public/js/transfer.js | |||
@@ -0,0 +1,14 @@ | |||
1 | /* eslint-env browser */ | ||
2 | const submitBtn = document.getElementById('submit'); | ||
3 | const fileInput = document.getElementById('file'); | ||
4 | const fileOutput = document.getElementById('fileoutput'); | ||
5 | |||
6 | fileInput.addEventListener('change', () => { | ||
7 | const reader = new FileReader(); | ||
8 | reader.onload = function () { | ||
9 | const text = reader.result; | ||
10 | fileOutput.value = text; | ||
11 | submitBtn.disabled = false; | ||
12 | }; | ||
13 | reader.readAsText(fileInput.files[0]); | ||
14 | }) | ||
diff --git a/resources/views/dashboard/account.edge b/resources/views/dashboard/account.edge index 0361fa4..a91aa11 100644 --- a/resources/views/dashboard/account.edge +++ b/resources/views/dashboard/account.edge | |||
@@ -55,6 +55,9 @@ | |||
55 | <a class="button" href="/user/data" style="margin-bottom:1rem;">My account data</a> | 55 | <a class="button" href="/user/data" style="margin-bottom:1rem;">My account data</a> |
56 | </div> | 56 | </div> |
57 | <div> | 57 | <div> |
58 | <a class="button" href="/user/transfer" style="margin-bottom:1rem;">Import/Export account data</a> | ||
59 | </div> | ||
60 | <div> | ||
58 | <a class="button" href="/user/logout">Logout</a> | 61 | <a class="button" href="/user/logout">Logout</a> |
59 | </div> | 62 | </div> |
60 | 63 | ||
diff --git a/resources/views/dashboard/transfer.edge b/resources/views/dashboard/transfer.edge new file mode 100644 index 0000000..94efeb0 --- /dev/null +++ b/resources/views/dashboard/transfer.edge | |||
@@ -0,0 +1,52 @@ | |||
1 | @layout('layouts.main') | ||
2 | |||
3 | @section('content') | ||
4 | <h2>Import/Export data from another Ferdi server</h2> | ||
5 | @if(flashMessage('error')) | ||
6 | <div class="alert"> | ||
7 | {{ flashMessage('error') }} | ||
8 | </div> | ||
9 | @endif | ||
10 | @if(old('message')) | ||
11 | <div class="alert"> | ||
12 | {{ old('message') }} | ||
13 | </div> | ||
14 | @endif | ||
15 | @if(flashMessage('notification')) | ||
16 | <div class="alert"> | ||
17 | {{ flashMessage('notification.message') }} | ||
18 | </div> | ||
19 | @endif | ||
20 | @if(success === true) | ||
21 | <div class="alert" style="background-color:#28C76F;"> | ||
22 | Sucessfully imported your account data | ||
23 | </div> | ||
24 | @endif | ||
25 | |||
26 | <h3>Import data</h3> | ||
27 | <div> | ||
28 | <label>Account data</label> | ||
29 | <div> | ||
30 | <input type="file" name="file" id="file" value="" accept=".json,.ferdi-data" required> | ||
31 | </div> | ||
32 | </div> | ||
33 | |||
34 | <form action="/user/transfer" method="POST"> | ||
35 | {{ csrfField() }} | ||
36 | <input type="hidden" name="file" id="fileoutput" value=""> | ||
37 | <div> | ||
38 | <button style="background-color:#28C76F;margin-bottom:1rem;" id="submit" disabled>Import data</button> | ||
39 | </div> | ||
40 | </form> | ||
41 | |||
42 | <h3>Export data</h3> | ||
43 | <a class="button" style="background-color:#28C76F;margin-bottom:1rem;" href="/user/export">Export data</a> | ||
44 | |||
45 | <div> | ||
46 | <a class="button" href="/user/account">Back to my account</a> | ||
47 | </div> | ||
48 | |||
49 | </div> | ||
50 | <script src="/js/transfer.js"></script> | ||
51 | |||
52 | @endsection \ No newline at end of file | ||
diff --git a/start/routes.js b/start/routes.js index 4373b75..7243079 100644 --- a/start/routes.js +++ b/start/routes.js | |||
@@ -67,6 +67,9 @@ Route.group(() => { | |||
67 | Route.get('account', 'DashboardController.account').middleware('auth:session'); | 67 | Route.get('account', 'DashboardController.account').middleware('auth:session'); |
68 | Route.post('account', 'DashboardController.edit').middleware('auth:session'); | 68 | Route.post('account', 'DashboardController.edit').middleware('auth:session'); |
69 | Route.get('data', 'DashboardController.data').middleware('auth:session'); | 69 | Route.get('data', 'DashboardController.data').middleware('auth:session'); |
70 | Route.get('export', 'DashboardController.export').middleware('auth:session'); | ||
71 | Route.post('transfer', 'DashboardController.import').middleware('auth:session'); | ||
72 | Route.get('transfer', ({ view }) => view.render('dashboard.transfer')).middleware('auth:session'); | ||
70 | Route.get('delete', ({ view }) => view.render('dashboard.delete')).middleware('auth:session'); | 73 | Route.get('delete', ({ view }) => view.render('dashboard.delete')).middleware('auth:session'); |
71 | Route.post('delete', 'DashboardController.delete').middleware('auth:session'); | 74 | Route.post('delete', 'DashboardController.delete').middleware('auth:session'); |
72 | Route.get('logout', 'DashboardController.logout').middleware('auth:session'); | 75 | Route.get('logout', 'DashboardController.logout').middleware('auth:session'); |