mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-01 23:15:27 +08:00
build: improve Docker robustness and fix potential security issues
This commit is contained in:
12
Dockerfile
12
Dockerfile
@@ -28,6 +28,8 @@ FROM debian:bookworm-slim AS runtime-base
|
||||
WORKDIR /app
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends ca-certificates \
|
||||
&& groupadd -r ds2api && useradd -r -g ds2api -d /app -s /sbin/nologin ds2api \
|
||||
&& mkdir -p /app/data && chown -R ds2api:ds2api /app \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
COPY --from=busybox-tools /bin/busybox /usr/local/bin/busybox
|
||||
EXPOSE 5001
|
||||
@@ -36,8 +38,9 @@ CMD ["/usr/local/bin/ds2api"]
|
||||
FROM runtime-base AS runtime-from-source
|
||||
COPY --from=go-builder /out/ds2api /usr/local/bin/ds2api
|
||||
|
||||
COPY --from=go-builder /app/config.example.json /app/config.example.json
|
||||
COPY --from=webui-builder /app/static/admin /app/static/admin
|
||||
COPY --from=go-builder --chown=ds2api:ds2api /app/config.example.json /app/config.example.json
|
||||
COPY --from=webui-builder --chown=ds2api:ds2api /app/static/admin /app/static/admin
|
||||
USER ds2api
|
||||
|
||||
FROM busybox-tools AS dist-extract
|
||||
ARG TARGETARCH
|
||||
@@ -60,7 +63,8 @@ RUN set -eux; \
|
||||
FROM runtime-base AS runtime-from-dist
|
||||
COPY --from=dist-extract /out/ds2api /usr/local/bin/ds2api
|
||||
|
||||
COPY --from=dist-extract /out/config.example.json /app/config.example.json
|
||||
COPY --from=dist-extract /out/static/admin /app/static/admin
|
||||
COPY --from=dist-extract --chown=ds2api:ds2api /out/config.example.json /app/config.example.json
|
||||
COPY --from=dist-extract --chown=ds2api:ds2api /out/static/admin /app/static/admin
|
||||
USER ds2api
|
||||
|
||||
FROM runtime-from-source AS final
|
||||
|
||||
@@ -35,8 +35,9 @@ func main() {
|
||||
}
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: "0.0.0.0:" + port,
|
||||
Handler: app.Router,
|
||||
Addr: "0.0.0.0:" + port,
|
||||
Handler: app.Router,
|
||||
ReadHeaderTimeout: 5 * time.Second,
|
||||
}
|
||||
localURL := fmt.Sprintf("http://127.0.0.1:%s", port)
|
||||
lanIP := detectLANIPv4()
|
||||
|
||||
56
start.mjs
56
start.mjs
@@ -126,9 +126,12 @@ function binaryExists() {
|
||||
|
||||
// 查找占用端口的进程 PID
|
||||
function findPidByPort(port) {
|
||||
const numericPort = parseInt(port, 10);
|
||||
if (isNaN(numericPort)) return [];
|
||||
|
||||
try {
|
||||
if (isWindows) {
|
||||
const output = execSync(`netstat -ano | findstr :${port} | findstr LISTENING`, {
|
||||
const output = execSync(`netstat -ano | findstr :${numericPort} | findstr LISTENING`, {
|
||||
encoding: 'utf-8',
|
||||
shell: true,
|
||||
stdio: ['pipe', 'pipe', 'ignore'],
|
||||
@@ -141,7 +144,7 @@ function findPidByPort(port) {
|
||||
}
|
||||
return [...pids];
|
||||
} else {
|
||||
const output = execSync(`lsof -ti :${port}`, {
|
||||
const output = execSync(`lsof -ti :${numericPort}`, {
|
||||
encoding: 'utf-8',
|
||||
stdio: ['pipe', 'pipe', 'ignore'],
|
||||
});
|
||||
@@ -217,7 +220,7 @@ async function installFrontendDeps() {
|
||||
const proc = spawn('npm', ['ci', '--registry', MIRRORS.npm], {
|
||||
cwd: CONFIG.webuiDir,
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
shell: isWindows,
|
||||
});
|
||||
proc.on('close', code => code === 0 ? resolve() : reject(new Error('前端依赖安装失败')));
|
||||
});
|
||||
@@ -239,7 +242,7 @@ async function buildBackend() {
|
||||
const proc = spawn('go', ['build', '-o', BINARY, './cmd/ds2api'], {
|
||||
cwd: __dirname,
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
shell: isWindows,
|
||||
env: { ...process.env, GOPROXY: MIRRORS.goproxy },
|
||||
});
|
||||
proc.on('close', code => code === 0 ? resolve() : reject(new Error('后端编译失败')));
|
||||
@@ -257,22 +260,21 @@ async function buildWebui() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const proc = spawn(
|
||||
'npm', ['run', 'build', '--', '--outDir', CONFIG.staticAdminDir, '--emptyOutDir'],
|
||||
{ cwd: CONFIG.webuiDir, stdio: 'inherit', shell: true }
|
||||
{ cwd: CONFIG.webuiDir, stdio: 'inherit', shell: isWindows }
|
||||
);
|
||||
proc.on('close', code => code === 0 ? resolve() : reject(new Error('前端构建失败')));
|
||||
});
|
||||
}
|
||||
|
||||
// 启动后端(开发模式:go run,无需预编译)
|
||||
async function startBackendDev() {
|
||||
if (!checkGo()) throw new Error('未找到 Go,请先安装 Go (https://go.dev/dl/)');
|
||||
log.info(`启动后端(go run)... 本地 http://127.0.0.1:${CONFIG.port} 绑定 0.0.0.0:${CONFIG.port}`);
|
||||
const proc = spawn('go', ['run', './cmd/ds2api'], {
|
||||
async function startBackendDev() {
|
||||
if (!checkGo()) throw new Error('未找到 Go,请先安装 Go (https://go.dev/dl/)');
|
||||
log.info(`启动后端(go run)... 本地 http://127.0.0.1:${CONFIG.port} 绑定 0.0.0.0:${CONFIG.port}`);
|
||||
const proc = spawn('go', ['run', './cmd/ds2api'], {
|
||||
cwd: __dirname,
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
env: {
|
||||
...process.env,
|
||||
shell: isWindows,
|
||||
env: { ...process.env,
|
||||
PORT: CONFIG.port,
|
||||
LOG_LEVEL: CONFIG.logLevel,
|
||||
DS2API_ADMIN_KEY: CONFIG.adminKey,
|
||||
@@ -284,13 +286,13 @@ async function startBackendDev() {
|
||||
}
|
||||
|
||||
// 启动后端(生产模式:运行编译好的二进制)
|
||||
async function startBackendProd() {
|
||||
if (!binaryExists()) {
|
||||
log.warn('未找到编译产物,正在编译...');
|
||||
await buildBackend();
|
||||
}
|
||||
log.info(`启动后端(二进制)... 本地 http://127.0.0.1:${CONFIG.port} 绑定 0.0.0.0:${CONFIG.port}`);
|
||||
const proc = spawn(BINARY, [], {
|
||||
async function startBackendProd() {
|
||||
if (!binaryExists()) {
|
||||
log.warn('未找到编译产物,正在编译...');
|
||||
await buildBackend();
|
||||
}
|
||||
log.info(`启动后端(二进制)... 本地 http://127.0.0.1:${CONFIG.port} 绑定 0.0.0.0:${CONFIG.port}`);
|
||||
const proc = spawn(BINARY, [], {
|
||||
cwd: __dirname,
|
||||
stdio: 'inherit',
|
||||
shell: false,
|
||||
@@ -323,14 +325,14 @@ async function startFrontend() {
|
||||
}
|
||||
|
||||
// 显示状态信息
|
||||
function showStatus() {
|
||||
console.log('\n' + '─'.repeat(50));
|
||||
log.success(`后端 API: http://127.0.0.1:${CONFIG.port}`);
|
||||
log.success(`管理界面: http://127.0.0.1:${CONFIG.port}/admin`);
|
||||
log.info(`后端绑定: 0.0.0.0:${CONFIG.port} (可通过局域网 IP 访问)`);
|
||||
if (existsSync(CONFIG.webuiDir)) {
|
||||
log.success(`前端 Dev: http://localhost:${CONFIG.frontendPort}`);
|
||||
}
|
||||
function showStatus() {
|
||||
console.log('\n' + '─'.repeat(50));
|
||||
log.success(`后端 API: http://127.0.0.1:${CONFIG.port}`);
|
||||
log.success(`管理界面: http://127.0.0.1:${CONFIG.port}/admin`);
|
||||
log.info(`后端绑定: 0.0.0.0:${CONFIG.port} (可通过局域网 IP 访问)`);
|
||||
if (existsSync(CONFIG.webuiDir)) {
|
||||
log.success(`前端 Dev: http://localhost:${CONFIG.frontendPort}`);
|
||||
}
|
||||
console.log('─'.repeat(50));
|
||||
log.info('按 Ctrl+C 停止所有服务\n');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user