Browse Source

Add Remember me (#1276)

pull/1290/head
Philip H. 8 months ago
committed by GitHub
parent
commit
7c521e8733
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      README.md
  2. 1
      src/config.js
  3. 11
      src/lib/Server.js
  4. 18
      src/www/index.html
  5. 11
      src/www/js/api.js
  6. 8
      src/www/js/app.js
  7. 2
      src/www/js/i18n.js

1
README.md

@ -122,6 +122,7 @@ These options can be configured by setting environment variables using `-e KEY="
| `UI_TRAFFIC_STATS` | `false` | `true` | Enable detailed RX / TX client stats in Web UI |
| `UI_CHART_TYPE` | `0` | `1` | UI_CHART_TYPE=0 # Charts disabled, UI_CHART_TYPE=1 # Line chart, UI_CHART_TYPE=2 # Area chart, UI_CHART_TYPE=3 # Bar chart |
| `UI_SHOW_LINKS` | `false` | `true` | Enable display of a short download link in Web UI |
| `MAX_AGE` | `0` | `1440` | The maximum age of Web UI sessions in minutes. `0` means that the session will exist until the browser is closed. |
> If you change `WG_PORT`, make sure to also change the exposed port.

1
src/config.js

@ -6,6 +6,7 @@ module.exports.RELEASE = version;
module.exports.PORT = process.env.PORT || '51821';
module.exports.WEBUI_HOST = process.env.WEBUI_HOST || '0.0.0.0';
module.exports.PASSWORD_HASH = process.env.PASSWORD_HASH;
module.exports.MAX_AGE = parseInt(process.env.MAX_AGE, 10) * 1000 * 60 || 0;
module.exports.WG_PATH = process.env.WG_PATH || '/etc/wireguard/';
module.exports.WG_DEVICE = process.env.WG_DEVICE || 'eth0';
module.exports.WG_HOST = process.env.WG_HOST;

11
src/lib/Server.js

@ -29,6 +29,7 @@ const {
WEBUI_HOST,
RELEASE,
PASSWORD_HASH,
MAX_AGE,
LANG,
UI_TRAFFIC_STATS,
UI_CHART_TYPE,
@ -83,6 +84,11 @@ module.exports = class Server {
return `"${LANG}"`;
}))
.get('/api/remember-me', defineEventHandler((event) => {
setHeader(event, 'Content-Type', 'application/json');
return MAX_AGE > 0;
}))
.get('/api/ui-traffic-stats', defineEventHandler((event) => {
setHeader(event, 'Content-Type', 'application/json');
return `"${UI_TRAFFIC_STATS}"`;
@ -121,7 +127,7 @@ module.exports = class Server {
return config;
}))
.post('/api/session', defineEventHandler(async (event) => {
const { password } = await readBody(event);
const { password, remember } = await readBody(event);
if (!requiresPassword) {
// if no password is required, the API should never be called.
@ -139,6 +145,9 @@ module.exports = class Server {
});
}
if (MAX_AGE && remember) {
event.node.req.session.cookie.maxAge = MAX_AGE;
}
event.node.req.session.authenticated = true;
event.node.req.session.save();

18
src/www/index.html

@ -565,7 +565,25 @@
<input type="password" name="password" :placeholder="$t('password')" v-model="password" autocomplete="current-password"
class="px-3 py-2 text-sm dark:bg-neutral-700 text-gray-500 dark:text-gray-500 mb-5 border-2 border-gray-100 dark:border-neutral-800 rounded-lg w-full focus:border-red-800 dark:focus:border-red-800 dark:placeholder:text-neutral-400 outline-none" />
<!-- Remember me -->
<label v-if="rememberMeEnabled"
class="inline-block mb-5 cursor-pointer whitespace-nowrap" :title="$t('titleRememberMe')">
<input type="checkbox" class="sr-only" v-model="remember">
<div v-if="remember"
class="inline-block align-middle rounded-full w-10 h-6 mr-1 bg-red-800 cursor-pointer hover:bg-red-700 transition-all">
<div class="rounded-full w-4 h-4 m-1 ml-5 bg-white"></div>
</div>
<div v-if="!remember"
class="inline-block align-middle rounded-full w-10 h-6 mr-1 bg-gray-200 dark:bg-neutral-400 cursor-pointer hover:bg-gray-300 dark:hover:bg-neutral-500 transition-all">
<div class="rounded-full w-4 h-4 m-1 bg-white"></div>
</div>
<span class="text-sm">{{$t("rememberMe")}}</span>
</label>
<button v-if="authenticating"
class="bg-red-800 dark:bg-red-800 w-full rounded shadow py-2 text-sm text-white dark:text-white cursor-not-allowed">
<svg class="w-5 animate-spin mx-auto" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"

11
src/www/js/api.js

@ -43,6 +43,13 @@ class API {
});
}
async getRememberMeEnabled() {
return this.call({
method: 'get',
path: '/remember-me',
});
}
async getuiTrafficStats() {
return this.call({
method: 'get',
@ -71,11 +78,11 @@ class API {
});
}
async createSession({ password }) {
async createSession({ password, remember }) {
return this.call({
method: 'post',
path: '/session',
body: { password },
body: { password, remember },
});
}

8
src/www/js/app.js

@ -53,6 +53,8 @@ new Vue({
authenticating: false,
password: null,
requiresPassword: null,
remember: false,
rememberMeEnabled: false,
clients: null,
clientsPersist: {},
@ -240,6 +242,7 @@ new Vue({
this.authenticating = true;
this.api.createSession({
password: this.password,
remember: this.remember,
})
.then(async () => {
const session = await this.api.getSession();
@ -363,6 +366,11 @@ new Vue({
alert(err.message || err.toString());
});
this.api.getRememberMeEnabled()
.then((rememberMeEnabled) => {
this.rememberMeEnabled = rememberMeEnabled;
});
setInterval(() => {
this.refresh({
updateCharts: this.updateCharts,

2
src/www/js/i18n.js

@ -34,6 +34,8 @@ const messages = { // eslint-disable-line no-unused-vars
backup: 'Backup',
titleRestoreConfig: 'Restore your configuration',
titleBackupConfig: 'Backup your configuration',
rememberMe: 'Remember me',
titleRememberMe: 'Stay logged after closing the browser',
},
ua: {
name: 'Ім`я',

Loading…
Cancel
Save