first commit

This commit is contained in:
nix
2026-05-16 11:10:19 +02:00
commit 509c9b3737
172 changed files with 14496 additions and 0 deletions
Executable
+1115
View File
File diff suppressed because it is too large Load Diff
+89
View File
@@ -0,0 +1,89 @@
<?php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Cache-Control: no-cache, must-revalidate');
header('Expires: 0');
ini_set('display_errors', 0);
error_reporting(0);
define('CRAFTY_URL', 'https://craft.ipv6.nix.roulaise.net');
define('CRAFTY_TOKEN', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJpYXQiOjE3NzczMDIwNjIsInRva2VuX2lkIjoxfQ.DXFKO4aim-nJ_Wyn-o3qMaVyv6AP3aCoAYukR-uQels');
// Ports connus → clé logique
const PORT_MAP = [9191 => 'mc1', 9292 => 'mc2'];
function craftyGet(string $path): ?array {
$ch = curl_init(CRAFTY_URL . $path);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 5,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . CRAFTY_TOKEN],
]);
$body = curl_exec($ch);
curl_close($ch);
return $body ? json_decode($body, true) : null;
}
// Extraire -Xmx depuis la commande d'exécution → retourne en Mio
function extractXmx(string $cmd): ?int {
if (preg_match('/-Xmx(\d+)([GgMm])/i', $cmd, $m)) {
return strtolower($m[2]) === 'g'
? (int)$m[1] * 1024
: (int)$m[1];
}
return null;
}
// 1. Lister les serveurs
$list = craftyGet('/api/v2/servers');
if (!$list || ($list['status'] ?? '') !== 'ok') {
echo json_encode(['success' => false, 'error' => 'Crafty unreachable']);
exit;
}
$result = [];
foreach ($list['data'] as $srv) {
$uuid = $srv['server_id'];
// Stats de ce serveur
$stats = craftyGet("/api/v2/servers/{$uuid}/stats");
if (!$stats || ($stats['status'] ?? '') !== 'ok') continue;
$d = $stats['data'];
// Identifier la clé logique par port
$port = (int)($d['server_port'] ?? $srv['server_port'] ?? 0);
$key = PORT_MAP[$port] ?? null;
// Fallback par nom si port inconnu
if (!$key) {
$name = strtolower($d['server_name'] ?? $srv['server_name'] ?? '');
if (str_contains($name, '#1') || str_contains($name, 'mc1') || str_contains($name, 'forge')) $key = 'mc1';
elseif (str_contains($name, '#2') || str_contains($name, 'mc2') || str_contains($name, 'fabric')) $key = 'mc2';
else $key = $uuid;
}
// RAM utilisée : Crafty retourne en octets
$ramUsed = isset($d['mem']) ? (int)round($d['mem'] / 1024 / 1024) : null;
// RAM allouée : extraire -Xmx depuis execution_command
$cmd = $d['execution_command'] ?? $srv['execution_command'] ?? '';
$ramAlloc = extractXmx($cmd);
$result[$key] = [
'uuid' => $uuid,
'name' => $d['server_name'] ?? $srv['server_name'] ?? $uuid,
'port' => $port,
'running' => (bool)($d['running'] ?? false),
'players' => (int)($d['online'] ?? 0),
'max_players' => (int)($d['max'] ?? 0),
'ram_used' => $ramUsed,
'ram_alloc' => $ramAlloc,
'cpu' => isset($d['cpu']) ? round((float)$d['cpu'], 1) : null,
];
}
echo json_encode(['success' => true, 'servers' => $result], JSON_NUMERIC_CHECK | JSON_UNESCAPED_UNICODE);
?>
+115
View File
@@ -0,0 +1,115 @@
<?php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Cache-Control: no-cache, must-revalidate');
ini_set('display_errors', 0);
error_reporting(0);
$result = [];
// ===== EVENTS (logs parsés par mc-log-watcher) =====
$logsFile = '/var/cache/mc-logs.json';
if (file_exists($logsFile)) {
$raw = @file_get_contents($logsFile);
$decoded = $raw ? @json_decode($raw, true) : null;
$result['events'] = $decoded['events'] ?? [];
// Renvoyer les N derniers triés du plus récent au plus ancien
$result['events'] = array_reverse(array_slice($result['events'], -100));
} else {
$result['events'] = [];
}
// ===== WHITELIST =====
$whitelists = [
'mc1' => '/srv/Minecraft/whitelist.json',
'mc2' => '/srv/Fabric-1.21.8/whitelist.json',
];
$result['whitelist'] = [];
foreach ($whitelists as $key => $path) {
if (file_exists($path)) {
$raw = @file_get_contents($path);
$players = $raw ? @json_decode($raw, true) : [];
$result['whitelist'][$key] = is_array($players) ? $players : [];
} else {
$result['whitelist'][$key] = null; // null = whitelist désactivée / fichier absent
}
}
// ===== BANNISSEMENTS =====
$banlists = [
'mc1' => '/srv/Minecraft/banned-players.json',
'mc2' => '/srv/Fabric-1.21.8/banned-players.json',
];
$result['banned'] = [];
foreach ($banlists as $key => $path) {
if (file_exists($path)) {
$raw = @file_get_contents($path);
$players = $raw ? @json_decode($raw, true) : [];
$result['banned'][$key] = is_array($players) ? $players : [];
} else {
$result['banned'][$key] = [];
}
}
// ===== MODS (Fabric uniquement - dossier mods/) =====
$modsDir = '/srv/Fabric-1.21.8/mods';
$result['mods'] = [];
if (is_dir($modsDir)) {
$files = @scandir($modsDir);
if ($files) {
foreach ($files as $file) {
if (pathinfo($file, PATHINFO_EXTENSION) === 'jar') {
// Tenter de lire fabric.mod.json à l'intérieur du jar (zip)
$jarPath = $modsDir . '/' . $file;
$modInfo = ['filename' => $file, 'name' => null, 'version' => null, 'description' => null];
// Lire le fabric.mod.json depuis le jar (zip)
if (class_exists('ZipArchive')) {
$zip = new ZipArchive();
if ($zip->open($jarPath) === true) {
$idx = $zip->locateName('fabric.mod.json');
if ($idx !== false) {
$json = @json_decode($zip->getFromIndex($idx), true);
if ($json) {
$modInfo['name'] = $json['name'] ?? $json['id'] ?? null;
$modInfo['version'] = $json['version'] ?? null;
$modInfo['description'] = $json['description'] ?? null;
$modInfo['id'] = $json['id'] ?? null;
}
}
$zip->close();
}
}
// Fallback: nom depuis le fichier
if (!$modInfo['name']) {
$modInfo['name'] = preg_replace('/[-_][\d.]+.*\.jar$/', '', $file);
}
$result['mods'][] = $modInfo;
}
}
// Trier par nom
usort($result['mods'], fn($a, $b) => strcasecmp($a['name'] ?? '', $b['name'] ?? ''));
}
}
// ===== SERVER PROPERTIES (whitelist activée ?) =====
$propsFiles = [
'mc1' => '/srv/Minecraft/server.properties',
'mc2' => '/srv/Fabric-1.21.8/server.properties',
];
$result['whitelist_enabled'] = [];
foreach ($propsFiles as $key => $path) {
$result['whitelist_enabled'][$key] = false;
if (file_exists($path)) {
$content = @file_get_contents($path);
if ($content && preg_match('/^white-list\s*=\s*true/m', $content)) {
$result['whitelist_enabled'][$key] = true;
}
}
}
echo json_encode(['success' => true, 'data' => $result], JSON_UNESCAPED_UNICODE);
?>
+32
View File
@@ -0,0 +1,32 @@
<?php
header('Content-Type: application/json');
header('Cache-Control: no-store');
$content = @file_get_contents('/proc/mdstat');
if (!$content) { echo json_encode(['error' => 'unreadable']); exit; }
$arrays = [];
$current = null;
foreach (explode("\n", $content) as $line) {
if (preg_match('/^(md\d+)\s*:\s*(\w+)\s+(\w+)\s+(.+)$/', $line, $m)) {
$current = $m[1];
$arrays[$current] = ['name' => $m[1], 'level' => $m[3], 'health' => 'ok', 'bitmap' => '', 'detail' => ''];
}
if ($current && preg_match('/\[(\d+)\/(\d+)\]\s*\[([U_]+)\]/', $line, $m)) {
$arrays[$current]['bitmap'] = $m[3];
$arrays[$current]['active'] = (int)$m[2];
$arrays[$current]['total'] = (int)$m[1];
if ((int)$m[2] < (int)$m[1] || str_contains($m[3], '_'))
$arrays[$current]['health'] = 'degraded';
}
if ($current && preg_match('/(resync|recovery)\s*=\s*([\d.]+%)/i', $line, $m)) {
$arrays[$current]['health'] = 'recovering';
$arrays[$current]['detail'] = "{$m[1]} {$m[2]}";
}
}
$list = array_values($arrays);
$healthy = !array_filter($list, fn($a) => $a['health'] !== 'ok');
echo json_encode(['healthy' => $healthy, 'arrays' => $list]);
+178
View File
@@ -0,0 +1,178 @@
<?php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Cache-Control: no-cache, must-revalidate');
header('Expires: 0');
ini_set('display_errors', 0);
error_reporting(0);
/**
* Minecraft Server List Ping (protocole 1.7+)
* Ref: https://wiki.vg/Server_List_Ping
*/
function pingMinecraftServer(string $host, int $port = 25565, int $timeout = 4): array {
$result = [
'online' => false,
'players' => 0,
'max_players' => 0,
'version' => '',
'motd' => '',
'latency' => null,
];
$start = microtime(true);
$socket = @fsockopen($host, $port, $errno, $errstr, $timeout);
if (!$socket) {
return $result;
}
stream_set_timeout($socket, $timeout);
// ---- Handshake packet (0x00) ----
$hostBytes = $host;
$hostLen = strlen($hostBytes);
$handshakeData = "\x00"; // Packet ID 0x00
$handshakeData .= varInt(47); // Protocol version (47 = 1.8, -1 non-spécifique)
$handshakeData .= varInt($hostLen) . $hostBytes; // Server address
$handshakeData .= pack('n', $port); // Port (unsigned short big-endian)
$handshakeData .= varInt(1); // Next state: 1 = status
fwrite($socket, varInt(strlen($handshakeData)) . $handshakeData);
// ---- Status Request (0x00, longueur 1) ----
fwrite($socket, "\x01\x00");
// ---- Lire la réponse ----
$packetLen = readVarInt($socket);
if ($packetLen <= 0) {
fclose($socket);
return $result;
}
// Lire l'ID du paquet (doit être 0x00)
readVarInt($socket);
// Lire la longueur de la chaîne JSON
$strLen = readVarInt($socket);
if ($strLen <= 0) {
fclose($socket);
return $result;
}
// Lire le JSON
$json = '';
$remaining = $strLen;
while ($remaining > 0 && !feof($socket)) {
$chunk = fread($socket, min($remaining, 4096));
if ($chunk === false || $chunk === '') break;
$json .= $chunk;
$remaining -= strlen($chunk);
}
fclose($socket);
$latency = (int) round((microtime(true) - $start) * 1000);
$data = @json_decode($json, true);
if (!$data) {
return $result;
}
// ---- Extraire le MOTD ----
$motd = '';
if (isset($data['description'])) {
if (is_string($data['description'])) {
$motd = $data['description'];
} elseif (is_array($data['description'])) {
$motd = $data['description']['text'] ?? '';
// Concatener les extras
if (!empty($data['description']['extra']) && is_array($data['description']['extra'])) {
foreach ($data['description']['extra'] as $extra) {
$motd .= $extra['text'] ?? '';
}
}
}
}
// Supprimer les codes couleur §X
$motd = preg_replace('/§[0-9a-fk-orA-FK-OR]/u', '', $motd);
$motd = trim(preg_replace('/\s+/', ' ', $motd));
$result['online'] = true;
$result['players'] = (int) ($data['players']['online'] ?? 0);
$result['max_players'] = (int) ($data['players']['max'] ?? 0);
$result['version'] = $data['version']['name'] ?? '';
$result['motd'] = $motd;
$result['latency'] = $latency;
return $result;
}
/**
* Encode un entier en VarInt Minecraft
*/
function varInt(int $value): string {
$out = '';
do {
$byte = $value & 0x7F;
$value = ($value >> 7) & PHP_INT_MAX; // shift sans signe
if ($value !== 0) $byte |= 0x80;
$out .= chr($byte);
} while ($value !== 0);
return $out;
}
/**
* Lit un VarInt depuis un socket
*/
function readVarInt($socket): int {
$value = 0;
$shift = 0;
do {
$raw = fgetc($socket);
if ($raw === false) return 0;
$byte = ord($raw);
$value |= ($byte & 0x7F) << $shift;
$shift += 7;
if ($shift > 35) break; // sécurité anti-boucle infinie
} while ($byte & 0x80);
return $value;
}
// ======================================================
// Configuration des serveurs à pinger
// ======================================================
$servers = [
[
'name' => 'Serveur MC #1',
'host' => 'nix.roulaise.net',
'port' => 9191,
],
[
'name' => 'Serveur MC #2',
'host' => 'nix.roulaise.net',
'port' => 9292,
],
];
// ======================================================
// Ping tous les serveurs et retourner le résultat JSON
// ======================================================
$results = [];
foreach ($servers as $srv) {
$ping = pingMinecraftServer($srv['host'], $srv['port']);
$results[] = array_merge([
'name' => $srv['name'],
'host' => $srv['host'],
'port' => $srv['port'],
], $ping);
}
echo json_encode([
'success' => true,
'servers' => $results,
'timestamp' => time(),
], JSON_UNESCAPED_UNICODE | JSON_NUMERIC_CHECK);
?>
+40
View File
@@ -0,0 +1,40 @@
<?php
// --- Configuration des serveurs ---
$servers = [
'Fabric 1.19.2' => '/srv/fabric-1.19.2',
'Forge 1.19.2' => '/srv/forge-1.19.2',
];
// ----------------------------------
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
function mod_name(string $filename): string {
$name = preg_replace('/\.jar$/i', '', $filename);
$name = preg_replace('/[-_](?:v?\d+[\.\d]*[\+\-\w]*).*$/i', '', $name);
$name = str_replace(['-', '_'], ' ', $name);
return ucwords(strtolower(trim($name)));
}
$result = [];
foreach ($servers as $label => $root) {
$mods_dir = $root . '/mods';
$entry = ['label' => $label, 'mods' => [], 'error' => null];
if (!is_dir($mods_dir)) {
$entry['error'] = 'Dossier introuvable : ' . $mods_dir;
} else {
$files = glob($mods_dir . '/*.jar');
sort($files);
foreach ($files as $f) {
$filename = basename($f);
$entry['mods'][] = [
'file' => $filename,
'name' => mod_name($filename),
];
}
}
$result[] = $entry;
}
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+133
View File
@@ -0,0 +1,133 @@
<?php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Cache-Control: no-cache, must-revalidate');
header('Expires: 0');
ini_set('display_errors', 0);
error_reporting(0);
function getSystemStats() {
$stats = [];
// ========== UPTIME ==========
if (file_exists('/proc/uptime')) {
$uptime = @file_get_contents('/proc/uptime');
if ($uptime !== false) {
$uptimeSeconds = floatval(explode(' ', $uptime)[0]);
$days = floor($uptimeSeconds / 86400);
$hours = floor(($uptimeSeconds % 86400) / 3600);
$minutes = floor(($uptimeSeconds % 3600) / 60);
$stats['uptime'] = sprintf('%dd %dh %dm', $days, $hours, $minutes);
} else {
$stats['uptime'] = 'N/A';
}
} else {
$stats['uptime'] = 'N/A';
}
// ========== CPU USAGE ==========
if (file_exists('/proc/stat')) {
$stat1 = @file('/proc/stat');
if ($stat1 !== false && count($stat1) > 0) {
usleep(100000);
$stat2 = @file('/proc/stat');
if ($stat2 !== false && count($stat2) > 0) {
$info1 = explode(' ', preg_replace('!cpu +!', '', $stat1[0]));
$info2 = explode(' ', preg_replace('!cpu +!', '', $stat2[0]));
$dif = [
'user' => floatval($info2[0]) - floatval($info1[0]),
'nice' => floatval($info2[1]) - floatval($info1[1]),
'sys' => floatval($info2[2]) - floatval($info1[2]),
'idle' => floatval($info2[3]) - floatval($info1[3])
];
$total = array_sum($dif);
$stats['cpu_usage'] = $total > 0 ? round(100 - ($dif['idle'] / $total * 100), 1) : 0;
} else {
$load = sys_getloadavg();
$stats['cpu_usage'] = round(min($load[0] * 25, 100), 1);
}
} else {
$load = sys_getloadavg();
$stats['cpu_usage'] = round(min($load[0] * 25, 100), 1);
}
} else {
$stats['cpu_usage'] = 0;
}
// ========== MEMORY ==========
if (file_exists('/proc/meminfo')) {
$meminfo = @file_get_contents('/proc/meminfo');
if ($meminfo !== false) {
preg_match_all('/^(\w+):\s+(\d+)/m', $meminfo, $matches);
$mem = array_combine($matches[1], $matches[2]);
$memTotal = floatval($mem['MemTotal']) / 1024 / 1024;
$memAvailable = isset($mem['MemAvailable'])
? floatval($mem['MemAvailable'])
: (floatval($mem['MemFree']) + floatval($mem['Buffers']) + floatval($mem['Cached']));
$memFree = $memAvailable / 1024 / 1024;
$memUsed = $memTotal - $memFree;
$stats['ram_total'] = round($memTotal, 1);
$stats['ram_used'] = round($memUsed, 1);
$stats['ram_free'] = round($memFree, 1);
$stats['ram_percent'] = round(($memUsed / $memTotal) * 100, 1);
} else {
$stats['ram_total'] = $stats['ram_used'] = $stats['ram_percent'] = 0;
}
} else {
$stats['ram_total'] = $stats['ram_used'] = $stats['ram_percent'] = 0;
}
// ========== DISK ==========
$partitions = ['/', '/var', '/srv'];
foreach ($partitions as $partition) {
$key = str_replace('/', '', $partition);
if ($key === '') $key = 'root';
$diskTotal = @disk_total_space($partition);
$diskFree = @disk_free_space($partition);
if ($diskTotal !== false && $diskFree !== false) {
$diskUsed = $diskTotal - $diskFree;
$stats['disk_' . $key . '_total'] = round($diskTotal / 1024 / 1024 / 1024, 1);
$stats['disk_' . $key . '_used'] = round($diskUsed / 1024 / 1024 / 1024, 1);
$stats['disk_' . $key . '_free'] = round($diskFree / 1024 / 1024 / 1024, 1);
$stats['disk_' . $key . '_percent'] = round(($diskUsed / $diskTotal) * 100, 1);
} else {
$stats['disk_' . $key . '_total'] = 0;
$stats['disk_' . $key . '_free'] = 0;
$stats['disk_' . $key . '_percent'] = 0;
}
}
// ========== LOAD AVERAGE ==========
$loadavg = sys_getloadavg();
$stats['load_1'] = round($loadavg[0], 2);
$stats['load_5'] = round($loadavg[1], 2);
$stats['load_15'] = round($loadavg[2], 2);
// ========== PROCESSES ==========
if (function_exists('shell_exec')) {
$processes = @shell_exec('ps aux 2>/dev/null | wc -l');
$stats['processes'] = $processes !== null ? max(intval(trim($processes)) - 1, 0) : 0;
} else {
$stats['processes'] = 0;
}
$stats['hostname'] = @gethostname() ?: 'Unknown';
$stats['timestamp'] = time();
return $stats;
}
try {
echo json_encode([
'success' => true,
'data' => getSystemStats()
], JSON_NUMERIC_CHECK);
} catch (Exception $e) {
http_response_code(500);
echo json_encode([
'success' => false,
'error' => $e->getMessage()
]);
}
?>
+94
View File
@@ -0,0 +1,94 @@
body {
font-family: 'Comfortaa', cursive;
background: #282a36;
color: #f8f8f2;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 1rem;
}
.container {
background: rgba(68, 71, 90, 0.95);
padding: 2rem;
border-radius: 16px;
width: 100%;
max-width: 480px;
box-shadow: 0 12px 30px rgba(0,0,0,0.4);
}
h1 {
text-align: center;
color: #bd93f9;
margin-bottom: 1.5rem;
}
label {
display: block;
margin-top: 1rem;
margin-bottom: 0.3rem;
font-weight: 700;
}
input,
select {
width: 100%;
padding: 0.7rem;
border-radius: 10px;
border: none;
font-family: inherit;
font-size: 1rem;
background: #6272a4;
color: #f8f8f2;
}
input:focus,
select:focus {
outline: none;
background: #50fa7b;
color: #282a36;
}
.row {
display: flex;
gap: 0.6rem;
flex-wrap: wrap;
}
.row > div {
flex: 1;
}
button {
margin-top: 1.5rem;
width: 100%;
background: #bd93f9;
color: #282a36;
border: none;
padding: 0.8rem;
font-size: 1.1rem;
font-weight: 700;
border-radius: 12px;
cursor: pointer;
transition: 0.2s;
}
button:hover {
background: #ff79c6;
transform: translateY(-2px);
}
.result {
margin-top: 1.5rem;
text-align: center;
font-size: 1.3rem;
font-weight: 700;
color: #50fa7b;
}
@media (max-width: 500px) {
.container {
padding: 1.5rem;
}
}
+756
View File
@@ -0,0 +1,756 @@
/* =============================== Font ================================ */
@import url('https://fonts.googleapis.com/css2?family=Comfortaa:wght@400;700&display=swap');
/* =============================== Reset ================================ */
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* =============================== Body ================================ */
body {
font-family: 'Comfortaa', cursive;
color: #f8f8f2;
line-height: 1.6;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
padding: 2rem;
background: transparent;
overflow-x: hidden;
}
/* =============================== Background Media ================================ */
#bg-video,
#bg-image {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
object-fit: cover;
z-index: -2;
pointer-events: none;
display: none;
}
/* Dark overlay for readability */
body::before {
content: "";
position: fixed;
inset: 0;
background: rgba(40, 42, 54, 0.55);
z-index: -1;
pointer-events: none;
}
/* =============================== Main Container ================================ */
.container {
position: relative;
z-index: 1;
max-width: 1400px;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
/* ============================== Descriptions ============================== */
.description {
position: relative;
z-index: 1;
margin-top: 3rem;
text-align: center;
color: #6272a4;
font-size: 0.9rem;
}
/* =============================== Header ================================ */
h1 {
font-size: 2.5rem;
color: #bd93f9;
margin-bottom: 1rem;
text-align: center;
}
header {
text-align: center;
color: #6272a4;
font-size: 1rem;
margin-bottom: 2rem;
}
/* =============================== Cards ================================ */
.card {
background-color: rgba(68, 71, 90, 0.95);
color: #f8f8f2;
padding: 1.5rem 2rem;
border-radius: 12px;
font-weight: 600;
font-size: 1.2rem;
text-align: center;
width: 100%;
margin: 0.7rem 0;
cursor: pointer;
transition:
transform 0.2s ease,
box-shadow 0.2s ease,
background-color 0.2s ease;
}
.card:hover {
background-color: #6272a4;
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
}
.card a {
display: block;
width: 100%;
height: 100%;
color: inherit;
text-decoration: none;
}
/* =============================== Images ================================ */
.img {
max-height: 200px;
max-width: 100%;
width: auto;
height: auto;
object-fit: contain;
}
/* =============================== Forms ================================ */
form {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
background-color: rgba(68, 71, 90, 0.95);
padding: 2rem;
border-radius: 12px;
max-width: 400px;
width: 100%;
margin: 1rem 0;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
}
label {
width: 100%;
margin-bottom: 0.5rem;
font-weight: 600;
}
input[type="text"],
select {
width: 100%;
padding: 0.6rem 1rem;
margin-bottom: 1rem;
border: none;
border-radius: 8px;
outline: none;
background-color: #6272a4;
color: #f8f8f2;
font-family: inherit;
font-size: 1rem;
transition:
background-color 0.2s ease,
transform 0.2s ease;
}
input[type="text"]:focus,
select:focus {
background-color: #50fa7b;
color: #282a36;
transform: scale(1.02);
}
/* =============================== Buttons ================================ */
button {
background-color: #bd93f9;
color: #282a36;
font-family: inherit;
font-weight: 700;
font-size: 1.1rem;
padding: 0.7rem 1.5rem;
border-radius: 10px;
border: none;
cursor: pointer;
transition:
background-color 0.2s ease,
transform 0.2s ease,
box-shadow 0.2s ease;
}
button:hover {
background-color: #ff79c6;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.4);
}
/* =============================== Footer ================================ */
footer {
position: relative;
z-index: 1;
margin-top: 3rem;
text-align: center;
color: #6272a4;
font-size: 0.9rem;
}
/* =============================== PHP Directory Index ================================ */
.card .file-type {
display: block;
margin-top: 0.3rem;
font-size: 0.9rem;
color: #bd93f9;
}
/* =============================== Cookies ================================ */
.cookie-btn {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 9999;
}
button.cookie-btn {
background-color: #bd93f9 !important;
color: #282a36 !important;
}
button.cookie-btn:hover {
background-color: #ff79c6 !important;
}
/* ============================= Barre de chargement ============================= */
#loading-bar {
position: fixed;
top: 0;
left: 0;
height: 3px;
background: linear-gradient(90deg, #ff69b4, #ff1493);
width: 0%;
transition: width 0.3s ease;
z-index: 9999;
box-shadow: 0 0 10px rgba(255, 105, 180, 0.6);
}
/* =============================== Dashboard Layout ================================ */
.dashboard-wrapper {
width: 100%;
display: grid;
grid-template-columns: 450px 1fr;
gap: 2rem;
margin-bottom: 2rem;
}
.sidebar {
display: flex;
flex-direction: column;
gap: 0.7rem;
}
.main-content {
display: flex;
flex-direction: column;
gap: 2rem;
}
/* Card avec image */
.card-with-image {
padding: 1rem;
min-height: 150px;
display: flex;
align-items: center;
justify-content: center;
}
.card-with-image .img {
max-height: 200px;
max-width: 100%;
width: auto;
height: auto;
object-fit: contain;
display: block;
margin: 0 auto;
}
/* =============================== Stats Section ================================ */
.stats-section {
width: 100%;
}
.stats-section h2 {
color: #bd93f9;
font-size: 1.5rem;
margin-bottom: 1rem;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}
.stat-card {
background-color: rgba(68, 71, 90, 0.95);
padding: 1.5rem;
border-radius: 12px;
text-align: center;
transition: transform 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
cursor: pointer;
}
.stat-card:hover {
background-color: #6272a4;
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
}
.stat-label {
font-size: 0.95rem;
color: #6272a4;
margin-bottom: 0.5rem;
font-weight: 600;
}
.stat-value {
font-size: 2.2rem;
font-weight: 700;
color: #50fa7b;
transition: color 0.3s ease;
}
.stat-subtitle {
font-size: 0.85rem;
color: #6272a4;
margin-top: 0.3rem;
}
/* =============================== Other Links Section ================================ */
.other-section {
width: 100%;
}
.other-section h2 {
color: #bd93f9;
font-size: 1.5rem;
margin-bottom: 1rem;
}
.other-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
}
.small-card {
padding: 1rem 1.5rem;
font-size: 1rem;
}
/* =============================== Credits Section ================================ */
.credits-section {
width: 100%;
}
.credits-section h2 {
color: #bd93f9;
font-size: 1.5rem;
margin-bottom: 1.5rem;
text-align: left;
}
.credits-category {
margin-bottom: 2.5rem;
}
.credits-category:last-child {
margin-bottom: 0;
}
.credits-category h3 {
color: #ff79c6;
font-size: 1.2rem;
margin-bottom: 1rem;
font-weight: 600;
}
.credits-grid {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
}
.credit-card {
background-color: rgba(68, 71, 90, 0.95);
padding: 1.2rem;
border-radius: 12px;
display: flex;
align-items: center;
gap: 1rem;
text-decoration: none;
color: #f8f8f2;
transition: transform 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease, border-color 0.2s ease;
cursor: pointer;
border: 2px solid transparent;
}
.credit-card:hover {
background-color: #6272a4;
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
border-color: #bd93f9;
}
.credit-icon {
flex-shrink: 0;
width: 48px;
height: 48px;
background-color: rgba(189, 147, 249, 0.2);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
color: #bd93f9;
}
.artist-card .credit-icon {
background-color: rgba(255, 121, 198, 0.2);
color: #ff79c6;
}
.credit-info {
flex: 1;
}
.credit-name {
font-size: 1.1rem;
font-weight: 700;
color: #f8f8f2;
margin-bottom: 0.3rem;
}
.credit-desc {
font-size: 0.9rem;
color: #6272a4;
}
.small-credits {
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.credit-tag {
background-color: rgba(68, 71, 90, 0.95);
padding: 0.8rem 1.2rem;
border-radius: 8px;
text-align: center;
transition: transform 0.2s ease, background-color 0.2s ease;
}
.credit-tag:hover {
background-color: #6272a4;
transform: translateY(-2px);
}
.credit-tag a {
color: #f8f8f2;
text-decoration: none;
font-size: 0.95rem;
display: block;
}
.credits-thanks {
background-color: rgba(68, 71, 90, 0.95);
padding: 1.5rem;
border-radius: 12px;
text-align: center;
}
.credits-thanks p {
margin: 0.5rem 0;
color: #f8f8f2;
font-size: 1rem;
line-height: 1.6;
}
.credits-thanks strong {
color: #50fa7b;
font-weight: 700;
}
/* =============================== Minecraft Server Widget ================================ */
/* ===== Titre de section MC ===== */
.mc-section-title {
display: flex;
align-items: center;
gap: 0.4rem;
font-size: 0.75rem;
font-weight: 700;
color: #bd93f9;
text-transform: uppercase;
letter-spacing: 0.1em;
padding: 0.5rem 0.2rem 0;
}
/* ===== Carte serveur ===== */
.card.mc-server-card {
padding: 0.9rem 1.1rem;
cursor: default;
text-align: left;
font-size: 1rem;
border-left: 3px solid #bd93f9;
}
.card.mc-server-card:hover {
background-color: rgba(80, 85, 110, 0.95);
transform: translateY(-2px);
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
}
/* En-tête: nom violet + badge */
.mc-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
margin-bottom: 0.3rem;
}
.mc-name {
font-size: 1rem;
font-weight: 700;
color: #bd93f9; /* violet dracula */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Badge en ligne / hors ligne */
.mc-badge {
font-size: 0.65rem;
font-weight: 700;
padding: 0.15rem 0.55rem;
border-radius: 20px;
white-space: nowrap;
flex-shrink: 0;
letter-spacing: 0.03em;
}
.mc-online {
background: rgba(80, 250, 123, 0.15);
color: #50fa7b;
border: 1px solid rgba(80, 250, 123, 0.4);
animation: mc-pulse 2.8s ease-in-out infinite;
}
.mc-offline {
background: rgba(255, 85, 85, 0.13);
color: #ff5555;
border: 1px solid rgba(255, 85, 85, 0.35);
}
@keyframes mc-pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(80, 250, 123, 0); }
50% { box-shadow: 0 0 0 4px rgba(80, 250, 123, 0.1); }
}
/* Adresse IP:port en cyan monospace */
.mc-address {
font-size: 0.7rem;
color: #8be9fd; /* cyan dracula */
font-family: 'Courier New', monospace;
margin-bottom: 0.3rem;
letter-spacing: 0.02em;
}
/* MOTD en jaune italique */
.mc-motd {
font-size: 0.72rem;
color: #f1fa8c; /* jaune dracula */
font-style: italic;
margin-bottom: 0.3rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Séparateur fin avant les infos */
.mc-info {
display: flex;
align-items: center;
gap: 0.55rem;
margin-top: 0.35rem;
padding-top: 0.35rem;
border-top: 1px solid rgba(98, 114, 164, 0.3);
}
/* Joueurs : icône rose + texte blanc */
.mc-players {
display: flex;
align-items: center;
gap: 0.25rem;
font-size: 0.82rem;
color: #f8f8f2;
font-weight: 700;
}
.mc-players svg {
color: #ff79c6; /* rose dracula */
flex-shrink: 0;
}
/* Latence en orange */
.mc-latency {
font-size: 0.7rem;
font-weight: 700;
color: #ffb86c; /* orange dracula */
background: rgba(255, 184, 108, 0.12);
border: 1px solid rgba(255, 184, 108, 0.25);
border-radius: 5px;
padding: 0.05rem 0.4rem;
}
/* Version en gris/bleu à droite */
.mc-version {
font-size: 0.66rem;
color: #6272a4;
margin-left: auto;
font-style: italic;
}
/* Skeleton loader */
.mc-loading {
display: flex;
align-items: center;
justify-content: center;
gap: 0.4rem;
padding: 0.4rem 0;
}
.mc-loading-dot {
width: 5px;
height: 5px;
border-radius: 50%;
background: #6272a4;
animation: mc-bounce 1.2s ease-in-out infinite;
}
.mc-loading-dot:nth-child(2) { animation-delay: 0.2s; }
.mc-loading-dot:nth-child(3) { animation-delay: 0.4s; }
@keyframes mc-bounce {
0%, 80%, 100% { transform: scale(0.6); opacity: 0.4; }
40% { transform: scale(1); opacity: 1; }
}
/* =============================== Responsive ================================ */
@media (max-width: 1200px) {
.dashboard-wrapper {
grid-template-columns: 380px 1fr;
}
.stats-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 1024px) {
.dashboard-wrapper {
grid-template-columns: 1fr;
}
.stats-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 600px) {
body {
padding: 1rem;
}
h1 {
font-size: 2rem;
}
.card,
form {
font-size: 1.1rem;
width: 100%;
}
.stats-grid {
grid-template-columns: 1fr;
}
.stat-value {
font-size: 1.8rem;
}
.other-grid {
grid-template-columns: 1fr;
}
.dashboard-wrapper {
gap: 1rem;
}
.credits-grid {
grid-template-columns: 1fr;
}
.small-credits {
grid-template-columns: 1fr;
}
}
/* =============================== Reduced Motion ================================ */
@media (prefers-reduced-motion: reduce) {
#bg-video {
display: none !important;
}
#bg-image {
display: block !important;
}
.mc-online {
animation: none;
}
.mc-loading-dot {
animation: none;
opacity: 0.6;
}
}
/* =============================== Stat Cards Minecraft ================================ */
.stat-card-mc {
border-top: 2px solid rgba(189, 147, 249, 0.4);
}
.stat-label-port {
font-size: 0.75rem;
color: #8be9fd;
font-family: 'Courier New', monospace;
font-weight: 400;
margin-left: 0.2rem;
}
.mc-ram {
display: flex;
align-items: center;
gap: 0.25rem;
font-size: 0.75rem;
font-weight: 700;
}
+48
View File
@@ -0,0 +1,48 @@
/* Dracula Log Viewer Theme */
@import url('https://fonts.googleapis.com/css2?family=Comfortaa:wght@400;700&display=swap');
body {
background-color: #282a36;
color: #f8f8f2;
font-family: 'Comfortaa', cursive;
margin: 0;
padding: 2rem;
display: flex;
flex-direction: column;
align-items: center;
}
h1 {
color: #bd93f9;
margin-bottom: 1.5rem;
font-size: 2.3rem;
text-align: center;
}
/* Output box */
.log-box {
background: #44475a;
border-radius: 10px;
padding: 1.5rem 2rem;
font-size: 1.2rem;
max-width: 900px;
width: 100%;
box-shadow: 0 6px 25px rgba(0,0,0,0.4);
line-height: 1.6;
overflow-wrap: break-word;
word-break: break-all;
}
/* Highlight line on hover */
.log-box:hover {
background-color: #6272a4;
transform: translateY(-3px);
transition: 0.2s;
}
/* Footer */
footer {
margin-top: 3rem;
font-size: 0.9rem;
color: #6272a4;
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 612 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+112
View File
@@ -0,0 +1,112 @@
/* Comfortaa */
@import url('https://fonts.googleapis.com/css2?family=Comfortaa:wght@300;400;700&display=swap');
/* Global */
body {
background: #1c1e26;
color: #ededed;
font-family: "Comfortaa", sans-serif;
padding: 2.5rem;
line-height: 1.7;
}
/* Title */
h1 {
color: #bd93f9;
text-align: center;
font-size: 2.4rem;
margin-bottom: 2.3rem;
font-weight: 700;
letter-spacing: 1px;
}
/* Table container */
table {
width: 100%;
max-width: 900px;
margin: 0 auto;
border-collapse: collapse;
background: #262933;
border-radius: 16px;
overflow: hidden;
box-shadow: 0 14px 32px rgba(0,0,0,0.4);
}
/* Header row */
th {
background: #2f3340;
padding: 1rem 1.2rem;
font-size: 0.95rem;
color: #bbb;
text-transform: uppercase;
letter-spacing: 0.08em;
text-align: left;
}
/* File rows */
td {
padding: 0.9rem 1.2rem;
border-bottom: 1px solid #343847;
font-size: 1rem;
vertical-align: middle;
}
/* Hover effect */
tr:hover td {
background: #3a3f51;
}
/* Links */
a {
color: #bd93f9;
text-decoration: none;
font-weight: 700;
transition: 0.2s;
}
a:hover {
color: #ffb2ff;
text-shadow: 0 0 6px rgba(255, 150, 255, 0.6);
}
/* Icons */
img {
width: 22px;
height: 22px;
margin-right: 6px;
vertical-align: middle;
}
/* Parent directory row */
tr:first-of-type td {
background: #232630;
}
/* File size/date columns */
td:nth-child(3),
td:nth-child(4) {
color: #a1a8c6;
font-size: 0.9rem;
}
/* Apache signature footer */
address {
text-align: center;
margin-top: 2rem;
color: #737a95;
font-size: 0.85rem;
font-style: normal;
}
/* Responsive */
@media (max-width: 700px) {
body {
padding: 1.3rem;
}
table {
font-size: 0.9rem;
}
td, th {
padding: 0.7rem;
}
}
+38
View File
@@ -0,0 +1,38 @@
const audios = [
"/data/media/mp3/win7.start.mp3",
"/data/media/mp3/win10.usb.mp3",
"/data/media/mp3/winxp.shut.mp3",
"/data/media/mp3/win10.error.mp3"
];
// Calculer l'index basé sur le jour actuel
const audioIndex = Math.floor(Date.now() / (1000 * 60 * 60 * 24)) % audios.length;
window.addEventListener('load', () => {
const audio = document.getElementById("monSon");
if (!audio) {
console.error("Element audio non trouvé");
return;
}
audio.src = audios[audioIndex];
audio.volume = 1.0;
console.log("En attente d'un clic pour jouer:", audios[audioIndex]);
// Jouer le son au premier clic
const playAudio = () => {
console.log("Clic détecté - lecture du son");
audio.play().catch((error) => {
console.log("Erreur de lecture:", error);
});
document.removeEventListener("click", playAudio);
document.removeEventListener("keydown", playAudio);
document.removeEventListener("touchstart", playAudio);
};
document.addEventListener("click", playAudio);
document.addEventListener("keydown", playAudio);
document.addEventListener("touchstart", playAudio);
});
Executable
+70
View File
@@ -0,0 +1,70 @@
// ============================================
// bg.js - Optimized with WebM support
// ============================================
(function() {
'use strict';
const bgs = [
"/data/fond/moon_1080p.webm", // Changed to WebM
"/data/fond/astro.webp", // Changed to WebP
"/data/fond/Pino.png",
];
const bgIndex = Math.floor(Date.now() / (1000 * 60 * 60 * 24)) % bgs.length;
let visibilityHandler = null;
document.addEventListener("DOMContentLoaded", () => {
const src = bgs[bgIndex];
const video = document.getElementById("bg-video");
const img = document.getElementById("bg-image");
if (!video || !img) return;
const isVideo = /\.(mp4|webm|ogg)$/i.test(src);
const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
// Reduced motion → force image with WebP fallback
if (prefersReducedMotion && isVideo) {
const fallback = src.replace(/\.(mp4|webm|ogg)$/i, ".webp");
img.src = fallback;
img.style.display = "block";
video.style.display = "none";
return;
}
if (isVideo) {
video.src = src;
video.style.display = "block";
img.style.display = "none";
// Visibility handler for pause/play
visibilityHandler = function() {
if (document.hidden) {
video.pause();
} else {
video.play().catch(e => console.log('Video play prevented:', e));
}
};
document.addEventListener('visibilitychange', visibilityHandler);
// Cleanup on page unload
window.addEventListener('beforeunload', () => {
if (visibilityHandler) {
document.removeEventListener('visibilitychange', visibilityHandler);
}
video.pause();
video.src = '';
});
video.load();
} else {
img.src = src;
img.style.display = "block";
video.style.display = "none";
video.pause();
video.src = '';
}
});
})();
+18
View File
@@ -0,0 +1,18 @@
document.addEventListener("DOMContentLoaded", () => {
const motdMessage = window.motds[window.motdIndex];
const targetMessage = "Linux c'est mieux que tout, si quelqu'un pointe les problèmes de compatibilité, c'est la faute des devs des logiciels.";
if (motdMessage === targetMessage) {
// Create a div with an ID
const linkTarget = document.createElement("div");
linkTarget.id = "linux-motd";
linkTarget.textContent = "Special MOTD section!";
document.getElementById("links-container").appendChild(linkTarget);
// Create an anchor linking to it
const anchor = document.createElement("a");
anchor.href = "#linux-motd";
anchor.textContent = "Go to special MOTD";
document.getElementById("links-container").appendChild(anchor);
}
});
+20
View File
@@ -0,0 +1,20 @@
// ============================================
// captcha.js - Optimized
// ============================================
(function() {
'use strict';
const captchas = [
"secret/captcha2.html",
"secret/captcha.html",
];
const captchaIndex = Math.floor(Date.now() / (1000 * 60 * 60 * 24)) % captchas.length;
document.addEventListener("DOMContentLoaded", () => {
const captchaLink = document.getElementById("captchas");
if (captchaLink) {
captchaLink.href = captchas[captchaIndex];
}
});
})();
+41
View File
@@ -0,0 +1,41 @@
window.addEventListener('load', () => {
const cookieBtn = document.createElement('button');
cookieBtn.textContent = 'Accept Cookies 🍪';
cookieBtn.className = 'cookie-btn';
cookieBtn.style.position = 'fixed';
cookieBtn.style.bottom = '20px';
cookieBtn.style.right = '20px';
cookieBtn.style.zIndex = '9999';
document.body.appendChild(cookieBtn);
cookieBtn.addEventListener('click', () => {
for (let i = 0; i < 100; i++) createCookie();
});
function createCookie() {
const cookie = document.createElement('div');
cookie.textContent = '🍪';
Object.assign(cookie.style, {
position: 'fixed',
fontSize: `${Math.random() * 30 + 20}px`,
left: `${Math.random() * window.innerWidth}px`,
top: `-${Math.random() * 50}px`,
zIndex: '9999',
pointerEvents: 'none',
transition: 'transform 5s linear, opacity 5s linear'
});
document.body.appendChild(cookie);
setTimeout(() => {
cookie.style.transform =
`translateY(${window.innerHeight + 50}px) rotate(${Math.random() * 360}deg)`;
cookie.style.opacity = '0';
}, 50);
setTimeout(() => cookie.remove(), 5500);
}
});
+388
View File
@@ -0,0 +1,388 @@
/**
* Sparklines & Easter Egg - Nixiews Dashboard
* Ajoute des mini-graphiques dans les stat-cards existantes
* + easter egg clavier dans le loader
* + lecture du fichier server.status (remplace news.json)
*/
(function () {
'use strict';
// =========================================================
// CONFIG
// =========================================================
const MAX_POINTS = 20;
const CANVAS_H = 36;
const COLOR_OK = '#50fa7b';
const COLOR_WARN = '#ffb86c';
const COLOR_DANGER = '#ff5555';
const COLOR_LINE = 'rgba(98,114,164,0.35)';
const COLOR_FILL_OK = 'rgba(80,250,123,0.12)';
const TRACKED = [
{
id: 'cpu',
max: 100,
warn: 50,
danger: 80,
parse: v => parseFloat(v),
},
{
id: 'ram',
max: null,
maxId: 'ram-subtitle',
warn: 60,
danger: 80,
parse: (v, sub) => {
const m = sub && sub.match(/\((\d+\.?\d*)%\)/);
return m ? parseFloat(m[1]) : null;
},
},
{
id: 'load',
max: 8,
warn: 50,
danger: 80,
parse: v => Math.min((parseFloat(v) / 8) * 100, 100),
},
];
const history = {};
TRACKED.forEach(t => { history[t.id] = []; });
// =========================================================
// CANVAS / SPARKLINES
// =========================================================
function injectCanvas(statCard) {
if (statCard.querySelector('.spark-canvas')) return;
const canvas = document.createElement('canvas');
canvas.className = 'spark-canvas';
canvas.width = statCard.offsetWidth || 150;
canvas.height = CANVAS_H;
canvas.style.cssText = `
display: block;
width: 100%;
height: ${CANVAS_H}px;
margin-top: 0.5rem;
border-radius: 6px;
opacity: 0.85;
`;
statCard.appendChild(canvas);
return canvas;
}
function drawSparkline(canvas, data, warn, danger) {
if (!canvas || data.length < 2) return;
const ctx = canvas.getContext('2d');
const W = canvas.offsetWidth || canvas.width;
const H = canvas.height;
canvas.width = W;
ctx.clearRect(0, 0, W, H);
const range = 100;
const stepX = W / (MAX_POINTS - 1);
const xOf = i => i * stepX;
const yOf = v => H - ((v / range) * (H - 4)) - 2;
const last = data[data.length - 1];
const color = last > danger ? COLOR_DANGER : last > warn ? COLOR_WARN : COLOR_OK;
ctx.beginPath();
ctx.moveTo(xOf(0), H);
data.forEach((v, i) => ctx.lineTo(xOf(i), yOf(v)));
ctx.lineTo(xOf(data.length - 1), H);
ctx.closePath();
ctx.fillStyle = last > danger
? 'rgba(255,85,85,0.10)'
: last > warn
? 'rgba(255,184,108,0.10)'
: COLOR_FILL_OK;
ctx.fill();
ctx.beginPath();
data.forEach((v, i) => {
i === 0 ? ctx.moveTo(xOf(i), yOf(v)) : ctx.lineTo(xOf(i), yOf(v));
});
ctx.strokeStyle = color;
ctx.lineWidth = 2;
ctx.lineJoin = 'round';
ctx.stroke();
const lx = xOf(data.length - 1);
const ly = yOf(last);
ctx.beginPath();
ctx.arc(lx, ly, 3, 0, Math.PI * 2);
ctx.fillStyle = color;
ctx.fill();
[50, 80].forEach(pct => {
const gy = yOf(pct);
ctx.beginPath();
ctx.moveTo(0, gy);
ctx.lineTo(W, gy);
ctx.strokeStyle = COLOR_LINE;
ctx.lineWidth = 0.8;
ctx.setLineDash([3, 4]);
ctx.stroke();
ctx.setLineDash([]);
});
}
function sample() {
TRACKED.forEach(t => {
const el = document.getElementById(t.id);
const subEl = document.getElementById(t.id + '-subtitle');
if (!el) return;
const raw = el.textContent.trim();
const sub = subEl ? subEl.textContent.trim() : '';
const val = t.parse(raw, sub);
if (val === null || isNaN(val)) return;
history[t.id].push(val);
if (history[t.id].length > MAX_POINTS) history[t.id].shift();
const card = el.closest('.stat-card');
if (!card) return;
let canvas = card.querySelector('.spark-canvas');
if (!canvas) canvas = injectCanvas(card);
if (!canvas) return;
drawSparkline(canvas, history[t.id], t.warn, t.danger);
});
}
function watchStats() {
const section = document.querySelector('.stats-section');
if (!section) return;
const obs = new MutationObserver(() => sample());
obs.observe(section, { subtree: true, characterData: true, childList: true });
setTimeout(sample, 6000);
}
// =========================================================
// EASTER EGG
// =========================================================
const EASTER_COMMANDS = {
'sudo': '⚠ [sudo] password for nixiews: \n💀 sudo: permission refusée. T\'es pas root ici.',
'rm -rf /': '💀 SIGTERM — Au revoir cruel monde... \n ... nan je déconne, j\'ai pas les droits.',
'rm -rf': '💀 Haha non. Pas sur mon serveur.',
'emerge': '🟢 emerge: Calcul du dépôt world...\n ETA: 3 jours, 14 heures, 7 minutes.\n (C\'est Gentoo, t\'avais qu\'à pas.)',
'nixos-rebuild': '❓ nixos-rebuild: command not found\n Ici c\'est Gentoo/Debian. Le pseudo c\'est juste un pseudo.',
'pacman': '🔴 erreur: pacman not found. Ici c\'est Gentoo/Debian.',
'apt': '🟡 apt: command not found (on Gentoo side)\n Essaie emerge plutôt.',
'reboot': '♻ Reboot programmé dans... nan, j\'ai changé d\'avis.',
'uname': '🐧 Linux nicoleta 6.x.x-gentoo #1 SMP PREEMPT_DYNAMIC\n x86_64 GNU/Linux',
'htop': '📊 htop: trop stylé pour être lancé dans un easter egg.',
'ls': '📁 . .. index.html data/ secret/ binpkg/ fun/ minecraft/',
'cat /etc/passwd': '😏 root:x:0:0::/root:/bin/bash\n nixiews:x:1000:1000::/home/nixiews:/bin/zsh\n claude:x:9999:9999:meilleur ami:/dev/null:/bin/sh',
'help': '📖 Commandes disponibles:\n sudo, rm -rf /, emerge, nixos-rebuild, pacman, apt,\n reboot, uname, htop, ls, cat /etc/passwd, help\n (Nixiews = pseudo, pas une distro 🙃)',
};
let typedBuffer = '';
let eggTimeout = null;
function resetBuffer() { typedBuffer = ''; }
function checkEasterEgg(key) {
const loader = document.getElementById('loader');
if (!loader || loader.style.display === 'none' || loader.style.opacity === '0') return;
typedBuffer += key.toLowerCase();
if (typedBuffer.length > 30) typedBuffer = typedBuffer.slice(-30);
clearTimeout(eggTimeout);
eggTimeout = setTimeout(resetBuffer, 2500);
for (const [cmd, response] of Object.entries(EASTER_COMMANDS)) {
if (typedBuffer.endsWith(cmd)) {
triggerEgg(cmd, response);
typedBuffer = '';
break;
}
}
}
function triggerEgg(cmd, response) {
const logs = document.querySelector('#loader .logs');
if (!logs) return;
const cmdLine = document.createElement('div');
cmdLine.style.cssText = `
color: #8be9fd;
font-family: 'Courier New', monospace;
font-size: 0.85rem;
margin-top: 0.3rem;
animation: fadeIn 0.2s ease;
`;
cmdLine.innerHTML = `<span style="color:#50fa7b">nixiews@nicoleta</span><span style="color:#f8f8f2">:</span><span style="color:#bd93f9">~</span><span style="color:#f8f8f2">$</span> ${cmd}`;
logs.appendChild(cmdLine);
setTimeout(() => {
const responseLine = document.createElement('div');
responseLine.style.cssText = `
color: #f1fa8c;
font-family: 'Courier New', monospace;
font-size: 0.82rem;
white-space: pre-wrap;
margin-bottom: 0.3rem;
animation: fadeIn 0.3s ease;
`;
responseLine.textContent = response;
logs.appendChild(responseLine);
logs.scrollTop = logs.scrollHeight;
}, 300);
logs.scrollTop = logs.scrollHeight;
}
// =========================================================
// SERVER STATUS — lecture directe du .status
// =========================================================
const STATUS_ENDPOINT = '/data/server.status';
function parseStatusFile(text) {
const result = { status: '', couleur: '#bd93f9', date: '', message: '', motds: [] };
const lines = text.split('\n');
for (const line of lines) {
const m = line.match(/^\[(\w+)\]\s*(.*)$/);
if (!m) continue;
const [, key, val] = m;
if (key === 'motd') {
result.motds.push(val.trim());
} else {
result[key] = val.trim();
}
}
return result;
}
function buildStatusCard(data) {
const motdsHtml = data.motds.map(m =>
`<div class="status-motd">💬 ${m}</div>`
).join('');
return `
<div class="status-header">
<span class="status-badge" style="color:${data.couleur};border-color:${data.couleur}20;background:${data.couleur}15">
${data.status}
</span>
${data.date ? `<span class="status-date">${data.date}</span>` : ''}
</div>
${data.message ? `<p class="status-message">${data.message}</p>` : ''}
${motdsHtml ? `<div class="status-motds">${motdsHtml}</div>` : ''}
`;
}
function injectStatusSection() {
const mainContent = document.querySelector('.main-content');
const otherSection = document.querySelector('.other-section');
if (!mainContent) return null;
const section = document.createElement('div');
section.className = 'news-section';
section.innerHTML = `
<h2>Infos du jour</h2>
<div class="news-card">
<div class="news-loading">
<div class="mc-loading-dot"></div>
<div class="mc-loading-dot"></div>
<div class="mc-loading-dot"></div>
</div>
</div>`;
if (otherSection) {
mainContent.insertBefore(section, otherSection);
} else {
mainContent.appendChild(section);
}
const style = document.createElement('style');
style.textContent = `
.news-section h2 { color: #bd93f9; font-size: 1.5rem; margin-bottom: 1rem; }
.news-card {
background-color: rgba(68,71,90,0.95);
border-radius: 12px;
padding: 1.4rem 1.6rem;
border-left: 3px solid #bd93f9;
transition: box-shadow 0.2s ease;
}
.news-card:hover { box-shadow: 0 8px 20px rgba(0,0,0,0.4); }
.status-header { display: flex; align-items: center; gap: 1rem; margin-bottom: 0.8rem; }
.status-badge {
font-size: 0.82rem;
font-weight: 700;
padding: 0.2rem 0.7rem;
border-radius: 20px;
border: 1px solid;
white-space: nowrap;
}
.status-date { font-size: 0.78rem; color: #6272a4; font-family: 'Courier New', monospace; margin-left: auto; }
.status-message { color: #f8f8f2; font-size: 0.95rem; margin-bottom: 0.9rem; line-height: 1.5; }
.status-motds { display: flex; flex-direction: column; gap: 0.4rem; }
.status-motd {
font-size: 0.85rem;
color: #6272a4;
padding: 0.3rem 0.6rem;
background: rgba(40,42,54,0.6);
border-radius: 6px;
font-family: 'Courier New', monospace;
}
.news-loading { display: flex; align-items: center; justify-content: center; gap: 0.4rem; padding: 0.6rem 0; }
.status-error { color: #ff5555; font-size: 0.85rem; font-family: 'Courier New', monospace; }
`;
document.head.appendChild(style);
return section.querySelector('.news-card');
}
async function fetchStatus(card) {
try {
const res = await fetch(STATUS_ENDPOINT, { cache: 'no-cache' });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const text = await res.text();
card.innerHTML = buildStatusCard(parseStatusFile(text));
} catch (e) {
card.innerHTML = `<span class="status-error">⚠ Impossible de charger server.status (${e.message})</span>`;
}
}
function initStatus() {
const card = injectStatusSection();
if (card) fetchStatus(card);
}
// =========================================================
// INIT
// =========================================================
function init() {
document.addEventListener('keypress', e => {
if (e.key && e.key.length === 1) checkEasterEgg(e.key);
});
document.addEventListener('keydown', e => {
if (e.key === 'Enter') checkEasterEgg(' ');
});
if (document.querySelector('.stats-section')) {
watchStats();
} else {
document.addEventListener('DOMContentLoaded', watchStats);
}
initStatus();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
Executable
+19
View File
@@ -0,0 +1,19 @@
// script normal
// change d'image tout les jours
const images = [
"autre/Image1.html",
"autre/Image2.html",
"autre/Image3.html",
"autre/Image4.html",
"autre/Image5.html",
"autre/Image6.html",
"autre/Image7.html",
"autre/Image8.html",
"autre/Image9.html",
]
const imageIndex = Math.floor(Date.now() / (1000 * 60 * 60 * 24)) % images.length
document.addEventListener("DOMContentLoaded", () => {
document.getElementById("image").href = images[imageIndex]
})
+315
View File
@@ -0,0 +1,315 @@
/* =========================
CONFIG
========================= */
const MIN_TIME = 10000;
const startTime = performance.now();
/* =========================
HELPERS
========================= */
function getTimeStamp() {
return new Date().toLocaleTimeString("fr-FR", {
hour: "2-digit",
minute: "2-digit",
second: "2-digit"
});
}
/* =========================
SEQUENTIAL TEXT + FROZEN TIME
========================= */
const steps = document.querySelectorAll(".step");
steps.forEach((el, i) => {
setTimeout(() => {
const timeEl = el.querySelector(".time");
if (timeEl) timeEl.textContent = getTimeStamp();
el.classList.add("visible");
}, i * 600);
});
/* =========================
RANDOM GRID / PIXEL ART
========================= */
const grid = document.getElementById("grid");
let gridSquares = []; // Track for cleanup
let pixelIntervals = []; // Track pixel intervals for cleanup
if (grid) {
const squareCount = Math.floor(Math.random() * 6) + 8;
const occupiedCells = new Set(); // Track occupied grid positions
for (let i = 0; i < squareCount; i++) {
const square = document.createElement("div");
square.className = "square";
// Find an unoccupied position
let col, row, posKey;
let attempts = 0;
do {
col = Math.floor(Math.random() * 5) + 1;
row = Math.floor(Math.random() * 4) + 1;
posKey = `${col}-${row}`;
attempts++;
} while (occupiedCells.has(posKey) && attempts < 50);
// Mark position as occupied
occupiedCells.add(posKey);
square.style.gridColumn = col;
square.style.gridRow = row;
// Make squares visible immediately without animation
square.style.opacity = "1";
square.style.transform = "none";
// Draw pixels on hover (can be triggered multiple times)
const drawHandler = () => {
const canvas = square.querySelector("canvas");
if (!canvas || !square.hasPixels) {
drawPixels(square);
if (!square.pixelInterval) {
startPixelCycle(square); // Start the 5-second cycle for this square
}
}
};
square.addEventListener("pointerenter", drawHandler);
gridSquares.push(square);
grid.appendChild(square);
}
}
function drawPixels(el) {
const existingCanvas = el.querySelector("canvas");
if (existingCanvas) {
existingCanvas.remove();
}
const canvas = document.createElement("canvas");
canvas.width = el.clientWidth;
canvas.height = el.clientHeight;
canvas.style.position = "absolute";
canvas.style.top = "0";
canvas.style.left = "0";
el.appendChild(canvas);
const ctx = canvas.getContext("2d");
const colors = ["#ff4fd8", "#9333ea", "#ef4444"];
const px = 6;
// Fill background
ctx.fillStyle = "#0a0a0c";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Collect all pixel positions that will be drawn
const pixels = [];
for (let x = 0; x < canvas.width; x += px) {
for (let y = 0; y < canvas.height; y += px) {
if (Math.random() > 0.6) {
pixels.push({
x: x,
y: y,
color: colors[Math.floor(Math.random() * colors.length)]
});
}
}
}
// Mark that this square has pixels
el.hasPixels = true;
// Create an offscreen canvas to draw all pixels at once
const offscreenCanvas = document.createElement("canvas");
offscreenCanvas.width = canvas.width;
offscreenCanvas.height = canvas.height;
const offscreenCtx = offscreenCanvas.getContext("2d");
// Draw all pixels on offscreen canvas
offscreenCtx.fillStyle = "#0a0a0c";
offscreenCtx.fillRect(0, 0, offscreenCanvas.width, offscreenCanvas.height);
pixels.forEach(pixel => {
offscreenCtx.fillStyle = pixel.color;
offscreenCtx.fillRect(pixel.x, pixel.y, px, px);
});
// Animate the entire pixel pattern sliding in from left
const duration = 400; // ms for animation
const startTime = performance.now();
const startX = -canvas.width; // Start completely off-screen to the left
const endX = 0;
function animate(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
// Clear canvas
ctx.fillStyle = "#0a0a0c";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Calculate current X position
const currentX = startX + (endX - startX) * progress;
// Draw the entire offscreen canvas at the current position
ctx.drawImage(offscreenCanvas, currentX, 0);
if (progress < 1) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
}
function clearPixels(el) {
const canvas = el.querySelector("canvas");
if (!canvas) return;
const ctx = canvas.getContext("2d");
// Create an offscreen canvas with current pixel state
const offscreenCanvas = document.createElement("canvas");
offscreenCanvas.width = canvas.width;
offscreenCanvas.height = canvas.height;
const offscreenCtx = offscreenCanvas.getContext("2d");
// Copy current canvas to offscreen
offscreenCtx.drawImage(canvas, 0, 0);
// Animate the entire pixel pattern sliding out to the right
const duration = 400; // ms for animation
const startTime = performance.now();
const startX = 0;
const endX = canvas.width; // Slide completely off-screen to the right
function animate(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
// Clear canvas
ctx.fillStyle = "#0a0a0c";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Calculate current X position
const currentX = startX + (endX - startX) * progress;
// Draw the entire offscreen canvas at the current position
ctx.drawImage(offscreenCanvas, currentX, 0);
if (progress < 1) {
requestAnimationFrame(animate);
} else {
// Final clear
ctx.fillStyle = "#0a0a0c";
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
}
requestAnimationFrame(animate);
}
function startPixelCycle(square) {
const interval = setInterval(() => {
clearPixels(square);
square.hasPixels = false; // Mark that pixels are cleared
}, 5000);
pixelIntervals.push(interval);
// Store interval on the element for later cleanup
square.pixelInterval = interval;
}
/* =========================
LOADER CLEANUP
========================= */
function cleanupLoader() {
// Clear all intervals
pixelIntervals.forEach(interval => clearInterval(interval));
pixelIntervals = [];
// Remove grid squares
gridSquares.forEach(sq => sq.remove());
gridSquares = [];
// Remove loader
const loader = document.getElementById("loader");
if (loader) {
loader.style.opacity = "0";
loader.style.pointerEvents = "none";
setTimeout(() => {
loader.remove();
}, 800);
}
}
/* =========================
LOADER HANDOFF
========================= */
function hideLoader() {
const elapsed = performance.now() - startTime;
const remaining = Math.max(0, MIN_TIME - elapsed);
setTimeout(() => {
document.body.classList.remove("loading");
cleanupLoader(); // Clean up properly
}, remaining);
}
window.addEventListener("load", hideLoader);
/* =========================
LOADING BAR - OPTIMIZED
========================= */
const loadingBar = document.getElementById('loading-bar');
let currentLoadingInterval = null;
function showLoadingBar() {
// Clear any existing interval first
if (currentLoadingInterval) {
clearInterval(currentLoadingInterval);
}
loadingBar.style.width = '0%';
loadingBar.style.display = 'block';
setTimeout(() => loadingBar.style.width = '30%', 50);
let progress = 30;
currentLoadingInterval = setInterval(() => {
progress += Math.random() * 20;
if (progress < 90) {
loadingBar.style.width = progress + '%';
} else {
clearInterval(currentLoadingInterval);
currentLoadingInterval = null;
}
}, 300);
return currentLoadingInterval;
}
function completeLoadingBar(interval) {
if (interval) {
clearInterval(interval);
if (interval === currentLoadingInterval) {
currentLoadingInterval = null;
}
}
loadingBar.style.width = '100%';
setTimeout(() => {
loadingBar.style.width = '0%';
}, 400);
}
// Initial page load
let pageLoadInterval;
if (document.readyState === 'loading') {
pageLoadInterval = showLoadingBar();
}
window.addEventListener('load', () => {
completeLoadingBar(pageLoadInterval);
});
+282
View File
@@ -0,0 +1,282 @@
/**
* Minecraft Server Status - Sidebar Widget
* Source : API Crafty Controller (proxy PHP crafty-stats.php)
*/
(function () {
'use strict';
const API = '/data/api/crafty-stats.php';
const MODS_API = '/data/api/mods-mc.php';
const INTERVAL = 30000; // 30s
const SERVERS = [
{ key: 'mc1', cardId: 'mc-card-0' },
{ key: 'mc2', cardId: 'mc-card-1' },
];
let timer = null;
let modsData = null; // cache des mods, chargé une seule fois
// ── Mods ────────────────────────────────────────────────────────────────
const modsStyle = document.createElement('style');
modsStyle.textContent = `
.mc-mods-section {
margin-top: 0.5rem;
border-top: 1px solid rgba(98, 114, 164, 0.3);
padding-top: 0.5rem;
}
.mc-mods-toggle {
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
padding: 0.25rem 0;
user-select: none;
}
.mc-mods-toggle-label {
font-size: 0.72rem;
font-weight: 700;
color: #6272a4;
text-transform: uppercase;
letter-spacing: 0.07em;
}
.mc-mods-toggle-meta {
display: flex;
align-items: center;
gap: 0.4rem;
font-size: 0.68rem;
color: #6272a4;
}
.mc-mods-arrow {
font-size: 0.6rem;
color: #6272a4;
transition: transform 0.2s;
display: inline-block;
}
.mc-mods-arrow.open { transform: rotate(90deg); }
.mc-mods-pills {
display: none;
flex-wrap: wrap;
gap: 0.3rem;
padding-top: 0.45rem;
}
.mc-mods-pills.open { display: flex; }
.mc-mod-pill {
background: rgba(98, 114, 164, 0.25);
color: #f8f8f2;
font-size: 0.65rem;
padding: 2px 7px;
border-radius: 20px;
border: 1px solid rgba(98, 114, 164, 0.35);
white-space: nowrap;
cursor: default;
}
.mc-mod-pill:hover {
background: rgba(189, 147, 249, 0.2);
border-color: rgba(189, 147, 249, 0.5);
color: #bd93f9;
}
.mc-mods-error {
font-size: 0.7rem;
color: #ff5555;
padding-top: 0.3rem;
font-style: italic;
}
`;
document.head.appendChild(modsStyle);
async function fetchMods() {
try {
const res = await fetch(MODS_API, { cache: 'no-cache' });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
modsData = await res.json();
} catch (e) {
console.warn('[mods-mc] Erreur fetch :', e);
}
}
function appendMods(card, srv) {
if (!modsData) return;
// trouve l'entrée correspondant au nom du serveur Crafty
const entry = modsData.find(m => m.label === srv?.name) ?? modsData[SERVERS.findIndex(s => s.cardId === card.id)];
if (!entry) return;
const section = document.createElement('div');
section.className = 'mc-mods-section';
if (entry.error) {
const err = document.createElement('div');
err.className = 'mc-mods-error';
err.textContent = entry.error;
section.appendChild(err);
card.appendChild(section);
return;
}
const count = entry.mods.length;
const toggle = document.createElement('div');
toggle.className = 'mc-mods-toggle';
toggle.innerHTML = `
<span class="mc-mods-toggle-label">Mods</span>
<span class="mc-mods-toggle-meta">
<span>${count} mod${count !== 1 ? 's' : ''}</span>
<span class="mc-mods-arrow">&#9656;</span>
</span>
`;
const pills = document.createElement('div');
pills.className = 'mc-mods-pills';
entry.mods.forEach(mod => {
const pill = document.createElement('span');
pill.className = 'mc-mod-pill';
pill.title = mod.file;
pill.textContent = mod.name;
pills.appendChild(pill);
});
toggle.addEventListener('click', () => {
pills.classList.toggle('open');
toggle.querySelector('.mc-mods-arrow').classList.toggle('open');
});
section.appendChild(toggle);
section.appendChild(pills);
card.appendChild(section);
}
// ── Status ───────────────────────────────────────────────────────────────
async function fetchAll() {
let data;
try {
const res = await fetch(API, { cache: 'no-cache' });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
data = await res.json();
} catch (e) {
console.error('crafty-stats error:', e);
return;
}
if (!data.success) return;
SERVERS.forEach(({ key, cardId }) => {
const card = document.getElementById(cardId);
if (!card) return;
renderCard(card, data.servers?.[key]);
});
updateStatCards(data.servers);
}
function renderCard(card, srv) {
if (!srv) {
card.innerHTML = `
<div class="mc-header">
<span class="mc-name">Serveur inconnu</span>
<span class="mc-badge mc-offline">&#9679; Hors ligne</span>
</div>`;
appendMods(card, srv);
return;
}
const online = srv.running;
const players = online ? `${srv.players} / ${srv.max_players}` : '— / —';
let ramHtml = '';
if (online && srv.ram_used !== null) {
const usedStr = ramFmt(srv.ram_used);
const allocStr = srv.ram_alloc ? ramFmt(srv.ram_alloc) : '?';
const pct = srv.ram_alloc > 0 ? Math.round((srv.ram_used / srv.ram_alloc) * 100) : 0;
const color = pct > 90 ? '#ff5555' : pct > 70 ? '#ffb86c' : '#50fa7b';
ramHtml = `
<span class="mc-ram" style="color:${color}">
<svg width="10" height="10" viewBox="0 0 24 24" fill="currentColor" style="color:#bd93f9;flex-shrink:0">
<path d="M4 6h16v2H4zm0 5h16v2H4zm0 5h16v2H4z"/>
</svg>
${usedStr}<span style="color:#6272a4;font-weight:400"> / ${allocStr}</span>
</span>`;
}
const cpuHtml = (online && srv.cpu !== null)
? `<span class="mc-version">${srv.cpu}% CPU</span>`
: '';
card.innerHTML = `
<div class="mc-header">
<span class="mc-name">${esc(srv.name)}</span>
<span class="mc-badge ${online ? 'mc-online' : 'mc-offline'}">${online ? '&#9679; En ligne' : '&#9679; Hors ligne'}</span>
</div>
<div class="mc-address">${esc('nix.roulaise.net')}:${srv.port}</div>
<div class="mc-info">
<span class="mc-players">
<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">
<path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"/>
</svg>
${players} joueurs
</span>
${ramHtml}
${cpuHtml}
</div>`;
appendMods(card, srv);
}
function updateStatCards(servers) {
['mc1', 'mc2'].forEach((key, i) => {
const n = i + 1;
const el = document.getElementById(`mc${n}-ram`);
const sub = document.getElementById(`mc${n}-ram-subtitle`);
if (!el) return;
const srv = servers?.[key];
if (!srv || !srv.running) {
el.textContent = 'Éteint';
if (sub) sub.textContent = '—';
el.style.color = '#6272a4';
return;
}
if (srv.ram_used !== null) {
const pct = srv.ram_alloc > 0 ? Math.round((srv.ram_used / srv.ram_alloc) * 100) : 0;
el.textContent = ramFmt(srv.ram_used);
if (sub) sub.textContent = `alloué: ${srv.ram_alloc ? ramFmt(srv.ram_alloc) : '?'} (${pct}%)`;
el.style.color = pct > 90 ? '#ff5555' : pct > 70 ? '#ffb86c' : '#50fa7b';
} else {
el.textContent = '...';
if (sub) sub.textContent = '—';
}
});
}
function ramFmt(mio) {
return mio >= 1024 ? (mio / 1024).toFixed(1) + ' Gio' : mio + ' Mio';
}
function esc(s) {
return String(s)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}
function start() { timer = setInterval(fetchAll, INTERVAL); }
function stop() { clearInterval(timer); timer = null; }
async function init() {
if (!document.getElementById('mc-card-0')) return;
await fetchMods(); // charge les mods une seule fois au démarrage
fetchAll();
start();
window.addEventListener('beforeunload', stop);
document.addEventListener('visibilitychange', () =>
document.hidden ? stop() : (fetchAll(), start())
);
}
document.readyState === 'loading'
? document.addEventListener('DOMContentLoaded', init)
: init();
})();
+128
View File
@@ -0,0 +1,128 @@
(function () {
const API_URL = '/data/api/mods-mc.php';
// Injecte le CSS dans <head>
const style = document.createElement('style');
style.textContent = `
.mc-mods-section {
margin-top: 0.5rem;
border-top: 1px solid rgba(98, 114, 164, 0.3);
padding-top: 0.5rem;
}
.mc-mods-toggle {
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
padding: 0.25rem 0;
user-select: none;
}
.mc-mods-toggle-label {
font-size: 0.72rem;
font-weight: 700;
color: #6272a4;
text-transform: uppercase;
letter-spacing: 0.07em;
}
.mc-mods-toggle-meta {
display: flex;
align-items: center;
gap: 0.4rem;
font-size: 0.68rem;
color: #6272a4;
}
.mc-mods-arrow {
font-size: 0.6rem;
color: #6272a4;
transition: transform 0.2s;
display: inline-block;
}
.mc-mods-arrow.open { transform: rotate(90deg); }
.mc-mods-pills {
display: none;
flex-wrap: wrap;
gap: 0.3rem;
padding-top: 0.45rem;
}
.mc-mods-pills.open { display: flex; }
.mc-mod-pill {
background: rgba(98, 114, 164, 0.25);
color: #f8f8f2;
font-size: 0.65rem;
padding: 2px 7px;
border-radius: 20px;
border: 1px solid rgba(98, 114, 164, 0.35);
white-space: nowrap;
cursor: default;
}
.mc-mod-pill:hover {
background: rgba(189, 147, 249, 0.2);
border-color: rgba(189, 147, 249, 0.5);
color: #bd93f9;
}
.mc-mods-error {
font-size: 0.7rem;
color: #ff5555;
padding-top: 0.3rem;
font-style: italic;
}
`;
document.head.appendChild(style);
function buildModsBlock(srv, cardEl) {
const section = document.createElement('div');
section.className = 'mc-mods-section';
if (srv.error) {
const err = document.createElement('div');
err.className = 'mc-mods-error';
err.textContent = srv.error;
section.appendChild(err);
cardEl.appendChild(section);
return;
}
const count = srv.mods.length;
const toggle = document.createElement('div');
toggle.className = 'mc-mods-toggle';
toggle.innerHTML = `
<span class="mc-mods-toggle-label">Mods</span>
<span class="mc-mods-toggle-meta">
<span>${count} mod${count !== 1 ? 's' : ''}</span>
<span class="mc-mods-arrow">&#9656;</span>
</span>
`;
const pills = document.createElement('div');
pills.className = 'mc-mods-pills';
srv.mods.forEach(mod => {
const pill = document.createElement('span');
pill.className = 'mc-mod-pill';
pill.title = mod.file;
pill.textContent = mod.name;
pills.appendChild(pill);
});
toggle.addEventListener('click', () => {
const arrow = toggle.querySelector('.mc-mods-arrow');
pills.classList.toggle('open');
arrow.classList.toggle('open');
});
section.appendChild(toggle);
section.appendChild(pills);
cardEl.appendChild(section);
}
fetch(API_URL)
.then(r => r.json())
.then(data => {
data.forEach((srv, i) => {
const card = document.getElementById('mc-card-' + i);
if (card) buildModsBlock(srv, card);
});
})
.catch(err => {
console.warn('[mods-mc] Erreur fetch :', err);
});
})();
+41
View File
@@ -0,0 +1,41 @@
// ============================================
// motd.js - Optimized
// ============================================
(function() {
'use strict';
const motds = [
"Gloire à Titebouille!",
"On dit chocolatine ici!",
"C'est un MOTD ça?",
"Je vais vous ciscoter!",
"Belgian gooner femboy ahhh",
"Miku cosplay UwU",
"Nothing to see",
"If it's stupid but works, then it's not stupid",
"Wait, you're playing Unturned?!",
"Revert to Aquaba!",
"Cherche GOTHIQUES svp :(",
"Vous m'ouvrez Packet Tracer.",
"Gooner Land",
"QT va te faire foutre",
"J'aime l'informatique vu que je suis obligé",
"Allo à l'huile",
"On à cours dans le local technique",
"Je suis pas con, je suis autiste",
"Toni aime bien penser que son café est italien.",
"C'est une violation de la loi Sherman",
"Le kernel cisco packet ryzen dragon s24+ c'est pour quand ?",
"Hacheur - HkmGZ",
"Met un PEKKA! Nan, CT3000+L!",
];
const motdIndex = Math.floor(Date.now() / (1000 * 60 * 60 * 24)) % motds.length;
document.addEventListener("DOMContentLoaded", () => {
const motdElement = document.getElementById("motd");
if (motdElement) {
motdElement.textContent = motds[motdIndex];
}
});
})();
+1
View File
@@ -0,0 +1 @@
console.log('Bienvenue sur le site le plus inutile du monde!');
Executable
+30
View File
@@ -0,0 +1,30 @@
// ============================================
// ri.js - Optimized (keeping GIFs as GIF)
// ============================================
(function() {
'use strict';
const ris = [
"data/media/linux-tux.gif", // Optimized GIF
"data/media/Titebouille.gif", // Optimized GIF
"data/media/gurl1.gif", // Optimized GIF
"data/media/gurl2.gif", // Optimized GIF
"data/media/gurl3.gif", // Optimized GIF
"data/media/Dance1.gif", // Optimized GIF
"data/media/Duck2.webp", // WebP (was PNG)
"data/media/Honk.gif", // Optimized GIF
"data/media/petpet.gif", // Optimized GIF
"data/media/Ereaser.jpg",
];
const riIndex = Math.floor(Date.now() / (1000 * 60 * 60 * 24)) % ris.length;
document.addEventListener("DOMContentLoaded", () => {
const riElement = document.getElementById("ris");
if (riElement) {
// Set loading attribute for better performance
riElement.loading = "lazy";
riElement.src = ris[riIndex];
}
});
})();
+166
View File
@@ -0,0 +1,166 @@
/**
* System Stats Loader — inclut MDADM
*/
(function() {
'use strict';
const API_STATS = '/data/api/system-stats.php';
const API_MDADM = '/data/api/mdadm-status.php';
const INTERVAL = 5000;
let timer = null;
let updating = false;
let loadedOnce = false;
const el = {
uptime: document.getElementById('uptime'),
cpu: document.getElementById('cpu'),
ram: document.getElementById('ram'),
ramSub: document.getElementById('ram-subtitle'),
diskRoot: document.getElementById('disk-root'),
diskRootSub: document.getElementById('disk-root-subtitle'),
diskVar: document.getElementById('disk-var'),
diskVarSub: document.getElementById('disk-var-subtitle'),
diskSrv: document.getElementById('disk-srv'),
diskSrvSub: document.getElementById('disk-srv-subtitle'),
load: document.getElementById('load'),
processes: document.getElementById('processes'),
mdadmCard: document.getElementById('mdadm-card'),
mdadmStatus: document.getElementById('mdadm-status'),
mdadmSub: document.getElementById('mdadm-subtitle'),
};
function set(element, value) {
if (!element || value === undefined) return;
const v = String(value);
if (element.textContent !== v) element.textContent = v;
}
function color(element, percent, warn, danger) {
if (!element) return;
const c = percent > danger ? '#ff5555' : percent > warn ? '#ffb86c' : '#50fa7b';
if (element.style.color !== c) element.style.color = c;
}
async function fetchStats() {
if (updating) return;
updating = true;
try {
const r = await fetch(API_STATS, { cache: 'no-cache' });
if (!r.ok) throw new Error(`HTTP ${r.status}`);
const { success, data } = await r.json();
if (success && data) renderStats(data);
else if (loadedOnce) showError();
} catch(e) {
console.error('system-stats:', e);
if (loadedOnce) showError();
} finally {
updating = false;
}
}
function renderStats(s) {
loadedOnce = true;
set(el.uptime, s.uptime);
if (s.cpu_usage !== undefined) {
set(el.cpu, s.cpu_usage + '%');
color(el.cpu, s.cpu_usage, 50, 80);
}
if (s.ram_used !== undefined) {
set(el.ram, s.ram_used + ' Gio');
set(el.ramSub, `sur ${s.ram_total} Gio (${s.ram_percent}%)`);
color(el.ram, s.ram_percent, 60, 80);
}
if (s.disk_root_free !== undefined) {
set(el.diskRoot, s.disk_root_free + ' Gio');
set(el.diskRootSub, `sur ${s.disk_root_total} Gio (${s.disk_root_percent}% utilisé)`);
color(el.diskRoot, s.disk_root_percent, 70, 90);
}
if (s.disk_var_free !== undefined) {
set(el.diskVar, s.disk_var_free + ' Gio');
set(el.diskVarSub, `sur ${s.disk_var_total} Gio (${s.disk_var_percent}% utilisé)`);
color(el.diskVar, s.disk_var_percent, 70, 90);
}
if (s.disk_srv_free !== undefined) {
set(el.diskSrv, s.disk_srv_free + ' Gio');
set(el.diskSrvSub, `sur ${s.disk_srv_total} Gio (${s.disk_srv_percent}% utilisé)`);
color(el.diskSrv, s.disk_srv_percent, 70, 90);
}
if (s.load_1 !== undefined) {
set(el.load, s.load_1);
color(el.load, (s.load_1 / 4) * 100, 50, 80);
}
set(el.processes, s.processes);
}
async function fetchMdadm() {
try {
const r = await fetch(API_MDADM, { cache: 'no-cache' });
if (!r.ok) throw new Error(`HTTP ${r.status}`);
const d = await r.json();
if (d.healthy) {
set(el.mdadmStatus, '✓ Sain');
if (el.mdadmStatus) el.mdadmStatus.style.color = '#50fa7b';
set(el.mdadmSub, d.arrays.map(a => `${a.name} [${a.bitmap}]`).join(' · '));
if (el.mdadmCard) el.mdadmCard.style.borderTop = '';
} else {
set(el.mdadmStatus, '⚠ Dégradé');
if (el.mdadmStatus) el.mdadmStatus.style.color = '#ff5555';
set(el.mdadmSub, d.arrays
.filter(a => a.health !== 'ok')
.map(a => `${a.name}: ${a.detail || a.health}`)
.join(' · '));
if (el.mdadmCard) el.mdadmCard.style.borderTop = '2px solid #ff5555';
}
} catch(e) {
set(el.mdadmStatus, '?');
if (el.mdadmStatus) el.mdadmStatus.style.color = '#ffb86c';
set(el.mdadmSub, 'erreur fetch');
}
}
function showError() {
['uptime','cpu','ram','diskRoot','diskVar','diskSrv','load','processes'].forEach(k => {
if (el[k]) { el[k].textContent = 'Erreur'; el[k].style.color = '#ff5555'; }
});
}
async function refresh() {
await fetchStats();
await fetchMdadm();
}
function stop() {
if (timer) { clearInterval(timer); timer = null; }
}
function start() {
stop();
timer = setInterval(refresh, INTERVAL);
}
function init() {
if (!document.querySelector('.stats-section')) return;
refresh();
start();
window.addEventListener('beforeunload', stop);
document.addEventListener('visibilitychange', () => {
if (document.hidden) stop();
else { refresh(); start(); }
});
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
else init();
})();
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

+224
View File
@@ -0,0 +1,224 @@
<!doctype html>
<html>
<head>
<!-- (used for SSR) -->
<meta name="description" content="1 shared photos &amp; videos" />
<!-- Facebook Meta Tags -->
<meta property="og:type" content="website" />
<meta property="og:title" content="Public Share" />
<meta property="og:description" content="1 shared photos &amp; videos" />
<meta property="og:image" content="https://immich.nix.roulaise.net/api/assets/c9f559b2-e6e7-4c25-b82c-2311833755c9/thumbnail?key=e6xKBo19ZjbUGxU_h_vv4nBEwS_XWdW9UN5OTHQ5C4Q_rTqltVgwLXUiwd98tY3Iofs" />
<!-- Twitter Meta Tags -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Public Share" />
<meta name="twitter:description" content="1 shared photos &amp; videos" />
<meta name="twitter:image" content="https://immich.nix.roulaise.net/api/assets/c9f559b2-e6e7-4c25-b82c-2311833755c9/thumbnail?key=e6xKBo19ZjbUGxU_h_vv4nBEwS_XWdW9UN5OTHQ5C4Q_rTqltVgwLXUiwd98tY3Iofs" />
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png" />
<link rel="icon" type="image/png" sizes="48x48" href="/favicon-48.png" />
<link rel="icon" type="image/png" sizes="96x96" href="/favicon-96.png" />
<link rel="icon" type="image/png" sizes="144x144" href="/favicon-144.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-icon-180.png" />
<link rel="preload" as="font" type="font/ttf" href="/_app/immutable/assets/GoogleSans.DGRbB7N7.ttf" crossorigin="anonymous" />
<link rel="preload" as="font" type="font/ttf" href="/_app/immutable/assets/GoogleSansCode.a41q3NA0.ttf" crossorigin="anonymous" />
<link rel="modulepreload" href="/_app/immutable/entry/start.RpA_pNyV.js">
<link rel="modulepreload" href="/_app/immutable/chunks/DlDBckNA.js">
<link rel="modulepreload" href="/_app/immutable/chunks/CSizKli0.js">
<link rel="modulepreload" href="/_app/immutable/chunks/yCQ_fqx4.js">
<link rel="modulepreload" href="/_app/immutable/chunks/DIeogL5L.js">
<link rel="modulepreload" href="/_app/immutable/chunks/94Xz9ZHe.js">
<link rel="modulepreload" href="/_app/immutable/chunks/BUApaBEI.js">
<link rel="modulepreload" href="/_app/immutable/entry/app.CepPnjFj.js">
<link rel="modulepreload" href="/_app/immutable/chunks/BOCziLuX.js">
<link rel="modulepreload" href="/_app/immutable/chunks/BP6NvnZ7.js">
<link rel="modulepreload" href="/_app/immutable/chunks/7tRQeUtl.js">
<link rel="modulepreload" href="/_app/immutable/chunks/CSfvng8n.js">
<link rel="modulepreload" href="/_app/immutable/chunks/DsnmJJEf.js">
<link rel="modulepreload" href="/_app/immutable/chunks/BzhrqIL4.js">
<link rel="modulepreload" href="/_app/immutable/chunks/DpNthyPY.js">
<link rel="modulepreload" href="/_app/env.js">
<style>
/* prevent FOUC */
html {
height: 100%;
width: 100%;
}
body,
html {
margin: 0;
padding: 0;
}
@keyframes delayedVisibility {
to {
visibility: visible;
}
}
@keyframes loadspin {
100% {
transform: rotate(360deg);
}
}
#stencil {
--stencil-width: 150px;
display: flex;
width: var(--stencil-width);
margin-left: auto;
margin-right: auto;
margin-top: calc(50vh - var(--stencil-width) / 2);
margin-bottom: 100vh;
place-items: center;
justify-content: center;
overflow: hidden;
visibility: hidden;
animation:
0s linear 0.3s forwards delayedVisibility,
loadspin 8s linear infinite;
}
.bg-immich-bg {
background-color: white;
}
.dark .dark\:bg-immich-dark-bg {
background-color: black;
}
</style>
<script>
/**
* Prevent FOUC on page load.
*/
const colorThemeKeyName = 'color-theme';
let theme = localStorage.getItem(colorThemeKeyName);
if (!theme) {
theme = { value: 'light', system: true };
} else if (theme === 'dark' || theme === 'light') {
theme = { value: theme, system: false };
localStorage.setItem(colorThemeKeyName, JSON.stringify(theme));
} else {
theme = JSON.parse(theme);
}
let themeValue = theme.value;
if (theme.system) {
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
themeValue = 'dark';
} else {
themeValue = 'light';
}
}
if (themeValue === 'light') {
document.documentElement.classList.remove('dark');
} else {
document.documentElement.classList.add('dark');
}
</script>
<link rel="stylesheet" href="/custom.css" />
</head>
<noscript
class="absolute z-1000 flex h-screen w-screen place-content-center place-items-center bg-immich-bg dark:bg-immich-dark-bg dark:text-immich-dark-fg"
>
To use Immich, you must enable JavaScript or use a JavaScript compatible browser.
</noscript>
<body class="bg-light text-dark">
<div id="stencil">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 792 792">
<style type="text/css">
.st0 {
fill: #fa2921;
}
.st1 {
fill: #ed79b5;
}
.st2 {
fill: #ffb400;
}
.st3 {
fill: #1e83f7;
}
.st4 {
fill: #18c249;
}
</style>
<g>
<path
class="st0"
d="M375.48,267.63c38.64,34.21,69.78,70.87,89.82,105.42c34.42-61.56,57.42-134.71,57.71-181.3
c0-0.33,0-0.63,0-0.91c0-68.94-68.77-95.77-128.01-95.77s-128.01,26.83-128.01,95.77c0,0.94,0,2.2,0,3.72
C300.01,209.24,339.15,235.47,375.48,267.63z"
/>
<path
class="st1"
d="M164.7,455.63c24.15-26.87,61.2-55.99,103.01-80.61c44.48-26.18,88.97-44.47,128.02-52.84
c-47.91-51.76-110.37-96.24-154.6-110.91c-0.31-0.1-0.6-0.19-0.86-0.28c-65.57-21.3-112.34,35.81-130.64,92.15
c-18.3,56.34-14.04,130.04,51.53,151.34C162.05,454.77,163.25,455.16,164.7,455.63z"
/>
<path
class="st2"
d="M681.07,302.19c-18.3-56.34-65.07-113.45-130.64-92.15c-0.9,0.29-2.1,0.68-3.54,1.15
c-3.75,35.93-16.6,81.27-35.96,125.76c-20.59,47.32-45.84,88.27-72.51,118c69.18,13.72,145.86,12.98,190.26-1.14
c0.31-0.1,0.6-0.2,0.86-0.28C695.11,432.22,699.37,358.52,681.07,302.19z"
/>
<path
class="st3"
d="M336.54,510.71c-11.15-50.39-14.8-98.36-10.7-138.08c-64.03,29.57-125.63,75.23-153.26,112.76
c-0.19,0.26-0.37,0.51-0.53,0.73c-40.52,55.78-0.66,117.91,47.27,152.72c47.92,34.82,119.33,53.54,159.86-2.24
c0.56-0.76,1.3-1.78,2.19-3.01C363.28,602.32,347.02,558.08,336.54,510.71z"
/>
<path
class="st4"
d="M617.57,482.52c-35.33,7.54-82.42,9.33-130.72,4.66c-51.37-4.96-98.11-16.32-134.63-32.5
c8.33,70.03,32.73,142.73,59.88,180.6c0.19,0.26,0.37,0.51,0.53,0.73c40.52,55.78,111.93,37.06,159.86,2.24
c47.92-34.82,87.79-96.95,47.27-152.72C619.2,484.77,618.46,483.75,617.57,482.52z"
/>
</g>
</svg>
</div>
<div>
<script>
{
__sveltekit_1gheimp = {
base: "",
env: null
};
const element = document.currentScript.parentElement;
import("/_app/env.js").then(({ env }) => {
__sveltekit_1gheimp.env = env;
Promise.all([
import("/_app/immutable/entry/start.RpA_pNyV.js"),
import("/_app/immutable/entry/app.CepPnjFj.js")
]).then(([kit, app]) => {
kit.start(app, element);
});
});
if ('serviceWorker' in navigator) {
addEventListener('load', function () {
navigator.serviceWorker.register('/service-worker.js');
});
}
}
</script>
</div>
</body>
</html>
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

+355
View File
File diff suppressed because one or more lines are too long
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB

BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

BIN
View File
Binary file not shown.
+7
View File
@@ -0,0 +1,7 @@
[status] Tout va bien
[couleur] #50fa7b
[date] 04/04/2026
[message] Status du serveur actuel :
[motd] HDD de 4To en place, partitions finies (je crois)
[motd] J'ai le RAID 10 le plus bordélique possible au monde!
[motd] J'adore le bordel que je fout et corige pas :O
Executable
+267
View File
@@ -0,0 +1,267 @@
/* =============================== Font ================================ */
@import url('https://fonts.googleapis.com/css2?family=Comfortaa:wght@400;700&display=swap');
/* =============================== Reset ================================ */
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* =============================== Body ================================ */
body {
font-family: 'Comfortaa', cursive;
color: #f8f8f2;
line-height: 1.6;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
padding: 2rem;
background: transparent;
overflow-x: hidden;
}
/* =============================== Background Media ================================ */
#bg-video,
#bg-image {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
object-fit: cover;
z-index: -2;
pointer-events: none;
display: none; /* JS decides which one is visible */
}
/* Dark overlay for readability */
body::before {
content: "";
position: fixed;
inset: 0;
background: rgba(40, 42, 54, 0.55);
z-index: -1;
pointer-events: none;
}
/* =============================== Main Container ================================ */
.container {
position: relative;
z-index: 1;
max-width: 900px;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
/* ============================== Descriptions de cases ============================== */
.description {
position: relative;
z-index: 1;
margin-top: 3rem;
text-align: center;
color: #6272a4;
font-size: 0.9rem;
}
/* =============================== Header ================================ */
h1 {
font-size: 2.5rem;
color: #bd93f9;
margin-bottom: 2rem;
text-align: center;
}
/* =============================== Cards ================================ */
.card {
background-color: rgba(68, 71, 90, 0.95);
color: #f8f8f2;
padding: 1.5rem 2rem;
border-radius: 12px;
font-weight: 600;
font-size: 1.2rem;
text-align: center;
width: 100%;
max-width: 400px;
margin: 0.7rem 0;
cursor: pointer;
transition:
transform 0.2s ease,
box-shadow 0.2s ease,
background-color 0.2s ease;
}
.card:hover {
background-color: #6272a4;
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
}
/* Make links fill card */
.card a {
display: block;
width: 100%;
height: 100%;
color: inherit;
text-decoration: none;
}
/* =============================== Images ================================ */
.img {
max-height: 200px;
max-width: 100%;
width: auto;
height: auto;
object-fit: contain;
}
/* =============================== Forms ================================ */
form {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
background-color: rgba(68, 71, 90, 0.95);
padding: 2rem;
border-radius: 12px;
max-width: 400px;
width: 100%;
margin: 1rem 0;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
}
label {
width: 100%;
margin-bottom: 0.5rem;
font-weight: 600;
}
input[type="text"],
select {
width: 100%;
padding: 0.6rem 1rem;
margin-bottom: 1rem;
border: none;
border-radius: 8px;
outline: none;
background-color: #6272a4;
color: #f8f8f2;
font-family: inherit;
font-size: 1rem;
transition:
background-color 0.2s ease,
transform 0.2s ease;
}
input[type="text"]:focus,
select:focus {
background-color: #50fa7b;
color: #282a36;
transform: scale(1.02);
}
/* =============================== Buttons ================================ */
button {
background-color: #bd93f9;
color: #282a36;
font-family: inherit;
font-weight: 700;
font-size: 1.1rem;
padding: 0.7rem 1.5rem;
border-radius: 10px;
border: none;
cursor: pointer;
transition:
background-color 0.2s ease,
transform 0.2s ease,
box-shadow 0.2s ease;
}
button:hover {
background-color: #ff79c6;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.4);
}
/* =============================== Footer ================================ */
footer {
position: relative;
z-index: 1;
margin-top: 3rem;
text-align: center;
color: #6272a4;
font-size: 0.9rem;
}
/* =============================== PHP Directory Index Helpers ================================ */
.card .file-type {
display: block;
margin-top: 0.3rem;
font-size: 0.9rem;
color: #bd93f9;
}
/* =============================== Responsive ================================ */
@media (max-width: 600px) {
body {
padding: 1rem;
}
h1 {
font-size: 2rem;
}
.card,
form {
font-size: 1.1rem;
width: 100%;
}
}
/* =============================== Cookies ================================ */
.cookie-btn {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 9999;
}
button.cookie-btn {
background-color: #bd93f9 !important;
color: #282a36 !important;
}
button.cookie-btn:hover {
background-color: #ff79c6 !important;
}
/* =============================== Reduced Motion ================================ */
@media (prefers-reduced-motion: reduce) {
#bg-video {
display: none !important;
}
#bg-image {
display: block !important;
}
}
/* ============================= Barre de chargement ============================= */
#loading-bar {
position: fixed;
top: 0;
left: 0;
height: 3px;
background: linear-gradient(90deg, #ff69b4, #ff1493);
width: 0%;
transition: width 0.3s ease;
z-index: 9999;
box-shadow: 0 0 10px rgba(255, 105, 180, 0.6);
}
+189
View File
@@ -0,0 +1,189 @@
/* =========================
LOADER STYLES
========================= */
#loader {
position: fixed;
inset: 0;
background: #0a0a0c;
color: #c7c7d1;
display: flex;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
z-index: 9999;
opacity: 1;
transition: opacity 0.8s ease;
}
.loader-left {
width: 48%;
padding: 80px 60px;
}
.ascii.big {
font-size: 18px;
line-height: 1.1;
letter-spacing: 2px;
color: #e2e2ff;
}
.subtitle {
margin-top: 30px;
font-size: 13px;
opacity: 0.6;
text-transform: uppercase;
letter-spacing: 4px;
}
.logs {
margin-top: 40px;
font-size: 12px;
opacity: 0.65;
}
.logs div {
margin-bottom: 8px;
}
.logs .ok {
opacity: 1;
color: #b6b6ff;
}
.loader-right {
flex: 1;
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-auto-rows: 140px;
gap: 30px;
padding: 80px;
}
.square {
border: 1px solid rgba(255,255,255,0.15);
position: relative;
overflow: hidden; /* Ensures pixels stay within bounds */
opacity: 1;
transform: none;
}
.square canvas {
display: block; /* Removes any spacing issues */
}
/* Remove slide animations - squares stay static */
.square.slide-in,
.square.slide-out {
/* No animation */
}
.time {
opacity: 0.5;
margin-right: 8px;
}
.step {
opacity: 0;
transform: translateY(6px);
transition: opacity 0.4s ease, transform 0.4s ease;
}
.step.visible {
opacity: 1;
transform: translateY(0);
}
/* =========================
SMOOTH CROSSFADE
========================= */
/* Main content starts invisible */
.container,
footer,
#bg-video,
#bg-image {
opacity: 0;
transition: opacity 0.8s ease;
}
/* When loading class is removed, content fades in */
body:not(.loading) .container,
body:not(.loading) footer,
body:not(.loading) #bg-video,
body:not(.loading) #bg-image {
opacity: 1;
visibility: visible;
}
/* Hide main site until loader starts fading */
body.loading .container,
body.loading footer,
body.loading #bg-video,
body.loading #bg-image {
visibility: hidden;
opacity: 0;
}
/* Block scroll bar while loading */
body.loading,
html:has(body.loading) {
overflow: hidden;
}
body.loading {
height: 100vh;
}
/* =========================
MOBILE / RESPONSIVE
========================= */
@media (max-width: 600px) {
.loader-left {
padding: 10px 0 0 0;
display: flex;
flex-direction: column;
align-items: flex-start;
}
.loader-left .ascii.big {
margin: 0;
padding: 0;
line-height: 1;
font-size: 10px;
letter-spacing: 1px;
white-space: pre;
overflow: hidden;
text-align: left;
display: block;
}
.loader-left .subtitle {
margin: 5px 0 0 0;
text-align: left;
}
.logs {
font-size: 10px;
margin: 5px 0 0 0;
}
.logs div {
white-space: normal;
word-break: break-word;
}
.loader-right {
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: 100px;
gap: 15px;
padding: 20px;
}
.square {
border-width: 0.5px;
}
}
/* Very small screens: hide pixel grid */
@media (max-width: 400px) {
.loader-right {
display: none;
}
}
+148
View File
@@ -0,0 +1,148 @@
/* =============================== Dashboard Specific Styles ================================ */
/* Dashboard Layout */
.dashboard-wrapper {
width: 100%;
display: grid;
grid-template-columns: 350px 1fr;
gap: 2rem;
margin-bottom: 2rem;
}
.sidebar {
display: flex;
flex-direction: column;
gap: 0.7rem;
}
.main-content {
display: flex;
flex-direction: column;
gap: 2rem;
}
/* Fix for image card */
.card-with-image {
padding: 1rem;
min-height: 150px;
display: flex;
align-items: center;
justify-content: center;
}
.card-with-image .img {
max-height: 200px;
max-width: 100%;
width: auto;
height: auto;
object-fit: contain;
display: block;
}
/* Stats Section */
.stats-section {
width: 100%;
}
.stats-section h2 {
color: #bd93f9;
font-size: 1.5rem;
margin-bottom: 1rem;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}
.stat-card {
background-color: rgba(68, 71, 90, 0.95);
padding: 1.5rem;
border-radius: 12px;
text-align: center;
transition: transform 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
cursor: pointer;
}
.stat-card:hover {
background-color: #6272a4;
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
}
.stat-label {
font-size: 0.95rem;
color: #6272a4;
margin-bottom: 0.5rem;
font-weight: 600;
}
.stat-value {
font-size: 2.2rem;
font-weight: 700;
color: #50fa7b;
}
.stat-subtitle {
font-size: 0.85rem;
color: #6272a4;
margin-top: 0.3rem;
}
/* Other Links Section */
.other-section {
width: 100%;
}
.other-section h2 {
color: #bd93f9;
font-size: 1.5rem;
margin-bottom: 1rem;
}
.other-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 1rem;
}
.small-card {
padding: 1rem 1.5rem;
font-size: 1rem;
}
/* Responsive */
@media (max-width: 1200px) {
.dashboard-wrapper {
grid-template-columns: 300px 1fr;
}
.stats-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 1024px) {
.dashboard-wrapper {
grid-template-columns: 1fr;
}
.stats-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 600px) {
.stats-grid {
grid-template-columns: 1fr;
}
.stat-value {
font-size: 1.8rem;
}
.other-grid {
grid-template-columns: 1fr;
}
}
Executable
+58
View File
@@ -0,0 +1,58 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Nix Storage Portal</title>
<link rel="stylesheet" href="style.css">
<style>
body {
font-family: "Fira Code", monospace;
background: #282a36;
color: #f8f8f2;
margin: 0;
padding: 0;
text-align: center;
}
header {
background: #44475a;
padding: 40px 20px;
font-size: 2em;
color: #ff79c6;
}
.container {
padding: 50px;
}
a.button {
display: inline-block;
padding: 15px 30px;
margin: 15px;
background: #6272a4;
color: #f8f8f2;
border-radius: 8px;
text-decoration: none;
font-size: 1.2em;
transition: 0.2s;
}
a.button:hover {
background: #bd93f9;
color: #fff;
}
footer {
margin-top: 50px;
font-size: 0.9em;
color: #6272a4;
}
</style>
</head>
<body>
<header>🍪 Nix Storage Portal</header>
<div class="container">
<p>Welcome to your secure storage interface.</p>
<a href="filemanager/index.php" class="button">📂 File Manager</a>
<a href="about.html" class="button"> About</a>
</div>
<footer>
<p>Powered by PHP · Dracula Theme · Nix Cookie</p>
</footer>
</body>
</html>