Files
files/scripts/dev
Ludwig Lehnert a2b6054440 feat: improve upload admin workflow
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.
2026-06-18 17:09:07 +00:00

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