diff --git a/.env.example b/.env.example index fcc0baa..d6e8d17 100644 --- a/.env.example +++ b/.env.example @@ -33,7 +33,7 @@ SESSION_ENCRYPT=false SESSION_PATH=/ SESSION_DOMAIN=null SESSION_SAME_SITE=none -SESSION_SECURE_COOKIE=false +SESSION_SECURE_COOKIE=true BROADCAST_CONNECTION=log FILESYSTEM_DISK=local diff --git a/Dockerfile b/Dockerfile index beac546..6825188 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,10 +17,10 @@ RUN apk update && apk add --no-cache \ && docker-php-ext-install pdo_pgsql # Set the working directory -WORKDIR /var/www/crm +WORKDIR /var/www/laravel COPY . . -RUN chmod -R 777 /var/www/crm/storage /var/www/crm/bootstrap/cache +RUN chmod -R 777 /var/www/laravel/storage /var/www/laravel/bootstrap/cache # install composer diff --git a/app/Http/Controllers/Admin/UserManagementController.php b/app/Http/Controllers/Admin/UserManagementController.php index 7f67cd1..0ea644d 100644 --- a/app/Http/Controllers/Admin/UserManagementController.php +++ b/app/Http/Controllers/Admin/UserManagementController.php @@ -5,8 +5,8 @@ namespace App\Http\Controllers\Admin; use App\Facades\DataTable\DataTableFacade; use App\Facades\File\FileFacade; use App\Http\Controllers\Controller; -use App\Http\Requests\Admin\StoreRequest; -use App\Http\Requests\Admin\UpdateRequest; +use App\Http\Requests\Admin\UserManagement\StoreRequest; +use App\Http\Requests\Admin\UserManagement\UpdateRequest; use App\Http\Resources\Admin\UserResource; use App\Http\Traits\ApiResponse; use App\Models\Province; @@ -22,7 +22,6 @@ class UserManagementController extends Controller public function index(Request $request): JsonResponse { - $columns = array('id', 'full_name', 'username', 'province_id', 'phone_number', 'national_id', 'telephone_id'); $allowedFilters = $columns; @@ -68,7 +67,7 @@ class UserManagementController extends Controller 'telephone_id' => $request->telephone_id, ]); - $user->syncPermissions($request->permissions); + $user->assignRole($request->role); }); return $this->successResponse(); @@ -102,7 +101,7 @@ class UserManagementController extends Controller 'telephone_id' => $request->telephone_id ]); - $user->syncPermissions($request->permissions); + $user->syncRoles($request->role); }); return $this->successResponse(); diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index 543f53c..2736a87 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -18,11 +18,11 @@ class AuthController extends Controller if ($this->attemptLogin($request)) { $request->session()->regenerate(); - $this->updateLastLogin($request); + $this->updateUserInfo($request); return $this->successResponse(); } - return $this->errorResponse(); + return $this->errorResponse(__('messages.invalid_credentials')); } protected function attemptLogin(Request $request): bool @@ -30,19 +30,19 @@ class AuthController extends Controller return Auth::guard()->attempt($request->only('username', 'password')); } - protected function updateLastLogin(Request $request): void + protected function updateUserInfo(Request $request): void { - if (!$request->session()->exists('last_login')) { - $request->session()->put('last_login', now()); - } - Auth::user()->update([ 'last_login' => now(), + 'telephone_id' => $request->telephone_id, ]); } public function logout(Request $request): JsonResponse { + Auth::user()->update([ + 'telephone_id' => null, + ]); Auth::guard()->logout(); $request->session()->invalidate(); $request->session()->regenerateToken(); diff --git a/app/Http/Controllers/CallController.php b/app/Http/Controllers/CallController.php index 17a4c0d..58a0b64 100644 --- a/app/Http/Controllers/CallController.php +++ b/app/Http/Controllers/CallController.php @@ -119,7 +119,6 @@ class CallController extends Controller public function list(ListRequest $request): JsonResponse { - User::query()->find(1)->givePermissionTo([1, 2, 3]); $phone_number = $request->phone_number; $size = $request->size ?? config('global_variables.MAX_RECORDS_COUNT'); diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 882e4c6..a74055c 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -5,7 +5,9 @@ namespace App\Http\Controllers; use App\Http\Requests\ChangePasswordRequest; use App\Http\Resources\Admin\UserResource; use App\Http\Traits\ApiResponse; +use App\Models\User; use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; class ProfileController extends Controller @@ -15,7 +17,6 @@ class ProfileController extends Controller public function info(): JsonResponse { return $this->successResponse(new UserResource(auth()->user())); - } public function changePassword(ChangePasswordRequest $request): JsonResponse { @@ -29,4 +30,20 @@ class ProfileController extends Controller return $this->successResponse(); } + + public function online(Request $request): JsonResponse + { + User::query()->where('telephone_id', '=', $request->telephone_id)->update([ + 'is_online' => true, + ]); + return $this->successResponse(); + } + + public function offline(Request $request): JsonResponse + { + User::query()->where('telephone_id', '=', $request->telephone_id)->update([ + 'is_online' => false, + ]); + return $this->successResponse(); + } } diff --git a/app/Http/Middleware/ValidateIP.php b/app/Http/Middleware/ValidateAdmin.php similarity index 80% rename from app/Http/Middleware/ValidateIP.php rename to app/Http/Middleware/ValidateAdmin.php index 46b778c..3f0449c 100644 --- a/app/Http/Middleware/ValidateIP.php +++ b/app/Http/Middleware/ValidateAdmin.php @@ -6,7 +6,7 @@ use Closure; use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\Response; -class ValidateIP +class ValidateAdmin { /** * Handle an incoming request. @@ -15,7 +15,7 @@ class ValidateIP */ public function handle(Request $request, Closure $next): Response { - if (in_array($request->ip(), config('globalVariables.TRUSTED_IP'))) { + if (auth()->user()->username === 'witel') { return $next($request); } abort(403); diff --git a/app/Http/Requests/Admin/StoreRequest.php b/app/Http/Requests/Admin/UserManagement/StoreRequest.php similarity index 92% rename from app/Http/Requests/Admin/StoreRequest.php rename to app/Http/Requests/Admin/UserManagement/StoreRequest.php index 39d219f..616f5c8 100644 --- a/app/Http/Requests/Admin/StoreRequest.php +++ b/app/Http/Requests/Admin/UserManagement/StoreRequest.php @@ -1,6 +1,6 @@ 'required|numeric|digits:11|unique:users,phone_number|regex:/^09\d{9}$/', 'province_id' => 'required|exists:provinces,id', 'password' => 'required|regex:/^(?=.*[a-zA-Z])(?=.*\d).+$/|min:8', - 'permissions' => 'required|exists:permissions,id', + 'role' => 'required|exists:roles,id', 'avatar' => 'mimes:png,jpg,pdf|max:2048', 'email' => 'email|unique:users,email', 'telephone_id' => 'required|string|unique:users,telephone_id' diff --git a/app/Http/Requests/Admin/UpdateRequest.php b/app/Http/Requests/Admin/UserManagement/UpdateRequest.php similarity index 93% rename from app/Http/Requests/Admin/UpdateRequest.php rename to app/Http/Requests/Admin/UserManagement/UpdateRequest.php index 2e6c989..c953eb3 100644 --- a/app/Http/Requests/Admin/UpdateRequest.php +++ b/app/Http/Requests/Admin/UserManagement/UpdateRequest.php @@ -1,6 +1,6 @@ 'required|exists:provinces,id', - 'permissions' => 'required|exists:permissions,id', + 'role' => 'required|exists:roles,id', 'avatar' => 'mimes:png,jpg,pdf|max:2048', 'email' => ['email', Rule::unique('users', 'email')->ignore($this->user->id)], 'telephone_id' => ['required', 'string', Rule::unique('users', 'telephone_id')->ignore($this->user->id)] diff --git a/app/Http/Requests/Auth/LoginRequest.php b/app/Http/Requests/Auth/LoginRequest.php index da0f86c..a2cd90f 100644 --- a/app/Http/Requests/Auth/LoginRequest.php +++ b/app/Http/Requests/Auth/LoginRequest.php @@ -2,7 +2,9 @@ namespace App\Http\Requests\Auth; +use App\Models\User; use Illuminate\Contracts\Validation\ValidationRule; +use Illuminate\Validation\Validator; use Illuminate\Foundation\Http\FormRequest; class LoginRequest extends FormRequest @@ -25,6 +27,25 @@ class LoginRequest extends FormRequest return [ 'username' => 'required|exists:users,username', 'password' => 'required|string', + 'telephone_id' => 'required|string' + ]; + } + + public function after(): array + { + return [ + function (Validator $validator) { + $user = User::query()->where('telephone_id', '=', $this->telephone_id); + if ($user->exists()) { + if ($user->first()->is_online) { + $validator->errors()->add( + 'telephone_id', + __('messages.telephone_id_is_in_use') + ); + } + $user->first()->update(['telephone_id' => null]); + } + } ]; } } diff --git a/app/Http/Requests/Call/ListRequest.php b/app/Http/Requests/Call/ListRequest.php index 664a005..96d7132 100644 --- a/app/Http/Requests/Call/ListRequest.php +++ b/app/Http/Requests/Call/ListRequest.php @@ -23,7 +23,7 @@ class ListRequest extends FormRequest public function rules(): array { return [ - 'phone_number' => 'regex:/^09\d{9}$/', + 'phone_number' => 'string', 'size' => 'integer' ]; } diff --git a/app/Http/Requests/Call/StoreRequest.php b/app/Http/Requests/Call/StoreRequest.php index 9815bfd..38dbc2f 100644 --- a/app/Http/Requests/Call/StoreRequest.php +++ b/app/Http/Requests/Call/StoreRequest.php @@ -24,7 +24,7 @@ class StoreRequest extends FormRequest { return [ 'extension' => 'required|string|exists:users,telephone_id', - 'caller_id' => 'required|numeric|regex:/^09\d{9}$/', + 'caller_id' => 'required|numeric', ]; } } diff --git a/app/Http/Requests/ChangePasswordRequest.php b/app/Http/Requests/ChangePasswordRequest.php index fb7d1e9..da65623 100644 --- a/app/Http/Requests/ChangePasswordRequest.php +++ b/app/Http/Requests/ChangePasswordRequest.php @@ -23,7 +23,7 @@ class ChangePasswordRequest extends FormRequest { return [ 'current_password' => 'required|min:8|max:255', - 'new_password' => 'required|confirmed|regex:/^(?=.*[a-zA-Z])(?=.*\d).+$/|min:8|max:255', + 'new_password' => 'required|regex:/^(?=.*[a-zA-Z])(?=.*\d).+$/|min:8|max:255', ]; } } diff --git a/app/Http/Resources/Admin/UserResource.php b/app/Http/Resources/Admin/UserResource.php index 9b97001..c53f084 100644 --- a/app/Http/Resources/Admin/UserResource.php +++ b/app/Http/Resources/Admin/UserResource.php @@ -26,7 +26,6 @@ class UserResource extends JsonResource 'province_id' => $this->province_id, 'province_name' => $this->province_name, 'role' => $this->getRoleNames()->first(), - 'permissions' => $this->getAllPermissions()->pluck('name'), 'telephone_id' => $this->telephone_id ]; } diff --git a/bootstrap/app.php b/bootstrap/app.php index 98c15f9..341349c 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -10,6 +10,7 @@ return Application::configure(basePath: dirname(__DIR__)) api: __DIR__.'/../routes/api.php', commands: __DIR__.'/../routes/console.php', health: '/up', + apiPrefix: 'server/api', ) ->withMiddleware(function (Middleware $middleware) { $middleware->validateCsrfTokens(except: [ diff --git a/config/pulse.php b/config/pulse.php index 1adb993..dd98e39 100644 --- a/config/pulse.php +++ b/config/pulse.php @@ -30,7 +30,7 @@ return [ | */ - 'path' => env('PULSE_PATH', 'pulse'), + 'path' => env('PULSE_PATH', 'server/pulse'), /* |-------------------------------------------------------------------------- @@ -122,7 +122,7 @@ return [ 'middleware' => [ 'web', - \App\Http\Middleware\ValidateIP::class + \App\Http\Middleware\ValidateAdmin::class ], /* diff --git a/config/telescope.php b/config/telescope.php index 53c8a97..982f860 100644 --- a/config/telescope.php +++ b/config/telescope.php @@ -42,7 +42,7 @@ return [ | */ - 'path' => env('TELESCOPE_PATH', 'telescope'), + 'path' => env('TELESCOPE_PATH', 'server/telescope'), /* |-------------------------------------------------------------------------- @@ -94,7 +94,7 @@ return [ 'middleware' => [ 'web', - \App\Http\Middleware\ValidateIP::class, + \App\Http\Middleware\ValidateAdmin::class, ], /* diff --git a/database/migrations/2025_04_28_110000_create_users_table.php b/database/migrations/2025_04_28_110000_create_users_table.php index 9f102bd..55e3dda 100644 --- a/database/migrations/2025_04_28_110000_create_users_table.php +++ b/database/migrations/2025_04_28_110000_create_users_table.php @@ -25,7 +25,8 @@ return new class extends Migration $table->string('province_fa'); $table->string('email')->unique()->nullable(); $table->dateTime('last_login')->nullable(); - $table->string('telephone_id')->unique(); + $table->string('telephone_id')->unique()->nullable(); + $table->boolean('is_online'); $table->rememberToken()->nullable(); $table->timestamps(); }); diff --git a/database/migrations/2025_04_29_113252_create_calls_table.php b/database/migrations/2025_04_29_113252_create_calls_table.php index a8086e4..179cce7 100644 --- a/database/migrations/2025_04_29_113252_create_calls_table.php +++ b/database/migrations/2025_04_29_113252_create_calls_table.php @@ -15,25 +15,15 @@ return new class extends Migration $table->id(); $table->string('telephone_id'); $table->string('caller_phone_number'); - - $table->foreignId('province_id') - ->nullable() - ->constrained(); + $table->foreignId('province_id')->nullable()->constrained(); $table->string('province_fa')->nullable(); - - $table->foreignId('operator_id') - ->nullable() - ->constrained('users'); - + $table->foreignId('operator_id')->nullable()->constrained('users'); $table->string('operator_username')->nullable(); $table->string('operator_full_name')->nullable(); - $table->unsignedSmallInteger('category_id')->nullable(); $table->string('category_name')->nullable(); - $table->unsignedSmallInteger('subcategory_id')->nullable(); $table->string('subcategory_name')->nullable(); - $table->text('description')->nullable(); $table->timestamps(); }); diff --git a/database/seeders/RoleSeeder.php b/database/seeders/RoleSeeder.php index f129660..8aea2e5 100644 --- a/database/seeders/RoleSeeder.php +++ b/database/seeders/RoleSeeder.php @@ -2,8 +2,12 @@ namespace Database\Seeders; +use App\Models\Permission; +use App\Models\Role; +use App\Models\User; use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; +use Illuminate\Support\Facades\DB; class RoleSeeder extends Seeder { @@ -12,6 +16,16 @@ class RoleSeeder extends Seeder */ public function run(): void { - // + $role = Role::query()->create([ + 'id' => 1, + 'name' => 'super-admin', + 'name_fa' => 'ادمین', + 'guard_name' => 'web', + 'created_at' => now(), + 'updated_at' => now() + ]); + + $role->givePermissionTo(Permission::all()); + User::query()->where('username', '=', 'witel')->first()->assignRole('super-admin'); } } diff --git a/database/seeders/UserSeeder.php b/database/seeders/UserSeeder.php index 22eff33..db6d1ef 100644 --- a/database/seeders/UserSeeder.php +++ b/database/seeders/UserSeeder.php @@ -25,8 +25,7 @@ class UserSeeder extends Seeder 'province_id' => 9, 'province_fa' => "تهران", 'telephone_id' => 0, + 'is_online' => false, ]); - -// $admin->givePermissionTo(\App\Models\Permission::all()); } } diff --git a/routes/api.php b/routes/api.php index ccc387f..660dc9c 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,8 +1,15 @@ user(); -})->middleware('auth:sanctum'); +Route::prefix('calls')->post('/', [CallController::class, 'store'])->name('store'); + +Route::prefix('operator') + ->controller(ProfileController::class) + ->group(function () { + Route::get('/online', 'online')->name('online'); + Route::get('/offline', 'offline')->name('offline'); + });