diff --git a/app/Users/Controllers/UserApiController.php b/app/Users/Controllers/UserApiController.php index ebc17e262f3..07878d21c0b 100644 --- a/app/Users/Controllers/UserApiController.php +++ b/app/Users/Controllers/UserApiController.php @@ -13,6 +13,11 @@ use Illuminate\Validation\Rules\Password; use Illuminate\Validation\Rules\Unique; +use BookStack\Api\ApiToken; +use BookStack\Activity\ActivityType; +use Illuminate\Support\Facades\Hash; +use Illuminate\Support\Str; + class UserApiController extends ApiController { protected UserRepo $userRepo; @@ -95,13 +100,21 @@ public function create(Request $request) $sendInvite = boolval($data['send_invite'] ?? false) === true; $user = null; - DB::transaction(function () use ($data, $sendInvite, &$user) { + $apiToken = null; + + DB::transaction(function () use ($data, $sendInvite, &$user, &$apiToken) { $user = $this->userRepo->create($data, $sendInvite); + + // 自动创建 API Token + $apiToken = $this->createInitialToken($user); }); $this->singleFormatter($user); - return response()->json($user); + return response()->json([ + 'user' => $user, + 'api_token' => $apiToken, + ]); } /** @@ -148,6 +161,38 @@ public function delete(Request $request, string $id) return response('', 204); } + + /** + * Create an initial API token for a new user. + */ + protected function createInitialToken(User $user): array + { + $secret = Str::random(32); + $token = (new ApiToken())->forceFill([ + 'name' => 'Initial Token', + 'token_id' => Str::random(32), + 'secret' => Hash::make($secret), + 'user_id' => $user->id, + 'expires_at' => ApiToken::defaultExpiry(), + ]); + + while (ApiToken::query()->where('token_id', '=', $token->token_id)->exists()) { + $token->token_id = Str::random(32); + } + + $token->save(); + + $this->logActivity(ActivityType::API_TOKEN_CREATE, $token); + + return [ + 'id' => $token->id, + 'name' => $token->name, + 'token_id' => $token->token_id, + 'secret' => $secret, + 'expires_at' => $token->expires_at->format('Y-m-d'), + ]; + } + /** * Format the given user model for single-result display. */ diff --git a/app/Users/Controllers/UserController.php b/app/Users/Controllers/UserController.php index f93c00a89c2..ab8811b422f 100644 --- a/app/Users/Controllers/UserController.php +++ b/app/Users/Controllers/UserController.php @@ -20,6 +20,11 @@ use Illuminate\Validation\Rules\Password; use Illuminate\Validation\ValidationException; +use BookStack\Api\ApiToken; +use BookStack\Activity\ActivityType; +use Illuminate\Support\Facades\Hash; +use Illuminate\Support\Str; + class UserController extends Controller { public function __construct( @@ -94,16 +99,46 @@ public function store(Request $request) $validated = $this->validate($request, array_filter($validationRules)); + $user = null; + $apiToken = null; + try { - DB::transaction(function () use ($validated, $sendInvite) { - $this->userRepo->create($validated, $sendInvite); + DB::transaction(function () use ($validated, $sendInvite, &$user, &$apiToken) { + $user = $this->userRepo->create($validated, $sendInvite); + + // 如果是 JSON 请求,自动创建 API Token + if ($request->wantsJson()) { + $apiToken = $this->createInitialToken($user); + } }); } catch (UserInviteException $e) { Log::error("Failed to send user invite with error: {$e->getMessage()}"); + + if ($request->wantsJson()) { + return response()->json([ + 'success' => false, + 'message' => trans('errors.users_could_not_send_invite'), + ], 500); + } + $this->showErrorNotification(trans('errors.users_could_not_send_invite')); return redirect('/settings/users/create')->withInput(); } + // 根据 Accept header 返回 JSON 或重定向 + if ($request->wantsJson()) { + return response()->json([ + 'success' => true, + 'message' => '用户创建成功', + 'user' => [ + 'id' => $user->id, + 'name' => $user->name, + 'email' => $user->email, + ], + 'api_token' => $apiToken, + ]); + } + return redirect('/settings/users'); } @@ -208,4 +243,35 @@ public function destroy(Request $request, int $id) return redirect('/settings/users'); } + + /** + * Create an initial API token for a new user. + */ + protected function createInitialToken(\BookStack\Users\Models\User $user): array + { + $secret = Str::random(32); + $token = (new ApiToken())->forceFill([ + 'name' => 'Initial Token', + 'token_id' => Str::random(32), + 'secret' => Hash::make($secret), + 'user_id' => $user->id, + 'expires_at' => ApiToken::defaultExpiry(), + ]); + + while (ApiToken::query()->where('token_id', '=', $token->token_id)->exists()) { + $token->token_id = Str::random(32); + } + + $token->save(); + + $this->logActivity(ActivityType::API_TOKEN_CREATE, $token); + + return [ + 'id' => $token->id, + 'name' => $token->name, + 'token_id' => $token->token_id, + 'secret' => $secret, + 'expires_at' => $token->expires_at->format('Y-m-d'), + ]; + } }