Files
minecraft-skin-server/index.php
2025-10-17 18:43:12 +03:00

142 lines
5.1 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
$path = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH);
/* === API: upload skin === */
if ($method === 'POST' && $path === '/upload') {
$name = $_POST['username'] ?? '';
if (!preg_match('/^[A-Za-z0-9_]{2,16}$/', $name)) return err('Неправильний нік (216, латиниця/цифри/_).');
$tmp = require_png_upload('file');
[$w, $h] = must_image_size($tmp);
if (!($w === 64 && ($h === 64 || $h === 128))) return err('Скін має бути 64×64 або 64×128.');
$img = @imagecreatefrompng($tmp) ?: err('Пошкоджений PNG.');
if (!is_dir(__DIR__ . '/skins')) @mkdir(__DIR__ . '/skins', 0755, true);
$dest = __DIR__ . "/skins/{$name}.png";
@imagepng($img, $dest) ?: err('Не вдалося зберегти (права на ./skins).');
imagedestroy($img);
json_ok(['skin_url' => base() . "/skins/{$name}.png", 'username' => $name]);
}
/* === API: upload cape === */
if ($method === 'POST' && $path === '/upload-cape') {
$name = $_POST['username'] ?? '';
if (!preg_match('/^[A-Za-z0-9_]{2,16}$/', $name)) return err('Неправильний нік (216, латиниця/цифри/_).');
$tmp = require_png_upload('file');
[$w, $h] = must_image_size($tmp);
// Плащ: співвідношення 2:1, мінімум 64×32, кратні двійці (типові 64×32, 128×64, 256×128, …)
if (!($w === 2 * $h && $w >= 64 && $h >= 32)) {
return err("Плащ має бути зі співвідношенням 2:1 (64×32, 128×64, …). Отримано {$w}×{$h}.");
}
$img = @imagecreatefrompng($tmp) ?: err('Пошкоджений PNG.');
if (!is_dir(__DIR__ . '/capes')) @mkdir(__DIR__ . '/capes', 0755, true);
$dest = __DIR__ . "/capes/{$name}.png";
@imagepng($img, $dest) ?: err('Не вдалося зберегти (права на ./capes).');
imagedestroy($img);
json_ok(['cape_url' => base() . "/capes/{$name}.png", 'username' => $name]);
}
/* === UI === */
if ($method === 'GET' && $path === '/') {
header('Content-Type: text/html; charset=utf-8');
echo <<<HTML
<!doctype html>
<html lang="uk">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MSS - MrAkells Skin Server</title>
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="shortcut icon" href="/favicon.ico">
<link rel="stylesheet" href="assets/style.css">
</head>
<body>
<div class="shell">
<div class="card">
<form id="form" class="form" autocomplete="off">
<div class="segment" role="tablist" aria-label="Тип">
<button type="button" class="segbtn active" id="seg-skin" aria-selected="true">Скін</button>
<button type="button" class="segbtn" id="seg-cape" aria-selected="false">Плащ</button>
</div>
<div>
<div class="lbl">Нікнейм</div>
<input class="input" name="username" id="username" placeholder="Наприклад, Notch" minlength="2" maxlength="16" pattern="[A-Za-z0-9_]{2,16}" required>
</div>
<div>
<div class="lbl">Файл PNG</div>
<input class="input" type="file" id="file" name="file" accept="image/png" required>
</div>
<button class="btn" id="submit" type="submit">Завантажити</button>
</form>
</div>
</div>
<div id="toasts"></div>
<a href="https://t.me/mrakells" class="credit" target="_blank" rel="noopener">by&nbsp;MrAkells</a>
<script src="assets/app.js"></script>
</body>
</html>
HTML;
exit;
}
/* === 404 === */
http_response_code(404);
header('Content-Type: text/plain; charset=utf-8');
echo "Not Found";
exit;
/* === helpers === */
function err(string $m): void
{
header('Content-Type: application/json; charset=utf-8', true, 400);
echo json_encode(['status' => 'error', 'error' => $m], JSON_UNESCAPED_UNICODE);
exit;
}
function json_ok(array $extra): void
{
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['status' => 'ok'] + $extra, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
exit;
}
function require_png_upload(string $key): string
{
if (!isset($_FILES[$key]) || $_FILES[$key]['error'] !== UPLOAD_ERR_OK) err('Файл не завантажено.');
$tmp = $_FILES[$key]['tmp_name'];
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $tmp);
finfo_close($finfo);
if ($mime !== 'image/png') err('Лише PNG.');
return $tmp;
}
function must_image_size(string $tmp): array
{
$info = @getimagesize($tmp);
if (!$info) err('Не PNG.');
return [$info[0], $info[1]];
}
function base(): string
{
$https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (($_SERVER['SERVER_PORT'] ?? '') === '443');
$scheme = $https ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
return $scheme . '://' . $host;
}