From 0ecb26fcc0d1358efeafc1512dfb67ac6695382b Mon Sep 17 00:00:00 2001 From: Bennett Date: Tue, 10 Mar 2020 15:07:46 +0100 Subject: Fix lint --- app/Controllers/Http/DashboardController.js | 10 +-- app/Controllers/Http/RecipeController.js | 9 +-- app/Controllers/Http/ServiceController.js | 103 ++++++++++++++-------------- app/Controllers/Http/StaticController.js | 36 +++++----- app/Controllers/Http/UserController.js | 8 +-- app/Controllers/Http/WorkspaceController.js | 2 +- 6 files changed, 84 insertions(+), 84 deletions(-) (limited to 'app') diff --git a/app/Controllers/Http/DashboardController.js b/app/Controllers/Http/DashboardController.js index fe179c9..1018f78 100644 --- a/app/Controllers/Http/DashboardController.js +++ b/app/Controllers/Http/DashboardController.js @@ -166,7 +166,7 @@ class DashboardController { session, response, }) { - let validation = await validateAll(request.all(), { + const validation = await validateAll(request.all(), { file: 'required', }); if (validation.fails()) { @@ -177,14 +177,14 @@ class DashboardController { let file; try { file = JSON.parse(request.input('file')); - } catch(e) { - session.flash({ type: 'danger', message: 'Invalid Ferdi account 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)' }) + if (!file || !file.services || !file.workspaces) { + session.flash({ type: 'danger', message: 'Invalid Ferdi account file (2)' }); return response.redirect('back'); } diff --git a/app/Controllers/Http/RecipeController.js b/app/Controllers/Http/RecipeController.js index 6c8aa21..9bfd92e 100644 --- a/app/Controllers/Http/RecipeController.js +++ b/app/Controllers/Http/RecipeController.js @@ -35,7 +35,7 @@ class RecipeController { const customRecipes = customRecipesArray.map((recipe) => ({ id: recipe.recipeId, name: recipe.name, - ...typeof recipe.data === "string" ? JSON.parse(recipe.data) : recipe.data, + ...typeof recipe.data === 'string' ? JSON.parse(recipe.data) : recipe.data, })); const recipes = [ @@ -144,7 +144,7 @@ class RecipeController { results = dbResults.map((recipe) => ({ id: recipe.recipeId, name: recipe.name, - ...typeof recipe.data === "string" ? JSON.parse(recipe.data) : recipe.data, + ...typeof recipe.data === 'string' ? JSON.parse(recipe.data) : recipe.data, })); } else { let remoteResults = []; @@ -155,7 +155,7 @@ class RecipeController { const localResults = localResultsArray.map((recipe) => ({ id: recipe.recipeId, name: recipe.name, - ...typeof recipe.data === "string" ? JSON.parse(recipe.data) : recipe.data, + ...typeof recipe.data === 'string' ? JSON.parse(recipe.data) : recipe.data, })); results = [ @@ -194,7 +194,8 @@ class RecipeController { // Check if recipe exists in recipes folder if (await Drive.exists(`${service}.tar.gz`)) { return response.send(await Drive.get(`${service}.tar.gz`)); - } else if (Env.get('CONNECT_WITH_FRANZ') == 'true') { // eslint-disable-line eqeqeq + } + if (Env.get('CONNECT_WITH_FRANZ') == 'true') { // eslint-disable-line eqeqeq return response.redirect(`https://api.franzinfra.com/v1/recipes/download/${service}`); } return response.status(400).send({ diff --git a/app/Controllers/Http/ServiceController.js b/app/Controllers/Http/ServiceController.js index 90055b6..a1d26cb 100644 --- a/app/Controllers/Http/ServiceController.js +++ b/app/Controllers/Http/ServiceController.js @@ -86,7 +86,7 @@ class ServiceController { const services = (await auth.user.services().fetch()).rows; // Convert to array with all data Franz wants const servicesArray = services.map((service) => { - const settings = typeof service.settings === "string" ? JSON.parse(service.settings) : service.settings; + const settings = typeof service.settings === 'string' ? JSON.parse(service.settings) : service.settings; return { customRecipe: false, @@ -105,7 +105,7 @@ class ServiceController { name: service.name, recipeId: service.recipeId, userId: auth.user.id, - } + }; }); return response.send(servicesArray); @@ -127,7 +127,7 @@ class ServiceController { // Upload custom service icon const icon = request.file('icon', { types: ['image'], - size: '2mb' + size: '2mb', }); const { id, @@ -135,17 +135,17 @@ class ServiceController { const service = (await Service.query() .where('serviceId', id) .where('userId', auth.user.id).fetch()).rows[0]; - const settings = typeof service.settings === "string" ? JSON.parse(service.settings) : service.settings; + const settings = typeof service.settings === 'string' ? JSON.parse(service.settings) : service.settings; let iconId; do { iconId = uuid() + uuid(); - } while(await fs.exists(path.join(Helpers.tmpPath('uploads'), iconId))); - + } while (await fs.exists(path.join(Helpers.tmpPath('uploads'), iconId))); + await icon.move(Helpers.tmpPath('uploads'), { name: iconId, - overwrite: true - }) + overwrite: true, + }); if (!icon.moved()) { return response.status(500).send(icon.error()); @@ -175,49 +175,48 @@ class ServiceController { iconUrl: `${Env.get('APP_URL')}/v1/icon/${newSettings.iconId}`, userId: auth.user.id, }, - status: ["updated"] + status: ['updated'], }); - } else { - // Update service info - const data = request.all(); - const { - id, - } = params; + } + // Update service info + const data = request.all(); + const { + id, + } = params; - // Get current settings from db - const serviceData = (await Service.query() - .where('serviceId', id) - .where('userId', auth.user.id).fetch()).rows[0]; + // Get current settings from db + const serviceData = (await Service.query() + .where('serviceId', id) + .where('userId', auth.user.id).fetch()).rows[0]; - const settings = { - ...typeof serviceData.settings === "string" ? JSON.parse(serviceData.settings) : serviceData.settings, - ...data, - }; + const settings = { + ...typeof serviceData.settings === 'string' ? JSON.parse(serviceData.settings) : serviceData.settings, + ...data, + }; - // Update data in database - await (Service.query() - .where('serviceId', id) - .where('userId', auth.user.id)).update({ - name: data.name, - settings: JSON.stringify(settings), - }); + // Update data in database + await (Service.query() + .where('serviceId', id) + .where('userId', auth.user.id)).update({ + name: data.name, + settings: JSON.stringify(settings), + }); - // Get updated row - const service = (await Service.query() - .where('serviceId', id) - .where('userId', auth.user.id).fetch()).rows[0]; + // Get updated row + const service = (await Service.query() + .where('serviceId', id) + .where('userId', auth.user.id).fetch()).rows[0]; - return response.send({ - data: { - id, - name: service.name, - ...settings, - iconUrl: `${Env.get('APP_URL')}/v1/icon/${settings.iconId}`, - userId: auth.user.id, - }, - status: ["updated"] - }); - } + return response.send({ + data: { + id, + name: service.name, + ...settings, + iconUrl: `${Env.get('APP_URL')}/v1/icon/${settings.iconId}`, + userId: auth.user.id, + }, + status: ['updated'], + }); } async icon({ @@ -231,7 +230,7 @@ class ServiceController { const iconPath = path.join(Helpers.tmpPath('uploads'), id); if (!await fs.exists(iconPath)) { return response.status(404).send({ - status: 'Icon doesn\'t exist' + status: 'Icon doesn\'t exist', }); } @@ -252,7 +251,7 @@ class ServiceController { .where('userId', auth.user.id).fetch()).rows[0]; const settings = { - ...typeof serviceData.settings === "string" ? JSON.parse(serviceData.settings) : serviceData.settings, + ...typeof serviceData.settings === 'string' ? JSON.parse(serviceData.settings) : serviceData.settings, order: data[service], }; @@ -260,16 +259,16 @@ class ServiceController { await (Service.query() // eslint-disable-line no-await-in-loop .where('serviceId', service) .where('userId', auth.user.id)) - .update({ - settings: JSON.stringify(settings), - }); + .update({ + settings: JSON.stringify(settings), + }); } // Get new services const services = (await auth.user.services().fetch()).rows; // Convert to array with all data Franz wants const servicesArray = services.map((service) => { - const settings = typeof service.settings === "string" ? JSON.parse(service.settings) : service.settings; + const settings = typeof service.settings === 'string' ? JSON.parse(service.settings) : service.settings; return { customRecipe: false, @@ -288,7 +287,7 @@ class ServiceController { name: service.name, recipeId: service.recipeId, userId: auth.user.id, - } + }; }); return response.send(servicesArray); diff --git a/app/Controllers/Http/StaticController.js b/app/Controllers/Http/StaticController.js index 265578f..cd38b13 100644 --- a/app/Controllers/Http/StaticController.js +++ b/app/Controllers/Http/StaticController.js @@ -31,44 +31,44 @@ class StaticController { isTeamManagementIncludedInCurrentPlan: true, isTodosEnabled: true, isTodosIncludedInCurrentPlan: true, - defaultTrialPlan: "franz-pro-yearly", - subscribeURL: "https://getferdi.com", - planSelectionURL: "https://getferdi.com", + defaultTrialPlan: 'franz-pro-yearly', + subscribeURL: 'https://getferdi.com', + planSelectionURL: 'https://getferdi.com', isMagicBarEnabled: true, hasInlineCheckout: true, isPlanSelectionEnabled: false, isTrialStatusBarEnabled: false, canSkipTrial: true, pricingConfig: { - currency: "$", - currencyID: "USD", + currency: '$', + currencyID: 'USD', plans: { personal: { monthly: { - id: "ferdi-free", + id: 'ferdi-free', price: 0, - billed: 0 + billed: 0, }, yearly: { - id: "ferdi-completely-free", + id: 'ferdi-completely-free', price: 0, - billed: 0 - } + billed: 0, + }, }, pro: { monthly: { - id: "ferdi-still-free", + id: 'ferdi-still-free', price: 0, - billed: 0 + billed: 0, }, yearly: { - id: "ferdi-forever-free", + id: 'ferdi-forever-free', price: 0, - billed: 0 - } - } - } - } + billed: 0, + }, + }, + }, + }, }); } diff --git a/app/Controllers/Http/UserController.js b/app/Controllers/Http/UserController.js index edfccf2..979e78a 100644 --- a/app/Controllers/Http/UserController.js +++ b/app/Controllers/Http/UserController.js @@ -155,17 +155,17 @@ class UserController { async updateMe({ request, response, - auth + auth, }) { let settings = auth.user.settings || {}; if (typeof settings === 'string') { settings = JSON.parse(settings); } - let newSettings = { + const newSettings = { ...settings, ...request.all(), - } + }; auth.user.settings = JSON.stringify(newSettings); await auth.user.save(); @@ -188,7 +188,7 @@ class UserController { }, status: [ 'data-updated', - ] + ], }); } diff --git a/app/Controllers/Http/WorkspaceController.js b/app/Controllers/Http/WorkspaceController.js index dc02e44..7be0e5b 100644 --- a/app/Controllers/Http/WorkspaceController.js +++ b/app/Controllers/Http/WorkspaceController.js @@ -168,7 +168,7 @@ class WorkspaceController { id: workspace.workspaceId, name: workspace.name, order: workspace.order, - services: typeof workspace.services === "string" ? JSON.parse(workspace.services) : workspace.services, + services: typeof workspace.services === 'string' ? JSON.parse(workspace.services) : workspace.services, userId: auth.user.id, })); } -- cgit v1.2.3-70-g09d2 From bca1ffbd601a19245717752df9f312172aaa3bf8 Mon Sep 17 00:00:00 2001 From: Bennett Date: Tue, 10 Mar 2020 15:11:03 +0100 Subject: Add "IS_REGISTRATION_ENABLED" option --- .env.example | 1 + .gitignore | 3 ++- README.md | 4 +++- app/Controllers/Http/DashboardController.js | 1 - app/Controllers/Http/UserController.js | 14 ++++++++++++++ 5 files changed, 20 insertions(+), 3 deletions(-) (limited to 'app') diff --git a/.env.example b/.env.example index c175cd1..f4e828b 100644 --- a/.env.example +++ b/.env.example @@ -19,4 +19,5 @@ DB_DATABASE=adonis HASH_DRIVER=bcrypt IS_CREATION_ENABLED=true +IS_REGISTRATION_ENABLED=true CONNECT_WITH_FRANZ=true \ No newline at end of file diff --git a/.gitignore b/.gitignore index f0a5147..12fc024 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,5 @@ public/terms.html public/privacy.html resources/announcements/*.json -!resources/announcements/version.json \ No newline at end of file +!resources/announcements/version.json +npm-debug.log diff --git a/README.md b/README.md index 6aa2d02..9b39460 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,8 @@ After setting up the docker container we recommend you to set up an NGINX revers - DB_PASSWORD= - DB_DATABASE= - IS_CREATION_ENABLED=true/false - - CONNECT_WITH_FRANZ=true/flase + - CONNECT_WITH_FRANZ=true/false + - IS_REGISTRATION_ENABLED=true/false volumes: - :/config - :/usr/src/app/database @@ -106,6 +107,7 @@ For more information on configuring the Docker image, visit the Docker image rep ## Configuration franz-server's configuration is saved inside the `.env` file. Besides AdonisJS's settings, ferdi-server has the following custom settings: - `IS_CREATION_ENABLED` (`true` or `false`, default: `true`): Whether to enable the [creation of custom recipes](#creating-and-using-custom-recipes) +- `IS_REGISTRATION_ENABLED` (`true` or `false`, default: `true`): Whether to enable the creation of new user accounts - `CONNECT_WITH_FRANZ` (`true` or `false`, default: `true`): Whether to enable connections to the Franz server. By enabling this option, ferdi-server can: - Show the full Franz recipe library instead of only custom recipes - Import Franz accounts diff --git a/app/Controllers/Http/DashboardController.js b/app/Controllers/Http/DashboardController.js index 1018f78..86cfa74 100644 --- a/app/Controllers/Http/DashboardController.js +++ b/app/Controllers/Http/DashboardController.js @@ -181,7 +181,6 @@ class DashboardController { 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)' }); diff --git a/app/Controllers/Http/UserController.js b/app/Controllers/Http/UserController.js index 979e78a..e580e49 100644 --- a/app/Controllers/Http/UserController.js +++ b/app/Controllers/Http/UserController.js @@ -38,6 +38,13 @@ class UserController { response, auth, }) { + if (Env.get('IS_REGISTRATION_ENABLED') == 'false') { // eslint-disable-line eqeqeq + return response.status(401).send({ + message: 'Registration is disabled on this server', + status: 401, + }); + } + // Validate user input const validation = await validateAll(request.all(), { firstname: 'required', @@ -197,6 +204,13 @@ class UserController { request, response, }) { + if (Env.get('IS_REGISTRATION_ENABLED') == 'false') { // eslint-disable-line eqeqeq + return response.status(401).send({ + message: 'Registration is disabled on this server', + status: 401, + }); + } + // Validate user input const validation = await validateAll(request.all(), { email: 'required|email|unique:users,email', -- cgit v1.2.3-70-g09d2 From a26ea3004f5a308c65305a4c8eecbe0fafd5d30d Mon Sep 17 00:00:00 2001 From: Bennett Date: Tue, 10 Mar 2020 15:35:30 +0100 Subject: #12 Handle multiple slashes in path --- app/Middleware/HandleDoubleSlash.js | 26 ++++++++++++++++++++++++++ start/kernel.js | 1 + 2 files changed, 27 insertions(+) create mode 100644 app/Middleware/HandleDoubleSlash.js (limited to 'app') diff --git a/app/Middleware/HandleDoubleSlash.js b/app/Middleware/HandleDoubleSlash.js new file mode 100644 index 0000000..94f4fe8 --- /dev/null +++ b/app/Middleware/HandleDoubleSlash.js @@ -0,0 +1,26 @@ +'use strict' +/** @typedef {import('@adonisjs/framework/src/Request')} Request */ +/** @typedef {import('@adonisjs/framework/src/Response')} Response */ +/** @typedef {import('@adonisjs/framework/src/View')} View */ + +class HandleDoubleSlash { + /** + * @param {object} ctx + * @param {Request} ctx.request + * @param {Function} next + */ + async handle ({ request, response }, next) { + console.log(request.url()); + + // Redirect requests that contain duplicate slashes to the right path + if (request.url().includes('//')) { + return response.redirect( + request.url().replace(/\/{2,}/g, '/'), + ); + } + + await next(); + } +} + +module.exports = HandleDoubleSlash diff --git a/start/kernel.js b/start/kernel.js index b54fc29..077151a 100644 --- a/start/kernel.js +++ b/start/kernel.js @@ -54,6 +54,7 @@ const namedMiddleware = { const serverMiddleware = [ 'Adonis/Middleware/Static', 'Adonis/Middleware/Cors', + 'App/Middleware/HandleDoubleSlash', ]; Server -- cgit v1.2.3-70-g09d2 From 6664abb0fe0f8e75e605d8344a6434b2bdf57ca2 Mon Sep 17 00:00:00 2001 From: Bennett Date: Tue, 10 Mar 2020 15:44:29 +0100 Subject: Add "IS_DASHBOARD_ENABLED" option --- .env.example | 1 + README.md | 2 ++ app/Middleware/HandleDoubleSlash.js | 2 -- start/routes.js | 44 ++++++++++++++++++++++--------------- 4 files changed, 29 insertions(+), 20 deletions(-) (limited to 'app') diff --git a/.env.example b/.env.example index f4e828b..bcc4c7c 100644 --- a/.env.example +++ b/.env.example @@ -19,5 +19,6 @@ DB_DATABASE=adonis HASH_DRIVER=bcrypt IS_CREATION_ENABLED=true +IS_DASHBOARD_ENABLED=true IS_REGISTRATION_ENABLED=true CONNECT_WITH_FRANZ=true \ No newline at end of file diff --git a/README.md b/README.md index 9b39460..99afc94 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ After setting up the docker container we recommend you to set up an NGINX revers - IS_CREATION_ENABLED=true/false - CONNECT_WITH_FRANZ=true/false - IS_REGISTRATION_ENABLED=true/false + - IS_DASHBOARD_ENABLED=true/false volumes: - :/config - :/usr/src/app/database @@ -108,6 +109,7 @@ For more information on configuring the Docker image, visit the Docker image rep franz-server's configuration is saved inside the `.env` file. Besides AdonisJS's settings, ferdi-server has the following custom settings: - `IS_CREATION_ENABLED` (`true` or `false`, default: `true`): Whether to enable the [creation of custom recipes](#creating-and-using-custom-recipes) - `IS_REGISTRATION_ENABLED` (`true` or `false`, default: `true`): Whether to enable the creation of new user accounts +- `IS_DASHBOARD_ENABLED` (`true` or `false`, default: `true`): Whether to enable the user dashboard - `CONNECT_WITH_FRANZ` (`true` or `false`, default: `true`): Whether to enable connections to the Franz server. By enabling this option, ferdi-server can: - Show the full Franz recipe library instead of only custom recipes - Import Franz accounts diff --git a/app/Middleware/HandleDoubleSlash.js b/app/Middleware/HandleDoubleSlash.js index 94f4fe8..456b774 100644 --- a/app/Middleware/HandleDoubleSlash.js +++ b/app/Middleware/HandleDoubleSlash.js @@ -10,8 +10,6 @@ class HandleDoubleSlash { * @param {Function} next */ async handle ({ request, response }, next) { - console.log(request.url()); - // Redirect requests that contain duplicate slashes to the right path if (request.url().includes('//')) { return response.redirect( diff --git a/start/routes.js b/start/routes.js index b5674fd..6385ca5 100644 --- a/start/routes.js +++ b/start/routes.js @@ -60,24 +60,32 @@ Route.group(() => { }).prefix('v1'); // User dashboard -Route.group(() => { - // Auth - Route.get('login', ({ view }) => view.render('dashboard.login')).middleware('guest'); - Route.post('login', 'DashboardController.login').middleware('guest'); - - // Dashboard - 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'); - - Route.get('*', ({ response }) => response.redirect('/user/account')); -}).prefix('user').middleware('shield'); +if (Env.get('IS_DASHBOARD_ENABLED') != 'false') { + Route.group(() => { + // Auth + Route.get('login', ({ view }) => view.render('dashboard.login')).middleware('guest'); + Route.post('login', 'DashboardController.login').middleware('guest'); + + // Dashboard + 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'); + + Route.get('*', ({ response }) => response.redirect('/user/account')); + }).prefix('user').middleware('shield'); +} else { + Route.group(() => { + Route.get('*', ({ + response, + }) => response.send('The user dashboard is disabled on this server\n\nIf you are the server owner, please set IS_DASHBOARD_ENABLED to true to enable the dashboard.')) + }).prefix('user'); +} // Recipe creation Route.post('new', 'RecipeController.create'); -- cgit v1.2.3-70-g09d2 From 8d61dc572b334e3bfc9cfbe835afe46914d759d8 Mon Sep 17 00:00:00 2001 From: Bennett Date: Tue, 10 Mar 2020 16:00:34 +0100 Subject: #15 Add error capturing --- app/Exceptions/Handler.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'app') diff --git a/app/Exceptions/Handler.js b/app/Exceptions/Handler.js index cb9e10b..14b840e 100644 --- a/app/Exceptions/Handler.js +++ b/app/Exceptions/Handler.js @@ -1,5 +1,6 @@ const BaseExceptionHandler = use('BaseExceptionHandler'); +const Sentry = require('@sentry/node'); /** * This class handles all exceptions thrown during @@ -39,7 +40,8 @@ class ExceptionHandler extends BaseExceptionHandler { * * @return {void} */ - async report() { + async report(error) { + Sentry.captureException(error); return true; } } -- cgit v1.2.3-70-g09d2