Add Debian package and Gitea APT repository support
debian/control, postinst, prerm, postrm — standard dpkg package lifecycle debian/files/opt/dupfinder/dupfinder-setup.sh — interactive setup: checks Docker, detects NVIDIA GPU, prompts for photos/data paths, writes docker-compose.override.yml with GPU reservation if present, pulls image from registry (builds from source as fallback) debian/files/usr/local/bin/dupfinder — CLI wrapper: setup / start / stop / restart / status / logs / open / update debian/files/etc/systemd/system/dupfinder.service — systemd unit, guards against starting before setup has run debian/build-deb.sh — builds .deb and uploads to Gitea package registry; prints the exact apt sources.list line on success Install on any Debian/Ubuntu machine: echo "deb [trusted=yes] http://192.168.1.64:3000/api/packages/tocmo0nlord/debian bookworm main" \ | sudo tee /etc/apt/sources.list.d/dupfinder.list sudo apt update && sudo apt install dupfinder sudo dupfinder setup Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
141
debian/build-deb.sh
vendored
Normal file
141
debian/build-deb.sh
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
#!/bin/bash
|
||||
# Build dupfinder.deb and upload it to the Gitea package registry.
|
||||
# Run this on the NAS / any Linux machine with dpkg-deb and curl installed.
|
||||
#
|
||||
# Usage:
|
||||
# ./debian/build-deb.sh
|
||||
# ./debian/build-deb.sh --no-upload # build only, skip Gitea upload
|
||||
set -e
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
DEBIAN_DIR="$REPO_ROOT/debian"
|
||||
BUILD_DIR="$REPO_ROOT/build/deb"
|
||||
|
||||
# ── Config ────────────────────────────────────────────────────────────────────
|
||||
PKG_NAME="dupfinder"
|
||||
PKG_VERSION="1.0.0"
|
||||
PKG_ARCH="amd64"
|
||||
DEB_FILE="${PKG_NAME}_${PKG_VERSION}_${PKG_ARCH}.deb"
|
||||
|
||||
GITEA_URL="http://192.168.1.64:3000"
|
||||
GITEA_OWNER="tocmo0nlord"
|
||||
GITEA_TOKEN="${GITEA_TOKEN:-72d5ea48d6b97d2185a774de0a36c88ae30f91b0}"
|
||||
DISTRO="bookworm"
|
||||
COMPONENT="main"
|
||||
|
||||
NO_UPLOAD=false
|
||||
[[ "${1}" == "--no-upload" ]] && NO_UPLOAD=true
|
||||
|
||||
# ── Helpers ───────────────────────────────────────────────────────────────────
|
||||
info() { echo -e "\033[0;36m==> $*\033[0m"; }
|
||||
ok() { echo -e "\033[0;32m OK $*\033[0m"; }
|
||||
fail() { echo -e "\033[0;31m !! $*\033[0m"; exit 1; }
|
||||
|
||||
# ── Check dependencies ────────────────────────────────────────────────────────
|
||||
command -v dpkg-deb &>/dev/null || fail "dpkg-deb not found. Run: sudo apt install dpkg-dev"
|
||||
command -v curl &>/dev/null || fail "curl not found. Run: sudo apt install curl"
|
||||
|
||||
# ── Prepare staging area ──────────────────────────────────────────────────────
|
||||
info "Preparing build directory..."
|
||||
PKG_STAGE="$BUILD_DIR/${PKG_NAME}_${PKG_VERSION}_${PKG_ARCH}"
|
||||
rm -rf "$PKG_STAGE"
|
||||
mkdir -p "$PKG_STAGE/DEBIAN"
|
||||
|
||||
# ── Copy DEBIAN control files ─────────────────────────────────────────────────
|
||||
info "Copying control files..."
|
||||
cp "$DEBIAN_DIR/control" "$PKG_STAGE/DEBIAN/control"
|
||||
cp "$DEBIAN_DIR/postinst" "$PKG_STAGE/DEBIAN/postinst"
|
||||
cp "$DEBIAN_DIR/prerm" "$PKG_STAGE/DEBIAN/prerm"
|
||||
cp "$DEBIAN_DIR/postrm" "$PKG_STAGE/DEBIAN/postrm"
|
||||
|
||||
# Inject current version into control file
|
||||
sed -i "s/^Version:.*/Version: $PKG_VERSION/" "$PKG_STAGE/DEBIAN/control"
|
||||
|
||||
# Fix permissions — maintainer scripts must be executable
|
||||
chmod 755 "$PKG_STAGE/DEBIAN/postinst" \
|
||||
"$PKG_STAGE/DEBIAN/prerm" \
|
||||
"$PKG_STAGE/DEBIAN/postrm"
|
||||
|
||||
# ── Copy payload files ────────────────────────────────────────────────────────
|
||||
info "Copying payload files..."
|
||||
cp -r "$DEBIAN_DIR/files/." "$PKG_STAGE/"
|
||||
|
||||
# Copy the docker-compose.yml from repo root into the package
|
||||
mkdir -p "$PKG_STAGE/opt/dupfinder"
|
||||
cp "$REPO_ROOT/docker-compose.yml" "$PKG_STAGE/opt/dupfinder/docker-compose.yml"
|
||||
|
||||
# Copy source as fallback build path
|
||||
cp -r "$REPO_ROOT/app" "$PKG_STAGE/opt/dupfinder/source/" 2>/dev/null || true
|
||||
cp -r "$REPO_ROOT/templates" "$PKG_STAGE/opt/dupfinder/source/" 2>/dev/null || true
|
||||
cp "$REPO_ROOT/Dockerfile" "$PKG_STAGE/opt/dupfinder/source/" 2>/dev/null || true
|
||||
cp "$REPO_ROOT/requirements.txt" "$PKG_STAGE/opt/dupfinder/source/" 2>/dev/null || true
|
||||
|
||||
# ── Fix file permissions ──────────────────────────────────────────────────────
|
||||
find "$PKG_STAGE" -type f -name "*.sh" -exec chmod 755 {} \;
|
||||
chmod 755 "$PKG_STAGE/usr/local/bin/dupfinder" 2>/dev/null || true
|
||||
# Directories must be 755, files 644 (except executables)
|
||||
find "$PKG_STAGE" -type d -exec chmod 755 {} \;
|
||||
find "$PKG_STAGE" -type f ! -name "*.sh" \
|
||||
! -path "*/DEBIAN/*" \
|
||||
! -name "dupfinder" \
|
||||
-exec chmod 644 {} \;
|
||||
|
||||
ok "Staging area ready: $PKG_STAGE"
|
||||
|
||||
# ── Build .deb ────────────────────────────────────────────────────────────────
|
||||
info "Building $DEB_FILE ..."
|
||||
mkdir -p "$BUILD_DIR"
|
||||
dpkg-deb --build --root-owner-group "$PKG_STAGE" "$BUILD_DIR/$DEB_FILE"
|
||||
|
||||
DEB_SIZE=$(du -sh "$BUILD_DIR/$DEB_FILE" | cut -f1)
|
||||
ok "Built: $BUILD_DIR/$DEB_FILE ($DEB_SIZE)"
|
||||
|
||||
# ── Upload to Gitea ───────────────────────────────────────────────────────────
|
||||
if [[ "$NO_UPLOAD" == "true" ]]; then
|
||||
echo ""
|
||||
echo "Skipping upload (--no-upload). File is at:"
|
||||
echo " $BUILD_DIR/$DEB_FILE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
info "Uploading to Gitea package registry..."
|
||||
UPLOAD_URL="$GITEA_URL/api/packages/$GITEA_OWNER/debian/pool/$DISTRO/$COMPONENT"
|
||||
|
||||
HTTP_STATUS=$(curl -s -o /tmp/gitea_upload_response.txt -w "%{http_code}" \
|
||||
-X PUT \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary @"$BUILD_DIR/$DEB_FILE" \
|
||||
"$UPLOAD_URL/$DEB_FILE")
|
||||
|
||||
if [[ "$HTTP_STATUS" == "201" || "$HTTP_STATUS" == "200" ]]; then
|
||||
ok "Uploaded successfully (HTTP $HTTP_STATUS)"
|
||||
elif [[ "$HTTP_STATUS" == "409" ]]; then
|
||||
echo " Package version $PKG_VERSION already exists in registry."
|
||||
echo " Bump PKG_VERSION in this script to publish a new version."
|
||||
else
|
||||
echo " Upload failed (HTTP $HTTP_STATUS):"
|
||||
cat /tmp/gitea_upload_response.txt
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── Print install instructions ────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "╔══════════════════════════════════════════════════════════════════╗"
|
||||
echo "║ Package published! Install on any Ubuntu/Debian machine with: ║"
|
||||
echo "╠══════════════════════════════════════════════════════════════════╣"
|
||||
echo "║ ║"
|
||||
echo "║ 1. Add the repo: ║"
|
||||
echo "║ echo \"deb [trusted=yes] \\ ║"
|
||||
echo "║ $GITEA_URL/api/packages/$GITEA_OWNER/debian \\ ║"
|
||||
echo "║ $DISTRO $COMPONENT\" \\ ║"
|
||||
echo "║ | sudo tee /etc/apt/sources.list.d/dupfinder.list ║"
|
||||
echo "║ ║"
|
||||
echo "║ 2. Install: ║"
|
||||
echo "║ sudo apt update && sudo apt install dupfinder ║"
|
||||
echo "║ ║"
|
||||
echo "║ 3. Configure: ║"
|
||||
echo "║ sudo dupfinder setup ║"
|
||||
echo "║ ║"
|
||||
echo "╚══════════════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
15
debian/control
vendored
Normal file
15
debian/control
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
Package: dupfinder
|
||||
Version: 1.0.0
|
||||
Architecture: amd64
|
||||
Maintainer: tocmo0nlord
|
||||
Depends: docker.io | docker-ce, docker-compose-plugin | docker-compose
|
||||
Recommends: nvidia-container-toolkit
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Description: Self-hosted duplicate photo and video finder
|
||||
DupFinder scans a photo/video library using four detection methods:
|
||||
exact hash (SHA-256), visual similarity (perceptual hash), EXIF
|
||||
timestamp matching, and file-size/dimension matching. All decisions
|
||||
are stored in SQLite — no files are ever moved or deleted.
|
||||
GPU acceleration via NVIDIA CUDA is supported automatically.
|
||||
Homepage: http://192.168.1.64:3000/tocmo0nlord/duplicate-finder
|
||||
33
debian/files/etc/systemd/system/dupfinder.service
vendored
Normal file
33
debian/files/etc/systemd/system/dupfinder.service
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
[Unit]
|
||||
Description=DupFinder Duplicate Photo Scanner
|
||||
Documentation=http://192.168.1.64:3000/tocmo0nlord/duplicate-finder
|
||||
After=docker.service network-online.target
|
||||
Requires=docker.service
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
EnvironmentFile=-/etc/dupfinder.conf
|
||||
WorkingDirectory=/opt/dupfinder
|
||||
|
||||
ExecStart=/usr/bin/docker compose \
|
||||
-f /opt/dupfinder/docker-compose.yml \
|
||||
-f /opt/dupfinder/docker-compose.override.yml \
|
||||
up --no-build --remove-orphans
|
||||
|
||||
ExecStop=/usr/bin/docker compose \
|
||||
-f /opt/dupfinder/docker-compose.yml \
|
||||
-f /opt/dupfinder/docker-compose.override.yml \
|
||||
down
|
||||
|
||||
# Don't start if override hasn't been created yet (setup not run)
|
||||
ExecStartPre=/bin/test -f /opt/dupfinder/docker-compose.override.yml
|
||||
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=dupfinder
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
167
debian/files/opt/dupfinder/dupfinder-setup.sh
vendored
Normal file
167
debian/files/opt/dupfinder/dupfinder-setup.sh
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
#!/bin/bash
|
||||
# DupFinder first-time setup — configure paths, pull image, write override
|
||||
set -e
|
||||
|
||||
CONF_FILE="/etc/dupfinder.conf"
|
||||
COMPOSE_DIR="/opt/dupfinder"
|
||||
OVERRIDE_YML="$COMPOSE_DIR/docker-compose.override.yml"
|
||||
IMAGE_NAME="tocmo0nlord/dupfinder:latest"
|
||||
DATA_DIR="/var/lib/dupfinder/data"
|
||||
APP_PORT=8765
|
||||
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; CYAN='\033[0;36m'; NC='\033[0m'
|
||||
info() { echo -e "${CYAN}==> $*${NC}"; }
|
||||
ok() { echo -e "${GREEN} OK $*${NC}"; }
|
||||
err() { echo -e "${RED} !! $*${NC}"; }
|
||||
|
||||
# ── Root check ────────────────────────────────────────────────────────────────
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
err "Please run as root: sudo dupfinder setup"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " ╔══════════════════════════════════════╗"
|
||||
echo " ║ DupFinder Setup ║"
|
||||
echo " ╚══════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
# ── Load existing config as defaults ─────────────────────────────────────────
|
||||
[[ -f "$CONF_FILE" ]] && source "$CONF_FILE"
|
||||
: "${PHOTOS_PATH:=/mnt/photos}"
|
||||
: "${DATA_PATH:=$DATA_DIR}"
|
||||
: "${APP_PORT:=8765}"
|
||||
|
||||
# ── Check Docker ──────────────────────────────────────────────────────────────
|
||||
info "Checking Docker..."
|
||||
if ! command -v docker &>/dev/null; then
|
||||
err "Docker is not installed."
|
||||
echo " Install with: curl -fsSL https://get.docker.com | sh"
|
||||
exit 1
|
||||
fi
|
||||
if ! docker info &>/dev/null; then
|
||||
err "Docker daemon is not running."
|
||||
echo " Start with: sudo systemctl start docker"
|
||||
exit 1
|
||||
fi
|
||||
ok "Docker is running"
|
||||
|
||||
# ── Check docker compose ──────────────────────────────────────────────────────
|
||||
if ! docker compose version &>/dev/null; then
|
||||
err "docker compose (V2 plugin) not found. Update Docker or install docker-compose-plugin."
|
||||
exit 1
|
||||
fi
|
||||
ok "docker compose V2 available"
|
||||
|
||||
# ── Check NVIDIA GPU ──────────────────────────────────────────────────────────
|
||||
info "Checking GPU..."
|
||||
if command -v nvidia-smi &>/dev/null && nvidia-smi &>/dev/null; then
|
||||
GPU_NAME=$(nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -1)
|
||||
ok "NVIDIA GPU detected: $GPU_NAME"
|
||||
GPU_AVAILABLE=true
|
||||
else
|
||||
echo " No NVIDIA GPU detected — will use CPU for perceptual hashing"
|
||||
GPU_AVAILABLE=false
|
||||
fi
|
||||
|
||||
# ── Photos path ───────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
info "Photos library path (mounted read-only):"
|
||||
echo " Current: $PHOTOS_PATH"
|
||||
read -rp " Path [Enter to keep]: " INPUT
|
||||
INPUT="${INPUT%\"}" ; INPUT="${INPUT#\"}" # strip quotes
|
||||
[[ -n "$INPUT" ]] && PHOTOS_PATH="$INPUT"
|
||||
|
||||
if [[ ! -d "$PHOTOS_PATH" ]]; then
|
||||
err "Path not found: $PHOTOS_PATH"
|
||||
echo " Create it or mount your drive first, then re-run setup."
|
||||
exit 1
|
||||
fi
|
||||
ok "Photos: $PHOTOS_PATH"
|
||||
|
||||
# ── Data path ─────────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
info "Database storage path:"
|
||||
echo " Current: $DATA_PATH"
|
||||
read -rp " Path [Enter to keep]: " INPUT
|
||||
INPUT="${INPUT%\"}" ; INPUT="${INPUT#\"}"
|
||||
[[ -n "$INPUT" ]] && DATA_PATH="$INPUT"
|
||||
mkdir -p "$DATA_PATH"
|
||||
ok "Data: $DATA_PATH"
|
||||
|
||||
# ── Port ──────────────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
read -rp " Web port [$APP_PORT]: " INPUT
|
||||
[[ -n "$INPUT" ]] && APP_PORT="$INPUT"
|
||||
ok "Port: $APP_PORT"
|
||||
|
||||
# ── Pull Docker image ─────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
info "Pulling Docker image ($IMAGE_NAME)..."
|
||||
if docker pull "$IMAGE_NAME"; then
|
||||
ok "Image pulled"
|
||||
else
|
||||
echo ""
|
||||
echo " Could not pull from registry. Trying to build from source..."
|
||||
if [[ -f "$COMPOSE_DIR/source/Dockerfile" ]]; then
|
||||
docker build -t "$IMAGE_NAME" "$COMPOSE_DIR/source"
|
||||
ok "Image built from source"
|
||||
else
|
||||
err "Neither pull nor local build succeeded."
|
||||
echo " Make sure you have internet access or the source files are present."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Write config + override ───────────────────────────────────────────────────
|
||||
info "Writing configuration..."
|
||||
|
||||
cat > "$CONF_FILE" <<EOF
|
||||
PHOTOS_PATH=$PHOTOS_PATH
|
||||
DATA_PATH=$DATA_PATH
|
||||
APP_PORT=$APP_PORT
|
||||
GPU_AVAILABLE=$GPU_AVAILABLE
|
||||
EOF
|
||||
chmod 600 "$CONF_FILE"
|
||||
|
||||
# Docker requires forward slashes
|
||||
PHOTOS_DOCKER="${PHOTOS_PATH//\\//}"
|
||||
DATA_DOCKER="${DATA_PATH//\\//}"
|
||||
|
||||
cat > "$OVERRIDE_YML" <<EOF
|
||||
services:
|
||||
dup-finder:
|
||||
image: $IMAGE_NAME
|
||||
ports:
|
||||
- "${APP_PORT}:8000"
|
||||
volumes:
|
||||
- "$PHOTOS_DOCKER:/photos:ro"
|
||||
- "$DATA_DOCKER:/data"
|
||||
EOF
|
||||
|
||||
# Add GPU reservation if available
|
||||
if [[ "$GPU_AVAILABLE" == "true" ]]; then
|
||||
cat >> "$OVERRIDE_YML" <<EOF
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- driver: nvidia
|
||||
count: 1
|
||||
capabilities: [gpu]
|
||||
EOF
|
||||
fi
|
||||
ok "Config saved to $CONF_FILE"
|
||||
|
||||
# ── Start service ─────────────────────────────────────────────────────────────
|
||||
info "Starting DupFinder..."
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now dupfinder.service
|
||||
ok "Service started"
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN} ╔══════════════════════════════════════════╗${NC}"
|
||||
echo -e "${GREEN} ║ DupFinder is running! ║${NC}"
|
||||
echo -e "${GREEN} ║ Open: http://localhost:$APP_PORT ║${NC}"
|
||||
echo -e "${GREEN} ╚══════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
81
debian/files/usr/local/bin/dupfinder
vendored
Normal file
81
debian/files/usr/local/bin/dupfinder
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
#!/bin/bash
|
||||
# DupFinder CLI wrapper
|
||||
CONF_FILE="/etc/dupfinder.conf"
|
||||
COMPOSE_DIR="/opt/dupfinder"
|
||||
COMPOSE_YML="$COMPOSE_DIR/docker-compose.yml"
|
||||
OVERRIDE_YML="$COMPOSE_DIR/docker-compose.override.yml"
|
||||
|
||||
[[ -f "$CONF_FILE" ]] && source "$CONF_FILE"
|
||||
: "${APP_PORT:=8765}"
|
||||
|
||||
_compose() {
|
||||
docker compose -f "$COMPOSE_YML" -f "$OVERRIDE_YML" "$@"
|
||||
}
|
||||
|
||||
_require_conf() {
|
||||
if [[ ! -f "$CONF_FILE" ]]; then
|
||||
echo "DupFinder is not configured. Run: sudo dupfinder setup"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
case "${1:-help}" in
|
||||
setup)
|
||||
exec bash /opt/dupfinder/dupfinder-setup.sh
|
||||
;;
|
||||
start)
|
||||
_require_conf
|
||||
sudo systemctl start dupfinder.service
|
||||
echo "DupFinder started — http://localhost:$APP_PORT"
|
||||
;;
|
||||
stop)
|
||||
sudo systemctl stop dupfinder.service
|
||||
echo "DupFinder stopped."
|
||||
;;
|
||||
restart)
|
||||
_require_conf
|
||||
sudo systemctl restart dupfinder.service
|
||||
echo "DupFinder restarted — http://localhost:$APP_PORT"
|
||||
;;
|
||||
status)
|
||||
systemctl status dupfinder.service --no-pager
|
||||
;;
|
||||
logs)
|
||||
_compose logs -f --tail=100
|
||||
;;
|
||||
open)
|
||||
_require_conf
|
||||
# Wait for service to be ready then open browser
|
||||
for i in $(seq 1 15); do
|
||||
curl -sf "http://localhost:$APP_PORT/" -o /dev/null && break
|
||||
sleep 1
|
||||
done
|
||||
xdg-open "http://localhost:$APP_PORT" 2>/dev/null || \
|
||||
echo "Open in browser: http://localhost:$APP_PORT"
|
||||
;;
|
||||
update)
|
||||
_require_conf
|
||||
echo "Pulling latest image..."
|
||||
docker pull tocmo0nlord/dupfinder:latest
|
||||
sudo systemctl restart dupfinder.service
|
||||
echo "Updated and restarted."
|
||||
;;
|
||||
uninstall)
|
||||
echo "To fully remove DupFinder: sudo apt remove dupfinder"
|
||||
echo "To also remove data: sudo apt purge dupfinder"
|
||||
;;
|
||||
help|--help|-h|*)
|
||||
echo "Usage: dupfinder <command>"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " setup Configure photos path, data path, pull image"
|
||||
echo " start Start the service"
|
||||
echo " stop Stop the service"
|
||||
echo " restart Restart the service"
|
||||
echo " status Show systemd service status"
|
||||
echo " logs Tail container logs"
|
||||
echo " open Open in browser"
|
||||
echo " update Pull latest image and restart"
|
||||
echo " uninstall Show removal instructions"
|
||||
;;
|
||||
esac
|
||||
27
debian/postinst
vendored
Normal file
27
debian/postinst
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Create data directory with correct permissions
|
||||
mkdir -p /var/lib/dupfinder/data
|
||||
chmod 755 /var/lib/dupfinder
|
||||
|
||||
# Reload systemd and enable service (don't start yet — needs user config first)
|
||||
systemctl daemon-reload
|
||||
systemctl enable dupfinder.service 2>/dev/null || true
|
||||
|
||||
echo ""
|
||||
echo "╔══════════════════════════════════════════════════╗"
|
||||
echo "║ DupFinder installed successfully! ║"
|
||||
echo "╠══════════════════════════════════════════════════╣"
|
||||
echo "║ ║"
|
||||
echo "║ Run setup to configure your photos path: ║"
|
||||
echo "║ ║"
|
||||
echo "║ sudo dupfinder setup ║"
|
||||
echo "║ ║"
|
||||
echo "║ After setup, manage with: ║"
|
||||
echo "║ sudo systemctl start dupfinder ║"
|
||||
echo "║ sudo systemctl stop dupfinder ║"
|
||||
echo "║ dupfinder status ║"
|
||||
echo "║ ║"
|
||||
echo "╚══════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
13
debian/postrm
vendored
Normal file
13
debian/postrm
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
case "$1" in
|
||||
purge)
|
||||
# Remove data only on purge (not regular remove)
|
||||
rm -rf /var/lib/dupfinder
|
||||
rm -f /etc/dupfinder.conf
|
||||
systemctl daemon-reload 2>/dev/null || true
|
||||
;;
|
||||
remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
|
||||
systemctl daemon-reload 2>/dev/null || true
|
||||
;;
|
||||
esac
|
||||
5
debian/prerm
vendored
Normal file
5
debian/prerm
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
# Stop and disable service before removal
|
||||
systemctl stop dupfinder.service 2>/dev/null || true
|
||||
systemctl disable dupfinder.service 2>/dev/null || true
|
||||
Reference in New Issue
Block a user