mirror of
https://github.com/Deathgarden-Rebirth/Deathgarden_Rebirth-Rewrite.git
synced 2026-04-25 16:15:23 -05:00
Add file manager functionality
This commit is contained in:
parent
679a132407
commit
8acf308554
1
dist/.gitignore
vendored
1
dist/.gitignore
vendored
|
|
@ -20,3 +20,4 @@ yarn-error.log
|
|||
/.vscode
|
||||
_ide_helper.php
|
||||
_ide_helper_models.php
|
||||
/public/game-files
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\GameFile;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
|
|
@ -38,4 +40,9 @@ public function getBattleyePatch()
|
|||
|
||||
return response()->download($disk->path('bottleEye/BEClient_x64.dll'));
|
||||
}
|
||||
|
||||
public function getGameFileList() : JsonResponse
|
||||
{
|
||||
return response()->json(GameFile::select(['name', 'hash', 'game_path'])->latest()->get(), 200);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
93
dist/app/Http/Controllers/Web/GameFileController.php
vendored
Normal file
93
dist/app/Http/Controllers/Web/GameFileController.php
vendored
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\GameFile;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class GameFileController extends Controller
|
||||
{
|
||||
public function store(Request $request)
|
||||
{
|
||||
if (!auth()->user()->can(\App\Enums\Auth\Permissions::FILE_UPLOAD->value)) {
|
||||
throw new AuthorizationException();
|
||||
}
|
||||
|
||||
// Validate the incoming request with appropriate rules for file uploads
|
||||
$request->validate([
|
||||
'files.*' => 'required|file',
|
||||
'gamepath.*' => 'required|string',
|
||||
]);
|
||||
|
||||
//dd($request);
|
||||
|
||||
$duplicateFiles = [];
|
||||
$overwrittenFiles = [];
|
||||
$uploadedFiles = [];
|
||||
|
||||
// Handle file upload logic
|
||||
if ($request->hasFile('files')) {
|
||||
for($i = 0; $i < count($request->file('files')); $i++) {
|
||||
$file = $request->file('files')[$i];
|
||||
|
||||
$filename = $file->getClientOriginalName();
|
||||
$filehash = str(hash_file('sha256', $file->getRealPath()))->upper();
|
||||
|
||||
$gameFile = GameFile::where('name', $filename)->first() ?? new GameFile;
|
||||
|
||||
if ($gameFile->hash == $filehash) {
|
||||
$duplicateFiles[] = $gameFile->name;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($gameFile->id)) {
|
||||
$overwrittenFiles[] = $gameFile->name;
|
||||
}
|
||||
|
||||
$gameFile->name = $filename;
|
||||
$gameFile->hash = $filehash;
|
||||
|
||||
$file->storeAs('', $gameFile->name, ['disk' => 'dg_public']);
|
||||
$uploadedFiles[] = $gameFile->name;
|
||||
$gameFile->game_path = $request->game_path[$i];
|
||||
$gameFile->save();
|
||||
}
|
||||
}
|
||||
|
||||
if (count($uploadedFiles) === 0) {
|
||||
Session::flash('alert-error', 'No files were uploaded');
|
||||
}
|
||||
|
||||
// Optionally, you can return a response
|
||||
if (count($overwrittenFiles) > 0) {
|
||||
Session::flash('alert-warning', 'Files uploaded successfully. The following files was overwritten: <br>' . implode('<br>', $overwrittenFiles));
|
||||
//return response()->json(['message' => 'Only some files was uploaded successfully. The following files already exist: ' . implode(', ', $overwrittenFiles)]);
|
||||
}
|
||||
|
||||
if (count($duplicateFiles) > 0) {
|
||||
Session::flash('alert-warning', 'The following files already exist: <br>' . implode('<br>', $duplicateFiles));
|
||||
//return response()->json(['message' => 'Only some files was uploaded successfully. The following files already exist: ' . implode(', ', $duplicateFiles)]);
|
||||
}
|
||||
|
||||
if (count($uploadedFiles) > 0) {
|
||||
Session::flash('alert-success', 'Files uploaded successfully <br>' . implode('<br>', $uploadedFiles));
|
||||
//return response()->json(['message' => 'Files uploaded successfully']);
|
||||
}
|
||||
|
||||
return redirect()->back();
|
||||
//return response()->json(['message' => 'Files uploaded successfully']);
|
||||
}
|
||||
|
||||
public function index() : View {
|
||||
if (!auth()->user()->can(\App\Enums\Auth\Permissions::FILE_UPLOAD->value)) {
|
||||
throw new AuthorizationException();
|
||||
}
|
||||
|
||||
return view('file-manager', ['files' => GameFile::latest()->get()]);
|
||||
}
|
||||
}
|
||||
11
dist/app/Models/GameFile.php
vendored
Normal file
11
dist/app/Models/GameFile.php
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class GameFile extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
}
|
||||
5
dist/config/filesystems.php
vendored
5
dist/config/filesystems.php
vendored
|
|
@ -44,6 +44,11 @@
|
|||
'throw' => false,
|
||||
],
|
||||
|
||||
'dg_public' => [
|
||||
'driver' => 'local',
|
||||
'root' => public_path().'/game-files',
|
||||
],
|
||||
|
||||
'patches' => [
|
||||
'driver' => 'local',
|
||||
'root' => resource_path('patches'),
|
||||
|
|
|
|||
34
dist/database/migrations/2024_02_24_191110_create_game_files_table.php
vendored
Normal file
34
dist/database/migrations/2024_02_24_191110_create_game_files_table.php
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?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('game_files', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name', 128);
|
||||
$table->string('game_path');
|
||||
$table->string('hash', 64); // SHA-256 hash is 64 characters long
|
||||
$table->integer('version')->default(1); // Default version is 1
|
||||
$table->tinyInteger('action')->default(1); //add = 1, delete = 2
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['name', 'hash', 'version']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('game_files');
|
||||
}
|
||||
};
|
||||
5
dist/resources/views/components/inputs/file-input.blade.php
vendored
Normal file
5
dist/resources/views/components/inputs/file-input.blade.php
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<input type="file"
|
||||
{{ $attributes->merge(['class' =>
|
||||
"block w-full text-sm text-slate-500 file:mr-4 file:py-2.5 file:px-4 file:rounded-md file:border-0 file:border-gray-600 file:font-semibold file:cursor-pointer file:bg-gray-800/75 file:text-gray-300 hover:file:bg-gray-900 hover:file:text-white"
|
||||
])}}
|
||||
/>
|
||||
143
dist/resources/views/file-manager.blade.php
vendored
Normal file
143
dist/resources/views/file-manager.blade.php
vendored
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
<x-raw-layout>
|
||||
<!-- Nothing in life is to be feared, it is only to be understood. Now is the time to understand more, so that we may fear less. - Marie Curie -->
|
||||
<h1 class="text-4xl font-semilight p-10">Deathgarden file manager</h1>
|
||||
@if(Session::has('alert-error'))
|
||||
<x-alerts.error heading="An error occured">{!! Session::get('alert-error') !!}</x-alerts.error>
|
||||
@endif
|
||||
@if(Session::has('alert-success'))
|
||||
<x-alerts.success heading="Success">{!! Session::get('alert-success') !!}</x-alerts.success>
|
||||
@endif
|
||||
@if(Session::has('alert-warning'))
|
||||
<x-alerts.warning heading="Warning">{!! Session::get('alert-warning') !!}</x-alerts.warning>
|
||||
@endif
|
||||
<div class="flex flex-col items-center justify-center p-12">
|
||||
<div class="mx-auto w-full max-w-7xl">
|
||||
<x-tables.table>
|
||||
<x-tables.thead>
|
||||
<x-tables.th>Name</x-tables.th>
|
||||
<x-tables.th>Hash</x-tables.th>
|
||||
<x-tables.th>Size</x-tables.th>
|
||||
<x-tables.th>Last update</x-tables.th>
|
||||
<x-tables.th>Actions</x-tables.th>
|
||||
</x-tables.thead>
|
||||
<tbody>
|
||||
@foreach($files as $file)
|
||||
<tr class="text-center">
|
||||
<x-tables.td>
|
||||
{{ $file->name }}
|
||||
</x-tables.td>
|
||||
<x-tables.td>
|
||||
{{ $file->hash }}
|
||||
</x-tables.td>
|
||||
<x-tables.td>
|
||||
@if(Storage::disk('dg_public')->exists($file->name))
|
||||
{{ Storage::disk('dg_public')->size($file->name) / 1000 }} kB
|
||||
@else
|
||||
Error while fetching file
|
||||
@endif
|
||||
</x-tables.td>
|
||||
<x-tables.td>
|
||||
{{ $file->updated_at }}
|
||||
</x-tables.td>
|
||||
<x-tables.td>
|
||||
<!-- <a href="">Delete</a> -->
|
||||
</x-tables.td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</x-tables.table>
|
||||
</div>
|
||||
|
||||
<div class="mx-auto w-full max-w-7xl mt-8">
|
||||
<form action="{{ route('file.store') }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
<div id="fileInputsContainer">
|
||||
<div class="flex flex-wrap gap-4">
|
||||
<div class="w-3/5 flex items-center mb-2">
|
||||
<x-inputs.text-input name="game_path[]" />
|
||||
</div>
|
||||
<div class="flex items-center mb-2">
|
||||
<x-inputs.file-input name="files[]" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end mt-4">
|
||||
<div class="w-auto mx-2">
|
||||
<button type="button" id="addFileInput" class="inline-flex rounded-md bg-gray-800/75 px-6 py-2 font-semibold text-gray-300 hover:text-white transition focus:outline-none focus-visible:ring-2 focus-visible:ring-[#BD92F5] focus-visible:ring-offset-2 focus-visible:ring-offset-black">
|
||||
Add More Files
|
||||
</button>
|
||||
</div>
|
||||
<div class="w-auto mx-2">
|
||||
<button class="inline-flex rounded-md bg-gray-800/75 px-6 py-2 font-semibold text-gray-300 hover:text-white transition focus:outline-none focus-visible:ring-2 focus-visible:ring-[#BD92F5] focus-visible:ring-offset-2 focus-visible:ring-offset-black">
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function addFileInputEventListener(fileInput) {
|
||||
fileInput.addEventListener('change', function(event) {
|
||||
var selectedFile = event.target.files[0];
|
||||
var textInput = event.target.closest('.flex-wrap').querySelector('input[name="game_path[]"]');
|
||||
if (textInput) {
|
||||
if (selectedFile) {
|
||||
var fileName = selectedFile.name;
|
||||
fileName = examineFilePaths(fileName);
|
||||
textInput.value = fileName;
|
||||
} else {
|
||||
textInput.value = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function examineFilePaths(fileName) {
|
||||
switch (fileName) {
|
||||
case 'TheExit_BE.exe':
|
||||
return './TheExit/Binaries/Win64/' + fileName;
|
||||
case 'BEClient_x64.dll':
|
||||
return './TheExit/Binaries/Win64/BattlEye/' + fileName;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
var extension = fileName.split('.').pop();
|
||||
switch (extension) {
|
||||
case 'pak':
|
||||
return './TheExit/Content/Paks/' + fileName;
|
||||
case 'sig':
|
||||
return './TheExit/Content/Paks/' + fileName;
|
||||
default:
|
||||
return './' + fileName;
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('addFileInput').addEventListener('click', function() {
|
||||
var container = document.getElementById('fileInputsContainer');
|
||||
var newInput = document.createElement('div');
|
||||
newInput.classList.add('flex', 'flex-wrap', 'gap-4');
|
||||
newInput.innerHTML = `
|
||||
<div class="w-3/5 flex items-center mb-2">
|
||||
<x-inputs.text-input name="game_path[]" />
|
||||
</div>
|
||||
<div class="flex items-center mb-2">
|
||||
<x-inputs.file-input name="files[]" />
|
||||
</div>
|
||||
`;
|
||||
container.appendChild(newInput);
|
||||
|
||||
// Add event listener to file input of the newly created input group
|
||||
var fileInput = newInput.querySelector('input[name="files[]"]');
|
||||
addFileInputEventListener(fileInput);
|
||||
});
|
||||
|
||||
// Add event listener to file input of the initial input group
|
||||
var initialFileInput = document.querySelector('input[name="files[]"]');
|
||||
addFileInputEventListener(initialFileInput);
|
||||
</script>
|
||||
</x-raw-layout>
|
||||
2
dist/routes/api.php
vendored
2
dist/routes/api.php
vendored
|
|
@ -19,6 +19,8 @@
|
|||
Route::get('patch/battleye', [PatchController::class, 'getBattleyePatch']);
|
||||
});
|
||||
|
||||
Route::get('patch/files', [PatchController::class, 'getGameFileList']);
|
||||
|
||||
Route::fallback(function () {
|
||||
return response('route not found', 404);
|
||||
});
|
||||
5
dist/routes/web.php
vendored
5
dist/routes/web.php
vendored
|
|
@ -32,6 +32,11 @@
|
|||
return \Inertia\Inertia::render('Dashboard');
|
||||
});
|
||||
|
||||
Route::middleware('auth')->group(function () {
|
||||
Route::get('/admin/file-manager', [\App\Http\Controllers\Web\GameFileController::class, 'index']);
|
||||
Route::post('/admin/file-manager', [\App\Http\Controllers\Web\GameFileController::class, 'store'])->name('file.store');
|
||||
});
|
||||
|
||||
Route::middleware('verify_migration_key')->get('/migrate-database', function () {
|
||||
Artisan::call('migrate --no-interaction');
|
||||
print Artisan::output();
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user