Something went wrong. Try again.
A lightweight, personal, self-hosted frontend and local caching proxy for pixiv artworks p.ufal.my.id
pixiv python archiver
Something went wrong. Try again.
localpixiv admin.php
5.9 kB · 138 lines
PHP
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139<?php/** * localpixiv - Admin Portal * * Handles three POST actions: * - Verifies credentials and creates a 30-day admin login. * - Logs the current admin out. * - Persists the admin-only public archive setting used by viewer.php. * * GET requests render either the login form or the authenticated status and * Logout button. The same file works through the /admin front-controller route * and when served directly as admin.php. */
// Shared helpers are required here as admin.php may also be requested directly.require_once __DIR__ . '/auth.php';
// Reuse bootstrap values from index.php, with standalone fallbacks when absent.if (!isset($sitetitle)) { $sitetitle = is_file(__DIR__ . '/config/title') ? trim((string) file_get_contents(__DIR__ . '/config/title')) ?: 'localpixiv' : 'localpixiv';}
if (!isset($faviconHtml)) { $faviconHtml = ''; $faviconPath = is_file(__DIR__ . '/config/favicon') ? trim((string) file_get_contents(__DIR__ . '/config/favicon')) : ''; if ($faviconPath !== '') { $faviconHtml = '<link rel="icon" href="/' . htmlspecialchars(ltrim($faviconPath, '/'), ENT_QUOTES, 'UTF-8') . '">'; }}
// All mutations use POST. The action field selects the narrow handler below.$error = '';$action = $_POST['action'] ?? '';
// Login verifies the submitted password against the configured password hash.if ($_SERVER['REQUEST_METHOD'] === 'POST' && $action === 'login') { $credentials = localpixivCredentials(); $username = trim((string) ($_POST['user'] ?? '')); $password = (string) ($_POST['pass'] ?? '');
if (localpixivCredentialsConfigured() && hash_equals($credentials['user'], $username) && password_verify($password, $credentials['hash'])) { // Redirect-after-POST prevents an accidental login resubmission on refresh. localpixivSetAdminCookie(localpixivAuthToken(), time() + (30 * 86400)); header('Location: /admin'); exit; }
$error = localpixivCredentialsConfigured() ? 'Invalid credentials' : 'Admin credentials are not configured';} elseif ($_SERVER['REQUEST_METHOD'] === 'POST' && $action === 'logout') { // Logout is a protected state change rather than an unguarded GET link. if (!localpixivValidCsrfToken('logout', $_POST['csrf'] ?? null)) { http_response_code(403); $error = 'Invalid logout request'; } else { localpixivSetAdminCookie('', time() - 3600); header('Location: /admin'); exit; }} elseif ($_SERVER['REQUEST_METHOD'] === 'POST' && $action === 'set_public_archive') { // The homepage checkbox posts here; only a logged-in admin may update the file. if (!localpixivValidCsrfToken('set_public_archive', $_POST['csrf'] ?? null)) { http_response_code(403); $error = 'Invalid settings request'; } else { $value = isset($_POST['public_archive']) ? 'on' : 'off'; // LOCK_EX prevents concurrent requests from interleaving their writes. $written = @file_put_contents(__DIR__ . '/config/public', $value . PHP_EOL, LOCK_EX); if ($written === false) { http_response_code(500); $error = 'Unable to save public archive setting'; } else { header('Location: /?settings=1'); exit; } }}
// Prepare escaped display values only after POST handlers have finished.$isAdmin = localpixivIsAdmin();$credentials = localpixivCredentials();$theme = $_COOKIE['theme'] ?? 'system';$dataThemeAttr = ($theme === 'light' || $theme === 'dark') ? ' data-theme="' . $theme . '"' : '';$siteTitleHtml = htmlspecialchars($sitetitle, ENT_QUOTES, 'UTF-8');$adminNameHtml = htmlspecialchars($credentials['user'], ENT_QUOTES, 'UTF-8');?><!DOCTYPE html><html lang="en"<?= $dataThemeAttr ?>><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>admin - <?= $siteTitleHtml ?></title> <?= $faviconHtml ?> <link rel="stylesheet" href="/css/style.css"></head><body class="viewer-body"> <main class="fetching-wrapper"> <div class="artwork-card"> <div class="header"> <h2 class="site-title"><a href="/"><?= $siteTitleHtml ?></a></h2> </div>
<div class="artwork-content"> <div class="fetching-container"> <?php if ($isAdmin): ?> <p>Admin logged in as <?= $adminNameHtml ?></p> <form method="post" action="/admin" class="admin-logout-form"> <input type="hidden" name="action" value="logout"> <input type="hidden" name="csrf" value="<?= htmlspecialchars(localpixivCsrfToken('logout'), ENT_QUOTES, 'UTF-8') ?>"> <button type="submit" class="btn-gallery">Logout</button> </form> <?php else: ?> <form method="post" action="/admin" class="admin-form"> <input type="hidden" name="action" value="login"> <input type="text" name="user" class="home-input" placeholder="Username" autocomplete="username" required autofocus> <input type="password" name="pass" class="home-input" placeholder="Password" autocomplete="current-password" required> <button type="submit" class="btn-gallery">Login</button> </form> <?php endif; ?>
<?php if ($error !== ''): ?> <p class="admin-error"><?= htmlspecialchars($error, ENT_QUOTES, 'UTF-8') ?></p> <?php endif; ?> </div> </div> </div> </main></body></html>