remove wip backup-feature for later releases, see branch backup-feature
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
This commit is contained in:
@@ -1,183 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of the Froxlor project.
|
|
||||||
* Copyright (c) 2010 the Froxlor Team (see authors).
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, you can also view it online at
|
|
||||||
* https://files.froxlor.org/misc/COPYING.txt
|
|
||||||
*
|
|
||||||
* @copyright the authors
|
|
||||||
* @author Froxlor team <team@froxlor.org>
|
|
||||||
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
|
||||||
*/
|
|
||||||
|
|
||||||
const AREA = 'admin';
|
|
||||||
require __DIR__ . '/lib/init.php';
|
|
||||||
|
|
||||||
use Froxlor\Api\Commands\Backups;
|
|
||||||
use Froxlor\Api\Commands\BackupStorages;
|
|
||||||
use Froxlor\FroxlorLogger;
|
|
||||||
use Froxlor\UI\Collection;
|
|
||||||
use Froxlor\UI\HTML;
|
|
||||||
use Froxlor\UI\Listing;
|
|
||||||
use Froxlor\UI\Panel\UI;
|
|
||||||
use Froxlor\UI\Request;
|
|
||||||
use Froxlor\UI\Response;
|
|
||||||
|
|
||||||
$id = (int)Request::any('id');
|
|
||||||
|
|
||||||
if (($page == 'backups' || $page == 'overview')) {
|
|
||||||
if ($action == '') {
|
|
||||||
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "viewed admin_backups");
|
|
||||||
|
|
||||||
try {
|
|
||||||
$admin_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/admin/tablelisting.backups.php';
|
|
||||||
$collection = (new Collection(Backups::class, $userinfo))
|
|
||||||
->withPagination($admin_list_data['backups_list']['columns'], $admin_list_data['backups_list']['default_sorting']);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
Response::dynamicError($e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
UI::view('user/table.html.twig', [
|
|
||||||
'listing' => Listing::format($collection, $admin_list_data, 'backups_list'),
|
|
||||||
'actions_links' => [
|
|
||||||
[
|
|
||||||
'href' => $linker->getLink(['section' => 'backups', 'page' => $page, 'action' => 'restore']),
|
|
||||||
'label' => lng('backup.backups_restore'),
|
|
||||||
'icon' => 'fa-solid fa-file-import',
|
|
||||||
'class' => 'btn-outline-secondary'
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'href' => $linker->getLink(['section' => 'backups', 'page' => 'storages']),
|
|
||||||
'label' => lng('backup.backup_storages'),
|
|
||||||
'icon' => 'fa-solid fa-hard-drive',
|
|
||||||
'class' => 'btn-outline-secondary',
|
|
||||||
'visible' => $userinfo['change_serversettings'] == '1'
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
} elseif ($action == 'delete' && $id != 0) {
|
|
||||||
|
|
||||||
} elseif ($action == 'add') {
|
|
||||||
|
|
||||||
} elseif ($action == 'edit' && $id != 0) {
|
|
||||||
|
|
||||||
} elseif ($action == 'restore') {
|
|
||||||
|
|
||||||
}
|
|
||||||
} else if ($page == 'storages' && $userinfo['change_serversettings'] == '1') {
|
|
||||||
if ($action == '') {
|
|
||||||
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "list backup storages");
|
|
||||||
|
|
||||||
try {
|
|
||||||
$backup_storage_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/admin/tablelisting.backup_storages.php';
|
|
||||||
$collection = (new Collection(BackupStorages::class, $userinfo))
|
|
||||||
->withPagination($backup_storage_list_data['backup_storages_list']['columns'], $backup_storage_list_data['backup_storages_list']['default_sorting']);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
Response::dynamicError($e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
UI::view('user/table.html.twig', [
|
|
||||||
'listing' => Listing::format($collection, $backup_storage_list_data, 'backup_storages_list'),
|
|
||||||
'actions_links' => [
|
|
||||||
[
|
|
||||||
'href' => $linker->getLink(['section' => 'backups', 'page' => 'backups']),
|
|
||||||
'label' => lng('backup.backups'),
|
|
||||||
'icon' => 'fa-solid fa-reply'
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'href' => $linker->getLink(['section' => 'backups', 'page' => $page, 'action' => 'add']),
|
|
||||||
'label' => lng('backup.backup_storage.add')
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
} elseif ($action == 'delete' && $id != 0) {
|
|
||||||
try {
|
|
||||||
$json_result = BackupStorages::getLocal($userinfo, [
|
|
||||||
'id' => $id
|
|
||||||
])->get();
|
|
||||||
} catch (Exception $e) {
|
|
||||||
Response::dynamicError($e->getMessage());
|
|
||||||
}
|
|
||||||
$result = json_decode($json_result, true)['data'];
|
|
||||||
|
|
||||||
if ($result['id'] != '') {
|
|
||||||
if (isset($_POST['send']) && $_POST['send'] == 'send') {
|
|
||||||
BackupStorages::getLocal($userinfo, [
|
|
||||||
'id' => $id
|
|
||||||
])->delete();
|
|
||||||
Response::redirectTo($filename, [
|
|
||||||
'page' => $page
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
HTML::askYesNo('backup_backup_server_reallydelete', $filename, [
|
|
||||||
'id' => $id,
|
|
||||||
'page' => $page,
|
|
||||||
'action' => $action
|
|
||||||
], $result['id']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} elseif ($action == 'add') {
|
|
||||||
if (isset($_POST['send']) && $_POST['send'] == 'send') {
|
|
||||||
try {
|
|
||||||
BackupStorages::getLocal($userinfo, $_POST)->add();
|
|
||||||
} catch (Exception $e) {
|
|
||||||
Response::dynamicError($e->getMessage());
|
|
||||||
}
|
|
||||||
Response::redirectTo($filename, [
|
|
||||||
'page' => $page
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
$admin_add_data = include_once dirname(__FILE__) . '/lib/formfields/admin/backup_storages/formfield.backup_storage_add.php';
|
|
||||||
|
|
||||||
UI::view('user/form.html.twig', [
|
|
||||||
'formaction' => $linker->getLink(['section' => 'backups']),
|
|
||||||
'formdata' => $admin_add_data['backup_storage_add']
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
} elseif ($action == 'edit' && $id != 0) {
|
|
||||||
try {
|
|
||||||
$json_result = BackupStorages::getLocal($userinfo, [
|
|
||||||
'id' => $id
|
|
||||||
])->get();
|
|
||||||
} catch (Exception $e) {
|
|
||||||
Response::dynamicError($e->getMessage());
|
|
||||||
}
|
|
||||||
$result = json_decode($json_result, true)['data'];
|
|
||||||
|
|
||||||
if ($result['id'] != '') {
|
|
||||||
if (isset($_POST['send']) && $_POST['send'] == 'send') {
|
|
||||||
try {
|
|
||||||
BackupStorages::getLocal($userinfo, $_POST)->update();
|
|
||||||
} catch (Exception $e) {
|
|
||||||
Response::dynamicError($e->getMessage());
|
|
||||||
}
|
|
||||||
Response::redirectTo($filename, [
|
|
||||||
'page' => $page
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
$backup_storage_edit_data = include_once dirname(__FILE__) . '/lib/formfields/admin/backup_storages/formfield.backup_storage_edit.php';
|
|
||||||
|
|
||||||
UI::view('user/form.html.twig', [
|
|
||||||
'formaction' => $linker->getLink(['section' => 'backups', 'id' => $id]),
|
|
||||||
'formdata' => $backup_storage_edit_data['backup_storage_edit'],
|
|
||||||
'editid' => $id
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Response::dynamicError('403');
|
|
||||||
}
|
|
||||||
@@ -57,9 +57,7 @@
|
|||||||
"erusev/parsedown": "^1.7",
|
"erusev/parsedown": "^1.7",
|
||||||
"symfony/console": "^5.4",
|
"symfony/console": "^5.4",
|
||||||
"pear/net_dns2": "^1.5",
|
"pear/net_dns2": "^1.5",
|
||||||
"amnuts/opcache-gui": "^3.4",
|
"amnuts/opcache-gui": "^3.4"
|
||||||
"aws/aws-sdk-php": "^3.280",
|
|
||||||
"phpseclib/phpseclib": "~3.0"
|
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^9",
|
"phpunit/phpunit": "^9",
|
||||||
|
|||||||
1012
composer.lock
generated
1012
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -921,8 +921,7 @@ INSERT INTO `cronjobs_run` (`id`, `module`, `cronfile`, `cronclass`, `interval`,
|
|||||||
(3, 'froxlor/reports', 'usage_report', '\\Froxlor\\Cron\\Traffic\\ReportsCron', '1 DAY', '1', 'cron_usage_report'),
|
(3, 'froxlor/reports', 'usage_report', '\\Froxlor\\Cron\\Traffic\\ReportsCron', '1 DAY', '1', 'cron_usage_report'),
|
||||||
(4, 'froxlor/core', 'mailboxsize', '\\Froxlor\\Cron\\System\\MailboxsizeCron', '6 HOUR', '1', 'cron_mailboxsize'),
|
(4, 'froxlor/core', 'mailboxsize', '\\Froxlor\\Cron\\System\\MailboxsizeCron', '6 HOUR', '1', 'cron_mailboxsize'),
|
||||||
(5, 'froxlor/letsencrypt', 'letsencrypt', '\\Froxlor\\Cron\\Http\\LetsEncrypt\\AcmeSh', '5 MINUTE', '0', 'cron_letsencrypt'),
|
(5, 'froxlor/letsencrypt', 'letsencrypt', '\\Froxlor\\Cron\\Http\\LetsEncrypt\\AcmeSh', '5 MINUTE', '0', 'cron_letsencrypt'),
|
||||||
(6, 'froxlor/export', 'export', '\\Froxlor\\Cron\\System\\ExportCron', '1 HOUR', '0', 'cron_export'),
|
(6, 'froxlor/export', 'export', '\\Froxlor\\Cron\\System\\ExportCron', '1 HOUR', '0', 'cron_export');
|
||||||
(7, 'froxlor/backup', 'backup', '\\Froxlor\\Cron\\Backup\\BackupCron', '1 DAY', '0', 'cron_backup');
|
|
||||||
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS `ftp_quotalimits`;
|
DROP TABLE IF EXISTS `ftp_quotalimits`;
|
||||||
@@ -1070,38 +1069,4 @@ CREATE TABLE `panel_loginlinks` (
|
|||||||
`allowed_from` text NOT NULL,
|
`allowed_from` text NOT NULL,
|
||||||
UNIQUE KEY `loginname` (`loginname`)
|
UNIQUE KEY `loginname` (`loginname`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||||
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS `panel_backup_storages`;
|
|
||||||
CREATE TABLE `panel_backup_storages` (
|
|
||||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
|
||||||
`description` varchar(255) NOT NULL,
|
|
||||||
`type` varchar(255) NOT NULL DEFAULT 'local',
|
|
||||||
`region` varchar(255) NULL,
|
|
||||||
`bucket` varchar(255) NULL,
|
|
||||||
`destination_path` varchar(255) NOT NULL,
|
|
||||||
`hostname` varchar(255) NULL,
|
|
||||||
`username` varchar(255) NULL,
|
|
||||||
`password` text,
|
|
||||||
`pgp_public_key` text,
|
|
||||||
`retention` int(3) NOT NULL DEFAULT 3,
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
|
||||||
|
|
||||||
INSERT INTO `panel_backup_storages` (`id`, `description`, `destination_path`) VALUES
|
|
||||||
(1, 'Local backup storage', '/var/customers/backups');
|
|
||||||
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS `panel_backups`;
|
|
||||||
CREATE TABLE `panel_backups` (
|
|
||||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
|
||||||
`adminid` int(11) NOT NULL,
|
|
||||||
`customerid` int(11) NOT NULL,
|
|
||||||
`loginname` varchar(255) NOT NULL,
|
|
||||||
`size` bigint(20) NOT NULL,
|
|
||||||
`storage_id` int(11) NOT NULL,
|
|
||||||
`filename` varchar(255) NOT NULL,
|
|
||||||
`created_at` int(15) NOT NULL,
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
|
||||||
FROXLORSQL;
|
FROXLORSQL;
|
||||||
|
|||||||
@@ -62,54 +62,6 @@ if (Froxlor::isDatabaseVersion('202304260')) {
|
|||||||
Update::lastStepStatus(1, 'Customized setting, not changing');
|
Update::lastStepStatus(1, 'Customized setting, not changing');
|
||||||
}
|
}
|
||||||
|
|
||||||
Update::showUpdateStep("Creating new tables and fields for backups");
|
|
||||||
Database::query("DROP TABLE IF EXISTS `" . TABLE_PANEL_BACKUP_STORAGES . "`;");
|
|
||||||
$sql = "CREATE TABLE `" . TABLE_PANEL_BACKUP_STORAGES . "` (
|
|
||||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
|
||||||
`description` varchar(255) NOT NULL,
|
|
||||||
`type` varchar(255) NOT NULL DEFAULT 'local',
|
|
||||||
`region` varchar(255) NULL,
|
|
||||||
`bucket` varchar(255) NULL,
|
|
||||||
`destination_path` varchar(255) NOT NULL,
|
|
||||||
`hostname` varchar(255) NULL,
|
|
||||||
`username` varchar(255) NULL,
|
|
||||||
`password` text,
|
|
||||||
`pgp_public_key` text,
|
|
||||||
`retention` int(3) NOT NULL DEFAULT 3,
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;";
|
|
||||||
Database::query($sql);
|
|
||||||
Database::query("
|
|
||||||
INSERT INTO `panel_backup_storages` (`id`, `description`, `destination_path`) VALUES
|
|
||||||
(1, 'Local backup storage', '/var/customers/backups');
|
|
||||||
");
|
|
||||||
Database::query("DROP TABLE IF EXISTS `" . TABLE_PANEL_BACKUPS . "`;");
|
|
||||||
$sql = "CREATE TABLE `" . TABLE_PANEL_BACKUPS . "` (
|
|
||||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
|
||||||
`adminid` int(11) NOT NULL,
|
|
||||||
`customerid` int(11) NOT NULL,
|
|
||||||
`loginname` varchar(255) NOT NULL,
|
|
||||||
`size` bigint(20) NOT NULL,
|
|
||||||
`storage_id` int(11) NOT NULL,
|
|
||||||
`filename` varchar(255) NOT NULL,
|
|
||||||
`created_at` int(15) NOT NULL,
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;";
|
|
||||||
Database::query($sql);
|
|
||||||
// add customer backup-target-storage
|
|
||||||
Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` ADD `backup` int(11) NOT NULL default '1' AFTER `allowed_mysqlserver`;");
|
|
||||||
Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` ADD `access_backups` tinyint(1) NOT NULL default '1' AFTER `backup`;");
|
|
||||||
Update::lastStepStatus(0);
|
|
||||||
|
|
||||||
Update::showUpdateStep("Adding new backup settings");
|
|
||||||
Settings::AddNew('backup.enabled', 0);
|
|
||||||
Settings::AddNew('backup.default_storage', 1);
|
|
||||||
Settings::AddNew('backup.default_customer_access', 1);
|
|
||||||
Settings::AddNew('backup.default_pgp_public_key', '');
|
|
||||||
Settings::AddNew('backup.default_retention', 3);
|
|
||||||
Settings::AddNew('backup.backup_tmp_dir', '/var/customers/backup/');
|
|
||||||
Update::lastStepStatus(0);
|
|
||||||
|
|
||||||
Update::showUpdateStep("Adjusting cronjobs");
|
Update::showUpdateStep("Adjusting cronjobs");
|
||||||
Database::query("
|
Database::query("
|
||||||
UPDATE `" . TABLE_PANEL_CRONRUNS . "` SET
|
UPDATE `" . TABLE_PANEL_CRONRUNS . "` SET
|
||||||
@@ -119,15 +71,6 @@ if (Froxlor::isDatabaseVersion('202304260')) {
|
|||||||
`interval` = '1 HOUR',
|
`interval` = '1 HOUR',
|
||||||
`desc_lng_key` = 'cron_export'
|
`desc_lng_key` = 'cron_export'
|
||||||
WHERE `module` = 'froxlor/backup'
|
WHERE `module` = 'froxlor/backup'
|
||||||
");
|
|
||||||
Database::query("
|
|
||||||
INSERT INTO `" . TABLE_PANEL_CRONRUNS . "` SET
|
|
||||||
`module`= 'froxlor/backup',
|
|
||||||
`cronfile` = 'backup',
|
|
||||||
`cronclass` = '\\Froxlor\\Cron\\Backup\\BackupCron',
|
|
||||||
`interval` = '1 DAY',
|
|
||||||
`isactive` = '0',
|
|
||||||
`desc_lng_key` = 'cron_backup'
|
|
||||||
");
|
");
|
||||||
Update::lastStepStatus(0);
|
Update::lastStepStatus(0);
|
||||||
|
|
||||||
|
|||||||
@@ -1,542 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of the Froxlor project.
|
|
||||||
* Copyright (c) 2010 the Froxlor Team (see authors).
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, you can also view it online at
|
|
||||||
* https://files.froxlor.org/misc/COPYING.txt
|
|
||||||
*
|
|
||||||
* @copyright the authors
|
|
||||||
* @author Froxlor team <team@froxlor.org>
|
|
||||||
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Froxlor\Api\Commands;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use Froxlor\Api\ApiCommand;
|
|
||||||
use Froxlor\Api\ResourceEntity;
|
|
||||||
use Froxlor\Database\Database;
|
|
||||||
use Froxlor\FileDir;
|
|
||||||
use Froxlor\FroxlorLogger;
|
|
||||||
use Froxlor\Settings;
|
|
||||||
use Froxlor\UI\Response;
|
|
||||||
use Froxlor\Validate\Validate;
|
|
||||||
use PDO;
|
|
||||||
use phpseclib3\Crypt\Common\PublicKey;
|
|
||||||
use phpseclib3\Crypt\PublicKeyLoader;
|
|
||||||
use phpseclib3\Exception\NoKeyLoadedException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @since 2.1.0
|
|
||||||
*/
|
|
||||||
class BackupStorages extends ApiCommand implements ResourceEntity
|
|
||||||
{
|
|
||||||
const SUPPORTED_TYPES = [
|
|
||||||
'local',
|
|
||||||
'ftp',
|
|
||||||
'sftp',
|
|
||||||
'rsync',
|
|
||||||
's3',
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* lists all backup storages entries
|
|
||||||
*
|
|
||||||
* @param array $sql_search
|
|
||||||
* optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),
|
|
||||||
* LIKE is used if left empty and 'value' => searchvalue
|
|
||||||
* @param int $sql_limit
|
|
||||||
* optional specify number of results to be returned
|
|
||||||
* @param int $sql_offset
|
|
||||||
* optional specify offset for resultset
|
|
||||||
* @param array $sql_orderby
|
|
||||||
* optional array with index = fieldname and value = ASC|DESC to order the resultset by one or more
|
|
||||||
* fields
|
|
||||||
*
|
|
||||||
* @access admin
|
|
||||||
* @return string json-encoded array count|list
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function listing()
|
|
||||||
{
|
|
||||||
if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {
|
|
||||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list backup storages");
|
|
||||||
$query_fields = [];
|
|
||||||
$result_stmt = Database::prepare("
|
|
||||||
SELECT * FROM `" . TABLE_PANEL_BACKUP_STORAGES . "` " . $this->getSearchWhere($query_fields) . $this->getOrderBy() . $this->getLimit()
|
|
||||||
);
|
|
||||||
Database::pexecute($result_stmt, $query_fields, true, true);
|
|
||||||
$result = [];
|
|
||||||
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
|
|
||||||
$result[] = $row;
|
|
||||||
}
|
|
||||||
return $this->response([
|
|
||||||
'count' => count($result),
|
|
||||||
'list' => $result
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
throw new Exception("Not allowed to execute given command.", 403);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* returns the total number of backup storages
|
|
||||||
*
|
|
||||||
* @access admin
|
|
||||||
* @return string json-encoded response message
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function listingCount()
|
|
||||||
{
|
|
||||||
if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {
|
|
||||||
$result_stmt = Database::prepare("
|
|
||||||
SELECT COUNT(*) as num_backup_storagess
|
|
||||||
FROM `" . TABLE_PANEL_BACKUP_STORAGES . "`
|
|
||||||
");
|
|
||||||
$result = Database::pexecute_first($result_stmt, null, true, true);
|
|
||||||
if ($result) {
|
|
||||||
return $this->response($result['num_backup_storagess']);
|
|
||||||
}
|
|
||||||
$this->response(0);
|
|
||||||
}
|
|
||||||
throw new Exception("Not allowed to execute given command.", 403);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create a backup storage
|
|
||||||
*
|
|
||||||
* @param string $type
|
|
||||||
* required, backup storage type
|
|
||||||
* @param string $destination_path
|
|
||||||
* required, destination path for backup storage
|
|
||||||
* @param string $description
|
|
||||||
* required, description for backup storage
|
|
||||||
* @param string $region
|
|
||||||
* optional, required if type=s3. Region for backup storage (used for S3)
|
|
||||||
* @param string $bucket
|
|
||||||
* optional, required if type=s3. Bucket for backup storage (used for S3)
|
|
||||||
* @param string $hostname
|
|
||||||
* optional, required if type != local. Hostname for backup storage
|
|
||||||
* @param string $username
|
|
||||||
* optional, required if type != local. Username for backup storage (also used as access key for S3)
|
|
||||||
* @param string $password
|
|
||||||
* optional, required if type != local. Password for backup storage (also used as secret key for S3)
|
|
||||||
* @param string $pgp_public_key
|
|
||||||
* optional, pgp public key for backup storage
|
|
||||||
* @param string $retention
|
|
||||||
* optional, retention for backup storage (default {backup.default_retention})
|
|
||||||
*
|
|
||||||
* @access admin
|
|
||||||
* @return string json-encoded array
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function add()
|
|
||||||
{
|
|
||||||
if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {
|
|
||||||
// required parameters
|
|
||||||
$type = $this->getParam('type');
|
|
||||||
$destination_path = $this->getParam('destination_path');
|
|
||||||
$description = $this->getParam('description');
|
|
||||||
|
|
||||||
// type related requirements
|
|
||||||
$optional_flags = [
|
|
||||||
'region' => true,
|
|
||||||
'bucket' => true,
|
|
||||||
'hostname' => true,
|
|
||||||
'username' => true,
|
|
||||||
'password' => true,
|
|
||||||
];
|
|
||||||
|
|
||||||
if (!in_array($type, self::SUPPORTED_TYPES)) {
|
|
||||||
throw new Exception("Unsupported storage type: '" . $type . "'", 406);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($type != 'local') {
|
|
||||||
$optional_flags['hostname'] = false;
|
|
||||||
$optional_flags['username'] = false;
|
|
||||||
$optional_flags['password'] = false;
|
|
||||||
}
|
|
||||||
if ($type == 's3') {
|
|
||||||
$optional_flags['region'] = false;
|
|
||||||
$optional_flags['bucket'] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// parameters
|
|
||||||
$region = $this->getParam('region', $optional_flags['region']);
|
|
||||||
$bucket = $this->getParam('bucket', $optional_flags['bucket']);
|
|
||||||
$hostname = $this->getParam('hostname', $optional_flags['hostname']);
|
|
||||||
$username = $this->getParam('username', $optional_flags['username']);
|
|
||||||
$password = $this->getParam('password', $optional_flags['password']);
|
|
||||||
$pgp_public_key = $this->getParam('pgp_public_key', true, null);
|
|
||||||
$retention = $this->getParam('retention', true, Settings::Get('backup.default_retention'));
|
|
||||||
|
|
||||||
// validation
|
|
||||||
$destination_path = FileDir::makeCorrectDir(Validate::validate($destination_path, 'destination_path', Validate::REGEX_DIR, '', [], true));
|
|
||||||
if ($type != 'local') {
|
|
||||||
if (!Validate::validateUrl($hostname)) {
|
|
||||||
Response::standardError('invalidhostname', '', true);
|
|
||||||
}
|
|
||||||
// check whether password is an ssh public key
|
|
||||||
$pwd_is_ssh_key = false;
|
|
||||||
try {
|
|
||||||
$key = PublicKeyLoader::loadPublicKey($password);
|
|
||||||
if ($key instanceof PublicKey) {
|
|
||||||
$pwd_is_ssh_key = true;
|
|
||||||
}
|
|
||||||
} catch (NoKeyLoadedException $e) {
|
|
||||||
/* nothing to do */
|
|
||||||
}
|
|
||||||
if (!$pwd_is_ssh_key) {
|
|
||||||
// normal password
|
|
||||||
$password = Validate::validate($password, 'password', '', '', [], true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($type == 's3') {
|
|
||||||
$region = Validate::validate($region, 'region', '', '', [], true);
|
|
||||||
$bucket = Validate::validate($bucket, 'bucket', '/(?!(^xn--|.+-s3alias$))^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/', '', [], true);
|
|
||||||
}
|
|
||||||
if ($retention <= 0) {
|
|
||||||
$retention = Settings::Get('backup.default_retention');
|
|
||||||
}
|
|
||||||
// TODO: add more validation
|
|
||||||
|
|
||||||
// pgp public key validation
|
|
||||||
if (!empty($pgp_public_key)) {
|
|
||||||
// check if gnupg extension is loaded
|
|
||||||
if (!extension_loaded('gnupg')) {
|
|
||||||
Response::standardError('gnupgextensionnotavailable', '', true);
|
|
||||||
}
|
|
||||||
// check if the pgp public key is a valid key
|
|
||||||
putenv('GNUPGHOME=' . sys_get_temp_dir());
|
|
||||||
if (gnupg_import(gnupg_init(), $pgp_public_key) === false) {
|
|
||||||
Response::standardError('invalidpgppublickey', '', true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// store
|
|
||||||
$stmt = Database::prepare("
|
|
||||||
INSERT INTO `" . TABLE_PANEL_BACKUP_STORAGES . "` (
|
|
||||||
`description`,
|
|
||||||
`type`,
|
|
||||||
`region`,
|
|
||||||
`bucket`,
|
|
||||||
`destination_path`,
|
|
||||||
`hostname`,
|
|
||||||
`username`,
|
|
||||||
`password`,
|
|
||||||
`pgp_public_key`,
|
|
||||||
`retention`
|
|
||||||
) VALUES (
|
|
||||||
:description,
|
|
||||||
:type,
|
|
||||||
:region,
|
|
||||||
:bucket,
|
|
||||||
:destination_path,
|
|
||||||
:hostname,
|
|
||||||
:username,
|
|
||||||
:password,
|
|
||||||
:pgp_public_key,
|
|
||||||
:retention
|
|
||||||
)
|
|
||||||
");
|
|
||||||
$params = [
|
|
||||||
"description" => $description,
|
|
||||||
"type" => $type,
|
|
||||||
"region" => $region,
|
|
||||||
"bucket" => $bucket,
|
|
||||||
"destination_path" => $destination_path,
|
|
||||||
"hostname" => $hostname,
|
|
||||||
"username" => $username,
|
|
||||||
"password" => $password,
|
|
||||||
"pgp_public_key" => $pgp_public_key,
|
|
||||||
"retention" => $retention,
|
|
||||||
];
|
|
||||||
Database::pexecute($stmt, $params, true, true);
|
|
||||||
$id = Database::lastInsertId();
|
|
||||||
|
|
||||||
// return
|
|
||||||
$result = $this->apiCall('BackupStorages.get', [
|
|
||||||
'id' => $id
|
|
||||||
]);
|
|
||||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] added backup storage '" . $result['description'] . "' (" . $result['type'] . ")");
|
|
||||||
return $this->response($result);
|
|
||||||
}
|
|
||||||
throw new Exception("Not allowed to execute given command.", 403);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* return a backup storage entry by id
|
|
||||||
*
|
|
||||||
* @param int $id
|
|
||||||
* the backup-storage-id
|
|
||||||
*
|
|
||||||
* @access admin
|
|
||||||
* @return string json-encoded array
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function get()
|
|
||||||
{
|
|
||||||
$id = $this->getParam('id');
|
|
||||||
|
|
||||||
if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {
|
|
||||||
$result_stmt = Database::prepare("
|
|
||||||
SELECT * FROM `" . TABLE_PANEL_BACKUP_STORAGES . "`
|
|
||||||
WHERE `id` = :id"
|
|
||||||
);
|
|
||||||
$params = [
|
|
||||||
'id' => $id
|
|
||||||
];
|
|
||||||
$result = Database::pexecute_first($result_stmt, $params, true, true);
|
|
||||||
if ($result) {
|
|
||||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] get backup storage '" . $result['description'] . "'");
|
|
||||||
return $this->response($result);
|
|
||||||
}
|
|
||||||
throw new Exception("Backup storage with " . $id . " could not be found", 404);
|
|
||||||
}
|
|
||||||
throw new Exception("Not allowed to execute given command.", 403);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* update a backup storage by given id
|
|
||||||
*
|
|
||||||
* @param int $id
|
|
||||||
* required, the backup-storage-id
|
|
||||||
* @param string $type
|
|
||||||
* optional, backup storage type
|
|
||||||
* @param string $destination_path
|
|
||||||
* optional, destination path for backup storage
|
|
||||||
* @param string $description
|
|
||||||
* required, description for backup storage
|
|
||||||
* @param string $region
|
|
||||||
* optional, region for backup storage (used for S3)
|
|
||||||
* @param string $bucket
|
|
||||||
* optional, bucket for backup storage (used for S3)
|
|
||||||
* @param string $hostname
|
|
||||||
* optional, hostname for backup storage
|
|
||||||
* @param string $username
|
|
||||||
* optional, username for backup storage (also used as access key for S3)
|
|
||||||
* @param string $password
|
|
||||||
* optional, password for backup storage (also used as secret key for S3)
|
|
||||||
* @param string $pgp_public_key
|
|
||||||
* optional, pgp public key for backup storage
|
|
||||||
* @param string $retention
|
|
||||||
* optional, retention for backup storage (default {backup.default_retention})
|
|
||||||
*
|
|
||||||
* @access admin
|
|
||||||
* @return string json-encoded array
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function update()
|
|
||||||
{
|
|
||||||
$id = $this->getParam('id');
|
|
||||||
|
|
||||||
if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {
|
|
||||||
// validation
|
|
||||||
$result = $this->apiCall('BackupStorages.get', [
|
|
||||||
'id' => $id
|
|
||||||
]);
|
|
||||||
|
|
||||||
// parameters
|
|
||||||
$description = $this->getParam('description', true, $result['description']);
|
|
||||||
$type = $this->getParam('type', true, $result['type']);
|
|
||||||
$region = $this->getParam('region', true, $result['region']);
|
|
||||||
$bucket = $this->getParam('bucket', true, $result['bucket']);
|
|
||||||
$destination_path = $this->getParam('destination_path', true, $result['destination_path']);
|
|
||||||
$hostname = $this->getParam('hostname', true, $result['hostname']);
|
|
||||||
$username = $this->getParam('username', true, $result['username']);
|
|
||||||
$password = $this->getParam('password', true, '');
|
|
||||||
$pgp_public_key = $this->getParam('pgp_public_key', true, $result['pgp_public_key']);
|
|
||||||
$retention = $this->getParam('retention', true, $result['retention']);
|
|
||||||
|
|
||||||
if (!in_array($type, self::SUPPORTED_TYPES)) {
|
|
||||||
throw new Exception("Unsupported storage type: '" . $type . "'", 406);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($type != 'local') {
|
|
||||||
if (empty($hostname)) {
|
|
||||||
throw new Exception("Field 'hostname' cannot be empty", 406);
|
|
||||||
}
|
|
||||||
if (empty($username)) {
|
|
||||||
throw new Exception("Field 'username' cannot be empty", 406);
|
|
||||||
}
|
|
||||||
// password change
|
|
||||||
if (!empty($password)) {
|
|
||||||
// check whether password is an ssh public key
|
|
||||||
$pwd_is_ssh_key = false;
|
|
||||||
try {
|
|
||||||
$key = PublicKeyLoader::loadPublicKey($password);
|
|
||||||
if ($key instanceof PublicKey) {
|
|
||||||
$pwd_is_ssh_key = true;
|
|
||||||
}
|
|
||||||
} catch (NoKeyLoadedException $e) {
|
|
||||||
/* nothing to do */
|
|
||||||
}
|
|
||||||
if (!$pwd_is_ssh_key) {
|
|
||||||
// normal password
|
|
||||||
$password = Validate::validate($password, 'password', '', '', [], true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($type == 's3') {
|
|
||||||
if (empty($region)) {
|
|
||||||
throw new Exception("Field 'region' cannot be empty", 406);
|
|
||||||
}
|
|
||||||
if (empty($bucket)) {
|
|
||||||
throw new Exception("Field 'bucket' cannot be empty", 406);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// validation
|
|
||||||
$destination_path = FileDir::makeCorrectDir(Validate::validate($destination_path, 'destination_path', Validate::REGEX_DIR, '', [], true));
|
|
||||||
if ($type != 'local' && !Validate::validateUrl($hostname)) {
|
|
||||||
Response::standardError('invalidhostname', '', true);
|
|
||||||
}
|
|
||||||
if ($type == 's3') {
|
|
||||||
$region = Validate::validate($region, 'region', '', '', [], true);
|
|
||||||
$bucket = Validate::validate($bucket, 'bucket', '/(?!(^xn--|.+-s3alias$))^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/', '', [], true);
|
|
||||||
}
|
|
||||||
if ($retention <= 0) {
|
|
||||||
$retention = Settings::Get('backup.default_retention');
|
|
||||||
}
|
|
||||||
// TODO: add more validation
|
|
||||||
|
|
||||||
// pgp public key validation
|
|
||||||
if (!empty($pgp_public_key) && $pgp_public_key != $result['pgp_public_key']) {
|
|
||||||
// check if gnupg extension is loaded
|
|
||||||
if (!extension_loaded('gnupg')) {
|
|
||||||
Response::standardError('gnupgextensionnotavailable', '', true);
|
|
||||||
}
|
|
||||||
// check if the pgp public key is a valid key
|
|
||||||
putenv('GNUPGHOME=' . sys_get_temp_dir());
|
|
||||||
if (gnupg_import(gnupg_init(), $pgp_public_key) === false) {
|
|
||||||
Response::standardError('invalidpgppublickey', '', true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($password)) {
|
|
||||||
$stmt = Database::prepare("UPDATE `" . TABLE_PANEL_BACKUP_STORAGES . "`
|
|
||||||
SET `password` = :password
|
|
||||||
WHERE `id` = :id
|
|
||||||
");
|
|
||||||
Database::pexecute($stmt, [
|
|
||||||
"id" => $id,
|
|
||||||
"password" => $password
|
|
||||||
], true, true);
|
|
||||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] updated password for backup-storage '" . $result['description'] . "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
// update
|
|
||||||
$stmt = Database::prepare("
|
|
||||||
UPDATE `" . TABLE_PANEL_BACKUP_STORAGES . "`
|
|
||||||
SET `description` = :description,
|
|
||||||
`type` = :type,
|
|
||||||
`region` = :region,
|
|
||||||
`bucket` = :bucket,
|
|
||||||
`destination_path` = :destination_path,
|
|
||||||
`hostname` = :hostname,
|
|
||||||
`username` = :username,
|
|
||||||
`pgp_public_key` = :pgp_public_key,
|
|
||||||
`retention` = :retention
|
|
||||||
WHERE `id` = :id
|
|
||||||
");
|
|
||||||
$params = [
|
|
||||||
"id" => $id,
|
|
||||||
"description" => $description,
|
|
||||||
"type" => $type,
|
|
||||||
"region" => $region,
|
|
||||||
"bucket" => $bucket,
|
|
||||||
"destination_path" => $destination_path,
|
|
||||||
"hostname" => $hostname,
|
|
||||||
"username" => $username,
|
|
||||||
"pgp_public_key" => $pgp_public_key,
|
|
||||||
"retention" => $retention,
|
|
||||||
];
|
|
||||||
Database::pexecute($stmt, $params, true, true);
|
|
||||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] edited backup storage '" . $result['description'] . "'");
|
|
||||||
|
|
||||||
// return
|
|
||||||
$result = $this->apiCall('BackupStorages.get', [
|
|
||||||
'id' => $id
|
|
||||||
]);
|
|
||||||
return $this->response($result);
|
|
||||||
}
|
|
||||||
throw new Exception("Not allowed to execute given command.", 403);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* delete a backup-storage entry by id
|
|
||||||
*
|
|
||||||
* @param int $id
|
|
||||||
* required, the backup-storage-id
|
|
||||||
*
|
|
||||||
* @access admin
|
|
||||||
* @return string json-encoded array
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function delete()
|
|
||||||
{
|
|
||||||
$id = $this->getParam('id');
|
|
||||||
|
|
||||||
if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {
|
|
||||||
// validation
|
|
||||||
$result = $this->apiCall('BackupStorages.get', [
|
|
||||||
'id' => $id
|
|
||||||
]);
|
|
||||||
|
|
||||||
// validate no-one's using it
|
|
||||||
|
|
||||||
// settings
|
|
||||||
if ($id == Settings::Get('backup.default_storage')) {
|
|
||||||
throw new Exception("Given backup storage is currently set as default storage and cannot be deleted.", 406);
|
|
||||||
}
|
|
||||||
// customers
|
|
||||||
$sel_stmt = Database::prepare("
|
|
||||||
SELECT COUNT(*) as num_storage_users
|
|
||||||
FROM `" . TABLE_PANEL_CUSTOMERS . "`
|
|
||||||
WHERE `backup` = :id
|
|
||||||
");
|
|
||||||
$storage_users_result = Database::pexecute_first($sel_stmt, ['id' => $id]);
|
|
||||||
if ($storage_users_result && $storage_users_result['num_storage_users'] > 0) {
|
|
||||||
throw new Exception("Given backup storage is currently assigned to " . $storage_users_result['num_storage_users'] . " customers and cannot be deleted.", 406);
|
|
||||||
}
|
|
||||||
// existing backups
|
|
||||||
$sel_stmt = Database::prepare("
|
|
||||||
SELECT COUNT(*) as num_storage_backups
|
|
||||||
FROM `" . TABLE_PANEL_BACKUPS . "`
|
|
||||||
WHERE `storage_id` = :id
|
|
||||||
");
|
|
||||||
$storage_backups_result = Database::pexecute_first($sel_stmt, ['id' => $id]);
|
|
||||||
if ($storage_backups_result && $storage_backups_result['num_storage_backups'] > 0) {
|
|
||||||
throw new Exception("Given backup storage has still " . $storage_backups_result['num_storage_backups'] . " backups on it and cannot be deleted.", 406);
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete
|
|
||||||
$stmt = Database::prepare("
|
|
||||||
DELETE FROM `" . TABLE_PANEL_BACKUP_STORAGES . "`
|
|
||||||
WHERE `id` = :id
|
|
||||||
");
|
|
||||||
$params = [
|
|
||||||
"id" => $id
|
|
||||||
];
|
|
||||||
Database::pexecute($stmt, $params, true, true);
|
|
||||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] deleted backup storage '" . $result['description'] . "'");
|
|
||||||
|
|
||||||
// return
|
|
||||||
return $this->response(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Exception("Not allowed to execute given command.", 403);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,211 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of the Froxlor project.
|
|
||||||
* Copyright (c) 2010 the Froxlor Team (see authors).
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, you can also view it online at
|
|
||||||
* https://files.froxlor.org/misc/COPYING.txt
|
|
||||||
*
|
|
||||||
* @copyright the authors
|
|
||||||
* @author Froxlor team <team@froxlor.org>
|
|
||||||
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Froxlor\Api\Commands;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use Froxlor\Api\ApiCommand;
|
|
||||||
use Froxlor\Api\ResourceEntity;
|
|
||||||
use Froxlor\Database\Database;
|
|
||||||
use Froxlor\FroxlorLogger;
|
|
||||||
use PDO;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @since 2.1.0
|
|
||||||
*/
|
|
||||||
class Backups extends ApiCommand implements ResourceEntity
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* lists all admin entries
|
|
||||||
*
|
|
||||||
* @param array $sql_search
|
|
||||||
* optional array with index = fieldname, and value = array with 'op' => operator (one of <, > or =),
|
|
||||||
* LIKE is used if left empty and 'value' => searchvalue
|
|
||||||
* @param int $sql_limit
|
|
||||||
* optional specify number of results to be returned
|
|
||||||
* @param int $sql_offset
|
|
||||||
* optional specify offset for resultset
|
|
||||||
* @param array $sql_orderby
|
|
||||||
* optional array with index = fieldname and value = ASC|DESC to order the resultset by one or more
|
|
||||||
* fields
|
|
||||||
*
|
|
||||||
* @access admin
|
|
||||||
* @return string json-encoded array count|list
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function listing()
|
|
||||||
{
|
|
||||||
if ($this->isAdmin()) {
|
|
||||||
// if we're an admin, list all backups of all the admins customers
|
|
||||||
// or optionally for one specific customer identified by id or loginname
|
|
||||||
$customerid = $this->getParam('customerid', true, 0);
|
|
||||||
$loginname = $this->getParam('loginname', true, '');
|
|
||||||
|
|
||||||
if (!empty($customerid) || !empty($loginname)) {
|
|
||||||
$result = $this->apiCall('Customers.get', [
|
|
||||||
'id' => $customerid,
|
|
||||||
'loginname' => $loginname
|
|
||||||
]);
|
|
||||||
$custom_list_result = [
|
|
||||||
$result
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
$_custom_list_result = $this->apiCall('Customers.listing');
|
|
||||||
$custom_list_result = $_custom_list_result['list'];
|
|
||||||
}
|
|
||||||
$customer_ids = [];
|
|
||||||
foreach ($custom_list_result as $customer) {
|
|
||||||
$customer_ids[] = $customer['customerid'];
|
|
||||||
}
|
|
||||||
if (empty($customer_ids)) {
|
|
||||||
throw new Exception("Required resource unsatisfied.", 405);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$customer_ids = [
|
|
||||||
$this->getUserDetail('customerid')
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list backups");
|
|
||||||
$query_fields = [];
|
|
||||||
$result_stmt = Database::prepare("
|
|
||||||
SELECT `b`.*, `a`.`loginname` as `adminname`
|
|
||||||
FROM `" . TABLE_PANEL_BACKUPS . "` `b`
|
|
||||||
LEFT JOIN `" . TABLE_PANEL_ADMINS . "` `a` USING(`adminid`)
|
|
||||||
WHERE `b`.`customerid` IN (" . implode(', ', $customer_ids) . ")
|
|
||||||
" . $this->getSearchWhere($query_fields, true) . $this->getOrderBy() . $this->getLimit()
|
|
||||||
);
|
|
||||||
Database::pexecute($result_stmt, $query_fields, true, true);
|
|
||||||
$result = [];
|
|
||||||
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
|
|
||||||
$result[] = $row;
|
|
||||||
}
|
|
||||||
return $this->response([
|
|
||||||
'count' => count($result),
|
|
||||||
'list' => $result
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* returns the total number of backups for the given admin
|
|
||||||
*
|
|
||||||
* @access admin
|
|
||||||
* @return string json-encoded response message
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function listingCount()
|
|
||||||
{
|
|
||||||
if ($this->isAdmin()) {
|
|
||||||
// if we're an admin, list all backups of all the admins customers
|
|
||||||
// or optionally for one specific customer identified by id or loginname
|
|
||||||
$customerid = $this->getParam('customerid', true, 0);
|
|
||||||
$loginname = $this->getParam('loginname', true, '');
|
|
||||||
|
|
||||||
if (!empty($customerid) || !empty($loginname)) {
|
|
||||||
$result = $this->apiCall('Customers.get', [
|
|
||||||
'id' => $customerid,
|
|
||||||
'loginname' => $loginname
|
|
||||||
]);
|
|
||||||
$custom_list_result = [
|
|
||||||
$result
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
$_custom_list_result = $this->apiCall('Customers.listing');
|
|
||||||
$custom_list_result = $_custom_list_result['list'];
|
|
||||||
}
|
|
||||||
$customer_ids = [];
|
|
||||||
foreach ($custom_list_result as $customer) {
|
|
||||||
$customer_ids[] = $customer['customerid'];
|
|
||||||
}
|
|
||||||
if (empty($customer_ids)) {
|
|
||||||
throw new Exception("Required resource unsatisfied.", 405);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$customer_ids = [
|
|
||||||
$this->getUserDetail('customerid')
|
|
||||||
];
|
|
||||||
}
|
|
||||||
$result_stmt = Database::prepare("
|
|
||||||
SELECT COUNT(*) as num_backups
|
|
||||||
FROM `" . TABLE_PANEL_BACKUPS . "` `b`
|
|
||||||
WHERE `b`.`customerid` IN (" . implode(', ', $customer_ids) . ")
|
|
||||||
");
|
|
||||||
$result = Database::pexecute_first($result_stmt, null, true, true);
|
|
||||||
if ($result) {
|
|
||||||
return $this->response($result['num_backups']);
|
|
||||||
}
|
|
||||||
$this->response(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* You cannot add a backup entry
|
|
||||||
*
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function add()
|
|
||||||
{
|
|
||||||
throw new Exception('You cannot add a backup entry', 303);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* return a backup entry by id
|
|
||||||
*
|
|
||||||
* @param int $id
|
|
||||||
* optional, the backup-entry-id
|
|
||||||
*
|
|
||||||
* @access admin, customers
|
|
||||||
* @return string json-encoded array
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function get()
|
|
||||||
{
|
|
||||||
throw new Exception("@TODO", 303);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* You cannot update a backup entry
|
|
||||||
*
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function update()
|
|
||||||
{
|
|
||||||
throw new Exception('You cannot update a backup entry', 303);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* delete a backup entry by id
|
|
||||||
*
|
|
||||||
* @param int $id
|
|
||||||
* required, the backup-entry-id
|
|
||||||
*
|
|
||||||
* @access admin, customer
|
|
||||||
* @return string json-encoded array
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function delete()
|
|
||||||
{
|
|
||||||
throw new Exception("@TODO", 303);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -273,13 +273,6 @@ class Customers extends ApiCommand implements ResourceEntity
|
|||||||
* @param array $allowed_mysqlserver
|
* @param array $allowed_mysqlserver
|
||||||
* optional, array of IDs of defined mysql-servers the customer is allowed to use,
|
* optional, array of IDs of defined mysql-servers the customer is allowed to use,
|
||||||
* default is to allow the default dbserver (id=0)
|
* default is to allow the default dbserver (id=0)
|
||||||
* @param int $backup
|
|
||||||
* optional, either 0 to disable backup for this customer or a backup-storage-id
|
|
||||||
* where backups are to be stored, requires change_serversettings permissions,
|
|
||||||
* default is system-setting backup.default_storage
|
|
||||||
* @param bool $access_backups
|
|
||||||
* optional, where the customer is allowed to view backups, default is system-setting
|
|
||||||
* default_customer_access
|
|
||||||
*
|
*
|
||||||
* @access admin
|
* @access admin
|
||||||
* @return string json-encoded array
|
* @return string json-encoded array
|
||||||
@@ -366,24 +359,6 @@ class Customers extends ApiCommand implements ResourceEntity
|
|||||||
$p_allowed_mysqlserver = [];
|
$p_allowed_mysqlserver = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->getUserDetail('change_serversettings')) {
|
|
||||||
$backup = $this->getParam('backup', true, Settings::Get('backup.default_storage'));
|
|
||||||
if ($backup > 0) {
|
|
||||||
try {
|
|
||||||
$this->apiCall('BackupStorages.get', [
|
|
||||||
'id' => $backup
|
|
||||||
]);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
// not found or other issue, set default
|
|
||||||
$backup = Settings::Get('backup.default_storage');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$access_backups = $this->getBoolParam('access_backups', true, Settings::Get('backup.default_customer_access'));
|
|
||||||
} else {
|
|
||||||
$backup = Settings::Get('backup.default_storage');
|
|
||||||
$access_backups = Settings::Get('backup.default_customer_access');
|
|
||||||
}
|
|
||||||
|
|
||||||
// validation
|
// validation
|
||||||
$name = Validate::validate($name, 'name', Validate::REGEX_DESC_TEXT, '', [], true);
|
$name = Validate::validate($name, 'name', Validate::REGEX_DESC_TEXT, '', [], true);
|
||||||
$firstname = Validate::validate($firstname, 'first name', Validate::REGEX_DESC_TEXT, '', [], true);
|
$firstname = Validate::validate($firstname, 'first name', Validate::REGEX_DESC_TEXT, '', [], true);
|
||||||
@@ -562,9 +537,7 @@ class Customers extends ApiCommand implements ResourceEntity
|
|||||||
'theme' => $_theme,
|
'theme' => $_theme,
|
||||||
'custom_notes' => $custom_notes,
|
'custom_notes' => $custom_notes,
|
||||||
'custom_notes_show' => $custom_notes_show,
|
'custom_notes_show' => $custom_notes_show,
|
||||||
'allowed_mysqlserver' => empty($allowed_mysqlserver) ? "" : json_encode($allowed_mysqlserver),
|
'allowed_mysqlserver' => empty($allowed_mysqlserver) ? "" : json_encode($allowed_mysqlserver)
|
||||||
'backup' => $backup,
|
|
||||||
'access_backups' => $access_backups
|
|
||||||
];
|
];
|
||||||
|
|
||||||
$ins_stmt = Database::prepare("
|
$ins_stmt = Database::prepare("
|
||||||
@@ -607,9 +580,7 @@ class Customers extends ApiCommand implements ResourceEntity
|
|||||||
`theme` = :theme,
|
`theme` = :theme,
|
||||||
`custom_notes` = :custom_notes,
|
`custom_notes` = :custom_notes,
|
||||||
`custom_notes_show` = :custom_notes_show,
|
`custom_notes_show` = :custom_notes_show,
|
||||||
`allowed_mysqlserver`= :allowed_mysqlserver,
|
`allowed_mysqlserver`= :allowed_mysqlserver
|
||||||
`backup` = :backup,
|
|
||||||
`access_backups` = :access_backups
|
|
||||||
");
|
");
|
||||||
Database::pexecute($ins_stmt, $ins_data, true, true);
|
Database::pexecute($ins_stmt, $ins_data, true, true);
|
||||||
|
|
||||||
@@ -1057,13 +1028,6 @@ class Customers extends ApiCommand implements ResourceEntity
|
|||||||
* @param array $allowed_mysqlserver
|
* @param array $allowed_mysqlserver
|
||||||
* optional, array of IDs of defined mysql-servers the customer is allowed to use,
|
* optional, array of IDs of defined mysql-servers the customer is allowed to use,
|
||||||
* default is to allow the default dbserver (id=0)
|
* default is to allow the default dbserver (id=0)
|
||||||
* @param int $backup
|
|
||||||
* optional, either 0 to disable backup for this customer or a backup-storage-id
|
|
||||||
* where backups are to be stored, requires change_serversettings permissions,
|
|
||||||
* default is system-setting backup.default_storage
|
|
||||||
* @param bool $access_backups
|
|
||||||
* optional, where the customer is allowed to view backups, default is system-setting
|
|
||||||
* default_customer_access
|
|
||||||
*
|
*
|
||||||
* @access admin, customer
|
* @access admin, customer
|
||||||
* @return string json-encoded array
|
* @return string json-encoded array
|
||||||
@@ -1125,24 +1089,6 @@ class Customers extends ApiCommand implements ResourceEntity
|
|||||||
$deactivated = $this->getBoolParam('deactivated', true, $result['deactivated']);
|
$deactivated = $this->getBoolParam('deactivated', true, $result['deactivated']);
|
||||||
$theme = $this->getParam('theme', true, $result['theme']);
|
$theme = $this->getParam('theme', true, $result['theme']);
|
||||||
$allowed_mysqlserver = $this->getParam('allowed_mysqlserver', true, json_decode($result['allowed_mysqlserver'], true));
|
$allowed_mysqlserver = $this->getParam('allowed_mysqlserver', true, json_decode($result['allowed_mysqlserver'], true));
|
||||||
|
|
||||||
if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) {
|
|
||||||
$backup = $this->getParam('backup', true, $result['backup']);
|
|
||||||
if ($backup > 0) {
|
|
||||||
try {
|
|
||||||
$this->apiCall('BackupStorages.get', [
|
|
||||||
'id' => $backup
|
|
||||||
]);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
// not found or other issue, dont update
|
|
||||||
$backup = $result['backup'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$access_backups = $this->getBoolParam('access_backups', true, Settings::Get('backup.default_customer_access'));
|
|
||||||
} else {
|
|
||||||
$backup = $result['backup'];
|
|
||||||
$access_backups = $result['access_backups'];
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// allowed parameters
|
// allowed parameters
|
||||||
$def_language = $this->getParam('def_language', true, $result['def_language']);
|
$def_language = $this->getParam('def_language', true, $result['def_language']);
|
||||||
@@ -1451,9 +1397,7 @@ class Customers extends ApiCommand implements ResourceEntity
|
|||||||
'custom_notes' => $custom_notes,
|
'custom_notes' => $custom_notes,
|
||||||
'custom_notes_show' => $custom_notes_show,
|
'custom_notes_show' => $custom_notes_show,
|
||||||
'api_allowed' => $api_allowed,
|
'api_allowed' => $api_allowed,
|
||||||
'allowed_mysqlserver' => empty($allowed_mysqlserver) ? "" : json_encode($allowed_mysqlserver),
|
'allowed_mysqlserver' => empty($allowed_mysqlserver) ? "" : json_encode($allowed_mysqlserver)
|
||||||
'backup' => $backup,
|
|
||||||
'access_backups' => $access_backups
|
|
||||||
];
|
];
|
||||||
$upd_data += $admin_upd_data;
|
$upd_data += $admin_upd_data;
|
||||||
}
|
}
|
||||||
@@ -1496,9 +1440,7 @@ class Customers extends ApiCommand implements ResourceEntity
|
|||||||
`custom_notes` = :custom_notes,
|
`custom_notes` = :custom_notes,
|
||||||
`custom_notes_show` = :custom_notes_show,
|
`custom_notes_show` = :custom_notes_show,
|
||||||
`api_allowed` = :api_allowed,
|
`api_allowed` = :api_allowed,
|
||||||
`allowed_mysqlserver` = :allowed_mysqlserver,
|
`allowed_mysqlserver` = :allowed_mysqlserver";
|
||||||
`backup`= :backup,
|
|
||||||
`access_backups` = :access_backups";
|
|
||||||
$upd_query .= $admin_upd_query;
|
$upd_query .= $admin_upd_query;
|
||||||
}
|
}
|
||||||
$upd_query .= " WHERE `customerid` = :customerid";
|
$upd_query .= " WHERE `customerid` = :customerid";
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of the Froxlor project.
|
|
||||||
* Copyright (c) 2010 the Froxlor Team (see authors).
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, you can also view it online at
|
|
||||||
* https://files.froxlor.org/misc/COPYING.txt
|
|
||||||
*
|
|
||||||
* @copyright the authors
|
|
||||||
* @author Froxlor team <team@froxlor.org>
|
|
||||||
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Froxlor\Backup;
|
|
||||||
|
|
||||||
use Froxlor\Database\Database;
|
|
||||||
use PDO;
|
|
||||||
|
|
||||||
class Backup
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* returns an array of existing backup-storages
|
|
||||||
* in our database for the settings-array
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function getBackupStorages(): array
|
|
||||||
{
|
|
||||||
$storages_array = [
|
|
||||||
0 => lng('backup.storage_none')
|
|
||||||
];
|
|
||||||
// get all storages
|
|
||||||
$result_stmt = Database::query("SELECT id, type, description FROM `" . TABLE_PANEL_BACKUP_STORAGES . "` ORDER BY type, description");
|
|
||||||
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
|
|
||||||
if (!isset($storages_array[$row['id']]) && !in_array($row['id'], $storages_array)) {
|
|
||||||
$storages_array[$row['id']] = "[" . $row['type'] . "] " . html_entity_decode($row['description']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $storages_array;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Froxlor\Backup\Storages;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use Froxlor\FileDir;
|
|
||||||
|
|
||||||
class Ftp extends Storage
|
|
||||||
{
|
|
||||||
private $ftp_conn = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function init(): bool
|
|
||||||
{
|
|
||||||
$hostname = $this->sData['storage']['hostname'] ?? '';
|
|
||||||
$username = $this->sData['storage']['username'] ?? '';
|
|
||||||
$password = $this->sData['storage']['password'] ?? '';
|
|
||||||
if (!empty($hostname) && !empty($username) && !empty($password)) {
|
|
||||||
$tmp = explode(":", $hostname);
|
|
||||||
$hostname = $tmp[0];
|
|
||||||
$port = $tmp[1] ?? 21;
|
|
||||||
$this->ftp_conn = ftp_connect($hostname, $port);
|
|
||||||
if ($this->ftp_conn === false) {
|
|
||||||
throw new Exception('Unable to connect to ftp-server "' . $hostname . ':' . $port . '"');
|
|
||||||
}
|
|
||||||
if (!ftp_login($this->ftp_conn, $username, $password)) {
|
|
||||||
throw new Exception('Unable to login to ftp-server "' . $hostname . ':' . $port . '"');
|
|
||||||
}
|
|
||||||
return $this->changeToCorrectDirectory();
|
|
||||||
}
|
|
||||||
throw new Exception('Empty hostname for FTP backup storage');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Move/Upload file from tmp-source-directory. The file should be moved or deleted afterward.
|
|
||||||
* Must return the (relative) path including filename to the backup.
|
|
||||||
*
|
|
||||||
* @param string $filename
|
|
||||||
* @param string $tmp_source_directory
|
|
||||||
* @return string
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
protected function putFile(string $filename, string $tmp_source_directory): string
|
|
||||||
{
|
|
||||||
$source = FileDir::makeCorrectFile($tmp_source_directory . "/" . $filename);
|
|
||||||
if (file_exists($source) && ftp_size($this->ftp_conn, $filename) == -1) {
|
|
||||||
if (ftp_put($this->ftp_conn, $filename, $source, FTP_BINARY)) {
|
|
||||||
return FileDir::makeCorrectFile($this->getDestinationDirectory() . '/' . $filename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $filename
|
|
||||||
* @return bool
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
protected function rmFile(string $filename): bool
|
|
||||||
{
|
|
||||||
$target = basename($filename);
|
|
||||||
if (ftp_size($this->ftp_conn, $target) >= 0) {
|
|
||||||
return ftp_delete($this->ftp_conn, $target);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function shutdown(): bool
|
|
||||||
{
|
|
||||||
return ftp_close($this->ftp_conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
private function changeToCorrectDirectory(): bool
|
|
||||||
{
|
|
||||||
$dirs = explode("/", $this->getDestinationDirectory());
|
|
||||||
array_shift($dirs);
|
|
||||||
if (count($dirs) > 0 && !empty($dirs[0])) {
|
|
||||||
foreach ($dirs as $dir) {
|
|
||||||
if (empty($dir)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!@ftp_chdir($this->ftp_conn, $dir)) {
|
|
||||||
ftp_mkdir($this->ftp_conn, $dir);
|
|
||||||
ftp_chmod($this->ftp_conn, 0700, $dir);
|
|
||||||
ftp_chdir($this->ftp_conn, $dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return ftp_chdir($this->ftp_conn, "/");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Froxlor\Backup\Storages;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use Froxlor\FileDir;
|
|
||||||
|
|
||||||
class Local extends Storage
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function init(): bool
|
|
||||||
{
|
|
||||||
// create destination_path
|
|
||||||
if (!file_exists($this->getDestinationDirectory())) {
|
|
||||||
return mkdir($this->getDestinationDirectory(), 0700, true);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Move/Upload file from tmp-source-directory. The file should be moved or deleted afterward.
|
|
||||||
* Must return the (relative) path including filename to the backup.
|
|
||||||
*
|
|
||||||
* @param string $filename
|
|
||||||
* @param string $tmp_source_directory
|
|
||||||
* @return string
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
protected function putFile(string $filename, string $tmp_source_directory): string
|
|
||||||
{
|
|
||||||
$source = FileDir::makeCorrectFile($tmp_source_directory . "/" . $filename);
|
|
||||||
$target = FileDir::makeCorrectFile($this->getDestinationDirectory() . "/" . $filename);
|
|
||||||
if (file_exists($source) && !file_exists($target)) {
|
|
||||||
rename($source, $target);
|
|
||||||
return $target;
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $filename
|
|
||||||
* @return bool
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
protected function rmFile(string $filename): bool
|
|
||||||
{
|
|
||||||
$target = FileDir::makeCorrectFile($this->getDestinationDirectory() . "/" . $filename);
|
|
||||||
if (file_exists($target)) {
|
|
||||||
return @unlink($target);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function shutdown(): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Froxlor\Backup\Storages;
|
|
||||||
|
|
||||||
class Rsync extends Storage
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function init(): bool
|
|
||||||
{
|
|
||||||
// TODO: Implement init() method.
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Move/Upload file from tmp-source-directory. The file should be moved or deleted afterward.
|
|
||||||
* Must return the (relative) path including filename to the backup.
|
|
||||||
*
|
|
||||||
* @param string $filename
|
|
||||||
* @param string $tmp_source_directory
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected function putFile(string $filename, string $tmp_source_directory): string
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $filename
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
protected function rmFile(string $filename): bool
|
|
||||||
{
|
|
||||||
// TODO: Implement removeOld() method.
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function shutdown(): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Froxlor\Backup\Storages;
|
|
||||||
|
|
||||||
use Aws\S3\S3Client;
|
|
||||||
use Froxlor\FileDir;
|
|
||||||
|
|
||||||
class S3 extends Storage
|
|
||||||
{
|
|
||||||
|
|
||||||
private S3Client $s3_client;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function init(): bool
|
|
||||||
{
|
|
||||||
$raw_credentials = [
|
|
||||||
'credentials' => [
|
|
||||||
'key' => $this->sData['storage']['username'] ?? '',
|
|
||||||
'secret' => $this->sData['storage']['password'] ?? ''
|
|
||||||
],
|
|
||||||
'endpoint' => $this->sData['storage']['hostname'] ?? '',
|
|
||||||
'region' => $this->sData['storage']['region'] ?? '',
|
|
||||||
'version' => 'latest',
|
|
||||||
'use_path_style_endpoint' => true
|
|
||||||
];
|
|
||||||
$this->s3_client = new S3Client($raw_credentials);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Move/Upload file from tmp-source-directory. The file should be moved or deleted afterward.
|
|
||||||
* Must return the (relative) path including filename to the backup.
|
|
||||||
*
|
|
||||||
* @param string $filename
|
|
||||||
* @param string $tmp_source_directory
|
|
||||||
* @return string
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
protected function putFile(string $filename, string $tmp_source_directory): string
|
|
||||||
{
|
|
||||||
$source = FileDir::makeCorrectFile($tmp_source_directory . "/" . $filename);
|
|
||||||
$target = FileDir::makeCorrectFile($this->getDestinationDirectory() . "/" . $filename);
|
|
||||||
$this->s3_client->putObject([
|
|
||||||
'Bucket' => $this->sData['storage']['bucket'],
|
|
||||||
'Key' => $target,
|
|
||||||
'SourceFile' => $source,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $filename
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
protected function rmFile(string $filename): bool
|
|
||||||
{
|
|
||||||
$result = $this->s3_client->deleteObject([
|
|
||||||
'Bucket' => $this->sData['storage']['bucket'],
|
|
||||||
'Key' => $filename,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if ($result['DeleteMarker']) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function shutdown(): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Froxlor\Backup\Storages;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use Froxlor\FileDir;
|
|
||||||
use phpseclib3\Net\SFTP as secSFTP;
|
|
||||||
|
|
||||||
class Sftp extends Storage
|
|
||||||
{
|
|
||||||
private secSFTP $sftp_client;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function init(): bool
|
|
||||||
{
|
|
||||||
$hostname = $this->sData['storage']['hostname'] ?? '';
|
|
||||||
$username = $this->sData['storage']['username'] ?? '';
|
|
||||||
$password = $this->sData['storage']['password'] ?? '';
|
|
||||||
if (!empty($hostname) && !empty($username) && !empty($password)) {
|
|
||||||
$tmp = explode(":", $hostname);
|
|
||||||
$hostname = $tmp[0];
|
|
||||||
$port = $tmp[1] ?? 22;
|
|
||||||
$this->sftp_client = new secSFTP($hostname, $port);
|
|
||||||
if ($this->sftp_client->isConnected()) {
|
|
||||||
// @todo login by either user/passwd or user/ssh-key
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
throw new Exception('Empty hostname for FTP backup storage');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Move/Upload file from tmp-source-directory. The file should be moved or deleted afterward.
|
|
||||||
* Must return the (relative) path including filename to the backup.
|
|
||||||
*
|
|
||||||
* @param string $filename
|
|
||||||
* @param string $tmp_source_directory
|
|
||||||
* @return string
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
protected function putFile(string $filename, string $tmp_source_directory): string
|
|
||||||
{
|
|
||||||
$source = FileDir::makeCorrectFile($tmp_source_directory . "/" . $filename);
|
|
||||||
$target = FileDir::makeCorrectFile($this->getDestinationDirectory() . "/" . $filename);
|
|
||||||
$this->sftp_client->put($target, $source, secSFTP::SOURCE_LOCAL_FILE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $filename
|
|
||||||
* @return bool
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
protected function rmFile(string $filename): bool
|
|
||||||
{
|
|
||||||
$target = FileDir::makeCorrectFile($this->getDestinationDirectory() . "/" . $filename);
|
|
||||||
if ($this->sftp_client->file_exists($target)) {
|
|
||||||
return $this->sftp_client->delete($target);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function shutdown(): bool
|
|
||||||
{
|
|
||||||
$this->sftp_client->disconnect();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,283 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Froxlor\Backup\Storages;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use Froxlor\Database\Database;
|
|
||||||
use Froxlor\FileDir;
|
|
||||||
use Froxlor\Settings;
|
|
||||||
|
|
||||||
abstract class Storage
|
|
||||||
{
|
|
||||||
private string $tmpDirectory;
|
|
||||||
protected array $sData;
|
|
||||||
|
|
||||||
protected array $filesToStore;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function __construct(array $storage_data)
|
|
||||||
{
|
|
||||||
$this->sData = $storage_data;
|
|
||||||
$tmpDirectory = Settings::Get('backup.backup_tmp_dir') ?: sys_get_temp_dir();
|
|
||||||
$this->tmpDirectory = FileDir::makeCorrectDir($tmpDirectory . '/backup-' . $this->sData['loginname']);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate sData, open connection to target storage, etc.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
abstract public function init(): bool;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disconnect / clean up connection if needed
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
abstract public function shutdown(): bool;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* prepare files to back up (e.g. create archive or similar) and fill $filesToStore
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function prepareFiles(): void
|
|
||||||
{
|
|
||||||
$this->filesToStore = [];
|
|
||||||
|
|
||||||
$tmpdir = FileDir::makeCorrectDir($this->tmpDirectory . '/.tmp/');
|
|
||||||
FileDir::safe_exec('mkdir -p ' . escapeshellarg($tmpdir));
|
|
||||||
|
|
||||||
// create archive of web, mail and database data
|
|
||||||
$this->prepareWebData();
|
|
||||||
$this->prepareDatabaseData();
|
|
||||||
$this->prepareMailData();
|
|
||||||
|
|
||||||
// create json-info-file
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
private function prepareWebData(): void
|
|
||||||
{
|
|
||||||
$tmpdir = FileDir::makeCorrectDir($this->tmpDirectory . '/.tmp/web');
|
|
||||||
FileDir::safe_exec('mkdir -p ' . escapeshellarg($tmpdir));
|
|
||||||
FileDir::safe_exec('tar cfz ' . escapeshellarg(FileDir::makeCorrectFile($tmpdir . '/' . $this->sData['loginname'] . '-web.tar.gz')) . ' -C ' . escapeshellarg($this->sData['documentroot']) . ' .');
|
|
||||||
$this->filesToStore[] = FileDir::makeCorrectFile($tmpdir . '/' . $this->sData['loginname'] . '-web.tar.gz');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
private function prepareDatabaseData(): void
|
|
||||||
{
|
|
||||||
$tmpdir = FileDir::makeCorrectDir($this->tmpDirectory . '/.tmp/mysql');
|
|
||||||
FileDir::safe_exec('mkdir -p ' . escapeshellarg($tmpdir));
|
|
||||||
|
|
||||||
// get all customer database-names
|
|
||||||
$sel_stmt = Database::prepare("
|
|
||||||
SELECT `databasename`, `dbserver` FROM `" . TABLE_PANEL_DATABASES . "`
|
|
||||||
WHERE `customerid` = :cid ORDER BY `dbserver`
|
|
||||||
");
|
|
||||||
Database::pexecute($sel_stmt, [
|
|
||||||
'cid' => $this->sData['customerid']
|
|
||||||
]);
|
|
||||||
|
|
||||||
$has_dbs = false;
|
|
||||||
$current_dbserver = -1;
|
|
||||||
while ($row = $sel_stmt->fetch()) {
|
|
||||||
// Get sql_root data for the specific database-server the database resides on
|
|
||||||
if ($current_dbserver != $row['dbserver']) {
|
|
||||||
Database::needRoot(true, $row['dbserver']);
|
|
||||||
Database::needSqlData();
|
|
||||||
$sql_root = Database::getSqlData();
|
|
||||||
Database::needRoot(false);
|
|
||||||
// create temporary mysql-defaults file for the connection-credentials/details
|
|
||||||
$mysqlcnf_file = tempnam("/tmp", "frx");
|
|
||||||
$mysqlcnf = "[mysqldump]\npassword=" . $sql_root['passwd'] . "\nhost=" . $sql_root['host'] . "\n";
|
|
||||||
if (!empty($sql_root['port'])) {
|
|
||||||
$mysqlcnf .= "port=" . $sql_root['port'] . "\n";
|
|
||||||
} elseif (!empty($sql_root['socket'])) {
|
|
||||||
$mysqlcnf .= "socket=" . $sql_root['socket'] . "\n";
|
|
||||||
}
|
|
||||||
file_put_contents($mysqlcnf_file, $mysqlcnf);
|
|
||||||
}
|
|
||||||
$bool_false = false;
|
|
||||||
FileDir::safe_exec('mysqldump --defaults-file=' . escapeshellarg($mysqlcnf_file) . ' -u ' . escapeshellarg($sql_root['user']) . ' ' . $row['databasename'] . ' > ' . FileDir::makeCorrectFile($tmpdir . '/' . $row['databasename'] . '_' . date('YmdHi', time()) . '.sql'), $bool_false, [
|
|
||||||
'>'
|
|
||||||
]);
|
|
||||||
$has_dbs = true;
|
|
||||||
$current_dbserver = $row['dbserver'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($has_dbs) {
|
|
||||||
$this->filesToStore[] = $tmpdir;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (@file_exists($mysqlcnf_file)) {
|
|
||||||
@unlink($mysqlcnf_file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function prepareMailData(): void
|
|
||||||
{
|
|
||||||
$tmpdir = FileDir::makeCorrectDir($this->tmpDirectory . '/.tmp/mail');
|
|
||||||
FileDir::safe_exec('mkdir -p ' . escapeshellarg($tmpdir));
|
|
||||||
|
|
||||||
// get all customer mail-accounts
|
|
||||||
$sel_stmt = Database::prepare("
|
|
||||||
SELECT `homedir`, `maildir` FROM `" . TABLE_MAIL_USERS . "`
|
|
||||||
WHERE `customerid` = :cid
|
|
||||||
");
|
|
||||||
Database::pexecute($sel_stmt, [
|
|
||||||
'cid' => $this->sData['customerid']
|
|
||||||
]);
|
|
||||||
|
|
||||||
$tar_file_list = "";
|
|
||||||
$mail_homedir = "";
|
|
||||||
while ($row = $sel_stmt->fetch()) {
|
|
||||||
$tar_file_list .= escapeshellarg("./" . $row['maildir']) . " ";
|
|
||||||
if (empty($mail_homedir)) {
|
|
||||||
// this should be equal for all entries
|
|
||||||
$mail_homedir = $row['homedir'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($tar_file_list)) {
|
|
||||||
FileDir::safe_exec('tar cfz ' . escapeshellarg(FileDir::makeCorrectFile($tmpdir . '/' . $this->sData['loginname'] . '-mail.tar.gz')) . ' -C ' . escapeshellarg($mail_homedir) . ' ' . trim($tar_file_list));
|
|
||||||
$this->filesToStore[] = FileDir::makeCorrectFile($tmpdir . '/' . $this->sData['loginname'] . '-mail.tar.gz');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Move/Upload file from tmp-source-directory. The file should be moved or deleted afterward.
|
|
||||||
* Must return the (relative) path including filename to the backup.
|
|
||||||
*
|
|
||||||
* @param string $filename
|
|
||||||
* @param string $tmp_source_directory
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
abstract protected function putFile(string $filename, string $tmp_source_directory): string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $filename
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
abstract protected function rmFile(string $filename): bool;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function removeOld(): bool
|
|
||||||
{
|
|
||||||
// retention in days
|
|
||||||
$retention = $this->sData['storage']['retention'] ?? 3;
|
|
||||||
// keep date
|
|
||||||
$keepDate = new \DateTime();
|
|
||||||
$keepDate->setTime(0, 0, 0, 1);
|
|
||||||
// subtract retention days
|
|
||||||
$keepDate->sub(new \DateInterval('P' . $retention . 'D'));
|
|
||||||
// select target backups to remove for this storage-id and customer
|
|
||||||
$sel_stmt = Database::prepare("
|
|
||||||
SELECT * FROM `" . TABLE_PANEL_BACKUPS . "`
|
|
||||||
WHERE `created_at` < :keepdate
|
|
||||||
AND `storage_id` = :sid
|
|
||||||
AND `customerid` = :cid
|
|
||||||
");
|
|
||||||
Database::pexecute($sel_stmt, [
|
|
||||||
'keepdate' => $keepDate->format('U'),
|
|
||||||
'sid' => $this->sData['backup'],
|
|
||||||
'cid' => $this->sData['customerid']
|
|
||||||
]);
|
|
||||||
while ($oldBackup = $sel_stmt->fetch(\PDO::FETCH_ASSOC)) {
|
|
||||||
$this->rmFile($oldBackup['filename']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the storage configured destination path for all backups
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function getDestinationDirectory(): string
|
|
||||||
{
|
|
||||||
return FileDir::makeCorrectDir($this->sData['storage']['destination_path'] ?? "/");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create backup-archive/file from $filesToStore and call putFile()
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function createFromFiles(): bool
|
|
||||||
{
|
|
||||||
if (empty($this->filesToStore)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$filename = FileDir::makeCorrectFile($this->tmpDirectory . "/backup-" . $this->sData['loginname'] . "-" . date('c') . ".tar.gz");
|
|
||||||
$tmpdir = FileDir::makeCorrectDir($this->tmpDirectory . '/.tmp/');
|
|
||||||
$create_export_tar_data = implode(" ", $this->filesToStore);
|
|
||||||
FileDir::safe_exec('chown -R ' . (int)$this->sData['guid'] . ':' . (int)$this->sData['guid'] . ' ' . escapeshellarg($tmpdir));
|
|
||||||
|
|
||||||
if (!empty($data['pgp_public_key'])) {
|
|
||||||
// pack all archives in tmp-dir to one archive and encrypt it with gpg
|
|
||||||
$recipient_file = FileDir::makeCorrectFile($this->tmpDirectory . '/' . $this->sData['loginname'] . '-recipients.gpg');
|
|
||||||
file_put_contents($recipient_file, $data['pgp_public_key']);
|
|
||||||
$return_value = [];
|
|
||||||
FileDir::safe_exec('tar cfz - -C ' . escapeshellarg($tmpdir) . ' ' . trim($create_export_tar_data) . ' | gpg --encrypt --recipient-file ' . escapeshellarg($recipient_file) . ' --output ' . escapeshellarg($filename) . ' --trust-model always --batch --yes', $return_value, ['|']);
|
|
||||||
} else {
|
|
||||||
// pack all archives in tmp-dir to one archive
|
|
||||||
FileDir::safe_exec('tar cfz ' . escapeshellarg($filename) . ' -C ' . escapeshellarg($tmpdir) . ' ' . trim($create_export_tar_data));
|
|
||||||
}
|
|
||||||
|
|
||||||
// determine filesize (use stat locally here b/c files are possibly large and php's filesize() can't handle them)
|
|
||||||
$fileSizeOutput = FileDir::safe_exec('/usr/bin/stat -c "%s" ' . escapeshellarg($filename));
|
|
||||||
$fileSize = (int)array_shift($fileSizeOutput);
|
|
||||||
|
|
||||||
// add entry to database and upload/store file
|
|
||||||
|
|
||||||
FileDir::safe_exec('rm -rf ' . escapeshellarg($tmpdir));
|
|
||||||
$fileDest = $this->putFile(basename($filename), $this->tmpDirectory);
|
|
||||||
if (!empty($fileDest)) {
|
|
||||||
$this->addEntry($fileDest, $fileSize);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $filename
|
|
||||||
* @param int $fileSize
|
|
||||||
* @return void
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
private function addEntry(string $filename, int $fileSize): void
|
|
||||||
{
|
|
||||||
$ins_stmt = Database::prepare("
|
|
||||||
INSERT INTO `" . TABLE_PANEL_BACKUPS . "` SET
|
|
||||||
`adminid` = :adminid,
|
|
||||||
`customerid` = :customerid,
|
|
||||||
`loginname` = :loginname,
|
|
||||||
`size` = :size,
|
|
||||||
`storage_id` = :sid,
|
|
||||||
`filename` = :filename,
|
|
||||||
`created_at` = UNIX_TIMESTAMP()
|
|
||||||
");
|
|
||||||
Database::pexecute($ins_stmt, [
|
|
||||||
'adminid' => $this->sData['adminid'],
|
|
||||||
'customerid' => $this->sData['customerid'],
|
|
||||||
'loginname' => $this->sData['loginname'],
|
|
||||||
'size' => $fileSize,
|
|
||||||
'sid' => $this->sData['backup'],
|
|
||||||
'filename' => $filename
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Froxlor\Backup\Storages;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use Froxlor\Database\Database;
|
|
||||||
|
|
||||||
class StorageFactory
|
|
||||||
{
|
|
||||||
public static function fromType(string $type, array $storage_data): Storage
|
|
||||||
{
|
|
||||||
$type = "\\Froxlor\\Backup\\Storages\\" . ucfirst($type);
|
|
||||||
return new $type($storage_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public static function fromStorageId(int $storage_id, array $user_data): Storage
|
|
||||||
{
|
|
||||||
$storage = self::readStorageData($storage_id);
|
|
||||||
$storage_data = $user_data;
|
|
||||||
$storage_data['storage'] = $storage;
|
|
||||||
return self::fromType($storage['type'], $storage_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
private static function readStorageData(int $storage_id): array
|
|
||||||
{
|
|
||||||
$stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_BACKUP_STORAGES . "` WHERE `id` = :bid");
|
|
||||||
$storage = Database::pexecute_first($stmt, ['bid' => $storage_id]);
|
|
||||||
if (empty($storage)) {
|
|
||||||
throw new Exception("Invalid/empty backup-storage. Unable to continue");
|
|
||||||
}
|
|
||||||
return $storage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of the Froxlor project.
|
|
||||||
* Copyright (c) 2010 the Froxlor Team (see authors).
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, you can also view it online at
|
|
||||||
* https://files.froxlor.org/misc/COPYING.txt
|
|
||||||
*
|
|
||||||
* @copyright the authors
|
|
||||||
* @author Froxlor team <team@froxlor.org>
|
|
||||||
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Froxlor\Cli;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use Froxlor\Backup\Storages\StorageFactory;
|
|
||||||
use Froxlor\Database\Database;
|
|
||||||
use Froxlor\Froxlor;
|
|
||||||
use Froxlor\Settings;
|
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
|
||||||
|
|
||||||
final class BackupCommand extends CliCommand
|
|
||||||
{
|
|
||||||
|
|
||||||
protected function configure()
|
|
||||||
{
|
|
||||||
$this->setName('froxlor:backup');
|
|
||||||
$this->setDescription('Various backup actions');
|
|
||||||
$this->addOption('list', 'L', InputOption::VALUE_OPTIONAL, 'List backups (optionally pass a customer loginname to list backups of a specific user)')
|
|
||||||
->addOption('create', 'c', InputOption::VALUE_REQUIRED, 'Manually run a backup task for given customer (loginname)')
|
|
||||||
->addOption('delete', 'd', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Remove given backup by id');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function execute(InputInterface $input, OutputInterface $output)
|
|
||||||
{
|
|
||||||
$result = $this->validateRequirements($input, $output);
|
|
||||||
|
|
||||||
require Froxlor::getInstallDir() . '/lib/functions.php';
|
|
||||||
|
|
||||||
// set error-handler
|
|
||||||
@set_error_handler([
|
|
||||||
'\\Froxlor\\Api\\Api',
|
|
||||||
'phpErrHandler'
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (!Settings::Get('backup.enabled')) {
|
|
||||||
$output->writeln('<error>Backup feature not enabled.</>');
|
|
||||||
$result = self::INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($result == self::SUCCESS) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
$loginname = "";
|
|
||||||
$userinfo = [];
|
|
||||||
if ($input->hasArgument('user')) {
|
|
||||||
$loginname = $input->getArgument('user');
|
|
||||||
$userinfo = $this->getUserByName($loginname, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
$do_list = $input->getOption('list');
|
|
||||||
$do_create = $input->getOption('create');
|
|
||||||
$do_delete = $input->getOption('delete');
|
|
||||||
|
|
||||||
if ($do_list === false && $do_create === false && $do_delete === false) {
|
|
||||||
$output->writeln('<error>No option given, nothing to do.</>');
|
|
||||||
return self::INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
// list
|
|
||||||
if ($do_list !== false) {
|
|
||||||
if ($do_list === null) {
|
|
||||||
// all customers
|
|
||||||
} elseif ($do_list !== false) {
|
|
||||||
// specific customer
|
|
||||||
}
|
|
||||||
} elseif ($do_create !== false) {
|
|
||||||
$stmt = Database::prepare("SELECT
|
|
||||||
customerid,
|
|
||||||
loginname,
|
|
||||||
adminid,
|
|
||||||
backup,
|
|
||||||
guid,
|
|
||||||
documentroot
|
|
||||||
FROM `" . TABLE_PANEL_CUSTOMERS . "`
|
|
||||||
WHERE `backup` > 0 AND `loginname` = :loginname
|
|
||||||
");
|
|
||||||
$customer = Database::pexecute_first($stmt, ['loginname' => $do_create]);
|
|
||||||
|
|
||||||
if (empty($customer)) {
|
|
||||||
$output->writeln('<error>Given customer not found or customer has backup=off</>');
|
|
||||||
return self::INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
$backupStorage = StorageFactory::fromStorageId($customer['backup'], $customer);
|
|
||||||
$output->writeln("Initializing storage");
|
|
||||||
$backupStorage->init();
|
|
||||||
$output->writeln("Preparing files and folders");
|
|
||||||
$backupStorage->prepareFiles();
|
|
||||||
$output->writeln("Creating backup file");
|
|
||||||
$backupStorage->createFromFiles();
|
|
||||||
$output->writeln("Removing older backups by retention");
|
|
||||||
$backupStorage->removeOld();
|
|
||||||
$output->writeln('<info>Backup created successfully</>');
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$output->writeln('<error>' . $e->getMessage() . '</>');
|
|
||||||
$result = self::FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of the Froxlor project.
|
|
||||||
* Copyright (c) 2010 the Froxlor Team (see authors).
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, you can also view it online at
|
|
||||||
* https://files.froxlor.org/misc/COPYING.txt
|
|
||||||
*
|
|
||||||
* @copyright the authors
|
|
||||||
* @author Froxlor team <team@froxlor.org>
|
|
||||||
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Froxlor\Cron\Backup;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use Froxlor\Backup\Storages\StorageFactory;
|
|
||||||
use Froxlor\Cron\Forkable;
|
|
||||||
use Froxlor\Cron\FroxlorCron;
|
|
||||||
use Froxlor\Database\Database;
|
|
||||||
use Froxlor\FroxlorLogger;
|
|
||||||
use Froxlor\Settings;
|
|
||||||
use PDO;
|
|
||||||
|
|
||||||
class BackupCron extends FroxlorCron
|
|
||||||
{
|
|
||||||
use Forkable;
|
|
||||||
|
|
||||||
public static function run()
|
|
||||||
{
|
|
||||||
if (!Settings::Get('backup.enabled')) {
|
|
||||||
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'BackupCron: disabled - exiting');
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
$stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_BACKUP_STORAGES . "`");
|
|
||||||
Database::pexecute($stmt);
|
|
||||||
|
|
||||||
$storages = [];
|
|
||||||
while ($storage = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
|
||||||
$storages[$storage['id']] = $storage;
|
|
||||||
}
|
|
||||||
|
|
||||||
$stmt = Database::prepare("SELECT
|
|
||||||
customerid,
|
|
||||||
loginname,
|
|
||||||
adminid,
|
|
||||||
backup,
|
|
||||||
guid,
|
|
||||||
documentroot
|
|
||||||
FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `backup` > 0
|
|
||||||
");
|
|
||||||
Database::pexecute($stmt);
|
|
||||||
|
|
||||||
$customers = [];
|
|
||||||
while ($customer = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
|
||||||
$customer['storage'] = $storages[$customer['backup']];
|
|
||||||
$customers[] = $customer;
|
|
||||||
}
|
|
||||||
|
|
||||||
self::runFork([self::class, 'handle'], $customers);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
private static function handle(array $userdata)
|
|
||||||
{
|
|
||||||
echo "BackupCron: started - creating customer backup for user " . $userdata['loginname'] . "\n";
|
|
||||||
|
|
||||||
$backupStorage = StorageFactory::fromType($userdata['storage']['type'], $userdata);
|
|
||||||
// initialize storage
|
|
||||||
$backupStorage->init();
|
|
||||||
// do what is required to obtain files/archives and move/upload
|
|
||||||
$backupStorage->prepareFiles();
|
|
||||||
// upload/move to target
|
|
||||||
$backupStorage->createFromFiles();
|
|
||||||
// remove by retention
|
|
||||||
$backupStorage->removeOld();
|
|
||||||
|
|
||||||
echo "BackupCron: finished - creating customer backup for user " . $userdata['loginname'] . "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of the Froxlor project.
|
|
||||||
* Copyright (c) 2010 the Froxlor Team (see authors).
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, you can also view it online at
|
|
||||||
* https://files.froxlor.org/misc/COPYING.txt
|
|
||||||
*
|
|
||||||
* @copyright the authors
|
|
||||||
* @author Froxlor team <team@froxlor.org>
|
|
||||||
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Froxlor\UI\Callbacks;
|
|
||||||
|
|
||||||
use Froxlor\Database\Database;
|
|
||||||
use Froxlor\UI\Panel\UI;
|
|
||||||
|
|
||||||
class Backup
|
|
||||||
{
|
|
||||||
public static function backupStorageLink(array $attributes)
|
|
||||||
{
|
|
||||||
$sel_stmt = Database::prepare("SELECT `description` FROM `" . TABLE_PANEL_BACKUP_STORAGES . "` WHERE `id` = :id");
|
|
||||||
$backupstorage = Database::pexecute_first($sel_stmt, ['id' => $attributes['data']]);
|
|
||||||
if ((int)UI::getCurrentUser()['adminsession'] == 1 && UI::getCurrentUser()['change_serversettings']) {
|
|
||||||
$linker = UI::getLinker();
|
|
||||||
$result = '<a href="' . $linker->getLink([
|
|
||||||
'section' => 'backups',
|
|
||||||
'page' => 'storages',
|
|
||||||
'searchfield' => 'id',
|
|
||||||
'searchtext' => $attributes['data'],
|
|
||||||
]) . '">' . $backupstorage['description'] . '</a>';
|
|
||||||
} else {
|
|
||||||
$result = $backupstorage['description'];
|
|
||||||
}
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of the Froxlor project.
|
|
||||||
* Copyright (c) 2010 the Froxlor Team (see authors).
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, you can also view it online at
|
|
||||||
* https://files.froxlor.org/misc/COPYING.txt
|
|
||||||
*
|
|
||||||
* @copyright the authors
|
|
||||||
* @author Froxlor team <team@froxlor.org>
|
|
||||||
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
|
||||||
*/
|
|
||||||
|
|
||||||
use Froxlor\Settings;
|
|
||||||
|
|
||||||
return [
|
|
||||||
'backup_storage_add' => [
|
|
||||||
'title' => lng('backup.backup_storage.add'),
|
|
||||||
'image' => 'fa-solid fa-file-archive',
|
|
||||||
'self_overview' => ['section' => 'backups', 'page' => 'storages'],
|
|
||||||
'sections' => [
|
|
||||||
'section_a' => [
|
|
||||||
'title' => lng('backup.backup_storage.create'),
|
|
||||||
'fields' => [
|
|
||||||
'description' => [
|
|
||||||
'label' => lng('backup.backup_storage.description'),
|
|
||||||
'type' => 'text',
|
|
||||||
'maxlength' => 200,
|
|
||||||
'mandatory' => true,
|
|
||||||
],
|
|
||||||
'type' => [
|
|
||||||
'label' => lng('backup.backup_storage.type'),
|
|
||||||
'type' => 'select',
|
|
||||||
'selected' => 'local',
|
|
||||||
'select_var' => [
|
|
||||||
'local' => lng('backup.backup_storage.type_local'),
|
|
||||||
'ftp' => lng('backup.backup_storage.type_ftp'),
|
|
||||||
'sftp' => lng('backup.backup_storage.type_sftp'),
|
|
||||||
'rsync' => lng('backup.backup_storage.type_rsync'),
|
|
||||||
's3' => lng('backup.backup_storage.type_s3'),
|
|
||||||
],
|
|
||||||
'mandatory' => true,
|
|
||||||
],
|
|
||||||
'region' => [
|
|
||||||
'label' => lng('backup.backup_storage.region'),
|
|
||||||
'type' => 'text'
|
|
||||||
],
|
|
||||||
'bucket' => [
|
|
||||||
'label' => lng('backup.backup_storage.bucket'),
|
|
||||||
'type' => 'text'
|
|
||||||
],
|
|
||||||
'destination_path' => [
|
|
||||||
'label' => lng('backup.backup_storage.destination_path'),
|
|
||||||
'type' => 'text',
|
|
||||||
'mandatory' => true,
|
|
||||||
],
|
|
||||||
'hostname' => [
|
|
||||||
'label' => lng('backup.backup_storage.hostname'),
|
|
||||||
'type' => 'text'
|
|
||||||
],
|
|
||||||
'username' => [
|
|
||||||
'label' => lng('backup.backup_storage.username'),
|
|
||||||
'type' => 'text'
|
|
||||||
],
|
|
||||||
'password' => [
|
|
||||||
'label' => lng('backup.backup_storage.password'),
|
|
||||||
'type' => 'textarea',
|
|
||||||
'autocomplete' => 'off',
|
|
||||||
],
|
|
||||||
'pgp_public_key' => [
|
|
||||||
'label' => lng('backup.backup_storage.pgp_public_key'),
|
|
||||||
'type' => 'textarea',
|
|
||||||
'value' => Settings::Get('backup.default_pgp_public_key')
|
|
||||||
],
|
|
||||||
'retention' => [
|
|
||||||
'label' => lng('backup.backup_storage.retention'),
|
|
||||||
'type' => 'number',
|
|
||||||
'min' => 0,
|
|
||||||
'value' => Settings::Get('backup.default_retention')
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
],
|
|
||||||
];
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of the Froxlor project.
|
|
||||||
* Copyright (c) 2010 the Froxlor Team (see authors).
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, you can also view it online at
|
|
||||||
* https://files.froxlor.org/misc/COPYING.txt
|
|
||||||
*
|
|
||||||
* @copyright the authors
|
|
||||||
* @author Froxlor team <team@froxlor.org>
|
|
||||||
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
|
||||||
*/
|
|
||||||
|
|
||||||
return [
|
|
||||||
'backup_storage_edit' => [
|
|
||||||
'title' => lng('backup.backup_storage.edit'),
|
|
||||||
'image' => 'fa-solid fa-file-archive',
|
|
||||||
'self_overview' => ['section' => 'backups', 'page' => 'storages'],
|
|
||||||
'sections' => [
|
|
||||||
'section_a' => [
|
|
||||||
'title' => lng('backup.backup_storage.edit'),
|
|
||||||
'fields' => [
|
|
||||||
'description' => [
|
|
||||||
'label' => lng('backup.backup_storage.description'),
|
|
||||||
'type' => 'text',
|
|
||||||
'value' => $result['description'],
|
|
||||||
'mandatory' => true,
|
|
||||||
],
|
|
||||||
'type' => [
|
|
||||||
'label' => lng('backup.backup_storage.type'),
|
|
||||||
'type' => 'select',
|
|
||||||
'selected' => $result['type'],
|
|
||||||
'select_var' => [
|
|
||||||
'local' => lng('backup.backup_storage.type_local'),
|
|
||||||
'ftp' => lng('backup.backup_storage.type_ftp'),
|
|
||||||
'sftp' => lng('backup.backup_storage.type_sftp'),
|
|
||||||
'rsync' => lng('backup.backup_storage.type_rsync'),
|
|
||||||
's3' => lng('backup.backup_storage.type_s3'),
|
|
||||||
],
|
|
||||||
'mandatory' => true,
|
|
||||||
],
|
|
||||||
'region' => [
|
|
||||||
'label' => lng('backup.backup_storage.region'),
|
|
||||||
'type' => 'text',
|
|
||||||
'value' => $result['region']
|
|
||||||
],
|
|
||||||
'bucket' => [
|
|
||||||
'label' => lng('backup.backup_storage.bucket'),
|
|
||||||
'type' => 'text',
|
|
||||||
'value' => $result['bucket']
|
|
||||||
],
|
|
||||||
'destination_path' => [
|
|
||||||
'label' => lng('backup.backup_storage.destination_path'),
|
|
||||||
'type' => 'text',
|
|
||||||
'value' => $result['destination_path'],
|
|
||||||
'mandatory' => true,
|
|
||||||
],
|
|
||||||
'hostname' => [
|
|
||||||
'label' => lng('backup.backup_storage.hostname'),
|
|
||||||
'type' => 'text',
|
|
||||||
'value' => $result['hostname']
|
|
||||||
],
|
|
||||||
'username' => [
|
|
||||||
'label' => lng('backup.backup_storage.username'),
|
|
||||||
'type' => 'text',
|
|
||||||
'value' => $result['username']
|
|
||||||
],
|
|
||||||
'password' => [
|
|
||||||
'label' => lng('backup.backup_storage.password.title'),
|
|
||||||
'desc' => lng('backup.backup_storage.password.description') . '<br>(' . lng('panel.emptyfornochanges') . ')',
|
|
||||||
'type' => 'textarea',
|
|
||||||
'autocomplete' => 'off'
|
|
||||||
],
|
|
||||||
'pgp_public_key' => [
|
|
||||||
'label' => lng('backup.backup_storage.pgp_public_key'),
|
|
||||||
'type' => 'textarea',
|
|
||||||
'value' => $result['pgp_public_key']
|
|
||||||
],
|
|
||||||
'retention' => [
|
|
||||||
'label' => lng('backup.backup_storage.retention'),
|
|
||||||
'type' => 'number',
|
|
||||||
'min' => 0,
|
|
||||||
'value' => $result['retention']
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
],
|
|
||||||
];
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of the Froxlor project.
|
|
||||||
* Copyright (c) 2010 the Froxlor Team (see authors).
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, you can also view it online at
|
|
||||||
* https://files.froxlor.org/misc/COPYING.txt
|
|
||||||
*
|
|
||||||
* @copyright the authors
|
|
||||||
* @author Froxlor team <team@froxlor.org>
|
|
||||||
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
|
||||||
*/
|
|
||||||
|
|
||||||
return [
|
|
||||||
'backups_add' => [
|
|
||||||
'title' => lng('backups.backups_add'),
|
|
||||||
'image' => 'fa-solid fa-file-archive',
|
|
||||||
'self_overview' => ['section' => 'backups', 'page' => 'storages'],
|
|
||||||
'sections' => []
|
|
||||||
],
|
|
||||||
];
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of the Froxlor project.
|
|
||||||
* Copyright (c) 2010 the Froxlor Team (see authors).
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, you can also view it online at
|
|
||||||
* https://files.froxlor.org/misc/COPYING.txt
|
|
||||||
*
|
|
||||||
* @copyright the authors
|
|
||||||
* @author Froxlor team <team@froxlor.org>
|
|
||||||
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
|
||||||
*/
|
|
||||||
|
|
||||||
return [
|
|
||||||
'backups_edit' => [
|
|
||||||
'title' => lng('backups.backups_edit'),
|
|
||||||
'image' => 'fa-solid fa-file-archive',
|
|
||||||
'self_overview' => ['section' => 'backups', 'page' => 'admins'],
|
|
||||||
'sections' => []
|
|
||||||
],
|
|
||||||
];
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of the Froxlor project.
|
|
||||||
* Copyright (c) 2010 the Froxlor Team (see authors).
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, you can also view it online at
|
|
||||||
* https://files.froxlor.org/misc/COPYING.txt
|
|
||||||
*
|
|
||||||
* @copyright the authors
|
|
||||||
* @author Froxlor team <team@froxlor.org>
|
|
||||||
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
|
||||||
*/
|
|
||||||
|
|
||||||
return [
|
|
||||||
'backups_restore' => [
|
|
||||||
'title' => lng('backups.backups_restore'),
|
|
||||||
'image' => 'fa-solid fa-file-archive',
|
|
||||||
'self_overview' => ['section' => 'backups', 'page' => 'storages'],
|
|
||||||
'sections' => []
|
|
||||||
],
|
|
||||||
];
|
|
||||||
@@ -316,21 +316,6 @@ return [
|
|||||||
'value' => '1',
|
'value' => '1',
|
||||||
'checked' => true
|
'checked' => true
|
||||||
],
|
],
|
||||||
'backup' => [
|
|
||||||
'label' => lng('backup.backup_storage.title'),
|
|
||||||
'desc' => lng('backup.backup_storage.description'),
|
|
||||||
'type' => 'select',
|
|
||||||
'select_var' => $backup_storages,
|
|
||||||
'selected' => Settings::Get('backup.default_storage'),
|
|
||||||
'visible' => Settings::Get('backup.enabled') == '1' && $userinfo['change_serversettings'] == '1'
|
|
||||||
],
|
|
||||||
'access_backups' => [
|
|
||||||
'label' => lng('backup.access_backups'),
|
|
||||||
'type' => 'checkbox',
|
|
||||||
'value' => '1',
|
|
||||||
'checked' => Settings::Get('backup.enabled') == '1' && Settings::Get('backup.default_customer_access'),
|
|
||||||
'visible' => Settings::Get('backup.enabled') == '1' && ($userinfo['change_serversettings'] == '1' || Settings::Get('backup.default_customer_access'))
|
|
||||||
],
|
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -324,21 +324,6 @@ return [
|
|||||||
'value' => '1',
|
'value' => '1',
|
||||||
'checked' => $result['logviewenabled']
|
'checked' => $result['logviewenabled']
|
||||||
],
|
],
|
||||||
'backup' => [
|
|
||||||
'label' => lng('backup.backup_storage.title'),
|
|
||||||
'desc' => lng('backup.backup_storage.description'),
|
|
||||||
'type' => 'select',
|
|
||||||
'select_var' => $backup_storages,
|
|
||||||
'selected' => $result['backup'],
|
|
||||||
'visible' => Settings::Get('backup.enabled') == '1' && $userinfo['change_serversettings'] == '1'
|
|
||||||
],
|
|
||||||
'access_backups' => [
|
|
||||||
'label' => lng('backup.access_backups'),
|
|
||||||
'type' => 'checkbox',
|
|
||||||
'value' => '1',
|
|
||||||
'checked' => $result['access_backups'],
|
|
||||||
'visible' => Settings::Get('backup.enabled') == '1' && ($userinfo['change_serversettings'] == '1' || Settings::Get('backup.default_customer_access'))
|
|
||||||
],
|
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'section_d' => [
|
'section_d' => [
|
||||||
|
|||||||
@@ -261,11 +261,6 @@ return [
|
|||||||
'label' => lng('admin.cron.cronsettings'),
|
'label' => lng('admin.cron.cronsettings'),
|
||||||
'required_resources' => 'change_serversettings'
|
'required_resources' => 'change_serversettings'
|
||||||
],
|
],
|
||||||
[
|
|
||||||
'url' => 'admin_backups.php?page=overview',
|
|
||||||
'label' => lng('admin.backups.backups'),
|
|
||||||
'show_element' => (Settings::Get('backup.enabled') == true)
|
|
||||||
],
|
|
||||||
[
|
[
|
||||||
'url' => 'admin_logger.php?page=log',
|
'url' => 'admin_logger.php?page=log',
|
||||||
'label' => lng('menue.logger.logger'),
|
'label' => lng('menue.logger.logger'),
|
||||||
|
|||||||
@@ -1,123 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of the Froxlor project.
|
|
||||||
* Copyright (c) 2010 the Froxlor Team (see authors).
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, you can also view it online at
|
|
||||||
* https://files.froxlor.org/misc/COPYING.txt
|
|
||||||
*
|
|
||||||
* @copyright the authors
|
|
||||||
* @author Froxlor team <team@froxlor.org>
|
|
||||||
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
|
||||||
*/
|
|
||||||
|
|
||||||
use Froxlor\UI\Callbacks\Admin;
|
|
||||||
use Froxlor\UI\Callbacks\Customer;
|
|
||||||
use Froxlor\UI\Callbacks\Impersonate;
|
|
||||||
use Froxlor\UI\Callbacks\PHPConf;
|
|
||||||
use Froxlor\UI\Callbacks\ProgressBar;
|
|
||||||
use Froxlor\UI\Callbacks\Style;
|
|
||||||
use Froxlor\UI\Callbacks\Text;
|
|
||||||
use Froxlor\UI\Listing;
|
|
||||||
|
|
||||||
return [
|
|
||||||
'backup_storages_list' => [
|
|
||||||
'title' => lng('backup.backup_storage.list'),
|
|
||||||
'icon' => 'fa-solid fa-file-archive',
|
|
||||||
'self_overview' => ['section' => 'backups', 'page' => 'storages'],
|
|
||||||
'default_sorting' => ['description' => 'asc'],
|
|
||||||
'columns' => [
|
|
||||||
'id' => [
|
|
||||||
'label' => 'ID',
|
|
||||||
'field' => 'id',
|
|
||||||
'sortable' => true,
|
|
||||||
],
|
|
||||||
'description' => [
|
|
||||||
'label' => lng('backup.backup_storage.description'),
|
|
||||||
'field' => 'description',
|
|
||||||
'sortable' => true,
|
|
||||||
],
|
|
||||||
'type' => [
|
|
||||||
'label' => lng('backup.backup_storage.type'),
|
|
||||||
'field' => 'type',
|
|
||||||
'sortable' => true,
|
|
||||||
],
|
|
||||||
'region' => [
|
|
||||||
'label' => lng('backup.backup_storage.region'),
|
|
||||||
'field' => 'region',
|
|
||||||
'sortable' => true,
|
|
||||||
],
|
|
||||||
'bucket' => [
|
|
||||||
'label' => lng('backup.backup_storage.bucket'),
|
|
||||||
'field' => 'bucket',
|
|
||||||
'sortable' => true,
|
|
||||||
],
|
|
||||||
'destination_path' => [
|
|
||||||
'label' => lng('backup.backup_storage.destination_path.title'),
|
|
||||||
'field' => 'destination_path',
|
|
||||||
'sortable' => true,
|
|
||||||
],
|
|
||||||
'hostname' => [
|
|
||||||
'label' => lng('backup.backup_storage.hostname'),
|
|
||||||
'field' => 'hostname',
|
|
||||||
'sortable' => true,
|
|
||||||
],
|
|
||||||
'username' => [
|
|
||||||
'label' => lng('backup.backup_storage.username'),
|
|
||||||
'field' => 'username',
|
|
||||||
'sortable' => true,
|
|
||||||
],
|
|
||||||
'retention' => [
|
|
||||||
'label' => lng('backup.backup_storage.retention'),
|
|
||||||
'field' => 'retention',
|
|
||||||
'sortable' => true,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'visible_columns' => Listing::getVisibleColumnsForListing('backup_storages_list', [
|
|
||||||
'id',
|
|
||||||
'description',
|
|
||||||
'type',
|
|
||||||
'retention',
|
|
||||||
]),
|
|
||||||
'actions' => [
|
|
||||||
'show' => [
|
|
||||||
'icon' => 'fa-solid fa-eye',
|
|
||||||
'title' => lng('usersettings.custom_notes.title'),
|
|
||||||
],
|
|
||||||
'edit' => [
|
|
||||||
'icon' => 'fa-solid fa-edit',
|
|
||||||
'title' => lng('panel.edit'),
|
|
||||||
'href' => [
|
|
||||||
'section' => 'backups',
|
|
||||||
'page' => 'storages',
|
|
||||||
'action' => 'edit',
|
|
||||||
'id' => ':id'
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'delete' => [
|
|
||||||
'icon' => 'fa-solid fa-trash',
|
|
||||||
'title' => lng('panel.delete'),
|
|
||||||
'class' => 'btn-danger',
|
|
||||||
'href' => [
|
|
||||||
'section' => 'backups',
|
|
||||||
'page' => 'storages',
|
|
||||||
'action' => 'delete',
|
|
||||||
'id' => ':id'
|
|
||||||
],
|
|
||||||
'visible' => [PHPConf::class, 'isNotDefault']
|
|
||||||
],
|
|
||||||
],
|
|
||||||
]
|
|
||||||
];
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of the Froxlor project.
|
|
||||||
* Copyright (c) 2010 the Froxlor Team (see authors).
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, you can also view it online at
|
|
||||||
* https://files.froxlor.org/misc/COPYING.txt
|
|
||||||
*
|
|
||||||
* @copyright the authors
|
|
||||||
* @author Froxlor team <team@froxlor.org>
|
|
||||||
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
|
||||||
*/
|
|
||||||
|
|
||||||
use Froxlor\UI\Callbacks\Admin;
|
|
||||||
use Froxlor\UI\Callbacks\Backup;
|
|
||||||
use Froxlor\UI\Callbacks\Customer;
|
|
||||||
use Froxlor\UI\Callbacks\Impersonate;
|
|
||||||
use Froxlor\UI\Callbacks\ProgressBar;
|
|
||||||
use Froxlor\UI\Callbacks\Style;
|
|
||||||
use Froxlor\UI\Callbacks\Text;
|
|
||||||
use Froxlor\UI\Listing;
|
|
||||||
|
|
||||||
return [
|
|
||||||
'backups_list' => [
|
|
||||||
'title' => lng('admin.backups.backups'),
|
|
||||||
'icon' => 'fa-solid fa-file-archive',
|
|
||||||
'self_overview' => ['section' => 'admins', 'page' => 'admins'],
|
|
||||||
'default_sorting' => ['loginname' => 'asc'],
|
|
||||||
'columns' => [
|
|
||||||
'id' => [
|
|
||||||
'label' => 'ID',
|
|
||||||
'field' => 'id',
|
|
||||||
'sortable' => true,
|
|
||||||
],
|
|
||||||
'customerid' => [
|
|
||||||
'label' => lng('customerid'),
|
|
||||||
'field' => 'customerid',
|
|
||||||
'sortable' => true,
|
|
||||||
],
|
|
||||||
'loginname' => [
|
|
||||||
'label' => lng('login.username'),
|
|
||||||
'field' => 'loginname',
|
|
||||||
'callback' => [Impersonate::class, 'customer'],
|
|
||||||
'sortable' => true,
|
|
||||||
],
|
|
||||||
'adminid' => [
|
|
||||||
'label' => lng('adminid'),
|
|
||||||
'field' => 'adminid',
|
|
||||||
'sortable' => true,
|
|
||||||
],
|
|
||||||
'adminname' => [
|
|
||||||
'label' => lng('admin.admin'),
|
|
||||||
'field' => 'adminname',
|
|
||||||
'callback' => [Impersonate::class, 'admin'],
|
|
||||||
],
|
|
||||||
'size' => [
|
|
||||||
'label' => lng('backup.size'),
|
|
||||||
'field' => 'size',
|
|
||||||
'sortable' => true,
|
|
||||||
'callback' => [Text::class, 'size'],
|
|
||||||
],
|
|
||||||
'storage_id' => [
|
|
||||||
'label' => lng('backup.backup_storage.title'),
|
|
||||||
'field' => 'storage_id',
|
|
||||||
'class' => 'text-center',
|
|
||||||
'callback' => [Backup::class, 'backupStorageLink'],
|
|
||||||
],
|
|
||||||
'filename' => [
|
|
||||||
'label' => lng('backup.size'),
|
|
||||||
'field' => 'filename',
|
|
||||||
'sortable' => true,
|
|
||||||
],
|
|
||||||
'created_at' => [
|
|
||||||
'label' => lng('backup.created_at'),
|
|
||||||
'field' => 'created_at',
|
|
||||||
'sortable' => true,
|
|
||||||
'callback' => [Text::class, 'timestamp'],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'visible_columns' => Listing::getVisibleColumnsForListing('backups_list', [
|
|
||||||
'id',
|
|
||||||
'adminname',
|
|
||||||
'loginname',
|
|
||||||
'size',
|
|
||||||
'created_at',
|
|
||||||
]),
|
|
||||||
'actions' => [
|
|
||||||
'delete' => [
|
|
||||||
'icon' => 'fa-solid fa-trash',
|
|
||||||
'title' => lng('panel.delete'),
|
|
||||||
'class' => 'btn-danger',
|
|
||||||
'href' => [
|
|
||||||
'section' => 'backups',
|
|
||||||
'page' => 'storages',
|
|
||||||
'action' => 'delete',
|
|
||||||
'id' => ':id'
|
|
||||||
],
|
|
||||||
'visible' => [Admin::class, 'canChangeServerSettings'],
|
|
||||||
],
|
|
||||||
]
|
|
||||||
]
|
|
||||||
];
|
|
||||||
@@ -23,13 +23,11 @@
|
|||||||
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use Froxlor\Settings;
|
|
||||||
use Froxlor\UI\Callbacks\Backup;
|
|
||||||
use Froxlor\UI\Callbacks\Customer;
|
use Froxlor\UI\Callbacks\Customer;
|
||||||
use Froxlor\UI\Callbacks\Impersonate;
|
use Froxlor\UI\Callbacks\Impersonate;
|
||||||
use Froxlor\UI\Callbacks\ProgressBar;
|
use Froxlor\UI\Callbacks\ProgressBar;
|
||||||
use Froxlor\UI\Callbacks\Text;
|
|
||||||
use Froxlor\UI\Callbacks\Style;
|
use Froxlor\UI\Callbacks\Style;
|
||||||
|
use Froxlor\UI\Callbacks\Text;
|
||||||
use Froxlor\UI\Listing;
|
use Froxlor\UI\Listing;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@@ -151,20 +149,6 @@ return [
|
|||||||
'class' => 'text-center',
|
'class' => 'text-center',
|
||||||
'callback' => [Text::class, 'boolean'],
|
'callback' => [Text::class, 'boolean'],
|
||||||
],
|
],
|
||||||
'c.backup' => [
|
|
||||||
'label' => lng('backup.backup_storage.title'),
|
|
||||||
'field' => 'backup',
|
|
||||||
'class' => 'text-center',
|
|
||||||
'callback' => [Backup::class, 'backupStorageLink'],
|
|
||||||
'visible' => (bool)Settings::Get('backup.enabled'),
|
|
||||||
],
|
|
||||||
'c.access_backups' => [
|
|
||||||
'label' => lng('backup.access_backups'),
|
|
||||||
'field' => 'access_backups',
|
|
||||||
'class' => 'text-center',
|
|
||||||
'callback' => [Text::class, 'boolean'],
|
|
||||||
'visible' => (bool)Settings::Get('backup.enabled'),
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
'visible_columns' => Listing::getVisibleColumnsForListing('customer_list', [
|
'visible_columns' => Listing::getVisibleColumnsForListing('customer_list', [
|
||||||
'c.name',
|
'c.name',
|
||||||
|
|||||||
@@ -32,8 +32,6 @@ const TABLE_MAIL_USERS = 'mail_users';
|
|||||||
const TABLE_MAIL_VIRTUAL = 'mail_virtual';
|
const TABLE_MAIL_VIRTUAL = 'mail_virtual';
|
||||||
const TABLE_PANEL_ACTIVATION = 'panel_activation';
|
const TABLE_PANEL_ACTIVATION = 'panel_activation';
|
||||||
const TABLE_PANEL_ADMINS = 'panel_admins';
|
const TABLE_PANEL_ADMINS = 'panel_admins';
|
||||||
const TABLE_PANEL_BACKUPS = 'panel_backups';
|
|
||||||
const TABLE_PANEL_BACKUP_STORAGES = 'panel_backup_storages';
|
|
||||||
const TABLE_PANEL_CUSTOMERS = 'panel_customers';
|
const TABLE_PANEL_CUSTOMERS = 'panel_customers';
|
||||||
const TABLE_PANEL_DATABASES = 'panel_databases';
|
const TABLE_PANEL_DATABASES = 'panel_databases';
|
||||||
const TABLE_PANEL_DOMAINS = 'panel_domains';
|
const TABLE_PANEL_DOMAINS = 'panel_domains';
|
||||||
|
|||||||
@@ -2296,39 +2296,4 @@ Vielen Dank, Ihr Administrator',
|
|||||||
'config_note' => 'Damit froxlor mit dem Backend vernünftig kommunizieren kann, musst du dieses noch konfigurieren.',
|
'config_note' => 'Damit froxlor mit dem Backend vernünftig kommunizieren kann, musst du dieses noch konfigurieren.',
|
||||||
'config_now' => 'Jetzt konfigurieren'
|
'config_now' => 'Jetzt konfigurieren'
|
||||||
],
|
],
|
||||||
'backup' => [
|
|
||||||
'backup' => 'Backup',
|
|
||||||
'backups' => 'Backups',
|
|
||||||
'backups_restore' => 'Wiederherstellen',
|
|
||||||
'backup_storages' => 'Speichermedien verwalten',
|
|
||||||
'size' => 'Größe',
|
|
||||||
'created_at' => 'Erstellt',
|
|
||||||
'backup_storage' => [
|
|
||||||
'add' => 'Speichermedium hinzufügen',
|
|
||||||
'create' => 'Neues Speichermedium erstellen',
|
|
||||||
'edit' => 'Speichermedien bearbeiten',
|
|
||||||
'list' => 'Speichermedien',
|
|
||||||
'description' => 'Beschreibung',
|
|
||||||
'type' => 'Typ',
|
|
||||||
'type_local' => 'Lokales Dateisystem',
|
|
||||||
'type_ftp' => 'FTP Server',
|
|
||||||
'type_sftp' => 'SFTP auf entfernten Server',
|
|
||||||
'type_rsync' => 'Rsync zu entfernten Server',
|
|
||||||
'type_s3' => 'S3 Server',
|
|
||||||
'region' => 'S3 Region',
|
|
||||||
'bucket' => 'S3 Bucket',
|
|
||||||
'destination_path' => [
|
|
||||||
'title' => 'Ziel-Pfad',
|
|
||||||
'description' => 'Absoluter Pfad, wenn Speicher-Typ ist "lokal", sonst relativ zum Heimatverzeichnisses des Benutzers',
|
|
||||||
],
|
|
||||||
'hostname' => 'Ziel Hostname',
|
|
||||||
'username' => 'Benutzername',
|
|
||||||
'password' => [
|
|
||||||
'title' => 'Passwort oder SSH Public-key',
|
|
||||||
'description' => 'Es kann entweder ein Passwort oder ein vollständiger SSH Public-Key angegeben werden'
|
|
||||||
],
|
|
||||||
'pgp_public_key' => 'PGP Public-key',
|
|
||||||
'retention' => 'Backup-Aufbewahrung in Tagen',
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -2430,39 +2430,4 @@ Yours sincerely, your administrator',
|
|||||||
'config_note' => 'In order for froxlor to be able to communicate properly with the backend, you have to configure it.',
|
'config_note' => 'In order for froxlor to be able to communicate properly with the backend, you have to configure it.',
|
||||||
'config_now' => 'Configure now'
|
'config_now' => 'Configure now'
|
||||||
],
|
],
|
||||||
'backup' => [
|
|
||||||
'backup' => 'Backup',
|
|
||||||
'backups' => 'Backups',
|
|
||||||
'backups_restore' => 'Restore backups',
|
|
||||||
'backup_storages' => 'Configure storages',
|
|
||||||
'size' => 'Size',
|
|
||||||
'created_at' => 'Created at',
|
|
||||||
'backup_storage' => [
|
|
||||||
'add' => 'Add backup storage',
|
|
||||||
'create' => 'Create new backup storage',
|
|
||||||
'edit' => 'Edit backup storage',
|
|
||||||
'list' => 'Backup storages',
|
|
||||||
'description' => 'Storage description',
|
|
||||||
'type' => 'Storage type',
|
|
||||||
'type_local' => 'local filesystem',
|
|
||||||
'type_ftp' => 'FTP server',
|
|
||||||
'type_sftp' => 'SFTP to remote server',
|
|
||||||
'type_rsync' => 'Rsync to remote server',
|
|
||||||
'type_s3' => 'S3 server',
|
|
||||||
'region' => 'S3 region',
|
|
||||||
'bucket' => 'S3 bucket',
|
|
||||||
'destination_path' => [
|
|
||||||
'title' => 'Destination path',
|
|
||||||
'description' => 'Absolute path if storage type is "local", else relative to home-directory of given user',
|
|
||||||
],
|
|
||||||
'hostname' => 'Target hostname',
|
|
||||||
'username' => 'Username',
|
|
||||||
'password' => [
|
|
||||||
'title' => 'Password or ssh public-key',
|
|
||||||
'description' => 'Can be either a password or a complete SSH public key'
|
|
||||||
],
|
|
||||||
'pgp_public_key' => 'PGP public key',
|
|
||||||
'retention' => 'Backup retention in days',
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user