From edc9e56d7ddeb96099cc7a4ce4c7cbccb9692398 Mon Sep 17 00:00:00 2001 From: xiaomlove Date: Sat, 29 Mar 2025 14:32:31 +0700 Subject: [PATCH] usercp token management --- app/Auth/Permission.php | 2 +- app/Console/Commands/Test.php | 60 +------------ app/Enums/{ => Permission}/PermissionEnum.php | 5 +- .../Controllers/AuthenticateController.php | 38 --------- app/Http/Controllers/TokenController.php | 85 +++++++++++++++++++ app/Http/Controllers/UploadController.php | 6 ++ app/Http/Kernel.php | 5 ++ app/Models/PersonalAccessToken.php | 21 +++++ app/Providers/AppServiceProvider.php | 2 + app/Providers/AuthServiceProvider.php | 7 -- app/Providers/RouteServiceProvider.php | 2 +- app/Repositories/TokenRepository.php | 21 +++++ include/constants.php | 2 +- include/globalfunctions.php | 4 + nexus/Database/NexusDB.php | 15 ++++ public/usercp.php | 35 +++++++- resources/lang/zh_CN/permission.php | 14 ++- resources/lang/zh_CN/token.php | 1 + routes/api.php | 5 +- routes/third-party.php | 5 ++ routes/web.php | 5 +- 21 files changed, 218 insertions(+), 122 deletions(-) rename app/Enums/{ => Permission}/PermissionEnum.php (56%) create mode 100644 app/Http/Controllers/TokenController.php create mode 100644 app/Models/PersonalAccessToken.php create mode 100644 app/Repositories/TokenRepository.php diff --git a/app/Auth/Permission.php b/app/Auth/Permission.php index f778c3de..f1a50783 100644 --- a/app/Auth/Permission.php +++ b/app/Auth/Permission.php @@ -2,7 +2,7 @@ namespace App\Auth; -use App\Enums\PermissionEnum; +use App\Enums\Permission\PermissionEnum; class Permission { diff --git a/app/Console/Commands/Test.php b/app/Console/Commands/Test.php index a1ccba8d..799718a8 100644 --- a/app/Console/Commands/Test.php +++ b/app/Console/Commands/Test.php @@ -2,59 +2,8 @@ namespace App\Console\Commands; -use App\Enums\PermissionEnum; -use App\Events\TorrentUpdated; -use App\Filament\Resources\System\AgentAllowResource; -use App\Http\Resources\TagResource; -use App\Models\AgentAllow; -use App\Models\Attendance; -use App\Models\Category; -use App\Models\Exam; -use App\Models\ExamProgress; -use App\Models\ExamUser; -use App\Models\HitAndRun; -use App\Models\Invite; -use App\Models\LoginLog; -use App\Models\Medal; -use App\Models\Peer; -use App\Models\Post; -use App\Models\SearchBox; -use App\Models\Setting; -use App\Models\Snatch; -use App\Models\Tag; -use App\Models\Torrent; -use App\Models\TorrentOperationLog; -use App\Models\User; -use App\Models\UserBanLog; -use App\Repositories\AgentAllowRepository; -use App\Repositories\AttendanceRepository; -use App\Repositories\CleanupRepository; -use App\Repositories\ExamRepository; -use App\Repositories\HitAndRunRepository; -use App\Repositories\MeiliSearchRepository; -use App\Repositories\PluginRepository; -use App\Repositories\SearchBoxRepository; -use App\Repositories\SearchRepository; -use App\Repositories\TagRepository; -use App\Repositories\ToolRepository; -use App\Repositories\TorrentRepository; -use App\Repositories\UserRepository; -use Carbon\Carbon; -use Filament\Notifications\Notification; -use GeoIp2\Database\Reader; -use GuzzleHttp\Client; +use App\Models\PersonalAccessToken; use Illuminate\Console\Command; -use Illuminate\Encryption\Encrypter; -use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Hash; -use Illuminate\Support\Facades\Http; -use Illuminate\Support\Facades\Redis; -use Illuminate\Support\Facades\Storage; -use Illuminate\Support\Str; -use Imdb\Cache; -use League\Flysystem\StorageAttributes; -use Nexus\Database\NexusDB; -use Nexus\Imdb\Imdb; use NexusPlugin\Menu\Filament\MenuItemResource\Pages\ManageMenuItems; use NexusPlugin\Menu\MenuRepository; use NexusPlugin\Menu\Models\MenuItem; @@ -66,9 +15,6 @@ use NexusPlugin\StickyPromotion\Models\StickyPromotionParticipator; use NexusPlugin\Tracker\TrackerRepository; use NexusPlugin\Work\Models\RoleWork; use NexusPlugin\Work\WorkRepository; -use PhpIP\IP; -use PhpIP\IPBlock; -use Rhilip\Bencode\Bencode; class Test extends Command { @@ -103,9 +49,9 @@ class Test extends Command */ public function handle() { - $r = microtime(); + $r = PersonalAccessToken::query()->find(11); // $r = SearchBox::query()->find(4)->ss()->orWhere("mode", 0)->get(); - dd($r); + dd($r->abilitiesText); } } diff --git a/app/Enums/PermissionEnum.php b/app/Enums/Permission/PermissionEnum.php similarity index 56% rename from app/Enums/PermissionEnum.php rename to app/Enums/Permission/PermissionEnum.php index d7af22b4..d33ed4c4 100644 --- a/app/Enums/PermissionEnum.php +++ b/app/Enums/Permission/PermissionEnum.php @@ -1,8 +1,11 @@ validate([ - 'name' => 'required|string', - ]); - $user = Auth::user(); - $count = $user->tokens()->count(); - if ($count >= 5) { - throw new NexusException("Token limit exceeded"); - } - $newAccessToken = $user->createToken($request->name); - PersonalAccessTokenPlain::query()->create([ - 'access_token_id' => $newAccessToken->accessToken->getKey(), - 'plain_text_token' => $newAccessToken->plainTextToken, - ]); - return $this->success(true); - } catch (\Exception $exception) { - return $this->fail(false, $exception->getMessage()); - } - } - public function delToken(Request $request) - { - try { - $request->validate([ - 'id' => 'required|integer', - ]); - $user = Auth::user(); - $token = $user->tokens()->where("id", $request->id)->first(); - if ($token) { - PersonalAccessTokenPlain::query()->where("access_token_id", $token->id)->delete(); - $token->delete(); - } - return $this->success(true); - } catch (\Exception $exception) { - return $this->fail(false, $exception->getMessage()); - } - } } diff --git a/app/Http/Controllers/TokenController.php b/app/Http/Controllers/TokenController.php new file mode 100644 index 00000000..b05810aa --- /dev/null +++ b/app/Http/Controllers/TokenController.php @@ -0,0 +1,85 @@ +repository = $repository; + } + + public function addToken(Request $request) + { + try { + $request->validate([ + 'name' => 'required|string', + 'permissions' => 'required|array|min:1', + ]); + $user = Auth::user(); + $count = $user->tokens()->count(); + if ($count >= 5) { + throw new NexusException("Token limit exceeded"); + } + $newAccessToken = $user->createToken($request->name, $request->permissions); + PersonalAccessTokenPlain::query()->create([ + 'access_token_id' => $newAccessToken->accessToken->getKey(), + 'plain_text_token' => $newAccessToken->plainTextToken, + ]); + return $this->success(true); + } catch (\Exception $exception) { + return $this->fail(false, $exception->getMessage()); + } + } + + public function delToken(Request $request) + { + try { + $request->validate([ + 'id' => 'required|integer', + ]); + $user = Auth::user(); + $token = $user->tokens()->where("id", $request->id)->first(); + if ($token) { + PersonalAccessTokenPlain::query()->where("access_token_id", $token->id)->delete(); + $token->delete(); + } + return $this->success(true); + } catch (\Exception $exception) { + return $this->fail(false, $exception->getMessage()); + } + } + + public function getPlainText(Request $request) + { + try { + $request->validate([ + 'id' => 'required|integer', + ]); + $user = Auth::user(); + $token = $user->tokens()->where("id", $request->id)->first(); + if (!$token) { + throw new NexusException("Token not found"); + } + $plainRecord = PersonalAccessTokenPlain::query()->where("access_token_id", $token->id)->first(); + if (!$plainRecord) { + throw new NexusException("Plain record not found"); + } + return $this->success($plainRecord->plain_text_token); + } catch (\Exception $exception) { + return $this->fail(false, $exception->getMessage()); + } + } + + + + +} diff --git a/app/Http/Controllers/UploadController.php b/app/Http/Controllers/UploadController.php index f66fb585..a2be43e8 100644 --- a/app/Http/Controllers/UploadController.php +++ b/app/Http/Controllers/UploadController.php @@ -26,4 +26,10 @@ class UploadController extends Controller return $this->success($resource); } + public function upload(Request $request) + { + $user = $request->user(); + return $this->success("OK"); + } + } diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 99dec876..bba14863 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -78,4 +78,9 @@ class Kernel extends HttpKernel 'locale' => \App\Http\Middleware\Locale::class, 'user' => \App\Http\Middleware\User::class, ]; + + protected $middlewareAliases = [ + 'abilities' => \Laravel\Sanctum\Http\Middleware\CheckAbilities::class, + 'ability' => \Laravel\Sanctum\Http\Middleware\CheckForAnyAbility::class, + ]; } diff --git a/app/Models/PersonalAccessToken.php b/app/Models/PersonalAccessToken.php new file mode 100644 index 00000000..7af01e1f --- /dev/null +++ b/app/Models/PersonalAccessToken.php @@ -0,0 +1,21 @@ +abilities)) { + return 'ALL'; + } + $result = []; + foreach ($this->abilities as $ability) { + if ($ability != '*') { + $result[] = nexus_trans("permission.{$ability}.text"); + } + } + return implode(', ', $result); + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 1c32b25a..7397dc2c 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -13,6 +13,7 @@ use Illuminate\Support\Facades\View; use Illuminate\Support\ServiceProvider; use Illuminate\Http\Resources\Json\JsonResource; use Laravel\Passport\Passport; +use Nexus\Database\NexusDB; use Nexus\Nexus; use Filament\Facades\Filament; @@ -37,6 +38,7 @@ class AppServiceProvider extends ServiceProvider { global $plugin; $plugin->start(); + NexusDB::customModel(); DB::connection(config('database.default'))->enableQueryLog(); $forceScheme = strtolower(env('FORCE_SCHEME')); if (env('APP_ENV') == "production" && in_array($forceScheme, ['https', 'http'])) { diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index aa307856..23ac8011 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -9,7 +9,6 @@ use App\Models\Category; use App\Models\Codec; use App\Models\Icon; use App\Models\Media; -use App\Models\OauthClient; use App\Models\Plugin; use App\Models\Processing; use App\Models\SearchBox; @@ -22,7 +21,6 @@ use App\Policies\CodecPolicy; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; -use Laravel\Passport\Passport; class AuthServiceProvider extends ServiceProvider { @@ -55,11 +53,6 @@ class AuthServiceProvider extends ServiceProvider */ public function boot() { -// $this->registerPolicies(); - if (class_exists(Passport::class)) { - Passport::useClientModel(OauthClient::class); - } - Auth::viaRequest('nexus-cookie', function (Request $request) { return $this->getUserByCookie($request->cookie()); }); diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 273129ff..00399302 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -39,7 +39,7 @@ class RouteServiceProvider extends ServiceProvider $this->configureRateLimiting(); $this->routes(function () { - Route::prefix('api') + Route::prefix('api/v1') ->middleware('api') ->namespace($this->namespace) ->group(base_path('routes/api.php')); diff --git a/app/Repositories/TokenRepository.php b/app/Repositories/TokenRepository.php new file mode 100644 index 00000000..2daf0ed8 --- /dev/null +++ b/app/Repositories/TokenRepository.php @@ -0,0 +1,21 @@ +value] = nexus_trans("permission.{$permission->value}.text"); + } + return $result; + } +} diff --git a/include/constants.php b/include/constants.php index 9a034d6c..33be1c48 100644 --- a/include/constants.php +++ b/include/constants.php @@ -1,6 +1,6 @@ danger()->title($msg ?: "Fail!")->send(); } + +function ability(\App\Enums\Permission\PermissionEnum $permission): string { + return sprintf("ability:%s", $permission->value); +} diff --git a/nexus/Database/NexusDB.php b/nexus/Database/NexusDB.php index 36987961..7a044641 100644 --- a/nexus/Database/NexusDB.php +++ b/nexus/Database/NexusDB.php @@ -2,12 +2,16 @@ namespace Nexus\Database; +use App\Models\OauthClient; +use App\Models\PersonalAccessToken; use Illuminate\Support\Facades\Schema; use Illuminate\Database\Capsule\Manager as Capsule; use Illuminate\Database\Query\Expression; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Redis; +use Laravel\Passport\Passport; +use Laravel\Sanctum\Sanctum; class NexusDB { @@ -260,6 +264,7 @@ class NexusDB $capsule->bootEloquent(); $connection = self::$eloquentConnection = $capsule->getConnection($connectionName); $connection->enableQueryLog(); + self::customModel(); } private static function schema(): \Illuminate\Database\Schema\Builder @@ -439,5 +444,15 @@ class NexusDB return false; } + public static function customModel(): void + { + if (class_exists(Passport::class)) { + Passport::useClientModel(OauthClient::class); + } + if (class_exists(Sanctum::class)) { + Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class); + } + } + } diff --git a/public/usercp.php b/public/usercp.php index 3837572d..ff702ad3 100644 --- a/public/usercp.php +++ b/public/usercp.php @@ -1045,10 +1045,13 @@ width: 80px .form-control-row .field { } -.form-control-row input,textarea { +.form-control-row input[type=text],textarea { width: 300px; padding: 4px; } +.form-control-row input[type=checkbox] { +vertical-align: sub; +} CSS; $seedBoxForm = <<
listUserTokenPermissions(); +$permissionOptions = []; +foreach ($permissions as $name => $label) { + $permissionOptions[] = sprintf('', $name, $label); +} +$permissionCheckbox = implode("", $permissionOptions); $token = ''; $tokenLabel = nexus_trans("token.label"); $columnName = nexus_trans('label.name'); +$columnPermission = nexus_trans('token.permission'); $columnCreatedAt = nexus_trans('label.created_at'); $actionCreate = nexus_trans('label.create'); $actionLabel = nexus_trans('label.action'); $res = $userInfo->tokens()->orderBy("id", "desc")->get(); if ($res->count() > 0) { - $token .= ""; + $token .= "
ID{$columnName}{$columnCreatedAt}{$actionLabel}
"; foreach ($res as $tokenRecord) { $token .= ""; $token .= sprintf('', $tokenRecord->id); $token .= sprintf('', $tokenRecord->name); + $token .= sprintf('', $tokenRecord->abilitiesText); $token .= sprintf('', $tokenRecord->created_at); - $token .= sprintf('', $lang_functions['text_delete'], $tokenRecord->id); + $token .= sprintf('', $tokenRecord->id, $lang_functions['text_delete'], $tokenRecord->id); $token .= ""; } $token .= '
ID{$columnName}{$columnPermission}{$columnCreatedAt}{$actionLabel}
%s%s%s%s获取删除获取删除
'; @@ -1152,6 +1164,10 @@ $tokenFoxForm = <<{$columnName}
+
+
{$columnPermission}
+
$permissionCheckbox
+
FORM; @@ -1195,6 +1211,19 @@ jQuery('#token-table').on('click', '.token-del', function () { }, 'json') }) }); +jQuery('#token-table').on('click', '.token-get', function () { + let params = {id: jQuery(this).attr("data-id")} + jQuery('body').loading({stoppable: false}); + jQuery.post('/web/token/get-plain', params, function (response) { + console.log(response) + jQuery('body').loading('stop'); + if (response.ret != 0) { + layer.alert(response.msg) + } else { + layer.alert(response.data) + } + }, 'json') +}); JS; \Nexus\Nexus::js($tokenBoxJs, 'footer', false); diff --git a/resources/lang/zh_CN/permission.php b/resources/lang/zh_CN/permission.php index cff33bbb..09ce5323 100644 --- a/resources/lang/zh_CN/permission.php +++ b/resources/lang/zh_CN/permission.php @@ -217,12 +217,10 @@ return [ 'text' => '允许个性条', 'desc' => '允许用户使用个性条', ], -// 'not-counting-downloaded' => [ -// 'text' => '不计下载量', -// 'desc' => '用户下载量不增加', -// ], -// 'not-counting-hit-and-run' => [ -// 'text' => '不计 H&R', -// 'desc' => '下载带 H&R 标记的种子不记 H&R。注意:等级为 VIP 是固定不计的', -// ], + + //新加 + 'torrent:list' => [ + 'text' => '获取种子列表', + 'desc' => '获取种子列表', + ], ]; diff --git a/resources/lang/zh_CN/token.php b/resources/lang/zh_CN/token.php index 6aa5fe97..f9757267 100644 --- a/resources/lang/zh_CN/token.php +++ b/resources/lang/zh_CN/token.php @@ -2,4 +2,5 @@ return [ "label" => "访问令牌", + "permission" => "权限", ]; diff --git a/routes/api.php b/routes/api.php index 43c2af5d..676544d3 100644 --- a/routes/api.php +++ b/routes/api.php @@ -2,6 +2,7 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; +use App\Enums\Permission\PermissionEnum; /* |-------------------------------------------------------------------------- @@ -48,6 +49,7 @@ Route::group(['middleware' => ['auth:sanctum', 'locale']], function () { Route::resource('topics', \App\Http\Controllers\TopicController::class); Route::get('sections', [\App\Http\Controllers\UploadController::class, 'sections']); + Route::post('upload', [\App\Http\Controllers\UploadController::class, 'upload'])->middleware(ability(PermissionEnum::UPLOAD)); }); Route::group(['middleware' => ['admin']], function () { @@ -99,6 +101,3 @@ Route::group(['middleware' => ['auth:sanctum', 'locale']], function () { Route::post('login', [\App\Http\Controllers\AuthenticateController::class, 'login']); -Route::group(['middleware' => ['auth.nexus:passkey', 'locale']], function () { - Route::post("pieces-hash", [\App\Http\Controllers\TorrentController::class, "queryByPiecesHash"])->name("torrent.pieces_hash.query"); -}); diff --git a/routes/third-party.php b/routes/third-party.php index 071e188a..8e6ca3c2 100644 --- a/routes/third-party.php +++ b/routes/third-party.php @@ -1,6 +1,11 @@ ['auth.nexus:passkey', 'locale']], function () { + Route::post("pieces-hash", [\App\Http\Controllers\TorrentController::class, "queryByPiecesHash"])->name("torrent.pieces_hash.query"); +}); + + Route::post('nastools/approve', [\App\Http\Controllers\AuthenticateController::class, 'nasToolsApprove']); Route::get('iyuu/approve', [\App\Http\Controllers\AuthenticateController::class, 'iyuuApprove']); Route::post('ammds/approve', [\App\Http\Controllers\AuthenticateController::class, 'ammdsApprove']); diff --git a/routes/web.php b/routes/web.php index 9654eae4..6ec39404 100644 --- a/routes/web.php +++ b/routes/web.php @@ -21,8 +21,9 @@ Route::group(['prefix' => 'web', 'middleware' => ['auth.nexus:nexus-web', 'local Route::get('torrent-approval-page', [\App\Http\Controllers\TorrentController::class, 'approvalPage']); Route::get('torrent-approval-logs', [\App\Http\Controllers\TorrentController::class, 'approvalLogs']); Route::post('torrent-approval', [\App\Http\Controllers\TorrentController::class, 'approval']); - Route::post('token/add', [\App\Http\Controllers\AuthenticateController::class, 'addToken']); - Route::post('token/del', [\App\Http\Controllers\AuthenticateController::class, 'delToken']); + Route::post('token/add', [\App\Http\Controllers\TokenController::class, 'addToken']); + Route::post('token/del', [\App\Http\Controllers\TokenController::class, 'delToken']); + Route::post('token/get-plain', [\App\Http\Controllers\TokenController::class, 'getPlainText']); }); if (!isRunningInConsole()) {