From 6adc8a13ed675c75d46490d60b0e5c5f441a2b90 Mon Sep 17 00:00:00 2001 From: Ilham Wara Nugroho Date: Tue, 7 Jul 2026 12:44:11 +0700 Subject: [PATCH] update --- app/Http/Controllers/ApiController.php | 12 ++- app/Http/Controllers/AuthApiController.php | 75 ++++++++++++++++ app/Http/Controllers/BaseController.php | 66 ++++++++++++++ app/Models/User.php | 5 +- bootstrap/app.php | 10 ++- composer.json | 3 +- composer.lock | 65 +++++++++++++- config/auth.php | 9 +- config/sanctum.php | 87 +++++++++++++++++++ ...13_create_personal_access_tokens_table.php | 33 +++++++ ...32_create_personal_access_tokens_table.php | 33 +++++++ routes/api.php | 19 ++++ routes/modules/modules.php | 7 +- routes/web.php | 7 +- 14 files changed, 409 insertions(+), 22 deletions(-) create mode 100644 app/Http/Controllers/AuthApiController.php create mode 100644 app/Http/Controllers/BaseController.php create mode 100644 config/sanctum.php create mode 100644 database/migrations/2026_07_07_032013_create_personal_access_tokens_table.php create mode 100644 database/migrations/2026_07_07_043932_create_personal_access_tokens_table.php create mode 100644 routes/api.php diff --git a/app/Http/Controllers/ApiController.php b/app/Http/Controllers/ApiController.php index 399154d..80b3e93 100644 --- a/app/Http/Controllers/ApiController.php +++ b/app/Http/Controllers/ApiController.php @@ -3,16 +3,22 @@ namespace App\Http\Controllers; use Illuminate\Http\Request; +use Auth; +use App\Models\User; +use Illuminate\Support\Str; +use App\Http\Controllers\BaseController; +use Illuminate\Support\Facades\Validator; use App\Services\DashboardService; use App\Services\DashboardAdaptationService; use App\Services\DashboardMitigationService; +use Hash; -class ApiController extends Controller +class ApiController extends BaseController { - // protected $emissionService; + public function __construct(DashboardService $emissionService,DashboardAdaptationService $adaptationService,DashboardMitigationService $mitigationService) { - $this->emissionService = $emissionService; + $this->emissionService = $emissionService; $this->adaptationService = $adaptationService; $this->mitigationService = $mitigationService; } diff --git a/app/Http/Controllers/AuthApiController.php b/app/Http/Controllers/AuthApiController.php new file mode 100644 index 0000000..56176f6 --- /dev/null +++ b/app/Http/Controllers/AuthApiController.php @@ -0,0 +1,75 @@ +only('username', 'password'))) { + return response()->json([ + 'message' => 'Username atau password salah' + ], 401); + } + + $user = Auth::user(); + + $token = $user->createToken('mobile')->plainTextToken; + + return response()->json([ + 'token' => $token, + 'user' => $user + ]); + + // $validator = Validator::make( + // $request->all(), + // [ + // 'username' => 'required', + // 'password' => 'required', + // ]); + + // if ($validator->fails()) { + // return $this->sendError('Bad Request', null, 400); + // } + + // $credentials = array( + // 'username' => $request->username, + // 'password' => $request->password + // ); + + // $token = null; + // if (Auth::attempt($credentials)) { + // $token = $this->generateToken($request); + + // return $this->sendResponse(null, $token, null, 'Login berhasil.'); + // } + + + // return $this->sendError('Login gagal.', null, 400); + } + + /** + * Update the authenticated user's Api token. + * + * @param \Illuminate\Http\Request $request + * @return array + */ + public function generateToken(Request $request) + { + $token = Str::random(60); + + $request->user()->forceFill([ + 'api_token' => hash('sha256', $token), + ])->save(); + + return $token; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php new file mode 100644 index 0000000..1f8ff39 --- /dev/null +++ b/app/Http/Controllers/BaseController.php @@ -0,0 +1,66 @@ +middleware('auth:api', ['except' => ['login']]); + } + + /** + * success response method. + * + * @return \Illuminate\Http\Response + */ + public function sendResponse($title=null, $result, $meta = null, $message) + { + $response = [ + 'title' => $title, + 'success' => true, + 'data' => $result, + 'meta' => $meta, + 'message' => $message, + ]; + + return response()->json($response, 200); + } + + /** + * return error response. + * + * @return \Illuminate\Http\Response + */ + public function sendError($error, $errorMessages = [], $code = 404) + { + $response = [ + 'success' => false, + 'message' => $error, + ]; + + if (!empty($errorMessages)) { + $response['data'] = $errorMessages; + } + + return response()->json($response, $code); + } + + /** + * Get the guard to be used during authentication. + * + * @return \Illuminate\Contracts\Auth\Guard + */ + public function guard() + { + return Auth::guard('api'); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index aa10f16..8c235d2 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -9,10 +9,11 @@ use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; +use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable implements MustVerifyEmail { - use HasFactory, Notifiable; + use HasFactory, Notifiable,HasApiTokens; protected $guarded = []; @@ -24,7 +25,7 @@ class User extends Authenticatable implements MustVerifyEmail { return [ 'email_verified_at' => 'datetime', - 'password' => 'hashed', + 'password' => 'hashed', ]; } diff --git a/bootstrap/app.php b/bootstrap/app.php index 7b162da..ca23971 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -7,6 +7,7 @@ use Illuminate\Foundation\Configuration\Middleware; return Application::configure(basePath: dirname(__DIR__)) ->withRouting( web: __DIR__.'/../routes/web.php', + api: __DIR__.'/../routes/api.php', commands: __DIR__.'/../routes/console.php', health: '/up', ) @@ -14,5 +15,12 @@ return Application::configure(basePath: dirname(__DIR__)) // }) ->withExceptions(function (Exceptions $exceptions) { - // + $exceptions->render(function (AuthenticationException $e, Request $request) { + if ($request->is('api/*')) { + return response()->json([ + 'success' => false, + 'message' => 'Unauthenticated.', + ], Response::HTTP_UNAUTHORIZED); + } + }); })->create(); diff --git a/composer.json b/composer.json index b710598..c111c62 100644 --- a/composer.json +++ b/composer.json @@ -9,10 +9,11 @@ "php": "^8.2", "barryvdh/laravel-dompdf": "^3.1", "laravel/framework": "^11.31", + "laravel/sanctum": "^4.3", "laravel/tinker": "^2.9", "laravel/ui": "^4.6", - "phpoffice/phpword": "dev-master", "maatwebsite/excel": "^3.1", + "phpoffice/phpword": "dev-master", "realrashid/sweet-alert": "^7.2", "yajra/laravel-datatables": "^11.0" }, diff --git a/composer.lock b/composer.lock index 64f3409..6ad24ef 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "86e283900a5622c68ed57be942c2e817", + "content-hash": "38adefad015a5f22b3eed45fc1bda8ca", "packages": [ { "name": "barryvdh/laravel-dompdf", @@ -1781,6 +1781,69 @@ }, "time": "2026-05-19T00:47:18+00:00" }, + { + "name": "laravel/sanctum", + "version": "v4.3.2", + "source": { + "type": "git", + "url": "https://github.com/laravel/sanctum.git", + "reference": "2a9bccc18e9907808e0018dd15fa643937886b1e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/2a9bccc18e9907808e0018dd15fa643937886b1e", + "reference": "2a9bccc18e9907808e0018dd15fa643937886b1e", + "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-04-30T11:46:25+00:00" + }, { "name": "laravel/serializable-closure", "version": "v2.0.13", diff --git a/config/auth.php b/config/auth.php index 0ba5d5d..6ebf705 100644 --- a/config/auth.php +++ b/config/auth.php @@ -40,6 +40,10 @@ return [ 'driver' => 'session', 'provider' => 'users', ], + 'api' => [ + 'driver' => 'sanctum', + 'provider' => 'users', + ], ], /* @@ -64,11 +68,6 @@ return [ 'driver' => 'eloquent', 'model' => env('AUTH_MODEL', App\Models\User::class), ], - - // 'users' => [ - // 'driver' => 'database', - // 'table' => 'users', - // ], ], /* diff --git a/config/sanctum.php b/config/sanctum.php new file mode 100644 index 0000000..cde73cf --- /dev/null +++ b/config/sanctum.php @@ -0,0 +1,87 @@ + 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, + ], + +]; diff --git a/database/migrations/2026_07_07_032013_create_personal_access_tokens_table.php b/database/migrations/2026_07_07_032013_create_personal_access_tokens_table.php new file mode 100644 index 0000000..40ff706 --- /dev/null +++ b/database/migrations/2026_07_07_032013_create_personal_access_tokens_table.php @@ -0,0 +1,33 @@ +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'); + } +}; diff --git a/database/migrations/2026_07_07_043932_create_personal_access_tokens_table.php b/database/migrations/2026_07_07_043932_create_personal_access_tokens_table.php new file mode 100644 index 0000000..40ff706 --- /dev/null +++ b/database/migrations/2026_07_07_043932_create_personal_access_tokens_table.php @@ -0,0 +1,33 @@ +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'); + } +}; diff --git a/routes/api.php b/routes/api.php new file mode 100644 index 0000000..75accd4 --- /dev/null +++ b/routes/api.php @@ -0,0 +1,19 @@ +json([ + 'ok' => true, + ]); +}); + +Route::post('login', [AuthApiController::class,'login'])->name('api.login'); +Route::middleware('auth:api')->group(function () { + Route::post('data/inventory', [ApiController::class,'inventory'])->name('api.inventory'); + Route::post('data/adaptasi', [ApiController::class,'adaptasi'])->name('api.adaptasi'); + Route::post('data/mitigasi', [ApiController::class,'mitigasi'])->name('api.mitigasi'); +}); diff --git a/routes/modules/modules.php b/routes/modules/modules.php index 035ea0d..d5a94ed 100644 --- a/routes/modules/modules.php +++ b/routes/modules/modules.php @@ -57,6 +57,7 @@ use App\Http\Controllers\Tool\CalculationController; use App\Http\Controllers\Tool\CopyActivityController; use App\Http\Controllers\Tool\LockActivityController; use App\Http\Controllers\Tool\ProdusenCalculateController; +use App\Services\Emisi\EnergyGPCService; Route::get('dashboard',[HomeController::class,'index'])->name('index'); Route::name('pengesahan.')->prefix('pengesahan')->group(function () { @@ -119,9 +120,9 @@ Route::name('management.')->prefix('management')->group(function () { - Route::post('change_password', [AuthController::class, 'changePassword'])->name('password.update'); - Route::get('user/profile', [AuthController::class, 'show'])->name('profile.show'); - Route::get('user/profile/edit', [AuthController::class, 'edit'])->name('profile.edit'); + // Route::post('change_password', [AuthController::class, 'changePassword'])->name('password.update'); + // Route::get('user/profile', [AuthController::class, 'show'])->name('profile.show'); + // Route::get('user/profile/edit', [AuthController::class, 'edit'])->name('profile.edit'); Route::prefix('master')->group(function () { Route::prefix('agency')->group(function () { diff --git a/routes/web.php b/routes/web.php index ccaef71..56cad76 100644 --- a/routes/web.php +++ b/routes/web.php @@ -4,7 +4,6 @@ use Illuminate\Support\Facades\Route; use App\Http\Middleware\Session; use App\Http\Controllers\FrontController; use App\Http\Controllers\AjaxController; -use App\Http\Controllers\ApiController; use App\Http\Controllers\Auth\CustomLoginController; use App\Http\Controllers\Auth\CustomRegisterController; @@ -88,11 +87,7 @@ Route::get('artisan/{command}', function ($command) { throw new NotFoundHttpException(); })->name('artisan'); -Route::prefix('api')->group(function () { - Route::get('/data/inventory', [ApiController::class,'inventory'])->name('api.inventory'); - Route::get('/data/adaptasi', [ApiController::class,'adaptasi'])->name('api.adaptasi'); - Route::get('/data/mitigasi', [ApiController::class,'mitigasi'])->name('api.mitigasi'); -}); + // Auth::routes();