aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.eslintignore1
-rw-r--r--.eslintrc.js8
-rw-r--r--.gitignore3
-rw-r--r--.husky/.gitignore1
-rwxr-xr-x.husky/pre-commit4
-rw-r--r--.npmrc2
-rw-r--r--.nvmrc1
-rw-r--r--CONTRIBUTING.md151
-rw-r--r--README.md36
-rw-r--r--app/Controllers/Http/DashboardController.js12
-rw-r--r--app/Controllers/Http/ServiceController.js2
-rw-r--r--app/Controllers/Http/StaticController.js9
-rw-r--r--app/Controllers/Http/UserController.js27
-rw-r--r--app/Controllers/Http/WorkspaceController.js3
-rw-r--r--app/Middleware/HandleDoubleSlash.js6
-rw-r--r--app/Models/Token.js2
-rw-r--r--config/mail.js20
-rw-r--r--config/persona.js8
-rw-r--r--database/migrations/1612629845398_users_update_schema.js18
-rw-r--r--package-lock.json36
-rw-r--r--package.json11
-rw-r--r--public/img/favicon.icobin0 -> 4286 bytes
-rw-r--r--public/privacy.html1
-rw-r--r--public/terms.html1
-rw-r--r--resources/views/dashboard/account.edge8
-rw-r--r--resources/views/dashboard/data.edge8
-rw-r--r--resources/views/layouts/main.edge1
-rw-r--r--resources/views/layouts/v2.edge1
-rw-r--r--start/events.js14
-rw-r--r--start/routes.js25
30 files changed, 333 insertions, 87 deletions
diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..49e45f9
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1 @@
officialrecipes/
diff --git a/.eslintrc.js b/.eslintrc.js
index d02f489..066d415 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -10,7 +10,12 @@ module.exports = {
10 globals: { 10 globals: {
11 Atomics: 'readonly', 11 Atomics: 'readonly',
12 SharedArrayBuffer: 'readonly', 12 SharedArrayBuffer: 'readonly',
13 use: 'readonly' 13 use: 'readonly',
14 window: 'readonly',
15 document: 'readonly',
16 ENV: 'readonly',
17 session: 'readonly',
18 response: 'readonly',
14 }, 19 },
15 parserOptions: { 20 parserOptions: {
16 ecmaVersion: 2018, 21 ecmaVersion: 2018,
@@ -18,5 +23,6 @@ module.exports = {
18 rules: { 23 rules: {
19 "class-methods-use-this": 'off', 24 "class-methods-use-this": 'off',
20 "no-restricted-syntax": 'off', 25 "no-restricted-syntax": 'off',
26 "max-len": 0,
21 }, 27 },
22}; 28};
diff --git a/.gitignore b/.gitignore
index 267390d..a8bc908 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,6 @@ recipes/
18resources/announcements/*.json 18resources/announcements/*.json
19!resources/announcements/version.json 19!resources/announcements/version.json
20npm-debug.log 20npm-debug.log
21yarn-error.log
22server*.log
23.idea
diff --git a/.husky/.gitignore b/.husky/.gitignore
new file mode 100644
index 0000000..31354ec
--- /dev/null
+++ b/.husky/.gitignore
@@ -0,0 +1 @@
_
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100755
index 0000000..20d0d06
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1,4 @@
1#!/bin/sh
2. "$(dirname "$0")/_/husky.sh"
3
4npm run lint
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000..aafab16
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1,2 @@
1save-exact = true
2engine-strict = true
diff --git a/.nvmrc b/.nvmrc
new file mode 100644
index 0000000..6b17d22
--- /dev/null
+++ b/.nvmrc
@@ -0,0 +1 @@
14.16.1
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..d276763
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,151 @@
1# Contributing to ferdi-server
2
3:tada: First off, thanks for taking the time and your effort to make Ferdi better! :tada:
4
5## Table of contents
6
7<!-- TOC depthFrom:2 depthTo:2 withLinks:1 updateOnSave:1 orderedList:0 -->
8
9- [Contributing to ferdi-server](#contributing-to-ferdi-server)
10 - [Table of contents](#table-of-contents)
11 - [Code of Conduct](#code-of-conduct)
12 - [What should I know before I get started?](#what-should-i-know-before-i-get-started)
13 - [How Can I Contribute?](#how-can-i-contribute)
14 - [Setting up your Development machine](#setting-up-your-development-machine)
15 - [Install System-level dependencies](#install-system-level-dependencies)
16 - [Node.js, npm, node-gyp](#nodejs-npm-node-gyp)
17 - [Git](#git)
18 - [Clone repository with submodule](#clone-repository-with-submodule)
19 - [Install dependencies](#install-dependencies)
20 - [Styleguide](#styleguide)
21 - [Git Commit Messages format](#git-commit-messages-format)
22 - [Javascript Coding style-checker](#javascript-coding-style-checker)
23
24<!-- /TOC -->
25
26## Code of Conduct
27
28This project and everyone participating in it is governed by the [Ferdi Code of Conduct](https://github.com/getferdi/ferdi/blob/develop/CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [stefan@adlk.io](mailto:stefan@adlk.io).
29
30## What should I know before I get started?
31
32For the moment, Ferdi's development is a bit slow but all contributions are highly appreciated. [Check this issue for discussion](https://github.com/getferdi/ferdi/issues/956).
33
34## How Can I Contribute?
35
36As a basic rule, before filing issues, feature requests or anything else. Take a look at the issues and check if this has not already been reported by another user. If so, engage in the already existing discussion.
37
38## Setting up your Development machine
39
40### Install System-level dependencies
41
42#### Node.js, npm, node-gyp
43
44Please make sure you are running the exact node version used by the developers/contributors as specified in the [nvmrc file](./.nvmrc).
45
46Currently, these are the combinations of system dependencies that work on an intel-based machines for MacOS/Linux/Windows (building on an ARM-based machine is still a work-in-progress due to node-sass native dependencies)
47
48```bash
49node -v
50v14.16.1
51npm -v
526.14.12
53node-gyp -v
54v8.0.0
55```
56
57#### Git
58
59The version [2.23.0](https://github.com/git-for-windows/git/releases/tag/v2.23.0.windows.1) for Git is working fine for development. You can then use the console from Git to do the development procedure.
60
61<!-- #### Debian/Ubuntu
62
63```bash
64apt install libx11-dev libxext-dev libxss-dev libxkbfile-dev rpm
65```
66
67#### Fedora
68
69```bash
70dnf install libX11-devel libXext-devel libXScrnSaver-devel libxkbfile-devel rpm
71```
72
73#### Windows
74
75Please make sure you run this command as an administrator:
76
77```bash
78npm i -g windows-build-tools --vs2015
79```
80 -->
81
82### Clone repository with submodule
83
84```bash
85git clone https://github.com/getferdi/server.git
86cd server
87git submodule update --init --recursive
88```
89
90It is important you execute the last command to get the required submodules (recipes, server).
91
92### Install dependencies
93
94- Run the following command to install all dependencies, and link sibling modules with ferdi-server.
95
96```bash
97npm i
98```
99
100- Copy the `.env.example` file into `.env` and change the values to match your system.
101
102```bash
103cp .env.example .env
104```
105
106_Note:_
107
1081. Have env DB_SSL=true only if your database is postgres and it is hosted online on platforms like GCP, AWS, etc
1092. You will have to provide a value for `API_KEY` that is at least 16 characters long.
110
111- If using sqlite for local development, create the database directory (whatever is set to `DATA` in `.env`)
112
113```bash
114mkdir -p data
115```
116
117- Run the database migrations with
118
119 ```bash
120 node ace migration:refresh
121 ```
122
123- To get the full functionality, you will need to have an SMTP server running for local development.
124
125<!-- ### Package recipe repository
126
127Ferdi requires its recipes to be packaged before it can use it. When running Ferdi as a development instance, you'll need to package the local recipes before you can create any services inside Ferdi.
128
129```bash
130cd recipes && npm i && npm run package
131``` -->
132
133### Start development app
134
135 ```bash
136 npm start --dev
137 ```
138
139### Styleguide
140
141#### Git Commit Messages format
142
143- Use the present tense ("Add feature" not "Added feature")
144- Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
145- Limit the first line to 72 characters or less
146- Reference issues and pull requests liberally after the first line
147- When only changing documentation, include `[ci skip]` in the commit description
148
149#### Javascript Coding style-checker
150
151- Please use `es-lint` and the defined rules to maintain a consistent style
diff --git a/README.md b/README.md
index 2e0d9f9..40834e2 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
1<p align="center"> 1<p align="center">
2 <img src="./logo.png" alt="" width="300"/> 2 <img src="./logo.png" alt="" width="300"/>
3</p> 3</p>
4 4
5# ferdi-server 5# ferdi-server
@@ -16,6 +16,7 @@ Official Server software for the [Ferdi Messaging Browser](https://getferdi.com)
16 - [Transferring user data](#transferring-user-data) 16 - [Transferring user data](#transferring-user-data)
17 - [Creating and using custom recipes](#creating-and-using-custom-recipes) 17 - [Creating and using custom recipes](#creating-and-using-custom-recipes)
18 - [Listing custom recipes](#listing-custom-recipes) 18 - [Listing custom recipes](#listing-custom-recipes)
19 - [Contributing to ferdi-server's development](#contributing-to-ferdi-servers-development)
19 - [License](#license) 20 - [License](#license)
20 21
21## Why use a custom Ferdi server? 22## Why use a custom Ferdi server?
@@ -37,7 +38,7 @@ If you are not interested in doing this you can use our official instance of Fer
37### with Docker 38### with Docker
38The easiest way to set up Ferdi server on your server is with Docker. 39The easiest way to set up Ferdi server on your server is with Docker.
39 40
40The Docker image can be run as is, with the default sqlite database or you can modifying your ENV variables to use an external database (e.g. MySQL, MariaDB, Postgres, etc). 41The Docker image can be run as is, with the default sqlite database or you can modifying your ENV variables to use an external database (e.g. MySQL, MariaDB, Postgres, etc).
41After setting up the docker container we recommend you to set up an NGINX reverse proxy to access ferdi-server outside of your home network and protect it with an SSL certificate. 42After setting up the docker container we recommend you to set up an NGINX reverse proxy to access ferdi-server outside of your home network and protect it with an SSL certificate.
42 43
431. Pull the Docker image 441. Pull the Docker image
@@ -47,11 +48,11 @@ After setting up the docker container we recommend you to set up an NGINX revers
47 ``` 48 ```
482. Create a new Docker container with your desired configuration 492. Create a new Docker container with your desired configuration
49 50
50 ```sh 51 ```sh
51 docker create \ 52 docker create \
52 --name=ferdi-server \ 53 --name=ferdi-server \
53 -e NODE_ENV=development \ 54 -e NODE_ENV=development \
54 -e EXTERNAL_DOMAIN=<ferdi-serverdomain> \ 55 -e EXTERNAL_DOMAIN=<ferdi-serverdomain> \
55 -e DB_CONNECTION=<database> \ 56 -e DB_CONNECTION=<database> \
56 -e DB_HOST=<yourdbhost> \ 57 -e DB_HOST=<yourdbhost> \
57 -e DB_PORT=<yourdbport> \ 58 -e DB_PORT=<yourdbport> \
@@ -75,7 +76,7 @@ After setting up the docker container we recommend you to set up an NGINX revers
75 -v <path to database>:/app/database \ 76 -v <path to database>:/app/database \
76 -v <path to recipes>:/app/recipes \ 77 -v <path to recipes>:/app/recipes \
77 --restart unless-stopped \ 78 --restart unless-stopped \
78 getferdi/ferdi-server 79 getferdi/ferdi-server
79 ``` 80 ```
80 81
81 Alternatively, you can also use docker-compose v2 schema. An example can be found [in the docker folder](./docker/docker-compose.yml). 82 Alternatively, you can also use docker-compose v2 schema. An example can be found [in the docker folder](./docker/docker-compose.yml).
@@ -85,29 +86,26 @@ After setting up the docker container we recommend you to set up an NGINX revers
85For more information on configuring the Docker image, please read [the ferdi docker documentation](./docker/README.md). 86For more information on configuring the Docker image, please read [the ferdi docker documentation](./docker/README.md).
86 87
87### Manual setup 88### Manual setup
89
881. Clone this repository 901. Clone this repository
892. Install the [AdonisJS CLI](https://adonisjs.com/) 912. Install the [AdonisJS CLI](https://adonisjs.com/)
903. Copy `.env.example` to `.env` and edit the [configuration](#configuration) to your needs 923. Copy `.env.example` to `.env` and edit the [configuration](#configuration) to your needs
914. Have env DB_SSL=true only if your database is postgres and it is hosted online on platforms like GCP, AWS, etc 934. Have env DB_SSL=true only if your database is postgres and it is hosted online on platforms like GCP, AWS, etc
925. Run `npm install` to install local dependencies 945. Run `npm install` to install local dependencies
936. Run the database migrations with 956. Run the database migrations with
96
94 ```js 97 ```js
95 adonis migration:run 98 node ace migration:run
96 ``` 99 ```
100
977. Start the server with 1017. Start the server with
98 ```js 102
99 adonis serve --dev
100 ```
1018. If on previous step it does not run the server then run with
102 ```js 103 ```js
103 npm start 104 npm start
104 ``` 105 ```
105 or
106 ```js
107 node server.js
108 ```
109 106
110## Configuration 107## Configuration
108
111franz-server's configuration is saved inside the `.env` file. Besides AdonisJS's settings, ferdi-server has the following custom settings: 109franz-server's configuration is saved inside the `.env` file. Besides AdonisJS's settings, ferdi-server has the following custom settings:
112- `IS_CREATION_ENABLED` (`true` or `false`, default: `true`): Whether to enable the [creation of custom recipes](#creating-and-using-custom-recipes) 110- `IS_CREATION_ENABLED` (`true` or `false`, default: `true`): Whether to enable the [creation of custom recipes](#creating-and-using-custom-recipes)
113- `IS_REGISTRATION_ENABLED` (`true` or `false`, default: `true`): Whether to enable the creation of new user accounts 111- `IS_REGISTRATION_ENABLED` (`true` or `false`, default: `true`): Whether to enable the creation of new user accounts
@@ -117,19 +115,23 @@ franz-server's configuration is saved inside the `.env` file. Besides AdonisJS's
117 - Import Franz accounts 115 - Import Franz accounts
118 116
119## Importing your Franz account 117## Importing your Franz account
118
120ferdi-server allows you to import your full Franz account, including all its settings. 119ferdi-server allows you to import your full Franz account, including all its settings.
121 120
122To import your Franz account, open `http://[YOUR FERDI-SERVER]/import` in your browser and login using your Franz account details. ferdi-server will create a new user with the same credentials and copy your Franz settings, services and workspaces. 121To import your Franz account, open `http://[YOUR FERDI-SERVER]/import` in your browser and login using your Franz account details. ferdi-server will create a new user with the same credentials and copy your Franz settings, services and workspaces.
123 122
124## Transferring user data 123## Transferring user data
124
125Please refer to <https://github.com/getferdi/ferdi/wiki/Transferring-data-between-servers> 125Please refer to <https://github.com/getferdi/ferdi/wiki/Transferring-data-between-servers>
126 126
127## Creating and using custom recipes 127## Creating and using custom recipes
128
128ferdi-server allows to extends the Franz recipe catalogue with custom Ferdi recipes. 129ferdi-server allows to extends the Franz recipe catalogue with custom Ferdi recipes.
129 130
130For documentation on how to create a recipe, please visit [the official guide by Franz](https://github.com/meetfranz/plugins/blob/master/docs/integration.md). 131For documentation on how to create a recipe, please visit [the official guide by Franz](https://github.com/meetfranz/plugins/blob/master/docs/integration.md).
131 132
132To add your recipe to ferdi-server, open `http://[YOUR FERDI-SERVER]/new` in your browser. You can now define the following settings: 133To add your recipe to ferdi-server, open `http://[YOUR FERDI-SERVER]/new` in your browser. You can now define the following settings:
134
133- `Author`: Author who created the recipe 135- `Author`: Author who created the recipe
134- `Name`: Name for your new service. Can contain spaces and unicode characters 136- `Name`: Name for your new service. Can contain spaces and unicode characters
135- `Service ID`: Unique ID for this recipe. Does not contain spaces or special characters (e.g. `google-drive`) 137- `Service ID`: Unique ID for this recipe. Does not contain spaces or special characters (e.g. `google-drive`)
@@ -137,7 +139,13 @@ To add your recipe to ferdi-server, open `http://[YOUR FERDI-SERVER]/new` in you
137- `Recipe files`: Recipe files that you created using the [Franz recipe creation guide](https://github.com/meetfranz/plugins/blob/master/docs/integration.md). Please do *not* package your files beforehand - upload the raw files (you can drag and drop multiple files). ferdi-server will automatically package and store the recipe in the right format. Please also do not drag and drop or select the whole folder, select the individual files. 139- `Recipe files`: Recipe files that you created using the [Franz recipe creation guide](https://github.com/meetfranz/plugins/blob/master/docs/integration.md). Please do *not* package your files beforehand - upload the raw files (you can drag and drop multiple files). ferdi-server will automatically package and store the recipe in the right format. Please also do not drag and drop or select the whole folder, select the individual files.
138 140
139### Listing custom recipes 141### Listing custom recipes
142
140Inside Ferdi, searching for `ferdi:custom` will list all your custom recipes. 143Inside Ferdi, searching for `ferdi:custom` will list all your custom recipes.
141 144
145## Contributing to ferdi-server's development
146
147We welcome all contributors. Please read the [contributing guidelines](CONTRIBUTING.md) to setup your development machine and proceed.
148
142## License 149## License
150
143ferdi-server is licensed under the MIT License 151ferdi-server is licensed under the MIT License
diff --git a/app/Controllers/Http/DashboardController.js b/app/Controllers/Http/DashboardController.js
index 3de4816..a588c75 100644
--- a/app/Controllers/Http/DashboardController.js
+++ b/app/Controllers/Http/DashboardController.js
@@ -63,7 +63,8 @@ class DashboardController {
63 } 63 }
64 try { 64 try {
65 await Persona.forgotPassword(request.input('mail')); 65 await Persona.forgotPassword(request.input('mail'));
66 } catch(e) {} 66 // eslint-disable-next-line no-empty
67 } catch (e) {}
67 68
68 return view.render('others.message', { 69 return view.render('others.message', {
69 heading: 'Reset password', 70 heading: 'Reset password',
@@ -91,11 +92,11 @@ class DashboardController {
91 const payload = { 92 const payload = {
92 password: crypto.createHash('sha256').update(request.input('password')).digest('base64'), 93 password: crypto.createHash('sha256').update(request.input('password')).digest('base64'),
93 password_confirmation: crypto.createHash('sha256').update(request.input('password_confirmation')).digest('base64'), 94 password_confirmation: crypto.createHash('sha256').update(request.input('password_confirmation')).digest('base64'),
94 } 95 };
95 96
96 try { 97 try {
97 await Persona.updatePasswordByToken(request.input('token'), payload); 98 await Persona.updatePasswordByToken(request.input('token'), payload);
98 } catch(e) { 99 } catch (e) {
99 return view.render('others.message', { 100 return view.render('others.message', {
100 heading: 'Cannot reset your password', 101 heading: 'Cannot reset your password',
101 text: 'Please make sure you are using a valid and recent link to reset your password and that your passwords entered match.', 102 text: 'Please make sure you are using a valid and recent link to reset your password and that your passwords entered match.',
@@ -122,6 +123,7 @@ class DashboardController {
122 return view.render('dashboard.account', { 123 return view.render('dashboard.account', {
123 username: auth.user.username, 124 username: auth.user.username,
124 email: auth.user.email, 125 email: auth.user.email,
126 lastname: auth.user.lastname,
125 }); 127 });
126 } 128 }
127 129
@@ -135,6 +137,7 @@ class DashboardController {
135 let validation = await validateAll(request.all(), { 137 let validation = await validateAll(request.all(), {
136 username: 'required', 138 username: 'required',
137 email: 'required', 139 email: 'required',
140 lastname: 'required',
138 }); 141 });
139 if (validation.fails()) { 142 if (validation.fails()) {
140 session.withErrors(validation.messages()).flashExcept(['password']); 143 session.withErrors(validation.messages()).flashExcept(['password']);
@@ -168,6 +171,7 @@ class DashboardController {
168 // Update user account 171 // Update user account
169 const { user } = auth; 172 const { user } = auth;
170 user.username = request.input('username'); 173 user.username = request.input('username');
174 user.lastname = request.input('lastname');
171 user.email = request.input('email'); 175 user.email = request.input('email');
172 if (request.input('password')) { 176 if (request.input('password')) {
173 const hashedPassword = crypto.createHash('sha256').update(request.input('password')).digest('base64'); 177 const hashedPassword = crypto.createHash('sha256').update(request.input('password')).digest('base64');
@@ -192,6 +196,7 @@ class DashboardController {
192 196
193 return view.render('dashboard.data', { 197 return view.render('dashboard.data', {
194 username: general.username, 198 username: general.username,
199 lastname: general.lastname,
195 mail: general.email, 200 mail: general.email,
196 created: general.created_at, 201 created: general.created_at,
197 updated: general.updated_at, 202 updated: general.updated_at,
@@ -211,6 +216,7 @@ class DashboardController {
211 216
212 const exportData = { 217 const exportData = {
213 username: general.username, 218 username: general.username,
219 lastname: general.lastname,
214 mail: general.email, 220 mail: general.email,
215 services, 221 services,
216 workspaces, 222 workspaces,
diff --git a/app/Controllers/Http/ServiceController.js b/app/Controllers/Http/ServiceController.js
index a1d26cb..4aa611c 100644
--- a/app/Controllers/Http/ServiceController.js
+++ b/app/Controllers/Http/ServiceController.js
@@ -140,7 +140,9 @@ class ServiceController {
140 let iconId; 140 let iconId;
141 do { 141 do {
142 iconId = uuid() + uuid(); 142 iconId = uuid() + uuid();
143 // eslint-disable-next-line no-await-in-loop
143 } while (await fs.exists(path.join(Helpers.tmpPath('uploads'), iconId))); 144 } while (await fs.exists(path.join(Helpers.tmpPath('uploads'), iconId)));
145 iconId = `${iconId}.${icon.extname}`;
144 146
145 await icon.move(Helpers.tmpPath('uploads'), { 147 await icon.move(Helpers.tmpPath('uploads'), {
146 name: iconId, 148 name: iconId,
diff --git a/app/Controllers/Http/StaticController.js b/app/Controllers/Http/StaticController.js
index d1a1179..114e369 100644
--- a/app/Controllers/Http/StaticController.js
+++ b/app/Controllers/Http/StaticController.js
@@ -34,7 +34,6 @@ class StaticController {
34 defaultTrialPlan: 'franz-pro-yearly', 34 defaultTrialPlan: 'franz-pro-yearly',
35 subscribeURL: 'https://getferdi.com', 35 subscribeURL: 'https://getferdi.com',
36 planSelectionURL: 'https://getferdi.com', 36 planSelectionURL: 'https://getferdi.com',
37 isMagicBarEnabled: true,
38 hasInlineCheckout: true, 37 hasInlineCheckout: true,
39 isPlanSelectionEnabled: false, 38 isPlanSelectionEnabled: false,
40 isTrialStatusBarEnabled: false, 39 isTrialStatusBarEnabled: false,
@@ -101,9 +100,11 @@ class StaticController {
101 }) { 100 }) {
102 return response.send( 101 return response.send(
103 fs 102 fs
104 .readJsonSync(path.join( 103 .readJsonSync(path.join(
105 Helpers.appRoot(), "officialrecipes", "recipes", "all.json")) 104 Helpers.appRoot(), 'officialrecipes', 'recipes', 'all.json',
106 .filter((recipe) => recipe.featured)); 105 ))
106 .filter((recipe) => recipe.featured),
107 );
107 } 108 }
108 109
109 // Show announcements 110 // Show announcements
diff --git a/app/Controllers/Http/UserController.js b/app/Controllers/Http/UserController.js
index e367d99..0d768a9 100644
--- a/app/Controllers/Http/UserController.js
+++ b/app/Controllers/Http/UserController.js
@@ -48,9 +48,11 @@ class UserController {
48 // Validate user input 48 // Validate user input
49 const validation = await validateAll(request.all(), { 49 const validation = await validateAll(request.all(), {
50 firstname: 'required', 50 firstname: 'required',
51 lastname: 'required',
51 email: 'required|email|unique:users,email', 52 email: 'required|email|unique:users,email',
52 password: 'required', 53 password: 'required',
53 }); 54 });
55
54 if (validation.fails()) { 56 if (validation.fails()) {
55 return response.status(401).send({ 57 return response.status(401).send({
56 message: 'Invalid POST arguments', 58 message: 'Invalid POST arguments',
@@ -59,7 +61,7 @@ class UserController {
59 }); 61 });
60 } 62 }
61 63
62 const data = request.only(['firstname', 'email', 'password']); 64 const data = request.only(['firstname', 'lastname', 'email', 'password']);
63 65
64 // Create user in DB 66 // Create user in DB
65 let user; 67 let user;
@@ -68,6 +70,7 @@ class UserController {
68 email: data.email, 70 email: data.email,
69 password: data.password, 71 password: data.password,
70 username: data.firstname, 72 username: data.firstname,
73 lastname: data.lastname,
71 }); 74 });
72 } catch (e) { 75 } catch (e) {
73 return response.status(401).send({ 76 return response.status(401).send({
@@ -149,13 +152,13 @@ class UserController {
149 email: auth.user.email, 152 email: auth.user.email,
150 emailValidated: true, 153 emailValidated: true,
151 features: {}, 154 features: {},
152 firstname: 'Franz', 155 firstname: auth.user.username,
153 id: '82c1cf9d-ab58-4da2-b55e-aaa41d2142d8', 156 id: '82c1cf9d-ab58-4da2-b55e-aaa41d2142d8',
154 isPremium: true, 157 isPremium: true,
155 isSubscriptionOwner: true, 158 isSubscriptionOwner: true,
156 lastname: 'Franz', 159 lastname: auth.user.lastname,
157 locale: 'en-US', 160 locale: 'en-US',
158 ...settings || {}, 161 ...settings || {},
159 }); 162 });
160 } 163 }
161 164
@@ -174,6 +177,7 @@ class UserController {
174 ...request.all(), 177 ...request.all(),
175 }; 178 };
176 179
180 // eslint-disable-next-line no-param-reassign
177 auth.user.settings = JSON.stringify(newSettings); 181 auth.user.settings = JSON.stringify(newSettings);
178 await auth.user.save(); 182 await auth.user.save();
179 183
@@ -185,13 +189,13 @@ class UserController {
185 email: auth.user.email, 189 email: auth.user.email,
186 emailValidated: true, 190 emailValidated: true,
187 features: {}, 191 features: {},
188 firstname: 'Franz', 192 firstname: auth.user.username,
189 id: '82c1cf9d-ab58-4da2-b55e-aaa41d2142d8', 193 id: '82c1cf9d-ab58-4da2-b55e-aaa41d2142d8',
190 isPremium: true, 194 isPremium: true,
191 isSubscriptionOwner: true, 195 isSubscriptionOwner: true,
192 lastname: 'Franz', 196 lastname: auth.user.lastname,
193 locale: 'en-US', 197 locale: 'en-US',
194 ...newSettings || {}, 198 ...newSettings || {},
195 }, 199 },
196 status: [ 200 status: [
197 'data-updated', 201 'data-updated',
@@ -246,6 +250,7 @@ class UserController {
246 email, 250 email,
247 password: hashedPassword, 251 password: hashedPassword,
248 username: 'Franz', 252 username: 'Franz',
253 lastname: 'Franz',
249 }); 254 });
250 255
251 return response.send('Your account has been created but due to this server\'s configuration, we could not import your Franz account data.\n\nIf you are the server owner, please set CONNECT_WITH_FRANZ to true to enable account imports.'); 256 return response.send('Your account has been created but due to this server\'s configuration, we could not import your Franz account data.\n\nIf you are the server owner, please set CONNECT_WITH_FRANZ to true to enable account imports.');
@@ -258,12 +263,19 @@ class UserController {
258 let token; 263 let token;
259 try { 264 try {
260 const basicToken = btoa(`${email}:${hashedPassword}`); 265 const basicToken = btoa(`${email}:${hashedPassword}`);
266 const loginBody = {
267 isZendeskLogin: false,
268 };
261 269
262 const rawResponse = await fetch(`${base}auth/login`, { 270 const rawResponse = await fetch(`${base}auth/login`, {
263 method: 'POST', 271 method: 'POST',
272 body: JSON.stringify(loginBody),
264 headers: { 273 headers: {
265 Authorization: `Basic ${basicToken}`, 274 Authorization: `Basic ${basicToken}`,
266 'User-Agent': userAgent, 275 'User-Agent': userAgent,
276 'Content-Type': 'application/json',
277 accept: '*/*',
278 'x-franz-source': 'Web',
267 }, 279 },
268 }); 280 });
269 const content = await rawResponse.json(); 281 const content = await rawResponse.json();
@@ -301,6 +313,7 @@ class UserController {
301 email: userInf.email, 313 email: userInf.email,
302 password: hashedPassword, 314 password: hashedPassword,
303 username: userInf.firstname, 315 username: userInf.firstname,
316 lastname: userInf.lastname,
304 }); 317 });
305 } catch (e) { 318 } catch (e) {
306 const errorMessage = `Could not create your user in our system.\nError: ${e}`; 319 const errorMessage = `Could not create your user in our system.\nError: ${e}`;
diff --git a/app/Controllers/Http/WorkspaceController.js b/app/Controllers/Http/WorkspaceController.js
index cbb6873..496912e 100644
--- a/app/Controllers/Http/WorkspaceController.js
+++ b/app/Controllers/Http/WorkspaceController.js
@@ -112,7 +112,8 @@ class WorkspaceController {
112 } 112 }
113 113
114 async delete({ 114 async delete({
115 request, 115 // eslint-disable-next-line no-unused-vars
116 _request,
116 response, 117 response,
117 auth, 118 auth,
118 params, 119 params,
diff --git a/app/Middleware/HandleDoubleSlash.js b/app/Middleware/HandleDoubleSlash.js
index 456b774..c4bc053 100644
--- a/app/Middleware/HandleDoubleSlash.js
+++ b/app/Middleware/HandleDoubleSlash.js
@@ -1,4 +1,3 @@
1'use strict'
2/** @typedef {import('@adonisjs/framework/src/Request')} Request */ 1/** @typedef {import('@adonisjs/framework/src/Request')} Request */
3/** @typedef {import('@adonisjs/framework/src/Response')} Response */ 2/** @typedef {import('@adonisjs/framework/src/Response')} Response */
4/** @typedef {import('@adonisjs/framework/src/View')} View */ 3/** @typedef {import('@adonisjs/framework/src/View')} View */
@@ -9,7 +8,8 @@ class HandleDoubleSlash {
9 * @param {Request} ctx.request 8 * @param {Request} ctx.request
10 * @param {Function} next 9 * @param {Function} next
11 */ 10 */
12 async handle ({ request, response }, next) { 11 // eslint-disable-next-line consistent-return
12 async handle({ request, response }, next) {
13 // Redirect requests that contain duplicate slashes to the right path 13 // Redirect requests that contain duplicate slashes to the right path
14 if (request.url().includes('//')) { 14 if (request.url().includes('//')) {
15 return response.redirect( 15 return response.redirect(
@@ -21,4 +21,4 @@ class HandleDoubleSlash {
21 } 21 }
22} 22}
23 23
24module.exports = HandleDoubleSlash 24module.exports = HandleDoubleSlash;
diff --git a/app/Models/Token.js b/app/Models/Token.js
index 50bcf1d..4a6c286 100644
--- a/app/Models/Token.js
+++ b/app/Models/Token.js
@@ -4,7 +4,7 @@ const Model = use('Model');
4 4
5class Token extends Model { 5class Token extends Model {
6 user() { 6 user() {
7 return this.belongsTo('App/Models/User') 7 return this.belongsTo('App/Models/User');
8 } 8 }
9} 9}
10 10
diff --git a/config/mail.js b/config/mail.js
index b876f19..1f11a8e 100644
--- a/config/mail.js
+++ b/config/mail.js
@@ -1,6 +1,4 @@
1'use strict' 1const Env = use('Env');
2
3const Env = use('Env')
4 2
5module.exports = { 3module.exports = {
6 /* 4 /*
@@ -30,15 +28,15 @@ module.exports = {
30 name: Env.get('EXTERNAL_DOMAIN'), 28 name: Env.get('EXTERNAL_DOMAIN'),
31 port: Env.get('SMTP_PORT', 2525), 29 port: Env.get('SMTP_PORT', 2525),
32 host: Env.get('SMTP_HOST'), 30 host: Env.get('SMTP_HOST'),
33 secure: JSON.parse(Env.get("MAIL_SSL", Env.get('SSL', false))), 31 secure: JSON.parse(Env.get('MAIL_SSL', Env.get('SSL', false))),
34 authMethod: 'LOGIN', 32 authMethod: 'LOGIN',
35 auth: { 33 auth: {
36 user: Env.get('MAIL_USERNAME'), 34 user: Env.get('MAIL_USERNAME'),
37 pass: Env.get('MAIL_PASSWORD') 35 pass: Env.get('MAIL_PASSWORD'),
38 }, 36 },
39 maxConnections: 5, 37 maxConnections: 5,
40 maxMessages: 100, 38 maxMessages: 100,
41 rateLimit: 10 39 rateLimit: 10,
42 }, 40 },
43 41
44 /* 42 /*
@@ -60,7 +58,7 @@ module.exports = {
60 sparkpost: { 58 sparkpost: {
61 driver: 'sparkpost', 59 driver: 'sparkpost',
62 apiKey: Env.get('SPARKPOST_API_KEY'), 60 apiKey: Env.get('SPARKPOST_API_KEY'),
63 extras: {} 61 extras: {},
64 }, 62 },
65 63
66 /* 64 /*
@@ -85,7 +83,7 @@ module.exports = {
85 domain: Env.get('MAILGUN_DOMAIN'), 83 domain: Env.get('MAILGUN_DOMAIN'),
86 region: Env.get('MAILGUN_API_REGION'), 84 region: Env.get('MAILGUN_API_REGION'),
87 apiKey: Env.get('MAILGUN_API_KEY'), 85 apiKey: Env.get('MAILGUN_API_KEY'),
88 extras: {} 86 extras: {},
89 }, 87 },
90 88
91 /* 89 /*
@@ -100,6 +98,6 @@ module.exports = {
100 | 98 |
101 */ 99 */
102 ethereal: { 100 ethereal: {
103 driver: 'ethereal' 101 driver: 'ethereal',
104 } 102 },
105} 103};
diff --git a/config/persona.js b/config/persona.js
index 71fbc3f..c259a06 100644
--- a/config/persona.js
+++ b/config/persona.js
@@ -1,5 +1,3 @@
1'use strict'
2
3/* 1/*
4|-------------------------------------------------------------------------- 2|--------------------------------------------------------------------------
5| Persona 3| Persona
@@ -92,7 +90,5 @@ module.exports = {
92 | An object of validation messages to be used when validation fails. 90 | An object of validation messages to be used when validation fails.
93 | 91 |
94 */ 92 */
95 validationMessages: () => { 93 validationMessages: () => ({}),
96 return {} 94};
97 }
98}
diff --git a/database/migrations/1612629845398_users_update_schema.js b/database/migrations/1612629845398_users_update_schema.js
new file mode 100644
index 0000000..10b82aa
--- /dev/null
+++ b/database/migrations/1612629845398_users_update_schema.js
@@ -0,0 +1,18 @@
1/** @type {import('@adonisjs/lucid/src/Schema')} */
2const Schema = use('Schema');
3
4class UsersUpdateSchema extends Schema {
5 up() {
6 this.table('users', (table) => {
7 table.string('lastname', 80).notNullable().default('');
8 });
9 }
10
11 down() {
12 this.table('users', (table) => {
13 table.dropColumn('lastname');
14 });
15 }
16}
17
18module.exports = UsersUpdateSchema;
diff --git a/package-lock.json b/package-lock.json
index 6c5fece..226ea65 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1253,9 +1253,9 @@
1253 } 1253 }
1254 }, 1254 },
1255 "bson": { 1255 "bson": {
1256 "version": "1.1.1", 1256 "version": "1.1.6",
1257 "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.1.tgz", 1257 "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz",
1258 "integrity": "sha512-jCGVYLoYMHDkOsbwJZBCqwMHyH4c+wzgI9hG7Z6SZJRXWr+x58pdIbm2i9a/jFGCkRJqRUr8eoI7lDWa0hTkxg==" 1258 "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg=="
1259 }, 1259 },
1260 "btoa": { 1260 "btoa": {
1261 "version": "1.2.1", 1261 "version": "1.2.1",
@@ -2883,9 +2883,9 @@
2883 } 2883 }
2884 }, 2884 },
2885 "glob-parent": { 2885 "glob-parent": {
2886 "version": "5.0.0", 2886 "version": "5.1.2",
2887 "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.0.0.tgz", 2887 "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
2888 "integrity": "sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==", 2888 "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
2889 "dev": true, 2889 "dev": true,
2890 "requires": { 2890 "requires": {
2891 "is-glob": "^4.0.1" 2891 "is-glob": "^4.0.1"
@@ -3066,9 +3066,9 @@
3066 } 3066 }
3067 }, 3067 },
3068 "hosted-git-info": { 3068 "hosted-git-info": {
3069 "version": "2.8.8", 3069 "version": "2.8.9",
3070 "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", 3070 "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
3071 "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", 3071 "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
3072 "dev": true 3072 "dev": true
3073 }, 3073 },
3074 "http-cache-semantics": { 3074 "http-cache-semantics": {
@@ -3122,6 +3122,12 @@
3122 } 3122 }
3123 } 3123 }
3124 }, 3124 },
3125 "husky": {
3126 "version": "6.0.0",
3127 "resolved": "https://registry.npmjs.org/husky/-/husky-6.0.0.tgz",
3128 "integrity": "sha512-SQS2gDTB7tBN486QSoKPKQItZw97BMOd+Kdb6ghfpBc0yXyzrddI0oDV5MkDAbuB4X2mO3/nj60TRMcYxwzZeQ==",
3129 "dev": true
3130 },
3125 "iconv-lite": { 3131 "iconv-lite": {
3126 "version": "0.4.24", 3132 "version": "0.4.24",
3127 "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 3133 "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -3842,9 +3848,9 @@
3842 } 3848 }
3843 }, 3849 },
3844 "lodash": { 3850 "lodash": {
3845 "version": "4.17.19", 3851 "version": "4.17.21",
3846 "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", 3852 "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
3847 "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" 3853 "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
3848 }, 3854 },
3849 "lodash.includes": { 3855 "lodash.includes": {
3850 "version": "4.3.0", 3856 "version": "4.3.0",
@@ -6188,9 +6194,9 @@
6188 "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" 6194 "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
6189 }, 6195 },
6190 "set-getter": { 6196 "set-getter": {
6191 "version": "0.1.0", 6197 "version": "0.1.1",
6192 "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz", 6198 "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.1.tgz",
6193 "integrity": "sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=", 6199 "integrity": "sha512-9sVWOy+gthr+0G9DzqqLaYNA7+5OKkSmcqjL9cBpDEaZrr3ShQlyX2cZ/O/ozE41oxn/Tt0LGEM/w4Rub3A3gw==",
6194 "requires": { 6200 "requires": {
6195 "to-object-path": "^0.3.0" 6201 "to-object-path": "^0.3.0"
6196 } 6202 }
diff --git a/package.json b/package.json
index 5b25aac..6fc32a1 100644
--- a/package.json
+++ b/package.json
@@ -4,10 +4,16 @@
4 "adonis-version": "4.1.0", 4 "adonis-version": "4.1.0",
5 "description": "Ferdi server to replace the default Franz server.", 5 "description": "Ferdi server to replace the default Franz server.",
6 "main": "index.js", 6 "main": "index.js",
7 "engines": {
8 "node": "^14.16",
9 "npm": "^6.14",
10 "node-gyp": "^8.0"
11 },
7 "scripts": { 12 "scripts": {
13 "prepare": "husky install",
8 "start": "node server.js", 14 "start": "node server.js",
9 "test": "node ace test", 15 "test": "node ace test",
10 "lint": "eslint --fix ./" 16 "lint": "eslint --quiet --fix ./"
11 }, 17 },
12 "keywords": [ 18 "keywords": [
13 "adonisjs", 19 "adonisjs",
@@ -48,7 +54,8 @@
48 "eslint-plugin-import": "^2.20.2", 54 "eslint-plugin-import": "^2.20.2",
49 "eslint-plugin-jsx-a11y": "^6.2.3", 55 "eslint-plugin-jsx-a11y": "^6.2.3",
50 "eslint-plugin-react": "^7.19.0", 56 "eslint-plugin-react": "^7.19.0",
51 "eslint-plugin-react-hooks": "^1.7.0" 57 "eslint-plugin-react-hooks": "^1.7.0",
58 "husky": "^6.0.0"
52 }, 59 },
53 "autoload": { 60 "autoload": {
54 "App": "./app" 61 "App": "./app"
diff --git a/public/img/favicon.ico b/public/img/favicon.ico
new file mode 100644
index 0000000..4ec9d51
--- /dev/null
+++ b/public/img/favicon.ico
Binary files differ
diff --git a/public/privacy.html b/public/privacy.html
index ac17608..33302ad 100644
--- a/public/privacy.html
+++ b/public/privacy.html
@@ -8,6 +8,7 @@
8 <title>Privacy Policy - Ferdi API</title> 8 <title>Privacy Policy - Ferdi API</title>
9 9
10 <link rel="stylesheet" href="css/vanilla.css"> 10 <link rel="stylesheet" href="css/vanilla.css">
11 <link rel="shortcut icon" type="image/jpg" href="img/favicon.ico" />
11</head> 12</head>
12 13
13<body> 14<body>
diff --git a/public/terms.html b/public/terms.html
index 270e3ab..d7ef44b 100644
--- a/public/terms.html
+++ b/public/terms.html
@@ -8,6 +8,7 @@
8 <title>Terms of Service - Ferdi API</title> 8 <title>Terms of Service - Ferdi API</title>
9 9
10 <link rel="stylesheet" href="css/vanilla.css"> 10 <link rel="stylesheet" href="css/vanilla.css">
11 <link rel="shortcut icon" type="image/jpg" href="img/favicon.ico" />
11 12
12 <style> 13 <style>
13 ol, 14 ol,
diff --git a/resources/views/dashboard/account.edge b/resources/views/dashboard/account.edge
index 9f3539e..6792615 100644
--- a/resources/views/dashboard/account.edge
+++ b/resources/views/dashboard/account.edge
@@ -38,6 +38,14 @@
38 </div> 38 </div>
39 </div> 39 </div>
40 <div class="mb-6"> 40 <div class="mb-6">
41 <label class="block text-gray-700 text-sm font-bold mb-2">Last Name</label>
42 <div>
43 <input
44 class="shadow appearance-none rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline"
45 type="text" value="{{ old('lastname', lastname) }}" placeholder="Last Name" name="lastname" required>
46 </div>
47 </div>
48 <div class="mb-6">
41 <label class="block text-gray-700 text-sm font-bold mb-2">E-Mail</label> 49 <label class="block text-gray-700 text-sm font-bold mb-2">E-Mail</label>
42 <div> 50 <div>
43 <input 51 <input
diff --git a/resources/views/dashboard/data.edge b/resources/views/dashboard/data.edge
index 60ef10b..cf48c1c 100644
--- a/resources/views/dashboard/data.edge
+++ b/resources/views/dashboard/data.edge
@@ -50,6 +50,14 @@
50 </tr> 50 </tr>
51 <tr> 51 <tr>
52 <td> 52 <td>
53 Last Name
54 </td>
55 <td>
56 {{ lastname }}
57 </td>
58 </tr>
59 <tr>
60 <td>
53 Created account on 61 Created account on
54 </td> 62 </td>
55 <td> 63 <td>
diff --git a/resources/views/layouts/main.edge b/resources/views/layouts/main.edge
index b6cc8ca..00d9987 100644
--- a/resources/views/layouts/main.edge
+++ b/resources/views/layouts/main.edge
@@ -6,6 +6,7 @@
6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 7 <meta http-equiv="X-UA-Compatible" content="ie=edge">
8 <title>ferdi-server</title> 8 <title>ferdi-server</title>
9 <link rel="shortcut icon" type="image/jpg" href="img/favicon.ico" />
9 10
10 {{ style('css/vanilla') }} 11 {{ style('css/vanilla') }}
11 {{ style('css/main') }} 12 {{ style('css/main') }}
diff --git a/resources/views/layouts/v2.edge b/resources/views/layouts/v2.edge
index 39f8085..f3bf37c 100644
--- a/resources/views/layouts/v2.edge
+++ b/resources/views/layouts/v2.edge
@@ -6,6 +6,7 @@
6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 7 <meta http-equiv="X-UA-Compatible" content="ie=edge">
8 <title>Ferdi Server</title> 8 <title>Ferdi Server</title>
9 <link rel="shortcut icon" type="image/jpg" href="img/favicon.ico" />
9 10
10 {{ style('css/tailwind') }} 11 {{ style('css/tailwind') }}
11 {{ style('css/main') }} 12 {{ style('css/main') }}
diff --git a/start/events.js b/start/events.js
index 1e4ed5f..c4076cc 100644
--- a/start/events.js
+++ b/start/events.js
@@ -12,12 +12,14 @@ ${Env.get('APP_URL')}/user/reset?token=${encodeURIComponent(token)}
12 12
13This message was sent automatically. Please do not reply. 13This message was sent automatically. Please do not reply.
14`; 14`;
15console.log('Sending message', body); 15 console.log('Sending message', body);
16 try { 16 try {
17 await Mail.raw(body, (message) => { 17 await Mail.raw(body, (message) => {
18 message.subject('[Ferdi] Reset your password') 18 message.subject('[Ferdi] Reset your password');
19 message.from(Env.get('MAIL_SENDER')) 19 message.from(Env.get('MAIL_SENDER'));
20 message.to(user.email) 20 message.to(user.email);
21 }); 21 });
22 } catch(e) {} 22 } catch (e) {
23}); \ No newline at end of file 23 console.log(`Couldn't send mail: ${e}`);
24 }
25});
diff --git a/start/routes.js b/start/routes.js
index 1c2d4f3..05bc538 100644
--- a/start/routes.js
+++ b/start/routes.js
@@ -60,26 +60,25 @@ Route.group(() => {
60}).prefix('v1'); 60}).prefix('v1');
61 61
62// User dashboard 62// User dashboard
63if (Env.get('IS_DASHBOARD_ENABLED') != 'false') { 63if (Env.get('IS_DASHBOARD_ENABLED') !== 'false') {
64 Route.group(() => { 64 Route.group(() => {
65 // Auth 65 // Auth
66 Route.get('login', ({ view }) => view.render('dashboard.login')).middleware('guest'); 66 Route.get('login', ({ view }) => view.render('dashboard.login')).middleware('guest');
67 Route.post('login', 'DashboardController.login').middleware('guest').as('login'); 67 Route.post('login', 'DashboardController.login').middleware('guest').as('login');
68 68
69 // Reset password 69 // Reset password
70 Route.get('forgot', ({ view }) => view.render('dashboard.forgotPassword')).middleware('guest'); 70 Route.get('forgot', ({ view }) => view.render('dashboard.forgotPassword')).middleware('guest');
71 Route.post('forgot', 'DashboardController.forgotPassword').middleware('guest'); 71 Route.post('forgot', 'DashboardController.forgotPassword').middleware('guest');
72 72
73 Route.get('reset', ({ view, request }) => { 73 Route.get('reset', ({ view, request }) => {
74 const token = request.get().token; 74 const { token } = request.get();
75 if (token) { 75 if (token) {
76 return view.render('dashboard.resetPassword', { token }) 76 return view.render('dashboard.resetPassword', { token });
77 } else {
78 return view.render('others.message', {
79 heading: 'Invalid token',
80 text: 'Please make sure you are using a valid and recent link to reset your password.',
81 });
82 } 77 }
78 return view.render('others.message', {
79 heading: 'Invalid token',
80 text: 'Please make sure you are using a valid and recent link to reset your password.',
81 });
83 }).middleware('guest'); 82 }).middleware('guest');
84 Route.post('reset', 'DashboardController.resetPassword').middleware('guest'); 83 Route.post('reset', 'DashboardController.resetPassword').middleware('guest');
85 84
@@ -92,19 +91,19 @@ if (Env.get('IS_DASHBOARD_ENABLED') != 'false') {
92 Route.get('export', 'DashboardController.export').middleware('auth:session'); 91 Route.get('export', 'DashboardController.export').middleware('auth:session');
93 Route.post('transfer', 'DashboardController.import').middleware('auth:session'); 92 Route.post('transfer', 'DashboardController.import').middleware('auth:session');
94 Route.get('transfer', ({ view }) => view.render('dashboard.transfer')).middleware('auth:session'); 93 Route.get('transfer', ({ view }) => view.render('dashboard.transfer')).middleware('auth:session');
95 94
96 Route.get('delete', ({ view }) => view.render('dashboard.delete')).middleware('auth:session'); 95 Route.get('delete', ({ view }) => view.render('dashboard.delete')).middleware('auth:session');
97 Route.post('delete', 'DashboardController.delete').middleware('auth:session'); 96 Route.post('delete', 'DashboardController.delete').middleware('auth:session');
98 97
99 Route.get('logout', 'DashboardController.logout').middleware('auth:session'); 98 Route.get('logout', 'DashboardController.logout').middleware('auth:session');
100 99
101 Route.get('*', ({ response }) => response.redirect('/user/account')); 100 Route.get('*', ({ response }) => response.redirect('/user/account'));
102 }).prefix('user').middleware('shield'); 101 }).prefix('user').middleware('shield');
103} else { 102} else {
104 Route.group(() => { 103 Route.group(() => {
105 Route.get('*', ({ 104 Route.get('*', ({
106 response, 105 response,
107 }) => 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.')) 106 }) => 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.'));
108 }).prefix('user'); 107 }).prefix('user');
109} 108}
110 109