mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2025-12-06 16:26:50 +00:00
Compare commits
8 Commits
36e040157f
...
v2.13.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64c5a863f8 | ||
|
|
cd94863850 | ||
|
|
3c252db46f | ||
|
|
8eba31913f | ||
|
|
e4e3415120 | ||
|
|
a03bb7ebce | ||
|
|
51e25d1a40 | ||
|
|
e88d55f1d2 |
285
Jenkinsfile
vendored
285
Jenkinsfile
vendored
@@ -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()
|
||||
}
|
||||
@@ -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.1-green.svg?style=for-the-badge">
|
||||
<img src="https://img.shields.io/badge/version-2.13.2-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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
});
|
||||
|
||||
@@ -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"];
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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"];
|
||||
|
||||
|
||||
@@ -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"];
|
||||
|
||||
|
||||
@@ -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"];
|
||||
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { Model } from "objection";
|
||||
import db from "../db.js";
|
||||
|
||||
Model.knex(db);
|
||||
Model.knex(db());
|
||||
|
||||
class Setting extends Model {
|
||||
$beforeInsert () {
|
||||
|
||||
@@ -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"];
|
||||
|
||||
|
||||
@@ -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"];
|
||||
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"pg": "^8.16.3",
|
||||
"proxy-agent": "^6.5.0",
|
||||
"signale": "1.4.0",
|
||||
"sqlite3": "5.1.6",
|
||||
"sqlite3": "^5.1.7",
|
||||
"temp-write": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796"
|
||||
integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==
|
||||
|
||||
"@mapbox/node-pre-gyp@^1.0.0", "@mapbox/node-pre-gyp@^1.0.11":
|
||||
"@mapbox/node-pre-gyp@^1.0.11":
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa"
|
||||
integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==
|
||||
@@ -368,6 +368,13 @@ binary-extensions@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522"
|
||||
integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==
|
||||
|
||||
bindings@^1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
|
||||
integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
|
||||
dependencies:
|
||||
file-uri-to-path "1.0.0"
|
||||
|
||||
bl@^4.0.3:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
|
||||
@@ -534,6 +541,11 @@ chokidar@^3.5.2:
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
chownr@^1.1.1:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
|
||||
integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
|
||||
|
||||
chownr@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
|
||||
@@ -727,6 +739,18 @@ decamelize@^1.2.0:
|
||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||
integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==
|
||||
|
||||
decompress-response@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc"
|
||||
integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==
|
||||
dependencies:
|
||||
mimic-response "^3.1.0"
|
||||
|
||||
deep-extend@^0.6.0:
|
||||
version "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"
|
||||
@@ -809,7 +833,7 @@ encoding@^0.1.12:
|
||||
dependencies:
|
||||
iconv-lite "^0.6.2"
|
||||
|
||||
end-of-stream@^1.4.1:
|
||||
end-of-stream@^1.1.0, end-of-stream@^1.4.1:
|
||||
version "1.4.5"
|
||||
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.5.tgz#7344d711dea40e0b74abc2ed49778743ccedb08c"
|
||||
integrity sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==
|
||||
@@ -901,6 +925,11 @@ etag@~1.8.1:
|
||||
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
||||
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
|
||||
|
||||
expand-template@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
|
||||
integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==
|
||||
|
||||
express-fileupload@^1.5.2:
|
||||
version "1.5.2"
|
||||
resolved "https://registry.yarnpkg.com/express-fileupload/-/express-fileupload-1.5.2.tgz#4da70ba6f2ffd4c736eab0776445865a9dbd9bfa"
|
||||
@@ -962,6 +991,11 @@ figures@^2.0.0:
|
||||
dependencies:
|
||||
escape-string-regexp "^1.0.5"
|
||||
|
||||
file-uri-to-path@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
|
||||
integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
|
||||
|
||||
fill-range@^7.1.1:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
|
||||
@@ -1118,6 +1152,11 @@ getopts@2.3.0:
|
||||
resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.3.0.tgz#71e5593284807e03e2427449d4f6712a268666f4"
|
||||
integrity sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==
|
||||
|
||||
github-from-package@0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce"
|
||||
integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==
|
||||
|
||||
glob-parent@~5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
|
||||
@@ -1304,6 +1343,11 @@ inherits@2.0.3:
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||
integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==
|
||||
|
||||
ini@~1.3.0:
|
||||
version "1.3.8"
|
||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
|
||||
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
|
||||
|
||||
interpret@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9"
|
||||
@@ -1647,6 +1691,11 @@ mime@1.6.0:
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
|
||||
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
||||
|
||||
mimic-response@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9"
|
||||
integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==
|
||||
|
||||
minimatch@^3.1.1, minimatch@^3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
||||
@@ -1661,6 +1710,11 @@ minimatch@^5.1.0:
|
||||
dependencies:
|
||||
brace-expansion "^2.0.1"
|
||||
|
||||
minimist@^1.2.0, minimist@^1.2.3:
|
||||
version "1.2.8"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
|
||||
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
|
||||
|
||||
minipass-collect@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617"
|
||||
@@ -1720,6 +1774,11 @@ minizlib@^2.0.0, minizlib@^2.1.1:
|
||||
minipass "^3.0.0"
|
||||
yallist "^4.0.0"
|
||||
|
||||
mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3:
|
||||
version "0.5.3"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
|
||||
integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
|
||||
|
||||
mkdirp@^1.0.3, mkdirp@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
||||
@@ -1767,6 +1826,11 @@ named-placeholders@^1.1.3:
|
||||
dependencies:
|
||||
lru-cache "^7.14.1"
|
||||
|
||||
napi-build-utils@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-2.0.0.tgz#13c22c0187fcfccce1461844136372a47ddc027e"
|
||||
integrity sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==
|
||||
|
||||
negotiator@0.6.3:
|
||||
version "0.6.3"
|
||||
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
|
||||
@@ -1782,16 +1846,23 @@ netmask@^2.0.2:
|
||||
resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7"
|
||||
integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==
|
||||
|
||||
node-addon-api@^4.2.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f"
|
||||
integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==
|
||||
node-abi@^3.3.0:
|
||||
version "3.78.0"
|
||||
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.78.0.tgz#fd0ecbd0aa89857b98da06bd3909194abb0821ba"
|
||||
integrity sha512-E2wEyrgX/CqvicaQYU3Ze1PFGjc4QYPGsjUrlYkqAE0WjHEZwgOsGMPMzkMse4LjJbDmaEuDX3CM036j5K2DSQ==
|
||||
dependencies:
|
||||
semver "^7.3.5"
|
||||
|
||||
node-addon-api@^5.0.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762"
|
||||
integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==
|
||||
|
||||
node-addon-api@^7.0.0:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558"
|
||||
integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==
|
||||
|
||||
node-fetch@^2.6.7:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
|
||||
@@ -1900,7 +1971,7 @@ on-headers@~1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.1.0.tgz#59da4f91c45f5f989c6e4bcedc5a3b0aed70ff65"
|
||||
integrity sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==
|
||||
|
||||
once@^1.3.0, once@^1.4.0:
|
||||
once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
|
||||
@@ -2121,6 +2192,24 @@ postgres-interval@^1.1.0:
|
||||
dependencies:
|
||||
xtend "^4.0.0"
|
||||
|
||||
prebuild-install@^7.1.1:
|
||||
version "7.1.3"
|
||||
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.3.tgz#d630abad2b147443f20a212917beae68b8092eec"
|
||||
integrity sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==
|
||||
dependencies:
|
||||
detect-libc "^2.0.0"
|
||||
expand-template "^2.0.3"
|
||||
github-from-package "0.0.0"
|
||||
minimist "^1.2.3"
|
||||
mkdirp-classic "^0.5.3"
|
||||
napi-build-utils "^2.0.0"
|
||||
node-abi "^3.3.0"
|
||||
pump "^3.0.0"
|
||||
rc "^1.2.7"
|
||||
simple-get "^4.0.0"
|
||||
tar-fs "^2.0.0"
|
||||
tunnel-agent "^0.6.0"
|
||||
|
||||
process-nextick-args@~2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
|
||||
@@ -2176,6 +2265,14 @@ pstree.remy@^1.1.8:
|
||||
resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a"
|
||||
integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==
|
||||
|
||||
pump@^3.0.0:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.3.tgz#151d979f1a29668dc0025ec589a455b53282268d"
|
||||
integrity sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==
|
||||
dependencies:
|
||||
end-of-stream "^1.1.0"
|
||||
once "^1.3.1"
|
||||
|
||||
qs@6.13.0:
|
||||
version "6.13.0"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
|
||||
@@ -2203,6 +2300,16 @@ raw-body@2.5.2:
|
||||
iconv-lite "0.4.24"
|
||||
unpipe "1.0.0"
|
||||
|
||||
rc@^1.2.7:
|
||||
version "1.2.8"
|
||||
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
|
||||
integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
|
||||
dependencies:
|
||||
deep-extend "^0.6.0"
|
||||
ini "~1.3.0"
|
||||
minimist "^1.2.0"
|
||||
strip-json-comments "~2.0.1"
|
||||
|
||||
readable-stream@^2.0.0, readable-stream@^2.0.5:
|
||||
version "2.3.8"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b"
|
||||
@@ -2420,6 +2527,20 @@ signale@1.4.0:
|
||||
figures "^2.0.0"
|
||||
pkg-conf "^2.1.0"
|
||||
|
||||
simple-concat@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f"
|
||||
integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==
|
||||
|
||||
simple-get@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543"
|
||||
integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==
|
||||
dependencies:
|
||||
decompress-response "^6.0.0"
|
||||
once "^1.3.1"
|
||||
simple-concat "^1.0.0"
|
||||
|
||||
simple-update-notifier@^1.0.7:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz#67694c121de354af592b347cdba798463ed49c82"
|
||||
@@ -2468,13 +2589,14 @@ split2@^4.1.0:
|
||||
resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4"
|
||||
integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==
|
||||
|
||||
sqlite3@5.1.6:
|
||||
version "5.1.6"
|
||||
resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-5.1.6.tgz#1d4fbc90fe4fbd51e952e0a90fd8f6c2b9098e97"
|
||||
integrity sha512-olYkWoKFVNSSSQNvxVUfjiVbz3YtBwTJj+mfV5zpHmqW3sELx2Cf4QCdirMelhM5Zh+KDVaKgQHqCxrqiWHybw==
|
||||
sqlite3@^5.1.7:
|
||||
version "5.1.7"
|
||||
resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-5.1.7.tgz#59ca1053c1ab38647396586edad019b1551041b7"
|
||||
integrity sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==
|
||||
dependencies:
|
||||
"@mapbox/node-pre-gyp" "^1.0.0"
|
||||
node-addon-api "^4.2.0"
|
||||
bindings "^1.5.0"
|
||||
node-addon-api "^7.0.0"
|
||||
prebuild-install "^7.1.1"
|
||||
tar "^6.1.11"
|
||||
optionalDependencies:
|
||||
node-gyp "8.x"
|
||||
@@ -2536,6 +2658,11 @@ strip-bom@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
|
||||
integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==
|
||||
|
||||
strip-json-comments@~2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
||||
integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==
|
||||
|
||||
supports-color@^5.3.0, supports-color@^5.5.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||
@@ -2555,7 +2682,17 @@ supports-preserve-symlinks-flag@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||
|
||||
tar-stream@^2.2.0:
|
||||
tar-fs@^2.0.0:
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.4.tgz#800824dbf4ef06ded9afea4acafe71c67c76b930"
|
||||
integrity sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==
|
||||
dependencies:
|
||||
chownr "^1.1.1"
|
||||
mkdirp-classic "^0.5.2"
|
||||
pump "^3.0.0"
|
||||
tar-stream "^2.1.4"
|
||||
|
||||
tar-stream@^2.1.4, tar-stream@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
|
||||
integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
|
||||
@@ -2631,6 +2768,13 @@ tslib@^2.0.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"
|
||||
integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==
|
||||
dependencies:
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
type-is@~1.6.18:
|
||||
version "1.6.18"
|
||||
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
|
||||
|
||||
@@ -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" />,
|
||||
|
||||
@@ -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} — ${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" />,
|
||||
})) || [];
|
||||
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 }} />
|
||||
— <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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 };
|
||||
@@ -30,13 +30,20 @@ const getLocale = (short = false) => {
|
||||
if (short) {
|
||||
return loc.slice(0, 2);
|
||||
}
|
||||
// finally, fallback
|
||||
if (!loc) {
|
||||
loc = "en";
|
||||
}
|
||||
return loc;
|
||||
};
|
||||
|
||||
const cache = createIntlCache();
|
||||
|
||||
const initialMessages = loadMessages(getLocale());
|
||||
let intl = createIntl({ locale: getLocale(), messages: initialMessages }, cache);
|
||||
let intl = createIntl(
|
||||
{ locale: getLocale(), messages: initialMessages },
|
||||
cache,
|
||||
);
|
||||
|
||||
const changeLocale = (locale: string): void => {
|
||||
const messages = loadMessages(locale);
|
||||
@@ -76,4 +83,12 @@ const T = ({
|
||||
);
|
||||
};
|
||||
|
||||
export { localeOptions, getFlagCodeForLocale, getLocale, createIntl, changeLocale, intl, T };
|
||||
export {
|
||||
localeOptions,
|
||||
getFlagCodeForLocale,
|
||||
getLocale,
|
||||
createIntl,
|
||||
changeLocale,
|
||||
intl,
|
||||
T,
|
||||
};
|
||||
|
||||
74
frontend/src/locale/Utils.test.tsx
Normal file
74
frontend/src/locale/Utils.test.tsx
Normal 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");
|
||||
});
|
||||
});
|
||||
42
frontend/src/locale/Utils.ts
Normal file
42
frontend/src/locale/Utils.ts
Normal 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 };
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from "./DateTimeFormat";
|
||||
export * from "./IntlProvider";
|
||||
export * from "./Utils";
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user