feat: dockerizar proyecto para produccion

main
parent 87e72bc029
commit 1f5f3ae81e

@ -0,0 +1,57 @@
# ===========================================
# Produccion - Direccion de Admision 2026
# ===========================================
# Copiar este archivo como .env.prod y completar los valores
# --- App ---
APP_NAME="Direccion de Admision"
APP_ENV=production
APP_KEY=
APP_DEBUG=false
APP_URL=https://tu-dominio.com
# Puerto donde se expone la app (default: 80)
APP_PORT=80
# --- Base de datos ---
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=admision_2026
DB_USERNAME=root
DB_PASSWORD=CAMBIAR_PASSWORD_SEGURA
# --- Sessions ---
SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=true
SESSION_DOMAIN=tu-dominio.com
# --- Cache & Queue ---
CACHE_STORE=database
QUEUE_CONNECTION=database
# --- Sanctum ---
SANCTUM_STATEFUL_DOMAINS=tu-dominio.com
# --- Frontend ---
VITE_API_URL=/api
# --- Mail (configurar segun proveedor) ---
MAIL_MAILER=smtp
MAIL_HOST=smtp.ejemplo.com
MAIL_PORT=587
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS="admision@tu-dominio.com"
MAIL_FROM_NAME="${APP_NAME}"
# --- Logging ---
LOG_CHANNEL=stack
LOG_STACK=single
LOG_LEVEL=error
# --- Misc ---
BCRYPT_ROUNDS=12
FILESYSTEM_DISK=local

@ -0,0 +1,687 @@
-- --------------------------------------------------------
-- Host: 127.0.0.1
-- Versión del servidor: 8.0.30 - MySQL Community Server - GPL
-- SO del servidor: Win64
-- HeidiSQL Versión: 12.1.0.6537
-- --------------------------------------------------------
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8 */;
/*!50503 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
-- Volcando estructura de base de datos para admision_2026
DROP DATABASE IF EXISTS `admision_2026`;
CREATE DATABASE IF NOT EXISTS `admision_2026` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;
USE `admision_2026`;
-- Volcando estructura para tabla admision_2026.areas
DROP TABLE IF EXISTS `areas`;
CREATE TABLE IF NOT EXISTS `areas` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`nombre` varchar(100) NOT NULL,
`codigo` varchar(20) NOT NULL,
`descripcion` varchar(500) DEFAULT NULL,
`activo` tinyint(1) NOT NULL DEFAULT '1',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `codigo` (`codigo`),
KEY `idx_areas_activo` (`activo`),
KEY `idx_areas_codigo` (`codigo`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.areas_admision
DROP TABLE IF EXISTS `areas_admision`;
CREATE TABLE IF NOT EXISTS `areas_admision` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`nombre` varchar(150) NOT NULL,
`descripcion` text,
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.area_curso
DROP TABLE IF EXISTS `area_curso`;
CREATE TABLE IF NOT EXISTS `area_curso` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`area_id` bigint unsigned NOT NULL,
`curso_id` bigint unsigned NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
KEY `fk_area_curso_area` (`area_id`) USING BTREE,
KEY `fk_area_curso_curso` (`curso_id`) USING BTREE,
CONSTRAINT `fk_area_curso_area` FOREIGN KEY (`area_id`) REFERENCES `areas` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_area_curso_curso` FOREIGN KEY (`curso_id`) REFERENCES `cursos` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.area_proceso
DROP TABLE IF EXISTS `area_proceso`;
CREATE TABLE IF NOT EXISTS `area_proceso` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`area_id` bigint unsigned NOT NULL,
`proceso_id` bigint unsigned NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `area_proceso_unique` (`area_id`,`proceso_id`),
KEY `fk_area_proceso_proceso` (`proceso_id`),
CONSTRAINT `fk_area_proceso_area` FOREIGN KEY (`area_id`) REFERENCES `areas` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_area_proceso_proceso` FOREIGN KEY (`proceso_id`) REFERENCES `procesos` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.cache
DROP TABLE IF EXISTS `cache`;
CREATE TABLE IF NOT EXISTS `cache` (
`key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`value` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`expiration` int NOT NULL,
PRIMARY KEY (`key`),
KEY `cache_expiration_index` (`expiration`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.cache_locks
DROP TABLE IF EXISTS `cache_locks`;
CREATE TABLE IF NOT EXISTS `cache_locks` (
`key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`owner` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`expiration` int NOT NULL,
PRIMARY KEY (`key`),
KEY `cache_locks_expiration_index` (`expiration`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.cursos
DROP TABLE IF EXISTS `cursos`;
CREATE TABLE IF NOT EXISTS `cursos` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`nombre` varchar(100) NOT NULL,
`codigo` varchar(20) NOT NULL,
`descripcion` varchar(500) DEFAULT NULL,
`activo` tinyint(1) NOT NULL DEFAULT '1',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `codigo` (`codigo`),
KEY `idx_cursos_activo` (`activo`),
KEY `idx_cursos_codigo` (`codigo`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.examenes
DROP TABLE IF EXISTS `examenes`;
CREATE TABLE IF NOT EXISTS `examenes` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`postulante_id` bigint unsigned NOT NULL,
`area_proceso_id` bigint unsigned NOT NULL,
`pagado` tinyint(1) NOT NULL DEFAULT '0',
`tipo_pago` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`pago_id` decimal(20,6) DEFAULT '0.000000',
`intentos` int NOT NULL DEFAULT '0',
`hora_inicio` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
KEY `examenes_postulante_id_foreign` (`postulante_id`) USING BTREE,
KEY `examenes_area_proceso_id_foreign` (`area_proceso_id`) USING BTREE,
CONSTRAINT `examenes_area_proceso_id_foreign` FOREIGN KEY (`area_proceso_id`) REFERENCES `area_proceso` (`id`) ON DELETE CASCADE,
CONSTRAINT `examenes_postulante_id_foreign` FOREIGN KEY (`postulante_id`) REFERENCES `postulantes` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.failed_jobs
DROP TABLE IF EXISTS `failed_jobs`;
CREATE TABLE IF NOT EXISTS `failed_jobs` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`uuid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`connection` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`queue` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`payload` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`exception` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `failed_jobs_uuid_unique` (`uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.jobs
DROP TABLE IF EXISTS `jobs`;
CREATE TABLE IF NOT EXISTS `jobs` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`queue` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`payload` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`attempts` tinyint unsigned NOT NULL,
`reserved_at` int unsigned DEFAULT NULL,
`available_at` int unsigned NOT NULL,
`created_at` int unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `jobs_queue_index` (`queue`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.job_batches
DROP TABLE IF EXISTS `job_batches`;
CREATE TABLE IF NOT EXISTS `job_batches` (
`id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`total_jobs` int NOT NULL,
`pending_jobs` int NOT NULL,
`failed_jobs` int NOT NULL,
`failed_job_ids` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`options` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
`cancelled_at` int DEFAULT NULL,
`created_at` int NOT NULL,
`finished_at` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.migrations
DROP TABLE IF EXISTS `migrations`;
CREATE TABLE IF NOT EXISTS `migrations` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`migration` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`batch` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.model_has_permissions
DROP TABLE IF EXISTS `model_has_permissions`;
CREATE TABLE IF NOT EXISTS `model_has_permissions` (
`permission_id` bigint unsigned NOT NULL,
`model_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`model_id` bigint unsigned NOT NULL,
PRIMARY KEY (`permission_id`,`model_id`,`model_type`),
KEY `model_has_permissions_model_id_model_type_index` (`model_id`,`model_type`),
CONSTRAINT `model_has_permissions_permission_id_foreign` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.model_has_roles
DROP TABLE IF EXISTS `model_has_roles`;
CREATE TABLE IF NOT EXISTS `model_has_roles` (
`role_id` bigint unsigned NOT NULL,
`model_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`model_id` bigint unsigned NOT NULL,
PRIMARY KEY (`role_id`,`model_id`,`model_type`),
KEY `model_has_roles_model_id_model_type_index` (`model_id`,`model_type`),
CONSTRAINT `model_has_roles_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.pagos
DROP TABLE IF EXISTS `pagos`;
CREATE TABLE IF NOT EXISTS `pagos` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`postulante_id` bigint unsigned NOT NULL,
`tipo_pago` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`codigo_pago` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`utilizado` tinyint(1) NOT NULL DEFAULT '0',
`monto` decimal(10,2) NOT NULL,
`original_date` timestamp NULL DEFAULT NULL,
`confirmed_date` timestamp NULL DEFAULT NULL,
`fecha_pago` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `pagos_codigo_pago_unique` (`codigo_pago`) USING BTREE,
KEY `pagos_postulante_id_foreign` (`postulante_id`) USING BTREE,
CONSTRAINT `pagos_postulante_id_foreign` FOREIGN KEY (`postulante_id`) REFERENCES `postulantes` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.password_reset_tokens
DROP TABLE IF EXISTS `password_reset_tokens`;
CREATE TABLE IF NOT EXISTS `password_reset_tokens` (
`email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`token` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.permissions
DROP TABLE IF EXISTS `permissions`;
CREATE TABLE IF NOT EXISTS `permissions` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`guard_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `permissions_name_guard_name_unique` (`name`,`guard_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.personal_access_tokens
DROP TABLE IF EXISTS `personal_access_tokens`;
CREATE TABLE IF NOT EXISTS `personal_access_tokens` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`tokenable_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`tokenable_id` bigint unsigned NOT NULL,
`name` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`token` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`abilities` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
`last_used_at` timestamp NULL DEFAULT NULL,
`expires_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `personal_access_tokens_token_unique` (`token`),
KEY `personal_access_tokens_tokenable_type_tokenable_id_index` (`tokenable_type`,`tokenable_id`),
KEY `personal_access_tokens_expires_at_index` (`expires_at`)
) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.postulantes
DROP TABLE IF EXISTS `postulantes`;
CREATE TABLE IF NOT EXISTS `postulantes` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`email` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`dni` varchar(20) NOT NULL,
`device_id` varchar(100) DEFAULT NULL,
`last_activity` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`),
UNIQUE KEY `dni` (`dni`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.preguntas
DROP TABLE IF EXISTS `preguntas`;
CREATE TABLE IF NOT EXISTS `preguntas` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`curso_id` bigint unsigned NOT NULL,
`enunciado` longtext NOT NULL,
`enunciado_adicional` longtext,
`opciones` json DEFAULT NULL,
`respuesta_correcta` longtext,
`explicacion` longtext,
`imagenes_explicacion` json DEFAULT NULL,
`imagenes` json DEFAULT NULL,
`nivel_dificultad` varchar(20) DEFAULT NULL,
`activo` tinyint(1) NOT NULL DEFAULT '1',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_preguntas_curso` (`curso_id`),
KEY `idx_preguntas_activo` (`activo`),
CONSTRAINT `fk_preguntas_curso` FOREIGN KEY (`curso_id`) REFERENCES `cursos` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.preguntas_asignadas
DROP TABLE IF EXISTS `preguntas_asignadas`;
CREATE TABLE IF NOT EXISTS `preguntas_asignadas` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`examen_id` bigint unsigned NOT NULL,
`pregunta_id` bigint unsigned NOT NULL,
`orden` int NOT NULL,
`respuesta_usuario` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Clave elegida (A, B, C, D) o texto si es abierta',
`es_correcta` tinyint(1) DEFAULT NULL COMMENT '1 correcta, 0 incorrecta, NULL no respondida',
`estado` enum('pendiente','respondida','anulada') COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'pendiente',
`puntaje` decimal(5,2) NOT NULL DEFAULT '0.00',
`respondida_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_preg_asig_examen` (`examen_id`) USING BTREE,
KEY `idx_preg_asig_pregunta` (`pregunta_id`) USING BTREE,
KEY `idx_preg_asig_estado` (`estado`) USING BTREE,
CONSTRAINT `fk_preg_asig_examen` FOREIGN KEY (`examen_id`) REFERENCES `examenes` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_preg_asig_pregunta` FOREIGN KEY (`pregunta_id`) REFERENCES `preguntas` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.procesos
DROP TABLE IF EXISTS `procesos`;
CREATE TABLE IF NOT EXISTS `procesos` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`nombre` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`descripcion` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
`estado` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'borrador',
`duracion` int NOT NULL COMMENT 'Duración total en minutos',
`intentos_maximos` int DEFAULT '1',
`requiere_pago` tinyint(1) NOT NULL DEFAULT '0',
`precio` decimal(8,2) DEFAULT NULL,
`calificacion_id` bigint unsigned DEFAULT NULL,
`slug` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`tipo_simulacro` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'simulacro | test | practica',
`tipo_proceso` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'admision | preuniversitario | universitario',
`activo` tinyint(1) NOT NULL DEFAULT '1',
`publico` tinyint(1) NOT NULL DEFAULT '0',
`fecha_inicio` datetime DEFAULT NULL,
`fecha_fin` datetime DEFAULT NULL,
`tiempo_por_pregunta` int DEFAULT NULL COMMENT 'Segundos por pregunta',
`cantidad_pregunta` int DEFAULT '10',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `slug` (`slug`),
KEY `idx_examenes_estado` (`estado`),
KEY `idx_examenes_activo` (`activo`),
KEY `idx_examenes_publico` (`publico`),
KEY `idx_examenes_tipo_simulacro` (`tipo_simulacro`),
KEY `idx_examenes_tipo_proceso` (`tipo_proceso`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.procesos_admision
DROP TABLE IF EXISTS `procesos_admision`;
CREATE TABLE IF NOT EXISTS `procesos_admision` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`titulo` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`subtitulo` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`descripcion` text COLLATE utf8mb4_unicode_ci,
`slug` varchar(120) COLLATE utf8mb4_unicode_ci NOT NULL,
`tipo_proceso` varchar(60) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`modalidad` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`publicado` tinyint(1) NOT NULL DEFAULT '0',
`fecha_publicacion` datetime DEFAULT NULL,
`fecha_inicio_preinscripcion` datetime DEFAULT NULL,
`fecha_fin_preinscripcion` datetime DEFAULT NULL,
`fecha_inicio_inscripcion` datetime DEFAULT NULL,
`fecha_fin_inscripcion` datetime DEFAULT NULL,
`fecha_examen1` datetime DEFAULT NULL,
`fecha_examen2` datetime DEFAULT NULL,
`fecha_resultados` datetime DEFAULT NULL,
`fecha_inicio_biometrico` datetime DEFAULT NULL,
`fecha_fin_biometrico` datetime DEFAULT NULL,
`imagen_path` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`banner_path` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`brochure_path` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`link_preinscripcion` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`link_inscripcion` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`link_resultados` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`link_reglamento` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`estado` enum('nuevo','publicado','en_proceso','finalizado','cancelado') COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'nuevo',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_procesos_slug` (`slug`),
KEY `idx_procesos_publico` (`publicado`,`estado`),
KEY `idx_procesos_fechas` (`fecha_inicio_inscripcion`,`fecha_examen1`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.proceso_admision_detalles
DROP TABLE IF EXISTS `proceso_admision_detalles`;
CREATE TABLE IF NOT EXISTS `proceso_admision_detalles` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`proceso_admision_id` bigint unsigned NOT NULL,
`tipo` enum('requisitos','pagos','vacantes','cronograma') COLLATE utf8mb4_unicode_ci NOT NULL,
`titulo_detalle` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`descripcion` text COLLATE utf8mb4_unicode_ci,
`listas` json DEFAULT NULL,
`meta` json DEFAULT NULL,
`url` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`imagen_path` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`imagen_path_2` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_proceso_modalidad_tipo` (`proceso_admision_id`),
KEY `idx_detalles_lookup` (`proceso_admision_id`,`tipo`),
CONSTRAINT `fk_detalles_proceso` FOREIGN KEY (`proceso_admision_id`) REFERENCES `procesos_admision` (`id`) ON DELETE CASCADE,
CONSTRAINT `proceso_admision_detalles_chk_1` CHECK (json_valid(`listas`)),
CONSTRAINT `proceso_admision_detalles_chk_2` CHECK (json_valid(`meta`))
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.reglas_area_proceso
DROP TABLE IF EXISTS `reglas_area_proceso`;
CREATE TABLE IF NOT EXISTS `reglas_area_proceso` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`area_proceso_id` bigint unsigned NOT NULL,
`curso_id` bigint unsigned NOT NULL,
`cantidad_preguntas` int NOT NULL DEFAULT '0',
`orden` int NOT NULL DEFAULT '1',
`nivel_dificultad` varchar(50) DEFAULT 'medio',
`ponderacion` decimal(5,2) DEFAULT '0.00',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `area_proceso_id` (`area_proceso_id`),
KEY `curso_id` (`curso_id`),
CONSTRAINT `reglas_area_proceso_ibfk_1` FOREIGN KEY (`area_proceso_id`) REFERENCES `area_proceso` (`id`),
CONSTRAINT `reglas_area_proceso_ibfk_2` FOREIGN KEY (`curso_id`) REFERENCES `cursos` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.resultados_admision
DROP TABLE IF EXISTS `resultados_admision`;
CREATE TABLE IF NOT EXISTS `resultados_admision` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`dni` varchar(20) NOT NULL,
`paterno` varchar(100) NOT NULL,
`materno` varchar(100) NOT NULL,
`nombres` varchar(150) NOT NULL,
`puntaje` decimal(6,2) DEFAULT '0.00',
`vocacional` decimal(6,2) DEFAULT '0.00',
`apto` enum('SI','NO') DEFAULT 'NO',
`obs` text,
`desprograma` tinyint(1) DEFAULT '0',
`idproceso` bigint unsigned NOT NULL,
`idearea` bigint unsigned NOT NULL,
`litho` varchar(50) DEFAULT NULL,
`numlectura` varchar(50) DEFAULT NULL,
`tipo` varchar(50) DEFAULT NULL,
`calificar` tinyint(1) DEFAULT '1',
`aula` varchar(50) DEFAULT NULL,
`respuestas` json DEFAULT NULL,
`puesto` int DEFAULT NULL,
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_dni` (`dni`),
KEY `idx_proceso` (`idproceso`),
KEY `idx_area` (`idearea`),
CONSTRAINT `fk_resultado_area_admision` FOREIGN KEY (`idearea`) REFERENCES `areas_admision` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_resultado_proceso_admision` FOREIGN KEY (`idproceso`) REFERENCES `procesos_admision` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.resultados_admision_carga
DROP TABLE IF EXISTS `resultados_admision_carga`;
CREATE TABLE IF NOT EXISTS `resultados_admision_carga` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`dni` varchar(20) DEFAULT NULL,
`paterno` varchar(100) DEFAULT NULL,
`materno` varchar(100) DEFAULT NULL,
`nombres` varchar(150) DEFAULT NULL,
`idproceso` bigint unsigned NOT NULL,
`idearea` bigint unsigned NOT NULL,
`apto` char(20) DEFAULT NULL,
`puntaje_total` decimal(8,2) DEFAULT NULL,
`puesto` int DEFAULT NULL,
`correctas_aritmetica` int DEFAULT NULL,
`blancas_aritmetica` int DEFAULT NULL,
`puntaje_aritmetica` decimal(6,2) DEFAULT NULL,
`porcentaje_aritmetica` decimal(5,2) DEFAULT NULL,
`correctas_algebra` int DEFAULT NULL,
`blancas_algebra` int DEFAULT NULL,
`puntaje_algebra` decimal(6,2) DEFAULT NULL,
`porcentaje_algebra` decimal(5,2) DEFAULT NULL,
`correctas_geometria` int DEFAULT NULL,
`blancas_geometria` int DEFAULT NULL,
`puntaje_geometria` decimal(6,2) DEFAULT NULL,
`porcentaje_geometria` decimal(5,2) DEFAULT NULL,
`correctas_trigonometria` int DEFAULT NULL,
`blancas_trigonometria` int DEFAULT NULL,
`puntaje_trigonometria` decimal(6,2) DEFAULT NULL,
`porcentaje_trigonometria` decimal(5,2) DEFAULT NULL,
`correctas_fisica` int DEFAULT NULL,
`blancas_fisica` int DEFAULT NULL,
`puntaje_fisica` decimal(6,2) DEFAULT NULL,
`porcentaje_fisica` decimal(5,2) DEFAULT NULL,
`correctas_quimica` int DEFAULT NULL,
`blancas_quimica` int DEFAULT NULL,
`puntaje_quimica` decimal(6,2) DEFAULT NULL,
`porcentaje_quimica` decimal(5,2) DEFAULT NULL,
`correctas_biologia_anatomia` int DEFAULT NULL,
`blancas_biologia_anatomia` int DEFAULT NULL,
`puntaje_biologia_anatomia` decimal(6,2) DEFAULT NULL,
`porcentaje_biologia_anatomia` decimal(5,2) DEFAULT NULL,
`correctas_psicologia_filosofia` int DEFAULT NULL,
`blancas_psicologia_filosofia` int DEFAULT NULL,
`puntaje_psicologia_filosofia` decimal(6,2) DEFAULT NULL,
`porcentaje_psicologia_filosofia` decimal(5,2) DEFAULT NULL,
`correctas_geografia` int DEFAULT NULL,
`blancas_geografia` int DEFAULT NULL,
`puntaje_geografia` decimal(6,2) DEFAULT NULL,
`porcentaje_geografia` decimal(5,2) DEFAULT NULL,
`correctas_historia` int DEFAULT NULL,
`blancas_historia` int DEFAULT NULL,
`puntaje_historia` decimal(6,2) DEFAULT NULL,
`porcentaje_historia` decimal(5,2) DEFAULT NULL,
`correctas_educacion_civica` int DEFAULT NULL,
`blancas_educacion_civica` int DEFAULT NULL,
`puntaje_educacion_civica` decimal(6,2) DEFAULT NULL,
`porcentaje_educacion_civica` decimal(5,2) DEFAULT NULL,
`correctas_economia` int DEFAULT NULL,
`blancas_economia` int DEFAULT NULL,
`puntaje_economia` decimal(6,2) DEFAULT NULL,
`porcentaje_economia` decimal(5,2) DEFAULT NULL,
`correctas_comunicacion` int DEFAULT NULL,
`blancas_comunicacion` int DEFAULT NULL,
`puntaje_comunicacion` decimal(6,2) DEFAULT NULL,
`porcentaje_comunicacion` decimal(5,2) DEFAULT NULL,
`correctas_literatura` int DEFAULT NULL,
`blancas_literatura` int DEFAULT NULL,
`puntaje_literatura` decimal(6,2) DEFAULT NULL,
`porcentaje_literatura` decimal(5,2) DEFAULT NULL,
`correctas_razonamiento_matematico` int DEFAULT NULL,
`blancas_razonamiento_matematico` int DEFAULT NULL,
`puntaje_razonamiento_matematico` decimal(6,2) DEFAULT NULL,
`porcentaje_razonamiento_matematico` decimal(5,2) DEFAULT NULL,
`correctas_razonamiento_verbal` int DEFAULT NULL,
`blancas_razonamiento_verbal` int DEFAULT NULL,
`puntaje_razonamiento_verbal` decimal(6,2) DEFAULT NULL,
`porcentaje_razonamiento_verbal` decimal(5,2) DEFAULT NULL,
`correctas_ingles` int DEFAULT NULL,
`blancas_ingles` int DEFAULT NULL,
`puntaje_ingles` decimal(6,2) DEFAULT NULL,
`porcentaje_ingles` decimal(5,2) DEFAULT NULL,
`correctas_quechua_aimara` int DEFAULT NULL,
`blancas_quechua_aimara` int DEFAULT NULL,
`puntaje_quechua_aimara` decimal(6,2) DEFAULT NULL,
`porcentaje_quechua_aimara` decimal(5,2) DEFAULT NULL,
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_dni` (`dni`),
KEY `idx_proceso_area` (`idproceso`,`idearea`),
KEY `fk_carga_area` (`idearea`),
CONSTRAINT `fk_carga_area` FOREIGN KEY (`idearea`) REFERENCES `areas_admision` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_carga_proceso` FOREIGN KEY (`idproceso`) REFERENCES `procesos_admision` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.roles
DROP TABLE IF EXISTS `roles`;
CREATE TABLE IF NOT EXISTS `roles` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`guard_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `roles_name_guard_name_unique` (`name`,`guard_name`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.role_has_permissions
DROP TABLE IF EXISTS `role_has_permissions`;
CREATE TABLE IF NOT EXISTS `role_has_permissions` (
`permission_id` bigint unsigned NOT NULL,
`role_id` bigint unsigned NOT NULL,
PRIMARY KEY (`permission_id`,`role_id`),
KEY `role_has_permissions_role_id_foreign` (`role_id`),
CONSTRAINT `role_has_permissions_permission_id_foreign` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE,
CONSTRAINT `role_has_permissions_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.sessions
DROP TABLE IF EXISTS `sessions`;
CREATE TABLE IF NOT EXISTS `sessions` (
`id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`user_id` bigint unsigned DEFAULT NULL,
`ip_address` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`user_agent` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
`payload` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`last_activity` int NOT NULL,
PRIMARY KEY (`id`),
KEY `sessions_user_id_index` (`user_id`),
KEY `sessions_last_activity_index` (`last_activity`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- La exportación de datos fue deseleccionada.
-- Volcando estructura para tabla admision_2026.users
DROP TABLE IF EXISTS `users`;
CREATE TABLE IF NOT EXISTS `users` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`email_verified_at` timestamp NULL DEFAULT NULL,
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`remember_token` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `users_email_unique` (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- La exportación de datos fue deseleccionada.
/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, 'system') */;
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */;

@ -0,0 +1,12 @@
vendor
node_modules
.env
.env.backup
.env.production
storage/logs/*
storage/framework/cache/*
storage/framework/sessions/*
storage/framework/views/*
tests
.git
.phpunit.cache

@ -0,0 +1,72 @@
# Stage 1: Composer dependencies
FROM composer:2 AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install \
--no-dev \
--no-interaction \
--no-scripts \
--prefer-dist \
--optimize-autoloader
# Stage 2: PHP-FPM production image
FROM php:8.4-fpm-alpine
# Install system dependencies
RUN apk add --no-cache \
libpng-dev \
libjpeg-turbo-dev \
freetype-dev \
libzip-dev \
libxml2-dev \
curl-dev \
oniguruma-dev \
icu-dev
# Install PHP extensions
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install \
pdo_mysql \
bcmath \
mbstring \
gd \
curl \
zip \
xml \
intl \
opcache
# Configure OPcache for production
RUN echo "opcache.enable=1" >> /usr/local/etc/php/conf.d/opcache.ini \
&& echo "opcache.memory_consumption=128" >> /usr/local/etc/php/conf.d/opcache.ini \
&& echo "opcache.interned_strings_buffer=8" >> /usr/local/etc/php/conf.d/opcache.ini \
&& echo "opcache.max_accelerated_files=10000" >> /usr/local/etc/php/conf.d/opcache.ini \
&& echo "opcache.validate_timestamps=0" >> /usr/local/etc/php/conf.d/opcache.ini
# PHP production settings
RUN echo "upload_max_filesize=20M" >> /usr/local/etc/php/conf.d/uploads.ini \
&& echo "post_max_size=25M" >> /usr/local/etc/php/conf.d/uploads.ini
WORKDIR /var/www/html
# Copy application code
COPY . .
# Copy vendor from composer stage
COPY --from=vendor /app/vendor ./vendor
# Set permissions
RUN chown -R www-data:www-data storage bootstrap/cache \
&& chmod -R 775 storage bootstrap/cache
# Laravel optimizations
RUN php artisan config:cache \
&& php artisan route:cache \
&& php artisan view:cache
EXPOSE 9000
CMD ["php-fpm"]

@ -0,0 +1,67 @@
services:
nginx:
image: nginx:alpine
container_name: admision_prod_nginx
restart: unless-stopped
ports:
- "${APP_PORT:-80}:80"
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
- backend_storage:/var/www/html/storage/app/public:ro
depends_on:
- backend
- frontend
networks:
- admision_net
backend:
build:
context: ./back
dockerfile: Dockerfile
container_name: admision_prod_backend
restart: unless-stopped
env_file:
- .env.prod
volumes:
- backend_storage:/var/www/html/storage
depends_on:
mysql:
condition: service_healthy
networks:
- admision_net
frontend:
build:
context: ./front
dockerfile: Dockerfile
args:
VITE_API_URL: ${VITE_API_URL:-/api}
container_name: admision_prod_frontend
restart: unless-stopped
networks:
- admision_net
mysql:
image: mysql:8.0
container_name: admision_prod_db
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
MYSQL_DATABASE: ${DB_DATABASE:-admision_2026}
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-p${DB_PASSWORD}"]
interval: 10s
timeout: 5s
retries: 5
networks:
- admision_net
volumes:
mysql_data:
backend_storage:
networks:
admision_net:
driver: bridge

@ -0,0 +1,15 @@
services:
db:
image: mysql:8.0
container_name: admision_2026_db
restart: unless-stopped
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: admision_2026
volumes:
- mysql_data:/var/lib/mysql
volumes:
mysql_data:

@ -0,0 +1,4 @@
node_modules
dist
.env
.git

@ -0,0 +1,31 @@
# Stage 1: Build Vue SPA
FROM node:20-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
ARG VITE_API_URL
ENV VITE_API_URL=${VITE_API_URL}
RUN npm run build
# Stage 2: Serve with nginx
FROM nginx:alpine
# Remove default nginx config
RUN rm /etc/nginx/conf.d/default.conf
# Copy custom nginx config
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Copy built files
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

@ -0,0 +1,33 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# Gzip compression
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 256;
gzip_types
text/plain
text/css
text/javascript
application/javascript
application/json
application/xml
image/svg+xml;
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
try_files $uri =404;
}
# SPA fallback - all routes to index.html
location / {
try_files $uri $uri/ /index.html;
}
}

@ -1188,7 +1188,6 @@
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=8"
}
@ -1198,7 +1197,6 @@
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"license": "MIT",
"peer": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@ -1344,7 +1342,6 @@
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=6"
}
@ -1354,6 +1351,7 @@
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz",
"integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@kurkle/color": "^0.3.0"
},
@ -1366,7 +1364,6 @@
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
"license": "ISC",
"peer": true,
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
@ -1387,7 +1384,6 @@
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"color-name": "~1.1.4"
},
@ -1399,8 +1395,7 @@
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/combined-stream": {
"version": "1.0.8",
@ -1472,7 +1467,6 @@
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@ -1553,8 +1547,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
"integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/dom-align": {
"version": "1.12.4",
@ -1586,8 +1579,7 @@
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/entities": {
"version": "7.0.1",
@ -1735,7 +1727,6 @@
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"license": "MIT",
"peer": true,
"dependencies": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
@ -1818,7 +1809,6 @@
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"license": "ISC",
"peer": true,
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
@ -1966,7 +1956,6 @@
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=8"
}
@ -2045,7 +2034,6 @@
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"license": "MIT",
"peer": true,
"dependencies": {
"p-locate": "^4.1.0"
},
@ -2262,7 +2250,6 @@
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"license": "MIT",
"peer": true,
"dependencies": {
"p-try": "^2.0.0"
},
@ -2278,7 +2265,6 @@
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"license": "MIT",
"peer": true,
"dependencies": {
"p-limit": "^2.2.0"
},
@ -2291,7 +2277,6 @@
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=6"
}
@ -2311,7 +2296,6 @@
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=8"
}
@ -2334,6 +2318,7 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@ -2367,7 +2352,6 @@
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=10.13.0"
}
@ -2518,7 +2502,6 @@
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@ -2527,8 +2510,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
"license": "ISC",
"peer": true
"license": "ISC"
},
"node_modules/resize-observer-polyfill": {
"version": "1.5.1",
@ -2600,8 +2582,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
"license": "ISC",
"peer": true
"license": "ISC"
},
"node_modules/set-function-length": {
"version": "1.2.2",
@ -2664,7 +2645,6 @@
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"license": "MIT",
"peer": true,
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@ -2679,7 +2659,6 @@
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"license": "MIT",
"peer": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
@ -2749,6 +2728,7 @@
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.27.0",
"fdir": "^6.5.0",
@ -2823,6 +2803,7 @@
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.27.tgz",
"integrity": "sha512-aJ/UtoEyFySPBGarREmN4z6qNKpbEguYHMmXSiOGk69czc+zhs0NF6tEFrY8TZKAl8N/LYAkd4JHVd5E/AsSmw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@vue/compiler-dom": "3.5.27",
"@vue/compiler-sfc": "3.5.27",
@ -2914,15 +2895,13 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
"license": "ISC",
"peer": true
"license": "ISC"
},
"node_modules/wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"license": "MIT",
"peer": true,
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
@ -2936,15 +2915,13 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
"license": "ISC",
"peer": true
"license": "ISC"
},
"node_modules/yargs": {
"version": "15.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
"license": "MIT",
"peer": true,
"dependencies": {
"cliui": "^6.0.0",
"decamelize": "^1.2.0",
@ -2967,7 +2944,6 @@
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"license": "ISC",
"peer": true,
"dependencies": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"

@ -0,0 +1,351 @@
# Instalacion - Direccion de Admision 2026
## Requisitos previos
| Herramienta | Version minima | Uso |
|---|---|---|
| Laragon | - | PHP + Composer (servidor local) |
| PHP | 8.4+ | Backend Laravel (Thread Safe, VS17 x64) |
| Composer | 2.x | Dependencias PHP |
| Node.js | 18+ | Frontend Vue |
| npm | 9+ | Dependencias JS |
| Docker | 20+ | Solo para MySQL |
| Docker Compose | 2+ | Solo para MySQL |
### Descargar PHP 8.4 para Laragon
Si Laragon no trae PHP 8.4, descargarlo manualmente:
1. Descargar desde https://windows.php.net/download/ el archivo **VS17 x64 Thread Safe** (zip)
2. Extraer en `C:\laragon\bin\php\php-8.4.xx-Win32-vs17-x64\`
3. En Laragon: clic derecho > **PHP** > seleccionar la version 8.4
4. Instalar Visual C++ 2022 si no lo tienes: https://aka.ms/vs/17/release/vc_redist.x64.exe
5. Agregar al PATH: clic derecho en Laragon > **Tools** > **Path** > **Add Laragon to Path**
---
## Paso 1: Clonar el repositorio
```bash
git clone <url-del-repo> direccion_de_admision_2026
cd direccion_de_admision_2026
```
---
## Paso 2: Levantar MySQL con Docker
```bash
docker compose up -d
```
Esto levanta un contenedor MySQL 8.0 con:
- **Host:** 127.0.0.1
- **Puerto:** 3306
- **Usuario:** root
- **Password:** root
- **Base de datos:** admision_2026
Verificar que funcione:
```bash
docker exec admision_2026_db mysql -uroot -proot -e "SELECT 'MySQL OK'"
```
---
## Paso 3: Importar la base de datos
```bash
docker exec -i admision_2026_db mysql -uroot -proot admision_2026 < admision_2026_200.sql
```
Verificar tablas importadas:
```bash
docker exec admision_2026_db mysql -uroot -proot admision_2026 -e "SHOW TABLES;"
```
Debe mostrar 32 tablas (users, postulantes, areas, cursos, examenes, procesos_admision, etc.)
---
## Paso 4: Configurar el Backend (Laravel)
```bash
cd back
```
### 4.1 Instalar dependencias
```bash
composer install
```
### 4.2 Configurar variables de entorno
```bash
copy .env.example .env
```
Editar `back/.env` y cambiar la password de la BD:
```
DB_PASSWORD=root
```
### 4.3 Generar clave de aplicacion
```bash
php artisan key:generate
```
### 4.4 Crear enlace de storage (para imagenes)
```bash
php artisan storage:link
```
---
## Paso 5: Configurar el Frontend (Vue 3)
```bash
cd front
```
### 5.1 Instalar dependencias
```bash
npm install
```
### 5.2 Configurar variables de entorno
```bash
copy .env.example .env
```
El archivo debe contener:
```
VITE_API_URL=http://127.0.0.1:8000/api
```
---
## Levantar el proyecto
Necesitas **3 cosas corriendo** al mismo tiempo:
### 1. MySQL (Docker) - si no esta corriendo
```bash
docker compose up -d
```
### 2. Backend (Terminal 1)
```bash
cd back
php artisan serve
```
Estara disponible en: **http://localhost:8000**
### 3. Frontend (Terminal 2)
```bash
cd front
npm run dev
```
Estara disponible en: **http://localhost:5173**
---
## Stack tecnologico
| Capa | Tecnologia |
|---|---|
| Backend | Laravel 12 + PHP 8.4 |
| Frontend | Vue 3 + Vite 7 + Ant Design Vue + Pinia |
| Base de datos | MySQL 8.0 (Docker) |
| Autenticacion | Laravel Sanctum (Bearer tokens) |
| Permisos | Spatie Laravel Permission |
| QR | SimpleSoftwareIO QRCode |
---
## Notas importantes
- **No modificar** `composer.lock` a menos que todos los del equipo acuerden (requiere PHP 8.4+)
- El dump SQL (`admision_2026_200.sql`) ya incluye todas las tablas y datos necesarios, no se necesita `php artisan migrate`
- Si el puerto 3306 esta ocupado (por otro MySQL local), detener ese servicio primero o cambiar el puerto en `docker-compose.yml`
- Los archivos `.env` no se suben al repositorio (estan en `.gitignore`)
---
# Deploy en Produccion (Docker)
Todo el proyecto se dockeriza para correr en un VPS con Linux. Se usan 4 contenedores: **nginx** (reverse proxy), **backend** (PHP-FPM), **frontend** (Vue SPA + nginx) y **mysql**.
## Requisitos del servidor
| Herramienta | Version minima |
|---|---|
| Docker | 20+ |
| Docker Compose | 2+ |
| RAM | 2 GB minimo |
| Disco | 10 GB minimo |
---
## Paso 1: Clonar el repositorio en el servidor
```bash
git clone <url-del-repo> direccion_de_admision_2026
cd direccion_de_admision_2026
```
---
## Paso 2: Configurar variables de entorno
```bash
cp .env.prod.example .env.prod
```
Editar `.env.prod` y configurar **obligatoriamente**:
```
APP_KEY= # Generar con: php artisan key:generate --show (o tras el build)
APP_URL=https://tu-dominio.com
DB_PASSWORD=UNA_PASSWORD_SEGURA
SESSION_DOMAIN=tu-dominio.com
SANCTUM_STATEFUL_DOMAINS=tu-dominio.com
VITE_API_URL=/api
```
Configurar tambien el correo SMTP si se usa envio de emails.
---
## Paso 3: Importar la base de datos (primera vez)
Construir y levantar solo MySQL primero:
```bash
docker compose -f docker-compose.prod.yml up -d mysql
```
Esperar a que MySQL este listo y luego importar el dump:
```bash
docker exec -i admision_prod_db mysql -uroot -pTU_PASSWORD admision_2026 < admision_2026_200.sql
```
Verificar:
```bash
docker exec admision_prod_db mysql -uroot -pTU_PASSWORD admision_2026 -e "SHOW TABLES;"
```
---
## Paso 4: Construir y levantar todos los servicios
```bash
docker compose -f docker-compose.prod.yml build
docker compose -f docker-compose.prod.yml up -d
```
Verificar que los 4 contenedores esten corriendo:
```bash
docker compose -f docker-compose.prod.yml ps
```
---
## Paso 5: Generar APP_KEY (primera vez)
```bash
docker exec admision_prod_backend php artisan key:generate --show
```
Copiar la clave generada al archivo `.env.prod` en `APP_KEY=` y reiniciar:
```bash
docker compose -f docker-compose.prod.yml restart backend
```
---
## Paso 6: Crear enlace de storage
```bash
docker exec admision_prod_backend php artisan storage:link
```
---
## Verificar el deploy
- Abrir `http://tu-dominio.com` -> debe cargar el frontend Vue
- Abrir `http://tu-dominio.com/api/...` -> debe responder la API Laravel
---
## Comandos utiles
```bash
# Ver logs de todos los servicios
docker compose -f docker-compose.prod.yml logs -f
# Ver logs de un servicio especifico
docker compose -f docker-compose.prod.yml logs -f backend
# Reiniciar todo
docker compose -f docker-compose.prod.yml restart
# Detener todo
docker compose -f docker-compose.prod.yml down
# Reconstruir tras cambios de codigo
git pull
docker compose -f docker-compose.prod.yml build
docker compose -f docker-compose.prod.yml up -d
# Ejecutar comandos artisan
docker exec admision_prod_backend php artisan migrate --force
docker exec admision_prod_backend php artisan cache:clear
```
---
## Arquitectura de contenedores
```
Puerto 80
|
[nginx] (reverse proxy)
/ \
/api/* / \ /*
/ \
[backend] [frontend]
PHP-FPM nginx+SPA
:9000 :80
|
[mysql]
:3306
```
---
## Notas de produccion
- Los datos de MySQL se persisten en un volumen Docker (`mysql_data`)
- El storage de Laravel se persiste en un volumen Docker (`backend_storage`)
- Para HTTPS, configurar un reverse proxy externo (Nginx/Caddy en el host) o usar Cloudflare
- El archivo `.env.prod` **nunca** debe subirse al repositorio

@ -0,0 +1,53 @@
upstream php-fpm {
server backend:9000;
}
upstream frontend-server {
server frontend:80;
}
server {
listen 80;
server_name _;
client_max_body_size 25M;
# API requests -> PHP-FPM backend
location /api {
root /var/www/html/public;
fastcgi_pass php-fpm;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/html/public/index.php;
include fastcgi_params;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
# Sanctum CSRF cookie
location /sanctum {
root /var/www/html/public;
fastcgi_pass php-fpm;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/html/public/index.php;
include fastcgi_params;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
# Storage files (uploaded images, etc.)
location /storage {
alias /var/www/html/storage/app/public;
expires 7d;
add_header Cache-Control "public";
try_files $uri =404;
}
# Everything else -> Vue SPA frontend
location / {
proxy_pass http://frontend-server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Loading…
Cancel
Save