Compare commits

..

8 Commits

35 changed files with 578 additions and 20 deletions

3
.gitignore vendored
View File

@@ -1,2 +1,3 @@
.idea
.env
.env
front

View File

@@ -30,7 +30,6 @@ ENV COMPOSER_ALLOW_SUPERUSER=1
# Default command
CMD composer install ;\
composer dump-autoload --optimize --classmap-authoritative ;\
php artisan key:generate ;\
php artisan migrate ;\
php artisan db:seed ;\
php-fpm

View File

@@ -0,0 +1,60 @@
<?php
namespace App\Admin\Controllers;
use App\Http\Controllers\Controller;
use App\Traits\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class AuthController extends Controller
{
use ApiResponse;
public function register(Request $request): JsonResponse
{
// Expert::query()->create();
$request->session()->regenerate();
return $this->successResponse();
}
public function login(Request $request): JsonResponse
{
$credentials = ['password' => $request->password];
if (filter_var($request->login, FILTER_VALIDATE_EMAIL)) {
$credentials['email'] = $request->login;
} else {
$credentials['username'] = $request->login;
}
if (Auth::attempt($credentials))
{
$request->session()->regenerate();
$user = Auth::user();
return $this->successResponse([
'id' => $user->id,
'username' => $user->username,
'email' => $user->email,
]);
}
return $this->errorResponse(__('messages.incorrect_or_username_password'));
}
public function logout(Request $request): JsonResponse
{
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return $this->successResponse();
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace App\Admin\Controllers;
use App\Facades\DataTable\DataTableFacade;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Traits\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class DocumentationController extends Controller
{
use ApiResponse;
public function index(Request $request): array
{
return DataTableFacade::run(
User::query(),
$request,
allowedFilters: ['*'],
allowedSortings: ['*'],
allowedSelects: [
'id', 'first_name', 'last_name', 'national_id',
'phone_number', 'postal_code', 'city_id', 'province_id',
'province_name', 'city_name', 'address'
]
);
}
public function files(User $user): JsonResponse
{
return $this->successResponse($user->load('documents'));
}
public function confirm(User $user): JsonResponse
{
$user->update([
]);
return $this->successResponse();
}
public function reject(User $user): JsonResponse
{
$user->update([
]);
return $this->successResponse();
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Admin\Controllers;
use App\Admin\Resources\ExpertResource;
use App\Http\Controllers\Controller;
use App\Traits\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Auth;
class ProfileController extends Controller
{
use ApiResponse;
public function info(): JsonResponse
{
return $this->successResponse(new ExpertResource(Auth::user()));
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace App\Admin\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class ExpertResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'username' => $this->username,
'email' => $this->email,
];
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace App\Models;
use Database\Factories\CityFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class City extends Model
{
/** @use HasFactory<CityFactory> */
use HasFactory;
protected $guarded = ['id'];
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Models;
use Database\Factories\DocumentFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Support\Facades\Storage;
class Document extends Model
{
/** @use HasFactory<DocumentFactory> */
use HasFactory;
protected $guarded = ['id'];
public function documentable(): MorphTo
{
return $this->morphTo();
}
protected function url(): Attribute
{
return Attribute::make(
get: fn($value) => Storage::disk('public')->url($value)
);
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace App\Models;
use Database\Factories\ExpertFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Expert extends Model
{
/** @use HasFactory<ExpertFactory> */
use HasFactory;
protected $guarded = ['id'];
}

View File

@@ -0,0 +1,15 @@
<?php
namespace App\Models;
use Database\Factories\ProvinceFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Province extends Model
{
/** @use HasFactory<ProvinceFactory> */
use HasFactory;
protected $guarded = ['id'];
}

View File

@@ -8,6 +8,7 @@ use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Attributes\Guarded;
use Illuminate\Database\Eloquent\Attributes\Hidden;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;
@@ -19,4 +20,9 @@ class User extends Authenticatable
/** @use HasFactory<UserFactory> */
use HasFactory, Notifiable, HasApiTokens;
protected $guarded = ['id'];
public function documents(): MorphToMany
{
return $this->morphToMany(Document::class, 'documentable');
}
}

View File

@@ -1,13 +1,12 @@
<?php
namespace App\User\Controller\Auth;
namespace App\User\Controllers;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Traits\ApiResponse;
use App\User\Requests\Auth\LoginRequest;
use App\User\Requests\Auth\RegisterRequest;
use App\User\Resources\UserResource;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
@@ -46,7 +45,11 @@ class AuthController extends Controller
$user = Auth::user();
return $this->successResponse(new UserResource($user));
return $this->successResponse([
'id' => $user->id,
'username' => $user->username,
'email' => $user->email,
]);
}
return $this->errorResponse(__('messages.incorrect_or_username_password'));

View File

@@ -1,6 +1,6 @@
<?php
namespace App\User\Controller\Dashboard;
namespace App\User\Controllers\Dashboard;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

View File

@@ -1,6 +1,6 @@
<?php
namespace App\User\Controller;
namespace App\User\Controllers;
use App\Http\Controllers\Controller;
use App\Traits\ApiResponse;

3
backend/app/User/Requests/Auth/LoginRequest.php Executable file → Normal file
View File

@@ -2,6 +2,7 @@
namespace App\User\Requests\Auth;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class LoginRequest extends FormRequest
@@ -17,7 +18,7 @@ class LoginRequest extends FormRequest
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
* @return array<string, ValidationRule|array|string>
*/
public function rules(): array
{

5
backend/app/User/Requests/Auth/RegisterRequest.php Executable file → Normal file
View File

@@ -2,6 +2,7 @@
namespace App\User\Requests\Auth;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class RegisterRequest extends FormRequest
@@ -17,14 +18,14 @@ class RegisterRequest extends FormRequest
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
* @return array<string, ValidationRule|array|string>
*/
public function rules(): array
{
return [
'username' => 'required|unique:users,username',
'email' => 'required|email|unique:users,email',
'password' => 'required|string|regex:/^(?=.*[a-zA-Z])(?=.*\d).+$/|min:6|confirmed:',
'password' => 'required|string|regex:/^(?=.*[a-zA-Z])(?=.*\d).+$/|min:6|confirmed',
];
}
}

0
backend/app/User/Resources/UserResource.php Executable file → Normal file
View File

View File

@@ -10,9 +10,15 @@ return Application::configure(basePath: dirname(__DIR__))
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
then: function () {
Route::middleware('web')
->prefix('expert')
->name('expert.')
->group(base_path('routes/expert.php'));
},
)
->withMiddleware(function (Middleware $middleware): void {
$middleware->web()->validateCsrfTokens(['*']);
$middleware->web()->preventRequestForgery(['*']);
})
->withExceptions(function (Exceptions $exceptions): void {
//

View File

@@ -46,6 +46,10 @@ return [
'driver' => 'passport',
'provider' => 'users',
],
'expert' => [
'driver' => 'session',
'provider' => 'experts',
]
],
/*
@@ -70,6 +74,10 @@ return [
'driver' => 'eloquent',
'model' => env('AUTH_MODEL', User::class),
],
'experts' => [
'driver' => 'eloquent',
'model' => env('AUTH_MODEL', Expert::class),
],
// 'users' => [
// 'driver' => 'database',

View File

@@ -0,0 +1,24 @@
<?php
namespace Database\Factories;
use App\Models\City;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<City>
*/
class CityFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
//
];
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Database\Factories;
use App\Models\Document;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<Document>
*/
class DocumentFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
//
];
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Database\Factories;
use App\Models\Expert;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<Expert>
*/
class ExpertFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
//
];
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Database\Factories;
use App\Models\Province;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<Province>
*/
class ProvinceFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
//
];
}
}

View File

@@ -0,0 +1,28 @@
<?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('provinces', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('provinces');
}
};

View File

@@ -0,0 +1,29 @@
<?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('cities', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->foreignId('province_id')->constrained('provinces');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('cities');
}
};

View File

@@ -18,17 +18,18 @@ return new class extends Migration
$table->string('last_name')->nullable();
$table->string('email')->unique();
$table->string('password');
$table->tinyInteger('status')->default(0);
$table->tinyInteger('kyc_status')->default(0);
$table->string('national_id')->nullable();
$table->string('address')->nullable();
$table->string('postal_code')->nullable();
$table->string('account_number')->nullable();
$table->string('tax_id')->nullable();
$table->string('phone_number')->nullable();
$table->string('province_id')->nullable();
$table->date('birth_date')->nullable();
$table->foreignId('province_id')->nullable()->constrained('provinces');
$table->string('province_name')->nullable();
$table->string('city_id')->nullable();
$table->foreignId('city_id')->nullable()->constrained('cities');
$table->string('city_name')->nullable();
$table->tinyInteger('authority_level')->default(0);
$table->timestamps();
});

View File

@@ -0,0 +1,30 @@
<?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('documents', function (Blueprint $table) {
$table->id();
$table->morphs('documentable');
$table->string('title');
$table->string('url');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('documents');
}
};

View File

@@ -0,0 +1,38 @@
<?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('experts', function (Blueprint $table) {
$table->id();
$table->string('username');
$table->string('first_name')->nullable();
$table->string('last_name')->nullable();
$table->string('email')->unique();
$table->string('password');
$table->string('national_id')->nullable();
$table->string('phone_number')->nullable();
$table->foreignId('province_id')->nullable()->constrained('provinces');
$table->string('province_name')->nullable();
$table->foreignId('city_id')->nullable()->constrained('cities');
$table->string('city_name')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('experts');
}
};

View File

@@ -0,0 +1,17 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class CitySeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
//
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DocumentSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
//
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class ExpertSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
//
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class ProvinceSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
//
}
}

View File

@@ -1,10 +1,8 @@
<?php
use App\User\Controller\Auth\AuthController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:api');

14
backend/routes/expert.php Executable file
View File

@@ -0,0 +1,14 @@
<?php
use App\Admin\Controllers\DocumentationController;
Route::prefix('documents')
->name('documents.')
->middleware('auth')
->controller(DocumentationController::class)
->group(function () {
Route::get('/', 'index')->name('index');
Route::post('confirm', 'confirm')->name('confirm');
Route::post('reject', 'reject')->name('reject');
Route::get('files/{user}', 'files')->name('files');
});

View File

@@ -1,7 +1,7 @@
<?php
use App\User\Controller\Auth\AuthController;
use App\User\Controller\ProfileController;
use App\User\Controllers\AuthController;
use App\User\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;
Route::prefix('auth')