mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2024-08-30 18:23:09 +00:00
Backup statuses
Default backups
This commit is contained in:
parent
bf196b68c0
commit
1b073a2401
@ -10,7 +10,6 @@ from peewee import (
|
||||
TextField,
|
||||
AutoField,
|
||||
BooleanField,
|
||||
UUIDField,
|
||||
)
|
||||
from playhouse.shortcuts import model_to_dict
|
||||
|
||||
@ -18,6 +17,7 @@ from app.classes.models.base_model import BaseModel
|
||||
from app.classes.models.users import HelperUsers
|
||||
from app.classes.models.servers import Servers
|
||||
from app.classes.models.server_permissions import PermissionsServers
|
||||
from app.classes.shared.helpers import Helpers
|
||||
from app.classes.shared.websocket_manager import WebSocketManager
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -105,7 +105,7 @@ class Schedules(BaseModel):
|
||||
# Backups Class
|
||||
# **********************************************************************************
|
||||
class Backups(BaseModel):
|
||||
backup_id = UUIDField(primary_key=True, default=uuid.uuid4)
|
||||
backup_id = CharField(primary_key=True, default=Helpers.create_uuid())
|
||||
backup_name = CharField(default="New Backup")
|
||||
backup_location = CharField(default="")
|
||||
excluded_dirs = CharField(null=True)
|
||||
@ -115,6 +115,8 @@ class Backups(BaseModel):
|
||||
shutdown = BooleanField(default=False)
|
||||
before = CharField(default="")
|
||||
after = CharField(default="")
|
||||
default = BooleanField(default=False)
|
||||
status = CharField(default='{"status": "Standby", "message": ""}')
|
||||
enabled = BooleanField(default=True)
|
||||
|
||||
class Meta:
|
||||
@ -348,7 +350,7 @@ class HelpersManagement:
|
||||
return model_to_dict(Backups.get(Backups.backup_id == backup_id))
|
||||
|
||||
@staticmethod
|
||||
def get_backups_by_server(server_id, model):
|
||||
def get_backups_by_server(server_id, model=False):
|
||||
if not model:
|
||||
data = {}
|
||||
for backup in (
|
||||
@ -365,11 +367,26 @@ class HelpersManagement:
|
||||
"shutdown": backup.shutdown,
|
||||
"before": backup.before,
|
||||
"after": backup.after,
|
||||
"default": backup.default,
|
||||
"enabled": backup.enabled,
|
||||
}
|
||||
else:
|
||||
data = Backups.select().where(Backups.server_id == server_id).execute()
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def get_default_server_backup(server_id: str) -> dict:
|
||||
bu_query = Backups.select().where(
|
||||
Backups.server_id == server_id & Backups.default == True
|
||||
)
|
||||
|
||||
backup_model = bu_query.first()
|
||||
|
||||
if backup_model:
|
||||
return model_to_dict(backup_model)
|
||||
else:
|
||||
raise IndexError
|
||||
|
||||
@staticmethod
|
||||
def remove_backup_config(backup_id):
|
||||
Backups.delete().where(Backups.backup_id == backup_id).execute()
|
||||
|
@ -1107,12 +1107,12 @@ class ServerInstance:
|
||||
f.write("eula=true")
|
||||
self.run_threaded_server(user_id)
|
||||
|
||||
def a_backup_server(self, backup_id):
|
||||
def server_backup_threader(self, backup_id, update=False):
|
||||
backup_thread = threading.Thread(
|
||||
target=self.backup_server,
|
||||
daemon=True,
|
||||
name=f"backup_{self.name}",
|
||||
args=[backup_id],
|
||||
args=[backup_id, update],
|
||||
)
|
||||
logger.info(
|
||||
f"Starting Backup Thread for server {self.settings['server_name']}."
|
||||
@ -1140,7 +1140,7 @@ class ServerInstance:
|
||||
logger.info(f"Backup Thread started for server {self.settings['server_name']}.")
|
||||
|
||||
@callback
|
||||
def backup_server(self, backup_id):
|
||||
def backup_server(self, backup_id, update):
|
||||
was_server_running = None
|
||||
logger.info(f"Starting server {self.name} (ID {self.server_id}) backup")
|
||||
server_users = PermissionsServers.get_server_user_list(self.server_id)
|
||||
@ -1174,9 +1174,12 @@ class ServerInstance:
|
||||
"Found shutdown preference. Delaying"
|
||||
+ "backup start. Shutting down server."
|
||||
)
|
||||
if self.check_running():
|
||||
self.stop_server()
|
||||
was_server_running = True
|
||||
if not update:
|
||||
if self.check_running():
|
||||
self.stop_server()
|
||||
was_server_running = True
|
||||
else:
|
||||
was_server_running = False
|
||||
|
||||
self.helper.ensure_dir_exists(backup_location)
|
||||
try:
|
||||
@ -1318,7 +1321,7 @@ class ServerInstance:
|
||||
def jar_update(self):
|
||||
self.stats_helper.set_update(True)
|
||||
update_thread = threading.Thread(
|
||||
target=self.a_jar_update, daemon=True, name=f"exe_update_{self.name}"
|
||||
target=self.threaded_jar_update, daemon=True, name=f"exe_update_{self.name}"
|
||||
)
|
||||
update_thread.start()
|
||||
|
||||
@ -1359,10 +1362,13 @@ class ServerInstance:
|
||||
def check_update(self):
|
||||
return self.stats_helper.get_server_stats()["updating"]
|
||||
|
||||
def a_jar_update(self):
|
||||
def threaded_jar_update(self):
|
||||
server_users = PermissionsServers.get_server_user_list(self.server_id)
|
||||
was_started = "-1"
|
||||
self.a_backup_server()
|
||||
# Get default backup configuration
|
||||
backup_config = HelpersManagement.get_default_server_backup(self.server_id)
|
||||
# start threaded backup
|
||||
self.server_backup_threader(backup_config["backup_id"], True)
|
||||
# checks if server is running. Calls shutdown if it is running.
|
||||
if self.check_running():
|
||||
was_started = True
|
||||
@ -1516,12 +1522,6 @@ class ServerInstance:
|
||||
WebSocketManager().broadcast_user_page(
|
||||
user, "/panel/dashboard", "send_start_reload", {}
|
||||
)
|
||||
WebSocketManager().broadcast_user(
|
||||
user,
|
||||
"notification",
|
||||
"Executable update finished for " + self.name,
|
||||
)
|
||||
|
||||
self.management_helper.add_to_audit_log_raw(
|
||||
"Alert",
|
||||
"-1",
|
||||
|
@ -140,7 +140,7 @@ class TasksManager:
|
||||
)
|
||||
|
||||
elif command == "backup_server":
|
||||
svr.a_backup_server(cmd["action_id"])
|
||||
svr.server_backup_threader(cmd["action_id"])
|
||||
|
||||
elif command == "update_executable":
|
||||
svr.jar_update()
|
||||
|
@ -124,6 +124,15 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler):
|
||||
server_id,
|
||||
self.get_remote_ip(),
|
||||
)
|
||||
if backup_conf["default"]:
|
||||
return self.finish_json(
|
||||
405,
|
||||
{
|
||||
"status": "error",
|
||||
"error": "NOT_ALLOWED",
|
||||
"error_data": "Cannot delete default backup",
|
||||
},
|
||||
)
|
||||
self.controller.management.delete_backup_config(backup_id)
|
||||
|
||||
return self.finish_json(200, {"status": "ok"})
|
||||
|
@ -70,7 +70,9 @@
|
||||
<tr class="rounded">
|
||||
<th scope="col" style="width: 15%; min-width: 10px;">{{ translate('serverBackups', 'name',
|
||||
data['lang']) }} </th>
|
||||
<th scope="col" style="width: 60%; min-width: 50px;">{{ translate('serverBackups',
|
||||
<th scope="col" style="width: 5%; min-width: 10px;">{{ translate('serverBackups', 'status',
|
||||
data['lang']) }} </th>
|
||||
<th scope="col" style="width: 55%; min-width: 50px;">{{ translate('serverBackups',
|
||||
'storageLocation', data['lang']) }}</th>
|
||||
<th scope="col" style="width: 10%; min-width: 50px;">{{ translate('serverBackups',
|
||||
'maxBackups', data['lang']) }}</th>
|
||||
@ -83,6 +85,19 @@
|
||||
<tr>
|
||||
<td id="{{backup.backup_name}}" class="id">
|
||||
<p>{{backup.backup_name}}</p>
|
||||
<br>
|
||||
{% if backup.default %}
|
||||
<span class="badge-pill badge-outline-warning">{{ translate('serverBackups', 'default',
|
||||
data['lang']) }}</span><small><button class="badge-pill badge-outline-info backup-explain"
|
||||
data-explain="{{ translate('serverBackups', 'defaultExplain', data['lang'])}}"><i
|
||||
class="fa-solid fa-question"></i></button></small>
|
||||
{% end %}
|
||||
</td>
|
||||
<td>
|
||||
<div id="{{backup.backup_id}}_status">
|
||||
<span class="badge-pill badge-outline-success backup-status"
|
||||
data-status="{{ backup.status }}"></span>
|
||||
</div>
|
||||
</td>
|
||||
<td id="{{backup.backup_location}}" class="type">
|
||||
<p>{{backup.backup_location}}</p>
|
||||
@ -96,9 +111,11 @@
|
||||
class="btn btn-info">
|
||||
<i class="fas fa-pencil-alt"></i>
|
||||
</button>
|
||||
{% if not backup.default %}
|
||||
<button data-backup={{ backup.backup_id }} class="btn btn-danger del_button">
|
||||
<i class="fas fa-trash" aria-hidden="true"></i>
|
||||
</button>
|
||||
{% end %}
|
||||
<button data-backup={{ backup.backup_id }} data-toggle="tooltip"
|
||||
title="{{ translate('serverBackups', 'run', data['lang']) }}"
|
||||
class="btn btn-outline-warning run-backup backup_now_button">
|
||||
@ -126,6 +143,13 @@
|
||||
<tr>
|
||||
<td id="{{backup.backup_name}}" class="id">
|
||||
<p>{{backup.backup_name}}</p>
|
||||
<br>
|
||||
{% if backup.default %}
|
||||
<span class="badge-pill badge-outline-warning">{{ translate('serverBackups', 'default',
|
||||
data['lang']) }}</span><small><button class="badge-pill badge-outline-info backup-explain"
|
||||
data-explain="{{ translate('serverBackups', 'defaultExplain', data['lang'])}}"><i
|
||||
class="fa-solid fa-question"></i></button></small>
|
||||
{% end %}
|
||||
</td>
|
||||
<td id="backup_edit" class="action">
|
||||
<button
|
||||
@ -133,9 +157,11 @@
|
||||
class="btn btn-info">
|
||||
<i class="fas fa-pencil-alt"></i>
|
||||
</button>
|
||||
{% if not backup.default %}
|
||||
<button data-backup={{ backup.backup_id }} class="btn btn-danger del_button">
|
||||
<i class="fas fa-trash" aria-hidden="true"></i>
|
||||
</button>
|
||||
{% end %}
|
||||
<button data-backup={{ backup.backup_id }} data-toggle="tooltip"
|
||||
title="{{ translate('serverBackups', 'run', data['lang']) }}"
|
||||
class="btn btn-outline-warning test-socket">
|
||||
@ -230,14 +256,7 @@
|
||||
let responseData = await res.json();
|
||||
if (responseData.status === "ok") {
|
||||
console.log(responseData);
|
||||
$("#backup_button").html(`<div class="progress" style="height: 15px;">
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated" id="backup_progress_bar"
|
||||
role="progressbar" style="width:{{data['backup_stats']['percent']}}%;"
|
||||
aria-valuenow="{{data['backup_stats']['percent']}}" aria-valuemin="0" aria-valuemax="100">{{
|
||||
data['backup_stats']['percent'] }}%</div>
|
||||
</div>
|
||||
<p>Backing up <i class="fas fa-spin fa-spinner"></i> <span
|
||||
id="total_files">{{data['server_stats']['world_size']}}</span></p>`);
|
||||
$("#backup_button").prop('disabled', true)
|
||||
} else {
|
||||
|
||||
bootbox.alert({
|
||||
@ -269,6 +288,54 @@
|
||||
|
||||
$(document).ready(function () {
|
||||
console.log("ready!");
|
||||
$(".backup-explain").on("click", function () {
|
||||
bootbox.alert($(this).data("explain"));
|
||||
});
|
||||
|
||||
$('.backup-status').each(function () {
|
||||
// Get the JSON string from the element's text
|
||||
var data = $(this).data('status');
|
||||
|
||||
try {
|
||||
|
||||
console.log("DATA " + data)
|
||||
|
||||
// Update the element's text with the status value
|
||||
$(this).text(data.status);
|
||||
|
||||
// Optionally, add classes based on status to style the element
|
||||
if (data.status === 'Active') {
|
||||
$(this).addClass('badge-pill badge-outline-success');
|
||||
} else if (data.status === 'Failed') {
|
||||
$(this).addClass('badge-pill badge-outline-danger');
|
||||
} else if (data.status === 'Standby') {
|
||||
$(this).addClass('badge-pill badge-outline-secondary');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Invalid JSON string:', jsonString);
|
||||
}
|
||||
});
|
||||
|
||||
if (webSocket) {
|
||||
webSocket.on('backup_status', function (backup) {
|
||||
console.log("PEEPEEPOOPOO")
|
||||
text = ``;
|
||||
console.log(backup)
|
||||
if (backup.percent >= 100) {
|
||||
$(`#${backup.backup_id}_status`).html(`<span class="badge-pill badge-outline-success backup-status"
|
||||
>Completed</span>`);
|
||||
setTimeout(function () {
|
||||
window.location.reload(1);
|
||||
}, 5000);
|
||||
} else {
|
||||
text = `<div class="progress-bar progress-bar-striped progress-bar-animated"
|
||||
role="progressbar" style="width:${backup.percent}%;"
|
||||
aria-valuenow="${backup.percent}" aria-valuemin="0" aria-valuemax="100">${backup.percent}%</div>`
|
||||
|
||||
$(`#${backup.backup_id}_status`).html(text);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$('#backup_table').DataTable({
|
||||
"order": [[1, "desc"]],
|
||||
@ -378,21 +445,6 @@
|
||||
bootbox.alert("You must input a path before selecting this button");
|
||||
}
|
||||
});
|
||||
if (webSocket) {
|
||||
webSocket.on('backup_status', function (backup) {
|
||||
if (backup.percent >= 100) {
|
||||
document.getElementById('backup_progress_bar').innerHTML = '100%';
|
||||
document.getElementById('backup_progress_bar').style.width = '100%';
|
||||
setTimeout(function () {
|
||||
window.location.reload(1);
|
||||
}, 5000);
|
||||
} else {
|
||||
document.getElementById('backup_progress_bar').innerHTML = backup.percent + '%';
|
||||
document.getElementById('backup_progress_bar').style.width = backup.percent + '%';
|
||||
document.getElementById('total_files').innerHTML = backup.total_files;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getDirView(event) {
|
||||
let path = event.target.parentElement.getAttribute("data-path");
|
||||
|
@ -63,7 +63,14 @@
|
||||
{% end %}
|
||||
<form id="backup-form" class="forms-sample">
|
||||
<div class="form-group">
|
||||
<label for="backup_name">{{ translate('serverBackups', 'name', data['lang']) }} </label>
|
||||
<label for="backup_name">{{ translate('serverBackups', 'name', data['lang']) }}
|
||||
{% if data["backup_config"]["default"] %}
|
||||
<span class="badge-pill badge-outline-warning">{{ translate('serverBackups', 'default',
|
||||
data['lang']) }}</span><small><button class="badge-pill badge-outline-info backup-explain"
|
||||
data-explain="{{ translate('serverBackups', 'defaultExplain', data['lang'])}}"><i
|
||||
class="fa-solid fa-question"></i></button></small>
|
||||
{% end %}
|
||||
</label>
|
||||
{% if data["backup_config"].get("backup_id", None) %}
|
||||
<input type="text" class="form-control" name="backup_name" id="backup_name"
|
||||
value="{{ data['backup_config']['backup_name'] }}">
|
||||
@ -188,7 +195,8 @@
|
||||
|
||||
<button type="submit" class="btn btn-success mr-2">{{ translate('serverBackups', 'save', data['lang'])
|
||||
}}</button>
|
||||
<button type="reset" class="btn btn-light">{{ translate('serverBackups', 'cancel', data['lang'])
|
||||
<button type="reset" class="btn btn-light cancel-button">{{ translate('serverBackups', 'cancel',
|
||||
data['lang'])
|
||||
}}</button>
|
||||
</form>
|
||||
</div>
|
||||
@ -431,6 +439,13 @@
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
$(".backup-explain").on("click", function (e) {
|
||||
e.preventDefault();
|
||||
bootbox.alert($(this).data("explain"));
|
||||
});
|
||||
$(".cancel-button").on("click", function () {
|
||||
location.href = `/panel/server_detail?id=${server_id}&subpage=backup`
|
||||
});
|
||||
$("#backup-form").on("submit", async function (e) {
|
||||
e.preventDefault();
|
||||
const token = getCookie("_xsrf")
|
||||
|
@ -3,9 +3,11 @@ import uuid
|
||||
import peewee
|
||||
import logging
|
||||
|
||||
|
||||
from app.classes.models.management import Backups, Schedules
|
||||
from app.classes.shared.helpers import Helpers
|
||||
from app.classes.shared.console import Console
|
||||
from app.classes.shared.migration import Migrator, MigrateHistory
|
||||
from app.classes.models.management import Backups, Schedules
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -15,11 +17,20 @@ def migrate(migrator: Migrator, database, **kwargs):
|
||||
Write your migrations here.
|
||||
"""
|
||||
db = database
|
||||
|
||||
Console.info("Starting Backups migrations")
|
||||
Console.info(
|
||||
"Migrations: Adding columns [backup_id, "
|
||||
"backup_name, backup_location, enabled, default, action_id, backup_status]"
|
||||
)
|
||||
migrator.add_columns("backups", backup_id=peewee.UUIDField(default=uuid.uuid4))
|
||||
migrator.add_columns("backups", backup_name=peewee.CharField(default="Default"))
|
||||
migrator.add_columns("backups", backup_location=peewee.CharField(default=""))
|
||||
migrator.add_columns("backups", enabled=peewee.BooleanField(default=True))
|
||||
migrator.add_columns("backups", default=peewee.BooleanField(default=False))
|
||||
migrator.add_columns(
|
||||
"backups",
|
||||
status=peewee.CharField(default='{"status": "Standby", "message": ""}'),
|
||||
)
|
||||
migrator.add_columns(
|
||||
"schedules", action_id=peewee.CharField(null=True, default=None)
|
||||
)
|
||||
@ -52,7 +63,7 @@ def migrate(migrator: Migrator, database, **kwargs):
|
||||
database = db
|
||||
|
||||
class NewBackups(peewee.Model):
|
||||
backup_id = peewee.UUIDField(primary_key=True, default=uuid.uuid4)
|
||||
backup_id = peewee.CharField(primary_key=True, default=Helpers.create_uuid())
|
||||
backup_name = peewee.CharField(default="New Backup")
|
||||
backup_location = peewee.CharField(default="")
|
||||
excluded_dirs = peewee.CharField(null=True)
|
||||
@ -62,6 +73,8 @@ def migrate(migrator: Migrator, database, **kwargs):
|
||||
shutdown = peewee.BooleanField(default=False)
|
||||
before = peewee.CharField(default="")
|
||||
after = peewee.CharField(default="")
|
||||
default = peewee.BooleanField(default=False)
|
||||
status = peewee.CharField(default='{"status": "Standby", "message": ""}')
|
||||
enabled = peewee.BooleanField(default=True)
|
||||
|
||||
class Meta:
|
||||
@ -97,11 +110,11 @@ def migrate(migrator: Migrator, database, **kwargs):
|
||||
for backup in Backups.select():
|
||||
# Fetch the related server entry from the Servers table
|
||||
server = Servers.get(Servers.server_id == backup.server_id)
|
||||
|
||||
Console.info(f"Migrations: Migrating backup for server {server.server_name}")
|
||||
# Create a new backup entry with data from the
|
||||
# old backup entry and related server
|
||||
NewBackups.create(
|
||||
backup_name="Default",
|
||||
backup_name=f"{server.server_name} Backup",
|
||||
# Set backup_location equal to backup_path
|
||||
backup_location=server.backup_path,
|
||||
excluded_dirs=backup.excluded_dirs,
|
||||
@ -111,19 +124,27 @@ def migrate(migrator: Migrator, database, **kwargs):
|
||||
shutdown=backup.shutdown,
|
||||
before=backup.before,
|
||||
after=backup.after,
|
||||
default=True,
|
||||
enabled=True,
|
||||
)
|
||||
|
||||
Console.debug("Migrations: Dropping old backup table")
|
||||
# Drop the existing backups table
|
||||
migrator.drop_table("backups")
|
||||
|
||||
Console.debug("Migrations: Renaming new_backups to backups")
|
||||
# Rename the new table to backups
|
||||
migrator.rename_table("new_backups", "backups")
|
||||
|
||||
Console.debug("Migrations: Dropping backup_path from servers table")
|
||||
migrator.drop_columns("servers", ["backup_path"])
|
||||
|
||||
for schedule in Schedules.select():
|
||||
action_id = None
|
||||
if schedule.command == "backup_server":
|
||||
Console.info(
|
||||
f"Migrations: Adding backup ID to task with name {schedule.name}"
|
||||
)
|
||||
backup = NewBackups.get(NewBackups.server_id == schedule.server_id)
|
||||
action_id = backup.backup_id
|
||||
NewSchedules.create(
|
||||
@ -144,9 +165,11 @@ def migrate(migrator: Migrator, database, **kwargs):
|
||||
next_run=schedule.next_run,
|
||||
)
|
||||
|
||||
Console.debug("Migrations: dropping old schedules table")
|
||||
# Drop the existing backups table
|
||||
migrator.drop_table("schedules")
|
||||
|
||||
Console.debug("Migrations: renaming new_schedules to schedules")
|
||||
# Rename the new table to backups
|
||||
migrator.rename_table("new_schedules", "schedules")
|
||||
|
||||
|
@ -335,7 +335,10 @@
|
||||
"newBackup": "Create New Backup",
|
||||
"edit": "Edit",
|
||||
"run": "Run Backup",
|
||||
"backups": "Server Backups"
|
||||
"backups": "Server Backups",
|
||||
"default": "Default Backup",
|
||||
"defaultExplain": "The backup that Crafty will use before updates. This cannot be changed or deleted.",
|
||||
"status": "Status"
|
||||
},
|
||||
"serverConfig": {
|
||||
"bePatientDelete": "Please be patient while we remove your server from the Crafty panel. This screen will close in a few moments.",
|
||||
|
Loading…
Reference in New Issue
Block a user