Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.git
.github
.idea

**/node_modules
**/.pnpm-store
**/.cache
**/dist
**/dist-ssr

.tmp-s6-overlay-root
.tmp-s6-overlay.tar
5 changes: 5 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Files copied into the Linux image and scripts executed by the host must use LF endings.
docker/rootfs/** text eol=lf
*.sh text eol=lf
scripts/** text eol=lf
docker/scripts/** text eol=lf
113 changes: 113 additions & 0 deletions .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
name: Build and push Docker image

on:
workflow_dispatch:
inputs:
version:
description: Docker image version tag (1.0 or later)
required: true
default: '1.0'
type: string

concurrency:
group: dockerhub-nginx-proxy-manager-${{ inputs.version }}
cancel-in-progress: false

permissions:
contents: read

env:
IMAGE_NAME: docker.io/moailaozi/nginx-proxy-manager

jobs:
quality:
name: Semantic config quality gate
runs-on: ubuntu-24.04

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: yarn
cache-dependency-path: |
backend/yarn.lock
frontend/yarn.lock

- name: Install Yarn
run: npm install --global yarn@1.22.22

- name: Validate backend schemas, catalog, lint, and tests
working-directory: backend
run: |
yarn install --frozen-lockfile --network-timeout 600000
yarn validate-schema
yarn check:proxy-directives
yarn lint
yarn test

- name: Validate frontend catalog coverage, types, locales, tests, and build
working-directory: frontend
run: |
yarn install --frozen-lockfile --network-timeout 600000
yarn lint
yarn locale-compile
yarn vitest run --no-color
yarn build

build-and-push:
needs: quality
runs-on: ubuntu-24.04

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Validate image version
env:
VERSION: ${{ inputs.version }}
shell: bash
run: |
if [[ ! "$VERSION" =~ ^[1-9][0-9]*\.[0-9]+(\.[0-9]+)?$ ]]; then
echo "Version must be numeric and at least 1.0 (for example: 1.0 or 1.0.1)."
exit 1
fi

- name: Set build metadata
id: metadata
shell: bash
run: |
echo "commit=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
echo "date=$(date --utc '+%Y-%m-%d %H:%M:%S UTC')" >> "$GITHUB_OUTPUT"

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver: docker-container

- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}

- name: Build, runtime-validate, and push Docker image
uses: docker/build-push-action@v6
with:
context: .
file: docker/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ env.IMAGE_NAME }}:${{ inputs.version }}
build-args: |
BUILD_VERSION=${{ inputs.version }}
BUILD_COMMIT=${{ steps.metadata.outputs.commit }}
BUILD_DATE=${{ steps.metadata.outputs.date }}
cache-from: type=gha
cache-to: type=gha,mode=max
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ test/node_modules
docker/dev/dnsrouter-config.json.tmp
docker/dev/resolv.conf
.claude

.tmp-s6-overlay-root/
.tmp-s6-overlay.tar
/plans/
3 changes: 3 additions & 0 deletions backend/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
auto-install-peers=true
registry=https://registry.npmmirror.com
strict-ssl=false
21 changes: 21 additions & 0 deletions backend/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import cors from "./lib/express/cors.js";
import jwt from "./lib/express/jwt.js";
import { debug, express as logger } from "./logger.js";
import mainRoutes from "./routes/main.js";
import nginxDeploymentCoordinator from "./internal/nginx-deployment-coordinator.js";

/**
* App
Expand Down Expand Up @@ -55,6 +56,18 @@ app.use((_, res, next) => {
});

app.use(jwt());

// Reconcile any interrupted atomic deployment before API routes can trigger a
// new configuration change. Recovery only touches journals left by the
// coordinator; an empty deployment directory is a no-op.
try {
const recoveredDeployments = await nginxDeploymentCoordinator.recover();
if (recoveredDeployments.length) logger.warn(`Recovered ${recoveredDeployments.length} interrupted nginx deployment(s)`);
} catch (error) {
logger.error(`Unable to recover nginx deployments: ${error.message}`);
throw error;
}

app.use("/", mainRoutes);

// production error handler
Expand All @@ -67,6 +80,14 @@ app.use((err, req, res, _) => {
},
};

if (typeof err.error_code !== "undefined") {
payload.error.error_code = err.error_code;
}

if (typeof err.details !== "undefined") {
payload.error.details = err.details;
}

if (typeof err.message_i18n !== "undefined") {
payload.error.message_i18n = err.message_i18n;
}
Expand Down
20 changes: 20 additions & 0 deletions backend/config/nginx-runtime-capability.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"schema_version": 1,
"profile_version": "npm-nginx-runtime-v1",
"runtime_family": "openresty",
"nginx_version": "1.29.2.5",
"architectures": [
"amd64",
"arm64"
],
"ipv6": true,
"modules": [
"http_core",
"http_headers",
"http_proxy"
],
"image": "nginxproxymanager/nginx-full:certbot-node",
"image_digest": "sha256:367def9b117c31c7c8f126feda930f6cd0068cf2511b86e54c2b5433799065c2",
"notes": "The recorded digest is the validated amd64 image identity. Multi-architecture release builds use the upstream tag and must pass exact OpenResty version, module, architecture, and generated-config validation on each target before publication.",
"validated_on": "2026-08-14"
}
Loading