Files
bookmarkly2/api/import_backup.php
T
2025-09-28 16:05:53 +02:00

206 lines
8.0 KiB
PHP

<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
// Set the working directory to the project root
chdir(__DIR__ . '/..');
// --- Custom Error Handling ---
register_shutdown_function(function () {
$error = error_get_last();
if ($error !== null && !headers_sent()) {
http_response_code(500);
header('Content-Type: application/json');
echo json_encode([
'success' => false,
'message' => 'A fatal server error occurred.',
'error_details' => $error
]);
}
});
require_once 'include/init.php';
require_once 'include/auth.php';
header('Content-Type: application/json');
if (!isset($_SESSION['user_id'])) {
http_response_code(401);
echo json_encode(['success' => false, 'message' => 'You must be logged in.']);
exit;
}
/**
* Recursively removes a directory and its contents.
* @param string $dir The directory to remove.
*/
function rrmdir($dir) {
if (is_dir($dir)) {
$objects = scandir($dir);
foreach ($objects as $object) {
if ($object != "." && $object != "..") {
is_dir($dir . DIRECTORY_SEPARATOR . $object) ? rrmdir($dir . DIRECTORY_SEPARATOR . $object) : unlink($dir . DIRECTORY_SEPARATOR . $object);
}
}
rmdir($dir);
}
}
// --- Main Logic ---
try {
if (!isset($_FILES['backup_file']) || $_FILES['backup_file']['error'] !== UPLOAD_ERR_OK) {
throw new Exception('File upload error.');
}
if (!isset($_POST['target_tab_id']) || empty($_POST['target_tab_id'])) {
throw new Exception('Target tab not selected.');
}
$target_tab_id = (int)$_POST['target_tab_id'];
$backup_file = $_FILES['backup_file']['tmp_name'];
$temp_dir = sys_get_temp_dir() . '/bookmarkly_import_' . uniqid();
if (!mkdir($temp_dir, 0777, true)) {
throw new Exception('Failed to create temporary directory.');
}
$zip = new ZipArchive;
if ($zip->open($backup_file) !== TRUE) {
rrmdir($temp_dir);
throw new Exception('Failed to open the backup file.');
}
$zip->extractTo($temp_dir);
$zip->close();
$backup_db_path = $temp_dir . '/database/bookmarkly.db';
$manifest_path = $temp_dir . '/manifest.json';
if (!file_exists($backup_db_path)) {
throw new Exception('Invalid backup file: bookmarkly.db not found.');
}
$backup_pdo = new PDO('sqlite:' . $backup_db_path);
$backup_pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$backup_pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
// --- Begin Transaction ---
$pdo->beginTransaction();
try {
$is_tab_backup = false;
if (file_exists($manifest_path)) {
$manifest = json_decode(file_get_contents($manifest_path), true);
if (isset($manifest['type']) && $manifest['type'] === 'tab') {
$is_tab_backup = true;
}
}
// --- Import Logic ---
$old_to_new_category_id_map = [];
$imported_categories_count = 0;
$imported_bookmarks_count = 0;
// If it's a tab backup, also import tab settings and files
if ($is_tab_backup) {
$backup_tab = $backup_pdo->query("SELECT * FROM tabs LIMIT 1")->fetch();
if ($backup_tab) {
// Handle background image
$new_background_url = $backup_tab['background_image_url'];
if (!empty($new_background_url) && strpos($new_background_url, 'images/backgrounds/') === 0) {
$source_bg = $temp_dir . '/' . $new_background_url;
if (file_exists($source_bg)) {
$bg_filename = basename($new_background_url);
$dest_dir = 'data/images/backgrounds/';
if (!is_dir($dest_dir)) mkdir($dest_dir, 0755, true);
$unique_filename = 'tab' . $target_tab_id . '_' . time() . '_' . $bg_filename;
$dest_bg = $dest_dir . $unique_filename;
if (copy($source_bg, $dest_bg)) {
$new_background_url = $dest_bg . '?v=' . time();
}
}
}
// Update target tab with settings from backup
$update_tab_stmt = $pdo->prepare(
"UPDATE tabs SET theme = ?, theme_transparency = ?, background_image_url = ?, background_brightness = ?, background_saturation = ? WHERE id = ?"
);
$update_tab_stmt->execute([
$backup_tab['theme'],
$backup_tab['theme_transparency'],
$new_background_url,
$backup_tab['background_brightness'],
$backup_tab['background_saturation'],
$target_tab_id
]);
}
}
// Import Categories
$categories = $backup_pdo->query("SELECT * FROM categories")->fetchAll();
$stmt_insert_category = $pdo->prepare("INSERT INTO categories (name, sort_order, tab_id) VALUES (?, ?, ?)");
foreach ($categories as $category) {
$stmt_insert_category->execute([$category['name'], $category['sort_order'], $target_tab_id]);
$old_to_new_category_id_map[$category['id']] = $pdo->lastInsertId();
$imported_categories_count++;
}
// Import Bookmarks
$bookmarks = $backup_pdo->query("SELECT * FROM bookmarks")->fetchAll();
$stmt_insert_bookmark = $pdo->prepare("INSERT INTO bookmarks (category_id, title, url, icon, is_favorite, sort_order, tab_id) VALUES (?, ?, ?, ?, ?, ?, ?)");
foreach ($bookmarks as $bookmark) {
// Determine the new category ID, allowing for bookmarks without a category
$new_category_id = null;
if (!empty($bookmark['category_id']) && isset($old_to_new_category_id_map[$bookmark['category_id']])) {
$new_category_id = $old_to_new_category_id_map[$bookmark['category_id']];
}
// Handle icon
$new_icon_path = $bookmark['icon'];
// In tab backups, icon paths are relative to the zip root, e.g., 'images/custom/icon.png'
if ($is_tab_backup && !empty($new_icon_path) && strpos($new_icon_path, 'images/custom/') === 0) {
$source_icon = $temp_dir . '/' . $new_icon_path;
if (file_exists($source_icon)) {
$icon_filename = basename($new_icon_path);
$dest_dir = 'data/images/custom/'; // The correct destination for custom icons
if (!is_dir($dest_dir)) mkdir($dest_dir, 0755, true);
$unique_filename = 'imported_' . time() . '_' . $icon_filename;
$dest_icon = $dest_dir . $unique_filename;
if (copy($source_icon, $dest_icon)) {
$new_icon_path = $dest_icon;
}
}
}
$stmt_insert_bookmark->execute([
$new_category_id, $bookmark['title'], $bookmark['url'],
$new_icon_path, $bookmark['is_favorite'], $bookmark['sort_order'], $target_tab_id
]);
$imported_bookmarks_count++;
}
$pdo->commit();
$message = "Import successful. Added {$imported_categories_count} categories and {$imported_bookmarks_count} bookmarks.";
echo json_encode(['success' => true, 'message' => $message]);
} catch (Exception $e) {
$pdo->rollBack();
throw new Exception('Database error during import: ' . $e->getMessage());
}
} catch (Exception $e) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
} finally {
if (isset($temp_dir) && is_dir($temp_dir)) {
rrmdir($temp_dir);
}
// Close DB connections
$pdo = null;
if(isset($backup_pdo)) {
$backup_pdo = null;
}
}