create profile api's with tests

This commit is contained in:
2024-08-20 08:04:33 +00:00
committed by Hamidreza Ranjbarpour
parent f1626a9d67
commit c710183aa9
18 changed files with 979 additions and 83 deletions

107
app/Facades/File/File.php Normal file
View File

@@ -0,0 +1,107 @@
<?php
namespace App\Facades\File;
use Illuminate\Support\Facades\Storage;
use Illuminate\Http\File as HttpFile;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Str;
class File
{
public function save(HttpFile|UploadedFile $file, string $path, string $name = null, string $disk = 'public'): string
{
return Storage::disk($disk)->putFileAs(
$path,
$file,
$name ?? Str::uuid() . '.' . $file->extension()
);
}
/**
* @param string $filePath
* @param bool $url_is_absolute
* @param string $disk
* @return void
* @throws \InvalidArgumentException when filePath is empty.
*/
public function delete(string $filePath, bool $url_is_absolute = false, string $disk = 'public'): void
{
if (!$filePath){
throw new \InvalidArgumentException('File path is invalid.');
}
if ($url_is_absolute) {
$needle = 'storage/';
$pos = strpos($filePath, $needle);
$filePath = substr($filePath, $pos + strlen($needle));
}
if (Storage::disk($disk)->exists($filePath)) {
Storage::disk($disk)->delete($filePath);
}
}
public function deleteContent($file_path): bool
{
if (file_exists($file_path)) {
file_put_contents($file_path, '');
return true;
}
return false;
}
public function tail($file_path, $lines = 100, $buffer = 2048): bool|string
{
// Open file
$file = @fopen($file_path, "rb");
if ($file === false) {
return false;
}
// Jump to last character
fseek($file, -1, SEEK_END);
// Read it and adjust line number if necessary
// (Otherwise the result would be wrong if file doesn't end with a blank line)
if (fread($file, 1) != "\n") {
$lines -= 1;
}
// Start reading
$output = '';
$chunk = '';
// While we would like more
while (ftell($file) > 0 && $lines >= 0) {
// Figure out how far back we should jump
$seek = min(ftell($file), $buffer);
// Do the jump (backwards, relative to where we are)
fseek($file, -$seek, SEEK_CUR);
// Read a chunk and prepend it to our output
$output = ($chunk = fread($file, $seek)) . $output;
// Jump back to where we started reading
fseek($file, -mb_strlen($chunk, '8bit'), SEEK_CUR);
// Decrease our line counter
$lines -= substr_count($chunk, "\n");
}
// While we have too many lines
// (Because of buffer size we might have read too many)
while ($lines++ < 0) {
// Find first newline and remove all text before that
$output = substr($output, strpos($output, "\n") + 1);
}
// Close file and return
fclose($file);
return trim($output);
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace App\Facades\File;
use Illuminate\Support\Facades\Facade;
class FileFacade extends Facade
{
protected static function getFacadeAccessor(): string
{
return 'file';
}
}

View File

@@ -0,0 +1,78 @@
<?php
namespace App\Http\Controllers\V3;
use App\Facades\File\FileFacade;
use App\Http\Controllers\Controller;
use App\Http\Requests\V3\Profile\ChangePasswordRequest;
use App\Http\Requests\V3\Profile\EditRequest;
use App\Http\Traits\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Storage;
class ProfileController extends Controller
{
use ApiResponse;
public function info(): JsonResponse
{
$user = Auth::user();
return $this->successResponse([
'username' => $user->username,
'first_name' => $user->first_name,
'last_name' => $user->last_name,
'name' => $user->name,
'last_login' => $user->last_login,
'major' => $user->major,
'degree' => $user->degree,
'mobile' => $user->mobile,
'avatar' => $user->avatar,
]);
}
public function edit(EditRequest $request): JsonResponse
{
$user = Auth::user();
if ($request->has('avatar'))
{
if ($user->avatar) {
FileFacade::delete($user->avatar);
}
$destinationPath = 'avatar/' . $user->username;
$filename = time() .".". $request->file('avatar')->getClientOriginalExtension();
$avatar = FileFacade::save($request->avatar, $destinationPath, $filename);
}
$user->update([
'first_name' => $request->first_name,
'last_name' => $request->last_name,
'major' => $request->major,
'degree' => $request->degree,
'mobile' => $request->mobile,
'avatar' => $avatar ?? $user->avatar
]);
return $this->successResponse();
}
public function changePassword(ChangePasswordRequest $request): JsonResponse
{
$user = Auth::user();
if (! Hash::check($request->current_password, $user->password))
{
return $this->errorResponse(__('messages.incorrect_current_password'));
}
$user->update([
'password' => Hash::make($request->new_password)
]);
return $this->successResponse();
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Http\Requests\V3\Profile;
use Illuminate\Foundation\Http\FormRequest;
class ChangePasswordRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'current_password' => 'required|string|regex:/^(?=.*[a-zA-Z])(?=.*\d).+$/|min:8',
'new_password' => 'required|string|regex:/^(?=.*[a-zA-Z])(?=.*\d).+$/|min:8',
];
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace App\Http\Requests\V3\Profile;
use Illuminate\Foundation\Http\FormRequest;
class EditRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'first_name' => 'required|string',
'last_name' => 'required|string',
'degree' => 'required',
'major' => 'required',
'mobile' => 'required|regex:/^09\d{9}$/|numeric|digits:11',
'avatar' => 'mimes:png,jpg,jpeg|max:2048'
];
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Providers;
use App\Facades\File\File;
use Illuminate\Support\ServiceProvider;
class FileServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* @return void
*/
public function register(): void
{
$this->app->bind('file', function () {
return new File();
});
}
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
}
}

View File

@@ -69,8 +69,8 @@ class RouteServiceProvider extends ServiceProvider
->group(base_path('routes/v2.php'));
Route::prefix('api/v3')
->name('v3')
->middleware('web', 'auth', 'confirmUser')
->name('v3.')
->middleware(['web', 'auth', 'confirmUser'])
->namespace($this->namespace)
->group(base_path('routes/v3.php'));
Route::prefix('api')