{% extends ../base.html %} {% block meta %} {% end %} {% block title %}Crafty Controller - {{ translate('dashboard', 'dashboard', data['lang']) }}{% end %} {% block content %} <div class="content-wrapper"> <!-- Page Title Header Starts--> <div class="row page-title-header"> <div class="col-12"> <div class="page-header"> <h4 class="page-title">{{ translate('dashboard', 'dashboard', data['lang']) }} {% if data['server_stats']['running'] != 0 %} <span id="sync" style="margin-left: 5px;"><i class="fas fa-sync fa-spin"></i></span> </h4> {% end %} </div> </div> </div> <!-- Page Title Header Ends--> {% if data['first_log'] %} <script> $(document).ready(function () { bootbox.alert({ backdrop: true, title: 'Your Feedback Is Appreciated...', message: '<p>We will only request this information from the admin user once... 😊</p>' + '<p><strong>All data collected is completely anonymous</strong> and is only used to improve Crafty 4.0 and allow us to more accurately report the number of Crafty 4 users.</p><iframe width="640px" height="480px"' + 'src="https://forms.office.com/Pages/ResponsePage.aspx?id=LwLajNkpXU2CKc95G1oO3MN0Hu3oEUNLr-EtLx31TS5UNUNVQlFNVUVYMEc' + '1V1BKS0FQUUlERUtWQy4u&embed=true" frameborder="0" marginwidth="0" marginheight="0" style="border: none; max-width:100%;' + ' max-height:100vh" allowfullscreen webkitallowfullscreen mozallowfullscreen msallowfullscreen> </iframe>', buttons: { ok: { label: 'Skip Survey/Done', className: 'btn-secondary' }, }, }); }); </script> {% end %} <div class="row"> <div class="col-md-12 grid-margin"> <div class="card"> <div class="card-body"> <div class="row"> <div class="col-xl-4 col-md-5"> <div class="d-flex"> <div class="wrapper"> <h5 class="mb-1 font-weight-medium text-primary"> {{ translate('dashboard', 'host', data['lang']) }} </h5> <h3 class="mb-0 font-weight-semibold"> <i class="fas fa-chart-line"></i></h3> </div> <div class="wrapper my-auto ml-auto ml-lg-4"> <p id="cpu_data" class="mb-0 text-success" data-toggle="tooltip" data-placement="top" data-html="true" title="{% raw translate('dashboard', 'cpuCores', data['lang']) %}: {{ data.get('hosts_data').get('cpu_cores') }} <br /> {% raw translate('dashboard', 'cpuCurFreq', data['lang']) %}: {{ data.get('hosts_data').get('cpu_cur_freq') }} <br /> {% raw translate('dashboard', 'cpuMaxFreq', data['lang']) %}: {{ data.get('hosts_data').get('cpu_max_freq') }}"> {{ translate('dashboard', 'cpuUsage', data['lang']) }}: <span id="cpu_usage">{{ data.get('hosts_data').get('cpu_usage') }}</span> </p> <p id="mem_usage" class="mb-0 text-danger" data-toggle="tooltip" data-placement="top" title="{{ translate('dashboard', 'memUsage', data['lang']) }}: {{ data.get('hosts_data').get('mem_usage') }}"> {{ translate('dashboard', 'memUsage', data['lang']) }}: <span id="mem_percent">{{ data.get('hosts_data').get('mem_percent') }}%</span> </p> </div> </div> </div> <div class="col-xl-4 col-md-4 mt-md-0 mt-4"> <div class="d-flex"> <div class="wrapper"> <h5 class="mb-1 font-weight-medium text-primary">{{ translate('dashboard', 'servers', data['lang']) }} </h5> <h3 class="mb-0 font-weight-semibold">{{ data['server_stats']['total'] }}</h3> </div> <div class="wrapper my-auto ml-auto ml-lg-4"> <p class="mb-0 text-success">{{ data['server_stats']['running'] }} {{ translate('dashboard', 'online', data['lang']).lower() }}</p> <p class="mb-0 text-warning"> {{ data['server_stats']['stopped'] }} {{ translate('dashboard', 'offline', data['lang']).lower() }}</p> </div> </div> </div> <div class="col-xl-4 col-md-3 mt-md-0 mt-4"> <div class="d-flex"> <div class="wrapper"> <h5 class="mb-1 font-weight-medium text-primary">{{ translate('dashboard', 'players', data['lang']) }} </h5> <h3 class="mb-0 font-weight-semibold" id="total_players">{{ data['num_players'] }}</h3> </div> <div class="wrapper my-auto ml-auto ml-lg-4"> <p class="mb-0 text-warning"><span id="max_players">0</span> {{ translate('dashboard', 'max', data['lang']) }}</p> </div> </div> </div> {% if len(data['hosts_data']['disk_json']) > 0 %} <div class="col-12 mt-4"> <div class="d-flex"> <div class="wrapper" style="width: 100%;"> <h5 class="mb-1 font-weight-medium text-primary">Storage </h5> <div id="storage_data"> <div class="row"> {% for item in data['hosts_data']['disk_json'] %} {% if item["mount"] in data["monitored"] %} <div id="{{item['device']}}" class="col-xl-3 col-lg-3 col-md-4 col-12"> <h4 class="mb-0 font-weight-semibold d-inline-block text-truncate storage-heading" id="title_{{item['device']}}" data-toggle="tooltip" data-placement="bottom" title="{{item['mount']}}" style="max-width: 100%;"><i class="fas fa-hdd"></i> {{item["mount"]}}</h4> <div class="progress d-inline-block" style="height: 20px; width: 100%; background-color: rgb(139, 139, 139) !important;"> <div class="progress-bar {% if item['percent_used'] <= 58 %} bg-success {% elif 59 <= item['percent_used'] <= 75 %} bg-warning {% else %} bg-danger {% end %} " role="progressbar" style="color: black; height: 100%; width: {{item['percent_used']}}%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">{{item["used"]}} / {{item["total"]}} </div> </div> </div> {% end %} {% end %} </div> </div> </div> </div> </div> {% end %} </div> </div> </div> </div> </div> <div class="row"> <div class="col-md-12 col-lg-12 grid-margin stretch-card"> <div class="card"> <div class="card-header header-sm d-flex justify-content-between align-items-center"> <h4 class="card-title"><i class="fas fa-server"></i> {{ translate('dashboard', 'allServers', data['lang']) }}</h4> {% if len(data['servers']) > 0 %} {% if data['user_data']['hints'] %} <span class="too_small" title="{{ translate('dashboard', 'cannotSeeOnMobile', data['lang']) }}" , data-content="{{ translate('dashboard', 'cannotSeeOnMobile2', data['lang']) }}" , data-placement="top"></span> {% end %} {% end %} <div><a class="nav-link" href="/server/step1"><i class="fas fa-plus-circle"></i> {{ translate('dashboard', 'newServer', data['lang']) }}</a></div> </div> <div class="card-body"> {% if len(data['servers']) == 0 and len(data['failed_servers']) == 0 %} <div style="text-align: center; color: grey;"> <h1>{{ translate('dashboard', 'welcome', data['lang']) }}</h1> <br> <h7>{{ translate('dashboard', 'no-servers', data['lang']) }} {{ translate('dashboard', 'newServer', data['lang']) }}.</h7> </div> {% end %} {% if len(data['servers']) > 0 or len(data['failed_servers']) > 0 %} <!-- View for Large screen --> <div class="table-responsive d-none d-sm-block"> <table id="servers_table" class="table table-hover"> <thead> <tr class="rounded" id="first" draggable="false"> <th draggable="false">{{ translate('dashboard', 'server', data['lang']) }}</th> <th draggable="false">{{ translate('dashboard', 'actions', data['lang']) }}</th> <th draggable="false">{{ translate('dashboard', 'cpuUsage', data['lang']) }}</th> <th draggable="false">{{ translate('dashboard', 'memUsage', data['lang']) }}</th> <th draggable="false">{{ translate('dashboard', 'size', data['lang']) }}</th> <th draggable="false">{{ translate('dashboard', 'players', data['lang']) }}</th> <th draggable="false">{{ translate('dashboard', 'status', data['lang']) }}</th> </tr> </thead> <tbody> {% for server in data['servers'] %} <tr id="{{server['server_data']['server_id']}}" draggable="true"> <td draggable="false"> <i class="fas fa-server"></i> {% if server['alert'] %} <a style="color: red !important;" draggable="false" href="/panel/server_detail?id={{server['server_data']['server_id']}}"> {{ server['server_data']['server_name'] }} <i class="fas fa-exclamation-triangle"></i> </a> {% else %} <a draggable="false" href="/panel/server_detail?id={{server['server_data']['server_id']}}"> {{ server['server_data']['server_name'] }} </a> {% end %} </td> <td draggable="false" id="controls{{server['server_data']['server_id']}}" class="actions_serverlist"> {% if server['user_command_permission'] %} {% if server['stats']['importing'] and server['stats']['running'] %} <!-- WHAT HAPPENED HERE --> <a data-id="{{server['server_data']['server_id']}}" class=""><i class="fa fa-spinner fa-spin"></i> {{ translate('serverTerm', 'installing', data['lang']) }}</i></a> {% elif server['stats']['updating']%} <!-- WHAT HAPPENED HERE --> <a data-id="{{server['server_data']['server_id']}}" class=""><i class="fa fa-spinner fa-spin"></i> {{ translate('serverTerm', 'updating', data['lang']) }}</i></a> {% elif server['stats']['waiting_start']%} <!-- WHAT HAPPENED HERE --> <a data-id="{{server['server_data']['server_id']}}" class="" title="{{ translate('dashboard', 'delay-explained' , data['lang'])}}">{{ translate('dashboard', 'starting', data['lang']) }}</i></a> {% elif server['stats']['importing']%} <a data-id="{{server['server_data']['server_id']}}" class=""><i class="fa fa-spinner fa-spin"></i> {{ translate('serverTerm', 'importing', data['lang']) }}</a> {% elif server['stats']['running'] %} <a data-id="{{server['server_data']['server_id']}}" class="stop_button" data-toggle="tooltip" title="{{ translate('dashboard', 'stop' , data['lang']) }}"> <i class="fas fa-stop"></i> </a> <a data-id="{{server['server_data']['server_id']}}" class="restart_button" data-toggle="tooltip" title="{{ translate('dashboard', 'restart' , data['lang']) }}"> <i class="fas fa-sync"></i> </a> <a data-id="{{server['server_data']['server_id']}}" class="kill_button" data-toggle="tooltip" title="{{ translate('dashboard', 'kill' , data['lang']) }}"> <i class="fas fa-skull"></i> </a> {% else %} <a data-id="{{server['server_data']['server_id']}}" class="play_button" data-toggle="tooltip" title="{{ translate('dashboard', 'start' , data['lang']) }}"> <i class="fas fa-play"></i> </a> <a data-id="{{server['server_data']['server_id']}}" class="clone_button" data-toggle="tooltip" title="{{ translate('dashboard', 'clone' , data['lang']) }}"> <i class="fas fa-clone"></i> </a> <a data-id="{{server['server_data']['server_id']}}" class="kill_button" data-toggle="tooltip" title="{{ translate('dashboard', 'kill' , data['lang']) }}"> <i class="fas fa-skull"></i> </a> {% end %} {% end %} </td> <td draggable="false" id="server_cpu_{{server['server_data']['server_id']}}"> <div class="progress mb-1" data-toggle="tooltip" data-placement="top" title="{{server['stats']['cpu']}}"> <div class="progress-bar {% if server['stats']['cpu'] <= 33 %} bg-success {% elif 34 <= server['stats']['cpu'] <= 66 %} bg-warning {% else %} bg-danger {% end %} " role="progressbar" style="width: {{server['stats']['cpu']}}%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div> </div> {{server['stats']['cpu']}}% </td> <td draggable="false" id="server_mem_{{server['server_data']['server_id']}}"> <div class="progress mb-1" data-toggle="tooltip" data-placement="top" title="{{server['stats']['mem']}}"> <div class="progress-bar {% if server['stats']['mem_percent'] <= 33 %} bg-success {% elif 34 <= server['stats']['mem_percent'] <= 66 %} bg-warning {% else %} bg-danger {% end %} " role="progressbar" style="width: {{server['stats']['mem_percent']}}%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div> </div> {{server['stats']['mem_percent']}}% - {% if server['stats']['mem'] == 0 %} 0 MB {% else %} {{server['stats']['mem']}} {% end %} </td> <td draggable="false" id="server_world_{{server['server_data']['server_id']}}"> {{ server['stats']['world_size'] }} </td> <td draggable="false" id="server_desc_{{server['server_data']['server_id']}}"> {% if server['stats']['int_ping_results'] %} {{ server['stats']['online'] }} / {{ server['stats']['max'] }} {{ translate('dashboard', 'max', data['lang']) }} <br /> {% if server['stats']['desc'] != 'False' %} <div id="desc_id" style="overflow-wrap: break-word !important; max-width: 85px !important; overflow: scroll;">{{ server['stats']['desc'] }}</div> <br /> {% end %} {% if server['stats']['version'] != 'False' %} {{ server['stats']['version'] }} {% end %} {% end %} </td> <td draggable="false"> <span class="port" data-toggle="tooltip" title="{{ server['server_data']['server_port'] }}"> <div id="server_running_status_{{server['server_data']['server_id']}}"> {% if server['stats']['running'] %} <span class="text-success"><i class="fas fa-signal"></i> {{ translate('dashboard', 'online', data['lang']) }}</span> {% elif server['stats']['crashed'] %} <span class="text-danger"><i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed', data['lang']) }}</span> {% else %} <span class="text-warning"><i class="fas fa-ban"></i> {{ translate('dashboard', 'offline', data['lang']) }}</span> {% end %} <br /> <br /> </td> <span class="server-player-totals" id="server_players_{{server['server_data']['server_id']}}" data-players="{{ server['stats']['online']}}" data-max="{{ server['stats']['max'] }}"></span> </tr> {% end %} </div> </span> {% for server in data['failed_servers'] %} <tr id="{{server['server_id']}}" draggable="false"> <td class="text-warning"><i class="fas fa-server"></i> <a class="text-warning" href="/panel/server_detail?id={{server['server_id']}}&subpage=config">{{server['server_name']}}</a> </td> <td></td> <td></td> <td></td> <td></td> <td></td> <td><i class="fas fa-cloud"></i> Unloaded</td> </tr> {% end %} </tbody> </table> </div> {% end %} {% if len(data['servers']) > 0 %} <!-- View for Small screen --> <div class="d-sm-none d-block"> <div class="accordion" id="accordionServers"> {% for server in data['servers'] %} <div class="card"> <div class="card-header" id="heading-{{server['server_data']['server_id']}}"> <h2 class="mb-0 container overflow-hidden"> <div class="row"> <div class="col-10 col-lg-3 mx-0 px-0"> {% if server['alert'] %} <a style="color: red !important" class="btn btn-link d-flex justify-content-start" type="button" href="/panel/server_detail?id={{server['server_data']['server_id']}}"> <i class="fas fa-server"></i> {{ server['server_data']['server_name'] }} <i class="fas fa-exclamation-triangle"></i> </a> {% else %} <a class="btn btn-link d-flex justify-content-start" type="button" href="/panel/server_detail?id={{server['server_data']['server_id']}}"> <i class="fas fa-server"></i> {{ server['server_data']['server_name'] }} </a> {% end %} </div> <div class="col-2 col-lg-3 mx-0 px-0"> <a class="btn btn-link d-flex justify-content-center" type="button" data-toggle="collapse" data-target="#collapse-{{server['server_data']['server_id']}}" aria-expanded="false" aria-controls="collapse-{{server['server_data']['server_id']}}"> <i class="fas fa-chart-bar"></i> </a> </div> <div class="col-4 col-lg-3 mx-0 px-0"> <a id="m_server_running_status_{{server['server_data']['server_id']}}" class="btn btn-link d-flex justify-content-start" type="button"> {% if server['stats']['running'] %} <span class="text-success"><i class="fas fa-signal"></i> {{ translate('dashboard', 'online', data['lang']) }}</span> {% elif server['stats']['crashed'] %} <span class="text-danger"><i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed', data['lang']) }}</span> {% else %} <span class="text-warning"><i class="fas fa-ban"></i> {{ translate('dashboard', 'offline', data['lang']) }}</span> {% end %} </a> </div> <div class="col-8 col-lg-3 mx-0 px-0"> <div id="controls{{server['server_data']['server_id']}}" class="container overflow-hidden"> {% if server['user_command_permission'] %} {% if server['stats']['running'] %} <div class="row"> <div class="col-4 px-0"> <a data-id="{{server['server_data']['server_id']}}" class="btn btn-link stop_button actions_serveritem" data-toggle="tooltip" title="{{ translate('dashboard', 'stop' , data['lang']) }}"> <i class="fas fa-stop"></i> </a> </div> <div class="col-4 px-0"> <a data-id="{{server['server_data']['server_id']}}" class="btn btn-link restart_button actions_serveritem" data-toggle="tooltip" title="{{ translate('dashboard', 'restart' , data['lang']) }}"> <i class="fas fa-sync"></i> </a> </div> <div class="col-4 px-0"> <a data-id="{{server['server_data']['server_id']}}" class="btn btn-link kill_button actions_serveritem" data-toggle="tooltip" title="{{ translate('dashboard', 'kill' , data['lang']) }}"> <i class="fas fa-skull"></i> </a> </div> </div> {% elif server['stats']['updating']%} <!-- WHAT HAPPENED HERE --> <div class="row"> <div class="col-12 px-0"> <a data-id="{{server['server_data']['server_id']}}" class="btn btn-link">{{ translate('serverTerm', 'updating', data['lang']) }}</i></a> </div> </div> {% elif server['stats']['waiting_start']%} <!-- WHAT HAPPENED HERE --> <div class="row"> <div class="col-12 px-0"> <a data-id="{{server['server_data']['server_id']}}" class="btn btn-link" title="{{ translate('dashboard', 'delay-explained' , data['lang'])}}">{{ translate('dashboard', 'starting', data['lang']) }}</i></a> </div> </div> {% elif server['stats']['importing']%} <div class="row"> <div class="col-12 px-0"> <a data-id="{{server['server_data']['server_id']}}" class="btn btn-link"><i class="fa fa-spinner fa-spin"></i> {{ translate('serverTerm', 'importing', data['lang']) }}</a> </div> </div> {% else %} <div class="row"> <div class="col-4 px-0"> <a data-id="{{server['server_data']['server_id']}}" class="btn play_button actions_serveritem" data-toggle="tooltip" title="{{ translate('dashboard', 'start' , data['lang']) }}"> <i class="fas fa-play"></i> </a> </div> <div class="col-4 px-0"> <a data-id="{{server['server_data']['server_id']}}" class="btn clone_button actions_serveritem" data-toggle="tooltip" title="{{ translate('dashboard', 'clone' , data['lang']) }}"> <i class="fas fa-clone"></i> </a> </div> <div class="col-4 px-0"> <a data-id="{{server['server_data']['server_id']}}" class="btn kill_button actions_serveritem" data-toggle="tooltip" title="{{ translate('dashboard', 'kill' , data['lang']) }}"> <i class="fas fa-skull"></i></a> </div> </div> {% end %} {% end %} </div> </div> </div> </h2> </div> <div id="collapse-{{server['server_data']['server_id']}}" class="collapse" aria-labelledby="heading-{{server['server_data']['server_id']}}" data-parent="#accordionServers"> <div class="card-body"> <div class="row"> <div class="col-6"> <h6>{{ translate('dashboard', 'cpuUsage', data['lang']) }}</h6> <div id="m_server_cpu_{{server['server_data']['server_id']}}"> <div class="progress mb-1" data-toggle="tooltip" data-placement="top" title="{{server['stats']['cpu']}}"> <div class="progress-bar {% if server['stats']['cpu'] <= 33 %} bg-success {% elif 34 <= server['stats']['cpu'] <= 66 %} bg-warning {% else %} bg-danger {% end %} " role="progressbar" style="width: {{server['stats']['cpu']}}%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div> </div> {{server['stats']['cpu']}}% </div> </div> <div class="col-6"> <h6>{{ translate('dashboard', 'memUsage', data['lang']) }}</h6> <div draggable="false" id="m_server_mem_{{server['server_data']['server_id']}}"> <div class="progress mb-1" data-toggle="tooltip" data-placement="top" title="{{server['stats']['mem']}}"> <div class="progress-bar {% if server['stats']['mem_percent'] <= 33 %} bg-success {% elif 34 <= server['stats']['mem_percent'] <= 66 %} bg-warning {% else %} bg-danger {% end %} " role="progressbar" style="width: {{server['stats']['mem_percent']}}%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div> </div> {{server['stats']['mem_percent']}}% - {% if server['stats']['mem'] == 0 %} 0 MB {% else %} {{server['stats']['mem']}} {% end %} </div> </div> </div> <br /> <div class="row"> <div class="col-6"> <h6>{{ translate('dashboard', 'size', data['lang']) }}</h6> <div draggable="false" id="m_server_world_{{server['server_data']['server_id']}}"> {{ server['stats']['world_size'] }} </div> </div> <div class="col-6" style="width: auto;"> <h6>{{ translate('dashboard', 'players', data['lang']) }}</h6> <div draggable="false" id="m_server_desc_{{server['server_data']['server_id']}}"> {% if server['stats']['int_ping_results'] %} {{ server['stats']['online'] }} / {{ server['stats']['max'] }} {{ translate('dashboard', 'max', data['lang']) }} <br /> {% if server['stats']['desc'] != 'False' %} <div id="desc_id" style="overflow-wrap: break-word !important; max-width: 85px !important; overflow: scroll;"> {{ server['stats']['desc'] }}</div> <br /> {% end %} {% if server['stats']['version'] != 'False' %} {{ server['stats']['version'] }} {% end %} {% end %} </div> </div> </div> </div> </div> </div> {% end %} </div> </div> {% end %} </div> </div> </div> </div> </div> <!-- content-wrapper ends --> <div id="mobile"></div> <style> .popover-body { color: white !important; ; } #desc_id { -ms-overflow-style: none; /* for Internet Explorer, Edge */ scrollbar-width: none; /* for Firefox */ overflow-y: scroll; } #desc_id::-webkit-scrollbar { display: none; /* for Chrome, Safari, and Opera */ } </style> {% end %} {% block js %} <script src="/static/assets/js/motd.js"></script> <script> function display_motd() { let all_motds = Array.from(document.getElementsByClassName('input_motd')); for (element of all_motds) { initParser(element.id, element.id); }; } $(document).ready(function () { $('[data-toggle="popover"]').popover(); if ($(window).width() < 1000) { $('.too_small').popover("show"); } }); $(window).ready(function () { $('body').click(function () { $('.too_small').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 }); </script> <script> function send_command(server_id, command) { /* this getCookie function is in base.html */ const token = getCookie("_xsrf"); $.ajax({ type: "POST", headers: { 'X-XSRFToken': token }, url: `/api/v2/servers/${server_id}/action/${command}`, success: function (data) { console.log("got response:"); console.log(data); if (command === "clone_server" && data.status === "ok") { window.location.reload(); } /*setTimeout(function () { if (command != 'start_server') { location.reload(); } }, 10000);*/ } }); } function warn(message, link = null, className = null) { var closeEl = document.createElement('span'); var strongEL = document.createElement('strong'); var msgEl = document.createElement('div'); closeEl.innerHTML = '×'; strongEL.textContent = 'Warning: '; msgEl.append(strongEL, message); closeEl.style.marginLeft = '15px'; closeEl.style.fontWeight = 'bold'; closeEl.style.float = 'right'; closeEl.style.fontSize = '22px'; closeEl.style.lineHeight = '20px'; closeEl.style.cursor = 'pointer'; closeEl.addEventListener('click', function () { this.parentElement.style.display = 'none'; }); var parentEl = document.createElement('div'); parentEl.style.padding = '20px'; parentEl.style.backgroundColor = '#f7970f'; parentEl.appendChild(closeEl); parentEl.appendChild(msgEl); if (link) { let linkEl = document.createElement('a') linkEl.href = link; linkEl.innerHTML = "See our documentation for details."; linkEl.style.color = 'white'; linkEl.style.textDecoration = 'underline'; linkEl.target = "_blank"; parentEl.appendChild(linkEl); } if (className) { parentEl.classList.add(className); } document.querySelector('.dynamicMsg').appendChild(parentEl); } function update_one_server_status(server) { /* Mobile view update */ server_cpu = document.getElementById('server_cpu_' + server.id); server_mem = document.getElementById('server_mem_' + server.id); server_world = document.getElementById('server_world_' + server.id); server_desc = document.getElementById('server_desc_' + server.id); server_online_status = document.getElementById('server_running_status_' + server.id); server_players = document.getElementById('server_players_' + server.id); total_players = document.getElementById('total_players'); /* Mobile view update */ m_server_cpu = document.getElementById('m_server_cpu_' + server.id); m_server_mem = document.getElementById('m_server_mem_' + server.id); m_server_world = document.getElementById('m_server_world_' + server.id); m_server_desc = document.getElementById('m_server_desc_' + server.id); m_server_online_status = document.getElementById('m_server_running_status_' + server.id); console.log("Received Data : " + server.id + ": " + server); /* TODO Update each element */ /* Update CPU */ cpu_status = "bg-danger"; if (server.cpu <= 33) { cpu_status = "bg-success"; } else if (server.cpu > 33 && server.cpu <= 66) { cpu_status = "bg-warning"; } server_cpu.innerHTML = `<div class="progress mb-1" data-toggle="tooltip" data-placement="top" title="` + server.cpu + `"><div class="progress-bar ` + cpu_status + `" role="progressbar" style="width: ` + server.cpu + `%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div></div>` + server.cpu + `%`; m_server_cpu.innerHTML = `<div class="progress mb-1" data-toggle="tooltip" data-placement="top" title="` + server.cpu + `"><div class="progress-bar ` + cpu_status + `" role="progressbar" style="width: ` + server.cpu + `%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div></div>` + server.cpu + `%`; /* Update Memory */ mem_status = "bg-danger"; total_mem = "0 MB"; if (server.mem_percent <= 33) { mem_status = "bg-success"; } else if (server.mem_percent > 33 && server.mem_percent <= 66) { mem_status = "bg-warning"; } if (server.mem !== 0) { total_mem = server.mem; } server_mem.innerHTML = `<div class="progress mb-1" data-toggle="tooltip" data-placement="top" title="` + server_mem + `"><div class="progress-bar ` + mem_status + `" role="progressbar" style="width: ` + server.mem_percent + `%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div></div>` + server.mem_percent + `% - ` + total_mem; m_server_mem.innerHTML = `<div class="progress mb-1" data-toggle="tooltip" data-placement="top" title="` + server_mem + `"><div class="progress-bar ` + mem_status + `" role="progressbar" style="width: ` + server.mem_percent + `%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div></div>` + server.mem_percent + `% - ` + total_mem; /* Update World Infos */ server_world.innerHTML = server.world_size m_server_world.innerHTML = server.world_size /* Update Server Infos */ if (server.int_ping_results) { /* Update Players */ if (server.players) { server_desc.innerHTML = server.online + ` / ` + server.max + ` {{ translate('dashboard', 'max', data['lang']) }}<br />` m_server_desc.innerHTML = server.online + ` / ` + server.max + ` {{ translate('dashboard', 'max', data['lang']) }}<br />` server_players.setAttribute('data-players', server.online); server_players.setAttribute('data-max', server.max); let servers = Array.from(document.getElementsByClassName("server-player-totals")); let all_total_players = 0; let all_total_max_players = 0; servers.forEach(server => { try { all_total_players += parseInt(server.getAttribute('data-players')); all_total_max_players += parseInt(server.getAttribute('data-max')); } catch { console.log("Player totals are not of type int"); } }) total_players.innerHTML = all_total_players; document.getElementById('max_players').innerHTML = all_total_max_players; document.getElementById('sync').innerHTML = ''; server_infos = ""; m_server_infos = ""; server_infos = server.online + " / " + server.max + " {{ translate('dashboard', 'max', data['lang']) }}<br />" } /* Update Motd */ let motd = ""; if (server.desc) { motd = `<span id="input_motd_` + server.id + `" class="input_motd">` + server.desc + `</span>`; m_server_infos = server_infos + '<div id="desc_id" style="word-wrap: break-word; overflow: auto;">' + motd + '</div>' + "<br />"; server_infos = server_infos + '<div id="desc_id" style="word-wrap: break-word; max-width: 85px !important; overflow: auto;">' + motd + '</div>' + "<br />"; } /* Version */ if (server.version) { server_infos = server_infos + server.version m_server_infos = m_server_infos + server.version } server_desc.innerHTML = server_infos; m_server_desc.innerHTML = m_server_infos; } /* Update Online Status */ let online_status = `<span class="text-warning"><i class="fas fa-ban"></i> {{ translate('dashboard', 'offline', data['lang'])}}</span>`; if (server.running) { online_status = `<span class="text-success"><i class="fas fa-signal"></i> {{ translate('dashboard', 'online', data['lang'])}}</span>`; } if (server.crashed) { online_status = `<span class="text-danger"><i class="fas fa-exclamation-triangle"></i> {{ translate('dashboard', 'crashed', data['lang'])}}</span>` } server_online_status.innerHTML = online_status; } function update_servers_status(data) { try { update_one_server_status(data[0]); } catch (e) { console.log('Failed to update server stats', e) } display_motd(); } $(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: '<span class="dynamicMsg">{% raw translate("dashboard", "sendingCommand", data["lang"]) %}</span>', message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> {% raw translate("dashboard", "bePatientStart", data["lang"]) %} </div>' }); setTimeout(finishTimeout, 60000); }); function finishTimeout() { warn("It seems this is taking a while...it's possible you're using UBlock or a similar ad blocker and it's causing some of our connections to not make it to the server. Try disabling your ad blocker.", null, 'wssError'); } $(".stop_button").click(function () { console.log("stopping server"); server_id = $(this).attr("data-id"); send_command(server_id, 'stop_server'); bootbox.alert({ backdrop: true, title: '{% raw translate("dashboard", "sendingCommand", data["lang"]) %}', message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> {% raw translate("dashboard", "bePatientStop", data["lang"]) %} </div>' }); }); $(".restart_button").click(function () { server_id = $(this).attr("data-id"); send_command(server_id, 'restart_server'); bootbox.alert({ backdrop: true, title: '{% raw translate("dashboard", "sendingCommand", data["lang"]) %}', message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> {% raw translate("dashboard", "bePatientRestart", data["lang"]) %} </div>' }); }); $(".kill_button").click(function () { server_id = $(this).attr("data-id"); bootbox.confirm({ message: "This will kill the server process and all it's subprocesses. Killing a process can potentially corrupt files. Only do this in extreme circumstances. Are you sure you would like to continue?", buttons: { confirm: { label: '{% raw translate("dashboard", "kill", data["lang"]) %}', className: 'btn-danger' }, cancel: { label: '{% raw translate("panelConfig", "cancel", data["lang"]) %}', className: 'btn-secondary' } }, callback: function (result) { if (result) { send_command(server_id, "kill_server"); let dialog = bootbox.dialog({ title: '{% raw translate("dashboard", "killing", data["lang"]) %}', message: '<p><i class="fa fa-spin fa-spinner"></i> Loading...</p>' }); } } }); }); if (webSocket) { cpu_data = document.getElementById('cpu_data'); cpu_usage = document.getElementById('cpu_usage'); mem_usage = document.getElementById('mem_usage'); mem_percent = document.getElementById('mem_percent'); webSocket.on('update_host_stats', function (hostStats) { let cpuDataTitle = `{% raw translate('dashboard', 'cpuCores', data['lang']) %}: ${hostStats.cpu_cores} <br /> {% raw translate("dashboard", "cpuCurFreq", data['lang']) %}: ${hostStats.cpu_cur_freq} <br /> {% raw translate("dashboard", "cpuMaxFreq", data['lang']) %}: ${hostStats.cpu_max_freq}`; cpu_data.setAttribute('data-original-title', cpuDataTitle); cpu_usage.textContent = hostStats.cpu_usage; mem_usage.setAttribute('data-original-title', `{% raw translate("dashboard", "memUsage", data['lang']) %}: ${hostStats.mem_usage}`); mem_percent.textContent = hostStats.mem_percent + '%'; var storage_html = '<div class="row">'; for (i = 0; i < hostStats.disk_usage.length; i++) { if (hostStats.mounts.includes(hostStats.disk_usage[i].mount)) { storage_html += `<div id="host_storage" class="col-xl-3 col-lg-3 col-md-4 col-12"> <h4 class="mb-0 font-weight-semibold d-inline-block text-truncate storage-heading" id="title_host_storage" data-toggle="tooltip" data-placement="bottom" title="${hostStats.disk_usage[i].mount}" style="max-width: 100%;"><i class="fas fa-hdd"></i> ${hostStats.disk_usage[i].mount}</h4> <div class="progress" style="display: inline-block; height: 20px; width: 100%; background-color: rgb(139, 139, 139) !important;"> <div class="progress-bar`; if (hostStats.disk_usage[i].percent_used <= 58) { storage_html += ` bg-success`; } else if (hostStats.disk_usage[i].percent_used <= 75 && hostStats.disk_usage[i].percent_used >= 59) { storage_html += ` bg-warning`; } else { storage_html += ` bg-danger`; } storage_html += `" role="progressbar" style="color: black; height: 100%; width: ${hostStats.disk_usage[i].percent_used}%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">${hostStats.disk_usage[i].used} / ${hostStats.disk_usage[i].total} </div> </div> </div>`; } } storage_html += `</div>`; $(".storage-heading").tooltip('hide'); $("#storage_data").html(storage_html); $("#storage_data").tooltip({ selector: '.storage-heading' }); }); } if (webSocket) { webSocket.on('send_start_reload', function () { location.reload() }); } if (webSocket) { webSocket.on('update_button_status', function (updateButton) { let serverId = updateButton.server_id; let message = updateButton.string; let updating = updateButton.isUpdating; let id = 'controls' + serverId; if (updating) { console.log(updating) document.getElementById(id).innerHTML = message; } else { window.location.reload() } }); } if (webSocket) { webSocket.on('update_server_status', update_servers_status); } $(".clone_button").click(function () { server_id = $(this).attr("data-id"); bootbox.confirm({ message: "{{ translate('dashboard', 'cloneConfirm' , data['lang']) }}", buttons: { confirm: { label: "{{ translate('dashboard', 'clone' , data['lang']) }}", className: 'btn-outline-warning' }, cancel: { label: '{% raw translate("panelConfig", "cancel", data["lang"]) %}', className: 'btn-secondary' } }, callback: function (result) { if (result) { send_command(server_id, 'clone_server'); bootbox.dialog({ backdrop: true, title: '{% raw translate("dashboard", "sendingCommand", data["lang"]) %}', message: '<div align="center"><i class="fas fa-spin fa-spinner"></i> {% raw translate("dashboard", "bePatientClone", data["lang"]) %} </div>', closeButton: false, }); } } }); }); }); </script> <script src="/static/assets/vendors/js/jquery-ui.js"></script> <link rel="stylesheet" href="/static/assets/vendors/css/jquery-ui.css"> <link rel="stylesheet" href="/static/assets/vendors/css/jquery-ui.structure.css"> <style> @media only screen and (max-width: 760px) { #mobile { display: none; } } .ui-sortable-handle { cursor: default; padding: 2px; } .ui-sortable-handle:hover { cursor: grab !important; padding: 2px; } </style> <script> /* Search Bar */ $(document).ready(function () { let servers_table = $('#servers_table').DataTable({ "ordering": false, // false to disable sorting (or any other option) "paging": false }); const first = document.querySelector('#first') first !== null && first.setAttribute('draggable', false); $('.dataTables_length').addClass('bs-select'); }); /* Search Bar End */ $(document).on("mousedown keyup click", function (event) { const value = $('.dataTables_filter input').val(); const is_mobile = $('#mobile').css('display') === 'none'; if ($("table#servers_table tbody").sortable("toArray").length > 1 && value === '' && !is_mobile) { $("table#servers_table tbody").sortable("enable"); } else { $("table#servers_table tbody").sortable("disable"); } }); $(document).ready(function () { function sendOrder(id_string) { const token = getCookie("_xsrf") $.ajax({ type: "PATCH", headers: { 'X-XSRFToken': token }, url: `/api/v2/users/@me`, data: JSON.stringify({ server_order: id_string, }), success: function (data) { console.log("got response:"); console.log(data); }, }); } // Inits the sortable $("table#servers_table tbody") .sortable({ items: '> tr', cursor: "grabbing", axis: "y", revert: true, handle: "i.fas.fa-server,a", forcePlaceholderSize: true, placeholder: 'table-placeholder', deactivate: function (event, ui) { // Gets the list of ids in the server list as an array, // and joins the elements with an [,] and sends to the server. const ids = $("table#servers_table tbody").sortable("toArray").join(); try { sendOrder(ids); } catch { console.log("Search is actively supressing order change") } }, }); // Give the table an actual width so the draggable does't collapse when dragged $("table#servers_table") .children() .find("td") .each(function () { $(this).css("width", "").css("width", $(this).outerWidth()); }); // Fixes the appearance of a scrollbar when a user tries to drag an item too low // Disabled on mobile since sorting is disabled on mobile if ($('#mobile').css('display') !== 'none') { $("div#servers_table_wrapper").css({ overflow: "hidden" }); } }); </script> {% end %}