Compare commits

...

56 Commits

Author SHA1 Message Date
jc21
f3633cb696 Merge pull request #4850 from TeenBiscuits/lang-spanish
Add Spanish language support and translations
2025-11-12 15:12:28 +10:00
Pablo Portas López
8773ce25d7 Merge branch 'develop' into lang-spanish 2025-11-12 02:14:09 +01:00
jc21
c3954e9845 Merge pull request #4824 from lastsamurai26/develop
Add German Support
2025-11-12 08:52:07 +10:00
Pablo Portas López
1600599410 Fix column.http-code translation 2025-11-11 13:53:45 +01:00
Pablo Portas López
74d381e7fa Add missing spanish translation 2025-11-11 13:50:23 +01:00
Frank
ba79bbc750 Update German translation for HTTP code
fix: Updated column http code
2025-11-11 08:56:32 +01:00
Frank
a7231777aa FIX: Update HTTP code message in German locale
fix: Updated column http code
2025-11-11 08:56:20 +01:00
Frank
3a6b221b0c Add HTTP Code translation to German locale
new: redirection-host.forward-http-code added
2025-11-11 07:13:13 +01:00
Frank
12b000abb9 Add HTTP Code message to German locale
new: redirection-host.forward-http-code added
2025-11-11 07:12:57 +01:00
jc21
39c9bbb167 Merge branch 'master' into develop
All checks were successful
Close stale issues and PRs / stale (push) Successful in 18s
2025-11-11 16:06:05 +10:00
jc21
30c2781a02 Merge pull request #4765 from mamasch19/develop
add MC-HOST24 DNS plugin
2025-11-11 16:05:32 +10:00
Jamie Curnow
53e78dcc17 Bump version 2025-11-11 16:01:06 +10:00
jc21
62092b2ddc Merge pull request #4859 from 7heMech/develop
Fix hamburger menu on mobile
2025-11-11 15:37:12 +10:00
Jamie Curnow
2c26ed8b11 Revert "Fix #4831 mobile header menu not working"
This reverts commit 4bd545c88e.
2025-11-11 15:36:46 +10:00
jc21
e3f5cd9a58 Merge pull request #4871 from prospo/develop
chore: Bump certbot-dns-leaseweb to 1.0.3
2025-11-11 15:24:11 +10:00
jc21
fba14817e7 Merge pull request #4894 from eduardpaul/feat-fix-pass_auth-template
Update _access.conf to fix access_list.pass_auth logic
2025-11-11 15:23:22 +10:00
Jamie Curnow
6825a9773b Fix #4854 Added missing forward http code for redirections 2025-11-11 15:17:43 +10:00
Jamie Curnow
8bc3078d87 Fix initial setup user bug, taking the fix from #4836 2025-11-11 14:52:39 +10:00
Jamie Curnow
8aeb2fa661 Fix #4692, #4856 - stick with auto for scheme in db, change it to $scheme when rendering 2025-11-11 14:46:25 +10:00
Jamie Curnow
4bd545c88e Fix #4831 mobile header menu not working 2025-11-11 14:05:26 +10:00
Jamie Curnow
7f0cce944d Relax the email validation in frontend 2025-11-11 08:54:48 +10:00
Pablo Portas López
7cde6ee7ca Add Spanish Test 2025-11-10 21:58:23 +01:00
Pablo Portas López
df1b414c2e Delete Spanish Test 2025-11-10 21:58:01 +01:00
Jamie Curnow
311d6a1541 Tweaks to CI stack for postgres
All checks were successful
Close stale issues and PRs / stale (push) Successful in 20s
2025-11-10 10:30:16 +10:00
mamasch19
5e7276e65b Add MC-HOST24 DNS plugin configuration
added the MC-HOST24 configuration to the new plugin file
2025-11-09 22:31:48 +01:00
Eduard Paul
2bcb942f93 Update _access.conf to ensure Authorization header remove when pass_auth = false or 0
Fixing prev commit as it's negative logic.
2025-11-09 21:02:18 +01:00
Eduard Paul
b3dac3df08 Update _access.conf to fix access_list.pass_auth logic
Wrong logic to pass auth as header: when disabled (pass_auth=0) credentials are included in Authorization header. However as soon as you enable (pass_auth=1) they are not.
2025-11-09 20:11:33 +01:00
jc21
64c5a863f8 Merge pull request #4878 from NginxProxyManager/develop
v2.13.2
2025-11-09 21:16:26 +10:00
Jamie Curnow
cd94863850 Bump version
All checks were successful
Close stale issues and PRs / stale (push) Successful in 25s
2025-11-09 20:25:10 +10:00
Emil
fd1d33444a chore: Bump certbot-dns-leaseweb to 1.0.3 2025-11-08 14:39:23 +01:00
7heMech
6fa2d6a98a Fix hamburger menu on mobile 2025-11-07 19:34:43 +00:00
Jamie Curnow
3c252db46f Fixes #4844 with more defensive date parsing
All checks were successful
Close stale issues and PRs / stale (push) Successful in 23s
2025-11-07 21:37:22 +10:00
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
Pablo Portas López
123f7d1999 Add Spanish language support and translations 2025-11-06 01:04:02 +01:00
Frank
b21d6d9d78 Fix German translations
Fix: German translations
2025-11-05 08:09:10 +01:00
Frank
bf1ad15ed7 Update de.json
fix: typos
2025-11-05 08:08:50 +01:00
Frank
1209303a1d Update DeadHosts.md
fix: translation "Umgangssprachlich"
2025-11-05 08:00:15 +01:00
Frank
cd3a09ebf6 Update Certificates.md
fix: typo
2025-11-05 07:59:45 +01:00
Frank
d0e20d4f1b Update de.json
fix: typo dark and light mode
2025-11-05 07:57:11 +01:00
Frank
ceb098fcfe Fix typo in German locale for min character length
fix: typo mainimale should be minimale
2025-11-05 07:53:56 +01:00
Frank
639ba3a525 Update de.json
fix: typo 
fix: translate Location with Pfad
2025-11-05 07:52:28 +01:00
jc21
e88d55f1d2 Merge pull request #4839 from NginxProxyManager/develop
v2.13.1
2025-11-05 15:40:32 +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
Frank
7e28d8a5d6 Add files via upload
add german
2025-11-03 17:51:48 +01:00
Frank
8991e88ff3 Update de.json 2025-11-03 14:22:13 +01:00
Frank
e2a8ffa2d3 Add files via upload
Add German
2025-11-03 14:18:08 +01:00
85 changed files with 2450 additions and 438 deletions

View File

@@ -1 +1 @@
2.13.0
2.13.3

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.3-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

@@ -370,7 +370,7 @@
"leaseweb": {
"name": "LeaseWeb",
"package_name": "certbot-dns-leaseweb",
"version": "~=1.0.1",
"version": "~=1.0.3",
"dependencies": "",
"credentials": "dns_leaseweb_api_token = 01234556789",
"full_plugin_name": "dns-leaseweb"
@@ -399,6 +399,14 @@
"credentials": "dns_luadns_email = user@example.com\ndns_luadns_token = 0123456789abcdef0123456789abcdef",
"full_plugin_name": "dns-luadns"
},
"mchost24": {
"name": "MC-HOST24",
"package_name": "certbot-dns-mchost24",
"version": "",
"dependencies": "",
"credentials": "# Obtain API token using https://github.com/JoeJoeTV/mchost24-api-python\ndns_mchost24_api_token=<insert obtained API token here>",
"full_plugin_name": "dns-mchost24"
},
"mijnhost": {
"name": "mijn.host",
"package_name": "certbot-dns-mijn-host",

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(
@@ -30,4 +32,11 @@ const generateDbConfig = () => {
};
};
export default knex(generateDbConfig());
const getInstance = () => {
if (!instance) {
instance = knex(generateDbConfig());
}
return instance;
}
export default getInstance;

View File

@@ -4,6 +4,7 @@ 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";
@@ -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

@@ -216,6 +216,11 @@ const internalNginx = {
}
}
// For redirection hosts, if the scheme is not http or https, set it to $scheme
if (nice_host_type === "redirection_host" && ['http', 'https'].indexOf(host.forward_scheme.toLowerCase()) === -1) {
host.forward_scheme = "$scheme";
}
if (host.locations) {
//logger.info ('host.locations = ' + JSON.stringify(host.locations, null, 2));
origLocations = [].concat(host.locations);

View File

@@ -25,6 +25,12 @@ 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;
@@ -33,12 +39,12 @@ const configure = () => {
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);
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");

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

@@ -0,0 +1,50 @@
import { migrate as logger } from "../logger.js";
const migrateName = "redirect_auto_scheme";
/**
* Migrate
*
* @see http://knexjs.org/#Schema
*
* @param {Object} knex
* @returns {Promise}
*/
const up = (knex) => {
logger.info(`[${migrateName}] Migrating Up...`);
return knex.schema
.table("redirection_host", async (table) => {
// change the column default from $scheme to auto
await table.string("forward_scheme").notNull().defaultTo("auto").alter();
await knex('redirection_host')
.where('forward_scheme', '$scheme')
.update({ forward_scheme: 'auto' });
})
.then(() => {
logger.info(`[${migrateName}] redirection_host Table altered`);
});
};
/**
* Undo Migrate
*
* @param {Object} knex
* @returns {Promise}
*/
const down = (knex) => {
logger.info(`[${migrateName}] Migrating Down...`);
return knex.schema
.table("redirection_host", async (table) => {
await table.string("forward_scheme").notNull().defaultTo("$scheme").alter();
await knex('redirection_host')
.where('forward_scheme', 'auto')
.update({ forward_scheme: '$scheme' });
})
.then(() => {
logger.info(`[${migrateName}] redirection_host Table altered`);
});
};
export { up, down };

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

@@ -37,7 +37,7 @@ const setupDefaultUser = async () => {
const data = {
is_deleted: 0,
email: email,
email: initialAdminEmail,
name: "Administrator",
nickname: "Admin",
avatar: "",
@@ -53,7 +53,7 @@ const setupDefaultUser = async () => {
.insert({
user_id: user.id,
type: "password",
secret: password,
secret: initialAdminPassword,
meta: {},
});

View File

@@ -4,7 +4,7 @@
auth_basic "Authorization required";
auth_basic_user_file /data/access/{{ access_list_id }};
{% if access_list.pass_auth == 0 or access_list.pass_auth == true %}
{% if access_list.pass_auth == 0 or access_list.pass_auth == false %}
proxy_set_header Authorization "";
{% endif %}

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,6 +1,6 @@
AUTHENTIK_SECRET_KEY=gl8woZe8L6IIX8SC0c5Ocsj0xPkX5uJo5DVZCFl+L/QGbzuplfutYuua2ODNLEiDD3aFd9H2ylJmrke0
AUTHENTIK_REDIS__HOST=authentik-redis
AUTHENTIK_POSTGRESQL__HOST=db-postgres
AUTHENTIK_POSTGRESQL__HOST=pgdb.internal
AUTHENTIK_POSTGRESQL__USER=authentik
AUTHENTIK_POSTGRESQL__NAME=authentik
AUTHENTIK_POSTGRESQL__PASSWORD=07EKS5NLI6Tpv68tbdvrxfvj

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

@@ -6,7 +6,7 @@ services:
fullstack:
environment:
DB_POSTGRES_HOST: "db-postgres"
DB_POSTGRES_HOST: "pgdb.internal"
DB_POSTGRES_PORT: "5432"
DB_POSTGRES_USER: "npm"
DB_POSTGRES_PASSWORD: "npmpass"
@@ -27,7 +27,9 @@ services:
- psql_vol:/var/lib/postgresql/data
- ./ci/postgres:/docker-entrypoint-initdb.d
networks:
- fulltest
fulltest:
aliases:
- pgdb.internal
authentik-redis:
image: "redis:alpine"
@@ -41,6 +43,8 @@ services:
timeout: 3s
volumes:
- redis_vol:/data
networks:
- fulltest
authentik:
image: ghcr.io/goauthentik/server:2024.10.1
@@ -51,6 +55,8 @@ services:
depends_on:
- authentik-redis
- db-postgres
networks:
- fulltest
authentik-worker:
image: ghcr.io/goauthentik/server:2024.10.1
@@ -61,6 +67,8 @@ services:
depends_on:
- authentik-redis
- db-postgres
networks:
- fulltest
authentik-ldap:
image: ghcr.io/goauthentik/ldap:2024.10.1
@@ -71,6 +79,8 @@ services:
restart: unless-stopped
depends_on:
- authentik
networks:
- fulltest
volumes:
psql_vol:

View File

@@ -3,31 +3,30 @@
# This is a base compose file, it should be extended with a
# docker-compose.ci.*.yml file
services:
fullstack:
image: "${IMAGE}:${BRANCH_LOWER}-ci-${BUILD_NUMBER}"
environment:
TZ: "${TZ:-Australia/Brisbane}"
DEBUG: 'true'
CI: 'true'
DEBUG: "true"
CI: "true"
FORCE_COLOR: 1
# Required for DNS Certificate provisioning in CI
LE_SERVER: 'https://ca.internal/acme/acme/directory'
REQUESTS_CA_BUNDLE: '/etc/ssl/certs/NginxProxyManager.crt'
LE_SERVER: "https://ca.internal/acme/acme/directory"
REQUESTS_CA_BUNDLE: "/etc/ssl/certs/NginxProxyManager.crt"
volumes:
- 'npm_data_ci:/data'
- 'npm_le_ci:/etc/letsencrypt'
- './dev/letsencrypt.ini:/etc/letsencrypt.ini:ro'
- './dev/resolv.conf:/etc/resolv.conf:ro'
- '/etc/localtime:/etc/localtime:ro'
- "npm_data_ci:/data"
- "npm_le_ci:/etc/letsencrypt"
- "./dev/letsencrypt.ini:/etc/letsencrypt.ini:ro"
- "./dev/resolv.conf:/etc/resolv.conf:ro"
- "/etc/localtime:/etc/localtime:ro"
healthcheck:
test: ["CMD", "/usr/bin/check-health"]
interval: 10s
timeout: 3s
expose:
- '80-81/tcp'
- '443/tcp'
- '1500-1503/tcp'
- "80-81/tcp"
- "443/tcp"
- "1500-1503/tcp"
networks:
fulltest:
aliases:
@@ -38,8 +37,8 @@ services:
stepca:
image: jc21/testca
volumes:
- './dev/resolv.conf:/etc/resolv.conf:ro'
- '/etc/localtime:/etc/localtime:ro'
- "./dev/resolv.conf:/etc/resolv.conf:ro"
- "/etc/localtime:/etc/localtime:ro"
networks:
fulltest:
aliases:
@@ -48,18 +47,18 @@ services:
pdns:
image: pschiffe/pdns-mysql:4.8
volumes:
- '/etc/localtime:/etc/localtime:ro'
- "/etc/localtime:/etc/localtime:ro"
environment:
PDNS_master: 'yes'
PDNS_api: 'yes'
PDNS_api_key: 'npm'
PDNS_webserver: 'yes'
PDNS_webserver_address: '0.0.0.0'
PDNS_webserver_password: 'npm'
PDNS_webserver-allow-from: '127.0.0.0/8,192.0.0.0/8,10.0.0.0/8,172.0.0.0/8'
PDNS_version_string: 'anonymous'
PDNS_master: "yes"
PDNS_api: "yes"
PDNS_api_key: "npm"
PDNS_webserver: "yes"
PDNS_webserver_address: "0.0.0.0"
PDNS_webserver_password: "npm"
PDNS_webserver-allow-from: "127.0.0.0/8,192.0.0.0/8,10.0.0.0/8,172.0.0.0/8"
PDNS_version_string: "anonymous"
PDNS_default_ttl: 1500
PDNS_allow_axfr_ips: '127.0.0.0/8,192.0.0.0/8,10.0.0.0/8,172.0.0.0/8'
PDNS_allow_axfr_ips: "127.0.0.0/8,192.0.0.0/8,10.0.0.0/8,172.0.0.0/8"
PDNS_gmysql_host: pdns-db
PDNS_gmysql_port: 3306
PDNS_gmysql_user: pdns
@@ -76,14 +75,14 @@ services:
pdns-db:
image: mariadb
environment:
MYSQL_ROOT_PASSWORD: 'pdns'
MYSQL_DATABASE: 'pdns'
MYSQL_USER: 'pdns'
MYSQL_PASSWORD: 'pdns'
MYSQL_ROOT_PASSWORD: "pdns"
MYSQL_DATABASE: "pdns"
MYSQL_USER: "pdns"
MYSQL_PASSWORD: "pdns"
volumes:
- 'pdns_mysql_vol:/var/lib/mysql'
- '/etc/localtime:/etc/localtime:ro'
- './dev/pdns-db.sql:/docker-entrypoint-initdb.d/01_init.sql:ro'
- "pdns_mysql_vol:/var/lib/mysql"
- "/etc/localtime:/etc/localtime:ro"
- "./dev/pdns-db.sql:/docker-entrypoint-initdb.d/01_init.sql:ro"
networks:
- fulltest
@@ -100,12 +99,12 @@ services:
context: ../
dockerfile: test/cypress/Dockerfile
environment:
HTTP_PROXY: 'squid:3128'
HTTPS_PROXY: 'squid:3128'
HTTP_PROXY: "squid:3128"
HTTPS_PROXY: "squid:3128"
volumes:
- 'cypress_logs:/test/results'
- './dev/resolv.conf:/etc/resolv.conf:ro'
- '/etc/localtime:/etc/localtime:ro'
- "cypress_logs:/test/results"
- "./dev/resolv.conf:/etc/resolv.conf:ro"
- "/etc/localtime:/etc/localtime:ro"
command: cypress run --browser chrome --config-file=cypress/config/ci.js
networks:
- fulltest
@@ -113,9 +112,9 @@ services:
squid:
image: ubuntu/squid
volumes:
- './dev/squid.conf:/etc/squid/squid.conf:ro'
- './dev/resolv.conf:/etc/resolv.conf:ro'
- '/etc/localtime:/etc/localtime:ro'
- "./dev/squid.conf:/etc/squid/squid.conf:ro"
- "./dev/resolv.conf:/etc/resolv.conf:ro"
- "/etc/localtime:/etc/localtime:ro"
networks:
- fulltest

View File

@@ -32,7 +32,7 @@ services:
# DB_MYSQL_PASSWORD: 'npm'
# DB_MYSQL_NAME: 'npm'
# db-postgres:
DB_POSTGRES_HOST: "db-postgres"
DB_POSTGRES_HOST: "pgdb.internal"
DB_POSTGRES_PORT: "5432"
DB_POSTGRES_USER: "npm"
DB_POSTGRES_PASSWORD: "npmpass"
@@ -81,8 +81,6 @@ services:
db-postgres:
image: postgres:17
container_name: npm2dev.db-postgres
networks:
- nginx_proxy_manager
environment:
POSTGRES_USER: "npm"
POSTGRES_PASSWORD: "npmpass"
@@ -90,6 +88,10 @@ services:
volumes:
- psql_data:/var/lib/postgresql/data
- ./ci/postgres:/docker-entrypoint-initdb.d
networks:
nginx_proxy_manager:
aliases:
- pgdb.internal
stepca:
image: jc21/testca

View File

@@ -8,7 +8,7 @@
const allLocales = [
["en", "en-US"],
["fa", "fa-IR"],
["es", "es-ES"],
];
const ignoreUnused = [

View File

@@ -4,7 +4,7 @@ import type { ReactNode } from "react";
import Select, { type ActionMeta, components, type OptionProps } from "react-select";
import type { AccessList } from "src/api/backend";
import { useAccessLists } from "src/hooks";
import { DateTimeFormat, intl, T } from "src/locale";
import { formatDateTime, intl, T } from "src/locale";
interface AccessOption {
readonly value: number;
@@ -48,7 +48,7 @@ export function AccessField({ name = "accessListId", label = "access-list", id =
{
users: item?.items?.length,
rules: item?.clients?.length,
date: item?.createdOn ? DateTimeFormat(item?.createdOn) : "N/A",
date: item?.createdOn ? formatDateTime(item?.createdOn) : "N/A",
},
),
icon: <IconLock size={14} className="text-lime" />,

View File

@@ -3,7 +3,7 @@ import { Field, useFormikContext } from "formik";
import Select, { type ActionMeta, components, type OptionProps } from "react-select";
import type { Certificate } from "src/api/backend";
import { useCertificates } from "src/hooks";
import { DateTimeFormat, intl, T } from "src/locale";
import { formatDateTime, intl, T } from "src/locale";
interface CertOption {
readonly value: number | "new";
@@ -75,7 +75,7 @@ export function SSLCertificateField({
data?.map((cert: Certificate) => ({
value: cert.id,
label: cert.niceName,
subLabel: `${cert.provider === "letsencrypt" ? intl.formatMessage({ id: "lets-encrypt" }) : cert.provider} &mdash; ${intl.formatMessage({ id: "expires.on" }, { date: cert.expiresOn ? DateTimeFormat(cert.expiresOn) : "N/A" })}`,
subLabel: `${cert.provider === "letsencrypt" ? intl.formatMessage({ id: "lets-encrypt" }) : cert.provider} ${intl.formatMessage({ id: "expires.on" }, { date: cert.expiresOn ? formatDateTime(cert.expiresOn) : "N/A" })}`,
icon: <IconShield size={14} className="text-pink" />,
})) || [];

View File

@@ -190,7 +190,7 @@ export function SiteMenu() {
return (
<header className="navbar-expand-md">
<div className="collapse navbar-collapse">
<div className="collapse navbar-collapse" id="navbar-menu">
<div className="navbar">
<div className="container-xl">
<div className="row flex-column flex-md-row flex-fill align-items-center">

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

@@ -1,6 +1,6 @@
import cn from "classnames";
import { differenceInDays, isPast, parseISO } from "date-fns";
import { DateTimeFormat } from "src/locale";
import { differenceInDays, isPast } from "date-fns";
import { formatDateTime, parseDate } from "src/locale";
interface Props {
value: string;
@@ -8,11 +8,12 @@ interface Props {
highlistNearlyExpired?: boolean;
}
export function DateFormatter({ value, highlightPast, highlistNearlyExpired }: Props) {
const dateIsPast = isPast(parseISO(value));
const days = differenceInDays(parseISO(value), new Date());
const d = parseDate(value);
const dateIsPast = d ? isPast(d) : false;
const days = d ? differenceInDays(d, new Date()) : 0;
const cl = cn({
"text-danger": highlightPast && dateIsPast,
"text-warning": highlistNearlyExpired && !dateIsPast && days <= 30 && days >= 0,
});
return <span className={cl}>{DateTimeFormat(value)}</span>;
return <span className={cl}>{formatDateTime(value)}</span>;
}

View File

@@ -1,6 +1,6 @@
import cn from "classnames";
import type { ReactNode } from "react";
import { DateTimeFormat, T } from "src/locale";
import { formatDateTime, T } from "src/locale";
interface Props {
domains: string[];
@@ -53,7 +53,7 @@ export function DomainsFormatter({ domains, createdOn, niceName, provider, color
<div className="font-weight-medium">{...elms}</div>
{createdOn ? (
<div className="text-secondary mt-1">
<T id="created-on" data={{ date: DateTimeFormat(createdOn) }} />
<T id="created-on" data={{ date: formatDateTime(createdOn) }} />
</div>
) : null}
</div>

View File

@@ -1,7 +1,7 @@
import { IconArrowsCross, IconBolt, IconBoltOff, IconDisc, IconLock, IconShield, IconUser } from "@tabler/icons-react";
import cn from "classnames";
import type { AuditLog } from "src/api/backend";
import { DateTimeFormat, T } from "src/locale";
import { formatDateTime, T } from "src/locale";
const getEventValue = (event: AuditLog) => {
switch (event.objectType) {
@@ -73,7 +73,7 @@ export function EventFormatter({ row }: Props) {
<T id={`object.event.${row.action}`} tData={{ object: row.objectType }} />
&nbsp; &mdash; <span className="badge">{getEventValue(row)}</span>
</div>
<div className="text-secondary mt-1">{DateTimeFormat(row.createdOn)}</div>
<div className="text-secondary mt-1">{formatDateTime(row.createdOn)}</div>
</div>
);
}

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,4 +1,4 @@
import { DateTimeFormat, T } from "src/locale";
import { formatDateTime, T } from "src/locale";
interface Props {
value: string;
@@ -13,7 +13,7 @@ export function ValueWithDateFormatter({ value, createdOn, disabled }: Props) {
</div>
{createdOn ? (
<div className={`text-secondary mt-1 ${disabled ? "text-red" : ""}`}>
<T id={disabled ? "disabled" : "created-on"} data={{ date: DateTimeFormat(createdOn) }} />
<T id={disabled ? "disabled" : "created-on"} data={{ date: formatDateTime(createdOn) }} />
</div>
) : null}
</div>

View File

@@ -1,15 +0,0 @@
import { 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,
});
export { DateTimeFormat };

View File

@@ -1,15 +1,25 @@
import { createIntl, createIntlCache } from "react-intl";
import langEn from "./lang/en.json";
import langEs from "./lang/es.json";
import langDe from "./lang/de.json";
import langList from "./lang/lang-list.json";
// first item of each array should be the language code,
// not the country code
// Remember when adding to this list, also update check-locales.js script
const localeOptions = [["en", "en-US"]];
const localeOptions = [
["en", "en-US"],
["es", "es-ES"],
["de", "de-DE"]
];
const loadMessages = (locale?: string): typeof langList & typeof langEn => {
const thisLocale = locale || "en";
switch (thisLocale.slice(0, 2)) {
case "es":
return Object.assign({}, langList, langEs);
case "de":
return Object.assign({}, langList, langEn, langDe);
default:
return Object.assign({}, langList, langEn);
}
@@ -17,6 +27,12 @@ const loadMessages = (locale?: string): typeof langList & typeof langEn => {
const getFlagCodeForLocale = (locale?: string) => {
switch (locale) {
case "es-ES":
case "es":
return "ES";
case "de-DE":
case "de":
return "DE";
default:
return "EN";
}
@@ -30,13 +46,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 +99,12 @@ const T = ({
);
};
export { localeOptions, getFlagCodeForLocale, getLocale, createIntl, changeLocale, intl, T };
export {
localeOptions,
getFlagCodeForLocale,
getLocale,
createIntl,
changeLocale,
intl,
T,
};

View File

@@ -0,0 +1,74 @@
import { formatDateTime } 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 = formatDateTime(value);
expect(text).toBe("Monday, 01/01/2024, 12:00:00 am");
});
it("format date from unix timestamp number", () => {
const value = 1762476112;
const text = formatDateTime(value);
expect(text).toBe("Friday, 07/11/2025, 12:41:52 am");
});
it("format date from unix timestamp string", () => {
const value = "1762476112";
const text = formatDateTime(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 = formatDateTime(value);
expect(text).toBe("this is not a good date");
});
it("catch bad format from number", () => {
const value = -100;
const text = formatDateTime(value);
expect(text).toBe("-100");
});
it("catch bad format from number as string", () => {
const value = "-100";
const text = formatDateTime(value);
expect(text).toBe("-100");
});
});

View File

@@ -0,0 +1,42 @@
import { fromUnixTime, intlFormat, parseISO } from "date-fns";
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 parseDate = (value: string | number): Date | null => {
if (typeof value !== "number" && typeof value !== "string") return null;
try {
return isUnixTimestamp(value) ? fromUnixTime(+value) : parseISO(`${value}`);
} catch {
return null;
}
};
const formatDateTime = (value: string | number): string => {
const d = parseDate(value);
if (!d) return `${value}`;
try {
return intlFormat(d, {
weekday: "long",
year: "numeric",
month: "numeric",
day: "numeric",
hour: "numeric",
minute: "numeric",
second: "numeric",
hour12: true,
});
} catch {
return `${value}`;
}
};
export { formatDateTime, parseDate, isUnixTimestamp };

View File

@@ -1,2 +1,2 @@
export * from "./DateTimeFormat";
export * from "./IntlProvider";
export * from "./Utils";

View File

@@ -0,0 +1,215 @@
{
"access-list": "Zugriffsliste",
"access-list.access-count": "{count} {count, plural, one {Regel} other {Regeln}}",
"access-list.auth-count": "{count} {count, plural, one {User} other {Users}}",
"access-list.help-rules-last": "Wenn mindestens eine Regel vorhanden ist, wird diese Regel zum Ablehnen aller Anfragen als letzte hinzugefügt.",
"access-list.help.rules-order": "Beachten Sie, dass die Anweisungen „Erlauben“ und „Verbieten“ in der Reihenfolge ihrer Definition angewendet werden.",
"access-list.pass-auth": "Authentifizierung an Upstream weiterleiten",
"access-list.public": "Öffentlich",
"access-list.public.subtitle": "Keine Authentifizierung erforderlich",
"access-list.satisfy-any": "Satisfy Any",
"access-list.subtitle": "{users} {users, plural, one {User} other {Users}}, {rules} {rules, plural, one {Regel} other {Regeln}} - Erstellt: {date}",
"access-lists": "Zugrifflisten",
"action.add": "Hinzufügen",
"action.add-location": "Pfad Hinzufügen",
"action.close": "Schließen",
"action.delete": "Löschen",
"action.disable": "Deaktivieren",
"action.download": "Herunterladen",
"action.edit": "Bearbeiten",
"action.enable": "Aktivieren",
"action.permissions": "Berechtigungen",
"action.renew": "Erneuert",
"action.view-details": "Details",
"auditlogs": "Protokoll",
"cancel": "Abbrechen",
"certificate": "Zertifikat",
"certificate.custom-certificate": "Zertifikat",
"certificate.custom-certificate-key": "Privater Schlüssel",
"certificate.custom-intermediate": "Zwischen Zertifikat",
"certificate.in-use": "In Benutzung",
"certificate.none.subtitle": "Kein Zertifikat zugewiesen",
"certificate.none.subtitle.for-http": "Dieser Host verwendet kein HTTPS.",
"certificate.none.title": "Kein",
"certificate.not-in-use": "Nicht in Benutzung",
"certificates": "Zertifikate",
"certificates.custom": "Benutzerdefiniertes Zertifikat",
"certificates.custom.warning": "Mit einem Passwort geschützte Schlüsseldateien werden nicht unterstützt.",
"certificates.dns.credentials": "Inhalt der Anmeldedaten-Datei",
"certificates.dns.credentials-note": "Dieses Plugin erfordert eine Konfigurationsdatei, die einen API-Token oder andere Anmeldedaten für Ihren Anbieter enthält.",
"certificates.dns.credentials-warning": "Diese Daten werden als Klartext in der Datenbank und in einer Datei gespeichert!",
"certificates.dns.propagation-seconds": "Wartzeit in Sekunden",
"certificates.dns.propagation-seconds-note": "Leer lassen um die Standardwartezeit des Plugins zu nutzen",
"certificates.dns.provider": "DNS Provider",
"certificates.dns.warning": "Dieser Abschnitt erfordert einige Kenntnisse über Certbot und seine DNS-Plugins. Bitte konsultieren Sie die jeweilige Plugin-Dokumentation.",
"certificates.http.reachability-404": "Unter dieser Domain wurde ein Server gefunden, aber es scheint sich nicht um Nginx Proxy Manager zu handeln. Bitte stellen Sie sicher, dass Ihre Domain auf die IP-Adresse verweist, unter der Ihre NPM-Instanz ausgeführt wird.",
"certificates.http.reachability-failed-to-check": "Die Erreichbarkeit konnte aufgrund eines Kommunikationsfehlers mit site24x7.com nicht überprüft werden.",
"certificates.http.reachability-not-resolved": "Unter dieser Domain ist kein Server verfügbar. Bitte stellen Sie sicher, dass Ihre Domain existiert und auf die IP-Adresse verweist, unter der Ihre NPM-Instanz läuft, und dass gegebenenfalls Port 80 in Ihrem Router weitergeleitet wird.",
"certificates.http.reachability-ok": "Ihr Server ist erreichbar und die Erstellung von Zertifikaten sollte möglich sein.",
"certificates.http.reachability-other": "Unter dieser Domain wurde ein Server gefunden, der jedoch einen unerwarteten Statuscode {code} zurückgegeben hat. Handelt es sich um den NPM-Server? Bitte stellen Sie sicher, dass Ihre Domain auf die IP-Adresse verweist, unter der Ihre NPM-Instanz ausgeführt wird.",
"certificates.http.reachability-wrong-data": "Unter dieser Domain wurde ein Server gefunden, der jedoch unerwartete Daten zurückgegeben hat. Handelt es sich um den NPM-Server? Bitte stellen Sie sicher, dass Ihre Domain auf die IP-Adresse verweist, unter der Ihre NPM-Instanz ausgeführt wird.",
"certificates.http.test-results": "Test Ergeniss",
"certificates.http.warning": "Diese Domänen müssen bereits so konfiguriert sein, dass sie auf diese Installation verweisen.",
"certificates.request.subtitle": "Über Let's Encrypt",
"certificates.request.title": "Anfordern eines neuen Zertifikates",
"column.access": "Zugriff",
"column.authorization": "Genehmigung",
"column.authorizations": "Genehmigungen",
"column.custom-locations": "Benutzerdefinierte Pfad",
"column.destination": "Ziel",
"column.details": "Details",
"column.email": "Email",
"column.event": "Ereignis",
"column.expires": "Verfällt am",
"column.http-code": "HTTP Code",
"column.incoming-port": "Eingehender Port",
"column.name": "Name",
"column.protocol": "Protokoll",
"column.provider": "Provider",
"column.roles": "Rollen",
"column.rules": "Regeln",
"column.satisfy": "Satisfy",
"column.satisfy-all": "Alle",
"column.satisfy-any": "Jeder",
"column.scheme": "Schema",
"column.source": "Quelle",
"column.ssl": "SSL",
"column.status": "Status",
"created-on": "Erstelldatum: {date}",
"dashboard": "Dashboard",
"dead-host": "404 Host",
"dead-hosts": "404 Hosts",
"dead-hosts.count": "{count} {count, plural, one {404 Host} other {404 Hosts}}",
"disabled": "Deaktiviert",
"domain-names": "Domain Names",
"domain-names.max": "{count} Maximale Anzahl von Domainnamen",
"domain-names.placeholder": "Eintragen der Domain...",
"domain-names.wildcards-not-permitted": "Wildcards sind für diesen Typ nicht zulässig.",
"domain-names.wildcards-not-supported": "Wildcards werden für diese Zertifizierungsstelle nicht unterstützt.",
"domains.force-ssl": "Erzwinge SSL",
"domains.hsts-enabled": "HSTS aktiviert",
"domains.hsts-subdomains": "HSTS Sub-domains",
"domains.http2-support": "HTTP/2 Support",
"domains.use-dns": "Nutze DNS Challenge",
"email-address": "Email Addresse",
"empty-search": "Keine Ergebnisse gefunden",
"empty-subtitle": "Warum erstellen Sie nicht eine?",
"enabled": "aktiviert",
"error.access.at-least-one": "Entweder eine Genehmigung oder eine Zugriffsregel ist erforderlich.",
"error.access.duplicate-usernames": "Autorisierung Benutzernamen müssen eindeutig sein",
"error.invalid-auth": "Ungültige E-Mail-Adresse oder Passwort",
"error.invalid-domain": "Ungültige Domain: {domain}",
"error.invalid-email": "Ungültige E-Mail-Adresse",
"error.max-character-length": "Die maximale Länge beträgt {max} Zeichen{max, plural, one {} other {s}}",
"error.max-domains": "Zu viele Domains, maximal sind {max}",
"error.maximum": "Maximum ist {max}",
"error.min-character-length": "Die minimale Länge beträgt {min} Zeichen{min, plural, one {} other {s}}",
"error.minimum": "Minimum ist {min}",
"error.passwords-must-match": "Passwörter müssen übereinstimmen",
"error.required": "Dies ist erforderlich.",
"expires.on": "Ablauf am: {date}",
"footer.github-fork": "Fork me on Github",
"host.flags.block-exploits": "Gängige Exploits blockieren",
"host.flags.cache-assets": "Cache Assets",
"host.flags.preserve-path": "Pfad beibehalten",
"host.flags.protocols": "Protokole",
"host.flags.websockets-upgrade": "Websockets Support",
"host.forward-port": "Forward Port",
"host.forward-scheme": "Schema",
"hosts": "Hosts",
"http-only": "HTTP Only",
"lets-encrypt": "Let's Encrypt",
"lets-encrypt-via-dns": "Let's Encrypt via DNS",
"lets-encrypt-via-http": "Let's Encrypt via HTTP",
"loading": "Laden…",
"login.title": "Account Login",
"nginx-config.label": "Benutzerdefinierte Nginx Konfiguration",
"nginx-config.placeholder": "# Geben Sie hier Ihre benutzerdefinierte Nginx-Konfiguration auf eigene Gefahr ein!",
"no-permission-error": "Sie haben keinen Zugriff, um dies anzuzeigen.",
"notfound.action": "Take me home",
"notfound.content": "We are sorry but the page you are looking for was not found",
"notfound.title": "Oops… You just found an error page",
"notification.error": "Error",
"notification.object-deleted": "{object} wurde gelöscht",
"notification.object-disabled": "{object} wurde deaktiviert",
"notification.object-enabled": "{object} wurde aktiviert",
"notification.object-renewed": "{object} wurde erneuert",
"notification.object-saved": "{object} wurde gespeichert",
"notification.success": "Erfolgreich",
"object.actions-title": "{object} #{id}",
"object.add": "{object} hinzufügen",
"object.delete": "{object} löschen",
"object.delete.content": "Bist du dir sicher das du diese(n) {object} löschen möchtest?",
"object.edit": "{object} bearbeiten",
"object.empty": "Keine {objects} vorhanden",
"object.event.created": "{object} erstellt",
"object.event.deleted": "{object} gelöscht",
"object.event.disabled": "{object} deaktiviert",
"object.event.enabled": "{object} aktiviert",
"object.event.renewed": "{object} erneuert",
"object.event.updated": "{object} aktualisiert",
"offline": "Offline",
"online": "Online",
"options": "Optionen",
"password": "Passwort",
"password.generate": "Zufälliges Passwort generieren",
"password.hide": "Passwort verstecken",
"password.show": "Passwort anzeigen",
"permissions.hidden": "Versteckt",
"permissions.manage": "Verwalten",
"permissions.view": "Nur anzeigen",
"permissions.visibility.all": "Alle Elemente",
"permissions.visibility.title": "Objekt Sichtbarkeit",
"permissions.visibility.user": "Nur erstellte Elemente",
"proxy-host": "Proxy Host",
"proxy-host.forward-host": "Forward Hostname / IP",
"proxy-hosts": "Proxy Hosts",
"proxy-hosts.count": "{count} {count, plural, one {Proxy Host} other {Proxy Hosts}}",
"public": "Öffentlich",
"redirection-host": "Redirection Host",
"redirection-host.forward-domain": "Forward Domain",
"redirection-host.forward-http-code" : "HTTP Code",
"redirection-hosts": "Redirection Hosts",
"redirection-hosts.count": "{count} {count, plural, one {Redirection Host} other {Redirection Hosts}}",
"role.admin": "Administrator",
"role.standard-user": "Standard User",
"save": "Speichern",
"setting": "Einstellung",
"settings": "Einstellungen",
"settings.default-site": "Standard Seite",
"settings.default-site.404": "404 Page",
"settings.default-site.444": "No Response (444)",
"settings.default-site.congratulations": "Willkommensseite",
"settings.default-site.description": "Was angezeigt wird, wenn der Nginx eine unbekannte Webseitenanfrage bekommt",
"settings.default-site.html": "Benutzerdefinierte HTML",
"settings.default-site.html.placeholder": "<!-- Geben Sie hier Ihren benutzerdefinierten HTML-Inhalt ein. -->",
"settings.default-site.redirect": "Weiterleitung",
"setup.preamble": "Beginnen Sie mit der Erstellung Ihres Administratorkontos.",
"setup.title": "Willkommen!",
"sign-in": "Login",
"ssl-certificate": "SSL Zertifikate",
"stream": "Stream",
"stream.forward-host": "Forward Host",
"stream.incoming-port": "Incoming Port",
"streams": "Streams",
"streams.count": "{count} {count, plural, one {Stream} other {Streams}}",
"streams.tcp": "TCP",
"streams.udp": "UDP",
"test": "Test",
"user": "User",
"user.change-password": "Passwort ändern",
"user.confirm-password": "Passwort wiederholen",
"user.current-password": "Aktuelles Passwort",
"user.edit-profile": "Profil bearbeiten",
"user.full-name": "Name",
"user.login-as": "Einloggen als {name}",
"user.logout": "Ausloggen",
"user.new-password": "Neues Password",
"user.nickname": "Nickname",
"user.set-password": "Passwort setzen",
"user.set-permissions": "Berechtigungen für {name} setzen",
"user.switch-dark": "Zum Dark Mode wechseln",
"user.switch-light": "Zum Light Mode wechslen",
"username": "Benutzername",
"users": "Benutzer"
}

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.",
@@ -61,7 +62,7 @@
"column.email": "Email",
"column.event": "Event",
"column.expires": "Expires",
"column.http-code": "Access",
"column.http-code": "HTTP Code",
"column.incoming-port": "Incoming Port",
"column.name": "Name",
"column.protocol": "Protocol",
@@ -168,6 +169,7 @@
"public": "Public",
"redirection-host": "Redirection Host",
"redirection-host.forward-domain": "Forward Domain",
"redirection-host.forward-http-code": "HTTP Code",
"redirection-hosts": "Redirection Hosts",
"redirection-hosts.count": "{count} {count, plural, one {Redirection Host} other {Redirection Hosts}}",
"role.admin": "Administrator",

View File

@@ -0,0 +1,216 @@
{
"access-list": "Lista de Acceso",
"access-list.access-count": "{count} {count, plural, one {Regla} other {Reglas}}",
"access-list.auth-count": "{count} {count, plural, one {Usuario} other {Usuarios}}",
"access-list.help-rules-last": "Cuando exista al menos 1 regla, esta regla de denegar todo se añadirá al final",
"access-list.help.rules-order": "Ten en cuenta que las directivas de permitir y denegar se aplicarán en el orden en que estén definidas.",
"access-list.pass-auth": "Pasar Autenticación al Upstream",
"access-list.public": "Accesible Públicamente",
"access-list.public.subtitle": "No se requiere autenticación básica",
"access-list.satisfy-any": "Satisfacer Cualquiera",
"access-list.subtitle": "{users} {users, plural, one {Usuario} other {Usuarios}}, {rules} {rules, plural, one {Regla} other {Reglas}} - Creado: {date}",
"access-lists": "Listas de Acceso",
"action.add": "Añadir",
"action.add-location": "Añadir Ubicación",
"action.close": "Cerrar",
"action.delete": "Eliminar",
"action.disable": "Deshabilitar",
"action.download": "Descargar",
"action.edit": "Editar",
"action.enable": "Habilitar",
"action.permissions": "Permisos",
"action.renew": "Renovar",
"action.view-details": "Ver Detalles",
"auditlogs": "Registros de Auditoría",
"cancel": "Cancelar",
"certificate": "Certificado",
"certificate.custom-certificate": "Certificado",
"certificate.custom-certificate-key": "Clave del Certificado",
"certificate.custom-intermediate": "Certificado Intermedio",
"certificate.in-use": "En Uso",
"certificate.none.subtitle": "Sin certificado asignado",
"certificate.none.subtitle.for-http": "Este host no usará HTTPS",
"certificate.none.title": "Ninguno",
"certificate.not-in-use": "Sin Usar",
"certificate.renew": "Renovar Certificado",
"certificates": "Certificados",
"certificates.custom": "Certificado Personalizado",
"certificates.custom.warning": "No se admiten archivos de claves protegidos con contraseña.",
"certificates.dns.credentials": "Contenido del Archivo de Credenciales",
"certificates.dns.credentials-note": "Este plugin requiere un archivo de configuración que contenga un token de API u otras credenciales para tu proveedor",
"certificates.dns.credentials-warning": "¡Estos datos se almacenarán como texto plano en la base de datos y en un archivo!",
"certificates.dns.propagation-seconds": "Segundos de Propagación",
"certificates.dns.propagation-seconds-note": "Dejar vacío para usar el valor predeterminado del plugin. Número de segundos a esperar para la propagación DNS.",
"certificates.dns.provider": "Proveedor DNS",
"certificates.dns.warning": "Esta sección requiere algunos conocimientos sobre Certbot y sus plugins DNS. Consulta la documentación de los plugins respectivos.",
"certificates.http.reachability-404": "Se encontró un servidor en este dominio pero no parece ser Nginx Proxy Manager. Asegúrate de que tu dominio apunte a la IP donde se está ejecutando tu instancia de NPM.",
"certificates.http.reachability-failed-to-check": "No se pudo verificar la accesibilidad debido a un error de comunicación con site24x7.com.",
"certificates.http.reachability-not-resolved": "No hay ningún servidor disponible en este dominio. Asegúrate de que tu dominio existe y apunta a la IP donde se está ejecutando tu instancia de NPM y, si es necesario, que el puerto 80 esté redirigido en tu router.",
"certificates.http.reachability-ok": "Tu servidor es accesible y debería ser posible crear certificados.",
"certificates.http.reachability-other": "Se encontró un servidor en este dominio pero devolvió un código de estado inesperado {code}. ¿Es el servidor NPM? Asegúrate de que tu dominio apunte a la IP donde se está ejecutando tu instancia de NPM.",
"certificates.http.reachability-wrong-data": "Se encontró un servidor en este dominio pero devolvió datos inesperados. ¿Es el servidor NPM? Asegúrate de que tu dominio apunte a la IP donde se está ejecutando tu instancia de NPM.",
"certificates.http.test-results": "Resultados de la Prueba",
"certificates.http.warning": "Estos dominios ya deben estar configurados para apuntar a esta instalación.",
"certificates.request.subtitle": "con Let's Encrypt",
"certificates.request.title": "Solicitar un nuevo Certificado",
"column.access": "Acceso",
"column.authorization": "Autorización",
"column.authorizations": "Autorizaciones",
"column.custom-locations": "Ubicaciones Personalizadas",
"column.destination": "Destino",
"column.details": "Detalles",
"column.email": "Correo Electrónico",
"column.event": "Evento",
"column.expires": "Expira",
"column.http-code": "Código HTTP",
"column.incoming-port": "Puerto de Entrada",
"column.name": "Nombre",
"column.protocol": "Protocolo",
"column.provider": "Proveedor",
"column.roles": "Roles",
"column.rules": "Reglas",
"column.satisfy": "Satisfacer",
"column.satisfy-all": "Todo",
"column.satisfy-any": "Cualquiera",
"column.scheme": "Esquema",
"column.source": "Origen",
"column.ssl": "SSL",
"column.status": "Estado",
"created-on": "Creado: {date}",
"dashboard": "Panel de Control",
"dead-host": "Host 404",
"dead-hosts": "Hosts 404",
"dead-hosts.count": "{count} {count, plural, one {Host 404} other {Hosts 404}}",
"disabled": "Deshabilitado",
"domain-names": "Nombres de Dominio",
"domain-names.max": "{count} nombres de dominio como máximo",
"domain-names.placeholder": "Comienza a escribir para añadir dominio...",
"domain-names.wildcards-not-permitted": "No se permiten comodines para este tipo",
"domain-names.wildcards-not-supported": "No se admiten comodines para esta CA",
"domains.force-ssl": "Forzar SSL",
"domains.hsts-enabled": "HSTS Habilitado",
"domains.hsts-subdomains": "HSTS en Subdominios",
"domains.http2-support": "Soporte HTTP/2",
"domains.use-dns": "Usar Desafío DNS",
"email-address": "Dirección de correo electrónico",
"empty-search": "No se encontraron resultados",
"empty-subtitle": "¿Por qué no creas uno?",
"enabled": "Habilitado",
"error.access.at-least-one": "Se requiere al menos una Autorización o una Regla de Acceso",
"error.access.duplicate-usernames": "Los nombres de usuario de autorización deben ser únicos",
"error.invalid-auth": "Correo electrónico o contraseña no válidos",
"error.invalid-domain": "Dominio no válido: {domain}",
"error.invalid-email": "Dirección de correo electrónico no válida",
"error.max-character-length": "La longitud máxima es {max} caracter{max, plural, one {} other {es}}",
"error.max-domains": "Demasiados dominios, el máximo es {max}",
"error.maximum": "El máximo es {max}",
"error.min-character-length": "La longitud mínima es {min} caracter{min, plural, one {} other {es}}",
"error.minimum": "El mínimo es {min}",
"error.passwords-must-match": "Las contraseñas deben coincidir",
"error.required": "Este campo es obligatorio",
"expires.on": "Expira: {date}",
"footer.github-fork": "Bifúrcame en Github",
"host.flags.block-exploits": "Bloquear Exploits Comunes",
"host.flags.cache-assets": "Cachear Recursos",
"host.flags.preserve-path": "Preservar Ruta",
"host.flags.protocols": "Protocolos",
"host.flags.websockets-upgrade": "Soporte de Websockets",
"host.forward-port": "Puerto de Reenvío",
"host.forward-scheme": "Esquema",
"hosts": "Hosts",
"http-only": "Solo HTTP",
"lets-encrypt": "Let's Encrypt",
"lets-encrypt-via-dns": "Let's Encrypt vía DNS",
"lets-encrypt-via-http": "Let's Encrypt vía HTTP",
"loading": "Cargando…",
"login.title": "Inicia sesión en tu cuenta",
"nginx-config.label": "Configuración Personalizada de Nginx",
"nginx-config.placeholder": "# ¡Introduce aquí tu configuración personalizada de Nginx bajo tu propio riesgo!",
"no-permission-error": "No tienes acceso para ver esto.",
"notfound.action": "Llévame al inicio",
"notfound.content": "Lo sentimos, pero la página que buscas no fue encontrada",
"notfound.title": "Ups… Has encontrado una página de error",
"notification.error": "Error",
"notification.object-deleted": "{object} ha sido eliminado",
"notification.object-disabled": "{object} ha sido deshabilitado",
"notification.object-enabled": "{object} ha sido habilitado",
"notification.object-renewed": "{object} ha sido renovado",
"notification.object-saved": "{object} ha sido guardado",
"notification.success": "Éxito",
"object.actions-title": "{object} #{id}",
"object.add": "Añadir {object}",
"object.delete": "Eliminar {object}",
"object.delete.content": "¿Estás seguro de que quieres eliminar este {object}?",
"object.edit": "Editar {object}",
"object.empty": "No hay {objects}",
"object.event.created": "{object} Creado",
"object.event.deleted": "{object} Eliminado",
"object.event.disabled": "{object} Deshabilitado",
"object.event.enabled": "{object} Habilitado",
"object.event.renewed": "{object} Renovado",
"object.event.updated": "{object} Actualizado",
"offline": "Desconectado",
"online": "Conectado",
"options": "Opciones",
"password": "Contraseña",
"password.generate": "Generar contraseña aleatoria",
"password.hide": "Ocultar Contraseña",
"password.show": "Mostrar Contraseña",
"permissions.hidden": "Oculto",
"permissions.manage": "Gestionar",
"permissions.view": "Solo Ver",
"permissions.visibility.all": "Todos los Elementos",
"permissions.visibility.title": "Visibilidad de Elementos",
"permissions.visibility.user": "Solo Elementos Creados",
"proxy-host": "Host Proxy",
"proxy-host.forward-host": "Nombre de Host / IP de Reenvío",
"proxy-hosts": "Hosts Proxy",
"proxy-hosts.count": "{count} {count, plural, one {Host Proxy} other {Hosts Proxy}}",
"public": "Público",
"redirection-host": "Host de Redirección",
"redirection-host.forward-domain": "Dominio de Reenvío",
"redirection-host.forward-http-code": "Código HTTP",
"redirection-hosts": "Hosts de Redirección",
"redirection-hosts.count": "{count} {count, plural, one {Host de Redirección} other {Hosts de Redirección}}",
"role.admin": "Administrador",
"role.standard-user": "Usuario Estándar",
"save": "Guardar",
"setting": "Configuración",
"settings": "Configuración",
"settings.default-site": "Sitio Predeterminado",
"settings.default-site.404": "Página 404",
"settings.default-site.444": "Sin Respuesta (444)",
"settings.default-site.congratulations": "Página de Felicitaciones",
"settings.default-site.description": "Qué mostrar cuando Nginx recibe un Host desconocido",
"settings.default-site.html": "HTML Personalizado",
"settings.default-site.html.placeholder": "<!-- Introduce aquí tu contenido HTML personalizado -->",
"settings.default-site.redirect": "Redirigir",
"setup.preamble": "Comienza creando tu cuenta de administrador.",
"setup.title": "¡Bienvenido!",
"sign-in": "Iniciar Sesión",
"ssl-certificate": "Certificado SSL",
"stream": "Stream",
"stream.forward-host": "Host de Reenvío",
"stream.incoming-port": "Puerto de Entrada",
"streams": "Streams",
"streams.count": "{count} {count, plural, one {Stream} other {Streams}}",
"streams.tcp": "TCP",
"streams.udp": "UDP",
"test": "Probar",
"user": "Usuario",
"user.change-password": "Cambiar Contraseña",
"user.confirm-password": "Confirmar Contraseña",
"user.current-password": "Contraseña Actual",
"user.edit-profile": "Editar Perfil",
"user.full-name": "Nombre Completo",
"user.login-as": "Iniciar sesión como {name}",
"user.logout": "Cerrar Sesión",
"user.new-password": "Nueva Contraseña",
"user.nickname": "Apodo",
"user.set-password": "Establecer Contraseña",
"user.set-permissions": "Establecer Permisos para {name}",
"user.switch-dark": "Cambiar a modo Oscuro",
"user.switch-light": "Cambiar a modo Claro",
"username": "Nombre de Usuario",
"users": "Usuarios"
}

View File

@@ -1,3 +1,5 @@
{
"locale-en-US": "English"
"locale-de-DE": "German",
"locale-en-US": "English",
"locale-es-ES": "Español"
}

View File

@@ -0,0 +1,7 @@
## Was ist eine Zugriffsliste?
Zugriffslisten bieten eine Blacklist oder Whitelist mit bestimmten Client-IP-Adressen sowie eine Authentifizierung für die Proxy-Hosts über die grundlegende HTTP-Authentifizierung.
Sie können mehrere Client-Regeln, Benutzernamen und Passwörter für eine einzelne Zugriffsliste konfigurieren und diese dann auf einen oder mehrere Proxy-Hosts anwenden.
Dies ist besonders nützlich für weitergeleitete Webdienste, die keine integrierten Authentifizierungsmechanismen haben, oder wenn Sie sich vor unbekannten Clients schützen möchten.

View File

@@ -0,0 +1,32 @@
## Hilfe zu Zertifikaten
### HTTP-Zertifikat
Ein HTTP-validiertes Zertifikat bedeutet, dass Let's Encrypt-Server
versuchen, Ihre Domains über HTTP (nicht HTTPS!) zu erreichen, und wenn dies erfolgreich ist,
stellen sie Ihr Zertifikat aus.
Für diese Methode müssen Sie einen _Proxy-Host_ für Ihre Domain(s) erstellen, der
über HTTP zugänglich ist und auf diese Nginx-Installation verweist. Nachdem ein Zertifikat
ausgestellt wurde, können Sie den _Proxy-Host_ so ändern, dass dieses Zertifikat auch für HTTPS-Verbindungen
verwendet wird. Der _Proxy-Host_ muss jedoch weiterhin für den HTTP-Zugriff konfiguriert sein,
damit das Zertifikat erneuert werden kann.
Dieser Prozess unterstützt keine Wildcard-Domains.
### DNS-Zertifikat
Für ein DNS-validiertes Zertifikat müssen Sie ein DNS-Provider-Plugin verwenden. Dieser DNS-
Provider wird verwendet, um temporäre Einträge auf Ihrer Domain zu erstellen. Anschließend fragt Let's
Encrypt diese Einträge ab, um sicherzustellen, dass Sie der Eigentümer sind. Bei Erfolg wird
Ihr Zertifikat ausgestellt.
Sie müssen vor der Beantragung dieser Art von Zertifikat keinen _Proxy-Host_ erstellen.
Sie müssen Ihren _Proxy-Host_ auch nicht für den HTTP-Zugriff konfigurieren.
Dieser Prozess unterstützt Wildcard-Domains.
### Benutzerdefiniertes Zertifikat
Verwenden Sie diese Option, um Ihr eigenes SSL-Zertifikat hochzuladen, das Ihnen von Ihrer eigenen
Zertifizierungsstelle bereitgestellt wurde.

View File

@@ -0,0 +1,10 @@
## Was ist ein 404-Host?
Ein 404-Host ist ein Host-Setup, das eine 404-Seite anzeigt.
Dies kann nützlich sein, wenn Ihre Domain in Suchmaschinen gelistet ist und Sie
eine ansprechendere Fehlerseite bereitstellen oder den Suchindexern ausdrücklich mitteilen möchten, dass
die Domain-Seiten nicht mehr existieren.
Ein weiterer Vorteil dieses Hosts besteht darin, dass Sie die Protokolle für Zugriffe darauf verfolgen und
die Verweise anzeigen können.

View File

@@ -0,0 +1,7 @@
## Was ist ein Proxy-Host?
Ein Proxy-Host ist der eingehende Endpunkt für einen Webdienst, den Sie weiterleiten möchten.
Er bietet optionale SSL-Terminierung für Ihren Dienst, der möglicherweise keine integrierte SSL-Unterstützung hat.
Proxy-Hosts sind die häufigste Verwendung für den Nginx Proxy Manager.

View File

@@ -0,0 +1,7 @@
## Was ist ein Redirection Host?
Ein Redirection Host leitet Anfragen von der eingehenden Domain weiter und leitet den
Besucher zu einer anderen Domain weiter.
Der häufigste Grund für die Verwendung dieses Host-Typs ist, wenn Ihre Website die
Domain wechselt, aber Sie noch Suchmaschinen- oder Referrer-Links haben, die auf die alte Domain verweisen.

View File

@@ -0,0 +1,6 @@
## Was ist ein Stream?
Ein Stream ist eine relativ neue Funktion von Nginx, die dazu dient, TCP/UDP-Datenverkehr
direkt an einen anderen Computer im Netzwerk weiterzuleiten.
Wenn Sie Spielserver, FTP- oder SSH-Server betreiben, kann dies sehr nützlich sein.

View File

@@ -0,0 +1,6 @@
export * as AccessLists from "./AccessLists.md";
export * as Certificates from "./Certificates.md";
export * as DeadHosts from "./DeadHosts.md";
export * as ProxyHosts from "./ProxyHosts.md";
export * as RedirectionHosts from "./RedirectionHosts.md";
export * as Streams from "./Streams.md";

View File

@@ -0,0 +1,7 @@
## ¿Qué es una Lista de Acceso?
Las Listas de Acceso proporcionan una lista negra o blanca de direcciones IP de cliente específicas junto con autenticación para los Hosts Proxy a través de Autenticación HTTP Básica.
Puede configurar múltiples reglas de cliente, nombres de usuario y contraseñas para una única Lista de Acceso y luego aplicarla a uno o más _Hosts Proxy_.
Esto es más útil para servicios web reenviados que no tienen mecanismos de autenticación integrados o cuando desea protegerse de clientes desconocidos.

View File

@@ -0,0 +1,32 @@
## Ayuda de Certificados
### Certificado HTTP
Un certificado validado por HTTP significa que los servidores de Let's Encrypt
intentarán acceder a tus dominios a través de HTTP (¡no HTTPS!) y, si tienen éxito,
emitirán tu certificado.
Para este método, deberás tener un _Host Proxy_ creado para tu(s) dominio(s) que
sea accesible por HTTP y que apunte a esta instalación de Nginx. Después de que se
haya emitido un certificado, puedes modificar el _Host Proxy_ para que también use
este certificado para conexiones HTTPS. Sin embargo, el _Host Proxy_ seguirá
necesitando estar configurado para acceso HTTP para que el certificado se renueve.
Este proceso _no_ admite dominios comodín.
### Certificado DNS
Un certificado validado por DNS requiere que uses un complemento de Proveedor de DNS.
Este Proveedor de DNS se usará para crear registros temporales en tu dominio y luego
Let's Encrypt consultará esos registros para asegurarse de que eres el propietario y,
si tiene éxito, emitirá tu certificado.
No necesitas tener un _Host Proxy_ creado antes de solicitar este tipo de certificado.
Tampoco necesitas tener tu _Host Proxy_ configurado para acceso HTTP.
Este proceso __ admite dominios comodín.
### Certificado Personalizado
Usa esta opción para cargar tu propio Certificado SSL, proporcionado por tu propia
Autoridad de Certificación.

View File

@@ -0,0 +1,10 @@
## ¿Qué es un Host 404?
Un Host 404 es simplemente una configuración de host que muestra una página 404.
Esto puede ser útil cuando tu dominio está listado en los motores de búsqueda y deseas
proporcionar una página de error más agradable o específicamente para indicar a los indexadores de búsqueda que
las páginas del dominio ya no existen.
Otro beneficio de tener este host es rastrear los registros de visitas a él y
ver los referentes.

View File

@@ -0,0 +1,7 @@
## ¿Qué es un Host Proxy?
Un Host Proxy es el punto de entrada para un servicio web que deseas reenviar.
Proporciona terminación SSL opcional para tu servicio que podría no tener soporte SSL integrado.
Los Hosts Proxy son el uso más común del Nginx Proxy Manager.

View File

@@ -0,0 +1,7 @@
## ¿Qué es un Host de Redirección?
Un Host de Redirección redirigirá las solicitudes del dominio entrante e impulsará al
visitante a otro dominio.
La razón más común para usar este tipo de host es cuando tu sitio web cambia de
dominios pero aún tienes enlaces de motores de búsqueda o referencias apuntando al dominio anterior.

View File

@@ -0,0 +1,6 @@
## ¿Qué es un Stream?
Una característica relativamente nueva para Nginx, un Stream servirá para reenviar tráfico TCP/UDP
directamente a otra computadora en la red.
Si estás ejecutando servidores de juegos, FTP o servidores SSH esto puede ser muy útil.

View File

@@ -0,0 +1,6 @@
export * as AccessLists from "./AccessLists.md";
export * as Certificates from "./Certificates.md";
export * as DeadHosts from "./DeadHosts.md";
export * as ProxyHosts from "./ProxyHosts.md";
export * as RedirectionHosts from "./RedirectionHosts.md";
export * as Streams from "./Streams.md";

View File

@@ -1,17 +1,24 @@
// import * as de from "./de/index";
// import * as fa from "./fa/index";
import * as en from "./en/index";
import * as de from "./de/index";
const items: any = { en };
const items: any = { en, de };
const fallbackLang = "en";
export const getHelpFile = (lang: string, section: string): string => {
if (typeof items[lang] !== "undefined" && typeof items[lang][section] !== "undefined") {
if (
typeof items[lang] !== "undefined" &&
typeof items[lang][section] !== "undefined"
) {
return items[lang][section].default;
}
// Fallback to English
if (typeof items[fallbackLang] !== "undefined" && typeof items[fallbackLang][section] !== "undefined") {
if (
typeof items[fallbackLang] !== "undefined" &&
typeof items[fallbackLang][section] !== "undefined"
) {
return items[fallbackLang][section].default;
}
throw new Error(`Cannot load help doc for ${lang}-${section}`);

View File

@@ -0,0 +1,641 @@
{
"access-list": {
"defaultMessage": "Zugriffsliste"
},
"access-list.access-count": {
"defaultMessage": "{count} {count, plural, one {Regel} other {Regeln}}"
},
"access-list.auth-count": {
"defaultMessage": "{count} {count, plural, one {User} other {Users}}"
},
"access-list.help-rules-last": {
"defaultMessage": "Wenn mindestens eine Regel vorhanden ist, wird diese Regel zum Ablehnen aller Anfragen als letzte hinzugefügt."
},
"access-list.help.rules-order": {
"defaultMessage": "Beachten Sie, dass die Anweisungen „Erlauben“ und „Verbieten“ in der Reihenfolge ihrer Definition angewendet werden."
},
"access-list.pass-auth": {
"defaultMessage": "Authentifizierung an Upstream weiterleiten"
},
"access-list.public": {
"defaultMessage": "Öffentlich"
},
"access-list.public.subtitle": {
"defaultMessage": "Keine Authentifizierung erforderlich"
},
"access-list.satisfy-any": {
"defaultMessage": "Satisfy Any"
},
"access-list.subtitle": {
"defaultMessage": "{users} {users, plural, one {User} other {Users}}, {rules} {rules, plural, one {Regel} other {Regeln}} - Erstellt: {date}"
},
"access-lists": {
"defaultMessage": "Zugrifflisten"
},
"action.add": {
"defaultMessage": "Hinzufügen"
},
"action.add-location": {
"defaultMessage": "Pfad Hinzufügen"
},
"action.close": {
"defaultMessage": "Schließen"
},
"action.delete": {
"defaultMessage": "Löschen"
},
"action.disable": {
"defaultMessage": "Deaktivieren"
},
"action.download": {
"defaultMessage": "Herunterladen"
},
"action.edit": {
"defaultMessage": "Bearbeiten"
},
"action.enable": {
"defaultMessage": "Aktivieren"
},
"action.permissions": {
"defaultMessage": "Berechtigungen"
},
"action.renew": {
"defaultMessage": "Erneuert"
},
"action.view-details": {
"defaultMessage": "Details"
},
"auditlogs": {
"defaultMessage": "Protokoll"
},
"cancel": {
"defaultMessage": "Abbrechen"
},
"certificate": {
"defaultMessage": "Zertifikat"
},
"certificate.custom-certificate": {
"defaultMessage": "Zertifikat"
},
"certificate.custom-certificate-key": {
"defaultMessage": "Privater Schlüssel"
},
"certificate.custom-intermediate": {
"defaultMessage": "Zwischen Zertifikat"
},
"certificate.in-use": {
"defaultMessage": "In Benutzung"
},
"certificate.none.subtitle": {
"defaultMessage": "Kein Zertifikat zugewiesen"
},
"certificate.none.subtitle.for-http": {
"defaultMessage": "Dieser Host verwendet kein HTTPS."
},
"certificate.none.title": {
"defaultMessage": "Kein"
},
"certificate.not-in-use": {
"defaultMessage": "Nicht in Benutzung"
},
"certificates": {
"defaultMessage": "Zertifikate"
},
"certificates.custom": {
"defaultMessage": "Benutzerdefiniertes Zertifikat"
},
"certificates.custom.warning": {
"defaultMessage": "Mit einem Passwort geschützte Schlüsseldateien werden nicht unterstützt."
},
"certificates.dns.credentials": {
"defaultMessage": "Inhalt der Anmeldedaten-Datei"
},
"certificates.dns.credentials-note": {
"defaultMessage": "Dieses Plugin erfordert eine Konfigurationsdatei, die einen API-Token oder andere Anmeldedaten für Ihren Anbieter enthält."
},
"certificates.dns.credentials-warning": {
"defaultMessage": "Diese Daten werden als Klartext in der Datenbank und in einer Datei gespeichert!"
},
"certificates.dns.propagation-seconds": {
"defaultMessage": "Wartzeit in Sekunden"
},
"certificates.dns.propagation-seconds-note": {
"defaultMessage": "Leer lassen um die Standardwartezeit des Plugins zu nutzen"
},
"certificates.dns.provider": {
"defaultMessage": "DNS Provider"
},
"certificates.dns.warning": {
"defaultMessage": "Dieser Abschnitt erfordert einige Kenntnisse über Certbot und seine DNS-Plugins. Bitte konsultieren Sie die jeweilige Plugin-Dokumentation."
},
"certificates.http.reachability-404": {
"defaultMessage": "Unter dieser Domain wurde ein Server gefunden, aber es scheint sich nicht um Nginx Proxy Manager zu handeln. Bitte stellen Sie sicher, dass Ihre Domain auf die IP-Adresse verweist, unter der Ihre NPM-Instanz ausgeführt wird."
},
"certificates.http.reachability-failed-to-check": {
"defaultMessage": "Die Erreichbarkeit konnte aufgrund eines Kommunikationsfehlers mit site24x7.com nicht überprüft werden."
},
"certificates.http.reachability-not-resolved": {
"defaultMessage": "Unter dieser Domain ist kein Server verfügbar. Bitte stellen Sie sicher, dass Ihre Domain existiert und auf die IP-Adresse verweist, unter der Ihre NPM-Instanz läuft, und dass gegebenenfalls Port 80 in Ihrem Router weitergeleitet wird."
},
"certificates.http.reachability-ok": {
"defaultMessage": "Ihr Server ist erreichbar und die Erstellung von Zertifikaten sollte möglich sein."
},
"certificates.http.reachability-other": {
"defaultMessage": "Unter dieser Domain wurde ein Server gefunden, der jedoch einen unerwarteten Statuscode {code} zurückgegeben hat. Handelt es sich um den NPM-Server? Bitte stellen Sie sicher, dass Ihre Domain auf die IP-Adresse verweist, unter der Ihre NPM-Instanz ausgeführt wird."
},
"certificates.http.reachability-wrong-data": {
"defaultMessage": "Unter dieser Domain wurde ein Server gefunden, der jedoch unerwartete Daten zurückgegeben hat. Handelt es sich um den NPM-Server? Bitte stellen Sie sicher, dass Ihre Domain auf die IP-Adresse verweist, unter der Ihre NPM-Instanz ausgeführt wird."
},
"certificates.http.test-results": {
"defaultMessage": "Test Ergeniss"
},
"certificates.http.warning": {
"defaultMessage": "Diese Domänen müssen bereits so konfiguriert sein, dass sie auf diese Installation verweisen."
},
"certificates.request.subtitle": {
"defaultMessage": "Über Let's Encrypt"
},
"certificates.request.title": {
"defaultMessage": "Anfordern eines neuen Zertifikates"
},
"column.access": {
"defaultMessage": "Zugriff"
},
"column.authorization": {
"defaultMessage": "Genehmigung"
},
"column.authorizations": {
"defaultMessage": "Genehmigungen"
},
"column.custom-locations": {
"defaultMessage": "Benutzerdefinierte Pfad"
},
"column.destination": {
"defaultMessage": "Ziel"
},
"column.details": {
"defaultMessage": "Details"
},
"column.email": {
"defaultMessage": "Email"
},
"column.event": {
"defaultMessage": "Ereignis"
},
"column.expires": {
"defaultMessage": "Verfällt am"
},
"column.http-code": {
"defaultMessage": "HTTP Code"
},
"column.incoming-port": {
"defaultMessage": "Eingehender Port"
},
"column.name": {
"defaultMessage": "Name"
},
"column.protocol": {
"defaultMessage": "Protokoll"
},
"column.provider": {
"defaultMessage": "Provider"
},
"column.roles": {
"defaultMessage": "Rollen"
},
"column.rules": {
"defaultMessage": "Regeln"
},
"column.satisfy": {
"defaultMessage": "Satisfy"
},
"column.satisfy-all": {
"defaultMessage": "Alle"
},
"column.satisfy-any": {
"defaultMessage": "Jeder"
},
"column.scheme": {
"defaultMessage": "Schema"
},
"column.source": {
"defaultMessage": "Quelle"
},
"column.ssl": {
"defaultMessage": "SSL"
},
"column.status": {
"defaultMessage": "Status"
},
"created-on": {
"defaultMessage": "Erstelldatum: {date}"
},
"dashboard": {
"defaultMessage": "Dashboard"
},
"dead-host": {
"defaultMessage": "404 Host"
},
"dead-hosts": {
"defaultMessage": "404 Hosts"
},
"dead-hosts.count": {
"defaultMessage": "{count} {count, plural, one {404 Host} other {404 Hosts}}"
},
"disabled": {
"defaultMessage": "Deaktiviert"
},
"domain-names": {
"defaultMessage": "Domain Names"
},
"domain-names.max": {
"defaultMessage": "{count} Maximale Anzahl von Domainnamen"
},
"domain-names.placeholder": {
"defaultMessage": "Eintragen der Domain..."
},
"domain-names.wildcards-not-permitted": {
"defaultMessage": "Wildcards sind für diesen Typ nicht zulässig."
},
"domain-names.wildcards-not-supported": {
"defaultMessage": "Wildcards werden für diese Zertifizierungsstelle nicht unterstützt."
},
"domains.force-ssl": {
"defaultMessage": "Erzwinge SSL"
},
"domains.hsts-enabled": {
"defaultMessage": "HSTS aktiviert"
},
"domains.hsts-subdomains": {
"defaultMessage": "HSTS Sub-domains"
},
"domains.http2-support": {
"defaultMessage": "HTTP/2 Support"
},
"domains.use-dns": {
"defaultMessage": "Nutze DNS Challenge"
},
"email-address": {
"defaultMessage": "Email Addresse"
},
"empty-search": {
"defaultMessage": "Keine Ergebnisse gefunden"
},
"empty-subtitle": {
"defaultMessage": "Warum erstellen Sie nicht eine?"
},
"enabled": {
"defaultMessage": "aktiviert"
},
"error.access.at-least-one": {
"defaultMessage": "Entweder eine Genehmigung oder eine Zugriffsregel ist erforderlich."
},
"error.access.duplicate-usernames": {
"defaultMessage": "Autorisierung Benutzernamen müssen eindeutig sein"
},
"error.invalid-auth": {
"defaultMessage": "Ungültige E-Mail-Adresse oder Passwort"
},
"error.invalid-domain": {
"defaultMessage": "Ungültige Domain: {domain}"
},
"error.invalid-email": {
"defaultMessage": "Ungültige E-Mail-Adresse"
},
"error.max-character-length": {
"defaultMessage": "Die maximale Länge beträgt {max} Zeichen{max, plural, one {} other {s}}"
},
"error.max-domains": {
"defaultMessage": "Zu viele Domains, maximal sind {max}"
},
"error.maximum": {
"defaultMessage": "Maximum ist {max}"
},
"error.min-character-length": {
"defaultMessage": "Die minimale Länge beträgt {min} Zeichen{min, plural, one {} other {s}}"
},
"error.minimum": {
"defaultMessage": "Minimum ist {min}"
},
"error.passwords-must-match": {
"defaultMessage": "Passwörter müssen übereinstimmen"
},
"error.required": {
"defaultMessage": "Dies ist erforderlich."
},
"expires.on": {
"defaultMessage": "Ablauf am: {date}"
},
"footer.github-fork": {
"defaultMessage": "Fork me on Github"
},
"host.flags.block-exploits": {
"defaultMessage": "Gängige Exploits blockieren"
},
"host.flags.cache-assets": {
"defaultMessage": "Cache Assets"
},
"host.flags.preserve-path": {
"defaultMessage": "Pfad beibehalten"
},
"host.flags.protocols": {
"defaultMessage": "Protokole"
},
"host.flags.websockets-upgrade": {
"defaultMessage": "Websockets Support"
},
"host.forward-port": {
"defaultMessage": "Forward Port"
},
"host.forward-scheme": {
"defaultMessage": "Schema"
},
"hosts": {
"defaultMessage": "Hosts"
},
"http-only": {
"defaultMessage": "HTTP Only"
},
"lets-encrypt": {
"defaultMessage": "Let's Encrypt"
},
"lets-encrypt-via-dns": {
"defaultMessage": "Let's Encrypt via DNS"
},
"lets-encrypt-via-http": {
"defaultMessage": "Let's Encrypt via HTTP"
},
"loading": {
"defaultMessage": "Laden…"
},
"login.title": {
"defaultMessage": "Account Login"
},
"nginx-config.label": {
"defaultMessage": "Benutzerdefinierte Nginx Konfiguration"
},
"nginx-config.placeholder": {
"defaultMessage": "# Geben Sie hier Ihre benutzerdefinierte Nginx-Konfiguration auf eigene Gefahr ein!"
},
"no-permission-error": {
"defaultMessage": "Sie haben keinen Zugriff, um dies anzuzeigen."
},
"notfound.action": {
"defaultMessage": "Take me home"
},
"notfound.content": {
"defaultMessage": "We are sorry but the page you are looking for was not found"
},
"notfound.title": {
"defaultMessage": "Oops… You just found an error page"
},
"notification.error": {
"defaultMessage": "Error"
},
"notification.object-deleted": {
"defaultMessage": "{object} wurde gelöscht"
},
"notification.object-disabled": {
"defaultMessage": "{object} wurde deaktiviert"
},
"notification.object-enabled": {
"defaultMessage": "{object} wurde aktiviert"
},
"notification.object-renewed": {
"defaultMessage": "{object} wurde erneuert"
},
"notification.object-saved": {
"defaultMessage": "{object} wurde gespeichert"
},
"notification.success": {
"defaultMessage": "Erfolgreich"
},
"object.actions-title": {
"defaultMessage": "{object} #{id}"
},
"object.add": {
"defaultMessage": "{object} hinzufügen"
},
"object.delete": {
"defaultMessage": "{object} löschen"
},
"object.delete.content": {
"defaultMessage": "Bist du dir sicher das du diese(n) {object} löschen möchtest?"
},
"object.edit": {
"defaultMessage": "{object} bearbeiten"
},
"object.empty": {
"defaultMessage": "Keine {objects} vorhanden"
},
"object.event.created": {
"defaultMessage": "{object} erstellt"
},
"object.event.deleted": {
"defaultMessage": "{object} gelöscht"
},
"object.event.disabled": {
"defaultMessage": "{object} deaktiviert"
},
"object.event.enabled": {
"defaultMessage": "{object} aktiviert"
},
"object.event.renewed": {
"defaultMessage": "{object} erneuert"
},
"object.event.updated": {
"defaultMessage": "{object} aktualisiert"
},
"offline": {
"defaultMessage": "Offline"
},
"online": {
"defaultMessage": "Online"
},
"options": {
"defaultMessage": "Optionen"
},
"password": {
"defaultMessage": "Passwort"
},
"password.generate": {
"defaultMessage": "Zufälliges Passwort generieren"
},
"password.hide": {
"defaultMessage": "Passwort verstecken"
},
"password.show": {
"defaultMessage": "Passwort anzeigen"
},
"permissions.hidden": {
"defaultMessage": "Versteckt"
},
"permissions.manage": {
"defaultMessage": "Verwalten"
},
"permissions.view": {
"defaultMessage": "Nur anzeigen"
},
"permissions.visibility.all": {
"defaultMessage": "Alle Elemente"
},
"permissions.visibility.title": {
"defaultMessage": "Objekt Sichtbarkeit"
},
"permissions.visibility.user": {
"defaultMessage": "Nur erstellte Elemente"
},
"proxy-host": {
"defaultMessage": "Proxy Host"
},
"proxy-host.forward-host": {
"defaultMessage": "Forward Hostname / IP"
},
"proxy-hosts": {
"defaultMessage": "Proxy Hosts"
},
"proxy-hosts.count": {
"defaultMessage": "{count} {count, plural, one {Proxy Host} other {Proxy Hosts}}"
},
"public": {
"defaultMessage": "Öffentlich"
},
"redirection-host": {
"defaultMessage": "Redirection Host"
},
"redirection-host.forward-domain": {
"defaultMessage": "Forward Domain"
},
"redirection-host.forward-http-code": {
"defaultMessage": "HTTP Code"
},
"redirection-hosts": {
"defaultMessage": "Redirection Hosts"
},
"redirection-hosts.count": {
"defaultMessage": "{count} {count, plural, one {Redirection Host} other {Redirection Hosts}}"
},
"role.admin": {
"defaultMessage": "Administrator"
},
"role.standard-user": {
"defaultMessage": "Standard User"
},
"save": {
"defaultMessage": "Speichern"
},
"setting": {
"defaultMessage": "Einstellung"
},
"settings": {
"defaultMessage": "Einstellungen"
},
"settings.default-site": {
"defaultMessage": "Standard Seite"
},
"settings.default-site.404": {
"defaultMessage": "404 Page"
},
"settings.default-site.444": {
"defaultMessage": "No Response (444)"
},
"settings.default-site.congratulations": {
"defaultMessage": "Willkommensseite"
},
"settings.default-site.description": {
"defaultMessage": "Was angezeigt wird, wenn der Nginx eine unbekannte Webseitenanfrage bekommt"
},
"settings.default-site.html": {
"defaultMessage": "Benutzerdefinierte HTML"
},
"settings.default-site.html.placeholder": {
"defaultMessage": "<!-- Geben Sie hier Ihren benutzerdefinierten HTML-Inhalt ein. -->"
},
"settings.default-site.redirect": {
"defaultMessage": "Weiterleitung"
},
"setup.preamble": {
"defaultMessage": "Beginnen Sie mit der Erstellung Ihres Administratorkontos."
},
"setup.title": {
"defaultMessage": "Willkommen!"
},
"sign-in": {
"defaultMessage": "Login"
},
"ssl-certificate": {
"defaultMessage": "SSL Zertifikate"
},
"stream": {
"defaultMessage": "Stream"
},
"stream.forward-host": {
"defaultMessage": "Forward Host"
},
"stream.incoming-port": {
"defaultMessage": "Incoming Port"
},
"streams": {
"defaultMessage": "Streams"
},
"streams.count": {
"defaultMessage": "{count} {count, plural, one {Stream} other {Streams}}"
},
"streams.tcp": {
"defaultMessage": "TCP"
},
"streams.udp": {
"defaultMessage": "UDP"
},
"test": {
"defaultMessage": "Test"
},
"user": {
"defaultMessage": "User"
},
"user.change-password": {
"defaultMessage": "Passwort ändern"
},
"user.confirm-password": {
"defaultMessage": "Passwort wiederholen"
},
"user.current-password": {
"defaultMessage": "Aktuelles Passwort"
},
"user.edit-profile": {
"defaultMessage": "Profil bearbeiten"
},
"user.full-name": {
"defaultMessage": "Name"
},
"user.login-as": {
"defaultMessage": "Einloggen als {name}"
},
"user.logout": {
"defaultMessage": "Ausloggen"
},
"user.new-password": {
"defaultMessage": "Neues Password"
},
"user.nickname": {
"defaultMessage": "Nickname"
},
"user.set-password": {
"defaultMessage": "Passwort setzen"
},
"user.set-permissions": {
"defaultMessage": "Berechtigungen für {name} setzen"
},
"user.switch-dark": {
"defaultMessage": "Zum Dark Mode wechseln"
},
"user.switch-light": {
"defaultMessage": "Zum Light Mode wechslen"
},
"username": {
"defaultMessage": "Benutzername"
},
"users": {
"defaultMessage": "Benutzer"
}
}

View File

@@ -98,6 +98,9 @@
"certificate.not-in-use": {
"defaultMessage": "Not Used"
},
"certificate.renew": {
"defaultMessage": "Renew Certificate"
},
"certificates": {
"defaultMessage": "Certificates"
},
@@ -506,6 +509,9 @@
"redirection-host.forward-domain": {
"defaultMessage": "Forward Domain"
},
"redirection-host.forward-http-code": {
"defaultMessage": "HTTP Code"
},
"redirection-hosts": {
"defaultMessage": "Redirection Hosts"
},

View File

@@ -0,0 +1,641 @@
{
"redirection-host.forward-domain": {
"defaultMessage": "Dominio de Reenvío"
},
"redirection-hosts.count": {
"defaultMessage": "{count} {count, plural, one {Host de Redirección} other {Hosts de Redirección}}"
},
"notification.success": {
"defaultMessage": "Éxito"
},
"setup.preamble": {
"defaultMessage": "Comienza creando tu cuenta de administrador."
},
"column.event": {
"defaultMessage": "Evento"
},
"auditlogs": {
"defaultMessage": "Registros de Auditoría"
},
"enabled": {
"defaultMessage": "Habilitado"
},
"column.source": {
"defaultMessage": "Origen"
},
"column.details": {
"defaultMessage": "Detalles"
},
"column.ssl": {
"defaultMessage": "SSL"
},
"column.destination": {
"defaultMessage": "Destino"
},
"settings.default-site.html": {
"defaultMessage": "HTML Personalizado"
},
"proxy-host": {
"defaultMessage": "Host Proxy"
},
"certificates.dns.propagation-seconds-note": {
"defaultMessage": "Dejar vacío para usar el valor predeterminado del plugin. Número de segundos a esperar para la propagación DNS."
},
"settings.default-site.redirect": {
"defaultMessage": "Redirigir"
},
"hosts": {
"defaultMessage": "Hosts"
},
"users": {
"defaultMessage": "Usuarios"
},
"permissions.visibility.all": {
"defaultMessage": "Todos los Elementos"
},
"password.generate": {
"defaultMessage": "Generar contraseña aleatoria"
},
"host.flags.preserve-path": {
"defaultMessage": "Preservar Ruta"
},
"password.show": {
"defaultMessage": "Mostrar Contraseña"
},
"permissions.visibility.title": {
"defaultMessage": "Visibilidad de Elementos"
},
"http-only": {
"defaultMessage": "Solo HTTP"
},
"empty-search": {
"defaultMessage": "No se encontraron resultados"
},
"column.provider": {
"defaultMessage": "Proveedor"
},
"user": {
"defaultMessage": "Usuario"
},
"domain-names.placeholder": {
"defaultMessage": "Comienza a escribir para añadir dominio..."
},
"certificates": {
"defaultMessage": "Certificados"
},
"host.flags.websockets-upgrade": {
"defaultMessage": "Soporte de Websockets"
},
"dead-host": {
"defaultMessage": "Host 404"
},
"column.email": {
"defaultMessage": "Correo Electrónico"
},
"settings.default-site": {
"defaultMessage": "Sitio Predeterminado"
},
"action.add-location": {
"defaultMessage": "Añadir Ubicación"
},
"settings.default-site.404": {
"defaultMessage": "Página 404"
},
"user.change-password": {
"defaultMessage": "Cambiar Contraseña"
},
"settings": {
"defaultMessage": "Configuración"
},
"certificate.custom-certificate": {
"defaultMessage": "Certificado"
},
"streams": {
"defaultMessage": "Streams"
},
"streams.count": {
"defaultMessage": "{count} {count, plural, one {Stream} other {Streams}}"
},
"error.passwords-must-match": {
"defaultMessage": "Las contraseñas deben coincidir"
},
"ssl-certificate": {
"defaultMessage": "Certificado SSL"
},
"certificate.none.subtitle.for-http": {
"defaultMessage": "Este host no usará HTTPS"
},
"user.confirm-password": {
"defaultMessage": "Confirmar Contraseña"
},
"notfound.title": {
"defaultMessage": "Ups… Has encontrado una página de error"
},
"object.edit": {
"defaultMessage": "Editar {object}"
},
"error.max-domains": {
"defaultMessage": "Demasiados dominios, el máximo es {max}"
},
"column.scheme": {
"defaultMessage": "Esquema"
},
"permissions.hidden": {
"defaultMessage": "Oculto"
},
"certificate.in-use": {
"defaultMessage": "En Uso"
},
"column.satisfy": {
"defaultMessage": "Satisfacer"
},
"column.protocol": {
"defaultMessage": "Protocolo"
},
"action.add": {
"defaultMessage": "Añadir"
},
"column.custom-locations": {
"defaultMessage": "Ubicaciones Personalizadas"
},
"certificate.renew": {
"defaultMessage": "Renovar Certificado"
},
"offline": {
"defaultMessage": "Desconectado"
},
"permissions.visibility.user": {
"defaultMessage": "Solo Elementos Creados"
},
"certificates.dns.credentials": {
"defaultMessage": "Contenido del Archivo de Credenciales"
},
"proxy-hosts": {
"defaultMessage": "Hosts Proxy"
},
"user.edit-profile": {
"defaultMessage": "Editar Perfil"
},
"user.switch-dark": {
"defaultMessage": "Cambiar a modo Oscuro"
},
"certificates.custom.warning": {
"defaultMessage": "No se admiten archivos de claves protegidos con contraseña."
},
"access-list.help.rules-order": {
"defaultMessage": "Ten en cuenta que las directivas de permitir y denegar se aplicarán en el orden en que estén definidas."
},
"save": {
"defaultMessage": "Guardar"
},
"certificates.http.reachability-404": {
"defaultMessage": "Se encontró un servidor en este dominio pero no parece ser Nginx Proxy Manager. Asegúrate de que tu dominio apunte a la IP donde se está ejecutando tu instancia de NPM."
},
"certificates.http.reachability-wrong-data": {
"defaultMessage": "Se encontró un servidor en este dominio pero devolvió datos inesperados. ¿Es el servidor NPM? Asegúrate de que tu dominio apunte a la IP donde se está ejecutando tu instancia de NPM."
},
"settings.default-site.congratulations": {
"defaultMessage": "Página de Felicitaciones"
},
"streams.tcp": {
"defaultMessage": "TCP"
},
"user.current-password": {
"defaultMessage": "Contraseña Actual"
},
"access-list": {
"defaultMessage": "Lista de Acceso"
},
"column.access": {
"defaultMessage": "Acceso"
},
"action.delete": {
"defaultMessage": "Eliminar"
},
"certificates.custom": {
"defaultMessage": "Certificado Personalizado"
},
"column.authorizations": {
"defaultMessage": "Autorizaciones"
},
"host.flags.block-exploits": {
"defaultMessage": "Bloquear Exploits Comunes"
},
"object.event.created": {
"defaultMessage": "{object} Creado"
},
"options": {
"defaultMessage": "Opciones"
},
"redirection-host": {
"defaultMessage": "Host de Redirección"
},
"certificate.none.title": {
"defaultMessage": "Ninguno"
},
"created-on": {
"defaultMessage": "Creado: {date}"
},
"user.login-as": {
"defaultMessage": "Iniciar sesión como {name}"
},
"certificates.request.subtitle": {
"defaultMessage": "con Let's Encrypt"
},
"proxy-hosts.count": {
"defaultMessage": "{count} {count, plural, one {Host Proxy} other {Hosts Proxy}}"
},
"host.forward-port": {
"defaultMessage": "Puerto de Reenvío"
},
"notfound.content": {
"defaultMessage": "Lo sentimos, pero la página que buscas no fue encontrada"
},
"access-list.public.subtitle": {
"defaultMessage": "No se requiere autenticación básica"
},
"proxy-host.forward-host": {
"defaultMessage": "Nombre de Host / IP de Reenvío"
},
"settings.default-site.444": {
"defaultMessage": "Sin Respuesta (444)"
},
"settings.default-site.description": {
"defaultMessage": "Qué mostrar cuando Nginx recibe un Host desconocido"
},
"column.expires": {
"defaultMessage": "Expira"
},
"object.delete": {
"defaultMessage": "Eliminar {object}"
},
"notification.object-deleted": {
"defaultMessage": "{object} ha sido eliminado"
},
"cancel": {
"defaultMessage": "Cancelar"
},
"user.set-password": {
"defaultMessage": "Establecer Contraseña"
},
"nginx-config.placeholder": {
"defaultMessage": "# ¡Introduce aquí tu configuración personalizada de Nginx bajo tu propio riesgo!"
},
"access-list.auth-count": {
"defaultMessage": "{count} {count, plural, one {Usuario} other {Usuarios}}"
},
"password.hide": {
"defaultMessage": "Ocultar Contraseña"
},
"settings.default-site.html.placeholder": {
"defaultMessage": "<!-- Introduce aquí tu contenido HTML personalizado -->"
},
"notification.object-renewed": {
"defaultMessage": "{object} ha sido renovado"
},
"object.event.deleted": {
"defaultMessage": "{object} Eliminado"
},
"error.invalid-auth": {
"defaultMessage": "Correo electrónico o contraseña no válidos"
},
"domains.force-ssl": {
"defaultMessage": "Forzar SSL"
},
"domains.hsts-subdomains": {
"defaultMessage": "HSTS en Subdominios"
},
"host.flags.protocols": {
"defaultMessage": "Protocolos"
},
"action.download": {
"defaultMessage": "Descargar"
},
"certificates.dns.warning": {
"defaultMessage": "Esta sección requiere algunos conocimientos sobre Certbot y sus plugins DNS. Consulta la documentación de los plugins respectivos."
},
"certificates.request.title": {
"defaultMessage": "Solicitar un nuevo Certificado"
},
"password": {
"defaultMessage": "Contraseña"
},
"notification.object-saved": {
"defaultMessage": "{object} ha sido guardado"
},
"setting": {
"defaultMessage": "Configuración"
},
"object.empty": {
"defaultMessage": "No hay {objects}"
},
"lets-encrypt-via-http": {
"defaultMessage": "Let's Encrypt vía HTTP"
},
"domains.hsts-enabled": {
"defaultMessage": "HSTS Habilitado"
},
"error.maximum": {
"defaultMessage": "El máximo es {max}"
},
"user.switch-light": {
"defaultMessage": "Cambiar a modo Claro"
},
"action.edit": {
"defaultMessage": "Editar"
},
"access-list.subtitle": {
"defaultMessage": "{users} {users, plural, one {Usuario} other {Usuarios}}, {rules} {rules, plural, one {Regla} other {Reglas}} - Creado: {date}"
},
"action.enable": {
"defaultMessage": "Habilitar"
},
"error.access.duplicate-usernames": {
"defaultMessage": "Los nombres de usuario de autorización deben ser únicos"
},
"notfound.action": {
"defaultMessage": "Llévame al inicio"
},
"notification.object-disabled": {
"defaultMessage": "{object} ha sido deshabilitado"
},
"user.logout": {
"defaultMessage": "Cerrar Sesión"
},
"stream.incoming-port": {
"defaultMessage": "Puerto de Entrada"
},
"certificates.http.reachability-ok": {
"defaultMessage": "Tu servidor es accesible y debería ser posible crear certificados."
},
"access-list.help-rules-last": {
"defaultMessage": "Cuando exista al menos 1 regla, esta regla de denegar todo se añadirá al final"
},
"action.view-details": {
"defaultMessage": "Ver Detalles"
},
"notification.object-enabled": {
"defaultMessage": "{object} ha sido habilitado"
},
"object.actions-title": {
"defaultMessage": "{object} #{id}"
},
"username": {
"defaultMessage": "Nombre de Usuario"
},
"user.nickname": {
"defaultMessage": "Apodo"
},
"column.name": {
"defaultMessage": "Nombre"
},
"column.roles": {
"defaultMessage": "Roles"
},
"footer.github-fork": {
"defaultMessage": "Bifúrcame en Github"
},
"object.event.updated": {
"defaultMessage": "{object} Actualizado"
},
"certificates.dns.credentials-warning": {
"defaultMessage": "¡Estos datos se almacenarán como texto plano en la base de datos y en un archivo!"
},
"access-list.satisfy-any": {
"defaultMessage": "Satisfacer Cualquiera"
},
"dead-hosts.count": {
"defaultMessage": "{count} {count, plural, one {Host 404} other {Hosts 404}}"
},
"certificates.dns.propagation-seconds": {
"defaultMessage": "Segundos de Propagación"
},
"error.min-character-length": {
"defaultMessage": "La longitud mínima es {min} caracter{min, plural, one {} other {es}}"
},
"domain-names.max": {
"defaultMessage": "{count} nombres de dominio como máximo"
},
"object.delete.content": {
"defaultMessage": "¿Estás seguro de que quieres eliminar este {object}?"
},
"column.rules": {
"defaultMessage": "Reglas"
},
"error.required": {
"defaultMessage": "Este campo es obligatorio"
},
"certificate": {
"defaultMessage": "Certificado"
},
"public": {
"defaultMessage": "Público"
},
"domains.use-dns": {
"defaultMessage": "Usar Desafío DNS"
},
"error.max-character-length": {
"defaultMessage": "La longitud máxima es {max} caracter{max, plural, one {} other {es}}"
},
"permissions.view": {
"defaultMessage": "Solo Ver"
},
"expires.on": {
"defaultMessage": "Expira: {date}"
},
"setup.title": {
"defaultMessage": "¡Bienvenido!"
},
"certificates.http.reachability-failed-to-check": {
"defaultMessage": "No se pudo verificar la accesibilidad debido a un error de comunicación con site24x7.com."
},
"certificates.http.warning": {
"defaultMessage": "Estos dominios ya deben estar configurados para apuntar a esta instalación."
},
"certificate.custom-certificate-key": {
"defaultMessage": "Clave del Certificado"
},
"empty-subtitle": {
"defaultMessage": "¿Por qué no creas uno?"
},
"dead-hosts": {
"defaultMessage": "Hosts 404"
},
"lets-encrypt": {
"defaultMessage": "Let's Encrypt"
},
"email-address": {
"defaultMessage": "Dirección de correo electrónico"
},
"domain-names": {
"defaultMessage": "Nombres de Dominio"
},
"user.full-name": {
"defaultMessage": "Nombre Completo"
},
"column.incoming-port": {
"defaultMessage": "Puerto de Entrada"
},
"column.http-code": {
"defaultMessage": "Acceso"
},
"certificate.not-in-use": {
"defaultMessage": "Sin Usar"
},
"action.disable": {
"defaultMessage": "Deshabilitar"
},
"redirection-hosts": {
"defaultMessage": "Hosts de Redirección"
},
"streams.udp": {
"defaultMessage": "UDP"
},
"role.admin": {
"defaultMessage": "Administrador"
},
"nginx-config.label": {
"defaultMessage": "Configuración Personalizada de Nginx"
},
"disabled": {
"defaultMessage": "Deshabilitado"
},
"domain-names.wildcards-not-permitted": {
"defaultMessage": "No se permiten comodines para este tipo"
},
"online": {
"defaultMessage": "Conectado"
},
"access-list.public": {
"defaultMessage": "Accesible Públicamente"
},
"action.renew": {
"defaultMessage": "Renovar"
},
"object.add": {
"defaultMessage": "Añadir {object}"
},
"certificates.dns.credentials-note": {
"defaultMessage": "Este plugin requiere un archivo de configuración que contenga un token de API u otras credenciales para tu proveedor"
},
"error.invalid-email": {
"defaultMessage": "Dirección de correo electrónico no válida"
},
"notification.error": {
"defaultMessage": "Error"
},
"error.invalid-domain": {
"defaultMessage": "Dominio no válido: {domain}"
},
"user.set-permissions": {
"defaultMessage": "Establecer Permisos para {name}"
},
"certificate.none.subtitle": {
"defaultMessage": "Sin certificado asignado"
},
"object.event.enabled": {
"defaultMessage": "{object} Habilitado"
},
"login.title": {
"defaultMessage": "Inicia sesión en tu cuenta"
},
"access-list.access-count": {
"defaultMessage": "{count} {count, plural, one {Regla} other {Reglas}}"
},
"certificate.custom-intermediate": {
"defaultMessage": "Certificado Intermedio"
},
"error.access.at-least-one": {
"defaultMessage": "Se requiere al menos una Autorización o una Regla de Acceso"
},
"stream": {
"defaultMessage": "Stream"
},
"action.close": {
"defaultMessage": "Cerrar"
},
"certificates.http.test-results": {
"defaultMessage": "Resultados de la Prueba"
},
"certificates.dns.provider": {
"defaultMessage": "Proveedor DNS"
},
"object.event.renewed": {
"defaultMessage": "{object} Renovado"
},
"lets-encrypt-via-dns": {
"defaultMessage": "Let's Encrypt vía DNS"
},
"certificates.http.reachability-not-resolved": {
"defaultMessage": "No hay ningún servidor disponible en este dominio. Asegúrate de que tu dominio existe y apunta a la IP donde se está ejecutando tu instancia de NPM y, si es necesario, que el puerto 80 esté redirigido en tu router."
},
"no-permission-error": {
"defaultMessage": "No tienes acceso para ver esto."
},
"column.satisfy-all": {
"defaultMessage": "Todo"
},
"host.forward-scheme": {
"defaultMessage": "Esquema"
},
"domains.http2-support": {
"defaultMessage": "Soporte HTTP/2"
},
"action.permissions": {
"defaultMessage": "Permisos"
},
"loading": {
"defaultMessage": "Cargando…"
},
"object.event.disabled": {
"defaultMessage": "{object} Deshabilitado"
},
"dashboard": {
"defaultMessage": "Panel de Control"
},
"sign-in": {
"defaultMessage": "Iniciar Sesión"
},
"permissions.manage": {
"defaultMessage": "Gestionar"
},
"user.new-password": {
"defaultMessage": "Nueva Contraseña"
},
"stream.forward-host": {
"defaultMessage": "Host de Reenvío"
},
"test": {
"defaultMessage": "Probar"
},
"column.authorization": {
"defaultMessage": "Autorización"
},
"column.satisfy-any": {
"defaultMessage": "Cualquiera"
},
"host.flags.cache-assets": {
"defaultMessage": "Cachear Recursos"
},
"access-lists": {
"defaultMessage": "Listas de Acceso"
},
"role.standard-user": {
"defaultMessage": "Usuario Estándar"
},
"certificates.http.reachability-other": {
"defaultMessage": "Se encontró un servidor en este dominio pero devolvió un código de estado inesperado {code}. ¿Es el servidor NPM? Asegúrate de que tu dominio apunte a la IP donde se está ejecutando tu instancia de NPM."
},
"column.status": {
"defaultMessage": "Estado"
},
"error.minimum": {
"defaultMessage": "El mínimo es {min}"
},
"domain-names.wildcards-not-supported": {
"defaultMessage": "No se admiten comodines para esta CA"
},
"access-list.pass-auth": {
"defaultMessage": "Pasar Autenticación al Upstream"
}
}

View File

@@ -1,5 +1,11 @@
{
"locale-en-US": {
"defaultMessage": "English"
},
"locale-es-ES": {
"defaultMessage": "Español"
},
"locale-de-DE": {
"defaultMessage": "German"
}
}

View File

@@ -162,7 +162,7 @@ const RedirectionHostModal = EasyModal.create(({ id, visible, remove }: Props) =
required
{...field}
>
<option value="$scheme">Auto</option>
<option value="auto">Auto</option>
<option value="http">http</option>
<option value="https">https</option>
</select>
@@ -212,6 +212,36 @@ const RedirectionHostModal = EasyModal.create(({ id, visible, remove }: Props) =
</Field>
</div>
</div>
<Field name="forwardHttpCode">
{({ field, form }: any) => (
<div className="mb-3">
<label className="form-label" htmlFor="forwardHttpCode">
<T id="redirection-host.forward-http-code" />
</label>
<select
id="forwardHttpCode"
className={`form-control ${form.errors.forwardHttpCode && form.touched.forwardHttpCode ? "is-invalid" : ""}`}
required
{...field}
>
<option value="300">300 Multiple choices</option>
<option value="301">301 Moved permanently</option>
<option value="302">302 Moved temporarily</option>
<option value="303">303 See other</option>
<option value="307">307 Temporary redirect</option>
<option value="308">308 Permanent redirect</option>
</select>
{form.errors.forwardHttpCode ? (
<div className="invalid-feedback">
{form.errors.forwardHttpCode &&
form.touched.forwardHttpCode
? form.errors.forwardHttpCode
: null}
</div>
) : null}
</div>
)}
</Field>
<div className="my-3">
<h4 className="py-2">
<T id="options" />

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

@@ -44,7 +44,7 @@ const validateEmail = () => {
if (!value.length) {
return intl.formatMessage({ id: "error.required" });
}
if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(value)) {
if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+$/i.test(value)) {
return intl.formatMessage({ id: "error.invalid-email" });
}
};

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" }),

View File

@@ -31,7 +31,7 @@ export default function Table({ data, isFetching, onDelete, onRenew, onDownload,
id: "owner",
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",

View File

@@ -31,7 +31,7 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
id: "owner",
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",

View File

@@ -32,7 +32,7 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
id: "owner",
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",

View File

@@ -31,7 +31,7 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog
id: "owner",
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",

View File

@@ -31,7 +31,7 @@ export default function Table({ data, isFetching, isFiltered, onEdit, onDelete,
id: "owner",
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",

View File

@@ -16,7 +16,7 @@ if hash docker 2>/dev/null; then
-e NODE_OPTIONS=--openssl-legacy-provider \
-v "$(pwd)/frontend:/app/frontend" \
-w /app/frontend "${DOCKER_IMAGE}" \
sh -c "yarn install && yarn lint && yarn build && chown -R $(id -u):$(id -g) /app/frontend"
sh -c "yarn install && yarn lint && yarn vitest run && yarn build && chown -R $(id -u):$(id -g) /app/frontend"
echo -e "${BLUE} ${GREEN}Building Frontend Complete${RESET}"
else