initial
This commit is contained in:
commit
abdd8e5530
73
.gitignore
vendored
Normal file
73
.gitignore
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
# Ignore config file
|
||||
config.php
|
||||
|
||||
# Ignore backup directory
|
||||
db_backup/
|
||||
|
||||
# Ignore upload directory
|
||||
upload/
|
||||
|
||||
# Ignore node_modules directory
|
||||
node_modules/
|
||||
|
||||
# Ignore log files
|
||||
*.log
|
||||
|
||||
# Ignore OS generated files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Ignore compiled source files
|
||||
*.com
|
||||
*.class
|
||||
*.dll
|
||||
*.exe
|
||||
*.o
|
||||
*.so
|
||||
|
||||
# Ignore packaged files
|
||||
*.7z
|
||||
*.dmg
|
||||
*.gz
|
||||
*.iso
|
||||
*.jar
|
||||
*.rar
|
||||
*.tar
|
||||
*.zip
|
||||
|
||||
# Ignore IDE and editor-specific files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# Ignore environment files
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Ignore macOS specific files
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon\r\r
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
.backup
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
10
LICENSE.md
Normal file
10
LICENSE.md
Normal file
@ -0,0 +1,10 @@
|
||||
### Explanation
|
||||
|
||||
1. **Features**: Describes the main functionalities of the project.
|
||||
2. **Configuration**: Explains the configuration variables in both `process.sh` and `upload_handler.php`.
|
||||
3. **Usage**: Provides step-by-step instructions on how to set up and use the scripts.
|
||||
4. **Security**: Offers tips on keeping the project secure.
|
||||
5. **Contributing**: Encourages contributions from the community.
|
||||
6. **License**: Mentions the licensing information.
|
||||
|
||||
This README should help users understand how to set up and use your project effectively. Adjust the paths and other configuration details according to your specific setup.
|
||||
57
README.md
Normal file
57
README.md
Normal file
@ -0,0 +1,57 @@
|
||||
# Database Archive Management
|
||||
|
||||
This project automates the backup and management of database archives for different EverQuest expansions. It ensures that database versions are tracked, and only the latest archives are kept while older ones are deleted. If a new database version is detected, the previous version is protected and not deleted.
|
||||
|
||||
## Features
|
||||
|
||||
- Dumps specific tables from multiple databases.
|
||||
- Compresses and uploads the database dumps to a web server.
|
||||
- Keeps track of the database version and marks new versions as protected.
|
||||
- Automatically cleans up old archives, retaining only the latest 30 days of backups.
|
||||
- Protects weekly (Sunday) backups and any backups with a version change.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Process Script (`process.sh`)
|
||||
|
||||
This script dumps tables from multiple databases, compresses them, and uploads them to the web server.
|
||||
|
||||
#### Configuration Variables
|
||||
|
||||
- `DB_USER`: Database username.
|
||||
- `DB_PASSWORD`: Database password.
|
||||
- `DB_HOST`: Database host address.
|
||||
- `DB_PORT`: Database port (e.g., `4406`).
|
||||
- `DB_IDENTIFIERS`: Array of database identifiers (e.g., `Classic`, `Kunark`).
|
||||
- `BACKUP_DIR`: Directory where backups are temporarily stored.
|
||||
- `WEB_SERVER`: Web server URL.
|
||||
- `UPLOAD_URL`: Full URL for the upload handler on the web server.
|
||||
- `UPLOAD_KEY`: Secure key for authenticating the upload request.
|
||||
- `SCHEMA_URL`: URL to download the latest `database_schema.h` file.
|
||||
- `SCHEMA_FILE`: Local path to store the downloaded `database_schema.h` file.
|
||||
|
||||
### Upload Handler (`upload_handler.php`)
|
||||
|
||||
This PHP script handles the uploaded database archives, marks versions as protected if needed, and cleans up old archives.
|
||||
|
||||
#### Configuration Variables
|
||||
|
||||
- `uploadDir`: Directory where uploaded files are stored.
|
||||
- `maxAgeDays`: Maximum number of days to keep unprotected archives.
|
||||
- `uploadKey`: Secure key for authenticating the upload request (defined in `config.php`).
|
||||
|
||||
### Database Schema
|
||||
|
||||
The `archive_logs` table should have the following structure:
|
||||
|
||||
```sql
|
||||
CREATE TABLE `archive_logs` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`archive` varchar(255) NOT NULL,
|
||||
`size` bigint(20) NOT NULL,
|
||||
`created` datetime NOT NULL,
|
||||
`db_identifier` varchar(255) NOT NULL,
|
||||
`db_version` varchar(255) NOT NULL,
|
||||
`protected` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
);
|
||||
113
config.php.template
Normal file
113
config.php.template
Normal file
@ -0,0 +1,113 @@
|
||||
<?php
|
||||
$dbHost = "";
|
||||
$dbPort = "3311";
|
||||
$dbUser = "";
|
||||
$dbPass = "";
|
||||
$dbName = "db_archives";
|
||||
$uploadKey = 'your_secure_upload_key';
|
||||
$uploadDir = './uploads/';
|
||||
$maxAgeDays = 30;
|
||||
|
||||
$main_db = new mysqli("$dbHost:$dbPort", $dbUser, $dbPass, $dbName);
|
||||
$currentDate = new DateTime();
|
||||
$maxAgeInterval = new DateInterval('P' . $maxAgeDays . 'D');
|
||||
$maxAgeDate = $currentDate->sub($maxAgeInterval)->format('Y-m-d H:i:s');
|
||||
|
||||
if ($main_db->connect_error) {
|
||||
die("Connection failed: " . $main_db->connect_error);
|
||||
}
|
||||
|
||||
$fullNames = [
|
||||
'Classic' => 'Classic EverQuest',
|
||||
'Kunark' => 'Ruins of Kunark',
|
||||
'Velious' => 'Scars of Velious',
|
||||
'Luclin' => 'Shadows of Luclin',
|
||||
'Planes' => 'Planes of Power',
|
||||
// Add more expansions as needed
|
||||
];
|
||||
|
||||
// Function to check if table exists and create it if it doesn't
|
||||
function createArchiveLogsTable($db) {
|
||||
$query = "CREATE TABLE IF NOT EXISTS archive_logs (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
downloads INT DEFAULT 0,
|
||||
archive VARCHAR(255),
|
||||
size BIGINT,
|
||||
created DATETIME,
|
||||
db_identifier VARCHAR(50),
|
||||
db_version varchar(255),
|
||||
protected tinyint(1) NOT NULL DEFAULT 0
|
||||
)";
|
||||
if ($db->query($query) === FALSE) {
|
||||
echo "Error creating table: " . $db->error . "<br>";
|
||||
}
|
||||
}
|
||||
|
||||
// Check and create the table
|
||||
createArchiveLogsTable($main_db);
|
||||
|
||||
function formatSize($bytes) {
|
||||
return number_format($bytes / 1048576, 2) . ' MB';
|
||||
}
|
||||
|
||||
function timeElapsedString($datetime, $full = false) {
|
||||
$now = new DateTime;
|
||||
$ago = new DateTime($datetime);
|
||||
$diff = $now->diff($ago);
|
||||
|
||||
$diff->w = floor($diff->d / 7);
|
||||
$diff->d -= $diff->w * 7;
|
||||
|
||||
$string = [
|
||||
'y' => 'year',
|
||||
'm' => 'month',
|
||||
'w' => 'week',
|
||||
'd' => 'day',
|
||||
'h' => 'hour',
|
||||
'i' => 'minute',
|
||||
's' => 'second',
|
||||
];
|
||||
foreach ($string as $k => &$v) {
|
||||
if ($diff->$k) {
|
||||
$v = $diff->$k . ' ' . $v . ($diff->$k > 1 ? 's' : '');
|
||||
} else {
|
||||
unset($string[$k]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$full) $string = array_slice($string, 0, 1);
|
||||
return $string ? implode(', ', $string) . ' ago' : 'just now';
|
||||
}
|
||||
|
||||
function cleanOldArchives($db, $uploadDir, $maxAgeDate) {
|
||||
// Get all non-protected archives older than the maximum age
|
||||
$stmt = $db->prepare("SELECT id, archive, created FROM archive_logs WHERE created < ? AND protected = 0");
|
||||
$stmt->bind_param("s", $maxAgeDate);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$archiveDate = new DateTime($row['created']);
|
||||
$dayOfWeek = $archiveDate->format('N'); // 1 (for Monday) through 7 (for Sunday)
|
||||
|
||||
// Delete the archive if it's Monday-Saturday or if it's older than 30 days
|
||||
if ($dayOfWeek != 7 || $archiveDate < $maxAgeDate) {
|
||||
$archivePath = $uploadDir . $row['archive'];
|
||||
|
||||
// Delete the file
|
||||
if (file_exists($archivePath)) {
|
||||
unlink($archivePath);
|
||||
}
|
||||
|
||||
// Delete the database entry
|
||||
$deleteStmt = $db->prepare("DELETE FROM archive_logs WHERE id = ?");
|
||||
$deleteStmt->bind_param("i", $row['id']);
|
||||
$deleteStmt->execute();
|
||||
$deleteStmt->close();
|
||||
}
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
}
|
||||
|
||||
?>
|
||||
44
download.php
Normal file
44
download.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
include 'config.php';
|
||||
|
||||
if (isset($_GET['file'])) {
|
||||
$file = $_GET['file'];
|
||||
|
||||
// Get file details from the database
|
||||
$stmt = $main_db->prepare("SELECT * FROM archive_logs WHERE archive = ?");
|
||||
$stmt->bind_param("s", $file);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$fileDetails = $result->fetch_assoc();
|
||||
$stmt->close();
|
||||
|
||||
if ($fileDetails) {
|
||||
$filePath = 'uploads/' . $file;
|
||||
|
||||
if (file_exists($filePath)) {
|
||||
// Increment download count
|
||||
$stmt = $main_db->prepare("UPDATE archive_logs SET downloads = downloads + 1 WHERE archive = ?");
|
||||
$stmt->bind_param("s", $file);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
|
||||
// Serve the file for download
|
||||
header('Content-Description: File Transfer');
|
||||
header('Content-Type: application/octet-stream');
|
||||
header('Content-Disposition: attachment; filename="'.basename($filePath).'"');
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate');
|
||||
header('Pragma: public');
|
||||
header('Content-Length: ' . filesize($filePath));
|
||||
readfile($filePath);
|
||||
exit;
|
||||
} else {
|
||||
echo "File not found.";
|
||||
}
|
||||
} else {
|
||||
echo "Invalid file.";
|
||||
}
|
||||
} else {
|
||||
echo "No file specified.";
|
||||
}
|
||||
?>
|
||||
132
index.php
Normal file
132
index.php
Normal file
@ -0,0 +1,132 @@
|
||||
<?php
|
||||
include 'config.php';
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Database Archives</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<script>
|
||||
function showTab() {
|
||||
var tabName = document.getElementById("expansionSelect").value;
|
||||
var i;
|
||||
var x = document.getElementsByClassName("tab-content");
|
||||
for (i = 0; i < x.length; i++) {
|
||||
x[i].style.display = "none";
|
||||
}
|
||||
document.getElementById(tabName).style.display = "block";
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="showTab()">
|
||||
<div class="page-container">
|
||||
<header class="site-header">
|
||||
<div class="header-content">
|
||||
<h1>Project Aatheria</h1>
|
||||
<div class="dropdown-container">
|
||||
<label for="expansionSelect">Select Expansion:</label>
|
||||
<select id="expansionSelect" onchange="showTab()">
|
||||
<?php
|
||||
foreach ($fullNames as $identifier => $fullName) {
|
||||
echo "<option value='$identifier'>$fullName</option>";
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="content-wrap">
|
||||
<div class="container">
|
||||
<div class="table-container">
|
||||
<?php
|
||||
foreach ($fullNames as $identifier => $fullName) {
|
||||
echo "<div id='$identifier' class='tab-content'>";
|
||||
|
||||
echo "<div class='site-sub-header'>";
|
||||
echo "<div class='sub-header-body'>";
|
||||
echo "<div class='row align-items-end'>";
|
||||
echo "<div class='col'>";
|
||||
echo "<h2 class='header-title'>";
|
||||
echo "Database Archives";
|
||||
echo "</h2>";
|
||||
echo "<h3 class='header-pretitle'>";
|
||||
echo "$fullName";
|
||||
echo "</h3>";
|
||||
echo "</div>";
|
||||
echo "<div class='col-auto text-right'>";
|
||||
|
||||
// Fetch the latest archive details
|
||||
$stmt = $main_db->prepare("SELECT downloads, archive, size, created FROM archive_logs WHERE db_identifier = ? ORDER BY created DESC LIMIT 1");
|
||||
$stmt->bind_param("s", $identifier);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$latest = $result->fetch_assoc();
|
||||
|
||||
if ($latest) {
|
||||
echo "<span class='download-latest-count badge badge-soft-primary'>Download Count: " . $latest['downloads'] . "</span>";
|
||||
echo "<span class='download-latest-size badge badge-soft-secondary'>Size: " . formatSize($latest['size']) . "</span>";
|
||||
echo "<br>";
|
||||
echo "<span class='download-latest-created badge badge-soft-success mt-2'>Created: " . date("l F jS Y", strtotime($latest['created'])) . "</span>";
|
||||
}
|
||||
echo "<br>";
|
||||
echo "<a href='download.php?file=" . $latest['archive'] . "' class='btn btn-sm btn-primary mt-3'>";
|
||||
echo "<svg xmlns='http://www.w3.org/2000/svg' class='icon' aria-hidden='true' focusable='false' viewBox='0 0 512 512'><path fill='currentColor' d='M234.5 5.7c13.9-5 29.1-5 43.1 0l192 68.6C495 83.4 512 107.5 512 134.6l0 242.9c0 27-17 51.2-42.5 60.3l-192 68.6c-13.9 5-29.1 5-43.1 0l-192-68.6C17 428.6 0 404.5 0 377.4L0 134.6c0-27 17-51.2 42.5-60.3l192-68.6zM256 66L82.3 128 256 190l173.7-62L256 66zm32 368.6l160-57.1 0-188L288 246.6l0 188z'/></svg>";
|
||||
echo "Download Latest $identifier Database";
|
||||
echo "</a>";
|
||||
$stmt->close();
|
||||
echo "</div>";
|
||||
echo "</div>";
|
||||
echo "</div>";
|
||||
echo "</div>";
|
||||
echo "<div class='info-bar'>";
|
||||
echo "<p>";
|
||||
echo "<svg xmlns='http://www.w3.org/2000/svg' class='icon' aria-hidden='true' focusable='false' viewBox='0 0 512 512'><path fill='currentColor' d='M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z' /></svg>";
|
||||
echo "Archives are generated nightly from our Live servers, retained for 30 days, and then kept monthly beyond that.</p>";
|
||||
echo "</div>";
|
||||
|
||||
echo "<table>";
|
||||
echo "<thead><tr><th style='width: 140px; text-align: center;'>Downloads</th><th style='text-align: center;'>Archive</th><th style='text-align: center;'>Size</th><th>Created</th></tr></thead>";
|
||||
echo "<tbody>";
|
||||
|
||||
$stmt = $main_db->prepare("SELECT downloads, archive, size, created, db_version, protected FROM archive_logs WHERE db_identifier = ? ORDER BY created DESC");
|
||||
$stmt->bind_param("s", $identifier);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
echo "<tr>";
|
||||
echo "<td style='text-align: center;'>" . $row['downloads'] . "</td>";
|
||||
echo "<td style='width:225px; text-align: center;'><a class='download-button' href='download.php?file=" . $row['archive'] . "'><svg xmlns='http://www.w3.org/2000/svg' class='icon' aria-hidden='true' focusable='false' viewBox='0 0 512 512'><path fill='currentColor' d='M234.5 5.7c13.9-5 29.1-5 43.1 0l192 68.6C495 83.4 512 107.5 512 134.6l0 242.9c0 27-17 51.2-42.5 60.3l-192 68.6c-13.9 5-29.1 5-43.1 0l-192-68.6C17 428.6 0 404.5 0 377.4L0 134.6c0-27 17-51.2 42.5-60.3l192-68.6zM256 66L82.3 128 256 190l173.7-62L256 66zm32 368.6l160-57.1 0-188L288 246.6l0 188z'/></svg>" . $row['archive'] . "</a></td>";
|
||||
echo "<td style='width:100px; text-align: center;'>" . formatSize($row['size']) . "</td>";
|
||||
echo "<td>" . date("l F jS Y", strtotime($row['created'])) . " (" . timeElapsedString($row['created']) . ")";
|
||||
echo "<span class='db-version'><svg xmlns='http://www.w3.org/2000/svg' class='icon' aria-hidden='true' focusable='false' viewBox='0 0 448 512'><path fill='currentColor' d='M448 80l0 48c0 44.2-100.3 80-224 80S0 172.2 0 128L0 80C0 35.8 100.3 0 224 0S448 35.8 448 80zM393.2 214.7c20.8-7.4 39.9-16.9 54.8-28.6L448 288c0 44.2-100.3 80-224 80S0 332.2 0 288L0 186.1c14.9 11.8 34 21.2 54.8 28.6C99.7 230.7 159.5 240 224 240s124.3-9.3 169.2-25.3zM0 346.1c14.9 11.8 34 21.2 54.8 28.6C99.7 390.7 159.5 400 224 400s124.3-9.3 169.2-25.3c20.8-7.4 39.9-16.9 54.8-28.6l0 85.9c0 44.2-100.3 80-224 80S0 476.2 0 432l0-85.9z'/></svg> " . $row['db_version'] . "</span>";
|
||||
if ($row['protected'] == 1) {
|
||||
echo "<span class='db-protected'><svg xmlns='http://www.w3.org/2000/svg' class='icon' aria-hidden='true' focusable='false' viewBox='0 0 448 512'><path fill='currentColor' d='M144 144l0 48 160 0 0-48c0-44.2-35.8-80-80-80s-80 35.8-80 80zM80 192l0-48C80 64.5 144.5 0 224 0s144 64.5 144 144l0 48 16 0c35.3 0 64 28.7 64 64l0 192c0 35.3-28.7 64-64 64L64 512c-35.3 0-64-28.7-64-64L0 256c0-35.3 28.7-64 64-64l16 0z'/></svg>Protected</span>";
|
||||
}
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
|
||||
echo "</tbody>";
|
||||
echo "</table>";
|
||||
echo "</div>";
|
||||
}
|
||||
?>
|
||||
<p class="contribute">Would you like to help keep our databases accurate? Click <a href="https://alla.aatheria.com" target="_blank">Here</a>!</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="site-footer">
|
||||
<div class="footer-content">
|
||||
<p>EverQuest is a registered trademark of Daybreak Game Company LLC.<br>
|
||||
EQEmulator and Project Aatheria are not associated or affiliated in any way with Daybreak Game Company LLC.</p><br>
|
||||
<p>© 2024 Project Aatheria. All rights reserved.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
78
process.sh
Normal file
78
process.sh
Normal file
@ -0,0 +1,78 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Configuration
|
||||
DB_USER=""
|
||||
DB_PASSWORD=""
|
||||
DB_HOST=""
|
||||
DB_PORT=3306
|
||||
WEB_SERVER="https://"
|
||||
UPLOAD_KEY="your_secure_upload_key"
|
||||
DB_IDENTIFIERS=("Classic" "Kunark" "Velious" "Luclin" "Planes")
|
||||
|
||||
BACKUP_DIR="./db_backup"
|
||||
UPLOAD_URL="${WEB_SERVER}/upload_handler.php"
|
||||
SCHEMA_URL="https://raw.githubusercontent.com/EQEmu/Server/master/common/database_schema.h"
|
||||
SCHEMA_FILE="./database_schema.h"
|
||||
|
||||
# Create backup directory if it doesn't exist
|
||||
mkdir -p ${BACKUP_DIR}
|
||||
|
||||
# Download the latest database_schema.h file
|
||||
curl -o ${SCHEMA_FILE} ${SCHEMA_URL}
|
||||
|
||||
# Extract tables from the schema file
|
||||
extract_tables() {
|
||||
local section=$1
|
||||
sed -n "/Get${section}Tables()/,/}/p" ${SCHEMA_FILE} | grep -Eo '"[a-zA-Z0-9_]+" ?,' | tr -d '",' | sort | uniq
|
||||
}
|
||||
|
||||
# Function to dump tables from a list
|
||||
dump_tables() {
|
||||
local db_identifier=$1
|
||||
local tables=$2
|
||||
local group_name=$3
|
||||
local lower_db_identifier=$(echo ${db_identifier} | tr '[:upper:]' '[:lower:]')
|
||||
local dump_file="${BACKUP_DIR}/${group_name}-${lower_db_identifier}_$(date +\%F).sql"
|
||||
|
||||
for table in ${tables}; do
|
||||
mysqldump -u${DB_USER} -p${DB_PASSWORD} -h${DB_HOST} --port=${DB_PORT} "content-${lower_db_identifier}" ${table} >> ${dump_file}
|
||||
done
|
||||
}
|
||||
|
||||
# Function to get the database version
|
||||
get_db_version() {
|
||||
local db_identifier=$1
|
||||
local lower_db_identifier=$(echo ${db_identifier} | tr '[:upper:]' '[:lower:]')
|
||||
local version_query="SELECT version FROM db_version LIMIT 1;"
|
||||
local version=$(mysql -u${DB_USER} -p${DB_PASSWORD} -h${DB_HOST} --port=${DB_PORT} -D "content-${lower_db_identifier}" -se "${version_query}")
|
||||
echo ${version}
|
||||
}
|
||||
|
||||
# Extract tables from the schema file
|
||||
CONTENT_TABLES=$(extract_tables "Content")
|
||||
VERSION_TABLES=$(extract_tables "Version")
|
||||
|
||||
# Dump each group of tables from each database
|
||||
for db_identifier in "${DB_IDENTIFIERS[@]}"; do
|
||||
timestamp=$(date +\%Y\%m\%d\%H\%M)
|
||||
dump_tables ${db_identifier} "${CONTENT_TABLES}" "content"
|
||||
dump_tables ${db_identifier} "${VERSION_TABLES}" "version"
|
||||
|
||||
# Get the database version
|
||||
db_version=$(get_db_version ${db_identifier})
|
||||
|
||||
# Compress the dumps
|
||||
TAR_FILE="${BACKUP_DIR}/pa-${db_identifier,,}-${timestamp}.tar.gz"
|
||||
tar -czf ${TAR_FILE} -C ${BACKUP_DIR} "content-${db_identifier,,}_$(date +\%F).sql" "version-${db_identifier,,}_$(date +\%F).sql"
|
||||
|
||||
# Upload the compressed file to the web server
|
||||
curl -F "file=@${TAR_FILE}" -F "db_identifier=${db_identifier}" -F "db_version=${db_version}" -F "key=${UPLOAD_KEY}" ${UPLOAD_URL}
|
||||
|
||||
# Clean up the backup files after upload
|
||||
rm ${BACKUP_DIR}/*.sql
|
||||
rm ${TAR_FILE}
|
||||
done
|
||||
|
||||
# Clean up old backups (optional)
|
||||
find ${BACKUP_DIR} -type f -name "*.sql" -mtime +7 -exec rm {} \;
|
||||
find ${BACKUP_DIR} -type f -name "*.tar.gz" -mtime +7 -exec rm {} \;
|
||||
449
styles.css
Normal file
449
styles.css
Normal file
@ -0,0 +1,449 @@
|
||||
html, body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.page-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.content-wrap {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
background-color: #f9fafb;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 70%;
|
||||
margin: auto;
|
||||
overflow: hidden;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.site-header {
|
||||
background: #1a1c21;
|
||||
color: #fff;
|
||||
padding: 10px 0;
|
||||
border-bottom: #3182ce 3px solid;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
max-width: 70%;
|
||||
margin: auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.header-content h1 {
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.dropdown-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dropdown-container label {
|
||||
margin-right: 10px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dropdown-container select {
|
||||
padding: 5px 10px;
|
||||
font-size: 1rem;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #ddd;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dropdown-container select:hover {
|
||||
border-color: #3182ce;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
margin-top: 20px;
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.tab-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
background-color: #f7fafc;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tab-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tab-status span {
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
padding: 5px 10px;
|
||||
background-color: #e2e8f0;
|
||||
border-radius: 5px;
|
||||
font-size: 0.875rem;
|
||||
color: #4a5568;
|
||||
}
|
||||
|
||||
.status-downloads {
|
||||
background-color: #ebf8ff;
|
||||
color: #3182ce;
|
||||
}
|
||||
|
||||
.status-size {
|
||||
background-color: #edf2f7;
|
||||
color: #718096;
|
||||
}
|
||||
|
||||
.status-created {
|
||||
background-color: #f0fff4;
|
||||
color: #38a169;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 20px 0 0 0;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
table, th, td {
|
||||
border: 1px solid #ddd;
|
||||
font-size: .8125rem;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 12px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #f7fafc;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
tr:nth-child(even) {
|
||||
background-color: #f7fafc;
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
background-color: #edf2f7;
|
||||
}
|
||||
|
||||
.download-button {
|
||||
padding: .125rem .5rem;
|
||||
font-size: .8125rem;
|
||||
line-height: 1.75;
|
||||
border-radius: .25rem;
|
||||
display: inline-block;
|
||||
background-color: #daa520;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.download-button:hover {
|
||||
background-color: #707885;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.upload-form {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.upload-form form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.upload-form label {
|
||||
margin: 10px 0 5px;
|
||||
}
|
||||
|
||||
.upload-form input, .upload-form select, .upload-form button {
|
||||
padding: 10px;
|
||||
margin: 5px 0;
|
||||
font-size: 1rem;
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.upload-form button {
|
||||
background: #3182ce;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.upload-form button:hover {
|
||||
background: #2c5282;
|
||||
}
|
||||
|
||||
.site-footer {
|
||||
background: #1a1c21;
|
||||
text-align: center;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
color: #707885;
|
||||
max-width: 70%;
|
||||
margin: auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.site-footer p {
|
||||
margin: 5px 0;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.container p {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.info-bar {
|
||||
background-color: #cce5ff;
|
||||
border-color: #3182ce;
|
||||
color: #004085;
|
||||
position: relative;
|
||||
padding: .75rem 1.25rem;
|
||||
margin-bottom: 1.5rem;
|
||||
border: 1px solid transparent;
|
||||
border-radius: .375rem;
|
||||
}
|
||||
|
||||
.info-bar p {
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.info-bar a {
|
||||
color: #fff;
|
||||
text-decoration: underline;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.info-bar a:hover {
|
||||
color: #d0e8ff;
|
||||
}
|
||||
|
||||
.sub-header-body {
|
||||
padding: 0 0 20px 0;
|
||||
}
|
||||
|
||||
.sub-header-pretitle {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: #6c757d;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.sub-header-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: #343a40;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-right: -15px;
|
||||
margin-left: -15px;
|
||||
}
|
||||
|
||||
.align-items-end {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.col, .col-auto {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-right: 15px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.col {
|
||||
flex: 1 0 0%;
|
||||
}
|
||||
|
||||
.col-auto {
|
||||
flex: 0 0 auto;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 0.35em 0.65em;
|
||||
font-size: 75%;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
vertical-align: baseline;
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
.badge-soft-primary {
|
||||
color: #004085;
|
||||
background-color: #cce5ff;
|
||||
}
|
||||
|
||||
.badge-soft-secondary {
|
||||
color: #383d41;
|
||||
background-color: #e2e3e5;
|
||||
}
|
||||
|
||||
.badge-soft-success {
|
||||
color: #155724;
|
||||
background-color: #d4edda;
|
||||
}
|
||||
|
||||
.mt-2 {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.mt-3 {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
font-weight: 400;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
user-select: none;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.375rem 0.75rem;
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
border-radius: 0.25rem;
|
||||
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.5;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: #fff;
|
||||
background-color: #daa520;
|
||||
border-color: #daa520;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
color: #fff;
|
||||
background-color: #0069d9;
|
||||
border-color: #0062cc;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.header-pretitle {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 0.35em 0.65em;
|
||||
font-size: 75%;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
vertical-align: baseline;
|
||||
border-radius: 0.375rem;
|
||||
margin-right: 5px; /* Add space between badges */
|
||||
}
|
||||
|
||||
.badge-soft-primary {
|
||||
color: #004085;
|
||||
background-color: #cce5ff;
|
||||
}
|
||||
|
||||
.badge-soft-secondary {
|
||||
color: #383d41;
|
||||
background-color: #e2e3e5;
|
||||
}
|
||||
|
||||
.badge-soft-success {
|
||||
color: #155724;
|
||||
background-color: #d4edda;
|
||||
}
|
||||
|
||||
a.btn-primary {
|
||||
text-decoration: none; /* Remove underline from the download button */
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.125em;
|
||||
padding: 5px 7px 0 0;
|
||||
}
|
||||
|
||||
.db-version .icon {
|
||||
padding: 0 7px 0 7px;
|
||||
}
|
||||
|
||||
.db-protected .icon {
|
||||
padding: 0 7px 0 7px;
|
||||
color: #FF0000; /* Light grey color */
|
||||
}
|
||||
|
||||
.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.db-version {
|
||||
float: right;
|
||||
color: #888; /* Light grey color */
|
||||
font-size: 0.875rem; /* Adjust font size if needed */
|
||||
}
|
||||
|
||||
.db-protected {
|
||||
float: right;
|
||||
color: #888; /* Light grey color */
|
||||
font-size: 0.875rem; /* Adjust font size if needed */
|
||||
}
|
||||
|
||||
.table-container p.contribute {
|
||||
text-align: center;
|
||||
margin: 20px 0 0 0;
|
||||
}
|
||||
34
targz_upload.php
Normal file
34
targz_upload.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
include 'config.php';
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Upload Database Dump</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>Upload Database Dump</h1>
|
||||
</header>
|
||||
<div class="upload-form">
|
||||
<form action="upload_handler.php" method="post" enctype="multipart/form-data">
|
||||
<label for="db_identifier">Database Identifier:</label>
|
||||
<select name="db_identifier" id="db_identifier" required>
|
||||
<?php
|
||||
foreach ($fullNames as $identifier => $fullName) {
|
||||
echo "<option value='$identifier'>$fullName</option>";
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
<label for="file">Select archive file (.tar.gz):</label>
|
||||
<input type="file" name="file" id="file" accept=".tar.gz" required>
|
||||
<button type="submit">Upload</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
54
upload_handler.php
Normal file
54
upload_handler.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
include 'config.php';
|
||||
|
||||
// Handle the file upload
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file']) && isset($_POST['key']) && $_POST['key'] === $uploadKey) {
|
||||
$fileName = basename($_FILES['file']['name']);
|
||||
$uploadFilePath = $uploadDir . $fileName;
|
||||
|
||||
if (move_uploaded_file($_FILES['file']['tmp_name'], $uploadFilePath)) {
|
||||
$dbIdentifier = $_POST['db_identifier'];
|
||||
$dbVersion = $_POST['db_version'];
|
||||
$size = filesize($uploadFilePath);
|
||||
|
||||
// Convert db_identifier to lowercase
|
||||
$dbIdentifierLower = strtolower($dbIdentifier);
|
||||
|
||||
// Check the latest version in the database for the given identifier
|
||||
$stmt = $main_db->prepare("SELECT db_version FROM archive_logs WHERE db_identifier = ? ORDER BY created DESC LIMIT 1");
|
||||
$stmt->bind_param("s", $dbIdentifierLower);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$latest = $result->fetch_assoc();
|
||||
$latestVersion = $latest ? $latest['db_version'] : null;
|
||||
$stmt->close();
|
||||
$db_changed = false;
|
||||
|
||||
// If the version has changed, protect the latest entry
|
||||
if ($latestVersion && $dbVersion !== $latestVersion) {
|
||||
$stmt = $main_db->prepare("UPDATE archive_logs SET protected = 1 WHERE db_identifier = ? AND db_version = ?");
|
||||
$stmt->bind_param("ss", $dbIdentifierLower, $latestVersion);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
$db_changed = true;
|
||||
}
|
||||
|
||||
// Insert the new archive log entry
|
||||
$protected = $db_changed ? 1 : 0;
|
||||
$stmt = $main_db->prepare("INSERT INTO archive_logs (archive, size, created, db_identifier, db_version, protected) VALUES (?, ?, NOW(), ?, ?, $protected)");
|
||||
$stmt->bind_param("sisss", $fileName, $size, $dbIdentifierLower, $dbVersion);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
|
||||
echo 'File uploaded successfully.';
|
||||
|
||||
// Clean up old archives
|
||||
cleanOldArchives($main_db, $uploadDir, $maxAgeDate);
|
||||
} else {
|
||||
echo 'File upload failed.';
|
||||
}
|
||||
} else {
|
||||
echo 'Invalid request or key.';
|
||||
}
|
||||
|
||||
?>
|
||||
Loading…
x
Reference in New Issue
Block a user