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, ]; } }