diff --git a/actions/admin/settings/230.backup.php b/actions/admin/settings/230.backup.php new file mode 100644 index 00000000..87a211c7 --- /dev/null +++ b/actions/admin/settings/230.backup.php @@ -0,0 +1,124 @@ + + * @license https://files.froxlor.org/misc/COPYING.txt GPLv2 + */ + +return [ + 'groups' => [ + 'backup' => [ + 'title' => lng('backup'), + 'icon' => 'fa-solid fa-sliders', + 'advanced_mode' => true, + 'fields' => [ + 'system_backup_enabled' => [ + 'label' => lng('serversettings.backup_enabled'), + 'settinggroup' => 'system', + 'varname' => 'diskquota_enabled', + 'type' => 'checkbox', + 'default' => false, + 'save_method' => 'storeSettingField', + 'overview_option' => true + ], + 'system_backup_type' => [ + 'label' => lng('serversettings.backup_type'), + 'settinggroup' => 'system', + 'varname' => 'backup_type', + 'type' => 'select', + 'default' => 'S3', + 'select_var' => [ + 'Local' => lng('serversettings.local'), + 'SFTP' => lng('serversettings.sftp'), + 'FTPS' => lng('serversettings.ftps'), + 'S3' => lng('serversettings.s3'), + ], + 'save_method' => 'storeSettingField', + 'overview_option' => true, + ], + 'system_backup_region' => [ + 'label' => lng('serversettings.backup_region'), + 'settinggroup' => 'system', + 'varname' => 'backup_region', + 'type' => 'text', + 'default' => 'eu-central-1', + 'save_method' => 'storeSettingField', + ], + 'system_backup_bucket' => [ + 'label' => lng('serversettings.backup_bucket'), + 'settinggroup' => 'system', + 'varname' => 'backup_bucket', + 'type' => 'text', + 'default' => '', + 'save_method' => 'storeSettingField', + ], + 'system_backup_destination_path' => [ + 'label' => lng('serversettings.backup_destination_path'), + 'settinggroup' => 'system', + 'varname' => 'backup_destination_path', + 'type' => 'text', + 'default' => 'backups', + 'save_method' => 'storeSettingField', + ], + 'system_backup_hostname' => [ + 'label' => lng('serversettings.backup_hostname'), + 'settinggroup' => 'system', + 'varname' => 'backup_hostname', + 'type' => 'text', + 'default' => '', + 'save_method' => 'storeSettingField', + ], + 'system_backup_username' => [ + 'label' => lng('serversettings.backup_username'), + 'settinggroup' => 'system', + 'varname' => 'backup_username', + 'type' => 'text', + 'default' => '', + 'save_method' => 'storeSettingField', + ], + 'system_backup_password' => [ + 'label' => lng('serversettings.backup_password'), + 'settinggroup' => 'system', + 'varname' => 'backup_password', + 'type' => 'password', + 'default' => '', + 'save_method' => 'storeSettingField', + ], + 'system_backup_pgp_public_key' => [ + 'label' => lng('serversettings.backup_pgp_public_key'), + 'settinggroup' => 'system', + 'varname' => 'backup_pgp_public_key', + 'type' => 'textarea', + 'default' => '', + 'save_method' => 'storeSettingField', + ], + 'system_backup_retention' => [ + 'label' => lng('serversettings.backup_retention'), + 'settinggroup' => 'system', + 'varname' => 'backup_retention', + 'type' => 'number', + 'default' => 3, + 'save_method' => 'storeSettingField', + ], + ] + ] + ] +]; diff --git a/admin_backups.php b/admin_backups.php new file mode 100644 index 00000000..d34933f0 --- /dev/null +++ b/admin_backups.php @@ -0,0 +1,75 @@ + + * @license https://files.froxlor.org/misc/COPYING.txt GPLv2 + */ + +const AREA = 'admin'; +require __DIR__ . '/lib/init.php'; + +use Froxlor\Api\Commands\Backups; +use Froxlor\FroxlorLogger; +use Froxlor\UI\Collection; +use Froxlor\UI\Listing; +use Froxlor\UI\Panel\UI; +use Froxlor\UI\Request; +use Froxlor\UI\Response; + +$id = (int)Request::any('id'); + +if (($page == 'admins' || $page == 'overview') && $userinfo['change_serversettings'] == '1') { + if ($action == '') { + $log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "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' => 'add']), + 'label' => lng('admin.backups_add') + ], + [ + 'href' => $linker->getLink(['section' => 'backups', 'page' => $page, 'action' => 'restore']), + 'label' => lng('admin.backups_restore'), + 'icon' => 'fa-solid fa-file-import', + 'class' => 'btn-outline-secondary' + ] + ] + ]); + } elseif ($action == 'delete' && $id != 0) { + + } elseif ($action == 'add') { + + } elseif ($action == 'edit' && $id != 0) { + + } elseif ($action == 'restore') { + + } +} diff --git a/lib/Froxlor/Api/Commands/Backups.php b/lib/Froxlor/Api/Commands/Backups.php new file mode 100644 index 00000000..638b4912 --- /dev/null +++ b/lib/Froxlor/Api/Commands/Backups.php @@ -0,0 +1,201 @@ + + * @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 Froxlor\Idna\IdnaWrapper; +use Froxlor\Settings; +use Froxlor\System\Crypt; +use Froxlor\UI\Response; +use Froxlor\User; +use Froxlor\Validate\Validate; +use PDO; + +/** + * @since 2.1.0 + */ +class Backups extends ApiCommand implements ResourceEntity +{ + + /** + * increase resource-usage + * + * @param int $adminid + * @param string $resource + * @param string $extra + * optional, default empty + * @param int $increase_by + * optional, default 1 + */ + public static function increaseUsage($adminid = 0, $resource = null, $extra = '', $increase_by = 1) + { + self::updateResourceUsage(TABLE_PANEL_BACKUPS, 'adminid', $adminid, '+', $resource, $extra, $increase_by); + } + + /** + * decrease resource-usage + * + * @param int $adminid + * @param string $resource + * @param string $extra + * optional, default empty + * @param int $decrease_by + * optional, default 1 + */ + public static function decreaseUsage($adminid = 0, $resource = null, $extra = '', $decrease_by = 1) + { + self::updateResourceUsage(TABLE_PANEL_BACKUPS, 'adminid', $adminid, '-', $resource, $extra, $decrease_by); + } + + /** + * 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() && $this->getUserDetail('change_serversettings') == 1) { + $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[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`) + "); + 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 backups for the given admin + * + * @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_backups + FROM `" . TABLE_PANEL_BACKUPS . "` + "); + $result = Database::pexecute_first($result_stmt, null, true, true); + if ($result) { + return $this->response($result['num_backups']); + } + $this->response(0); + } + throw new Exception("Not allowed to execute given command.", 403); + } + + /** + * create a new admin user + * + * @param string $name + * + * @access admin + * @return string json-encoded array + * @throws Exception + */ + public function add() + { + throw new Exception("Not allowed to execute given command.", 403); + } + + /** + * return an admin entry by either id or loginname + * + * @param int $id + * optional, the admin-id + * @param string $loginname + * optional, the loginname + * + * @access admin + * @return string json-encoded array + * @throws Exception + */ + public function get() + { + throw new Exception("Not allowed to execute given command.", 403); + } + + /** + * update an admin user by given id or loginname + * + * @param int $id + * required, the admin-id + * + * @access admin + * @return string json-encoded array + * @throws Exception + */ + public function update() + { + throw new Exception("Not allowed to execute given command.", 403); + } + + /** + * delete a admin entry by either id or loginname + * + * @param int $id + * required, the admin-id + * + * @access admin + * @return string json-encoded array + * @throws Exception + */ + public function delete() + { + throw new Exception("Not allowed to execute given command.", 403); + } +} diff --git a/lib/formfields/admin/backups/formfield.backups_add.php b/lib/formfields/admin/backups/formfield.backups_add.php new file mode 100644 index 00000000..2b2546ab --- /dev/null +++ b/lib/formfields/admin/backups/formfield.backups_add.php @@ -0,0 +1,33 @@ + + * @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' => 'backups'], + 'sections' => [] + ], +]; diff --git a/lib/formfields/admin/backups/formfield.backups_edit.php b/lib/formfields/admin/backups/formfield.backups_edit.php new file mode 100644 index 00000000..dea4d84f --- /dev/null +++ b/lib/formfields/admin/backups/formfield.backups_edit.php @@ -0,0 +1,33 @@ + + * @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' => [] + ], +]; diff --git a/lib/formfields/admin/backups/formfield.backups_restore.php b/lib/formfields/admin/backups/formfield.backups_restore.php new file mode 100644 index 00000000..f146bdec --- /dev/null +++ b/lib/formfields/admin/backups/formfield.backups_restore.php @@ -0,0 +1,33 @@ + + * @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' => 'backups'], + 'sections' => [] + ], +]; diff --git a/lib/formfields/admin/backups/index.html b/lib/formfields/admin/backups/index.html new file mode 100644 index 00000000..e69de29b diff --git a/lib/navigation/00.froxlor.main.php b/lib/navigation/00.froxlor.main.php index d65819fb..b01e5f43 100644 --- a/lib/navigation/00.froxlor.main.php +++ b/lib/navigation/00.froxlor.main.php @@ -223,6 +223,12 @@ return [ 'required_resources' => 'customers', 'add_shortlink' => 'admin_plans.php?page=overview&action=add' ], + [ + 'url' => 'admin_backups.php?page=overview', + 'label' => lng('admin.backups.backups'), + 'required_resources' => 'change_serversettings', + 'add_shortlink' => 'admin_backups.php?page=overview&action=add' + ], [ 'url' => 'admin_settings.php?page=updatecounters', 'label' => lng('admin.updatecounters'), diff --git a/lib/tablelisting/admin/tablelisting.backups.php b/lib/tablelisting/admin/tablelisting.backups.php new file mode 100644 index 00000000..b5b2b20e --- /dev/null +++ b/lib/tablelisting/admin/tablelisting.backups.php @@ -0,0 +1,122 @@ + + * @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\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'], + 'sortable' => true, + ], + 'size' => [ + 'label' => lng('backup.size'), + 'field' => 'size', + 'sortable' => true, + ], + 'created_at' => [ + 'label' => lng('backup.created_at'), + 'field' => 'created_at', + 'sortable' => true, + ], + ], + 'visible_columns' => Listing::getVisibleColumnsForListing('admin_list', [ + 'id', + 'adminname', + 'loginname', + 'size', + 'created_at', + ]), + 'actions' => [ + 'show' => [ + 'icon' => 'fa-solid fa-eye', + 'title' => lng('usersettings.custom_notes.title'), + 'modal' => [Text::class, 'customerNoteDetailModal'], + 'visible' => [Customer::class, 'hasNote'] + ], + 'edit' => [ + 'icon' => 'fa-solid fa-edit', + 'title' => lng('panel.edit'), + 'href' => [ + 'section' => 'backups', + 'page' => 'backups', + 'action' => 'edit', + 'id' => ':id' + ], + ], + 'delete' => [ + 'icon' => 'fa-solid fa-trash', + 'title' => lng('panel.delete'), + 'class' => 'btn-danger', + 'href' => [ + 'section' => 'backups', + 'page' => 'backups', + 'action' => 'delete', + 'id' => ':id' + ], + 'visible' => [Admin::class, 'isNotMe'] + ], + ], + 'format_callback' => [ + [Style::class, 'deactivated'], + [Style::class, 'diskspaceWarning'], + [Style::class, 'trafficWarning'] + ] + ] +]; diff --git a/lib/tables.inc.php b/lib/tables.inc.php index 43c52b08..7476a9ab 100644 --- a/lib/tables.inc.php +++ b/lib/tables.inc.php @@ -32,6 +32,7 @@ const TABLE_MAIL_USERS = 'mail_users'; const TABLE_MAIL_VIRTUAL = 'mail_virtual'; const TABLE_PANEL_ACTIVATION = 'panel_activation'; const TABLE_PANEL_ADMINS = 'panel_admins'; +const TABLE_PANEL_BACKUPS = 'panel_backups'; const TABLE_PANEL_CUSTOMERS = 'panel_customers'; const TABLE_PANEL_DATABASES = 'panel_databases'; const TABLE_PANEL_DOMAINS = 'panel_domains'; diff --git a/lng/en.lng.php b/lng/en.lng.php index 0a05daf4..617af153 100644 --- a/lng/en.lng.php +++ b/lng/en.lng.php @@ -500,6 +500,9 @@ return [ 'adminguide' => 'Admin guide', 'userguide' => 'User guide', 'apiguide' => 'API guide', + 'backups' => [ + 'backups' => 'Backups', + ], ], 'apcuinfo' => [ 'clearcache' => 'Clear APCu cache', @@ -2420,4 +2423,9 @@ 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_now' => 'Configure now' ], + 'backup' => [ + 'backup' => 'Backup', + 'size' => 'Size', + 'created_at' => 'Created at', + ], ];