init: 信创文件安全传输系统内外网本地 Docker 部署
包含 s-file 后端、React 前端、MeshSafe 安全检测对接,以及内外网两套 compose/nginx/配置(仅前端 28180/28280 对外发布,其余端口不暴露)。 说明:deploy/config/application-iw.yml 与 application-ow.yml 按维护者 明确要求原样入库,其中含本机 Docker 用的库口令、RabbitMQ 口令与 JWT 密钥;上生产前需替换。
This commit is contained in:
115
deploy/config/application-iw.yml
Normal file
115
deploy/config/application-iw.yml
Normal file
@@ -0,0 +1,115 @@
|
||||
# s-file 内网侧(file.transfer.location=1)本地联调配置
|
||||
# 仅本机 docker 部署使用,密码与 JWT secret 不要带到生产
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: s-file
|
||||
datasource:
|
||||
url: jdbc:postgresql://pg-iw:5432/s_file
|
||||
driver-class-name: org.postgresql.Driver
|
||||
username: sfile
|
||||
password: sfile_iw_2026
|
||||
druid:
|
||||
initial-size: 2
|
||||
min-idle: 2
|
||||
max-active: 10
|
||||
validation-query: SELECT 1
|
||||
redis:
|
||||
host: redis-iw
|
||||
port: 6379
|
||||
timeout: 5000
|
||||
lettuce:
|
||||
pool:
|
||||
max-active: 8
|
||||
max-idle: 4
|
||||
rabbitmq:
|
||||
host: mq-iw
|
||||
port: 5672
|
||||
username: sfile
|
||||
password: sfile_mq_iw_2026
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 500MB
|
||||
max-request-size: 500MB
|
||||
main:
|
||||
allow-bean-definition-overriding: true
|
||||
|
||||
mybatis-plus:
|
||||
mapper-locations: classpath*:com/senmesh/**/mapper/*.xml
|
||||
|
||||
# 平台编码(init-data 中 uc_platform.platform_code=1)
|
||||
system:
|
||||
code: 1
|
||||
|
||||
# 发号器雪花 workerId:键为 compose 中固定的容器 IP
|
||||
"[code.snowflake.workid.172.25.18.10]": 1
|
||||
|
||||
security:
|
||||
jwt:
|
||||
enable: true
|
||||
secret: sfile-local-iw-secret-key-please-change-0123456789abcdef
|
||||
expiration: 86400000
|
||||
header: Authorization
|
||||
ignoring: /error,/favicon.ico
|
||||
login-page: /login
|
||||
superior-only: false
|
||||
use-local: true
|
||||
allow-if-all-abstain-decisions: true
|
||||
oauth2:
|
||||
enable: false
|
||||
not-logs: ""
|
||||
anonymous: ""
|
||||
|
||||
# 新用户初始密码
|
||||
user:
|
||||
default:
|
||||
password: Qaz123567!@#
|
||||
|
||||
file:
|
||||
upload-dir: /data/upload
|
||||
storage:
|
||||
type: local
|
||||
transfer:
|
||||
# 内网侧
|
||||
location: "1"
|
||||
# send-dir 与外网侧 receive-dir 指向同一摆渡目录,反之亦然
|
||||
send-dir: /data/transfer/send
|
||||
receive-dir: /data/transfer/receive
|
||||
unpack-risk-check: false
|
||||
|
||||
dify:
|
||||
risk:
|
||||
rule:
|
||||
api-key: test-rule-key
|
||||
scan:
|
||||
api-key: test-scan-key
|
||||
|
||||
# MeshSafe 文件安全检测:复用本机已部署的 meshsafe-nginx(44380)
|
||||
# 凭证需存在于 MeshSafe .env 的 MESHSAFE_API_CLIENTS 白名单中
|
||||
meshsafe:
|
||||
enabled: true
|
||||
base-url: http://host.docker.internal:44380
|
||||
client-id: test-client
|
||||
secret: test-secret
|
||||
channel: sfile-iw
|
||||
timeout-seconds: 120
|
||||
fail-open: true
|
||||
poll-timeout-minutes: 60
|
||||
review-sync-to-meshsafe: true
|
||||
poll:
|
||||
cron: "0/10 * * * * ?"
|
||||
|
||||
rabbitmq:
|
||||
custom:
|
||||
topic-exchange-name: s-file-iw-topic
|
||||
delayed-exchange-name: s-file-iw-delayed
|
||||
broadcast-id: s-file-iw-broadcast
|
||||
|
||||
swagger:
|
||||
enable: false
|
||||
|
||||
logging:
|
||||
level:
|
||||
com.senmesh: info
|
||||
115
deploy/config/application-ow.yml
Normal file
115
deploy/config/application-ow.yml
Normal file
@@ -0,0 +1,115 @@
|
||||
# s-file 外网侧(file.transfer.location=2)本地联调配置
|
||||
# 仅本机 docker 部署使用,密码与 JWT secret 不要带到生产
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: s-file
|
||||
datasource:
|
||||
url: jdbc:postgresql://pg-ow:5432/s_file
|
||||
driver-class-name: org.postgresql.Driver
|
||||
username: sfile
|
||||
password: sfile_ow_2026
|
||||
druid:
|
||||
initial-size: 2
|
||||
min-idle: 2
|
||||
max-active: 10
|
||||
validation-query: SELECT 1
|
||||
redis:
|
||||
host: redis-ow
|
||||
port: 6379
|
||||
timeout: 5000
|
||||
lettuce:
|
||||
pool:
|
||||
max-active: 8
|
||||
max-idle: 4
|
||||
rabbitmq:
|
||||
host: mq-ow
|
||||
port: 5672
|
||||
username: sfile
|
||||
password: sfile_mq_ow_2026
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 500MB
|
||||
max-request-size: 500MB
|
||||
main:
|
||||
allow-bean-definition-overriding: true
|
||||
|
||||
mybatis-plus:
|
||||
mapper-locations: classpath*:com/senmesh/**/mapper/*.xml
|
||||
|
||||
# 平台编码(init-data 中 uc_platform.platform_code=1)
|
||||
system:
|
||||
code: 1
|
||||
|
||||
# 发号器雪花 workerId:键为 compose 中固定的容器 IP;两侧 workerId 必须不同
|
||||
"[code.snowflake.workid.172.25.28.10]": 2
|
||||
|
||||
security:
|
||||
jwt:
|
||||
enable: true
|
||||
secret: sfile-local-ow-secret-key-please-change-0123456789abcdef
|
||||
expiration: 86400000
|
||||
header: Authorization
|
||||
ignoring: /error,/favicon.ico
|
||||
login-page: /login
|
||||
superior-only: false
|
||||
use-local: true
|
||||
allow-if-all-abstain-decisions: true
|
||||
oauth2:
|
||||
enable: false
|
||||
not-logs: ""
|
||||
anonymous: ""
|
||||
|
||||
# 新用户初始密码
|
||||
user:
|
||||
default:
|
||||
password: Qaz123567!@#
|
||||
|
||||
file:
|
||||
upload-dir: /data/upload
|
||||
storage:
|
||||
type: local
|
||||
transfer:
|
||||
# 外网侧:与内网侧相反
|
||||
location: "2"
|
||||
# compose 里 send-dir 挂到 gate/ow2iw、receive-dir 挂到 gate/iw2ow
|
||||
send-dir: /data/transfer/send
|
||||
receive-dir: /data/transfer/receive
|
||||
unpack-risk-check: false
|
||||
|
||||
dify:
|
||||
risk:
|
||||
rule:
|
||||
api-key: test-rule-key
|
||||
scan:
|
||||
api-key: test-scan-key
|
||||
|
||||
# MeshSafe 文件安全检测:复用本机已部署的 meshsafe-nginx(44380)
|
||||
# 凭证需存在于 MeshSafe .env 的 MESHSAFE_API_CLIENTS 白名单中
|
||||
meshsafe:
|
||||
enabled: true
|
||||
base-url: http://host.docker.internal:44380
|
||||
client-id: test-client
|
||||
secret: test-secret
|
||||
channel: sfile-ow
|
||||
timeout-seconds: 120
|
||||
fail-open: true
|
||||
poll-timeout-minutes: 60
|
||||
review-sync-to-meshsafe: true
|
||||
poll:
|
||||
cron: "0/10 * * * * ?"
|
||||
|
||||
rabbitmq:
|
||||
custom:
|
||||
topic-exchange-name: s-file-ow-topic
|
||||
delayed-exchange-name: s-file-ow-delayed
|
||||
broadcast-id: s-file-ow-broadcast
|
||||
|
||||
swagger:
|
||||
enable: false
|
||||
|
||||
logging:
|
||||
level:
|
||||
com.senmesh: info
|
||||
252
deploy/docker-compose.yml
Normal file
252
deploy/docker-compose.yml
Normal file
@@ -0,0 +1,252 @@
|
||||
# =============================================================================
|
||||
# s-file 信创文件安全传输系统 —— 本机 docker 部署(内网侧 + 外网侧两套)
|
||||
#
|
||||
# 启动:
|
||||
# docker build -f deploy/docker/Dockerfile.app -t sfile-app:local backend/target
|
||||
# docker build -f deploy/docker/Dockerfile.web -t sfile-web:local frontend
|
||||
# cd deploy && docker compose up -d
|
||||
#
|
||||
# 端口:
|
||||
# 内网前端 http://<本机IP>:28180 外网前端 http://<本机IP>:28280
|
||||
# 其余(后端/postgres/redis/rabbitmq)只在容器网络内通信,不映射宿主机端口
|
||||
# MeshSafe 复用本机已运行的 meshsafe-nginx:44380(两套后端都经 host.docker.internal 调用)
|
||||
#
|
||||
# 摆渡目录(模拟光闸):
|
||||
# gate/iw2ow = 内网 send + 外网 receive gate/ow2iw = 外网 send + 内网 receive
|
||||
# =============================================================================
|
||||
|
||||
name: sfile
|
||||
|
||||
services:
|
||||
# ===================== 内网侧 =====================
|
||||
web-iw:
|
||||
image: sfile-web:local
|
||||
container_name: sfile-iw-web
|
||||
restart: unless-stopped
|
||||
networks: [iw_net]
|
||||
ports:
|
||||
- "0.0.0.0:28180:80"
|
||||
volumes:
|
||||
- ./nginx/web-iw.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
depends_on:
|
||||
app-iw:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -sf -o /dev/null http://127.0.0.1:80/ || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
|
||||
app-iw:
|
||||
image: sfile-app:local
|
||||
container_name: sfile-iw-app
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
iw_net:
|
||||
ipv4_address: 172.25.18.10
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
SPRING_CONFIG_ADDITIONAL_LOCATION: file:/config/application.yml
|
||||
CODE_SNOWFLAKE_WORKID_172_25_18_10: "1"
|
||||
volumes:
|
||||
- ./config/application-iw.yml:/config/application.yml:ro
|
||||
- ${SFILE_DATA:-D:/zcodedata/sfile}/iw/upload:/data/upload
|
||||
- ${SFILE_DATA:-D:/zcodedata/sfile}/iw/logs:/app/logs
|
||||
- ${SFILE_DATA:-D:/zcodedata/sfile}/gate/iw2ow:/data/transfer/send
|
||||
- ${SFILE_DATA:-D:/zcodedata/sfile}/gate/ow2iw:/data/transfer/receive
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
depends_on:
|
||||
pg-iw:
|
||||
condition: service_healthy
|
||||
redis-iw:
|
||||
condition: service_healthy
|
||||
mq-iw:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -s --max-time 5 -o /dev/null http://127.0.0.1:8080/ || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 8s
|
||||
retries: 5
|
||||
start_period: 150s
|
||||
|
||||
pg-iw:
|
||||
image: postgres:latest
|
||||
container_name: sfile-iw-pg
|
||||
restart: unless-stopped
|
||||
networks: [iw_net]
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
POSTGRES_DB: s_file
|
||||
POSTGRES_USER: sfile
|
||||
POSTGRES_PASSWORD: sfile_iw_2026
|
||||
volumes:
|
||||
# postgres:latest 为 18.x,数据目录挂载点已改为 /var/lib/postgresql
|
||||
- sfile-pgdata-iw:/var/lib/postgresql
|
||||
- ../backend/db/init-table-postgres-full.sql:/docker-entrypoint-initdb.d/01-init-table.sql:ro
|
||||
- ../backend/db/init-data.sql:/docker-entrypoint-initdb.d/02-init-data.sql:ro
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U sfile -d s_file"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
start_period: 40s
|
||||
|
||||
redis-iw:
|
||||
image: redis:latest
|
||||
container_name: sfile-iw-redis
|
||||
restart: unless-stopped
|
||||
networks: [iw_net]
|
||||
command: ["redis-server", "--appendonly", "yes", "--maxmemory", "256mb", "--maxmemory-policy", "allkeys-lru"]
|
||||
volumes:
|
||||
- sfile-redis-iw:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
|
||||
mq-iw:
|
||||
image: rabbitmq:3-management-alpine
|
||||
container_name: sfile-iw-mq
|
||||
restart: unless-stopped
|
||||
networks: [iw_net]
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
RABBITMQ_DEFAULT_USER: sfile
|
||||
RABBITMQ_DEFAULT_PASS: sfile_mq_iw_2026
|
||||
volumes:
|
||||
- sfile-mq-iw:/var/lib/rabbitmq
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "rabbitmq-diagnostics -q ping"]
|
||||
interval: 20s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
start_period: 40s
|
||||
|
||||
# ===================== 外网侧 =====================
|
||||
web-ow:
|
||||
image: sfile-web:local
|
||||
container_name: sfile-ow-web
|
||||
restart: unless-stopped
|
||||
networks: [ow_net]
|
||||
ports:
|
||||
- "0.0.0.0:28280:80"
|
||||
volumes:
|
||||
- ./nginx/web-ow.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
depends_on:
|
||||
app-ow:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -sf -o /dev/null http://127.0.0.1:80/ || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
|
||||
app-ow:
|
||||
image: sfile-app:local
|
||||
container_name: sfile-ow-app
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
ow_net:
|
||||
ipv4_address: 172.25.28.10
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
SPRING_CONFIG_ADDITIONAL_LOCATION: file:/config/application.yml
|
||||
CODE_SNOWFLAKE_WORKID_172_25_28_10: "2"
|
||||
volumes:
|
||||
- ./config/application-ow.yml:/config/application.yml:ro
|
||||
- ${SFILE_DATA:-D:/zcodedata/sfile}/ow/upload:/data/upload
|
||||
- ${SFILE_DATA:-D:/zcodedata/sfile}/ow/logs:/app/logs
|
||||
- ${SFILE_DATA:-D:/zcodedata/sfile}/gate/ow2iw:/data/transfer/send
|
||||
- ${SFILE_DATA:-D:/zcodedata/sfile}/gate/iw2ow:/data/transfer/receive
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
depends_on:
|
||||
pg-ow:
|
||||
condition: service_healthy
|
||||
redis-ow:
|
||||
condition: service_healthy
|
||||
mq-ow:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -s --max-time 5 -o /dev/null http://127.0.0.1:8080/ || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 8s
|
||||
retries: 5
|
||||
start_period: 150s
|
||||
|
||||
pg-ow:
|
||||
image: postgres:latest
|
||||
container_name: sfile-ow-pg
|
||||
restart: unless-stopped
|
||||
networks: [ow_net]
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
POSTGRES_DB: s_file
|
||||
POSTGRES_USER: sfile
|
||||
POSTGRES_PASSWORD: sfile_ow_2026
|
||||
volumes:
|
||||
- sfile-pgdata-ow:/var/lib/postgresql
|
||||
- ../backend/db/init-table-postgres-full.sql:/docker-entrypoint-initdb.d/01-init-table.sql:ro
|
||||
- ../backend/db/init-data.sql:/docker-entrypoint-initdb.d/02-init-data.sql:ro
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U sfile -d s_file"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
start_period: 40s
|
||||
|
||||
redis-ow:
|
||||
image: redis:latest
|
||||
container_name: sfile-ow-redis
|
||||
restart: unless-stopped
|
||||
networks: [ow_net]
|
||||
command: ["redis-server", "--appendonly", "yes", "--maxmemory", "256mb", "--maxmemory-policy", "allkeys-lru"]
|
||||
volumes:
|
||||
- sfile-redis-ow:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
|
||||
mq-ow:
|
||||
image: rabbitmq:3-management-alpine
|
||||
container_name: sfile-ow-mq
|
||||
restart: unless-stopped
|
||||
networks: [ow_net]
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
RABBITMQ_DEFAULT_USER: sfile
|
||||
RABBITMQ_DEFAULT_PASS: sfile_mq_ow_2026
|
||||
volumes:
|
||||
- sfile-mq-ow:/var/lib/rabbitmq
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "rabbitmq-diagnostics -q ping"]
|
||||
interval: 20s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
start_period: 40s
|
||||
|
||||
networks:
|
||||
iw_net:
|
||||
driver: bridge
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 172.25.18.0/24
|
||||
gateway: 172.25.18.1
|
||||
ow_net:
|
||||
driver: bridge
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 172.25.28.0/24
|
||||
gateway: 172.25.28.1
|
||||
|
||||
volumes:
|
||||
sfile-pgdata-iw:
|
||||
sfile-redis-iw:
|
||||
sfile-mq-iw:
|
||||
sfile-pgdata-ow:
|
||||
sfile-redis-ow:
|
||||
sfile-mq-ow:
|
||||
15
deploy/docker/Dockerfile.app
Normal file
15
deploy/docker/Dockerfile.app
Normal file
@@ -0,0 +1,15 @@
|
||||
# s-file 后端运行镜像(构建上下文 = backend/target,内含 s-file.jar)
|
||||
# 用 JVM 参数 -Xmx 控制内存,配置文件由容器挂载 /config/application.yml
|
||||
FROM eclipse-temurin:8-jre-jammy
|
||||
|
||||
ENV TZ=Asia/Shanghai
|
||||
ENV JAVA_OPTS="-Xms256m -Xmx1g -Dfile.encoding=UTF-8"
|
||||
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
WORKDIR /app
|
||||
COPY s-file.jar /app/s-file.jar
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENTRYPOINT ["sh", "-c", "exec java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app/s-file.jar"]
|
||||
28
deploy/docker/Dockerfile.web
Normal file
28
deploy/docker/Dockerfile.web
Normal file
@@ -0,0 +1,28 @@
|
||||
# 前端镜像:一次构建同时产出内网包与外网包
|
||||
# 构建上下文 = frontend/ (见 frontend/.dockerignore)
|
||||
# 站点根:/usr/share/nginx/inside(内网)、/usr/share/nginx/outside(外网)
|
||||
# 由两套 nginx 容器各挂一份 conf 选择站点根,镜像只读共用
|
||||
FROM node:20 AS build
|
||||
|
||||
WORKDIR /src
|
||||
COPY package.json ./
|
||||
RUN npm config set registry https://registry.npmmirror.com \
|
||||
&& npm install --no-audit --no-fund
|
||||
|
||||
COPY . .
|
||||
|
||||
# .env.production.local 优先级高于 .env.production,用它固化两套包的差异:
|
||||
# VITE_API_URL=/api —— 走同域 /api,由 nginx 反代时去掉前缀
|
||||
# VITE_IS_INSIDE —— true 内网包 / false 外网包
|
||||
RUN set -eux; \
|
||||
printf 'VITE_API_URL=/api\nVITE_IS_INSIDE=true\n' > .env.production.local; \
|
||||
npx vite build --outDir dist/inside; \
|
||||
printf 'VITE_API_URL=/api\nVITE_IS_INSIDE=false\n' > .env.production.local; \
|
||||
npx vite build --outDir dist/outside
|
||||
|
||||
FROM nginx:alpine
|
||||
|
||||
COPY --from=build /src/dist/inside /usr/share/nginx/inside
|
||||
COPY --from=build /src/dist/outside /usr/share/nginx/outside
|
||||
|
||||
EXPOSE 80
|
||||
37
deploy/nginx/web-iw.conf
Normal file
37
deploy/nginx/web-iw.conf
Normal file
@@ -0,0 +1,37 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
charset utf-8;
|
||||
|
||||
root /usr/share/nginx/inside;
|
||||
index index.html;
|
||||
|
||||
# 与后端 multipart 上限(500MB)保持一致
|
||||
client_max_body_size 500m;
|
||||
|
||||
# /api/xxx -> 后端 /xxx(去掉 /api 前缀,与 vite dev 代理行为一致)
|
||||
location /api/ {
|
||||
proxy_pass http://app-iw:8080/;
|
||||
proxy_http_version 1.1;
|
||||
# 必须保留端口($http_host):后端 getRootDomain 对无端口的 localhost/IP 会抛
|
||||
# IllegalStateException 导致 /login 成功回调 500(带端口时按非法域名返回 null,Cookie 不带 domain)
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_read_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
proxy_request_buffering off;
|
||||
}
|
||||
|
||||
# SPA:hash 路由,静态资源找不到时回落 index.html
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location ~* \.(js|mjs|css|woff2?|ttf|png|jpe?g|gif|svg|ico)$ {
|
||||
expires 7d;
|
||||
access_log off;
|
||||
}
|
||||
}
|
||||
37
deploy/nginx/web-ow.conf
Normal file
37
deploy/nginx/web-ow.conf
Normal file
@@ -0,0 +1,37 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
charset utf-8;
|
||||
|
||||
root /usr/share/nginx/outside;
|
||||
index index.html;
|
||||
|
||||
# 与后端 multipart 上限(500MB)保持一致
|
||||
client_max_body_size 500m;
|
||||
|
||||
# /api/xxx -> 后端 /xxx(去掉 /api 前缀,与 vite dev 代理行为一致)
|
||||
location /api/ {
|
||||
proxy_pass http://app-ow:8080/;
|
||||
proxy_http_version 1.1;
|
||||
# 必须保留端口($http_host):后端 getRootDomain 对无端口的 localhost/IP 会抛
|
||||
# IllegalStateException 导致 /login 成功回调 500(带端口时按非法域名返回 null,Cookie 不带 domain)
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_read_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
proxy_request_buffering off;
|
||||
}
|
||||
|
||||
# SPA:hash 路由,静态资源找不到时回落 index.html
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location ~* \.(js|mjs|css|woff2?|ttf|png|jpe?g|gif|svg|ico)$ {
|
||||
expires 7d;
|
||||
access_log off;
|
||||
}
|
||||
}
|
||||
93
deploy/部署说明.md
Normal file
93
deploy/部署说明.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# s-file 本机 docker 部署说明(内网侧 + 外网侧两套)
|
||||
|
||||
> 环境:Windows + Docker Desktop 4.54,MeshSafe 复用本机已运行的 `meshsafe-nginx:44380`
|
||||
> 编排文件:`deploy/docker-compose.yml`
|
||||
|
||||
## 1. 访问入口
|
||||
|
||||
| 侧 | 地址 | 前端包 | 后端 `file.transfer.location` |
|
||||
|---|---|---|---|
|
||||
| 内网 | http://本机IP:28180 | `VITE_IS_INSIDE=true` | `1` |
|
||||
| 外网 | http://本机IP:28280 | `VITE_IS_INSIDE=false` | `2` |
|
||||
|
||||
只有这两个 80 端口映射到宿主机(`0.0.0.0`,便于局域网真机联调)。后端 8080、PostgreSQL、Redis、RabbitMQ 全部只在容器网络内通信,**不映射宿主机端口**;nginx 通过 `location /api/ → http://app-xx:8080/` 反代并去掉 `/api` 前缀。
|
||||
|
||||
## 2. 容器与网段
|
||||
|
||||
| 内网侧 | 外网侧 | 说明 |
|
||||
|---|---|---|
|
||||
| `sfile-iw-web` | `sfile-ow-web` | nginx:alpine(本机已有) |
|
||||
| `sfile-iw-app` | `sfile-ow-app` | sfile-app:local(eclipse-temurin:8-jre-jammy + s-file.jar) |
|
||||
| `sfile-iw-pg` | `sfile-ow-pg` | postgres:latest(本机已有,18.3) |
|
||||
| `sfile-iw-redis` | `sfile-ow-redis` | redis:latest(本机已有) |
|
||||
| `sfile-iw-mq` | `sfile-ow-mq` | rabbitmq:3-management-alpine(本机已有) |
|
||||
| 网络 `sfile_iw_net` 172.25.18.0/24 | 网络 `sfile_ow_net` 172.25.28.0/24 | 两侧物理隔离,仅通过摆渡目录互通 |
|
||||
|
||||
app 容器固定 IP(`172.25.18.10` / `172.25.28.10`),对应雪花 workerId `1` / `2`,两侧必须不同;改网段时同步改 `deploy/config/application-*.yml` 里的 `[code.snowflake.workid.<ip>]` 与 compose 的 `CODE_SNOWFLAKE_WORKID_*` 环境变量。
|
||||
|
||||
## 3. 摆渡目录(模拟光闸)
|
||||
|
||||
宿主机根目录:`D:/zcodedata/sfile/`(可用 `.env` 的 `SFILE_DATA` 覆盖)
|
||||
|
||||
| 目录 | 内网侧挂载 | 外网侧挂载 |
|
||||
|---|---|---|
|
||||
| `gate/iw2ow` | `/data/transfer/send` | `/data/transfer/receive` |
|
||||
| `gate/ow2iw` | `/data/transfer/receive` | `/data/transfer/send` |
|
||||
| `iw/upload`、`ow/upload` | 各自 `/data/upload` | 网盘物理文件,两侧独立 |
|
||||
|
||||
内网「我的发送」写入 `gate/iw2ow`,外网侧 `TransferFileMonitorJob` 扫描自己的 receive 目录解包入库并触发 MeshSafe 送检,反方向同理。
|
||||
|
||||
## 4. MeshSafe 对接
|
||||
|
||||
`deploy/config/application-iw.yml` / `application-ow.yml`:
|
||||
|
||||
```yaml
|
||||
meshsafe:
|
||||
enabled: true
|
||||
base-url: http://host.docker.internal:44380 # 本机 meshsafe-nginx
|
||||
client-id: test-client # 需在 MeshSafe .env 的 MESHSAFE_API_CLIENTS 白名单内
|
||||
secret: test-secret
|
||||
fail-open: true # 本地联调优先可用性;安全优先部署改 false
|
||||
```
|
||||
|
||||
compose 给 app 容器加了 `extra_hosts: host.docker.internal:host-gateway`。上线前把两侧 channel 区分(`sfile-iw` / `sfile-ow`)并保持 MeshSafe 策略 `engine_failure_mode: fail_closed`。
|
||||
|
||||
## 5. 构建与启动
|
||||
|
||||
```bash
|
||||
# 1) 后端 jar(需 JDK8 + Maven,本机已装在 D:/dev/jdk8、D:/dev/maven-tmp)
|
||||
cd backend
|
||||
JAVA_HOME=D:/dev/jdk8/jdk8u504-b01 mvn -s D:/dev/maven-conf/settings.xml clean package -DskipTests
|
||||
|
||||
# 2) 镜像
|
||||
docker build -f deploy/docker/Dockerfile.app -t sfile-app:local backend/target
|
||||
docker build -f deploy/docker/Dockerfile.web -t sfile-web:local frontend # 一次产出内网+外网两个站点
|
||||
|
||||
# 3) 启动(首次会执行 backend/db 的 PG 初始化脚本建 62 张表)
|
||||
cd deploy && docker compose up -d
|
||||
```
|
||||
|
||||
常用运维:
|
||||
|
||||
```bash
|
||||
docker compose ps # 全部 10 个容器应为 healthy
|
||||
docker compose restart app-iw app-ow # 改配置后重启后端
|
||||
docker compose logs -f app-iw
|
||||
docker compose down # 停止(保留数据卷与摆渡目录)
|
||||
```
|
||||
|
||||
## 6. 已知取舍
|
||||
|
||||
- **spire.pdf 3.7.4**:公共仓库无此版本,本机用按 `PdfUtils.java` 引用签名手写的桩 jar(`~/.m2/repository/e-iceblue/spire.pdf/3.7.4/`)保证编译打包通过;**PDF 盖章功能运行时不可用**,正式环境需替换公司私服的真实 jar。
|
||||
- **前端 `VITE_API_URL=/api`**:`.env.production` 原值为空,容器构建时用 `.env.production.local` 固化为 `/api`,与 nginx 反代规则和 vite dev 行为保持一致;改构建口径需同步改 `Dockerfile.web`。
|
||||
- **postgres:latest 为 18.x**:数据目录挂载点已从 `/var/lib/postgresql/data` 改为 `/var/lib/postgresql`,挂错路径容器会反复重启。
|
||||
- **配置与口令仅用于本地联调**:JWT secret、数据库/RabbitMQ 密码为占位值,勿带到生产。
|
||||
- 数据库卷为命名卷(`sfile_sfile-pgdata-iw` / `-ow`);重新初始化需删卷后 `docker compose up -d`。
|
||||
|
||||
## 7. 验收清单
|
||||
|
||||
1. 两套登录页可访问(28180 / 28280),登录后内网侧菜单为「我的接收 / 我的发送」、外网侧相反;
|
||||
2. 上传文件后「安全检测」列出现「检测中」→ 数秒内变「检测通过/不通过」(`docker exec sfile-iw-pg psql -U sfile -d s_file -c 'select * from x_file_security order by id desc limit 5;'`);
|
||||
3. 检测不通过的文件分享/跨网传输被拒;文件夹部分不通过时确认后可只传通过项;
|
||||
4. 内网发送到外网接收全链路走通,接收端解包后自动送检;
|
||||
5. 管理端人工复核改判后文件恢复可分享/可传输。
|
||||
Reference in New Issue
Block a user