diff --git a/Docker/nginx/nginx.conf b/Docker/nginx/nginx.conf index 4ce5974..9d07059 100644 --- a/Docker/nginx/nginx.conf +++ b/Docker/nginx/nginx.conf @@ -1,7 +1,7 @@ server { listen 80; server_name _; - root /var/www/golscontrols/public; + root /var/www/netbien/public; index index.php index.html; # Logging @@ -17,7 +17,7 @@ server { location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; - fastcgi_pass golscontrols:9000; + fastcgi_pass netbien-backend:9000; fastcgi_index index.php; # Timeouts importantes para evitar errores 500 @@ -45,17 +45,17 @@ server { # Handle storage files (Laravel storage link) location /storage { - alias /var/www/golscontrols/storage/app; + alias /var/www/netbien/storage/app; try_files $uri =404; } location /profile { - alias /var/www/golscontrols/storage/app/profile; + alias /var/www/netbien/storage/app/profile; try_files $uri =404; } location /images { - alias /var/www/golscontrols/storage/app/images; + alias /var/www/netbien/storage/app/images; try_files $uri =404; } diff --git a/app/Enums/SimCardStatus.php b/app/Enums/SimCardStatus.php new file mode 100644 index 0000000..59bb2c2 --- /dev/null +++ b/app/Enums/SimCardStatus.php @@ -0,0 +1,28 @@ + 'Disponible', + self::ASSIGNED => 'Asignada', + }; + } +} diff --git a/app/Http/Controllers/Netbien/PackagesController.php b/app/Http/Controllers/Netbien/PackagesController.php new file mode 100644 index 0000000..3057912 --- /dev/null +++ b/app/Http/Controllers/Netbien/PackagesController.php @@ -0,0 +1,46 @@ +paginate(config('app.pagination')); + + return ApiResponse::OK->response([ + 'data' => $packages, + ]); + } + + public function store(PackagesStoreRequest $request) + { + $validated = $request->validated(); + + $package = Packages::create($validated); + + return ApiResponse::CREATED->response([ + 'data' => $package, + ]); + } + + public function update(PackagesStoreRequest $request, Packages $package) + { + $validated = $request->validated(); + + $package->update($validated); + + return ApiResponse::OK->response([ + 'data' => $package, + ]); + } +} diff --git a/app/Http/Controllers/Netbien/SimCardController.php b/app/Http/Controllers/Netbien/SimCardController.php new file mode 100644 index 0000000..0ae0207 --- /dev/null +++ b/app/Http/Controllers/Netbien/SimCardController.php @@ -0,0 +1,44 @@ +paginate(config('app.pagination')); + + return ApiResponse::OK->response([ + 'data' => $simCards, + ]); + } + + public function store(SimCardStoreRequest $request) + { + $simCard = SimCard::create($request->validated()); + + return ApiResponse::CREATED->response([ + 'data' => $simCard, + ]); + } + + public function update(SimCardUpdateRequest $request, SimCard $simCard) + { + $simCard->update($request->validated()); + + return ApiResponse::OK->response([ + 'data' => $simCard, + ]); + } + +} diff --git a/app/Http/Requests/Netbien/PackagesStoreRequest.php b/app/Http/Requests/Netbien/PackagesStoreRequest.php new file mode 100644 index 0000000..85c03c3 --- /dev/null +++ b/app/Http/Requests/Netbien/PackagesStoreRequest.php @@ -0,0 +1,52 @@ + + * + * @version 1.0.0 + */ +class PackagesStoreRequest extends FormRequest +{ + public function authorize(): bool + { + return true; + } + + /** + * Get the validation rules that apply to the request. + */ + public function rules(): array + { + return [ + 'name' => ['required', 'string', 'max:80'], + 'price' => ['required', 'numeric', 'min:0'], + 'period' => ['required', 'integer', 'min:1'], + 'data_limit' => ['required', 'integer', 'min:0'], + ]; + } + + public function messages() : array + { + return [ + 'name.required' => 'El campo Nombre es obligatorio.', + 'name.string' => 'El campo Nombre debe ser una cadena de texto.', + 'name.max' => 'El campo Nombre no debe exceder los 80 caracteres.', + + 'price.required' => 'El campo Precio es obligatorio.', + 'price.min' => 'El campo Precio no debe ser negativo.', + + 'period.required' => 'El campo Periodo es obligatorio.', + + 'data_limit.required' => 'El campo Límite de Datos es obligatorio.', + 'data_limit.integer' => 'El campo Límite de Datos debe ser un número entero.', + 'data_limit.min' => 'El campo Límite de Datos no debe ser negativo.', + ]; + } +} diff --git a/app/Http/Requests/Netbien/PackagesUpdateRequest.php b/app/Http/Requests/Netbien/PackagesUpdateRequest.php new file mode 100644 index 0000000..aecd75a --- /dev/null +++ b/app/Http/Requests/Netbien/PackagesUpdateRequest.php @@ -0,0 +1,55 @@ + + * + * @version 1.0.0 + */ +class PackagesUpdateRequest extends FormRequest +{ + public function authorize(): bool + { + return true; + } + + /** + * Get the validation rules that apply to the request. + */ + public function rules(): array + { + return [ + 'name' => ['required', 'string', 'max:80'], + 'price' => ['required', 'numeric', 'min:0'], + 'period' => ['required', 'string', 'max:20'], + 'data_limit' => ['required', 'integer', 'min:0'], + ]; + } + + public function messages() : array + { + return [ + 'name.required' => 'El campo Nombre es obligatorio.', + 'name.string' => 'El campo Nombre debe ser una cadena de texto.', + 'name.max' => 'El campo Nombre no debe exceder los 80 caracteres.', + + 'price.required' => 'El campo Precio es obligatorio.', + 'price.numeric' => 'El campo Precio debe ser un número.', + 'price.min' => 'El campo Precio no debe ser negativo.', + + 'period.required' => 'El campo Periodo es obligatorio.', + 'period.string' => 'El campo Periodo debe ser una cadena de texto.', + 'period.max' => 'El campo Periodo no debe exceder los 20 caracteres.', + + 'data_limit.required' => 'El campo Límite de Datos es obligatorio.', + 'data_limit.integer' => 'El campo Límite de Datos debe ser un número entero.', + 'data_limit.min' => 'El campo Límite de Datos no debe ser negativo.', + ]; + } +} diff --git a/app/Http/Requests/Netbien/SimCardStoreRequest.php b/app/Http/Requests/Netbien/SimCardStoreRequest.php new file mode 100644 index 0000000..3bf8090 --- /dev/null +++ b/app/Http/Requests/Netbien/SimCardStoreRequest.php @@ -0,0 +1,49 @@ + + * + * @version 1.0.0 + */ +class SimCardStoreRequest extends FormRequest +{ + public function authorize(): bool + { + return true; + } + + /** + * Get the validation rules that apply to the request. + */ + public function rules(): array + { + return [ + 'iccid' => ['required', 'string', 'max:25', 'unique:sim_cards,iccid'], + 'msisdn' => ['required', 'string', 'max:10', 'unique:sim_cards,msisdn'], + ]; + } + + public function messages() : array + { + return [ + 'iccid.required' => 'El campo ICCID es obligatorio.', + 'iccid.string' => 'El campo ICCID debe ser una cadena de texto.', + 'iccid.max' => 'El campo ICCID no debe exceder los 25 caracteres.', + 'iccid.unique' => 'El ICCID ya está en uso.', + + 'msisdn.required' => 'El campo MSISDN es obligatorio.', + 'msisdn.string' => 'El campo MSISDN debe ser una cadena de texto.', + 'msisdn.max' => 'El campo MSISDN no debe exceder los 10 caracteres.', + 'msisdn.unique' => 'El MSISDN ya está en uso.', + ]; + } +} diff --git a/app/Http/Requests/Netbien/SimCardUpdateRequest.php b/app/Http/Requests/Netbien/SimCardUpdateRequest.php new file mode 100644 index 0000000..67763fc --- /dev/null +++ b/app/Http/Requests/Netbien/SimCardUpdateRequest.php @@ -0,0 +1,43 @@ + + * + * @version 1.0.0 + */ +class SimCardUpdateRequest extends FormRequest +{ + public function authorize(): bool + { + return true; + } + + /** + * Get the validation rules that apply to the request. + */ + public function rules(): array + { + return [ + 'msisdn' => ['required', 'string', 'max:10', 'unique:sim_cards,msisdn'], + ]; + } + + public function messages() : array + { + return [ + 'msisdn.required' => 'El campo MSISDN es obligatorio.', + 'msisdn.string' => 'El campo MSISDN debe ser una cadena de texto.', + 'msisdn.max' => 'El campo MSISDN no debe exceder los 10 caracteres.', + 'msisdn.unique' => 'El MSISDN ya está en uso.', + ]; + } +} diff --git a/app/Models/PackSim.php b/app/Models/PackSim.php new file mode 100644 index 0000000..1c43682 --- /dev/null +++ b/app/Models/PackSim.php @@ -0,0 +1,37 @@ + + * + * @version 1.0.0 + */ +class PackSim extends Model +{ + protected $fillable = [ + 'package_id', + 'sim_card_id', + ]; + + protected $casts = [ + 'package_id' => 'integer', + 'sim_card_id' => 'integer', + ]; + + public function package() + { + return $this->belongsTo(Packages::class, 'package_id'); + } + + public function simCard() + { + return $this->belongsTo(SimCard::class, 'sim_card_id'); + } +} diff --git a/app/Models/Packages.php b/app/Models/Packages.php new file mode 100644 index 0000000..5ac7c7e --- /dev/null +++ b/app/Models/Packages.php @@ -0,0 +1,36 @@ + + * + * @version 1.0.0 + */ +class Packages extends Model +{ + protected $fillable = [ + 'name', + 'price', + 'period', + 'data_limit', + ]; + + protected $casts = [ + 'name' => 'string', + 'price' => 'float', + 'period' => 'integer', + 'data_limit' => 'integer', + ]; + + public function packSims() + { + return $this->hasMany(PackSim::class, 'package_id'); + } +} diff --git a/app/Models/SimCard.php b/app/Models/SimCard.php new file mode 100644 index 0000000..42550e3 --- /dev/null +++ b/app/Models/SimCard.php @@ -0,0 +1,33 @@ + + * + * @version 1.0.0 + */ +class SimCard extends Model +{ + protected $fillable = [ + 'iccid', + 'msisdn', + 'status', + ]; + + protected $casts = [ + 'status' => SimCardStatus::class, + ]; + + public function packSims() + { + return $this->hasMany(PackSim::class, 'sim_card_id'); + } +} diff --git a/database/migrations/2025_11_04_132401_create_sim_cards_table.php b/database/migrations/2025_11_04_132401_create_sim_cards_table.php new file mode 100644 index 0000000..cbb29ef --- /dev/null +++ b/database/migrations/2025_11_04_132401_create_sim_cards_table.php @@ -0,0 +1,31 @@ +id(); + $table->string('iccid')->unique(); + $table->string('msisdn')->unique(); + $table->enum('status', SimCardStatus::values())->default(SimCardStatus::AVAILABLE->value); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('sim_cards'); + } +}; diff --git a/database/migrations/2025_11_04_132448_create_packages_table.php b/database/migrations/2025_11_04_132448_create_packages_table.php new file mode 100644 index 0000000..8265ec0 --- /dev/null +++ b/database/migrations/2025_11_04_132448_create_packages_table.php @@ -0,0 +1,31 @@ +id(); + $table->string('name'); + $table->integer('price'); + $table->integer('period'); + $table->integer('data_limit'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('packages'); + } +}; diff --git a/database/migrations/2025_11_04_132537_create_pack_sims_table.php b/database/migrations/2025_11_04_132537_create_pack_sims_table.php new file mode 100644 index 0000000..d554d53 --- /dev/null +++ b/database/migrations/2025_11_04_132537_create_pack_sims_table.php @@ -0,0 +1,29 @@ +id(); + $table->foreignId('sim_card_id')->constrained('sim_cards')->onDelete('cascade'); + $table->foreignId('package_id')->constrained('packages')->onDelete('cascade'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('pack_sims'); + } +}; diff --git a/database/seeders/PackagesSeeder.php b/database/seeders/PackagesSeeder.php new file mode 100644 index 0000000..84d0943 --- /dev/null +++ b/database/seeders/PackagesSeeder.php @@ -0,0 +1,25 @@ + + * + * @version 1.0.0 + */ +class Packages extends Seeder +{ + /** + * Ejecutar sembrado de base de datos + */ + public function run(): void + { + // + } +} diff --git a/database/seeders/SimCardSeeder.php b/database/seeders/SimCardSeeder.php new file mode 100644 index 0000000..b7d01b9 --- /dev/null +++ b/database/seeders/SimCardSeeder.php @@ -0,0 +1,25 @@ + + * + * @version 1.0.0 + */ +class SimCardSeeder extends Seeder +{ + /** + * Ejecutar sembrado de base de datos + */ + public function run(): void + { + // + } +} diff --git a/docker-compose.yml b/docker-compose.yml index 3b1c760..f4e85f1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,9 +1,9 @@ services: - repuve-backend: + netbien-backend: build: context: . - dockerfile: dockerfile.dev - working_dir: /var/www/repuve-v1 + dockerfile: dockerfile + working_dir: /var/www/netbien environment: - DB_HOST=mysql - DB_USERNAME=${DB_USERNAME} @@ -11,27 +11,28 @@ services: - DB_DATABASE=${DB_DATABASE} - DB_PORT=${DB_PORT} volumes: - - ./:/var/www/repuve-v1 - - /var/www/repuve-v1/vendor + - ./:/var/www/netbien + - ./vendor:/var/www/netbien/vendor + - /var/www/netbien/node_modules networks: - - repuve-network + - netbien-network + mem_limit: 512m depends_on: mysql: condition: service_healthy - redis: - condition: service_healthy nginx: image: nginx:alpine ports: - - "${NGINX_PORT}:80" + - "${NGINX_PORT:-8080}:80" volumes: - - ./public:/var/www/repuve-v1/public + - ./:/var/www/netbien - ./Docker/nginx/nginx.conf:/etc/nginx/conf.d/default.conf networks: - - repuve-network + - netbien-network + mem_limit: 512m depends_on: - - repuve-backend + - netbien-backend mysql: image: mysql:8.0 @@ -41,11 +42,12 @@ services: MYSQL_PASSWORD: ${DB_PASSWORD} MYSQL_USER: ${DB_USERNAME} ports: - - ${DB_PORT}:${DB_PORT} + - "${DB_PORT:-3306}:3306" volumes: - mysql_data:/var/lib/mysql networks: - - repuve-network + - netbien-network + mem_limit: 512m healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] timeout: 15s @@ -57,31 +59,17 @@ services: PMA_HOST: mysql PMA_PORT: 3306 ports: - - '${PMA_PORT}:80' + - "${PMA_PORT:-8081}:80" depends_on: - - mysql + - mysql networks: - - repuve-network - - redis: - image: redis:alpine - ports: - - "${REDIS_PORT}:6379" - volumes: - - redis_data:/data - networks: - - repuve-network - healthcheck: - test: ["CMD", "redis-cli", "ping"] - timeout: 5s - retries: 5 + - netbien-network + mem_limit: 512m volumes: mysql_data: driver: local - redis_data: - driver: local networks: - repuve-network: + netbien-network: driver: bridge diff --git a/dockerfile b/dockerfile index 7cee8a0..714f9f8 100644 --- a/dockerfile +++ b/dockerfile @@ -1,8 +1,8 @@ FROM php:8.3-fpm -RUN mkdir -p /var/www/repuve-v1 +RUN mkdir -p /var/www/netbien -WORKDIR /var/www/repuve-v1 +WORKDIR /var/www/netbien RUN apt-get update && apt-get install -y\ git \ @@ -31,8 +31,8 @@ RUN chmod +x /usr/local/bin/entrypoint-dev.sh RUN mkdir -p storage/app/keys storage/logs bootstrap/cache -RUN chown -R www-data:www-data /var/www/repuve-v1/storage /var/www/repuve-v1/bootstrap/cache -RUN chmod -R 775 /var/www/repuve-v1/storage /var/www/repuve-v1/bootstrap/cache +RUN chown -R www-data:www-data /var/www/netbien/storage /var/www/netbien/bootstrap/cache +RUN chmod -R 775 /var/www/netbien/storage /var/www/netbien/bootstrap/cache EXPOSE 9000 diff --git a/entrypoint.sh b/entrypoint.sh deleted file mode 100644 index 0bf890d..0000000 --- a/entrypoint.sh +++ /dev/null @@ -1,105 +0,0 @@ -#!/bin/bash -set -e - -echo "=== Iniciando entrypoint ===" - -# Variables desde Docker environment -DB_HOST=${DB_HOST:-mysql} -DB_USERNAME=${DB_USERNAME:-root} -DB_PASSWORD=${DB_PASSWORD:-} -DB_DATABASE=${DB_DATABASE:-laravel} -MAX_RETRIES=30 -RETRY_COUNT=0 - -echo "Configuración de BD: Host=${DB_HOST}, Usuario=${DB_USERNAME}, Base=${DB_DATABASE}" - -# Función para verificar conectividad con MySQL usando PHP -check_mysql() { - php -r " - try { - \$pdo = new PDO('mysql:host=${DB_HOST};port=3306', '${DB_USERNAME}', '${DB_PASSWORD}'); - \$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - exit(0); - } catch (Exception \$e) { - exit(1); - } - " -} - -# Esperar a que MySQL esté disponible -echo "Esperando conexión a MySQL..." -until check_mysql; do - RETRY_COUNT=$((RETRY_COUNT + 1)) - if [ $RETRY_COUNT -ge $MAX_RETRIES ]; then - echo "ERROR: No se pudo conectar a MySQL después de $MAX_RETRIES intentos" - exit 1 - fi - echo "Intento $RETRY_COUNT/$MAX_RETRIES - Esperando a MySQL..." - sleep 2 -done - -echo "✓ MySQL está disponible" - -# Comandos de inicialización -echo "Ejecutando comandos de inicialización..." - -echo "Ejecutando package:discover..." -php artisan package:discover --ansi - -echo "Creando enlaces simbólicos..." -php artisan storage:link --force || true - -echo "Ejecutando configuración de producción..." -composer run env:prod - -echo "Creando directorio de claves Passport..." -mkdir -p storage/app/keys - -echo "Generando claves de Passport..." -php artisan passport:keys --force || true - -# Verificar que las claves se crearon -if [ ! -f "storage/app/keys/oauth-private.key" ] || [ ! -f "storage/app/keys/oauth-public.key" ]; then - echo "ERROR: Las claves de Passport no se generaron correctamente" - echo "Intentando generar manualmente..." - - # Generar claves manualmente usando OpenSSL - openssl genrsa -out storage/app/keys/oauth-private.key 4096 - openssl rsa -in storage/app/keys/oauth-private.key -pubout -out storage/app/keys/oauth-public.key - - echo "✓ Claves generadas manualmente" -fi - -# Establecer permisos correctos para las claves -chmod 600 storage/app/keys/oauth-private.key -chmod 644 storage/app/keys/oauth-public.key -chown www-data:www-data storage/app/keys/oauth-*.key - -echo "✓ Claves de Passport verificadas" - -# Archivo de control para primera ejecución -FIRST_RUN_FLAG="/var/www/holos.backend/.first_run_completed" - -# Solo en la primera ejecución -if [ ! -f "$FIRST_RUN_FLAG" ]; then - echo "=== PRIMERA EJECUCIÓN DETECTADA ===" - - echo "Ejecutando migraciones y seeders..." - if composer run db:prod; then - echo "✓ db:prod completado" - else - echo "ERROR: Falló db:prod" - exit 1 - fi - - # Marcar como completado - touch "$FIRST_RUN_FLAG" - echo "✓ Primera ejecución completada exitosamente" -else - echo "✓ No es primera ejecución, omitiendo setup inicial" -fi - -echo "=== Iniciando PHP-FPM ===" - -# Iniciar PHP-FPM -exec "$@" diff --git a/routes/api.php b/routes/api.php index b752e50..dc4f2ec 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,23 +1,29 @@ group(function() { - // Tus rutas protegidas + + Route::resource('sim-cards', SimCardController::class); + + Route::resource('packages', PackagesController::class); }); /** Rutas públicas */