Compare commits

..

No commits in common. "main" and "v0.2" have entirely different histories.
main ... v0.2

16 changed files with 184 additions and 639 deletions

1
README.md Normal file
View file

@ -0,0 +1 @@
# Hosting

View file

@ -204,51 +204,6 @@ def delete_file_or_folder():
return jsonify({"error": str(e)}), 500
@api.route('/files/content', methods=['GET'])
@oidc.require_login
def get_file_content():
username = oidc.user_getfield('preferred_username')
path = request.args.get('path')
base_path = os.path.abspath(f'./servers/mc-{username}')
file_path = os.path.abspath(os.path.join(base_path, path))
if not file_path.startswith(base_path) or not os.path.isfile(file_path):
return abort(403)
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
return jsonify({"success": True, "content": content})
except Exception as e:
return jsonify({"success": False, "error": str(e)}), 500
@api.route('/files/content', methods=['POST'])
@oidc.require_login
def save_file_content():
data = request.get_json()
username = data.get('username')
if username != oidc.user_getfield('preferred_username'):
return jsonify({"error": "Unauthorized request."}), 403
path = data.get('path')
content = data.get('content')
base_path = os.path.abspath(f'./servers/mc-{username}')
file_path = os.path.abspath(os.path.join(base_path, path))
if not file_path.startswith(base_path) or not os.path.isfile(file_path):
return abort(403)
try:
with open(file_path, 'w', encoding='utf-8') as f:
f.write(content)
return jsonify({"success": True})
except Exception as e:
return jsonify({"success": False, "error": str(e)}), 500
# Server config
@api.route('/config')
@oidc.require_login
@ -375,64 +330,31 @@ def download_mod():
server_version = data['version']
base_path = f"./servers/mc-{username}/{'plugins' if server_type == 'paper' else 'mods'}"
os.makedirs(base_path, exist_ok=True)
downloaded_files = []
visited_versions = set()
downloaded = []
def download_project(pid, version_id=None):
if version_id:
version_data = requests.get(f"https://api.modrinth.com/v2/version/{version_id}").json()
else:
all_versions = requests.get(f"https://api.modrinth.com/v2/project/{pid}/version").json()
version_data = next(
(v for v in all_versions
if server_version in v['game_versions']
and server_type in v['loaders']
and v.get('server_side') != 'unsupported'),
None)
version_data = next((v for v in all_versions if server_version in v['game_versions'] and server_type in v['loaders']), None)
if not version_data:
print(f"[SKIP] No compatible version found for {pid}")
return
if version_data["id"] in visited_versions:
return
visited_versions.add(version_data["id"])
if version_data.get('server_side') == 'unsupported':
print(f"[SKIP] {version_data.get('name', 'Nieznana nazwa')} is client-only")
return
file = next((f for f in version_data['files'] if f.get('primary', True)), version_data['files'][0])
file_path = os.path.join(base_path, file['filename'])
if not os.path.exists(file_path):
print(f"[DOWNLOAD] {file['filename']}")
file = version_data['files'][0]
file_path = f"{base_path}/{file['filename']}"
if file['filename'] not in downloaded:
with requests.get(file['url'], stream=True) as r:
with open(file_path, 'wb') as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
downloaded_files.append({
"filename": file['filename'],
"name": version_data.get("name") or version_data.get("version_number") or "Nieznana wersja",
"project_id": version_data["project_id"],
"version_id": version_data["id"]
})
downloaded.append(file['filename'])
for dep in version_data.get('dependencies', []):
if dep.get('version_id'):
try:
download_project(dep['project_id'], dep['version_id'])
except Exception as e:
print(f"[ERROR] Failed to fetch dep version {dep['version_id']}: {e}")
elif dep.get('project_id'):
try:
download_project(dep['project_id'])
except Exception as e:
print(f"[ERROR] Failed to fetch dep project {dep['project_id']}: {e}")
download_project(dep['project_id'], dep['version_id'])
download_project(project_id)
return jsonify({"success": True, "downloaded": downloaded_files})
return jsonify({"success": True, "downloaded": downloaded})

View file

@ -90,7 +90,6 @@ def start_server(username):
"EULA": "TRUE",
"SERVER_PORT": ports[0],
"ENABLE_RCON": "TRUE",
"USE_AIKAR_FLAGS": "TRUE",
"MOTD": f"Serwer użytkownika §9{username}",
"TYPE": server_type,
"VERSION": server_version,
@ -107,18 +106,6 @@ def start_server(username):
"MAX_MEMORY": "4G"
}
if server_config["andus-drasl"]:
drasl_args = (
"-Dminecraft.api.env=custom "
"-Dminecraft.api.auth.host=https://drasl.andus.ovh/auth "
"-Dminecraft.api.account.host=https://drasl.andus.ovh/account "
"-Dminecraft.api.session.host=https://drasl.andus.ovh/session "
"-Dminecraft.api.services.host=https://drasl.andus.ovh/services"
)
environment["JVM_OPTS"] = drasl_args
print(f"server {username} uses drasl {drasl_args}")
print(f"env: {environment}")
client.containers.run(
"itzg/minecraft-server",
detach=True,
@ -128,9 +115,9 @@ def start_server(username):
f"{ports[1]}/tcp": ports[1],
f"{ports[2]}/tcp": ports[2],
},
volumes=[
f"{os.path.abspath(path)}:/data"
],
volumes={
os.path.abspath(path): {'bind': '/data', 'mode': 'rw'}
},
environment=environment,
restart_policy={"Name": "unless-stopped"}
)

View file

@ -4,8 +4,6 @@ import random
from flask import Blueprint, render_template, jsonify
from .auth import oidc
from .port_utils import get_user_ports
from .docker_utils import client
from docker.errors import NotFound
from dotenv import load_dotenv
main = Blueprint('main', __name__, static_folder='static')
@ -14,14 +12,12 @@ load_dotenv()
@main.route("/")
@oidc.require_login
def home():
is_logged_in = oidc.user_loggedin
has_server = False
if is_logged_in:
username = oidc.user_getfield("preferred_username")
server_path = f"./servers/mc-{username}"
has_server = os.path.exists(server_path)
return render_template("home.html", has_server=has_server, is_logged_in=is_logged_in)
username = oidc.user_getfield("preferred_username")
server_path = f"./servers/mc-{username}"
has_server = os.path.exists(server_path)
return render_template("home.html", has_server=has_server)
@main.route('/setup')
@ -61,32 +57,6 @@ def list_ads():
return jsonify(files)
@main.route("/status")
def servers_status():
servers_dir = "./servers"
server_dirs = [
d for d in os.listdir(servers_dir)
if os.path.isdir(os.path.join(servers_dir, d)) and d.startswith('mc-')
]
server_data = []
for server_dir in server_dirs:
server_name = server_dir
server_status = "Offline"
try:
container = client.containers.get(server_name)
if container.status == "running":
server_status = "Online"
except NotFound:
pass
server_data.append({"name": server_name, "status": server_status})
return render_template('servers_list.html', servers=server_data)
@main.app_errorhandler(404)
def page_not_found(e):
return render_template("404.html"), 404

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

View file

@ -14,7 +14,6 @@ function loadConfig() {
document.getElementById("allow-nether").checked = cfg["allow-nether"];
document.getElementById("max-build-height").value = cfg["max-build-height"];
document.getElementById("view-distance").value = cfg["view-distance"];
document.getElementById("andus-drasl").value = cfg["andus-drasl"]
})
.catch(err => {
console.error("Błąd wczytywania konfiguracji: ", err);
@ -36,8 +35,7 @@ function saveConfig() {
spawn_animals: document.getElementById("spawn-animals").checked,
allow_nether: document.getElementById("allow-nether").checked,
max_build_height: document.getElementById("max-build-height").value,
view_distance: document.getElementById("view-distance").value,
andus_drasl: document.getElementById("andus-drasl").value
view_distance: document.getElementById("view-distance").value
};
fetch('/api/config', {

View file

@ -1,134 +0,0 @@
let codeMirrorEditor = null;
let currentEditingFilePath = null;
function getFileExtension(filename) {
const lastDot = filename.lastIndexOf('.');
if (lastDot === -1) return '';
return filename.slice(lastDot + 1);
}
function getCodeMirrorMode(filename) {
const ext = getFileExtension(filename).toLowerCase();
switch (ext) {
case 'js':
return 'javascript';
case 'json':
return {
name: "javascript",
json: true
};
case 'html':
case 'htm':
return 'htmlmixed';
case 'css':
return 'css';
case 'xml':
return 'xml';
case 'yml':
case 'yaml':
return 'yaml';
case 'properties':
return 'properties';
case 'log':
case 'txt':
return 'text/plain';
default:
return null;
}
}
async function openFileEditor(filePath) {
const fileExtension = getFileExtension(filePath);
const textFileExtensions = ['txt', 'log', 'json', 'yml', 'yaml', 'properties', 'html', 'css', 'js', 'xml', 'py'];
if (!textFileExtensions.includes(fileExtension.toLowerCase())) {
alert('Ten typ pliku nie może być edytowany bezpośrednio. Możesz go pobrać.');
return;
}
try {
const response = await fetch(`/api/files/content?username=${username}&path=${encodeURIComponent(filePath)}`);
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Server responded with status ${response.status}: ${errorText}`);
}
const data = await response.json();
if (data.success) {
document.getElementById('file-list').style.display = 'none';
document.getElementById('drop-zone').style.display = 'none';
document.getElementById('file-editor-container').style.display = 'block';
document.getElementById('editing-filename').textContent = filePath.split('/').pop();
currentEditingFilePath = filePath;
if (!codeMirrorEditor) {
codeMirrorEditor = CodeMirror.fromTextArea(document.getElementById('file-editor'), {
lineNumbers: true,
theme: 'material-darker',
mode: getCodeMirrorMode(filePath),
indentUnit: 4,
tabSize: 4,
indentWithTabs: false,
lineWrapping: true
});
} else {
codeMirrorEditor.setValue(data.content);
codeMirrorEditor.setOption('mode', getCodeMirrorMode(filePath));
}
codeMirrorEditor.refresh();
} else {
alert('Błąd podczas ładowania pliku: ' + (data.error || 'Nieznany błąd.'));
}
} catch (error) {
console.error('Błąd:', error);
alert('Wystąpił błąd podczas ładowania pliku: ' + error.message);
}
}
async function saveFileContent() {
if (!codeMirrorEditor || !currentEditingFilePath) {
alert('Brak pliku do zapisania.');
return;
}
const content = codeMirrorEditor.getValue();
try {
const response = await fetch('/api/files/content', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: username,
path: currentEditingFilePath,
content: content
})
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Server responded with status ${response.status}: ${errorText}`);
}
const data = await response.json();
if (data.success) {
alert('Plik został pomyślnie zapisany!');
} else {
alert('Błąd podczas zapisywania pliku: ' + (data.error || 'Nieznany błąd.'));
}
} catch (error) {
console.error('Błąd:', error);
alert('Wystąpił błąd podczas zapisywania pliku: ' + error.message);
}
}
function closeFileEditor() {
document.getElementById('file-editor-container').style.display = 'none';
document.getElementById('file-list').style.display = 'block';
document.getElementById('drop-zone').style.display = 'block';
currentEditingFilePath = null;
if (codeMirrorEditor) {
codeMirrorEditor.setValue('');
}
}

View file

@ -9,42 +9,25 @@ function loadFileList(path = '') {
if (path) {
list.innerHTML += `<li><a class="non-link" href="#" onclick="loadFileList('${path.split('/').slice(0, -1).join('/')}')">⬅️ ..</a></li>`;
}
data.sort((a, b) => {
if (a.is_dir === b.is_dir) {
return a.name.localeCompare(b.name);
}
return a.is_dir ? -1 : 1;
});
data.sort((a, b) => a.name.localeCompare(b.name));
data.forEach(entry => {
const itemPath = path ? `${path}/${entry.name}` : entry.name;
let html = '';
if (entry.is_dir) {
html = `<li>📁 <a class="non-link" href="#" onclick="loadFileList('${itemPath}')">${entry.name}</a>
<a class="non-link action-icon" onclick="deleteItem('${itemPath}')" title="Usuń folder">
const html = entry.is_dir
? `<li>📁 <a class="non-link" href="#" onclick="loadFileList('${itemPath}')">${entry.name}</a>
<a class="non-link" onclick="deleteItem('${itemPath}')">
<i class="fa-solid fa-trash"></i>
</a>
</li>`;
} else {
const fileExtension = getFileExtension(entry.name);
const editableExtensions = ['txt', 'log', 'json', 'yml', 'yaml', 'properties', 'html', 'css', 'js', 'xml', 'py'];
const isEditable = editableExtensions.includes(fileExtension.toLowerCase());
html = `<li>📄 ${entry.name}
${isEditable ? `
<a class="non-link action-icon" onclick="openFileEditor('${itemPath}')" title="Edytuj plik">
<i class="fa-solid fa-edit"></i>
</a>` : ''}
<a class="non-link action-icon" href="/api/files/download?username=${username}&path=${encodeURIComponent(itemPath)}" target="_blank" title="Pobierz plik">
</li>`
: `<li>📄 ${entry.name}
<a class="non-link" href="/api/files/download?username=${username}&path=${encodeURIComponent(itemPath)}" target="_blank">
<i class="fa-solid fa-download"></i>
</a>
<a class="non-link action-icon" onclick="deleteItem('${itemPath}')" title="Usuń plik">
<a class="non-link" onclick="deleteItem('${itemPath}')">
<i class="fa-solid fa-trash"></i>
</a>
</li>`;
}
list.innerHTML += html;
});
closeFileEditor();
});
}
@ -70,9 +53,7 @@ function deleteItem(path) {
});
}
document.querySelector('[onclick="showTab(\'files\')"]').addEventListener('click', () => {
loadFileList(); // Load root directory when the tab is opened
});
document.querySelector('[onclick="showTab(\'files\')"]').addEventListener('click', () => loadFileList());
const dropZone = document.getElementById('drop-zone');
const fileInput = document.getElementById('file-input');

View file

@ -33,36 +33,36 @@ function searchMods() {
}
function installMod(projectId) {
fetch('/api/config')
.then(response => response.json())
.then(configData => {
if (configData.success) {
const serverType = configData.type;
const serverVersion = configData.version;
if (serverType && serverVersion) {
fetch('/api/modrinth/download', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ project_id: projectId, username, type: serverType, version: serverVersion }),
})
.then(r => r.json())
.then(data => {
if (data.success) {
const modFiles = data.downloaded.map(mod => "- " + mod.filename).join("\n");
alert(`Zainstalowano:\n${modFiles}`);
loadFileList();
} else {
alert("Nie udało się zainstalować moda.");
}
});
fetch('/api/config')
.then(response => response.json())
.then(configData => {
console.log(configData);
if (configData.success) {
const serverType = configData.type;
const serverVersion = configData.version;
if (serverType && serverVersion) {
fetch('/api/modrinth/download', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ project_id: projectId, username, type: serverType, version: serverVersion }),
})
.then(r => r.json())
.then(data => {
if (data.success) {
alert(`Zainstalowano: ${data.file}`);
loadFileList();
} else {
alert("Nie udało się zainstalować moda.");
}
});
} else {
console.error('Server type or version not available');
}
} else {
console.error('Server type or version not available');
console.error('Server config not found');
}
} else {
console.error('Server config not found');
}
})
.catch(err => {
console.error('Error fetching server config:', err);
});
})
.catch(err => {
console.error('Error fetching server config:', err);
});
}

View file

@ -1,6 +1,3 @@
/* Imports */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap');
/* Reset & Base Styles */
* {
margin: 0;
@ -105,9 +102,8 @@ h2 {
/* Tabs */
.tabs {
/* Desktop default layout */
display: grid;
grid-template-columns: repeat(3, 1fr); /* Original desktop layout */
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 14px;
margin-bottom: 26px;
}
@ -122,11 +118,6 @@ h2 {
color: #ccc;
transition: background-color 0.25s, transform 0.2s;
box-shadow: 0 0 8px transparent;
/* Flexbox for text and icon alignment */
display: flex;
align-items: center;
justify-content: center; /* Center content horizontally */
gap: 8px; /* Space between text and icon */
}
.tab-button:hover {
@ -140,16 +131,6 @@ h2 {
box-shadow: 0 0 14px #4a5aef99;
}
.tab-button .tab-icon {
font-size: 1.2em; /* Adjust icon size as needed */
}
.tab-button .tab-text {
/* By default, text is visible on desktop */
display: block;
}
.square-button {
aspect-ratio: 1 / 1;
width: 200px;
@ -228,7 +209,6 @@ h2 {
.btn-primary { background: #4a5aef; color: white; box-shadow: 0 0 10px #4a5aef99; }
.btn-success { background: #2ecc71; color: white; box-shadow: 0 0 10px #2ecc7199; }
.btn-danger { background: #e74c3c; color: white; box-shadow: 0 0 10px #e74c3c99; }
.btn-worsedanger { background: #8b0000; color: white; box-shadow: 0 0 10px #8b000099; }
.btn-warning { background: #f39c12; color: black; box-shadow: 0 0 10px #f39c1288; }
.btn-info { background: #3498db; color: white; box-shadow: 0 0 10px #3498db99; }
@ -276,7 +256,18 @@ h2 {
background-color: #666;
}
body > *:not(.cursor-glow) {
/* Chart Container */
.chart-container {
background: #12121c;
padding: 20px;
border-radius: 14px;
margin-top: 30px;
border: 1px solid #232334;
box-shadow: 0 0 12px #1a1a2a;
overflow-x: auto;
}
body > *:not(script):not(style):not(.cursor-glow) {
animation: growIn 0.6s ease-out;
}
@ -586,197 +577,117 @@ a.non-link:hover {
color: #000;
}
/* Servers Status */
.server-table-container {
overflow-x: auto;
padding: 1rem;
border-radius: 16px;
background: rgba(14, 14, 26, 0.6);
backdrop-filter: blur(12px);
border: 1px solid #1a1a2a;
box-shadow: 0 0 24px rgba(74, 90, 239, 0.15);
}
.server-table {
width: 100%;
border-collapse: collapse;
color: #f0f0f0;
font-size: 0.95rem;
border-radius: 12px;
overflow: hidden;
}
.server-table thead {
background: linear-gradient(145deg, #1a1a2f, #23233a);
text-transform: uppercase;
letter-spacing: 0.05em;
font-size: 0.85rem;
color: #b9c5ff;
}
.server-table th,
.server-table td {
padding: 1rem;
text-align: left;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.server-table tbody tr:hover {
background: rgba(255, 255, 255, 0.03);
transition: background 0.3s ease;
}
.server-table th:first-child,
.server-table td:first-child {
border-left: none;
}
.server-table th:last-child,
.server-table td:last-child {
border-right: none;
}
.server-table tbody tr {
box-shadow: inset 0 0 0 rgba(0, 0, 0, 0);
transition: box-shadow 0.2s ease;
}
.server-table tbody tr:hover {
box-shadow: inset 0 0 12px rgba(255, 255, 255, 0.03);
}
.status-label {
display: inline-block;
padding: 0.4em 0.75em;
border-radius: 999px;
font-weight: 600;
font-size: 0.875rem;
text-transform: capitalize;
color: white;
}
.status-label.online {
background-color: #2ecc71;
}
.status-label.offline {
background-color: #e74c3c;
}
/* Mobile Tweaks */
@media (max-width: 768px) {
html {
font-size: clamp(15px, 4vw, 16px);
}
@media (max-width: 480px) {
html {
font-size: clamp(14px, 2.5vw, 16px);
}
body {
padding: 0 8px;
}
/* Heading adjustment */
h2 {
font-size: 1.5rem;
}
h1, h2, h3 {
line-height: 1.2;
}
.dashboard-card {
padding: 16px;
max-width: 100%;
box-sizing: border-box;
}
/* Tabs on Mobile: Single row, icons only */
.tabs {
display: flex; /* Use flexbox for a single row */
grid-template-columns: unset; /* Override grid for mobile */
justify-content: space-around; /* Distribute items evenly */
flex-wrap: nowrap; /* Keep tabs in a single row */
overflow-x: auto; /* Enable horizontal scrolling if buttons exceed screen width */
-webkit-overflow-scrolling: touch; /* Smooth scrolling on iOS */
padding-bottom: 5px; /* Add some padding for scrollbar if present */
gap: 8px; /* Slightly smaller gap on mobile */
}
.tab-button {
flex-shrink: 0; /* Prevent buttons from shrinking */
padding: 10px 15px; /* Adjust padding for icon-only buttons */
/* Center content for icon-only */
justify-content: center;
width: auto; /* Allow buttons to size to content */
}
.tab-button .tab-text {
display: none; /* Hide the text on mobile */
}
.square-button {
display: flex;
align-items: center;
width: 100%;
flex-direction: row;
justify-content: flex-start;
gap: 12px;
padding: 12px;
font-size: 0.95rem;
}
.square-button img {
width: 32px;
height: auto;
margin: 0;
}
.top-nav {
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
}
.nav-links {
display: flex;
flex-direction: row;
align-items: center;
margin-top: 12px;
}
.chart-container {
width: 100%;
height: auto;
padding: 12px;
}
.btn {
font-size: 0.95rem;
padding: 14px;
width: 100%;
}
form {
padding: 0 8px;
}
.tab-content {
margin-top: 12px;
}
.charts-row {
overflow-x: auto;
padding-bottom: 10px;
}
input[type="text"], input[type="number"], select, textarea {
width: 100%;
font-size: 1rem;
padding: 10px;
box-sizing: border-box;
}
#drop-zone {
padding: 20px;
}
}
/* Universal Fixes */
* {
/* Dashboard Card */
.dashboard-card {
padding: 16px;
max-width: 100%;
word-wrap: break-word;
box-sizing: border-box;
}
/* Tab buttons */
.tab-button {
padding: 10px;
font-size: 0.85rem;
}
/* Square button adjustments */
.square-button {
width: 100%;
aspect-ratio: unset;
flex-direction: row;
justify-content: flex-start;
gap: 12px;
padding: 10px;
font-size: 0.9rem;
}
.square-button img {
width: 32px;
margin: 0;
}
/* Navigation Bar */
.top-nav {
padding: 12px;
font-size: 0.9rem;
flex-direction: column;
align-items: flex-start;
}
.nav-links a {
margin-left: 10px;
font-size: 0.9rem;
}
/* Console Output */
.console-output {
font-size: 0.8rem;
height: 160px;
}
/* Chart container */
.chart-container {
width: 100%;
height: auto;
padding: 12px;
}
/* Buttons */
.btn {
font-size: 0.9rem;
padding: 12px;
width: 100%;
}
/* Form layout */
form {
padding: 0 8px;
}
/* Tab content spacing */
.tab-content {
margin-top: 8px;
}
/* Particle background on small screens */
#particle-background {
display: none; /* Improves performance */
}
/* Charts Row - Allow horizontal scroll */
.charts-row {
overflow-x: auto;
padding-bottom: 10px;
}
/* Input Fields */
input[type="text"], input[type="number"], select {
width: 100%;
}
/* Adjust drop zone */
#drop-zone {
padding: 20px;
}
/* Adjust text area height */
.console-output {
height: 160px;
}
}
/* Additional general fixes for small screens */
* {
max-width: 100%;
word-wrap: break-word;
}

View file

@ -3,24 +3,15 @@
<h2>Twój serwer Minecraft</h2>
<div class="tabs">
<button class="tab-button" onclick="showTab('controls')">
<span class="tab-text">Sterowanie</span> <span class="tab-icon">🎛️</span>
</button>
<button class="tab-button" onclick="showTab('console')">
<span class="tab-text">Konsola</span> <span class="tab-icon">📟</span>
</button>
<button class="tab-button" onclick="showTab('files')">
<span class="tab-text">Pliki</span> <span class="tab-icon">📁</span>
</button>
<button class="tab-button" onclick="showTab('config')">
<span class="tab-text">Konfiguracja</span> <span class="tab-icon">🛠️</span>
</button>
<button class="tab-button" onclick="showTab('mods')">
<span class="tab-text">Mody/Pluginy</span> <span class="tab-icon">🧩</span>
</button>
<button class="tab-button" onclick="showTab('statistics')">
<span class="tab-text">Statystyki</span> <span class="tab-icon">📈</span>
</button>
<button class="tab-button" onclick="showTab('controls')">Sterowanie 🎛️</button>
<button class="tab-button" onclick="showTab('console')">Konsola 📟</button>
<button class="tab-button" onclick="showTab('files')">Pliki 📁</button>
</div>
<div class="tabs">
<button class="tab-button" onclick="showTab('config')">Konfiguracja 🛠️</button>
<button class="tab-button" onclick="showTab('mods')">Mody/Pluginy 🧩</button>
<button class="tab-button" onclick="showTab('statistics')">Statystyki 📈</button>
</div>
<div class="tab-content">
@ -34,7 +25,7 @@
<button class="btn btn-success" onclick="sendAction('start')">Start</button>
<button class="btn btn-warning" onclick="sendAction('restart')">Restart</button>
<button class="btn btn-danger" onclick="sendAction('stop')">Stop</button><br>
<button class="btn btn-worsedanger" onclick="deleteServer()">Usuń Serwer</button>
<button class="btn btn-danger" onclick="deleteServer()">Usuń Serwer</button>
</div>
<div class="tab-panel" id="console">
@ -58,18 +49,11 @@
<br>
<h4>Lista plików:</h4>
<ul id="file-list"></ul>
<div id="file-editor-container" style="display: none;">
<h3>Edytuj plik: <span id="editing-filename"></span></h3>
<textarea id="file-editor" style="width: 100%; height: 400px;"></textarea>
<button class="btn btn-primary" onclick="saveFileContent()">Zapisz zmiany</button>
<button class="btn btn-secondary" onclick="closeFileEditor()">Zamknij</button>
</div>
</div>
<div class="tab-panel" id="config">
<h4>Konfiguracja Serwera:</h4>
<form id="config-form">
<h4>Konfiguracja Serwera:</h4>
<div class="form-group">
<label for="server-type">Typ serwera</label>
<select id="server-type" class="form-control">
@ -124,21 +108,12 @@
<label for="view-distance">Zasięg widzenia</label>
<input type="number" id="view-distance" class="form-control" min="1" value="{{ config['view-distance'] }}">
</div>
<h5>Inne:</h5>
<div class="form-group">
<label for="andus-drasl">Serwer Drasl Andusa</label>
<h5>Tylko dla serwerów 1.16 i wyższych</h5>
<h6>W tym trybie mogą dołączyć tylko gracze z kontem Drasl (https://drasl.andus.ovh).
<br>Tryb online musi być włączony, ponieważ ten serwer dziala tak samo jak serwery Mojang (Ale bez konieczności kupowania Minecrafta).</h6>
<h6 style="color: red">UWAGA: W tym trybie konta z kupionym Minecraftem nie mogą dołączyć na serwer, też muszą utworzyć konto Drasl.</h6>
<input type="checkbox" id="andus-drasl" class="form-check-input" {% if config['andus-drasl'] %} checked {% endif %}>
</div>
<button type="button" class="btn btn-primary" onclick="saveConfig()">Zapisz zmiany</button>
</form>
</div>
<div class="tab-panel" id="mods">
<h4>Zainstaluj mody/pluginy z <span style="color: #2ecc71;">Modrinth</span></h4>
<h2>Zainstaluj mody/pluginy z Modrinth</h2>
<input type="text" id="mod-search" placeholder="Wyszukaj mod/plugin..." oninput="searchMods()">
<div id="mod-results"></div>
</div>
@ -151,8 +126,6 @@
</div>
</div>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/codemirror.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/theme/material-darker.min.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="/static/js/controls.js"></script>
<script src="/static/js/console.js"></script>
@ -171,11 +144,6 @@
function showTab(id) {
document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active'));
$(id).classList.add('active');
// Close the mobile menu if it's open (optional, depending on your menu implementation)
const tabsContainer = document.querySelector('.tabs');
if (tabsContainer.classList.contains('mobile-active')) {
tabsContainer.classList.remove('mobile-active');
}
}
let currentPath = '';
@ -295,26 +263,11 @@
pointRadius: 0
}]
},
options: {
...chartOptions,
scales: { ...chartOptions.scales, y: { min: 0, max: 4096, ticks: { color: '#aaa' } } }
}
options: chartOptions
});
checkServerStatus();
setInterval(checkServerStatus, 5000);
</script>
<!-- CSS do Edytora Plików -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/codemirror.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/mode/javascript/javascript.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/mode/xml/xml.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/mode/css/css.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/mode/htmlmixed/htmlmixed.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/mode/clike/clike.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/mode/python/python.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/mode/yaml/yaml.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/mode/properties/properties.min.js"></script>
<script src="/static/js/file_editor.js"></script>
{% endblock %}

View file

@ -2,30 +2,20 @@
{% block content %}
<div class="logo-wrapper">
<h2 class="logo-text">MCPanel</h2>
<p id="quote" class="splash-text"></p>
<p id="quote" class="splash-text">Jesteśmy 1.5x lepsi od Aternosa!</p>
</div>
<div class="tabs">
{% if is_logged_in %}
{% if has_server %}
<a class="tab-button" href="{{ url_for('main.dashboard') }}">Idź do panelu</a>
{% else %}
<a class="tab-button" href="{{ url_for('main.setup') }}">Ustaw własny serwer</a>
{% endif %}
{% if has_server %}
<a class="tab-button" href="{{ url_for('main.dashboard') }}">Idź do panelu</a>
{% else %}
<a class="tab-button" href="{{ url_for('main.dashboard') }}">Zaloguj się</a>
<a class="tab-button" href="{{ url_for('main.setup') }}">Ustaw własny serwer</a>
{% endif %}
<a class="tab-button" href="#news">Wiadomości</a>
</div>
<div id="news">
<!-- Z Tagami jak: WERSJA PANELU, PROBLEM -->
<h3>Wiadomości:</h3>
<p>Soon&trade;</p>
</div>
<script>
const quotes = [
"Jesteśmy 2x lepsi od Aternosa!",
"Twój serwer, Twoje zasady!",
"Stabilność? Nie znam takiego słowa!",
"Zero kolejki, tylko gra!",

View file

@ -3,7 +3,6 @@
<head>
<meta charset="UTF-8">
<title>MCPanel</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css" rel="stylesheet">
</head>
@ -23,9 +22,8 @@
<div class="nav-logo"><a href="/">MCPanel <h6>BETA</h6></a></div>
<div class="nav-links">
<a href="/dashboard">Panel</a>
<a href="/status">Statusy</a>
<a style="color: cornflowerblue;" href="/donate">Donate</a>
<a style="color: red;" href="/logout">Wyloguj</a>
<a href="/logout">Wyloguj</a>
</div>
</nav>
@ -33,7 +31,7 @@
{% block content %}{% endblock %}
</div>
<div id="ad-banner" class="ad-banner"></div>
<p>Wersja: v0.3.1</p>
<p>Wersja: v0.2</p>
<script>
window.addEventListener('dragover', e => e.preventDefault());
window.addEventListener('drop', e => e.preventDefault());

View file

@ -1,32 +0,0 @@
{% extends "layout.html" %}
{% block content %}
<h1 class="page-title">Wszystkie Serwery</h1>
<div class="server-table-container">
<table class="server-table">
<thead>
<tr>
<th>Nazwa</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{% for server in servers %}
<tr>
<td>{{ server.name }}</td>
<td>
<span class="status-label {{ 'online' if server.status == 'Online' else 'offline' }}">
{{ server.status }}
</span>
</td>
</tr>
{% else %}
<tr>
<td colspan="2" class="empty-row">Nie znaleziono serwerów</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

View file

@ -4,7 +4,7 @@
<div style="display: flex; justify-content: center; gap: 2rem; margin-bottom: 2rem;">
<button class="square-button" onclick="selectType(this, 'paper')">
<img src="https://assets.papermc.io/brand/papermc_logo.min.svg" alt="Paper">
<img src="https://docs.papermc.io/assets/images/papermc-logomark-512-f125384f3367cd4d9291ca983fcb7334.png" alt="Paper">
<b>Paper</b>
<br>
<p>Zoptymalizowany serwer Minecraft oferujący lepszą wydajność.</p>