Skip to content
This repository was archived by the owner on Apr 26, 2025. It is now read-only.

Commit fe53676

Browse files
committed
fix: modfiy servers didn't decrease user's coins
1. Added filament for future usage. 2. Modify servers now require coins. 3. Handle Laravel Socialite's invalid state exception. 4. Users without avatars can now log in without error. 5. Coupon redeem webhook now shows the correct used times. 6. Added admin middleware for future usage. 7. Added log viewer for admins to view logs conveniently. 8. If actions that require coins don't run successfully, coins will be refunded. 9. Panel URL without path now works. 10. Added default user's coins configuration.
1 parent 1ed7416 commit fe53676

File tree

44 files changed

+3452
-869
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+3452
-869
lines changed

.env.example

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,6 @@ WEBHOOK_RESOURCE=https://discord.com/api/webhooks/0000000000000000000/cReAtEaChA
7474
WEBHOOK_EXCEPTION=https://discord.com/api/webhooks/0000000000000000000/cReAtEaChAnNeLaNdAwEbHoOk
7575
WEBHOOK_SERVER=https://discord.com/api/webhooks/0000000000000000000/cReAtEaChAnNeLaNdAwEbHoOk
7676

77-
PROXY_HTTPS=false
77+
PROXY_HTTPS=false
78+
79+
NO_AVATAR_ALTERNATIVE=https://cdn.discordapp.com/embed/avatars/0.png

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ yarn-error.log
1212
/.fleet
1313
/.idea
1414
/.vscode
15-
/public/build/assets/*
15+
/public/build
16+
/public/vendor
1617
/public/hot
1718
/caddy
1819
frankenphp

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,11 @@ Composer、PHP 8.1、Node.js、NPM、Nginx
1717
7. Run `npm run build`.
1818
8. Run `php artisan key:generate`.
1919
9. Configure your webserver.
20+
10. Change your Pterodactyl Panel's `app/Http/Controllers/Api/Application/Servers/ServerController.php`.
21+
22+
```
23+
$servers = QueryBuilder::for(Server::query())
24+
->allowedFilters(['id', 'uuid', 'uuidShort', 'name', AllowedFilter::exact('owner_id'), 'node_id', 'external_id'])
25+
->allowedSorts(['id', 'uuid', 'uuidShort', 'name', 'owner_id', 'node_id', 'status'])
26+
->paginate($perPage);
27+
```

app/Exceptions/Handler.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ class Handler extends ExceptionHandler
2626
*/
2727
public function register(): void
2828
{
29+
$this->renderable(function (\Laravel\Socialite\Two\InvalidStateException $e, $request) {
30+
return response()->view('errors.state', [], 400);
31+
});
2932
$this->renderable(function (ProxyException $e, Request $request) {
3033
return response()->view('errors.proxy', [], 403);
3134
});

app/Http/Controllers/Auth/SocialController.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,12 @@ public function handleDiscordCallback(): RedirectResponse
168168
$newUser->password = bcrypt(request($password));
169169
$database_id = Str::random(16);
170170
$newUser->database_id = $database_id;
171-
$newUser->avatar = $user->avatar;
171+
if ($user->avatar != null) {
172+
$newUser->avatar = $user->avatar;
173+
} else {
174+
$newUser->avatar = env('NO_AVATAR_ALTERNATIVE');
175+
}
176+
172177
$url = config('shdactyl.pterodactyl.url');
173178
$auth = config('shdactyl.pterodactyl.api_key');
174179
$res = Http::withHeaders([

app/Http/Controllers/Dashboard/DashboardController.php

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,8 @@ public function serverCreation(Request $request)
415415
]);
416416
return redirect('/dashboard/server/create')->with('success', '成功花費 $ ' . number_format($price, 2) . ' SDC 創建伺服器!');
417417
} else {
418+
$user->increment('coins', $price);
419+
$user->save();
418420
return redirect('/dashboard/server/create')->with('error', '創建伺服器時發生錯誤 ' . $res);
419421
}
420422
}
@@ -483,6 +485,8 @@ public function unsuspendServer(Request $request)
483485
]);
484486
return redirect('/dashboard/server/manage')->with('success', '成功花費 $ ' . number_format($price, 2) . ' SDC 續約伺服器');
485487
} else {
488+
$user->increment('coins', $price);
489+
$user->save();
486490
return redirect('/dashboard/server/manage')->with('error', '續約伺服器時發生錯誤 ' . $res);
487491
}
488492
} else {
@@ -572,7 +576,18 @@ public function modifyServer(Request $request)
572576
if ($total['ports'] - $resData['attributes']['feature_limits']['allocations'] + $data['ports'] > $user->ports) {
573577
return redirect('/dashboard/server/manage')->with('error', '你沒有那麼多 Ports 資源來編輯這個伺服器');
574578
}
575-
579+
$new_price = config('shdactyl.fee.create') * config('shdactyl.fee.node.' . $resData['attributes']['node']) * ((config('shdactyl.fee.resource.cpu') * $data['cpu']) + (config('shdactyl.fee.resource.ram') * $data['ram']) + (config('shdactyl.fee.resource.disk') * $data['disk']) + (config('shdactyl.fee.resource.databases') * $data['databases']) + (config('shdactyl.fee.resource.backups') * $data['backups']) + (config('shdactyl.fee.resource.ports') * $data['ports']));
580+
$old_price = config('shdactyl.fee.create') * config('shdactyl.fee.node.' . $resData['attributes']['node']) * ((config('shdactyl.fee.resource.cpu') * $resData['attributes']['limits']['cpu']) + (config('shdactyl.fee.resource.ram') * $resData['attributes']['limits']['memory']) + (config('shdactyl.fee.resource.disk') * $resData['attributes']['limits']['disk']) + (config('shdactyl.fee.resource.databases') * $resData['attributes']['feature_limits']['databases']) + (config('shdactyl.fee.resource.backups') * $resData['attributes']['feature_limits']['backups']) + (config('shdactyl.fee.resource.ports') * $resData['attributes']['feature_limits']['allocations']));
581+
if ($new_price < $old_price) {
582+
$price = 0;
583+
} else {
584+
$price = $new_price - $old_price;
585+
}
586+
if ($user->coins < $price) {
587+
return redirect('/dashboard/server/manage')->with('error', '你沒有足夠的代幣來編輯伺服器');
588+
}
589+
$user->decrement('coins', $price);
590+
$user->save();
576591
$response = Http::withHeaders([
577592
'Accept' => 'application/json',
578593
'Authorization' => $auth,
@@ -601,7 +616,7 @@ public function modifyServer(Request $request)
601616
[
602617
'title' => '[編輯伺服器]',
603618
'description' => '帳號:<@' . $user->discord_id . '> (' . $user->discord_id . ')\n' .
604-
'伺服器識別碼:' . $serverData['attributes']['identifier'] . '\n伺服器名稱:' . $serverData['attributes']['name'] . '\n節點識別碼:' . $serverData['attributes']['node'] . '\n處理器:' . $serverData['attributes']['limits']['cpu'] . '%\n記憶體:' . $serverData['attributes']['limits']['memory'] . ' MiB\n儲存空間:' . $serverData['attributes']['limits']['disk'] . ' MiB\n資料庫:' . $serverData['attributes']['feature_limits']['databases'] . ' 個\n額外端口:' . $serverData['attributes']['feature_limits']['allocations'] . ' 個\n備份欄位:' . $serverData['attributes']['feature_limits']['backups'] . '',
619+
'伺服器識別碼:' . $serverData['attributes']['identifier'] . '\n伺服器名稱:' . $serverData['attributes']['name'] . '\n節點識別碼:' . $serverData['attributes']['node'] . '\n花費:$ ' . number_format($price, 2) . ' SDC\n處理器:' . $serverData['attributes']['limits']['cpu'] . '%\n記憶體:' . $serverData['attributes']['limits']['memory'] . ' MiB\n儲存空間:' . $serverData['attributes']['limits']['disk'] . ' MiB\n資料庫:' . $serverData['attributes']['feature_limits']['databases'] . ' 個\n額外端口:' . $serverData['attributes']['feature_limits']['allocations'] . ' 個\n備份欄位:' . $serverData['attributes']['feature_limits']['backups'] . '',
605620
'color' => '#03cafc',
606621
'footer' => [
607622
'icon_url' => config('shdactyl.webhook.icon_url'),
@@ -614,8 +629,10 @@ public function modifyServer(Request $request)
614629
],
615630
]
616631
]);
617-
return redirect('/dashboard/server/manage')->with('success', '成功編輯伺服器');
632+
return redirect('/dashboard/server/manage')->with('success', '成功花費 $ ' . number_format($price, 2) . ' SDC 編輯伺服器');
618633
} else {
634+
$user->increment('coins', $price);
635+
$user->save();
619636
return redirect('/dashboard/server/manage')->with('error', '編輯伺服器時發生錯誤 ' . $response);
620637
}
621638
} else {
@@ -639,6 +656,11 @@ public function redeemCoupon(Request $request)
639656
->whereJsonContains('content->code', $realCoupon->code)->first();
640657

641658
if (!$history) {
659+
660+
$user->increment('coins', $realCoupon->coins);
661+
$user->save();
662+
$realCoupon->used_times++;
663+
$realCoupon->save();
642664
ActionLog::create([
643665
'action_type' => 'resource.coupon.redeem',
644666
'user_id' => $user->id,
@@ -664,10 +686,6 @@ public function redeemCoupon(Request $request)
664686
],
665687
]
666688
]);
667-
$user->increment('coins', $realCoupon->coins);
668-
$user->save();
669-
$realCoupon->used_times++;
670-
$realCoupon->save();
671689
return redirect('/dashboard/resource/coupon')->with('success', '成功兌換名為 "' . $realCoupon->name . '" 的代碼並獲得了 $ ' . $realCoupon->coins . ' SDC');
672690

673691
} else {
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace App\Http\Middleware;
4+
5+
use Closure;
6+
use Illuminate\Http\Request;
7+
use Symfony\Component\HttpFoundation\Response;
8+
use Illuminate\Support\Facades\Auth;
9+
use Illuminate\Support\Facades\Http;
10+
11+
class AdminAuthenticate
12+
{
13+
/**
14+
* Handle an incoming request.
15+
*
16+
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
17+
*/
18+
public function handle(Request $request, Closure $next): Response
19+
{
20+
$user = Auth::user();
21+
if (!$user) {
22+
return redirect()->route('login');
23+
}
24+
$url = config('shdactyl.pterodactyl.url');
25+
$auth = config('shdactyl.pterodactyl.api_key');
26+
$res = Http::withHeaders([
27+
'Accept' => 'application/json',
28+
'Authorization' => $auth,
29+
])->get($url . '/api/application/users/' . $user->panel_id);
30+
$data = json_decode($res, true);
31+
if ($data['attributes']['root_admin'] === false) {
32+
return redirect()->route('login');
33+
}
34+
return $next($request);
35+
}
36+
}

app/Providers/AppServiceProvider.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Illuminate\Support\ServiceProvider;
66
use Illuminate\Support\Facades\URL;
7+
use Opcodes\LogViewer\Facades\LogViewer;
78

89
class AppServiceProvider extends ServiceProvider
910
{
@@ -23,5 +24,9 @@ public function boot(): void
2324
if (env('PROXY_HTTPS') === true) {
2425
URL::forceScheme('https');
2526
}
27+
LogViewer::auth(function ($request) {
28+
return $request->user()
29+
&& in_array($request->user()->email, config('shdactyl.log_viewer.can_view'));
30+
});
2631
}
2732
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
namespace App\Providers\Filament;
4+
5+
use App\Http\Middleware\AdminAuthenticate;
6+
use Filament\Http\Middleware\Authenticate;
7+
use Filament\Http\Middleware\DisableBladeIconComponents;
8+
use Filament\Http\Middleware\DispatchServingFilamentEvent;
9+
use Filament\Pages;
10+
use Filament\Panel;
11+
use Filament\PanelProvider;
12+
use Filament\Support\Colors\Color;
13+
use Filament\Widgets;
14+
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
15+
use Illuminate\Cookie\Middleware\EncryptCookies;
16+
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
17+
use Illuminate\Routing\Middleware\SubstituteBindings;
18+
use Illuminate\Session\Middleware\AuthenticateSession;
19+
use Illuminate\Session\Middleware\StartSession;
20+
use Illuminate\View\Middleware\ShareErrorsFromSession;
21+
22+
class AdminPanelProvider extends PanelProvider
23+
{
24+
public function panel(Panel $panel): Panel
25+
{
26+
return $panel
27+
->default()
28+
->id('admin')
29+
->path('admin')
30+
->login()
31+
->colors([
32+
'primary' => Color::Amber,
33+
])
34+
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
35+
->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
36+
->pages([
37+
Pages\Dashboard::class,
38+
])
39+
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
40+
->widgets([
41+
Widgets\AccountWidget::class,
42+
Widgets\FilamentInfoWidget::class,
43+
])
44+
->middleware([
45+
EncryptCookies::class,
46+
AddQueuedCookiesToResponse::class,
47+
StartSession::class,
48+
AuthenticateSession::class,
49+
ShareErrorsFromSession::class,
50+
VerifyCsrfToken::class,
51+
SubstituteBindings::class,
52+
DisableBladeIconComponents::class,
53+
DispatchServingFilamentEvent::class,
54+
AdminAuthenticate::class,
55+
])
56+
->authMiddleware([
57+
Authenticate::class,
58+
]);
59+
}
60+
}

composer.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22
"name": "laravel/laravel",
33
"type": "project",
44
"description": "The skeleton application for the Laravel framework.",
5-
"keywords": ["laravel", "framework"],
5+
"keywords": [
6+
"laravel",
7+
"framework"
8+
],
69
"license": "MIT",
710
"require": {
811
"php": "^8.1",
912
"beyondcode/laravel-websockets": "^1.14",
13+
"filament/filament": "3.2",
1014
"guzzlehttp/guzzle": "^7.8",
1115
"inertiajs/inertia-laravel": "^0.6.8",
1216
"laravel/framework": "^10.10",
@@ -18,6 +22,7 @@
1822
"laravel/socialite": "^5.11",
1923
"laravel/tinker": "^2.8",
2024
"nwidart/laravel-modules": "^10.0",
25+
"opcodesio/log-viewer": "^3.4",
2126
"pusher/pusher-php-server": "^7.2",
2227
"realrashid/sweet-alert": "^7.1",
2328
"socialiteproviders/discord": "^4.2",
@@ -52,7 +57,8 @@
5257
"scripts": {
5358
"post-autoload-dump": [
5459
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
55-
"@php artisan package:discover --ansi"
60+
"@php artisan package:discover --ansi",
61+
"@php artisan filament:upgrade"
5662
],
5763
"post-update-cmd": [
5864
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"

0 commit comments

Comments
 (0)