diff --git a/app/Http/Controllers/AppVersionController.php b/app/Http/Controllers/AppVersionController.php new file mode 100644 index 0000000..85b7c8d --- /dev/null +++ b/app/Http/Controllers/AppVersionController.php @@ -0,0 +1,93 @@ +json([ + 'data' => AppVersion::with('image')->orderByDesc('version_code')->get(), + ]); + } + + // App side: the newest version (highest version_code) for update checks. + public function latest() + { + return response()->json([ + 'data' => AppVersion::with('image')->orderByDesc('version_code')->first(), + ]); + } + + public function store(Request $request) + { + $data = $request->validate([ + 'title' => 'required|string|max:255', + 'description' => 'nullable|string', + 'version_name' => 'required|string|max:255', + 'version_code' => 'required|integer|min:0', + 'link' => 'nullable|string|max:500', + 'image_id' => 'nullable|exists:images,id', + 'image' => 'nullable|file|extensions:jpg,jpeg,png,gif,webp,bmp,svg|max:8192', + ]); + + // An uploaded image file takes precedence over a provided image_id. + $data['image_id'] = $this->uploadedImageId($request) ?? ($data['image_id'] ?? null); + + $version = AppVersion::create($data); + + return response()->json([ + 'message' => 'Version created successfully', + 'data' => $version->load('image'), + ], 201); + } + + public function show($id) + { + return response()->json( + AppVersion::with('image')->findOrFail($id) + ); + } + + public function update(Request $request, $id) + { + $version = AppVersion::findOrFail($id); + + $data = $request->validate([ + 'title' => 'sometimes|string|max:255', + 'description' => 'nullable|string', + 'version_name' => 'sometimes|string|max:255', + 'version_code' => 'sometimes|integer|min:0', + 'link' => 'nullable|string|max:500', + 'image_id' => 'nullable|exists:images,id', + 'image' => 'nullable|file|extensions:jpg,jpeg,png,gif,webp,bmp,svg|max:8192', + ]); + + // An uploaded image file takes precedence over a provided image_id. + if (($uploadedImageId = $this->uploadedImageId($request)) !== null) { + $data['image_id'] = $uploadedImageId; + } + + $version->update($data); + + return response()->json([ + 'message' => 'Version updated successfully', + 'data' => $version->load('image'), + ]); + } + + public function destroy($id) + { + $version = AppVersion::findOrFail($id); + $version->delete(); + + return response()->json(['message' => 'Version deleted successfully']); + } +} diff --git a/app/Models/AppVersion.php b/app/Models/AppVersion.php new file mode 100644 index 0000000..60c3d3d --- /dev/null +++ b/app/Models/AppVersion.php @@ -0,0 +1,22 @@ + 'integer', + ]; + + public function image(): BelongsTo + { + return $this->belongsTo(Image::class); + } +} diff --git a/database/migrations/2026_06_24_110000_create_app_versions_table.php b/database/migrations/2026_06_24_110000_create_app_versions_table.php new file mode 100644 index 0000000..618ed58 --- /dev/null +++ b/database/migrations/2026_06_24_110000_create_app_versions_table.php @@ -0,0 +1,27 @@ +id(); + $table->foreignId('image_id')->nullable()->constrained('images')->nullOnDelete(); + $table->string('title'); + $table->text('description')->nullable(); + $table->string('version_name'); // e.g. "1.2.0" + $table->unsignedInteger('version_code'); // monotonic build number, e.g. 42 + $table->string('link')->nullable(); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('app_versions'); + } +}; diff --git a/routes/api.php b/routes/api.php index aa7f129..c1850fb 100644 --- a/routes/api.php +++ b/routes/api.php @@ -19,6 +19,7 @@ use App\Http\Controllers\FaqCategoryController; use App\Http\Controllers\SliderController; use App\Http\Controllers\AnnouncementController; +use App\Http\Controllers\AppVersionController; use App\Http\Controllers\SceneController; use App\Http\Controllers\ThemeController; use App\Http\Controllers\BellSoundController; @@ -208,6 +209,15 @@ Route::post('/announcements/{id}', [AnnouncementController::class, 'update']); // update (multipart: image) Route::delete('/announcements/{id}', [AnnouncementController::class, 'destroy']); // delete + /// app versions + Route::get('/versions/latest', [AppVersionController::class, 'latest']); // newest version (app update check) + Route::get('/versions', [AppVersionController::class, 'index']); // list all (admin) + Route::post('/versions', [AppVersionController::class, 'store']); // create + Route::get('/versions/{id}', [AppVersionController::class, 'show']); // single + Route::put('/versions/{id}', [AppVersionController::class, 'update']); // update (json) + Route::post('/versions/{id}', [AppVersionController::class, 'update']); // update (multipart: image) + Route::delete('/versions/{id}', [AppVersionController::class, 'destroy']); // delete + /// themes (color themes a scene can use) Route::get('/themes', [ThemeController::class, 'index']);