From b065793980a01e800e9f6bec41ba94c71023955d Mon Sep 17 00:00:00 2001 From: root Date: Tue, 15 Oct 2024 16:21:34 +0300 Subject: [PATCH] Added support for Dicebear Avatars --- README.md | 2 ++ src/config.js | 2 ++ src/lib/Server.js | 11 +++++++++++ src/www/js/api.js | 7 +++++++ src/www/js/app.js | 19 ++++++++++++++++++- 5 files changed, 40 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3c100cc4..66a18027 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,8 @@ These options can be configured by setting environment variables using `-e KEY=" | `LANG` | `en` | `de` | Web UI language (Supports: en, ua, ru, tr, no, pl, fr, de, ca, es, ko, vi, nl, is, pt, chs, cht, it, th, hi, ja, si). | | `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 | +| `DICEBEAR_TYPE` | `false` | `bottts` | see [dicebear types](https://www.dicebear.com/styles/) | +| `USE_GRAVATAR` | `false` | `true` | Use or not GRAVATAR service | | `WG_ENABLE_ONE_TIME_LINKS` | `false` | `true` | Enable display and generation of short one time download links (expire after 5 minutes) | | `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. | | `UI_ENABLE_SORT_CLIENTS` | `false` | `true` | Enable UI sort clients by name | diff --git a/src/config.js b/src/config.js index 72314ae1..a5fc7757 100644 --- a/src/config.js +++ b/src/config.js @@ -45,3 +45,5 @@ module.exports.UI_ENABLE_SORT_CLIENTS = process.env.UI_ENABLE_SORT_CLIENTS || 'f module.exports.WG_ENABLE_EXPIRES_TIME = process.env.WG_ENABLE_EXPIRES_TIME || 'false'; module.exports.ENABLE_PROMETHEUS_METRICS = process.env.ENABLE_PROMETHEUS_METRICS || 'false'; module.exports.PROMETHEUS_METRICS_PASSWORD = process.env.PROMETHEUS_METRICS_PASSWORD; +module.exports.DICEBEAR_TYPE = process.env.DICEBEAR_TYPE || false; +module.exports.USE_GRAVATAR = process.env.USE_GRAVATAR || false; \ No newline at end of file diff --git a/src/lib/Server.js b/src/lib/Server.js index da2f6c9d..a420e7ce 100644 --- a/src/lib/Server.js +++ b/src/lib/Server.js @@ -35,6 +35,8 @@ const { LANG, UI_TRAFFIC_STATS, UI_CHART_TYPE, + DICEBEAR_TYPE, + USE_GRAVATAR, WG_ENABLE_ONE_TIME_LINKS, UI_ENABLE_SORT_CLIENTS, WG_ENABLE_EXPIRES_TIME, @@ -125,6 +127,14 @@ module.exports = class Server { return `${WG_ENABLE_EXPIRES_TIME}`; })) + .get('/api/ui-avatar-settings', defineEventHandler((event) => { + setHeader(event, 'Content-Type', 'application/json'); + return { + dicebear: DICEBEAR_TYPE, + gravatar: USE_GRAVATAR, + } + })) + // Authentication .get('/api/session', defineEventHandler((event) => { const authenticated = requiresPassword @@ -418,6 +428,7 @@ module.exports = class Server { if (id.endsWith('.json')) setHeader(event, 'Content-Type', 'application/json'); if (id.endsWith('.css')) setHeader(event, 'Content-Type', 'text/css'); if (id.endsWith('.png')) setHeader(event, 'Content-Type', 'image/png'); + if (id.endsWith('.svg')) setHeader(event, 'Content-Type', 'image/svg+xml'); return { size: stats.size, diff --git a/src/www/js/api.js b/src/www/js/api.js index 7b836d37..183d6c98 100644 --- a/src/www/js/api.js +++ b/src/www/js/api.js @@ -78,6 +78,13 @@ class API { }); } + async getAvatarSettings() { + return this.call({ + method: 'get', + path: '/ui-avatar-settings', + }); + } + async getSession() { return this.call({ method: 'get', diff --git a/src/www/js/app.js b/src/www/js/app.js index bef58260..868bf307 100644 --- a/src/www/js/app.js +++ b/src/www/js/app.js @@ -92,6 +92,10 @@ new Vue({ uiTrafficStats: false, uiChartType: 0, + avatarSettings: { + 'dicebear': null, + 'gravatar': false, + }, enableOneTimeLinks: false, enableSortClient: false, sortClient: true, // Sort clients by name, true = asc, false = desc @@ -200,8 +204,10 @@ new Vue({ const clients = await this.api.getClients(); this.clients = clients.map((client) => { - if (client.name.includes('@') && client.name.includes('.')) { + if (client.name.includes('@') && client.name.includes('.')) && this.avatarSettings.gravatar) { client.avatar = `https://gravatar.com/avatar/${sha256(client.name.toLowerCase().trim())}.jpg`; + } else if (this.avatarSettings.dicebear) { + client.avatar = `https://api.dicebear.com/9.x/${this.avatarSettings.dicebear}/svg?seed=${sha256(client.name.toLowerCase().trim())}` } if (!this.clientsPersist[client.id]) { @@ -465,6 +471,17 @@ new Vue({ this.enableExpireTime = false; }); + this.api.getAvatarSettings() + .then((res) => { + this.avatarSettings = res; + }) + .catch(() => { + this.avatarSettings = { + 'dicebear': null, + 'gravatar': false, + }; + }); + Promise.resolve().then(async () => { const lang = await this.api.getLang(); if (lang !== localStorage.getItem('lang') && i18n.availableLocales.includes(lang)) {