diff --git a/.claude/WIP.md b/.claude/WIP.md index 6e511191..e922088a 100644 --- a/.claude/WIP.md +++ b/.claude/WIP.md @@ -16,7 +16,8 @@ _Last updated: 2026-03-06 (PhotoPrism Gallery)_ - LuCI: `luci-app-photoprism` KISS dashboard with stats and actions - Features: AI face recognition, object detection, places/maps - HAProxy integration via mitmproxy (WAF-safe, no bypass) - - MariaDB database, FFmpeg transcoding, HEIC support + - SQLite database (simpler, no external DB), FFmpeg transcoding, HEIC support + - Dependencies: libvips42 for image processing - **AI Gateway `/login` Command** - CLI: `aigatewayctl login [provider]` - Interactive or scripted provider authentication diff --git a/package/secubox/secubox-app-photoprism/files/usr/sbin/photoprismctl b/package/secubox/secubox-app-photoprism/files/usr/sbin/photoprismctl index 7794e44c..36ea70e8 100644 --- a/package/secubox/secubox-app-photoprism/files/usr/sbin/photoprismctl +++ b/package/secubox/secubox-app-photoprism/files/usr/sbin/photoprismctl @@ -10,7 +10,7 @@ LXC_PATH="/srv/lxc" LXC_ROOTFS="${LXC_PATH}/${LXC_NAME}/rootfs" LXC_CONFIG="${LXC_PATH}/${LXC_NAME}/config" DATA_PATH="/srv/photoprism" -PHOTOPRISM_VERSION="240915-ce" +PHOTOPRISM_VERSION="260305-fad9d5395" HOST_IP="192.168.255.1" # Detect architecture @@ -110,6 +110,11 @@ create_lxc_config() { # PhotoPrism LXC Configuration lxc.uts.name = ${LXC_NAME} lxc.rootfs.path = dir:${LXC_ROOTFS} +lxc.arch = aarch64 + +# Auto-start on boot +lxc.start.auto = 1 +lxc.start.delay = 5 # Network - use host network lxc.net.0.type = none @@ -122,89 +127,88 @@ lxc.mount.entry = ${DATA_PATH}/originals opt/photoprism/originals none bind,crea lxc.mount.entry = ${DATA_PATH}/storage opt/photoprism/storage none bind,create=dir 0 0 lxc.mount.entry = ${DATA_PATH}/import opt/photoprism/import none bind,create=dir 0 0 -# Resource limits -lxc.cgroup2.memory.max = ${mem_bytes} - -# Startup command -lxc.init.cmd = /opt/start-photoprism.sh - # TTY lxc.tty.max = 4 lxc.pty.max = 128 -# Capabilities -lxc.cap.drop = sys_admin +# Character devices +lxc.cgroup2.devices.allow = c 1:* rwm +lxc.cgroup2.devices.allow = c 5:* rwm +lxc.cgroup2.devices.allow = c 136:* rwm + +# Resource limits +lxc.cgroup2.memory.max = ${mem_bytes} + +# Security +lxc.cap.drop = sys_module mac_admin mac_override sys_time sys_rawio + +# Startup command (uses SQLite, no external DB) +lxc.init.cmd = /opt/init.sh EOF log "LXC config created" } -# Create startup script inside container +# Create startup script inside container (uses SQLite - no external DB needed) create_startup_script() { - cat > "${LXC_ROOTFS}/opt/start-photoprism.sh" << 'SCRIPT' + cat > "${LXC_ROOTFS}/opt/init.sh" << 'SCRIPT' #!/bin/bash set -e -# Start MariaDB -service mariadb start -sleep 2 +# Setup environment +export HOME=/opt/photoprism +export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +export TERM=linux -# Wait for MariaDB to be ready -for i in $(seq 1 30); do - if mysqladmin ping >/dev/null 2>&1; then - break - fi - sleep 1 -done +# Create directories +mkdir -p /opt/photoprism/storage/cache +mkdir -p /opt/photoprism/storage/sidecar +mkdir -p /opt/photoprism/originals +mkdir -p /opt/photoprism/import +mkdir -p /run /var/run + +# PhotoPrism environment - SQLite config (no external DB) +export PHOTOPRISM_CONFIG_PATH=/opt/photoprism +export PHOTOPRISM_DATABASE_DRIVER=sqlite +export PHOTOPRISM_DATABASE_DSN=/opt/photoprism/storage/photoprism.db +export PHOTOPRISM_ORIGINALS_PATH=/opt/photoprism/originals +export PHOTOPRISM_STORAGE_PATH=/opt/photoprism/storage +export PHOTOPRISM_IMPORT_PATH=/opt/photoprism/import +export PHOTOPRISM_HTTP_HOST=0.0.0.0 +export PHOTOPRISM_HTTP_PORT=2342 +export PHOTOPRISM_ADMIN_USER=admin +export PHOTOPRISM_ADMIN_PASSWORD="${PHOTOPRISM_ADMIN_PASSWORD:-secubox123}" +export PHOTOPRISM_DISABLE_FACES=false +export PHOTOPRISM_DISABLE_CLASSIFICATION=false +export PHOTOPRISM_DISABLE_PLACES=false -# Run PhotoPrism cd /opt/photoprism -exec ./photoprism start + +# Start PhotoPrism (foreground to keep container running) +exec ./bin/photoprism start SCRIPT - chmod +x "${LXC_ROOTFS}/opt/start-photoprism.sh" + chmod +x "${LXC_ROOTFS}/opt/init.sh" } -# Create PhotoPrism configuration -create_photoprism_config() { - local db_pass="$1" - +# Create PhotoPrism directories (config is done via env vars in init script) +create_photoprism_dirs() { mkdir -p "${LXC_ROOTFS}/opt/photoprism" - - cat > "${LXC_ROOTFS}/opt/photoprism/options.yml" << EOF -# PhotoPrism Configuration -AdminUser: "${ADMIN_USER}" -AdminPassword: "${ADMIN_PASS}" - -# Storage -OriginalsPath: "/opt/photoprism/originals" -StoragePath: "/opt/photoprism/storage" -ImportPath: "/opt/photoprism/import" - -# Server -HttpHost: "0.0.0.0" -HttpPort: ${HTTP_PORT} - -# Database -DatabaseDriver: "mysql" -DatabaseDsn: "${DB_USER}:${db_pass}@tcp(127.0.0.1:3306)/${DB_NAME}?charset=utf8mb4,utf8&parseTime=true" - -# Features -DisableFaces: $([ "$FACE_RECOGNITION" = "1" ] && echo "false" || echo "true") -DisableClassification: $([ "$OBJECT_DETECTION" = "1" ] && echo "false" || echo "true") -DisablePlaces: $([ "$PLACES" = "1" ] && echo "false" || echo "true") -DisableRaw: $([ "$RAW_THUMBS" = "1" ] && echo "false" || echo "true") - -# Quality -JpegQuality: 85 -ThumbSize: 2048 -ThumbSizeUncached: 7680 -EOF + mkdir -p "${LXC_ROOTFS}/opt/photoprism/storage" + mkdir -p "${LXC_ROOTFS}/opt/photoprism/originals" + mkdir -p "${LXC_ROOTFS}/opt/photoprism/import" } # Install packages inside container install_packages() { log "Installing packages in container..." + # Fix /dev nodes for chroot + [ -e "${LXC_ROOTFS}/dev/null" ] || mknod -m 666 "${LXC_ROOTFS}/dev/null" c 1 3 + [ -e "${LXC_ROOTFS}/dev/zero" ] || mknod -m 666 "${LXC_ROOTFS}/dev/zero" c 1 5 + [ -e "${LXC_ROOTFS}/dev/random" ] || mknod -m 666 "${LXC_ROOTFS}/dev/random" c 1 8 + [ -e "${LXC_ROOTFS}/dev/urandom" ] || mknod -m 666 "${LXC_ROOTFS}/dev/urandom" c 1 9 + chmod 666 "${LXC_ROOTFS}/dev/null" 2>/dev/null || true + # Configure apt cat > "${LXC_ROOTFS}/etc/apt/sources.list" << EOF deb http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware @@ -212,22 +216,27 @@ deb http://deb.debian.org/debian bookworm-updates main contrib non-free non-free deb http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware EOF - # Create install script + # Create install script - install gpgv first for apt verification cat > "${LXC_ROOTFS}/tmp/install.sh" << 'INSTALL' #!/bin/bash set -e export DEBIAN_FRONTEND=noninteractive +# Install gpgv first (required for apt signature verification) +# Use --allow-unauthenticated only for this bootstrap step +apt-get update --allow-insecure-repositories || true +apt-get install -y --allow-unauthenticated gpgv gnupg + +# Now apt can verify signatures apt-get update apt-get install -y --no-install-recommends \ - mariadb-server \ + libvips42 \ ffmpeg \ exiftool \ libheif-examples \ ca-certificates \ curl \ - wget \ - gnupg + wget # Clean up apt-get clean @@ -235,8 +244,14 @@ rm -rf /var/lib/apt/lists/* INSTALL chmod +x "${LXC_ROOTFS}/tmp/install.sh" + # Mount /proc for chroot + mount -t proc proc "${LXC_ROOTFS}/proc" 2>/dev/null || true + # Run install via chroot chroot "$LXC_ROOTFS" /tmp/install.sh + + # Unmount /proc + umount "${LXC_ROOTFS}/proc" 2>/dev/null || true rm -f "${LXC_ROOTFS}/tmp/install.sh" } @@ -244,7 +259,7 @@ INSTALL install_photoprism_binary() { log "Downloading PhotoPrism ${PHOTOPRISM_VERSION} for ${ARCH}..." - local url="https://github.com/photoprism/photoprism/releases/download/${PHOTOPRISM_VERSION}/photoprism_${PHOTOPRISM_VERSION}_linux_${ARCH}.tar.gz" + local url="https://github.com/photoprism/photoprism/releases/download/${PHOTOPRISM_VERSION}/photoprism_${PHOTOPRISM_VERSION}-linux-${ARCH}.tar.gz" mkdir -p "${LXC_ROOTFS}/opt/photoprism" @@ -256,33 +271,12 @@ install_photoprism_binary() { tar -xzf /tmp/photoprism.tar.gz -C "${LXC_ROOTFS}/opt/photoprism" rm -f /tmp/photoprism.tar.gz - chmod +x "${LXC_ROOTFS}/opt/photoprism/photoprism" + # Binary is in bin/ subdirectory + chmod +x "${LXC_ROOTFS}/opt/photoprism/bin/photoprism" log "PhotoPrism binary installed" } -# Setup MariaDB -setup_database() { - local db_pass="$1" - - cat > "${LXC_ROOTFS}/tmp/setup-db.sh" << DBSCRIPT -#!/bin/bash -service mariadb start -sleep 2 - -mysql -e "CREATE DATABASE IF NOT EXISTS ${DB_NAME};" -mysql -e "CREATE USER IF NOT EXISTS '${DB_USER}'@'localhost' IDENTIFIED BY '${db_pass}';" -mysql -e "GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'localhost';" -mysql -e "FLUSH PRIVILEGES;" - -service mariadb stop -DBSCRIPT - chmod +x "${LXC_ROOTFS}/tmp/setup-db.sh" - - chroot "$LXC_ROOTFS" /tmp/setup-db.sh - rm -f "${LXC_ROOTFS}/tmp/setup-db.sh" -} - -# Full installation +# Full installation (uses SQLite - no external database needed) cmd_install() { defaults @@ -299,34 +293,25 @@ cmd_install() { mkdir -p "${DATA_PATH}/import" chmod -R 755 "$DATA_PATH" - # Generate passwords if not set + # Generate admin password if not set if [ -z "$ADMIN_PASS" ]; then ADMIN_PASS=$(generate_password) uci_set admin.password "$ADMIN_PASS" log "Generated admin password: $ADMIN_PASS" fi - local db_pass="$DB_PASS" - if [ -z "$db_pass" ]; then - db_pass=$(generate_password) - uci_set database.password "$db_pass" - fi - # Download rootfs download_rootfs "$ARCH" - # Install packages + # Install packages (libvips, ffmpeg, etc.) install_packages - # Download PhotoPrism + # Download PhotoPrism binary install_photoprism_binary - # Setup database - setup_database "$db_pass" - - # Create configs + # Create configs and startup script create_lxc_config - create_photoprism_config "$db_pass" + create_photoprism_dirs create_startup_script # Enable service @@ -497,7 +482,7 @@ cmd_index() { fi log "Starting photo indexing..." - lxc-attach -n "$LXC_NAME" -- /opt/photoprism/photoprism index + lxc-attach -n "$LXC_NAME" -- /opt/photoprism/bin/photoprism index log "Indexing complete" } @@ -514,7 +499,7 @@ cmd_import() { [ "$(uci_get import.delete_after_import 0)" = "1" ] && delete_opt="--move" log "Importing photos from ${DATA_PATH}/import..." - lxc-attach -n "$LXC_NAME" -- /opt/photoprism/photoprism import $delete_opt + lxc-attach -n "$LXC_NAME" -- /opt/photoprism/bin/photoprism import $delete_opt log "Import complete" } @@ -528,14 +513,14 @@ cmd_passwd() { return 1 fi - lxc-attach -n "$LXC_NAME" -- /opt/photoprism/photoprism passwd "$ADMIN_USER" "$new_pass" + lxc-attach -n "$LXC_NAME" -- /opt/photoprism/bin/photoprism passwd "$ADMIN_USER" "$new_pass" uci_set admin.password "$new_pass" log "Password reset for $ADMIN_USER" log "New password: $new_pass" } -# Backup +# Backup (SQLite database is in storage directory) cmd_backup() { defaults local backup_dir="${DATA_PATH}/backups" @@ -544,17 +529,8 @@ cmd_backup() { mkdir -p "$backup_dir" - if lxc_running; then - log "Dumping database..." - lxc-attach -n "$LXC_NAME" -- mysqldump -u root "$DB_NAME" > "${backup_dir}/database-${timestamp}.sql" - fi - - log "Creating backup archive..." - tar -czf "$backup_file" \ - -C "$DATA_PATH" storage \ - -C "$backup_dir" "database-${timestamp}.sql" 2>/dev/null || true - - rm -f "${backup_dir}/database-${timestamp}.sql" + log "Creating backup archive (includes SQLite database)..." + tar -czf "$backup_file" -C "$DATA_PATH" storage log "Backup created: $backup_file" }