404 Not Found

404 Not Found


nginx

Docker Self-Hosting & Deployment

Self-hosted deployments give you complete control over your application’s runtime environment—when compliance, cost, or network requirements prevent you from using a cloud platform, Docker is your most reliable partner.

1. What You'll Learn


2. A True Story of a DevOps Engineer

(1) Pain Point: The client requires that data not be transferred outside the country

Charlie works at a SaaS company that serves financial institutions in the Middle East. Their TaskFlow product needs to be deployed in a local data center in Saudi Arabia—the client requires that all user data be physically stored within Saudi Arabia.

However, Vercel does not have a data center in Saudi Arabia. The problem Charlie faces:

Issue Impact
Data Sovereignty Compliance Saudi Arabia’s Financial Regulatory Requirements Prohibit Data Transfer Abroad
Network Latency Latency when accessing from European servers > 200 ms
Vendor Lock-in Vercel Monthly Bill of $2,000+
Internal Network Requirements The customer wishes to deploy the solution on the corporate internal network

(2) Solutions for Self-Hosted Docker

Charlie built portable deployment packages using Docker:

BASH
# Build Once,Running everywhere
docker build -t taskflow:latest .
docker run -p 3000:3000 \
  -e DATABASE_URL="postgresql://..." \
  -e AUTH_SECRET="..." \
  taskflow:latest

(3) Revenue

Dimension Vercel Self-hosted Docker
Data Location Vercel region only Any data center
Monthly Costs $2,000+ $300 (server)
Deployment Latency Global ~100 ms Local < 20 ms
Vendor Lock-in High Low (Migrable)

3. output: 'standalone' Configuration

The output: 'standalone' mode in Next.js 16 creates a standalone Node.js server that contains all the files needed to run the application.

100%
graph TB
    A[next.config.js] --> B[output: 'standalone']
    B --> C[Build Process]
    C --> D[.next/standalone/ Table of Contents]
    D --> E[server.js — Independence HTTP Server]
    D --> F[.next/static — Static Resources]
    D --> G[node_modules — Minimal Dependencies]
    D --> H[package.json — Input Configuration]
    
    style A fill:#cce5ff
    style D fill:#d4edda

(1) Configuring next.config.js

JS
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'standalone',
  
  // Dependencies that require external processing
  serverExternalPackages: ['@prisma/client'],
  
  // Production Environment Optimization
  productionBrowserSourceMaps: false,
  swcMinify: true,
  
  // Image Optimization Retained
  images: {
    unoptimized: false
  }
}

module.exports = nextConfig

(2) standalone output directory structure

TEXT
.next/standalone/
├── server.js              # Independence HTTP Server(Entrance)
├── package.json           # Runtime Dependency Declarations
├── node_modules/          # Build-only dependencies
├── .next/
│   ├── server/            # Server-side code
│   ├── static/            # Static Resources
│   ├── build-manifest.json
│   └── ...
├── public/                # Public Static Resources
└── trace                  # Build and Track

▶ Example: Verifying a standalone build

BASH
# Build Project
npm run build

# View standalone Directory Size
du -sh .next/standalone/

# Start a Dedicated Server
node .next/standalone/server.js

# Verify on another terminal
curl http://localhost:3000
💻 Output:

TEXT
.next/standalone/    358M    # Total Size
.next/standalone/server.js   # Input File(Automatically Generated)

Output:

TEXT
.next/standalone/    358M
node .next/standalone/server.js
  ▲ Next.js 16.0.0
  - Local: http://localhost:3000
  ✓ Ready in 1.2s

4. Multi-stage Docker Builds

Multi-stage builds divide the image into three stages: dependency installation → application build → minimal runtime environment.

DOCKERFILE
# ============================================
# Dockerfile — Next.js 16 Multi-stage Construction
# ============================================

# --- Phase 1: Dependency Installation ---
FROM node:20-alpine AS deps
LABEL stage=deps

RUN apk add --no-cache libc6-compat

WORKDIR /app

COPY package.json package-lock.json pnpm-lock.yaml ./

RUN npm ci --only=production && \
    npm cache clean --force

# --- Phase 2: Build ---
FROM node:20-alpine AS build
LABEL stage=build

WORKDIR /app

COPY --from=deps /app/node_modules ./node_modules
COPY . .

ENV NEXT_TELEMETRY_DISABLED=1
ENV NODE_ENV=production

RUN npm run build

# --- Phase 3: Run ---
FROM node:20-alpine AS runner
LABEL stage=runner

RUN addgroup --system --gid 1001 nodejs && \
    adduser --system --uid 1001 nextjs

WORKDIR /app

# Copy the build artifacts
COPY --from=build --chown=nextjs:nodejs \
    /app/.next/standalone ./
COPY --from=build --chown=nextjs:nodejs \
    /app/.next/static ./.next/static
COPY --from=build --chown=nextjs:nodejs \
    /app/public ./public

# Health Checkup
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1

USER nextjs

EXPOSE 3000

ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
ENV NODE_ENV=production

CMD ["node", "server.js"]

(1) Building and Running

BASH
# Build an image
docker build -t taskflow:latest .

# View Image Size
docker images taskflow:latest

# Run a Container
docker run -d \
  --name taskflow-app \
  -p 3000:3000 \
  -e DATABASE_URL="postgresql://user:pass@host:5432/taskflow" \
  -e AUTH_SECRET="your-secret-key" \
  -e NEXT_PUBLIC_API_URL="https://api.taskflow.local" \
  --restart unless-stopped \
  taskflow:latest

(2) Comparison of Image Sizes Across Stages

Stage Base Image Size Contents
deps node:20-alpine ~150 MB node_modules + system dependencies
build node:20-alpine ~450 MB source code + node_modules + build artifacts
runner node:20-alpine ~358 MB standalone + production dependencies
Bare node:20-alpine ~126 MB Base system

▶ Example: Environment Variable Injection in Docker

Output:

TEXT
Sending build context to Docker daemon  4.096kB
Step 1/8 : FROM node:20-alpine AS deps
 ---> 1dd67de0f936
...
Successfully built a7b3c2d1e0f9
Successfully tagged shophub:latest
Docker command completed successfully.
d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4
BASH
# Usage .env Injecting Environment Variables into a File
cat > .env.production << EOF
DATABASE_URL=postgresql://user:pass@db:5432/taskflow
AUTH_SECRET=super-secret-key
NEXT_PUBLIC_API_URL=https://api.taskflow.local
NEXT_PUBLIC_POSTHOG_KEY=phc_xxxx
REDIS_URL=redis://redis:6379
EOF

docker run -d \
  --name taskflow-app \
  --env-file .env.production \
  -p 3000:3000 \
  --network taskflow-net \
  taskflow:latest

Output:

TEXT
Docker operation completed.

5. Nginx Reverse Proxy

Nginx handles SSL termination, static resource caching, and load balancing, making it an essential component in production environments.

100%
graph LR
    A[User's browser] --> B[Nginx :443]
    B --> C{Path Matching}
    C -->|/_next/static/*| D[Nginx Direct Services<br/>Cache 1 year]
    C -->|/api/health| E[Next.js :3000]
    C -->|/*| E
    B --> F[SSL Termination<br/>Let's Encrypt]
    
    style B fill:#cce5ff
    style D fill:#d4edda

Nginx configuration

NGINX
# nginx/nginx.conf
upstream nextjs_upstream {
    server app:3000;
    keepalive 64;
}

server {
    listen 80;
    server_name taskflow.local;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name taskflow.local;

    # SSL Certificate
    ssl_certificate /etc/nginx/ssl/taskflow.crt;
    ssl_certificate_key /etc/nginx/ssl/taskflow.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    # Safety Head
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

    # Log
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    # Caching of Static Resources (processed by Next.js)
    location /_next/static/ {
        proxy_pass http://nextjs_upstream;
        expires 365d;
        add_header Cache-Control "public, immutable";
    }

    location /static/ {
        proxy_pass http://nextjs_upstream;
        expires 30d;
        add_header Cache-Control "public";
    }

    # Health Check Endpoints
    location /api/health {
        proxy_pass http://nextjs_upstream;
        access_log off;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }

    # All other requests are forwarded to Next.js
    location / {
        proxy_pass http://nextjs_upstream;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 60s;
        proxy_send_timeout 60s;
    }
}

6. PM2 Process Manager

PM2 ensures that Node.js processes automatically restart after a crash and provides log management and cluster mode.

(1) PM2 Configuration

JS
// ecosystem.config.js
module.exports = {
  apps: [{
    name: 'taskflow',
    script: 'server.js',
    cwd: '/app',
    
    // Cluster Mode(Use all CPU Core)
    exec_mode: 'cluster',
    instances: 'max',
    
    // Environment Variables
    env: {
      NODE_ENV: 'production',
      PORT: 3000,
      HOSTNAME: '0.0.0.0'
    },
    
    // Log Configuration
    log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
    error_file: '/var/log/pm2/taskflow-error.log',
    out_file: '/var/log/pm2/taskflow-out.log',
    merge_logs: true,
    
    // Automatic Restart
    max_restarts: 10,
    restart_delay: 1000,
    min_uptime: 5000,
    
    // Memory Monitoring
    max_memory_restart: '500M',
    
    // Health Checkup
    listen_timeout: 3000,
    kill_timeout: 5000
  }]
}

(2) PM2 Docker Integration

DOCKERFILE
# Install PM2 in runner stage
FROM node:20-alpine AS runner

RUN npm install -g pm2 && \
    addgroup --system --gid 1001 nodejs && \
    adduser --system --uid 1001 nextjs

WORKDIR /app

COPY --from=build --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=build --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=build --chown=nextjs:nodejs /app/public ./public
COPY --chown=nextjs:nodejs ecosystem.config.js ./

USER nextjs

EXPOSE 3000

# Usage PM2 Enable Cluster Mode
CMD ["pm2-runtime", "start", "ecosystem.config.js"]

▶ Example: Common PM2 Commands

Output:

TEXT
Docker image built and container started successfully.
BASH
# View All Processes
pm2 list

# View Logs
pm2 logs taskflow
pm2 logs taskflow --lines 100

# Monitor Resources
pm2 monit

# Reload(Zero Downtime)
pm2 reload taskflow

# Stop/Restart
pm2 stop taskflow
pm2 restart taskflow

# Save the current list of processes
pm2 save
pm2 startup

Output:

TEXT
PM2 command executed successfully.
PM2 command executed successfully.
PM2 command executed successfully.
PM2 command executed successfully.
PM2 command executed successfully.
PM2 command executed successfully.
┌────┬──────────┬─────────┬─────────┐
│ id │ name     │ status  │ cpu     │
├────┼──────────┼─────────┼─────────┤
│ 0  │ shophub  │ online  │ 0%      │
└────┴──────────┴─────────┴─────────┘
PM2 command executed successfully.
┌────┬──────────┬─────────┬─────────┐
│ id │ name     │ status  │ cpu     │
├────┼──────────┼─────────┼─────────┤
│ 0  │ shophub  │ online  │ 0%      │
└────┴──────────┴─────────┴─────────┘

7. Docker Compose: Orchestrating Three Containers

Docker Compose orchestrates three containers—App, Nginx, and PostgreSQL—to launch the entire environment with a single click.

YAML
# docker-compose.yml
version: '3.8'

networks:
  taskflow-net:
    driver: bridge

volumes:
  postgres-data:
    driver: local
  nginx-logs:
    driver: local

services:
  # === 1. PostgreSQL Database ===
  db:
    image: postgres:16-alpine
    container_name: taskflow-db
    restart: unless-stopped
    networks:
      - taskflow-net
    volumes:
      - postgres-data:/var/lib/postgresql/data
      - ./db/init:/docker-entrypoint-initdb.d
    environment:
      POSTGRES_USER: taskflow
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: taskflow
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U taskflow"]
      interval: 10s
      timeout: 5s
      retries: 5
    ports:
      - "5432:5432"

  # === 2. Next.js Applications ===
  app:
    build:
      context: .
      dockerfile: Dockerfile
      target: runner
    image: taskflow:latest
    container_name: taskflow-app
    restart: unless-stopped
    networks:
      - taskflow-net
    depends_on:
      db:
        condition: service_healthy
    environment:
      NODE_ENV: production
      PORT: 3000
      HOSTNAME: "0.0.0.0"
      DATABASE_URL: postgresql://taskflow:${DB_PASSWORD}@db:5432/taskflow
      AUTH_SECRET: ${AUTH_SECRET}
      AUTH_URL: ${AUTH_URL}
      NEXT_PUBLIC_API_URL: ${PUBLIC_API_URL}
      NEXT_PUBLIC_POSTHOG_KEY: ${POSTHOG_KEY:-}
    env_file:
      - .env.production
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/api/health"]
      interval: 30s
      timeout: 3s
      retries: 3

  # === 3. Nginx Reverse Proxy ===
  nginx:
    image: nginx:alpine
    container_name: taskflow-nginx
    restart: unless-stopped
    networks:
      - taskflow-net
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - ./nginx/ssl:/etc/nginx/ssl:ro
      - nginx-logs:/var/log/nginx
    depends_on:
      app:
        condition: service_healthy

(1) Environment Variable File

BASH
# .env.production(Do not submit to Git)
DB_PASSWORD=StrongPassword123!
AUTH_SECRET=your-auth-secret-key-min-32-chars
AUTH_URL=https://auth.taskflow.local
PUBLIC_API_URL=https://api.taskflow.local
POSTHOG_KEY=phc_exampleKey123

(2) Startup and Management

BASH
# First Launch
docker compose up -d

# View Logs
docker compose logs -f app
docker compose logs -f nginx

# Rebuild the application
docker compose build app
docker compose up -d app

# Update Database Migration
docker compose exec app npx prisma migrate deploy

# View Operating Status
docker compose ps

# Stop all services
docker compose down

# Completely Clean Up(Han Juan)
docker compose down -v

▶ Example: docker-compose.override.yml (development environment)

Output:

TEXT
Save the above YAML configuration to the specified file path. The settings will take effect on the next server restart.
YAML
# docker-compose.override.yml
version: '3.8'

services:
  app:
    build:
      target: build  # For use during the development phase build rather than runner
    environment:
      NODE_ENV: development
    volumes:
      - ./src:/app/src:ro
      - ./public:/app/public:ro
    command: npm run dev  # Using the Development Server

  db:
    ports:
      - "5432:5432"  # Exposing the Database Port During Development

  nginx:
    ports:
      - "3000:80"  # Simplify Port Mapping During Development

Output:

TEXT
Save the above YAML configuration to the specified file path. The settings will take effect on the next server restart.

8. Runtime Environment Variable Injection

Environment variables for Docker containers are injected at runtime, not during the build process—this allows a single image to be deployed to multiple environments.

100%
graph LR
    A[Docker Build] --> B[Image<br/>(No environment variables)]
    B --> C[Runtime Injection]
    C --> D[Development Environment .env.dev]
    C --> E[Test Environment .env.test]
    C --> F[Production Environment .env.prod]
    D --> G[Container Startup]
    E --> G
    F --> G
    G --> H[server.js Read process.env]
    
    style B fill:#cce5ff
    style G fill:#d4edda

(1) Build-time vs. Runtime Variables

Variable Type Injection Timing Example Storage Location
During Build docker build NEXT_PUBLIC_*, Version Number Dockerfile ARG
Runtime docker run DATABASE_URLAUTH_SECRET docker compose env_file
Mixed Both are required NEXT_PUBLIC_API_URL Injected into the front end during build time, and into the back end at runtime

(2) Variable Injection During Build

DOCKERFILE
# Dockerfile Usage ARG Passing Build-Time Variables
FROM node:20-alpine AS build

ARG NEXT_PUBLIC_API_URL
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL

ARG SENTRY_DSN
ENV SENTRY_DSN=$SENTRY_DSN

RUN npm run build
BASH
# Variables passed during construction
docker build \
  --build-arg NEXT_PUBLIC_API_URL=https://api.taskflow.com \
  --build-arg SENTRY_DSN=https://xxx@sentry.io/123 \
  -t taskflow:latest .

▶ Example: Runtime Environment Validation Script

Output:

TEXT
Sending build context to Docker daemon  4.096kB
Step 1/8 : FROM node:20-alpine AS deps
 ---> 1dd67de0f936
...
Successfully built a7b3c2d1e0f9
Successfully tagged shophub:latest
TS
// src/lib/env.ts
// Runtime Environment Variable Validation
function getRequiredEnvVar(name: string): string {
  const value = process.env[name]
  if (!value) {
    throw new Error(`Missing required environment variable: ${name}`)
  }
  return value
}

export const env = {
  databaseUrl: getRequiredEnvVar('DATABASE_URL'),
  authSecret: getRequiredEnvVar('AUTH_SECRET'),
  authUrl: process.env.AUTH_URL || 'http://localhost:3000',
  publicApiUrl: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000',
  posthogKey: process.env.NEXT_PUBLIC_POSTHOG_KEY,
  nodeEnv: process.env.NODE_ENV || 'development',
  isProduction: process.env.NODE_ENV === 'production',
  port: parseInt(process.env.PORT || '3000', 10)
}

Output:

TEXT
Exports: env.

9. Complete Example: TaskFlow Docker Deployment

BASH
# ============================================
# Production Deployment Scripts:deploy.sh
# Features:Build → Migration → Start → Health Checkup
# ============================================

#!/bin/bash
set -euo pipefail

echo "=== TaskFlow Production Deployment ==="

# 1. Load Environment Variables
if [ ! -f .env.production ]; then
    echo "ERROR: .env.production not found"
    exit 1
fi
source .env.production

# 2. Build Docker Image
echo "Building Docker image..."
docker compose build app

# 3. Start the database(If it is not running)
echo "Starting database..."
docker compose up -d db
echo "Waiting for database to be ready..."
sleep 5

# 4. Run the database migration
echo "Running database migrations..."
docker compose run --rm app npx prisma migrate deploy

# 5. Launch the app and Nginx
echo "Starting application and Nginx..."
docker compose up -d app nginx

# 6. Health Checkup
echo "Running health check..."
for i in {1..10}; do
    if curl -s -o /dev/null -w "%{http_code}" http://localhost:80/api/health | grep -q 200; then
        echo "Health check passed!"
        break
    fi
    echo "Waiting... ($i/10)"
    sleep 3
done

# 7. Clean up old images
echo "Cleaning up old images..."
docker image prune -f

# 8. Deployment Information
echo ""
echo "=== Deployment Complete ==="
echo "App:      http://localhost:80"
echo "API:      http://localhost:80/api/health"
echo "DB:       postgresql://taskflow@localhost:5432/taskflow"
echo "Logs:     docker compose logs -f app"
echo "Restart:  docker compose restart app"
TS
// src/app/api/health/route.ts
// ============================================
// Health Check API: called by Docker HEALTHCHECK
// ============================================
import { NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'

export async function GET() {
  const checks = {
    status: 'healthy',
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
    memory: process.memoryUsage(),
    checks: {} as Record<string, boolean>
  }

  try {
    // Check the database connection
    await prisma.$queryRaw`SELECT 1`
    checks.checks.database = true
  } catch {
    checks.checks.database = false
    checks.status = 'degraded'
  }

  try {
    // Inspection Redis(If configured)
    // await redis.ping()
    checks.checks.redis = true
  } catch {
    checks.checks.redis = false
    if (!checks.checks.database) {
      checks.status = 'unhealthy'
    }
  }

  const statusCode = checks.status === 'healthy' ? 200 : 503

  return NextResponse.json(checks, { status: statusCode })
}

❓ FAQ

Q What is the difference between output: 'standalone' and output: 'export'?
A standalone generates a standalone package that includes a Node.js server and supports all Next.js features, such as SSR, ISR, and API Routes. export generates pure static HTML (with SSR disabled), which is suitable for CDN hosting. For self-hosting with Docker, you must use the standalone mode.
Q Why is a multi-stage build better than a single-stage build?
A (1) Smaller images—the runtime stage contains only the minimum files required for execution (358 MB vs. 1.2 GB); (2) More secure—build tools and source code are not included in the final image; (3) More efficient build caching—since the dependency layers rarely change, Docker cache layers can be reused.
Q Is Nginx required or optional?
A Nginx is strongly recommended for production environments: (1) SSL termination—handling HTTPS certificates; (2) static resource caching—reducing the load on Node.js; (3) Security header injection—XSS, CSP, HSTS, etc.; (4) Load balancing—distributing requests across multiple instances. In simple internal network environments, you can skip this step and expose the Next.js port directly.
Q Do I need to use both PM2 and Docker’s restart policy?
A We recommend using both. Docker’s restart: unless-stopped handles container-level crashes (such as OOM), while PM2 handles Node.js process-level crashes (such as uncaught exceptions). PM2 also provides features that Docker itself does not offer, such as log rotation, cluster mode, and zero-downtime restarts.
Q Which should I choose, Docker Compose or Kubernetes?
A For single-server deployments, choose Docker Compose (simple configuration, low learning curve); for multi-server clusters, automatic scaling, and service discovery, choose Kubernetes. For small to medium-sized teams (1–5 servers), Docker Compose in Swarm mode is sufficient for most scenarios.

📖 Summary


📝 Exercises

  1. Basic Problem (⭐): Create a next.config.js that contains output: 'standalone', write a multi-stage Dockerfile, successfully build and run docker run, and then verify it using curl localhost:3000.

  2. Advanced Exercise (⭐⭐): Add an Nginx reverse proxy container to Docker Compose: (1) Configure a self-signed SSL certificate; (2) Add caching rules for static resources; (3) Configure /_next/static to be cached for 365 days; (4) Verify that HTTPS access works properly.

  3. Challenge (⭐⭐⭐): Build a complete self-hosted CI/CD + Docker pipeline: (1) Use GitHub Actions to automatically build Docker images and push them to GHCR; (2) Pull the new images to the target server via SSH; (3) Use Docker Compose for zero-downtime updates (docker compose up -d --no-deps --build app); (4) Configure PM2 cluster mode and log rotation.

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏