删除无用docker配置
This commit is contained in:
@ -1,19 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Error</title>
|
||||
<style>
|
||||
html { color-scheme: light dark; }
|
||||
body { width: 35em; margin: 0 auto;
|
||||
font-family: Tahoma, Verdana, Arial, sans-serif; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>An error occurred.</h1>
|
||||
<p>Sorry, the page you are looking for is currently unavailable.<br/>
|
||||
Please try again later.</p>
|
||||
<p>If you are the system administrator of this resource then you should check
|
||||
the error log for details.</p>
|
||||
<p><em>Faithfully yours, nginx.</em></p>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,13 +0,0 @@
|
||||
FROM --platform=linux/arm64 docker.xuanyuan.run/library/nginx:1.29.0
|
||||
|
||||
COPY ./nginx.conf /etc/nginx/nginx.conf
|
||||
COPY ./50x.html /usr/share/nginx/html/50x.html
|
||||
COPY ./dist/ /usr/share/nginx/html/
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
CMD ["nginx","-g","daemon off;"]
|
||||
|
||||
# docker build -t pcitc/itc-web:1.0.0 .
|
||||
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
FROM --platform=linux/arm64 docker.xuanyuan.run/library/node:20.15.1-slim
|
||||
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
|
||||
# 安装依赖(http-server和http-proxy-middleware)
|
||||
RUN npm install http-server http-proxy-middleware --production
|
||||
|
||||
# 复制自定义服务器脚本server.js
|
||||
COPY ./server.js /app/
|
||||
|
||||
|
||||
# 复制静态文件到工作目录(根据实际项目调整,如dist目录)
|
||||
# 假设静态文件在当前目录的dist文件夹下
|
||||
RUN mkdir -p /app/html
|
||||
|
||||
COPY index.html /app/html/index.html
|
||||
|
||||
# 暴露端口(与server.js中配置的port一致)
|
||||
EXPOSE 8080
|
||||
|
||||
# 启动命令:通过node运行server.js
|
||||
CMD ["node", "server.js"]
|
||||
|
||||
# docker build -f Dockerfile-node -t node-http-server:1.0.3 .
|
||||
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
FROM --platform=linux/arm64 node-vue:1.0.0
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
CMD ["npm","run","prod"]
|
||||
|
||||
# docker build -f Dockerfile-vue -t pcitc/itc-web:1.0.2 .
|
||||
|
||||
|
||||
@ -1,67 +0,0 @@
|
||||
@echo off
|
||||
chcp 65001 >nul 2>&1 :: <20><><EFBFBD><EFBFBD>UTF-8<><38><EFBFBD>룬<EFBFBD><EBA3AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
:: <20><><EFBFBD><EFBFBD>·<EFBFBD><C2B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ¼ΪdockerĿ¼<C4BF><C2BC><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD>
|
||||
set "ROOT_DIR=../"
|
||||
set "DIST_DIR=%ROOT_DIR%dist"
|
||||
set "DOCKER_DIR=%~dp0"
|
||||
echo ======================
|
||||
echo <20><>Ŀ¼ %ROOT_DIR%
|
||||
echo <20><><EFBFBD><EFBFBD>Ŀ¼ %DIST_DIR%
|
||||
echo DockerĿ¼ %DOCKER_DIR%
|
||||
echo ======================
|
||||
|
||||
if not exist "%DIST_DIR%" (
|
||||
echo <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>δ<EFBFBD><CEB4><EFBFBD><EFBFBD>distĿ¼<C4BF><C2BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʧ<EFBFBD><CAA7>
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
:: <20><><EFBFBD><EFBFBD>2<EFBFBD><32><EFBFBD><EFBFBD><EFBFBD><EFBFBD>dist<73><74>dockerĿ¼
|
||||
echo ======================
|
||||
echo <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD>distĿ¼<C4BF><C2BC>dockerĿ¼...
|
||||
echo ======================
|
||||
|
||||
if exist "%DOCKER_DIR%dist" (
|
||||
echo <20><><EFBFBD><EFBFBD><EFBFBD>ɵ<EFBFBD>distĿ¼...
|
||||
rd /s /q "%DOCKER_DIR%dist" || (
|
||||
echo <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɾ<EFBFBD><C9BE><EFBFBD><EFBFBD>distĿ¼<C4BF><C2BC><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD>ռ<EFBFBD>õij<C3B5><C4B3><EFBFBD>
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
xcopy "%DIST_DIR%" "%DOCKER_DIR%dist" /e /i /h /r /y || (
|
||||
echo <20><><EFBFBD><EFBFBD><F3A3BAB8><EFBFBD>distĿ¼ʧ<C2BC><CAA7>
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
:: <20><><EFBFBD><EFBFBD>3<EFBFBD><33><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Docker<65><72><EFBFBD><EFBFBD>
|
||||
echo ======================
|
||||
echo <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD>Docker<65><72><EFBFBD><EFBFBD>...
|
||||
echo ======================
|
||||
cd /d "%DOCKER_DIR%" || (
|
||||
echo <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><DEB7><EFBFBD><EFBFBD><EFBFBD>dockerĿ¼ %DOCKER_DIR%
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
where docker >nul 2>nul || (
|
||||
echo <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>δ<EFBFBD>ҵ<EFBFBD>docker<65><72><EFBFBD>밲װ<EBB0B2><D7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Docker
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
docker build -t pcitc/itc-web:1.0.1 . || (
|
||||
echo <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>docker build ִ<><D6B4>ʧ<EFBFBD><CAA7>
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo ======================
|
||||
echo <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɣ<EFBFBD><C9A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>pcitc/itc-web:1.0.1
|
||||
echo ======================
|
||||
pause
|
||||
@ -1,130 +0,0 @@
|
||||
@echo off
|
||||
chcp 65001 >nul 2>&1 :: <20><><EFBFBD><EFBFBD>UTF-8<><38><EFBFBD>룬<EFBFBD><EBA3AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
:: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>־<EFBFBD>ļ<EFBFBD>·<EFBFBD><C2B7>
|
||||
set "LOG_FILE=%~dp0docker_build.log"
|
||||
|
||||
:: <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD>־<EFBFBD>ļ<EFBFBD>
|
||||
echo ============================================== > "%LOG_FILE%"
|
||||
echo <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼʱ<CABC><CAB1>: %date% %time% >> "%LOG_FILE%"
|
||||
echo ============================================== >> "%LOG_FILE%"
|
||||
|
||||
:: <20><><EFBFBD><EFBFBD>Ŀ¼<C4BF><C2BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǰĿ¼ΪdockerĿ¼<C4BF><C2BC><EFBFBD><EFBFBD>һ<EFBFBD><D2BB>
|
||||
set "ROOT_DIR=../"
|
||||
set "DIST_DIR=%ROOT_DIR%dist"
|
||||
set "DOCKER_DIR=%~dp0"
|
||||
|
||||
:: <20><><EFBFBD><EFBFBD>Ŀ¼<C4BF><C2BC>Ϣ<EFBFBD><CFA2>д<EFBFBD><D0B4><EFBFBD><EFBFBD>־
|
||||
echo ======================
|
||||
echo <20><>Ŀ¼ %ROOT_DIR%
|
||||
echo <20><><EFBFBD><EFBFBD>Ŀ¼ %DIST_DIR%
|
||||
echo DockerĿ¼ %DOCKER_DIR%
|
||||
echo ----------------------
|
||||
echo ====================== >> "%LOG_FILE%"
|
||||
echo <20><>Ŀ¼ %ROOT_DIR% >> "%LOG_FILE%"
|
||||
echo <20><><EFBFBD><EFBFBD>Ŀ¼ %DIST_DIR% >> "%LOG_FILE%"
|
||||
echo DockerĿ¼ %DOCKER_DIR% >> "%LOG_FILE%"
|
||||
echo ---------------------- >> "%LOG_FILE%"
|
||||
|
||||
:: <20><><EFBFBD><EFBFBD>1:<3A>ڸ<EFBFBD>Ŀ¼ִ<C2BC><D6B4>npm run build
|
||||
echo ======================
|
||||
echo <20><>ʼִ<CABC><D6B4>npm run build...
|
||||
echo ======================
|
||||
echo ====================== >> "%LOG_FILE%"
|
||||
echo %date% %time% <20><>ʼִ<CABC><D6B4>npm run build... >> "%LOG_FILE%"
|
||||
echo ====================== >> "%LOG_FILE%"
|
||||
|
||||
cd /d "%ROOT_DIR%" || (
|
||||
echo <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><DEB7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ¼ %ROOT_DIR%
|
||||
echo %date% %time% <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><DEB7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ¼ %ROOT_DIR% >> "%LOG_FILE%"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
where npm >nul 2>nul || (
|
||||
echo <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>δ<EFBFBD>ҵ<EFBFBD>npm<70><6D><EFBFBD>밲װNode.js<6A><73><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><C3BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
echo %date% %time% <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>δ<EFBFBD>ҵ<EFBFBD>npm<70><6D><EFBFBD>밲װNode.js<6A><73><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><C3BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> >> "%LOG_FILE%"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
npm run build || (
|
||||
echo <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>npm run build ִ<><D6B4>ʧ<EFBFBD><CAA7>
|
||||
echo %date% %time% <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>npm run build ִ<><D6B4>ʧ<EFBFBD><CAA7> >> "%LOG_FILE%"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
echo %date% %time% <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> >> "%LOG_FILE%"
|
||||
pause
|
||||
|
||||
:: <20><><EFBFBD><EFBFBD>2:<3A><><EFBFBD><EFBFBD>dist<73><74>dockerĿ¼
|
||||
echo ======================
|
||||
echo <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD>distĿ¼<C4BF><C2BC>dockerĿ¼...
|
||||
echo ======================
|
||||
echo ====================== >> "%LOG_FILE%"
|
||||
echo %date% %time% <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD>distĿ¼<C4BF><C2BC>dockerĿ¼... >> "%LOG_FILE%"
|
||||
echo ====================== >> "%LOG_FILE%"
|
||||
|
||||
if exist "%DOCKER_DIR%dist" (
|
||||
echo <20>Ƴ<EFBFBD><C6B3>Ѵ<EFBFBD><D1B4>ڵ<EFBFBD>distĿ¼...
|
||||
echo %date% %time% <20>Ƴ<EFBFBD><C6B3>Ѵ<EFBFBD><D1B4>ڵ<EFBFBD>distĿ¼... >> "%LOG_FILE%"
|
||||
rd /s /q "%DOCKER_DIR%dist" || (
|
||||
echo <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɾ<EFBFBD><C9BE><EFBFBD><EFBFBD>distĿ¼<C4BF><C2BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƿ<EFBFBD><C7B7><EFBFBD>ռ<EFBFBD><D5BC>
|
||||
echo %date% %time% <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɾ<EFBFBD><C9BE><EFBFBD><EFBFBD>distĿ¼<C4BF><C2BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƿ<EFBFBD><C7B7><EFBFBD>ռ<EFBFBD><D5BC> >> "%LOG_FILE%"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
|
||||
pause
|
||||
|
||||
xcopy "%DIST_DIR%" "%DOCKER_DIR%dist" /e /i /h /r /y || (
|
||||
echo <20><><EFBFBD><EFBFBD><F3A3BAB8><EFBFBD>distĿ¼ʧ<C2BC><CAA7>
|
||||
echo %date% %time% <20><><EFBFBD><EFBFBD><F3A3BAB8><EFBFBD>distĿ¼ʧ<C2BC><CAA7> >> "%LOG_FILE%"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
echo %date% %time% distĿ¼<C4BF><C2BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> >> "%LOG_FILE%"
|
||||
|
||||
:: <20><><EFBFBD><EFBFBD>3:<3A><><EFBFBD><EFBFBD>Docker<65><72><EFBFBD><EFBFBD>
|
||||
echo ======================
|
||||
echo <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD>Docker<65><72><EFBFBD><EFBFBD>...
|
||||
echo ======================
|
||||
echo ====================== >> "%LOG_FILE%"
|
||||
echo %date% %time% <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD>Docker<65><72><EFBFBD><EFBFBD>... >> "%LOG_FILE%"
|
||||
echo ====================== >> "%LOG_FILE%"
|
||||
|
||||
cd /d "%DOCKER_DIR%" || (
|
||||
echo <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><DEB7><EFBFBD><EFBFBD><EFBFBD>dockerĿ¼ %DOCKER_DIR%
|
||||
echo %date% %time% <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><DEB7><EFBFBD><EFBFBD><EFBFBD>dockerĿ¼ %DOCKER_DIR% >> "%LOG_FILE%"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
where docker >nul 2>&1 || (
|
||||
echo <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>δ<EFBFBD>ҵ<EFBFBD>docker<65><72><EFBFBD>밲װ<EBB0B2><D7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Docker
|
||||
echo %date% %time% <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>δ<EFBFBD>ҵ<EFBFBD>docker<65><72><EFBFBD>밲װ<EBB0B2><D7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Docker >> "%LOG_FILE%"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
docker build -t pcitc/itc-web:1.0.0 . || (
|
||||
echo <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>docker build ִ<><D6B4>ʧ<EFBFBD><CAA7>
|
||||
echo %date% %time% <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>docker build ִ<><D6B4>ʧ<EFBFBD><CAA7> >> "%LOG_FILE%"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo ======================
|
||||
echo <20><><EFBFBD><EFBFBD><EFBFBD>ɹ<EFBFBD><C9B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊpcitc/itc-web:1.0.0
|
||||
echo ======================
|
||||
echo ====================== >> "%LOG_FILE%"
|
||||
echo %date% %time% <20><><EFBFBD><EFBFBD><EFBFBD>ɹ<EFBFBD><C9B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊpcitc/itc-web:1.0.0 >> "%LOG_FILE%"
|
||||
echo ====================== >> "%LOG_FILE%"
|
||||
echo <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1>: %date% %time% >> "%LOG_FILE%"
|
||||
echo ============================================== >> "%LOG_FILE%"
|
||||
|
||||
pause
|
||||
@ -1,135 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 定义变量
|
||||
CONTAINER_NAME="itc-web"
|
||||
IMAGE_NAME="pcitc/itc-web"
|
||||
IMAGE_VERSION="1.0.0"
|
||||
IMAGE_FULL="$IMAGE_NAME:$IMAGE_VERSION"
|
||||
PORT=80 # 服务暴露端口
|
||||
CONTAINER_PORT=8080 # 服务暴露端口
|
||||
HTML_DIR="/docker-data/nginx/html" # 服务暴露端口
|
||||
CONF_DIR="/docker-data/nginx/conf" # 服务暴露端口
|
||||
TZ="Asia/Shanghai" # 时区设置
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "使用方法: $0 [操作类型]"
|
||||
echo "操作类型:"
|
||||
echo " deploy - 部署并启动服务容器"
|
||||
echo " remove - 停止并删除容器及数据/日志目录"
|
||||
echo " restart - 重启容器"
|
||||
echo " logs - 查看容器日志"
|
||||
echo " help - 显示帮助信息"
|
||||
}
|
||||
|
||||
|
||||
|
||||
# 部署服务
|
||||
deploy_service() {
|
||||
# 创建数据和日志目录
|
||||
if [ ! -d "$HTML_DIR" ]; then
|
||||
echo "创建html数据目录: $HTML_DIR"
|
||||
mkdir -p "$HTML_DIR"
|
||||
chmod 777 "$HTML_DIR"
|
||||
fi
|
||||
|
||||
# 创建数据和日志目录
|
||||
if [ ! -d "$CONF_DIR" ]; then
|
||||
echo "创建配置数据目录: $CONF_DIR"
|
||||
mkdir -p "$CONF_DIR"
|
||||
chmod 777 "$CONF_DIR"
|
||||
fi
|
||||
|
||||
echo "部署镜像【$IMAGE_FULL】"
|
||||
# 检查容器是否已存在,存在则停止并删除
|
||||
if [ "$(docker ps -aq -f name=$CONTAINER_NAME)" ]; then
|
||||
echo "停止并删除现有容器: $CONTAINER_NAME"
|
||||
docker stop $CONTAINER_NAME >/dev/null
|
||||
docker rm $CONTAINER_NAME >/dev/null
|
||||
fi
|
||||
|
||||
# 启动服务容器
|
||||
echo "启动服务容器..."
|
||||
docker run -d --privileged --restart always \
|
||||
--name $CONTAINER_NAME \
|
||||
-p $PORT:$CONTAINER_PORT \
|
||||
-v $HTML_DIR:/usr/share/nginx/html \
|
||||
-v $CONF_DIR:/etc/nginx \
|
||||
$IMAGE_FULL
|
||||
|
||||
# 检查启动状态
|
||||
if [ "$(docker ps -aq -f name=$CONTAINER_NAME -f status=running)" ]; then
|
||||
echo "服务启动成功!"
|
||||
echo "容器名称: $CONTAINER_NAME"
|
||||
echo "访问地址: http://localhost:$PORT"
|
||||
echo "数据目录: $HTML_DIR"
|
||||
echo "配置目录: $CONF_DIR"
|
||||
else
|
||||
echo "服务启动失败,日志如下:"
|
||||
docker logs $CONTAINER_NAME
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 删除服务
|
||||
remove_service() {
|
||||
# 检查容器是否存在
|
||||
if [ "$(docker ps -aq -f name=$CONTAINER_NAME)" ]; then
|
||||
echo "停止容器: $CONTAINER_NAME"
|
||||
docker stop $CONTAINER_NAME >/dev/null
|
||||
|
||||
echo "删除容器: $CONTAINER_NAME"
|
||||
docker rm $CONTAINER_NAME >/dev/null
|
||||
else
|
||||
echo "容器 $CONTAINER_NAME 不存在"
|
||||
fi
|
||||
}
|
||||
|
||||
# 启动服务
|
||||
restart_service() {
|
||||
if [ "$(docker ps -aq -f name=$CONTAINER_NAME)" ]; then
|
||||
echo "重启容器: $CONTAINER_NAME"
|
||||
docker restart $CONTAINER_NAME >/dev/null
|
||||
else
|
||||
echo "容器 $CONTAINER_NAME 不存在"
|
||||
fi
|
||||
}
|
||||
|
||||
# 查看日志
|
||||
show_logs() {
|
||||
if [ "$(docker ps -aq -f name=$CONTAINER_NAME)" ]; then
|
||||
echo "查看 $CONTAINER_NAME 容器日志 (按 Ctrl+C 退出)..."
|
||||
docker logs -f --tail 100 $CONTAINER_NAME
|
||||
else
|
||||
echo "容器 $CONTAINER_NAME 不存在"
|
||||
fi
|
||||
}
|
||||
|
||||
# 主逻辑
|
||||
if [ $# -lt 1 ]; then
|
||||
show_help
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
deploy)
|
||||
deploy_service
|
||||
;;
|
||||
remove)
|
||||
remove_service
|
||||
;;
|
||||
restart)
|
||||
restart_service
|
||||
;;
|
||||
logs)
|
||||
show_logs
|
||||
;;
|
||||
help)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
echo "无效的操作类型: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@ -1,180 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>静态服务器首页</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: '#3b82f6',
|
||||
secondary: '#64748b',
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['Inter', 'system-ui', 'sans-serif'],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style type="text/tailwindcss">
|
||||
@layer utilities {
|
||||
.text-shadow {
|
||||
text-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
.card-hover {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.card-hover:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-50 min-h-screen">
|
||||
<header class="bg-gradient-to-r from-primary to-blue-700 text-white py-16 px-4">
|
||||
<div class="container mx-auto max-w-4xl">
|
||||
<h1 class="text-[clamp(2rem,5vw,3.5rem)] font-bold text-shadow mb-4">
|
||||
静态服务器
|
||||
</h1>
|
||||
<p class="text-xl opacity-90 max-w-2xl">
|
||||
已成功启动,可访问静态文件和通过代理接口与后端交互
|
||||
</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="container mx-auto max-w-4xl px-4 py-12">
|
||||
<div class="grid md:grid-cols-2 gap-8">
|
||||
<!-- 静态文件测试卡片 -->
|
||||
<div class="bg-white rounded-xl shadow-md p-6 card-hover">
|
||||
<div class="text-primary text-3xl mb-4">
|
||||
<i class="fa fa-file-text-o" aria-hidden="true"></i>
|
||||
</div>
|
||||
<h2 class="text-xl font-semibold mb-3">静态文件服务</h2>
|
||||
<p class="text-gray-600 mb-4">
|
||||
该服务器可提供静态文件访问,当前根目录为 <code class="bg-gray-100 px-2 py-1 rounded">./html</code>
|
||||
</p>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<a href="/" class="inline-flex items-center px-4 py-2 bg-primary text-white rounded-md hover:bg-blue-600 transition">
|
||||
<i class="fa fa-home mr-2"></i>当前首页
|
||||
</a>
|
||||
<a href="/test.txt" class="inline-flex items-center px-4 py-2 bg-gray-200 text-gray-700 rounded-md hover:bg-gray-300 transition">
|
||||
<i class="fa fa-file-text mr-2"></i>测试文件
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 代理接口测试卡片 -->
|
||||
<div class="bg-white rounded-xl shadow-md p-6 card-hover">
|
||||
<div class="text-green-500 text-3xl mb-4">
|
||||
<i class="fa fa-exchange" aria-hidden="true"></i>
|
||||
</div>
|
||||
<h2 class="text-xl font-semibold mb-3">API代理服务</h2>
|
||||
<p class="text-gray-600 mb-4">
|
||||
所有 <code class="bg-gray-100 px-2 py-1 rounded">/api</code> 前缀的请求将代理到:
|
||||
<br>
|
||||
<code class="bg-gray-100 px-2 py-1 rounded">http://10.4.126.116:23000</code>
|
||||
</p>
|
||||
<button id="testApiBtn" class="inline-flex items-center px-4 py-2 bg-green-500 text-white rounded-md hover:bg-green-600 transition">
|
||||
<i class="fa fa-play-circle mr-2"></i>测试API连接
|
||||
</button>
|
||||
<div id="apiTestResult" class="mt-4 hidden"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-12 bg-white rounded-xl shadow-md p-6">
|
||||
<h2 class="text-xl font-semibold mb-4 flex items-center">
|
||||
<i class="fa fa-info-circle text-primary mr-2"></i>
|
||||
服务器信息
|
||||
</h2>
|
||||
<ul class="space-y-2 text-gray-700">
|
||||
<li class="flex">
|
||||
<span class="w-32 font-medium">服务器地址:</span>
|
||||
<span>http://localhost:8080</span>
|
||||
</li>
|
||||
<li class="flex">
|
||||
<span class="w-32 font-medium">启动时间:</span>
|
||||
<span id="startTime"></span>
|
||||
</li>
|
||||
<li class="flex">
|
||||
<span class="w-32 font-medium">静态文件目录:</span>
|
||||
<span>./html</span>
|
||||
</li>
|
||||
<li class="flex">
|
||||
<span class="w-32 font-medium">代理目标:</span>
|
||||
<span>http://10.4.126.116:23000</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="bg-gray-800 text-white py-8 mt-16">
|
||||
<div class="container mx-auto max-w-4xl px-4 text-center">
|
||||
<p>© <span id="year"></span> 静态服务器 | 基于Node.js构建</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// 设置当前年份
|
||||
document.getElementById('year').textContent = new Date().getFullYear();
|
||||
|
||||
// 设置启动时间
|
||||
document.getElementById('startTime').textContent = new Date().toLocaleString();
|
||||
|
||||
// API测试功能
|
||||
document.getElementById('testApiBtn').addEventListener('click', async () => {
|
||||
const resultEl = document.getElementById('apiTestResult');
|
||||
const btn = document.getElementById('testApiBtn');
|
||||
|
||||
// 显示加载状态
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<i class="fa fa-spinner fa-spin mr-2"></i>测试中...';
|
||||
resultEl.className = 'mt-4 text-blue-600';
|
||||
resultEl.innerHTML = '正在发送请求到 /api ...';
|
||||
|
||||
try {
|
||||
// 发送测试请求(根据实际API调整路径)
|
||||
const response = await fetch('/api/system/generator/generator-code/code-first', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ test: 'api-check' }),
|
||||
timeout: 5000
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
resultEl.className = 'mt-4 text-green-600 p-3 bg-green-50 rounded-md';
|
||||
resultEl.innerHTML = `
|
||||
<div class="font-medium mb-1">API代理成功!</div>
|
||||
<pre class="text-sm overflow-x-auto">${JSON.stringify(data, null, 2)}</pre>
|
||||
`;
|
||||
} else {
|
||||
resultEl.className = 'mt-4 text-yellow-600 p-3 bg-yellow-50 rounded-md';
|
||||
resultEl.innerHTML = `
|
||||
<div class="font-medium mb-1">API返回非成功状态 (${response.status})</div>
|
||||
<pre class="text-sm overflow-x-auto">${JSON.stringify(data, null, 2)}</pre>
|
||||
`;
|
||||
}
|
||||
} catch (error) {
|
||||
resultEl.className = 'mt-4 text-red-600 p-3 bg-red-50 rounded-md';
|
||||
resultEl.innerHTML = `
|
||||
<div class="font-medium mb-1">API请求失败</div>
|
||||
<div class="text-sm">${error.message}</div>
|
||||
`;
|
||||
} finally {
|
||||
// 恢复按钮状态
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="fa fa-play-circle mr-2"></i>测试API连接';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,153 +0,0 @@
|
||||
|
||||
#user nobody;
|
||||
|
||||
# 工作进程数:自动匹配CPU核心数(比固定值更灵活)
|
||||
worker_processes auto;
|
||||
# 绑定CPU核心(减少进程切换开销,0,1,2,3对应4核CPU)
|
||||
worker_cpu_affinity 0001 0010 0100 1000;
|
||||
|
||||
# 错误日志:开启并按级别分离(方便问题定位)
|
||||
error_log /var/log/nginx/error.log notice;
|
||||
pid /run/nginx.pid;
|
||||
|
||||
# 全局资源限制(避免文件描述符不足)
|
||||
worker_rlimit_nofile 65535;
|
||||
|
||||
events {
|
||||
# 每个工作进程最大连接数(结合系统ulimit调整)
|
||||
worker_connections 8192;
|
||||
# 高效事件模型(Linux推荐epoll,FreeBSD用kqueue)
|
||||
use epoll;
|
||||
# 一次性接受所有新连接(提高连接处理效率)
|
||||
multi_accept on;
|
||||
}
|
||||
|
||||
|
||||
http {
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
# 日志格式优化:增加响应时间和 upstream 信息(便于性能分析)
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for" '
|
||||
'$request_time $upstream_response_time';
|
||||
# 访问日志:开启(生产环境可按需关闭或调整路径)
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
# 高效文件传输
|
||||
sendfile on;
|
||||
# 静态文件传输优化(配合sendfile使用,减少TCP包数量)
|
||||
tcp_nopush on;
|
||||
# 动态内容优化(减少网络延迟)
|
||||
tcp_nodelay on;
|
||||
|
||||
# 长连接设置
|
||||
keepalive_timeout 65; # 连接超时时间
|
||||
keepalive_requests 1000; # 每个连接最大请求数(防止长连接占用)
|
||||
keepalive_disable msie6; # 对老旧IE禁用长连接
|
||||
|
||||
# 客户端请求限制(防攻击)
|
||||
client_max_body_size 100m; # 缩小上传限制(1024m过大,按需调整)
|
||||
client_body_buffer_size 1m; # 客户端请求体缓冲区(原1024k保留,单位统一)
|
||||
client_body_timeout 120s; # 客户端发送请求体超时
|
||||
client_header_timeout 120s; # 客户端发送请求头超时
|
||||
|
||||
|
||||
# Gzip压缩优化
|
||||
gzip on;
|
||||
gzip_min_length 1k; # 最小压缩尺寸
|
||||
gzip_comp_level 5; # 压缩级别(平衡CPU和带宽)
|
||||
gzip_types
|
||||
text/plain text/css text/xml application/json application/javascript application/x-javascript text/javascript application/xml application/xml+rss text/rss; # 明确需要压缩的类型
|
||||
gzip_disable "MSIE [1-6]\."; # 禁用老旧IE压缩
|
||||
gzip_vary on; # 告诉代理服务器缓存压缩和非压缩版本
|
||||
gzip_buffers 16 8k; # 压缩缓冲区(默认值优化)
|
||||
gzip_http_version 1.1; # 仅对HTTP/1.1及以上启用(避免兼容问题)
|
||||
gzip_proxied any; # 对代理请求也启用压缩
|
||||
|
||||
# 隐藏nginx版本号(安全加固)
|
||||
server_tokens off;
|
||||
|
||||
# 通用安全响应头(全局生效)
|
||||
add_header X-Frame-Options "SAMEORIGIN"; # 防止点击劫持
|
||||
add_header X-XSS-Protection "1; mode=block"; # 防XSS攻击
|
||||
add_header X-Content-Type-Options "nosniff"; # 防止MIME类型嗅探
|
||||
|
||||
# 反向代理配置 http://10.4.126.112:23000
|
||||
upstream upstream_name{
|
||||
server 172.20.0.2:8090;
|
||||
keepalive 32; # 长连接池大小,减少连接建立开销
|
||||
keepalive_timeout 60s;
|
||||
keepalive_requests 1000;
|
||||
}
|
||||
|
||||
# 虚拟主机配置
|
||||
server {
|
||||
listen 8080;
|
||||
listen [::]:8080;
|
||||
# 建议替换为实际域名(如example.com),避免用localhost
|
||||
server_name 10.4.126.116;
|
||||
|
||||
# 网站根目录
|
||||
root /usr/share/nginx/html;
|
||||
# 默认索引文件
|
||||
index index.html;
|
||||
|
||||
# API接口代理配置(优化版)
|
||||
location ^~/api/ {
|
||||
# 代理目标地址
|
||||
proxy_pass http://upstream_name/;
|
||||
|
||||
# 增强头信息转发
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
gzip off; # 禁用该接口的 gzip 压缩
|
||||
proxy_set_header Accept-Encoding ""; # 清除传给后端的 Accept-Encoding 头
|
||||
|
||||
# 超时设置(API专用,可根据业务调整)
|
||||
proxy_connect_timeout 300s;
|
||||
proxy_send_timeout 300s;
|
||||
proxy_read_timeout 300s;
|
||||
|
||||
|
||||
}
|
||||
|
||||
# 主路径匹配
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html; # 适合SPA应用(如Vue/React)
|
||||
# 缓存控制:动态内容不缓存
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
add_header Expires "0";
|
||||
}
|
||||
|
||||
# 静态资源缓存优化(比原配置更精细)
|
||||
location ~* \.(gif|jpg|jpeg|png|ico|svg|js|css|json|txt)$ {
|
||||
# 缓存1天(可根据资源更新频率调整,如图片可设30d)
|
||||
expires 1d;
|
||||
add_header Cache-Control "public, max-age=86400";
|
||||
# 防盗链:只允许指定域名引用资源(替换为实际域名)
|
||||
# valid_referers none blocked localhost example.com *.example.com;
|
||||
# if ($invalid_referer) {
|
||||
# return 403;
|
||||
# }
|
||||
}
|
||||
|
||||
# 错误页面配置
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
# 显式指定root(避免继承外层可能的变更)
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
|
||||
# 禁止访问隐藏文件(如.git .env等)
|
||||
location ~ /\. {
|
||||
deny all;
|
||||
access_log off;
|
||||
log_not_found off;
|
||||
}
|
||||
}
|
||||
}
|
||||
179
docker/server.js
179
docker/server.js
@ -1,179 +0,0 @@
|
||||
const http = require('http');
|
||||
const httpServer = require('http-server');
|
||||
const { createProxyMiddleware } = require('http-proxy-middleware');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { PassThrough } = require('stream');
|
||||
|
||||
const staticRoot = './html';
|
||||
const log = (message) => {
|
||||
const timestamp = new Date().toISOString();
|
||||
console.log(`[${timestamp}] ${message}`);
|
||||
};
|
||||
|
||||
const staticServer = httpServer.createServer({
|
||||
root: staticRoot,
|
||||
showDir: false,
|
||||
cors: true,
|
||||
autoIndex: false
|
||||
});
|
||||
|
||||
const proxyConfig = {
|
||||
path: "/api",
|
||||
target: "http://172.20.0.2:8090"
|
||||
};
|
||||
|
||||
// 跟踪响应是否已处理
|
||||
const responseHandled = new WeakMap();
|
||||
|
||||
const apiProxy = createProxyMiddleware({
|
||||
target: proxyConfig.target,
|
||||
changeOrigin: true,
|
||||
pathRewrite: {'^/api': '' },
|
||||
logLevel: 'silent',
|
||||
timeout: 120000,
|
||||
// 禁用内置的分块处理,手动控制
|
||||
selfHandleResponse: true,
|
||||
onTimeout: (req, res) => {
|
||||
if (!responseHandled.has(res)) {
|
||||
responseHandled.set(res, true);
|
||||
res.writeHead(504, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'Proxy timeout' }));
|
||||
}
|
||||
},
|
||||
on: {
|
||||
proxyReq: (proxyReq, req, res) => {
|
||||
const forwardedUrl = `${proxyReq.protocol}//${proxyReq.host}:${proxyReq.port}${proxyReq.path}`;
|
||||
log(`转发请求到: ${proxyReq.method} ${forwardedUrl}`);
|
||||
log(`请求头: ${JSON.stringify(req.headers, null, 2)}`);
|
||||
|
||||
const contentType = req.headers['content-type'] || '';
|
||||
if (contentType.includes('application/json')) {
|
||||
let requestBody = '';
|
||||
req.on('data', (chunk) => { requestBody += chunk.toString(); });
|
||||
req.on('end', () => { log(`请求体: ${requestBody}`); });
|
||||
}
|
||||
},
|
||||
|
||||
proxyRes: (proxyRes, req, res) => {
|
||||
if (responseHandled.has(res)) {
|
||||
log(`已处理响应,跳过重复处理: ${req.url}`);
|
||||
return;
|
||||
}
|
||||
responseHandled.set(res, false);
|
||||
|
||||
log(`后端响应: ${proxyRes.statusCode} (${req.url})`);
|
||||
log(`响应头: ${JSON.stringify(proxyRes.headers, null, 2)}`);
|
||||
|
||||
const contentType = proxyRes.headers['content-type'] || '';
|
||||
const isChunked = proxyRes.headers['transfer-encoding'] === 'chunked';
|
||||
|
||||
// 复制响应头(排除可能引起冲突的头)
|
||||
if (!res.headersSent) {
|
||||
res.statusCode = proxyRes.statusCode;
|
||||
Object.keys(proxyRes.headers).forEach(key => {
|
||||
// 排除分块传输和连接控制头,由Node.js自动处理
|
||||
if (!['transfer-encoding', 'connection', 'content-length'].includes(key)) {
|
||||
res.setHeader(key, proxyRes.headers[key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 处理JSON响应
|
||||
if (contentType.includes('application/json')) {
|
||||
let responseBody = '';
|
||||
const logStream = new PassThrough();
|
||||
|
||||
// 收集数据用于日志
|
||||
logStream.on('data', (chunk) => {
|
||||
responseBody += chunk.toString();
|
||||
});
|
||||
|
||||
logStream.on('end', () => {
|
||||
log(`响应体: ${responseBody}`);
|
||||
});
|
||||
|
||||
// 分块传输处理
|
||||
if (isChunked) {
|
||||
// 直接通过流转发,保留分块特性
|
||||
proxyRes.pipe(logStream).pipe(res);
|
||||
} else {
|
||||
// 非分块传输,收集完再发送
|
||||
proxyRes.on('data', (chunk) => {
|
||||
responseBody += chunk.toString();
|
||||
});
|
||||
proxyRes.on('end', () => {
|
||||
if (!responseHandled.get(res)) {
|
||||
responseHandled.set(res, true);
|
||||
log(`响应体: ${responseBody}`);
|
||||
res.end(responseBody);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// 非JSON响应直接转发
|
||||
proxyRes.pipe(res);
|
||||
}
|
||||
|
||||
// 监听响应结束
|
||||
proxyRes.on('end', () => {
|
||||
responseHandled.set(res, true);
|
||||
});
|
||||
|
||||
proxyRes.on('error', (err) => {
|
||||
if (!responseHandled.get(res)) {
|
||||
responseHandled.set(res, true);
|
||||
log(`响应错误: ${err.message}`);
|
||||
res.writeHead(500, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: '响应处理错误' }));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
error: (err, req, res) => {
|
||||
if (!responseHandled.has(res) || !responseHandled.get(res)) {
|
||||
responseHandled.set(res, true);
|
||||
log(`代理错误 [${err.code}]: ${err.message} (请求: ${req.url})`);
|
||||
if (!res.headersSent) {
|
||||
res.writeHead(500, { 'Content-Type': 'application/json' });
|
||||
}
|
||||
res.end(JSON.stringify({
|
||||
error: '代理服务错误',
|
||||
message: err.message
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
if (req.url.startsWith(proxyConfig.path)) {
|
||||
log(`收到代理请求: ${req.method} ${req.url}`);
|
||||
return apiProxy(req, res);
|
||||
}
|
||||
else if (req.url === '' || req.url === '/' || req.url.endsWith('/')) {
|
||||
log(`访问根路径,返回index.html: ${req.url}`);
|
||||
const indexPath = path.join(staticRoot, 'index.html');
|
||||
|
||||
if (fs.existsSync(indexPath)) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/html' });
|
||||
fs.createReadStream(indexPath).pipe(res);
|
||||
} else {
|
||||
log(`警告: index.html不存在于 ${staticRoot}`);
|
||||
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
||||
res.end('index.html not found');
|
||||
}
|
||||
}
|
||||
else {
|
||||
log(`处理静态文件请求: ${req.url}`);
|
||||
staticServer.server.emit('request', req, res);
|
||||
}
|
||||
});
|
||||
|
||||
const port = 8080;
|
||||
server.listen(port, () => {
|
||||
log(`静态服务器已启动,监听端口 ${port}`);
|
||||
log(`静态文件目录: ${path.resolve(staticRoot)}`);
|
||||
log(`API代理目标: ${proxyConfig.target}`);
|
||||
log(`访问地址: http://localhost:${port}`);
|
||||
});
|
||||
Reference in New Issue
Block a user