v1.0.0.0
StocketBase
- <?php
-
- declare(strict_types=1);
-
- require_once __DIR__ . '/includes/config.php';
- require_once __DIR__ . '/includes/database.php';
- require_once __DIR__ . '/includes/user-auth.php';
- require_once __DIR__ . '/includes/csrf.php';
- require_once __DIR__ . '/includes/language.php';
- require_once __DIR__ . '/includes/site-front.php';
- require_once __DIR__ . '/includes/asset.php';
- require_once __DIR__ . '/includes/antibot.php';
- require_once __DIR__ . '/includes/login-throttle.php';
-
- UserAuth::boot();
- AntiBot::boot('user');
- if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
- AntiBot::refresh('user');
- }
-
- $settings = SiteFront::settings();
- $loginEnabled = ($settings['login_enabled'] ?? '1') !== '0';
-
- function sanitizeRedirectTarget(string $target): string
- {
- if ($target !== '' && preg_match('#^[a-zA-Z0-9_\-]+\.php(\?[a-zA-Z0-9_\-\.=&%]*)?$#', $target) === 1) {
- return $target;
- }
- return '';
- }
-
- $redirectTarget = sanitizeRedirectTarget((string) ($_GET['redirect'] ?? ''));
-
- if (isset($_GET['logout'])) {
- UserAuth::logout();
- header('Location: user-login.php');
- exit;
- }
-
- if (UserAuth::check()) {
- header('Location: ' . ($redirectTarget !== '' ? $redirectTarget : 'index.php'));
- exit;
- }
-
- if (!$loginEnabled) {
- $siteName = Config::get('APP_NAME', 'StocketBase');
- ?>
- <!DOCTYPE html>
- <html lang="en">
-
- <head>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <title><?= htmlspecialchars(Language::get('auth_login_button', 'Log in')) ?> - <?= htmlspecialchars($siteName) ?></title>
- <?= Asset::favicon() ?>
- <?= Asset::css('assets/site.css') ?>
- <style>
- <?= SiteFront::renderFontFaces() ?><?= SiteFront::fontVariablesCss() ?>
- </style>
- <?php if (!empty($settings['custom_css'])): ?>
- <style>
- <?= $settings['custom_css'] ?>
- </style>
- <?php endif; ?>
- </head>
-
- <body class="auth-page" data-theme="light">
- <script>
- (function() {
- try {
- var t = localStorage.getItem('stocketbase_theme');
- if (t === 'dark' || t === 'light') {
- document.body.setAttribute('data-theme', t);
- } else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
- document.body.setAttribute('data-theme', 'dark');
- }
- } catch (e) {}
- })();
- </script>
- <div class="auth-card auth-card-disabled">
- <h1><?= htmlspecialchars($siteName) ?></h1>
- <p class="auth-subtitle"><?= htmlspecialchars(Language::get('auth_login_disabled', 'Customer login is temporarily disabled. Please check back later.')) ?></p>
- </div>
- </body>
-
- </html>
- <?php
- exit;
- }
-
- $error = null;
- $mode = ($_GET['mode'] ?? 'login') === 'register' ? 'register' : 'login';
-
- if ($_SERVER['REQUEST_METHOD'] === 'POST') {
- if (!Csrf::verify($_POST['csrf_token'] ?? null)) {
- $error = Language::get('form_security_failed', 'Security check failed, please try again.');
- } elseif (!AntiBot::verify('user', $_POST['antibot_answer'] ?? null, $_POST['antibot_started'] ?? null, $_POST['website'] ?? null)) {
- $error = Language::get('auth_security_failed', 'Security verification failed, please try again.');
- } else {
- $formAction = $_POST['form_action'] ?? '';
-
- if ($formAction === 'login') {
- $email = trim((string) ($_POST['email'] ?? ''));
- $password = (string) ($_POST['password'] ?? '');
-
- if ($email === '' || $password === '') {
- $error = Language::get('auth_missing_credentials', 'Please enter your email and password.');
- $mode = 'login';
- } elseif (($wait = LoginThrottle::secondsUntilAllowed('reader', $email)) > 0) {
- $error = LoginThrottle::message($wait);
- $mode = 'login';
- } elseif (UserAuth::attemptLogin($email, $password)) {
- LoginThrottle::clear('reader', $email);
- header('Location: ' . ($redirectTarget !== '' ? $redirectTarget : 'index.php'));
- exit;
- } else {
- LoginThrottle::recordFailure('reader', $email);
- $error = Language::get('auth_invalid_credentials', 'Invalid email or password.');
- $mode = 'login';
- }
- }
-
- if ($formAction === 'register') {
- $email = trim((string) ($_POST['email'] ?? ''));
- $password = (string) ($_POST['password'] ?? '');
- $passwordConfirm = (string) ($_POST['password_confirm'] ?? '');
- $displayName = trim((string) ($_POST['display_name'] ?? ''));
-
- if ($password !== $passwordConfirm) {
- $error = Language::get('auth_password_mismatch', 'Passwords do not match.');
- $mode = 'register';
- } else {
- [$success, $registerError] = UserAuth::register($email, $password, $displayName);
- if ($success) {
- header('Location: ' . ($redirectTarget !== '' ? $redirectTarget : 'index.php'));
- exit;
- }
- $error = $registerError;
- $mode = 'register';
- }
- }
- }
- }
-
- $siteName = Config::get('APP_NAME', 'StocketBase');
-
- ?>
- <!DOCTYPE html>
- <html lang="en">
-
- <head>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <title><?= $mode === 'register' ? htmlspecialchars(Language::get('auth_register_button', 'Create account')) : htmlspecialchars(Language::get('auth_login_button', 'Log in')) ?> - <?= htmlspecialchars($siteName) ?></title>
- <?= Asset::favicon() ?>
- <?= Asset::css('assets/site.css') ?>
- <style>
- <?= SiteFront::renderFontFaces() ?><?= SiteFront::fontVariablesCss() ?>
- </style>
- <?php if (!empty($settings['custom_css'])): ?>
- <style>
- <?= $settings['custom_css'] ?>
- </style>
- <?php endif; ?>
- </head>
-
- <body class="auth-page" data-theme="light">
- <script>
- (function() {
- try {
- var t = localStorage.getItem('stocketbase_theme');
- if (t === 'dark' || t === 'light') {
- document.body.setAttribute('data-theme', t);
- } else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
- document.body.setAttribute('data-theme', 'dark');
- }
- } catch (e) {}
- })();
- </script>
- <div class="auth-card">
- <h1><?= htmlspecialchars($siteName) ?></h1>
- <p class="auth-subtitle"><?= htmlspecialchars(Language::get('auth_account_subtitle', 'Customer account')) ?></p>
-
- <div class="auth-tabs">
- <a href="user-login.php?mode=login<?= $redirectTarget !== '' ? '&redirect=' . urlencode($redirectTarget) : '' ?>" class="<?= $mode === 'login' ? 'active' : '' ?>"><?= htmlspecialchars(Language::get('auth_login_button', 'Log in')) ?></a>
- <a href="user-login.php?mode=register<?= $redirectTarget !== '' ? '&redirect=' . urlencode($redirectTarget) : '' ?>" class="<?= $mode === 'register' ? 'active' : '' ?>"><?= htmlspecialchars(Language::get('auth_register_button', 'Create account')) ?></a>
- </div>
-
- <?php if ($error !== null): ?>
- <div class="auth-error"><?= htmlspecialchars($error) ?></div>
- <?php endif; ?>
-
- <?php if ($mode === 'login'): ?>
- <form method="post">
- <?= Csrf::field() ?>
- <?= AntiBot::field('user') ?>
- <input type="hidden" name="form_action" value="login">
- <label><?= htmlspecialchars(Language::get('auth_email', 'Email')) ?></label>
- <input type="email" name="email" required autofocus>
- <label><?= htmlspecialchars(Language::get('auth_password', 'Password')) ?></label>
- <input type="password" name="password" required>
- <div class="antibot-box">
- <div class="antibot-image"><?= AntiBot::image('user') ?></div>
- <label for="antibot-answer"><?= htmlspecialchars(Language::get('auth_security_code', 'Security code')) ?></label>
- <input id="antibot-answer" type="text" name="antibot_answer" required inputmode="text" autocomplete="off" maxlength="6" spellcheck="false">
- </div>
- <button type="submit"><?= htmlspecialchars(Language::get('auth_login_button', 'Log in')) ?></button>
- </form>
- <?php else: ?>
- <form method="post">
- <?= Csrf::field() ?>
- <?= AntiBot::field('user') ?>
- <input type="hidden" name="form_action" value="register">
- <label><?= htmlspecialchars(Language::get('auth_display_name', 'Display name')) ?></label>
- <input type="text" name="display_name">
- <label><?= htmlspecialchars(Language::get('auth_email', 'Email')) ?></label>
- <input type="email" name="email" required>
- <label><?= htmlspecialchars(Language::get('auth_password', 'Password')) ?></label>
- <input type="password" name="password" required minlength="8">
- <label><?= htmlspecialchars(Language::get('auth_confirm_password', 'Confirm password')) ?></label>
- <input type="password" name="password_confirm" required minlength="8">
- <div class="antibot-box">
- <div class="antibot-image"><?= AntiBot::image('user') ?></div>
- <label for="antibot-answer"><?= htmlspecialchars(Language::get('auth_security_code', 'Security code')) ?></label>
- <input id="antibot-answer" type="text" name="antibot_answer" required inputmode="text" autocomplete="off" maxlength="6" spellcheck="false">
- </div>
- <button type="submit"><?= htmlspecialchars(Language::get('auth_register_button', 'Create account')) ?></button>
- </form>
- <?php endif; ?>
-
- </div>
- </body>
-
- </html>
-