diff options
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 | }; |
@@ -18,3 +18,6 @@ recipes/ | |||
18 | resources/announcements/*.json | 18 | resources/announcements/*.json |
19 | !resources/announcements/version.json | 19 | !resources/announcements/version.json |
20 | npm-debug.log | 20 | npm-debug.log |
21 | yarn-error.log | ||
22 | server*.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 | |||
4 | npm run lint | ||
@@ -0,0 +1,2 @@ | |||
1 | save-exact = true | ||
2 | engine-strict = true | ||
@@ -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 | |||
28 | This 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 | |||
32 | For 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 | |||
36 | As 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 | |||
44 | Please make sure you are running the exact node version used by the developers/contributors as specified in the [nvmrc file](./.nvmrc). | ||
45 | |||
46 | Currently, 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 | ||
49 | node -v | ||
50 | v14.16.1 | ||
51 | npm -v | ||
52 | 6.14.12 | ||
53 | node-gyp -v | ||
54 | v8.0.0 | ||
55 | ``` | ||
56 | |||
57 | #### Git | ||
58 | |||
59 | The 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 | ||
64 | apt install libx11-dev libxext-dev libxss-dev libxkbfile-dev rpm | ||
65 | ``` | ||
66 | |||
67 | #### Fedora | ||
68 | |||
69 | ```bash | ||
70 | dnf install libX11-devel libXext-devel libXScrnSaver-devel libxkbfile-devel rpm | ||
71 | ``` | ||
72 | |||
73 | #### Windows | ||
74 | |||
75 | Please make sure you run this command as an administrator: | ||
76 | |||
77 | ```bash | ||
78 | npm i -g windows-build-tools --vs2015 | ||
79 | ``` | ||
80 | --> | ||
81 | |||
82 | ### Clone repository with submodule | ||
83 | |||
84 | ```bash | ||
85 | git clone https://github.com/getferdi/server.git | ||
86 | cd server | ||
87 | git submodule update --init --recursive | ||
88 | ``` | ||
89 | |||
90 | It 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 | ||
97 | npm i | ||
98 | ``` | ||
99 | |||
100 | - Copy the `.env.example` file into `.env` and change the values to match your system. | ||
101 | |||
102 | ```bash | ||
103 | cp .env.example .env | ||
104 | ``` | ||
105 | |||
106 | _Note:_ | ||
107 | |||
108 | 1. Have env DB_SSL=true only if your database is postgres and it is hosted online on platforms like GCP, AWS, etc | ||
109 | 2. 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 | ||
114 | mkdir -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 | |||
127 | Ferdi 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 | ||
130 | cd 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 | ||
@@ -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 |
38 | The easiest way to set up Ferdi server on your server is with Docker. | 39 | The easiest way to set up Ferdi server on your server is with Docker. |
39 | 40 | ||
40 | The 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). | 41 | The 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). |
41 | After 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 | After 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 | ||
43 | 1. Pull the Docker image | 44 | 1. 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 | ``` |
48 | 2. Create a new Docker container with your desired configuration | 49 | 2. 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 | |||
85 | For more information on configuring the Docker image, please read [the ferdi docker documentation](./docker/README.md). | 86 | For 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 | |||
88 | 1. Clone this repository | 90 | 1. Clone this repository |
89 | 2. Install the [AdonisJS CLI](https://adonisjs.com/) | 91 | 2. Install the [AdonisJS CLI](https://adonisjs.com/) |
90 | 3. Copy `.env.example` to `.env` and edit the [configuration](#configuration) to your needs | 92 | 3. Copy `.env.example` to `.env` and edit the [configuration](#configuration) to your needs |
91 | 4. Have env DB_SSL=true only if your database is postgres and it is hosted online on platforms like GCP, AWS, etc | 93 | 4. Have env DB_SSL=true only if your database is postgres and it is hosted online on platforms like GCP, AWS, etc |
92 | 5. Run `npm install` to install local dependencies | 94 | 5. Run `npm install` to install local dependencies |
93 | 6. Run the database migrations with | 95 | 6. Run the database migrations with |
96 | |||
94 | ```js | 97 | ```js |
95 | adonis migration:run | 98 | node ace migration:run |
96 | ``` | 99 | ``` |
100 | |||
97 | 7. Start the server with | 101 | 7. Start the server with |
98 | ```js | 102 | |
99 | adonis serve --dev | ||
100 | ``` | ||
101 | 8. 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 | |||
111 | franz-server's configuration is saved inside the `.env` file. Besides AdonisJS's settings, ferdi-server has the following custom settings: | 109 | franz-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 | |||
120 | ferdi-server allows you to import your full Franz account, including all its settings. | 119 | ferdi-server allows you to import your full Franz account, including all its settings. |
121 | 120 | ||
122 | To 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. | 121 | To 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 | |||
125 | Please refer to <https://github.com/getferdi/ferdi/wiki/Transferring-data-between-servers> | 125 | Please 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 | |||
128 | ferdi-server allows to extends the Franz recipe catalogue with custom Ferdi recipes. | 129 | ferdi-server allows to extends the Franz recipe catalogue with custom Ferdi recipes. |
129 | 130 | ||
130 | For 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 | For 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 | ||
132 | To add your recipe to ferdi-server, open `http://[YOUR FERDI-SERVER]/new` in your browser. You can now define the following settings: | 133 | To 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 | |||
140 | Inside Ferdi, searching for `ferdi:custom` will list all your custom recipes. | 143 | Inside Ferdi, searching for `ferdi:custom` will list all your custom recipes. |
141 | 144 | ||
145 | ## Contributing to ferdi-server's development | ||
146 | |||
147 | We welcome all contributors. Please read the [contributing guidelines](CONTRIBUTING.md) to setup your development machine and proceed. | ||
148 | |||
142 | ## License | 149 | ## License |
150 | |||
143 | ferdi-server is licensed under the MIT License | 151 | ferdi-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 | ||
24 | module.exports = HandleDoubleSlash | 24 | module.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 | ||
5 | class Token extends Model { | 5 | class 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' | 1 | const Env = use('Env'); |
2 | |||
3 | const Env = use('Env') | ||
4 | 2 | ||
5 | module.exports = { | 3 | module.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')} */ | ||
2 | const Schema = use('Schema'); | ||
3 | |||
4 | class 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 | |||
18 | module.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 | ||
13 | This message was sent automatically. Please do not reply. | 13 | This message was sent automatically. Please do not reply. |
14 | `; | 14 | `; |
15 | console.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 |
63 | if (Env.get('IS_DASHBOARD_ENABLED') != 'false') { | 63 | if (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 | ||