### Initialize Server Uptime and Details Display Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/parts/details_stats.html Sets up the display for server start time and uptime on page load. It fetches the server start time, formats it, and starts an interval to update the uptime display every second. ```javascript let uptime = document.querySelector('#uptime'); let started = document.querySelector('#started'); let startedUTC; let startedLocal; let uptimeLoop; document.body.onload = (() => { console.log('calculateTime'); startedUTC = "{{ data['server_stats']['started'] }}"; if (startedUTC != 'False') { console.log('started utc:', startedUTC); startedUTC = moment.utc(startedUTC, 'YYYY-MM-DD HH:mm:ss'); var browserUTCOffset = moment().utcOffset(); // This is in minutes startedLocal = startedUTC.utcOffset(browserUTCOffset); startedLocalFormatted = startedLocal.format('YYYY-MM-DD HH:mm:ss'); console.log('started local time:', startedLocalFormatted); started.textContent = startedLocalFormatted } var calculateUptime = () => { var msdiff = moment().diff(startedLocal); var diff = moment.duration(msdiff); uptime.textContent = durationToHumanizedString(diff); } if (uptime != null && started != null) { console.log('startedLocal', startedLocal) if (startedLocal) { calculateUptime(); uptimeLoop = setInterval(calculateUptime, 1000); } } initParser('input_motd', 'input_motd'); }); ``` -------------------------------- ### Initiate Server Backup Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_backup.html Starts a server backup process by sending a POST request to the server API. It disables the backup button upon successful initiation. ```javascript async function backup_started(backup_id) { const token = getCookie("_xsrf") console.log(backup_id) let res = await fetch(`/api/v2/servers/${serverId}/action/backup_server/${backup_id}/`, { method: 'POST', headers: { 'X-XSRFToken': token } }); let responseData = await res.json(); if (responseData.status === "ok") { console.log(responseData); $("#backup_button").prop('disabled', true) } else { bootbox.alert({ title: responseData.error, message: responseData.error_data }); } return; } ``` -------------------------------- ### Start and Monitor Crafty Controller with Docker Compose Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/README.md Starts the Crafty Controller container in detached mode and follows its logs. This command should be run after defining your `docker-compose.yml`. ```sh docker-compose up -d && docker-compose logs -f ``` -------------------------------- ### Initialize Server Status and Button States Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_term.html Parses the server's running status and initializes the state of start, restart, and stop buttons based on whether the server is online. ```javascript // Convert running to lower case (example: 'True' converts to 'true') and // then to boolean via JSON.parse() let online = JSON.parse("{{ data\[\'server_stats\' 1] ['running'] }}".toLowerCase()); let startBtn = document.querySelector('#start-btn'); let restartBtn = document.querySelector('#restart-btn'); let stopBtn = document.querySelector('#stop-btn'); //{% if data\[\'permissions\' 1] ['Commands'] in data\[\'user_permissions\'\] %} if (online) { startBtn.setAttribute('disabled', 'disabled'); restartBtn.removeAttribute('disabled'); stopBtn.removeAttribute('disabled'); } else { startBtn.removeAttribute('disabled'); restartBtn.setAttribute('disabled', 'disabled'); stopBtn.setAttribute('disabled', 'disabled'); } if (webSocket) { webSocket.on('send_start_reload', function () { location.reload() }); } ``` -------------------------------- ### Initiate Server Backup Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_backup_edit.html Sends a POST request to the API to start a server backup. It includes the CSRF token in the headers and handles the API response to enable/disable the backup button or show errors. ```javascript async function backup_started() { const token = getCookie("_xsrf") let res = await fetch(`/api/v2/servers/${serverId}/action/backup_server/${backup_id}`, { method: 'POST', headers: { 'X-XSRFToken': token } }); let responseData = await res.json(); if (responseData.status === "ok") { console.log(responseData); $("#backup_button").prop('disabled', true) } else { bootbox.alert({ title: responseData.error, message: responseData.error_data }); } return; } ``` -------------------------------- ### Tree Reset Navigation Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/server/bedrock_wizard.html Handles the click event for the tree reset button, navigating the user to the server setup step. ```javascript $(".tree-reset").on("click", function () { location.href = "/server/bedrock_step1"; }); ``` -------------------------------- ### Play Button Click Handler Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/dashboard.html Handles the click event for the 'play' button, initiating a server start command and displaying a loading indicator. It also sets a timeout for the operation. ```javascript $(document).ready(function () { console.log('ready for JS!') $(".play_button").click(function () { server_id = $(this).attr("data-id"); send_command(server_id, 'start_server'); bootbox.alert({ backdrop: true, title: '{% raw translate("dashboard", "sendingCommand", data["lang"]) %}', message: '
${responseData.data}`
});
} else {
bootbox.alert({
title: responseData.error,
message: responseData.error_data
});
}
});
});
```
--------------------------------
### Display Server Status and Details
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/parts/details_stats.html
Conditionally displays server status (online, crashed, offline), start time, and uptime based on the server's state. Also shows CPU, memory, player counts, and version.
```html
{% if data['server_stats']['running'] %} **{{ translate('serverStats', 'serverStatus', data['lang']) }}:** {{ translate('serverStats', 'online', data['lang']) }}
**{{ translate('serverStats', 'serverStarted', data['lang']) }}:** {{ data['server_stats']['started'] }}
**{{ translate('serverStats', 'serverUptime', data['lang']) }}:** {{ translate('serverStats', 'errorCalculatingUptime', data['lang']) }} {% elif data['server_stats']['crashed'] %} **{{ translate('serverStats', 'serverStatus', data['lang'])}:** {{ translate('dashboard', 'crashed', data['lang']) }}
**{{ translate('serverStats', 'serverStarted', data['lang']) }}:** {{ translate('dashboard', 'crashed',data['lang']) }}
**{{ translate('serverStats', 'serverUptime', data['lang']) }}:** {{ translate('dashboard', 'crashed', data['lang']) }} {% else %} **{{ translate('serverStats', 'serverStatus', data['lang']) }}:** {{ translate('serverStats', 'offline', data['lang']) }}
**{{ translate('serverStats', 'serverStarted', data['lang']) }}:** {{ translate('serverStats', 'offline', data['lang']) }}
**{{ translate('serverStats', 'serverUptime', data['lang']) }}:** {{ translate('serverStats', 'offline', data['lang']) }} {% end %}
**{{ translate('serverStats', 'serverTimeZone', data['lang']) }}:** {{ data['serverTZ'] }}
**{{ translate('serverStats', 'cpuUsage', data['lang']) }}:** {{ data['server_stats']['cpu'] }}%
**{{ translate('serverStats', 'memUsage', data['lang']) }}:** {{ data['server_stats']['mem'] }}
{% if data['server_stats']['int_ping_results'] %} **{{ translate('serverStats', 'players', data['lang']) }}:** {{ data['server_stats']['online'] }} / {{ data['server_stats']['max'] }}
{% else %} **{{ translate('serverStats', 'players', data['lang']) }}:** 0/0
{% end %}
{% if data['server_stats']['version'] != 'False' %} **{{ translate('serverStats', 'version', data['lang']) }}:** {{ data['server_stats']['version'] }}
**{{ translate('serverStats', 'description', data['lang']) }}:** {{ translate('serverStats', 'loadingMotd', data['lang']) }}
{% else %} **{{ translate('serverStats', 'version', data['lang']) }}:** {{ translate('serverStats', 'unableToConnect', data['lang']) }}
**{{ translate('serverStats', 'description', data['lang']) }}:** {{ translate('serverStats', 'unableToConnect', data['lang']) }}
{% end %} **Server Type: {{data['server_stats']['server_type']}}**
```
--------------------------------
### Handle Backup Explanation Click
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_backup.html
Attaches a click event listener to elements with the class 'backup-explain' to display detailed explanations using bootbox.
```javascript
$(".backup-explain").on("click", function () {
bootbox.alert($(this).data("explain"));
});
```
--------------------------------
### Initiate Immediate Backup
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_backup_edit.html
Handles the click event for the 'Backup Now' button to initiate an immediate backup process.
```javascript
$("#backup_now_button").click(function () {
backup_started();
});
```
--------------------------------
### Get Cookie Function
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_webhook_edit.html
Retrieves a cookie by its name. This is used for obtaining the CSRF token for security.
```javascript
function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=(\[^;]\*)\\b");
return r ? r[1] : undefined;
}
```
--------------------------------
### Jinja2 Conditional Logic
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/base.html
Example of Jinja2 conditional statements for rendering content based on data attributes.
```html
{% if not data.get("mfa", None) and not data.get("password\_auth\_disabled", False) %} {% end %}
```
--------------------------------
### Document Ready and WebSocket Initialization
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_term.html
Initializes logging and sets up the WebSocket listener for new log lines when the document is ready.
```javascript
$(document).ready(function () {
console.log("ready!");
get_server_log()
webSocket.on('vterm_new_line', new_line_handler)
});
```
--------------------------------
### Get CSRF Token
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_schedules.html
A utility function to retrieve the CSRF token from browser cookies, used for security in requests.
```javascript
function getCookie(name) {
var r = document.cookie.match(String.raw`\b` + name + String.raw`=([^;]*)`)
return r ? r[1] : undefined;
}
```
--------------------------------
### Initiate Server Backup
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_backup.html
Attaches a click event to elements with the class 'backup_now_button' to initiate a server backup. It logs the action and calls the `backup_started` function with the backup identifier.
```JavaScript
}); $(".backup_now_button").click(function () { console.log("Backup started") backup_started($(this).data('backup')); }); });
```
--------------------------------
### Get CSRF Cookie
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_term.html
Utility function to retrieve the CSRF token cookie, used for authenticated API requests.
```javascript
function getCookie(name) {
let r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
return r ? r[1] : undefined;
}
```
--------------------------------
### Initialize Chart.js for Server Metrics
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_metrics.html
Sets up a Chart.js instance to display server metrics with dual Y-axes and time-based X-axis. It configures datasets for CPU, RAM (percentage and GB), and player counts, enabling decimation for performance and interactive zoom/pan functionality.
```javascript
function serverCutoff(hours) { return moment(serverNow().subtract(hours, 'hours').format(dateFormat), dateFormat).valueOf(); }
const timestamps = rawDates.map(d => parseServerTime(d));
const playerData = timestamps.map((t, i) => ({x: t, y: rawPlayers[i]}));
const ramPercentData = timestamps.map((t, i) => ({x: t, y: rawRamPercent[i]}));
const ramGbData = timestamps.map((t, i) => ({x: t, y: rawRamGb[i]}));
const cpuData = timestamps.map((t, i) => ({x: t, y: rawCpu[i]}));
// Compute decimation samples based on chart width (~1 point per 3 CSS pixels)
function getDecimationSamples() {
var canvas = document.getElementById("lineChart");
var width = canvas.clientWidth || canvas.offsetWidth || 800;
return Math.max(60, Math.round(width / 3));
}
// Create chart with dual Y-axes and time-based x-axis
var ctxL = document.getElementById("lineChart").getContext('2d');
var hist_chart = new Chart(ctxL, {
type: 'line',
data: {
datasets: [
{
label: "CPU %",
data: cpuData,
borderColor: 'rgba(255, 175, 0, .7)',
borderWidth: 2,
lineTension: 0,
spanGaps: false,
yAxisID: 'y-left',
},
{
label: "RAM %",
data: ramPercentData,
borderColor: 'rgba(33, 150, 243, .8)',
borderWidth: 2,
lineTension: 0,
spanGaps: false,
yAxisID: 'y-left',
},
{
label: "RAM GB",
data: ramGbData,
borderColor: 'rgba(0, 188, 212, .9)',
borderWidth: 2,
lineTension: 0,
spanGaps: false,
yAxisID: 'y-right',
},
{
label: "Players",
data: playerData,
borderColor: 'rgba(136, 98, 224, .7)',
borderWidth: 2,
lineTension: 0,
spanGaps: false,
yAxisID: 'y-right',
},
]
},
options: {
maintainAspectRatio: false,
parsing: false,
onResize(chart, size) {
var newSamples = Math.max(60, Math.round(size.width / 3));
chart.options.plugins.decimation.samples = newSamples;
chart.options.plugins.decimation.threshold = newSamples;
},
plugins: {
decimation: {
enabled: true,
algorithm: 'lttb',
samples: getDecimationSamples(),
threshold: getDecimationSamples(),
},
zoom: {
zoom: {
onZoom() {
document.getElementById("reset-button").classList.remove("d-none");
document.getElementById("reset-button").classList.add("d-block");
zoomed = true;
},
wheel: {
enabled: true,
modifierKey: 'shift',
},
drag: {
enabled: true,
modifierKey: "shift"
},
pinch: {
enabled: true
},
mode: 'x',
},
pan: {
enabled: true,
mode: "x",
threshold: 1,
}
},
tooltip: {
callbacks: {
label: function(context) {
let label = context.dataset.label || '';
if (label) label += ': ';
// Format based on metric type
if (context.dataset.label === 'RAM GB') {
label += parseFloat(context.parsed.y).toFixed(2) + ' GB';
} else if (context.dataset.label === 'Players') {
label += Math.round(context.parsed.y);
} else {
label += context.parsed.y.toFixed(1) + '%';
}
return label;
}
}
}
},
responsive: true,
scales: {
'y-left': {
type: 'linear',
position: 'left',
min: 0,
title: {
display: true,
text: 'Percentage (%)'
},
grid: {
display: true
}
},
'y-right': {
type: 'linear',
position: 'right',
min: 0,
title: {
display: true,
text: 'Count / GB'
},
grid: {
display: false
}
},
x: {
type: 'time',
time: {
displayFormats: {
minute: 'HH:mm',
hour: 'MMM D, HH:mm',
day: 'MMM D',
}
},
position: 'bottom',
}
}
}
});
$(window).ready(function () {
$('body').click(function () {
$('.hints').popover("hide");
});
});
// WebSocket updates and reset button
const samplingTiers = {% raw json_encode(data.get('sampling_tiers', [])) %};
const samplingFallbackDivisor = {{ data.get('sampling_fallback_divisor', 12) }};
// Match live update frequency to historical data interval.
// The backend records stats every 30 seconds; adaptive sampling then
// keeps every Nth point depending on the selected time range.
```
--------------------------------
### Get Directory View
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_backup.html
Retrieves the path from the target element's parent and calls `getTreeView` if the directory has not been previously clicked.
```JavaScript
function getDirView(event) { let path = event.target.parentElement.getAttribute("data-path"); if (document.getElementById(path).classList.contains('clicked')) { return; } else { getTreeView(path); } }
```
--------------------------------
### Initialize Document Ready
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_config.html
Logs a message to the console when the DOM is fully loaded and ready.
```javascript
$(document).ready(function () { console.log("ready!"); });
```
--------------------------------
### Get CSRF Token Cookie
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_config.html
Retrieves the CSRF token from the browser's cookies, essential for authenticated API requests.
```javascript
function getCookie(name) { var r = document.cookie.match("\\b" + name + "=(\[^;\\\]*)\\b"); return r ? r\[1\] : undefined; }
```
--------------------------------
### JavaScript: Get Random Number (Exclusive Max)
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/base.html
Generates a random floating-point number between a minimum (inclusive) and maximum (exclusive).
```javascript
/**
* Returns a random number between min (inclusive) and max (exclusive)
*/
function getRandomArbitrary(min, max) {
return Math.random() * (max - min) + min;
}
```
--------------------------------
### Confirm Server Deletion (Basic)
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_config.html
Displays a Bootbox confirmation dialog before proceeding with server deletion. If confirmed, it calls the `deleteServer` function.
```javascript
function deleteConfirm() { path = "{{data\[\'server_stats\'\ Indian\server_id'\server_name'\path'\}}"; name = "{{data\[\'server_stats\'\ Indian\server_id'\server_name'\server_name'\}}"; bootbox.confirm({ size: "", title: "{% raw translate('serverConfig', 'deleteServerQuestion', data\[\'lang\'\ Indian) %}", closeButton: false, message: "{% raw translate('serverConfig', 'deleteServerQuestionMessage', data\[\'lang\'\ Indian) %}", buttons: { confirm: { label: "{{ translate('serverConfig', 'yesDelete', data\[\'lang\'\ Indian) }}", className: 'btn-danger', }, cancel: { label: "{{ translate('serverConfig', 'noDelete', data\[\'lang\'\ Indian) }}", className: 'btn-link', } }, callback: function (result) { if (!result) { return; return; } else { deleteServer(); } } }); }
```
--------------------------------
### Initialize Popovers and Handle Small Screens
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/config_json.html
Initializes popovers and shows them on small screens when the document is ready. Hides popovers when the body is clicked.
```javascript
$(document).ready(function () {
$(' [data-toggle="popover"]').popover();
if ($(window).width() < 1000) {
$('.too_small').popover("show");
$('.too_small2').popover("show");
}
});
$(window).ready(function () {
$('body').click(function () {
$('.too_small').popover("hide");
$('.too_small2').popover("hide");
});
});
```
--------------------------------
### JavaScript Cookie Retrieval Function
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/server/wizard.html
Retrieves the value of a specified cookie. This is often used to get CSRF tokens for authenticated requests.
```javascript
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trimStart();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
```
--------------------------------
### Include Main Menu Partial
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/base.html
Includes the 'main_menu.html' partial template.
```html
{% include main_menu.html %}
```
--------------------------------
### Get Server ID and XSRF Token
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_backup.html
Retrieves the server ID from the URL and a secure XSRF token from cookies for API requests.
```javascript
const serverId = new URLSearchParams(document.location.search).get('id') //used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security
function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=(\[^;]\*)\\b");
return r ? r[1] : undefined;
}
```
--------------------------------
### Get Cookie Utility Function
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/panel_edit_user.html
Retrieves a cookie by its name from the browser's document cookies. This is used for obtaining the XSRF token.
```javascript
function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=([^;]*) \\b");
return r ? r[1] : undefined;
}
```
--------------------------------
### Initialize Announcements and Style MFA Link
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/notify.html
On document ready, fetches announcements and applies styling to the MFA link based on the computed background color of the root element.
```javascript
$(document).ready(function () {
getAnnouncements();
const bg = getComputedStyle(document.documentElement)
.getPropertyValue("--dropdown-bg")
.trim();
if (isLight(bg)) {
$("#mfa-link").addClass("mfa-light");
$("#mfa-link").removeClass("mfa-dark");
}
})
```
--------------------------------
### Submit Server Update Configuration (Basic)
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_update_center.html
Submits basic server update configuration, including server JAR, type, and version. It then updates the executable path.
```javascript
if ($("#ba").val() === "0") {
let payload = {
"category": $("#server_jar").val(),
"type": $("#server_type").val(),
"version": $("#server").val()
}
let formDataJsonString = JSON.stringify(payload);
if ($("#server_jar").val() && $("#server_type").val() && $("#server").val()) {
//Only send the first payload if it exists
let res = await fetch(`/api/v2/servers/${serverId}/update/config/`, {
method: 'PATCH',
body: formDataJsonString,
headers: {
'X-XSRFToken': token
},
});
let responseData = await res.json()
if (responseData.status === "ok") {
$("#update-url").text(responseData.data["executable_update_url"])
} else {
bootbox.alert({
title: responseData.error,
message: responseData.error_data
});
}
}
payload = {
"executable": $("#executable").val()
};
formDataJsonString = JSON.stringify(payload);
let res1 = await fetch(`/api/v2/servers/${serverId}/`, {
method: 'PATCH',
body: formDataJsonString,
headers: {
'X-XSRFToken': token
},
});
let responseData1 = await res1.json()
if (responseData1.status === "ok") {
$("#span_executable").text($("#executable").val())
} else {
bootbox.alert({
title: responseData1.error,
message: responseData1.error_data
});
}
}
```
--------------------------------
### JavaScript: Get Cookie by Name
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/base.html
Utility function to retrieve a cookie's value from the browser, used for Tornado's XSRF protection.
```javascript
//used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security
function getCookie(name) {
var r = document.cookie.match(String.raw`\b` + name + String.raw`=([^;]*)\b`);
return r ? r[1] : undefined;
}
```
--------------------------------
### Sidebar Initialization and Resize Handling
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/main_menu.html
Initializes sidebar behavior on document ready and sets up a debounced resize handler for responsive adjustments.
```javascript
$(document).ready(function () {
sidebarResizeHandler(null);
$(window).on( 'resize', debounce(sidebarResizeHandler, 25, true) );
});
```
--------------------------------
### JavaScript: Get Random Integer (Inclusive Max)
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/base.html
Generates a random integer between a minimum and maximum, both inclusive. Avoids non-uniform distribution from Math.round().
```javascript
/**
* Returns a random integer between min (inclusive) and max (inclusive).
* The value is no lower than min (or the next integer greater than min
* if min isn't an integer) and no greater than max (or the next integer
* lower than max if max isn't an integer).
* Using Math.round() will give you a non-uniform distribution!
*/
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
```
--------------------------------
### Initialize Popovers and Handle Small Screen Display
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_schedules.html
Initializes popovers and adjusts table visibility based on screen width. Hides the main schedule table and shows the mini version on screens smaller than 1000px.
```javascript
$(document).ready(function () {
$(' [data-toggle="popover"]').popover();
if ($(globalThis).width() < 1000) {
$('.too_small').popover("show");
document.getElementById('schedule_table_wrapper').hidden = true;
document.getElementById('mini_schedule_table_wrapper').hidden = false;
}
});
```
--------------------------------
### Get CSRF Cookie
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_admin_controls.html
Retrieves the value of a specified cookie, commonly used for Cross-Site Request Forgery (CSRF) protection in web applications.
```javascript
function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=(\[^;\\]*)\\b");
return r ? r[1] : undefined;
}
```
--------------------------------
### Get Cookie for XSRF Protection
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/panel_edit_user_apikeys.html
Utility function to retrieve cookies, specifically used for obtaining the XSRF token required for secure requests.
```javascript
function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=(\[^;]\*)\\b");
return r ? r[1] : undefined;
}
```
--------------------------------
### Create or Update Server Backup Configuration
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_backup_edit.html
Handles the creation or update of a server backup configuration. It formats form data into JSON and makes a PATCH or POST request to the server API. Redirects to the server detail page on success or shows an error message.
```javascript
delete formDataObject.root_path
console.log(formDataObject);
// Format the plain form data as JSON
let formDataJsonString = JSON.stringify(formDataObject, replacer);
console.log(formDataJsonString);
let url = `/api/v2/servers/${serverId}/backups/backup/${backup_id}/`
let method = "PATCH"
if (!backup_id) {
url = `/api/v2/servers/${serverId}/backups/`
method = "POST";
}
let res = await fetch(url, {
method: method,
headers: {
'X-XSRFToken': token
},
body: formDataJsonString,
});
let responseData = await res.json();
if (responseData.status === "ok") {
window.location.href = `/panel/server_detail?id=${serverId}&subpage=backup`;
} else {
bootbox.alert({ title: responseData.error, message: responseData.error_data });
});
```
--------------------------------
### Conditional Server Creation Menu Item
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/main_menu.html
Renders a 'New Server' menu item only if the user has the 'Server_Creation' permission.
```html
{% if data['crafty_permissions']['Server_Creation'] in data['user_crafty_permissions'] %}* [{{ translate('sidebar', 'newServer', data['lang']) }}](/server/step1)
{% end %}
```
--------------------------------
### Countdown Timer for Cooldown
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/public/login.html
JavaScript function to start and manage a countdown timer for login cooldown periods. It updates a display element with the remaining time.
```javascript
function startCountdown(minutes, seconds) {
let totalSeconds = parseInt(minutes) * 60 + parseInt(seconds);
const interval = setInterval(() => {
const mins = Math.floor(totalSeconds / 60);
const secs = totalSeconds % 60;
const formattedTime = `${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
$("#cooldown").html(formattedTime);
// Stop timer at 0
if (totalSeconds === 0) {
clearInterval(interval);
$("#error-field").html("")
}
totalSeconds--;
}, 1000);
}
```
--------------------------------
### Get CSRF Cookie
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/parts/details_stats.html
Retrieves the CSRF token cookie, which is used for security purposes in web applications, particularly with Tornado's XSRF protection.
```javascript
function getCookie(name) { var r = document.cookie.match("\\b" + name + "=(\[^;]\*)\\b"); return r ? r[1] : undefined; }
const token = getCookie("_xsrf")
```
--------------------------------
### JavaScript WebSocket Initialization and Reconnection Logic
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/base.html
Sets up WebSocket connection, handles messages, errors, and automatic reconnection with exponential backoff. Includes logic to display a warning if WebSockets are required but closed.
```javascript
let usingWebSockets = false;
let webSocket = null;
let websocketTimeoutId = null;
// {% if request.protocol == 'https' %}
usingWebSockets = true;
let listenEvents = [] ;
let wsOpen = false;
/**
* @type {number | null} reconnectorId An interval ID for the reconnector.
*/
let reconnectorId = null;
let failedConnectionCounter = 0;
// https://stackoverflow.com/a/37038217/15388424
const wsPageQueryParams = 'page_query_params=' + encodeURIComponent(location.search)
const wsPage = 'page=' + encodeURIComponent(location.pathname)
const sendWssError = () => wsOpen || warn( 'WebSockets are required for Crafty to work. This websocket connection has been closed. Are you using a reverse proxy?', link = 'https://docs.craftycontrol.com/pages/getting-started/proxies/', link_msg = "See our documentation for details", className = 'wssError'
)
function startWebSocket() {
console.log('%c[Crafty Controller] %cConnecting the WebSocket', 'font-weight: 900; color: #800080;', 'font-weight: 900; color: #eee;');
try {
var wsInternal = new WebSocket('wss://' + location.host + '/ws?' + wsPage + '&' + wsPageQueryParams);
wsInternal.onopen = function () {
console.log('opened WebSocket connection:', wsInternal)
wsOpen = true;
failedConnectionCounter = 0;
if (typeof reconnectorId === 'number') {
document.querySelectorAll('.wssError').forEach(el => el.remove())
clearInterval(reconnectorId);
reconnectorId = null;
}
};
wsInternal.onmessage = function (rawMessage) {
var message = JSON.parse(rawMessage.data);
console.log('got message: ', message)
listenEvents
.filter(listenedEvent => listenedEvent.event == message.event)
.forEach(listenedEvent => listenedEvent.callback(message.data))
};
wsInternal.onerror = function (errorEvent) {
console.error('WebSocket Error', errorEvent);
};
wsInternal.onclose = function (closeEvent) {
wsOpen = false;
console.log('Closed WebSocket', closeEvent);
if (!document.hidden) {
if (typeof reconnectorId !== 'number') {
setTimeout(sendWssError, 7000);
}
console.info("Reconnecting with a timeout of", (getRandomArbitrary(0, 2 ** failedConnectionCounter - 1) + 5) * 1000, "milliseconds");
// Discard old websocket and create a new one in 5 seconds
wsInternal = null
reconnectorId = setTimeout(startWebSocket, (getRandomArbitrary(0, 2 ** failedConnectionCounter - 1) + 5) * 1000)
failedConnectionCounter++;
}
};
webSocket = {
on: function (event, callback) {
console.log('registered ' + event + ' event');
listenEvents.push({ event: event, callback: callback })
},
emit: function (event, data) {
var message = { event: event, data: data }
wsInternal.send(JSON.stringify(message));
},
close: function (code, reason) {
wsInternal.close(code, reason);
},
getStatus: function () {
return wsInternal.readyState;
}
}
} catch (error) {
console.error('Error while making websocket helpers', error);
usingWebSockets = false;
}
}
startWebSocket();
// {% else %}
```
--------------------------------
### Get Server ID from URL
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_admin_controls.html
Extracts the server ID from the URL's query parameters. This is used to identify the target server for subsequent operations.
```javascript
const serverId = new URLSearchParams(document.location.search).get('id')
```
--------------------------------
### Build and Run Crafty Controller from Source using Docker
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/README.md
Builds a Docker image from the current directory (containing the Dockerfile) and then runs it as a container. This is for users who want to build from the cloned repository.
```sh
# REMEMBER, Build your image first!
$ docker build . -t crafty
$ docker run \
--name crafty_container \
--detach \
--restart always \
-P 5520-5550:5520-5550/udp \
-p 8000:8000 \
-p 8443:8443 \
-p 8123:8123 \
-p 19132:19132/udp \
-p 25500-25600:25500-25600 \
-e TZ=Etc/UTC \
-v "/$(pwd)/docker/backups:/crafty/backups" \
-v "/$(pwd)/docker/logs:/crafty/logs" \
-v "/$(pwd)/docker/servers:/crafty/servers" \
-v "/$(pwd)/docker/config:/crafty/app/config" \
-v "/$(pwd)/docker/import:/crafty/import" \
crafty
```
--------------------------------
### Get CSRF Token Cookie
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_webhooks.html
Retrieves the CSRF token from browser cookies. This function is essential for authenticating POST and PATCH requests to the server, ensuring security.
```javascript
function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=([^;]*) \\b");
return r ? r[1] : undefined;
}
```
--------------------------------
### Initialize Popovers and Handle Small Screens
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/activity_logs.html
Initializes popovers and shows a specific popover when the window width is less than 1000px. This is useful for displaying contextual information on smaller displays.
```javascript
$(document).ready(function () {
$('["data-toggle"="popover"]').popover();
if ($(window).width() < 1000) {
$('.too_small').popover("show");
}
});
```
--------------------------------
### Initialize and Display File Tree
Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_backup_edit.html
This snippet initializes the file tree view after a delay, handling potential modal backdrop cleanup and setting the initial path for the tree. It's used when the server backup edit panel is loaded.
```javascript
$("#root_files_button").data('server_path') console.log($("#root_files_button").data('server_path')) const token = getCookie("\_xsrf"); var dialog = bootbox.dialog({ message: 'Please wait while we gather your files...
', closeButton: false }); setTimeout(function () { var x = document.querySelector('.bootbox'); if (x) { x.remove() } var x = document.querySelector('.modal-backdrop'); if (x) { x.remove() } document.getElementById('main-tree-input').setAttribute('value', path) getTreeView(path); show_file_tree(); }, 5000); } else { bootbox.alert("You must input a path before selecting this button"); } }); ``` -------------------------------- ### Start Image Rotation Animation Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/activity_logs.html Initiates the image rotation animation by calling the `rotateImage` function with a 360-degree rotation after a 2-second delay. This is typically used for introductory animations. ```javascript $(document).ready(function () { setTimeout(function () { rotateImage(360); }, 2000); }); ``` -------------------------------- ### Get Server and Backup IDs Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_backup_edit.html Retrieves the server ID and backup ID from the URL query parameters. These IDs are crucial for API calls related to server backups. ```javascript const serverId = new URLSearchParams(document.location.search).get('id') const backup_id = new URLSearchParams(document.location.search).get('backup_id') ``` -------------------------------- ### Toggle Backup Configuration Visibility Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_backup_edit.html Handles the click event for a 'Show Configuration' button to toggle the visibility of the backup configuration box and related elements. ```javascript $("#backup_config_box").hide(); $("#backup_save_note").hide(); $("#show_config").click(function () { $("#backup_config_box").toggle(); $('#backup_button').hide(); $('#backup_save_note').show(); $('#backup_data').hide(); }); ``` -------------------------------- ### Password Reset API Call Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/public/login.html Asynchronous JavaScript function to call the password reset API. It makes a GET request and displays the response data in an alert box. ```javascript async function resetPass() { let res = await fetch(`/api/v2/crafty/resetPass/`, { method: 'GET', }); let responseData = await res.json(); console.log(responseData); bootbox.alert(responseData.data) } ``` -------------------------------- ### Document Ready Event Handlers Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/server_backup_edit.html Sets up event listeners when the DOM is ready. This includes handlers for backup explanation popups, cancel button navigation, and WebSocket 'backup_status' events for real-time progress updates. ```javascript $(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=${serverId}&subpage=backup` }); webSocket.on('backup_status', function (backup) { text = ``; $("#${backup.backup_id}_status").show() if (backup.percent >= 100) { $("#${backup.backup_id}_status").hide() setTimeout(function () { window.location.reload(1); }, 5000); } else { text = `` $("#${backup.backup_id}_status").html(text); } }); }) ``` -------------------------------- ### Initialize Popovers and Handle Window Resize Source: https://gitlab.com/crafty-controller/crafty-4/-/blob/master/app/frontend/templates/panel/panel_config.html Initializes popovers on elements with 'data-toggle="popover"' and shows/hides specific popovers ('too_small', 'too_small2') based on the window width, particularly for smaller screens. ```javascript $(document).ready(function () { $('["data-toggle"="popover"]').popover(); if ($(window).width() < 1000) { $('.too_small').popover("show"); $('.too_small2').popover("show"); } }); $(window).ready(function () { $('body').click(function () { $('.too_small').popover("hide"); $('.too_small2').popover("hide"); }); }); $(window).resize(function () { // This will execute whenever the window is resized if ($(window).width() < 1000) { $('.too_small').popover("show"); } else { $('.too_small').popover("hide"); } // New width if ($(window).width() < 1000) { $('.too_small2').popover("show"); } else { $('.too_small2').popover("hide"); } // New width }); ```