From 4f39dce953ea8e197105ee9042fa376c120087ce Mon Sep 17 00:00:00 2001 From: vantezzen Date: Wed, 28 Aug 2019 12:10:16 +0200 Subject: Add import Franz account feature --- app/Controllers/Http/UserController.js | 170 +++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) (limited to 'app/Controllers/Http/UserController.js') 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 @@ 'use strict' const User = use('App/Models/User'); +const Service = use('App/Models/Service'); +const Workspace = use('App/Models/Workspace'); const { validateAll } = use('Validator'); + const atob = require('atob'); +const btoa = require('btoa'); +const fetch = require('node-fetch'); +const uuid = require('uuid/v4'); +const crypto = require('crypto'); + +const franzRequest = async (route, method, auth) => { + return new Promise(async (resolve, reject) => { + const base = 'https://api.franzinfra.com/v1/'; + 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'; + + try { + const rawResponse = await fetch(base + route, { + method, + headers: { + 'Authorization': 'Bearer ' + auth, + 'User-Agent': user + }, + }); + const content = await rawResponse.json(); + + resolve(content); + } catch (e) { + reject(); + } + }) +} class UserController { @@ -127,6 +156,147 @@ class UserController { locale: "en-US" }); } + + + + async import({ + request, + response + }) { + // Validate user input + const validation = await validateAll(request.all(), { + email: 'required|email|unique:users,email', + password: 'required' + }); + if (validation.fails()) { + let errorMessage = "There was an error while trying to import your account:\n"; + for (const message of validation.messages()) { + if (message.validation == 'required') { + errorMessage += '- Please make sure to supply your ' + message.field + '\n' + } else if (message.validation == 'unique') { + errorMessage += '- There is already a user with this email.\n' + } else { + errorMessage += message.message + '\n'; + } + } + return response.status(401).send(errorMessage) + } + + const { + email, + password + } = request.all() + + const hashedPassword = crypto.createHash('sha256').update(password).digest('base64'); + + const base = 'https://api.franzinfra.com/v1/'; + 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'; + + // Try to get an authentication token + let token; + try { + const basicToken = btoa(email + ':' + hashedPassword) + + const rawResponse = await fetch(base + 'auth/login', { + method: 'POST', + headers: { + 'Authorization': 'Basic ' + basicToken, + 'User-Agent': userAgent + }, + }); + const content = await rawResponse.json(); + + if (!content.message || content.message !== 'Successfully logged in') { + const errorMessage = 'Could not login into Franz with your supplied credentials. Please check and try again'; + return response.status(401).send(errorMessage) + } + + token = content.token; + } catch (e) { + return response.status(401).send({ + "message": "Cannot login to Franz", + "error": e + }) + } + + // Get user information + let userInf; + try { + userInf = await franzRequest('me', 'GET', token) + } catch (e) { + const errorMessage = 'Could not get your user info from Franz. Please check your credentials or try again later.\nError: ' + e; + return response.status(401).send(errorMessage) + } + + // Create user in DB + let user; + try { + user = await User.create({ + email: userInf.email, + password: hashedPassword, + username: userInf.firstname + }); + } catch (e) { + const errorMessage = 'Could not create your user in our system.\nError: ' + e; + return response.status(401).send(errorMessage) + } + + let serviceIdTranslation = {}; + + // Import services + try { + const services = await franzRequest('me/services', 'GET', token) + + for (const service of services) { + // Get new, unused uuid + let serviceId; + do { + serviceId = uuid(); + } while ((await Service.query().where('serviceId', serviceId).fetch()).rows.length > 0) + + await Service.create({ + userId: user.id, + serviceId, + name: service.name, + recipeId: service.recipeId, + settings: JSON.stringify(service) + }); + + serviceIdTranslation[service.id] = serviceId; + } + } catch (e) { + const errorMessage = 'Could not import your services into our system.\nError: ' + e; + return response.status(401).send(errorMessage) + } + + // Import workspaces + try { + const workspaces = await franzRequest('workspace', 'GET', token) + + for (const workspace of workspaces) { + let workspaceId; + do { + workspaceId = uuid(); + } while ((await Workspace.query().where('workspaceId', workspaceId).fetch()).rows.length > 0) + + const services = workspace.services.map(service => serviceIdTranslation[service]) + + await Workspace.create({ + userId: auth.user.id, + workspaceId, + name: workspace.name, + order: workspace.order, + services: JSON.stringify(services), + data: JSON.stringify({}) + }); + } + } catch (e) { + const errorMessage = 'Could not import your workspaces into our system.\nError: ' + e; + return response.status(401).send(errorMessage) + } + + return response.send('Your account has been imported. You can now use your Franz account in Ferdi.') + } } module.exports = UserController -- cgit v1.2.3-54-g00ecf