From 1dfcf9b2f57e006f65e030e13929aa2394da14de Mon Sep 17 00:00:00 2001 From: Irwan Cahyono Date: Fri, 20 Feb 2026 17:04:36 +0700 Subject: [PATCH] update api --- app/Enums/KondisiTanaman.php | 10 ++ app/Enums/Role.php | 11 ++ app/Enums/StatusLahan.php | 10 ++ app/Http/Controllers/Api/AuthController.php | 44 ++++++ app/Http/Controllers/Api/LahanController.php | 140 ++++++++++++++++++ .../Controllers/Api/ProfileController.php | 78 ++++++++++ app/Models/Komoditas/Komoditas.php | 17 +++ app/Models/Map/Lahan.php | 38 +++++ app/Models/Map/Tanaman.php | 25 ++++ app/Models/Profile.php | 22 +++ app/Models/User.php | 11 ++ app/Models/Wilayah/DesaKelurahan.php | 3 + app/Models/Wilayah/KabupatenKota.php | 3 + app/Models/Wilayah/Kecamatan.php | 3 + app/Models/Wilayah/Provinsi.php | 3 + app/Providers/AppServiceProvider.php | 19 ++- bootstrap/app.php | 2 + .../0001_01_01_000000_create_users_table.php | 23 +-- ...026_02_19_172905_create_profiles_table.php | 91 ++++++++++++ ...26_02_20_110207_create_komoditas_table.php | 34 +++++ .../2026_02_20_131504_create_lahans_table.php | 66 +++++++++ routes/api.php | 15 +- 22 files changed, 655 insertions(+), 13 deletions(-) create mode 100644 app/Enums/KondisiTanaman.php create mode 100644 app/Enums/Role.php create mode 100644 app/Enums/StatusLahan.php create mode 100644 app/Http/Controllers/Api/LahanController.php create mode 100644 app/Http/Controllers/Api/ProfileController.php create mode 100644 app/Models/Komoditas/Komoditas.php create mode 100644 app/Models/Map/Lahan.php create mode 100644 app/Models/Map/Tanaman.php create mode 100644 app/Models/Profile.php create mode 100644 database/migrations/2026_02_19_172905_create_profiles_table.php create mode 100644 database/migrations/2026_02_20_110207_create_komoditas_table.php create mode 100644 database/migrations/2026_02_20_131504_create_lahans_table.php diff --git a/app/Enums/KondisiTanaman.php b/app/Enums/KondisiTanaman.php new file mode 100644 index 0000000..ee29a12 --- /dev/null +++ b/app/Enums/KondisiTanaman.php @@ -0,0 +1,10 @@ +validate([ + 'name' => ['required', 'string', 'max:255'], + 'email' => ['required', 'email'], + 'password' => ['required', 'string', 'min:8', 'confirmed'], + 'role' => ['string'], + ]); + + $user = User::create([ + 'name' => $credentials['name'], + 'email' => $credentials['email'], + 'password' => Hash::make($credentials['password']), + 'role' => $credentials['role'], + ]); + + $profile = Profile::create([ + 'nama' => $credentials['name'], + 'email' => $credentials['email'], + 'role' => $credentials['role'], + 'user_id' => $user->id, + ]); + + if (!Auth::attempt($credentials)) { + return response()->json([ + 'message' => 'Email dan/atau password tidak sesuai' + ], 401); + } + + $user = Auth::user(); + $user->tokens()->delete(); + + $token = $user->createToken('mobile-token')->plainTextToken; + + return response()->json([ + 'access_token' => $token, + 'token_type' => 'Bearer', + ]); + } + public function me(Request $request): JSONResponse { return response()->json($request->user()); diff --git a/app/Http/Controllers/Api/LahanController.php b/app/Http/Controllers/Api/LahanController.php new file mode 100644 index 0000000..87ee3d2 --- /dev/null +++ b/app/Http/Controllers/Api/LahanController.php @@ -0,0 +1,140 @@ +profile; + + $listLahan = Lahan::where('profile_id', $profile->id)->get(); + + return response()->json($listLahan); + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request): JsonResponse + { + Gate::authorize('petani'); + + $user = Auth::user(); + $profile = $user->profile; + + $validated = $request->validate([ + 'nama' => ['required', 'string', 'max:255'], + 'status_kepemilikan' => ['required', 'string'], + 'desa_kelurahan_id' => ['numeric'], + 'path' => ['string'], + 'luas_lahan' => ['numeric'], + ]); + + $lahan = Lahan::create([ + 'nama' => $validated['nama'], + 'status_kepemilikan' => $validated['status_kepemilikan'], + 'profile_id' => $profile->id, + 'desa_kelurahan_id' => $validated['desa_kelurahan_id'] ?? null, + 'path' => $validated['path'] ?? null, + 'luas_lahan' => $validated['luas_lahan'] ?? null, + ]); + + return response()->json($lahan, 201); + } + + /** + * Display the specified resource. + */ + public function show(string $id) + { + Gate::authorize('petani'); + + $lahan = Lahan::findOrFail($id); + + return response()->json($lahan); + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, string $id) + { + Gate::authorize('petani'); + + $lahan = Lahan::findOrFail($id); + + $validated = $request->validate([ + 'nama' => ['required', 'string', 'max:255'], + 'status_kepemilikan' => ['required', 'string'], + 'desa_kelurahan_id' => ['numeric'], + 'path' => ['string'], + 'luas_lahan' => ['numeric'], + ]); + + $lahan->update($validated); + + return response()->json($lahan); + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + Gate::authorize('petani'); + + $lahan = Lahan::findOrFail($id); + $lahan->delete(); + + return response()->json(null, 204); + } + + public function batchUpsert(Request $request): JsonResponse + { + Gate::authorize('petani'); + $user = Auth::user(); + $profile = $user->profile; + + $validated = $request->validate([ + 'lahans' => ['required', 'array'], + 'lahans.*.id' => ['numeric'], + 'lahans.*.nama' => ['required', 'string', 'max:255'], + 'lahans.*.status_kepemilikan' => ['required', 'numeric'], + 'lahans.*.desa_kelurahan_id' => ['numeric'], + 'lahans.*.path' => ['string'], + 'lahans.*.luas_lahan' => ['numeric'], + ]); + + $lahans = []; + foreach ($validated['lahans'] as $lahan) { + $lahans[] = [ + 'id' => $lahan['id'] ?? null, + 'nama' => $lahan['nama'], + 'status_kepemilikan' => $lahan['status_kepemilikan'], + 'profile_id' => $profile->id, + 'desa_kelurahan_id' => $lahan['desa_kelurahan_id'] ?? null, + 'path' => $lahan['path'] ?? null, + 'luas_lahan' => $lahan['luas_lahan'] ?? null, + ]; + } + + Lahan::upsert($lahans, ['id'], ['nama', 'status_kepemilikan', 'profile_id', 'desa_kelurahan_id', 'path', 'luas_lahan']); + + return response()->json(null, 204); + } +} diff --git a/app/Http/Controllers/Api/ProfileController.php b/app/Http/Controllers/Api/ProfileController.php new file mode 100644 index 0000000..2e2c592 --- /dev/null +++ b/app/Http/Controllers/Api/ProfileController.php @@ -0,0 +1,78 @@ +first(); + return response()->json($profile); + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(string $id) + { + + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request) + { + $validated = $request->validate([ + 'nama' => ['required', 'string', 'max:255'], + 'email' => ['required', 'email'], + 'telepon' => ['nullable', 'string'], + 'alamat' => ['nullable', 'string'], + ]); + + $profile = Profile::where('user_id', Auth::id())->first(); + $profile->update($validated); + + return response()->json($profile); + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + // + } +} diff --git a/app/Models/Komoditas/Komoditas.php b/app/Models/Komoditas/Komoditas.php new file mode 100644 index 0000000..631aa25 --- /dev/null +++ b/app/Models/Komoditas/Komoditas.php @@ -0,0 +1,17 @@ + \App\Enums\StatusLahan::class, + ]; + } + + public function tanaman(): HasMany + { + return $this->hasMany(Tanaman::class, 'lahan_id'); + } +} diff --git a/app/Models/Map/Tanaman.php b/app/Models/Map/Tanaman.php new file mode 100644 index 0000000..9bdaa67 --- /dev/null +++ b/app/Models/Map/Tanaman.php @@ -0,0 +1,25 @@ + 'datetime', 'password' => 'hashed', + 'role' => \App\Enums\Role::class, ]; } + + protected function profile(): HasOne + { + return $this->hasOne(\App\Models\Profile::class); + } } diff --git a/app/Models/Wilayah/DesaKelurahan.php b/app/Models/Wilayah/DesaKelurahan.php index 4884607..a2861d9 100644 --- a/app/Models/Wilayah/DesaKelurahan.php +++ b/app/Models/Wilayah/DesaKelurahan.php @@ -3,9 +3,12 @@ namespace App\Models\wilayah; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\SoftDeletes; class DesaKelurahan extends Model { + use SoftDeletes; + protected $table = 'master_desa_kelurahan'; protected $fillable = [ diff --git a/app/Models/Wilayah/KabupatenKota.php b/app/Models/Wilayah/KabupatenKota.php index 1a864fe..f71adee 100644 --- a/app/Models/Wilayah/KabupatenKota.php +++ b/app/Models/Wilayah/KabupatenKota.php @@ -3,9 +3,12 @@ namespace App\Models\Wilayah; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\SoftDeletes; class KabupatenKota extends Model { + use SoftDeletes; + protected $table = 'master_kabupaten_kota'; protected $fillable = [ diff --git a/app/Models/Wilayah/Kecamatan.php b/app/Models/Wilayah/Kecamatan.php index 5d4db35..55b8ca7 100644 --- a/app/Models/Wilayah/Kecamatan.php +++ b/app/Models/Wilayah/Kecamatan.php @@ -3,9 +3,12 @@ namespace App\Models\Wilayah; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\SoftDeletes; class Kecamatan extends Model { + use SoftDeletes; + protected $table = 'master_kecamatan'; protected $fillable = [ diff --git a/app/Models/Wilayah/Provinsi.php b/app/Models/Wilayah/Provinsi.php index 31dbc31..9f245da 100644 --- a/app/Models/Wilayah/Provinsi.php +++ b/app/Models/Wilayah/Provinsi.php @@ -3,9 +3,12 @@ namespace App\Models\Wilayah; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\SoftDeletes; class Provinsi extends Model { + use SoftDeletes; + protected $table = 'master_provinsi'; protected $fillable = [ diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 452e6b6..78c2f14 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -3,6 +3,9 @@ namespace App\Providers; use Illuminate\Support\ServiceProvider; +use Illuminate\Support\Facades\Gate; +use App\Models\User; +use App\Enums\Role; class AppServiceProvider extends ServiceProvider { @@ -19,6 +22,20 @@ class AppServiceProvider extends ServiceProvider */ public function boot(): void { - // + Gate::define('super-admin', function (User $user) { + return $user->role === Role::SUPER_ADMIN; + }); + + Gate::define('admin', function (User $user) { + return $user->role === Role::ADMIN; + }); + + Gate::define('fasilitator', function (User $user) { + return $user->role === Role::FASILITATOR; + }); + + Gate::define('petani', function (User $user) { + return $user->role === Role::PETANI; + }); } } diff --git a/bootstrap/app.php b/bootstrap/app.php index c3928c5..a81640d 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -3,6 +3,8 @@ use Illuminate\Foundation\Application; use Illuminate\Foundation\Configuration\Exceptions; use Illuminate\Foundation\Configuration\Middleware; +use Illuminate\Http\Request; +use Illuminate\Auth\AuthenticationException; return Application::configure(basePath: dirname(__DIR__)) ->withRouting( diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php index 5cf5277..2872310 100644 --- a/database/migrations/0001_01_01_000000_create_users_table.php +++ b/database/migrations/0001_01_01_000000_create_users_table.php @@ -15,6 +15,7 @@ return new class extends Migration $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->rememberToken(); + $table->string('role')->nullable(); $table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null'); $table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null'); $table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null'); @@ -22,16 +23,16 @@ return new class extends Migration $table->softDeletes(); }); - Schema::create('user_roles', function (Blueprint $table) { - $table->id(); - $table->foreignId('user_id')->unique()->constrained('users')->onDelete('cascade'); - $table->enum('role_name', ['Super Admin', 'Admin', 'Koordinator', 'Penyuluh', 'Pendamping', 'Petani'])->default('Petani'); - $table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null'); - $table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null'); - $table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null'); - $table->timestamps(); - $table->softDeletes(); - }); + // Schema::create('user_roles', function (Blueprint $table) { + // $table->id(); + // $table->foreignId('user_id')->unique()->constrained('users')->onDelete('cascade'); + // $table->enum('role_name', ['Super Admin', 'Admin', 'Koordinator', 'Penyuluh', 'Pendamping', 'Petani'])->default('Petani'); + // $table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null'); + // $table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null'); + // $table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null'); + // $table->timestamps(); + // $table->softDeletes(); + // }); Schema::create('password_reset_tokens', function (Blueprint $table) { $table->string('email')->primary(); @@ -52,7 +53,7 @@ return new class extends Migration public function down(): void { Schema::dropIfExists('users'); - Schema::dropIfExists('user_roles'); + // Schema::dropIfExists('user_roles'); Schema::dropIfExists('password_reset_tokens'); Schema::dropIfExists('sessions'); } diff --git a/database/migrations/2026_02_19_172905_create_profiles_table.php b/database/migrations/2026_02_19_172905_create_profiles_table.php new file mode 100644 index 0000000..1423340 --- /dev/null +++ b/database/migrations/2026_02_19_172905_create_profiles_table.php @@ -0,0 +1,91 @@ +id(); + + $table->string('nama')->nullable(); + + $table->foreignId('desa_kelurahan_id')->nullable()->constrained('master_desa_kelurahan'); + + $table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null'); + $table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null'); + $table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null'); + + $table->timestamps(); + $table->softDeletes(); + }); + + Schema::create('keluarga', function (Blueprint $table) { + $table->id(); + + $table->string('kk')->nullable(); + + $table->foreignId('adat_id')->nullable()->constrained('adat'); + + $table->foreignId('desa_kelurahan_id')->nullable()->constrained('master_desa_kelurahan'); + + $table->string('path_kk', 500)->nullable(); + $table->string('file_kk')->nullable(); + + $table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null'); + $table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null'); + $table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null'); + + $table->timestamps(); + $table->softDeletes(); + }); + + Schema::create('profiles', function (Blueprint $table) { + $table->id(); + $table->foreignId('user_id')->constrained(); + + $table->string('role')->nullable(); + $table->string('nama'); + $table->string('email'); + $table->string('telepon')->nullable(); + $table->string('alamat')->nullable(); + + $table->string('kk')->nullable(); + $table->string('ktp')->nullable(); + + $table->foreignId('desa_kelurahan_id')->nullable()->constrained('master_desa_kelurahan'); + + $table->string('path_kk', 500)->nullable(); + $table->string('file_kk')->nullable(); + $table->string('path_ktp', 500)->nullable(); + $table->string('file_ktp')->nullable(); + + $table->foreignId('keluarga_id')->nullable()->constrained('keluarga')->onDelete('set null'); + + $table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null'); + $table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null'); + $table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null'); + + $table->timestamps(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('profiles'); + Schema::dropIfExists('keluarga'); + Schema::dropIfExists('adat'); + } +}; diff --git a/database/migrations/2026_02_20_110207_create_komoditas_table.php b/database/migrations/2026_02_20_110207_create_komoditas_table.php new file mode 100644 index 0000000..4238135 --- /dev/null +++ b/database/migrations/2026_02_20_110207_create_komoditas_table.php @@ -0,0 +1,34 @@ +id(); + $table->string('nama'); + + $table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null'); + $table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null'); + $table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null'); + + $table->timestamps(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('master_komoditas'); + } +}; diff --git a/database/migrations/2026_02_20_131504_create_lahans_table.php b/database/migrations/2026_02_20_131504_create_lahans_table.php new file mode 100644 index 0000000..8780a83 --- /dev/null +++ b/database/migrations/2026_02_20_131504_create_lahans_table.php @@ -0,0 +1,66 @@ +id(); + $table->string('nama'); + $table->string('status_kepemilikan'); + + $table->foreignId('profile_id')->constrained('profiles'); + $table->foreignId('keluarga_pengelola_id')->nullable()->constrained('keluarga')->onDelete('set null'); + $table->foreignId('adat_pemilik_id')->nullable()->constrained('keluarga')->onDelete('set null'); + + $table->foreignId('desa_kelurahan_id')->nullable()->constrained('master_desa_kelurahan'); + $table->text('path')->nullable(); + + $table->integer('luas_lahan')->nullable(); + + $table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null'); + $table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null'); + $table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null'); + + $table->timestamps(); + $table->softDeletes(); + }); + + Schema::create('map_tanaman', function (Blueprint $table) { + $table->id(); + $table->string('nama'); + + $table->foreignId('komoditas_id')->constrained('master_komoditas')->onDelete('set null'); + $table->foreignId('profile_id')->constrained('profiles')->onDelete('set null'); + $table->foreignId('lahan_id')->constrained('map_lahan')->onDelete('set null'); + + $table->float('latitude')->nullable(); + $table->float('longitude')->nullable(); + + $table->string('kondisi_tanaman')->nullable(); + + $table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null'); + $table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null'); + $table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null'); + + $table->timestamps(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('map_tanaman'); + Schema::dropIfExists('map_lahan'); + } +}; diff --git a/routes/api.php b/routes/api.php index d163db2..57c5593 100644 --- a/routes/api.php +++ b/routes/api.php @@ -3,7 +3,9 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; -Route::post('/auth/login', [\App\Http\Controllers\Api\AuthController::class, 'login']); +Route::post('/auth/login', [\App\Http\Controllers\Api\AuthController::class, 'login']) + ->middleware('throttle:5,1'); +Route::post('/auth/signup', [\App\Http\Controllers\Api\AuthController::class, 'signup']); Route::get('/user', function (Request $request) { return $request->user(); @@ -15,4 +17,15 @@ Route::middleware('auth:sanctum') ->group(function () { Route::get('/me', [\App\Http\Controllers\Api\AuthController::class, 'me']); Route::post('/logout', [\App\Http\Controllers\Api\AuthController::class, 'logout']); + + Route::get('/profile', [\App\Http\Controllers\Api\ProfileController::class, 'show']); + Route::put('/profile', [\App\Http\Controllers\Api\ProfileController::class, 'update']); +}); + +Route::middleware('auth:sanctum') + ->name('map.') + ->prefix('map') + ->group(function () { + + Route::apiResource('/lahan', \App\Http\Controllers\Api\LahanController::class)->except(['create', 'edit']); });