Compare commits

...

38 Commits

Author SHA1 Message Date
Jamie Curnow
8eba31913f Remove pebble certs, they removed the dockerhub image that had armv7 support.
The ghcr image doesn't have it, so it was causing builds to fail.
2025-11-07 11:18:53 +10:00
Jamie Curnow
e4e3415120 Safer handling of backend date formats
and add frontend testing
2025-11-07 11:15:15 +10:00
Jamie Curnow
a03bb7ebce Remove Jenkinsfile, managed in other repo now 2025-11-07 10:54:21 +10:00
Jamie Curnow
51e25d1a40 Attempt to fix race condition with database instantiation 2025-11-07 09:46:00 +10:00
Jamie Curnow
4cb85f6480 Fix #4833 supports the usual proxy env vars for outgoing admin related requests
All checks were successful
Close stale issues and PRs / stale (push) Successful in 20s
2025-11-05 15:16:42 +10:00
jc21
df7dea2d16 Merge branch 'master' into develop 2025-11-05 12:35:06 +10:00
Jamie Curnow
23f4948bde Bump version 2025-11-05 12:33:59 +10:00
Jamie Curnow
0ceb7d0892 Fix #4838 when showing avatars of deleted users 2025-11-05 12:33:13 +10:00
Jamie Curnow
f35671db21 Fix #4837 for those with older config 2025-11-05 10:56:23 +10:00
Jamie Curnow
a3a0614948 Fix #4828 showing incorrect certicificate value 2025-11-05 10:21:55 +10:00
Jamie Curnow
06b67ed4bc Remove user name column from audit log
All checks were successful
Close stale issues and PRs / stale (push) Successful in 20s
2025-11-04 14:57:10 +10:00
Jamie Curnow
4a0e27572e Fix missing translation for renew cert dialog 2025-11-04 14:54:02 +10:00
jc21
fbea8dfa9e Merge pull request #4825 from NginxProxyManager/develop
v2.13.0
2025-11-04 14:23:00 +10:00
Jamie Curnow
8c37348b65 Properly wrap debug calls 2025-11-04 13:43:52 +10:00
Jamie Curnow
2b3e9d72f4 Updated docs screenshots 2025-11-04 13:05:21 +10:00
jc21
a3e5235d81 Merge branch 'master' into develop
All checks were successful
Close stale issues and PRs / stale (push) Successful in 26s
2025-11-04 07:47:04 +10:00
jc21
9875fa92f1 Merge pull request #4794 from Johno-ACSLive/develop
Add basic MySQL TLS support
2025-11-04 07:13:15 +10:00
jc21
ef5156b613 Merge pull request #4813 from potatojuicemachine/develop
Adds Hetzner Cloud to available plugins
2025-11-03 13:38:11 +10:00
Jamie Curnow
b9a34ebb7e Revert to cypress 14, 15 was causing problems with executing external commands 2025-11-03 12:53:23 +10:00
Jamie Curnow
7642d0a000 Cleanup cypress tests 2025-11-03 12:35:58 +10:00
Jamie Curnow
7a6a9de0ea Update frontend deps
All checks were successful
Close stale issues and PRs / stale (push) Successful in 19s
2025-11-03 10:53:46 +10:00
Jamie Curnow
a5d50f9588 Update test deps 2025-11-03 10:52:53 +10:00
Jamie Curnow
612695c2e8 Upgrade biomejs 2025-11-03 10:51:16 +10:00
Jonathon Aroutsidis
71a2277b9b Replace spaces with tabs 2025-11-03 10:48:14 +11:00
Jonathon Aroutsidis
5acf287ea7 Aligned Assignments and arrow-parens 2025-11-03 10:48:14 +11:00
Jonathon Aroutsidis
e34206b526 Include SSL Options for MySQL 2025-11-03 10:46:20 +11:00
jc21
6b00adf8b9 Merge pull request #4725 from NginxProxyManager/dependabot/npm_and_yarn/test/eslint/plugin-kit-0.3.5
Bump @eslint/plugin-kit from 0.3.2 to 0.3.5 in /test
2025-11-03 08:49:30 +10:00
jc21
a93558278e Merge pull request #4763 from NginxProxyManager/dependabot/npm_and_yarn/test/axios-1.12.0
Bump axios from 1.10.0 to 1.12.0 in /test
2025-11-03 08:37:03 +10:00
jc21
bc2867b357 Merge pull request #4803 from NginxProxyManager/dependabot/npm_and_yarn/docs/vite-5.4.21
Bump vite from 5.4.19 to 5.4.21 in /docs
2025-11-03 08:18:00 +10:00
jc21
52093ba258 Merge pull request #4805 from vlauciani/patch-1
Update PostgreSQL volume path in setup documentation for 18+
2025-11-03 08:15:23 +10:00
Tim Burr
e0985bee43 Merge remote-tracking branch 'base/react' into develop 2025-10-29 13:15:58 +01:00
Tim Burr
51dd6e6a1b Sets postgres version to 17 2025-10-29 10:59:01 +01:00
Tim Burr
a2ea63a539 Adds Hetzner Cloud 2025-10-27 13:48:41 +01:00
Valentino Lauciani
bfcd057755 Update PostgreSQL volume path in setup documentation for 18+ 2025-10-24 09:30:19 +02:00
dependabot[bot]
08bdc23131 Bump vite from 5.4.19 to 5.4.21 in /docs
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.19 to 5.4.21.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.4.21/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.4.21/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 5.4.21
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-21 07:13:05 +00:00
dependabot[bot]
c9aba0c928 Bump axios from 1.10.0 to 1.12.0 in /test
Bumps [axios](https://github.com/axios/axios) from 1.10.0 to 1.12.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.10.0...v1.12.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.12.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-13 15:18:55 +00:00
dependabot[bot]
4397f57a51 Bump @eslint/plugin-kit from 0.3.2 to 0.3.5 in /test
Bumps [@eslint/plugin-kit](https://github.com/eslint/rewrite/tree/HEAD/packages/plugin-kit) from 0.3.2 to 0.3.5.
- [Release notes](https://github.com/eslint/rewrite/releases)
- [Changelog](https://github.com/eslint/rewrite/blob/main/packages/plugin-kit/CHANGELOG.md)
- [Commits](https://github.com/eslint/rewrite/commits/plugin-kit-v0.3.5/packages/plugin-kit)

---
updated-dependencies:
- dependency-name: "@eslint/plugin-kit"
  dependency-version: 0.3.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-22 02:38:28 +00:00
jc21
356eaa0691 Merge pull request #4653 from NginxProxyManager/develop
v2.12.6
2025-07-10 07:18:53 +10:00
112 changed files with 842 additions and 749 deletions

View File

@@ -1 +1 @@
2.13.0
2.13.1

285
Jenkinsfile vendored
View File

@@ -1,285 +0,0 @@
import groovy.transform.Field
@Field
def shOutput = ""
def buildxPushTags = ""
pipeline {
agent {
label 'docker-multiarch'
}
options {
buildDiscarder(logRotator(numToKeepStr: '5'))
disableConcurrentBuilds()
ansiColor('xterm')
}
environment {
IMAGE = 'nginx-proxy-manager'
BUILD_VERSION = getVersion()
MAJOR_VERSION = '2'
BRANCH_LOWER = "${BRANCH_NAME.toLowerCase().replaceAll('\\\\', '-').replaceAll('/', '-').replaceAll('\\.', '-')}"
BUILDX_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}"
COMPOSE_INTERACTIVE_NO_CLI = 1
}
stages {
stage('Environment') {
parallel {
stage('Master') {
when {
branch 'master'
}
steps {
script {
buildxPushTags = "-t docker.io/jc21/${IMAGE}:${BUILD_VERSION} -t docker.io/jc21/${IMAGE}:${MAJOR_VERSION} -t docker.io/jc21/${IMAGE}:latest"
}
}
}
stage('Other') {
when {
not {
branch 'master'
}
}
steps {
script {
// Defaults to the Branch name, which is applies to all branches AND pr's
buildxPushTags = "-t docker.io/nginxproxymanager/${IMAGE}-dev:${BRANCH_LOWER}"
}
}
}
stage('Versions') {
steps {
sh 'cat frontend/package.json | jq --arg BUILD_VERSION "${BUILD_VERSION}" \'.version = $BUILD_VERSION\' | sponge frontend/package.json'
sh 'echo -e "\\E[1;36mFrontend Version is:\\E[1;33m $(cat frontend/package.json | jq -r .version)\\E[0m"'
sh 'cat backend/package.json | jq --arg BUILD_VERSION "${BUILD_VERSION}" \'.version = $BUILD_VERSION\' | sponge backend/package.json'
sh 'echo -e "\\E[1;36mBackend Version is:\\E[1;33m $(cat backend/package.json | jq -r .version)\\E[0m"'
sh 'sed -i -E "s/(version-)[0-9]+\\.[0-9]+\\.[0-9]+(-green)/\\1${BUILD_VERSION}\\2/" README.md'
}
}
stage('Docker Login') {
steps {
withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
sh 'docker login -u "${duser}" -p "${dpass}"'
}
}
}
}
}
stage('Builds') {
parallel {
stage('Project') {
steps {
script {
// Frontend and Backend
def shStatusCode = sh(label: 'Checking and Building', returnStatus: true, script: '''
set -e
./scripts/ci/frontend-build > ${WORKSPACE}/tmp-sh-build 2>&1
./scripts/ci/test-and-build > ${WORKSPACE}/tmp-sh-build 2>&1
''')
shOutput = readFile "${env.WORKSPACE}/tmp-sh-build"
if (shStatusCode != 0) {
error "${shOutput}"
}
}
}
post {
always {
sh 'rm -f ${WORKSPACE}/tmp-sh-build'
}
failure {
npmGithubPrComment("CI Error:\n\n```\n${shOutput}\n```", true)
}
}
}
stage('Docs') {
steps {
dir(path: 'docs') {
sh 'yarn install'
sh 'yarn build'
}
}
}
}
}
stage('Test Sqlite') {
environment {
COMPOSE_PROJECT_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}_sqlite"
COMPOSE_FILE = 'docker/docker-compose.ci.yml:docker/docker-compose.ci.sqlite.yml'
}
when {
not {
equals expected: 'UNSTABLE', actual: currentBuild.result
}
}
steps {
sh 'rm -rf ./test/results/junit/*'
sh './scripts/ci/fulltest-cypress'
}
post {
always {
// Dumps to analyze later
sh 'mkdir -p debug/sqlite'
sh 'docker logs $(docker compose ps --all -q fullstack) > debug/sqlite/docker_fullstack.log 2>&1'
sh 'docker logs $(docker compose ps --all -q stepca) > debug/sqlite/docker_stepca.log 2>&1'
sh 'docker logs $(docker compose ps --all -q pdns) > debug/sqlite/docker_pdns.log 2>&1'
sh 'docker logs $(docker compose ps --all -q pdns-db) > debug/sqlite/docker_pdns-db.log 2>&1'
sh 'docker logs $(docker compose ps --all -q dnsrouter) > debug/sqlite/docker_dnsrouter.log 2>&1'
junit 'test/results/junit/*'
sh 'docker compose down --remove-orphans --volumes -t 30 || true'
}
unstable {
dir(path: 'test/results') {
archiveArtifacts(allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml')
}
}
}
}
stage('Test Mysql') {
environment {
COMPOSE_PROJECT_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}_mysql"
COMPOSE_FILE = 'docker/docker-compose.ci.yml:docker/docker-compose.ci.mysql.yml'
}
when {
not {
equals expected: 'UNSTABLE', actual: currentBuild.result
}
}
steps {
sh 'rm -rf ./test/results/junit/*'
sh './scripts/ci/fulltest-cypress'
}
post {
always {
// Dumps to analyze later
sh 'mkdir -p debug/mysql'
sh 'docker logs $(docker compose ps --all -q fullstack) > debug/mysql/docker_fullstack.log 2>&1'
sh 'docker logs $(docker compose ps --all -q stepca) > debug/mysql/docker_stepca.log 2>&1'
sh 'docker logs $(docker compose ps --all -q pdns) > debug/mysql/docker_pdns.log 2>&1'
sh 'docker logs $(docker compose ps --all -q pdns-db) > debug/mysql/docker_pdns-db.log 2>&1'
sh 'docker logs $(docker compose ps --all -q dnsrouter) > debug/mysql/docker_dnsrouter.log 2>&1'
junit 'test/results/junit/*'
sh 'docker compose down --remove-orphans --volumes -t 30 || true'
}
unstable {
dir(path: 'test/results') {
archiveArtifacts(allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml')
}
}
}
}
stage('Test Postgres') {
environment {
COMPOSE_PROJECT_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}_postgres"
COMPOSE_FILE = 'docker/docker-compose.ci.yml:docker/docker-compose.ci.postgres.yml'
}
when {
not {
equals expected: 'UNSTABLE', actual: currentBuild.result
}
}
steps {
sh 'rm -rf ./test/results/junit/*'
sh './scripts/ci/fulltest-cypress'
}
post {
always {
// Dumps to analyze later
sh 'mkdir -p debug/postgres'
sh 'docker logs $(docker compose ps --all -q fullstack) > debug/postgres/docker_fullstack.log 2>&1'
sh 'docker logs $(docker compose ps --all -q stepca) > debug/postgres/docker_stepca.log 2>&1'
sh 'docker logs $(docker compose ps --all -q pdns) > debug/postgres/docker_pdns.log 2>&1'
sh 'docker logs $(docker compose ps --all -q pdns-db) > debug/postgres/docker_pdns-db.log 2>&1'
sh 'docker logs $(docker compose ps --all -q dnsrouter) > debug/postgres/docker_dnsrouter.log 2>&1'
sh 'docker logs $(docker compose ps --all -q db-postgres) > debug/postgres/docker_db-postgres.log 2>&1'
sh 'docker logs $(docker compose ps --all -q authentik) > debug/postgres/docker_authentik.log 2>&1'
sh 'docker logs $(docker compose ps --all -q authentik-redis) > debug/postgres/docker_authentik-redis.log 2>&1'
sh 'docker logs $(docke rcompose ps --all -q authentik-ldap) > debug/postgres/docker_authentik-ldap.log 2>&1'
junit 'test/results/junit/*'
sh 'docker compose down --remove-orphans --volumes -t 30 || true'
}
unstable {
dir(path: 'test/results') {
archiveArtifacts(allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml')
}
}
}
}
stage('MultiArch Build') {
when {
not {
equals expected: 'UNSTABLE', actual: currentBuild.result
}
}
steps {
sh "./scripts/buildx --push ${buildxPushTags}"
}
}
stage('Docs / Comment') {
parallel {
stage('Docs Job') {
when {
allOf {
branch pattern: "^(develop|master)\$", comparator: "REGEXP"
not {
equals expected: 'UNSTABLE', actual: currentBuild.result
}
}
}
steps {
build wait: false, job: 'nginx-proxy-manager-docs', parameters: [string(name: 'docs_branch', value: "$BRANCH_NAME")]
}
}
stage('PR Comment') {
when {
allOf {
changeRequest()
not {
equals expected: 'UNSTABLE', actual: currentBuild.result
}
}
}
steps {
script {
npmGithubPrComment("""Docker Image for build ${BUILD_NUMBER} is available on [DockerHub](https://cloud.docker.com/repository/docker/nginxproxymanager/${IMAGE}-dev):
```
nginxproxymanager/${IMAGE}-dev:${BRANCH_LOWER}
```
> [!NOTE]
> Ensure you backup your NPM instance before testing this image! Especially if there are database changes.
> This is a different docker image namespace than the official image.
> [!WARNING]
> Changes and additions to DNS Providers require verification by at least 2 members of the community!
""", true)
}
}
}
}
}
}
post {
always {
sh 'echo Reverting ownership'
sh 'docker run --rm -v "$(pwd):/data" jc21/ci-tools chown -R "$(id -u):$(id -g)" /data'
printResult(true)
}
failure {
archiveArtifacts(artifacts: 'debug/**/*.*', allowEmptyArchive: true)
}
unstable {
archiveArtifacts(artifacts: 'debug/**/*.*', allowEmptyArchive: true)
}
}
}
def getVersion() {
ver = sh(script: 'cat .version', returnStdout: true)
return ver.trim()
}
def getCommit() {
ver = sh(script: 'git log -n 1 --format=%h', returnStdout: true)
return ver.trim()
}

View File

@@ -1,7 +1,7 @@
<p align="center">
<img src="https://nginxproxymanager.com/github.png">
<br><br>
<img src="https://img.shields.io/badge/version-2.13.0-green.svg?style=for-the-badge">
<img src="https://img.shields.io/badge/version-2.13.1-green.svg?style=for-the-badge">
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge">
</a>

View File

@@ -5,7 +5,7 @@ import fileUpload from "express-fileupload";
import { isDebugMode } from "./lib/config.js";
import cors from "./lib/express/cors.js";
import jwt from "./lib/express/jwt.js";
import { express as logger } from "./logger.js";
import { debug, express as logger } from "./logger.js";
import mainRoutes from "./routes/main.js";
/**
@@ -80,7 +80,7 @@ app.use((err, req, res, _) => {
// Not every error is worth logging - but this is good for now until it gets annoying.
if (typeof err.stack !== "undefined" && err.stack) {
logger.debug(err.stack);
debug(logger, err.stack);
if (typeof err.public === "undefined" || !err.public) {
logger.warn(err.message);
}

View File

@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.3.1/schema.json",
"$schema": "https://biomejs.dev/schemas/2.3.2/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",

View File

@@ -295,6 +295,14 @@
"credentials": "dns_hetzner_api_token = 0123456789abcdef0123456789abcdef",
"full_plugin_name": "dns-hetzner"
},
"hetzner-cloud": {
"name": "Hetzner Cloud",
"package_name": "certbot-dns-hetzner-cloud",
"version": "~=1.0.4",
"dependencies": "",
"credentials": "dns_hetzner_cloud_api_token = your_api_token_here",
"full_plugin_name": "dns-hetzner-cloud"
},
"hostingnl": {
"name": "Hosting.nl",
"package_name": "certbot-dns-hostingnl",

View File

@@ -1,6 +1,8 @@
import knex from "knex";
import {configGet, configHas} from "./lib/config.js";
let instance = null;
const generateDbConfig = () => {
if (!configHas("database")) {
throw new Error(
@@ -21,7 +23,8 @@ const generateDbConfig = () => {
user: cfg.user,
password: cfg.password,
database: cfg.name,
port: cfg.port,
port: cfg.port,
...(cfg.ssl ? { ssl: cfg.ssl } : {})
},
migrations: {
tableName: "migrations",
@@ -29,4 +32,11 @@ const generateDbConfig = () => {
};
};
export default knex(generateDbConfig());
const getInstance = () => {
if (!instance) {
instance = knex(generateDbConfig());
}
return instance;
}
export default getInstance;

View File

@@ -4,13 +4,14 @@ import path from "path";
import archiver from "archiver";
import _ from "lodash";
import moment from "moment";
import { ProxyAgent } from "proxy-agent";
import tempWrite from "temp-write";
import dnsPlugins from "../certbot/dns-plugins.json" with { type: "json" };
import { installPlugin } from "../lib/certbot.js";
import { useLetsencryptServer, useLetsencryptStaging } from "../lib/config.js";
import error from "../lib/error.js";
import utils from "../lib/utils.js";
import { ssl as logger } from "../logger.js";
import { debug, ssl as logger } from "../logger.js";
import certificateModel from "../models/certificate.js";
import tokenModel from "../models/token.js";
import userModel from "../models/user.js";
@@ -355,7 +356,7 @@ const internalCertificate = {
const opName = `/tmp/${downloadName}`;
await internalCertificate.zipFiles(certFiles, opName);
logger.debug("zip completed : ", opName);
debug(logger, "zip completed : ", opName);
return {
fileName: opName,
};
@@ -375,7 +376,7 @@ const internalCertificate = {
return new Promise((resolve, reject) => {
source.map((fl) => {
const fileName = path.basename(fl);
logger.debug(fl, "added to certificate zip");
debug(logger, fl, "added to certificate zip");
archive.file(fl, { name: fileName });
return true;
});
@@ -1114,6 +1115,7 @@ const internalCertificate = {
performTestForDomain: async (domain) => {
logger.info(`Testing http challenge for ${domain}`);
const agent = new ProxyAgent();
const url = `http://${domain}/.well-known/acme-challenge/test-challenge`;
const formBody = `method=G&url=${encodeURI(url)}&bodytype=T&requestbody=&headername=User-Agent&headervalue=None&locationid=1&ch=false&cc=false`;
const options = {
@@ -1123,6 +1125,7 @@ const internalCertificate = {
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": Buffer.byteLength(formBody),
},
agent,
};
const result = await new Promise((resolve) => {

View File

@@ -2,6 +2,7 @@ import fs from "node:fs";
import https from "node:https";
import { dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { ProxyAgent } from "proxy-agent";
import errs from "../lib/error.js";
import utils from "../lib/utils.js";
import { ipRanges as logger } from "../logger.js";
@@ -29,10 +30,11 @@ const internalIpRanges = {
},
fetchUrl: (url) => {
const agent = new ProxyAgent();
return new Promise((resolve, reject) => {
logger.info(`Fetching ${url}`);
return https
.get(url, (res) => {
.get(url, { agent }, (res) => {
res.setEncoding("utf8");
let raw_data = "";
res.on("data", (chunk) => {

View File

@@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
import _ from "lodash";
import errs from "../lib/error.js";
import utils from "../lib/utils.js";
import { nginx as logger } from "../logger.js";
import { debug, nginx as logger } from "../logger.js";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
@@ -68,7 +68,7 @@ const internalNginx = {
return true;
});
logger.debug("Nginx test failed:", valid_lines.join("\n"));
debug(logger, "Nginx test failed:", valid_lines.join("\n"));
// config is bad, update meta and delete config
combined_meta = _.assign({}, host.meta, {
@@ -102,7 +102,7 @@ const internalNginx = {
* @returns {Promise}
*/
test: () => {
logger.debug("Testing Nginx configuration");
debug(logger, "Testing Nginx configuration");
return utils.execFile("/usr/sbin/nginx", ["-t", "-g", "error_log off;"]);
},
@@ -190,7 +190,7 @@ const internalNginx = {
const host = JSON.parse(JSON.stringify(host_row));
const nice_host_type = internalNginx.getFileFriendlyHostType(host_type);
logger.debug(`Generating ${nice_host_type} Config:`, JSON.stringify(host, null, 2));
debug(logger, `Generating ${nice_host_type} Config:`, JSON.stringify(host, null, 2));
const renderEngine = utils.getRenderEngine();
@@ -241,7 +241,7 @@ const internalNginx = {
.parseAndRender(template, host)
.then((config_text) => {
fs.writeFileSync(filename, config_text, { encoding: "utf8" });
logger.debug("Wrote config:", filename, config_text);
debug(logger, "Wrote config:", filename, config_text);
// Restore locations array
host.locations = origLocations;
@@ -249,7 +249,7 @@ const internalNginx = {
resolve(true);
})
.catch((err) => {
logger.debug(`Could not write ${filename}:`, err.message);
debug(logger, `Could not write ${filename}:`, err.message);
reject(new errs.ConfigurationError(err.message));
});
});
@@ -265,7 +265,7 @@ const internalNginx = {
* @returns {Promise}
*/
generateLetsEncryptRequestConfig: (certificate) => {
logger.debug("Generating LetsEncrypt Request Config:", certificate);
debug(logger, "Generating LetsEncrypt Request Config:", certificate);
const renderEngine = utils.getRenderEngine();
return new Promise((resolve, reject) => {
@@ -285,11 +285,11 @@ const internalNginx = {
.parseAndRender(template, certificate)
.then((config_text) => {
fs.writeFileSync(filename, config_text, { encoding: "utf8" });
logger.debug("Wrote config:", filename, config_text);
debug(logger, "Wrote config:", filename, config_text);
resolve(true);
})
.catch((err) => {
logger.debug(`Could not write ${filename}:`, err.message);
debug(logger, `Could not write ${filename}:`, err.message);
reject(new errs.ConfigurationError(err.message));
});
});
@@ -305,10 +305,10 @@ const internalNginx = {
return;
}
try {
logger.debug(`Deleting file: ${filename}`);
debug(logger, `Deleting file: ${filename}`);
fs.unlinkSync(filename);
} catch (err) {
logger.debug("Could not delete file:", JSON.stringify(err, null, 2));
debug(logger, "Could not delete file:", JSON.stringify(err, null, 2));
}
},

View File

@@ -25,15 +25,26 @@ const configure = () => {
if (configData?.database) {
logger.info(`Using configuration from file: ${filename}`);
// Migrate those who have "mysql" engine to "mysql2"
if (configData.database.engine === "mysql") {
configData.database.engine = mysqlEngine;
}
instance = configData;
instance.keys = getKeys();
return;
}
}
const envMysqlHost = process.env.DB_MYSQL_HOST || null;
const envMysqlUser = process.env.DB_MYSQL_USER || null;
const envMysqlName = process.env.DB_MYSQL_NAME || null;
const toBool = (v) => /^(1|true|yes|on)$/i.test((v || '').trim());
const envMysqlHost = process.env.DB_MYSQL_HOST || null;
const envMysqlUser = process.env.DB_MYSQL_USER || null;
const envMysqlName = process.env.DB_MYSQL_NAME || null;
const envMysqlSSL = toBool(process.env.DB_MYSQL_SSL);
const envMysqlSSLRejectUnauthorized = process.env.DB_MYSQL_SSL_REJECT_UNAUTHORIZED === undefined ? true : toBool(process.env.DB_MYSQL_SSL_REJECT_UNAUTHORIZED);
const envMysqlSSLVerifyIdentity = process.env.DB_MYSQL_SSL_VERIFY_IDENTITY === undefined ? true : toBool(process.env.DB_MYSQL_SSL_VERIFY_IDENTITY);
if (envMysqlHost && envMysqlUser && envMysqlName) {
// we have enough mysql creds to go with mysql
logger.info("Using MySQL configuration");
@@ -44,7 +55,8 @@ const configure = () => {
port: process.env.DB_MYSQL_PORT || 3306,
user: envMysqlUser,
password: process.env.DB_MYSQL_PASSWORD,
name: envMysqlName,
name: envMysqlName,
ssl: envMysqlSSL ? { rejectUnauthorized: envMysqlSSLRejectUnauthorized, verifyIdentity: envMysqlSSLVerifyIdentity } : false,
},
keys: getKeys(),
};
@@ -90,7 +102,9 @@ const configure = () => {
const getKeys = () => {
// Get keys from file
logger.debug("Cheecking for keys file:", keysFile);
if (isDebugMode()) {
logger.debug("Checking for keys file:", keysFile);
}
if (!fs.existsSync(keysFile)) {
generateKeys();
} else if (process.env.DEBUG) {

View File

@@ -3,14 +3,14 @@ import { dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { Liquid } from "liquidjs";
import _ from "lodash";
import { global as logger } from "../logger.js";
import { debug, global as logger } from "../logger.js";
import errs from "./error.js";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const exec = async (cmd, options = {}) => {
logger.debug("CMD:", cmd);
debug(logger, "CMD:", cmd);
const { stdout, stderr } = await new Promise((resolve, reject) => {
const child = nodeExec(cmd, options, (isError, stdout, stderr) => {
if (isError) {
@@ -34,7 +34,7 @@ const exec = async (cmd, options = {}) => {
* @returns {Promise}
*/
const execFile = (cmd, args, options) => {
logger.debug(`CMD: ${cmd} ${args ? args.join(" ") : ""}`);
debug(logger, `CMD: ${cmd} ${args ? args.join(" ") : ""}`);
const opts = options || {};
return new Promise((resolve, reject) => {

View File

@@ -1,4 +1,5 @@
import signale from "signale";
import { isDebugMode } from "./lib/config.js";
const opts = {
logLevel: "info",
@@ -15,4 +16,10 @@ const importer = new signale.Signale({ scope: "Importer ", ...opts });
const setup = new signale.Signale({ scope: "Setup ", ...opts });
const ipRanges = new signale.Signale({ scope: "IP Ranges", ...opts });
export { global, migrate, express, access, nginx, ssl, certbot, importer, setup, ipRanges };
const debug = (logger, ...args) => {
if (isDebugMode()) {
logger.debug(...args);
}
};
export { debug, global, migrate, express, access, nginx, ssl, certbot, importer, setup, ipRanges };

View File

@@ -2,9 +2,9 @@ import db from "./db.js";
import { migrate as logger } from "./logger.js";
const migrateUp = async () => {
const version = await db.migrate.currentVersion();
const version = await db().migrate.currentVersion();
logger.info("Current database version:", version);
return await db.migrate.latest({
return await db().migrate.latest({
tableName: "migrations",
directory: "migrations",
});

View File

@@ -10,7 +10,7 @@ import now from "./now_helper.js";
import ProxyHostModel from "./proxy_host.js";
import User from "./user.js";
Model.knex(db);
Model.knex(db());
const boolFields = ["is_deleted", "satisfy_any", "pass_auth"];

View File

@@ -6,7 +6,7 @@ import db from "../db.js";
import accessListModel from "./access_list.js";
import now from "./now_helper.js";
Model.knex(db);
Model.knex(db());
class AccessListAuth extends Model {
$beforeInsert() {

View File

@@ -6,7 +6,7 @@ import db from "../db.js";
import accessListModel from "./access_list.js";
import now from "./now_helper.js";
Model.knex(db);
Model.knex(db());
class AccessListClient extends Model {
$beforeInsert() {

View File

@@ -6,7 +6,7 @@ import db from "../db.js";
import now from "./now_helper.js";
import User from "./user.js";
Model.knex(db);
Model.knex(db());
class AuditLog extends Model {
$beforeInsert() {

View File

@@ -8,7 +8,7 @@ import { convertBoolFieldsToInt, convertIntFieldsToBool } from "../lib/helpers.j
import now from "./now_helper.js";
import User from "./user.js";
Model.knex(db);
Model.knex(db());
const boolFields = ["is_deleted"];

View File

@@ -11,7 +11,7 @@ import redirectionHostModel from "./redirection_host.js";
import streamModel from "./stream.js";
import userModel from "./user.js";
Model.knex(db);
Model.knex(db());
const boolFields = ["is_deleted"];

View File

@@ -8,7 +8,7 @@ import Certificate from "./certificate.js";
import now from "./now_helper.js";
import User from "./user.js";
Model.knex(db);
Model.knex(db());
const boolFields = ["is_deleted", "ssl_forced", "http2_support", "enabled", "hsts_enabled", "hsts_subdomains"];

View File

@@ -2,7 +2,7 @@ import { Model } from "objection";
import db from "../db.js";
import { isSqlite } from "../lib/config.js";
Model.knex(db);
Model.knex(db());
export default () => {
if (isSqlite()) {

View File

@@ -9,7 +9,7 @@ import Certificate from "./certificate.js";
import now from "./now_helper.js";
import User from "./user.js";
Model.knex(db);
Model.knex(db());
const boolFields = [
"is_deleted",

View File

@@ -8,7 +8,7 @@ import Certificate from "./certificate.js";
import now from "./now_helper.js";
import User from "./user.js";
Model.knex(db);
Model.knex(db());
const boolFields = [
"is_deleted",

View File

@@ -4,7 +4,7 @@
import { Model } from "objection";
import db from "../db.js";
Model.knex(db);
Model.knex(db());
class Setting extends Model {
$beforeInsert () {

View File

@@ -5,7 +5,7 @@ import Certificate from "./certificate.js";
import now from "./now_helper.js";
import User from "./user.js";
Model.knex(db);
Model.knex(db());
const boolFields = ["is_deleted", "enabled", "tcp_forwarding", "udp_forwarding"];

View File

@@ -7,7 +7,7 @@ import { convertBoolFieldsToInt, convertIntFieldsToBool } from "../lib/helpers.j
import now from "./now_helper.js";
import UserPermission from "./user_permission.js";
Model.knex(db);
Model.knex(db());
const boolFields = ["is_deleted", "is_disabled"];

View File

@@ -5,7 +5,7 @@ import { Model } from "objection";
import db from "../db.js";
import now from "./now_helper.js";
Model.knex(db);
Model.knex(db());
class UserPermission extends Model {
$beforeInsert () {

View File

@@ -32,6 +32,7 @@
"objection": "3.0.1",
"path": "^0.12.7",
"pg": "^8.16.3",
"proxy-agent": "^6.5.0",
"signale": "1.4.0",
"sqlite3": "^5.1.7",
"temp-write": "^4.0.0"

View File

@@ -2,7 +2,7 @@ import express from "express";
import internalAuditLog from "../internal/audit-log.js";
import jwtdecode from "../lib/express/jwt-decode.js";
import validator from "../lib/validator/index.js";
import { express as logger } from "../logger.js";
import { debug, express as logger } from "../logger.js";
const router = express.Router({
caseSensitive: true,
@@ -47,7 +47,7 @@ router
const rows = await internalAuditLog.getAll(res.locals.access, data.expand, data.query);
res.status(200).send(rows);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});
@@ -99,7 +99,7 @@ router
});
res.status(200).send(item);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});

View File

@@ -3,7 +3,7 @@ import internalAccessList from "../../internal/access-list.js";
import jwtdecode from "../../lib/express/jwt-decode.js";
import apiValidator from "../../lib/validator/api.js";
import validator from "../../lib/validator/index.js";
import { express as logger } from "../../logger.js";
import { debug, express as logger } from "../../logger.js";
import { getValidationSchema } from "../../schema/index.js";
const router = express.Router({
@@ -49,7 +49,7 @@ router
const rows = await internalAccessList.getAll(res.locals.access, data.expand, data.query);
res.status(200).send(rows);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
})
@@ -65,7 +65,7 @@ router
const result = await internalAccessList.create(res.locals.access, payload);
res.status(201).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});
@@ -113,7 +113,7 @@ router
});
res.status(200).send(row);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
})
@@ -130,7 +130,7 @@ router
const result = await internalAccessList.update(res.locals.access, payload);
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
})
@@ -147,7 +147,7 @@ router
});
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});

View File

@@ -5,7 +5,7 @@ import errs from "../../lib/error.js";
import jwtdecode from "../../lib/express/jwt-decode.js";
import apiValidator from "../../lib/validator/api.js";
import validator from "../../lib/validator/index.js";
import { express as logger } from "../../logger.js";
import { debug, express as logger } from "../../logger.js";
import { getValidationSchema } from "../../schema/index.js";
const router = express.Router({
@@ -58,7 +58,7 @@ router
);
res.status(200).send(rows);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
})
@@ -81,7 +81,7 @@ router
);
res.status(201).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});
@@ -115,7 +115,7 @@ router
clean.sort((a, b) => a.name.localeCompare(b.name));
res.status(200).send(clean);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});
@@ -151,7 +151,7 @@ router
);
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});
@@ -185,7 +185,7 @@ router
});
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});
@@ -236,7 +236,7 @@ router
});
res.status(200).send(row);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
})
@@ -253,7 +253,7 @@ router
});
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});
@@ -288,7 +288,7 @@ router
});
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});
@@ -318,7 +318,7 @@ router
});
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});
@@ -347,7 +347,7 @@ router
});
res.status(200).download(result.fileName);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});

View File

@@ -3,7 +3,7 @@ import internalDeadHost from "../../internal/dead-host.js";
import jwtdecode from "../../lib/express/jwt-decode.js";
import apiValidator from "../../lib/validator/api.js";
import validator from "../../lib/validator/index.js";
import { express as logger } from "../../logger.js";
import { debug, express as logger } from "../../logger.js";
import { getValidationSchema } from "../../schema/index.js";
const router = express.Router({
@@ -49,7 +49,7 @@ router
const rows = await internalDeadHost.getAll(res.locals.access, data.expand, data.query);
res.status(200).send(rows);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
})
@@ -65,7 +65,7 @@ router
const result = await internalDeadHost.create(res.locals.access, payload);
res.status(201).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});
@@ -113,7 +113,7 @@ router
});
res.status(200).send(row);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
})
@@ -130,7 +130,7 @@ router
const result = await internalDeadHost.update(res.locals.access, payload);
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
})
@@ -147,7 +147,7 @@ router
});
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});
@@ -174,7 +174,7 @@ router
});
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});
@@ -199,7 +199,7 @@ router
const result = internalDeadHost.disable(res.locals.access, { id: Number.parseInt(req.params.host_id, 10) });
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});

View File

@@ -3,7 +3,7 @@ import internalProxyHost from "../../internal/proxy-host.js";
import jwtdecode from "../../lib/express/jwt-decode.js";
import apiValidator from "../../lib/validator/api.js";
import validator from "../../lib/validator/index.js";
import { express as logger } from "../../logger.js";
import { debug, express as logger } from "../../logger.js";
import { getValidationSchema } from "../../schema/index.js";
const router = express.Router({
@@ -49,7 +49,7 @@ router
const rows = await internalProxyHost.getAll(res.locals.access, data.expand, data.query);
res.status(200).send(rows);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
})
@@ -65,7 +65,7 @@ router
const result = await internalProxyHost.create(res.locals.access, payload);
res.status(201).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err} ${JSON.stringify(err.debug, null, 2)}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err} ${JSON.stringify(err.debug, null, 2)}`);
next(err);
}
});
@@ -113,7 +113,7 @@ router
});
res.status(200).send(row);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
})
@@ -130,7 +130,7 @@ router
const result = await internalProxyHost.update(res.locals.access, payload);
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
})
@@ -147,7 +147,7 @@ router
});
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});
@@ -174,7 +174,7 @@ router
});
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});
@@ -201,7 +201,7 @@ router
});
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});

View File

@@ -3,7 +3,7 @@ import internalRedirectionHost from "../../internal/redirection-host.js";
import jwtdecode from "../../lib/express/jwt-decode.js";
import apiValidator from "../../lib/validator/api.js";
import validator from "../../lib/validator/index.js";
import { express as logger } from "../../logger.js";
import { debug, express as logger } from "../../logger.js";
import { getValidationSchema } from "../../schema/index.js";
const router = express.Router({
@@ -49,7 +49,7 @@ router
const rows = await internalRedirectionHost.getAll(res.locals.access, data.expand, data.query);
res.status(200).send(rows);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
})
@@ -65,7 +65,7 @@ router
const result = await internalRedirectionHost.create(res.locals.access, payload);
res.status(201).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});
@@ -113,7 +113,7 @@ router
});
res.status(200).send(row);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
})
@@ -133,7 +133,7 @@ router
const result = await internalRedirectionHost.update(res.locals.access, payload);
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
})
@@ -150,7 +150,7 @@ router
});
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});
@@ -177,7 +177,7 @@ router
});
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});
@@ -204,7 +204,7 @@ router
});
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});

View File

@@ -3,7 +3,7 @@ import internalStream from "../../internal/stream.js";
import jwtdecode from "../../lib/express/jwt-decode.js";
import apiValidator from "../../lib/validator/api.js";
import validator from "../../lib/validator/index.js";
import { express as logger } from "../../logger.js";
import { debug, express as logger } from "../../logger.js";
import { getValidationSchema } from "../../schema/index.js";
const router = express.Router({
@@ -49,7 +49,7 @@ router
const rows = await internalStream.getAll(res.locals.access, data.expand, data.query);
res.status(200).send(rows);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
})
@@ -65,7 +65,7 @@ router
const result = await internalStream.create(res.locals.access, payload);
res.status(201).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});
@@ -113,7 +113,7 @@ router
});
res.status(200).send(row);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
})
@@ -130,7 +130,7 @@ router
const result = await internalStream.update(res.locals.access, payload);
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
})
@@ -147,7 +147,7 @@ router
});
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});
@@ -174,7 +174,7 @@ router
});
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});
@@ -201,7 +201,7 @@ router
});
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});

View File

@@ -1,7 +1,7 @@
import express from "express";
import internalReport from "../internal/report.js";
import jwtdecode from "../lib/express/jwt-decode.js";
import { express as logger } from "../logger.js";
import { debug, express as logger } from "../logger.js";
const router = express.Router({
caseSensitive: true,
@@ -24,7 +24,7 @@ router
const data = await internalReport.getHostsReport(res.locals.access);
res.status(200).send(data);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});

View File

@@ -1,5 +1,5 @@
import express from "express";
import { express as logger } from "../logger.js";
import { debug, express as logger } from "../logger.js";
import PACKAGE from "../package.json" with { type: "json" };
import { getCompiledSchema } from "../schema/index.js";
@@ -36,7 +36,7 @@ router
swaggerJSON.servers[0].url = `${origin}/api`;
res.status(200).send(swaggerJSON);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});

View File

@@ -3,7 +3,7 @@ import internalSetting from "../internal/setting.js";
import jwtdecode from "../lib/express/jwt-decode.js";
import apiValidator from "../lib/validator/api.js";
import validator from "../lib/validator/index.js";
import { express as logger } from "../logger.js";
import { debug, express as logger } from "../logger.js";
import { getValidationSchema } from "../schema/index.js";
const router = express.Router({
@@ -32,7 +32,7 @@ router
const rows = await internalSetting.getAll(res.locals.access);
res.status(200).send(rows);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});
@@ -76,7 +76,7 @@ router
});
res.status(200).send(row);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
})
@@ -93,7 +93,7 @@ router
const result = await internalSetting.update(res.locals.access, payload);
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});

View File

@@ -2,7 +2,7 @@ import express from "express";
import internalToken from "../internal/token.js";
import jwtdecode from "../lib/express/jwt-decode.js";
import apiValidator from "../lib/validator/api.js";
import { express as logger } from "../logger.js";
import { debug, express as logger } from "../logger.js";
import { getValidationSchema } from "../schema/index.js";
const router = express.Router({
@@ -32,7 +32,7 @@ router
});
res.status(200).send(data);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
})
@@ -48,7 +48,7 @@ router
const result = await internalToken.getTokenFromEmail(data);
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});

View File

@@ -7,7 +7,7 @@ import jwtdecode from "../lib/express/jwt-decode.js";
import userIdFromMe from "../lib/express/user-id-from-me.js";
import apiValidator from "../lib/validator/api.js";
import validator from "../lib/validator/index.js";
import { express as logger } from "../logger.js";
import { debug, express as logger } from "../logger.js";
import { getValidationSchema } from "../schema/index.js";
import { isSetup } from "../setup.js";
@@ -61,7 +61,7 @@ router
);
res.status(200).send(users);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
})
@@ -101,7 +101,7 @@ router
const user = await internalUser.create(res.locals.access, payload);
res.status(201).send(user);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
})
@@ -124,7 +124,7 @@ router
await internalUser.deleteAll();
res.status(200).send(true);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
return;
@@ -185,7 +185,7 @@ router
});
res.status(200).send(user);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
})
@@ -205,7 +205,7 @@ router
const result = await internalUser.update(res.locals.access, payload);
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
})
@@ -222,7 +222,7 @@ router
});
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});
@@ -255,7 +255,7 @@ router
const result = await internalUser.setPassword(res.locals.access, payload);
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});
@@ -291,7 +291,7 @@ router
);
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});
@@ -320,7 +320,7 @@ router
});
res.status(200).send(result);
} catch (err) {
logger.debug(`${req.method.toUpperCase()} ${req.path}: ${err}`);
debug(logger, `${req.method.toUpperCase()} ${req.path}: ${err}`);
next(err);
}
});

View File

@@ -143,6 +143,11 @@
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==
"@tootallnate/quickjs-emscripten@^0.23.0":
version "0.23.0"
resolved "https://registry.yarnpkg.com/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz#db4ecfd499a9765ab24002c3b696d02e6d32a12c"
integrity sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==
"@types/json-schema@^7.0.15":
version "7.0.15"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
@@ -168,6 +173,11 @@ agent-base@6, agent-base@^6.0.2:
dependencies:
debug "4"
agent-base@^7.1.0, agent-base@^7.1.2:
version "7.1.4"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.4.tgz#e3cd76d4c548ee895d3c3fd8dc1f6c5b9032e7a8"
integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==
agentkeepalive@^4.1.3:
version "4.6.0"
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.6.0.tgz#35f73e94b3f40bf65f105219c623ad19c136ea6a"
@@ -308,6 +318,13 @@ asn1@^0.2.4:
dependencies:
safer-buffer "~2.1.0"
ast-types@^0.13.4:
version "0.13.4"
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782"
integrity sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==
dependencies:
tslib "^2.0.1"
async@^3.2.4:
version "3.2.6"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce"
@@ -328,6 +345,11 @@ base64-js@^1.3.1:
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
basic-ftp@^5.0.2:
version "5.0.5"
resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.0.5.tgz#14a474f5fffecca1f4f406f1c26b18f800225ac0"
integrity sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==
batchflow@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/batchflow/-/batchflow-0.4.0.tgz#7d419df79b6b7587b06f9ea34f96ccef6f74e5b5"
@@ -667,6 +689,11 @@ crc32-stream@^4.0.2:
crc-32 "^1.2.0"
readable-stream "^3.4.0"
data-uri-to-buffer@^6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz#8a58bb67384b261a38ef18bea1810cb01badd28b"
integrity sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==
db-errors@^0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/db-errors/-/db-errors-0.2.3.tgz#a6a38952e00b20e790f2695a6446b3c65497ffa2"
@@ -700,6 +727,13 @@ debug@^3.2.7:
dependencies:
ms "^2.1.1"
debug@^4.3.4:
version "4.4.3"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a"
integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
dependencies:
ms "^2.1.3"
decamelize@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
@@ -717,6 +751,15 @@ deep-extend@^0.6.0:
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
degenerator@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-5.0.1.tgz#9403bf297c6dad9a1ece409b37db27954f91f2f5"
integrity sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==
dependencies:
ast-types "^0.13.4"
escodegen "^2.1.0"
esprima "^4.0.1"
delegates@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
@@ -846,11 +889,37 @@ escape-string-regexp@^1.0.5:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
escodegen@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17"
integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==
dependencies:
esprima "^4.0.1"
estraverse "^5.2.0"
esutils "^2.0.2"
optionalDependencies:
source-map "~0.6.1"
esm@^3.2.25:
version "3.2.25"
resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10"
integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==
esprima@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
estraverse@^5.2.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
esutils@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
etag@~1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
@@ -1069,6 +1138,15 @@ get-proto@^1.0.1:
dunder-proto "^1.0.1"
es-object-atoms "^1.0.0"
get-uri@^6.0.1:
version "6.0.5"
resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-6.0.5.tgz#714892aa4a871db671abc5395e5e9447bc306a16"
integrity sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==
dependencies:
basic-ftp "^5.0.2"
data-uri-to-buffer "^6.0.2"
debug "^4.3.4"
getopts@2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.3.0.tgz#71e5593284807e03e2427449d4f6712a268666f4"
@@ -1170,6 +1248,14 @@ http-proxy-agent@^4.0.1:
agent-base "6"
debug "4"
http-proxy-agent@^7.0.0, http-proxy-agent@^7.0.1:
version "7.0.2"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e"
integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==
dependencies:
agent-base "^7.1.0"
debug "^4.3.4"
https-proxy-agent@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
@@ -1178,6 +1264,14 @@ https-proxy-agent@^5.0.0:
agent-base "6"
debug "4"
https-proxy-agent@^7.0.6:
version "7.0.6"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9"
integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==
dependencies:
agent-base "^7.1.2"
debug "4"
humanize-ms@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed"
@@ -1747,6 +1841,11 @@ negotiator@^0.6.2, negotiator@~0.6.4:
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7"
integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==
netmask@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7"
integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==
node-abi@^3.3.0:
version "3.78.0"
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.78.0.tgz#fd0ecbd0aa89857b98da06bd3909194abb0821ba"
@@ -1924,6 +2023,28 @@ p-try@^2.0.0:
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
pac-proxy-agent@^7.1.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz#9cfaf33ff25da36f6147a20844230ec92c06e5df"
integrity sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==
dependencies:
"@tootallnate/quickjs-emscripten" "^0.23.0"
agent-base "^7.1.2"
debug "^4.3.4"
get-uri "^6.0.1"
http-proxy-agent "^7.0.0"
https-proxy-agent "^7.0.6"
pac-resolver "^7.0.1"
socks-proxy-agent "^8.0.5"
pac-resolver@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-7.0.1.tgz#54675558ea368b64d210fd9c92a640b5f3b8abb6"
integrity sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==
dependencies:
degenerator "^5.0.0"
netmask "^2.0.2"
parse-json@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
@@ -2120,6 +2241,25 @@ proxy-addr@~2.0.7:
forwarded "0.2.0"
ipaddr.js "1.9.1"
proxy-agent@^6.5.0:
version "6.5.0"
resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.5.0.tgz#9e49acba8e4ee234aacb539f89ed9c23d02f232d"
integrity sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==
dependencies:
agent-base "^7.1.2"
debug "^4.3.4"
http-proxy-agent "^7.0.1"
https-proxy-agent "^7.0.6"
lru-cache "^7.14.1"
pac-proxy-agent "^7.1.0"
proxy-from-env "^1.1.0"
socks-proxy-agent "^8.0.5"
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
pstree.remy@^1.1.8:
version "1.1.8"
resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a"
@@ -2422,7 +2562,16 @@ socks-proxy-agent@^6.0.0:
debug "^4.3.3"
socks "^2.6.2"
socks@^2.6.2:
socks-proxy-agent@^8.0.5:
version "8.0.5"
resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz#b9cdb4e7e998509d7659d689ce7697ac21645bee"
integrity sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==
dependencies:
agent-base "^7.1.2"
debug "^4.3.4"
socks "^2.8.3"
socks@^2.6.2, socks@^2.8.3:
version "2.8.7"
resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.7.tgz#e2fb1d9a603add75050a2067db8c381a0b5669ea"
integrity sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==
@@ -2430,6 +2579,11 @@ socks@^2.6.2:
ip-address "^10.0.1"
smart-buffer "^4.2.0"
source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
split2@^4.1.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4"
@@ -2609,6 +2763,11 @@ tr46@~0.0.3:
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
tslib@^2.0.1:
version "2.8.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
tunnel-agent@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"

View File

@@ -4,7 +4,6 @@
# This file assumes that the frontend has been built using ./scripts/frontend-build
FROM nginxproxymanager/testca AS testca
FROM letsencrypt/pebble AS pebbleca
FROM nginxproxymanager/nginx-full:certbot-node
ARG TARGETPLATFORM
@@ -46,7 +45,6 @@ RUN yarn install \
# add late to limit cache-busting by modifications
COPY docker/rootfs /
COPY --from=pebbleca /test/certs/pebble.minica.pem /etc/ssl/certs/pebble.minica.pem
COPY --from=testca /home/step/certs/root_ca.crt /etc/ssl/certs/NginxProxyManager.crt
# Remove frontend service not required for prod, dev nginx config as well

View File

@@ -1,5 +1,4 @@
FROM nginxproxymanager/testca AS testca
FROM letsencrypt/pebble AS pebbleca
FROM nginxproxymanager/nginx-full:certbot-node
LABEL maintainer="Jamie Curnow <jc@jc21.com>"
@@ -33,7 +32,6 @@ RUN rm -f /etc/nginx/conf.d/production.conf \
&& chmod 644 -R /root/.cache
# Certs for testing purposes
COPY --from=pebbleca /test/certs/pebble.minica.pem /etc/ssl/certs/pebble.minica.pem
COPY --from=testca /home/step/certs/root_ca.crt /etc/ssl/certs/NginxProxyManager.crt
EXPOSE 80 81 443

View File

@@ -1,12 +0,0 @@
{
"pebble": {
"listenAddress": "0.0.0.0:443",
"managementListenAddress": "0.0.0.0:15000",
"certificate": "test/certs/localhost/cert.pem",
"privateKey": "test/certs/localhost/key.pem",
"httpPort": 80,
"tlsPort": 443,
"ocspResponderURL": "",
"externalAccountBindingRequired": false
}
}

View File

@@ -24,4 +24,5 @@
.inline-img img {
display: inline;
margin-right: 8px;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

View File

@@ -4,17 +4,44 @@ outline: deep
# Screenshots
### Light Mode
::: raw
<div class="inline-img">
<a href="/screenshots/login.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/login.png" alt="Login" title="Login" width="200"/></a>
<a href="/screenshots/dashboard.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/dashboard.png" alt="Dashboard" title="Dashboard" width="200"/></a>
<a href="/screenshots/proxy-hosts.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/proxy-hosts.png" alt="Proxy Hosts" title="Proxy Hosts" width="200"/></a>
<a href="/screenshots/proxy-hosts-add.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/proxy-hosts-add.png" alt="Add Proxy Host" title="Add Proxy Host" width="200"/></a>
<a href="/screenshots/redirection-hosts.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/redirection-hosts.png" alt="Redirection Hosts" title="Redirection Hosts" width="200"/></a>
<a href="/screenshots/dead-hosts.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/dead-hosts.png" alt="404 Hosts" title="404 Hosts" width="200"/></a>
<a href="/screenshots/permissions.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/permissions.png" alt="User Permissions" title="User Permissions" width="200"/></a>
<a href="/screenshots/certificates.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/certificates.png" alt="Certificates" title="Certificates" width="200"/></a>
<a href="/screenshots/audit-log.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/audit-log.png" alt="Audit Log" title="Audit Log" width="200"/></a>
<a href="/screenshots/custom-settings.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/custom-settings.png" alt="Custom Settings" title="Custom Settings" width="200"/></a>
<a href="/screenshots/light/01_first-user.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/light/01_first-user.png" alt="Setup" title="Setup" width="200"/></a>
<a href="/screenshots/light/02_login.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/light/02_login.png" alt="Login" title="Login" width="200"/></a>
<a href="/screenshots/light/03_dashboard.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/light/03_dashboard.png" alt="Dashboard" title="Dashboard" width="200"/></a>
<a href="/screenshots/light/04_proxy-hosts.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/light/04_proxy-hosts.png" alt="Proxy Hosts" title="Proxy Hosts" width="200"/></a>
<a href="/screenshots/light/05_redirection_hosts.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/light/05_redirection_hosts.png" alt="Redirection Hosts" title="Redirection Hosts" width="200"/></a>
<a href="/screenshots/light/06_streams.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/light/06_streams.png" alt="Streams" title="Streams" width="200"/></a>
<a href="/screenshots/light/07_404_hosts.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/light/07_404_hosts.png" alt="404 Hosts" title="404 Hosts" width="200"/></a>
<a href="/screenshots/light/08_access-lists.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/light/08_access-lists.png" alt="Access Lists" title="Access Lists" width="200"/></a>
<a href="/screenshots/light/09_certificates.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/light/09_certificates.png" alt="Certificates" title="Certificates" width="200"/></a>
<a href="/screenshots/light/10_users.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/light/10_users.png" alt="Users" title="Users" width="200"/></a>
<a href="/screenshots/light/11_audit-logs.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/light/11_audit-logs.png" alt="Audit Logs" title="Audit Logs" width="200"/></a>
<a href="/screenshots/light/12_settings.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/light/12_settings.png" alt="Settings" title="Settings" width="200"/></a>
<a href="/screenshots/light/13_add-proxy_host.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/light/13_add-proxy_host.png" alt="Add Proxy Host" title="Add Proxy Host" width="200"/></a>
<a href="/screenshots/light/14_add_proxy_host_dns.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/light/14_add_proxy_host_dns.png" alt="Add Proxy Host with DNS" title="Add Proxy Host with DNS" width="200"/></a>
</div>
:::
### Dark Mode
::: raw
<div class="inline-img">
<a href="/screenshots/dark/01_first-user.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/dark/01_first-user.png" alt="Setup" title="Setup" width="200"/></a>
<a href="/screenshots/dark/02_login.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/dark/02_login.png" alt="Login" title="Login" width="200"/></a>
<a href="/screenshots/dark/03_dashboard.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/dark/03_dashboard.png" alt="Dashboard" title="Dashboard" width="200"/></a>
<a href="/screenshots/dark/04_proxy-hosts.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/dark/04_proxy-hosts.png" alt="Proxy Hosts" title="Proxy Hosts" width="200"/></a>
<a href="/screenshots/dark/05_redirection_hosts.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/dark/05_redirection_hosts.png" alt="Redirection Hosts" title="Redirection Hosts" width="200"/></a>
<a href="/screenshots/dark/06_streams.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/dark/06_streams.png" alt="Streams" title="Streams" width="200"/></a>
<a href="/screenshots/dark/07_404_hosts.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/dark/07_404_hosts.png" alt="404 Hosts" title="404 Hosts" width="200"/></a>
<a href="/screenshots/dark/08_access-lists.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/dark/08_access-lists.png" alt="Access Lists" title="Access Lists" width="200"/></a>
<a href="/screenshots/dark/09_certificates.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/dark/09_certificates.png" alt="Certificates" title="Certificates" width="200"/></a>
<a href="/screenshots/dark/10_users.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/dark/10_users.png" alt="Users" title="Users" width="200"/></a>
<a href="/screenshots/dark/11_audit-logs.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/dark/11_audit-logs.png" alt="Audit Logs" title="Audit Logs" width="200"/></a>
<a href="/screenshots/dark/12_settings.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/dark/12_settings.png" alt="Settings" title="Settings" width="200"/></a>
<a href="/screenshots/dark/13_add-proxy_host.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/dark/13_add-proxy_host.png" alt="Add Proxy Host" title="Add Proxy Host" width="200"/></a>
<a href="/screenshots/dark/14_add_proxy_host_dns.png" target="_blank"><img class="no-medium-zoom zooming" src="/screenshots/dark/14_add_proxy_host_dns.png" alt="Add Proxy Host with DNS" title="Add Proxy Host with DNS" width="200"/></a>
</div>
:::

View File

@@ -75,6 +75,10 @@ services:
DB_MYSQL_USER: "npm"
DB_MYSQL_PASSWORD: "npm"
DB_MYSQL_NAME: "npm"
# Optional SSL (see section below)
# DB_MYSQL_SSL: 'true'
# DB_MYSQL_SSL_REJECT_UNAUTHORIZED: 'true'
# DB_MYSQL_SSL_VERIFY_IDENTITY: 'true'
# Uncomment this if IPv6 is not enabled on your host
# DISABLE_IPV6: 'true'
volumes:
@@ -102,6 +106,16 @@ Please note, that `DB_MYSQL_*` environment variables will take precedent over `D
:::
### Optional: MySQL / MariaDB SSL
You can enable TLS for the MySQL/MariaDB connection with these environment variables:
- DB_MYSQL_SSL: Enable SSL when set to true. If unset or false, SSL disabled (previous default behaviour).
- DB_MYSQL_SSL_REJECT_UNAUTHORIZED: (default: true) Validate the server certificate chain. Set to false to allow selfsigned/unknown CA.
- DB_MYSQL_SSL_VERIFY_IDENTITY: (default: true) Performs host name / identity verification.
Enabling SSL using a self-signed cert (not recommended for production).
## Using Postgres database
Similar to the MySQL server setup:
@@ -141,7 +155,7 @@ services:
POSTGRES_PASSWORD: 'npmpass'
POSTGRES_DB: 'npm'
volumes:
- ./postgres:/var/lib/postgresql/data
- ./postgresql:/var/lib/postgresql
```
::: warning

View File

@@ -1065,9 +1065,9 @@ vfile@^6.0.0:
vfile-message "^4.0.0"
vite@^5.4.8:
version "5.4.19"
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.19.tgz#20efd060410044b3ed555049418a5e7d1998f959"
integrity sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==
version "5.4.21"
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.21.tgz#84a4f7c5d860b071676d39ba513c0d598fdc7027"
integrity sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==
dependencies:
esbuild "^0.21.3"
postcss "^8.4.43"

View File

@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.3.1/schema.json",
"$schema": "https://biomejs.dev/schemas/2.3.2/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",

View File

@@ -18,7 +18,7 @@
"dependencies": {
"@tabler/core": "^1.4.0",
"@tabler/icons-react": "^3.35.0",
"@tanstack/react-query": "^5.90.5",
"@tanstack/react-query": "^5.90.6",
"@tanstack/react-table": "^8.21.3",
"@uiw/react-textarea-code-editor": "^3.1.1",
"classnames": "^2.5.1",
@@ -34,13 +34,13 @@
"react-dom": "^19.2.0",
"react-intl": "^7.1.14",
"react-markdown": "^10.1.0",
"react-router-dom": "^7.9.4",
"react-router-dom": "^7.9.5",
"react-select": "^5.10.2",
"react-toastify": "^11.0.5",
"rooks": "^9.3.0"
},
"devDependencies": {
"@biomejs/biome": "^2.3.1",
"@biomejs/biome": "^2.3.2",
"@formatjs/cli": "^6.7.4",
"@tanstack/react-query-devtools": "^5.90.2",
"@testing-library/dom": "^10.4.1",
@@ -52,15 +52,15 @@
"@types/react-dom": "^19.2.2",
"@types/react-table": "^7.7.20",
"@vitejs/plugin-react": "^5.1.0",
"happy-dom": "^20.0.8",
"happy-dom": "^20.0.10",
"postcss": "^8.5.6",
"postcss-simple-vars": "^7.0.1",
"sass": "^1.93.2",
"sass": "^1.93.3",
"tmp": "^0.2.5",
"typescript": "5.9.3",
"vite": "^7.1.12",
"vite-plugin-checker": "^0.11.0",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^4.0.3"
"vitest": "^4.0.6"
}
}

View File

@@ -5,5 +5,14 @@ interface Props {
certificate?: Certificate;
}
export function CertificateFormatter({ certificate }: Props) {
return <T id={certificate ? "lets-encrypt" : "http-only"} />;
let translation = "http-only";
if (certificate) {
translation = certificate.provider;
if (translation === "letsencrypt") {
translation = "lets-encrypt";
} else if (translation === "other") {
translation = "certificates.custom";
}
}
return <T id={translation} />;
}

View File

@@ -0,0 +1,77 @@
import { DateTimeFormat } from "src/locale";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
describe("DateFormatter", () => {
// Keep a reference to the real Intl to restore later
const RealIntl = global.Intl;
const desiredTimeZone = "Europe/London";
const desiredLocale = "en-GB";
beforeAll(() => {
// Ensure Node-based libs using TZ behave deterministically
try {
process.env.TZ = desiredTimeZone;
} catch {
// ignore if not available
}
// Mock Intl.DateTimeFormat so formatting is stable regardless of host
const MockedDateTimeFormat = class extends RealIntl.DateTimeFormat {
constructor(
_locales?: string | string[],
options?: Intl.DateTimeFormatOptions,
) {
super(desiredLocale, {
...options,
timeZone: desiredTimeZone,
});
}
} as unknown as typeof Intl.DateTimeFormat;
global.Intl = {
...RealIntl,
DateTimeFormat: MockedDateTimeFormat,
};
});
afterAll(() => {
// Restore original Intl after tests
global.Intl = RealIntl;
});
it("format date from iso date", () => {
const value = "2024-01-01T00:00:00.000Z";
const text = DateTimeFormat(value);
expect(text).toBe("Monday, 01/01/2024, 12:00:00 am");
});
it("format date from unix timestamp number", () => {
const value = 1762476112;
const text = DateTimeFormat(value);
expect(text).toBe("Friday, 07/11/2025, 12:41:52 am");
});
it("format date from unix timestamp string", () => {
const value = "1762476112";
const text = DateTimeFormat(value);
expect(text).toBe("Friday, 07/11/2025, 12:41:52 am");
});
it("catch bad format from string", () => {
const value = "this is not a good date";
const text = DateTimeFormat(value);
expect(text).toBe("this is not a good date");
});
it("catch bad format from number", () => {
const value = -100;
const text = DateTimeFormat(value);
expect(text).toBe("-100");
});
it("catch bad format from number as string", () => {
const value = "-100";
const text = DateTimeFormat(value);
expect(text).toBe("-100");
});
});

View File

@@ -1,5 +1,7 @@
const defaultImg = "/images/default-avatar.jpg";
interface Props {
url: string;
url?: string;
name?: string;
}
export function GravatarFormatter({ url, name }: Props) {
@@ -9,7 +11,7 @@ export function GravatarFormatter({ url, name }: Props) {
title={name}
className="avatar avatar-2 me-2"
style={{
backgroundImage: `url(${url})`,
backgroundImage: `url(${url || defaultImg})`,
}}
/>
</div>

View File

@@ -1,15 +1,36 @@
import { intlFormat, parseISO } from "date-fns";
import { fromUnixTime, intlFormat, parseISO } from "date-fns";
const DateTimeFormat = (isoDate: string) =>
intlFormat(parseISO(isoDate), {
weekday: "long",
year: "numeric",
month: "numeric",
day: "numeric",
hour: "numeric",
minute: "numeric",
second: "numeric",
hour12: true,
});
const isUnixTimestamp = (value: unknown): boolean => {
if (typeof value !== "number" && typeof value !== "string") return false;
const num = Number(value);
if (!Number.isFinite(num)) return false;
// Check plausible Unix timestamp range: from 1970 to ~year 3000
// Support both seconds and milliseconds
if (num > 0 && num < 10000000000) return true; // seconds (<= 10 digits)
if (num >= 10000000000 && num < 32503680000000) return true; // milliseconds (<= 13 digits)
return false;
};
const DateTimeFormat = (value: string | number): string => {
if (typeof value !== "number" && typeof value !== "string") return `${value}`;
try {
const d = isUnixTimestamp(value)
? fromUnixTime(+value)
: parseISO(`${value}`);
return intlFormat(d, {
weekday: "long",
year: "numeric",
month: "numeric",
day: "numeric",
hour: "numeric",
minute: "numeric",
second: "numeric",
hour12: true,
});
} catch {
return `${value}`;
}
};
export { DateTimeFormat };

View File

@@ -30,13 +30,20 @@ const getLocale = (short = false) => {
if (short) {
return loc.slice(0, 2);
}
// finally, fallback
if (!loc) {
loc = "en";
}
return loc;
};
const cache = createIntlCache();
const initialMessages = loadMessages(getLocale());
let intl = createIntl({ locale: getLocale(), messages: initialMessages }, cache);
let intl = createIntl(
{ locale: getLocale(), messages: initialMessages },
cache,
);
const changeLocale = (locale: string): void => {
const messages = loadMessages(locale);
@@ -76,4 +83,12 @@ const T = ({
);
};
export { localeOptions, getFlagCodeForLocale, getLocale, createIntl, changeLocale, intl, T };
export {
localeOptions,
getFlagCodeForLocale,
getLocale,
createIntl,
changeLocale,
intl,
T,
};

View File

@@ -32,6 +32,7 @@
"certificate.none.subtitle.for-http": "This host will not use HTTPS",
"certificate.none.title": "None",
"certificate.not-in-use": "Not Used",
"certificate.renew": "Renew Certificate",
"certificates": "Certificates",
"certificates.custom": "Custom Certificate",
"certificates.custom.warning": "Key files protected with a passphrase are not supported.",

View File

@@ -98,6 +98,9 @@
"certificate.not-in-use": {
"defaultMessage": "Not Used"
},
"certificate.renew": {
"defaultMessage": "Renew Certificate"
},
"certificates": {
"defaultMessage": "Certificates"
},

View File

@@ -47,7 +47,7 @@ const RenewCertificateModal = EasyModal.create(({ id, visible, remove }: Props)
<Modal show={visible} onHide={isSubmitting ? undefined : remove}>
<Modal.Header closeButton={!isSubmitting}>
<Modal.Title>
<T id="renew-certificate" />
<T id="certificate.renew" />
</Modal.Title>
</Modal.Header>
<Modal.Body>

View File

@@ -21,7 +21,10 @@ export default function Table({ data, isFetching, isFiltered, onEdit, onDelete,
() => [
columnHelper.accessor((row: any) => row.owner, {
id: "owner",
cell: (info: any) => <GravatarFormatter url={info.getValue().avatar} name={info.getValue().name} />,
cell: (info: any) => {
const value = info.getValue();
return <GravatarFormatter url={value ? value.avatar : ""} name={value ? value.name : ""} />;
},
meta: {
className: "w-1",
},

View File

@@ -18,16 +18,12 @@ export default function Table({ data, isFetching, onSelectItem }: Props) {
id: "user.avatar",
cell: (info: any) => {
const value = info.getValue();
return <GravatarFormatter url={value.avatar} name={value.name} />;
return <GravatarFormatter url={value ? value.avatar : ""} name={value ? value.name : ""} />;
},
meta: {
className: "w-1",
},
}),
columnHelper.accessor((row: AuditLog) => row.user?.name, {
id: "user.name",
header: intl.formatMessage({ id: "column.name" }),
}),
columnHelper.accessor((row: AuditLog) => row, {
id: "objectType",
header: intl.formatMessage({ id: "column.event" }),

Some files were not shown because too many files have changed in this diff Show More