fa4ddbe164
POST /api/updater/apply dispara scripts/apply-update.sh (backup + git pull + migraciones + build + restart) en un proceso separado. Apagado por defecto: solo se activa si el servidor define UPDATE_APP_DIR/UPDATE_BACKEND_SERVICE/UPDATE_FRONTEND_SERVICE en su backend/.env. Sin esa config, el panel se comporta igual que antes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
310 lines
10 KiB
Markdown
310 lines
10 KiB
Markdown
# Deploy Web - project.linkedcorp.net
|
|
|
|
Arquetipo B (dos servicios separados). Primer deploy realizado el 2026-09-06.
|
|
|
|
---
|
|
|
|
## Instalación automatizada en un servidor NUEVO
|
|
|
|
`scripts/install.sh` automatiza todo lo de esta sección para un servidor Debian/Ubuntu nuevo:
|
|
verifica requisitos (Node.js, npm, cliente mysql, systemd, espacio, puertos libres), pregunta
|
|
dominio/puertos y datos de base de datos (MariaDB local — se instala sola si hace falta — o un
|
|
servidor existente), crea la base + usuario, genera `backend/.env` con secretos aleatorios,
|
|
instala dependencias, corre migraciones + catálogos, compila el frontend y crea/arranca los
|
|
servicios systemd.
|
|
|
|
```bash
|
|
# En el servidor nuevo, ya con el repo clonado en /opt/apps/project.linkedcorp.net/source:
|
|
cd /opt/apps/project.linkedcorp.net/source
|
|
sudo scripts/install.sh
|
|
```
|
|
|
|
No configura el reverse proxy/TLS (paso 11 más abajo, específico de cada proveedor) ni crea
|
|
ningún usuario: el primer registro público en `/registro` se vuelve superadministrador solo — no
|
|
hace falta `create-admin.js` en instalaciones nuevas.
|
|
|
|
Las secciones de abajo son la referencia manual (lo que hace el script paso a paso) — útiles para
|
|
entender el porqué de cada cosa o para resolver un problema puntual sin volver a correr todo.
|
|
|
|
**Requisito para el panel "Actualizaciones"** (Configuración → superadmin): el servidor instalado
|
|
necesita poder llegar por HTTPS a `gitea.linkedcorp.net` de forma anónima (repo público + vista
|
|
anónima habilitada en Gitea) para poder consultar el `CHANGELOG.md` y las migraciones nuevas. No
|
|
requiere ningún token ni credencial. Si no hay conectividad a Gitea, ese panel simplemente muestra
|
|
el error al buscar actualizaciones — el resto de la app funciona igual.
|
|
|
|
---
|
|
|
|
## Primer despliegue en producción (ya ejecutado — referencia)
|
|
|
|
### 1. Entrar al servidor
|
|
|
|
```bash
|
|
ssh deploy@192.168.222.3 -p 2203
|
|
```
|
|
|
|
### 2. Crear estructura de directorios
|
|
|
|
```bash
|
|
sudo mkdir -p /opt/apps/project.linkedcorp.net/{source,backend,frontend,storage,logs}
|
|
sudo chown -R deploy:deploy /opt/apps/project.linkedcorp.net
|
|
```
|
|
|
|
### 3. Clonar el repositorio
|
|
|
|
```bash
|
|
cd /opt/apps/project.linkedcorp.net/source
|
|
git clone http://[gitea_user]:[token]@gitea.linkedcorp.net/mgonzalez/project.linkedcorp.net.git .
|
|
git remote set-url origin http://gitea.linkedcorp.net/mgonzalez/project.linkedcorp.net.git # sin token en claro
|
|
```
|
|
|
|
### 4. Crear el `.env` de producción
|
|
|
|
```bash
|
|
nano /opt/apps/project.linkedcorp.net/backend/.env
|
|
```
|
|
|
|
Variables reales (valores sensibles solo en el archivo del servidor, nunca en git):
|
|
|
|
```env
|
|
PORT=4019
|
|
NODE_ENV=production
|
|
DB_HOST=192.168.222.32
|
|
DB_PORT=3306
|
|
DB_NAME=project_linkedcorp
|
|
DB_USER=project_linkedcorp
|
|
DB_PASSWORD=[generada por crear-db-prod.sh]
|
|
JWT_SECRET=[openssl rand -hex 32]
|
|
JWT_EXPIRES=8h
|
|
CORS_ORIGIN=https://project.linkedcorp.net
|
|
STORAGE_ROOT=/opt/apps/project.linkedcorp.net/storage
|
|
```
|
|
|
|
**Nunca copiar el `.env` de desarrollo.**
|
|
|
|
### 5. Instalar dependencias del backend
|
|
|
|
```bash
|
|
cd /opt/apps/project.linkedcorp.net/source/backend
|
|
npm install --omit=dev
|
|
cp -r node_modules /opt/apps/project.linkedcorp.net/backend/
|
|
```
|
|
|
|
### 6. Compilar el frontend y publicar el runtime
|
|
|
|
El frontend es un proceso separado (`serve` sirviendo el build). Runtime propio en `frontend/`
|
|
con un `package.json` minimo (solo depende de `serve`) — evita copiar todo `node_modules` de Vite:
|
|
|
|
```bash
|
|
cd /opt/apps/project.linkedcorp.net/source/frontend
|
|
npm install
|
|
npm run build
|
|
cp -r dist /opt/apps/project.linkedcorp.net/frontend/dist
|
|
|
|
cat > /opt/apps/project.linkedcorp.net/frontend/package.json <<'EOF'
|
|
{ "name": "project-linkedcorp-frontend-runtime", "private": true, "version": "1.0.0",
|
|
"dependencies": { "serve": "^14.2.6" } }
|
|
EOF
|
|
cd /opt/apps/project.linkedcorp.net/frontend && npm install --omit=dev
|
|
```
|
|
|
|
> En un redeploy solo hace falta reemplazar `frontend/dist` (borrar y volver a copiar) — el
|
|
> `node_modules` del runtime frontend no cambia salvo que cambie la version de `serve`.
|
|
|
|
### 7. Ejecutar migración de base de datos
|
|
|
|
```bash
|
|
cd /opt/apps/project.linkedcorp.net/source/backend
|
|
set -a && source /opt/apps/project.linkedcorp.net/backend/.env && set +a
|
|
node src/scripts/migrate-db.js
|
|
```
|
|
|
|
### 8. Inicializar datos
|
|
|
|
```bash
|
|
# Catalogos base (roles, prioridades, estados, tipos de proyecto) — sin datos de ejemplo en produccion:
|
|
node src/scripts/seed-db.js
|
|
|
|
# Usuario Administrador inicial (clave fuerte generada, se muestra UNA sola vez):
|
|
node src/scripts/create-admin.js "Nombre Apellido" correo@linkedcorp.net
|
|
```
|
|
|
|
### 9. Crear el servicio systemd del backend
|
|
|
|
```ini
|
|
# /etc/systemd/system/project-linkedcorp-backend.service
|
|
[Unit]
|
|
Description=project.linkedcorp.net backend
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=deploy
|
|
WorkingDirectory=/opt/apps/project.linkedcorp.net/source/backend
|
|
ExecStart=/usr/bin/node src/server.js
|
|
EnvironmentFile=/opt/apps/project.linkedcorp.net/backend/.env
|
|
Restart=on-failure
|
|
RestartSec=5
|
|
StandardOutput=append:/opt/apps/project.linkedcorp.net/logs/backend.log
|
|
StandardError=append:/opt/apps/project.linkedcorp.net/logs/backend-error.log
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
```
|
|
|
|
### 10. Crear el servicio systemd del frontend
|
|
|
|
```ini
|
|
# /etc/systemd/system/project-linkedcorp-frontend.service
|
|
[Unit]
|
|
Description=project.linkedcorp.net frontend
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=deploy
|
|
WorkingDirectory=/opt/apps/project.linkedcorp.net/frontend
|
|
ExecStart=/usr/bin/node node_modules/.bin/serve -s dist -l 5196
|
|
Restart=on-failure
|
|
RestartSec=5
|
|
StandardOutput=append:/opt/apps/project.linkedcorp.net/logs/frontend.log
|
|
StandardError=append:/opt/apps/project.linkedcorp.net/logs/frontend-error.log
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
```
|
|
|
|
```bash
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable project-linkedcorp-backend project-linkedcorp-frontend
|
|
sudo systemctl start project-linkedcorp-backend project-linkedcorp-frontend
|
|
sudo systemctl status project-linkedcorp-backend project-linkedcorp-frontend
|
|
```
|
|
|
|
### 11. Configurar Nginx Proxy Manager
|
|
|
|
Proxy Host:
|
|
|
|
| Campo | Valor |
|
|
|-------|-------|
|
|
| Domain | `project.linkedcorp.net` |
|
|
| Forward Hostname/IP | `192.168.222.3` |
|
|
| Forward Port | `5196` (frontend) |
|
|
| Scheme | `http` |
|
|
| SSL | Let's Encrypt, Force SSL |
|
|
|
|
Custom Location:
|
|
|
|
| Campo | Valor |
|
|
|-------|-------|
|
|
| Location | `/api` |
|
|
| Forward Hostname/IP | `192.168.222.3` |
|
|
| Forward Port | `4019` (backend) |
|
|
| Scheme | `http` |
|
|
|
|
### 12. Verificar
|
|
|
|
```bash
|
|
curl http://127.0.0.1:4019/health
|
|
curl -o /dev/null -w "%{http_code}\n" http://127.0.0.1:5196/
|
|
curl https://project.linkedcorp.net/api/auth/login -X POST -H 'Content-Type: application/json' -d '{...}'
|
|
```
|
|
|
|
### 12.5 Habilitar el botón "Actualizar" (auto-actualización, opcional)
|
|
|
|
Por defecto el panel Configuración → Actualizaciones solo informa. Para que el botón
|
|
"Actualizar" aplique el cambio solo (backup + git pull + migraciones + build + restart), hace
|
|
falta en el servidor:
|
|
|
|
```bash
|
|
# 1. sudo (si no esta instalado) y permiso acotado para el usuario deploy — SOLO reiniciar
|
|
# estos dos servicios puntuales, nada mas.
|
|
sudo apt-get install -y sudo # si el servidor no lo trae (ej. instalaciones minimas de Debian)
|
|
echo 'deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart <backend-service>, /usr/bin/systemctl restart <frontend-service>' \
|
|
| sudo tee /etc/sudoers.d/deploy-update-<dominio>
|
|
sudo visudo -c # valida sintaxis antes de confiar en el archivo
|
|
|
|
# 2. copiar el script fuera de source/ (donde vive el runtime, no el checkout de git)
|
|
mkdir -p /opt/apps/<dominio>/scripts
|
|
cp /opt/apps/<dominio>/source/scripts/apply-update.sh /opt/apps/<dominio>/scripts/
|
|
chmod +x /opt/apps/<dominio>/scripts/apply-update.sh
|
|
|
|
# 3. agregar al backend/.env (el de runtime, NO el de source/backend)
|
|
cat >> /opt/apps/<dominio>/backend/.env <<EOF
|
|
UPDATE_APP_DIR=/opt/apps/<dominio>
|
|
UPDATE_BACKEND_SERVICE=<backend-service>
|
|
UPDATE_FRONTEND_SERVICE=<frontend-service>
|
|
EOF
|
|
|
|
sudo systemctl restart <backend-service>
|
|
```
|
|
|
|
Importante: el script (`scripts/apply-update.sh`) hace `git reset --hard` sobre `source/`
|
|
mientras corre — por eso tiene que ejecutarse SIEMPRE desde la copia en
|
|
`$UPDATE_APP_DIR/scripts/`, nunca desde dentro de `source/scripts/` directamente. Cada vez que
|
|
el script cambie en un release, hay que repetir el paso 2 (copiarlo de nuevo) en un deploy
|
|
manual — el propio update automático no se autoactualiza a sí mismo.
|
|
|
|
### 13. Limpiar archivos `.md` del source
|
|
|
|
```bash
|
|
find /opt/apps/project.linkedcorp.net/source -type f -iname '*.md' -delete
|
|
```
|
|
|
|
> Pendiente al 2026-09-06: no ejecutado en el primer deploy (bloqueado por el entorno del agente
|
|
> que hizo el despliegue). Ejecutar manualmente o en el proximo deploy.
|
|
|
|
---
|
|
|
|
## Actualizaciones (deploys posteriores)
|
|
|
|
1. Cambios en desarrollo, `npm run build` local OK.
|
|
2. Si corresponde, subir version (`VERSIONADO.md`): actualizar `VERSION` y `CHANGELOG.md`.
|
|
3. `git add . && git commit -m "..." && git push origin main`.
|
|
4. `ssh deploy@192.168.222.3 -p 2203`
|
|
5. `cd /opt/apps/project.linkedcorp.net/source && git fetch origin main && git reset --hard origin/main`
|
|
6. Si cambio el backend:
|
|
```bash
|
|
cd backend && npm install --omit=dev
|
|
cp -r node_modules /opt/apps/project.linkedcorp.net/backend/
|
|
```
|
|
7. Si cambio el frontend:
|
|
```bash
|
|
cd ../frontend && npm install && npm run build
|
|
rm -rf /opt/apps/project.linkedcorp.net/frontend/dist
|
|
cp -r dist /opt/apps/project.linkedcorp.net/frontend/dist
|
|
```
|
|
8. Si cambio el schema de BD:
|
|
```bash
|
|
cd ../backend
|
|
set -a && source /opt/apps/project.linkedcorp.net/backend/.env && set +a
|
|
node src/scripts/migrate-db.js
|
|
```
|
|
9. Confirmar `DB_HOST=192.168.222.32` en el `.env` de produccion antes de reiniciar.
|
|
10. Reiniciar servicios:
|
|
```bash
|
|
sudo systemctl restart project-linkedcorp-backend
|
|
sudo systemctl restart project-linkedcorp-frontend
|
|
```
|
|
11. **Confirmar cambio de PID** (si cambio el backend):
|
|
```bash
|
|
ps -ef | grep 'node src/server.js' | grep -v grep
|
|
```
|
|
12. Verificar: `curl http://127.0.0.1:4019/health`.
|
|
13. Limpiar `.md`: `find /opt/apps/project.linkedcorp.net/source -type f -iname '*.md' -delete`.
|
|
|
|
---
|
|
|
|
## No tocar durante deploy
|
|
|
|
- `/opt/apps/project.linkedcorp.net/backend/.env`
|
|
- `/opt/apps/project.linkedcorp.net/storage/`
|
|
- `/opt/apps/project.linkedcorp.net/logs/`
|
|
|
|
## Notas
|
|
|
|
- `.env` de produccion nunca se versiona ni se copia desde desarrollo.
|
|
- BD de produccion siempre `192.168.222.32`; desarrollo siempre `192.168.222.20`. Confirmar antes
|
|
de cada deploy.
|
|
- `create-admin.js` es idempotente por email (`ON DUPLICATE KEY UPDATE`) — reutilizable para
|
|
resetear la clave del admin si hace falta (genera una nueva clave fuerte cada vez que se corre).
|