Basic API

Includes routes for login, logout, listing users, listing mods, listing a user, and listing a mod. Very basic, just laying out the structure.

https://www.postman.com/refringe/workspace/spt-forge
This commit is contained in:
Refringe 2024-06-27 16:58:11 -04:00
parent 251b1ffd14
commit 0e3783555e
Signed by: Refringe
SSH Key Fingerprint: SHA256:t865XsQpfTeqPRBMN2G6+N8wlDjkgUCZF3WGW6O9N/k
14 changed files with 437 additions and 4 deletions

View File

@ -0,0 +1,47 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Requests\Api\LoginUserRequest;
use App\Models\User;
use App\Traits\ApiResponses;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class AuthController extends Controller
{
use ApiResponses;
public function login(LoginUserRequest $request): JsonResponse
{
$request->validated($request->all());
if (! Auth::attempt($request->only('email', 'password'))) {
return $this->error(__('Invalid credentials'), 401);
}
$user = User::firstWhere('email', $request->email);
$tokenName = $request->token_name ?? __('Dynamic API Token');
return $this->success(__('Authenticated'), [
// Only allowing the 'read' scope to be dynamically created. Can revisit later when writes are possible.
'token' => $user->createToken($tokenName, ['read'])->plainTextToken,
]);
}
public function logout(Request $request): JsonResponse
{
$request->user()->currentAccessToken()->delete();
return $this->success(__('Revoked API token'));
}
public function logoutAll(Request $request): JsonResponse
{
$request->user()->tokens()->delete();
return $this->success(__('Revoked all API tokens'));
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace App\Http\Controllers\Api\V0;
use App\Http\Controllers\Controller;
use App\Http\Requests\Api\V0\StoreModRequest;
use App\Http\Requests\Api\V0\UpdateModRequest;
use App\Http\Resources\Api\V0\ModResource;
use App\Models\Mod;
class ModController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
return ModResource::collection(Mod::paginate());
}
/**
* Store a newly created resource in storage.
*/
public function store(StoreModRequest $request)
{
//
}
/**
* Display the specified resource.
*/
public function show(Mod $mod)
{
return new ModResource($mod);
}
/**
* Update the specified resource in storage.
*/
public function update(UpdateModRequest $request, Mod $mod)
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Mod $mod)
{
//
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace App\Http\Controllers\Api\V0;
use App\Http\Controllers\Controller;
use App\Http\Requests\Api\V0\StoreUserRequest;
use App\Http\Requests\Api\V0\UpdateUserRequest;
use App\Http\Resources\Api\V0\UserResource;
use App\Models\User;
class UsersController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
return UserResource::collection(User::paginate());
}
/**
* Store a newly created resource in storage.
*/
public function store(StoreUserRequest $request)
{
//
}
/**
* Display the specified resource.
*/
public function show(User $user)
{
return new UserResource($user);
}
/**
* Update the specified resource in storage.
*/
public function update(UpdateUserRequest $request, User $user)
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(User $user)
{
//
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Http\Requests\Api;
use Illuminate\Foundation\Http\FormRequest;
class LoginUserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
return [
'email' => ['required', 'string', 'email'],
'password' => ['required', 'string'],
'token_name' => ['sometimes'],
];
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Http\Requests\Api\V0;
use Illuminate\Foundation\Http\FormRequest;
class StoreModRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return false;
}
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
return [
//
];
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Http\Requests\Api\V0;
use Illuminate\Foundation\Http\FormRequest;
class StoreUserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return false;
}
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
return [
//
];
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Http\Requests\Api\V0;
use Illuminate\Foundation\Http\FormRequest;
class UpdateModRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return false;
}
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
return [
//
];
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Http\Requests\Api\V0;
use Illuminate\Foundation\Http\FormRequest;
class UpdateUserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return false;
}
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
return [
//
];
}
}

View File

@ -0,0 +1,61 @@
<?php
namespace App\Http\Resources\Api\V0;
use App\Models\Mod;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
/** @mixin Mod */
class ModResource extends JsonResource
{
/**
* Transform the resource into an array.
*/
public function toArray(Request $request): array
{
return [
'type' => 'mod',
'id' => $this->id,
'attributes' => [
'name' => $this->name,
'slug' => $this->slug,
'description' => $this->when(
$request->routeIs('api.v0.mods.show'),
$this->description
),
'source_code_link' => $this->source_code_link,
'user_id' => $this->user_id,
'license_id' => $this->license_id,
'created_at' => $this->created_at,
],
'relationships' => [
'user' => [
'data' => [
'type' => 'user',
'id' => $this->user_id,
],
// TODO: Provide 'links.self' to user profile:
//'links' => ['self' => '#'],
],
'license' => [
'data' => [
'type' => 'license',
'id' => $this->license_id,
],
],
],
'included' => [
new UserResource($this->user),
// TODO: Provide 'included' data for attached 'license':
//new LicenseResource($this->license),
],
'links' => [
'self' => route('mod.show', [
'mod' => $this->id,
'slug' => $this->slug,
]),
],
];
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace App\Http\Resources\Api\V0;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
/** @mixin User */
class UserResource extends JsonResource
{
/**
* Transform the resource into an array.
*/
public function toArray(Request $request): array
{
return [
'type' => 'user',
'id' => $this->id,
'attributes' => [
'name' => $this->name,
'user_role_id' => $this->user_role_id,
'created_at' => $this->created_at,
],
'relationships' => [
'user_role' => [
'data' => [
'type' => 'user_role',
'id' => $this->user_role_id,
],
],
],
// TODO: Provide 'included' data for attached 'user_role'
//'included' => [new UserRoleResource($this->role)],
// TODO: Provide 'links.self' to user profile:
//'links' => ['self' => '#'],
];
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Traits;
use Illuminate\Http\JsonResponse;
trait ApiResponses
{
protected function success(string $message, ?array $data = []): JsonResponse
{
return $this->baseResponse(message: $message, data: $data, code: 200);
}
private function baseResponse(?string $message = '', ?array $data = [], ?int $code = 200): JsonResponse
{
return response()->json([
'message' => $message,
'data' => $data,
], $code);
}
protected function error(string $message, int $code): JsonResponse
{
return $this->baseResponse(message: $message, code: $code);
}
}

View File

@ -3,6 +3,7 @@
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
use Illuminate\Support\Facades\Route;
use Mchev\Banhammer\Middleware\IPBanned;
return Application::configure(basePath: dirname(__DIR__))
@ -11,6 +12,12 @@ return Application::configure(basePath: dirname(__DIR__))
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
then: function () {
Route::middleware('api')
->prefix('api/v0')
->name('api.v0.')
->group(base_path('routes/api_v0.php'));
},
)
->withMiddleware(function (Middleware $middleware) {
$middleware->append(IPBanned::class);

View File

@ -1,8 +1,14 @@
<?php
use Illuminate\Http\Request;
use App\Http\Controllers\Api\AuthController;
use Illuminate\Support\Facades\Route;
Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:sanctum');
Route::get('/status', function () {
return response()->json(['message' => 'okay']);
});
Route::post('/login', [AuthController::class, 'login']);
Route::group(['middleware' => 'auth:sanctum'], function () {
Route::delete('/logout', [AuthController::class, 'logout']);
Route::delete('/logout/all', [AuthController::class, 'logoutAll']);
});

10
routes/api_v0.php Normal file
View File

@ -0,0 +1,10 @@
<?php
use App\Http\Controllers\Api\V0\ModController;
use App\Http\Controllers\Api\V0\UsersController;
use Illuminate\Support\Facades\Route;
Route::group(['middleware' => 'auth:sanctum'], function () {
Route::apiResource('users', UsersController::class);
Route::apiResource('mods', ModController::class);
});