搭建轻舟一期门户、后台和统一登录。

让分类、会员和示例工具共用同一账号,本地即可跑通登录与授权。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Dev
2026-11-09 16:46:35 +08:00
co-authored by Cursor
commit 60daff2365
173 changed files with 21585 additions and 0 deletions
@@ -0,0 +1,123 @@
<?php
namespace App\Http\Controllers\Api\V1\Admin;
use App\Http\Controllers\Controller;
use App\Models\LoginLog;
use App\Models\OauthClient;
use App\Models\Order;
use App\Models\ServiceTicket;
use App\Models\Setting;
use App\Models\Subscription;
use App\Models\ToolOpenLog;
use App\Models\User;
use App\Support\Settings;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
class SystemController extends Controller
{
public function dashboard()
{
return $this->ok([
'users' => User::query()->count(),
'members' => Subscription::query()->where('status', 'active')->where('ends_at', '>', now())->count(),
'logins_today' => LoginLog::query()->where('success', true)->where('created_at', '>=', today())->count(),
'opens_today' => ToolOpenLog::query()->where('created_at', '>=', today())->count(),
'pending_orders' => Order::query()->where('status', 'pending')->count(),
'open_tickets' => ServiceTicket::query()->whereIn('status', ['pending', 'quoted', 'doing', 'checking'])->count(),
]);
}
public function clients()
{
return $this->ok(OauthClient::query()->with('tool:id,name')->latest()->get()->map(fn (OauthClient $client) => $this->clientPayload($client)));
}
public function storeClient(Request $request)
{
$data = $this->clientData($request);
$secret = Str::random(32);
$client = OauthClient::query()->create($data + ['secret_hash' => hash('sha256', $secret)]);
$this->audit($request->user(), 'client.create', $client, '新建客户端 '.$client->name);
return $this->ok($this->clientPayload($client) + ['secret' => $secret], '密钥只显示一次');
}
public function updateClient(Request $request, OauthClient $client)
{
$client->update($this->clientData($request, $client->id));
$this->audit($request->user(), 'client.update', $client, '更新客户端 '.$client->name);
return $this->ok($this->clientPayload($client));
}
public function resetSecret(Request $request, OauthClient $client)
{
$secret = Str::random(32);
$client->update(['secret_hash' => hash('sha256', $secret)]);
$this->audit($request->user(), 'client.secret', $client, '重置客户端密钥 '.$client->name);
return $this->ok(['secret' => $secret], '新密钥只显示一次');
}
public function settings()
{
return $this->ok(Setting::query()->orderBy('key')->get());
}
public function updateSettings(Request $request)
{
$data = $request->validate([
'items' => ['required', 'array'],
'items.site_name' => ['nullable', 'string', 'max:40'],
'items.site_description' => ['nullable', 'string', 'max:255'],
'items.icp' => ['nullable', 'string', 'max:80'],
'items.grace_days' => ['nullable', 'integer', 'min:0', 'max:30'],
'items.sms_register_creates_user' => ['nullable', 'in:0,1'],
'items.mail_enabled' => ['nullable', 'in:0,1'],
]);
foreach ($data['items'] as $key => $value) {
Setting::query()->updateOrCreate(['key' => $key], ['value' => (string) $value]);
}
Settings::flush();
$this->audit($request->user(), 'settings.update', null, '更新系统设置');
return $this->ok(Setting::query()->orderBy('key')->get(), '设置已保存');
}
private function clientData(Request $request, ?int $id = null): array
{
$data = $request->validate([
'name' => ['required', 'string', 'max:80'],
'client_id' => ['required', 'regex:/^[a-z0-9_\-]+$/', Rule::unique('oauth_clients', 'client_id')->ignore($id)],
'redirect_uris' => ['required', 'array', 'min:1'],
'redirect_uris.*' => ['url', 'max:500'],
'logout_uris' => ['nullable', 'array'],
'logout_uris.*' => ['url', 'max:500'],
'tool_id' => ['nullable', 'integer', 'exists:tools,id'],
'is_first_party' => ['required', 'boolean'],
'is_public' => ['required', 'boolean'],
'status' => ['required', 'in:active,disabled'],
]);
return $data;
}
private function clientPayload(OauthClient $client): array
{
return [
'id' => $client->id,
'name' => $client->name,
'client_id' => $client->client_id,
'redirect_uris' => $client->redirect_uris,
'logout_uris' => $client->logout_uris ?? [],
'tool_id' => $client->tool_id,
'tool_name' => $client->tool?->name,
'is_first_party' => $client->is_first_party,
'is_public' => $client->is_public,
'status' => $client->status,
];
}
}