Files
SimpleNav/login.php
T
laoyang 92eb9d90be 后台上传站点图标:首页品牌图 + favicon + PWA 图标一起换
需求:后台新增设置,允许管理员上传自己的图标;上传后首页品牌图标与浏览器
标签页 favicon(以及「添加到手机主屏」图标)一起换成它。

要点:
- 一张上传图服务端居中裁剪成正方形,生成 16/32/48/64/180/192/512 +
  maskable 192/512 整套尺寸,并手工拼装多帧 favicon.ico。
- 单一来源 site_icon_url() 统一取址;site_icon_head() 被首页/登录页/后台
  三处共用;PWA manifest 改由 manifest.php 动态输出、maskable 也跟随 ——
  三处防线共同杜绝「首页换了、标签页还是旧的」这类半生效。
- 目录可写则落静态文件(最快);不可写的保守环境整套压进设置、由
  site-icon.php 按需输出,功能不因权限限制整块不可用。
- 与「网站列表里每个网站自己的图标」是两个维度:目录与设置键都分开,
  清理时互不误伤。

顺手修掉两个真问题:
- login.php / admin/_auth.php 各留了一行写死的 favicon,与新的
  site_icon_head() 并存 → head 里两条 rel="icon",是「上传了却没换」的
  温床。已删掉写死那两行。
- 自定义 favicon.ico 原先只塞 16+32 两帧,而内置是 16/32/48/64 四帧 ——
  换图标反而退步。已补齐 icon-64 并统一为 4 帧。
- setting() 的请求内缓存导致「同请求写后再读」拿到旧值(真实坑):
  settings_cache() 支持强制重载 + settings_reload(),写入后立即失效。

验证:单测 44/44(含只读目录动态回退)、HTTP 56/56、e2e 174/0、
反向验证 5 变体全部被抓住(含「写死残留」与「ico 只塞两帧」两个新哨兵)、
真机几何 360/390/768/1440 无溢出、线上直连桌面+移动端几何全绿、
线上诊断页走通「上传→前台全换→复原」。
2026-09-28 22:28:23 +08:00

67 lines
2.6 KiB
PHP

<?php
/**
* 简航 SimpleNav —— 管理员登录 / 首次初始化
*/
declare(strict_types=1);
require_once __DIR__ . '/functions.php';
if (session_status() !== PHP_SESSION_ACTIVE) {
session_start();
}
send_no_cache();
$hasAdmin = (int)db()->query('SELECT COUNT(*) FROM users')->fetchColumn() > 0;
$err = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$u = trim((string)($_POST['username'] ?? ''));
$p = (string)($_POST['password'] ?? '');
if ($u === '' || $p === '') {
$err = '请输入用户名和密码';
} elseif (mb_strlen($p) < 4) {
$err = '密码至少 4 位';
} elseif (!$hasAdmin) {
// 首次创建管理员
$st = db()->prepare('INSERT INTO users (username, password_hash, created_at) VALUES (?,?,?)');
$st->execute([$u, password_hash($p, PASSWORD_DEFAULT), date('Y-m-d H:i:s')]);
$_SESSION['uid'] = (int)db()->lastInsertId();
header('Location: ' . url('admin/'));
exit;
} else {
// 登录校验
$st = db()->prepare('SELECT * FROM users WHERE username=? LIMIT 1');
$st->execute([$u]);
$row = $st->fetch();
if ($row && password_verify($p, $row['password_hash'])) {
$_SESSION['uid'] = (int)$row['id'];
header('Location: ' . url('admin/'));
exit;
}
$err = '用户名或密码错误';
}
}
$title = $hasAdmin ? '登录' : '初始化管理员';
?><!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= e($title) ?> · <?= e(site_name()) ?></title>
<link rel="stylesheet" href="<?= e(asset('assets/css/style.css')) ?>">
<link rel="stylesheet" href="<?= e(asset('assets/css/admin.css')) ?>">
<?= site_icon_head() ?>
</head>
<body class="admin">
<div class="login-wrap">
<form class="login-card" method="post" action="">
<img class="login-logo" src="<?= e(site_icon_url('logo', 512) ?: url('assets/img/logo.svg')) ?>" alt="" onerror="this.style.display='none'">
<h1><?= e(site_name()) ?></h1>
<p class="sub"><?= $hasAdmin ? '管理员登录' : '首次使用,请创建管理员账号' ?></p>
<?php if ($err): ?><div class="alert"><?= e($err) ?></div><?php endif; ?>
<input type="text" name="username" placeholder="用户名" autocomplete="username" required>
<input type="password" name="password" placeholder="密码" autocomplete="<?= $hasAdmin ? 'current-password' : 'new-password' ?>" required>
<button type="submit" class="btn primary block"><?= $hasAdmin ? '登录' : '创建并进入' ?></button>
</form>
</div>
</body>
</html>