feat: Turnstile captcha, menu khách + nút tắt topbar cấu hình từ admin, ẩn setting nhạy cảm

- Turnstile (App\Support\Turnstile, middleware turnstile) cho login admin và login/register/forgot/reset của khách; cấu hình ở setting key=turnstile, secret mã hoá SignRSA.
- Menu sidebar khách (setting client_menu) và nút tắt topbar (setting client_quick_links) do admin sửa qua administrator/client-menu[/quick-links], khách đọc v1/setting/menu, v1/setting/quick-links.
- v1/setting và settings/public chỉ trả whitelist PublicSetting::FIELDS (trước đây lộ bot_token, inet_api_key).
- Cài đặt tài khoản khách (preferences), rate limit theo IP cho register/resend-verify.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
baodev0
2026-10-12 15:53:43 +07:00
co-authored by Claude Opus 5.5
parent 823af4a56a
commit e888a6ed5d
33 changed files with 1022 additions and 52 deletions
@@ -0,0 +1,55 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
private const KEY = 'client_menu';
public function up(): void
{
if (DB::table('setting')->where('key', self::KEY)->exists()) {
return;
}
$link = fn (string $title, string $icon, ?string $href, array $children = []) => [
'title' => $title, 'icon' => $icon, 'href' => $href, 'disabled' => false, 'children' => $children,
];
$child = fn (string $title, string $icon, string $href) => [
'title' => $title, 'icon' => $icon, 'href' => $href, 'disabled' => false,
];
$menu = [
$link('Dashboard', 'hugeicons:dashboard-square-01', '/'),
$link('Đăng ký dịch vụ', 'hugeicons:cloud', null, [
$child('VPS', 'hugeicons:archive-04', '/san-pham/vps'),
$child('Tên miền', 'hugeicons:tags', '/dang-ky-ten-mien'),
$child('Hosting', 'hugeicons:hard-drive', '/san-pham/hosting'),
]),
$link('Quản lý dịch vụ', 'hugeicons:3-d-view', null, [
$child('VPS', 'hugeicons:archive-04', '/dich-vu/vps'),
$child('Domain', 'hugeicons:global', '/dich-vu/domain'),
$child('Hosting', 'hugeicons:hard-drive', '/dich-vu/hosting'),
]),
$link('Quản lý thanh toán', 'hugeicons:layer', '/don-hang'),
$link('Ticket hỗ trợ', 'hugeicons:customer-service', '/tickets'),
$link('Trung tâm hướng dẫn', 'hugeicons:book-open-01', '/docs'),
$link('Nạp tiền', 'hugeicons:coins-dollar', '/nap-tien'),
$link('Lịch sử giao dịch', 'hugeicons:settings-02', '/lich-su-giao-dich'),
$link('Thông báo', 'hugeicons:notification-03', '/thong-bao'),
];
DB::table('setting')->insert([
'key' => self::KEY,
'value' => json_encode($menu, JSON_UNESCAPED_UNICODE),
'created_at' => now(),
'updated_at' => now(),
]);
}
public function down(): void
{
DB::table('setting')->where('key', self::KEY)->delete();
}
};
@@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
private const KEY = 'client_quick_links';
public function up(): void
{
if (DB::table('setting')->where('key', self::KEY)->exists()) {
return;
}
DB::table('setting')->insert([
'key' => self::KEY,
'value' => json_encode([
['title' => 'Tạo Cloud VPS', 'icon' => 'lucide:cloud', 'href' => '/san-pham/vps', 'disabled' => false],
], JSON_UNESCAPED_UNICODE),
'created_at' => now(),
'updated_at' => now(),
]);
}
public function down(): void
{
DB::table('setting')->where('key', self::KEY)->delete();
}
};