Remove scripts/ directory from repo
One-off migration scripts and deploy helpers do not belong in the repository. Run them locally or from /tmp as needed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,72 +0,0 @@
|
|||||||
#!/usr/bin/env php
|
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Migration: Add closed_at column to tickets table
|
|
||||||
*
|
|
||||||
* Adds a dedicated timestamp for when tickets are closed,
|
|
||||||
* so avg resolution time isn't inflated by post-close edits.
|
|
||||||
*
|
|
||||||
* Usage: php scripts/add_closed_at_column.php
|
|
||||||
*/
|
|
||||||
|
|
||||||
require_once dirname(__DIR__) . '/config/config.php';
|
|
||||||
|
|
||||||
$conn = new mysqli(
|
|
||||||
$GLOBALS['config']['DB_HOST'],
|
|
||||||
$GLOBALS['config']['DB_USER'],
|
|
||||||
$GLOBALS['config']['DB_PASS'],
|
|
||||||
$GLOBALS['config']['DB_NAME']
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($conn->connect_error) {
|
|
||||||
die("Database connection failed: " . $conn->connect_error . "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "Adding closed_at column to tickets table...\n";
|
|
||||||
|
|
||||||
// Add the column if it doesn't exist
|
|
||||||
$result = $conn->query("SHOW COLUMNS FROM tickets LIKE 'closed_at'");
|
|
||||||
if ($result->num_rows > 0) {
|
|
||||||
echo "Column 'closed_at' already exists, skipping ALTER TABLE.\n";
|
|
||||||
} else {
|
|
||||||
$sql = "ALTER TABLE tickets ADD COLUMN closed_at TIMESTAMP NULL DEFAULT NULL AFTER updated_at";
|
|
||||||
if ($conn->query($sql)) {
|
|
||||||
echo "Column added successfully.\n";
|
|
||||||
} else {
|
|
||||||
die("Failed to add column: " . $conn->error . "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add index for stats queries
|
|
||||||
$conn->query("CREATE INDEX idx_tickets_closed_at ON tickets (closed_at)");
|
|
||||||
echo "Index created.\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Backfill: For existing closed tickets, use the audit log to find when they were closed
|
|
||||||
echo "\nBackfilling closed_at from audit log...\n";
|
|
||||||
|
|
||||||
$sql = "UPDATE tickets t
|
|
||||||
JOIN (
|
|
||||||
SELECT entity_id as ticket_id, MIN(created_at) as first_closed
|
|
||||||
FROM audit_log
|
|
||||||
WHERE entity_type = 'ticket'
|
|
||||||
AND action_type = 'update'
|
|
||||||
AND details LIKE '%\"status\":\"Closed\"%'
|
|
||||||
GROUP BY entity_id
|
|
||||||
) al ON t.ticket_id = al.ticket_id
|
|
||||||
SET t.closed_at = al.first_closed
|
|
||||||
WHERE t.status = 'Closed' AND t.closed_at IS NULL";
|
|
||||||
|
|
||||||
$result = $conn->query($sql);
|
|
||||||
$backfilled = $conn->affected_rows;
|
|
||||||
echo "Backfilled $backfilled tickets from audit log.\n";
|
|
||||||
|
|
||||||
// For any remaining closed tickets without audit log entries, use updated_at as fallback
|
|
||||||
$sql = "UPDATE tickets SET closed_at = updated_at WHERE status = 'Closed' AND closed_at IS NULL";
|
|
||||||
$conn->query($sql);
|
|
||||||
$fallback = $conn->affected_rows;
|
|
||||||
if ($fallback > 0) {
|
|
||||||
echo "Used updated_at as fallback for $fallback tickets without audit log entries.\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "\nMigration complete!\n";
|
|
||||||
$conn->close();
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Migration script to add updated_at column to ticket_comments table
|
|
||||||
* Run this on the production server: php scripts/add_comment_updated_at.php
|
|
||||||
*/
|
|
||||||
|
|
||||||
require_once dirname(__DIR__) . '/config/config.php';
|
|
||||||
|
|
||||||
echo "Adding updated_at column to ticket_comments table...\n";
|
|
||||||
|
|
||||||
try {
|
|
||||||
$conn = new mysqli(
|
|
||||||
$GLOBALS['config']['DB_HOST'],
|
|
||||||
$GLOBALS['config']['DB_USER'],
|
|
||||||
$GLOBALS['config']['DB_PASS'],
|
|
||||||
$GLOBALS['config']['DB_NAME']
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($conn->connect_error) {
|
|
||||||
throw new Exception("Connection failed: " . $conn->connect_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if column already exists
|
|
||||||
$result = $conn->query("SHOW COLUMNS FROM ticket_comments LIKE 'updated_at'");
|
|
||||||
|
|
||||||
if ($result->num_rows > 0) {
|
|
||||||
echo "Column 'updated_at' already exists in ticket_comments table.\n";
|
|
||||||
} else {
|
|
||||||
// Add the column
|
|
||||||
$sql = "ALTER TABLE ticket_comments ADD COLUMN updated_at TIMESTAMP NULL DEFAULT NULL AFTER created_at";
|
|
||||||
|
|
||||||
if ($conn->query($sql)) {
|
|
||||||
echo "Successfully added 'updated_at' column to ticket_comments table.\n";
|
|
||||||
} else {
|
|
||||||
throw new Exception("Failed to add column: " . $conn->error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$conn->close();
|
|
||||||
echo "Done!\n";
|
|
||||||
|
|
||||||
} catch (Exception $e) {
|
|
||||||
echo "Error: " . $e->getMessage() . "\n";
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
#!/usr/bin/env php
|
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Cleanup Orphan Uploads
|
|
||||||
*
|
|
||||||
* Removes uploaded files that are no longer associated with any ticket.
|
|
||||||
* Run periodically via cron: 0 2 * * * php /path/to/cleanup_orphan_uploads.php
|
|
||||||
*/
|
|
||||||
|
|
||||||
require_once dirname(__DIR__) . '/config/config.php';
|
|
||||||
|
|
||||||
$conn = new mysqli(
|
|
||||||
$GLOBALS['config']['DB_HOST'],
|
|
||||||
$GLOBALS['config']['DB_USER'],
|
|
||||||
$GLOBALS['config']['DB_PASS'],
|
|
||||||
$GLOBALS['config']['DB_NAME']
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($conn->connect_error) {
|
|
||||||
die("Database connection failed: " . $conn->connect_error . "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
$uploadsDir = dirname(__DIR__) . '/uploads';
|
|
||||||
$dryRun = in_array('--dry-run', $argv);
|
|
||||||
|
|
||||||
if ($dryRun) {
|
|
||||||
echo "DRY RUN MODE - No files will be deleted\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "Scanning uploads directory: $uploadsDir\n";
|
|
||||||
|
|
||||||
// Get all valid ticket IDs from database
|
|
||||||
$ticketIds = [];
|
|
||||||
$result = $conn->query("SELECT ticket_id FROM tickets");
|
|
||||||
if (!$result) {
|
|
||||||
die("Failed to query tickets: " . $conn->error . "\n");
|
|
||||||
}
|
|
||||||
while ($row = $result->fetch_assoc()) {
|
|
||||||
$ticketIds[$row['ticket_id']] = true;
|
|
||||||
}
|
|
||||||
echo "Found " . count($ticketIds) . " tickets in database\n";
|
|
||||||
|
|
||||||
// Get all attachment records
|
|
||||||
$attachments = [];
|
|
||||||
$result = $conn->query("SELECT ticket_id, filename FROM ticket_attachments");
|
|
||||||
if ($result) {
|
|
||||||
while ($row = $result->fetch_assoc()) {
|
|
||||||
$key = $row['ticket_id'] . '/' . $row['filename'];
|
|
||||||
$attachments[$key] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
echo "Found " . count($attachments) . " attachment records in database\n";
|
|
||||||
|
|
||||||
// Scan uploads directory
|
|
||||||
$orphanedFolders = [];
|
|
||||||
$orphanedFiles = [];
|
|
||||||
$totalSize = 0;
|
|
||||||
|
|
||||||
$ticketDirs = glob($uploadsDir . '/*', GLOB_ONLYDIR);
|
|
||||||
foreach ($ticketDirs as $ticketDir) {
|
|
||||||
$ticketId = basename($ticketDir);
|
|
||||||
|
|
||||||
// Skip non-ticket directories
|
|
||||||
if (!preg_match('/^\d{9}$/', $ticketId)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if ticket exists
|
|
||||||
if (!isset($ticketIds[$ticketId])) {
|
|
||||||
// Ticket doesn't exist - entire folder is orphaned
|
|
||||||
$orphanedFolders[] = $ticketDir;
|
|
||||||
$folderSize = 0;
|
|
||||||
foreach (glob($ticketDir . '/*') as $file) {
|
|
||||||
if (is_file($file)) {
|
|
||||||
$folderSize += filesize($file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$totalSize += $folderSize;
|
|
||||||
echo "Orphan folder (ticket deleted): $ticketDir (" . formatBytes($folderSize) . ")\n";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check individual files
|
|
||||||
$files = glob($ticketDir . '/*');
|
|
||||||
foreach ($files as $file) {
|
|
||||||
if (is_file($file)) {
|
|
||||||
$filename = basename($file);
|
|
||||||
$key = $ticketId . '/' . $filename;
|
|
||||||
|
|
||||||
if (!isset($attachments[$key])) {
|
|
||||||
$orphanedFiles[] = $file;
|
|
||||||
$fileSize = filesize($file);
|
|
||||||
$totalSize += $fileSize;
|
|
||||||
echo "Orphan file (no DB record): $file (" . formatBytes($fileSize) . ")\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "\n=== Summary ===\n";
|
|
||||||
echo "Orphaned folders: " . count($orphanedFolders) . "\n";
|
|
||||||
echo "Orphaned files: " . count($orphanedFiles) . "\n";
|
|
||||||
echo "Total size to recover: " . formatBytes($totalSize) . "\n";
|
|
||||||
|
|
||||||
if (!$dryRun && ($orphanedFolders || $orphanedFiles)) {
|
|
||||||
echo "\nDeleting orphaned items...\n";
|
|
||||||
|
|
||||||
foreach ($orphanedFiles as $file) {
|
|
||||||
if (unlink($file)) {
|
|
||||||
echo "Deleted: $file\n";
|
|
||||||
} else {
|
|
||||||
echo "Failed to delete: $file\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($orphanedFolders as $folder) {
|
|
||||||
deleteDirectory($folder);
|
|
||||||
echo "Deleted folder: $folder\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "Cleanup complete!\n";
|
|
||||||
} elseif ($dryRun) {
|
|
||||||
echo "\nRun without --dry-run to delete these items.\n";
|
|
||||||
} else {
|
|
||||||
echo "\nNo orphaned items found.\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
$conn->close();
|
|
||||||
|
|
||||||
function formatBytes($bytes) {
|
|
||||||
if ($bytes >= 1073741824) {
|
|
||||||
return number_format($bytes / 1073741824, 2) . ' GB';
|
|
||||||
} elseif ($bytes >= 1048576) {
|
|
||||||
return number_format($bytes / 1048576, 2) . ' MB';
|
|
||||||
} elseif ($bytes >= 1024) {
|
|
||||||
return number_format($bytes / 1024, 2) . ' KB';
|
|
||||||
} else {
|
|
||||||
return $bytes . ' bytes';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteDirectory($dir) {
|
|
||||||
if (!is_dir($dir)) return;
|
|
||||||
|
|
||||||
$files = array_diff(scandir($dir), ['.', '..']);
|
|
||||||
foreach ($files as $file) {
|
|
||||||
$path = "$dir/$file";
|
|
||||||
is_dir($path) ? deleteDirectory($path) : unlink($path);
|
|
||||||
}
|
|
||||||
rmdir($dir);
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Create ticket_dependencies table if it doesn't exist
|
|
||||||
* Run once: php scripts/create_dependencies_table.php
|
|
||||||
*/
|
|
||||||
|
|
||||||
require_once dirname(__DIR__) . '/config/config.php';
|
|
||||||
|
|
||||||
$conn = new mysqli(
|
|
||||||
$GLOBALS['config']['DB_HOST'],
|
|
||||||
$GLOBALS['config']['DB_USER'],
|
|
||||||
$GLOBALS['config']['DB_PASS'],
|
|
||||||
$GLOBALS['config']['DB_NAME']
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($conn->connect_error) {
|
|
||||||
die("Connection failed: " . $conn->connect_error . "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "Connected to database successfully.\n";
|
|
||||||
|
|
||||||
// Check if table exists
|
|
||||||
$tableCheck = $conn->query("SHOW TABLES LIKE 'ticket_dependencies'");
|
|
||||||
if ($tableCheck->num_rows > 0) {
|
|
||||||
echo "Table 'ticket_dependencies' already exists.\n";
|
|
||||||
$conn->close();
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the table
|
|
||||||
$sql = "CREATE TABLE ticket_dependencies (
|
|
||||||
dependency_id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
ticket_id VARCHAR(9) NOT NULL,
|
|
||||||
depends_on_id VARCHAR(9) NOT NULL,
|
|
||||||
dependency_type ENUM('blocks', 'blocked_by', 'relates_to', 'duplicates') NOT NULL DEFAULT 'blocks',
|
|
||||||
created_by INT NULL,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
FOREIGN KEY (ticket_id) REFERENCES tickets(ticket_id) ON DELETE CASCADE,
|
|
||||||
FOREIGN KEY (depends_on_id) REFERENCES tickets(ticket_id) ON DELETE CASCADE,
|
|
||||||
FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL,
|
|
||||||
UNIQUE KEY unique_dependency (ticket_id, depends_on_id, dependency_type),
|
|
||||||
INDEX idx_ticket_id (ticket_id),
|
|
||||||
INDEX idx_depends_on_id (depends_on_id),
|
|
||||||
INDEX idx_dependency_type (dependency_type)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci";
|
|
||||||
|
|
||||||
if ($conn->query($sql) === TRUE) {
|
|
||||||
echo "Table 'ticket_dependencies' created successfully.\n";
|
|
||||||
} else {
|
|
||||||
echo "Error creating table: " . $conn->error . "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
$conn->close();
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# TinkerTickets Deployment Script
|
|
||||||
# This script safely deploys updates while preserving user data
|
|
||||||
set -e
|
|
||||||
|
|
||||||
WEBROOT="/var/www/html/tinkertickets"
|
|
||||||
UPLOADS_BACKUP="/tmp/tinker_uploads_backup"
|
|
||||||
|
|
||||||
echo "[TinkerTickets] Starting deployment..."
|
|
||||||
|
|
||||||
# Backup .env if it exists
|
|
||||||
if [ -f "$WEBROOT/.env" ]; then
|
|
||||||
echo "[TinkerTickets] Backing up .env..."
|
|
||||||
cp "$WEBROOT/.env" /tmp/.env.backup
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Backup uploads folder if it exists and has files
|
|
||||||
if [ -d "$WEBROOT/uploads" ] && [ "$(ls -A $WEBROOT/uploads 2>/dev/null)" ]; then
|
|
||||||
echo "[TinkerTickets] Backing up uploads folder..."
|
|
||||||
rm -rf "$UPLOADS_BACKUP"
|
|
||||||
cp -r "$WEBROOT/uploads" "$UPLOADS_BACKUP"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -d "$WEBROOT/.git" ]; then
|
|
||||||
echo "[TinkerTickets] Directory not a git repo — performing initial clone..."
|
|
||||||
rm -rf "$WEBROOT"
|
|
||||||
git clone https://code.lotusguild.org/LotusGuild/tinker_tickets.git "$WEBROOT"
|
|
||||||
else
|
|
||||||
echo "[TinkerTickets] Updating existing repo..."
|
|
||||||
cd "$WEBROOT"
|
|
||||||
git fetch --all
|
|
||||||
git reset --hard origin/main
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Restore .env if it was backed up
|
|
||||||
if [ -f /tmp/.env.backup ]; then
|
|
||||||
echo "[TinkerTickets] Restoring .env..."
|
|
||||||
mv /tmp/.env.backup "$WEBROOT/.env"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Restore uploads folder if it was backed up
|
|
||||||
if [ -d "$UPLOADS_BACKUP" ]; then
|
|
||||||
echo "[TinkerTickets] Restoring uploads folder..."
|
|
||||||
# Don't overwrite .htaccess from repo
|
|
||||||
rsync -av --exclude='.htaccess' --exclude='.gitkeep' "$UPLOADS_BACKUP/" "$WEBROOT/uploads/"
|
|
||||||
rm -rf "$UPLOADS_BACKUP"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Ensure uploads directory exists with proper permissions
|
|
||||||
mkdir -p "$WEBROOT/uploads"
|
|
||||||
chmod 755 "$WEBROOT/uploads"
|
|
||||||
|
|
||||||
echo "[TinkerTickets] Setting permissions..."
|
|
||||||
chown -R www-data:www-data "$WEBROOT"
|
|
||||||
|
|
||||||
# Run migrations if .env exists
|
|
||||||
if [ -f "$WEBROOT/.env" ]; then
|
|
||||||
echo "[TinkerTickets] Running database migrations..."
|
|
||||||
cd "$WEBROOT/migrations"
|
|
||||||
php run_migrations.php || echo "[TinkerTickets] Warning: Migration errors occurred"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "[TinkerTickets] Deployment complete!"
|
|
||||||
Reference in New Issue
Block a user