Add users.is_admin SQLite migration for promoted admins. Support curl PUT request uploads, save uploads into admin file-manager paths, default 25 GiB uploads, tabbed UI, and local dev preview.
227 lines
5.2 KiB
Bash
Executable File
227 lines
5.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
script_path=${BASH_SOURCE[0]}
|
|
if [[ "$script_path" == */* ]]; then
|
|
script_dir=${script_path%/*}
|
|
else
|
|
script_dir=.
|
|
fi
|
|
|
|
cd "$script_dir/.."
|
|
root_dir=$PWD
|
|
|
|
if [[ -n "${CONTAINER_ENGINE:-}" ]]; then
|
|
engine=$CONTAINER_ENGINE
|
|
elif command -v podman >/dev/null 2>&1; then
|
|
engine=podman
|
|
elif command -v docker >/dev/null 2>&1; then
|
|
engine=docker
|
|
else
|
|
printf 'Need podman or docker in PATH.\n' >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v "$engine" >/dev/null 2>&1; then
|
|
printf 'Container engine not found: %s\n' "$engine" >&2
|
|
exit 1
|
|
fi
|
|
|
|
image=${FILES_DEV_IMAGE:-files-dev-nextjs:latest}
|
|
container=${FILES_DEV_CONTAINER:-files-dev-nextjs}
|
|
host_port=${FILES_DEV_PORT:-3000}
|
|
dev_dir=${FILES_DEV_DIR:-$root_dir/.dev}
|
|
app_data_dir=$dev_dir/app-data
|
|
storage_dir=$dev_dir/storage
|
|
password_hash='$2a$12$8d4a4zH/tb5TYiP4wQpuFO4GheZarYDWVDGl2S90R4xdMd83JOPMm'
|
|
|
|
volume_arg() {
|
|
if [[ "$engine" == podman ]]; then
|
|
printf '%s:%s:Z' "$1" "$2"
|
|
else
|
|
printf '%s:%s' "$1" "$2"
|
|
fi
|
|
}
|
|
|
|
mkdir -p "$app_data_dir" "$storage_dir/_share"
|
|
|
|
printf 'Using %s\n' "$engine"
|
|
printf 'Building %s\n' "$image"
|
|
"$engine" build -t "$image" "$root_dir/nextjs"
|
|
|
|
printf 'Seeding dev user john.doe / 123456\n'
|
|
"$engine" run --rm -i \
|
|
-e DB_PATH=/app/data/uploads.sqlite \
|
|
-e DEV_PASSWORD_HASH="$password_hash" \
|
|
-v "$(volume_arg "$app_data_dir" /app/data)" \
|
|
-v "$(volume_arg "$storage_dir" /data)" \
|
|
"$image" node - <<'NODE'
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const sqlite3 = require('sqlite3');
|
|
|
|
const dbPath = process.env.DB_PATH || '/app/data/uploads.sqlite';
|
|
const passwordHash = process.env.DEV_PASSWORD_HASH;
|
|
|
|
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
|
|
fs.mkdirSync('/data/_share', { recursive: true });
|
|
|
|
const db = new sqlite3.Database(dbPath);
|
|
|
|
function run(sql, params = []) {
|
|
return new Promise((resolve, reject) => {
|
|
db.run(sql, params, function onRun(error) {
|
|
if (error) {
|
|
reject(error);
|
|
return;
|
|
}
|
|
resolve(this);
|
|
});
|
|
});
|
|
}
|
|
|
|
function close() {
|
|
return new Promise((resolve, reject) => {
|
|
db.close((error) => {
|
|
if (error) {
|
|
reject(error);
|
|
return;
|
|
}
|
|
resolve();
|
|
});
|
|
});
|
|
}
|
|
|
|
(async () => {
|
|
await run(`CREATE TABLE IF NOT EXISTS users (
|
|
username TEXT PRIMARY KEY,
|
|
password_hash TEXT NOT NULL,
|
|
created_at INTEGER NOT NULL,
|
|
is_admin INTEGER NOT NULL DEFAULT 0
|
|
)`);
|
|
await run('ALTER TABLE users ADD COLUMN is_admin INTEGER NOT NULL DEFAULT 0').catch(() => undefined);
|
|
await run(
|
|
`INSERT INTO users (username, password_hash, created_at, is_admin)
|
|
VALUES (?, ?, ?, 1)
|
|
ON CONFLICT(username) DO UPDATE SET password_hash = excluded.password_hash, is_admin = 1`,
|
|
['john.doe', passwordHash, Date.now()]
|
|
);
|
|
await close();
|
|
})().catch(async (error) => {
|
|
try {
|
|
await close();
|
|
} catch {
|
|
}
|
|
console.error(error);
|
|
process.exit(1);
|
|
});
|
|
NODE
|
|
|
|
if "$engine" container inspect "$container" >/dev/null 2>&1; then
|
|
printf 'Removing old %s\n' "$container"
|
|
"$engine" rm -f "$container" >/dev/null
|
|
fi
|
|
|
|
logs_pid=
|
|
watchdog_pid=
|
|
|
|
stop_watchdog() {
|
|
if [[ -n "$watchdog_pid" ]]; then
|
|
kill "$watchdog_pid" >/dev/null 2>&1 || true
|
|
wait "$watchdog_pid" >/dev/null 2>&1 || true
|
|
watchdog_pid=
|
|
fi
|
|
}
|
|
|
|
cleanup() {
|
|
status=$?
|
|
trap - EXIT INT TERM HUP QUIT
|
|
|
|
stop_watchdog
|
|
|
|
if [[ -n "$logs_pid" ]]; then
|
|
kill "$logs_pid" >/dev/null 2>&1 || true
|
|
wait "$logs_pid" >/dev/null 2>&1 || true
|
|
fi
|
|
|
|
if "$engine" container inspect "$container" >/dev/null 2>&1; then
|
|
printf '\nKilling %s\n' "$container" >&2
|
|
"$engine" rm -f "$container" >/dev/null 2>&1 || true
|
|
fi
|
|
|
|
exit "$status"
|
|
}
|
|
|
|
start_watchdog() {
|
|
local parent_pid=$$
|
|
(
|
|
trap '' INT TERM HUP QUIT
|
|
while kill -0 "$parent_pid" >/dev/null 2>&1; do
|
|
sleep 0.2
|
|
done
|
|
"$engine" rm -f "$container" >/dev/null 2>&1 || true
|
|
) &
|
|
watchdog_pid=$!
|
|
}
|
|
|
|
trap cleanup EXIT
|
|
trap 'exit 130' INT
|
|
trap 'exit 143' TERM
|
|
trap 'exit 129' HUP
|
|
trap 'exit 131' QUIT
|
|
|
|
printf '\nPreview: http://localhost:%s\n' "$host_port"
|
|
printf 'User login: john.doe / 123456\n'
|
|
printf 'Admin area: /manage/admin, password 123456\n'
|
|
printf 'Data dir: %s\n' "$dev_dir"
|
|
printf 'Stop: Ctrl-C\n\n'
|
|
|
|
"$engine" run --rm -d \
|
|
--name "$container" \
|
|
--stop-timeout 1 \
|
|
-p "127.0.0.1:$host_port:3000" \
|
|
-e DATA_DIR=/data \
|
|
-e DB_PATH=/app/data/uploads.sqlite \
|
|
-e SERVICE_FQDN=localhost \
|
|
-e PUBLIC_BASE_URL="http://localhost:$host_port" \
|
|
-e UPLOAD_TTL_SECONDS=604800 \
|
|
-e UPLOAD_MAX_BYTES=26843545600 \
|
|
-e MANAGEMENT_ADMIN_HASH="$password_hash" \
|
|
-e COOKIE_SECURE=false \
|
|
-e SMTP_HOST= \
|
|
-e SMTP_PORT=587 \
|
|
-e SMTP_USER= \
|
|
-e SMTP_PASS= \
|
|
-e SMTP_MAIL= \
|
|
-e SMTP_NAME= \
|
|
-e PORT=3000 \
|
|
-v "$(volume_arg "$app_data_dir" /app/data)" \
|
|
-v "$(volume_arg "$storage_dir" /data)" \
|
|
"$image" >/dev/null
|
|
|
|
start_watchdog
|
|
|
|
"$engine" logs -f "$container" &
|
|
logs_pid=$!
|
|
|
|
set +e
|
|
container_status=$("$engine" wait "$container")
|
|
wait_status=$?
|
|
set -e
|
|
|
|
kill "$logs_pid" >/dev/null 2>&1 || true
|
|
wait "$logs_pid" >/dev/null 2>&1 || true
|
|
logs_pid=
|
|
stop_watchdog
|
|
|
|
trap - EXIT INT TERM HUP QUIT
|
|
|
|
if [[ $wait_status -ne 0 ]]; then
|
|
exit "$wait_status"
|
|
fi
|
|
|
|
if [[ "$container_status" =~ ^[0-9]+$ ]]; then
|
|
exit "$container_status"
|
|
fi
|