diff options
author | 2019-08-28 12:10:16 +0200 | |
---|---|---|
committer | 2019-08-28 12:10:16 +0200 | |
commit | 4f39dce953ea8e197105ee9042fa376c120087ce (patch) | |
tree | da953ea5837b8547c04ec1d3de383dfd4f2ca502 /app/Controllers/Http | |
parent | Merge branch 'master' of https://github.com/vantezzen/ferdi-server (diff) | |
download | ferdium-server-4f39dce953ea8e197105ee9042fa376c120087ce.tar.gz ferdium-server-4f39dce953ea8e197105ee9042fa376c120087ce.tar.zst ferdium-server-4f39dce953ea8e197105ee9042fa376c120087ce.zip |
Add import Franz account feature
Diffstat (limited to 'app/Controllers/Http')
-rw-r--r-- | app/Controllers/Http/UserController.js | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/app/Controllers/Http/UserController.js b/app/Controllers/Http/UserController.js index 084b023..7c6cece 100644 --- a/app/Controllers/Http/UserController.js +++ b/app/Controllers/Http/UserController.js | |||
@@ -1,10 +1,39 @@ | |||
1 | 'use strict' | 1 | 'use strict' |
2 | 2 | ||
3 | const User = use('App/Models/User'); | 3 | const User = use('App/Models/User'); |
4 | const Service = use('App/Models/Service'); | ||
5 | const Workspace = use('App/Models/Workspace'); | ||
4 | const { | 6 | const { |
5 | validateAll | 7 | validateAll |
6 | } = use('Validator'); | 8 | } = use('Validator'); |
9 | |||
7 | const atob = require('atob'); | 10 | const atob = require('atob'); |
11 | const btoa = require('btoa'); | ||
12 | const fetch = require('node-fetch'); | ||
13 | const uuid = require('uuid/v4'); | ||
14 | const crypto = require('crypto'); | ||
15 | |||
16 | const franzRequest = async (route, method, auth) => { | ||
17 | return new Promise(async (resolve, reject) => { | ||
18 | const base = 'https://api.franzinfra.com/v1/'; | ||
19 | const user = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Ferdi/5.3.0-beta.1 Chrome/69.0.3497.128 Electron/4.2.4 Safari/537.36'; | ||
20 | |||
21 | try { | ||
22 | const rawResponse = await fetch(base + route, { | ||
23 | method, | ||
24 | headers: { | ||
25 | 'Authorization': 'Bearer ' + auth, | ||
26 | 'User-Agent': user | ||
27 | }, | ||
28 | }); | ||
29 | const content = await rawResponse.json(); | ||
30 | |||
31 | resolve(content); | ||
32 | } catch (e) { | ||
33 | reject(); | ||
34 | } | ||
35 | }) | ||
36 | } | ||
8 | 37 | ||
9 | class UserController { | 38 | class UserController { |
10 | 39 | ||
@@ -127,6 +156,147 @@ class UserController { | |||
127 | locale: "en-US" | 156 | locale: "en-US" |
128 | }); | 157 | }); |
129 | } | 158 | } |
159 | |||
160 | |||
161 | |||
162 | async import({ | ||
163 | request, | ||
164 | response | ||
165 | }) { | ||
166 | // Validate user input | ||
167 | const validation = await validateAll(request.all(), { | ||
168 | email: 'required|email|unique:users,email', | ||
169 | password: 'required' | ||
170 | }); | ||
171 | if (validation.fails()) { | ||
172 | let errorMessage = "There was an error while trying to import your account:\n"; | ||
173 | for (const message of validation.messages()) { | ||
174 | if (message.validation == 'required') { | ||
175 | errorMessage += '- Please make sure to supply your ' + message.field + '\n' | ||
176 | } else if (message.validation == 'unique') { | ||
177 | errorMessage += '- There is already a user with this email.\n' | ||
178 | } else { | ||
179 | errorMessage += message.message + '\n'; | ||
180 | } | ||
181 | } | ||
182 | return response.status(401).send(errorMessage) | ||
183 | } | ||
184 | |||
185 | const { | ||
186 | email, | ||
187 | password | ||
188 | } = request.all() | ||
189 | |||
190 | const hashedPassword = crypto.createHash('sha256').update(password).digest('base64'); | ||
191 | |||
192 | const base = 'https://api.franzinfra.com/v1/'; | ||
193 | const userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Ferdi/5.3.0-beta.1 Chrome/69.0.3497.128 Electron/4.2.4 Safari/537.36'; | ||
194 | |||
195 | // Try to get an authentication token | ||
196 | let token; | ||
197 | try { | ||
198 | const basicToken = btoa(email + ':' + hashedPassword) | ||
199 | |||
200 | const rawResponse = await fetch(base + 'auth/login', { | ||
201 | method: 'POST', | ||
202 | headers: { | ||
203 | 'Authorization': 'Basic ' + basicToken, | ||
204 | 'User-Agent': userAgent | ||
205 | }, | ||
206 | }); | ||
207 | const content = await rawResponse.json(); | ||
208 | |||
209 | if (!content.message || content.message !== 'Successfully logged in') { | ||
210 | const errorMessage = 'Could not login into Franz with your supplied credentials. Please check and try again'; | ||
211 | return response.status(401).send(errorMessage) | ||
212 | } | ||
213 | |||
214 | token = content.token; | ||
215 | } catch (e) { | ||
216 | return response.status(401).send({ | ||
217 | "message": "Cannot login to Franz", | ||
218 | "error": e | ||
219 | }) | ||
220 | } | ||
221 | |||
222 | // Get user information | ||
223 | let userInf; | ||
224 | try { | ||
225 | userInf = await franzRequest('me', 'GET', token) | ||
226 | } catch (e) { | ||
227 | const errorMessage = 'Could not get your user info from Franz. Please check your credentials or try again later.\nError: ' + e; | ||
228 | return response.status(401).send(errorMessage) | ||
229 | } | ||
230 | |||
231 | // Create user in DB | ||
232 | let user; | ||
233 | try { | ||
234 | user = await User.create({ | ||
235 | email: userInf.email, | ||
236 | password: hashedPassword, | ||
237 | username: userInf.firstname | ||
238 | }); | ||
239 | } catch (e) { | ||
240 | const errorMessage = 'Could not create your user in our system.\nError: ' + e; | ||
241 | return response.status(401).send(errorMessage) | ||
242 | } | ||
243 | |||
244 | let serviceIdTranslation = {}; | ||
245 | |||
246 | // Import services | ||
247 | try { | ||
248 | const services = await franzRequest('me/services', 'GET', token) | ||
249 | |||
250 | for (const service of services) { | ||
251 | // Get new, unused uuid | ||
252 | let serviceId; | ||
253 | do { | ||
254 | serviceId = uuid(); | ||
255 | } while ((await Service.query().where('serviceId', serviceId).fetch()).rows.length > 0) | ||
256 | |||
257 | await Service.create({ | ||
258 | userId: user.id, | ||
259 | serviceId, | ||
260 | name: service.name, | ||
261 | recipeId: service.recipeId, | ||
262 | settings: JSON.stringify(service) | ||
263 | }); | ||
264 | |||
265 | serviceIdTranslation[service.id] = serviceId; | ||
266 | } | ||
267 | } catch (e) { | ||
268 | const errorMessage = 'Could not import your services into our system.\nError: ' + e; | ||
269 | return response.status(401).send(errorMessage) | ||
270 | } | ||
271 | |||
272 | // Import workspaces | ||
273 | try { | ||
274 | const workspaces = await franzRequest('workspace', 'GET', token) | ||
275 | |||
276 | for (const workspace of workspaces) { | ||
277 | let workspaceId; | ||
278 | do { | ||
279 | workspaceId = uuid(); | ||
280 | } while ((await Workspace.query().where('workspaceId', workspaceId).fetch()).rows.length > 0) | ||
281 | |||
282 | const services = workspace.services.map(service => serviceIdTranslation[service]) | ||
283 | |||
284 | await Workspace.create({ | ||
285 | userId: auth.user.id, | ||
286 | workspaceId, | ||
287 | name: workspace.name, | ||
288 | order: workspace.order, | ||
289 | services: JSON.stringify(services), | ||
290 | data: JSON.stringify({}) | ||
291 | }); | ||
292 | } | ||
293 | } catch (e) { | ||
294 | const errorMessage = 'Could not import your workspaces into our system.\nError: ' + e; | ||
295 | return response.status(401).send(errorMessage) | ||
296 | } | ||
297 | |||
298 | return response.send('Your account has been imported. You can now use your Franz account in Ferdi.') | ||
299 | } | ||
130 | } | 300 | } |
131 | 301 | ||
132 | module.exports = UserController | 302 | module.exports = UserController |