fix(deploy): sync static assets and nginx CSS route

Copy .next/static and public into standalone after build; serve
/_next/static from nginx; verify CSS in install.sh.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Андрей Бобырев
2026-05-24 23:19:03 +03:00
parent 35c677eede
commit 1196bc3d11
5 changed files with 66 additions and 5 deletions

View File

@@ -13,13 +13,15 @@ docker compose exec web npx prisma db push
curl -fsSL https://raw.githubusercontent.com/andrey271192/Domain_web/main/install.sh | sudo bash curl -fsSL https://raw.githubusercontent.com/andrey271192/Domain_web/main/install.sh | sudo bash
``` ```
Install path: `/opt/domain_web` Install path: `/opt/domain-scanner` (lite VPS install via `install.sh`)
Service: Docker Compose (`web`, `postgres`, `redis`) Service: `domain-scanner.service``npm start` from repo root (not standalone `server.js` alone)
Nginx site: `domain-scanner` on port 80 → app `:3000` Nginx site: `domain-scanner` on port 80 → app `:3000`, with `/_next/static/` served from disk
**CSS / static assets:** After `next build`, run `scripts/sync-standalone-assets.sh` (also via `postbuild`) so `.next/static` and `public` exist under `.next/standalone` for Docker. Host deploy must keep `${INSTALL_DIR}/.next/static` — never run `node .next/standalone/server.js` without that copy.
## Environment on server ## Environment on server
Edit `/opt/domain_web/.env`: Edit `/opt/domain-scanner/.env`:
- `NEXTAUTH_SECRET` — auto-generated on first install - `NEXTAUTH_SECRET` — auto-generated on first install
- `NEXTAUTH_URL` / `NEXT_PUBLIC_APP_URL` — public IP or domain - `NEXTAUTH_URL` / `NEXT_PUBLIC_APP_URL` — public IP or domain
@@ -39,5 +41,5 @@ Place certbot in front of nginx or terminate TLS at a reverse proxy.
## Upgrades ## Upgrades
```bash ```bash
cd /opt/domain_web && git pull && docker compose up -d --build cd /opt/domain-scanner && git pull && npm ci && npm run build && systemctl restart domain-scanner nginx
``` ```

View File

@@ -109,6 +109,7 @@ build_app() {
npx prisma generate npx prisma generate
npx prisma db push npx prisma db push
npm run build npm run build
bash scripts/sync-standalone-assets.sh "${INSTALL_DIR}"
# Keep production deps at repo root; `next start` is more reliable than # Keep production deps at repo root; `next start` is more reliable than
# standalone on small VPS (avoids incomplete traced node_modules). # standalone on small VPS (avoids incomplete traced node_modules).
npm ci --omit=dev npm ci --omit=dev
@@ -154,6 +155,13 @@ server {
client_max_body_size 32m; client_max_body_size 32m;
location /_next/static/ {
alias ${INSTALL_DIR}/.next/static/;
expires 1y;
add_header Cache-Control "public, max-age=31536000, immutable";
access_log off;
}
location / { location / {
proxy_pass http://127.0.0.1:${APP_PORT}; proxy_pass http://127.0.0.1:${APP_PORT};
proxy_http_version 1.1; proxy_http_version 1.1;
@@ -188,6 +196,18 @@ verify() {
echo "Health check failed on port ${APP_PORT}" >&2 echo "Health check failed on port ${APP_PORT}" >&2
exit 1 exit 1
fi fi
local css
css="$(find "${INSTALL_DIR}/.next/static/css" -name '*.css' -type f 2>/dev/null | head -1 || true)"
if [[ -z "${css}" ]]; then
echo "ERROR: no CSS build output in .next/static/css" >&2
exit 1
fi
local css_url="/_next/static/css/$(basename "${css}")"
if ! curl -fsSI "http://127.0.0.1${css_url}" | grep -qi 'content-type:.*css'; then
echo "ERROR: CSS not served (${css_url})" >&2
exit 1
fi
log "CSS OK: ${css_url}"
local title local title
title="$(curl -fsS "http://127.0.0.1/" | grep -o '<title>[^<]*</title>' | head -1 || true)" title="$(curl -fsS "http://127.0.0.1/" | grep -o '<title>[^<]*</title>' | head -1 || true)"
if echo "${title}" | grep -qi geoexport; then if echo "${title}" | grep -qi geoexport; then

View File

@@ -5,6 +5,7 @@
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
"build": "prisma generate && next build", "build": "prisma generate && next build",
"postbuild": "bash scripts/sync-standalone-assets.sh",
"start": "next start", "start": "next start",
"lint": "eslint", "lint": "eslint",
"postinstall": "prisma generate", "postinstall": "prisma generate",

View File

@@ -50,6 +50,7 @@ build_app() {
npm ci npm ci
npx prisma generate npx prisma generate
npm run build npm run build
bash scripts/sync-standalone-assets.sh "${INSTALL_DIR}"
npx prisma db push --accept-data-loss npx prisma db push --accept-data-loss
} }
@@ -81,6 +82,14 @@ server {
listen 80 default_server; listen 80 default_server;
listen [::]:80 default_server; listen [::]:80 default_server;
server_name _; server_name _;
location /_next/static/ {
alias ${INSTALL_DIR}/.next/static/;
expires 1y;
add_header Cache-Control "public, max-age=31536000, immutable";
access_log off;
}
location / { location / {
proxy_pass http://127.0.0.1:${NODE_PORT}; proxy_pass http://127.0.0.1:${NODE_PORT};
proxy_http_version 1.1; proxy_http_version 1.1;

View File

@@ -0,0 +1,29 @@
#!/usr/bin/env bash
# Copy Next.js static assets into standalone output (required for `node server.js`).
set -euo pipefail
ROOT="${1:-$(cd "$(dirname "$0")/.." && pwd)}"
STANDALONE="${ROOT}/.next/standalone"
STATIC_SRC="${ROOT}/.next/static"
PUBLIC_SRC="${ROOT}/public"
if [[ ! -d "${STANDALONE}" ]]; then
echo "[sync-standalone-assets] No .next/standalone — skipping (non-standalone build?)" >&2
exit 0
fi
if [[ ! -d "${STATIC_SRC}" ]]; then
echo "[sync-standalone-assets] ERROR: missing ${STATIC_SRC} — run npm run build first" >&2
exit 1
fi
mkdir -p "${STANDALONE}/.next"
rm -rf "${STANDALONE}/.next/static"
cp -a "${STATIC_SRC}" "${STANDALONE}/.next/static"
if [[ -d "${PUBLIC_SRC}" ]]; then
rm -rf "${STANDALONE}/public"
cp -a "${PUBLIC_SRC}" "${STANDALONE}/public"
fi
echo "[sync-standalone-assets] OK: .next/static + public → ${STANDALONE}"