feat(skills): add coruna-lab-migrate and coruna-lab-cleanup skills
- coruna-lab-migrate: server migration SOP (code/db/file transfer, supervisor setup, DNS cutover, verification, common pitfalls) - coruna-lab-cleanup: disk cleanup skill with disk_cleanup.sh and cleanup_scanned_photos.php scripts Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/**
|
||||
* Clean up photos that were scanned (scan_status=1, "已扫-空") but have no mnemonic findings.
|
||||
*
|
||||
* These photos were scanned for mnemonic phrases but nothing was found,
|
||||
* so they are safe to delete: file blobs, photo_reads rows, and photos rows.
|
||||
*
|
||||
* Usage:
|
||||
* php cleanup_scanned_photos.php [project-path] [--execute]
|
||||
*
|
||||
* Without --execute: dry-run (counts only, deletes nothing)
|
||||
* With --execute: deletes files and DB rows in batches of 500
|
||||
*
|
||||
* Requires: Laravel project with photos, photo_reads, mnemonic_findings tables.
|
||||
*/
|
||||
require __DIR__.'/vendor/autoload.php';
|
||||
$app = require_once __DIR__.'/bootstrap/app.php';
|
||||
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
|
||||
$kernel->bootstrap();
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
$execute = in_array('--execute', $argv ?? []);
|
||||
$dryRun = !$execute;
|
||||
|
||||
echo ($dryRun ? 'DRY-RUN' : 'EXECUTE') . " clean scan_status=1 photos without mnemonic findings\n";
|
||||
|
||||
$disk = Storage::disk('local');
|
||||
$batchSize = 500;
|
||||
$totalSeen = 0;
|
||||
$totalFiles = 0;
|
||||
$totalRows = 0;
|
||||
$totalReads = 0;
|
||||
$lastId = 0;
|
||||
$batchNum = 0;
|
||||
|
||||
while (true) {
|
||||
$photos = DB::table('photos')
|
||||
->select(['id', 'device_id', 'path'])
|
||||
->where('scan_status', 1)
|
||||
->where('id', '>', $lastId)
|
||||
->whereNotIn('id', function ($q) {
|
||||
$q->select('photo_id')->from('mnemonic_findings')->whereNotNull('photo_id');
|
||||
})
|
||||
->orderBy('id')
|
||||
->limit($batchSize)
|
||||
->get();
|
||||
|
||||
if ($photos->isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
$batchNum++;
|
||||
$ids = [];
|
||||
foreach ($photos as $photo) {
|
||||
$ids[] = $photo->id;
|
||||
$totalSeen++;
|
||||
if (!$dryRun) {
|
||||
$path = trim((string) ($photo->path ?? ''));
|
||||
if ($path !== '' && $disk->exists($path)) {
|
||||
$disk->delete($path);
|
||||
$totalFiles++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$dryRun && !empty($ids)) {
|
||||
$deletedReads = DB::table('photo_reads')->whereIn('photo_id', $ids)->delete();
|
||||
$totalReads += $deletedReads;
|
||||
DB::table('photos')->whereIn('id', $ids)->delete();
|
||||
$totalRows += count($ids);
|
||||
}
|
||||
|
||||
$lastId = (int) $photos->last()->id;
|
||||
|
||||
if ($batchNum % 20 === 0 || $photos->count() < $batchSize) {
|
||||
echo sprintf(
|
||||
" batch=%d seen=%d %s reads=%d\n",
|
||||
$batchNum,
|
||||
$totalSeen,
|
||||
$dryRun ? '(dry-run)' : "deleted_files=$totalFiles deleted_rows=$totalRows",
|
||||
$totalReads
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n=== Done ===\n";
|
||||
echo "matched_photos: $totalSeen\n";
|
||||
if (!$dryRun) {
|
||||
echo "deleted_files: $totalFiles\n";
|
||||
echo "deleted_photo_reads: $totalReads\n";
|
||||
echo "deleted_photos_rows: $totalRows\n";
|
||||
} else {
|
||||
echo "dry-run: pass --execute to delete\n";
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# coruna-lab disk cleanup — full pipeline
|
||||
#
|
||||
# Usage: sudo bash disk_cleanup.sh [project_root] [--execute]
|
||||
#
|
||||
# Without --execute: dry-run (shows what would be cleaned, deletes nothing)
|
||||
# With --execute: performs all cleanup steps
|
||||
#
|
||||
# Env vars (override defaults):
|
||||
# LOG_RETENTION_DAYS=7 public/log retention (days)
|
||||
# DS_RESULTS_RETENTION_DAYS=1 ds-results retention (days)
|
||||
# DS_CHUNK_RETENTION_DAYS=1 ds-chunks retention (days)
|
||||
# INBOX_RETENTION_DAYS=3 inbox retention (days)
|
||||
# CLEAN_NGINX_LOGS=1 truncate large nginx logs (1=yes, 0=no)
|
||||
# CLEAN_BACKUPS=1 delete /www/backup contents (1=yes, 0=no)
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PROJECT="${1:-/www/wwwroot/coruna-lab}"
|
||||
EXECUTE_FLAG="${2:-}"
|
||||
EXECUTE=0
|
||||
[ "$EXECUTE_FLAG" = "--execute" ] && EXECUTE=1
|
||||
|
||||
# Retention settings (can be overridden by env vars)
|
||||
LOG_DAYS="${LOG_RETENTION_DAYS:-7}"
|
||||
DS_RESULTS_DAYS="${DS_RESULTS_RETENTION_DAYS:-1}"
|
||||
DS_CHUNK_DAYS="${DS_CHUNK_RETENTION_DAYS:-1}"
|
||||
INBOX_DAYS="${INBOX_RETENTION_DAYS:-3}"
|
||||
CLEAN_NGINX_LOGS="${CLEAN_NGINX_LOGS:-1}"
|
||||
CLEAN_BACKUPS="${CLEAN_BACKUPS:-1}"
|
||||
|
||||
STORAGE_C2="$PROJECT/storage/app/private/c2"
|
||||
PUBLIC_LOG="$PROJECT/public/log"
|
||||
DELETE_CMD=""
|
||||
if [ $EXECUTE -eq 1 ]; then
|
||||
DELETE_CMD="-delete"
|
||||
echo "=== EXECUTE MODE — files will be deleted ==="
|
||||
else
|
||||
echo "=== DRY-RUN MODE — no files will be deleted ==="
|
||||
fi
|
||||
|
||||
echo ""
|
||||
df -h / 2>/dev/null | tail -1
|
||||
echo ""
|
||||
|
||||
# ─── 1. Old application logs (public/log) ───────────────────────────
|
||||
echo ">>> 1. Cleaning public/log (>$LOG_DAYS days)"
|
||||
find "$PUBLIC_LOG" -type f -name "*.log" -mtime +$LOG_DAYS -print $DELETE_CMD 2>/dev/null | wc -l | xargs -I{} echo " matched files: {}"
|
||||
rm -f "$PUBLIC_LOG"/*.tar.gz 2>/dev/null && echo " removed tar.gz archives" || true
|
||||
|
||||
# ─── 2. Scanned photos without mnemonic findings ────────────────────
|
||||
echo ">>> 2. Cleaning scan_status=1 photos (no mnemonic findings)"
|
||||
if [ -f "$PROJECT/cleanup_scanned_photos.php" ]; then
|
||||
SCRIPT="$PROJECT/cleanup_scanned_photos.php"
|
||||
elif [ -f "$(dirname "$0")/cleanup_scanned_photos.php" ]; then
|
||||
SCRIPT="$(dirname "$0")/cleanup_scanned_photos.php"
|
||||
cp "$SCRIPT" "$PROJECT/cleanup_scanned_photos.php"
|
||||
else
|
||||
echo " ERROR: cleanup_scanned_photos.php not found, skipping"
|
||||
SCRIPT=""
|
||||
fi
|
||||
if [ -n "$SCRIPT" ]; then
|
||||
if [ $EXECUTE -eq 1 ]; then
|
||||
(cd "$PROJECT" && php cleanup_scanned_photos.php --execute 2>&1 | tail -5)
|
||||
rm -f "$PROJECT/cleanup_scanned_photos.php"
|
||||
else
|
||||
(cd "$PROJECT" && php cleanup_scanned_photos.php 2>&1 | tail -3)
|
||||
fi
|
||||
fi
|
||||
|
||||
# ─── 3. Broken images (file missing, DB row exists) ─────────────────
|
||||
echo ">>> 3. Pruning broken-image DB rows (file missing on disk)"
|
||||
if [ $EXECUTE -eq 1 ]; then
|
||||
(cd "$PROJECT" && php artisan coruna:prune-missing-photo-files --execute 2>&1 | tail -3)
|
||||
else
|
||||
(cd "$PROJECT" && php artisan coruna:prune-missing-photo-files 2>&1 | tail -3)
|
||||
fi
|
||||
|
||||
# ─── 4. Photo preview caches ────────────────────────────────────────
|
||||
echo ">>> 4. Clearing photo preview caches"
|
||||
du -sh "$STORAGE_C2/photo-previews/" 2>/dev/null || true
|
||||
if [ $EXECUTE -eq 1 ]; then
|
||||
rm -rf "$STORAGE_C2/photo-previews/"* 2>/dev/null
|
||||
echo " cleared"
|
||||
fi
|
||||
|
||||
# ─── 5. DS results (keep N days) ────────────────────────────────────
|
||||
echo ">>> 5. Cleaning ds-results (>$DS_RESULTS_DAYS days)"
|
||||
find "$STORAGE_C2/ds-results/" -type f -mtime +$DS_RESULTS_DAYS -print $DELETE_CMD 2>/dev/null | wc -l | xargs -I{} echo " deleted files: {}"
|
||||
if [ $EXECUTE -eq 1 ]; then
|
||||
find "$STORAGE_C2/ds-results/" -type d -empty -delete 2>/dev/null
|
||||
fi
|
||||
|
||||
# ─── 6. DS chunks (keep N days) ────────────────────────────────────
|
||||
echo ">>> 6. Cleaning ds-chunks (>$DS_CHUNK_DAYS days)"
|
||||
find "$STORAGE_C2/ds-chunks/" -type f -mtime +$DS_CHUNK_DAYS -print $DELETE_CMD 2>/dev/null | wc -l | xargs -I{} echo " deleted files: {}"
|
||||
if [ $EXECUTE -eq 1 ]; then
|
||||
find "$STORAGE_C2/ds-chunks/" -type d -empty -delete 2>/dev/null
|
||||
fi
|
||||
|
||||
# ─── 7. Inbox (keep N days) ─────────────────────────────────────────
|
||||
echo ">>> 7. Cleaning inbox (>$INBOX_DAYS days)"
|
||||
find "$STORAGE_C2/inbox/" -type f -mtime +$INBOX_DAYS -print $DELETE_CMD 2>/dev/null | wc -l | xargs -I{} echo " deleted files: {}"
|
||||
if [ $EXECUTE -eq 1 ]; then
|
||||
find "$STORAGE_C2/inbox/" -type d -empty -delete 2>/dev/null
|
||||
fi
|
||||
|
||||
# ─── 8. Nginx logs (truncate large active logs) ─────────────────────
|
||||
if [ "$CLEAN_NGINX_LOGS" = "1" ] && [ $EXECUTE -eq 1 ]; then
|
||||
echo ">>> 8. Truncating nginx logs (/www/wwwlogs)"
|
||||
for f in /www/wwwlogs/*.log; do
|
||||
sz=$(stat -c%s "$f" 2>/dev/null || echo 0)
|
||||
if [ "$sz" -gt 104857600 ]; then
|
||||
truncate -s 0 "$f"
|
||||
echo " truncated $(basename $f) ($(numfmt --to=iec $sz))"
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo ">>> 8. Nginx logs: skipped (CLEAN_NGINX_LOGS=$CLEAN_NGINX_LOGS or dry-run)"
|
||||
fi
|
||||
|
||||
# ─── 9. Backups ────────────────────────────────────────────────────
|
||||
if [ "$CLEAN_BACKUPS" = "1" ] && [ $EXECUTE -eq 1 ]; then
|
||||
echo ">>> 9. Cleaning /www/backup"
|
||||
rm -rf /www/backup/backup_restore/* /www/backup/database/* /www/backup/file_history/* /www/backup/panel/* 2>/dev/null || true
|
||||
rm -f /www/backup/*.Bak /www/backup/*.bak /www/backup/nginxBak 2>/dev/null || true
|
||||
echo " cleaned"
|
||||
else
|
||||
echo ">>> 9. Backups: skipped (CLEAN_BACKUPS=$CLEAN_BACKUPS or dry-run)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== Result ==="
|
||||
df -h / 2>/dev/null | tail -1
|
||||
Reference in New Issue
Block a user