2026-09-29 23:48:45 +08:00
|
|
|
|
<?php
|
|
|
|
|
|
/**
|
2026-09-30 00:49:53 +08:00
|
|
|
|
* 家书 FamilyBook · 前端控制器
|
2026-09-29 23:48:45 +08:00
|
|
|
|
*
|
2026-09-30 00:49:53 +08:00
|
|
|
|
* 路由走 query-string(index.php?r=模块/动作),不依赖 Apache 重写,
|
|
|
|
|
|
* 换服务器、换 Nginx 都不用改配置。
|
2026-09-29 23:48:45 +08:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
2026-09-30 00:49:53 +08:00
|
|
|
|
require __DIR__ . '/app/bootstrap.php';
|
2026-09-29 23:48:45 +08:00
|
|
|
|
|
2026-09-30 00:49:53 +08:00
|
|
|
|
use App\Account;
|
|
|
|
|
|
use App\Auth;
|
|
|
|
|
|
use App\Crypto;
|
|
|
|
|
|
use App\Database;
|
|
|
|
|
|
use App\Family;
|
|
|
|
|
|
use App\Person;
|
|
|
|
|
|
use App\Setting;
|
|
|
|
|
|
use App\Upload;
|
|
|
|
|
|
use App\View;
|
2026-09-29 23:48:45 +08:00
|
|
|
|
|
2026-09-30 00:49:53 +08:00
|
|
|
|
if (!Database::hasTables()) {
|
|
|
|
|
|
header('Location: install.php');
|
|
|
|
|
|
exit;
|
2026-09-29 23:48:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-30 00:49:53 +08:00
|
|
|
|
$r = isset($_GET['r']) ? trim((string)$_GET['r'], '/') : 'home';
|
|
|
|
|
|
$r = $r === '' ? 'home' : $r;
|
|
|
|
|
|
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
|
2026-09-29 23:48:45 +08:00
|
|
|
|
|
2026-09-30 00:49:53 +08:00
|
|
|
|
$publicRoutes = ['auth/login'];
|
|
|
|
|
|
if (!Auth::check() && !in_array($r, $publicRoutes, true)) {
|
|
|
|
|
|
redirect('auth/login');
|
2026-09-29 23:48:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-30 00:49:53 +08:00
|
|
|
|
switch ($r) {
|
2026-09-29 23:48:45 +08:00
|
|
|
|
|
2026-09-30 00:49:53 +08:00
|
|
|
|
// ==================== 登录 / 退出 ====================
|
|
|
|
|
|
case 'auth/login':
|
|
|
|
|
|
if (Auth::check()) {
|
|
|
|
|
|
redirect('home');
|
|
|
|
|
|
}
|
|
|
|
|
|
if ($method === 'POST') {
|
|
|
|
|
|
csrf_check();
|
|
|
|
|
|
$phone = trim((string)($_POST['phone'] ?? ''));
|
|
|
|
|
|
$pass = (string)($_POST['password'] ?? '');
|
|
|
|
|
|
if (Auth::login($phone, $pass)) {
|
|
|
|
|
|
redirect('home');
|
|
|
|
|
|
}
|
|
|
|
|
|
View::renderBare('login', [
|
|
|
|
|
|
'title' => '登录',
|
|
|
|
|
|
'familyName' => fb_config('site_name', '家书'),
|
|
|
|
|
|
'error' => '手机号或密码不对,或者账号还没开通/已被停用。',
|
|
|
|
|
|
'phone' => $phone,
|
|
|
|
|
|
]);
|
|
|
|
|
|
exit;
|
|
|
|
|
|
}
|
|
|
|
|
|
View::renderBare('login', [
|
|
|
|
|
|
'title' => '登录',
|
|
|
|
|
|
'familyName' => fb_config('site_name', '家书'),
|
2026-09-29 23:48:45 +08:00
|
|
|
|
]);
|
2026-09-30 00:49:53 +08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'auth/logout':
|
|
|
|
|
|
if (isset($_GET['_csrf'], $_SESSION['_csrf'])
|
|
|
|
|
|
&& hash_equals($_SESSION['_csrf'], (string)$_GET['_csrf'])) {
|
|
|
|
|
|
Auth::logout();
|
|
|
|
|
|
}
|
|
|
|
|
|
redirect('auth/login');
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'auth/password':
|
|
|
|
|
|
Auth::requireLogin();
|
|
|
|
|
|
if ($method === 'POST') {
|
|
|
|
|
|
csrf_check();
|
|
|
|
|
|
$old = (string)($_POST['old_password'] ?? '');
|
|
|
|
|
|
$new = (string)($_POST['password'] ?? '');
|
|
|
|
|
|
$new2 = (string)($_POST['password2'] ?? '');
|
|
|
|
|
|
|
|
|
|
|
|
$st = Database::get()->prepare('SELECT password_hash FROM accounts WHERE id = ?');
|
|
|
|
|
|
$st->execute([Auth::id()]);
|
|
|
|
|
|
$hash = (string)$st->fetchColumn();
|
|
|
|
|
|
|
|
|
|
|
|
$err = null;
|
|
|
|
|
|
if (!password_verify($old, $hash)) {
|
|
|
|
|
|
$err = '当前密码不对';
|
|
|
|
|
|
} elseif ($new !== $new2) {
|
|
|
|
|
|
$err = '两次输入的新密码不一致';
|
|
|
|
|
|
} elseif (Account::validatePassword($new)) {
|
|
|
|
|
|
$err = implode(';', Account::validatePassword($new));
|
|
|
|
|
|
}
|
|
|
|
|
|
if ($err) {
|
|
|
|
|
|
View::render('auth/password', ['title' => '修改密码', 'error' => $err], 'home');
|
|
|
|
|
|
exit;
|
|
|
|
|
|
}
|
|
|
|
|
|
Account::setPassword((int)Auth::id(), $new);
|
|
|
|
|
|
flash('ok', '密码已更新,下次登录用新密码。');
|
|
|
|
|
|
redirect('home');
|
|
|
|
|
|
}
|
|
|
|
|
|
View::render('auth/password', ['title' => '修改密码'], 'home');
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 首页 ====================
|
|
|
|
|
|
case 'home':
|
|
|
|
|
|
$familyId = (int)Auth::familyId();
|
|
|
|
|
|
View::render('home', [
|
|
|
|
|
|
'title' => '首页',
|
|
|
|
|
|
'family' => Family::find($familyId),
|
|
|
|
|
|
'people' => Person::all($familyId),
|
|
|
|
|
|
'birthdays' => Person::upcomingBirthdays($familyId),
|
|
|
|
|
|
], 'home');
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 名册 ====================
|
|
|
|
|
|
case 'people':
|
|
|
|
|
|
$q = trim((string)($_GET['q'] ?? ''));
|
|
|
|
|
|
View::render('people/index', [
|
|
|
|
|
|
'title' => '家庭名册',
|
|
|
|
|
|
'people' => Person::all((int)Auth::familyId(), $q),
|
|
|
|
|
|
'q' => $q,
|
|
|
|
|
|
], 'people');
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'people/new':
|
|
|
|
|
|
Auth::requireAdmin();
|
|
|
|
|
|
clear_old();
|
|
|
|
|
|
View::render('people/form', [
|
|
|
|
|
|
'title' => '添加成员',
|
|
|
|
|
|
'person' => null,
|
|
|
|
|
|
'errors' => [],
|
|
|
|
|
|
], 'people');
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'people/create':
|
|
|
|
|
|
Auth::requireAdmin();
|
|
|
|
|
|
csrf_check();
|
|
|
|
|
|
$d = [
|
|
|
|
|
|
'name' => trim((string)($_POST['name'] ?? '')),
|
|
|
|
|
|
'nickname' => trim((string)($_POST['nickname'] ?? '')),
|
|
|
|
|
|
'gender' => (string)($_POST['gender'] ?? ''),
|
|
|
|
|
|
'birthday' => trim((string)($_POST['birthday'] ?? '')),
|
|
|
|
|
|
'phone' => trim((string)($_POST['phone'] ?? '')),
|
|
|
|
|
|
'id_card' => trim((string)($_POST['id_card'] ?? '')),
|
|
|
|
|
|
'email' => trim((string)($_POST['email'] ?? '')),
|
|
|
|
|
|
'address' => trim((string)($_POST['address'] ?? '')),
|
|
|
|
|
|
'note' => trim((string)($_POST['note'] ?? '')),
|
|
|
|
|
|
'sort_order'=> (int)($_POST['sort_order'] ?? 1000),
|
|
|
|
|
|
'avatar' => '',
|
|
|
|
|
|
'kind' => 'member',
|
2026-09-29 23:48:45 +08:00
|
|
|
|
];
|
2026-09-30 00:49:53 +08:00
|
|
|
|
$errors = Person::validate($d);
|
|
|
|
|
|
|
|
|
|
|
|
$up = Upload::avatar($_FILES['avatar'] ?? []);
|
|
|
|
|
|
if (!$up['ok'] && ($up['error'] ?? '') !== '') {
|
|
|
|
|
|
$errors['avatar'] = $up['error'];
|
|
|
|
|
|
}
|
|
|
|
|
|
if ($errors) {
|
|
|
|
|
|
set_old($_POST);
|
|
|
|
|
|
View::render('people/form', [
|
|
|
|
|
|
'title' => '添加成员',
|
|
|
|
|
|
'person' => null,
|
|
|
|
|
|
'errors' => $errors,
|
|
|
|
|
|
'phoneValue' => $d['phone'],
|
|
|
|
|
|
'idCardValue'=> $d['id_card'],
|
|
|
|
|
|
], 'people');
|
|
|
|
|
|
exit;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ($up['ok']) {
|
|
|
|
|
|
$d['avatar'] = $up['file'];
|
|
|
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
|
|
|
$id = Person::create((int)Auth::familyId(), $d);
|
|
|
|
|
|
} catch (\PDOException $ex) {
|
|
|
|
|
|
set_old($_POST);
|
|
|
|
|
|
View::render('people/form', [
|
|
|
|
|
|
'title' => '添加成员',
|
|
|
|
|
|
'person' => null,
|
|
|
|
|
|
'errors' => ['phone' => '这个手机号已经在名册里了'],
|
|
|
|
|
|
'phoneValue' => $d['phone'],
|
|
|
|
|
|
'idCardValue'=> $d['id_card'],
|
|
|
|
|
|
], 'people');
|
|
|
|
|
|
exit;
|
|
|
|
|
|
}
|
|
|
|
|
|
clear_old();
|
|
|
|
|
|
flash('ok', '已把「' . $d['name'] . '」加进名册。');
|
|
|
|
|
|
redirect('people/show', ['id' => $id]);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'people/edit':
|
|
|
|
|
|
Auth::requireAdmin();
|
|
|
|
|
|
$p = Person::find((int)($_GET['id'] ?? 0));
|
|
|
|
|
|
if (!$p || (int)$p['family_id'] !== (int)Auth::familyId()) {
|
|
|
|
|
|
flash('err', '找不到这个人。');
|
|
|
|
|
|
redirect('people');
|
|
|
|
|
|
}
|
|
|
|
|
|
clear_old();
|
|
|
|
|
|
View::render('people/form', [
|
|
|
|
|
|
'title' => '编辑成员',
|
|
|
|
|
|
'person' => $p,
|
|
|
|
|
|
'errors' => [],
|
|
|
|
|
|
'phoneValue' => Crypto::decrypt((string)$p['phone_encrypted']),
|
|
|
|
|
|
'idCardValue' => Crypto::decrypt((string)$p['id_card_encrypted']),
|
|
|
|
|
|
], 'people');
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'people/update':
|
|
|
|
|
|
Auth::requireAdmin();
|
|
|
|
|
|
csrf_check();
|
|
|
|
|
|
$id = (int)($_POST['id'] ?? 0);
|
|
|
|
|
|
$p = Person::find($id);
|
|
|
|
|
|
if (!$p || (int)$p['family_id'] !== (int)Auth::familyId()) {
|
|
|
|
|
|
flash('err', '找不到这个人。');
|
|
|
|
|
|
redirect('people');
|
|
|
|
|
|
}
|
|
|
|
|
|
$d = [
|
|
|
|
|
|
'name' => trim((string)($_POST['name'] ?? '')),
|
|
|
|
|
|
'nickname' => trim((string)($_POST['nickname'] ?? '')),
|
|
|
|
|
|
'gender' => (string)($_POST['gender'] ?? ''),
|
|
|
|
|
|
'birthday' => trim((string)($_POST['birthday'] ?? '')),
|
|
|
|
|
|
'phone' => trim((string)($_POST['phone'] ?? '')),
|
|
|
|
|
|
'id_card' => trim((string)($_POST['id_card'] ?? '')),
|
|
|
|
|
|
'email' => trim((string)($_POST['email'] ?? '')),
|
|
|
|
|
|
'address' => trim((string)($_POST['address'] ?? '')),
|
|
|
|
|
|
'note' => trim((string)($_POST['note'] ?? '')),
|
|
|
|
|
|
'sort_order'=> (int)($_POST['sort_order'] ?? 1000),
|
|
|
|
|
|
'avatar' => (string)$p['avatar'],
|
|
|
|
|
|
];
|
|
|
|
|
|
$errors = Person::validate($d);
|
|
|
|
|
|
$up = Upload::avatar($_FILES['avatar'] ?? []);
|
|
|
|
|
|
if (!$up['ok'] && ($up['error'] ?? '') !== '') {
|
|
|
|
|
|
$errors['avatar'] = $up['error'];
|
|
|
|
|
|
}
|
|
|
|
|
|
if ($errors) {
|
|
|
|
|
|
set_old($_POST);
|
|
|
|
|
|
View::render('people/form', [
|
|
|
|
|
|
'title' => '编辑成员',
|
|
|
|
|
|
'person' => $p,
|
|
|
|
|
|
'errors' => $errors,
|
|
|
|
|
|
'phoneValue' => $d['phone'],
|
|
|
|
|
|
'idCardValue' => $d['id_card'],
|
|
|
|
|
|
], 'people');
|
|
|
|
|
|
exit;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ($up['ok']) {
|
|
|
|
|
|
Upload::remove((string)$p['avatar']);
|
|
|
|
|
|
$d['avatar'] = $up['file'];
|
|
|
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
|
|
|
Person::update($id, $d);
|
|
|
|
|
|
} catch (\PDOException $ex) {
|
|
|
|
|
|
set_old($_POST);
|
|
|
|
|
|
View::render('people/form', [
|
|
|
|
|
|
'title' => '编辑成员',
|
|
|
|
|
|
'person' => $p,
|
|
|
|
|
|
'errors' => ['phone' => '这个手机号已经被名册里的另一个人用了'],
|
|
|
|
|
|
'phoneValue' => $d['phone'],
|
|
|
|
|
|
'idCardValue' => $d['id_card'],
|
|
|
|
|
|
], 'people');
|
|
|
|
|
|
exit;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ((int)$p['id'] === (int)Auth::personId()) {
|
|
|
|
|
|
Auth::refreshAvatar((string)$d['avatar']);
|
|
|
|
|
|
}
|
|
|
|
|
|
clear_old();
|
|
|
|
|
|
flash('ok', '已保存。');
|
|
|
|
|
|
redirect('people/show', ['id' => $id]);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'people/show':
|
|
|
|
|
|
$p = Person::findWithAccount((int)($_GET['id'] ?? 0));
|
|
|
|
|
|
if (!$p || (int)$p['family_id'] !== (int)Auth::familyId()) {
|
|
|
|
|
|
flash('err', '找不到这个人。');
|
|
|
|
|
|
redirect('people');
|
|
|
|
|
|
}
|
|
|
|
|
|
View::render('people/show', [
|
|
|
|
|
|
'title' => $p['name'],
|
|
|
|
|
|
'person' => $p,
|
|
|
|
|
|
], 'people');
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'people/destroy':
|
|
|
|
|
|
Auth::requireAdmin();
|
|
|
|
|
|
csrf_check();
|
|
|
|
|
|
$id = (int)($_POST['id'] ?? 0);
|
|
|
|
|
|
$p = Person::find($id);
|
|
|
|
|
|
if ($p && (int)$p['family_id'] === (int)Auth::familyId()) {
|
|
|
|
|
|
if ((int)$p['id'] === (int)Auth::personId()) {
|
|
|
|
|
|
flash('err', '不能把自己从名册里移除。');
|
|
|
|
|
|
redirect('people/show', ['id' => $id]);
|
|
|
|
|
|
}
|
|
|
|
|
|
// 不让最后一个管理员消失:系统里没人能管理家庭就锁死了
|
|
|
|
|
|
$acc = Account::findByPerson($id);
|
|
|
|
|
|
if ($acc && $acc['role'] === 'admin' && Account::adminCount() <= 1) {
|
|
|
|
|
|
flash('err', '这是唯一的管理员,先把另一个人设为管理员再移除。');
|
|
|
|
|
|
redirect('people/show', ['id' => $id]);
|
|
|
|
|
|
}
|
|
|
|
|
|
Upload::remove((string)$p['avatar']);
|
|
|
|
|
|
Person::delete($id);
|
|
|
|
|
|
flash('ok', '已从名册移除「' . $p['name'] . '」。');
|
|
|
|
|
|
}
|
|
|
|
|
|
redirect('people');
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 账号 ====================
|
|
|
|
|
|
case 'accounts/create':
|
|
|
|
|
|
Auth::requireAdmin();
|
|
|
|
|
|
csrf_check();
|
|
|
|
|
|
$personId = (int)($_POST['person_id'] ?? 0);
|
|
|
|
|
|
$p = Person::find($personId);
|
|
|
|
|
|
if (!$p || (int)$p['family_id'] !== (int)Auth::familyId()) {
|
|
|
|
|
|
flash('err', '找不到这个人。');
|
|
|
|
|
|
redirect('people');
|
|
|
|
|
|
}
|
|
|
|
|
|
if (Account::findByPerson($personId)) {
|
|
|
|
|
|
flash('err', '这个人的账号已经开通过了。');
|
|
|
|
|
|
redirect('people/show', ['id' => $personId]);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (Crypto::decrypt((string)$p['phone_encrypted']) === '') {
|
|
|
|
|
|
flash('err', '要先填手机号,才能开通登录。');
|
|
|
|
|
|
redirect('people/show', ['id' => $personId]);
|
|
|
|
|
|
}
|
|
|
|
|
|
$pass = (string)($_POST['password'] ?? '');
|
|
|
|
|
|
$errs = Account::validatePassword($pass);
|
|
|
|
|
|
if ($errs) {
|
|
|
|
|
|
flash('err', implode(';', $errs));
|
|
|
|
|
|
redirect('people/show', ['id' => $personId]);
|
|
|
|
|
|
}
|
|
|
|
|
|
Account::create($personId, $pass, ($_POST['role'] ?? '') === 'admin' ? 'admin' : 'member');
|
|
|
|
|
|
flash('ok', '账号已开通,用手机号就能登录。');
|
|
|
|
|
|
redirect('people/show', ['id' => $personId]);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'accounts/role':
|
|
|
|
|
|
Auth::requireAdmin();
|
|
|
|
|
|
csrf_check();
|
|
|
|
|
|
$personId = (int)($_POST['person_id'] ?? 0);
|
|
|
|
|
|
$role = ($_POST['role'] ?? '') === 'admin' ? 'admin' : 'member';
|
|
|
|
|
|
$p = Person::find($personId);
|
|
|
|
|
|
$acc = Account::findByPerson($personId);
|
|
|
|
|
|
if ($p && $acc) {
|
|
|
|
|
|
if ($role === 'member' && $acc['role'] === 'admin' && Account::adminCount() <= 1) {
|
|
|
|
|
|
flash('err', '至少要留一位管理员。');
|
|
|
|
|
|
redirect('people/show', ['id' => $personId]);
|
|
|
|
|
|
}
|
|
|
|
|
|
Account::setRole((int)$acc['id'], $role);
|
|
|
|
|
|
flash('ok', '权限已更新。');
|
|
|
|
|
|
}
|
|
|
|
|
|
redirect('people/show', ['id' => $personId]);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'accounts/status':
|
|
|
|
|
|
Auth::requireAdmin();
|
|
|
|
|
|
csrf_check();
|
|
|
|
|
|
$personId = (int)($_POST['person_id'] ?? 0);
|
|
|
|
|
|
$status = ($_POST['status'] ?? '') === 'active' ? 'active' : 'disabled';
|
|
|
|
|
|
$acc = Account::findByPerson($personId);
|
|
|
|
|
|
if ($acc) {
|
|
|
|
|
|
if ((int)$acc['person_id'] === (int)Auth::personId()) {
|
|
|
|
|
|
flash('err', '不能停用自己。');
|
|
|
|
|
|
redirect('people/show', ['id' => $personId]);
|
|
|
|
|
|
}
|
|
|
|
|
|
if ($status === 'disabled' && $acc['role'] === 'admin' && Account::adminCount() <= 1) {
|
|
|
|
|
|
flash('err', '至少要留一位管理员。');
|
|
|
|
|
|
redirect('people/show', ['id' => $personId]);
|
|
|
|
|
|
}
|
|
|
|
|
|
Account::setStatus((int)$acc['id'], $status);
|
|
|
|
|
|
flash('ok', $status === 'active' ? '账号已启用。' : '账号已停用。');
|
|
|
|
|
|
}
|
|
|
|
|
|
redirect('people/show', ['id' => $personId]);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== 设置 ====================
|
|
|
|
|
|
case 'settings':
|
|
|
|
|
|
Auth::requireAdmin();
|
|
|
|
|
|
$dbVersion = '未知';
|
|
|
|
|
|
try {
|
|
|
|
|
|
$dbVersion = Database::get()->query('SELECT VERSION()')->fetchColumn();
|
|
|
|
|
|
} catch (\Throwable $ex) {
|
|
|
|
|
|
// 拿不到版本不影响页面
|
|
|
|
|
|
}
|
|
|
|
|
|
$rawKey = (string)fb_config('app_key', '');
|
|
|
|
|
|
View::render('settings', [
|
|
|
|
|
|
'title' => '设置',
|
|
|
|
|
|
'family' => Family::find((int)Auth::familyId()),
|
|
|
|
|
|
'dbVersion' => $dbVersion,
|
|
|
|
|
|
'keyOk' => $rawKey !== '' && strpos($rawKey, '请填写') !== 0,
|
|
|
|
|
|
], 'settings');
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'settings/update':
|
|
|
|
|
|
Auth::requireAdmin();
|
|
|
|
|
|
csrf_check();
|
|
|
|
|
|
Family::update(
|
|
|
|
|
|
(int)Auth::familyId(),
|
|
|
|
|
|
trim((string)($_POST['name'] ?? '家书')),
|
|
|
|
|
|
trim((string)($_POST['motto'] ?? ''))
|
|
|
|
|
|
);
|
|
|
|
|
|
flash('ok', '已保存。');
|
|
|
|
|
|
redirect('settings');
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
http_response_code(404);
|
|
|
|
|
|
View::render('error', [
|
|
|
|
|
|
'title' => '页面不存在',
|
|
|
|
|
|
'message' => '找不到这一页。',
|
|
|
|
|
|
], 'home');
|
|
|
|
|
|
break;
|
2026-09-29 23:48:45 +08:00
|
|
|
|
}
|