Compare commits

...

28 Commits

Author SHA1 Message Date
96548f4773 small adjustments
All checks were successful
Check scripts syntax / check-scripts-syntax (push) Successful in 36s
Haven Notify Build and Deploy / Build Haven Notify Image (push) Successful in 10m45s
Haven Notify Build and Deploy / Deploy Haven Notify (internal) (push) Successful in 19s
2025-09-07 11:19:56 -03:00
c34ee5185d fixing docker update
All checks were successful
Check scripts syntax / check-scripts-syntax (push) Successful in 2s
2025-08-24 21:07:13 -03:00
1489062943 removing auto update
All checks were successful
Check scripts syntax / check-scripts-syntax (push) Successful in 36s
2025-08-24 20:59:49 -03:00
eb8ca78f4f tweaks to the scripts
All checks were successful
Check scripts syntax / check-scripts-syntax (push) Successful in 41s
2025-08-24 20:55:14 -03:00
45567b2242 updating templates
All checks were successful
Check scripts syntax / check-scripts-syntax (push) Successful in 2s
Haven Notify Build and Deploy / Build Haven Notify Image (push) Successful in 10m0s
Haven Notify Build and Deploy / Deploy Haven Notify (internal) (push) Successful in 11s
2025-08-23 22:01:01 -03:00
b0324ac9d8 forgot copy
Some checks failed
Check scripts syntax / check-scripts-syntax (push) Successful in 3s
Haven Notify Build and Deploy / Build Haven Notify Image (push) Successful in 10m0s
Haven Notify Build and Deploy / Deploy Haven Notify (internal) (push) Failing after 17s
2025-08-23 21:36:32 -03:00
06cf78a4a6 adding error & deploy notification
Some checks failed
Check scripts syntax / check-scripts-syntax (push) Successful in 3s
Haven Notify Build and Deploy / Build Haven Notify Image (push) Successful in 10m1s
Haven Notify Build and Deploy / Deploy Haven Notify (internal) (push) Failing after 11s
2025-08-23 20:44:32 -03:00
cd57837696 templates + arm64 build (slow)
All checks were successful
Check scripts syntax / check-scripts-syntax (push) Successful in 36s
Haven Notify Build and Deploy / Build Haven Notify Image (push) Successful in 10m5s
Haven Notify Build and Deploy / Deploy Haven Notify (internal) (push) Successful in 17s
2025-08-23 20:27:49 -03:00
cedc435df0 maybe this gon work
All checks were successful
Check scripts syntax / check-scripts-syntax (push) Successful in 6s
Haven Notify Build and Deploy / Build Haven Notify Image (push) Successful in 32s
Haven Notify Build and Deploy / Deploy Haven Notify (internal) (push) Successful in 22s
2025-08-22 22:35:50 -03:00
aa7a9b8548 back to only amd64
Some checks failed
Check scripts syntax / check-scripts-syntax (push) Successful in 7s
Haven Notify Build and Deploy / Build Haven Notify Image (push) Failing after 20s
Haven Notify Build and Deploy / Deploy Haven Notify (internal) (push) Has been skipped
2025-08-22 22:33:09 -03:00
11e2a28bd4 removing crossbuild on docker
Some checks failed
Check scripts syntax / check-scripts-syntax (push) Successful in 7s
Haven Notify Build and Deploy / Build Haven Notify Image (push) Failing after 11m14s
Haven Notify Build and Deploy / Deploy Haven Notify (internal) (push) Has been skipped
2025-08-22 22:00:11 -03:00
8b52bd2c45 yet anoter retry
Some checks failed
Check scripts syntax / check-scripts-syntax (push) Successful in 7s
Haven Notify Build and Deploy / Deploy Haven Notify (internal) (push) Has been cancelled
Haven Notify Build and Deploy / Build Haven Notify Image (push) Has been cancelled
2025-08-22 21:50:20 -03:00
442ff12039 new try
Some checks failed
Check scripts syntax / check-scripts-syntax (push) Successful in 6s
Haven Notify Build and Deploy / Deploy Haven Notify (internal) (push) Has been cancelled
Haven Notify Build and Deploy / Build Haven Notify Image (push) Has been cancelled
2025-08-22 21:41:31 -03:00
12920c10d4 anoter retry
Some checks failed
Check scripts syntax / check-scripts-syntax (push) Successful in 6s
Haven Notify Build and Deploy / Deploy Haven Notify (internal) (push) Has been cancelled
Haven Notify Build and Deploy / Build Haven Notify Image (push) Has been cancelled
2025-08-22 21:32:27 -03:00
99e110afb3 removing arch from go build
Some checks failed
Check scripts syntax / check-scripts-syntax (push) Successful in 1m7s
Haven Notify Build and Deploy / Build Haven Notify Image (push) Failing after 13m14s
Haven Notify Build and Deploy / Deploy Haven Notify (internal) (push) Successful in 24s
2025-08-22 21:08:28 -03:00
8108ca7e7b add setup for buildx
Some checks failed
Check scripts syntax / check-scripts-syntax (push) Successful in 8s
Haven Notify Build and Deploy / Deploy Haven Notify (internal) (push) Successful in 24s
Haven Notify Build and Deploy / Build Haven Notify Image (push) Has been cancelled
2025-08-21 22:06:25 -03:00
100262513b Improvements for haven-notify
Some checks failed
Check scripts syntax / check-scripts-syntax (push) Successful in 1m19s
Haven Notify Build and Deploy / Build Haven Notify Image (push) Failing after 1m51s
Haven Notify Build and Deploy / Deploy Haven Notify (internal) (push) Successful in 33s
2025-08-21 21:11:22 -03:00
7520d70ce9 things 2025-08-17 12:40:37 -03:00
7016ced89e improving backup.sh
All checks were successful
Check scripts syntax / check-scripts-syntax (push) Successful in 2s
2025-08-16 22:58:36 -03:00
928605a696 fixing URL
All checks were successful
Check scripts syntax / check-scripts-syntax (push) Successful in 4s
2025-08-16 22:53:15 -03:00
b75240c693 trying to fix arch :c
All checks were successful
Check scripts syntax / check-scripts-syntax (push) Successful in 4s
Haven Notify Build and Deploy / Build Haven Notify Image (amd64) (push) Successful in 16s
Haven Notify Build and Deploy / Deploy Haven Notify (push) Successful in 11s
2025-08-16 22:51:35 -03:00
9ab518e149 improving dockerfile
All checks were successful
Check scripts syntax / check-scripts-syntax (push) Successful in 2s
Haven Notify Build and Deploy / Build Haven Notify Image (amd64) (push) Successful in 24s
Haven Notify Build and Deploy / Deploy Haven Notify (push) Successful in 10s
2025-08-16 22:48:14 -03:00
89f0afe334 improving logging
All checks were successful
Check scripts syntax / check-scripts-syntax (push) Successful in 4s
Haven Notify Build and Deploy / Build Haven Notify Image (amd64) (push) Successful in 20s
Haven Notify Build and Deploy / Deploy Haven Notify (push) Successful in 10s
2025-08-16 22:46:02 -03:00
018a4a5d60 updating haven notify
All checks were successful
Check scripts syntax / check-scripts-syntax (push) Successful in 3s
Haven Notify Build and Deploy / Build Haven Notify Image (amd64) (push) Successful in 15s
Haven Notify Build and Deploy / Deploy Haven Notify (push) Successful in 11s
2025-08-16 22:41:41 -03:00
cbd813a76e whoops
All checks were successful
Check scripts syntax / check-scripts-syntax (push) Successful in 2s
Haven Notify Build and Deploy / Build Haven Notify Image (amd64) (push) Successful in 18s
Haven Notify Build and Deploy / Deploy Haven Notify (push) Successful in 11s
2025-08-16 22:38:07 -03:00
5f77376a46 updating backup.sh
All checks were successful
Check scripts syntax / check-scripts-syntax (push) Successful in 2s
2025-08-16 22:35:10 -03:00
a32bcb34a3 haven notify!
All checks were successful
Check scripts syntax / check-scripts-syntax (push) Successful in 2s
Haven Notify Build and Deploy / Build Haven Notify Image (amd64) (push) Successful in 24s
Haven Notify Build and Deploy / Deploy Haven Notify (push) Successful in 10s
2025-08-16 22:31:54 -03:00
20ef0eb4b5 haven notify only for amd64 2025-08-16 22:31:41 -03:00
14 changed files with 500 additions and 281 deletions

View File

@@ -16,41 +16,9 @@ env:
KUBE_CONFIG: ${{ secrets.KUBE_CONFIG }} KUBE_CONFIG: ${{ secrets.KUBE_CONFIG }}
jobs: jobs:
# build_haven_notify: build_haven_notify:
# name: Build Haven Notify Image (arm64) name: Build Haven Notify Image
# runs-on: ubuntu-arm64 runs-on: ubuntu-22.04
# steps:
# - name: Check out repository
# uses: actions/checkout@v2
# - name: Install Docker
# run: |
# apt-get update
# apt-get install -y docker.io
# - name: Log in to Container Registry
# run: |
# echo "${{ secrets.REGISTRY_PASSWORD }}" \
# | docker login "${{ env.REGISTRY_HOST }}" \
# -u "${{ env.REGISTRY_USERNAME }}" \
# --password-stdin
# - name: Build and Push Image
# run: |
# TAG=latest
# cd haven-notify
# docker build \
# -t "${{ env.IMAGE_NOTIFY }}:${TAG}" \
# -f Dockerfile .
# docker push "${{ env.IMAGE_NOTIFY }}:${TAG}"
build_haven_notify_amd64:
name: Build Haven Notify Image (amd64)
runs-on: ubuntu-latest
steps: steps:
- name: Check out repository - name: Check out repository
@@ -63,27 +31,25 @@ jobs:
-u "${{ env.REGISTRY_USERNAME }}" \ -u "${{ env.REGISTRY_USERNAME }}" \
--password-stdin --password-stdin
- name: Display Directory Structure - name: Set up QEMU
run: | uses: docker/setup-qemu-action@v3
echo "Current directory structure:"
ls -lR .
- name: Build and Push Image - name: Set up Docker Buildx
run: | uses: docker/setup-buildx-action@v3
TAG=latest
cd haven-notify - name: Build and Push Multi-Arch Image
uses: docker/build-push-action@v6
docker build \ with:
-t "${{ env.IMAGE_NOTIFY }}:${TAG}" \ push: true
-f Dockerfile . context: haven-notify
platforms: linux/amd64,linux/arm64
docker push "${{ env.IMAGE_NOTIFY }}:${TAG}" tags: |
${{ env.IMAGE_NOTIFY }}:latest
deploy_haven_notify: deploy_haven_notify:
name: Deploy Haven Notify name: Deploy Haven Notify (internal)
runs-on: ubuntu-amd64 runs-on: ubuntu-amd64
needs: build_haven_notify_amd64 needs: build_haven_notify
steps: steps:
- name: Check KUBE_CONFIG validity - name: Check KUBE_CONFIG validity
run: | run: |
@@ -110,7 +76,7 @@ jobs:
env: env:
KUBE_CONFIG: ${{ env.KUBE_CONFIG }} KUBE_CONFIG: ${{ env.KUBE_CONFIG }}
- name: Test connection to cluster - name: Check connection to cluster
run: | run: |
cd haven-notify/deploy cd haven-notify/deploy
kubectl --kubeconfig=kubeconfig.yaml cluster-info kubectl --kubeconfig=kubeconfig.yaml cluster-info

View File

@@ -1,40 +1,23 @@
#!/bin/bash #!/bin/bash
### AUTO-UPDATER ### # Function to send notification
# Variables HOSTNAME=$(cat /etc/hostname)
SERVER_FILE="https://git.ivanch.me/ivanch/server-scripts/raw/branch/main/backup.sh" NOTIFY_URL_ERROR="http://notify.haven/template/notify/error"
SERVER_OK=1 NOTIFY_URL_BACKUP="http://notify.haven/template/notify/backup"
send_error_notification() {
# Check if the server file exists local message="$1"
curl -s --head $SERVER_FILE | head -n 1 | grep -E "HTTP/[12] [23].." > /dev/null local critical="$2"
curl -s -X POST "$NOTIFY_URL_ERROR" \
if [ $? -ne 0 ]; then -H "Content-Type: application/json" \
echo "Error: $SERVER_FILE not found." >&2 -d "{\"caller\": \"Docker Backup - $HOSTNAME\", \"message\": \"$message\", \"critical\": $critical}"
SERVER_OK=0 }
fi send_backup_notification() {
local message="$1"
if [ $SERVER_OK -eq 1 ]; then local backup_size="$2"
echo "Running auto-update..." curl -s -X POST "$NOTIFY_URL_BACKUP" \
-H "Content-Type: application/json" \
# Compare the local and server files sha256sum to check if an update is needed -d "{\"title\": \"Docker Backup - $HOSTNAME\", \"message\": \"$message\", \"backupSizeInMB\": $backup_size}"
LOCAL_SHA256=$(sha256sum backup.sh | awk '{print $1}') }
SERVER_SHA256=$(curl -s $SERVER_FILE | sha256sum | awk '{print $1}')
if [ "$LOCAL_SHA256" != "$SERVER_SHA256" ]; then
echo "Updating backup.sh..."
curl -s -o backup.sh $SERVER_FILE
echo "backup.sh updated."
chmod +x backup.sh
echo "Permissions set up."
echo "Running updated backup.sh..."
./backup.sh
exit 0
else
echo "backup.sh is up to date.."
fi
fi
#################### ####################
@@ -46,21 +29,34 @@ REMOTE_HOST="nas.haven"
REMOTE_DIR="/export/Backup/Docker/$(cat /etc/hostname)" REMOTE_DIR="/export/Backup/Docker/$(cat /etc/hostname)"
# Create a compressed backup file # Create a compressed backup file
zip -r $BACKUP_FILE $SOURCE_DIR zip -q -r $BACKUP_FILE $SOURCE_DIR || true
if [ $? -ne 0 ]; then
send_error_notification "⚠️ Some files or folders in $SOURCE_DIR could not be backed up (possibly in use or locked). Backup archive created with available files." false
fi
# Check if remote path exists # Check if remote path exists
ssh $REMOTE_USER@$REMOTE_HOST "mkdir -p $REMOTE_DIR" if ! ssh $REMOTE_USER@$REMOTE_HOST "mkdir -p $REMOTE_DIR"; then
send_error_notification "❌ Failed to create remote directory: $REMOTE_DIR on $REMOTE_HOST" true
exit 1
fi
# Transfer the backup file to the remote server # Transfer the backup file to the remote server
scp $BACKUP_FILE $REMOTE_USER@$REMOTE_HOST:$REMOTE_DIR if ! scp $BACKUP_FILE $REMOTE_USER@$REMOTE_HOST:$REMOTE_DIR; then
send_error_notification "❌ Failed to transfer backup file to remote server: $REMOTE_HOST:$REMOTE_DIR" true
exit 1
fi
# Remove the backup file # Remove the backup file
BACKUP_SIZE=$(du -m $BACKUP_FILE | cut -f1)
rm $BACKUP_FILE rm $BACKUP_FILE
# Erase last 7 days backups from remote server # Erase last 7 days backups from remote server
ssh $REMOTE_USER@$REMOTE_HOST "find $REMOTE_DIR -type f -name 'docker_backup_*' -mtime +7 -exec rm {} \;" if ! ssh $REMOTE_USER@$REMOTE_HOST "find $REMOTE_DIR -type f -name 'docker_backup_*' -mtime +7 -exec rm {} \;"; then
send_error_notification "⚠️ Failed to clean old backups on remote server: $REMOTE_HOST:$REMOTE_DIR" false
fi
# Exit # Success notification
send_backup_notification "✅ Backup completed successfully for: $SOURCE_DIR to $REMOTE_HOST:$REMOTE_DIR" $BACKUP_SIZE
echo "Backup completed successfully" echo "Backup completed successfully"
exit 0 exit 0

View File

@@ -4,7 +4,6 @@
# #
# Description: Comprehensive system cleanup for Docker containers and Linux systems # Description: Comprehensive system cleanup for Docker containers and Linux systems
# Features: # Features:
# - Self-updating capability
# - Docker resource cleanup (images, containers, volumes, networks) # - Docker resource cleanup (images, containers, volumes, networks)
# - Package manager cache cleanup (APK/APT) # - Package manager cache cleanup (APK/APT)
# - System cache and temporary file cleanup # - System cache and temporary file cleanup
@@ -30,10 +29,6 @@ readonly LIGHT_BLUE='\033[1;34m'
readonly LIGHT_GREY='\033[0;37m' readonly LIGHT_GREY='\033[0;37m'
readonly YELLOW='\033[1;33m' readonly YELLOW='\033[1;33m'
# Script configuration
readonly SCRIPT_NAME="clean.sh"
readonly SERVER_BASE_URL="https://git.ivanch.me/ivanch/server-scripts/raw/branch/main"
# Cleanup configuration # Cleanup configuration
readonly LOG_RETENTION_DAYS=30 readonly LOG_RETENTION_DAYS=30
readonly JOURNAL_RETENTION_DAYS=7 readonly JOURNAL_RETENTION_DAYS=7
@@ -133,79 +128,6 @@ get_system_info() {
echo "$info" echo "$info"
} }
#==============================================================================
# AUTO-UPDATE FUNCTIONALITY
#==============================================================================
# Check server connectivity
check_server_connectivity() {
local url="$1"
curl -s --head "$url" | head -n 1 | grep -E "HTTP/[12] [23].." >/dev/null 2>&1
}
# Get SHA256 hash of a file
get_file_hash() {
local file="$1"
sha256sum "$file" 2>/dev/null | awk '{print $1}' || echo ""
}
# Get SHA256 hash from URL content
get_url_hash() {
local url="$1"
curl -s "$url" 2>/dev/null | sha256sum | awk '{print $1}' || echo ""
}
# Perform self-update if newer version is available
perform_self_update() {
if [[ "$AUTO_UPDATE_ENABLED" != "true" ]]; then
log_info "Auto-update is disabled"
return 0
fi
local server_url="$SERVER_BASE_URL/$SCRIPT_NAME"
log_step "Checking for script updates..."
# Check if server file is accessible
if ! check_server_connectivity "$server_url"; then
log_warning "Cannot connect to update server, continuing with current version"
return 0
fi
# Compare local and server file hashes
local local_hash server_hash
local_hash=$(get_file_hash "$SCRIPT_NAME")
server_hash=$(get_url_hash "$server_url")
if [[ -z "$local_hash" || -z "$server_hash" ]]; then
log_warning "Cannot determine file hashes, skipping update"
return 0
fi
if [[ "$local_hash" != "$server_hash" ]]; then
log_info "Update available, downloading new version..."
# Create backup of current script
local backup_file="${SCRIPT_NAME}.backup.$(date +%s)"
cp "$SCRIPT_NAME" "$backup_file" || die "Failed to create backup"
# Download updated script
if curl -s -o "$SCRIPT_NAME" "$server_url"; then
chmod +x "$SCRIPT_NAME" || die "Failed to set executable permissions"
log_success "Script updated successfully"
log_step "Running updated script..."
exec ./"$SCRIPT_NAME" "$@"
else
# Restore backup on failure
mv "$backup_file" "$SCRIPT_NAME"
die "Failed to download updated script"
fi
else
log_success "Script is already up to date"
fi
}
#============================================================================== #==============================================================================
# DOCKER CLEANUP FUNCTIONS # DOCKER CLEANUP FUNCTIONS
#============================================================================== #==============================================================================
@@ -526,9 +448,6 @@ main() {
echo echo
fi fi
# Perform self-update if enabled
perform_self_update "$@"
# Docker cleanup # Docker cleanup
cleanup_docker cleanup_docker

View File

@@ -4,7 +4,6 @@
# #
# Description: Automatically updates Docker containers and manages Docker images # Description: Automatically updates Docker containers and manages Docker images
# Features: # Features:
# - Self-updating capability
# - Updates all Docker Compose projects in /root/docker # - Updates all Docker Compose projects in /root/docker
# - Skips containers with .ignore file # - Skips containers with .ignore file
# - Removes obsolete Docker Compose version attributes # - Removes obsolete Docker Compose version attributes
@@ -13,6 +12,22 @@
# Version: 2.0 # Version: 2.0
set -euo pipefail # Exit on error, undefined vars, and pipe failures set -euo pipefail # Exit on error, undefined vars, and pipe failures
HOSTNAME=$(cat /etc/hostname)
NOTIFY_URL_ERROR="http://notify.haven/template/notify/error"
NOTIFY_URL_UPDATE="http://notify.haven/template/notify/update"
send_error_notification() {
local message="$1"
local critical="$2"
curl -s -X POST "$NOTIFY_URL_ERROR" \
-H "Content-Type: application/json" \
-d "{\"caller\": \"$HOSTNAME\", \"message\": \"$message\", \"critical\": $critical}"
}
send_update_notification() {
local script_time="$1"
curl -s -X POST "$NOTIFY_URL_UPDATE" \
-H "Content-Type: application/json" \
-d "{\"host\": \"$HOSTNAME\", \"asset\": \"Docker containers\", \"time\": $script_time}"
}
#============================================================================== #==============================================================================
# CONFIGURATION # CONFIGURATION
@@ -28,8 +43,6 @@ readonly LIGHT_GREY='\033[0;37m'
readonly YELLOW='\033[1;33m' readonly YELLOW='\033[1;33m'
# Script configuration # Script configuration
readonly SCRIPT_NAME="docker-updater.sh"
readonly SERVER_BASE_URL="https://git.ivanch.me/ivanch/server-scripts/raw/branch/main"
readonly DOCKER_FOLDER="/root/docker" readonly DOCKER_FOLDER="/root/docker"
readonly COMPOSE_FILES=("docker-compose.yml" "docker-compose.yaml" "compose.yaml" "compose.yml") readonly COMPOSE_FILES=("docker-compose.yml" "docker-compose.yaml" "compose.yaml" "compose.yml")
@@ -43,11 +56,18 @@ readonly AUTO_UPDATE_ENABLED=true
# Print formatted log messages # Print formatted log messages
log_info() { echo -e "${LIGHT_GREY}[i] $1${NC}"; } log_info() { echo -e "${LIGHT_GREY}[i] $1${NC}"; }
log_success() { echo -e "${LIGHT_GREEN}[✓] $1${NC}"; } log_success() { echo -e "${LIGHT_GREEN}[✓] $1${NC}"; }
log_warning() { echo -e "${YELLOW}[!] $1${NC}"; }
log_error() { echo -e "${RED}[x] $1${NC}" >&2; }
log_step() { echo -e "${LIGHT_BLUE}[i] $1${NC}"; } log_step() { echo -e "${LIGHT_BLUE}[i] $1${NC}"; }
log_container() { echo -e "${LIGHT_BLUE}[$1] $2${NC}"; } log_container() { echo -e "${LIGHT_BLUE}[$1] $2${NC}"; }
log_warning() {
echo -e "${YELLOW}[!] $1${NC}";
send_error_notification "$1" false
}
log_error() {
echo -e "${RED}[x] $1${NC}" >&2;
send_error_notification "$1" true
}
# Exit with error message # Exit with error message
die() { die() {
log_error "$1" log_error "$1"
@@ -86,69 +106,6 @@ get_url_hash() {
curl -s "$url" 2>/dev/null | sha256sum | awk '{print $1}' || echo "" curl -s "$url" 2>/dev/null | sha256sum | awk '{print $1}' || echo ""
} }
# Check if server file is accessible
check_server_connectivity() {
local url="$1"
curl -s --head "$url" | head -n 1 | grep -E "HTTP/[12] [23].." >/dev/null 2>&1
}
#==============================================================================
# AUTO-UPDATE FUNCTIONALITY
#==============================================================================
# Perform self-update if newer version is available
perform_self_update() {
if [[ "$AUTO_UPDATE_ENABLED" != "true" ]]; then
log_info "Auto-update is disabled"
return 0
fi
local server_url="$SERVER_BASE_URL/$SCRIPT_NAME"
log_step "Checking for script updates..."
# Check if server file is accessible
if ! check_server_connectivity "$server_url"; then
log_warning "Cannot connect to update server, continuing with current version"
return 0
fi
# Compare local and server file hashes
local local_hash
local server_hash
local_hash=$(get_file_hash "$SCRIPT_NAME")
server_hash=$(get_url_hash "$server_url")
if [[ -z "$local_hash" || -z "$server_hash" ]]; then
log_warning "Cannot determine file hashes, skipping update"
return 0
fi
if [[ "$local_hash" != "$server_hash" ]]; then
log_info "Update available, downloading new version..."
# Create backup of current script
local backup_file="${SCRIPT_NAME}.backup.$(date +%s)"
cp "$SCRIPT_NAME" "$backup_file" || die "Failed to create backup"
# Download updated script
if curl -s -o "$SCRIPT_NAME" "$server_url"; then
chmod +x "$SCRIPT_NAME" || die "Failed to set executable permissions"
log_success "Script updated successfully"
log_step "Running updated script..."
exec ./"$SCRIPT_NAME" "$@"
else
# Restore backup on failure
mv "$backup_file" "$SCRIPT_NAME"
die "Failed to download updated script"
fi
else
log_success "Script is already up to date"
fi
}
#============================================================================== #==============================================================================
# DOCKER COMPOSE MANAGEMENT # DOCKER COMPOSE MANAGEMENT
#============================================================================== #==============================================================================
@@ -260,37 +217,17 @@ update_all_docker_projects() {
# Change to Docker folder # Change to Docker folder
cd "$DOCKER_FOLDER" || die "Cannot access Docker folder: $DOCKER_FOLDER" cd "$DOCKER_FOLDER" || die "Cannot access Docker folder: $DOCKER_FOLDER"
local updated_count=0
local failed_count=0
local skipped_count=0
# Process each subdirectory # Process each subdirectory
for project_dir in */; do for project_dir in */; do
if [[ -d "$project_dir" ]]; then if [[ -d "$project_dir" ]]; then
local project_path="$DOCKER_FOLDER/$project_dir" local project_path="$DOCKER_FOLDER/$project_dir"
if update_docker_project "$project_path"; then update_docker_project "$project_path"
if should_skip_container; then
((skipped_count++))
else
((updated_count++))
fi
else
((failed_count++))
fi
# Return to Docker folder for next iteration # Return to Docker folder for next iteration
cd "$DOCKER_FOLDER" || die "Cannot return to Docker folder" cd "$DOCKER_FOLDER" || die "Cannot return to Docker folder"
fi fi
done done
# Report results
log_success "Docker update summary:"
log_info " Updated: $updated_count projects"
log_info " Skipped: $skipped_count projects"
if [[ $failed_count -gt 0 ]]; then
log_warning " Failed: $failed_count projects"
fi
} }
#============================================================================== #==============================================================================
@@ -315,15 +252,14 @@ cleanup_docker_resources() {
#============================================================================== #==============================================================================
main() { main() {
START_TIME=$(date +%s)
log_step "Starting Docker Container Updater" log_step "Starting Docker Container Updater"
echo echo
# Check requirements # Check requirements
check_docker_requirements check_docker_requirements
# Perform self-update if enabled
perform_self_update "$@"
# Update all Docker projects # Update all Docker projects
update_all_docker_projects update_all_docker_projects
@@ -332,6 +268,12 @@ main() {
echo echo
log_success "Docker container update process completed!" log_success "Docker container update process completed!"
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
log_info "Total duration: $DURATION seconds"
send_update_notification $DURATION
} }
# Execute main function with all arguments # Execute main function with all arguments

View File

@@ -1,12 +1,20 @@
# Start from the official Golang image for building # Start from the official Golang image for building
FROM golang:1.25 AS builder FROM --platform=$BUILDPLATFORM golang:1.22-alpine AS builder
ARG TARGETARCH
ARG TARGETOS
WORKDIR /app WORKDIR /app
COPY . . COPY . .
RUN go build -o haven-notify main.go # Build statically for Linux
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o haven-notify main.go
# Use a minimal image for running # Use Alpine for running, with CA certificates for TLS
FROM busybox:latest FROM alpine:latest
WORKDIR /app WORKDIR /app
RUN apk --no-cache add ca-certificates
COPY template/ template/
COPY --from=builder /app/haven-notify . COPY --from=builder /app/haven-notify .
EXPOSE 8080 EXPOSE 8080
ENV WEBHOOK_URL=""
ENTRYPOINT ["/app/haven-notify"] ENTRYPOINT ["/app/haven-notify"]

95
haven-notify/README.md Normal file
View File

@@ -0,0 +1,95 @@
<div align="center">
<img src="./assets/widelogo.png" alt="Haven Notify Logo">
</div>
## Overview
Haven Notify is an internal service designed to send notifications to a specified Discord channel.
It's built in Go and can be deployed as a container or managed service.
## Prerequisites
- Go 1.18 or newer
- Docker
- A Discord Webhook URL
## API Specification
### Send Notification
- **Endpoint**: `/notify`
- **Method**: `POST`
- **Request Body**:
```json
{
"title": "Notification Title",
"message": "Notification Message"
}
```
### Send Backup Notification
- **Endpoint**: `/template/notify/backup`
- **Method**: `POST`
- **Request Body**:
```json
{
"title": "Notification Title",
"asset": "Notification Asset Name",
"backupSizeInMB": 500,
"extra": [
{
"name": "Additional Info",
"value": "Some extra information"
}
]
}
```
### Send Update Notification
- **Endpoint**: `/template/notify/update`
- **Method**: `POST`
- **Request Body**:
```json
{
"host": "Notification Title",
"asset": "Notification Message",
"time": 500 // in seconds
}
```
### Send Error Notification
- **Endpoint**: `/template/notify/error`
- **Method**: `POST`
- **Request Body**:
```json
{
"caller": "Who triggered the error",
"message": "Error while moving file",
"critical": true,
"extra": [
{
"name": "Additional Info",
"value": "Some extra information"
}
]
}
```
## Setup & Usage
### Docker
1. Build the Docker image:
```sh
docker build -t haven-notify .
```
2. Run the container:
```sh
docker run -e WEBHOOK_URL=your_webhook_url haven-notify
```
### Kubernetes
Deployment manifest is available at `deploy/haven-notify.yaml`.
1. Edit the manifest to set your environment variables.
2. Create a generic secret named `WEBHOOK_URL` with `discord-webhook=your_webhook_url`
3. Apply deployment:
```sh
kubectl apply -f deploy/haven-notify.yaml
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 KiB

View File

@@ -20,6 +20,24 @@ spec:
imagePullPolicy: Always imagePullPolicy: Always
ports: ports:
- containerPort: 8080 - containerPort: 8080
env:
- name: WEBHOOK_URL
valueFrom:
secretKeyRef:
name: discord-webhook
key: HAVEN_WEBHOOK_URL
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /live
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
@@ -41,6 +59,7 @@ metadata:
annotations: annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web traefik.ingress.kubernetes.io/router.entrypoints: web
spec: spec:
ingressClassName: nginx
rules: rules:
- host: notify.haven - host: notify.haven
http: http:

View File

@@ -1,12 +1,16 @@
package main package main
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"html/template"
"io/ioutil"
"log" "log"
"net/http" "net/http"
"os"
"strings"
"time"
) )
// Notification payload // Notification payload
@@ -17,12 +21,17 @@ type Notification struct {
func main() { func main() {
http.HandleFunc("/notify", notifyHandler) http.HandleFunc("/notify", notifyHandler)
http.HandleFunc("/ready", readinessHandler)
http.HandleFunc("/live", livenessHandler)
http.HandleFunc("/template/notify/", templateNotifyHandler)
log.Println("Starting server on :8080...") log.Println("Starting server on :8080...")
log.Fatal(http.ListenAndServe(":8080", nil)) log.Fatal(http.ListenAndServe(":8080", nil))
} }
func notifyHandler(w http.ResponseWriter, r *http.Request) { func notifyHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("Incoming %s request from %s to %s", r.Method, r.RemoteAddr, r.URL.Path)
if r.Method != http.MethodPost { if r.Method != http.MethodPost {
log.Printf("Method not allowed: %s", r.Method)
w.WriteHeader(http.StatusMethodNotAllowed) w.WriteHeader(http.StatusMethodNotAllowed)
w.Write([]byte("Method not allowed")) w.Write([]byte("Method not allowed"))
return return
@@ -30,24 +39,44 @@ func notifyHandler(w http.ResponseWriter, r *http.Request) {
var notif Notification var notif Notification
if err := json.NewDecoder(r.Body).Decode(&notif); err != nil { if err := json.NewDecoder(r.Body).Decode(&notif); err != nil {
log.Printf("Invalid payload: %v", err)
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Invalid payload")) w.Write([]byte("Invalid payload"))
return return
} }
log.Printf("Received notification payload: Title='%s', Message='%s'", notif.Title, notif.Message)
// Call Discord notification function // Call Discord notification function
if err := sendDiscordNotification(notif.Title, notif.Message); err != nil { if err := sendDiscordNotification(notif.Title, notif.Message); err != nil {
log.Printf("Failed to send Discord notification: %v", err)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Failed to send Discord notification")) w.Write([]byte("Failed to send Discord notification"))
return return
} }
log.Printf("Notification sent successfully for Title='%s'", notif.Title)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write([]byte("Notification sent")) w.Write([]byte("Notification sent"))
} }
// Readiness handler
func readinessHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("Ready"))
}
// Liveness handler
func livenessHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("Alive"))
}
func sendDiscordNotification(title, message string) error { func sendDiscordNotification(title, message string) error {
const webhookURL = "" webhookURL := os.Getenv("WEBHOOK_URL")
if webhookURL == "" {
log.Printf("WEBHOOK_URL environment variable not set")
return fmt.Errorf("WEBHOOK_URL environment variable not set")
}
// Discord webhook payload // Discord webhook payload
type discordPayload struct { type discordPayload struct {
@@ -59,18 +88,127 @@ func sendDiscordNotification(title, message string) error {
jsonData, err := json.Marshal(payload) jsonData, err := json.Marshal(payload)
if err != nil { if err != nil {
log.Printf("Failed to marshal Discord payload: %v", err)
return err return err
} }
log.Printf("Sending Discord notification: Title='%s', Message='%s'", title, message)
resp, err := http.Post(webhookURL, "application/json", bytes.NewBuffer(jsonData)) resp, err := http.Post(webhookURL, "application/json", bytes.NewBuffer(jsonData))
if err != nil { if err != nil {
log.Printf("Error posting to Discord webhook: %v", err)
return err return err
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 { if resp.StatusCode < 200 || resp.StatusCode >= 300 {
log.Printf("Discord webhook returned status: %s", resp.Status)
return fmt.Errorf("Discord webhook returned status: %s", resp.Status) return fmt.Errorf("Discord webhook returned status: %s", resp.Status)
} }
log.Printf("Discord notification sent successfully: Title='%s'", title)
return nil return nil
} }
func templateNotifyHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("Incoming %s request from %s to %s", r.Method, r.RemoteAddr, r.URL.Path)
if r.Method != http.MethodPost {
log.Printf("Method not allowed: %s", r.Method)
w.WriteHeader(http.StatusMethodNotAllowed)
w.Write([]byte("Method not allowed"))
return
}
templateName := r.URL.Path[len("/template/notify/"):] // Extract template name
if templateName == "" {
log.Printf("Template name not provided")
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Template name not provided"))
return
}
templatePath := "template/" + templateName + ".tmpl"
templateData, err := ioutil.ReadFile(templatePath)
if err != nil {
log.Printf("Failed to read template: %v", err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Failed to read template"))
return
}
tmpl, err := template.New(templateName).Funcs(template.FuncMap{
"formatSize": func(size float64) string {
if size > 1024 {
return fmt.Sprintf("%.2f GiB", size/1024)
}
return fmt.Sprintf("%.2f MiB", size)
},
"upper": strings.ToUpper,
"lower": strings.ToLower,
"title": strings.Title,
"now": func() string {
return fmt.Sprintf("%d", time.Now().Unix())
},
"formatTime": func(timestamp string) string {
if timestamp == "" {
return time.Now().Format("2006-01-02T15:04:05Z")
}
return timestamp
},
}).Parse(string(templateData))
if err != nil {
log.Printf("Failed to parse template: %v", err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Failed to parse template"))
return
}
var rawPayload map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&rawPayload); err != nil {
log.Printf("Invalid payload: %v", err)
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Invalid payload"))
return
}
// Normalize keys to lowercase for case-insensitive parsing
payload := make(map[string]interface{})
for key, value := range rawPayload {
payload[strings.ToLower(key)] = value
}
var filledTemplate bytes.Buffer
if err := tmpl.Execute(&filledTemplate, payload); err != nil {
log.Printf("Failed to execute template: %v", err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Failed to execute template"))
return
}
webhookURL := os.Getenv("WEBHOOK_URL")
if webhookURL == "" {
log.Printf("WEBHOOK_URL environment variable not set")
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("WEBHOOK_URL environment variable not set"))
return
}
resp, err := http.Post(webhookURL, "application/json", &filledTemplate)
if err != nil {
log.Printf("Error posting to Discord webhook: %v", err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Failed to send notification"))
return
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
log.Printf("Discord webhook returned status: %s", resp.Status)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Failed to send notification"))
return
}
log.Printf("Notification sent successfully using template '%s'", templateName)
w.WriteHeader(http.StatusOK)
w.Write([]byte("Notification sent"))
}

View File

@@ -0,0 +1,39 @@
{{/*
Docker Backup Notification Template
Variables expected:
- .title: The backup title/name
- .asset: The asset being backed up
- .backupsizeinmb: The backup size in MB (will be formatted automatically)
- .extra: Optional array of additional fields with .name and .value
Template Functions Available:
- formatSize: Formats size in MB/GB automatically
*/}}
{
"embeds": [
{
"title": "📦 Backup - {{.title}}",
"description": "**{{.asset}}** has been backup-ed successfully! ✅🫡\n",
"color": 3066993,
"fields": [
{
"name": "💾 Backup Size",
"value": "{{if .backupsizeinmb}}{{formatSize .backupsizeinmb}}{{else}}Unknown{{end}}",
"inline": true
}
{{- if .extra}}
{{- range $index, $field := .extra}},
{
"name": "{{$field.name}}",
"value": "{{$field.value}}",
"inline": true
}
{{- end}}
{{- end}}
],
"footer": {
"text": "✨ Haven Notify ✨"
}
}
]
}

View File

@@ -0,0 +1,36 @@
{{/*
Error Notification Template
Variables expected:
- .caller: The caller of the error
- .message: The error message
- .critical: Boolean indicating if the error is critical
- .extra: Optional array of additional fields with .name and .value
*/}}
{
"embeds": [
{
"title": "❌ Error",
"description": "**{{.caller}}** encountered an error!",
"color": {{if .critical}}15158332{{else}}15844367{{end}},
"fields": [
{
"name": "📄 Message",
"value": "{{.message}}",
"inline": false
}
{{- if .extra}}
{{- range $index, $field := .extra}},
{
"name": "{{$field.name}}",
"value": "{{$field.value}}",
"inline": true
}
{{- end}}
{{- end}}
],
"footer": {
"text": "✨ Haven Notify ✨"
}
}
]
}

View File

@@ -0,0 +1,29 @@
{{/*
Update Notification Template
Variables expected:
- .host: The host where the update occurred
- .asset: The asset being updated (Docker or k8s)
- .time: The time in seconds that the script took to run
Template Functions Available:
- formatTime: Formats time in seconds to a human-readable format
*/}}
{
"embeds": [
{
"title": "🔄 Update - {{.asset}}",
"description": "**{{.host}}** has successfully updated **{{.asset}}**! ✅",
"color": 3447003,
"fields": [
{
"name": "⏱️ Time Taken",
"value": "{{if .time}}{{.time}}{{else}}Unknown{{end}} seconds",
"inline": true
}
],
"footer": {
"text": "✨ Haven Notify ✨"
}
}
]
}

View File

@@ -26,9 +26,9 @@ readonly AVAILABLE_SCRIPTS=("clean.sh" "backup.sh" "docker-updater.sh")
# Format: [script_name]="cron_schedule" # Format: [script_name]="cron_schedule"
declare -A CRONTAB_SCHEDULES=( declare -A CRONTAB_SCHEDULES=(
["clean.sh"]="0 23 * * *" # Daily at 11 PM ["clean.sh"]="0 3 * * *" # Daily at 3 AM
["backup.sh"]="30 23 * * 1,5" # Monday and Friday at 11:30 PM ["backup.sh"]="0 23 * * 1,5" # Monday and Friday at 11 PM
["docker-updater.sh"]="0 3 */4 * *" # Every 4 days at 3 AM ["docker-updater.sh"]="0 3 * * 6" # Every Saturday at 3 AM
) )
#============================================================================== #==============================================================================

View File

@@ -4,18 +4,41 @@ $7zipPath = "$env:ProgramFiles\7-Zip\7z.exe"
if (!(Test-Path "$env:ProgramFiles\7-Zip\7z.exe")) { if (!(Test-Path "$env:ProgramFiles\7-Zip\7z.exe")) {
Write-Host "7-Zip is not installed. Please install it to use this script." Write-Host "7-Zip is not installed. Please install it to use this script."
exit 1 exit 1
Send-Notify "❌ 7-Zip is not installed. Backup aborted."
} }
$BackupSource = @( $BackupSource = @(
"$env:USERPROFILE\Documents", "$env:USERPROFILE\Documents",
"$env:USERPROFILE\Desktop", "$env:USERPROFILE\Desktop",
"$env:USERPROFILE\Pictures" "$env:USERPROFILE\Pictures",
"$env:USERPROFILE\.ssh",
"$env:USERPROFILE\.kube"
) )
$NASDestination = "\\OMV\Backup\$env:COMPUTERNAME" $NASDestination = "\\OMV\Backup\$env:COMPUTERNAME"
$TempDir = "$env:TEMP\BackupTemp" $TempDir = "$env:TEMP\BackupTemp"
$Date = Get-Date -Format "yyyy-MM-dd" $Date = Get-Date -Format "yyyy-MM-dd"
$NotifyUrl = "http://notify.haven/notify"
function Send-Notify {
param (
[string]$Message
)
if (-not $NotifyUrl) {
Write-Host "NOTIFY_URL environment variable is not set. Notification not sent."
return
}
$Title = "Backup - $env:COMPUTERNAME"
$Body = @{ title = $Title; message = $Message } | ConvertTo-Json
try {
Invoke-RestMethod -Uri $NotifyUrl -Method Post -ContentType 'application/json' -Body $Body | Out-Null
Write-Host "Notification sent: $Title - $Message"
} catch {
Write-Host "Failed to send notification: $_"
}
}
# Create temp directory # Create temp directory
New-Item -ItemType Directory -Path $TempDir -Force New-Item -ItemType Directory -Path $TempDir -Force
@@ -30,18 +53,26 @@ foreach ($Folder in $BackupSource) {
$ZipFile = "$TempDir\$FolderName-$Date.zip" $ZipFile = "$TempDir\$FolderName-$Date.zip"
Write-Host "Compressing $Folder..." Write-Host "Compressing $Folder..."
& "$7zipPath" a -tzip "$ZipFile" "$Folder\*" -mx=9 $compressResult = & "$7zipPath" a -tzip "$ZipFile" "$Folder\*" -mx=9
if ($LASTEXITCODE -ne 0) {
Write-Host "Compression failed for $Folder."
Send-Notify "❌ Compression failed for $Folder."
continue
}
Write-Host "Copying $ZipFile to NAS..." Write-Host "Copying $ZipFile to NAS..."
Copy-Item $ZipFile $NASDestination -Force Copy-Item $ZipFile $NASDestination -Force
Write-Host "Removing $ZipFile..." Write-Host "Removing $ZipFile..."
Remove-Item $ZipFile Remove-Item $ZipFile
} else {
Write-Host "Source folder not found: $Folder"
Send-Notify "⚠️ Source folder not found: $Folder"
} }
} }
Write-Host "Removing Files older than 15 days from $NASDestination..." Write-Host "Removing Files older than 7 days from $NASDestination..."
$OldFiles = Get-ChildItem -Path $NASDestination -File | Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-15) } $OldFiles = Get-ChildItem -Path $NASDestination -File | Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-7) }
foreach ($OldFile in $OldFiles) { foreach ($OldFile in $OldFiles) {
Remove-Item $OldFile.FullName -Force Remove-Item $OldFile.FullName -Force
Write-Host "Removed: $($OldFile.FullName)" Write-Host "Removed: $($OldFile.FullName)"
@@ -50,3 +81,4 @@ foreach ($OldFile in $OldFiles) {
# Cleanup # Cleanup
Remove-Item $TempDir -Recurse -Force Remove-Item $TempDir -Recurse -Force
Write-Host "Backup completed!" Write-Host "Backup completed!"
Send-Notify "✅ Backup completed successfully."