feat: add docker

main
parent 3e205ae76a
commit ede7d7442c

@ -10,8 +10,8 @@ APP_KEY=
APP_DEBUG=false
APP_URL=https://tu-dominio.com
# Puerto donde se expone la app (default: 80)
APP_PORT=80
# Puerto interno donde Docker escucha (el nginx del host hace proxy a este puerto)
APP_PORT=8080
# --- Base de datos ---
DB_CONNECTION=mysql
@ -52,6 +52,10 @@ LOG_CHANNEL=stack
LOG_STACK=single
LOG_LEVEL=error
# --- Docker / Registry ---
GITHUB_REPO=tu-usuario/tu-repo
IMAGE_TAG=latest
# --- Misc ---
BCRYPT_ROUNDS=12
FILESYSTEM_DISK=local

@ -0,0 +1,87 @@
name: Build & Deploy
on:
workflow_dispatch:
inputs:
deploy:
description: 'Desplegar en el VPS despues de construir'
required: true
default: true
type: boolean
env:
REGISTRY: ghcr.io
BACKEND_IMAGE: ghcr.io/${{ github.repository }}/backend
FRONTEND_IMAGE: ghcr.io/${{ github.repository }}/frontend
jobs:
build:
name: Build & Push Images
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build & push backend
uses: docker/build-push-action@v5
with:
context: ./back
push: true
tags: |
${{ env.BACKEND_IMAGE }}:latest
${{ env.BACKEND_IMAGE }}:${{ github.sha }}
- name: Build & push frontend
uses: docker/build-push-action@v5
with:
context: ./front
push: true
build-args: |
VITE_API_URL=/api
tags: |
${{ env.FRONTEND_IMAGE }}:latest
${{ env.FRONTEND_IMAGE }}:${{ github.sha }}
deploy:
name: Deploy to VPS
needs: build
if: ${{ inputs.deploy }}
runs-on: ubuntu-latest
steps:
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
port: ${{ secrets.VPS_PORT || 22 }}
script: |
cd ${{ secrets.VPS_PROJECT_PATH }}
# Login al registry
echo ${{ secrets.CR_PAT }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
# Descargar imagenes nuevas
docker pull ${{ env.BACKEND_IMAGE }}:latest
docker pull ${{ env.FRONTEND_IMAGE }}:latest
# Reiniciar servicios
docker compose --env-file .env.prod -f docker-compose.prod.yml up -d --force-recreate backend frontend nginx
# Ejecutar migraciones si hay pendientes
docker exec admision_prod_backend php artisan migrate --force
# Limpiar imagenes viejas
docker image prune -f

1
.gitignore vendored

@ -17,6 +17,7 @@ public_html/hot
storage/*.key
.env
.env.prod
Homestead.yaml
Homestead.json
/.vagrant

@ -1,6 +1,6 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:wEHrK9znRe1M/jdkBIM7b+TihyUtHygp8hKeqE57eB4=
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost:8000

@ -62,11 +62,14 @@ COPY --from=vendor /app/vendor ./vendor
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
# View cache (no depende de .env, se puede hacer en build)
RUN php artisan view:cache
# Entrypoint: cachear config y rutas con las variables reales de .env.prod
RUN printf '#!/bin/sh\nphp artisan config:cache\nphp artisan route:cache\nexec "$@"\n' > /usr/local/bin/entrypoint.sh \
&& chmod +x /usr/local/bin/entrypoint.sh
EXPOSE 9000
ENTRYPOINT ["entrypoint.sh"]
CMD ["php-fpm"]

@ -4,7 +4,7 @@ services:
container_name: admision_prod_nginx
restart: unless-stopped
ports:
- "${APP_PORT:-80}:80"
- "127.0.0.1:${APP_PORT:-8080}:80"
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
- backend_storage:/var/www/html/storage/app/public:ro
@ -15,9 +15,7 @@ services:
- admision_net
backend:
build:
context: ./back
dockerfile: Dockerfile
image: ghcr.io/${GITHUB_REPO:-usuario/repo}/backend:${IMAGE_TAG:-latest}
container_name: admision_prod_backend
restart: unless-stopped
env_file:
@ -31,11 +29,7 @@ services:
- admision_net
frontend:
build:
context: ./front
dockerfile: Dockerfile
args:
VITE_API_URL: ${VITE_API_URL:-/api}
image: ghcr.io/${GITHUB_REPO:-usuario/repo}/frontend:${IMAGE_TAG:-latest}
container_name: admision_prod_frontend
restart: unless-stopped
networks:

@ -0,0 +1,213 @@
# Instalacion Local (Desarrollo) - 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_estructura.sql
```
Verificar tablas importadas:
```bash
docker exec admision_2026_db mysql -uroot -proot admision_2026 -e "SHOW TABLES;"
```
Debe mostrar ~31 tablas (users, postulantes, areas, cursos, examenes, procesos_admision, etc.)
> **Nota:** El dump ya incluye los registros en la tabla `migrations`, por lo que no es necesario ejecutar `php artisan migrate`.
---
## 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 configurar:
```
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
```
### 4.5 Ejecutar seeders
```bash
php artisan db:seed --class=RoleSeeder
```
Esto crea los roles: `usuario`, `administrador`, `superadmin`.
> **Nota:** Solo ejecutar `php artisan migrate` si en el futuro se agregan nuevas migraciones al proyecto.
### 4.6 Asignar rol a tu usuario
Si ya tienes un usuario creado (por registro o por el dump SQL), asignarle un rol desde tinker:
```bash
php artisan tinker
```
```php
$user = \App\Models\User::where('email', 'tu@email.com')->first();
$user->assignRole('superadmin');
exit
```
> **Importante:** Sin un rol asignado, el login devuelve 200 pero no redirige al dashboard.
---
## 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_estructura.sql`) incluye la estructura de todas las tablas + registros en `migrations`. No es necesario ejecutar `php artisan migrate` a menos que se hayan agregado nuevas migraciones al proyecto.
- 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`)

@ -0,0 +1,362 @@
# Deploy en Produccion (Docker + CI/CD) - Direccion de Admision 2026
Todo el proyecto se dockeriza y se despliega mediante **GitHub Actions**. Las imagenes se construyen en GitHub y se suben a GitHub Container Registry (GHCR). El VPS solo descarga y ejecuta.
## Resumen rapido — ¿Que hago yo?
### Setup inicial (solo una vez, ~25 min)
| Paso | Que haces | Donde | Tiempo |
|---|---|---|---|
| 1 | Pegar 6 secrets en GitHub | Navegador (GitHub Settings) | 5 min |
| 2 | Conectar al VPS, clonar repo, crear `.env.prod` y editar valores | Terminal SSH | 10 min |
| 3 | Copiar/pegar 2 comandos para MySQL + importar dump | Terminal SSH | 2 min |
| 4 | Clic en "Run workflow" en GitHub Actions y esperar | Navegador (GitHub Actions) | 3 min |
| 5 | Copiar/pegar 2 comandos (storage link + seeders) | Terminal SSH | 1 min |
| 6 | Copiar/pegar server block de nginx + correr certbot | Terminal SSH | 5 min |
### Deploys del dia a dia
| Tu haces | Que pasa |
|---|---|
| `git push` a main | Nada (hasta que tu decidas) |
| Clic en **"Run workflow"** en GitHub Actions | Build + Push al registry + Deploy al VPS (~3 min) |
**No tocas terminal, no compilas, no haces SSH. Solo un clic desde el navegador.**
---
## Arquitectura de deploy
```
Tu PC GitHub GHCR VPS
| | | |
| git push | | |
| ──────────► [main branch] | |
| | | |
| [GitHub Actions] | |
| (trigger manual) | |
| | | |
| Build backend ──────► ghcr.io/.../backend |
| Build frontend ──────► ghcr.io/.../frontend |
| | | |
| SSH al VPS ────────────────────────────► |
| | | docker pull |
| | | docker up |
| | | App lista |
```
## Arquitectura de contenedores
```
Puerto 80/443
|
[nginx del host] ← SSL/Certbot
|
proxy_pass
|
127.0.0.1:8080
|
[nginx container] (reverse proxy)
/ \
/api/* / \ /*
/ \
[backend] [frontend]
PHP-FPM nginx+SPA
:9000 :80
|
[mysql]
:3306
```
---
## De donde sale cada credencial
Hay 3 lugares donde se ponen credenciales. Esta tabla explica cada una y como obtenerla:
### A. Archivo `.env.prod` (en el VPS)
| Credencial | Como la obtienes |
|---|---|
| `APP_KEY` | Ejecutas `php -r "echo 'base64:'.base64_encode(random_bytes(32)).PHP_EOL;"` en tu PC y copias el resultado |
| `DB_PASSWORD` | La inventas tu. Para generar una segura: `openssl rand -base64 32` |
| `APP_URL` | Tu dominio (ej: `https://admision.tu-universidad.edu.pe`) |
| `SESSION_DOMAIN` | Tu dominio sin https (ej: `admision.tu-universidad.edu.pe`) |
| `SANCTUM_STATEFUL_DOMAINS` | Igual que SESSION_DOMAIN |
| `GITHUB_REPO` | Tu usuario/repo de GitHub (ej: `juanperez/direccion_de_admision_2026`) |
| `MAIL_PASSWORD` | Te lo da tu proveedor SMTP (Gmail, Mailgun, etc.) |
### B. GitHub Secrets (en el navegador)
| Secret | Como lo obtienes |
|---|---|
| `VPS_HOST` | La IP de tu droplet, la ves en el panel de DigitalOcean |
| `VPS_USER` | Normalmente `root` |
| `VPS_SSH_KEY` | El contenido de la clave privada SSH (ver paso 1 abajo) |
| `VPS_PORT` | Normalmente `22` |
| `VPS_PROJECT_PATH` | La ruta donde clonas el repo (ej: `/root/direccion_de_admision_2026`) |
| `CR_PAT` | Token que generas en GitHub > Settings > Developer settings > Tokens |
### C. Se generan automaticamente (no los tocas)
| Credencial | Descripcion |
|---|---|
| `GITHUB_TOKEN` | GitHub Actions lo crea solo en cada ejecucion |
| `MYSQL_ROOT_PASSWORD` | Docker lo lee de `DB_PASSWORD` del `.env.prod` |
---
## Setup inicial (solo la primera vez)
### 1. Crear clave SSH en el VPS y configurar secrets en GitHub
GitHub Actions necesita conectarse al VPS por SSH. Si actualmente entras con contraseña, necesitas crear una clave SSH.
**En el VPS** (conectate con tu contraseña como siempre):
```bash
# Generar clave SSH para GitHub Actions
ssh-keygen -t ed25519 -C "github-actions-deploy" -f ~/.ssh/github_actions -N ""
# Agregar la clave publica a las claves autorizadas
cat ~/.ssh/github_actions.pub >> ~/.ssh/authorized_keys
# Mostrar la clave PRIVADA (la que copias a GitHub)
cat ~/.ssh/github_actions
```
> Copia TODO el contenido que muestra el ultimo comando (desde `-----BEGIN` hasta `-----END`). Esa es la clave que pegas en GitHub como secret `VPS_SSH_KEY`.
**En GitHub** (navegador):
1. Ir a tu repo > **Settings** > **Secrets and variables** > **Actions**
2. Clic en **"New repository secret"** y agregar uno por uno:
| Secret | Que pegar |
|---|---|
| `VPS_HOST` | La IP de tu droplet (ej: `164.92.xxx.xxx`) |
| `VPS_USER` | `root` |
| `VPS_SSH_KEY` | El contenido completo de `~/.ssh/github_actions` (clave privada) |
| `VPS_PORT` | `22` |
| `VPS_PROJECT_PATH` | `/root/direccion_de_admision_2026` |
| `CR_PAT` | El token generado en el siguiente paso |
### Generar el CR_PAT (Personal Access Token)
1. Ir a GitHub > tu foto (esquina superior derecha) > **Settings**
2. Ir a **Developer settings** > **Personal access tokens** > **Tokens (classic)**
3. Clic en **"Generate new token (classic)"**
4. Nombre: `deploy-admision` | Expiracion: 90 dias o sin expiracion
5. Marcar permisos: `read:packages`, `write:packages`
6. Clic en **"Generate token"**
7. Copiar el token y guardarlo como secret `CR_PAT` en el repo
---
### 2. Preparar el VPS
**En el VPS** (por SSH):
```bash
# Instalar Docker si no lo tienes
curl -fsSL https://get.docker.com | sh
# Clonar el repo
git clone <url-del-repo> direccion_de_admision_2026
cd direccion_de_admision_2026
# Configurar variables de entorno
cp .env.prod.example .env.prod
```
Editar `.env.prod`:
```bash
nano .env.prod
```
Cambiar estos valores **obligatoriamente**:
```
APP_KEY= # Pegar la clave generada (ver abajo)
APP_URL=https://tu-dominio.com
DB_PASSWORD= # Pegar la password generada (ver abajo)
SESSION_DOMAIN=tu-dominio.com
SANCTUM_STATEFUL_DOMAINS=tu-dominio.com
GITHUB_REPO=tu-usuario/tu-repo
```
Para generar APP_KEY (en tu PC local si no hay PHP en el VPS):
```bash
php -r "echo 'base64:'.base64_encode(random_bytes(32)).PHP_EOL;"
```
Para generar DB_PASSWORD:
```bash
openssl rand -base64 32
```
Asegurar permisos del archivo:
```bash
chmod 600 .env.prod
```
### Credenciales de la base de datos
La base de datos MySQL se crea automaticamente con estos valores (definidos en `.env.prod`):
| Variable | Valor | Descripcion |
|---|---|---|
| `DB_HOST` | `mysql` | Nombre del contenedor Docker (no cambiar) |
| `DB_PORT` | `3306` | Puerto interno de MySQL (no cambiar) |
| `DB_DATABASE` | `admision_2026` | Nombre de la base de datos |
| `DB_USERNAME` | `root` | Usuario de MySQL |
| `DB_PASSWORD` | Lo que pongas en `.env.prod` | La misma password para todo: MySQL root y conexion Laravel |
> **Importante:** La password que pongas en `DB_PASSWORD` es la que usa Docker para crear el usuario root de MySQL **y** la que usa Laravel para conectarse. Debe ser la misma en ambos casos porque es una sola variable. Esta misma password es la que usas en los comandos de importacion donde dice `TU_PASSWORD`.
---
### 3. Levantar MySQL e importar la base de datos
```bash
docker compose --env-file .env.prod -f docker-compose.prod.yml up -d mysql
```
Esperar ~10 segundos y luego importar:
```bash
docker exec -i admision_prod_db mysql -uroot -pTU_PASSWORD admision_2026 < admision_2026_estructura.sql
```
Verificar:
```bash
docker exec admision_prod_db mysql -uroot -pTU_PASSWORD admision_2026 -e "SHOW TABLES;"
```
---
### 4. Primer deploy
Ir a GitHub > pestaña **Actions** > seleccionar **"Build & Deploy"** > clic en **"Run workflow"** > marcar **"Desplegar en el VPS"** > confirmar.
GitHub Actions construira las imagenes, las subira al registry y las desplegara en el VPS.
---
### 5. Post-deploy (solo la primera vez)
```bash
# Crear enlace de storage
docker exec admision_prod_backend php artisan storage:link
# Ejecutar seeders
docker exec admision_prod_backend php artisan db:seed --class=RoleSeeder --force
```
---
### 6. Configurar nginx del host como proxy
Crear o editar el server block:
```bash
sudo nano /etc/nginx/sites-available/tu-dominio.com
```
```nginx
server {
listen 80;
server_name tu-dominio.com;
location / {
proxy_pass http://127.0.0.1:8080;
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;
client_max_body_size 25M;
}
}
```
```bash
sudo ln -sf /etc/nginx/sites-available/tu-dominio.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
```
Para HTTPS:
```bash
sudo certbot --nginx -d tu-dominio.com
```
---
## Deploys posteriores
Solo necesitas ir a GitHub > **Actions** > **Run workflow**. Eso es todo.
O si prefieres activar deploy automatico en cada push a main, edita `.github/workflows/deploy.yml` y agrega:
```yaml
on:
workflow_dispatch:
push:
branches: [main]
```
---
## Comandos utiles en el VPS
```bash
# Ver logs
docker compose --env-file .env.prod -f docker-compose.prod.yml logs -f
# Ver logs de un servicio
docker compose --env-file .env.prod -f docker-compose.prod.yml logs -f backend
# Reiniciar todo
docker compose --env-file .env.prod -f docker-compose.prod.yml restart
# Detener todo
docker compose --env-file .env.prod -f docker-compose.prod.yml down
# Ejecutar comandos artisan
docker exec admision_prod_backend php artisan migrate --force
docker exec admision_prod_backend php artisan cache:clear
# Ver que imagenes estan corriendo
docker compose --env-file .env.prod -f docker-compose.prod.yml ps
```
---
## Rollback (volver a una version anterior)
Cada imagen se etiqueta con el SHA del commit. Para volver a una version anterior:
```bash
# En .env.prod cambiar IMAGE_TAG al SHA del commit deseado
IMAGE_TAG=abc123def456
# Recrear contenedores
docker compose --env-file .env.prod -f docker-compose.prod.yml up -d --force-recreate backend frontend nginx
```
---
## 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`)
- El archivo `.env.prod` **nunca** debe subirse al repositorio (esta en `.gitignore`)
- Siempre usar `--env-file .env.prod` en los comandos de Docker Compose
- Las imagenes en GHCR son **privadas** (solo tu cuenta las ve)
- Para HTTPS, usar Certbot en el nginx del host
Loading…
Cancel
Save