WIP #1

Merged
juan.zapata merged 10 commits from develop into main 2025-11-10 22:45:59 +00:00
20 changed files with 603 additions and 150 deletions
Showing only changes of commit 87648a5318 - Show all commits

View File

@ -1,7 +1,7 @@
server { server {
listen 80; listen 80;
server_name _; server_name _;
root /var/www/golscontrols/public; root /var/www/netbien/public;
index index.php index.html; index index.php index.html;
# Logging # Logging
@ -17,7 +17,7 @@ server {
location ~ \.php$ { location ~ \.php$ {
try_files $uri =404; try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass golscontrols:9000; fastcgi_pass netbien-backend:9000;
fastcgi_index index.php; fastcgi_index index.php;
# Timeouts importantes para evitar errores 500 # Timeouts importantes para evitar errores 500
@ -45,17 +45,17 @@ server {
# Handle storage files (Laravel storage link) # Handle storage files (Laravel storage link)
location /storage { location /storage {
alias /var/www/golscontrols/storage/app; alias /var/www/netbien/storage/app;
try_files $uri =404; try_files $uri =404;
} }
location /profile { location /profile {
alias /var/www/golscontrols/storage/app/profile; alias /var/www/netbien/storage/app/profile;
try_files $uri =404; try_files $uri =404;
} }
location /images { location /images {
alias /var/www/golscontrols/storage/app/images; alias /var/www/netbien/storage/app/images;
try_files $uri =404; try_files $uri =404;
} }

View File

@ -0,0 +1,28 @@
<?php
namespace App\Enums;
enum SimCardStatus: string
{
case AVAILABLE = 'available';
case ASSIGNED = 'assigned';
/**
* Get all possible values
*/
public static function values(): array
{
return array_column(self::cases(), 'value');
}
/**
* Get a human-readable label
*/
public function label(): string
{
return match($this) {
self::AVAILABLE => 'Disponible',
self::ASSIGNED => 'Asignada',
};
}
}

View File

@ -0,0 +1,46 @@
<?php namespace App\Http\Controllers\Netbien;
/**
* @copyright (c) 2025 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
*/
use App\Http\Controllers\Controller;
use App\Http\Requests\Netbien\PackagesStoreRequest;
use App\Models\Packages;
use Notsoweb\ApiResponse\Enums\ApiResponse;
/**
*
*/
class PackagesController extends Controller
{
public function index()
{
$packages = Packages::OrderBy('id', 'asc')->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,
]);
}
}

View File

@ -0,0 +1,44 @@
<?php namespace App\Http\Controllers\Netbien;
/**
* @copyright (c) 2025 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
*/
use App\Http\Controllers\Controller;
use App\Http\Requests\Netbien\SimCardStoreRequest;
use App\Http\Requests\Netbien\SimCardUpdateRequest;
use App\Models\SimCard;
use Notsoweb\ApiResponse\Enums\ApiResponse;
/**
*
*/
class SimCardController extends Controller
{
public function index()
{
$simCards = SimCard::orderBy('id', 'asc')->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,
]);
}
}

View File

@ -0,0 +1,52 @@
<?php namespace App\Http\Requests\Netbien;
/**
* @copyright (c) 2025 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
*/
use Illuminate\Foundation\Http\FormRequest;
/**
* Almacenar rol
*
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
*
* @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.',
];
}
}

View File

@ -0,0 +1,55 @@
<?php namespace App\Http\Requests\Netbien;
/**
* @copyright (c) 2025 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
*/
use Illuminate\Foundation\Http\FormRequest;
/**
* Almacenar rol
*
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
*
* @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.',
];
}
}

View File

@ -0,0 +1,49 @@
<?php namespace App\Http\Requests\Netbien;
/**
* @copyright (c) 2025 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
*/
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
/**
* Almacenar rol
*
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
*
* @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.',
];
}
}

View File

@ -0,0 +1,43 @@
<?php namespace App\Http\Requests\Netbien;
/**
* @copyright (c) 2025 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
*/
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
/**
* Almacenar rol
*
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
*
* @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.',
];
}
}

37
app/Models/PackSim.php Normal file
View File

@ -0,0 +1,37 @@
<?php namespace App\Models;
/**
* @copyright (c) 2025 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
*/
use Illuminate\Database\Eloquent\Model;
/**
* Descripción
*
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
*
* @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');
}
}

36
app/Models/Packages.php Normal file
View File

@ -0,0 +1,36 @@
<?php namespace App\Models;
/**
* @copyright (c) 2025 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
*/
use Illuminate\Database\Eloquent\Model;
/**
* Descripción
*
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
*
* @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');
}
}

33
app/Models/SimCard.php Normal file
View File

@ -0,0 +1,33 @@
<?php namespace App\Models;
/**
* @copyright (c) 2025 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
*/
use App\Enums\SimCardStatus;
use Illuminate\Database\Eloquent\Model;
/**
* Modelo para tarjetas SIM
*
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
*
* @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');
}
}

View File

@ -0,0 +1,31 @@
<?php
use App\Enums\SimCardStatus;
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('sim_cards', function (Blueprint $table) {
$table->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');
}
};

View File

@ -0,0 +1,31 @@
<?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('packages', function (Blueprint $table) {
$table->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');
}
};

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('pack_sims', function (Blueprint $table) {
$table->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');
}
};

View File

@ -0,0 +1,25 @@
<?php namespace Database\Seeders;
/**
* @copyright (c) 2025 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
*/
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
/**
* Descripción
*
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
*
* @version 1.0.0
*/
class Packages extends Seeder
{
/**
* Ejecutar sembrado de base de datos
*/
public function run(): void
{
//
}
}

View File

@ -0,0 +1,25 @@
<?php namespace Database\Seeders;
/**
* @copyright (c) 2025 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
*/
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
/**
* Descripción
*
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
*
* @version 1.0.0
*/
class SimCardSeeder extends Seeder
{
/**
* Ejecutar sembrado de base de datos
*/
public function run(): void
{
//
}
}

View File

@ -1,9 +1,9 @@
services: services:
repuve-backend: netbien-backend:
build: build:
context: . context: .
dockerfile: dockerfile.dev dockerfile: dockerfile
working_dir: /var/www/repuve-v1 working_dir: /var/www/netbien
environment: environment:
- DB_HOST=mysql - DB_HOST=mysql
- DB_USERNAME=${DB_USERNAME} - DB_USERNAME=${DB_USERNAME}
@ -11,27 +11,28 @@ services:
- DB_DATABASE=${DB_DATABASE} - DB_DATABASE=${DB_DATABASE}
- DB_PORT=${DB_PORT} - DB_PORT=${DB_PORT}
volumes: volumes:
- ./:/var/www/repuve-v1 - ./:/var/www/netbien
- /var/www/repuve-v1/vendor - ./vendor:/var/www/netbien/vendor
- /var/www/netbien/node_modules
networks: networks:
- repuve-network - netbien-network
mem_limit: 512m
depends_on: depends_on:
mysql: mysql:
condition: service_healthy condition: service_healthy
redis:
condition: service_healthy
nginx: nginx:
image: nginx:alpine image: nginx:alpine
ports: ports:
- "${NGINX_PORT}:80" - "${NGINX_PORT:-8080}:80"
volumes: volumes:
- ./public:/var/www/repuve-v1/public - ./:/var/www/netbien
- ./Docker/nginx/nginx.conf:/etc/nginx/conf.d/default.conf - ./Docker/nginx/nginx.conf:/etc/nginx/conf.d/default.conf
networks: networks:
- repuve-network - netbien-network
mem_limit: 512m
depends_on: depends_on:
- repuve-backend - netbien-backend
mysql: mysql:
image: mysql:8.0 image: mysql:8.0
@ -41,11 +42,12 @@ services:
MYSQL_PASSWORD: ${DB_PASSWORD} MYSQL_PASSWORD: ${DB_PASSWORD}
MYSQL_USER: ${DB_USERNAME} MYSQL_USER: ${DB_USERNAME}
ports: ports:
- ${DB_PORT}:${DB_PORT} - "${DB_PORT:-3306}:3306"
volumes: volumes:
- mysql_data:/var/lib/mysql - mysql_data:/var/lib/mysql
networks: networks:
- repuve-network - netbien-network
mem_limit: 512m
healthcheck: healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
timeout: 15s timeout: 15s
@ -57,31 +59,17 @@ services:
PMA_HOST: mysql PMA_HOST: mysql
PMA_PORT: 3306 PMA_PORT: 3306
ports: ports:
- '${PMA_PORT}:80' - "${PMA_PORT:-8081}:80"
depends_on: depends_on:
- mysql - mysql
networks: networks:
- repuve-network - netbien-network
mem_limit: 512m
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
volumes: volumes:
mysql_data: mysql_data:
driver: local driver: local
redis_data:
driver: local
networks: networks:
repuve-network: netbien-network:
driver: bridge driver: bridge

View File

@ -1,8 +1,8 @@
FROM php:8.3-fpm 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\ RUN apt-get update && apt-get install -y\
git \ 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 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 chown -R www-data:www-data /var/www/netbien/storage /var/www/netbien/bootstrap/cache
RUN chmod -R 775 /var/www/repuve-v1/storage /var/www/repuve-v1/bootstrap/cache RUN chmod -R 775 /var/www/netbien/storage /var/www/netbien/bootstrap/cache
EXPOSE 9000 EXPOSE 9000

View File

@ -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 "$@"

View File

@ -1,4 +1,7 @@
<?php <?php
use App\Http\Controllers\Netbien\PackagesController;
use App\Http\Controllers\Netbien\SimCardController;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
/** /**
@ -17,7 +20,10 @@
/** Rutas protegidas (requieren autenticación) */ /** Rutas protegidas (requieren autenticación) */
Route::middleware('auth:api')->group(function() { Route::middleware('auth:api')->group(function() {
// Tus rutas protegidas
Route::resource('sim-cards', SimCardController::class);
Route::resource('packages', PackagesController::class);
}); });
/** Rutas públicas */ /** Rutas públicas */