Explorar o código

improved api endpoints

added includes and filters on most GET request routes
added discord id support for notifications
added register user endpoint
AVMG20 %!s(int64=3) %!d(string=hai) anos
pai
achega
1f8b34e684

+ 24 - 3
app/Http/Controllers/Api/NotificationController.php

@@ -6,11 +6,13 @@ use App\Http\Controllers\Controller;
 use App\Models\DiscordUser;
 use App\Models\User;
 use App\Notifications\DynamicNotification;
+use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Request;
 use Illuminate\Notifications\Messages\MailMessage;
 use Illuminate\Support\Facades\Notification;
 use Illuminate\Support\HtmlString;
+use Illuminate\Validation\ValidationException;
 use Spatie\ValidationRules\Rules\Delimited;
 
 class NotificationController extends Controller
@@ -61,13 +63,14 @@ class NotificationController extends Controller
         $data = $request->validate([
             "via" => ["required", new Delimited("in:mail,database")],
             "all" => "required_without:users|boolean",
-            "users" => ["required_without:all", new Delimited("exists:users,id")],
+            "users" => ["required_without:all"],
             "title" => "required|string|min:1",
             "content" => "required|string|min:1"
         ]);
         $via = explode(",", $data["via"]);
         $mail = null;
         $database = null;
+
         if (in_array("database", $via)) {
             $database = [
                 "title" => $data["title"],
@@ -79,10 +82,28 @@ class NotificationController extends Controller
                 ->subject($data["title"])
                 ->line(new HtmlString($data["content"]));
         }
+
         $all = $data["all"] ?? false;
-        $users = $all ? User::all() : User::whereIn("id", explode(",", $data["users"]))->get();
+        if ($all) {
+            $users = User::all();
+        } else {
+            $userIds = explode(",", $data["users"]);
+            $users = User::query()
+                    ->whereIn("id", $userIds)
+                    ->orWhereHas('discordUser' , function (Builder $builder) use ($userIds) {
+                        $builder->whereIn('id' , $userIds);
+                    })
+                    ->get();
+        }
+
+        if ($users->count() == 0) {
+            throw ValidationException::withMessages([
+                'users' => ['No users found!'],
+            ]);
+        }
+
         Notification::send($users, new DynamicNotification($via, $database, $mail));
-        return response()->json(["message" => "Notification successfully sent."]);
+        return response()->json(["message" => "Notification successfully sent.", 'user_count' => $users->count()]);
     }
 
     /**

+ 18 - 3
app/Http/Controllers/Api/ServerController.php

@@ -6,11 +6,16 @@ use App\Http\Controllers\Controller;
 use App\Models\Server;
 use Exception;
 use Illuminate\Contracts\Pagination\LengthAwarePaginator;
+use Illuminate\Database\Eloquent\Collection;
 use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Request;
+use Spatie\QueryBuilder\QueryBuilder;
 
 class ServerController extends Controller
 {
+    const ALLOWED_INCLUDES = ['product', 'user'];
+    const ALLOWED_FILTERS = ['name', 'suspended', 'identifier', 'pterodactyl_id', 'user_id' , 'product_id'];
+
     /**
      * Display a listing of the resource.
      *
@@ -19,19 +24,29 @@ class ServerController extends Controller
      */
     public function index(Request $request)
     {
-        return Server::with('product')->paginate($request->query('per_page') ?? 50);
+        $query = QueryBuilder::for(Server::class)
+            ->allowedIncludes(self::ALLOWED_INCLUDES)
+            ->allowedFilters(self::ALLOWED_FILTERS);
+
+        return $query->paginate($request->input('per_page') ?? 50);
     }
 
 
+
+
     /**
      * Display the specified resource.
      *
      * @param Server $server
-     * @return Server
+     * @return Server|Collection
      */
     public function show(Server $server)
     {
-        return $server->load('product');
+        $query = QueryBuilder::for(Server::class)
+            ->where('id' ,'=' , $server->id)
+            ->allowedIncludes(self::ALLOWED_INCLUDES);
+
+        return $query->get();
     }
 
 

+ 58 - 6
app/Http/Controllers/Api/UserController.php

@@ -2,8 +2,10 @@
 
 namespace App\Http\Controllers\Api;
 
+use App\Classes\Pterodactyl;
 use App\Events\UserUpdateCreditsEvent;
 use App\Http\Controllers\Controller;
+use App\Models\Configuration;
 use App\Models\DiscordUser;
 use App\Models\User;
 use Illuminate\Contracts\Foundation\Application;
@@ -11,16 +13,21 @@ use Illuminate\Contracts\Pagination\LengthAwarePaginator;
 use Illuminate\Contracts\Routing\ResponseFactory;
 use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Database\Eloquent\Collection;
+use Illuminate\Http\Client\RequestException;
 use Illuminate\Http\Request;
 use Illuminate\Http\Response;
+use Illuminate\Support\Facades\App;
+use Illuminate\Support\Facades\Hash;
+use Illuminate\Support\Str;
 use Illuminate\Validation\Rule;
 use Illuminate\Validation\ValidationException;
 use Spatie\QueryBuilder\QueryBuilder;
 
 class UserController extends Controller
 {
-    const ALLOWED_INCLUDES = ['servers', 'payments', 'vouchers', 'discordUser'];
-    const ALLOWED_FILTERS = ['email', 'pterodactyl_id', 'role', 'suspended'];
+    const ALLOWED_INCLUDES = ['servers', 'notifications', 'payments', 'vouchers', 'discordUser'];
+    const ALLOWED_FILTERS = ['name', 'server_limit', 'email', 'pterodactyl_id', 'role', 'suspended'];
+
     /**
      * Display a listing of the resource.
      *
@@ -46,14 +53,14 @@ class UserController extends Controller
     public function show(int $id)
     {
         $discordUser = DiscordUser::find($id);
-        $user =  $discordUser ? $discordUser->user : User::findOrFail($id);
+        $user = $discordUser ? $discordUser->user : User::findOrFail($id);
 
         $query = QueryBuilder::for($user)
             ->with('discordUser')
             ->allowedIncludes(self::ALLOWED_INCLUDES)
-            ->where('users.id' , '=' , $id)
-            ->orWhereHas('discordUser' , function (Builder $builder) use ($id) {
-                $builder->where('id' , '=' , $id);
+            ->where('users.id', '=', $id)
+            ->orWhereHas('discordUser', function (Builder $builder) use ($id) {
+                $builder->where('id', '=', $id);
             });
 
         return $query->get();
@@ -158,6 +165,51 @@ class UserController extends Controller
         return $user;
     }
 
+    /**
+     * @throws RequestException
+     */
+    public function store(Request $request)
+    {
+        $request->validate([
+            'name' => ['required', 'string', 'max:30', 'min:4', 'alpha_num', 'unique:users'],
+            'email' => ['required', 'string', 'email', 'max:64', 'unique:users'],
+            'password' => ['required', 'string', 'min:8', 'max:191'],
+        ]);
+
+        $user = User::create([
+            'name' => $request->input('name'),
+            'email' => $request->input('email'),
+            'credits' => Configuration::getValueByKey('INITIAL_CREDITS', 150),
+            'server_limit' => Configuration::getValueByKey('INITIAL_SERVER_LIMIT', 1),
+            'password' => Hash::make($request->input('password')),
+        ]);
+
+        $response = Pterodactyl::client()->post('/application/users', [
+            "external_id" => App::environment('local') ? Str::random(16) : (string)$user->id,
+            "username" => $user->name,
+            "email" => $user->email,
+            "first_name" => $user->name,
+            "last_name" => $user->name,
+            "password" => $request->input('password'),
+            "root_admin" => false,
+            "language" => "en"
+        ]);
+
+        if ($response->failed()) {
+            $user->delete();
+            throw ValidationException::withMessages([
+                'pterodactyl_error_message' => $response->toException()->getMessage(),
+                'pterodactyl_error_status' => $response->toException()->getCode()
+            ]);
+        }
+
+        $user->update([
+            'pterodactyl_id' => $response->json()['attributes']['id']
+        ]);
+
+        return $user;
+    }
+
     /**
      * Remove the specified resource from storage.
      *

+ 26 - 12
app/Http/Controllers/Api/VoucherController.php

@@ -3,22 +3,32 @@
 namespace App\Http\Controllers\Api;
 
 use App\Http\Controllers\Controller;
+use App\Models\Server;
 use App\Models\Voucher;
 use Illuminate\Contracts\Pagination\LengthAwarePaginator;
+use Illuminate\Database\Eloquent\Collection;
 use Illuminate\Http\Request;
 use Illuminate\Http\Response;
 use Illuminate\Validation\Rule;
+use Spatie\QueryBuilder\QueryBuilder;
 
 class VoucherController extends Controller
 {
+    const ALLOWED_INCLUDES = ['users'];
+    const ALLOWED_FILTERS = ['code', 'memo', 'credits', 'uses'];
+
     /**
      * Display a listing of the resource.
      *
-     * @return Response
+     * @return LengthAwarePaginator
      */
     public function index(Request $request)
     {
-        return Voucher::paginate($request->query('per_page') ?? 50);
+        $query = QueryBuilder::for(Voucher::class)
+            ->allowedIncludes(self::ALLOWED_INCLUDES)
+            ->allowedFilters(self::ALLOWED_FILTERS);
+
+        return $query->paginate($request->input('per_page') ?? 50);
     }
 
     /**
@@ -40,10 +50,10 @@ class VoucherController extends Controller
     public function store(Request $request)
     {
         $request->validate([
-            'memo'       => 'nullable|string|max:191',
-            'code'       => 'required|string|alpha_dash|max:36|min:4|unique:vouchers',
-            'uses'       => 'required|numeric|max:2147483647|min:1',
-            'credits'    => 'required|numeric|between:0,99999999',
+            'memo' => 'nullable|string|max:191',
+            'code' => 'required|string|alpha_dash|max:36|min:4|unique:vouchers',
+            'uses' => 'required|numeric|max:2147483647|min:1',
+            'credits' => 'required|numeric|between:0,99999999',
             'expires_at' => 'nullable|multiple_date_format:d-m-Y H:i:s,d-m-Y|after:now|before:10 years'
         ]);
 
@@ -54,11 +64,15 @@ class VoucherController extends Controller
      * Display the specified resource.
      *
      * @param int $id
-     * @return Response
+     * @return Collection|Voucher
      */
     public function show(int $id)
     {
-        return Voucher::findOrFail($id);
+        $query = QueryBuilder::for(Voucher::class)
+            ->where('id' ,'=' , $id)
+            ->allowedIncludes(self::ALLOWED_INCLUDES);
+
+        return $query->get();
     }
 
     /**
@@ -84,10 +98,10 @@ class VoucherController extends Controller
         $voucher = Voucher::findOrFail($id);
 
         $request->validate([
-            'memo'       => 'nullable|string|max:191',
-            'code'       => "required|string|alpha_dash|max:36|min:4|unique:vouchers,code,{$voucher->id}",
-            'uses'       => 'required|numeric|max:2147483647|min:1',
-            'credits'    => 'required|numeric|between:0,99999999',
+            'memo' => 'nullable|string|max:191',
+            'code' => "required|string|alpha_dash|max:36|min:4|unique:vouchers,code,{$voucher->id}",
+            'uses' => 'required|numeric|max:2147483647|min:1',
+            'credits' => 'required|numeric|between:0,99999999',
             'expires_at' => 'nullable|multiple_date_format:d-m-Y H:i:s,d-m-Y|after:now|before:10 years'
         ]);
 

+ 1 - 1
routes/api.php

@@ -20,7 +20,7 @@ use Illuminate\Support\Facades\Route;
 Route::middleware('api.token')->group(function () {
     Route::patch('/users/{user}/increment', [UserController::class, 'increment']);
     Route::patch('/users/{user}/decrement', [UserController::class, 'decrement']);
-    Route::resource('users', UserController::class)->except(['store', 'create']);
+    Route::resource('users', UserController::class)->except(['create']);
 
     Route::patch('/servers/{server}/suspend', [ServerController::class, 'suspend']);
     Route::patch('/servers/{server}/unsuspend', [ServerController::class, 'unSuspend']);