Files
hmac-file-server/installer.sh

1670 lines
57 KiB
Bash
Executable File

#!/bin/bash
# HMAC File Server Installer Script
# Version: 3.2
# Compatible with systemd Linux distributions
set -e
# Trap to handle script errors
trap 'handle_error $? $LINENO' ERR
# Error handling function
handle_error() {
local exit_code=$1
local line_number=$2
echo -e "${RED}Error occurred in script at line $line_number with exit code $exit_code${NC}"
echo -e "${YELLOW}Installation failed. Please check the error message above.${NC}"
exit $exit_code
}
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Default values
DEFAULT_USER="hmac-server"
DEFAULT_INSTALL_DIR="/opt/hmac-file-server"
DEFAULT_CONFIG_DIR="/etc/hmac-file-server"
DEFAULT_DATA_DIR="/var/lib/hmac-file-server"
DEFAULT_LOG_DIR="/var/log/hmac-file-server"
DEFAULT_PORT="8080"
DEFAULT_METRICS_PORT="9090"
# Help function
show_help() {
echo -e "${BLUE}HMAC File Server 3.2 Installer${NC}"
echo ""
echo "Usage: $0 [OPTION]"
echo ""
echo "Options:"
echo " --help Show this help message"
echo " --uninstall Uninstall HMAC File Server completely"
echo ""
echo "Environment Variables (optional):"
echo " HMAC_SECRET Pre-set HMAC secret (minimum 32 characters)"
echo " JWT_SECRET Pre-set JWT secret (minimum 32 characters)"
echo ""
echo "Example:"
echo " HMAC_SECRET='your-super-secret-hmac-key-here-32chars' sudo -E $0"
echo ""
echo "This installer will:"
echo " - Install Go 1.24 (if not present, native installation only)"
echo " - Create system user and directories"
echo " - Build and install HMAC File Server (native) or create Docker deployment"
echo " - Configure systemd service (native) or docker-compose setup (Docker)"
echo " - Install Redis and/or ClamAV (optional, native installation only)"
echo ""
echo "Installation options:"
echo " - Native: Traditional systemd service installation"
echo " - Docker: Container-based deployment with docker-compose"
echo ""
echo "For XMPP operators: This installer is optimized for easy integration"
echo "with Prosody, Ejabberd, and other XMPP servers."
echo ""
}
# Check for help flag first (before root check)
if [[ "$1" == "--help" || "$1" == "-h" ]]; then
show_help
exit 0
fi
# Professional installer header with branding
echo ""
echo -e "${BLUE} __ _____ __ ${NC}"
echo -e "${BLUE} / /_ ____ ___ ____ ______ / __(_) /__ ________ ______ _____ _____${NC}"
echo -e "${BLUE} / __ \\/ __ \`__ \\/ __ \`/ ___/_____/ /_/ / / _ \\______/ ___/ _ \\/ ___/ | / / _ \\/ ___/${NC}"
echo -e "${BLUE} / / / / / / / / / /_/ / /__/_____/ __/ / / __/_____(__ ) __/ / | |/ / __/ / ${NC}"
echo -e "${BLUE}/_/ /_/_/ /_/ /_/\\__,_/\\___/ /_/ /_/_/\\___/ /____/\\___/_/ |___/\\___/_/ ${NC}"
echo ""
echo -e "${BLUE} HMAC File Server 3.2 Installer${NC}"
echo -e "${BLUE} Professional XMPP Integration${NC}"
echo ""
echo -e "${YELLOW}--------------------------------------------------------------------------------${NC}"
echo -e "${GREEN} Secure File Uploads & Downloads JWT & HMAC Authentication${NC}"
echo -e "${GREEN} Prometheus Metrics Integration ClamAV Virus Scanning${NC}"
echo -e "${GREEN} Redis Cache & Session Management Chunked Upload/Download Support${NC}"
echo -e "${YELLOW}--------------------------------------------------------------------------------${NC}"
echo ""
# Check if running as root
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}This script must be run as root (use sudo)${NC}"
exit 1
fi
# Check for systemd
if ! command -v systemctl &> /dev/null; then
echo -e "${RED}Error: systemctl not found. This installer requires a systemd-based Linux distribution.${NC}"
exit 1
fi
# Pre-installation checks
pre_installation_checks() {
echo -e "${YELLOW}Running pre-installation checks...${NC}"
# Check if service already exists
if systemctl is-enabled hmac-file-server.service &>/dev/null; then
echo -e "${YELLOW}Warning: HMAC File Server service already exists${NC}"
read -p "Do you want to continue and overwrite the existing installation? (y/N): " OVERWRITE
if [[ ! $OVERWRITE =~ ^[Yy]$ ]]; then
echo -e "${YELLOW}Installation cancelled${NC}"
exit 0
fi
# Stop existing service
echo -e "${YELLOW}Stopping existing service...${NC}"
systemctl stop hmac-file-server.service || true
fi
# Check available disk space (minimum 1GB)
AVAILABLE_SPACE=$(df / | awk 'NR==2 {print $4}')
if [[ $AVAILABLE_SPACE -lt 1048576 ]]; then
echo -e "${RED}Error: Insufficient disk space. At least 1GB required${NC}"
exit 1
fi
# Check if we're in the correct directory (should contain go.mod)
if [[ ! -f "go.mod" ]]; then
echo -e "${RED}Error: go.mod not found. Please run this installer from the HMAC File Server source directory${NC}"
exit 1
fi
echo -e "${GREEN}Pre-installation checks passed${NC}"
echo ""
}
# Check for Go installation
check_go() {
if ! command -v go &> /dev/null; then
echo -e "${YELLOW}Go is not installed. Installing Go 1.24...${NC}"
# Detect architecture
ARCH=$(uname -m)
case $ARCH in
x86_64) GO_ARCH="amd64" ;;
aarch64|arm64) GO_ARCH="arm64" ;;
armv7l) GO_ARCH="armv6l" ;;
*) echo -e "${RED}Unsupported architecture: $ARCH${NC}"; exit 1 ;;
esac
# Download and install Go
cd /tmp
wget -q "https://go.dev/dl/go1.24.linux-${GO_ARCH}.tar.gz"
tar -C /usr/local -xzf "go1.24.linux-${GO_ARCH}.tar.gz"
# Add Go to PATH
echo 'export PATH=$PATH:/usr/local/go/bin' >> /etc/profile
export PATH=$PATH:/usr/local/go/bin
echo -e "${GREEN}Go 1.24 installed successfully${NC}"
else
GO_VERSION=$(go version | awk '{print $3}' | sed 's/go//')
echo -e "${GREEN}Go $GO_VERSION is already installed${NC}"
fi
}
# User input function
get_user_input() {
echo -e "${BLUE}Installation Type Selection${NC}"
echo "Choose your preferred installation method:"
echo ""
echo " 1) Native installation (systemd service)"
echo " 2) Docker deployment (docker-compose)"
echo ""
while true; do
read -p "Installation type [1]: " INSTALL_TYPE
INSTALL_TYPE=${INSTALL_TYPE:-1}
case $INSTALL_TYPE in
1)
echo -e "${GREEN}Selected: Native installation${NC}"
DEPLOYMENT_TYPE="native"
break
;;
2)
echo -e "${GREEN}Selected: Docker deployment${NC}"
DEPLOYMENT_TYPE="docker"
break
;;
*)
echo -e "${RED}Please enter 1 or 2${NC}"
;;
esac
done
echo ""
echo -e "${BLUE}Configuration Setup${NC}"
echo "Please provide the following information (or press Enter for defaults):"
echo ""
# System user
read -p "System user for HMAC File Server [$DEFAULT_USER]: " HMAC_USER
HMAC_USER=${HMAC_USER:-$DEFAULT_USER}
if [[ "$DEPLOYMENT_TYPE" == "native" ]]; then
# Installation directory
read -p "Installation directory [$DEFAULT_INSTALL_DIR]: " INSTALL_DIR
INSTALL_DIR=${INSTALL_DIR:-$DEFAULT_INSTALL_DIR}
# Configuration directory
read -p "Configuration directory [$DEFAULT_CONFIG_DIR]: " CONFIG_DIR
CONFIG_DIR=${CONFIG_DIR:-$DEFAULT_CONFIG_DIR}
# Data directory
read -p "Data directory (uploads) [$DEFAULT_DATA_DIR]: " DATA_DIR
DATA_DIR=${DATA_DIR:-$DEFAULT_DATA_DIR}
else
# Docker deployment paths
read -p "Docker deployment directory [./hmac-file-server-docker]: " DOCKER_DIR
DOCKER_DIR=${DOCKER_DIR:-"./hmac-file-server-docker"}
# Convert to absolute path
DOCKER_DIR=$(realpath "$DOCKER_DIR" 2>/dev/null || echo "$DOCKER_DIR")
# Set Docker-specific paths
INSTALL_DIR="$DOCKER_DIR"
CONFIG_DIR="$DOCKER_DIR/config"
DATA_DIR="$DOCKER_DIR/data"
fi
# Server port
read -p "Server port [$DEFAULT_PORT]: " SERVER_PORT
SERVER_PORT=${SERVER_PORT:-$DEFAULT_PORT}
# Metrics port
read -p "Metrics port [$DEFAULT_METRICS_PORT]: " METRICS_PORT
METRICS_PORT=${METRICS_PORT:-$DEFAULT_METRICS_PORT}
# HMAC secret
if [[ -n "$HMAC_SECRET" ]]; then
# Use environment variable if provided
if [[ ${#HMAC_SECRET} -ge 32 ]]; then
echo -e "${GREEN}Using HMAC secret from environment variable${NC}"
else
echo -e "${RED}Error: HMAC_SECRET environment variable must be at least 32 characters long${NC}"
echo -e "${YELLOW}Current length: ${#HMAC_SECRET}${NC}"
exit 1
fi
else
# Interactive input with auto-generation option
echo ""
echo -e "${BLUE}HMAC Secret Configuration${NC}"
echo "Choose how to set the HMAC secret:"
echo " 1) Generate automatically (recommended)"
echo " 2) Enter manually"
echo ""
while true; do
read -p "Choice [1]: " hmac_choice
hmac_choice=${hmac_choice:-1}
case $hmac_choice in
1)
echo -e "${YELLOW}Generating secure HMAC secret...${NC}"
HMAC_SECRET=$(generate_random_key 48)
echo -e "${GREEN}Generated 48-character HMAC secret${NC}"
echo -e "${BLUE}Secret preview: ${HMAC_SECRET:0:8}...${HMAC_SECRET: -8}${NC}"
break
;;
2)
while true; do
echo -n "HMAC secret (minimum 32 characters): "
# Use bash built-in silent read if available
if read -s -r HMAC_SECRET 2>/dev/null; then
echo ""
else
# Fallback: use regular read with warning
echo ""
echo -e "${YELLOW}Note: Input will be visible (your terminal doesn't support hidden input)${NC}"
echo -n "HMAC secret (minimum 32 characters): "
read -r HMAC_SECRET
fi
if [[ ${#HMAC_SECRET} -ge 32 ]]; then
echo -e "${GREEN}HMAC secret accepted (${#HMAC_SECRET} characters)${NC}"
break 2
else
echo -e "${RED}HMAC secret must be at least 32 characters long (you entered ${#HMAC_SECRET} characters)${NC}"
echo -e "${YELLOW}Tip: Choose option 1 for automatic generation${NC}"
fi
done
;;
*)
echo -e "${RED}Please enter 1 or 2${NC}"
;;
esac
done
fi
# JWT settings
echo ""
read -p "Enable JWT authentication? (y/N): " ENABLE_JWT
if [[ $ENABLE_JWT =~ ^[Yy]$ ]]; then
ENABLE_JWT="true"
# JWT secret
if [[ -n "$JWT_SECRET" ]]; then
# Use environment variable if provided
if [[ ${#JWT_SECRET} -ge 32 ]]; then
echo -e "${GREEN}Using JWT secret from environment variable${NC}"
else
echo -e "${RED}Error: JWT_SECRET environment variable must be at least 32 characters long${NC}"
echo -e "${YELLOW}Current length: ${#JWT_SECRET}${NC}"
exit 1
fi
else
# Interactive input with auto-generation option
echo ""
echo -e "${BLUE}JWT Secret Configuration${NC}"
echo "Choose how to set the JWT secret:"
echo " 1) Generate automatically (recommended)"
echo " 2) Enter manually"
echo ""
while true; do
read -p "Choice [1]: " jwt_choice
jwt_choice=${jwt_choice:-1}
case $jwt_choice in
1)
echo -e "${YELLOW}Generating secure JWT secret...${NC}"
JWT_SECRET=$(generate_random_key 48)
echo -e "${GREEN}Generated 48-character JWT secret${NC}"
echo -e "${BLUE}Secret preview: ${JWT_SECRET:0:8}...${JWT_SECRET: -8}${NC}"
break
;;
2)
while true; do
echo -n "JWT secret (minimum 32 characters): "
# Use bash built-in silent read if available
if read -s -r JWT_SECRET 2>/dev/null; then
echo ""
else
# Fallback: use regular read with warning
echo ""
echo -e "${YELLOW}Note: Input will be visible (your terminal doesn't support hidden input)${NC}"
echo -n "JWT secret (minimum 32 characters): "
read -r JWT_SECRET
fi
if [[ ${#JWT_SECRET} -ge 32 ]]; then
echo -e "${GREEN}JWT secret accepted (${#JWT_SECRET} characters)${NC}"
break 2
else
echo -e "${RED}JWT secret must be at least 32 characters long (you entered ${#JWT_SECRET} characters)${NC}"
echo -e "${YELLOW}Tip: Choose option 1 for automatic generation${NC}"
fi
done
;;
*)
echo -e "${RED}Please enter 1 or 2${NC}"
;;
esac
done
fi
# JWT expiration
read -p "JWT token expiration [24h]: " JWT_EXPIRATION
JWT_EXPIRATION=${JWT_EXPIRATION:-"24h"}
# JWT algorithm
read -p "JWT algorithm (HS256/HS384/HS512) [HS256]: " JWT_ALGORITHM
JWT_ALGORITHM=${JWT_ALGORITHM:-"HS256"}
else
ENABLE_JWT="false"
JWT_SECRET=""
JWT_EXPIRATION="24h"
JWT_ALGORITHM="HS256"
fi
# Redis settings
echo ""
read -p "Enable Redis integration? (y/N): " ENABLE_REDIS
if [[ $ENABLE_REDIS =~ ^[Yy]$ ]]; then
ENABLE_REDIS="true"
read -p "Redis host [localhost]: " REDIS_HOST
REDIS_HOST=${REDIS_HOST:-"localhost"}
read -p "Redis port [6379]: " REDIS_PORT
REDIS_PORT=${REDIS_PORT:-"6379"}
read -p "Redis database [0]: " REDIS_DB
REDIS_DB=${REDIS_DB:-"0"}
read -s -p "Redis password (optional): " REDIS_PASSWORD
echo ""
else
ENABLE_REDIS="false"
fi
# ClamAV settings
echo ""
read -p "Enable ClamAV virus scanning? (y/N): " ENABLE_CLAMAV
if [[ $ENABLE_CLAMAV =~ ^[Yy]$ ]]; then
ENABLE_CLAMAV="true"
CLAMAV_CONFIG="socket = \"/var/run/clamav/clamd.ctl\"" # Default, will be updated during installation
else
ENABLE_CLAMAV="false"
CLAMAV_CONFIG=""
fi
# SSL/TLS settings
echo ""
read -p "Enable SSL/TLS? (y/N): " ENABLE_TLS
if [[ $ENABLE_TLS =~ ^[Yy]$ ]]; then
ENABLE_TLS="true"
read -p "SSL certificate path: " SSL_CERT
read -p "SSL private key path: " SSL_KEY
else
ENABLE_TLS="false"
fi
# Show configuration summary
# Professional configuration summary
echo ""
echo -e "${BLUE}Configuration Summary${NC}"
echo -e "${YELLOW}----------------------------------------------------------------${NC}"
echo -e "${YELLOW}System User:${NC} $HMAC_USER"
echo -e "${YELLOW}Install Dir:${NC} $INSTALL_DIR"
echo -e "${YELLOW}Config Dir:${NC} $CONFIG_DIR"
echo -e "${YELLOW}Data Dir:${NC} $DATA_DIR"
echo -e "${YELLOW}Server Port:${NC} $SERVER_PORT"
echo -e "${YELLOW}Metrics Port:${NC} $METRICS_PORT"
echo -e "${YELLOW}JWT Auth:${NC} $([[ "$ENABLE_JWT" == "true" ]] && echo "Enabled" || echo "Disabled")"
echo -e "${YELLOW}Redis:${NC} $([[ "$ENABLE_REDIS" == "true" ]] && echo "Enabled ($REDIS_HOST:$REDIS_PORT)" || echo "Disabled")"
echo -e "${YELLOW}ClamAV:${NC} $([[ "$ENABLE_CLAMAV" == "true" ]] && echo "Enabled" || echo "Disabled")"
echo -e "${YELLOW}SSL/TLS:${NC} $([[ "$ENABLE_TLS" == "true" ]] && echo "Enabled" || echo "Disabled")"
echo -e "${YELLOW}----------------------------------------------------------------${NC}"
echo ""
read -p "Continue with installation? (y/N): " CONFIRM_INSTALL
if [[ ! $CONFIRM_INSTALL =~ ^[Yy]$ ]]; then
echo -e "${YELLOW}Installation cancelled by user${NC}"
exit 0
fi
}
# Create system user
create_user() {
if ! id "$HMAC_USER" &>/dev/null; then
echo -e "${YELLOW}Creating system user: $HMAC_USER${NC}"
useradd --system --home-dir "$INSTALL_DIR" --shell /bin/false --comment "HMAC File Server" "$HMAC_USER"
else
echo -e "${GREEN}User $HMAC_USER already exists${NC}"
fi
}
# Create directories
create_directories() {
echo -e "${YELLOW}Creating directories...${NC}"
if [[ "$DEPLOYMENT_TYPE" == "docker" ]]; then
# Docker-specific directory structure
mkdir -p "$DOCKER_DIR"/{config,data/{uploads,deduplication,logs,temp}}
# Set ownership if running as root
if [[ $EUID -eq 0 ]]; then
chown -R "$HMAC_USER:$HMAC_USER" "$DOCKER_DIR" 2>/dev/null || true
fi
# Set permissions
chmod 755 "$DOCKER_DIR"
chmod 755 "$DOCKER_DIR"/{config,data}
chmod 755 "$DOCKER_DIR"/data/{uploads,deduplication,logs,temp}
else
# Native installation directory structure
mkdir -p "$INSTALL_DIR"
mkdir -p "$CONFIG_DIR"
mkdir -p "$DATA_DIR/uploads"
mkdir -p "$DATA_DIR/deduplication"
mkdir -p "$DATA_DIR/runtime"
mkdir -p "$DEFAULT_LOG_DIR"
# Set ownership
chown -R "$HMAC_USER:$HMAC_USER" "$INSTALL_DIR"
chown -R "$HMAC_USER:$HMAC_USER" "$DATA_DIR"
chown -R "$HMAC_USER:$HMAC_USER" "$DEFAULT_LOG_DIR"
# Set permissions
chmod 755 "$INSTALL_DIR"
chmod 755 "$DATA_DIR"
chmod 750 "$DEFAULT_LOG_DIR"
fi
}
# Build HMAC File Server
build_server() {
echo -e "${YELLOW}Building HMAC File Server...${NC}"
# Build the server
cd "$(dirname "$0")"
go build -o "$INSTALL_DIR/hmac-file-server" cmd/server/main.go cmd/server/helpers.go cmd/server/config_validator.go cmd/server/config_test_scenarios.go
# Set ownership and permissions
chown "$HMAC_USER:$HMAC_USER" "$INSTALL_DIR/hmac-file-server"
chmod 755 "$INSTALL_DIR/hmac-file-server"
echo -e "${GREEN}HMAC File Server built successfully${NC}"
}
# Generate configuration file
generate_config() {
echo -e "${YELLOW}Generating configuration file...${NC}"
cat > "$CONFIG_DIR/config.toml" << EOF
# HMAC File Server Configuration
# Generated by installer on $(date)
[server]
bind_ip = "0.0.0.0"
listenport = "$SERVER_PORT"
unixsocket = false
storagepath = "$DATA_DIR/uploads"
metricsenabled = true
metricsport = "$METRICS_PORT"
deduplicationenabled = true
deduplicationpath = "$DATA_DIR/deduplication"
filenaming = "HMAC"
force_protocol = "auto"
pidfilepath = "$DATA_DIR/runtime/hmac-file-server.pid"
EOF
if [[ $ENABLE_TLS == "true" ]]; then
cat >> "$CONFIG_DIR/config.toml" << EOF
sslenabled = true
sslcert = "$SSL_CERT"
sslkey = "$SSL_KEY"
EOF
else
cat >> "$CONFIG_DIR/config.toml" << EOF
sslenabled = false
EOF
fi
cat >> "$CONFIG_DIR/config.toml" << EOF
[security]
secret = "$HMAC_SECRET"
enablejwt = $ENABLE_JWT
EOF
if [[ $ENABLE_JWT == "true" ]]; then
cat >> "$CONFIG_DIR/config.toml" << EOF
jwtsecret = "$JWT_SECRET"
jwtalgorithm = "$JWT_ALGORITHM"
jwtexpiration = "$JWT_EXPIRATION"
EOF
fi
cat >> "$CONFIG_DIR/config.toml" << EOF
[uploads]
allowedextensions = [".txt", ".pdf", ".jpg", ".jpeg", ".png", ".gif", ".webp", ".zip", ".tar", ".gz", ".7z", ".mp4", ".webm", ".ogg", ".mp3", ".wav", ".flac", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".odt", ".ods", ".odp"]
maxfilesize = "100MB"
chunkeduploadsenabled = true
chunksize = "10MB"
ttlenabled = false
ttl = "168h"
[downloads]
chunkeddownloadsenabled = true
chunksize = "10MB"
[logging]
level = "INFO"
file = "$DEFAULT_LOG_DIR/hmac-file-server.log"
max_size = 100
max_backups = 3
max_age = 30
compress = true
[workers]
numworkers = 10
uploadqueuesize = 1000
autoscaling = true
[timeouts]
readtimeout = "30s"
writetimeout = "30s"
idletimeout = "120s"
shutdown = "30s"
EOF
if [[ $ENABLE_CLAMAV == "true" ]]; then
cat >> "$CONFIG_DIR/config.toml" << EOF
[clamav]
enabled = true
${CLAMAV_CONFIG}
timeout = "30s"
EOF
else
cat >> "$CONFIG_DIR/config.toml" << EOF
[clamav]
enabled = false
EOF
fi
if [[ $ENABLE_REDIS == "true" ]]; then
cat >> "$CONFIG_DIR/config.toml" << EOF
[redis]
enabled = true
host = "$REDIS_HOST"
port = $REDIS_PORT
database = $REDIS_DB
password = "$REDIS_PASSWORD"
timeout = "5s"
EOF
else
cat >> "$CONFIG_DIR/config.toml" << EOF
[redis]
enabled = false
EOF
fi
# Set ownership and permissions
chown "$HMAC_USER:$HMAC_USER" "$CONFIG_DIR/config.toml"
chmod 640 "$CONFIG_DIR/config.toml"
echo -e "${GREEN}Configuration file created: $CONFIG_DIR/config.toml${NC}"
}
# Create Docker deployment
create_docker_deployment() {
echo -e "${YELLOW}Creating Docker deployment...${NC}"
# Create directory structure
mkdir -p "$DOCKER_DIR"/{config,data/{uploads,deduplication,logs,temp}}
# Copy and modify docker-compose.yml
cat > "$DOCKER_DIR/docker-compose.yml" << EOF
version: '3.8'
services:
hmac-file-server:
container_name: hmac-file-server
build:
context: .
dockerfile: Dockerfile
ports:
- "$SERVER_PORT:$SERVER_PORT"
- "$METRICS_PORT:$METRICS_PORT"
volumes:
- ./config:/etc/hmac-file-server:ro
- ./data/uploads:/var/lib/hmac-file-server/uploads
- ./data/deduplication:/var/lib/hmac-file-server/deduplication
- ./data/logs:/var/log/hmac-file-server
- ./data/temp:/tmp/hmac-file-server
environment:
- CONFIG_PATH=/etc/hmac-file-server/config.toml
restart: unless-stopped
user: "$HMAC_USER"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:$SERVER_PORT/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
EOF
if [[ $ENABLE_REDIS == "true" ]]; then
cat >> "$DOCKER_DIR/docker-compose.yml" << EOF
redis:
image: redis:7-alpine
container_name: hmac-redis
ports:
- "$REDIS_PORT:6379"
volumes:
- redis_data:/data
restart: unless-stopped
command: redis-server --requirepass "$REDIS_PASSWORD"
EOF
fi
if [[ $ENABLE_CLAMAV == "true" ]]; then
cat >> "$DOCKER_DIR/docker-compose.yml" << EOF
clamav:
image: clamav/clamav:latest
container_name: hmac-clamav
volumes:
- clamav_data:/var/lib/clamav
restart: unless-stopped
environment:
- CLAMAV_NO_FRESHCLAMD=false
EOF
fi
# Add volumes section if needed
if [[ $ENABLE_REDIS == "true" || $ENABLE_CLAMAV == "true" ]]; then
cat >> "$DOCKER_DIR/docker-compose.yml" << EOF
volumes:
EOF
[[ $ENABLE_REDIS == "true" ]] && echo " redis_data:" >> "$DOCKER_DIR/docker-compose.yml"
[[ $ENABLE_CLAMAV == "true" ]] && echo " clamav_data:" >> "$DOCKER_DIR/docker-compose.yml"
fi
# Create Dockerfile
cat > "$DOCKER_DIR/Dockerfile" << EOF
FROM golang:1.24-alpine AS builder
WORKDIR /app
COPY . .
RUN apk add --no-cache git ca-certificates tzdata && \\
go mod download && \\
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o hmac-file-server cmd/server/main.go cmd/server/helpers.go cmd/server/config_validator.go cmd/server/config_test_scenarios.go
FROM alpine:latest
RUN apk --no-cache add ca-certificates curl && \\
addgroup -g 1000 hmac && \\
adduser -D -s /bin/sh -u 1000 -G hmac hmac
WORKDIR /opt/hmac-file-server
COPY --from=builder /app/hmac-file-server .
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
RUN chown hmac:hmac /opt/hmac-file-server/hmac-file-server && \\
chmod +x /opt/hmac-file-server/hmac-file-server
USER hmac
EXPOSE $SERVER_PORT $METRICS_PORT
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \\
CMD curl -f http://localhost:$SERVER_PORT/health || exit 1
CMD ["./hmac-file-server", "-config", "/etc/hmac-file-server/config.toml"]
EOF
# Copy source files for building
echo -e "${YELLOW}Copying source files for Docker build...${NC}"
cp -r "$(dirname "$0")"/{cmd,go.mod,go.sum} "$DOCKER_DIR/"
# Create start script
cat > "$DOCKER_DIR/start.sh" << 'EOF'
#!/bin/bash
# HMAC File Server Docker Start Script
set -e
echo "Starting HMAC File Server Docker deployment..."
# Check if Docker is installed
if ! command -v docker &> /dev/null; then
echo "Error: Docker is not installed. Please install Docker first."
exit 1
fi
# Check if Docker Compose is installed
if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then
echo "Error: Docker Compose is not installed. Please install Docker Compose first."
exit 1
fi
# Build and start services
echo "Building and starting services..."
if command -v docker-compose &> /dev/null; then
docker-compose up --build -d
else
docker compose up --build -d
fi
echo "HMAC File Server is starting up..."
echo "You can check the status with: docker-compose logs -f"
echo "Or: docker compose logs -f"
EOF
# Create stop script
cat > "$DOCKER_DIR/stop.sh" << 'EOF'
#!/bin/bash
# HMAC File Server Docker Stop Script
echo "Stopping HMAC File Server Docker deployment..."
if command -v docker-compose &> /dev/null; then
docker-compose down
else
docker compose down
fi
echo "HMAC File Server stopped."
EOF
# Make scripts executable
chmod +x "$DOCKER_DIR/start.sh" "$DOCKER_DIR/stop.sh"
# Set ownership
chown -R "$HMAC_USER:$HMAC_USER" "$DOCKER_DIR" 2>/dev/null || true
echo -e "${GREEN}Docker deployment created successfully${NC}"
echo -e "${GREEN}Location: $DOCKER_DIR${NC}"
}
# Generate Docker-compatible configuration
generate_docker_config() {
echo -e "${YELLOW}Generating Docker configuration file...${NC}"
cat > "$CONFIG_DIR/config.toml" << EOF
# HMAC File Server Configuration for Docker
# Generated by installer on $(date)
[server]
bind_ip = "0.0.0.0"
listenport = "$SERVER_PORT"
unixsocket = false
storagepath = "/var/lib/hmac-file-server/uploads"
metricsenabled = true
metricsport = "$METRICS_PORT"
deduplicationenabled = true
deduplicationpath = "/var/lib/hmac-file-server/deduplication"
filenaming = "HMAC"
force_protocol = "auto"
pidfilepath = "/tmp/hmac-file-server/hmac-file-server.pid"
EOF
if [[ $ENABLE_TLS == "true" ]]; then
cat >> "$CONFIG_DIR/config.toml" << EOF
sslenabled = true
sslcert = "$SSL_CERT"
sslkey = "$SSL_KEY"
EOF
else
cat >> "$CONFIG_DIR/config.toml" << EOF
sslenabled = false
EOF
fi
cat >> "$CONFIG_DIR/config.toml" << EOF
[security]
secret = "$HMAC_SECRET"
enablejwt = $ENABLE_JWT
EOF
if [[ $ENABLE_JWT == "true" ]]; then
cat >> "$CONFIG_DIR/config.toml" << EOF
jwtsecret = "$JWT_SECRET"
jwtalgorithm = "$JWT_ALGORITHM"
jwtexpiration = "$JWT_EXPIRATION"
EOF
fi
cat >> "$CONFIG_DIR/config.toml" << EOF
[uploads]
allowedextensions = [".txt", ".pdf", ".jpg", ".jpeg", ".png", ".gif", ".webp", ".zip", ".tar", ".gz", ".7z", ".mp4", ".webm", ".ogg", ".mp3", ".wav", ".flac", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".odt", ".ods", ".odp"]
maxfilesize = "100MB"
chunkeduploadsenabled = true
chunksize = "10MB"
ttlenabled = false
ttl = "168h"
[downloads]
chunkeddownloadsenabled = true
chunksize = "10MB"
[logging]
level = "INFO"
file = "/var/log/hmac-file-server/hmac-file-server.log"
max_size = 100
max_backups = 3
max_age = 30
compress = true
[workers]
numworkers = 10
uploadqueuesize = 1000
autoscaling = true
[timeouts]
readtimeout = "30s"
writetimeout = "30s"
idletimeout = "120s"
shutdown = "30s"
EOF
if [[ $ENABLE_CLAMAV == "true" ]]; then
cat >> "$CONFIG_DIR/config.toml" << EOF
[clamav]
enabled = true
address = "hmac-clamav:3310"
timeout = "30s"
EOF
else
cat >> "$CONFIG_DIR/config.toml" << EOF
[clamav]
enabled = false
EOF
fi
if [[ $ENABLE_REDIS == "true" ]]; then
cat >> "$CONFIG_DIR/config.toml" << EOF
[redis]
enabled = true
host = "hmac-redis"
port = 6379
database = $REDIS_DB
password = "$REDIS_PASSWORD"
timeout = "5s"
EOF
else
cat >> "$CONFIG_DIR/config.toml" << EOF
[redis]
enabled = false
EOF
fi
# Set ownership and permissions for Docker config
chmod 644 "$CONFIG_DIR/config.toml"
echo -e "${GREEN}Docker configuration file created: $CONFIG_DIR/config.toml${NC}"
}
# Create systemd service
create_systemd_service() {
echo -e "${YELLOW}Creating systemd service...${NC}"
cat > /etc/systemd/system/hmac-file-server.service << EOF
[Unit]
Description=HMAC File Server 3.2
Documentation=https://github.com/PlusOne/hmac-file-server
After=network.target
Wants=network-online.target
EOF
if [[ $ENABLE_REDIS == "true" ]]; then
echo "After=redis.service" >> /etc/systemd/system/hmac-file-server.service
fi
if [[ $ENABLE_CLAMAV == "true" ]]; then
echo "After=clamav-daemon.service" >> /etc/systemd/system/hmac-file-server.service
fi
cat >> /etc/systemd/system/hmac-file-server.service << EOF
[Service]
Type=simple
User=$HMAC_USER
Group=$HMAC_USER
ExecStart=$INSTALL_DIR/hmac-file-server -config $CONFIG_DIR/config.toml
ExecReload=/bin/kill -SIGHUP \$MAINPID
WorkingDirectory=$INSTALL_DIR
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=hmac-file-server
# Security settings
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=$DATA_DIR $DEFAULT_LOG_DIR
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
# Resource limits
LimitNOFILE=65536
LimitNPROC=4096
[Install]
WantedBy=multi-user.target
EOF
# Reload systemd and enable service
systemctl daemon-reload
systemctl enable hmac-file-server.service
echo -e "${GREEN}Systemd service created and enabled${NC}"
}
# Install dependencies
install_dependencies() {
echo -e "${YELLOW}Installing dependencies...${NC}"
# Detect package manager and install dependencies
if command -v apt-get &> /dev/null; then
apt-get update
if [[ $ENABLE_REDIS == "true" ]]; then
apt-get install -y redis-server
systemctl enable redis-server
fi
if [[ $ENABLE_CLAMAV == "true" ]]; then
apt-get install -y clamav clamav-daemon
systemctl enable clamav-daemon
# Update virus definitions
freshclam || true
# Detect ClamAV configuration and configure accordingly
echo -e "${YELLOW}Configuring ClamAV connection...${NC}"
# Check if ClamAV daemon is running and detect socket/port
if systemctl is-active --quiet clamav-daemon; then
echo " ClamAV daemon is running"
# Check for Unix socket (preferred)
if [[ -S "/var/run/clamav/clamd.ctl" ]]; then
echo " Unix socket detected: /var/run/clamav/clamd.ctl"
CLAMAV_CONFIG="socket = \"/var/run/clamav/clamd.ctl\""
elif [[ -S "/run/clamav/clamd.ctl" ]]; then
echo " Unix socket detected: /run/clamav/clamd.ctl"
CLAMAV_CONFIG="socket = \"/run/clamav/clamd.ctl\""
elif [[ -S "/tmp/clamd" ]]; then
echo " Unix socket detected: /tmp/clamd"
CLAMAV_CONFIG="socket = \"/tmp/clamd\""
# Check for TCP port
elif netstat -ln | grep -q ":3310"; then
echo " TCP port detected: 127.0.0.1:3310"
CLAMAV_CONFIG="address = \"127.0.0.1:3310\""
else
echo " ClamAV socket/port not detected, using default Unix socket"
CLAMAV_CONFIG="socket = \"/var/run/clamav/clamd.ctl\""
fi
else
echo " ClamAV daemon not running, using default configuration"
CLAMAV_CONFIG="socket = \"/var/run/clamav/clamd.ctl\""
# Try to start the daemon
echo " Attempting to start ClamAV daemon..."
systemctl start clamav-daemon || echo " Failed to start ClamAV daemon"
fi
fi
elif command -v yum &> /dev/null; then
if [[ $ENABLE_REDIS == "true" ]]; then
yum install -y redis
systemctl enable redis
fi
if [[ $ENABLE_CLAMAV == "true" ]]; then
yum install -y clamav clamav-update clamd
systemctl enable clamd
freshclam || true
fi
elif command -v dnf &> /dev/null; then
if [[ $ENABLE_REDIS == "true" ]]; then
dnf install -y redis
systemctl enable redis
fi
if [[ $ENABLE_CLAMAV == "true" ]]; then
dnf install -y clamav clamav-update clamd
systemctl enable clamd
freshclam || true
fi
else
echo -e "${YELLOW}Unknown package manager. Please install Redis and/or ClamAV manually if needed.${NC}"
fi
}
# Generate secure random key
generate_random_key() {
local length=${1:-48} # Default 48 characters for extra security
local key=""
# Try different methods in order of preference
if command -v openssl &> /dev/null; then
# Method 1: OpenSSL (most common and secure)
key=$(openssl rand -base64 $((length * 3 / 4 + 1)) | tr -d "=+/\n" | cut -c1-$length)
elif command -v head &> /dev/null && [[ -r /dev/urandom ]]; then
# Method 2: /dev/urandom with head (Linux/Unix)
key=$(head -c $((length * 3 / 4 + 1)) /dev/urandom | base64 | tr -d "=+/\n" | cut -c1-$length)
elif command -v dd &> /dev/null && [[ -r /dev/urandom ]]; then
# Method 3: dd with /dev/urandom
key=$(dd if=/dev/urandom bs=$((length * 3 / 4 + 1)) count=1 2>/dev/null | base64 | tr -d "=+/\n" | cut -c1-$length)
elif command -v date &> /dev/null; then
# Method 4: Fallback using date and process info (less secure but works)
local timestamp=$(date +%s%N)
local random_data="${timestamp}${RANDOM}${$}$(hostname)"
key=$(echo -n "$random_data" | sha256sum | cut -c1-$length)
else
# Method 5: Last resort - basic fallback
echo -e "${YELLOW}Warning: Using basic key generation (consider installing openssl)${NC}" >&2
key="hmac-file-server-$(date +%s)-$(hostname | cut -c1-16)"
key=$(echo -n "$key" | sha256sum | cut -c1-$length)
fi
# Ensure exact length
key=$(echo -n "$key" | cut -c1-$length)
# If still too short, pad with additional random data
while [[ ${#key} -lt $length ]]; do
local padding=$(date +%s | sha256sum | cut -c1-$((length - ${#key})))
key="${key}${padding}"
key=$(echo -n "$key" | cut -c1-$length)
done
echo "$key"
}
# Main installation function
main() {
echo -e "${BLUE}Starting HMAC File Server installation...${NC}"
echo ""
# Run pre-installation checks
pre_installation_checks
# Get user input
get_user_input
echo ""
echo -e "${BLUE}Installation Summary:${NC}"
echo "User: $HMAC_USER"
echo "Install Directory: $INSTALL_DIR"
echo "Config Directory: $CONFIG_DIR"
echo "Data Directory: $DATA_DIR"
echo "Server Port: $SERVER_PORT"
echo "Metrics Port: $METRICS_PORT"
echo "JWT Enabled: $ENABLE_JWT"
echo "Redis Enabled: $ENABLE_REDIS"
echo "ClamAV Enabled: $ENABLE_CLAMAV"
echo "TLS Enabled: $ENABLE_TLS"
echo ""
read -p "Continue with installation? (y/N): " CONFIRM
if [[ ! $CONFIRM =~ ^[Yy]$ ]]; then
echo -e "${YELLOW}Installation cancelled.${NC}"
exit 0
fi
echo ""
echo -e "${BLUE}Installing...${NC}"
if [[ "$DEPLOYMENT_TYPE" == "docker" ]]; then
# Docker deployment
echo -e "${YELLOW}Setting up Docker deployment...${NC}"
create_directories
generate_docker_config
create_docker_deployment
echo ""
echo -e "${GREEN}Docker deployment setup complete!${NC}"
echo ""
echo -e "${BLUE}To start your HMAC File Server:${NC}"
echo -e "1. ${YELLOW}cd $DOCKER_DIR${NC}"
echo -e "2. ${YELLOW}./start.sh${NC} (or manually: docker-compose up -d)"
echo ""
echo -e "${BLUE}To stop your HMAC File Server:${NC}"
echo -e "1. ${YELLOW}cd $DOCKER_DIR${NC}"
echo -e "2. ${YELLOW}./stop.sh${NC} (or manually: docker-compose down)"
echo ""
echo -e "${BLUE}Configuration file:${NC} ${YELLOW}$CONFIG_DIR/config.toml${NC}"
echo -e "${BLUE}Data directories:${NC}"
echo -e " Uploads: ${YELLOW}$DATA_DIR/uploads${NC}"
echo -e " Deduplication: ${YELLOW}$DATA_DIR/deduplication${NC}"
echo -e " Logs: ${YELLOW}$DATA_DIR/logs${NC}"
echo ""
else
# Native installation
check_go
create_user
create_directories
install_dependencies
build_server
generate_config
create_systemd_service
# Ask if user wants to start the service now
echo ""
read -p "Start HMAC File Server service now? (Y/n): " START_SERVICE
START_SERVICE=${START_SERVICE:-Y}
if [[ $START_SERVICE =~ ^[Yy]$ ]]; then
echo -e "${YELLOW}Starting HMAC File Server service...${NC}"
systemctl start hmac-file-server.service
# Wait a moment and check status
sleep 3
if systemctl is-active --quiet hmac-file-server.service; then
echo -e "${GREEN}Service started successfully${NC}"
else
echo -e "${RED}Service failed to start. Check logs with: journalctl -u hmac-file-server.service${NC}"
fi
fi
print_completion_info
fi
}
# Function to print completion information
print_completion_info() {
echo ""
echo -e "${GREEN} Installation Complete!${NC}"
echo -e "${GREEN}----------------------------------------------------------------${NC}"
echo -e "${GREEN} HMAC File Server 3.2 Successfully Deployed! ${NC}"
echo -e "${GREEN}----------------------------------------------------------------${NC}"
echo ""
echo -e "${BLUE}Service Information:${NC}"
echo -e " Status: ${YELLOW}sudo systemctl status hmac-file-server${NC}"
echo -e " Logs: ${YELLOW}sudo journalctl -u hmac-file-server -f${NC}"
echo -e " Config: ${YELLOW}sudo nano $CONFIG_DIR/config.toml${NC}"
echo -e " Reload: ${YELLOW}sudo systemctl reload hmac-file-server${NC}"
echo ""
echo -e "${BLUE}Service Endpoints:${NC}"
if [[ $ENABLE_TLS == "true" ]]; then
echo -e " Server: ${YELLOW}https://$(hostname -I | awk '{print $1}'):$SERVER_PORT${NC}"
else
echo -e " Server: ${YELLOW}http://$(hostname -I | awk '{print $1}'):$SERVER_PORT${NC}"
fi
echo -e " Metrics: ${YELLOW}http://$(hostname -I | awk '{print $1}'):$METRICS_PORT/metrics${NC}"
echo ""
echo -e "${BLUE}File Locations:${NC}"
echo -e " Binary: ${YELLOW}$INSTALL_DIR/hmac-file-server${NC}"
echo -e " Config: ${YELLOW}$CONFIG_DIR/config.toml${NC}"
echo -e " Uploads: ${YELLOW}$DATA_DIR/uploads${NC}"
echo -e " Logs: ${YELLOW}$DEFAULT_LOG_DIR/hmac-file-server.log${NC}"
echo ""
echo -e "${BLUE}Quick Commands:${NC}"
echo -e " Start: ${YELLOW}sudo systemctl start hmac-file-server${NC}"
echo -e " Stop: ${YELLOW}sudo systemctl stop hmac-file-server${NC}"
echo -e " Restart: ${YELLOW}sudo systemctl restart hmac-file-server${NC}"
echo -e " Status: ${YELLOW}sudo systemctl status hmac-file-server${NC}"
echo ""
echo -e "${BLUE}Next Steps for XMPP Integration:${NC}"
echo -e "1. ${YELLOW}Configure firewall${NC} to allow ports $SERVER_PORT (server) and $METRICS_PORT (metrics)"
echo -e "2. Configure your reverse proxy (nginx/apache) with SSL"
echo -e "3. Update your Prosody/Ejabberd configuration:"
echo -e " ${YELLOW}http_file_share = \"http://localhost:$SERVER_PORT\"${NC}"
echo -e "4. Set up monitoring and log rotation"
echo -e "5. Test file uploads with your XMPP client"
echo ""
echo -e "${BLUE}Documentation & Support:${NC}"
echo -e " README: https://github.com/PlusOne/hmac-file-server/blob/main/README.MD"
echo -e " Wiki: https://github.com/PlusOne/hmac-file-server/blob/main/WIKI.MD"
echo -e " Issues: https://github.com/PlusOne/hmac-file-server/issues"
echo ""
echo -e "${GREEN}----------------------------------------------------------------${NC}"
echo -e "${GREEN} Thank you for choosing HMAC File Server for your XMPP setup! ${NC}"
echo -e "${GREEN}----------------------------------------------------------------${NC}"
}
# Helper function to safely preserve a directory
preserve_directory() {
local source_dir="$1"
local backup_path="$2"
if [[ -d "$source_dir" ]]; then
local parent_dir=$(dirname "$backup_path")
mkdir -p "$parent_dir"
if mv "$source_dir" "$backup_path" 2>/dev/null; then
echo " Preserved: $source_dir$backup_path"
else
# Fallback to copy if move fails
if cp -r "$source_dir" "$backup_path" 2>/dev/null; then
echo " Copied: $source_dir$backup_path"
rm -rf "$source_dir"
echo " Removed original: $source_dir"
else
echo " Failed to preserve: $source_dir"
fi
fi
else
echo " Directory not found: $source_dir"
fi
}
# Custom data selection for option 4
custom_data_selection() {
echo ""
echo -e "${BLUE}Custom Data Selection:${NC}"
echo "Choose which data directories to preserve:"
echo ""
CUSTOM_PRESERVE_UPLOADS=""
CUSTOM_PRESERVE_DEDUP=""
CUSTOM_PRESERVE_LOGS=""
# Ask about uploads
if [[ -d "$UPLOAD_DIR" ]]; then
FILE_COUNT=$(find "$UPLOAD_DIR" -type f 2>/dev/null | wc -l)
DIR_SIZE=$(du -sh "$UPLOAD_DIR" 2>/dev/null | cut -f1)
echo -e "${GREEN}Upload Directory: ${UPLOAD_DIR}${NC} (Files: $FILE_COUNT, Size: $DIR_SIZE)"
read -p "Preserve upload directory? (y/N): " PRESERVE_UPLOADS
if [[ $PRESERVE_UPLOADS =~ ^[Yy]$ ]]; then
CUSTOM_PRESERVE_UPLOADS="yes"
echo " Will preserve uploads"
else
echo " Will delete uploads"
fi
else
echo -e "${YELLOW}Upload Directory: Not found${NC}"
fi
echo ""
# Ask about deduplication
if [[ -d "$DEDUP_DIR" ]]; then
FILE_COUNT=$(find "$DEDUP_DIR" -type f 2>/dev/null | wc -l)
DIR_SIZE=$(du -sh "$DEDUP_DIR" 2>/dev/null | cut -f1)
echo -e "${GREEN}Deduplication Directory: ${DEDUP_DIR}${NC} (Files: $FILE_COUNT, Size: $DIR_SIZE)"
read -p "Preserve deduplication directory? (y/N): " PRESERVE_DEDUP
if [[ $PRESERVE_DEDUP =~ ^[Yy]$ ]]; then
CUSTOM_PRESERVE_DEDUP="yes"
echo " Will preserve deduplication data"
else
echo " Will delete deduplication data"
fi
else
echo -e "${YELLOW}Deduplication Directory: Not found${NC}"
fi
echo ""
# Ask about logs
if [[ -d "$LOG_DIR" ]]; then
FILE_COUNT=$(find "$LOG_DIR" -type f 2>/dev/null | wc -l)
DIR_SIZE=$(du -sh "$LOG_DIR" 2>/dev/null | cut -f1)
echo -e "${GREEN}Log Directory: ${LOG_DIR}${NC} (Files: $FILE_COUNT, Size: $DIR_SIZE)"
read -p "Preserve log directory? (y/N): " PRESERVE_LOGS
if [[ $PRESERVE_LOGS =~ ^[Yy]$ ]]; then
CUSTOM_PRESERVE_LOGS="yes"
echo " Will preserve logs"
else
echo " Will delete logs"
fi
else
echo -e "${YELLOW}Log Directory: Not found${NC}"
fi
# Store custom selection for later processing
PRESERVE_DATA="custom"
echo ""
echo -e "${BLUE}Custom selection complete:${NC}"
[[ "$CUSTOM_PRESERVE_UPLOADS" == "yes" ]] && echo " Uploads: Preserve" || echo " Uploads: Delete"
[[ "$CUSTOM_PRESERVE_DEDUP" == "yes" ]] && echo " Deduplication: Preserve" || echo " Deduplication: Delete"
[[ "$CUSTOM_PRESERVE_LOGS" == "yes" ]] && echo " Logs: Preserve" || echo " Logs: Delete"
echo ""
}
# Handle custom preservation choices
handle_custom_preservation() {
# Check if any data needs to be preserved
if [[ "$CUSTOM_PRESERVE_UPLOADS" == "yes" || "$CUSTOM_PRESERVE_DEDUP" == "yes" || "$CUSTOM_PRESERVE_LOGS" == "yes" ]]; then
BACKUP_DIR="/var/backups/hmac-file-server-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"
echo " Created backup directory: $BACKUP_DIR"
fi
# Handle uploads
if [[ "$CUSTOM_PRESERVE_UPLOADS" == "yes" ]]; then
preserve_directory "$UPLOAD_DIR" "$BACKUP_DIR/uploads"
elif [[ -d "$UPLOAD_DIR" ]]; then
rm -rf "$UPLOAD_DIR"
echo " Removed uploads: $UPLOAD_DIR"
fi
# Handle deduplication
if [[ "$CUSTOM_PRESERVE_DEDUP" == "yes" ]]; then
preserve_directory "$DEDUP_DIR" "$BACKUP_DIR/deduplication"
elif [[ -d "$DEDUP_DIR" ]]; then
rm -rf "$DEDUP_DIR"
echo " Removed deduplication: $DEDUP_DIR"
fi
# Handle logs
if [[ "$CUSTOM_PRESERVE_LOGS" == "yes" ]]; then
preserve_directory "$LOG_DIR" "$BACKUP_DIR/logs"
elif [[ -d "$LOG_DIR" ]]; then
rm -rf "$LOG_DIR"
echo " Removed logs: $LOG_DIR"
fi
# Remove the main data directory if it's separate and empty
if [[ -d "$DEFAULT_DATA_DIR" ]]; then
# Only remove if it's different from preserved directories and if it's empty or only contains subdirs we've handled
if [[ "$DEFAULT_DATA_DIR" != "$UPLOAD_DIR" && "$DEFAULT_DATA_DIR" != "$DEDUP_DIR" && "$DEFAULT_DATA_DIR" != "$LOG_DIR" ]]; then
# Check if directory is effectively empty (only contains directories we've already handled)
remaining_files=$(find "$DEFAULT_DATA_DIR" -type f 2>/dev/null | wc -l)
if [[ $remaining_files -eq 0 ]]; then
rm -rf "$DEFAULT_DATA_DIR"
echo " Removed empty data directory: $DEFAULT_DATA_DIR"
else
echo " Data directory contains additional files: $DEFAULT_DATA_DIR"
fi
fi
fi
}
# Uninstaller function (can be called with ./installer.sh --uninstall)
uninstall() {
echo ""
echo -e "${RED} HMAC File Server Uninstaller${NC}"
echo -e "${RED}----------------------------------------------------------------${NC}"
echo -e "${RED} Warning: This will remove the server installation! ${NC}"
echo -e "${RED}----------------------------------------------------------------${NC}"
echo ""
read -p "Are you sure you want to uninstall HMAC File Server? (y/N): " CONFIRM_UNINSTALL
if [[ ! $CONFIRM_UNINSTALL =~ ^[Yy]$ ]]; then
echo -e "${YELLOW}Uninstall cancelled${NC}"
exit 0
fi
echo ""
echo -e "${BLUE}Data Preservation Options:${NC}"
echo -e "${BLUE}----------------------------------------------------------------${NC}"
echo ""
echo "The following data directories may contain important files:"
# Check what data directories exist and show their contents
PRESERVE_DATA=""
UPLOAD_DIR=""
DEDUP_DIR=""
LOG_DIR=""
# Find upload directory from config if it exists
if [[ -f "$DEFAULT_CONFIG_DIR/config.toml" ]]; then
UPLOAD_DIR=$(grep -E "^storagepath\s*=" "$DEFAULT_CONFIG_DIR/config.toml" 2>/dev/null | sed 's/.*=\s*"*\([^"]*\)"*.*/\1/' | xargs)
DEDUP_DIR=$(grep -E "^directory\s*=" "$DEFAULT_CONFIG_DIR/config.toml" 2>/dev/null | sed 's/.*=\s*"*\([^"]*\)"*.*/\1/' | xargs)
fi
# Fallback to default locations
[[ -z "$UPLOAD_DIR" ]] && UPLOAD_DIR="$DEFAULT_DATA_DIR/uploads"
[[ -z "$DEDUP_DIR" ]] && DEDUP_DIR="$DEFAULT_DATA_DIR/deduplication"
LOG_DIR="$DEFAULT_LOG_DIR"
# Show upload directory status
if [[ -d "$UPLOAD_DIR" ]]; then
FILE_COUNT=$(find "$UPLOAD_DIR" -type f 2>/dev/null | wc -l)
DIR_SIZE=$(du -sh "$UPLOAD_DIR" 2>/dev/null | cut -f1)
echo -e "${GREEN} Upload Directory: ${UPLOAD_DIR}${NC}"
echo -e " Files: $FILE_COUNT, Size: $DIR_SIZE"
else
echo -e "${YELLOW} Upload Directory: Not found or empty${NC}"
fi
# Show deduplication directory status
if [[ -d "$DEDUP_DIR" ]]; then
FILE_COUNT=$(find "$DEDUP_DIR" -type f 2>/dev/null | wc -l)
DIR_SIZE=$(du -sh "$DEDUP_DIR" 2>/dev/null | cut -f1)
echo -e "${GREEN} Deduplication Directory: ${DEDUP_DIR}${NC}"
echo -e " Files: $FILE_COUNT, Size: $DIR_SIZE"
else
echo -e "${YELLOW} Deduplication Directory: Not found or empty${NC}"
fi
# Show log directory status
if [[ -d "$LOG_DIR" ]]; then
FILE_COUNT=$(find "$LOG_DIR" -type f 2>/dev/null | wc -l)
DIR_SIZE=$(du -sh "$LOG_DIR" 2>/dev/null | cut -f1)
echo -e "${GREEN} Log Directory: ${LOG_DIR}${NC}"
echo -e " Files: $FILE_COUNT, Size: $DIR_SIZE"
else
echo -e "${YELLOW} Log Directory: Not found or empty${NC}"
fi
echo ""
echo -e "${BLUE}Choose data handling option:${NC}"
echo " 1) Delete all data (complete removal)"
echo " 2) Preserve upload and deduplication data only"
echo " 3) Preserve all data (uploads, deduplication, and logs)"
echo " 4) Custom selection (choose what to preserve)"
echo " 5) Cancel uninstallation"
echo ""
while true; do
read -p "Select option (1-5): " DATA_OPTION
case $DATA_OPTION in
1)
echo -e "${RED}Selected: Delete all data${NC}"
PRESERVE_DATA="none"
break
;;
2)
echo -e "${GREEN}Selected: Preserve uploads and deduplication data${NC}"
PRESERVE_DATA="uploads_dedup"
break
;;
3)
echo -e "${GREEN}Selected: Preserve all data${NC}"
PRESERVE_DATA="all"
break
;;
4)
echo -e "${BLUE}Custom selection:${NC}"
custom_data_selection
break
;;
5)
echo -e "${YELLOW}Uninstall cancelled${NC}"
exit 0
;;
*)
echo -e "${RED}Invalid option. Please choose 1-5.${NC}"
;;
esac
done
# Final confirmation for complete deletion
if [[ "$PRESERVE_DATA" == "none" ]]; then
echo ""
echo -e "${RED}FINAL WARNING: This will permanently delete ALL data!${NC}"
echo -e "${RED} This includes all uploaded files, deduplication data, and logs.${NC}"
echo -e "${RED} This action cannot be undone!${NC}"
echo ""
read -p "Type 'DELETE' to confirm complete data removal: " FINAL_CONFIRM
if [[ "$FINAL_CONFIRM" != "DELETE" ]]; then
echo -e "${YELLOW}Uninstall cancelled - confirmation failed${NC}"
exit 0
fi
fi
echo ""
echo -e "${YELLOW}Starting uninstallation process...${NC}"
echo ""
echo -e "${YELLOW}Stopping and disabling service...${NC}"
if systemctl is-active --quiet hmac-file-server.service; then
systemctl stop hmac-file-server.service || true
echo " Service stopped"
else
echo " Service was not running"
fi
if systemctl is-enabled --quiet hmac-file-server.service 2>/dev/null; then
systemctl disable hmac-file-server.service || true
echo " Service disabled"
else
echo " Service was not enabled"
fi
if [[ -f /etc/systemd/system/hmac-file-server.service ]]; then
rm -f /etc/systemd/system/hmac-file-server.service
echo " Service file removed"
else
echo " Service file not found"
fi
systemctl daemon-reload
echo " Systemd reloaded"
echo -e "${YELLOW}Removing installation and configuration...${NC}"
# Always remove installation directory
if [[ -d "$DEFAULT_INSTALL_DIR" ]]; then
rm -rf "$DEFAULT_INSTALL_DIR"
echo " Removed installation directory: $DEFAULT_INSTALL_DIR"
else
echo " Installation directory not found: $DEFAULT_INSTALL_DIR"
fi
# Always remove configuration directory
if [[ -d "$DEFAULT_CONFIG_DIR" ]]; then
rm -rf "$DEFAULT_CONFIG_DIR"
echo " Removed configuration directory: $DEFAULT_CONFIG_DIR"
else
echo " Configuration directory not found: $DEFAULT_CONFIG_DIR"
fi
# Handle data directories based on user choice
echo -e "${YELLOW}Processing data directories...${NC}"
case $PRESERVE_DATA in
"none")
# Delete everything
for dir in "$UPLOAD_DIR" "$DEDUP_DIR" "$LOG_DIR" "$DEFAULT_DATA_DIR"; do
if [[ -d "$dir" ]]; then
rm -rf "$dir"
echo " Removed: $dir"
fi
done
;;
"uploads_dedup")
# Preserve uploads and deduplication, remove logs
if [[ -d "$LOG_DIR" ]]; then
rm -rf "$LOG_DIR"
echo " Removed logs: $LOG_DIR"
fi
# Move preserved data to a safe location
BACKUP_DIR="/var/backups/hmac-file-server-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"
preserve_directory "$UPLOAD_DIR" "$BACKUP_DIR/uploads"
preserve_directory "$DEDUP_DIR" "$BACKUP_DIR/deduplication"
# Remove original data directory structure but keep preserved data
if [[ -d "$DEFAULT_DATA_DIR" && "$DEFAULT_DATA_DIR" != "$UPLOAD_DIR" && "$DEFAULT_DATA_DIR" != "$DEDUP_DIR" ]]; then
rm -rf "$DEFAULT_DATA_DIR"
echo " Removed data directory (preserved content moved to $BACKUP_DIR)"
fi
;;
"all")
# Preserve everything
BACKUP_DIR="/var/backups/hmac-file-server-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"
preserve_directory "$UPLOAD_DIR" "$BACKUP_DIR/uploads"
preserve_directory "$DEDUP_DIR" "$BACKUP_DIR/deduplication"
preserve_directory "$LOG_DIR" "$BACKUP_DIR/logs"
# Remove original data directory structure but keep preserved data
if [[ -d "$DEFAULT_DATA_DIR" ]]; then
rm -rf "$DEFAULT_DATA_DIR"
echo " Removed data directory (all content preserved in $BACKUP_DIR)"
fi
;;
"custom")
# Handle custom selection
handle_custom_preservation
;;
esac
echo -e "${YELLOW}Removing system user...${NC}"
if id "$DEFAULT_USER" &>/dev/null; then
userdel "$DEFAULT_USER" || true
echo " User $DEFAULT_USER removed"
else
echo " User $DEFAULT_USER not found"
fi
# Remove any remaining binary in common locations
echo -e "${YELLOW}Cleaning up any remaining files...${NC}"
for location in "/usr/local/bin/hmac-file-server" "/usr/bin/hmac-file-server"; do
if [[ -f "$location" ]]; then
rm -f "$location"
echo " Removed $location"
fi
done
echo ""
if [[ "$PRESERVE_DATA" != "none" ]]; then
echo -e "${GREEN}HMAC File Server uninstalled successfully with data preservation${NC}"
if [[ -d "$BACKUP_DIR" ]]; then
echo -e "${BLUE}Preserved data location: $BACKUP_DIR${NC}"
echo -e "${BLUE} You can safely delete this directory if you no longer need the data.${NC}"
fi
else
echo -e "${GREEN}HMAC File Server uninstalled completely${NC}"
echo -e "${BLUE}All files, services, and user accounts have been removed.${NC}"
fi
echo ""
}
# Check for help flag
if [[ "$1" == "--help" || "$1" == "-h" ]]; then
show_help
exit 0
fi
# Check for uninstall flag
if [[ "$1" == "--uninstall" ]]; then
uninstall
exit 0
fi
# Run main function
main "$@"