Skip to content
Permalink
Browse files

Add page to transfer user accounts

  • Loading branch information
vantezzen committed Oct 19, 2019
1 parent 8314a07 commit 5bf9eb5898f14afc66f690c866637b4815195bad
Showing with 178 additions and 0 deletions.
  1. +106 βˆ’0 app/Controllers/Http/DashboardController.js
  2. +14 βˆ’0 public/js/transfer.js
  3. +3 βˆ’0 resources/views/dashboard/account.edge
  4. +52 βˆ’0 resources/views/dashboard/transfer.edge
  5. +3 βˆ’0 start/routes.js
@@ -3,7 +3,11 @@ const {
validateAll,
} = use('Validator');

const Service = use('App/Models/Service');
const Workspace = use('App/Models/Workspace');

const crypto = require('crypto');
const uuid = require('uuid/v4');

class DashboardController {
async login({
@@ -135,6 +139,108 @@ class DashboardController {
});
}

async export({
auth,
response,
}) {
const general = auth.user;
const services = (await auth.user.services().fetch()).toJSON();
const workspaces = (await auth.user.workspaces().fetch()).toJSON();

const exportData = {
username: general.username,
mail: general.email,
services,
workspaces,
};

return response
.header('Content-Type', 'application/force-download')
.header('Content-disposition', 'attachment; filename=export.ferdi-data')
.send(exportData);
}

async import({
auth,
request,
session,
response,
}) {
let validation = await validateAll(request.all(), {
file: 'required',
});
if (validation.fails()) {
session.withErrors(validation.messages()).flashExcept(['password']);
return response.redirect('back');
}

let file;
try {
file = JSON.parse(request.input('file'));
} catch(e) {
session.flash({ type: 'danger', message: 'Invalid Ferdi account file' })
return response.redirect('back');
}
console.log(file);

if(!file || !file.services || !file.workspaces) {
session.flash({ type: 'danger', message: 'Invalid Ferdi account file (2)' })
return response.redirect('back');
}

const serviceIdTranslation = {};

// Import services
try {
for (const service of file.services) {
// Get new, unused uuid
let serviceId;
do {
serviceId = uuid();
} while ((await Service.query().where('serviceId', serviceId).fetch()).rows.length > 0); // eslint-disable-line no-await-in-loop

await Service.create({ // eslint-disable-line no-await-in-loop
userId: auth.user.id,
serviceId,
name: service.name,
recipeId: service.recipeId,
settings: JSON.stringify(service.settings),
});

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 {
for (const workspace of file.workspaces) {
let workspaceId;
do {
workspaceId = uuid();
} while ((await Workspace.query().where('workspaceId', workspaceId).fetch()).rows.length > 0); // eslint-disable-line no-await-in-loop

const services = workspace.services.map((service) => serviceIdTranslation[service]);

await Workspace.create({ // eslint-disable-line no-await-in-loop
userId: auth.user.id,
workspaceId,
name: workspace.name,
order: workspace.order,
services: JSON.stringify(services),
data: JSON.stringify(workspace.data),
});
}
} 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.');
}

logout({
auth,
response,
@@ -0,0 +1,14 @@
/* eslint-env browser */
const submitBtn = document.getElementById('submit');
const fileInput = document.getElementById('file');
const fileOutput = document.getElementById('fileoutput');

fileInput.addEventListener('change', () => {
const reader = new FileReader();
reader.onload = function () {
const text = reader.result;
fileOutput.value = text;
submitBtn.disabled = false;
};
reader.readAsText(fileInput.files[0]);
})
@@ -54,6 +54,9 @@
<div>
<a class="button" href="/user/data" style="margin-bottom:1rem;">My account data</a>
</div>
<div>
<a class="button" href="/user/transfer" style="margin-bottom:1rem;">Import/Export account data</a>
</div>
<div>
<a class="button" href="/user/logout">Logout</a>
</div>
@@ -0,0 +1,52 @@
@layout('layouts.main')

@section('content')
<h2>Import/Export data from another Ferdi server</h2>
@if(flashMessage('error'))
<div class="alert">
{{ flashMessage('error') }}
</div>
@endif
@if(old('message'))
<div class="alert">
{{ old('message') }}
</div>
@endif
@if(flashMessage('notification'))
<div class="alert">
{{ flashMessage('notification.message') }}
</div>
@endif
@if(success === true)
<div class="alert" style="background-color:#28C76F;">
Sucessfully imported your account data
</div>
@endif

<h3>Import data</h3>
<div>
<label>Account data</label>
<div>
<input type="file" name="file" id="file" value="" accept=".json,.ferdi-data" required>
</div>
</div>

<form action="/user/transfer" method="POST">
{{ csrfField() }}
<input type="hidden" name="file" id="fileoutput" value="">
<div>
<button style="background-color:#28C76F;margin-bottom:1rem;" id="submit" disabled>Import data</button>
</div>
</form>

<h3>Export data</h3>
<a class="button" style="background-color:#28C76F;margin-bottom:1rem;" href="/user/export">Export data</a>

<div>
<a class="button" href="/user/account">Back to my account</a>
</div>

</div>
<script src="/js/transfer.js"></script>

@endsection
@@ -67,6 +67,9 @@ Route.group(() => {
Route.get('account', 'DashboardController.account').middleware('auth:session');
Route.post('account', 'DashboardController.edit').middleware('auth:session');
Route.get('data', 'DashboardController.data').middleware('auth:session');
Route.get('export', 'DashboardController.export').middleware('auth:session');
Route.post('transfer', 'DashboardController.import').middleware('auth:session');
Route.get('transfer', ({ view }) => view.render('dashboard.transfer')).middleware('auth:session');
Route.get('delete', ({ view }) => view.render('dashboard.delete')).middleware('auth:session');
Route.post('delete', 'DashboardController.delete').middleware('auth:session');
Route.get('logout', 'DashboardController.logout').middleware('auth:session');

0 comments on commit 5bf9eb5

Please sign in to comment.
You can’t perform that action at this time.