Compare commits
17 Commits
aa6d4e84c0
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 5342fb489d | |||
| c42814f963 | |||
| 0d1de7f7e2 | |||
| b34433a71e | |||
| 492d844523 | |||
| 8237e38164 | |||
| e04be61952 | |||
| 1b7e4f36e9 | |||
| d8cc6e9613 | |||
| aa04270550 | |||
| 2b47faf66a | |||
| 97db600b1f | |||
| f9bf5f4824 | |||
| 1db2f11c19 | |||
| 511b370a2e | |||
| 3d3ef3f002 | |||
| abaf43b8d6 |
@@ -1,6 +1,9 @@
|
|||||||
# Ignorar los directorios de dependencias
|
# Ignorar los directorios de dependencias
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
|
# Ignorar los volumenes respaldados
|
||||||
|
docker-volumes*
|
||||||
|
|
||||||
# Ignorar las carpetas de bases de datos
|
# Ignorar las carpetas de bases de datos
|
||||||
.db/
|
.db/
|
||||||
|
|
||||||
|
|||||||
@@ -1,116 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="es">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Cargar Comanda</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h2>Formulario de Carga de Comanda</h2>
|
|
||||||
|
|
||||||
<form id="formComanda">
|
|
||||||
<label for="mesaSelect">Mesa:</label>
|
|
||||||
<select id="mesaSelect" name="mesa_id" required></select>
|
|
||||||
<br><br>
|
|
||||||
|
|
||||||
<label for="productoSelect">Producto:</label>
|
|
||||||
<select id="productosSelect"></select>
|
|
||||||
|
|
||||||
<label for="cantidadInput">Cantidad:</label>
|
|
||||||
<input type="number" id="cantidadInput" min="1" value="1">
|
|
||||||
<button type="button" onclick="agregarProducto()">Agregar</button>
|
|
||||||
|
|
||||||
<ul id="listaProductos"></ul>
|
|
||||||
|
|
||||||
<input type="hidden" name="productos" id="productosJSON">
|
|
||||||
|
|
||||||
<br>
|
|
||||||
<button type="submit">Guardar Comanda</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// Cargar categorías y mesas (productos se maneja distinto por el precio)
|
|
||||||
async function cargarSelect(endpoint, selectId, mostrar) {
|
|
||||||
const res = await fetch(endpoint);
|
|
||||||
const data = await res.json();
|
|
||||||
const select = document.getElementById(selectId);
|
|
||||||
data.forEach(item => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = item.id;
|
|
||||||
option.textContent = mostrar(item);
|
|
||||||
select.appendChild(option);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function cargarProductosConPrecio() {
|
|
||||||
const res = await fetch('/api/obtenerProductos');
|
|
||||||
const productos = await res.json();
|
|
||||||
const select = document.getElementById('productosSelect');
|
|
||||||
productos.forEach(prod => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = prod.id;
|
|
||||||
option.textContent = `${prod.nombre} ($${prod.precio})`;
|
|
||||||
option.setAttribute('data-precio', prod.precio);
|
|
||||||
select.appendChild(option);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const listaProductos = [];
|
|
||||||
function agregarProducto() {
|
|
||||||
const select = document.getElementById('productosSelect');
|
|
||||||
const cantidad = parseInt(document.getElementById('cantidadInput').value);
|
|
||||||
const productoId = select.value;
|
|
||||||
const nombre = select.options[select.selectedIndex].textContent;
|
|
||||||
const precioUnitario = parseFloat(select.options[select.selectedIndex].dataset.precio);
|
|
||||||
|
|
||||||
if (!productoId || isNaN(cantidad) || cantidad <= 0) {
|
|
||||||
alert("Producto o cantidad inválida");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
listaProductos.push({
|
|
||||||
producto_id: productoId,
|
|
||||||
cantidad,
|
|
||||||
precio_unitario: precioUnitario
|
|
||||||
});
|
|
||||||
|
|
||||||
// Mostrar en lista visual
|
|
||||||
const li = document.createElement('li');
|
|
||||||
li.textContent = `${nombre} x${cantidad} - $${(precioUnitario * cantidad).toFixed(2)}`;
|
|
||||||
document.getElementById('listaProductos').appendChild(li);
|
|
||||||
|
|
||||||
// Actualizar JSON oculto
|
|
||||||
document.getElementById('productosJSON').value = JSON.stringify(listaProductos);
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('formComanda').addEventListener('submit', async function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
const mesaId = document.getElementById('mesaSelect').value;
|
|
||||||
const productos = listaProductos;
|
|
||||||
|
|
||||||
if (!mesaId || productos.length === 0) {
|
|
||||||
alert('Selecciona una mesa y al menos un producto');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await fetch('/api/cargarComandas', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({
|
|
||||||
mesa_id: mesaId,
|
|
||||||
productos: productos
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const resultado = await res.json();
|
|
||||||
alert(resultado.mensaje || 'Comanda cargada correctamente');
|
|
||||||
location.reload();
|
|
||||||
});
|
|
||||||
|
|
||||||
window.onload = () => {
|
|
||||||
cargarSelect('/api/obtenerMesas', 'mesaSelect', mesa => `Mesa ${mesa.numero}`);
|
|
||||||
cargarProductosConPrecio();
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,195 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="es">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<title>Crear comanda</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Crear comanda</h1>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<label>
|
|
||||||
Mesa (ID o número):
|
|
||||||
<input type="number" id="mesaId" required />
|
|
||||||
</label>
|
|
||||||
<br />
|
|
||||||
<label>
|
|
||||||
Mozo (ID de usuario):
|
|
||||||
<input type="text" id="mozoId" required />
|
|
||||||
</label>
|
|
||||||
<br />
|
|
||||||
<label>
|
|
||||||
Notas:
|
|
||||||
<input type="text" id="notas" placeholder="Sin observaciones" />
|
|
||||||
</label>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<h2>Agregar productos</h2>
|
|
||||||
<label>
|
|
||||||
Producto:
|
|
||||||
<select id="productoSelect"></select>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
Cantidad:
|
|
||||||
<input type="number" id="cantidadInput" value="1" min="1" />
|
|
||||||
</label>
|
|
||||||
<button id="agregarBtn">Agregar</button>
|
|
||||||
|
|
||||||
<h3>Items de la comanda</h3>
|
|
||||||
<ul id="itemsList"></ul>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<button id="enviarBtn">Enviar comanda</button>
|
|
||||||
<pre id="salida"></pre>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// === CONFIGURA AQUÍ SI ES NECESARIO ===
|
|
||||||
const API_BASE = "http://localhost:3000"; // Cambia al puerto/host de tu servidor Node
|
|
||||||
const PRODUCTOS_PATH = "/productos"; // GET
|
|
||||||
const COMANDAS_PATH = "/comandas"; // POST
|
|
||||||
|
|
||||||
// === ESTADO EN MEMORIA ===
|
|
||||||
const productosCache = new Map(); // id -> {id, nombre, ...}
|
|
||||||
const items = []; // {producto_id, cantidad}
|
|
||||||
|
|
||||||
// === ELEMENTOS DEL DOM ===
|
|
||||||
const productoSelect = document.getElementById("productoSelect");
|
|
||||||
const cantidadInput = document.getElementById("cantidadInput");
|
|
||||||
const agregarBtn = document.getElementById("agregarBtn");
|
|
||||||
const itemsList = document.getElementById("itemsList");
|
|
||||||
const enviarBtn = document.getElementById("enviarBtn");
|
|
||||||
const mesaIdInput = document.getElementById("mesaId");
|
|
||||||
const mozoIdInput = document.getElementById("mozoId");
|
|
||||||
const notasInput = document.getElementById("notas");
|
|
||||||
const salida = document.getElementById("salida");
|
|
||||||
|
|
||||||
// === UTILIDADES ===
|
|
||||||
function renderItems() {
|
|
||||||
itemsList.innerHTML = "";
|
|
||||||
items.forEach((it, idx) => {
|
|
||||||
const li = document.createElement("li");
|
|
||||||
const prod = productosCache.get(it.producto_id);
|
|
||||||
const nombre = prod ? (prod.nombre || prod.name || `Producto ${it.producto_id}`) : `ID ${it.producto_id}`;
|
|
||||||
li.textContent = `${nombre} × ${it.cantidad}`;
|
|
||||||
const btn = document.createElement("button");
|
|
||||||
btn.textContent = "Quitar";
|
|
||||||
btn.onclick = () => {
|
|
||||||
items.splice(idx, 1);
|
|
||||||
renderItems();
|
|
||||||
};
|
|
||||||
li.appendChild(document.createTextNode(" "));
|
|
||||||
li.appendChild(btn);
|
|
||||||
itemsList.appendChild(li);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function mostrarMensaje(obj) {
|
|
||||||
try {
|
|
||||||
salida.textContent = typeof obj === "string" ? obj : JSON.stringify(obj, null, 2);
|
|
||||||
} catch {
|
|
||||||
salida.textContent = String(obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function validarEnteroPositivo(valor) {
|
|
||||||
const n = Number(valor);
|
|
||||||
return Number.isInteger(n) && n > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// === LOGICA ===
|
|
||||||
async function cargarProductos() {
|
|
||||||
try {
|
|
||||||
const res = await fetch(API_BASE + PRODUCTOS_PATH);
|
|
||||||
if (!res.ok) throw new Error("No se pudieron obtener los productos");
|
|
||||||
const data = await res.json();
|
|
||||||
// Espera un array de productos con al menos {id, nombre}
|
|
||||||
productoSelect.innerHTML = "";
|
|
||||||
data.forEach(p => {
|
|
||||||
productosCache.set(p.id, p);
|
|
||||||
const opt = document.createElement("option");
|
|
||||||
opt.value = p.id;
|
|
||||||
opt.textContent = p.nombre || p.name || `Producto ${p.id}`;
|
|
||||||
productoSelect.appendChild(opt);
|
|
||||||
});
|
|
||||||
if (data.length === 0) {
|
|
||||||
mostrarMensaje("No hay productos disponibles.");
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
mostrarMensaje("Error cargando productos: " + e.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
agregarBtn.addEventListener("click", () => {
|
|
||||||
const prodId = Number(productoSelect.value);
|
|
||||||
const cant = Number(cantidadInput.value);
|
|
||||||
if (!validarEnteroPositivo(prodId)) {
|
|
||||||
return mostrarMensaje("Selecciona un producto válido.");
|
|
||||||
}
|
|
||||||
if (!validarEnteroPositivo(cant)) {
|
|
||||||
return mostrarMensaje("La cantidad debe ser un entero positivo.");
|
|
||||||
}
|
|
||||||
// Si ya existe el producto en la lista, acumula cantidad
|
|
||||||
const existente = items.find(i => i.producto_id === prodId);
|
|
||||||
if (existente) {
|
|
||||||
existente.cantidad += cant;
|
|
||||||
} else {
|
|
||||||
items.push({ producto_id: prodId, cantidad: cant });
|
|
||||||
}
|
|
||||||
renderItems();
|
|
||||||
cantidadInput.value = 1;
|
|
||||||
mostrarMensaje("Producto agregado.");
|
|
||||||
});
|
|
||||||
|
|
||||||
enviarBtn.addEventListener("click", async () => {
|
|
||||||
const mesa_id = Number(mesaIdInput.value);
|
|
||||||
const mozo_id = mozoIdInput.value.trim();
|
|
||||||
const notas = notasInput.value.trim();
|
|
||||||
|
|
||||||
if (!validarEnteroPositivo(mesa_id)) {
|
|
||||||
return mostrarMensaje("Debes indicar un número de mesa válido.");
|
|
||||||
}
|
|
||||||
if (!mozo_id) {
|
|
||||||
return mostrarMensaje("Debes indicar el ID del mozo.");
|
|
||||||
}
|
|
||||||
if (items.length === 0) {
|
|
||||||
return mostrarMensaje("Agrega al menos un producto a la comanda.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const payload = { mesa_id, mozo_id, notas, items };
|
|
||||||
|
|
||||||
enviarBtn.disabled = true;
|
|
||||||
mostrarMensaje("Enviando comanda...");
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await fetch(API_BASE + COMANDAS_PATH, {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify(payload)
|
|
||||||
});
|
|
||||||
const data = await res.json();
|
|
||||||
if (!res.ok) {
|
|
||||||
mostrarMensaje({ error: "No se pudo crear la comanda", detalle: data });
|
|
||||||
} else {
|
|
||||||
mostrarMensaje({ ok: true, comanda: data });
|
|
||||||
// Limpia el estado
|
|
||||||
items.length = 0;
|
|
||||||
renderItems();
|
|
||||||
notasInput.value = "";
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
mostrarMensaje("Error al enviar comanda: " + e.message);
|
|
||||||
} finally {
|
|
||||||
enviarBtn.disabled = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Cargar productos al iniciar
|
|
||||||
cargarProductos();
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="es">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Dashboard</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h2>Mesas</h2>
|
|
||||||
<select id="mesasSelect"></select>
|
|
||||||
|
|
||||||
<h2>Productos</h2>
|
|
||||||
<select id="productosSelect"></select>
|
|
||||||
|
|
||||||
<h2>Categorías</h2>
|
|
||||||
<select id="categoriasSelect"></select>
|
|
||||||
|
|
||||||
<h2>Comandas</h2>
|
|
||||||
<select id="comandasSelect"></select>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
async function cargarDatos(endpoint, selectId, mostrar) {
|
|
||||||
const res = await fetch(endpoint); // Usar endpoint relativo
|
|
||||||
const data = await res.json();
|
|
||||||
const select = document.getElementById(selectId);
|
|
||||||
|
|
||||||
data.forEach(item => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = item.id;
|
|
||||||
option.textContent = mostrar(item);
|
|
||||||
select.appendChild(option);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Al cargar la página, cargamos los datos
|
|
||||||
window.onload = () => {
|
|
||||||
cargarDatos('api/obtenerMesas', 'mesasSelect', mesa => `Mesa ${mesa.numero}`);
|
|
||||||
cargarDatos('api/obtenerProductos', 'productosSelect', prod => `${prod.nombre} ($${prod.precio})`);
|
|
||||||
cargarDatos('api/obtenerCategorias', 'categoriasSelect', cat => cat.nombre);
|
|
||||||
cargarDatos('api/obtenerComandas', 'comandasSelect', com => `Comanda ${com.id} - Mesa ${com.mesa_id} - $${com.total}`);
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -0,0 +1,633 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import datetime
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
import shlex
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from typing import List, Dict, Tuple, Optional, Set
|
||||||
|
|
||||||
|
PROJECT_ROOT = pathlib.Path.cwd()
|
||||||
|
COMPOSE_BASE = PROJECT_ROOT / "compose.yaml"
|
||||||
|
COMPOSE_DEV = PROJECT_ROOT / "compose.dev.yaml"
|
||||||
|
COMPOSE_PROD = PROJECT_ROOT / "compose.prod.yaml"
|
||||||
|
COMPOSE_NPM = PROJECT_ROOT / "compose.npm.yaml"
|
||||||
|
COMPOSE_DBVR = PROJECT_ROOT / "compose.dbeaver.yaml"
|
||||||
|
|
||||||
|
GLOBAL_DEFAULT_PROJECT = "suitecoffee" # proyecto global (NPM/DBeaver)
|
||||||
|
|
||||||
|
# ---------- Shell utils ----------
|
||||||
|
|
||||||
|
def run(cmd: List[str], check=True, capture_output=True, text=True) -> subprocess.CompletedProcess:
|
||||||
|
return subprocess.run(cmd, check=check, capture_output=capture_output, text=text)
|
||||||
|
|
||||||
|
def which(program: str) -> bool:
|
||||||
|
from shutil import which as _which
|
||||||
|
return _which(program) is not None
|
||||||
|
|
||||||
|
# ---------- Docker volume discovery ----------
|
||||||
|
|
||||||
|
def docker_volume_ls_json(filters: List[str]) -> List[Dict[str, str]]:
|
||||||
|
"""
|
||||||
|
Devuelve objetos de 'docker volume ls' (formato json por entrada).
|
||||||
|
Soporta filtros como '--filter label=...'.
|
||||||
|
"""
|
||||||
|
cmd = ["docker", "volume", "ls", "--format", "{{json .}}"]
|
||||||
|
for f in filters:
|
||||||
|
cmd += ["--filter", f]
|
||||||
|
try:
|
||||||
|
cp = run(cmd)
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
return []
|
||||||
|
out = []
|
||||||
|
for line in cp.stdout.splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
out.append(json.loads(line))
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
pass
|
||||||
|
return out
|
||||||
|
|
||||||
|
def docker_volume_ls_names(filters: List[str]) -> List[str]:
|
||||||
|
"""Devuelve solo los nombres (Name) con filtros aplicados."""
|
||||||
|
rows = docker_volume_ls_json(filters)
|
||||||
|
names = []
|
||||||
|
for v in rows:
|
||||||
|
name = v.get("Name")
|
||||||
|
if name:
|
||||||
|
names.append(name)
|
||||||
|
return names
|
||||||
|
|
||||||
|
def list_by_label_project(project: str) -> List[Dict[str, str]]:
|
||||||
|
return docker_volume_ls_json([f"label=com.docker.compose.project={project}"])
|
||||||
|
|
||||||
|
def list_by_name_prefix(prefix: str) -> List[Dict[str, str]]:
|
||||||
|
vols = docker_volume_ls_json([])
|
||||||
|
keep = []
|
||||||
|
for v in vols:
|
||||||
|
name = v.get("Name")
|
||||||
|
if not name:
|
||||||
|
continue
|
||||||
|
if name.startswith(prefix + "_") or name.startswith(prefix + "-") or name == prefix:
|
||||||
|
keep.append(v)
|
||||||
|
return keep
|
||||||
|
|
||||||
|
def normalize_project_name(p: str) -> str:
|
||||||
|
return (p or "").replace(" ", "_")
|
||||||
|
|
||||||
|
# ---------- Compose config parsing ----------
|
||||||
|
|
||||||
|
def compose_config_json(files: List[pathlib.Path]) -> Optional[dict]:
|
||||||
|
if not files or not all(p.exists() for p in files):
|
||||||
|
return None
|
||||||
|
cmd = ["docker", "compose"]
|
||||||
|
for f in files:
|
||||||
|
cmd += ["-f", str(f)]
|
||||||
|
cmd += ["config", "--format", "json"]
|
||||||
|
try:
|
||||||
|
cp = run(cmd)
|
||||||
|
return json.loads(cp.stdout or "{}")
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def extract_short_volume_names_from_config(cfg: dict) -> Set[str]:
|
||||||
|
"""
|
||||||
|
Extrae short names de volúmenes usados en services[].volumes (type: volume)
|
||||||
|
y las claves del toplevel 'volumes'.
|
||||||
|
"""
|
||||||
|
names: Set[str] = set()
|
||||||
|
if not cfg:
|
||||||
|
return names
|
||||||
|
|
||||||
|
# services[].volumes
|
||||||
|
services = cfg.get("services") or {}
|
||||||
|
for svc in services.values():
|
||||||
|
vols = svc.get("volumes") or []
|
||||||
|
for m in vols:
|
||||||
|
# en JSON canonical, cada mount es un dict con 'type', 'source', 'target', ...
|
||||||
|
if isinstance(m, dict) and m.get("type") == "volume":
|
||||||
|
src = m.get("source")
|
||||||
|
if isinstance(src, str) and src:
|
||||||
|
names.add(src)
|
||||||
|
|
||||||
|
# top-level volumes (claves)
|
||||||
|
top_vols = cfg.get("volumes") or {}
|
||||||
|
if isinstance(top_vols, dict):
|
||||||
|
for k in top_vols.keys():
|
||||||
|
if isinstance(k, str) and k:
|
||||||
|
names.add(k)
|
||||||
|
|
||||||
|
return names
|
||||||
|
|
||||||
|
def docker_compose_name_from(files: List[pathlib.Path]) -> Optional[str]:
|
||||||
|
cfg = compose_config_json(files)
|
||||||
|
if cfg and isinstance(cfg, dict):
|
||||||
|
name = cfg.get("name")
|
||||||
|
if name:
|
||||||
|
return name
|
||||||
|
return None
|
||||||
|
|
||||||
|
def read_compose_project_from_env(env_path: pathlib.Path) -> Optional[str]:
|
||||||
|
try:
|
||||||
|
if env_path.exists():
|
||||||
|
for line in env_path.read_text(encoding="utf-8").splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
if not line or line.startswith("#"):
|
||||||
|
continue
|
||||||
|
if line.startswith("COMPOSE_PROJECT_NAME="):
|
||||||
|
return line.split("=", 1)[1].strip()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
def base_folder_slug() -> str:
|
||||||
|
return PROJECT_ROOT.name.lower().replace(" ", "_")
|
||||||
|
|
||||||
|
def candidates_for_env(env: str) -> List[str]:
|
||||||
|
cand: List[str] = []
|
||||||
|
if env == "development":
|
||||||
|
n1 = docker_compose_name_from([COMPOSE_BASE, COMPOSE_DEV])
|
||||||
|
n2 = read_compose_project_from_env(PROJECT_ROOT / ".env.development")
|
||||||
|
n3 = f"{base_folder_slug()}_dev"
|
||||||
|
n4 = f"{base_folder_slug()}-dev"
|
||||||
|
cand.extend([n1, n2, n3, n4, base_folder_slug()])
|
||||||
|
elif env == "production":
|
||||||
|
n1 = docker_compose_name_from([COMPOSE_BASE, COMPOSE_PROD])
|
||||||
|
n2 = read_compose_project_from_env(PROJECT_ROOT / ".env.production")
|
||||||
|
n3 = f"{base_folder_slug()}_prod"
|
||||||
|
n4 = f"{base_folder_slug()}-prod"
|
||||||
|
cand.extend([n1, n2, n3, n4, base_folder_slug()])
|
||||||
|
# dedup preservando orden
|
||||||
|
seen = set(); ordered = []
|
||||||
|
for x in cand:
|
||||||
|
if x and x not in seen:
|
||||||
|
seen.add(x); ordered.append(x)
|
||||||
|
return ordered
|
||||||
|
|
||||||
|
def candidates_for_global() -> List[str]:
|
||||||
|
cand: List[str] = []
|
||||||
|
# nombres desde compose globales
|
||||||
|
if COMPOSE_NPM.exists():
|
||||||
|
n = docker_compose_name_from([COMPOSE_NPM])
|
||||||
|
if n: cand.append(n)
|
||||||
|
if COMPOSE_DBVR.exists():
|
||||||
|
n = docker_compose_name_from([COMPOSE_DBVR])
|
||||||
|
if n and n not in cand: cand.append(n)
|
||||||
|
# fallback esperados
|
||||||
|
if GLOBAL_DEFAULT_PROJECT not in cand: cand.append(GLOBAL_DEFAULT_PROJECT)
|
||||||
|
bf = base_folder_slug()
|
||||||
|
if bf not in cand: cand.append(bf)
|
||||||
|
return cand
|
||||||
|
|
||||||
|
# ---------- Nueva detección por grupo: COMPOSE + labels ----------
|
||||||
|
|
||||||
|
def detect_group_volumes_with_compose(filesets: List[List[pathlib.Path]],
|
||||||
|
project_candidates: List[str]) -> Tuple[Optional[str], str, List[str]]:
|
||||||
|
"""
|
||||||
|
filesets: lista de listas de archivos compose a considerar (dev=[base,dev], prod=[base,prod],
|
||||||
|
global=[[npm], [dbeaver]] -> dos sets para unir shortnames).
|
||||||
|
Devuelve (project_seleccionado, metodo, [nombres_de_volumen]).
|
||||||
|
"""
|
||||||
|
# 1) Unir shortnames de todos los filesets
|
||||||
|
shortnames: Set[str] = set()
|
||||||
|
for files in filesets:
|
||||||
|
cfg = compose_config_json(files)
|
||||||
|
shortnames |= extract_short_volume_names_from_config(cfg)
|
||||||
|
|
||||||
|
# 2) Si hay shortnames, probar a buscar por (project,label.volume)
|
||||||
|
if shortnames:
|
||||||
|
for proj in project_candidates:
|
||||||
|
# Buscar cada shortname con ambos labels
|
||||||
|
found: List[str] = []
|
||||||
|
for sn in sorted(shortnames):
|
||||||
|
names = docker_volume_ls_names([
|
||||||
|
f"label=com.docker.compose.project={proj}",
|
||||||
|
f"label=com.docker.compose.volume={sn}"
|
||||||
|
])
|
||||||
|
if names:
|
||||||
|
found.extend(names)
|
||||||
|
# dedup preservando orden
|
||||||
|
if found:
|
||||||
|
seen = set(); ordered = []
|
||||||
|
for n in found:
|
||||||
|
if n not in seen:
|
||||||
|
seen.add(n); ordered.append(n)
|
||||||
|
return proj, f"compose+labels:{proj}", ordered
|
||||||
|
|
||||||
|
# 3) Fallback: probar cualquier volumen del proyecto (label) o por prefijo
|
||||||
|
for proj in project_candidates:
|
||||||
|
method, rows = discover_volumes_for_project(proj)
|
||||||
|
if rows:
|
||||||
|
return proj, f"fallback:{method}", [r.get("Name") for r in rows if r.get("Name")]
|
||||||
|
|
||||||
|
# 4) Nada
|
||||||
|
first = project_candidates[0] if project_candidates else None
|
||||||
|
return first, "none", []
|
||||||
|
|
||||||
|
def discover_volumes_for_project(project_raw: str) -> Tuple[str, List[Dict[str, str]]]:
|
||||||
|
"""
|
||||||
|
Método previo de respaldo: por label de proyecto y prefijo (para CLI y fallback).
|
||||||
|
"""
|
||||||
|
project_norm = normalize_project_name(project_raw)
|
||||||
|
project_lower = project_norm.lower()
|
||||||
|
|
||||||
|
vols = list_by_label_project(project_norm)
|
||||||
|
if vols:
|
||||||
|
return f"label:{project_norm}", vols
|
||||||
|
|
||||||
|
vols2 = list_by_label_project(project_lower)
|
||||||
|
if vols2:
|
||||||
|
return f"label:{project_lower}", vols2
|
||||||
|
|
||||||
|
by_name = list_by_name_prefix(project_norm)
|
||||||
|
if by_name:
|
||||||
|
return f"name-prefix:{project_norm}", by_name
|
||||||
|
|
||||||
|
by_name2 = list_by_name_prefix(project_lower)
|
||||||
|
if by_name2:
|
||||||
|
return f"name-prefix:{project_lower}", by_name2
|
||||||
|
|
||||||
|
return "none", []
|
||||||
|
|
||||||
|
# ---------- Backup helpers ----------
|
||||||
|
|
||||||
|
def ensure_alpine_image():
|
||||||
|
try:
|
||||||
|
run(["docker", "image", "inspect", "alpine:latest"])
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
print("Pulling alpine:latest ...")
|
||||||
|
run(["docker", "pull", "alpine:latest"], check=True, capture_output=False)
|
||||||
|
|
||||||
|
def build_archive_name(project: str, volume_name: str, ts: str) -> str:
|
||||||
|
"""
|
||||||
|
Construye el nombre del .tar.gz evitando duplicar el prefijo del proyecto.
|
||||||
|
- Si volume_name ya empieza con '<project>_' o '<project>-', se usa tal cual.
|
||||||
|
- Si no, se antepone '<project>_'.
|
||||||
|
Resultado: <project>_<shortname>-<ts>.tar.gz
|
||||||
|
"""
|
||||||
|
proj_token = project.lower().replace(" ", "_")
|
||||||
|
v_lower = volume_name.lower()
|
||||||
|
if v_lower.startswith(proj_token + "_") or v_lower.startswith(proj_token + "-"):
|
||||||
|
base = volume_name
|
||||||
|
else:
|
||||||
|
base = f"{proj_token}_{volume_name}"
|
||||||
|
return f"{base}-{ts}.tar.gz"
|
||||||
|
|
||||||
|
def backup_volume(volume_name: str, out_dir: pathlib.Path, archive_name: str, dry_run: bool = False) -> int:
|
||||||
|
out_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
out_dir_abs = out_dir.resolve()
|
||||||
|
out_path = out_dir_abs / archive_name
|
||||||
|
docker_cmd = [
|
||||||
|
"docker", "run", "--rm",
|
||||||
|
"-v", f"{volume_name}:/volume:ro",
|
||||||
|
"-v", f"{str(out_dir_abs)}:/backup",
|
||||||
|
# "--user", f"{os.getuid()}:{os.getgid()}",
|
||||||
|
"alpine:latest",
|
||||||
|
"sh", "-lc",
|
||||||
|
f"tar czf /backup/{shlex.quote(out_path.name)} -C /volume ."
|
||||||
|
]
|
||||||
|
if dry_run:
|
||||||
|
print("[DRY RUN] Would run:", " ".join(shlex.quote(c) for c in docker_cmd))
|
||||||
|
return 0
|
||||||
|
cp = subprocess.run(docker_cmd)
|
||||||
|
return cp.returncode
|
||||||
|
|
||||||
|
def backup_explicit(volume_names: List[str], ts: str, output_dir: Optional[str], dry_run: bool, prefix_project: Optional[str]) -> int:
|
||||||
|
"""
|
||||||
|
Respalda exactamente los volúmenes indicados.
|
||||||
|
- Directorio por defecto: ./docker-volumes-<ts>
|
||||||
|
- Nombre de archivo: build_archive_name(prefix_project, volume_name, ts)
|
||||||
|
"""
|
||||||
|
out_dir = pathlib.Path(output_dir) if output_dir else (PROJECT_ROOT / f"docker-volumes-{ts}")
|
||||||
|
if not dry_run:
|
||||||
|
ensure_alpine_image()
|
||||||
|
|
||||||
|
failures = []
|
||||||
|
for vname in volume_names:
|
||||||
|
if not vname:
|
||||||
|
continue
|
||||||
|
archive = build_archive_name(prefix_project or "", vname, ts)
|
||||||
|
print(f"Backing up volume: {vname} -> {archive}")
|
||||||
|
rc = backup_volume(vname, out_dir, archive, dry_run=dry_run)
|
||||||
|
if rc != 0:
|
||||||
|
print(f" ERROR: backup failed for volume '{vname}' (exit code {rc})", file=sys.stderr)
|
||||||
|
failures.append(vname)
|
||||||
|
if failures:
|
||||||
|
print("\nCompleted with errors. Failed volumes:", ", ".join(failures))
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
print("\nAll done. Archives written to:", str(out_dir.resolve()))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def backup_group(project_name: str, ts: str, output_dir: Optional[str] = None,
|
||||||
|
dry_run: bool = False, excludes: Optional[List[str]] = None) -> int:
|
||||||
|
"""
|
||||||
|
Fallback legacy (label/prefix). Mantiene coherencia con nombres y directorio por defecto.
|
||||||
|
"""
|
||||||
|
method, rows = discover_volumes_for_project(project_name)
|
||||||
|
|
||||||
|
print_header(f"Proyecto '{project_name}': {len(rows)} volumen(es) detectado(s) (método: {method})")
|
||||||
|
for v in rows:
|
||||||
|
print(" -", v.get("Name"))
|
||||||
|
|
||||||
|
if not rows:
|
||||||
|
warn("No hay volúmenes para respaldar.")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
vols = [v.get("Name") for v in rows if v.get("Name")]
|
||||||
|
if excludes:
|
||||||
|
excl = set(excludes)
|
||||||
|
vols = [n for n in vols if n not in excl]
|
||||||
|
if not vols:
|
||||||
|
warn("Tras aplicar exclusiones, no quedó nada por respaldar.")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
out_dir = pathlib.Path(output_dir) if output_dir else (PROJECT_ROOT / f"docker-volumes-{ts}")
|
||||||
|
if not dry_run:
|
||||||
|
ensure_alpine_image()
|
||||||
|
|
||||||
|
failures = []
|
||||||
|
for vname in vols:
|
||||||
|
archive = build_archive_name(project_name, vname, ts)
|
||||||
|
print(f"Backing up volume: {vname} -> {archive}")
|
||||||
|
rc = backup_volume(vname, out_dir, archive, dry_run=dry_run)
|
||||||
|
if rc != 0:
|
||||||
|
print(f" ERROR: backup failed for volume '{vname}' (exit code {rc})", file=sys.stderr)
|
||||||
|
failures.append(vname)
|
||||||
|
|
||||||
|
if failures:
|
||||||
|
print("\nCompleted with errors. Failed volumes:", ", ".join(failures))
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
print("\nAll done. Archives written to:", str(out_dir.resolve()))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# ---------- UI helpers ----------
|
||||||
|
|
||||||
|
def yes_no(prompt: str, default="n") -> bool:
|
||||||
|
default = default.lower()
|
||||||
|
hint = "[Y/n]" if default == "y" else "[y/N]"
|
||||||
|
while True:
|
||||||
|
resp = input(f"{prompt} {hint} ").strip().lower()
|
||||||
|
if not resp:
|
||||||
|
return default == "y"
|
||||||
|
if resp in ("y","yes","s","si","sí"):
|
||||||
|
return True
|
||||||
|
if resp in ("n","no"):
|
||||||
|
return False
|
||||||
|
print("Respuesta no reconocida. Por favor, responde con 'y' o 'n'.")
|
||||||
|
|
||||||
|
def print_header(title: str):
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print(title)
|
||||||
|
print("=" * 60 + "\n")
|
||||||
|
|
||||||
|
def info(msg): print(f"• {msg}")
|
||||||
|
def ok(msg): print(f"✓ {msg}")
|
||||||
|
def warn(msg): print(f"! {msg}")
|
||||||
|
def fail(msg):
|
||||||
|
print(f"✗ {msg}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# ---------- Menú interactivo ----------
|
||||||
|
|
||||||
|
def interactive_menu():
|
||||||
|
if not which("docker"):
|
||||||
|
fail("ERROR: 'docker' no está en el PATH.")
|
||||||
|
try:
|
||||||
|
run(["docker", "version"], check=True, capture_output=True)
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
fail("ERROR: No se puede hablar con el daemon de Docker. ¿Está corriendo? ¿Tu usuario está en el grupo 'docker'?")
|
||||||
|
|
||||||
|
# DEV
|
||||||
|
dev_candidates = candidates_for_env("development") if COMPOSE_BASE.exists() and COMPOSE_DEV.exists() else []
|
||||||
|
dev_proj, dev_method, dev_names = detect_group_volumes_with_compose(
|
||||||
|
filesets=[[COMPOSE_BASE, COMPOSE_DEV]] if dev_candidates else [],
|
||||||
|
project_candidates=dev_candidates
|
||||||
|
)
|
||||||
|
|
||||||
|
# PROD
|
||||||
|
prod_candidates = candidates_for_env("production") if COMPOSE_BASE.exists() and COMPOSE_PROD.exists() else []
|
||||||
|
prod_proj, prod_method, prod_names = detect_group_volumes_with_compose(
|
||||||
|
filesets=[[COMPOSE_BASE, COMPOSE_PROD]] if prod_candidates else [],
|
||||||
|
project_candidates=prod_candidates
|
||||||
|
)
|
||||||
|
|
||||||
|
# GLOBAL = NPM + DBEAVER (unir shortnames de ambos)
|
||||||
|
global_candidates = candidates_for_global()
|
||||||
|
global_filesets = []
|
||||||
|
if COMPOSE_NPM.exists():
|
||||||
|
global_filesets.append([COMPOSE_NPM])
|
||||||
|
if COMPOSE_DBVR.exists():
|
||||||
|
global_filesets.append([COMPOSE_DBVR])
|
||||||
|
glob_proj, glob_method, glob_names = detect_group_volumes_with_compose(
|
||||||
|
filesets=global_filesets,
|
||||||
|
project_candidates=global_candidates
|
||||||
|
)
|
||||||
|
|
||||||
|
# Resumen
|
||||||
|
print_header("Resumen de volúmenes detectados")
|
||||||
|
if dev_proj:
|
||||||
|
info(f"DESARROLLO ({dev_proj}): {len(dev_names)} volumen(es) (método: {dev_method})")
|
||||||
|
else:
|
||||||
|
info("DESARROLLO: archivos compose no encontrados.")
|
||||||
|
if prod_proj:
|
||||||
|
info(f"PRODUCCIÓN ({prod_proj}): {len(prod_names)} volumen(es) (método: {prod_method})")
|
||||||
|
else:
|
||||||
|
info("PRODUCCIÓN: archivos compose no encontrados.")
|
||||||
|
if glob_proj:
|
||||||
|
info(f"GLOBALES ({glob_proj}): {len(glob_names)} volumen(es) (método: {glob_method})")
|
||||||
|
else:
|
||||||
|
info("GLOBALES: no se detectaron archivos compose globales.")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Menú
|
||||||
|
options = {}
|
||||||
|
key = 1
|
||||||
|
if dev_proj:
|
||||||
|
print(f" {key}) Respaldar volúmenes de DESARROLLO ({dev_proj})")
|
||||||
|
options[str(key)] = ("backup_explicit", dev_proj, dev_names); key += 1
|
||||||
|
if prod_proj:
|
||||||
|
print(f" {key}) Respaldar volúmenes de PRODUCCIÓN ({prod_proj})")
|
||||||
|
options[str(key)] = ("backup_explicit", prod_proj, prod_names); key += 1
|
||||||
|
if glob_proj:
|
||||||
|
print(f" {key}) Respaldar volúmenes GLOBALES ({glob_proj})")
|
||||||
|
options[str(key)] = ("backup_explicit", glob_proj, glob_names); key += 1
|
||||||
|
|
||||||
|
# TODOS: unión deduplicada por nombre (respalda 1 vez cada volumen)
|
||||||
|
groups = []
|
||||||
|
if dev_proj: groups.append( (dev_proj, dev_names) )
|
||||||
|
if prod_proj: groups.append( (prod_proj, prod_names) )
|
||||||
|
if glob_proj: groups.append( (glob_proj, glob_names) )
|
||||||
|
|
||||||
|
if len(groups) >= 2:
|
||||||
|
print(f" {key}) Respaldar TODOS los grupos detectados")
|
||||||
|
options[str(key)] = ("backup_all_explicit", groups); key += 1
|
||||||
|
|
||||||
|
print(f" {key}) Salir")
|
||||||
|
exit_key = str(key)
|
||||||
|
|
||||||
|
ts = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
choice = input("> ").strip()
|
||||||
|
if choice == exit_key:
|
||||||
|
ok("Saliendo.")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if choice not in options:
|
||||||
|
print("Opción inválida.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
action = options[choice][0]
|
||||||
|
dry = yes_no("¿Dry-run (no escribir archivos)?", default="n")
|
||||||
|
outd = input(f"Directorio de salida (vacío = ./docker-volumes-{ts}): ").strip() or None
|
||||||
|
excl_input = input("Excluir volúmenes (nombres separados por coma, vacío = ninguno): ").strip()
|
||||||
|
excludes = set(e.strip() for e in excl_input.split(",") if e.strip()) if excl_input else set()
|
||||||
|
|
||||||
|
if action == "backup_explicit":
|
||||||
|
_, proj, names = options[choice]
|
||||||
|
names = [n for n in names if n not in excludes]
|
||||||
|
if not names:
|
||||||
|
warn("No hay volúmenes para respaldar.")
|
||||||
|
sys.exit(0)
|
||||||
|
rc = backup_explicit(names, ts, output_dir=outd, dry_run=dry, prefix_project=proj)
|
||||||
|
sys.exit(rc)
|
||||||
|
|
||||||
|
elif action == "backup_all_explicit":
|
||||||
|
_, groups_payload = options[choice]
|
||||||
|
vol_to_proj: Dict[str, str] = {}
|
||||||
|
for proj, names in groups_payload:
|
||||||
|
for n in names:
|
||||||
|
if n not in excludes and n not in vol_to_proj:
|
||||||
|
vol_to_proj[n] = proj
|
||||||
|
if not vol_to_proj:
|
||||||
|
warn("No hay volúmenes para respaldar.")
|
||||||
|
sys.exit(0)
|
||||||
|
if not dry:
|
||||||
|
ensure_alpine_image()
|
||||||
|
out_dir = pathlib.Path(outd) if outd else (PROJECT_ROOT / f"docker-volumes-{ts}")
|
||||||
|
failures = []
|
||||||
|
for vname, proj in vol_to_proj.items():
|
||||||
|
archive = build_archive_name(proj, vname, ts)
|
||||||
|
print(f"Backing up volume: {vname} -> {archive}")
|
||||||
|
rc = backup_volume(vname, out_dir, archive, dry_run=dry)
|
||||||
|
if rc != 0:
|
||||||
|
print(f" ERROR: backup failed for volume '{vname}' (exit code {rc})", file=sys.stderr)
|
||||||
|
failures.append(vname)
|
||||||
|
if failures:
|
||||||
|
print("\nCompleted with errors. Failed volumes:", ", ".join(failures))
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
print("\nAll done. Archives written to:", str(out_dir.resolve()))
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# ---------- CLI legacy (se mantiene) ----------
|
||||||
|
|
||||||
|
def detect_project_name(args_project: Optional[str]) -> str:
|
||||||
|
if args_project:
|
||||||
|
return args_project
|
||||||
|
env_name = os.environ.get("COMPOSE_PROJECT_NAME")
|
||||||
|
if env_name:
|
||||||
|
return env_name
|
||||||
|
return PROJECT_ROOT.name.replace(" ", "_")
|
||||||
|
|
||||||
|
def cli_main():
|
||||||
|
parser = argparse.ArgumentParser(description="Export (compress) every Docker volume of a Docker Compose project.")
|
||||||
|
parser.add_argument("-p", "--project", help="Compose project or prefix (see --discovery).")
|
||||||
|
parser.add_argument("-o", "--output", help="Output directory (default: ./docker-volumes-<timestamp>).")
|
||||||
|
parser.add_argument("--exclude", nargs="*", default=[], help="Volume names to exclude (space-separated).")
|
||||||
|
parser.add_argument("--dry-run", action="store_true", help="Show what would be done without doing it.")
|
||||||
|
parser.add_argument("--timestamp", default=datetime.datetime.now().strftime("%Y%m%d-%H%M%S"),
|
||||||
|
help="Timestamp to embed into filenames (default: current time).")
|
||||||
|
parser.add_argument("--discovery", choices=["auto","label","name"], default="auto",
|
||||||
|
help="How to discover volumes: 'label' (strict), 'name' (prefix), or 'auto' (default).")
|
||||||
|
parser.add_argument("--list-only", action="store_true", help="Only list volumes that would be backed up and exit.")
|
||||||
|
parser.add_argument("--menu", action="store_true", help="Launch interactive menu instead of CLI behavior.")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.menu or not args.project:
|
||||||
|
interactive_menu()
|
||||||
|
return
|
||||||
|
|
||||||
|
if not which("docker"):
|
||||||
|
print("ERROR: 'docker' not on PATH.", file=sys.stderr)
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
project_raw = detect_project_name(args.project)
|
||||||
|
project_norm = normalize_project_name(project_raw)
|
||||||
|
project_lower = project_norm.lower()
|
||||||
|
ts = args.timestamp
|
||||||
|
out_dir = pathlib.Path(args.output) if args.output else (PROJECT_ROOT / f"docker-volumes-{ts}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
run(["docker", "version"], check=True, capture_output=True)
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
print("ERROR: Docker daemon not reachable.", file=sys.stderr)
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
# Descubrimiento legacy por label/prefijo (se mantiene para compatibilidad)
|
||||||
|
selected = []
|
||||||
|
method_used = None
|
||||||
|
vols = list_by_label_project(project_norm)
|
||||||
|
if vols:
|
||||||
|
selected = vols; method_used = f"label:{project_norm}"
|
||||||
|
else:
|
||||||
|
vols2 = list_by_label_project(project_lower)
|
||||||
|
if vols2:
|
||||||
|
selected = vols2; method_used = f"label:{project_lower}"
|
||||||
|
if not selected:
|
||||||
|
by_name = list_by_name_prefix(project_norm)
|
||||||
|
if by_name:
|
||||||
|
selected = by_name; method_used = f"name-prefix:{project_norm}"
|
||||||
|
else:
|
||||||
|
by_name2 = list_by_name_prefix(project_lower)
|
||||||
|
if by_name2:
|
||||||
|
selected = by_name2; method_used = f"name-prefix:{project_lower}"
|
||||||
|
|
||||||
|
if not selected:
|
||||||
|
print(f"No volumes found for project/prefix '{project_raw}'.")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
exclude_set = set(args.exclude or [])
|
||||||
|
names = [v.get("Name") for v in selected if v.get("Name") not in exclude_set]
|
||||||
|
|
||||||
|
print(f"Discovery method: {method_used}")
|
||||||
|
print(f"Volumes discovered: {len(names)}")
|
||||||
|
for n in names:
|
||||||
|
print(" -", n)
|
||||||
|
|
||||||
|
if args.list_only:
|
||||||
|
return
|
||||||
|
|
||||||
|
if not args.dry_run:
|
||||||
|
ensure_alpine_image()
|
||||||
|
|
||||||
|
failures = []
|
||||||
|
for vname in names:
|
||||||
|
archive = build_archive_name(project_lower, vname, ts)
|
||||||
|
print(f"Backing up volume: {vname} -> {archive}")
|
||||||
|
rc = backup_volume(vname, out_dir, archive, dry_run=args.dry_run)
|
||||||
|
if rc != 0:
|
||||||
|
print(f" ERROR: backup failed for volume '{vname}' (exit code {rc})", file=sys.stderr)
|
||||||
|
failures.append(vname)
|
||||||
|
|
||||||
|
if failures:
|
||||||
|
print("\nCompleted with errors. Failed volumes:", ", ".join(failures))
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
print("\nAll done. Archives written to:", str(out_dir.resolve()))
|
||||||
|
|
||||||
|
# ---------- Entry point ----------
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(sys.argv) == 1:
|
||||||
|
interactive_menu()
|
||||||
|
else:
|
||||||
|
cli_main()
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# docker-compose.overrride.yml
|
||||||
|
# Docker Comose para entorno de desarrollo o development.
|
||||||
|
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
app:
|
||||||
|
image: node:20-bookworm
|
||||||
|
expose:
|
||||||
|
- ${APP_LOCAL_PORT}
|
||||||
|
working_dir: /app
|
||||||
|
user: "${UID:-1000}:${GID:-1000}"
|
||||||
|
volumes:
|
||||||
|
- ./services/app:/app:rw
|
||||||
|
- ./services/app/node_modules:/app/node_modules
|
||||||
|
env_file:
|
||||||
|
- ./services/app/.env.development
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=${NODE_ENV}
|
||||||
|
networks:
|
||||||
|
net:
|
||||||
|
aliases: [dev-app]
|
||||||
|
command: npm run dev
|
||||||
|
|
||||||
|
auth:
|
||||||
|
image: node:20-bookworm
|
||||||
|
expose:
|
||||||
|
- ${AUTH_LOCAL_PORT}
|
||||||
|
working_dir: /app
|
||||||
|
user: "${UID:-1000}:${GID:-1000}"
|
||||||
|
volumes:
|
||||||
|
- ./services/auth:/app:rw
|
||||||
|
- ./services/auth/node_modules:/app/node_modules
|
||||||
|
env_file:
|
||||||
|
- ./services/auth/.env.development
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=${NODE_ENV}
|
||||||
|
command: npm run dev
|
||||||
|
networks:
|
||||||
|
net:
|
||||||
|
aliases: [dev-auth]
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: postgres:16
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: ${DB_NAME}
|
||||||
|
POSTGRES_USER: ${DB_USER}
|
||||||
|
POSTGRES_PASSWORD: ${DB_PASS}
|
||||||
|
volumes:
|
||||||
|
- suitecoffee-db:/var/lib/postgresql/data
|
||||||
|
networks:
|
||||||
|
net:
|
||||||
|
aliases: [dev-db]
|
||||||
|
|
||||||
|
tenants:
|
||||||
|
image: postgres:16
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: ${TENANTS_DB_NAME}
|
||||||
|
POSTGRES_USER: ${TENANTS_DB_USER}
|
||||||
|
POSTGRES_PASSWORD: ${TENANTS_DB_PASS}
|
||||||
|
volumes:
|
||||||
|
- tenants-db:/var/lib/postgresql/data
|
||||||
|
networks:
|
||||||
|
net:
|
||||||
|
aliases: [dev-tenants]
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
tenants-db:
|
||||||
|
suitecoffee-db:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
net:
|
||||||
|
driver: bridge
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
# compose.prod.yml
|
||||||
|
# Docker Comose para entorno de producción o production.
|
||||||
|
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
app:
|
||||||
|
build:
|
||||||
|
context: ./services/app
|
||||||
|
dockerfile: Dockerfile.production
|
||||||
|
expose:
|
||||||
|
- ${APP_LOCAL_PORT}
|
||||||
|
volumes:
|
||||||
|
- ./services/app:/app
|
||||||
|
env_file:
|
||||||
|
- ./services/app/.env.production
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=${NODE_ENV}
|
||||||
|
networks:
|
||||||
|
net:
|
||||||
|
aliases: [prod-app]
|
||||||
|
command: npm run start
|
||||||
|
|
||||||
|
auth:
|
||||||
|
build:
|
||||||
|
context: ./services/auth
|
||||||
|
dockerfile: Dockerfile.production
|
||||||
|
expose:
|
||||||
|
- ${AUTH_LOCAL_PORT}
|
||||||
|
volumes:
|
||||||
|
- ./services/auth:/app
|
||||||
|
env_file:
|
||||||
|
- ./services/auth/.env.production
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=${NODE_ENV}
|
||||||
|
command: npm run start
|
||||||
|
networks:
|
||||||
|
net:
|
||||||
|
aliases: [prod-auth]
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: postgres:16
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: ${DB_NAME}
|
||||||
|
POSTGRES_USER: ${DB_USER}
|
||||||
|
POSTGRES_PASSWORD: ${DB_PASS}
|
||||||
|
volumes:
|
||||||
|
- suitecoffee-db:/var/lib/postgresql/data
|
||||||
|
networks:
|
||||||
|
net:
|
||||||
|
aliases: [prod-db]
|
||||||
|
|
||||||
|
tenants:
|
||||||
|
image: postgres:16
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: ${TENANTS_DB_NAME}
|
||||||
|
POSTGRES_USER: ${TENANTS_DB_USER}
|
||||||
|
POSTGRES_PASSWORD: ${TENANTS_DB_PASS}
|
||||||
|
volumes:
|
||||||
|
- tenants-db:/var/lib/postgresql/data
|
||||||
|
networks:
|
||||||
|
net:
|
||||||
|
aliases: [prod-tenants]
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
tenants-db:
|
||||||
|
suitecoffee-db:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
net:
|
||||||
|
driver: bridge
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# compose.tools.yaml
|
||||||
|
name: suitecoffee_tools
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
dbeaver:
|
||||||
|
image: dbeaver/cloudbeaver:latest
|
||||||
|
profiles: [dbeaver]
|
||||||
|
ports:
|
||||||
|
- 8978:8978
|
||||||
|
environment:
|
||||||
|
TZ: America/Montevideo
|
||||||
|
volumes:
|
||||||
|
- dbeaver_logs:/opt/cloudbeaver/logs
|
||||||
|
- dbeaver_workspace:/opt/cloudbeaver/workspace
|
||||||
|
networks:
|
||||||
|
suitecoffee_prod_net: {}
|
||||||
|
suitecoffee_dev_net: {}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "curl -fsS http://localhost:8978 || exit 1"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 10
|
||||||
|
start_period: 20s
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
npm:
|
||||||
|
image: jc21/nginx-proxy-manager:latest
|
||||||
|
profiles: [npm]
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "80:80" # HTTP público
|
||||||
|
- "81:81" # UI de administración
|
||||||
|
environment:
|
||||||
|
TZ: America/Montevideo
|
||||||
|
volumes:
|
||||||
|
- npm_data:/data
|
||||||
|
- npm_letsencrypt:/etc/letsencrypt
|
||||||
|
networks:
|
||||||
|
suitecoffee_prod_net: {}
|
||||||
|
suitecoffee_dev_net: {}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "curl -fsS http://localhost:81 || exit 1"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 10
|
||||||
|
start_period: 20s
|
||||||
|
|
||||||
|
|
||||||
|
networks:
|
||||||
|
suitecoffee_dev_net:
|
||||||
|
external: true
|
||||||
|
suitecoffee_prod_net:
|
||||||
|
external: true
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
npm_data:
|
||||||
|
npm_letsencrypt:
|
||||||
|
|
||||||
|
dbeaver_logs:
|
||||||
|
dbeaver_workspace:
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
# compose.yml
|
||||||
|
# Comose base
|
||||||
|
name: ${COMPOSE_PROJECT_NAME:-suitecoffee}
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
# app:
|
||||||
|
# depends_on:
|
||||||
|
# db:
|
||||||
|
# condition: service_healthy
|
||||||
|
# tenants:
|
||||||
|
# condition: service_healthy
|
||||||
|
# healthcheck:
|
||||||
|
# test: ["CMD-SHELL", "curl -fsS http://localhost:${APP_DOCKER_PORT}/health || exit 1"]
|
||||||
|
# interval: 10s
|
||||||
|
# timeout: 3s
|
||||||
|
# retries: 10
|
||||||
|
# start_period: 20s
|
||||||
|
# restart: unless-stopped
|
||||||
|
|
||||||
|
# auth:
|
||||||
|
# depends_on:
|
||||||
|
# db:
|
||||||
|
# condition: service_healthy
|
||||||
|
# healthcheck:
|
||||||
|
# test: ["CMD-SHELL", "curl -fsS http://localhost:${AUTH_DOCKER_PORT}/health || exit 1"]
|
||||||
|
# interval: 10s
|
||||||
|
# timeout: 3s
|
||||||
|
# retries: 10
|
||||||
|
# start_period: 15s
|
||||||
|
# restart: unless-stopped
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: postgres:16
|
||||||
|
environment:
|
||||||
|
TZ: America/Montevideo
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 20
|
||||||
|
start_period: 10s
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
tenants:
|
||||||
|
image: postgres:16
|
||||||
|
environment:
|
||||||
|
TZ: America/Montevideo
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U ${TENANTS_DB_USER} -d ${TENANTS_DB_NAME}"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 20
|
||||||
|
start_period: 10s
|
||||||
|
restart: unless-stopped
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
# docker-compose.overrride.yml
|
|
||||||
# Docker Comose para entorno de desarrollo o development.
|
|
||||||
|
|
||||||
# <---- Estructura ---->
|
|
||||||
# services:
|
|
||||||
# suitecoffe-app
|
|
||||||
# suitecoffe-db
|
|
||||||
# auth-service (No implementado aún)
|
|
||||||
# auth-db (No implementado aún)
|
|
||||||
# volumes:
|
|
||||||
# pg-appdb-data:
|
|
||||||
# pg-authdb-data: (No implementado aún)
|
|
||||||
|
|
||||||
|
|
||||||
services:
|
|
||||||
|
|
||||||
suitecoffee-app:
|
|
||||||
container_name: suitecoffee-app
|
|
||||||
depends_on:
|
|
||||||
- suitecoffee-db
|
|
||||||
build:
|
|
||||||
context: ./services/app
|
|
||||||
dockerfile: Dockerfile.development
|
|
||||||
volumes:
|
|
||||||
- ./services/app:/app
|
|
||||||
ports:
|
|
||||||
- ${APP_LOCAL_PORT}:${APP_DOCKER_PORT}
|
|
||||||
env_file:
|
|
||||||
- ./services/app/.env.development
|
|
||||||
environment:
|
|
||||||
- NODE_ENV=${NODE_ENV}
|
|
||||||
command: npm run dev
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
suitecoffee-db:
|
|
||||||
image: postgres:16
|
|
||||||
container_name: suitecoffee-db
|
|
||||||
environment:
|
|
||||||
POSTGRES_DB: suitecoffee
|
|
||||||
POSTGRES_USER: suitecoffee
|
|
||||||
POSTGRES_PASSWORD: suitecoffee
|
|
||||||
ports:
|
|
||||||
- "54321:5432"
|
|
||||||
volumes:
|
|
||||||
- pg-appdb-data:/var/lib/postgresql/data
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
# auth-service:
|
|
||||||
# container_name: auth-service
|
|
||||||
# depends_on:
|
|
||||||
# - auth-db
|
|
||||||
# build:
|
|
||||||
# context: ./services/app
|
|
||||||
# dockerfile: Dockerfile.dev
|
|
||||||
# volumes:
|
|
||||||
# - ./services/auth:/app
|
|
||||||
# ports:
|
|
||||||
# - 3030:3030 # Usa la variable de entorno PORT
|
|
||||||
# # env_file:
|
|
||||||
# # - ./services/auth/.env.dev
|
|
||||||
# environment:
|
|
||||||
# # - NODE_ENV=dev
|
|
||||||
# - PORT=3030
|
|
||||||
# command: npm run dev # Usa nodemon para desarrollo (visitar package.json)
|
|
||||||
# restart: unless-stopped
|
|
||||||
|
|
||||||
# auth-db:
|
|
||||||
# container_name: auth-db
|
|
||||||
# image: postgres:16
|
|
||||||
# # ports:
|
|
||||||
# # - ${AUTH_DB_LOCAL_PORT}:${AUTH_DB_DOCKER_PORT}
|
|
||||||
# # env_file:
|
|
||||||
# # - ./services/auth/.env.dev
|
|
||||||
# environment:
|
|
||||||
# POSTGRES_DB: "auth"
|
|
||||||
# POSTGRES_USER: "dev_user"
|
|
||||||
# POSTGRES_PASSWORD: "dev_password"
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
pg-appdb-data:
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
@startuml Modelo Entidad-Relacion
|
|
||||||
class Producto {
|
|
||||||
- idProducto: int
|
|
||||||
+ getId(): int
|
|
||||||
}
|
|
||||||
@enduml
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
docker compose -f docker-compose.yml -f docker-compose.override.yml \
|
|
||||||
--env-file .env.development up -d
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "suitecoffee",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Software para gestión de cafeterías",
|
||||||
|
"keywords": [
|
||||||
|
"coffee",
|
||||||
|
"suite",
|
||||||
|
"suitecoffee"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://gitea.mateosaldain.uy/msaldain/SuiteCoffee.git"
|
||||||
|
},
|
||||||
|
"license": "ISC",
|
||||||
|
"author": "Mateo Saldain",
|
||||||
|
"type": "module",
|
||||||
|
"main": "suitecoffee.py",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,466 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
restore_compose_volumes.py
|
||||||
|
--------------------------
|
||||||
|
Restaura volúmenes desde backups generados por backup_compose_volumes.py.
|
||||||
|
|
||||||
|
- Busca carpetas ./docker-volumes-<timestamp>
|
||||||
|
- Lee .tar.gz (nombres: <volume_name>-<YYYYMMDD-HHMMSS>.tar.gz)
|
||||||
|
- Dos modos:
|
||||||
|
1) Tradicional (sin labels)
|
||||||
|
2) Reconocido por Compose (aplica labels com.docker.compose.* para evitar el warning)
|
||||||
|
|
||||||
|
Además:
|
||||||
|
- Si un volumen existe y está en uso, ofrece detener y eliminar contenedores que lo usan
|
||||||
|
para poder recrearlo con labels correctos (solo en modo 2).
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import shlex
|
||||||
|
import pathlib
|
||||||
|
import subprocess
|
||||||
|
from typing import List, Tuple, Dict, Optional
|
||||||
|
|
||||||
|
PROJECT_ROOT = pathlib.Path.cwd()
|
||||||
|
BACKUP_DIR_PATTERN = re.compile(r"^docker-volumes-\d{8}-\d{6}$")
|
||||||
|
ARCHIVE_PATTERN = re.compile(r"^(?P<basename>.+)-(?P<ts>\d{8}-\d{6})\.tar\.gz$")
|
||||||
|
|
||||||
|
# ---------- utils ----------
|
||||||
|
|
||||||
|
def run(cmd: List[str], check: bool = False, capture_output: bool = True, text: bool = True) -> subprocess.CompletedProcess:
|
||||||
|
return subprocess.run(cmd, check=check, capture_output=capture_output, text=text)
|
||||||
|
|
||||||
|
def which(prog: str) -> bool:
|
||||||
|
from shutil import which as _w
|
||||||
|
return _w(prog) is not None
|
||||||
|
|
||||||
|
def fail(msg: str):
|
||||||
|
print(f"✗ {msg}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def ok(msg: str):
|
||||||
|
print(f"✓ {msg}")
|
||||||
|
|
||||||
|
def info(msg: str):
|
||||||
|
print(f"• {msg}")
|
||||||
|
|
||||||
|
def warn(msg: str):
|
||||||
|
print(f"! {msg}")
|
||||||
|
|
||||||
|
def yes_no(prompt: str, default: str = "n") -> bool:
|
||||||
|
default = default.lower()
|
||||||
|
hint = "[Y/n]" if default == "y" else "[y/N]"
|
||||||
|
while True:
|
||||||
|
resp = input(f"{prompt} {hint} ").strip().lower()
|
||||||
|
if not resp:
|
||||||
|
return default == "y"
|
||||||
|
if resp in ("y","yes","s","si","sí"):
|
||||||
|
return True
|
||||||
|
if resp in ("n","no"):
|
||||||
|
return False
|
||||||
|
print("Respuesta no reconocida. Responde 'y' o 'n'.")
|
||||||
|
|
||||||
|
# ---------- docker helpers ----------
|
||||||
|
|
||||||
|
def ensure_alpine_image():
|
||||||
|
try:
|
||||||
|
run(["docker", "image", "inspect", "alpine:latest"], check=True)
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
info("Descargando alpine:latest ...")
|
||||||
|
run(["docker", "pull", "alpine:latest"], check=True, capture_output=False, text=True)
|
||||||
|
|
||||||
|
def volume_exists(name: str) -> bool:
|
||||||
|
try:
|
||||||
|
run(["docker", "volume", "inspect", name], check=True)
|
||||||
|
return True
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def inspect_volume_labels(name: str) -> Dict[str, str]:
|
||||||
|
try:
|
||||||
|
cp = run(["docker", "volume", "inspect", name, "--format", "{{json .Labels}}"], check=True)
|
||||||
|
return json.loads(cp.stdout or "null") or {}
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def containers_using_volume(name: str) -> List[str]:
|
||||||
|
# docker ps soporta --filter volume=<name>
|
||||||
|
try:
|
||||||
|
cp = run(["docker", "ps", "-a", "--filter", f"volume={name}", "-q"], check=True)
|
||||||
|
return [l.strip() for l in cp.stdout.splitlines() if l.strip()]
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def stop_containers(ids: List[str]) -> None:
|
||||||
|
if not ids:
|
||||||
|
return
|
||||||
|
info("Deteniendo contenedores que usan el volumen...")
|
||||||
|
run(["docker", "stop"] + ids, check=False, capture_output=False)
|
||||||
|
|
||||||
|
def remove_containers(ids: List[str]) -> None:
|
||||||
|
if not ids:
|
||||||
|
return
|
||||||
|
info("Eliminando contenedores detenidos que usan el volumen...")
|
||||||
|
run(["docker", "rm"] + ids, check=False, capture_output=False)
|
||||||
|
|
||||||
|
def remove_volume(name: str) -> bool:
|
||||||
|
try:
|
||||||
|
run(["docker", "volume", "rm", "-f", name], check=True, capture_output=False)
|
||||||
|
return True
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
warn(f"No se pudo eliminar volumen '{name}': {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def create_volume(name: str, labels: Optional[Dict[str,str]] = None) -> bool:
|
||||||
|
cmd = ["docker", "volume", "create"]
|
||||||
|
if labels:
|
||||||
|
for k, v in labels.items():
|
||||||
|
cmd += ["--label", f"{k}={v}"]
|
||||||
|
cmd.append(name)
|
||||||
|
try:
|
||||||
|
run(cmd, check=True, capture_output=False)
|
||||||
|
return True
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
warn(f"Fallo creando volumen '{name}': {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def restore_into_volume(volume_name: str, backup_dir: pathlib.Path, archive_file: pathlib.Path) -> int:
|
||||||
|
bdir_abs = backup_dir.resolve()
|
||||||
|
docker_cmd = [
|
||||||
|
"docker", "run", "--rm",
|
||||||
|
"-v", f"{volume_name}:/volume",
|
||||||
|
"-v", f"{str(bdir_abs)}:/backup",
|
||||||
|
"alpine:latest",
|
||||||
|
"sh", "-lc",
|
||||||
|
f"tar xzf /backup/{shlex.quote(archive_file.name)} -C /volume"
|
||||||
|
]
|
||||||
|
proc = subprocess.run(docker_cmd)
|
||||||
|
return proc.returncode
|
||||||
|
|
||||||
|
# ---------- parsing helpers ----------
|
||||||
|
|
||||||
|
def find_backup_dirs(root: pathlib.Path) -> List[pathlib.Path]:
|
||||||
|
dirs = [p for p in root.iterdir() if p.is_dir() and BACKUP_DIR_PATTERN.match(p.name)]
|
||||||
|
dirs.sort(key=lambda p: p.stat().st_mtime, reverse=True)
|
||||||
|
return dirs
|
||||||
|
|
||||||
|
def find_archives(dirpath: pathlib.Path) -> List[pathlib.Path]:
|
||||||
|
files = [p for p in dirpath.iterdir() if p.is_file() and p.name.endswith(".tar.gz")]
|
||||||
|
files.sort(key=lambda p: p.name)
|
||||||
|
return files
|
||||||
|
|
||||||
|
def parse_archive_basename(archive_name: str) -> Optional[str]:
|
||||||
|
m = ARCHIVE_PATTERN.match(archive_name)
|
||||||
|
if not m:
|
||||||
|
return None
|
||||||
|
return m.group("basename")
|
||||||
|
|
||||||
|
# ---------- compose label helpers ----------
|
||||||
|
|
||||||
|
def derive_labels_auto(volume_name: str) -> Tuple[Optional[str], Optional[str]]:
|
||||||
|
"""
|
||||||
|
project = parte antes del primer '_' o '-'
|
||||||
|
short = resto luego del separador
|
||||||
|
"""
|
||||||
|
for sep in ("_", "-"):
|
||||||
|
if sep in volume_name:
|
||||||
|
idx = volume_name.find(sep)
|
||||||
|
return volume_name[:idx], volume_name[idx+1:]
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
def derive_labels_with_fixed_project(volume_name: str, project: str) -> Tuple[str, str]:
|
||||||
|
"""
|
||||||
|
Usa project fijo. Si volume_name empieza con '<project>_' o '<project>-', recorta.
|
||||||
|
"""
|
||||||
|
p = project
|
||||||
|
if volume_name.startswith(p + "_"):
|
||||||
|
return p, volume_name[len(p) + 1:]
|
||||||
|
if volume_name.startswith(p + "-"):
|
||||||
|
return p, volume_name[len(p) + 1:]
|
||||||
|
return p, volume_name
|
||||||
|
|
||||||
|
def labels_match_compose(name: str, project: str, short: str) -> bool:
|
||||||
|
labels = inspect_volume_labels(name)
|
||||||
|
return (
|
||||||
|
labels.get("com.docker.compose.project") == project and
|
||||||
|
labels.get("com.docker.compose.volume") == short
|
||||||
|
)
|
||||||
|
|
||||||
|
# ---------- UI flows ----------
|
||||||
|
|
||||||
|
def pick_backup_dir(dirs: List[pathlib.Path]) -> Optional[pathlib.Path]:
|
||||||
|
if not dirs:
|
||||||
|
warn("No se encontraron carpetas de backup 'docker-volumes-<timestamp>'.")
|
||||||
|
return None
|
||||||
|
print("\nCarpetas de backup encontradas:")
|
||||||
|
for i, d in enumerate(dirs, 1):
|
||||||
|
print(f" {i}) {d.name}")
|
||||||
|
while True:
|
||||||
|
sel = input("> Elige una carpeta (número) o Enter para cancelar: ").strip()
|
||||||
|
if not sel:
|
||||||
|
return None
|
||||||
|
if sel.isdigit() and 1 <= int(sel) <= len(dirs):
|
||||||
|
return dirs[int(sel) - 1]
|
||||||
|
print("Opción inválida.")
|
||||||
|
|
||||||
|
def pick_archives(files: List[pathlib.Path]) -> List[pathlib.Path]:
|
||||||
|
if not files:
|
||||||
|
warn("No hay archivos .tar.gz en esa carpeta.")
|
||||||
|
return []
|
||||||
|
print("\nBackups disponibles:")
|
||||||
|
for i, f in enumerate(files, 1):
|
||||||
|
base = parse_archive_basename(f.name) or f.name
|
||||||
|
print(f" {i}) {f.name} -> volumen: {base}")
|
||||||
|
print("\nOpciones:")
|
||||||
|
print(" a) Restaurar TODOS")
|
||||||
|
print(" s) Seleccionar algunos (ej: 1,3,5)")
|
||||||
|
while True:
|
||||||
|
sel = input("> Elige 'a' o 's': ").strip().lower()
|
||||||
|
if sel == "a":
|
||||||
|
return files
|
||||||
|
if sel == "s":
|
||||||
|
picks = input("> Números separados por coma: ").strip()
|
||||||
|
idxs = []
|
||||||
|
try:
|
||||||
|
for tok in picks.split(","):
|
||||||
|
tok = tok.strip()
|
||||||
|
if tok:
|
||||||
|
idx = int(tok)
|
||||||
|
idxs.append(idx - 1)
|
||||||
|
chosen = [files[i] for i in sorted(set(i for i in idxs if 0 <= i < len(files)))]
|
||||||
|
if chosen:
|
||||||
|
return chosen
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
print("Selección inválida.")
|
||||||
|
else:
|
||||||
|
print("Opción inválida.")
|
||||||
|
|
||||||
|
def pick_restore_mode() -> str:
|
||||||
|
print("\nModo de restauración:")
|
||||||
|
print(" 1) Tradicional (sin labels)")
|
||||||
|
print(" 2) Reconocido por Compose (aplica labels para evitar el warning)")
|
||||||
|
while True:
|
||||||
|
sel = input("> Elige 1 o 2: ").strip()
|
||||||
|
if sel in ("1", "2"):
|
||||||
|
return sel
|
||||||
|
print("Opción inválida.")
|
||||||
|
|
||||||
|
def confirm_overwrite(volume_name: str) -> bool:
|
||||||
|
return yes_no(f"El volumen '{volume_name}' ya existe. ¿Sobrescribir (recrear)?", default="n")
|
||||||
|
|
||||||
|
# ---------- restore flows ----------
|
||||||
|
|
||||||
|
def restore_traditional(backup_dir: pathlib.Path, archives: List[pathlib.Path]):
|
||||||
|
ensure_alpine_image()
|
||||||
|
print("\n=== Restauración TRADICIONAL ===\n")
|
||||||
|
for arch in archives:
|
||||||
|
vname = parse_archive_basename(arch.name)
|
||||||
|
if not vname:
|
||||||
|
warn(f"Nombre de backup no reconocible: {arch.name}, se omite.")
|
||||||
|
continue
|
||||||
|
info(f"Volumen: {vname}")
|
||||||
|
|
||||||
|
# Tradicional: no cambiamos labels; si existe, restauramos sobre volumen nuevo (recreándolo)
|
||||||
|
if volume_exists(vname):
|
||||||
|
# Intentar eliminar: si está en uso, ofrecer detener/remover contenedores
|
||||||
|
if not confirm_overwrite(vname):
|
||||||
|
info(" → Omitido (ya existe).")
|
||||||
|
continue
|
||||||
|
ids = containers_using_volume(vname)
|
||||||
|
if ids:
|
||||||
|
info(f"Contenedores que usan '{vname}': {', '.join(ids)}")
|
||||||
|
if yes_no("¿Detener y eliminar esos contenedores para continuar?", default="y"):
|
||||||
|
stop_containers(ids)
|
||||||
|
remove_containers(ids)
|
||||||
|
else:
|
||||||
|
warn(" → No se puede recrear el volumen en uso. Omitido.")
|
||||||
|
continue
|
||||||
|
if not remove_volume(vname):
|
||||||
|
warn(" → No se pudo eliminar el volumen. Omitido.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not create_volume(vname):
|
||||||
|
warn(" → No se pudo crear el volumen, se omite.")
|
||||||
|
continue
|
||||||
|
rc = restore_into_volume(vname, backup_dir, arch)
|
||||||
|
if rc == 0:
|
||||||
|
ok(" Restaurado.")
|
||||||
|
else:
|
||||||
|
warn(f" Falló la restauración (rc={rc}).")
|
||||||
|
|
||||||
|
def restore_with_compose_labels(backup_dir: pathlib.Path, archives: List[pathlib.Path]):
|
||||||
|
"""
|
||||||
|
Restaura creando volúmenes con labels de Compose para que NO aparezca el warning:
|
||||||
|
"volume ... already exists but was not created by Docker Compose..."
|
||||||
|
"""
|
||||||
|
ensure_alpine_image()
|
||||||
|
print("\n=== Restauración RECONOCIDA POR COMPOSE (con labels) ===\n")
|
||||||
|
print("Estrategia de etiquetado:")
|
||||||
|
print(" 1) Auto (project = prefijo de <vol> antes de '_' o '-', short = resto)")
|
||||||
|
print(" 2) Fijar un 'project' para todos (p. ej. suitecoffee, suitecoffee_dev, suitecoffee_prod)")
|
||||||
|
mode = ""
|
||||||
|
while mode not in ("1", "2"):
|
||||||
|
mode = input("> Elige 1 o 2: ").strip()
|
||||||
|
|
||||||
|
fixed_project = None
|
||||||
|
if mode == "2":
|
||||||
|
fixed_project = input("> Indica el 'project' de Compose (exacto): ").strip()
|
||||||
|
if not fixed_project:
|
||||||
|
warn("Project vacío, cancelado.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Previsualización de etiquetas
|
||||||
|
preview = []
|
||||||
|
for arch in archives:
|
||||||
|
vname = parse_archive_basename(arch.name)
|
||||||
|
if not vname:
|
||||||
|
continue
|
||||||
|
if mode == "1":
|
||||||
|
proj, short = derive_labels_auto(vname)
|
||||||
|
else:
|
||||||
|
proj, short = derive_labels_with_fixed_project(vname, fixed_project)
|
||||||
|
preview.append((arch, vname, proj, short))
|
||||||
|
|
||||||
|
print("\nVista previa de etiquetas (project / volume):")
|
||||||
|
for _, vname, proj, short in preview:
|
||||||
|
if proj and short:
|
||||||
|
print(f" {vname} → project='{proj}', volume='{short}'")
|
||||||
|
else:
|
||||||
|
print(f" {vname} → (no derivado; se pedirá manualmente)")
|
||||||
|
|
||||||
|
if not yes_no("\n¿Confirmar restauración con estas etiquetas?", default="y"):
|
||||||
|
warn("Cancelado por el usuario.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Restaurar con labels
|
||||||
|
for arch, vname, proj, short in preview:
|
||||||
|
# completar manual si falta
|
||||||
|
if not proj or not short:
|
||||||
|
print(f"\nDefinir etiquetas para: {vname}")
|
||||||
|
proj = input(" project = ").strip()
|
||||||
|
short = input(" volume = ").strip()
|
||||||
|
if not proj or not short:
|
||||||
|
warn(" → Etiquetas incompletas; se omite.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
info(f"\nVolumen: {vname} (labels: project='{proj}', volume='{short}')")
|
||||||
|
|
||||||
|
if volume_exists(vname):
|
||||||
|
# ¿ya tiene labels correctas? entonces solo restauramos datos sin recrear
|
||||||
|
if labels_match_compose(vname, proj, short):
|
||||||
|
info(" Volumen ya tiene labels de Compose correctas. Sobrescribiendo datos...")
|
||||||
|
rc = restore_into_volume(vname, backup_dir, arch)
|
||||||
|
if rc == 0:
|
||||||
|
ok(" Restaurado (labels ya correctas).")
|
||||||
|
else:
|
||||||
|
warn(f" Falló la restauración (rc={rc}).")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Pedir permiso para detener/eliminar contenedores y recrear volumen con labels correctas
|
||||||
|
if not yes_no(" El volumen existe sin labels correctas. ¿Detener/eliminar contenedores y recrearlo con labels para evitar el warning?", default="y"):
|
||||||
|
warn(" → Omitido (mantiene warning de Compose).")
|
||||||
|
continue
|
||||||
|
|
||||||
|
ids = containers_using_volume(vname)
|
||||||
|
if ids:
|
||||||
|
info(f" Contenedores que usan '{vname}': {', '.join(ids)}")
|
||||||
|
stop_containers(ids)
|
||||||
|
remove_containers(ids)
|
||||||
|
|
||||||
|
if not remove_volume(vname):
|
||||||
|
warn(" → No se pudo eliminar el volumen. Omitido.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
labels = {
|
||||||
|
"com.docker.compose.project": proj,
|
||||||
|
"com.docker.compose.volume": short,
|
||||||
|
}
|
||||||
|
if not create_volume(vname, labels=labels):
|
||||||
|
warn(" → No se pudo crear el volumen con labels. Omitido.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
rc = restore_into_volume(vname, backup_dir, arch)
|
||||||
|
if rc == 0:
|
||||||
|
ok(" Restaurado con labels de Compose (warning resuelto).")
|
||||||
|
else:
|
||||||
|
warn(f" Falló la restauración (rc={rc}).")
|
||||||
|
|
||||||
|
# ---------- main ----------
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if not which("docker"):
|
||||||
|
fail("No se encontró 'docker' en el PATH.")
|
||||||
|
try:
|
||||||
|
run(["docker", "version"], check=True)
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
fail("No se puede comunicar con el daemon de Docker. ¿Está corriendo?")
|
||||||
|
|
||||||
|
# Elegir carpeta docker-volumes-<ts>
|
||||||
|
dirs = [p for p in PROJECT_ROOT.iterdir() if p.is_dir() and BACKUP_DIR_PATTERN.match(p.name)]
|
||||||
|
dirs.sort(key=lambda p: p.stat().st_mtime, reverse=True)
|
||||||
|
if not dirs:
|
||||||
|
warn("No hay carpetas de backup 'docker-volumes-<timestamp>'.")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("\nCarpetas de backup encontradas:")
|
||||||
|
for i, d in enumerate(dirs, 1):
|
||||||
|
print(f" {i}) {d.name}")
|
||||||
|
chosen = None
|
||||||
|
while not chosen:
|
||||||
|
sel = input("> Elige una carpeta (número) o Enter para salir: ").strip()
|
||||||
|
if not sel:
|
||||||
|
warn("Cancelado."); return
|
||||||
|
if sel.isdigit() and 1 <= int(sel) <= len(dirs):
|
||||||
|
chosen = dirs[int(sel)-1]
|
||||||
|
else:
|
||||||
|
print("Opción inválida.")
|
||||||
|
|
||||||
|
# Archivos en carpeta
|
||||||
|
archives = [p for p in chosen.iterdir() if p.is_file() and p.name.endswith(".tar.gz")]
|
||||||
|
archives.sort(key=lambda p: p.name)
|
||||||
|
if not archives:
|
||||||
|
warn("No hay .tar.gz en esa carpeta."); return
|
||||||
|
|
||||||
|
print("\nBackups disponibles:")
|
||||||
|
for i, f in enumerate(archives, 1):
|
||||||
|
base = parse_archive_basename(f.name) or f.name
|
||||||
|
print(f" {i}) {f.name} -> volumen: {base}")
|
||||||
|
|
||||||
|
print("\nOpciones de selección:")
|
||||||
|
print(" a) Restaurar TODOS")
|
||||||
|
print(" s) Elegir algunos (ej: 1,3,5)")
|
||||||
|
selected: List[pathlib.Path] = []
|
||||||
|
while not selected:
|
||||||
|
mode = input("> Elige 'a' o 's': ").strip().lower()
|
||||||
|
if mode == "a":
|
||||||
|
selected = archives
|
||||||
|
elif mode == "s":
|
||||||
|
picks = input("> Números separados por coma: ").strip()
|
||||||
|
try:
|
||||||
|
idxs = [int(x.strip())-1 for x in picks.split(",") if x.strip()]
|
||||||
|
selected = [archives[i] for i in sorted(set(i for i in idxs if 0 <= i < len(archives)))]
|
||||||
|
except Exception:
|
||||||
|
selected = []
|
||||||
|
else:
|
||||||
|
print("Opción inválida.")
|
||||||
|
|
||||||
|
# Modo de restauración
|
||||||
|
choice = pick_restore_mode()
|
||||||
|
if choice == "1":
|
||||||
|
restore_traditional(chosen, selected)
|
||||||
|
else:
|
||||||
|
restore_with_compose_labels(chosen, selected)
|
||||||
|
|
||||||
|
ok("\nProceso finalizado.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
main()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n✓ Interrumpido por el usuario (Ctrl+C).")
|
||||||
@@ -1,22 +1,20 @@
|
|||||||
# Dockerfile.dev
|
# Dockerfile.dev
|
||||||
FROM node:20.17
|
FROM node:22.18
|
||||||
|
|
||||||
# Definir variables de entorno con valores predeterminados
|
# Definir variables de entorno con valores predeterminados
|
||||||
ARG NODE_ENV=development
|
# ARG NODE_ENV=production
|
||||||
ARG PORT=3000
|
# ARG PORT=3000
|
||||||
|
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Copia archivos de configuración primero para aprovechar el cache
|
# Copia archivos de configuración primero para aprovechar el cache
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
|
|
||||||
# Instala dependencias
|
# Instala dependencias
|
||||||
RUN npm i express pg dotenv cors
|
RUN npm i
|
||||||
RUN npm i --save-dev nodemon
|
|
||||||
|
|
||||||
# Copia el resto de la app
|
# Copia el resto de la app
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Expone el puerto
|
CMD ["npm", "run", "start"]
|
||||||
EXPOSE 3000
|
|
||||||
|
|
||||||
# Usa nodemon para hot reload si lo tenés
|
|
||||||
CMD ["npm", "run", "dev"]
|
|
||||||
Generated
+164
-21
@@ -1,19 +1,21 @@
|
|||||||
{
|
{
|
||||||
"name": "app",
|
"name": "aplication",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "app",
|
"name": "aplication",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"chalk": "^5.6.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^17.2.1",
|
"dotenv": "^17.2.1",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"express-ejs-layouts": "^2.5.1",
|
"express-ejs-layouts": "^2.5.1",
|
||||||
"pg": "^8.16.3"
|
"pg": "^8.16.3",
|
||||||
|
"pg-format": "^1.0.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"cross-env": "^10.0.0",
|
"cross-env": "^10.0.0",
|
||||||
@@ -24,12 +26,14 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz",
|
||||||
"integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==",
|
"integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/accepts": {
|
"node_modules/accepts": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
|
||||||
"integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
|
"integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mime-types": "^3.0.0",
|
"mime-types": "^3.0.0",
|
||||||
"negotiator": "^1.0.0"
|
"negotiator": "^1.0.0"
|
||||||
@@ -43,6 +47,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"normalize-path": "^3.0.0",
|
"normalize-path": "^3.0.0",
|
||||||
"picomatch": "^2.0.4"
|
"picomatch": "^2.0.4"
|
||||||
@@ -55,13 +60,15 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/binary-extensions": {
|
"node_modules/binary-extensions": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||||
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
|
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
},
|
},
|
||||||
@@ -73,6 +80,7 @@
|
|||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
|
||||||
"integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
|
"integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bytes": "^3.1.2",
|
"bytes": "^3.1.2",
|
||||||
"content-type": "^1.0.5",
|
"content-type": "^1.0.5",
|
||||||
@@ -93,6 +101,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
@@ -103,6 +112,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fill-range": "^7.1.1"
|
"fill-range": "^7.1.1"
|
||||||
},
|
},
|
||||||
@@ -114,6 +124,7 @@
|
|||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||||
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
@@ -122,6 +133,7 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
"function-bind": "^1.1.2"
|
"function-bind": "^1.1.2"
|
||||||
@@ -134,6 +146,7 @@
|
|||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
||||||
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind-apply-helpers": "^1.0.2",
|
"call-bind-apply-helpers": "^1.0.2",
|
||||||
"get-intrinsic": "^1.3.0"
|
"get-intrinsic": "^1.3.0"
|
||||||
@@ -145,11 +158,24 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/chalk": {
|
||||||
|
"version": "5.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.0.tgz",
|
||||||
|
"integrity": "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/chokidar": {
|
"node_modules/chokidar": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||||
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"anymatch": "~3.1.2",
|
"anymatch": "~3.1.2",
|
||||||
"braces": "~3.0.2",
|
"braces": "~3.0.2",
|
||||||
@@ -173,12 +199,14 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/content-disposition": {
|
"node_modules/content-disposition": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
|
||||||
"integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
|
"integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"safe-buffer": "5.2.1"
|
"safe-buffer": "5.2.1"
|
||||||
},
|
},
|
||||||
@@ -190,6 +218,7 @@
|
|||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
||||||
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
@@ -198,6 +227,7 @@
|
|||||||
"version": "0.7.2",
|
"version": "0.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||||
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
@@ -206,6 +236,7 @@
|
|||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
|
||||||
"integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
|
"integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.6.0"
|
"node": ">=6.6.0"
|
||||||
}
|
}
|
||||||
@@ -214,6 +245,7 @@
|
|||||||
"version": "2.8.5",
|
"version": "2.8.5",
|
||||||
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
||||||
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"object-assign": "^4",
|
"object-assign": "^4",
|
||||||
"vary": "^1"
|
"vary": "^1"
|
||||||
@@ -227,6 +259,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.0.0.tgz",
|
||||||
"integrity": "sha512-aU8qlEK/nHYtVuN4p7UQgAwVljzMg8hB4YK5ThRqD2l/ziSnryncPNn7bMLt5cFYsKVKBh8HqLqyCoTupEUu7Q==",
|
"integrity": "sha512-aU8qlEK/nHYtVuN4p7UQgAwVljzMg8hB4YK5ThRqD2l/ziSnryncPNn7bMLt5cFYsKVKBh8HqLqyCoTupEUu7Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@epic-web/invariant": "^1.0.0",
|
"@epic-web/invariant": "^1.0.0",
|
||||||
"cross-spawn": "^7.0.6"
|
"cross-spawn": "^7.0.6"
|
||||||
@@ -244,6 +277,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"path-key": "^3.1.0",
|
"path-key": "^3.1.0",
|
||||||
"shebang-command": "^2.0.0",
|
"shebang-command": "^2.0.0",
|
||||||
@@ -257,6 +291,7 @@
|
|||||||
"version": "4.4.1",
|
"version": "4.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
||||||
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": "^2.1.3"
|
"ms": "^2.1.3"
|
||||||
},
|
},
|
||||||
@@ -273,6 +308,7 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||||
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
@@ -281,6 +317,7 @@
|
|||||||
"version": "17.2.1",
|
"version": "17.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz",
|
||||||
"integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==",
|
"integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -292,6 +329,7 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind-apply-helpers": "^1.0.1",
|
"call-bind-apply-helpers": "^1.0.1",
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
@@ -304,12 +342,14 @@
|
|||||||
"node_modules/ee-first": {
|
"node_modules/ee-first": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/encodeurl": {
|
"node_modules/encodeurl": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
||||||
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
@@ -318,6 +358,7 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
@@ -326,6 +367,7 @@
|
|||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
@@ -334,6 +376,7 @@
|
|||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0"
|
"es-errors": "^1.3.0"
|
||||||
},
|
},
|
||||||
@@ -344,12 +387,14 @@
|
|||||||
"node_modules/escape-html": {
|
"node_modules/escape-html": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
|
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/etag": {
|
"node_modules/etag": {
|
||||||
"version": "1.8.1",
|
"version": "1.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||||
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
|
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
@@ -358,6 +403,7 @@
|
|||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
|
||||||
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
|
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"accepts": "^2.0.0",
|
"accepts": "^2.0.0",
|
||||||
"body-parser": "^2.2.0",
|
"body-parser": "^2.2.0",
|
||||||
@@ -405,6 +451,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"to-regex-range": "^5.0.1"
|
"to-regex-range": "^5.0.1"
|
||||||
},
|
},
|
||||||
@@ -416,6 +463,7 @@
|
|||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
|
||||||
"integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
|
"integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "^4.4.0",
|
"debug": "^4.4.0",
|
||||||
"encodeurl": "^2.0.0",
|
"encodeurl": "^2.0.0",
|
||||||
@@ -432,6 +480,7 @@
|
|||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||||
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
|
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
@@ -440,6 +489,7 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
|
||||||
"integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
|
"integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
@@ -450,6 +500,7 @@
|
|||||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
@@ -462,6 +513,7 @@
|
|||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||||
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
@@ -470,6 +522,7 @@
|
|||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind-apply-helpers": "^1.0.2",
|
"call-bind-apply-helpers": "^1.0.2",
|
||||||
"es-define-property": "^1.0.1",
|
"es-define-property": "^1.0.1",
|
||||||
@@ -493,6 +546,7 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dunder-proto": "^1.0.1",
|
"dunder-proto": "^1.0.1",
|
||||||
"es-object-atoms": "^1.0.0"
|
"es-object-atoms": "^1.0.0"
|
||||||
@@ -506,6 +560,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-glob": "^4.0.1"
|
"is-glob": "^4.0.1"
|
||||||
},
|
},
|
||||||
@@ -517,6 +572,7 @@
|
|||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
},
|
},
|
||||||
@@ -529,6 +585,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
@@ -537,6 +594,7 @@
|
|||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
},
|
},
|
||||||
@@ -548,6 +606,7 @@
|
|||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"function-bind": "^1.1.2"
|
"function-bind": "^1.1.2"
|
||||||
},
|
},
|
||||||
@@ -559,6 +618,7 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||||
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
|
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"depd": "2.0.0",
|
"depd": "2.0.0",
|
||||||
"inherits": "2.0.4",
|
"inherits": "2.0.4",
|
||||||
@@ -574,6 +634,7 @@
|
|||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||||
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
@@ -582,6 +643,7 @@
|
|||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||||
},
|
},
|
||||||
@@ -593,17 +655,20 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
|
||||||
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
|
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/inherits": {
|
"node_modules/inherits": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||||
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/ipaddr.js": {
|
"node_modules/ipaddr.js": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||||
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
@@ -613,6 +678,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"binary-extensions": "^2.0.0"
|
"binary-extensions": "^2.0.0"
|
||||||
},
|
},
|
||||||
@@ -625,6 +691,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -634,6 +701,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-extglob": "^2.1.1"
|
"is-extglob": "^2.1.1"
|
||||||
},
|
},
|
||||||
@@ -646,6 +714,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.12.0"
|
"node": ">=0.12.0"
|
||||||
}
|
}
|
||||||
@@ -653,18 +722,21 @@
|
|||||||
"node_modules/is-promise": {
|
"node_modules/is-promise": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
|
||||||
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="
|
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/isexe": {
|
"node_modules/isexe": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/math-intrinsics": {
|
"node_modules/math-intrinsics": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
@@ -673,6 +745,7 @@
|
|||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
||||||
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
|
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
@@ -681,6 +754,7 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
|
||||||
"integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
|
"integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
},
|
},
|
||||||
@@ -692,6 +766,7 @@
|
|||||||
"version": "1.54.0",
|
"version": "1.54.0",
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
||||||
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
@@ -700,6 +775,7 @@
|
|||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
|
||||||
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
|
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mime-db": "^1.54.0"
|
"mime-db": "^1.54.0"
|
||||||
},
|
},
|
||||||
@@ -712,6 +788,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
},
|
},
|
||||||
@@ -722,12 +799,14 @@
|
|||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/negotiator": {
|
"node_modules/negotiator": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
||||||
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
|
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
@@ -737,6 +816,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz",
|
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz",
|
||||||
"integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==",
|
"integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": "^3.5.2",
|
"chokidar": "^3.5.2",
|
||||||
"debug": "^4",
|
"debug": "^4",
|
||||||
@@ -765,6 +845,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -773,6 +854,7 @@
|
|||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -781,6 +863,7 @@
|
|||||||
"version": "1.13.4",
|
"version": "1.13.4",
|
||||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||||
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
|
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
},
|
},
|
||||||
@@ -792,6 +875,7 @@
|
|||||||
"version": "2.4.1",
|
"version": "2.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||||
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ee-first": "1.1.1"
|
"ee-first": "1.1.1"
|
||||||
},
|
},
|
||||||
@@ -803,6 +887,7 @@
|
|||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||||
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
@@ -811,6 +896,7 @@
|
|||||||
"version": "1.3.3",
|
"version": "1.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||||
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
@@ -820,6 +906,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
@@ -828,6 +915,7 @@
|
|||||||
"version": "8.2.0",
|
"version": "8.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
|
||||||
"integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
|
"integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16"
|
"node": ">=16"
|
||||||
}
|
}
|
||||||
@@ -836,6 +924,7 @@
|
|||||||
"version": "8.16.3",
|
"version": "8.16.3",
|
||||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz",
|
"resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz",
|
||||||
"integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==",
|
"integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pg-connection-string": "^2.9.1",
|
"pg-connection-string": "^2.9.1",
|
||||||
"pg-pool": "^3.10.1",
|
"pg-pool": "^3.10.1",
|
||||||
@@ -862,17 +951,29 @@
|
|||||||
"version": "1.2.7",
|
"version": "1.2.7",
|
||||||
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz",
|
||||||
"integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==",
|
"integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==",
|
||||||
|
"license": "MIT",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"node_modules/pg-connection-string": {
|
"node_modules/pg-connection-string": {
|
||||||
"version": "2.9.1",
|
"version": "2.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz",
|
||||||
"integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w=="
|
"integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/pg-format": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-format/-/pg-format-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-YyKEF78pEA6wwTAqOUaHIN/rWpfzzIuMh9KdAhc3rSLQ/7zkRFcCgYBAEGatDstLyZw4g0s9SNICmaTGnBVeyw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/pg-int8": {
|
"node_modules/pg-int8": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
||||||
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
|
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
|
||||||
|
"license": "ISC",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4.0.0"
|
"node": ">=4.0.0"
|
||||||
}
|
}
|
||||||
@@ -881,6 +982,7 @@
|
|||||||
"version": "3.10.1",
|
"version": "3.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz",
|
||||||
"integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==",
|
"integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==",
|
||||||
|
"license": "MIT",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"pg": ">=8.0"
|
"pg": ">=8.0"
|
||||||
}
|
}
|
||||||
@@ -888,12 +990,14 @@
|
|||||||
"node_modules/pg-protocol": {
|
"node_modules/pg-protocol": {
|
||||||
"version": "1.10.3",
|
"version": "1.10.3",
|
||||||
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz",
|
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz",
|
||||||
"integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ=="
|
"integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/pg-types": {
|
"node_modules/pg-types": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
|
||||||
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
|
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pg-int8": "1.0.1",
|
"pg-int8": "1.0.1",
|
||||||
"postgres-array": "~2.0.0",
|
"postgres-array": "~2.0.0",
|
||||||
@@ -909,6 +1013,7 @@
|
|||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
|
||||||
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
|
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"split2": "^4.1.0"
|
"split2": "^4.1.0"
|
||||||
}
|
}
|
||||||
@@ -918,6 +1023,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.6"
|
"node": ">=8.6"
|
||||||
},
|
},
|
||||||
@@ -929,6 +1035,7 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||||
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
|
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
@@ -937,6 +1044,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
|
||||||
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
|
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -945,6 +1053,7 @@
|
|||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
|
||||||
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
|
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -953,6 +1062,7 @@
|
|||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
|
||||||
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
|
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"xtend": "^4.0.0"
|
"xtend": "^4.0.0"
|
||||||
},
|
},
|
||||||
@@ -964,6 +1074,7 @@
|
|||||||
"version": "2.0.7",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||||
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"forwarded": "0.2.0",
|
"forwarded": "0.2.0",
|
||||||
"ipaddr.js": "1.9.1"
|
"ipaddr.js": "1.9.1"
|
||||||
@@ -976,12 +1087,14 @@
|
|||||||
"version": "1.1.8",
|
"version": "1.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
|
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
|
||||||
"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
|
"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/qs": {
|
"node_modules/qs": {
|
||||||
"version": "6.14.0",
|
"version": "6.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
|
||||||
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
|
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"side-channel": "^1.1.0"
|
"side-channel": "^1.1.0"
|
||||||
},
|
},
|
||||||
@@ -996,6 +1109,7 @@
|
|||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||||
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
|
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
@@ -1004,6 +1118,7 @@
|
|||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
|
||||||
"integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
|
"integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bytes": "3.1.2",
|
"bytes": "3.1.2",
|
||||||
"http-errors": "2.0.0",
|
"http-errors": "2.0.0",
|
||||||
@@ -1019,6 +1134,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"picomatch": "^2.2.1"
|
"picomatch": "^2.2.1"
|
||||||
},
|
},
|
||||||
@@ -1030,6 +1146,7 @@
|
|||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
|
||||||
"integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
|
"integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "^4.4.0",
|
"debug": "^4.4.0",
|
||||||
"depd": "^2.0.0",
|
"depd": "^2.0.0",
|
||||||
@@ -1058,18 +1175,21 @@
|
|||||||
"type": "consulting",
|
"type": "consulting",
|
||||||
"url": "https://feross.org/support"
|
"url": "https://feross.org/support"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/safer-buffer": {
|
"node_modules/safer-buffer": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
"version": "7.7.2",
|
"version": "7.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
"bin": {
|
"bin": {
|
||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
},
|
},
|
||||||
@@ -1081,6 +1201,7 @@
|
|||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
|
||||||
"integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
|
"integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "^4.3.5",
|
"debug": "^4.3.5",
|
||||||
"encodeurl": "^2.0.0",
|
"encodeurl": "^2.0.0",
|
||||||
@@ -1102,6 +1223,7 @@
|
|||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
|
||||||
"integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
|
"integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"encodeurl": "^2.0.0",
|
"encodeurl": "^2.0.0",
|
||||||
"escape-html": "^1.0.3",
|
"escape-html": "^1.0.3",
|
||||||
@@ -1115,13 +1237,15 @@
|
|||||||
"node_modules/setprototypeof": {
|
"node_modules/setprototypeof": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
|
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
||||||
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/shebang-command": {
|
"node_modules/shebang-command": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"shebang-regex": "^3.0.0"
|
"shebang-regex": "^3.0.0"
|
||||||
},
|
},
|
||||||
@@ -1134,6 +1258,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
@@ -1142,6 +1267,7 @@
|
|||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
||||||
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
|
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
"object-inspect": "^1.13.3",
|
"object-inspect": "^1.13.3",
|
||||||
@@ -1160,6 +1286,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
||||||
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
"object-inspect": "^1.13.3"
|
"object-inspect": "^1.13.3"
|
||||||
@@ -1175,6 +1302,7 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
|
||||||
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
|
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bound": "^1.0.2",
|
"call-bound": "^1.0.2",
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
@@ -1192,6 +1320,7 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
|
||||||
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
|
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bound": "^1.0.2",
|
"call-bound": "^1.0.2",
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
@@ -1211,6 +1340,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
|
||||||
"integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
|
"integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"semver": "^7.5.3"
|
"semver": "^7.5.3"
|
||||||
},
|
},
|
||||||
@@ -1222,6 +1352,7 @@
|
|||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||||
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
||||||
|
"license": "ISC",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10.x"
|
"node": ">= 10.x"
|
||||||
}
|
}
|
||||||
@@ -1230,6 +1361,7 @@
|
|||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
||||||
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
|
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
@@ -1239,6 +1371,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"has-flag": "^3.0.0"
|
"has-flag": "^3.0.0"
|
||||||
},
|
},
|
||||||
@@ -1251,6 +1384,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-number": "^7.0.0"
|
"is-number": "^7.0.0"
|
||||||
},
|
},
|
||||||
@@ -1262,6 +1396,7 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||||
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.6"
|
"node": ">=0.6"
|
||||||
}
|
}
|
||||||
@@ -1271,6 +1406,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz",
|
||||||
"integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
|
"integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
"bin": {
|
"bin": {
|
||||||
"nodetouch": "bin/nodetouch.js"
|
"nodetouch": "bin/nodetouch.js"
|
||||||
}
|
}
|
||||||
@@ -1279,6 +1415,7 @@
|
|||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
|
||||||
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
|
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"content-type": "^1.0.5",
|
"content-type": "^1.0.5",
|
||||||
"media-typer": "^1.1.0",
|
"media-typer": "^1.1.0",
|
||||||
@@ -1292,12 +1429,14 @@
|
|||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
|
||||||
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
|
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/unpipe": {
|
"node_modules/unpipe": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||||
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
@@ -1306,6 +1445,7 @@
|
|||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
@@ -1315,6 +1455,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"isexe": "^2.0.0"
|
"isexe": "^2.0.0"
|
||||||
},
|
},
|
||||||
@@ -1328,12 +1469,14 @@
|
|||||||
"node_modules/wrappy": {
|
"node_modules/wrappy": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||||
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/xtend": {
|
"node_modules/xtend": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.4"
|
"node": ">=0.4"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "app",
|
"name": "aplication",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"main": "index.js",
|
"main": "src/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "NODE_ENV=production node ./src/index.js",
|
"start": "NODE_ENV=production node ./src/index.js",
|
||||||
"dev": "NODE_ENV=development node ./src/index.js",
|
"dev": "NODE_ENV=development npx nodemon ./src/index.js",
|
||||||
"test": "NODE_ENV=stage node ./src/index.js"
|
"test": "NODE_ENV=stage node ./src/index.js"
|
||||||
},
|
},
|
||||||
"author": "Mateo Saldain",
|
"author": "Mateo Saldain",
|
||||||
@@ -15,11 +15,13 @@
|
|||||||
"nodemon": "^3.1.10"
|
"nodemon": "^3.1.10"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"chalk": "^5.6.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^17.2.1",
|
"dotenv": "^17.2.1",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"express-ejs-layouts": "^2.5.1",
|
"express-ejs-layouts": "^2.5.1",
|
||||||
"pg": "^8.16.3"
|
"pg": "^8.16.3",
|
||||||
|
"pg-format": "^1.0.4"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"description": ""
|
"description": ""
|
||||||
|
|||||||
+22
-12
@@ -1,4 +1,5 @@
|
|||||||
// app/src/index.js
|
// app/src/index.js
|
||||||
|
import chalk from 'chalk'; // Colores!
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import expressLayouts from 'express-ejs-layouts';
|
import expressLayouts from 'express-ejs-layouts';
|
||||||
import cors from 'cors';
|
import cors from 'cors';
|
||||||
@@ -17,13 +18,13 @@ import dotenv, { config } from 'dotenv';
|
|||||||
try {
|
try {
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
dotenv.config({ path: path.resolve(__dirname, '../.env.development' )});
|
dotenv.config({ path: path.resolve(__dirname, '../.env.development' )});
|
||||||
console.log("Activando entorno de -> development");
|
console.log(`Activando entorno de ->${chalk.green(` DEVELOPMENT `)}`);
|
||||||
} else if (process.env.NODE_ENV === 'stage') {
|
} else if (process.env.NODE_ENV === 'stage') {
|
||||||
dotenv.config({ path: path.resolve(__dirname, '../.env.test' )});
|
dotenv.config({ path: path.resolve(__dirname, '../.env.test' )});
|
||||||
console.log("Activando entorno de -> testing");
|
console.log(`Activando entorno de ->${chalk.yellow(` TESTING `)}`);
|
||||||
} else if (process.env.NODE_ENV === 'production') {
|
} else if (process.env.NODE_ENV === 'production') {
|
||||||
dotenv.config({ path: path.resolve(__dirname, '../.env' )});
|
dotenv.config({ path: path.resolve(__dirname, '../.env.production' )});
|
||||||
console.log("Activando entorno de -> producción");
|
console.log(`Activando entorno de ->${chalk.red(` PRODUCTION `)}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("A ocurrido un error al seleccionar el entorno. \nError: " + error);
|
console.log("A ocurrido un error al seleccionar el entorno. \nError: " + error);
|
||||||
@@ -33,6 +34,7 @@ try {
|
|||||||
const app = express();
|
const app = express();
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
app.use(express.static(path.join(__dirname, 'pages')));
|
||||||
|
|
||||||
// Configuración de conexión PostgreSQL
|
// Configuración de conexión PostgreSQL
|
||||||
|
|
||||||
@@ -51,22 +53,19 @@ async function verificarConexion() {
|
|||||||
try {
|
try {
|
||||||
const client = await pool.connect();
|
const client = await pool.connect();
|
||||||
const res = await client.query('SELECT NOW() AS hora');
|
const res = await client.query('SELECT NOW() AS hora');
|
||||||
console.log('Conexión con la base de datos fue exitosa.');
|
console.log(`\nConexión con la base de datos ${chalk.green(process.env.DB_NAME)} fue exitosa.`);
|
||||||
console.log('Fecha y hora actual de la base de datos:', res.rows[0].hora);
|
console.log('Fecha y hora actual de la base de datos:', res.rows[0].hora);
|
||||||
client.release(); // liberar el cliente de nuevo al pool
|
client.release(); // liberar el cliente de nuevo al pool
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error al conectar con la base de datos al iniciar:', error.message);
|
console.error('Error al conectar con la base de datos al iniciar:', error.message);
|
||||||
|
console.error(`Troubleshooting:\n1. Compruebe que las bases de datos se iniciaron correctamente.\n2. Verifique las credenciales y puertos de acceso a la base de datos.\n3. Si está conectandose a una base de datos externa a localhost, verifique las reglas del firewal de entrada y salida de ambos dispositivos.`);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// === Servir páginas estáticas ===
|
// === Servir páginas estáticas ===
|
||||||
app.use('/pages', express.static(path.join(__dirname, 'pages')));
|
|
||||||
|
|
||||||
|
|
||||||
// Rutas de conveniencia para abrir cada página rápido:
|
|
||||||
// (Opcional: puedes usar directamente /pages/roles.html, etc.)
|
|
||||||
app.get('/roles', (req, res) => res.sendFile(path.join(__dirname, 'pages', 'roles.html')));
|
app.get('/roles', (req, res) => res.sendFile(path.join(__dirname, 'pages', 'roles.html')));
|
||||||
app.get('/usuarios', (req, res) => res.sendFile(path.join(__dirname, 'pages', 'usuarios.html')));
|
app.get('/usuarios', (req, res) => res.sendFile(path.join(__dirname, 'pages', 'usuarios.html')));
|
||||||
app.get('/categorias',(req, res) => res.sendFile(path.join(__dirname, 'pages', 'categorias.html')));
|
app.get('/categorias',(req, res) => res.sendFile(path.join(__dirname, 'pages', 'categorias.html')));
|
||||||
@@ -231,10 +230,21 @@ async function q(text, params) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Colores personalizados
|
||||||
|
let primaryColor = chalk.hex('#'+`${process.env.COL_PRI}`);
|
||||||
|
let secondaryColor = chalk.hex('#'+`${process.env.COL_SEC}`);
|
||||||
|
// let backgroundColor = chalk.hex('#'+`${process.env.COL_BG}`);
|
||||||
|
|
||||||
|
|
||||||
app.use(expressLayouts);
|
app.use(expressLayouts);
|
||||||
// Iniciar servidor
|
// Iniciar servidor
|
||||||
app.listen( process.env.PORT, () => {
|
app.listen( process.env.PORT, () => {
|
||||||
console.log(`Servidor corriendo en http://localhost:${process.env.PORT}`);
|
console.log(`Servidor de ${chalk.red('aplicación')} de ${secondaryColor('SuiteCoffee')} corriendo en ${chalk.yellow(`http://localhost:${process.env.PORT}\n`)}` );
|
||||||
console.log('Estableciendo conexión con la db...');
|
console.log(chalk.grey(`Comprobando accesibilidad a la db ${chalk.green(process.env.DB_NAME)} del host ${chalk.white(`${process.env.DB_HOST}`)} ...`));
|
||||||
verificarConexion();
|
verificarConexion();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/health", async (req, res) => {
|
||||||
|
// Podés chequear DB aquí. 200 = healthy; 503 = not ready.
|
||||||
|
res.status(200).json({ status: "ok" });
|
||||||
});
|
});
|
||||||
@@ -1,22 +1,20 @@
|
|||||||
# Dockerfile.dev
|
# Dockerfile.dev
|
||||||
FROM node:20.17
|
FROM node:22.18
|
||||||
|
|
||||||
# Definir variables de entorno con valores predeterminados
|
# Definir variables de entorno con valores predeterminados
|
||||||
ARG NODE_ENV=development
|
# ARG NODE_ENV=production
|
||||||
ARG PORT=3000
|
# ARG PORT=4000
|
||||||
|
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Copia archivos de configuración primero para aprovechar el cache
|
# Copia archivos de configuración primero para aprovechar el cache
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
|
|
||||||
# Instala dependencias
|
# Instala dependencias
|
||||||
RUN npm i express pg dotenv cors
|
RUN npm i
|
||||||
RUN npm i --save-dev nodemon
|
|
||||||
|
|
||||||
# Copia el resto de la app
|
# Copia el resto de la app
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Expone el puerto
|
CMD ["npm", "run", "start"]
|
||||||
EXPOSE 3000
|
|
||||||
|
|
||||||
# Usa nodemon para hot reload si lo tenés
|
|
||||||
CMD ["npm", "run", "dev"]
|
|
||||||
Generated
+534
-8
@@ -1,19 +1,22 @@
|
|||||||
{
|
{
|
||||||
"name": "auth",
|
"name": "authentication",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "auth",
|
"name": "authentication",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"bcrypt": "^5.1.1",
|
||||||
|
"chalk": "^5.6.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^17.2.1",
|
"dotenv": "^17.2.1",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"express-ejs-layouts": "^2.5.1",
|
"express-ejs-layouts": "^2.5.1",
|
||||||
"pg": "^8.16.3"
|
"pg": "^8.16.3",
|
||||||
|
"pg-format": "^1.0.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"cross-env": "^10.0.0",
|
"cross-env": "^10.0.0",
|
||||||
@@ -27,6 +30,32 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@mapbox/node-pre-gyp": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"detect-libc": "^2.0.0",
|
||||||
|
"https-proxy-agent": "^5.0.0",
|
||||||
|
"make-dir": "^3.1.0",
|
||||||
|
"node-fetch": "^2.6.7",
|
||||||
|
"nopt": "^5.0.0",
|
||||||
|
"npmlog": "^5.0.1",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"semver": "^7.3.5",
|
||||||
|
"tar": "^6.1.11"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"node-pre-gyp": "bin/node-pre-gyp"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/abbrev": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/accepts": {
|
"node_modules/accepts": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
|
||||||
@@ -40,6 +69,27 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/agent-base": {
|
||||||
|
"version": "6.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
||||||
|
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ansi-regex": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/anymatch": {
|
"node_modules/anymatch": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||||
@@ -54,13 +104,46 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/aproba": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/are-we-there-yet": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
|
||||||
|
"deprecated": "This package is no longer supported.",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"delegates": "^1.0.0",
|
||||||
|
"readable-stream": "^3.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/bcrypt": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@mapbox/node-pre-gyp": "^1.0.11",
|
||||||
|
"node-addon-api": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/binary-extensions": {
|
"node_modules/binary-extensions": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||||
@@ -98,7 +181,6 @@
|
|||||||
"version": "1.1.12",
|
"version": "1.1.12",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
@@ -156,6 +238,18 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/chalk": {
|
||||||
|
"version": "5.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.0.tgz",
|
||||||
|
"integrity": "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/chokidar": {
|
"node_modules/chokidar": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||||
@@ -181,13 +275,36 @@
|
|||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/chownr": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/color-support": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"bin": {
|
||||||
|
"color-support": "bin.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/concat-map": {
|
"node_modules/concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/console-control-strings": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/content-disposition": {
|
"node_modules/content-disposition": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
|
||||||
@@ -290,6 +407,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/delegates": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/depd": {
|
"node_modules/depd": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||||
@@ -299,6 +422,15 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/detect-libc": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/dotenv": {
|
"node_modules/dotenv": {
|
||||||
"version": "17.2.1",
|
"version": "17.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz",
|
||||||
@@ -331,6 +463,12 @@
|
|||||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
|
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/emoji-regex": {
|
||||||
|
"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"
|
||||||
|
},
|
||||||
"node_modules/encodeurl": {
|
"node_modules/encodeurl": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
||||||
@@ -480,6 +618,36 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fs-minipass": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"minipass": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fs-minipass/node_modules/minipass": {
|
||||||
|
"version": "3.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
|
||||||
|
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fs.realpath": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/fsevents": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
@@ -504,6 +672,27 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/gauge": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
|
||||||
|
"deprecated": "This package is no longer supported.",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"aproba": "^1.0.3 || ^2.0.0",
|
||||||
|
"color-support": "^1.1.2",
|
||||||
|
"console-control-strings": "^1.0.0",
|
||||||
|
"has-unicode": "^2.0.1",
|
||||||
|
"object-assign": "^4.1.1",
|
||||||
|
"signal-exit": "^3.0.0",
|
||||||
|
"string-width": "^4.2.3",
|
||||||
|
"strip-ansi": "^6.0.1",
|
||||||
|
"wide-align": "^1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/get-intrinsic": {
|
"node_modules/get-intrinsic": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||||
@@ -541,6 +730,27 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/glob": {
|
||||||
|
"version": "7.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
|
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||||
|
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.1.1",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/glob-parent": {
|
"node_modules/glob-parent": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||||
@@ -588,6 +798,12 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/has-unicode": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/hasown": {
|
"node_modules/hasown": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||||
@@ -625,6 +841,19 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/https-proxy-agent": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"agent-base": "6",
|
||||||
|
"debug": "4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/iconv-lite": {
|
"node_modules/iconv-lite": {
|
||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||||
@@ -644,6 +873,17 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/inflight": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||||
|
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/inherits": {
|
"node_modules/inherits": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
@@ -682,6 +922,15 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-fullwidth-code-point": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"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",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/is-glob": {
|
"node_modules/is-glob": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||||
@@ -718,6 +967,30 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/make-dir": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"semver": "^6.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/make-dir/node_modules/semver": {
|
||||||
|
"version": "6.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||||
|
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/math-intrinsics": {
|
"node_modules/math-intrinsics": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||||
@@ -773,7 +1046,6 @@
|
|||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
@@ -782,6 +1054,52 @@
|
|||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/minipass": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/minizlib": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"minipass": "^3.0.0",
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/minizlib/node_modules/minipass": {
|
||||||
|
"version": "3.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
|
||||||
|
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mkdirp": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"mkdirp": "bin/cmd.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
@@ -797,6 +1115,32 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-addon-api": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/node-fetch": {
|
||||||
|
"version": "2.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||||
|
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"whatwg-url": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "4.x || >=6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"encoding": "^0.1.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"encoding": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/nodemon": {
|
"node_modules/nodemon": {
|
||||||
"version": "3.1.10",
|
"version": "3.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz",
|
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz",
|
||||||
@@ -826,6 +1170,21 @@
|
|||||||
"url": "https://opencollective.com/nodemon"
|
"url": "https://opencollective.com/nodemon"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/nopt": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"abbrev": "1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"nopt": "bin/nopt.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/normalize-path": {
|
"node_modules/normalize-path": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||||
@@ -836,6 +1195,19 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/npmlog": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
|
||||||
|
"deprecated": "This package is no longer supported.",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"are-we-there-yet": "^2.0.0",
|
||||||
|
"console-control-strings": "^1.1.0",
|
||||||
|
"gauge": "^3.0.0",
|
||||||
|
"set-blocking": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/object-assign": {
|
"node_modules/object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
@@ -887,6 +1259,15 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/path-is-absolute": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/path-key": {
|
"node_modules/path-key": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||||
@@ -946,6 +1327,15 @@
|
|||||||
"integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==",
|
"integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/pg-format": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-format/-/pg-format-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-YyKEF78pEA6wwTAqOUaHIN/rWpfzzIuMh9KdAhc3rSLQ/7zkRFcCgYBAEGatDstLyZw4g0s9SNICmaTGnBVeyw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pg-int8": {
|
"node_modules/pg-int8": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
||||||
@@ -1106,6 +1496,20 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/readable-stream": {
|
||||||
|
"version": "3.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||||
|
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"string_decoder": "^1.1.1",
|
||||||
|
"util-deprecate": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/readdirp": {
|
"node_modules/readdirp": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
@@ -1119,6 +1523,22 @@
|
|||||||
"node": ">=8.10.0"
|
"node": ">=8.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/rimraf": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||||
|
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"glob": "^7.1.3"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rimraf": "bin.js"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/router": {
|
"node_modules/router": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
|
||||||
@@ -1165,7 +1585,6 @@
|
|||||||
"version": "7.7.2",
|
"version": "7.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"bin": {
|
"bin": {
|
||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
@@ -1211,6 +1630,12 @@
|
|||||||
"node": ">= 18"
|
"node": ">= 18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/set-blocking": {
|
||||||
|
"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"
|
||||||
|
},
|
||||||
"node_modules/setprototypeof": {
|
"node_modules/setprototypeof": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||||
@@ -1312,6 +1737,12 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/signal-exit": {
|
||||||
|
"version": "3.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||||
|
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/simple-update-notifier": {
|
"node_modules/simple-update-notifier": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
|
||||||
@@ -1343,6 +1774,41 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/string_decoder": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "~5.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/string-width": {
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"emoji-regex": "^8.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
|
"strip-ansi": "^6.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/strip-ansi": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/supports-color": {
|
"node_modules/supports-color": {
|
||||||
"version": "5.5.0",
|
"version": "5.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||||
@@ -1356,6 +1822,23 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tar": {
|
||||||
|
"version": "6.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
|
||||||
|
"integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"chownr": "^2.0.0",
|
||||||
|
"fs-minipass": "^2.0.0",
|
||||||
|
"minipass": "^5.0.0",
|
||||||
|
"minizlib": "^2.1.1",
|
||||||
|
"mkdirp": "^1.0.3",
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/to-regex-range": {
|
"node_modules/to-regex-range": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
@@ -1388,6 +1871,12 @@
|
|||||||
"nodetouch": "bin/nodetouch.js"
|
"nodetouch": "bin/nodetouch.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tr46": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/type-is": {
|
"node_modules/type-is": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
|
||||||
@@ -1418,6 +1907,12 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/util-deprecate": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/vary": {
|
"node_modules/vary": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
@@ -1427,6 +1922,22 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/webidl-conversions": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||||
|
"license": "BSD-2-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-url": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": "~0.0.3",
|
||||||
|
"webidl-conversions": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
@@ -1443,6 +1954,15 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/wide-align": {
|
||||||
|
"version": "1.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
|
||||||
|
"integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"string-width": "^1.0.2 || 2 || 3 || 4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/wrappy": {
|
"node_modules/wrappy": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
@@ -1457,6 +1977,12 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.4"
|
"node": ">=0.4"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yallist": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||||
|
"license": "ISC"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "auth",
|
"name": "authentication",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"main": "index.js",
|
"main": "src/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "NODE_ENV=production node ./src/index.js",
|
"start": "NODE_ENV=production node ./src/index.js",
|
||||||
"dev": "NODE_ENV=development node ./src/index.js",
|
"dev": "NODE_ENV=development npx nodemon ./src/index.js",
|
||||||
"test": "NODE_ENV=stage node ./src/index.js"
|
"test": "NODE_ENV=stage node ./src/index.js"
|
||||||
},
|
},
|
||||||
"author": "Mateo Saldain",
|
"author": "Mateo Saldain",
|
||||||
@@ -15,11 +15,14 @@
|
|||||||
"nodemon": "^3.1.10"
|
"nodemon": "^3.1.10"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"bcrypt": "^5.1.1",
|
||||||
|
"chalk": "^5.6.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^17.2.1",
|
"dotenv": "^17.2.1",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"express-ejs-layouts": "^2.5.1",
|
"express-ejs-layouts": "^2.5.1",
|
||||||
"pg": "^8.16.3"
|
"pg": "^8.16.3",
|
||||||
|
"pg-format": "^1.0.4"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"description": ""
|
"description": ""
|
||||||
|
|||||||
+133
-13
@@ -1,8 +1,10 @@
|
|||||||
// auth/src/index.js
|
// auth/src/index.js
|
||||||
|
import chalk from 'chalk';
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import expressLayouts from 'express-ejs-layouts';
|
import expressLayouts from 'express-ejs-layouts';
|
||||||
import cors from 'cors';
|
import cors from 'cors';
|
||||||
import { Pool } from 'pg';
|
import { Pool } from 'pg';
|
||||||
|
import bcrypt from'bcrypt';
|
||||||
|
|
||||||
// Rutas
|
// Rutas
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
@@ -17,22 +19,25 @@ import dotenv, { config } from 'dotenv';
|
|||||||
try {
|
try {
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
dotenv.config({ path: path.resolve(__dirname, '../.env.development' )});
|
dotenv.config({ path: path.resolve(__dirname, '../.env.development' )});
|
||||||
console.log("Activando entorno de -> development");
|
console.log(`Activando entorno de ->${chalk.green(` DEVELOPMENT `)}`);
|
||||||
} else if (process.env.NODE_ENV === 'stage') {
|
} else if (process.env.NODE_ENV === 'stage') {
|
||||||
dotenv.config({ path: path.resolve(__dirname, '../.env.test' )});
|
dotenv.config({ path: path.resolve(__dirname, '../.env.test' )});
|
||||||
console.log("Activando entorno de -> testing");
|
console.log(`Activando entorno de ->${chalk.yellow(` TESTING `)}`);
|
||||||
} else if (process.env.NODE_ENV === 'production') {
|
} else if (process.env.NODE_ENV === 'production') {
|
||||||
dotenv.config({ path: path.resolve(__dirname, '../.env' )});
|
dotenv.config({ path: path.resolve(__dirname, '../.env.production' )});
|
||||||
console.log("Activando entorno de -> producción");
|
console.log(`Activando entorno de ->${chalk.red(` PRODUCTION `)}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("A ocurrido un error al seleccionar el entorno. \nError: " + error);
|
console.log("A ocurrido un error al seleccionar el entorno. \nError: " + error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renderiado
|
// Configuración de renderizado
|
||||||
const app = express();
|
const app = express();
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
app.set('trust proxy', true);
|
||||||
|
app.use(express.static(path.join(__dirname, 'pages')));
|
||||||
|
|
||||||
|
|
||||||
// Configuración de conexión PostgreSQL
|
// Configuración de conexión PostgreSQL
|
||||||
|
|
||||||
@@ -51,29 +56,144 @@ async function verificarConexion() {
|
|||||||
try {
|
try {
|
||||||
const client = await pool.connect();
|
const client = await pool.connect();
|
||||||
const res = await client.query('SELECT NOW() AS hora');
|
const res = await client.query('SELECT NOW() AS hora');
|
||||||
console.log('Conexión con la base de datos fue exitosa.');
|
console.log(`\nConexión con la base de datos ${chalk.green(process.env.DB_NAME)} fue exitosa.`);
|
||||||
console.log('Fecha y hora actual de la base de datos:', res.rows[0].hora);
|
console.log('Fecha y hora actual de la base de datos:', res.rows[0].hora);
|
||||||
client.release(); // liberar el cliente de nuevo al pool
|
client.release(); // liberar el cliente de nuevo al pool
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error al conectar con la base de datos al iniciar:', error.message);
|
console.error('Error al conectar con la base de datos al iniciar:', error.message);
|
||||||
|
console.error(`Troubleshooting:\n1. Compruebe que las bases de datos se iniciaron correctamente.\n2. Verifique las credenciales y puertos de acceso a la base de datos.\n3. Si está conectandose a una base de datos externa a localhost, verifique las reglas del firewal de entrada y salida de ambos dispositivos.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// === Servir páginas estáticas ===
|
// === Servir páginas estáticas ===
|
||||||
app.use('/pages', express.static(path.join(__dirname, 'pages')));
|
|
||||||
|
app.get('/',(req, res) => res.sendFile(path.join(__dirname, 'pages', 'index.html')));
|
||||||
|
|
||||||
|
app.get('/planes', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const result = await pool.query(`
|
||||||
|
SELECT id, nombre, descripcion, precio
|
||||||
|
FROM plan
|
||||||
|
WHERE activo = true
|
||||||
|
ORDER BY id
|
||||||
|
`);
|
||||||
|
res.json(result.rows);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).json({ error: 'Error al cargar planes' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/api/registro', async (req, res) => {
|
||||||
|
const {
|
||||||
|
nombre_empresa,
|
||||||
|
rut,
|
||||||
|
correo,
|
||||||
|
telefono,
|
||||||
|
direccion,
|
||||||
|
logo,
|
||||||
|
clave_acceso,
|
||||||
|
plan_id
|
||||||
|
} = req.body;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const client = await pool.connect();
|
||||||
|
|
||||||
|
// 1. Hashear la contraseña
|
||||||
|
const hash = await bcrypt.hash(clave_acceso, 10);
|
||||||
|
|
||||||
|
// 2. Insertar el tenant
|
||||||
|
const result = await client.query(`
|
||||||
|
INSERT INTO tenant (
|
||||||
|
nombre_empresa, rut, correo, telefono, direccion, logo,
|
||||||
|
clave_acceso, plan_id, nombre_base_datos
|
||||||
|
) VALUES (
|
||||||
|
$1, $2, $3, $4, $5, $6,
|
||||||
|
$7, $8, 'TEMPORAL'
|
||||||
|
)
|
||||||
|
RETURNING uuid;
|
||||||
|
`, [
|
||||||
|
nombre_empresa, rut, correo, telefono, direccion, logo,
|
||||||
|
hash, plan_id
|
||||||
|
]);
|
||||||
|
|
||||||
|
const uuid = result.rows[0].uuid;
|
||||||
|
const nombre_base_datos = `tenantdb_${uuid}`.replace(/-/g, '').substring(0, 24); // ajustamos para longitud segura
|
||||||
|
|
||||||
|
// 3. Actualizar el campo nombre_base_datos
|
||||||
|
await client.query(`
|
||||||
|
UPDATE tenant SET nombre_base_datos = $1 WHERE uuid = $2
|
||||||
|
`, [nombre_base_datos, uuid]);
|
||||||
|
|
||||||
|
client.release();
|
||||||
|
|
||||||
|
return res.status(201).json({
|
||||||
|
message: 'Tenant registrado correctamente',
|
||||||
|
uuid,
|
||||||
|
nombre_base_datos
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
return res.status(500).json({ error: 'Error al registrar tenant' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// Rutas de conveniencia para abrir cada página rápido:
|
app.post('/api/login', async (req, res) => {
|
||||||
// (Opcional: puedes usar directamente /pages/roles.html, etc.)
|
const { correo, clave_acceso } = req.body;
|
||||||
app.get('/', (req, res) => res.sendFile(path.join(__dirname, 'pages', 'index.html')));
|
|
||||||
|
try {
|
||||||
|
const client = await pool.connect();
|
||||||
|
|
||||||
|
const result = await client.query(`
|
||||||
|
SELECT uuid, clave_acceso, nombre_empresa, nombre_base_datos
|
||||||
|
FROM tenant
|
||||||
|
WHERE correo = $1 AND estado = true
|
||||||
|
`, [correo]);
|
||||||
|
|
||||||
|
client.release();
|
||||||
|
|
||||||
|
if (result.rows.length === 0) {
|
||||||
|
return res.status(401).json({ error: 'Correo no registrado o inactivo' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const tenant = result.rows[0];
|
||||||
|
const coincide = await bcrypt.compare(clave_acceso, tenant.clave_acceso);
|
||||||
|
|
||||||
|
if (!coincide) {
|
||||||
|
return res.status(401).json({ error: 'Clave incorrecta' });
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).json({
|
||||||
|
message: 'Login correcto',
|
||||||
|
uuid: tenant.uuid,
|
||||||
|
nombre_empresa: tenant.nombre_empresa,
|
||||||
|
base_datos: tenant.nombre_base_datos
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
return res.status(500).json({ error: 'Error al validar login' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Colores personalizados
|
||||||
|
let primaryColor = chalk.hex('#'+`${process.env.COL_PRI}`);
|
||||||
|
let secondaryColor = chalk.hex('#'+`${process.env.COL_SEC}`);
|
||||||
|
// let backgroundColor = chalk.hex('#'+`${process.env.COL_BG}`);
|
||||||
|
|
||||||
|
|
||||||
app.use(expressLayouts);
|
app.use(expressLayouts);
|
||||||
// Iniciar servidor
|
// Iniciar servidor
|
||||||
app.listen( process.env.PORT, () => {
|
app.listen( process.env.PORT, () => {
|
||||||
console.log(`Servidor corriendo en http://localhost:${process.env.PORT}`);
|
console.log(`Servidor de ${chalk.yellow('autenticación')} de ${secondaryColor('SuiteCoffee')} corriendo en ${chalk.yellow(`http://localhost:${process.env.PORT}\n`)}` );
|
||||||
console.log('Estableciendo conexión con la db...');
|
console.log(chalk.grey(`Comprobando accesibilidad a la db ${chalk.green(process.env.DB_NAME)} del host ${chalk.white(`${process.env.DB_HOST}`)} ...`));
|
||||||
verificarConexion();
|
verificarConexion();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/health", async (req, res) => {
|
||||||
|
// Podés chequear DB aquí. 200 = healthy; 503 = not ready.
|
||||||
|
res.status(200).json({ status: "ok" });
|
||||||
});
|
});
|
||||||
@@ -1,45 +1,154 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="es">
|
<html lang="es">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Login Bootstrap</title>
|
<title>SuiteCoffee - Autenticación</title>
|
||||||
<!-- Bootstrap CDN -->
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="bg-light d-flex justify-content-center align-items-center vh-100">
|
<body class="bg-light d-flex justify-content-center align-items-center vh-100">
|
||||||
|
|
||||||
<div class="card shadow p-4" style="width: 100%; max-width: 350px;">
|
<div class="card shadow p-4" style="width: 100%; max-width: 400px;">
|
||||||
<h4 class="text-center mb-4">Iniciar Sesión</h4>
|
<h4 class="text-center mb-3" id="form-title">Iniciar Sesión</h4>
|
||||||
|
|
||||||
<form id="form-login">
|
<!-- Mensajes -->
|
||||||
|
<div id="mensaje" class="alert d-none" role="alert"></div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<!-- Formulario compartido -->
|
||||||
<label for="usuario" class="form-label">Usuario</label>
|
<form id="formulario">
|
||||||
<input type="text" class="form-control" id="usuario" placeholder="Ingrese su usuario" required>
|
<div id="registro-extra" style="display: none;">
|
||||||
</div>
|
<div class="mb-2">
|
||||||
|
<input type="text" class="form-control" id="nombre_empresa" placeholder="Nombre de la empresa" required>
|
||||||
<div class="mb-3">
|
</div>
|
||||||
<label for="clave" class="form-label">Contraseña</label>
|
<div class="mb-2">
|
||||||
<input type="password" class="form-control" id="clave" placeholder="Ingrese su contraseña" required>
|
<input type="text" class="form-control" id="rut" placeholder="RUT (opcional)" required>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mb-2">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
<input type="text" class="form-control" id="telefono" placeholder="Teléfono">
|
||||||
<div class="form-check">
|
</div>
|
||||||
<input class="form-check-input" type="checkbox" id="recordarme">
|
<div class="mb-2">
|
||||||
<label class="form-check-label" for="recordarme">
|
<input type="text" class="form-control" id="direccion" placeholder="Dirección">
|
||||||
Recordarme
|
</div>
|
||||||
</label>
|
<div class="mb-2">
|
||||||
|
<input type="text" class="form-control" id="logo" placeholder="Logo URL (opcional)">
|
||||||
|
</div>
|
||||||
|
<div class="mb-2">
|
||||||
|
<select class="form-select" id="plan_id" required>
|
||||||
|
<option value="">Cargando planes...</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<a href="#" class="small">¿Olvidaste tu contraseña?</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary w-100">Entrar</button>
|
<div class="mb-2">
|
||||||
|
<input type="email" class="form-control" id="correo" placeholder="Correo" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<input type="password" class="form-control" id="clave" placeholder="Contraseña" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary w-100" id="btn-submit">Entrar</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<div class="text-center mt-3">
|
||||||
|
<button class="btn btn-link btn-sm" id="toggle-mode">¿No tienes cuenta? Regístrate</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Bootstrap JS (opcional) -->
|
<script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
const form = document.getElementById('formulario');
|
||||||
|
const mensaje = document.getElementById('mensaje');
|
||||||
|
const toggleModeBtn = document.getElementById('toggle-mode');
|
||||||
|
const registroExtra = document.getElementById('registro-extra');
|
||||||
|
const formTitle = document.getElementById('form-title');
|
||||||
|
const btnSubmit = document.getElementById('btn-submit');
|
||||||
|
|
||||||
|
let modoRegistro = false;
|
||||||
|
|
||||||
|
toggleModeBtn.addEventListener('click', () => {
|
||||||
|
modoRegistro = !modoRegistro;
|
||||||
|
registroExtra.style.display = modoRegistro ? 'block' : 'none';
|
||||||
|
formTitle.textContent = modoRegistro ? 'Registrar Cuenta' : 'Iniciar Sesión';
|
||||||
|
btnSubmit.textContent = modoRegistro ? 'Registrarse' : 'Entrar';
|
||||||
|
toggleModeBtn.textContent = modoRegistro ? '¿Ya tienes cuenta? Inicia sesión' : '¿No tienes cuenta? Regístrate';
|
||||||
|
|
||||||
|
if (modoRegistro) {
|
||||||
|
cargarPlanes(); // ✅ ahora sí se ejecutará correctamente
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
form.addEventListener('submit', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
mensaje.classList.add('d-none');
|
||||||
|
|
||||||
|
const datos = {
|
||||||
|
correo: document.getElementById('correo').value,
|
||||||
|
clave_acceso: document.getElementById('clave').value
|
||||||
|
};
|
||||||
|
|
||||||
|
if (modoRegistro) {
|
||||||
|
Object.assign(datos, {
|
||||||
|
nombre_empresa: document.getElementById('nombre_empresa').value,
|
||||||
|
rut: document.getElementById('rut').value,
|
||||||
|
telefono: document.getElementById('telefono').value,
|
||||||
|
direccion: document.getElementById('direccion').value,
|
||||||
|
logo: document.getElementById('logo').value,
|
||||||
|
plan_id: document.getElementById('plan_id').value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const url = modoRegistro ? '/api/registro' : '/api/login';
|
||||||
|
const res = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(datos)
|
||||||
|
});
|
||||||
|
|
||||||
|
const resultado = await res.json();
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(resultado.error || 'Error inesperado');
|
||||||
|
}
|
||||||
|
|
||||||
|
mensaje.className = 'alert alert-success';
|
||||||
|
mensaje.textContent = resultado.message || (modoRegistro ? 'Registro exitoso' : 'Inicio exitoso');
|
||||||
|
mensaje.classList.remove('d-none');
|
||||||
|
|
||||||
|
if (!modoRegistro) {
|
||||||
|
// Redirigir a dashboard, por ejemplo
|
||||||
|
// window.location.href = `/dashboard?tenant=${resultado.uuid}`;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
mensaje.className = 'alert alert-danger';
|
||||||
|
mensaje.textContent = err.message;
|
||||||
|
mensaje.classList.remove('d-none');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ✅ Ahora la función está declarada correctamente
|
||||||
|
async function cargarPlanes() {
|
||||||
|
const select = document.getElementById('plan_id');
|
||||||
|
select.innerHTML = '<option value="">Cargando planes...</option>';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch('/planes');
|
||||||
|
const planes = await res.json();
|
||||||
|
|
||||||
|
select.innerHTML = '<option value="">Seleccione un plan</option>';
|
||||||
|
planes.forEach(plan => {
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = plan.id;
|
||||||
|
opt.textContent = plan.nombre.charAt(0).toUpperCase() + plan.nombre.slice(1);
|
||||||
|
select.appendChild(opt);
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
select.innerHTML = '<option value="">Error al cargar planes</option>';
|
||||||
|
console.error('Error cargando planes:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
+386
@@ -0,0 +1,386 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
from shutil import which
|
||||||
|
|
||||||
|
PROJECT_ROOT = os.path.abspath(os.getcwd())
|
||||||
|
|
||||||
|
# === Archivos Compose (entornos) ===
|
||||||
|
BASE_COMPOSE = os.path.join(PROJECT_ROOT, "compose.yaml")
|
||||||
|
DEV_COMPOSE = os.path.join(PROJECT_ROOT, "compose.dev.yaml")
|
||||||
|
PROD_COMPOSE = os.path.join(PROJECT_ROOT, "compose.prod.yaml")
|
||||||
|
|
||||||
|
# === Archivos Compose (globales) ===
|
||||||
|
NPM_COMPOSE = os.path.join(PROJECT_ROOT, "compose.npm.yaml")
|
||||||
|
DBEAVER_COMPOSE = os.path.join(PROJECT_ROOT, "compose.dbeaver.yaml")
|
||||||
|
|
||||||
|
# Archivos .env
|
||||||
|
ENV_FILES = {
|
||||||
|
"development": ".env.development",
|
||||||
|
"production": ".env.production",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Nombres de proyecto para permitir DEV y PROD simultáneos
|
||||||
|
def _base_project():
|
||||||
|
return os.path.basename(PROJECT_ROOT).lower() or "composeproj"
|
||||||
|
|
||||||
|
PROJECT_NAMES = {
|
||||||
|
"development": f"{_base_project()}_dev",
|
||||||
|
"production": f"{_base_project()}_prod",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Nombre de proyecto global (ambos yaml globales usan name: suitecoffee)
|
||||||
|
GLOBAL_PROJECT_NAME = "suitecoffee"
|
||||||
|
|
||||||
|
# ---------- Utilidades ----------
|
||||||
|
def check_prereqs():
|
||||||
|
if which("docker") is None:
|
||||||
|
fail("No se encontró 'docker' en el PATH.")
|
||||||
|
try:
|
||||||
|
run(["docker", "compose", "version"], check=True, capture_output=True)
|
||||||
|
except Exception:
|
||||||
|
fail("No se pudo ejecutar 'docker compose'. Asegúrate de tener Docker Compose v2.")
|
||||||
|
|
||||||
|
def run(cmd, check=False, capture_output=False):
|
||||||
|
return subprocess.run(cmd, check=check, capture_output=capture_output, text=True)
|
||||||
|
|
||||||
|
def compose_files_args(env_key):
|
||||||
|
"""
|
||||||
|
Devuelve los -f correctos según el entorno (dev/prod) + base.
|
||||||
|
"""
|
||||||
|
if not os.path.exists(BASE_COMPOSE):
|
||||||
|
fail("No se encontró compose.yaml en la raíz del proyecto.")
|
||||||
|
|
||||||
|
args = ["-f", BASE_COMPOSE]
|
||||||
|
|
||||||
|
if env_key == "development":
|
||||||
|
if not os.path.exists(DEV_COMPOSE):
|
||||||
|
fail("No se encontró compose.dev.yaml.")
|
||||||
|
args += ["-f", DEV_COMPOSE]
|
||||||
|
elif env_key == "production":
|
||||||
|
if not os.path.exists(PROD_COMPOSE):
|
||||||
|
fail("No se encontró compose.prod.yaml.")
|
||||||
|
args += ["-f", PROD_COMPOSE]
|
||||||
|
else:
|
||||||
|
fail(f"Entorno desconocido: {env_key}")
|
||||||
|
return args
|
||||||
|
|
||||||
|
def compose_files_args_global(kind):
|
||||||
|
"""
|
||||||
|
Devuelve los -f correctos para servicios globales (npm/dbeaver).
|
||||||
|
"""
|
||||||
|
if kind == "npm":
|
||||||
|
if not os.path.exists(NPM_COMPOSE):
|
||||||
|
fail("No se encontró compose.npm.yaml.")
|
||||||
|
return ["-f", NPM_COMPOSE]
|
||||||
|
elif kind == "dbeaver":
|
||||||
|
if not os.path.exists(DBEAVER_COMPOSE):
|
||||||
|
fail("No se encontró compose.dbeaver.yaml.")
|
||||||
|
return ["-f", DBEAVER_COMPOSE]
|
||||||
|
else:
|
||||||
|
fail(f"Servicio global desconocido: {kind}")
|
||||||
|
|
||||||
|
def env_file_path(env_key):
|
||||||
|
fname = ENV_FILES.get(env_key)
|
||||||
|
if not fname:
|
||||||
|
return None
|
||||||
|
path = os.path.join(PROJECT_ROOT, fname)
|
||||||
|
return path if os.path.exists(path) else None
|
||||||
|
|
||||||
|
def compose_cmd(base_args, env_key, env_file=None, project_name=None):
|
||||||
|
"""
|
||||||
|
Construye: docker compose -f base -f env --env-file ... -p <name> <COMANDO> [OPCIONES]
|
||||||
|
(importante: --env-file y -p son opciones globales y van antes del subcomando)
|
||||||
|
"""
|
||||||
|
cmd = ["docker", "compose"]
|
||||||
|
cmd += compose_files_args(env_key)
|
||||||
|
if env_file:
|
||||||
|
cmd += ["--env-file", env_file]
|
||||||
|
if project_name:
|
||||||
|
cmd += ["-p", project_name]
|
||||||
|
cmd += base_args # ["up","-d","--force-recreate"] o ["ps","--status","running","-q"]
|
||||||
|
return cmd
|
||||||
|
|
||||||
|
def compose_cmd_global(base_args, kind, project_name=GLOBAL_PROJECT_NAME):
|
||||||
|
"""
|
||||||
|
Comandos para servicios globales (npm/dbeaver):
|
||||||
|
docker compose -f compose.<kind>.yaml -p suitecoffee <COMANDO> ...
|
||||||
|
"""
|
||||||
|
cmd = ["docker", "compose"]
|
||||||
|
cmd += compose_files_args_global(kind)
|
||||||
|
if project_name:
|
||||||
|
cmd += ["-p", project_name]
|
||||||
|
cmd += base_args
|
||||||
|
return cmd
|
||||||
|
|
||||||
|
def yes_no(prompt, default="n"):
|
||||||
|
default = default.lower()
|
||||||
|
hint = "[Y/n]" if default == "y" else "[y/N]"
|
||||||
|
while True:
|
||||||
|
resp = input(f"{prompt} {hint} ").strip().lower()
|
||||||
|
if not resp:
|
||||||
|
return default == "y"
|
||||||
|
if resp in ("y", "yes", "s", "si", "sí"):
|
||||||
|
return True
|
||||||
|
if resp in ("n", "no"):
|
||||||
|
return False
|
||||||
|
print("Respuesta no reconocida. Por favor, responde con 'y' o 'n'.")
|
||||||
|
|
||||||
|
def print_header(title):
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print(title)
|
||||||
|
print("=" * 60 + "\n")
|
||||||
|
|
||||||
|
def info(msg): print(f"• {msg}")
|
||||||
|
def ok(msg): print(f"✓ {msg}")
|
||||||
|
def warn(msg): print(f"! {msg}")
|
||||||
|
def fail(msg):
|
||||||
|
print(f"✗ {msg}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# ---------- Helpers globales (servicios por archivo y ps por labels) ----------
|
||||||
|
def list_services_from_compose_file(compose_path):
|
||||||
|
"""Obtiene la lista de servicios definidos en un archivo compose específico."""
|
||||||
|
cmd = ["docker", "compose", "-f", compose_path, "config", "--services"]
|
||||||
|
proc = run(cmd, capture_output=True)
|
||||||
|
if proc.returncode != 0:
|
||||||
|
return []
|
||||||
|
return [s.strip() for s in proc.stdout.splitlines() if s.strip()]
|
||||||
|
|
||||||
|
def list_services_global(kind):
|
||||||
|
compose_path = NPM_COMPOSE if kind == "npm" else DBEAVER_COMPOSE
|
||||||
|
return list_services_from_compose_file(compose_path)
|
||||||
|
|
||||||
|
def docker_ps_by_labels(project, service=None, running_only=True):
|
||||||
|
"""Lista contenedores por labels de compose (project/service)."""
|
||||||
|
cmd = ["docker", "ps"]
|
||||||
|
if running_only:
|
||||||
|
# por defecto docker ps ya lista solo running; se deja explícito por claridad
|
||||||
|
pass
|
||||||
|
cmd += ["--filter", f"label=com.docker.compose.project={project}"]
|
||||||
|
if service:
|
||||||
|
cmd += ["--filter", f"label=com.docker.compose.service={service}"]
|
||||||
|
cmd += ["-q"]
|
||||||
|
proc = run(cmd, capture_output=True)
|
||||||
|
if proc.returncode != 0:
|
||||||
|
return []
|
||||||
|
return [l.strip() for l in proc.stdout.splitlines() if l.strip()]
|
||||||
|
|
||||||
|
# ---------- Acciones (entornos) ----------
|
||||||
|
def bring_up(env_key, force_recreate=False):
|
||||||
|
env_path = env_file_path(env_key)
|
||||||
|
if not env_path:
|
||||||
|
warn(f"No se encontró archivo de entorno para '{env_key}'. Continuando sin --env-file.")
|
||||||
|
pname = PROJECT_NAMES.get(env_key)
|
||||||
|
|
||||||
|
base_args = ["up", "-d"]
|
||||||
|
if force_recreate:
|
||||||
|
base_args.append("--force-recreate")
|
||||||
|
|
||||||
|
cmd = compose_cmd(base_args, env_key=env_key, env_file=env_path, project_name=pname)
|
||||||
|
info("Ejecutando: " + " ".join(cmd))
|
||||||
|
proc = run(cmd)
|
||||||
|
if proc.returncode == 0:
|
||||||
|
ok(f"Entorno '{env_key}' levantado correctamente.")
|
||||||
|
else:
|
||||||
|
fail(f"Fallo al levantar entorno '{env_key}'. Código: {proc.returncode}")
|
||||||
|
|
||||||
|
def bring_down(env_key):
|
||||||
|
env_path = env_file_path(env_key)
|
||||||
|
pname = PROJECT_NAMES.get(env_key)
|
||||||
|
cmd = compose_cmd(["down"], env_key=env_key, env_file=env_path, project_name=pname)
|
||||||
|
info("Ejecutando: " + " ".join(cmd))
|
||||||
|
proc = run(cmd)
|
||||||
|
if proc.returncode == 0:
|
||||||
|
ok(f"Entorno '{env_key}' detenido correctamente.")
|
||||||
|
else:
|
||||||
|
fail(f"Fallo al detener entorno '{env_key}'. Código: {proc.returncode}")
|
||||||
|
|
||||||
|
def running_ids(env_key):
|
||||||
|
env_path = env_file_path(env_key)
|
||||||
|
pname = PROJECT_NAMES.get(env_key)
|
||||||
|
cmd = compose_cmd(["ps", "--status", "running", "-q"], env_key=env_key, env_file=env_path, project_name=pname)
|
||||||
|
proc = run(cmd, capture_output=True)
|
||||||
|
if proc.returncode != 0:
|
||||||
|
return []
|
||||||
|
return [l.strip() for l in proc.stdout.splitlines() if l.strip()]
|
||||||
|
|
||||||
|
# ---------- Acciones (globales) ----------
|
||||||
|
def bring_up_global(kind, force_recreate=False):
|
||||||
|
base_args = ["up", "-d"]
|
||||||
|
if force_recreate:
|
||||||
|
base_args.append("--force-recreate")
|
||||||
|
cmd = compose_cmd_global(base_args, kind=kind)
|
||||||
|
info("Ejecutando: " + " ".join(cmd))
|
||||||
|
proc = run(cmd)
|
||||||
|
if proc.returncode == 0:
|
||||||
|
ok(f"Servicio global '{kind}' levantado correctamente.")
|
||||||
|
else:
|
||||||
|
fail(f"Fallo al levantar servicio global '{kind}'. Código: {proc.returncode}")
|
||||||
|
|
||||||
|
def bring_down_global(kind, remove=False):
|
||||||
|
"""
|
||||||
|
Apaga SOLO los servicios definidos en el compose global indicado.
|
||||||
|
- Primero 'stop <servicios>'.
|
||||||
|
- Opcionalmente, 'rm -f <servicios>' si remove=True.
|
||||||
|
"""
|
||||||
|
services = list_services_global(kind)
|
||||||
|
if not services:
|
||||||
|
warn(f"No se encontraron servicios en compose.{kind}.yaml.")
|
||||||
|
return
|
||||||
|
# stop específico
|
||||||
|
cmd = compose_cmd_global(["stop"] + services, kind=kind)
|
||||||
|
info("Ejecutando: " + " ".join(cmd))
|
||||||
|
proc = run(cmd)
|
||||||
|
if proc.returncode != 0:
|
||||||
|
fail(f"Fallo al detener servicios de '{kind}'. Código: {proc.returncode}")
|
||||||
|
|
||||||
|
if remove:
|
||||||
|
cmd_rm = compose_cmd_global(["rm", "-f"] + services, kind=kind)
|
||||||
|
info("Ejecutando: " + " ".join(cmd_rm))
|
||||||
|
proc_rm = run(cmd_rm)
|
||||||
|
if proc_rm.returncode != 0:
|
||||||
|
fail(f"Fallo al remover servicios de '{kind}'. Código: {proc_rm.returncode}")
|
||||||
|
|
||||||
|
ok(f"Servicio(s) global(es) '{kind}' detenido(s).")
|
||||||
|
|
||||||
|
def running_ids_global(kind):
|
||||||
|
"""
|
||||||
|
Detecta si el compose global está corriendo revisando contenedores por servicio,
|
||||||
|
filtrando por labels (project+service).
|
||||||
|
"""
|
||||||
|
services = list_services_global(kind)
|
||||||
|
ids = []
|
||||||
|
for svc in services:
|
||||||
|
ids += docker_ps_by_labels(GLOBAL_PROJECT_NAME, service=svc, running_only=True)
|
||||||
|
# eliminar duplicados
|
||||||
|
return list(dict.fromkeys(ids))
|
||||||
|
|
||||||
|
# ---------- Estado y flujo ----------
|
||||||
|
def detect_status_summary():
|
||||||
|
dev_running = running_ids("development")
|
||||||
|
prod_running = running_ids("production")
|
||||||
|
npm_running = running_ids_global("npm")
|
||||||
|
dbeaver_running = running_ids_global("dbeaver")
|
||||||
|
|
||||||
|
print_header("Estado actual")
|
||||||
|
info(f"DESARROLLO: {len(dev_running)} contenedor(es) en ejecución.")
|
||||||
|
info(f"PRODUCCIÓN: {len(prod_running)} contenedor(es) en ejecución.")
|
||||||
|
info(f"NPM (global): {len(npm_running)} contenedor(es) en ejecución.")
|
||||||
|
info(f"DBEAVER (global): {len(dbeaver_running)} contenedor(es) en ejecución.\n")
|
||||||
|
|
||||||
|
return bool(dev_running), bool(prod_running), bool(npm_running), bool(dbeaver_running)
|
||||||
|
|
||||||
|
def detect_and_optionally_shutdown():
|
||||||
|
"""Muestra estado y ofrece (opcional) apagar dev/prod.
|
||||||
|
Los servicios globales se gestionan desde el menú principal (levantar/apagar).
|
||||||
|
"""
|
||||||
|
dev_on, prod_on, _npm_on, _dbeaver_on = detect_status_summary()
|
||||||
|
|
||||||
|
options = []
|
||||||
|
if dev_on:
|
||||||
|
options.append(("1", "Apagar entorno de DESARROLLO", "development"))
|
||||||
|
if prod_on:
|
||||||
|
options.append(("2", "Apagar entorno de PRODUCCIÓN", "production"))
|
||||||
|
options.append(("3", "Continuar sin detener nada", None))
|
||||||
|
|
||||||
|
if len(options) == 1:
|
||||||
|
return
|
||||||
|
|
||||||
|
print("Selecciona una opción:")
|
||||||
|
for opt in options:
|
||||||
|
key, label = opt[0], opt[1]
|
||||||
|
print(f" {key}) {label}")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
choice = input("> ").strip()
|
||||||
|
selected = next((opt for opt in options if opt[0] == choice), None)
|
||||||
|
if not selected:
|
||||||
|
print("Opción inválida.")
|
||||||
|
continue
|
||||||
|
if choice == "3" or selected[2] is None:
|
||||||
|
ok("Continuamos sin detener nada.")
|
||||||
|
return
|
||||||
|
env_key = selected[2]
|
||||||
|
bring_down(env_key)
|
||||||
|
return
|
||||||
|
|
||||||
|
def main_menu():
|
||||||
|
# Consultar estado de globales para decidir si mostrar opciones de "Levantar" o "Apagar"
|
||||||
|
_dev_on, _prod_on, npm_on, dbeaver_on = detect_status_summary()
|
||||||
|
|
||||||
|
print_header("Gestor de entornos Docker Compose")
|
||||||
|
print("Selecciona una opción:")
|
||||||
|
print(" 1) Levantar entorno de DESARROLLO")
|
||||||
|
print(" 2) Levantar entorno de PRODUCCIÓN")
|
||||||
|
|
||||||
|
dynamic_keys = {}
|
||||||
|
next_key = 3
|
||||||
|
|
||||||
|
# NPM: opción según estado
|
||||||
|
if not npm_on:
|
||||||
|
print(f" {next_key}) Levantar NPM (compose.npm.yaml)")
|
||||||
|
dynamic_keys[str(next_key)] = ("global_up", "npm")
|
||||||
|
else:
|
||||||
|
print(f" {next_key}) Apagar NPM (compose.npm.yaml)")
|
||||||
|
dynamic_keys[str(next_key)] = ("global_down", "npm")
|
||||||
|
next_key += 1
|
||||||
|
|
||||||
|
# DBEAVER: opción según estado
|
||||||
|
if not dbeaver_on:
|
||||||
|
print(f" {next_key}) Levantar DBEAVER (compose.dbeaver.yaml)")
|
||||||
|
dynamic_keys[str(next_key)] = ("global_up", "dbeaver")
|
||||||
|
else:
|
||||||
|
print(f" {next_key}) Apagar DBEAVER (compose.dbeaver.yaml)")
|
||||||
|
dynamic_keys[str(next_key)] = ("global_down", "dbeaver")
|
||||||
|
next_key += 1
|
||||||
|
|
||||||
|
# Salir
|
||||||
|
print(f" {next_key}) Salir")
|
||||||
|
exit_key = str(next_key)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
choice = input("> ").strip()
|
||||||
|
if choice == "1":
|
||||||
|
force = yes_no("¿Usar --force-recreate para DESARROLLO?", default="n")
|
||||||
|
bring_up("development", force_recreate=force)
|
||||||
|
return
|
||||||
|
elif choice == "2":
|
||||||
|
force = yes_no("¿Usar --force-recreate para PRODUCCIÓN?", default="n")
|
||||||
|
bring_up("production", force_recreate=force)
|
||||||
|
return
|
||||||
|
elif choice in dynamic_keys:
|
||||||
|
action, kind = dynamic_keys[choice]
|
||||||
|
if action == "global_up":
|
||||||
|
force = yes_no(f"¿Usar --force-recreate para {kind.upper()}?", default="n")
|
||||||
|
bring_up_global(kind, force_recreate=force)
|
||||||
|
return
|
||||||
|
elif action == "global_down":
|
||||||
|
remove = yes_no(f"¿También remover contenedores de {kind.upper()}? (rm -f)", default="n")
|
||||||
|
bring_down_global(kind, remove=remove)
|
||||||
|
return
|
||||||
|
elif choice == exit_key:
|
||||||
|
ok("Saliendo.")
|
||||||
|
sys.exit(0)
|
||||||
|
else:
|
||||||
|
print("Opción inválida. Elige una de las opciones listadas.")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
try:
|
||||||
|
check_prereqs()
|
||||||
|
# Mostrar estado y permitir opcionalmente apagar dev/prod
|
||||||
|
detect_and_optionally_shutdown()
|
||||||
|
# Menú de gestión (incluye globales: levantar o apagar según estado)
|
||||||
|
main_menu()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n")
|
||||||
|
ok("Interrumpido por el usuario (Ctrl+C). Saliendo.")
|
||||||
|
sys.exit(0)
|
||||||
|
except Exception as e:
|
||||||
|
fail(f"Ocurrió un error inesperado: {e}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|||||||
Reference in New Issue
Block a user