u
This commit is contained in:
@@ -15,15 +15,6 @@ COPY --from=syslibs /usr/lib/x86_64-linux-gnu/libfontconfig.so* /usr/lib/x86_64-
|
|||||||
COPY --from=syslibs /usr/lib/x86_64-linux-gnu/libxml2.so* /usr/lib/x86_64-linux-gnu/
|
COPY --from=syslibs /usr/lib/x86_64-linux-gnu/libxml2.so* /usr/lib/x86_64-linux-gnu/
|
||||||
COPY --from=syslibs /usr/lib/x86_64-linux-gnu/libfribidi.so* /usr/lib/x86_64-linux-gnu/
|
COPY --from=syslibs /usr/lib/x86_64-linux-gnu/libfribidi.so* /usr/lib/x86_64-linux-gnu/
|
||||||
|
|
||||||
# The standalone SEA binary contains Node.js runtime, src/, package.json, and all app logic
|
|
||||||
COPY dist/dashback ./dist/dashback
|
|
||||||
|
|
||||||
# Only native C++ addon packages required by OS dynamic linker (dlopen)
|
|
||||||
COPY node_modules/better-sqlite3 ./node_modules/better-sqlite3
|
|
||||||
COPY node_modules/canvas ./node_modules/canvas
|
|
||||||
COPY node_modules/@img ./node_modules/@img
|
|
||||||
COPY node_modules/sharp ./node_modules/sharp
|
|
||||||
|
|
||||||
# Set production defaults: port 4208
|
# Set production defaults: port 4208
|
||||||
ENV NODE_ENV=production \
|
ENV NODE_ENV=production \
|
||||||
HOST=0.0.0.0 \
|
HOST=0.0.0.0 \
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
services:
|
|
||||||
dashback:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
image: dashback:latest
|
|
||||||
container_name: dashback
|
|
||||||
read_only: true
|
|
||||||
user: "${UID:-1000}:${GID:-1000}"
|
|
||||||
tmpfs:
|
|
||||||
- /tmp:rw,noexec,nosuid,size=512m
|
|
||||||
- /run:rw,noexec,nosuid,size=16m
|
|
||||||
networks:
|
|
||||||
- dash_net
|
|
||||||
ports:
|
|
||||||
- "127.0.0.1:4208:4208"
|
|
||||||
environment:
|
|
||||||
- HOST=0.0.0.0
|
|
||||||
- PORT=4208
|
|
||||||
- HTTPS_PORT=0
|
|
||||||
- MSSQL_SERVER=sql_server
|
|
||||||
- HEALTH_MONITOR_ENABLED=false
|
|
||||||
- SQLITE_PATH=/app/data/dash.db
|
|
||||||
- STORAGE_DIR=/app/data/storage
|
|
||||||
- BACKUP_DIR=/app/data/backups
|
|
||||||
# If a .env file is present, its values will be loaded:
|
|
||||||
env_file:
|
|
||||||
- path: .env
|
|
||||||
required: false
|
|
||||||
volumes:
|
|
||||||
# Writable data directory
|
|
||||||
- ./data:/app/data:rw
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
networks:
|
|
||||||
dash_net:
|
|
||||||
external: true
|
|
||||||
@@ -18,10 +18,7 @@ if (!fs.existsSync(distDir)) {
|
|||||||
fs.mkdirSync(distDir, { recursive: true });
|
fs.mkdirSync(distDir, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Packaging application, siblings, and JavaScript dependencies into SEA payload...');
|
console.log('Packaging application, siblings, and dependencies into SEA payload...');
|
||||||
|
|
||||||
// Native C++ addon folders that will be loaded from the host filesystem via dlopen
|
|
||||||
const NATIVE_ADDON_PKGS = new Set(['better-sqlite3', 'canvas', 'sharp', '@img']);
|
|
||||||
|
|
||||||
function packDirectory(dir, prefix, result) {
|
function packDirectory(dir, prefix, result) {
|
||||||
if (!fs.existsSync(dir)) return;
|
if (!fs.existsSync(dir)) return;
|
||||||
@@ -30,11 +27,10 @@ function packDirectory(dir, prefix, result) {
|
|||||||
const full = path.join(dir, entry.name);
|
const full = path.join(dir, entry.name);
|
||||||
const rel = path.join(prefix, entry.name);
|
const rel = path.join(prefix, entry.name);
|
||||||
if (entry.isDirectory()) {
|
if (entry.isDirectory()) {
|
||||||
if (entry.name === 'test' || entry.name === 'tests' || entry.name === 'docs' || entry.name === '.github' || entry.name === '.git') continue;
|
if (entry.name === 'test' || entry.name === 'tests' || entry.name === 'docs' || entry.name === '.github' || entry.name === '.git' || entry.name === 'obj.target') continue;
|
||||||
packDirectory(full, rel, result);
|
packDirectory(full, rel, result);
|
||||||
} else if (entry.isFile()) {
|
} else if (entry.isFile()) {
|
||||||
// Ignore compiled native binary extensions which are linked from the host
|
if (entry.name.endsWith('.a') || entry.name.endsWith('.o') || entry.name.endsWith('.cc') || entry.name.endsWith('.cpp') || entry.name.endsWith('.c') || entry.name.endsWith('.h')) continue;
|
||||||
if (entry.name.endsWith('.node') || entry.name.endsWith('.so') || entry.name.endsWith('.dylib') || entry.name.endsWith('.dll') || entry.name.endsWith('.a')) continue;
|
|
||||||
result[rel] = fs.readFileSync(full).toString('base64');
|
result[rel] = fs.readFileSync(full).toString('base64');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,15 +63,14 @@ if (fs.existsSync(path.join(llmorbiterDir, 'src'))) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Pack all non-native node_modules dependencies
|
// 3. Pack all node_modules dependencies
|
||||||
function packNodeModules(nmPath) {
|
function packNodeModules(nmPath) {
|
||||||
if (!fs.existsSync(nmPath)) return;
|
if (!fs.existsSync(nmPath)) return;
|
||||||
for (const name of fs.readdirSync(nmPath)) {
|
for (const name of fs.readdirSync(nmPath)) {
|
||||||
if (NATIVE_ADDON_PKGS.has(name) || name.startsWith('.') || name === 'libdashstore' || name === 'libllmorbiter') continue;
|
if (name.startsWith('.') || name === 'libdashstore' || name === 'libllmorbiter') continue;
|
||||||
const pkgDir = path.join(nmPath, name);
|
const pkgDir = path.join(nmPath, name);
|
||||||
if (name.startsWith('@')) {
|
if (name.startsWith('@')) {
|
||||||
for (const sub of fs.readdirSync(pkgDir)) {
|
for (const sub of fs.readdirSync(pkgDir)) {
|
||||||
if (NATIVE_ADDON_PKGS.has(sub) || NATIVE_ADDON_PKGS.has(`${name}/${sub}`)) continue;
|
|
||||||
packDirectory(path.join(pkgDir, sub), `node_modules/${name}/${sub}`, payload);
|
packDirectory(path.join(pkgDir, sub), `node_modules/${name}/${sub}`, payload);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
* SEA Entry point for dashback.
|
* SEA Entry point for dashback.
|
||||||
* Embedded inside the Node.js single executable binary.
|
* Embedded inside the Node.js single executable binary.
|
||||||
*
|
*
|
||||||
* Unpacks the bundled application, sibling packages, and JavaScript dependencies
|
* Unpacks the bundled application, sibling packages, and all dependencies
|
||||||
* to a runtime directory, and links only the native C++ addons from the host.
|
* (including native C++ addons) to a runtime directory.
|
||||||
*/
|
*/
|
||||||
const sea = require('node:sea');
|
const sea = require('node:sea');
|
||||||
const fs = require('node:fs');
|
const fs = require('node:fs');
|
||||||
@@ -32,23 +32,6 @@ function getProjectRoot() {
|
|||||||
fs.writeFileSync(markerFile, '1');
|
fs.writeFileSync(markerFile, '1');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Link host native addons into runtime node_modules
|
|
||||||
const hostNodeModules = path.resolve(process.cwd(), 'node_modules');
|
|
||||||
const runtimeNodeModules = path.join(runtimeDir, 'node_modules');
|
|
||||||
fs.mkdirSync(runtimeNodeModules, { recursive: true });
|
|
||||||
|
|
||||||
const NATIVE_ADDON_DIRS = ['better-sqlite3', 'canvas', 'sharp', '@img'];
|
|
||||||
for (const addon of NATIVE_ADDON_DIRS) {
|
|
||||||
const hostAddon = path.join(hostNodeModules, addon);
|
|
||||||
const targetAddon = path.join(runtimeNodeModules, addon);
|
|
||||||
if (fs.existsSync(hostAddon) && !fs.existsSync(targetAddon)) {
|
|
||||||
try {
|
|
||||||
fs.mkdirSync(path.dirname(targetAddon), { recursive: true });
|
|
||||||
fs.symlinkSync(hostAddon, targetAddon, 'junction');
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Link host .env if present so config/env.js can find it
|
// Link host .env if present so config/env.js can find it
|
||||||
const hostEnv = path.resolve(process.cwd(), '.env');
|
const hostEnv = path.resolve(process.cwd(), '.env');
|
||||||
const runtimeEnv = path.join(runtimeDir, '.env');
|
const runtimeEnv = path.join(runtimeDir, '.env');
|
||||||
|
|||||||
33
start-docker.sh
Executable file
33
start-docker.sh
Executable file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
# Ensure the writable data directory exists on the host
|
||||||
|
mkdir -p "${SCRIPT_DIR}/data"
|
||||||
|
|
||||||
|
ENV_ARGS=()
|
||||||
|
if [ -f "${SCRIPT_DIR}/.env" ]; then
|
||||||
|
ENV_ARGS+=(--env-file "${SCRIPT_DIR}/.env")
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker run -d \
|
||||||
|
--name dashback \
|
||||||
|
--restart unless-stopped \
|
||||||
|
--read-only \
|
||||||
|
--user "$(id -u):$(id -g)" \
|
||||||
|
--tmpfs /tmp:rw,exec,nosuid,size=512m \
|
||||||
|
--tmpfs /run:rw,noexec,nosuid,size=16m \
|
||||||
|
-p 127.0.0.1:4608:4208 \
|
||||||
|
-e HOST=0.0.0.0 \
|
||||||
|
-e PORT=4208 \
|
||||||
|
-e HTTPS_PORT=0 \
|
||||||
|
-e HEALTH_MONITOR_ENABLED=false \
|
||||||
|
-e SQLITE_PATH=/app/data/dash.db \
|
||||||
|
-e STORAGE_DIR=/app/data/storage \
|
||||||
|
-e BACKUP_DIR=/app/data/backups \
|
||||||
|
"${ENV_ARGS[@]}" \
|
||||||
|
-v "${SCRIPT_DIR}/dist/dashback:/app/dist/dashback:ro" \
|
||||||
|
-v "${SCRIPT_DIR}/data:/app/data:rw" \
|
||||||
|
dashback:latest
|
||||||
|
|
||||||
Reference in New Issue
Block a user