diff --git a/LICENSE b/LICENSE index 100a0a5..9e6581c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,26 +1,25 @@ -GNU AFFERO GENERAL PUBLIC LICENSE -Version 3, 19 November 2007 +MIT License ---- +Copyright (c) Patrick Asmus (scriptos) -Copyright © - -Name: Patrick Asmus (scriptos) -Email: support@techniverse.net Website: https://www.patrick-asmus.de Blog: https://www.cleveradmin.de +Email: support@techniverse.net ---- +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index dc14603..9cb6c9c 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,10 @@

-

Name des Projekts

+

Kutt Links Cleanup

- Kurzbeschreibung des Projekts/Anwendung, um die es geht + Bash-Script zum automatischen Aufräumen anonymer Links in einer selbst gehosteten Kutt-Instanz.

@@ -22,7 +22,84 @@

-CONTENT BEREICH +## Was macht das Script? + +Anonyme Links (ohne Account erstellt) werden gelöscht, wenn sie beispielhaft seit **365 Tagen nicht mehr aufgerufen** wurden. Als Referenzdatum gilt: + +- Der **letzte Aufruf** (letzter Eintrag in der `visits`-Tabelle) +- Falls der Link **nie aufgerufen** wurde: das **Erstelldatum** + +Links von eingeloggten Usern werden **nie** angefasst. + +## Voraussetzungen + +- Kutt läuft als Docker-Setup mit einem PostgreSQL-Container namens `kutt-db` +- Der ausführende User hat Zugriff auf `docker exec` + +## Installation + +### 1. Script ablegen + +```bash +mkdir -p /home/docker-projekte/kutt/scripts +cp kutt-links-cleanup.sh /home/docker-projekte/kutt/scripts/ +chmod +x /home/docker-projekte/kutt/scripts/kutt-links-cleanup.sh +``` + +### 2. Cronjob einrichten + +Täglich um 03:00 Uhr ausführen: + +```bash +crontab -e +``` + +Folgende Zeile hinzufügen: + +``` +0 3 * * * /home/docker-projekte/kutt/scripts/kutt-links-cleanup.sh >> /home/docker-projekte/kutt/scripts/cleanup.log 2>&1 +``` + +## Verwendung + +### Dry Run (nur anzeigen, nichts löschen) + +```bash +./kutt-links-cleanup.sh --dry-run +``` + +Zeigt alle Links an, die gelöscht werden würden — ohne etwas zu verändern. + +### Ausführen (Links löschen) + +```bash +./kutt-links-cleanup.sh +``` + +### Log prüfen + +```bash +cat /home/docker-projekte/kutt/scripts/cleanup.log +``` + +## Konfiguration + +Die Variablen stehen am Anfang des Scripts: + +| Variable | Standard | Beschreibung | +|---|---|---| +| `CONTAINER` | `kutt-db` | Name des PostgreSQL-Containers | +| `DB_USER` | `kutt` | Datenbank-Benutzer | +| `DB_NAME` | `kutt` | Datenbank-Name | +| `MAX_AGE_DAYS` | `365` | Tage ohne Zugriff bis zur Löschung | + +## Technische Details + +- Gelöschte Links sind sofort aus der Kutt-Oberfläche verschwunden +- Zugehörige Visit-Statistiken werden automatisch mitgelöscht (`ON DELETE CASCADE`) +- Kutt nutzt Redis nur für Rate-Limiting, nicht als Link-Cache — es gibt kein Cache-Problem nach dem Löschen +- Die freigewordenen Short-Link-Adressen können danach wieder vergeben werden +

diff --git a/kutt-links-cleanup.sh b/kutt-links-cleanup.sh new file mode 100644 index 0000000..62f9ac3 --- /dev/null +++ b/kutt-links-cleanup.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# +# Löscht anonyme Kutt-Links die seit %MAX_AGE_DAYS% Tagen nicht mehr aufgerufen wurden. +# Als Referenzdatum gilt der letzte Visit oder — falls nie aufgerufen — das Erstelldatum. +# +# Nutzung: Per Cron z.B. täglich ausführen. +# 0 3 * * * /home/docker-projekte/kutt/scripts/kutt-links-cleanup.sh +# + +set -euo pipefail + +CONTAINER="kutt-db" +DB_USER="kutt" +DB_NAME="kutt" +MAX_AGE_DAYS=365 +DRY_RUN="${1:-}" + +SQL_SELECT=" +SELECT l.id, l.address, l.target, + COALESCE(MAX(v.created_at), l.created_at) AS last_activity +FROM links l +LEFT JOIN visits v ON v.link_id = l.id +WHERE l.user_id IS NULL +GROUP BY l.id +HAVING COALESCE(MAX(v.created_at), l.created_at) < NOW() - INTERVAL '${MAX_AGE_DAYS} days' +ORDER BY last_activity; +" + +SQL_DELETE=" +DELETE FROM links +WHERE id IN ( + SELECT l.id + FROM links l + LEFT JOIN visits v ON v.link_id = l.id + WHERE l.user_id IS NULL + GROUP BY l.id + HAVING COALESCE(MAX(v.created_at), l.created_at) < NOW() - INTERVAL '${MAX_AGE_DAYS} days' +); +" + +echo "$(date '+%Y-%m-%d %H:%M:%S') — Cleanup anonymer Links (älter als ${MAX_AGE_DAYS} Tage ohne Zugriff)" + +if [ "$DRY_RUN" = "--dry-run" ]; then + echo "DRY RUN — es wird nichts gelöscht." + docker exec "$CONTAINER" psql -U "$DB_USER" -d "$DB_NAME" -c "$SQL_SELECT" +else + CANDIDATES=$(docker exec "$CONTAINER" psql -U "$DB_USER" -d "$DB_NAME" -t -A -c " + SELECT COUNT(*) FROM links l + LEFT JOIN visits v ON v.link_id = l.id + WHERE l.user_id IS NULL + GROUP BY l.id + HAVING COALESCE(MAX(v.created_at), l.created_at) < NOW() - INTERVAL '${MAX_AGE_DAYS} days'; + " | wc -l) + + if [ "$CANDIDATES" -eq 0 ]; then + echo "Keine abgelaufenen anonymen Links gefunden." + exit 0 + fi + + echo "${CANDIDATES} Link(s) werden gelöscht..." + docker exec "$CONTAINER" psql -U "$DB_USER" -d "$DB_NAME" -c "$SQL_DELETE" + echo "Fertig." +fi