Refactor: Standardize units to GB/MB, fix Dashboard live traffic aggregation, and optimize WireGuard client layout with expanded actions.

This commit is contained in:
xtcnet 2026-03-10 19:02:44 +07:00
parent b77da8e6de
commit 090894021a
13 changed files with 236 additions and 591 deletions

4
.dockerignore Normal file
View file

@ -0,0 +1,4 @@
node_modules/
frontend/node_modules/
backend/node_modules/
dist/

View file

@ -29,9 +29,13 @@ const internalWireguard = {
async getOrCreateInterface(knex) {
let iface = await knex("wg_interface").first();
if (!iface) {
const privateKey = await wgHelpers.generatePrivateKey();
const publicKey = await wgHelpers.getPublicKey(privateKey);
// Seed a default config if it doesn't exist
const insertData = {
name: "wg0",
private_key: privateKey,
public_key: publicKey,
listen_port: 51820,
ipv4_cidr: "10.0.0.1/24",
mtu: 1420,

View file

@ -39,13 +39,9 @@ router.get("/dashboard", async (req, res, next) => {
try {
const knex = db();
const access = res.locals.access;
const accessData = await access.can("proxy_hosts:get");
const accessData = await access.can("proxy_hosts:list");
const query = knex("wg_client").select("*");
if (accessData.permission_visibility !== "all") {
query.where("owner_user_id", access.token.getUserId(1));
}
const clients = await query;
const clients = await internalWireguard.getClients(knex, access, accessData);
let totalStorageBytes = 0;
let totalTransferRx = 0;
@ -61,14 +57,20 @@ router.get("/dashboard", async (req, res, next) => {
try {
totalStorageBytes += await internalWireguardFs.getClientStorageUsage(client.ipv4_address);
} catch (_) {}
totalTransferRx += Number(client.transfer_rx || 0);
totalTransferTx += Number(client.transfer_tx || 0);
totalTransferRx += parseInt(client.transfer_rx || 0, 10);
totalTransferTx += parseInt(client.transfer_tx || 0, 10);
if (client.latest_handshake_at) {
const handshake = new Date(client.latest_handshake_at).getTime();
if (now - handshake <= DAY) online24h++;
if (now - handshake <= 7 * DAY) online7d++;
if (now - handshake <= 30 * DAY) online30d++;
const handshakeStr = String(client.latest_handshake_at);
let handshakeTime = Date.parse(handshakeStr);
// Handle 0 or invalid epoch
if (handshakeTime > 0) {
if (now - handshakeTime <= DAY) online24h++;
if (now - handshakeTime <= 7 * DAY) online7d++;
if (now - handshakeTime <= 30 * DAY) online30d++;
}
}
}

View file

@ -2,13 +2,6 @@
# yarn lockfile v1
"@apidevtools/json-schema-ref-parser@^15.3.1":
version "15.3.1"
resolved "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-15.3.1.tgz"
integrity sha512-FIweGOR9zrNuskfDXn8dfsA4eJEe8LmmGsGSDikEZvgYm36SO36yMhasXSOX7/OTGZ3b7I9iPhOxB24D8xL5uQ==
dependencies:
js-yaml "^4.1.1"
"@apidevtools/json-schema-ref-parser@14.0.1":
version "14.0.1"
resolved "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-14.0.1.tgz"
@ -17,6 +10,13 @@
"@types/json-schema" "^7.0.15"
js-yaml "^4.1.0"
"@apidevtools/json-schema-ref-parser@^15.3.1":
version "15.3.1"
resolved "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-15.3.1.tgz"
integrity sha512-FIweGOR9zrNuskfDXn8dfsA4eJEe8LmmGsGSDikEZvgYm36SO36yMhasXSOX7/OTGZ3b7I9iPhOxB24D8xL5uQ==
dependencies:
js-yaml "^4.1.1"
"@apidevtools/openapi-schemas@^2.1.0":
version "2.1.0"
resolved "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz"
@ -53,6 +53,41 @@
"@biomejs/cli-win32-arm64" "2.4.5"
"@biomejs/cli-win32-x64" "2.4.5"
"@biomejs/cli-darwin-arm64@2.4.5":
version "2.4.5"
resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.5.tgz#a62472ab3529a3905b16e1f3fdbbc74f2e5f0023"
integrity sha512-lGS4Nd5O3KQJ6TeWv10mElnx1phERhBxqGP/IKq0SvZl78kcWDFMaTtVK+w3v3lusRFxJY78n07PbKplirsU5g==
"@biomejs/cli-darwin-x64@2.4.5":
version "2.4.5"
resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.5.tgz#e8bb001fcf6a8c751b0971cccf53993e9ba2e6e9"
integrity sha512-6MoH4tyISIBNkZ2Q5T1R7dLd5BsITb2yhhhrU9jHZxnNSNMWl+s2Mxu7NBF8Y3a7JJcqq9nsk8i637z4gqkJxQ==
"@biomejs/cli-linux-arm64-musl@2.4.5":
version "2.4.5"
resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.5.tgz#b7ef7902237f16113061659a4c54aff5ad4513d5"
integrity sha512-iqLDgpzobG7gpBF0fwEVS/LT8kmN7+S0E2YKFDtqliJfzNLnAiV2Nnyb+ehCDCJgAZBASkYHR2o60VQWikpqIg==
"@biomejs/cli-linux-arm64@2.4.5":
version "2.4.5"
resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.5.tgz#f110af748965cb1b57624dbbbd7acba729da8780"
integrity sha512-U1GAG6FTjhAO04MyH4xn23wRNBkT6H7NentHh+8UxD6ShXKBm5SY4RedKJzkUThANxb9rUKIPc7B8ew9Xo/cWg==
"@biomejs/cli-linux-x64-musl@2.4.5":
version "2.4.5"
resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.5.tgz#c3493eba094216e735538c55354dbc8867b51909"
integrity sha512-NlKa7GpbQmNhZf9kakQeddqZyT7itN7jjWdakELeXyTU3pg/83fTysRRDPJD0akTfKDl6vZYNT9Zqn4MYZVBOA==
"@biomejs/cli-linux-x64@2.4.5":
version "2.4.5"
resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.5.tgz#15805550db4e45ffbd6c42d140d0cb5c6dbe07af"
integrity sha512-NdODlSugMzTlENPTa4z0xB82dTUlCpsrOxc43///aNkTLblIYH4XpYflBbf5ySlQuP8AA4AZd1qXhV07IdrHdQ==
"@biomejs/cli-win32-arm64@2.4.5":
version "2.4.5"
resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.5.tgz#8dae57dc8ffc1e82e00a59e623b5023c09726a4d"
integrity sha512-EBfrTqRIWOFSd7CQb/0ttjHMR88zm3hGravnDwUA9wHAaCAYsULKDebWcN5RmrEo1KBtl/gDVJMrFjNR0pdGUw==
"@biomejs/cli-win32-x64@2.4.5":
version "2.4.5"
resolved "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.5.tgz"
@ -166,13 +201,6 @@
resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz"
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
"@types/node@>= 8":
version "25.3.5"
resolved "https://registry.npmjs.org/@types/node/-/node-25.3.5.tgz"
integrity sha512-oX8xrhvpiyRCQkG1MFchB09f+cXftgIXb3a7UUa4Y3wpmZPw5tyZGTLWhlESOLq1Rq6oDlc8npVU2/9xiCuXMA==
dependencies:
undici-types "~7.18.0"
abbrev@1:
version "1.1.1"
resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz"
@ -193,7 +221,7 @@ accepts@^2.0.0:
mime-types "^3.0.0"
negotiator "^1.0.0"
agent-base@^6.0.2, agent-base@6:
agent-base@6, agent-base@^6.0.2:
version "6.0.2"
resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz"
integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
@ -232,7 +260,7 @@ ajv-formats@^2.1.1:
dependencies:
ajv "^8.0.0"
ajv@^8.0.0, ajv@^8.17.1, ajv@^8.18.0, ajv@^8.5.0:
ajv@^8.0.0, ajv@^8.17.1, ajv@^8.18.0:
version "8.18.0"
resolved "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz"
integrity sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==
@ -501,7 +529,7 @@ busboy@^1.6.0:
dependencies:
streamsearch "^1.1.0"
bytes@^3.1.2, bytes@~3.1.2, bytes@3.1.2:
bytes@3.1.2, bytes@^3.1.2, bytes@~3.1.2:
version "3.1.2"
resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz"
integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
@ -556,6 +584,11 @@ camelcase@^5.0.0:
resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
chalk@5.6.2:
version "5.6.2"
resolved "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz"
integrity sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==
chalk@^2.3.2:
version "2.4.2"
resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz"
@ -565,11 +598,6 @@ chalk@^2.3.2:
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
chalk@5.6.2:
version "5.6.2"
resolved "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz"
integrity sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==
chokidar@^3.5.2:
version "3.6.0"
resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz"
@ -623,16 +651,16 @@ color-convert@^2.0.1:
dependencies:
color-name "~1.1.4"
color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
color-name@1.1.3:
version "1.1.3"
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
color-support@^1.1.3:
version "1.1.3"
resolved "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz"
@ -746,13 +774,6 @@ db-errors@^0.2.3:
resolved "https://registry.npmjs.org/db-errors/-/db-errors-0.2.3.tgz"
integrity sha512-OOgqgDuCavHXjYSJoV2yGhv6SeG8nk42aoCSoyXLZUH7VwFG27rxbavU1z+VrZbZjphw5UkDQwUlD21MwZpUng==
debug@^4, debug@^4.3.3, debug@^4.3.4, debug@^4.4.0, debug@^4.4.3, debug@4:
version "4.4.3"
resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz"
integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
dependencies:
ms "^2.1.3"
debug@2.6.9:
version "2.6.9"
resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
@ -760,6 +781,13 @@ debug@2.6.9:
dependencies:
ms "2.0.0"
debug@4, debug@^4, debug@^4.3.3, debug@^4.3.4, debug@^4.4.0, debug@^4.4.3:
version "4.4.3"
resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz"
integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
dependencies:
ms "^2.1.3"
debug@4.3.4:
version "4.3.4"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"
@ -1117,6 +1145,11 @@ fs.realpath@^1.0.0:
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
fsevents@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
function-bind@^1.1.2:
version "1.1.2"
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"
@ -1215,19 +1248,7 @@ glob@^10.0.0:
package-json-from-dist "^1.0.0"
path-scurry "^1.11.1"
glob@^7.1.3:
version "7.2.3"
resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.1.1"
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.1.4:
glob@^7.1.3, glob@^7.1.4:
version "7.2.3"
resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
@ -1384,7 +1405,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@~2.0.4, inherits@2:
inherits@2, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@~2.0.4:
version "2.0.4"
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@ -1549,7 +1570,7 @@ jws@^4.0.1:
jwa "^2.0.1"
safe-buffer "^5.0.1"
knex@>=1.0.1, knex@3.1.0:
knex@3.1.0:
version "3.1.0"
resolved "https://registry.npmjs.org/knex/-/knex-3.1.0.tgz"
integrity sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==
@ -1712,7 +1733,7 @@ merge-descriptors@^2.0.0:
resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz"
integrity sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==
mime-db@^1.54.0, "mime-db@>= 1.43.0 < 2":
"mime-db@>= 1.43.0 < 2", mime-db@^1.54.0:
version "1.54.0"
resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz"
integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==
@ -1801,44 +1822,23 @@ minipass-sized@^1.0.3:
dependencies:
minipass "^3.0.0"
minipass@^3.0.0:
minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3:
version "3.3.6"
resolved "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz"
integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==
dependencies:
yallist "^4.0.0"
minipass@^3.1.0:
version "3.3.6"
resolved "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz"
integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==
dependencies:
yallist "^4.0.0"
minipass@^3.1.1:
version "3.3.6"
resolved "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz"
integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==
dependencies:
yallist "^4.0.0"
minipass@^3.1.3:
version "3.3.6"
resolved "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz"
integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==
dependencies:
yallist "^4.0.0"
"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2:
version "7.1.3"
resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz"
integrity sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==
minipass@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz"
integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==
"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2:
version "7.1.3"
resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz"
integrity sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==
minizlib@^2.0.0, minizlib@^2.1.1:
version "2.1.2"
resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz"
@ -1862,11 +1862,6 @@ moment@^2.30.1:
resolved "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz"
integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==
ms@^2.0.0, ms@^2.1.1, ms@^2.1.3:
version "2.1.3"
resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
ms@2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
@ -1877,6 +1872,11 @@ ms@2.1.2:
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
ms@^2.0.0, ms@^2.1.1, ms@^2.1.3:
version "2.1.3"
resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
mysql2@^3.18.2:
version "3.18.2"
resolved "https://registry.npmjs.org/mysql2/-/mysql2-3.18.2.tgz"
@ -2034,11 +2034,6 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
dependencies:
wrappy "1"
openapi-types@>=7:
version "12.1.3"
resolved "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz"
integrity sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==
otplib@^13.3.0:
version "13.3.0"
resolved "https://registry.npmjs.org/otplib/-/otplib-13.3.0.tgz"
@ -2187,16 +2182,16 @@ pg-cloudflare@^1.3.0:
resolved "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz"
integrity sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==
pg-connection-string@^2.11.0:
version "2.11.0"
resolved "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.11.0.tgz"
integrity sha512-kecgoJwhOpxYU21rZjULrmrBJ698U2RxXofKVzOn5UDj61BPj/qMb7diYUR1nLScCDbrztQFl1TaQZT0t1EtzQ==
pg-connection-string@2.6.2:
version "2.6.2"
resolved "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz"
integrity sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==
pg-connection-string@^2.11.0:
version "2.11.0"
resolved "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.11.0.tgz"
integrity sha512-kecgoJwhOpxYU21rZjULrmrBJ698U2RxXofKVzOn5UDj61BPj/qMb7diYUR1nLScCDbrztQFl1TaQZT0t1EtzQ==
pg-int8@1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz"
@ -2223,7 +2218,7 @@ pg-types@2.2.0:
postgres-date "~1.0.4"
postgres-interval "^1.1.0"
pg@^8.19.0, pg@>=8.0:
pg@^8.19.0:
version "8.19.0"
resolved "https://registry.npmjs.org/pg/-/pg-8.19.0.tgz"
integrity sha512-QIcLGi508BAHkQ3pJNptsFz5WQMlpGbuBGBaIaXsWK8mel2kQ/rThYI+DbgjUvZrIr7MiuEuc9LcChJoEZK1xQ==
@ -2414,25 +2409,7 @@ readable-stream@^2.0.5:
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
readable-stream@^3.1.1:
version "3.6.2"
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz"
integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==
dependencies:
inherits "^2.0.3"
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
readable-stream@^3.4.0:
version "3.6.2"
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz"
integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==
dependencies:
inherits "^2.0.3"
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
readable-stream@^3.6.0:
readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
version "3.6.2"
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz"
integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==
@ -2525,7 +2502,7 @@ router@^2.2.0:
parseurl "^1.3.3"
path-to-regexp "^8.0.0"
safe-buffer@^5.0.1, safe-buffer@~5.2.0, safe-buffer@5.2.1:
safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
@ -2758,20 +2735,6 @@ streamx@^2.15.0:
fast-fifo "^1.3.2"
text-decoder "^1.1.0"
string_decoder@^1.1.1, string_decoder@^1.3.0:
version "1.3.0"
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz"
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
dependencies:
safe-buffer "~5.2.0"
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz"
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
safe-buffer "~5.1.0"
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
@ -2781,25 +2744,7 @@ string_decoder@~1.1.1:
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
"string-width@^1.0.2 || 2 || 3 || 4":
version "4.2.3"
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@^4.1.0, string-width@^4.2.0:
version "4.2.3"
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@^4.2.3:
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@ -2817,6 +2762,20 @@ string-width@^5.0.1, string-width@^5.1.2:
emoji-regex "^9.2.2"
strip-ansi "^7.0.1"
string_decoder@^1.1.1, string_decoder@^1.3.0:
version "1.3.0"
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz"
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
dependencies:
safe-buffer "~5.2.0"
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz"
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
safe-buffer "~5.1.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
@ -2981,11 +2940,6 @@ undefsafe@^2.0.5:
resolved "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz"
integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==
undici-types@~7.18.0:
version "7.18.2"
resolved "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz"
integrity sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==
unique-filename@^1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz"

View file

@ -1,255 +1 @@
/*
How this was generated:
1. bring up an empty pdns stack
2. use api to create a zone ...
curl -X POST \
'http://npm.dev:8081/api/v1/servers/localhost/zones' \
--header 'X-API-Key: npm' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "example.com.",
"kind": "Native",
"masters": [],
"nameservers": [
"ns1.pdns.",
"ns2.pdns."
]
}'
3. Dump sql:
docker exec -ti npm.pdns.db mysqldump -u pdns -p pdns
*/
----------------------------------------------------------------------
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `comments`
--
DROP TABLE IF EXISTS `comments`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `comments` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`domain_id` int(11) NOT NULL,
`name` varchar(255) NOT NULL,
`type` varchar(10) NOT NULL,
`modified_at` int(11) NOT NULL,
`account` varchar(40) CHARACTER SET utf8mb3 DEFAULT NULL,
`comment` text CHARACTER SET utf8mb3 NOT NULL,
PRIMARY KEY (`id`),
KEY `comments_name_type_idx` (`name`,`type`),
KEY `comments_order_idx` (`domain_id`,`modified_at`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `comments`
--
LOCK TABLES `comments` WRITE;
/*!40000 ALTER TABLE `comments` DISABLE KEYS */;
/*!40000 ALTER TABLE `comments` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `cryptokeys`
--
DROP TABLE IF EXISTS `cryptokeys`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `cryptokeys` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`domain_id` int(11) NOT NULL,
`flags` int(11) NOT NULL,
`active` tinyint(1) DEFAULT NULL,
`published` tinyint(1) DEFAULT 1,
`content` text DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `domainidindex` (`domain_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `cryptokeys`
--
LOCK TABLES `cryptokeys` WRITE;
/*!40000 ALTER TABLE `cryptokeys` DISABLE KEYS */;
/*!40000 ALTER TABLE `cryptokeys` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `domainmetadata`
--
DROP TABLE IF EXISTS `domainmetadata`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `domainmetadata` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`domain_id` int(11) NOT NULL,
`kind` varchar(32) DEFAULT NULL,
`content` text DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `domainmetadata_idx` (`domain_id`,`kind`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `domainmetadata`
--
LOCK TABLES `domainmetadata` WRITE;
/*!40000 ALTER TABLE `domainmetadata` DISABLE KEYS */;
INSERT INTO `domainmetadata` VALUES
(1,1,'SOA-EDIT-API','DEFAULT');
/*!40000 ALTER TABLE `domainmetadata` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `domains`
--
DROP TABLE IF EXISTS `domains`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `domains` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`master` varchar(128) DEFAULT NULL,
`last_check` int(11) DEFAULT NULL,
`type` varchar(8) NOT NULL,
`notified_serial` int(10) unsigned DEFAULT NULL,
`account` varchar(40) CHARACTER SET utf8mb3 DEFAULT NULL,
`options` varchar(64000) DEFAULT NULL,
`catalog` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name_index` (`name`),
KEY `catalog_idx` (`catalog`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `domains`
--
LOCK TABLES `domains` WRITE;
/*!40000 ALTER TABLE `domains` DISABLE KEYS */;
INSERT INTO `domains` VALUES
(1,'example.com','',NULL,'NATIVE',NULL,'',NULL,NULL);
/*!40000 ALTER TABLE `domains` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `records`
--
DROP TABLE IF EXISTS `records`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `records` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`domain_id` int(11) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`type` varchar(10) DEFAULT NULL,
`content` varchar(64000) DEFAULT NULL,
`ttl` int(11) DEFAULT NULL,
`prio` int(11) DEFAULT NULL,
`disabled` tinyint(1) DEFAULT 0,
`ordername` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL,
`auth` tinyint(1) DEFAULT 1,
PRIMARY KEY (`id`),
KEY `nametype_index` (`name`,`type`),
KEY `domain_id` (`domain_id`),
KEY `ordername` (`ordername`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `records`
--
LOCK TABLES `records` WRITE;
/*!40000 ALTER TABLE `records` DISABLE KEYS */;
INSERT INTO `records` VALUES
(1,1,'example.com','NS','ns1.pdns',1500,0,0,NULL,1),
(2,1,'example.com','NS','ns2.pdns',1500,0,0,NULL,1),
(3,1,'example.com','SOA','a.misconfigured.dns.server.invalid hostmaster.example.com 2023030501 10800 3600 604800 3600',1500,0,0,NULL,1);
/*!40000 ALTER TABLE `records` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `supermasters`
--
DROP TABLE IF EXISTS `supermasters`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `supermasters` (
`ip` varchar(64) NOT NULL,
`nameserver` varchar(255) NOT NULL,
`account` varchar(40) CHARACTER SET utf8mb3 NOT NULL,
PRIMARY KEY (`ip`,`nameserver`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `supermasters`
--
LOCK TABLES `supermasters` WRITE;
/*!40000 ALTER TABLE `supermasters` DISABLE KEYS */;
/*!40000 ALTER TABLE `supermasters` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `tsigkeys`
--
DROP TABLE IF EXISTS `tsigkeys`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `tsigkeys` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`algorithm` varchar(50) DEFAULT NULL,
`secret` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `namealgoindex` (`name`,`algorithm`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `tsigkeys`
--
LOCK TABLES `tsigkeys` WRITE;
/*!40000 ALTER TABLE `tsigkeys` DISABLE KEYS */;
/*!40000 ALTER TABLE `tsigkeys` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- MySQL DB Init Script Mock

View file

@ -1,92 +1 @@
# WELCOME TO SQUID 6.6
# ----------------------------
#
# This is the documentation for the Squid configuration file.
# This documentation can also be found online at:
# http://www.squid-cache.org/Doc/config/
#
# You may wish to look at the Squid home page and wiki for the
# FAQ and other documentation:
# http://www.squid-cache.org/
# https://wiki.squid-cache.org/SquidFaq
# https://wiki.squid-cache.org/ConfigExamples
#
# Example rule allowing access from your local networks.
# Adapt to list your (internal) IP networks from where browsing
# should be allowed
acl localnet src 0.0.0.1-0.255.255.255 # RFC 1122 "this" network (LAN)
acl localnet src 10.0.0.0/8 # RFC 1918 local private network (LAN)
acl localnet src 100.64.0.0/10 # RFC 6598 shared address space (CGN)
acl localnet src 169.254.0.0/16 # RFC 3927 link-local (directly plugged) machines
acl localnet src 172.0.0.0/8
acl localnet src 192.168.0.0/16 # RFC 1918 local private network (LAN)
acl localnet src fc00::/7 # RFC 4193 local private network range
acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines
acl SSL_ports port 443
acl Safe_ports port 80 # http
acl Safe_ports port 81
acl Safe_ports port 443 # https
#
# Recommended minimum Access Permission configuration:
#
# Deny requests to certain unsafe ports
http_access deny !Safe_ports
# Deny CONNECT to other than secure SSL ports
http_access deny CONNECT !SSL_ports
# Only allow cachemgr access from localhost
http_access allow localhost manager
http_access deny manager
# This default configuration only allows localhost requests because a more
# permissive Squid installation could introduce new attack vectors into the
# network by proxying external TCP connections to unprotected services.
http_access allow localhost
# The two deny rules below are unnecessary in this default configuration
# because they are followed by a "deny all" rule. However, they may become
# critically important when you start allowing external requests below them.
# Protect web applications running on the same server as Squid. They often
# assume that only local users can access them at "localhost" ports.
http_access deny to_localhost
# Protect cloud servers that provide local users with sensitive info about
# their server via certain well-known link-local (a.k.a. APIPA) addresses.
http_access deny to_linklocal
#
# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS
#
include /etc/squid/conf.d/*.conf
# For example, to allow access from your local networks, you may uncomment the
# following rule (and/or add rules that match your definition of "local"):
# http_access allow localnet
# And finally deny all other access to this proxy
http_access deny all
# Squid normally listens to port 3128
http_port 3128
# Leave coredumps in the first cache dir
coredump_dir /var/spool/squid
#
# Add any of your own refresh_pattern entries above these.
#
refresh_pattern ^ftp: 1440 20% 10080
refresh_pattern -i (/cgi-bin/|\?) 0 0% 0
refresh_pattern \/(Packages|Sources)(|\.bz2|\.gz|\.xz)$ 0 0% 0 refresh-ims
refresh_pattern \/Release(|\.gpg)$ 0 0% 0 refresh-ims
refresh_pattern \/InRelease$ 0 0% 0 refresh-ims
refresh_pattern \/(Translation-.*)(|\.bz2|\.gz|\.xz)$ 0 0% 0 refresh-ims
# example pattern for deb packages
#refresh_pattern (\.deb|\.udeb)$ 129600 100% 129600
refresh_pattern . 0 20% 4320
# Squid config mock

View file

@ -12,8 +12,8 @@ const WireGuardClientEditModal = EasyModal.create(({ client }: WireGuardClientEd
const modal = useModal<any>();
const [name, setName] = useState(client.name);
const [storageLimitMb, setStorageLimitMb] = useState<number>(client.storageLimitMb ?? 500);
const [txLimit, setTxLimit] = useState<number>(client.txLimit ?? 0);
const [rxLimit, setRxLimit] = useState<number>(client.rxLimit ?? 0);
const [txLimit, setTxLimit] = useState<number>(client.txLimit ? client.txLimit / 125000 : 0);
const [rxLimit, setRxLimit] = useState<number>(client.rxLimit ? client.rxLimit / 125000 : 0);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
@ -21,8 +21,8 @@ const WireGuardClientEditModal = EasyModal.create(({ client }: WireGuardClientEd
modal.resolve({
name: name.trim(),
storage_limit_mb: storageLimitMb,
tx_limit: txLimit,
rx_limit: rxLimit
tx_limit: txLimit * 125000,
rx_limit: rxLimit * 125000
});
modal.hide();
}
@ -81,7 +81,7 @@ const WireGuardClientEditModal = EasyModal.create(({ client }: WireGuardClientEd
<div className="row">
<div className="col-md-6 mb-3">
<label htmlFor="wg-edit-tx" className="form-label">
Upload Bandwidth Limit (Bytes)
Upload Bandwidth Limit (Mbps)
</label>
<input
type="number"
@ -90,13 +90,14 @@ const WireGuardClientEditModal = EasyModal.create(({ client }: WireGuardClientEd
value={txLimit}
onChange={(e) => setTxLimit(Number(e.target.value))}
min="0"
step="1"
required
/>
<div className="form-text">0 = Unlimited.</div>
</div>
<div className="col-md-6 mb-3">
<label htmlFor="wg-edit-rx" className="form-label">
Download Bandwidth Limit (Bytes)
Download Bandwidth Limit (Mbps)
</label>
<input
type="number"
@ -105,6 +106,7 @@ const WireGuardClientEditModal = EasyModal.create(({ client }: WireGuardClientEd
value={rxLimit}
onChange={(e) => setRxLimit(Number(e.target.value))}
min="0"
step="1"
required
/>
<div className="form-text">0 = Unlimited.</div>

View file

@ -24,8 +24,22 @@ const WireGuardClientLogsModal = EasyModal.create(({ clientId, clientName }: Pro
};
const formatDate = (dateString: string) => {
const d = new Date(dateString);
return d.toLocaleString();
try {
// Ensure UTC parsing from raw SQLite timestamp
const d = new Date(dateString.endsWith('Z') ? dateString : dateString + 'Z');
return isNaN(d.getTime()) ? dateString : d.toLocaleString();
} catch {
return dateString;
}
};
const parseLogMeta = (metaString: string | any) => {
try {
const meta = typeof metaString === 'string' ? JSON.parse(metaString) : metaString;
return meta?.message || typeof meta === 'string' ? meta : JSON.stringify(meta);
} catch {
return String(metaString);
}
};
const getActionBadge = (action: string) => {
@ -75,7 +89,7 @@ const WireGuardClientLogsModal = EasyModal.create(({ clientId, clientName }: Pro
</td>
<td>{getActionBadge(log.action)}</td>
<td className="small text-muted text-wrap">
{log.meta && log.meta.message ? log.meta.message : JSON.stringify(log.meta)}
{parseLogMeta(log.meta)}
</td>
</tr>
))

View file

@ -32,8 +32,8 @@ const WireGuardClientModal = EasyModal.create(({ interfaces, defaultInterfaceId
name: name.trim(),
interface_id: selectedInterfaceId,
storage_limit_mb: storageLimitMb,
tx_limit: txLimit,
rx_limit: rxLimit
tx_limit: txLimit * 125000,
rx_limit: rxLimit * 125000
});
modal.hide();
}
@ -121,7 +121,7 @@ const WireGuardClientModal = EasyModal.create(({ interfaces, defaultInterfaceId
<div className="row">
<div className="col-md-6 mb-3">
<label htmlFor="wg-client-tx" className="form-label">
Upload Bandwidth Limit (Bytes)
Upload Bandwidth Limit (Mbps)
</label>
<input
type="number"
@ -130,13 +130,14 @@ const WireGuardClientModal = EasyModal.create(({ interfaces, defaultInterfaceId
value={txLimit}
onChange={(e) => setTxLimit(Number(e.target.value))}
min="0"
step="1"
required
/>
<div className="form-text">Optional. 0 = Unlimited.</div>
</div>
<div className="col-md-6 mb-3">
<label htmlFor="wg-client-rx" className="form-label">
Download Bandwidth Limit (Bytes)
Download Bandwidth Limit (Mbps)
</label>
<input
type="number"
@ -145,6 +146,7 @@ const WireGuardClientModal = EasyModal.create(({ interfaces, defaultInterfaceId
value={rxLimit}
onChange={(e) => setRxLimit(Number(e.target.value))}
min="0"
step="1"
required
/>
<div className="form-text">Optional. 0 = Unlimited.</div>

View file

@ -13,11 +13,17 @@ interface Props {
ipv4Address: string;
}
function formatBytes(bytes: number | null): string {
if (bytes === null || bytes === 0) return "0 B";
function formatBytes(bytes: number | null, unit?: string): string {
if (bytes === null || bytes === 0) return unit ? `0.00 ${unit}` : "0 B";
const k = 1024;
const sizes = ["B", "KB", "MB", "GB", "TB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
let i: number;
if (unit) {
i = sizes.indexOf(unit.toUpperCase());
if (i === -1) i = Math.floor(Math.log(bytes) / Math.log(k));
} else {
i = Math.floor(Math.log(bytes) / Math.log(k));
}
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
}

View file

@ -21,12 +21,18 @@ import {
VIEW,
} from "src/modules/Permissions";
function formatBytes(bytes: number | null): string {
if (bytes === null || bytes === 0) return "0 B";
function formatBytes(bytes: number | null, unit?: string): string {
if (bytes === null || bytes === 0) return unit ? `0.00 ${unit}` : "0 B";
const k = 1024;
const sizes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${Number.parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
let i: number;
if (unit) {
i = sizes.indexOf(unit.toUpperCase());
if (i === -1) i = Math.floor(Math.log(bytes) / Math.log(k));
} else {
i = Math.floor(Math.log(bytes) / Math.log(k));
}
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
}
const Dashboard = () => {
@ -218,7 +224,7 @@ const Dashboard = () => {
<div className="d-flex align-items-center">
<div className="subheader">Total Storage Utilized</div>
</div>
<div className="h1 mb-3">{formatBytes(wgStats?.totalStorageBytes || 0)}</div>
<div className="h1 mb-3">{((wgStats?.totalStorageBytes || 0) / (1024 * 1024 * 1024)).toFixed(2)} GB</div>
<div className="d-flex mb-2">
<div className="text-muted small"><IconFolder size={14} className="me-1"/> Encrypted Partition Capacity</div>
</div>
@ -231,10 +237,10 @@ const Dashboard = () => {
<div className="d-flex align-items-center">
<div className="subheader">Global Traffic Transfer</div>
</div>
<div className="h1 mb-3 text-blue">{formatBytes((wgStats?.totalTransferRx || 0) + (wgStats?.totalTransferTx || 0))}</div>
<div className="h1 mb-3 text-blue">{formatBytes((wgStats?.totalTransferRx || 0) + (wgStats?.totalTransferTx || 0), "GB")}</div>
<div className="d-flex mb-2">
<div className="text-muted small">
{formatBytes(wgStats?.totalTransferRx || 0)} | {formatBytes(wgStats?.totalTransferTx || 0)}
{formatBytes(wgStats?.totalTransferRx || 0, "GB")} | {formatBytes(wgStats?.totalTransferTx || 0, "GB")}
</div>
</div>
</div>

View file

@ -1,12 +1,18 @@
import { useState, useEffect } from "react";
import { IconShieldLock, IconNetwork, IconApi, IconFolders } from "@tabler/icons-react";
function formatBytes(bytes: number | null): string {
if (!bytes) return "0 B";
const k = 1024;
const sizes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${Number.parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
function formatBytes(bytes: number | null, unit?: string): string {
if (bytes === null || bytes === 0) return unit ? `0.00 ${unit}` : "0 B";
const k = 1024;
const sizes = ["B", "KB", "MB", "GB", "TB"];
let i: number;
if (unit) {
i = sizes.indexOf(unit.toUpperCase());
if (i === -1) i = Math.floor(Math.log(bytes) / Math.log(k));
} else {
i = Math.floor(Math.log(bytes) / Math.log(k));
}
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
}
export default function WgPublicPortal() {

View file

@ -1,17 +1,17 @@
import {
IconPlus,
IconDownload,
IconQrcode,
IconPlayerPlay,
IconPlayerPause,
IconTrash,
IconNetwork,
IconServer,
IconEdit,
IconPlus,
IconQrcode,
IconTrash,
IconLink,
IconZip,
IconServer,
IconFolder,
IconNotes,
IconDownload,
IconZip,
IconPlayerPause,
IconPlayerPlay,
IconNetwork,
} from "@tabler/icons-react";
import EasyModal from "ez-modal-react";
import { useState } from "react";
@ -37,11 +37,17 @@ import WireGuardFileManagerModal from "src/modals/WireGuardFileManagerModal";
import WireGuardClientEditModal from "src/modals/WireGuardClientEditModal";
import WireGuardClientLogsModal from "src/modals/WireGuardClientLogsModal";
function formatBytes(bytes: number | null): string {
if (bytes === null || bytes === 0) return "0 B";
function formatBytes(bytes: number | null, unit?: string): string {
if (bytes === null || bytes === 0) return unit ? `0.00 ${unit}` : "0 B";
const k = 1024;
const sizes = ["B", "KB", "MB", "GB", "TB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
let i: number;
if (unit) {
i = sizes.indexOf(unit.toUpperCase());
if (i === -1) i = Math.floor(Math.log(bytes) / Math.log(k));
} else {
i = Math.floor(Math.log(bytes) / Math.log(k));
}
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
}
@ -271,7 +277,7 @@ function WireGuard() {
<td>
<div className="d-flex flex-column small">
<span className="text-muted"><IconServer size={14} className="me-1"/> {iface.clientCount || 0} Clients</span>
<span className="text-muted"><IconFolder size={14} className="me-1"/> {formatBytes(iface.storageUsageBytes ?? 0)}</span>
<span className="text-muted"><IconFolder size={14} className="me-1"/> {formatBytes(iface.storageUsageBytes ?? 0, "GB")}</span>
</div>
</td>
<td>
@ -383,13 +389,13 @@ function WireGuard() {
<table className="table table-vcenter table-nowrap card-table">
<thead>
<tr>
<th>Status</th>
<th>Name</th>
<th>Server</th>
<th>IP Address</th>
<th>Last Handshake</th>
<th>Traffic & Storage</th>
<th className="text-end">Actions</th>
<th className="text-muted">Status</th>
<th className="text-muted">Name</th>
<th className="text-muted">Server</th>
<th className="text-muted">IP Address</th>
<th className="text-muted">Last Handshake</th>
<th className="text-muted">Traffic & Storage</th>
<th className="text-muted text-end">Actions</th>
</tr>
</thead>
<tbody>
@ -427,9 +433,9 @@ function WireGuard() {
<td>{timeAgo(client.latestHandshakeAt)}</td>
<td>
<div className="d-flex flex-column text-muted small">
<span> {formatBytes(client.transferRx)} / {formatBytes(client.transferTx)}</span>
<span className={client.storageLimitMb > 0 && ((client.storageUsageBytes||0) / (client.storageLimitMb * 1024 * 1024)) > 0.9 ? "text-danger" : ""}>
<IconFolder size={14} className="me-1"/> {formatBytes(client.storageUsageBytes || 0)} / {client.storageLimitMb === 0 ? "∞" : formatBytes(client.storageLimitMb * 1024 * 1024)}
<span> {formatBytes(client.transferRx || 0, "GB")} / {formatBytes(client.transferTx || 0, "GB")}</span>
<span className={client.storageLimitMb > 0 && ((client.storageUsageBytes || 0) / (client.storageLimitMb * 1024 * 1024)) > 0.9 ? "text-danger" : ""}>
<IconFolder size={14} className="me-1"/> {formatBytes(client.storageUsageBytes || 0, "MB")} / {client.storageLimitMb > 0 ? formatBytes(client.storageLimitMb * 1024 * 1024, "MB") : "∞"}
</span>
</div>
</td>
@ -467,57 +473,41 @@ function WireGuard() {
</button>
<button
type="button"
className="btn btn-outline-dark"
title="View Event Logs"
onClick={() =>
handleLogs(client)
}
className="btn btn-outline-secondary"
title="Event Logs"
onClick={() => handleLogs(client)}
>
<IconNotes size={16} />
</button>
<button
type="button"
className="btn btn-outline-primary"
className="btn btn-outline-success"
title="Download Config"
onClick={() =>
handleDownload(client.id, client.name)
}
onClick={() => handleDownload(client.id, client.name)}
>
<IconDownload size={16} />
</button>
<button
type="button"
className="btn btn-outline-primary"
title="Download Config + QR (ZIP)"
onClick={() =>
handleDownloadZip(client.id, client.name)
}
className="btn btn-outline-success"
title="Download ZIP Archive"
onClick={() => handleDownloadZip(client.id, client.name)}
>
<IconZip size={16} />
</button>
<button
type="button"
className={`btn ${client.enabled ? "btn-outline-warning" : "btn-outline-success"}`}
title={
client.enabled ? "Disable" : "Enable"
}
onClick={() =>
handleToggleClient(client.id, client.enabled)
}
title={client.enabled ? "Disable Client" : "Enable Client"}
onClick={() => handleToggleClient(client.id, client.enabled)}
>
{client.enabled ? (
<IconPlayerPause size={16} />
) : (
<IconPlayerPlay size={16} />
)}
{client.enabled ? <IconPlayerPause size={16} /> : <IconPlayerPlay size={16} />}
</button>
<button
type="button"
className="btn btn-outline-danger"
title="Delete"
onClick={() =>
handleDeleteClient(client.id, client.name)
}
title="Delete Client"
onClick={() => handleDeleteClient(client.id, client.name)}
>
<IconTrash size={16} />
</button>