api
This commit is contained in:
@@ -14,12 +14,4 @@ enum TaxonomyType: string
|
|||||||
self::TAG => 'Tag',
|
self::TAG => 'Tag',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public function value(): string
|
|
||||||
{
|
|
||||||
return match ($this) {
|
|
||||||
self::CATEGORY => 'category',
|
|
||||||
self::TAG => 'tag',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api\Auth;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Illuminate\Validation\ValidationException;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
class AuthController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Login
|
||||||
|
*/
|
||||||
|
public function login(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$validated = $request->validate([
|
||||||
|
'username' => ['required'],
|
||||||
|
'password' => ['required'],
|
||||||
|
], [
|
||||||
|
'username.required' => 'Username harus diisi.',
|
||||||
|
'password.required' => 'Password harus diisi.',
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (! Auth::guard()->attempt($validated)) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Kredensial yang digunakan salah.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = Auth::user();
|
||||||
|
|
||||||
|
$user->tokens()->delete();
|
||||||
|
$token = $user->createToken('web')->plainTextToken;
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Login berhasil.',
|
||||||
|
'data' => [
|
||||||
|
'user' => $user->toResource(),
|
||||||
|
'token' => $token,
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logout
|
||||||
|
*/
|
||||||
|
public function logout(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
if ($request->user()?->currentAccessToken()) {
|
||||||
|
$request->user()->currentAccessToken()->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Logout berhasil.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the authenticated user.
|
||||||
|
*/
|
||||||
|
public function me(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'data' => [
|
||||||
|
'user' => $request->user()->toResource()
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api\Content;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
|
||||||
|
use App\Enums\TaxonomyType;
|
||||||
|
use App\Models\Taxonomy;
|
||||||
|
use App\Http\Resources\TaxonomyResource;
|
||||||
|
|
||||||
|
class TaxonomyController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*/
|
||||||
|
public function index(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$validated = $request->validate([
|
||||||
|
'type' => ['nullable', Rule::enum(TaxonomyType::class)],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$taxonomies = Taxonomy::query()
|
||||||
|
->when(
|
||||||
|
isset($validated['type']),
|
||||||
|
fn ($query) => $query->where('type', $validated['type'])
|
||||||
|
)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'data' => [
|
||||||
|
'taxonomies' => TaxonomyResource::collection($taxonomies),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the specified resource.
|
||||||
|
*/
|
||||||
|
public function show(Taxonomy $taxonomy): JsonResponse
|
||||||
|
{
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'data' => [
|
||||||
|
'taxonomy' => new TaxonomyResource($taxonomy),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Resources;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
|
class TaxonomyResource extends JsonResource
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Transform the resource into an array.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function toArray(Request $request): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => $this->id,
|
||||||
|
'name' => $this->name,
|
||||||
|
'slug' => $this->slug,
|
||||||
|
'type' => [
|
||||||
|
'value' => $this->type->value,
|
||||||
|
'label' => $this->type->label(),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Resources;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
|
class UserResource extends JsonResource
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Transform the resource into an array.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function toArray(Request $request): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => $this->id,
|
||||||
|
'name' => $this->name,
|
||||||
|
'username' => $this->username,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,10 +8,22 @@ use Illuminate\Database\Eloquent\Model;
|
|||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
#[Fillable(['type', 'name', 'slug', 'created_by', 'updated_by', 'deleted_by'])]
|
#[Fillable(['type', 'name', 'slug', 'created_by', 'updated_by', 'deleted_by'])]
|
||||||
|
#[UseResource(\App\Http\Resources\TaxonomyResource::class)]
|
||||||
class Taxonomy extends Model
|
class Taxonomy extends Model
|
||||||
{
|
{
|
||||||
use SoftDeletes;
|
use SoftDeletes;
|
||||||
|
|
||||||
|
public function resolveRouteBinding($value, $field = null)
|
||||||
|
{
|
||||||
|
return $this->where(function ($query) use ($value) {
|
||||||
|
if (is_numeric($value)) {
|
||||||
|
$query->where('id', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
$query->orWhere('slug', $value);
|
||||||
|
})->firstOrFail();
|
||||||
|
}
|
||||||
|
|
||||||
protected function casts(): array
|
protected function casts(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
|||||||
+3
-1
@@ -9,13 +9,15 @@ use Illuminate\Database\Eloquent\Attributes\Hidden;
|
|||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
|
use Laravel\Sanctum\HasApiTokens;
|
||||||
|
|
||||||
#[Fillable(['name', 'username', 'password', 'created_by', 'updated_by', 'deleted_by'])]
|
#[Fillable(['name', 'username', 'password', 'created_by', 'updated_by', 'deleted_by'])]
|
||||||
#[Hidden(['password', 'remember_token'])]
|
#[Hidden(['password', 'remember_token'])]
|
||||||
|
#[UseResource(\App\Http\Resources\UserResource::class)]
|
||||||
class User extends Authenticatable
|
class User extends Authenticatable
|
||||||
{
|
{
|
||||||
/** @use HasFactory<UserFactory> */
|
/** @use HasFactory<UserFactory> */
|
||||||
use HasFactory, Notifiable;
|
use HasApiTokens, HasFactory, Notifiable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the attributes that should be cast.
|
* Get the attributes that should be cast.
|
||||||
|
|||||||
+46
-1
@@ -3,11 +3,17 @@
|
|||||||
use Illuminate\Foundation\Application;
|
use Illuminate\Foundation\Application;
|
||||||
use Illuminate\Foundation\Configuration\Exceptions;
|
use Illuminate\Foundation\Configuration\Exceptions;
|
||||||
use Illuminate\Foundation\Configuration\Middleware;
|
use Illuminate\Foundation\Configuration\Middleware;
|
||||||
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||||
|
use Illuminate\Validation\ValidationException;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||||
|
use Illuminate\Auth\AuthenticationException;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
return Application::configure(basePath: dirname(__DIR__))
|
return Application::configure(basePath: dirname(__DIR__))
|
||||||
->withRouting(
|
->withRouting(
|
||||||
web: __DIR__.'/../routes/web.php',
|
web: __DIR__.'/../routes/web.php',
|
||||||
|
api: __DIR__.'/../routes/api.php',
|
||||||
commands: __DIR__.'/../routes/console.php',
|
commands: __DIR__.'/../routes/console.php',
|
||||||
health: '/up',
|
health: '/up',
|
||||||
)
|
)
|
||||||
@@ -16,6 +22,45 @@ return Application::configure(basePath: dirname(__DIR__))
|
|||||||
})
|
})
|
||||||
->withExceptions(function (Exceptions $exceptions): void {
|
->withExceptions(function (Exceptions $exceptions): void {
|
||||||
$exceptions->shouldRenderJsonWhen(
|
$exceptions->shouldRenderJsonWhen(
|
||||||
fn (Request $request) => $request->is('api/*'),
|
fn (Request $request) => $request->is('api/*')
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$exceptions->render(function (ModelNotFoundException $e) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Data tidak ditemukan.',
|
||||||
|
], 404);
|
||||||
|
});
|
||||||
|
|
||||||
|
$exceptions->render(function (
|
||||||
|
NotFoundHttpException $e,
|
||||||
|
Request $request
|
||||||
|
) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Data tidak ditemukan.',
|
||||||
|
], 404);
|
||||||
|
});
|
||||||
|
|
||||||
|
$exceptions->render(function (AuthenticationException $e) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Unauthenticated.',
|
||||||
|
], 401);
|
||||||
|
});
|
||||||
|
|
||||||
|
$exceptions->render(function (ValidationException $e) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Validasi gagal.',
|
||||||
|
'errors' => $e->errors(),
|
||||||
|
], 422);
|
||||||
|
});
|
||||||
|
|
||||||
|
$exceptions->render(function (HttpException $e) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => $e->getMessage() ?: 'Terjadi kesalahan.',
|
||||||
|
], $e->getStatusCode());
|
||||||
|
});
|
||||||
})->create();
|
})->create();
|
||||||
|
|||||||
@@ -7,7 +7,9 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^8.3",
|
"php": "^8.3",
|
||||||
|
"dedoc/scramble": "^0.13.35",
|
||||||
"laravel/framework": "^13.8",
|
"laravel/framework": "^13.8",
|
||||||
|
"laravel/sanctum": "^4.0",
|
||||||
"laravel/tinker": "^3.0"
|
"laravel/tinker": "^3.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
|||||||
Generated
+313
-61
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "36793882908b6cbdd344b5a4840231db",
|
"content-hash": "f18e50d8c9d6bcf4d309afe2727d096e",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "brick/math",
|
"name": "brick/math",
|
||||||
@@ -134,6 +134,87 @@
|
|||||||
],
|
],
|
||||||
"time": "2024-02-09T16:56:22+00:00"
|
"time": "2024-02-09T16:56:22+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "dedoc/scramble",
|
||||||
|
"version": "v0.13.35",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/dedoc/scramble.git",
|
||||||
|
"reference": "c072f731e060972134b6916ca6899609bacc0dd2"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/dedoc/scramble/zipball/c072f731e060972134b6916ca6899609bacc0dd2",
|
||||||
|
"reference": "c072f731e060972134b6916ca6899609bacc0dd2",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"illuminate/contracts": "^10.0|^11.0|^12.0|^13.0",
|
||||||
|
"myclabs/deep-copy": "^1.12",
|
||||||
|
"nikic/php-parser": "^5.0",
|
||||||
|
"php": "^8.1",
|
||||||
|
"phpstan/phpdoc-parser": "^1.0|^2.0",
|
||||||
|
"spatie/laravel-package-tools": "^1.9.2"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"larastan/larastan": "^3.3",
|
||||||
|
"laravel/pint": "^v1.1.0",
|
||||||
|
"laravel/scout": "^10.0|^11.0",
|
||||||
|
"nunomaduro/collision": "^7.0|^8.0",
|
||||||
|
"orchestra/testbench": "^8.0|^9.0|^10.0|^11.0",
|
||||||
|
"pestphp/pest": "^2.34|^3.7|^4.4",
|
||||||
|
"pestphp/pest-plugin-laravel": "^2.3|^3.1|^4.1",
|
||||||
|
"phpstan/extension-installer": "^1.4",
|
||||||
|
"phpstan/phpstan-deprecation-rules": "^2.0",
|
||||||
|
"phpstan/phpstan-phpunit": "^2.0",
|
||||||
|
"phpunit/phpunit": "^10.5|^11.5.3|^12.5.12",
|
||||||
|
"spatie/laravel-permission": "^6.10|^7.2",
|
||||||
|
"spatie/pest-plugin-snapshots": "^2.1"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"Dedoc\\Scramble\\ScrambleServiceProvider"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Dedoc\\Scramble\\": "src",
|
||||||
|
"Dedoc\\Scramble\\Database\\Factories\\": "database/factories"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Roman Lytvynenko",
|
||||||
|
"email": "litvinenko95@gmail.com",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Automatic generation of API documentation for Laravel applications.",
|
||||||
|
"homepage": "https://github.com/dedoc/scramble",
|
||||||
|
"keywords": [
|
||||||
|
"documentation",
|
||||||
|
"laravel",
|
||||||
|
"openapi"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/dedoc/scramble/issues",
|
||||||
|
"source": "https://github.com/dedoc/scramble/tree/v0.13.35"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/romalytvynenko",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2026-07-15T13:21:15+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "dflydev/dot-access-data",
|
"name": "dflydev/dot-access-data",
|
||||||
"version": "v3.0.3",
|
"version": "v3.0.3",
|
||||||
@@ -1343,6 +1424,69 @@
|
|||||||
},
|
},
|
||||||
"time": "2026-06-26T00:11:25+00:00"
|
"time": "2026-06-26T00:11:25+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "laravel/sanctum",
|
||||||
|
"version": "v4.3.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/laravel/sanctum.git",
|
||||||
|
"reference": "fee27a573d1a013af3721d86153a65e0b11927e6"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/laravel/sanctum/zipball/fee27a573d1a013af3721d86153a65e0b11927e6",
|
||||||
|
"reference": "fee27a573d1a013af3721d86153a65e0b11927e6",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-json": "*",
|
||||||
|
"illuminate/console": "^11.0|^12.0|^13.0",
|
||||||
|
"illuminate/contracts": "^11.0|^12.0|^13.0",
|
||||||
|
"illuminate/database": "^11.0|^12.0|^13.0",
|
||||||
|
"illuminate/support": "^11.0|^12.0|^13.0",
|
||||||
|
"php": "^8.2",
|
||||||
|
"symfony/console": "^7.0|^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"mockery/mockery": "^1.6",
|
||||||
|
"orchestra/testbench": "^9.15|^10.8|^11.0",
|
||||||
|
"phpstan/phpstan": "^1.10"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"Laravel\\Sanctum\\SanctumServiceProvider"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Laravel\\Sanctum\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Taylor Otwell",
|
||||||
|
"email": "taylor@laravel.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.",
|
||||||
|
"keywords": [
|
||||||
|
"auth",
|
||||||
|
"laravel",
|
||||||
|
"sanctum"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/laravel/sanctum/issues",
|
||||||
|
"source": "https://github.com/laravel/sanctum"
|
||||||
|
},
|
||||||
|
"time": "2026-06-23T18:26:55+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "laravel/serializable-closure",
|
"name": "laravel/serializable-closure",
|
||||||
"version": "v2.0.13",
|
"version": "v2.0.13",
|
||||||
@@ -2135,6 +2279,66 @@
|
|||||||
],
|
],
|
||||||
"time": "2026-01-02T08:56:05+00:00"
|
"time": "2026-01-02T08:56:05+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "myclabs/deep-copy",
|
||||||
|
"version": "1.13.4",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/myclabs/DeepCopy.git",
|
||||||
|
"reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a",
|
||||||
|
"reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.1 || ^8.0"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"doctrine/collections": "<1.6.8",
|
||||||
|
"doctrine/common": "<2.13.3 || >=3 <3.2.2"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"doctrine/collections": "^1.6.8",
|
||||||
|
"doctrine/common": "^2.13.3 || ^3.2.2",
|
||||||
|
"phpspec/prophecy": "^1.10",
|
||||||
|
"phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"src/DeepCopy/deep_copy.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"DeepCopy\\": "src/DeepCopy/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"description": "Create deep copies (clones) of your objects",
|
||||||
|
"keywords": [
|
||||||
|
"clone",
|
||||||
|
"copy",
|
||||||
|
"duplicate",
|
||||||
|
"object",
|
||||||
|
"object graph"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/myclabs/DeepCopy/issues",
|
||||||
|
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.4"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2025-08-01T08:46:24+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "nesbot/carbon",
|
"name": "nesbot/carbon",
|
||||||
"version": "3.13.1",
|
"version": "3.13.1",
|
||||||
@@ -2617,6 +2821,53 @@
|
|||||||
],
|
],
|
||||||
"time": "2025-12-27T19:41:33+00:00"
|
"time": "2025-12-27T19:41:33+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "phpstan/phpdoc-parser",
|
||||||
|
"version": "2.3.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/phpstan/phpdoc-parser.git",
|
||||||
|
"reference": "fb19eedd2bb67ff8cf7a5502ad329e701d6398a3"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fb19eedd2bb67ff8cf7a5502ad329e701d6398a3",
|
||||||
|
"reference": "fb19eedd2bb67ff8cf7a5502ad329e701d6398a3",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.4 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"doctrine/annotations": "^2.0",
|
||||||
|
"nikic/php-parser": "^5.3.0",
|
||||||
|
"php-parallel-lint/php-parallel-lint": "^1.2",
|
||||||
|
"phpstan/extension-installer": "^1.0",
|
||||||
|
"phpstan/phpstan": "^2.0",
|
||||||
|
"phpstan/phpstan-phpunit": "^2.0",
|
||||||
|
"phpstan/phpstan-strict-rules": "^2.0",
|
||||||
|
"phpunit/phpunit": "^9.6",
|
||||||
|
"symfony/process": "^5.2"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"PHPStan\\PhpDocParser\\": [
|
||||||
|
"src/"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"description": "PHPDoc parser with support for nullable, intersection and generic types",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
|
||||||
|
"source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.3"
|
||||||
|
},
|
||||||
|
"time": "2026-07-08T07:01:06+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "psr/clock",
|
"name": "psr/clock",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@@ -3306,6 +3557,67 @@
|
|||||||
},
|
},
|
||||||
"time": "2026-06-18T03:57:49+00:00"
|
"time": "2026-06-18T03:57:49+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "spatie/laravel-package-tools",
|
||||||
|
"version": "1.93.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/spatie/laravel-package-tools.git",
|
||||||
|
"reference": "d5552849801f2642aea710557463234b59ef65eb"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/d5552849801f2642aea710557463234b59ef65eb",
|
||||||
|
"reference": "d5552849801f2642aea710557463234b59ef65eb",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"illuminate/contracts": "^10.0|^11.0|^12.0|^13.0",
|
||||||
|
"php": "^8.1"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"mockery/mockery": "^1.5",
|
||||||
|
"orchestra/testbench": "^8.0|^9.2|^10.0|^11.0",
|
||||||
|
"pestphp/pest": "^2.1|^3.1|^4.0",
|
||||||
|
"phpunit/php-code-coverage": "^10.0|^11.0|^12.0",
|
||||||
|
"phpunit/phpunit": "^10.5|^11.5|^12.5",
|
||||||
|
"spatie/pest-plugin-test-time": "^2.2|^3.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Spatie\\LaravelPackageTools\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Freek Van der Herten",
|
||||||
|
"email": "freek@spatie.be",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Tools for creating Laravel packages",
|
||||||
|
"homepage": "https://github.com/spatie/laravel-package-tools",
|
||||||
|
"keywords": [
|
||||||
|
"laravel-package-tools",
|
||||||
|
"spatie"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/spatie/laravel-package-tools/issues",
|
||||||
|
"source": "https://github.com/spatie/laravel-package-tools/tree/1.93.1"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/spatie",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2026-05-19T14:06:37+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/clock",
|
"name": "symfony/clock",
|
||||||
"version": "v8.1.0",
|
"version": "v8.1.0",
|
||||||
@@ -6563,66 +6875,6 @@
|
|||||||
},
|
},
|
||||||
"time": "2024-05-16T03:13:13+00:00"
|
"time": "2024-05-16T03:13:13+00:00"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "myclabs/deep-copy",
|
|
||||||
"version": "1.13.4",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/myclabs/DeepCopy.git",
|
|
||||||
"reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a",
|
|
||||||
"reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": "^7.1 || ^8.0"
|
|
||||||
},
|
|
||||||
"conflict": {
|
|
||||||
"doctrine/collections": "<1.6.8",
|
|
||||||
"doctrine/common": "<2.13.3 || >=3 <3.2.2"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"doctrine/collections": "^1.6.8",
|
|
||||||
"doctrine/common": "^2.13.3 || ^3.2.2",
|
|
||||||
"phpspec/prophecy": "^1.10",
|
|
||||||
"phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"autoload": {
|
|
||||||
"files": [
|
|
||||||
"src/DeepCopy/deep_copy.php"
|
|
||||||
],
|
|
||||||
"psr-4": {
|
|
||||||
"DeepCopy\\": "src/DeepCopy/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"description": "Create deep copies (clones) of your objects",
|
|
||||||
"keywords": [
|
|
||||||
"clone",
|
|
||||||
"copy",
|
|
||||||
"duplicate",
|
|
||||||
"object",
|
|
||||||
"object graph"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"issues": "https://github.com/myclabs/DeepCopy/issues",
|
|
||||||
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.4"
|
|
||||||
},
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
|
|
||||||
"type": "tidelift"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2025-08-01T08:46:24+00:00"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "nunomaduro/collision",
|
"name": "nunomaduro/collision",
|
||||||
"version": "v8.9.5",
|
"version": "v8.9.5",
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Cookie\Middleware\EncryptCookies;
|
||||||
|
use Illuminate\Foundation\Http\Middleware\ValidateCsrfToken;
|
||||||
|
use Laravel\Sanctum\Http\Middleware\AuthenticateSession;
|
||||||
|
use Laravel\Sanctum\Sanctum;
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Stateful Domains
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Requests from the following domains / hosts will receive stateful API
|
||||||
|
| authentication cookies. Typically, these should include your local
|
||||||
|
| and production domains which access your API via a frontend SPA.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
|
||||||
|
'%s%s',
|
||||||
|
'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
|
||||||
|
Sanctum::currentApplicationUrlWithPort(),
|
||||||
|
// Sanctum::currentRequestHost(),
|
||||||
|
))),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Sanctum Guards
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This array contains the authentication guards that will be checked when
|
||||||
|
| Sanctum is trying to authenticate a request. If none of these guards
|
||||||
|
| are able to authenticate the request, Sanctum will use the bearer
|
||||||
|
| token that's present on an incoming request for authentication.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'guard' => ['web'],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Expiration Minutes
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value controls the number of minutes until an issued token will be
|
||||||
|
| considered expired. This will override any values set in the token's
|
||||||
|
| "expires_at" attribute, but first-party sessions are not affected.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'expiration' => null,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Token Prefix
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Sanctum can prefix new tokens in order to take advantage of numerous
|
||||||
|
| security scanning initiatives maintained by open source platforms
|
||||||
|
| that notify developers if they commit tokens into repositories.
|
||||||
|
|
|
||||||
|
| See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Sanctum Middleware
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| When authenticating your first-party SPA with Sanctum you may need to
|
||||||
|
| customize some of the middleware Sanctum uses while processing the
|
||||||
|
| request. You may change the middleware listed below as required.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'middleware' => [
|
||||||
|
'authenticate_session' => AuthenticateSession::class,
|
||||||
|
'encrypt_cookies' => EncryptCookies::class,
|
||||||
|
'validate_csrf_token' => ValidateCsrfToken::class,
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Dedoc\Scramble\Http\Middleware\RestrictedDocsAccess;
|
||||||
|
|
||||||
|
return [
|
||||||
|
/*
|
||||||
|
* Which routes to document. String or array form; use Scramble::routes() for custom selection.
|
||||||
|
*
|
||||||
|
* 'api_path' => [
|
||||||
|
* 'include' => 'api',
|
||||||
|
* 'exclude' => ['api/internal'],
|
||||||
|
* ],
|
||||||
|
*
|
||||||
|
* Without *, patterns match path segments (api matches api and api/users, not apiary).
|
||||||
|
* With *, Str::is is used (e.g. api/v*).
|
||||||
|
*
|
||||||
|
* One static include → default server is /{include} and paths are stripped (/users).
|
||||||
|
* Multiple includes or wildcards → server defaults to / and paths stay full (/api/users).
|
||||||
|
* Override with `servers`, or use Scramble::registerApi() for separate bases.
|
||||||
|
*/
|
||||||
|
'api_path' => 'api',
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Your API domain. By default, app domain is used. This is also a part of the default API routes
|
||||||
|
* matcher, so when implementing your own, make sure you use this config if needed.
|
||||||
|
*/
|
||||||
|
'api_domain' => null,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The path where your OpenAPI specification will be exported.
|
||||||
|
*/
|
||||||
|
'export_path' => 'api.json',
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cache configuration for the generated OpenAPI document.
|
||||||
|
*
|
||||||
|
* Use `scramble:cache` to warm the cache and `scramble:clear` to invalidate it.
|
||||||
|
*/
|
||||||
|
'cache' => [
|
||||||
|
'key' => 'scramble.openapi',
|
||||||
|
'store' => 'file',
|
||||||
|
],
|
||||||
|
|
||||||
|
'info' => [
|
||||||
|
/*
|
||||||
|
* API version.
|
||||||
|
*/
|
||||||
|
'version' => env('API_VERSION', '0.0.1'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Description rendered on the home page of the API documentation (`/docs/api`).
|
||||||
|
*/
|
||||||
|
'description' => '',
|
||||||
|
],
|
||||||
|
|
||||||
|
'ui' => [
|
||||||
|
'title' => null,
|
||||||
|
],
|
||||||
|
|
||||||
|
'renderer' => 'elements',
|
||||||
|
|
||||||
|
'renderers' => [
|
||||||
|
/*
|
||||||
|
* Stoplight Elements config options: https://docs.stoplight.io/docs/elements/b074dc47b2826-elements-configuration-options
|
||||||
|
*/
|
||||||
|
'elements' => [
|
||||||
|
'view' => 'scramble::docs',
|
||||||
|
'theme' => 'light',
|
||||||
|
'hideTryIt' => false,
|
||||||
|
'hideSchemas' => false,
|
||||||
|
'logo' => '',
|
||||||
|
'tryItCredentialsPolicy' => 'include',
|
||||||
|
'layout' => 'responsive',
|
||||||
|
'router' => 'hash',
|
||||||
|
],
|
||||||
|
/*
|
||||||
|
* Scalar API reference config options: https://scalar.com/products/api-references/configuration
|
||||||
|
*/
|
||||||
|
'scalar' => [
|
||||||
|
'view' => 'scramble::scalar',
|
||||||
|
'cdn' => 'https://cdn.jsdelivr.net/npm/@scalar/api-reference',
|
||||||
|
'theme' => 'laravel',
|
||||||
|
'proxyUrl' => 'https://proxy.scalar.com',
|
||||||
|
'darkMode' => false,
|
||||||
|
'showDeveloperTools' => 'never',
|
||||||
|
'agent' => ['disabled' => true],
|
||||||
|
'credentials' => 'include',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The list of servers of the API. By default, when `null`, server URL will be created from
|
||||||
|
* `scramble.api_path` and `scramble.api_domain` config variables. When providing an array, you
|
||||||
|
* will need to specify the local server URL manually (if needed).
|
||||||
|
*
|
||||||
|
* Example of non-default config (final URLs are generated using Laravel `url` helper):
|
||||||
|
*
|
||||||
|
* ```php
|
||||||
|
* 'servers' => [
|
||||||
|
* 'Live' => 'api',
|
||||||
|
* 'Prod' => 'https://scramble.dedoc.co/api',
|
||||||
|
* ],
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
'servers' => null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines how Scramble stores the descriptions of enum cases.
|
||||||
|
* Available options:
|
||||||
|
* - 'description' – Case descriptions are stored as the enum schema's description using table formatting.
|
||||||
|
* - 'extension' – Case descriptions are stored in the `x-enumDescriptions` enum schema extension.
|
||||||
|
*
|
||||||
|
* @see https://redocly.com/docs-legacy/api-reference-docs/specification-extensions/x-enum-descriptions
|
||||||
|
* - false - Case descriptions are ignored.
|
||||||
|
*/
|
||||||
|
'enum_cases_description_strategy' => 'description',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines how Scramble stores the names of enum cases.
|
||||||
|
* Available options:
|
||||||
|
* - 'names' – Case names are stored in the `x-enumNames` enum schema extension.
|
||||||
|
* - 'varnames' - Case names are stored in the `x-enum-varnames` enum schema extension.
|
||||||
|
* - false - Case names are not stored.
|
||||||
|
*/
|
||||||
|
'enum_cases_names_strategy' => false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When Scramble encounters deep objects in query parameters, it flattens the parameters so the generated
|
||||||
|
* OpenAPI document correctly describes the API. Flattening deep query parameters is relevant until
|
||||||
|
* OpenAPI 3.2 is released and query string structure can be described properly.
|
||||||
|
*
|
||||||
|
* For example, this nested validation rule describes the object with `bar` property:
|
||||||
|
* `['foo.bar' => ['required', 'int']]`.
|
||||||
|
*
|
||||||
|
* When `flatten_deep_query_parameters` is `true`, Scramble will document the parameter like so:
|
||||||
|
* `{"name":"foo[bar]", "schema":{"type":"int"}, "required":true}`.
|
||||||
|
*
|
||||||
|
* When `flatten_deep_query_parameters` is `false`, Scramble will document the parameter like so:
|
||||||
|
* `{"name":"foo", "schema": {"type":"object", "properties":{"bar":{"type": "int"}}, "required": ["bar"]}, "required":true}`.
|
||||||
|
*/
|
||||||
|
'flatten_deep_query_parameters' => true,
|
||||||
|
|
||||||
|
'middleware' => [
|
||||||
|
'web',
|
||||||
|
RestrictedDocsAccess::class,
|
||||||
|
],
|
||||||
|
|
||||||
|
'extensions' => [],
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Automatically document API security (OpenAPI `security` / `securitySchemes`) based on route
|
||||||
|
* middleware.
|
||||||
|
*
|
||||||
|
* Disabled by default. Uncomment the line below to enable `MiddlewareAuthSecurityStrategy`.
|
||||||
|
* When at least one documented route uses middleware matching the configured patterns (by default
|
||||||
|
* `auth` and `auth:*`), bearer auth is applied globally. Routes without matching middleware are
|
||||||
|
* marked as public (`security: []`).
|
||||||
|
*
|
||||||
|
* Set to `null` explicitly to disable. If you already configure security manually via
|
||||||
|
* `afterOpenApiGenerated` / `extendOpenApi`, keep this disabled to avoid duplicate schemes.
|
||||||
|
*
|
||||||
|
* Customize with a class-string or [class, options]:
|
||||||
|
*
|
||||||
|
* 'security_strategy' => [
|
||||||
|
* \Dedoc\Scramble\SecurityDocumentation\MiddlewareAuthSecurityStrategy::class,
|
||||||
|
* [
|
||||||
|
* 'middleware' => ['auth', 'auth:*'],
|
||||||
|
* 'scheme' => \Dedoc\Scramble\Support\Generator\SecurityScheme::http('bearer'),
|
||||||
|
* ],
|
||||||
|
* ],
|
||||||
|
*/
|
||||||
|
'security_strategy' => \Dedoc\Scramble\SecurityDocumentation\MiddlewareAuthSecurityStrategy::class,
|
||||||
|
// 'security_strategy' => null,
|
||||||
|
];
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<?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('personal_access_tokens', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->morphs('tokenable');
|
||||||
|
$table->text('name');
|
||||||
|
$table->string('token', 64)->unique();
|
||||||
|
$table->text('abilities')->nullable();
|
||||||
|
$table->timestamp('last_used_at')->nullable();
|
||||||
|
$table->timestamp('expires_at')->nullable()->index();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('personal_access_tokens');
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
|
Route::post('/login', [\App\Http\Controllers\Api\Auth\AuthController::class, 'login']);
|
||||||
|
|
||||||
|
Route::middleware('auth:sanctum')->group(function () {
|
||||||
|
Route::post('/logout', [AuthController::class, 'logout']);
|
||||||
|
Route::get('/me', [AuthController::class, 'me']);
|
||||||
|
|
||||||
|
Route::resource('taxonomies',\App\Http\Controllers\Api\Content\TaxonomyController::class)->only(['index', 'show']);
|
||||||
|
});
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class AuthControllerTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
public function test_user_can_login_and_logout_with_sanctum_token(): void
|
||||||
|
{
|
||||||
|
$user = User::factory()->create([
|
||||||
|
'username' => 'tester',
|
||||||
|
'password' => Hash::make('secret123'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$loginResponse = $this->postJson('/api/login', [
|
||||||
|
'username' => 'tester',
|
||||||
|
'password' => 'secret123',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$loginResponse->assertStatus(200)
|
||||||
|
->assertJsonPath('message', 'Login successful')
|
||||||
|
->assertJsonStructure([
|
||||||
|
'message',
|
||||||
|
'user' => ['id', 'name', 'username'],
|
||||||
|
'token',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertDatabaseHas('personal_access_tokens', [
|
||||||
|
'tokenable_id' => $user->id,
|
||||||
|
'tokenable_type' => User::class,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$token = $loginResponse->json('token');
|
||||||
|
|
||||||
|
$logoutResponse = $this->withHeader('Authorization', 'Bearer ' . $token)
|
||||||
|
->postJson('/api/logout');
|
||||||
|
|
||||||
|
$logoutResponse->assertStatus(200)
|
||||||
|
->assertJsonPath('message', 'Logout successful');
|
||||||
|
|
||||||
|
$this->assertDatabaseCount('personal_access_tokens', 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user