mirror of
https://github.com/lkddi/nexusphp.git
synced 2026-04-03 14:10:57 +08:00
finish oauth provider + docker service wait for MySQL
This commit is contained in:
@@ -53,7 +53,17 @@ fi
|
||||
echo_info "USE_HTTPS: $USE_HTTPS"
|
||||
|
||||
# 组合子域名变量
|
||||
export PHPMYADMIN_SERVER_NAME="phpmyadmin.${NP_DOMAIN}"
|
||||
dot_count_tmp="${NP_DOMAIN//[^.]/}"
|
||||
dot_count="${#dot_count_tmp}"
|
||||
PHPMYADMIN="phpmyadmin"
|
||||
if [ "$dot_count" -eq 1 ]; then
|
||||
PHPMYADMIN="${PHPMYADMIN}."
|
||||
else
|
||||
PHPMYADMIN="${PHPMYADMIN}-"
|
||||
fi
|
||||
export PHPMYADMIN_SERVER_NAME="${PHPMYADMIN}${NP_DOMAIN}"
|
||||
|
||||
echo_info "PHPMYADMIN_SERVER_NAME: ${PHPMYADMIN_SERVER_NAME}"
|
||||
|
||||
# 生成配置
|
||||
APP_CONF="/etc/nginx/conf.d/app.conf"
|
||||
|
||||
@@ -36,6 +36,18 @@ VENDOR_DIR="${ROOT_PATH}/vendor"
|
||||
|
||||
chown -R www-data:www-data $ROOT_PATH
|
||||
|
||||
until nc -z mysql 3306; do
|
||||
echo_info "Waiting for MySQL to be ready..."
|
||||
sleep 2
|
||||
done
|
||||
echo_success "MySQL is ready."
|
||||
|
||||
until nc -z redis 6379; do
|
||||
echo_info "Waiting for Redis to be ready..."
|
||||
sleep 2
|
||||
done
|
||||
echo_success "Redis is ready."
|
||||
|
||||
if [ "$SERVICE_NAME" = "php" ]; then
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
echo_info ".env file: $ENV_FILE not exists, copy $SOURCE_DIR to $TARGET_DIR ..."
|
||||
|
||||
@@ -45,20 +45,23 @@ class Handler extends ExceptionHandler
|
||||
if (app()->runningInConsole()) {
|
||||
return;
|
||||
}
|
||||
$this->reportable(function (InsufficientPermissionException $e) {
|
||||
if (request()->expectsJson()) {
|
||||
return response()->json(fail($e->getMessage(), request()->all()), 403);
|
||||
$request = request();
|
||||
$this->reportable(function (InsufficientPermissionException $e) use ($request) {
|
||||
if ($request->expectsJson()) {
|
||||
return response()->json(fail($e->getMessage(), $request->all()), 403);
|
||||
} else {
|
||||
return abort(403);
|
||||
}
|
||||
});
|
||||
$this->renderable(function (PassportAuthenticationException $e) {
|
||||
$request = request();
|
||||
$this->renderable(function (PassportAuthenticationException $e) use ($request) {
|
||||
return response()->redirectTo(sprintf("%s/login.php?returnto=%s", $request->getSchemeAndHttpHost(), urlencode($request->fullUrl())));
|
||||
});
|
||||
|
||||
//Other Only handle in json request
|
||||
if (!request()->expectsJson()) {
|
||||
if (!$request->expectsJson()) {
|
||||
$this->renderable(function (NexusException $e) use ($request) {
|
||||
return redirect(url('/error?error=' . urlencode($e->getMessage())));
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Filament\Resources\Oauth;
|
||||
use App\Filament\Resources\Oauth\ProviderResource\Pages;
|
||||
use App\Filament\Resources\Oauth\ProviderResource\RelationManagers;
|
||||
use App\Models\OauthProvider;
|
||||
use App\Models\User;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\Resource;
|
||||
@@ -71,6 +72,14 @@ class ProviderResource extends Resource
|
||||
Forms\Components\TextInput::make('email_claim')
|
||||
->label(__('oauth.email_claim'))
|
||||
,
|
||||
Forms\Components\TextInput::make('level_claim')
|
||||
->label(__('oauth.level_claim'))
|
||||
,
|
||||
Forms\Components\TextInput::make('level_limit')
|
||||
->numeric()
|
||||
->label(__('oauth.level_limit'))
|
||||
->helperText(__('oauth.level_limit_help'))
|
||||
,
|
||||
Forms\Components\TextInput::make('priority')
|
||||
->label(__('label.priority'))
|
||||
->default(0)
|
||||
@@ -88,6 +97,7 @@ class ProviderResource extends Resource
|
||||
return $table
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('id'),
|
||||
Tables\Columns\TextColumn::make('name')->label(__('label.name')),
|
||||
Tables\Columns\TextColumn::make('client_id')->label(__('oauth.client_id')),
|
||||
Tables\Columns\TextColumn::make('client_secret')->label(__('oauth.secret')),
|
||||
Tables\Columns\TextColumn::make('authorization_endpoint_url')->label(__('oauth.authorization_endpoint_url')),
|
||||
@@ -97,6 +107,7 @@ class ProviderResource extends Resource
|
||||
,
|
||||
Tables\Columns\TextColumn::make('priority')->label(__('label.priority')),
|
||||
Tables\Columns\TextColumn::make('updated_at')->label(__('label.updated_at')),
|
||||
Tables\Columns\TextColumn::make('level_limit')->label(__('oauth.level_limit')),
|
||||
Tables\Columns\IconColumn::make('enabled')->boolean()->label(__('label.enabled')),
|
||||
])
|
||||
->defaultSort('priority', 'desc')
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Exceptions\NexusException;
|
||||
use App\Http\Resources\UserResource;
|
||||
use App\Models\OauthClient;
|
||||
use App\Models\OauthProvider;
|
||||
use App\Models\SocialAccount;
|
||||
use App\Models\User;
|
||||
use App\Repositories\UserRepository;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Str;
|
||||
@@ -26,7 +30,7 @@ class OauthController extends Controller
|
||||
|
||||
$query = http_build_query([
|
||||
'client_id' => $provider->client_id,
|
||||
'redirect_uri' => $provider->redirect,
|
||||
'redirect_uri' => $this->getRedirectUri($provider),
|
||||
'response_type' => 'code',
|
||||
'scope' => '',
|
||||
'state' => $state,
|
||||
@@ -42,6 +46,11 @@ class OauthController extends Controller
|
||||
|
||||
}
|
||||
|
||||
private function getRedirectUri(OauthProvider $provider): string
|
||||
{
|
||||
return sprintf("%s/oauth/callback/%s", getSchemeAndHttpHost(), $provider->uuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* authorization server redirect to this url with auth code after user authorized
|
||||
* and then use auth code to request to authorization server token endpoint url to get access token
|
||||
@@ -63,23 +72,98 @@ class OauthController extends Controller
|
||||
|
||||
$provider = OauthProvider::query()->where('uuid', $uuid)->firstOrFail();
|
||||
|
||||
$response = Http::asForm()->post($provider->token_endpoint_url, [
|
||||
$params = [
|
||||
'grant_type' => 'authorization_code',
|
||||
'client_id' => $provider->client_id,
|
||||
'client_secret' => $provider->client_secret,
|
||||
// 'redirect_uri' => url("oauth/login"),
|
||||
'redirect_uri' => $this->getRedirectUri($provider),
|
||||
'code' => $request->code,
|
||||
]);
|
||||
];
|
||||
$response = Http::asForm()->post($provider->token_endpoint_url, $params);
|
||||
$tokenInfo = $response->json();
|
||||
do_log("tokenInfo: " . $response->body());
|
||||
if (empty($tokenInfo['access_token'])) {
|
||||
do_log("Get tokenInfo with: " . json_encode($params) . " error: " . $response->body(), 'error');
|
||||
throw new NexusException(nexus_trans('oauth.get_access_token_error', ['error' => $tokenInfo['error'] ?? '']));
|
||||
}
|
||||
//use token get user-info
|
||||
$response = Http::withToken($tokenInfo['access_token'])->get($provider->user_info_endpoint_url);
|
||||
$userInfo = $response->json();
|
||||
do_log("userInfo: " . $response->body());
|
||||
$userId = data_get($userInfo, $provider->id_claim);
|
||||
$username = data_get($userInfo, $provider->username_claim);
|
||||
$email = data_get($userInfo, $provider->email_claim);
|
||||
dd($userInfo, $userId, $username, $email);
|
||||
$providerUserId = data_get($userInfo, $provider->id_claim);
|
||||
if (empty($providerUserId)) {
|
||||
throw new NexusException(nexus_trans('oauth.get_provider_user_id_error', ['id_claim' => $provider->id_claim]));
|
||||
}
|
||||
$providerUsername = data_get($userInfo, $provider->username_claim);
|
||||
$providerEmail = data_get($userInfo, $provider->email_claim);
|
||||
$providerLevel = data_get($userInfo, $provider->level_claim);
|
||||
$homeUrl = getSchemeAndHttpHost() . "/index.php";
|
||||
$socialAccount = SocialAccount::query()
|
||||
->where('provider_id', $provider->id)
|
||||
->where('provider_user_id', $providerUserId)
|
||||
->first();
|
||||
if ($socialAccount) {
|
||||
//already bind, login directly
|
||||
$authUser = $socialAccount->user;
|
||||
logincookie($authUser->id, $authUser->auth_key);
|
||||
return redirect($homeUrl);
|
||||
}
|
||||
$minLevel = $provider->level_limit;
|
||||
if ($minLevel) {
|
||||
if (!$providerLevel) {
|
||||
throw new NexusException(nexus_trans('oauth.get_provider_level_error', ['level_claim' => $provider->level_claim]));
|
||||
}
|
||||
if ($providerLevel < $minLevel) {
|
||||
throw new NexusException(nexus_trans("oauth.provider_level_not_allowed", ['level_limit' => $provider->level_limit]));
|
||||
}
|
||||
}
|
||||
if ($providerEmail) {
|
||||
$sameEmailUser = User::query()->where('email', $providerEmail)->first();
|
||||
if ($sameEmailUser) {
|
||||
//login to bind is better, not implement this time
|
||||
throw new NexusException(nexus_trans('oauth.provider_email_already_exists', ['email' => $providerEmail]));
|
||||
}
|
||||
}
|
||||
$newUser = $this->createUser($providerUsername, $providerEmail);
|
||||
$socialAccountData = [
|
||||
'user_id' => $newUser->id,
|
||||
'provider_id' => $provider->id,
|
||||
'provider_user_id' => $providerUserId,
|
||||
'provider_username' => $providerUsername,
|
||||
'provider_email' => $providerEmail,
|
||||
];
|
||||
SocialAccount::query()->create($socialAccountData);
|
||||
do_log(sprintf("newUser: %s, socialAccount: %s", json_encode($newUser), json_encode($socialAccountData)));
|
||||
logincookie($newUser->id, $newUser->auth_key);
|
||||
return redirect($homeUrl);
|
||||
}
|
||||
|
||||
private function createUser($username, $email): User
|
||||
{
|
||||
if ($username) {
|
||||
if (User::query()->where('username', $username)->exists()) {
|
||||
//already in use
|
||||
$username .= Str::random(2);
|
||||
}
|
||||
} else {
|
||||
$username = $email;
|
||||
}
|
||||
$password = Str::random(10);
|
||||
$userData = [
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'password_confirmation' => $password,
|
||||
];
|
||||
$userRep = new UserRepository();
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$userData['username'] = $username;
|
||||
try {
|
||||
return $userRep->store($userData);
|
||||
} catch (\Throwable $e) {
|
||||
do_log($e->getMessage(), "error");
|
||||
}
|
||||
$username = Str::random(2) . $username;
|
||||
}
|
||||
throw new NexusException("Unable to create user");
|
||||
}
|
||||
|
||||
public function debug(Request $request)
|
||||
|
||||
@@ -37,4 +37,9 @@ class ToolController extends Controller
|
||||
return $this->success($resource);
|
||||
}
|
||||
|
||||
public function error(Request $request)
|
||||
{
|
||||
return view('error', ['error' => $request->query('error')]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ class Locale
|
||||
/** @var Response $response */
|
||||
$response = $next($request);
|
||||
if ($response instanceof Response || $response instanceof JsonResponse) {
|
||||
$response->header('X-Request-Id', nexus()->getRequestId())->header('X-NexusPHP-Version', VERSION_NUMBER);
|
||||
$response->header('X-Request-Id', nexus()->getRequestId())->header('X-Nexusphp-Version', VERSION_NUMBER);
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ class UserResource extends JsonResource
|
||||
'added_human' => gettime($this->added),
|
||||
'last_access' => format_datetime($this->last_access),
|
||||
'last_access_human' => gettime($this->last_access),
|
||||
'last_login' => format_datetime($this->last_login),
|
||||
'last_login_human' => gettime($this->last_login),
|
||||
'class' => $this->class,
|
||||
'class_text' => $this->class_text,
|
||||
'avatar' => $this->avatar,
|
||||
@@ -44,6 +46,7 @@ class UserResource extends JsonResource
|
||||
'seedtime_text' => mkprettytime($this->seedtime),
|
||||
'leechtime' => $this->leechtime,
|
||||
'leechtime_text' => mkprettytime($this->leechtime),
|
||||
'share_ratio' => get_ratio($this->id),
|
||||
'inviter' => new UserResource($this->whenLoaded('inviter')),
|
||||
'valid_medals' => MedalResource::collection($this->whenLoaded('valid_medals')),
|
||||
];
|
||||
|
||||
@@ -10,6 +10,7 @@ class OauthProvider extends NexusModel
|
||||
protected $fillable = [
|
||||
'uuid', 'name', 'client_id', 'client_secret', 'authorization_endpoint_url', 'token_endpoint_url',
|
||||
'user_info_endpoint_url', 'id_claim', 'username_claim', 'email_claim', 'enabled', 'priority',
|
||||
'level_claim', 'level_limit',
|
||||
];
|
||||
|
||||
public $timestamps = true;
|
||||
|
||||
22
app/Models/SocialAccount.php
Normal file
22
app/Models/SocialAccount.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Laravel\Passport\Client;
|
||||
use Ramsey\Uuid;
|
||||
use function Ramsey\Uuid;
|
||||
|
||||
class SocialAccount extends NexusModel
|
||||
{
|
||||
protected $fillable = [
|
||||
'user_id', 'provider_id', 'provider_user_id', 'provider_username', 'provider_email',
|
||||
];
|
||||
|
||||
public $timestamps = true;
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'user_id');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,6 +23,8 @@ return new class extends Migration
|
||||
$table->string('id_claim');
|
||||
$table->string('username_claim')->nullable();
|
||||
$table->string('email_claim')->nullable();
|
||||
$table->string('level_claim')->nullable();
|
||||
$table->string('level_limit')->nullable();
|
||||
$table->boolean('enabled');
|
||||
$table->integer('priority');
|
||||
$table->timestamps();
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('social_accounts', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->bigInteger('user_id');
|
||||
$table->bigInteger('provider_id');
|
||||
$table->string('provider_user_id');
|
||||
$table->string('provider_email');
|
||||
$table->string('provider_username');
|
||||
$table->timestamps();
|
||||
$table->unique(['user_id', 'provider_id']);
|
||||
$table->unique(['provider_id', 'provider_user_id']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('social_accounts');
|
||||
}
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
defined('VERSION_NUMBER') || define('VERSION_NUMBER', '1.9.0');
|
||||
defined('RELEASE_DATE') || define('RELEASE_DATE', '2025-04-27');
|
||||
defined('RELEASE_DATE') || define('RELEASE_DATE', '2025-05-01');
|
||||
defined('IN_TRACKER') || define('IN_TRACKER', false);
|
||||
defined('PROJECTNAME') || define("PROJECTNAME","NexusPHP");
|
||||
defined('NEXUSPHPURL') || define("NEXUSPHPURL","https://nexusphp.org");
|
||||
|
||||
@@ -4890,7 +4890,6 @@ function get_searchbox_value($mode = 1, $item = 'showsubcat'){
|
||||
}
|
||||
|
||||
function get_ratio($userid, $html = true){
|
||||
global $lang_functions;
|
||||
$row = get_user_row($userid);
|
||||
$uped = $row['uploaded'];
|
||||
$downed = $row['downloaded'];
|
||||
@@ -4905,7 +4904,7 @@ function get_ratio($userid, $html = true){
|
||||
$ratio = "<font color=\"".$color."\">".$ratio."</font>";
|
||||
}
|
||||
elseif ($uped > 0)
|
||||
$ratio = $lang_functions['text_inf'];
|
||||
$ratio = nexus_trans("label.infinite");
|
||||
else
|
||||
$ratio = "---";
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ use App\Models\Exam;
|
||||
use App\Models\ExamUser;
|
||||
use App\Models\HitAndRun;
|
||||
use App\Models\Icon;
|
||||
use App\Models\Language;
|
||||
use App\Models\SearchBox;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Tag;
|
||||
@@ -342,6 +343,7 @@ class Update extends Install
|
||||
$this->runMigrate("database/migrations/2025_01_08_133552_create_torrent_extra_table.php");
|
||||
Artisan::call("upgrade:migrate_torrents_table_text_column");
|
||||
}
|
||||
Language::updateTransStatus();
|
||||
}
|
||||
|
||||
public function runExtraMigrate()
|
||||
|
||||
@@ -50,7 +50,13 @@ if (\App\Models\User::query()->where("id", $row['owner'])->exists()) {
|
||||
$dt = sqlesc(date("Y-m-d H:i:s"));
|
||||
$subject = nexus_trans("torrent.msg_torrent_deleted", [], get_user_locale($row['owner']));
|
||||
$msg = nexus_trans("torrent.msg_the_torrent_you_uploaded", [], get_user_locale($row['owner']));
|
||||
sql_query("INSERT INTO messages (sender, receiver, subject, added, msg) VALUES(0, $row[owner], $subject, $dt, $msg)") or sqlerr(__FILE__, __LINE__);
|
||||
\App\Models\Message::add([
|
||||
'sender' => 0,
|
||||
'receiver' => $row['owner'],
|
||||
'subject' => $subject,
|
||||
'msg' => $msg,
|
||||
'added' => $dt,
|
||||
]);
|
||||
}
|
||||
}
|
||||
header("Location: torrents.php");
|
||||
|
||||
@@ -229,7 +229,6 @@ $reply = " [ <a href=\"sendmessage.php?receiver=" . $message['sender'] . "&reply
|
||||
}
|
||||
}
|
||||
$body = format_comment($message['msg'], true);
|
||||
//$body = htmlspecialchars_decode($body);
|
||||
$added = $message['added'];
|
||||
if ($message['sender'] == $CURUSER['id'])
|
||||
{
|
||||
|
||||
@@ -17,12 +17,12 @@ return [
|
||||
|
||||
'msg_title' => 'Claim settle result last month(:month):',
|
||||
'msg_subject' => ':month claim settlement',
|
||||
'claim_total' => 'Claim torrent total: <b>:total</b>',
|
||||
'claim_reached_counts' => 'Reached torrent counts: <b>:counts</b>',
|
||||
'claim_reached_summary' => 'Reached torrent get bonus per hour: <b>:bonus_per_hour</b>, seed time average(hour): <b>:hours</b>, get bonus total: <b>:bonus_total</b>',
|
||||
'claim_unreached_remain_counts' => 'Unreached torrent remain counts: <b>:counts</b>',
|
||||
'claim_unreached_remove_counts' => 'Unreached torrent remove counts: <b>:counts</b>',
|
||||
'claim_unreached_summary' => 'Deduct bonus every unreached torrent:<b>:deduct_per_torrent</b>, total deduct: <b>:deduct_total</b>',
|
||||
'claim_total' => 'Claim torrent total: [b]:total[/b]',
|
||||
'claim_reached_counts' => 'Reached torrent counts: [b]:counts[/b]',
|
||||
'claim_reached_summary' => 'Reached torrent get bonus per hour: [b]:bonus_per_hour[/b], seed time average(hour): [b]:hours[/b], get bonus total: [b]:bonus_total[/b]',
|
||||
'claim_unreached_remain_counts' => 'Unreached torrent remain counts: [b]:counts[/b]',
|
||||
'claim_unreached_remove_counts' => 'Unreached torrent remove counts: [b]:counts[/b]',
|
||||
'claim_unreached_summary' => 'Deduct bonus every unreached torrent:[b]:deduct_per_torrent[/b], total deduct: [b]:deduct_total[/b]',
|
||||
|
||||
'confirm_give_up' => 'Are you sure you want to give up claiming this torrent?',
|
||||
'add_claim' => 'Claim',
|
||||
|
||||
@@ -34,7 +34,7 @@ return [
|
||||
'cancel' => 'Cancel',
|
||||
'reset' => 'Reset',
|
||||
'anonymous' => 'Anonymous',
|
||||
'infinite' => 'Infinite',
|
||||
'infinite' => 'Inf.',
|
||||
'save' => 'Save',
|
||||
'country' => 'Country',
|
||||
'city' => 'City',
|
||||
|
||||
@@ -18,4 +18,12 @@ return [
|
||||
'id_claim' => 'ID claim',
|
||||
'username_claim' => 'username claim',
|
||||
'email_claim' => 'email claim',
|
||||
'level_claim' => 'Level field name',
|
||||
'level_limit' => 'Level limit',
|
||||
'level_limit_help' => 'Allow only users not below this level to log in',
|
||||
'get_access_token_error' => 'Getting access token error: :error',
|
||||
'get _provider_user_id_error' => 'Unable to get user ID via field :id_claim',
|
||||
'get_provider_level_error' => 'Unable to get user level via field :level_claim',
|
||||
'provider_level_not_ allowed' => 'Only users with level :level_limit or above are allowed to log in',
|
||||
'provider_email_already_exists' => 'Email: :email is already in use',
|
||||
];
|
||||
|
||||
@@ -17,12 +17,12 @@ return [
|
||||
|
||||
'msg_title' => '上个月(:month)认领结算如下:',
|
||||
'msg_subject' => ':month 认领结算',
|
||||
'claim_total' => '认领种子数:<b>:total</b>',
|
||||
'claim_reached_counts' => '达标数:<b>:counts</b>',
|
||||
'claim_reached_summary' => '达标种子数每小时魔力:<b>:bonus_per_hour</b>, 平均做种小时数:<b>:hours</b>, 获得魔力:<b>:bonus_total</b>',
|
||||
'claim_unreached_remain_counts' => '未达标保留数:<b>:counts</b>',
|
||||
'claim_unreached_remove_counts' => '未达标删除数:<b>:counts</b>',
|
||||
'claim_unreached_summary' => '未达标每个种子扣除魔力:<b>:deduct_per_torrent</b>,总扣除魔力:<b>:deduct_total</b>',
|
||||
'claim_total' => '认领种子数:[b]:total[/b]',
|
||||
'claim_reached_counts' => '达标数:[b]:counts[/b]',
|
||||
'claim_reached_summary' => '达标种子数每小时魔力:[b]:bonus_per_hour[/b], 平均做种小时数:[b]:hours[/b], 获得魔力:[b]:bonus_total[/b]',
|
||||
'claim_unreached_remain_counts' => '未达标保留数:[b]:counts[/b]',
|
||||
'claim_unreached_remove_counts' => '未达标删除数:[b]:counts[/b]',
|
||||
'claim_unreached_summary' => '未达标每个种子扣除魔力:[b]:deduct_per_torrent[/b],总扣除魔力:[b]:deduct_total[/b]',
|
||||
|
||||
'confirm_give_up' => '确定要放弃认领此种子吗?',
|
||||
'add_claim' => '认领',
|
||||
|
||||
@@ -18,4 +18,12 @@ return [
|
||||
'id_claim' => 'ID 字段名',
|
||||
'username_claim' => '用户名字段名',
|
||||
'email_claim' => '邮箱字段名',
|
||||
'level_claim' => '等级字段名',
|
||||
'level_limit' => '等级限制',
|
||||
'level_limit_help' => '只允许不低于此等级的用户登录',
|
||||
'get_access_token_error' => '获取访问令牌错误::error',
|
||||
'get_provider_user_id_error' => '无法通过字段 :id_claim 获取用户 ID',
|
||||
'get_provider_level_error' => '无法通过字段 :level_claim 获取用户等级',
|
||||
'provider_level_not_allowed' => '仅允许 :level_limit 或以上等级的用户登录',
|
||||
'provider_email_already_exists' => '邮箱::email 已经被使用',
|
||||
];
|
||||
|
||||
@@ -16,12 +16,12 @@ return [
|
||||
|
||||
'msg_title' => '上個月(:month)認領結算如下:',
|
||||
'msg_subject' => ':month 認領結算',
|
||||
'claim_total' => '認領種子數:<b>:total</b>',
|
||||
'claim_reached_counts' => '達標數:<b>:counts</b>',
|
||||
'claim_reached_summary' => '達標種子數每小時魔力:<b>:bonus_per_hour</b>, 平均做種小時數:<b>:hours</b>, 獲得魔力:<b>:bonus_total</b>',
|
||||
'claim_unreached_remain_counts' => '未達標保留數:<b>:counts</b>',
|
||||
'claim_unreached_remove_counts' => '未達標刪除數:<b>:counts</b>',
|
||||
'claim_unreached_summary' => '未達標每個種子扣除魔力:<b>:deduct_per_torrent</b>,總扣除魔力:<b>:deduct_total</b>',
|
||||
'claim_total' => '認領種子數:[b]:total[/b]',
|
||||
'claim_reached_counts' => '達標數:[b]:counts[/b]',
|
||||
'claim_reached_summary' => '達標種子數每小時魔力:[b]:bonus_per_hour[/b], 平均做種小時數:[b]:hours[/b], 獲得魔力:[b]:bonus_total[/b]',
|
||||
'claim_unreached_remain_counts' => '未達標保留數:[b]:counts[/b]',
|
||||
'claim_unreached_remove_counts' => '未達標刪除數:[b]:counts[/b]',
|
||||
'claim_unreached_summary' => '未達標每個種子扣除魔力:[b]:deduct_per_torrent[/b],總扣除魔力:[b]:deduct_total[/b]',
|
||||
|
||||
'confirm_give_up' => '確定要放棄認領此種子嗎?',
|
||||
'add_claim' => '認領',
|
||||
|
||||
@@ -18,4 +18,12 @@ return [
|
||||
'id_claim' => 'ID 字段名',
|
||||
'username_claim' => '用戶名字段名',
|
||||
'email_claim' => '郵箱字段名',
|
||||
'level_claim' => '等級字段名',
|
||||
'level_limit' => '等級限製',
|
||||
'level_limit_help' => '只允許不低於此等級的用戶登錄',
|
||||
'get_access_token_error' => '獲取訪問令牌錯誤::error',
|
||||
'get_provider_user_id_error' => '無法通過字段 :id_claim 獲取用戶 ID',
|
||||
'get_provider_level_error' => '無法通過字段 :level_claim 獲取用戶等級',
|
||||
'provider_level_not_allowed' => '僅允許 :level_limit 或以上等級的用戶登錄',
|
||||
'provider_email_already_exists' => '郵箱::email 已經被使用',
|
||||
];
|
||||
|
||||
190
resources/views/error.blade.php
Normal file
190
resources/views/error.blade.php
Normal file
@@ -0,0 +1,190 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Error</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "PingFang SC", "Helvetica Neue", Helvetica, "Microsoft YaHei", Arial, sans-serif;
|
||||
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: url('/pic/oauth2-authorize-bg.jpg');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
opacity: 0.2;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
max-width: 800px;
|
||||
padding: 3rem;
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
font-size: 5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
color: #5d76cb;
|
||||
}
|
||||
|
||||
.error-title {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 1.5rem;
|
||||
color: #333;
|
||||
background: linear-gradient(135deg, #4568dc 0%, #b06ab3 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
font-size: 1.2rem;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 2.5rem;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.action-links {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 0.8rem 1.8rem;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
border-radius: 8px;
|
||||
text-decoration: none;
|
||||
transition: all 0.3s ease;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, #4568dc 0%, #b06ab3 100%);
|
||||
color: #fff;
|
||||
box-shadow: 0 4px 15px rgba(69, 104, 220, 0.3);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 20px rgba(69, 104, 220, 0.5);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: transparent;
|
||||
color: #555;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 2rem;
|
||||
color: #777;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.ornament {
|
||||
position: absolute;
|
||||
background: linear-gradient(135deg, #4568dc 0%, #b06ab3 100%);
|
||||
border-radius: 50%;
|
||||
filter: blur(70px);
|
||||
opacity: 0.15;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.ornament-1 {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
top: -150px;
|
||||
left: -150px;
|
||||
}
|
||||
|
||||
.ornament-2 {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
bottom: -100px;
|
||||
right: -100px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
width: 90%;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.error-title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.action-links {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="background"></div>
|
||||
<div class="ornament ornament-1"></div>
|
||||
<div class="ornament ornament-2"></div>
|
||||
|
||||
<div class="container">
|
||||
<div class="error-icon">
|
||||
<i class="fas fa-exclamation-circle"></i>
|
||||
</div>
|
||||
<h1 class="error-title">Error</h1>
|
||||
<p class="error-message">
|
||||
{{ $error }}
|
||||
</p>
|
||||
|
||||
<div class="action-links">
|
||||
<a href="/" class="btn btn-primary">Go home</a>
|
||||
<a href="javascript:history.back()" class="btn btn-secondary">Go back</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -17,6 +17,8 @@ Route::get('/', function () {
|
||||
return redirect('index.php');
|
||||
});
|
||||
|
||||
Route::get("/error", [\App\Http\Controllers\ToolController::class, "error"]);
|
||||
|
||||
Route::group(['prefix' => 'web', 'middleware' => ['auth.nexus:nexus-web']], function () {
|
||||
Route::get('torrent-approval-page', [\App\Http\Controllers\TorrentController::class, 'approvalPage']);
|
||||
Route::get('torrent-approval-logs', [\App\Http\Controllers\TorrentController::class, 'approvalLogs']);
|
||||
|
||||
Reference in New Issue
Block a user