| 1 | #!/bin/sh |
| 2 | set -eu |
| 3 | |
| 4 | TIMESTAMP=$(date -u +%Y-%m-%dT%H%M%SZ) |
| 5 | BUCKET="${R2_BUCKET:-andromeda-backups}" |
| 6 | RETENTION_DAYS="${RETENTION_DAYS:-30}" |
| 7 | |
| 8 | DBS="jotts:/data/jotts/jotts.sqlite sipp:/data/sipp/sipp.sqlite cellar:/data/cellar/cellar.sqlite posts:/data/posts/posts.sqlite feeds:/data/feeds/feeds.sqlite library:/data/library/library.sqlite bookmarks:/data/bookmarks/bookmarks.sqlite parcels:/data/parcels/parcels.db easel:/data/easel/easel.sqlite" |
| 9 | |
| 10 | for entry in $DBS; do |
| 11 | name="${entry%%:*}" |
| 12 | path="${entry#*:}" |
| 13 | |
| 14 | if [ ! -f "$path" ]; then |
| 15 | echo "WARN: $path not found, skipping $name" |
| 16 | continue |
| 17 | fi |
| 18 | |
| 19 | backup_file="/tmp/${name}-${TIMESTAMP}.sqlite" |
| 20 | echo "$(date -u) Backing up $name..." |
| 21 | sqlite3 "$path" ".backup '$backup_file'" |
| 22 | gzip "$backup_file" |
| 23 | aws s3 cp "${backup_file}.gz" "s3://${BUCKET}/${name}/${TIMESTAMP}.sqlite.gz" \ |
| 24 | --endpoint-url "${R2_ENDPOINT}" |
| 25 | rm -f "${backup_file}.gz" |
| 26 | echo "$(date -u) OK: $name uploaded" |
| 27 | done |
| 28 | |
| 29 | # Prune old backups |
| 30 | cutoff=$(date -u -d "-${RETENTION_DAYS} days" +%Y-%m-%d 2>/dev/null || date -u -v-${RETENTION_DAYS}d +%Y-%m-%d) |
| 31 | for name in jotts sipp cellar posts feeds library bookmarks parcels easel; do |
| 32 | aws s3 ls "s3://${BUCKET}/${name}/" --endpoint-url "${R2_ENDPOINT}" 2>/dev/null | while read -r line; do |
| 33 | filedate=$(echo "$line" | awk '{print $1}') |
| 34 | filename=$(echo "$line" | awk '{print $4}') |
| 35 | if [ -n "$filename" ] && [ "$filedate" \< "$cutoff" ]; then |
| 36 | aws s3 rm "s3://${BUCKET}/${name}/${filename}" --endpoint-url "${R2_ENDPOINT}" |
| 37 | echo "$(date -u) Pruned: ${name}/${filename}" |
| 38 | fi |
| 39 | done |
| 40 | done |
| 41 | |
| 42 | echo "$(date -u) Backup complete" |