uploads
This commit is contained in:
475
server/src/Module/Auth/Controller/AuthController.php
Normal file
475
server/src/Module/Auth/Controller/AuthController.php
Normal file
@@ -0,0 +1,475 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Auth\Controller;
|
||||
|
||||
use App\Config\ConfigLoader;
|
||||
use App\Module\PlanetGenerator\Service\PlanetGenerator;
|
||||
use App\Shared\Http\JsonResponder;
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Slim\Psr7\Response;
|
||||
|
||||
final class AuthController
|
||||
{
|
||||
private const PASSWORD_MIN_LENGTH = 8;
|
||||
private const TITLE_MIN_LENGTH = 2;
|
||||
private const TITLE_MAX_LENGTH = 40;
|
||||
private const USERNAME_MIN_LENGTH = 3;
|
||||
private const USERNAME_MAX_LENGTH = 20;
|
||||
|
||||
public function __construct(
|
||||
private PDO $pdo,
|
||||
private ConfigLoader $configLoader,
|
||||
private PlanetGenerator $planetGenerator
|
||||
) {
|
||||
}
|
||||
|
||||
public function login(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
|
||||
{
|
||||
$body = $this->parseBody($request);
|
||||
$identifier = trim((string)($body['username_or_email'] ?? ''));
|
||||
$password = (string)($body['password'] ?? '');
|
||||
|
||||
if ($identifier === '' || $password === '') {
|
||||
return JsonResponder::withJson($response, [
|
||||
'error' => 'invalid_input',
|
||||
'message' => 'Username/E-Mail und Passwort sind Pflichtfelder.',
|
||||
], 400);
|
||||
}
|
||||
|
||||
$user = $this->findUserByIdentifier($identifier);
|
||||
if (!$user || !password_verify($password, (string)($user['password_hash'] ?? ''))) {
|
||||
return JsonResponder::withJson(new Response(), [
|
||||
'error' => 'invalid_credentials',
|
||||
'message' => 'Login fehlgeschlagen.',
|
||||
], 401);
|
||||
}
|
||||
|
||||
$this->loginUser((int)$user['id']);
|
||||
return JsonResponder::withJson($response, [
|
||||
'user' => $this->buildUserSummary($user),
|
||||
]);
|
||||
}
|
||||
|
||||
public function logout(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
|
||||
{
|
||||
$this->startSession();
|
||||
$_SESSION = [];
|
||||
|
||||
if (ini_get('session.use_cookies')) {
|
||||
$params = session_get_cookie_params();
|
||||
setcookie(session_name(), '', time() - 42000, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
|
||||
}
|
||||
session_destroy();
|
||||
|
||||
return JsonResponder::withJson($response, ['ok' => true]);
|
||||
}
|
||||
|
||||
public function me(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
|
||||
{
|
||||
$user = $request->getAttribute('user');
|
||||
if (!is_array($user) || !isset($user['id'])) {
|
||||
return JsonResponder::withJson(new Response(), [
|
||||
'error' => 'auth_required',
|
||||
'message' => 'Authentifizierung erforderlich.',
|
||||
], 401);
|
||||
}
|
||||
|
||||
return JsonResponder::withJson($response, [
|
||||
'user' => $this->buildUserSummary($user),
|
||||
]);
|
||||
}
|
||||
|
||||
public function registerStep1(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
|
||||
{
|
||||
$body = $this->parseBody($request);
|
||||
$raceKey = trim((string)($body['race_key'] ?? ''));
|
||||
$races = $this->configLoader->races();
|
||||
$raceConfig = $races['races'][$raceKey] ?? null;
|
||||
|
||||
if ($raceKey === '' || !is_array($raceConfig)) {
|
||||
return JsonResponder::withJson($response, [
|
||||
'error' => 'invalid_race',
|
||||
'message' => 'Ungültige Rasse.',
|
||||
], 422);
|
||||
}
|
||||
|
||||
$draft = $this->getDraft();
|
||||
$draft['race_key'] = $raceKey;
|
||||
$this->saveDraft($draft);
|
||||
|
||||
return JsonResponder::withJson($response, [
|
||||
'draft' => $this->sanitizeDraft($draft),
|
||||
]);
|
||||
}
|
||||
|
||||
public function registerStep2(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
|
||||
{
|
||||
$draft = $this->getDraft();
|
||||
if (empty($draft['race_key'])) {
|
||||
return JsonResponder::withJson($response, [
|
||||
'error' => 'draft_missing',
|
||||
'message' => 'Bitte zuerst eine Rasse wählen.',
|
||||
], 409);
|
||||
}
|
||||
|
||||
$body = $this->parseBody($request);
|
||||
$avatarKey = trim((string)($body['avatar_key'] ?? ''));
|
||||
$title = $this->sanitizeTitle((string)($body['title'] ?? ''));
|
||||
|
||||
if ($avatarKey === '' || !$this->avatarExists($avatarKey)) {
|
||||
return JsonResponder::withJson($response, [
|
||||
'error' => 'invalid_avatar',
|
||||
'message' => 'Ungültiger Avatar.',
|
||||
], 422);
|
||||
}
|
||||
|
||||
if (!$this->isValidTitle($title)) {
|
||||
return JsonResponder::withJson($response, [
|
||||
'error' => 'invalid_title',
|
||||
'message' => 'Titel muss zwischen 2 und 40 Zeichen lang sein.',
|
||||
], 422);
|
||||
}
|
||||
|
||||
$draft['avatar_key'] = $avatarKey;
|
||||
$draft['title'] = $title;
|
||||
$this->saveDraft($draft);
|
||||
|
||||
return JsonResponder::withJson($response, [
|
||||
'draft' => $this->sanitizeDraft($draft),
|
||||
]);
|
||||
}
|
||||
|
||||
public function registerStep3(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
|
||||
{
|
||||
$draft = $this->getDraft();
|
||||
if (empty($draft['race_key']) || empty($draft['avatar_key']) || empty($draft['title'])) {
|
||||
return JsonResponder::withJson($response, [
|
||||
'error' => 'draft_incomplete',
|
||||
'message' => 'Bitte Registrierungsschritte 1 und 2 abschließen.',
|
||||
], 409);
|
||||
}
|
||||
|
||||
$body = $this->parseBody($request);
|
||||
$username = trim((string)($body['username'] ?? ''));
|
||||
$email = trim((string)($body['email'] ?? ''));
|
||||
$password = (string)($body['password'] ?? '');
|
||||
|
||||
if (!$this->isValidUsername($username)) {
|
||||
return JsonResponder::withJson($response, [
|
||||
'error' => 'invalid_username',
|
||||
'message' => 'Username muss zwischen 3 und 20 Zeichen lang sein.',
|
||||
], 422);
|
||||
}
|
||||
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
return JsonResponder::withJson($response, [
|
||||
'error' => 'invalid_email',
|
||||
'message' => 'Ungültige E-Mail-Adresse.',
|
||||
], 422);
|
||||
}
|
||||
|
||||
if ($this->length($password) < self::PASSWORD_MIN_LENGTH) {
|
||||
return JsonResponder::withJson($response, [
|
||||
'error' => 'invalid_password',
|
||||
'message' => 'Passwort zu kurz (mindestens 8 Zeichen).',
|
||||
], 422);
|
||||
}
|
||||
|
||||
$existing = $this->findUserByUsernameOrEmail($username, $email);
|
||||
if ($existing['username'] ?? false) {
|
||||
return JsonResponder::withJson($response, [
|
||||
'error' => 'username_taken',
|
||||
'message' => 'Username bereits vergeben.',
|
||||
], 409);
|
||||
}
|
||||
if ($existing['email'] ?? false) {
|
||||
return JsonResponder::withJson($response, [
|
||||
'error' => 'email_taken',
|
||||
'message' => 'E-Mail bereits registriert.',
|
||||
], 409);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->pdo->beginTransaction();
|
||||
|
||||
$stmt = $this->pdo->prepare(
|
||||
'INSERT INTO users (username, email, password_hash, race_key, title, avatar_key)
|
||||
VALUES (:username, :email, :password_hash, :race_key, :title, :avatar_key)
|
||||
RETURNING *'
|
||||
);
|
||||
$stmt->execute([
|
||||
'username' => $username,
|
||||
'email' => $this->lower($email),
|
||||
'password_hash' => password_hash($password, PASSWORD_DEFAULT),
|
||||
'race_key' => $draft['race_key'],
|
||||
'title' => $draft['title'],
|
||||
'avatar_key' => $draft['avatar_key'],
|
||||
]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
$userId = (int)($user['id'] ?? 0);
|
||||
if ($userId <= 0) {
|
||||
throw new \\RuntimeException('User-ID fehlt.');
|
||||
}
|
||||
|
||||
$this->assignRole($userId, 'player');
|
||||
$this->createStarterPlanet($userId);
|
||||
|
||||
$this->pdo->commit();
|
||||
} catch (PDOException $e) {
|
||||
$this->pdo->rollBack();
|
||||
if ($e->getCode() === '23505') {
|
||||
return JsonResponder::withJson($response, [
|
||||
'error' => 'duplicate',
|
||||
'message' => 'Username oder E-Mail bereits registriert.',
|
||||
], 409);
|
||||
}
|
||||
return JsonResponder::withJson($response, [
|
||||
'error' => 'registration_failed',
|
||||
'message' => 'Registrierung fehlgeschlagen.',
|
||||
], 500);
|
||||
} catch (\\Throwable $e) {
|
||||
$this->pdo->rollBack();
|
||||
return JsonResponder::withJson($response, [
|
||||
'error' => 'registration_failed',
|
||||
'message' => 'Registrierung fehlgeschlagen.',
|
||||
], 500);
|
||||
}
|
||||
|
||||
$this->clearDraft();
|
||||
$this->loginUser((int)$user['id']);
|
||||
|
||||
return JsonResponder::withJson($response, [
|
||||
'user' => $this->buildUserSummary($user),
|
||||
], 201);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
private function parseBody(ServerRequestInterface $request): array
|
||||
{
|
||||
$body = $request->getParsedBody();
|
||||
if (!is_array($body)) {
|
||||
return [];
|
||||
}
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $draft
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
private function sanitizeDraft(array $draft): array
|
||||
{
|
||||
return [
|
||||
'race_key' => $draft['race_key'] ?? null,
|
||||
'avatar_key' => $draft['avatar_key'] ?? null,
|
||||
'title' => $draft['title'] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
private function findUserByIdentifier(string $identifier): ?array
|
||||
{
|
||||
$stmt = $this->pdo->prepare('SELECT * FROM users WHERE username = :id OR LOWER(email) = :email');
|
||||
$stmt->execute([
|
||||
'id' => $identifier,
|
||||
'email' => $this->lower($identifier),
|
||||
]);
|
||||
$user = $stmt->fetch();
|
||||
return $user ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{username:bool,email:bool}
|
||||
*/
|
||||
private function findUserByUsernameOrEmail(string $username, string $email): array
|
||||
{
|
||||
$stmt = $this->pdo->prepare('SELECT username, email FROM users WHERE username = :username OR email = :email');
|
||||
$stmt->execute([
|
||||
'username' => $username,
|
||||
'email' => $this->lower($email),
|
||||
]);
|
||||
$rows = $stmt->fetchAll();
|
||||
$result = ['username' => false, 'email' => false];
|
||||
foreach ($rows as $row) {
|
||||
if (isset($row['username']) && $row['username'] === $username) {
|
||||
$result['username'] = true;
|
||||
}
|
||||
if (isset($row['email']) && $this->lower((string)$row['email']) === $this->lower($email)) {
|
||||
$result['email'] = true;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $user
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
private function buildUserSummary(array $user): array
|
||||
{
|
||||
return [
|
||||
'id' => (int)($user['id'] ?? 0),
|
||||
'username' => (string)($user['username'] ?? ''),
|
||||
'email' => (string)($user['email'] ?? ''),
|
||||
'race_key' => (string)($user['race_key'] ?? ''),
|
||||
'title' => (string)($user['title'] ?? ''),
|
||||
'avatar_key' => (string)($user['avatar_key'] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
private function avatarExists(string $avatarKey): bool
|
||||
{
|
||||
$config = $this->configLoader->avatars();
|
||||
$avatars = $config['avatars'] ?? [];
|
||||
foreach ($avatars as $avatar) {
|
||||
if (is_array($avatar) && ($avatar['key'] ?? null) === $avatarKey) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function isValidTitle(string $title): bool
|
||||
{
|
||||
$len = $this->length($title);
|
||||
return $len >= self::TITLE_MIN_LENGTH && $len <= self::TITLE_MAX_LENGTH;
|
||||
}
|
||||
|
||||
private function sanitizeTitle(string $title): string
|
||||
{
|
||||
$clean = trim(strip_tags($title));
|
||||
$clean = preg_replace('/\s+/', ' ', $clean) ?? '';
|
||||
return $clean;
|
||||
}
|
||||
|
||||
private function isValidUsername(string $username): bool
|
||||
{
|
||||
$len = $this->length($username);
|
||||
if ($len < self::USERNAME_MIN_LENGTH || $len > self::USERNAME_MAX_LENGTH) {
|
||||
return false;
|
||||
}
|
||||
return (bool)preg_match('/^[A-Za-z0-9_\-]+$/', $username);
|
||||
}
|
||||
|
||||
private function length(string $value): int
|
||||
{
|
||||
if (function_exists('mb_strlen')) {
|
||||
return mb_strlen($value);
|
||||
}
|
||||
return strlen($value);
|
||||
}
|
||||
|
||||
private function lower(string $value): string
|
||||
{
|
||||
if (function_exists('mb_strtolower')) {
|
||||
return mb_strtolower($value);
|
||||
}
|
||||
return strtolower($value);
|
||||
}
|
||||
|
||||
private function startSession(): void
|
||||
{
|
||||
if (session_status() === PHP_SESSION_ACTIVE) {
|
||||
return;
|
||||
}
|
||||
$secure = !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
|
||||
session_start([
|
||||
'cookie_httponly' => true,
|
||||
'cookie_samesite' => 'Lax',
|
||||
'cookie_secure' => $secure,
|
||||
'cookie_path' => '/',
|
||||
]);
|
||||
}
|
||||
|
||||
private function loginUser(int $userId): void
|
||||
{
|
||||
$this->startSession();
|
||||
session_regenerate_id(true);
|
||||
$_SESSION['user_id'] = $userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
private function getDraft(): array
|
||||
{
|
||||
$this->startSession();
|
||||
$draft = $_SESSION['reg_draft'] ?? [];
|
||||
return is_array($draft) ? $draft : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $draft
|
||||
*/
|
||||
private function saveDraft(array $draft): void
|
||||
{
|
||||
$this->startSession();
|
||||
$_SESSION['reg_draft'] = $draft;
|
||||
}
|
||||
|
||||
private function clearDraft(): void
|
||||
{
|
||||
$this->startSession();
|
||||
unset($_SESSION['reg_draft']);
|
||||
}
|
||||
|
||||
private function assignRole(int $userId, string $roleKey): void
|
||||
{
|
||||
$stmt = $this->pdo->prepare(
|
||||
'INSERT INTO user_roles (user_id, role_id)
|
||||
SELECT :user_id, r.id FROM roles r WHERE r.key = :role_key
|
||||
ON CONFLICT DO NOTHING'
|
||||
);
|
||||
$stmt->execute([
|
||||
'user_id' => $userId,
|
||||
'role_key' => $roleKey,
|
||||
]);
|
||||
}
|
||||
|
||||
private function createStarterPlanet(int $userId): void
|
||||
{
|
||||
$config = $this->configLoader->planetClasses();
|
||||
$resources = [];
|
||||
foreach (($config['resources'] ?? []) as $res) {
|
||||
$resources[$res] = 500.0;
|
||||
}
|
||||
|
||||
$seed = random_int(10, 999999);
|
||||
$generated = $this->planetGenerator->generate('temperate', 'normal', $seed);
|
||||
|
||||
$stmt = $this->pdo->prepare(
|
||||
'INSERT INTO planets (user_id, name, class_key, planet_seed, temperature_c, modifiers, resources, last_resource_update_at)
|
||||
VALUES (:user_id, :name, :class_key, :planet_seed, :temperature_c, :modifiers, :resources, :last_update)
|
||||
RETURNING id'
|
||||
);
|
||||
$stmt->execute([
|
||||
'user_id' => $userId,
|
||||
'name' => 'Heimatwelt',
|
||||
'class_key' => $generated['class_key'],
|
||||
'planet_seed' => $seed,
|
||||
'temperature_c' => (int)$generated['temperature_c'],
|
||||
'modifiers' => json_encode($generated['modifiers'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
||||
'resources' => json_encode($resources, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
||||
'last_update' => (new \DateTimeImmutable('now'))->format('Y-m-d H:i:s'),
|
||||
]);
|
||||
$planetId = (int)$stmt->fetchColumn();
|
||||
|
||||
$stmt = $this->pdo->prepare(
|
||||
'INSERT INTO planet_buildings (planet_id, building_key, count, level)
|
||||
VALUES (:planet_id, :building_key, :count, 0)
|
||||
ON CONFLICT (planet_id, building_key) DO NOTHING'
|
||||
);
|
||||
$stmt->execute([
|
||||
'planet_id' => $planetId,
|
||||
'building_key' => 'build_center',
|
||||
'count' => 1,
|
||||
]);
|
||||
}
|
||||
}
|
||||
101
server/src/Module/Auth/Controller/MetaController.php
Normal file
101
server/src/Module/Auth/Controller/MetaController.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Auth\Controller;
|
||||
|
||||
use App\Config\ConfigLoader;
|
||||
use App\Shared\Http\JsonResponder;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
final class MetaController
|
||||
{
|
||||
public function __construct(private ConfigLoader $configLoader)
|
||||
{
|
||||
}
|
||||
|
||||
public function races(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
|
||||
{
|
||||
$config = $this->configLoader->races();
|
||||
$races = $config['races'] ?? [];
|
||||
|
||||
$items = [];
|
||||
foreach ($races as $key => $race) {
|
||||
if (!is_array($race)) {
|
||||
continue;
|
||||
}
|
||||
$items[] = [
|
||||
'key' => (string)$key,
|
||||
'name' => (string)($race['name'] ?? $key),
|
||||
'description' => (string)($race['description'] ?? ''),
|
||||
'modifier_summary' => $this->buildModifierSummary($race['modifiers'] ?? []),
|
||||
'modifiers' => $race['modifiers'] ?? [],
|
||||
];
|
||||
}
|
||||
|
||||
return JsonResponder::withJson($response, ['races' => $items]);
|
||||
}
|
||||
|
||||
public function avatars(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
|
||||
{
|
||||
$config = $this->configLoader->avatars();
|
||||
$avatars = $config['avatars'] ?? [];
|
||||
return JsonResponder::withJson($response, ['avatars' => $avatars]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $modifiers
|
||||
* @return string[]
|
||||
*/
|
||||
private function buildModifierSummary(array $modifiers): array
|
||||
{
|
||||
$summary = [];
|
||||
$labels = [
|
||||
'produce' => 'Produktion',
|
||||
'consume' => 'Verbrauch',
|
||||
];
|
||||
$resourceNames = [
|
||||
'metal' => 'Metall',
|
||||
'alloy' => 'Legierung',
|
||||
'crystals' => 'Kristall',
|
||||
'energy' => 'Energie',
|
||||
'credits' => 'Credits',
|
||||
'population' => 'Bevölkerung',
|
||||
'water' => 'Wasser',
|
||||
'deuterium' => 'Deuterium',
|
||||
'food' => 'Nahrung',
|
||||
];
|
||||
|
||||
foreach ($labels as $section => $label) {
|
||||
$entries = $modifiers[$section] ?? null;
|
||||
if (!is_array($entries)) {
|
||||
continue;
|
||||
}
|
||||
$parts = [];
|
||||
foreach ($entries as $res => $val) {
|
||||
if (!is_numeric($val) || (float)$val == 0.0) {
|
||||
continue;
|
||||
}
|
||||
$percent = (float)$val * 100;
|
||||
$sign = $percent >= 0 ? '+' : '';
|
||||
$name = $resourceNames[$res] ?? (string)$res;
|
||||
$parts[] = sprintf('%s %s%s%%', $name, $sign, $this->formatPercent($percent));
|
||||
}
|
||||
if ($parts) {
|
||||
$summary[] = $label . ': ' . implode(', ', $parts);
|
||||
}
|
||||
}
|
||||
|
||||
return $summary;
|
||||
}
|
||||
|
||||
private function formatPercent(float $value): string
|
||||
{
|
||||
$formatted = number_format($value, 1, ',', '');
|
||||
if (str_ends_with($formatted, ',0')) {
|
||||
return substr($formatted, 0, -2);
|
||||
}
|
||||
return $formatted;
|
||||
}
|
||||
}
|
||||
30
server/src/Module/Auth/Routes.php
Normal file
30
server/src/Module/Auth/Routes.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Module\Auth;
|
||||
|
||||
use App\Module\Auth\Controller\AuthController;
|
||||
use App\Module\Auth\Controller\MetaController;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Slim\Interfaces\RouteCollectorProxyInterface;
|
||||
|
||||
final class Routes
|
||||
{
|
||||
public static function register(RouteCollectorProxyInterface $group, ContainerInterface $container): void
|
||||
{
|
||||
$auth = $container->get(AuthController::class);
|
||||
$meta = $container->get(MetaController::class);
|
||||
|
||||
$group->post('/auth/login', [$auth, 'login']);
|
||||
$group->post('/auth/logout', [$auth, 'logout']);
|
||||
$group->get('/me', [$auth, 'me']);
|
||||
|
||||
$group->post('/auth/register/step1', [$auth, 'registerStep1']);
|
||||
$group->post('/auth/register/step2', [$auth, 'registerStep2']);
|
||||
$group->post('/auth/register/step3', [$auth, 'registerStep3']);
|
||||
|
||||
$group->get('/meta/races', [$meta, 'races']);
|
||||
$group->get('/meta/avatars', [$meta, 'avatars']);
|
||||
}
|
||||
}
|
||||
@@ -18,11 +18,17 @@ final class AuthService
|
||||
*/
|
||||
public function resolveUser(ServerRequestInterface $request): ?array
|
||||
{
|
||||
$this->ensureSession();
|
||||
$id = $this->extractUserId($request);
|
||||
if ($id !== null) {
|
||||
return $this->findUserById($id);
|
||||
}
|
||||
|
||||
$sessionUserId = $this->getSessionUserId();
|
||||
if ($sessionUserId !== null) {
|
||||
return $this->findUserById($sessionUserId);
|
||||
}
|
||||
|
||||
if ((int)(getenv('DEV_MODE') ?: 0) === 1) {
|
||||
$devUserId = getenv('DEV_USER_ID');
|
||||
if ($devUserId !== false && is_numeric($devUserId)) {
|
||||
@@ -54,6 +60,32 @@ final class AuthService
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getSessionUserId(): ?int
|
||||
{
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
return null;
|
||||
}
|
||||
$val = $_SESSION['user_id'];
|
||||
if (is_numeric($val)) {
|
||||
return (int)$val;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private function ensureSession(): void
|
||||
{
|
||||
if (session_status() === PHP_SESSION_ACTIVE) {
|
||||
return;
|
||||
}
|
||||
$secure = !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
|
||||
session_start([
|
||||
'cookie_httponly' => true,
|
||||
'cookie_samesite' => 'Lax',
|
||||
'cookie_secure' => $secure,
|
||||
'cookie_path' => '/',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,mixed>|null
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user