diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..0019580 --- /dev/null +++ b/.env.example @@ -0,0 +1,30 @@ +# SQL Server - Servidor Primary (.202) +DB_PRIMARY_HOST=104.197.7.202 +DB_PRIMARY_USER=sa +DB_PRIMARY_PASS=Clave.2025 +DB_PRIMARY_DB=master + +# SQL Server - Servidor Secondary (.152) +DB_SECONDARY_HOST=104.192.7.152 +DB_SECONDARY_USER=sa +DB_SECONDARY_PASS=Clave.2025 +DB_SECONDARY_DB=master + +# SQL Server - Azure/CONTROLDESK +DB_AZURE_HOST=104.192.7.152 +DB_AZURE_USER=sa +DB_AZURE_PASS=Clave.2025 +DB_AZURE_DB=CONTROLDESK + +# PostgreSQL - Base de datos de usuarios (Docker local) +DB_POSTGRES_HOST=localhost +DB_POSTGRES_PORT=5432 +DB_POSTGRES_USER=postgres +DB_POSTGRES_PASS=Control. +DB_POSTGRES_DB=CONTROLDESK + +# JWT Secret (cambiar en producción) +JWT_SECRET=change-this-secret-in-production-please-use-a-long-random-string + +# Ruta de backups +BACKUP_PATH=D:/BackupSFTP/ diff --git a/INSTALACION_USUARIOS.md b/INSTALACION_USUARIOS.md new file mode 100644 index 0000000..386960e --- /dev/null +++ b/INSTALACION_USUARIOS.md @@ -0,0 +1,322 @@ +# 🚀 Sistema Completo de Gestión de Usuarios y Permisos + +## ✅ ¿Qué se ha implementado? + +### 1. **Sistema de Autenticación** +- ✅ Login con usuario y contraseña +- ✅ Tokens JWT seguros +- ✅ Cookies HTTP-only +- ✅ Sesiones persistentes (7 días) +- ✅ Logout seguro +- ✅ Protección de rutas + +### 2. **Gestión de Usuarios (Solo Admins)** +- ✅ Crear nuevos usuarios +- ✅ Editar usuarios existentes +- ✅ Activar/desactivar usuarios +- ✅ Eliminar usuarios +- ✅ Asignar rol de administrador + +### 3. **Sistema de Permisos** +- ✅ Permisos granulares por base de datos +- ✅ Asignación/revocación de permisos +- ✅ Filtrado automático de bases de datos según permisos +- ✅ Administradores con acceso total + +### 4. **Base de Datos PostgreSQL** +- ✅ Tabla de usuarios +- ✅ Tabla de permisos +- ✅ Tabla de sesiones +- ✅ Índices para rendimiento +- ✅ Constraints de integridad + +### 5. **Seguridad** +- ✅ Contraseñas hasheadas con bcrypt +- ✅ Validación de contraseñas fuertes +- ✅ Tokens JWT con expiración +- ✅ Protección contra SQL injection +- ✅ Sanitización de entradas + +## 📦 Archivos Creados + +### Backend (Server) +``` +src/lib/server/ +├── auth.ts # Utilidades de autenticación JWT y bcrypt +├── users.ts # CRUD de usuarios y permisos +└── db.ts # Conexión a PostgreSQL (actualizado) + +src/routes/ +├── login/ +│ ├── +page.svelte # Interfaz de login +│ └── +page.server.ts # Lógica de autenticación +├── logout/ +│ └── +server.ts # Cerrar sesión +├── usuarios/ +│ ├── +page.svelte # Gestión de usuarios (UI) +│ ├── +page.server.ts # CRUD y permisos +│ └── api/permissions/ +│ └── +server.ts # API de permisos +└── +page.server.ts # Panel principal (actualizado con filtros) +``` + +### Base de Datos +``` +database/ +└── schema.sql # Script de creación de tablas +``` + +### Documentación +``` +docs/ +└── USUARIOS.md # Guía completa del sistema + +scripts/ +├── generate-password-hash.js # Generar hash de contraseñas +└── init-users.sh # Script de inicialización +``` + +### Configuración +``` +.env.example # Variables de entorno de ejemplo +``` + +## 🛠️ Instalación y Configuración + +### Paso 1: Instalar Dependencias + +```bash +npm install +``` + +Las dependencias ya están en package.json: +- `bcrypt` - Hash de contraseñas +- `jsonwebtoken` - Tokens JWT +- `pg` - Cliente PostgreSQL +- `@types/pg`, `@types/bcrypt`, `@types/jsonwebtoken` - Tipos TypeScript + +### Paso 2: Configurar PostgreSQL + +#### Opción A: Desde la línea de comandos +```bash +psql -h 10.0.20.152 -U postgres -d CONTROLDESK -f database/schema.sql +``` + +#### Opción B: Desde pgAdmin o DBeaver +1. Abre el archivo `database/schema.sql` +2. Copia todo el contenido +3. Pégalo en la consola SQL +4. Ejecuta el script + +### Paso 3: Generar Contraseña del Admin + +```bash +node scripts/generate-password-hash.js +``` + +Este script generará un hash para la contraseña `Admin123!`. + +**IMPORTANTE:** Copia el hash generado y actualiza el archivo `database/schema.sql` en la línea del INSERT del usuario admin, reemplazando el hash temporal. + +Luego vuelve a ejecutar el INSERT en PostgreSQL: + +```sql +INSERT INTO usuarios (username, email, password_hash, nombre_completo, es_admin, activo) +VALUES ( + 'admin', + 'admin@aduanasoft.com', + 'EL_HASH_QUE_GENERASTE', -- <-- Pega aquí el hash + 'Administrador del Sistema', + true, + true +) +ON CONFLICT (username) DO UPDATE SET + password_hash = EXCLUDED.password_hash; +``` + +### Paso 4: Configurar Variables de Entorno + +```bash +cp .env.example .env +``` + +Edita `.env` y actualiza los valores: + +```env +# PostgreSQL +DB_POSTGRES_HOST=10.0.20.152 +DB_POSTGRES_PORT=5432 +DB_POSTGRES_USER=postgres +DB_POSTGRES_PASS=Control. +DB_POSTGRES_DB=CONTROLDESK + +# JWT Secret (¡CÁMBIALO!) +JWT_SECRET=un-secreto-muy-largo-y-aleatorio-para-produccion +``` + +**⚠️ IMPORTANTE:** En producción, genera un JWT_SECRET aleatorio y seguro: +```bash +node -e "console.log(require('crypto').randomBytes(64).toString('hex'))" +``` + +### Paso 5: Iniciar el Servidor + +```bash +npm run dev +``` + +### Paso 6: Primer Login + +1. Ve a `http://localhost:5173/login` +2. Usuario: `admin` +3. Contraseña: `Admin123!` (o la que configuraste) + +## 📖 Uso del Sistema + +### Para Administradores + +#### 1. Crear un Nuevo Usuario +1. Ve a "Gestión de Usuarios" en el sidebar +2. Haz clic en "Nuevo Usuario" +3. Completa el formulario: + - **Usuario**: Nombre de usuario único + - **Email**: Correo electrónico + - **Nombre Completo**: Nombre real + - **Contraseña**: Mínimo 8 caracteres con mayúsculas, minúsculas, números y símbolos + - **Es Administrador**: Marca si quieres que sea admin +4. Haz clic en "Crear Usuario" + +#### 2. Asignar Permisos de Bases de Datos +1. En la lista de usuarios, haz clic en el ícono 🔒 (candado) +2. Verás todas las bases de datos disponibles +3. Haz clic en "Sin Acceso" para otorgar acceso +4. Haz clic en "Con Acceso" para revocar acceso +5. Los cambios se guardan automáticamente + +#### 3. Editar un Usuario +1. Haz clic en el ícono ✏️ (editar) +2. Modifica los campos que necesites +3. Para cambiar la contraseña, escribe una nueva (si dejas vacío, no se cambia) +4. Haz clic en "Guardar Cambios" + +#### 4. Desactivar un Usuario +1. Haz clic en el ícono de toggle (activar/desactivar) +2. Confirma la acción +3. El usuario no podrá iniciar sesión, pero sus datos se conservan + +#### 5. Eliminar un Usuario +1. Haz clic en el ícono 🗑️ (eliminar) +2. Confirma la acción +3. **¡CUIDADO!** Esta acción es permanente + +### Para Usuarios Normales + +Los usuarios normales solo verán: +- Las bases de datos que les han sido asignadas +- No tendrán acceso a "Gestión de Usuarios" +- El resto del panel funciona igual + +## 🔒 Cómo Funciona la Seguridad + +### Flujo de Autenticación + +``` +1. Usuario ingresa credenciales + ↓ +2. Servidor verifica con bcrypt + ↓ +3. Se genera token JWT + ↓ +4. Token se guarda en cookie HTTP-only + ↓ +5. Cada petición verifica el token + ↓ +6. Se aplican filtros de permisos +``` + +### Filtrado de Bases de Datos + +```typescript +// En +page.server.ts (líneas 376-379) +if (!currentUser.es_admin) { + // Filtra solo las bases de datos que el usuario puede ver + databaseRows = await filterDatabasesByUserPermissions(currentUser.id, databaseRows); + alertsData = await filterDatabasesByUserPermissions(currentUser.id, alertsData); +} +``` + +## 📊 Estructura de la Base de Datos + +### Tabla `usuarios` +Almacena información de los usuarios del sistema. + +| Campo | Tipo | Descripción | +|-------|------|-------------| +| id | SERIAL | ID único | +| username | VARCHAR(50) | Nombre de usuario único | +| email | VARCHAR(100) | Email único | +| password_hash | VARCHAR(255) | Contraseña hasheada | +| nombre_completo | VARCHAR(100) | Nombre completo | +| activo | BOOLEAN | Si el usuario está activo | +| es_admin | BOOLEAN | Si es administrador | +| fecha_creacion | TIMESTAMP | Fecha de creación | +| ultimo_acceso | TIMESTAMP | Último login | + +### Tabla `usuario_base_datos` +Relaciona usuarios con bases de datos y sus permisos. + +| Campo | Tipo | Descripción | +|-------|------|-------------| +| id | SERIAL | ID único | +| usuario_id | INTEGER | FK a usuarios | +| base_datos_nombre | VARCHAR(255) | Nombre de la BD | +| puede_ver | BOOLEAN | Permiso de visualización | +| puede_descargar_backup | BOOLEAN | Permiso de descarga (futuro) | +| puede_restaurar | BOOLEAN | Permiso de restauración (futuro) | +| fecha_asignacion | TIMESTAMP | Fecha de asignación | + +## 🐛 Solución de Problemas + +### "No puedo iniciar sesión" +1. Verifica que el hash de contraseña sea correcto en la BD +2. Revisa los logs del servidor (terminal donde corre `npm run dev`) +3. Asegúrate de que PostgreSQL esté corriendo +4. Verifica las credenciales en `.env` + +### "No veo ninguna base de datos" +1. Si eres admin, deberías ver todas +2. Si eres usuario normal, verifica que tengas permisos asignados: + ```sql + SELECT * FROM usuario_base_datos WHERE usuario_id = TU_ID; + ``` + +### "Error de conexión a PostgreSQL" +1. Verifica que PostgreSQL esté corriendo: + ```bash + psql -h 10.0.20.152 -U postgres -c "SELECT version();" + ``` +2. Revisa el firewall del servidor +3. Verifica las credenciales en `.env` + +### "Invalid token" o "Session expired" +1. Cierra sesión y vuelve a iniciar +2. Limpia las cookies del navegador +3. Verifica que `JWT_SECRET` sea el mismo en `.env` + +## 🎯 Próximos Pasos Recomendados + +1. **Cambiar el JWT_SECRET** en `.env` a un valor aleatorio y seguro +2. **Actualizar la contraseña del admin** después del primer login +3. **Crear usuarios** para tu equipo +4. **Asignar permisos** según las responsabilidades de cada usuario +5. **Probar el sistema** con diferentes usuarios +6. **Configurar backups** de la base de datos PostgreSQL +7. **Documentar** los usuarios y sus permisos para tu organización + +## 📞 Soporte + +Para preguntas o problemas, contacta al equipo de desarrollo. + +--- + +**Desarrollado para Aduanasoft** - Panel de Gestión de Bases de Datos SCAII diff --git a/database/schema.sql b/database/schema.sql new file mode 100644 index 0000000..cd414e7 --- /dev/null +++ b/database/schema.sql @@ -0,0 +1,66 @@ +-- Schema para sistema de usuarios y permisos +-- Ejecutar en PostgreSQL (base de datos CONTROLDESK) + +-- Tabla de usuarios +CREATE TABLE IF NOT EXISTS usuarios ( + id SERIAL PRIMARY KEY, + username VARCHAR(50) UNIQUE NOT NULL, + email VARCHAR(100) UNIQUE NOT NULL, + password_hash VARCHAR(255) NOT NULL, + nombre_completo VARCHAR(100), + activo BOOLEAN DEFAULT true, + es_admin BOOLEAN DEFAULT false, + fecha_creacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + ultimo_acceso TIMESTAMP, + CONSTRAINT chk_username_length CHECK (char_length(username) >= 3) +); + +-- Tabla de permisos de usuario a bases de datos +CREATE TABLE IF NOT EXISTS usuario_base_datos ( + id SERIAL PRIMARY KEY, + usuario_id INTEGER NOT NULL REFERENCES usuarios(id) ON DELETE CASCADE, + base_datos_nombre VARCHAR(255) NOT NULL, -- Nombre de la base de datos (visible_name) + puede_ver BOOLEAN DEFAULT true, + puede_descargar_backup BOOLEAN DEFAULT false, + puede_restaurar BOOLEAN DEFAULT false, + fecha_asignacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + UNIQUE(usuario_id, base_datos_nombre) +); + +-- Tabla de sesiones +CREATE TABLE IF NOT EXISTS sesiones ( + id SERIAL PRIMARY KEY, + usuario_id INTEGER NOT NULL REFERENCES usuarios(id) ON DELETE CASCADE, + token VARCHAR(255) UNIQUE NOT NULL, + ip_address VARCHAR(50), + user_agent TEXT, + fecha_creacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + fecha_expiracion TIMESTAMP NOT NULL, + activa BOOLEAN DEFAULT true +); + +-- Índices para mejor rendimiento +CREATE INDEX IF NOT EXISTS idx_usuario_base_datos_usuario ON usuario_base_datos(usuario_id); +CREATE INDEX IF NOT EXISTS idx_usuario_base_datos_nombre ON usuario_base_datos(base_datos_nombre); +CREATE INDEX IF NOT EXISTS idx_usuarios_activo ON usuarios(activo); +CREATE INDEX IF NOT EXISTS idx_sesiones_token ON sesiones(token); +CREATE INDEX IF NOT EXISTS idx_sesiones_usuario ON sesiones(usuario_id); + +-- Insertar usuario admin por defecto +-- Password: Admin123! +-- Nota: Este hash es temporal, debes cambiarlo después del primer inicio de sesión +INSERT INTO usuarios (username, email, password_hash, nombre_completo, es_admin, activo) +VALUES ( + 'admin', + 'admin@aduanasoft.com', + '$2b$10$rKvvkYpNwF5J5K5J5J5J5uX5X5X5X5X5X5X5X5X5X5X5X5X5X5X5X', + 'Administrador del Sistema', + true, + true +) +ON CONFLICT (username) DO NOTHING; + +-- Comentarios en tablas +COMMENT ON TABLE usuarios IS 'Usuarios del sistema de gestión de bases de datos'; +COMMENT ON TABLE usuario_base_datos IS 'Permisos de acceso de usuarios a bases de datos específicas'; +COMMENT ON TABLE sesiones IS 'Sesiones activas de usuarios'; diff --git a/docker-compose.postgres.yml b/docker-compose.postgres.yml new file mode 100644 index 0000000..82a2fe3 --- /dev/null +++ b/docker-compose.postgres.yml @@ -0,0 +1,52 @@ +version: '3.8' + +services: + postgres: + image: postgres:15-alpine + container_name: aduanasoft-postgres + restart: unless-stopped + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: Control. + POSTGRES_DB: CONTROLDESK + PGDATA: /var/lib/postgresql/data/pgdata + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + - ./database/schema.sql:/docker-entrypoint-initdb.d/01-schema.sql + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 10s + timeout: 5s + retries: 5 + networks: + - aduanasoft-network + + # Tu aplicación Node.js (opcional, comentado por ahora) + # app: + # build: . + # container_name: aduanasoft-app + # restart: unless-stopped + # ports: + # - "3000:3000" + # environment: + # - NODE_ENV=production + # - DB_POSTGRES_HOST=postgres + # - DB_POSTGRES_PORT=5432 + # - DB_POSTGRES_USER=postgres + # - DB_POSTGRES_PASS=Control. + # - DB_POSTGRES_DB=CONTROLDESK + # depends_on: + # postgres: + # condition: service_healthy + # networks: + # - aduanasoft-network + +volumes: + postgres_data: + driver: local + +networks: + aduanasoft-network: + driver: bridge diff --git a/docs/USUARIOS.md b/docs/USUARIOS.md new file mode 100644 index 0000000..c8ff09f --- /dev/null +++ b/docs/USUARIOS.md @@ -0,0 +1,220 @@ +# Sistema de Gestión de Usuarios y Permisos + +Este sistema permite administrar usuarios y controlar qué bases de datos puede ver cada usuario. + +## Características + +- ✅ Autenticación con JWT +- ✅ Roles de usuario (Admin y Usuario normal) +- ✅ Permisos granulares por base de datos +- ✅ Gestión completa de usuarios (CRUD) +- ✅ Sesiones seguras con cookies HTTP-only +- ✅ Validación de contraseñas fuertes + +## Instalación + +### 1. Instalar dependencias + +```bash +npm install bcrypt jsonwebtoken pg @types/pg @types/bcrypt @types/jsonwebtoken +``` + +### 2. Configurar PostgreSQL + +Ejecuta el script SQL en tu base de datos PostgreSQL: + +```bash +psql -h 10.0.20.152 -U postgres -d CONTROLDESK -f database/schema.sql +``` + +O manualmente desde pgAdmin o DBeaver. + +### 3. Generar hash de contraseña para admin + +```bash +node scripts/generate-password-hash.js +``` + +Copia el hash generado y actualiza el script `database/schema.sql` con la contraseña real. + +### 4. Configurar variables de entorno + +Copia `.env.example` a `.env` y actualiza los valores: + +```bash +cp .env.example .env +``` + +Asegúrate de cambiar el `JWT_SECRET` por un valor seguro y aleatorio. + +## Uso + +### Primer Login + +1. Ve a `http://localhost:5173/login` +2. Usuario: `admin` +3. Contraseña: `Admin123!` (o la que hayas configurado) + +### Gestión de Usuarios (Solo Admins) + +1. Inicia sesión como admin +2. Ve a "Gestión de Usuarios" en el sidebar +3. Podrás: + - Crear nuevos usuarios + - Editar usuarios existentes + - Activar/desactivar usuarios + - Eliminar usuarios + - Asignar permisos de bases de datos + +### Asignar Permisos + +1. En la página de "Gestión de Usuarios", haz clic en el ícono de candado 🔒 del usuario +2. Verás la lista de todas las bases de datos disponibles +3. Haz clic en "Sin Acceso" para otorgar acceso a una base de datos +4. Haz clic en "Con Acceso" para revocar el acceso + +**Nota:** Los usuarios con rol de Administrador tienen acceso a todas las bases de datos automáticamente. + +### Permisos + +Cada usuario puede tener los siguientes permisos por base de datos: + +- **puede_ver**: Permite ver la base de datos en el panel +- **puede_descargar_backup**: Permite descargar archivos de backup (futuro) +- **puede_restaurar**: Permite ejecutar restauraciones (futuro) + +Actualmente, el sistema filtra las bases de datos que el usuario puede ver en: +- Panel principal (dashboard) +- Alertas críticas + +## Roles + +### Administrador +- Acceso completo a todas las bases de datos +- Puede gestionar usuarios y permisos +- Puede ver la sección "Gestión de Usuarios" + +### Usuario Normal +- Solo ve las bases de datos que le han sido asignadas +- No puede gestionar usuarios +- No tiene acceso a la sección de administración + +## Seguridad + +### Contraseñas + +Las contraseñas deben cumplir con los siguientes requisitos: +- Mínimo 8 caracteres +- Al menos una mayúscula +- Al menos una minúscula +- Al menos un número +- Al menos un carácter especial (!@#$%^&*(),.?":{}|<>) + +### Sesiones + +- Las sesiones usan JWT (JSON Web Tokens) +- Los tokens se almacenan en cookies HTTP-only (no accesibles desde JavaScript) +- Los tokens expiran después de 7 días +- En producción, las cookies usan el flag `secure` (solo HTTPS) + +## Estructura de Base de Datos + +### Tabla: `usuarios` +```sql +id SERIAL PRIMARY KEY +username VARCHAR(50) UNIQUE NOT NULL +email VARCHAR(100) UNIQUE NOT NULL +password_hash VARCHAR(255) NOT NULL +nombre_completo VARCHAR(100) +activo BOOLEAN DEFAULT true +es_admin BOOLEAN DEFAULT false +fecha_creacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP +ultimo_acceso TIMESTAMP +``` + +### Tabla: `usuario_base_datos` +```sql +id SERIAL PRIMARY KEY +usuario_id INTEGER NOT NULL (FK a usuarios) +base_datos_nombre VARCHAR(255) NOT NULL +puede_ver BOOLEAN DEFAULT true +puede_descargar_backup BOOLEAN DEFAULT false +puede_restaurar BOOLEAN DEFAULT false +fecha_asignacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP +``` + +### Tabla: `sesiones` +```sql +id SERIAL PRIMARY KEY +usuario_id INTEGER NOT NULL (FK a usuarios) +token VARCHAR(255) UNIQUE NOT NULL +ip_address VARCHAR(50) +user_agent TEXT +fecha_creacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP +fecha_expiracion TIMESTAMP NOT NULL +activa BOOLEAN DEFAULT true +``` + +## API + +### Rutas de Autenticación + +- `GET /login` - Página de inicio de sesión +- `POST /login?/login` - Procesar inicio de sesión +- `GET /logout` - Cerrar sesión + +### Rutas de Usuarios (Solo Admins) + +- `GET /usuarios` - Página de gestión de usuarios +- `POST /usuarios?/createUser` - Crear usuario +- `POST /usuarios?/updateUser` - Actualizar usuario +- `POST /usuarios?/deleteUser` - Eliminar usuario +- `POST /usuarios?/managePermissions` - Asignar/revocar permisos +- `GET /usuarios/api/permissions?userId=X` - Obtener permisos de un usuario + +## Troubleshooting + +### Error: "Could not connect to PostgreSQL" + +Verifica que: +1. PostgreSQL esté corriendo en el servidor +2. Las credenciales en `.env` sean correctas +3. El firewall permita conexiones al puerto 5432 +4. La base de datos `CONTROLDESK` exista + +### Error: "Invalid token" + +1. Cierra sesión y vuelve a iniciar sesión +2. Verifica que `JWT_SECRET` sea consistente +3. Limpia las cookies del navegador + +### Error: "User not found" + +1. Verifica que hayas ejecutado el script SQL +2. Revisa que el usuario admin se haya creado correctamente: + ```sql + SELECT * FROM usuarios WHERE username = 'admin'; + ``` + +### No puedo ver algunas bases de datos + +Si eres usuario normal (no admin): +1. Contacta al administrador para que te asigne permisos +2. Verifica en la base de datos: + ```sql + SELECT * FROM usuario_base_datos WHERE usuario_id = TU_ID; + ``` + +## Futuras Mejoras + +- [ ] Recuperación de contraseña por email +- [ ] Autenticación de dos factores (2FA) +- [ ] Registro de auditoría de acciones +- [ ] Permisos más granulares (lectura, escritura, eliminación) +- [ ] Grupos de usuarios +- [ ] API REST para integraciones +- [ ] Notificaciones por email de cambios de permisos + +## Soporte + +Para reportar problemas o sugerencias, contacta al equipo de desarrollo. diff --git a/package-lock.json b/package-lock.json index c0d5155..4e1b4b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,24 +8,46 @@ "name": "transmitiras-dashboard", "version": "0.0.1", "dependencies": { + "bcrypt": "^6.0.0", "bootstrap": "^5.3.3", "dotenv": "^16.4.5", - "mssql": "^10.0.2" + "jsonwebtoken": "^9.0.3", + "mssql": "^10.0.2", + "pg": "^8.18.0" }, "devDependencies": { "@sveltejs/adapter-auto": "^3.3.1", "@sveltejs/adapter-node": "^5.5.2", "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^4.0.0-next.0", + "@types/bcrypt": "^6.0.0", + "@types/jsonwebtoken": "^9.0.10", "@types/mssql": "^9.1.5", "@types/node": "^22.0.0", + "@types/pg": "^8.16.0", + "autoprefixer": "^10.4.24", + "postcss": "^8.5.6", "svelte": "^5.0.0-next.1", "svelte-check": "^3.6.0", + "tailwindcss": "^3.4.19", "tslib": "^2.4.1", "typescript": "^5.0.0", "vite": "^5.0.0" } }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@azure-rest/core-client": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/@azure-rest/core-client/-/core-client-2.5.1.tgz", @@ -737,6 +759,44 @@ "integrity": "sha512-WBu4ULVVxySLLzK1Ppq+OdfP+adRS4ntmDQT915rzDJ++i95gc2jZkM5B6LWEAwN3lGXpfie3yPABozdD3K3Vg==", "license": "BSD-3-Clause" }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@polka/url": { "version": "1.0.0-next.29", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", @@ -1390,6 +1450,16 @@ "integrity": "sha512-7qSgZbincDDDFyRweCIEvZULFAw5iz/DeunhvuxpL31nfntX3P4Yd4HkHBRg9H8CdqY1e5WFN1PZIz/REL9MVQ==", "license": "MIT" }, + "node_modules/@types/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", @@ -1404,6 +1474,24 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/mssql": { "version": "9.1.9", "resolved": "https://registry.npmjs.org/@types/mssql/-/mssql-9.1.9.tgz", @@ -1425,6 +1513,18 @@ "undici-types": "~6.21.0" } }, + "node_modules/@types/pg": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.16.0.tgz", + "integrity": "sha512-RmhMd/wD+CF8Dfo+cVIy3RR5cl8CyfXQ0tGgW6XBL8L4LM/UTEbNXYRbLwU6w+CgrKBNbrQWt4FUtTfaU5jSYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, "node_modules/@types/pug": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz", @@ -1496,6 +1596,13 @@ "node": ">= 14" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -1510,6 +1617,13 @@ "node": ">= 8" } }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, "node_modules/aria-query": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", @@ -1566,6 +1680,43 @@ "node": ">= 0.4" } }, + "node_modules/autoprefixer": { + "version": "10.4.24", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.24.tgz", + "integrity": "sha512-uHZg7N9ULTVbutaIsDRoUkoS8/h3bdsmVJYZ5l3wv8Cp/6UIIoRDm90hZ+BwxUj/hGBEzLxdHNSKuFpn8WOyZw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001766", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -1618,6 +1769,30 @@ ], "license": "MIT" }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", + "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.3.0", + "node-gyp-build": "^4.8.4" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -1686,6 +1861,40 @@ "node": ">=8" } }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -1789,6 +1998,37 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001769", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", + "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -1857,6 +2097,19 @@ "node": ">= 0.6" } }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -2029,6 +2282,20 @@ "dev": true, "license": "MIT" }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, "node_modules/dotenv": { "version": "16.6.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", @@ -2064,6 +2331,13 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/electron-to-chromium": { + "version": "1.5.286", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", + "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", + "dev": true, + "license": "ISC" + }, "node_modules/es-abstract": { "version": "1.24.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", @@ -2262,6 +2536,16 @@ "@esbuild/win32-x64": "0.21.5" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/esm-env": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", @@ -2304,6 +2588,33 @@ "node": ">=0.8.x" } }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -2332,6 +2643,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3167,6 +3492,16 @@ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "license": "MIT" }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/js-md4": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", @@ -3232,6 +3567,26 @@ "node": ">=6" } }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, "node_modules/locate-character": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", @@ -3300,6 +3655,30 @@ "node": ">= 0.4" } }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -3551,6 +3930,18 @@ "node": ">=16" } }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -3582,6 +3973,33 @@ "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", "license": "MIT" }, + "node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -3592,6 +4010,26 @@ "node": ">=0.10.0" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -3696,6 +4134,95 @@ "dev": true, "license": "MIT" }, + "node_modules/pg": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.18.0.tgz", + "integrity": "sha512-xqrUDL1b9MbkydY/s+VZ6v+xiMUmOUk7SS9d/1kpyQxoJ6U9AO1oIJyUWVZojbfe5Cc/oluutcgFG4L9RDP1iQ==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.11.0", + "pg-pool": "^3.11.0", + "pg-protocol": "^1.11.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.3.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz", + "integrity": "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.11.0.tgz", + "integrity": "sha512-kecgoJwhOpxYU21rZjULrmrBJ698U2RxXofKVzOn5UDj61BPj/qMb7diYUR1nLScCDbrztQFl1TaQZT0t1EtzQ==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.11.0.tgz", + "integrity": "sha512-MJYfvHwtGp870aeusDh+hg9apvOe2zmpZJpyt+BMtzUWlVqbhFmMK6bOBXLBUPd7iRtIF9fZplDc7KrPN3PN7w==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.11.0.tgz", + "integrity": "sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -3716,6 +4243,26 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -3754,6 +4301,176 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-5.1.0.tgz", + "integrity": "sha512-G5AJ+IX0aD0dygOE0yFZQ/huFFMSNneyfp0e3/bT05a8OfPC5FUoZRPfGijUdGOJNMewJiwzcHJXFafFzeKFVA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1", + "yaml": "^2.4.2" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -3763,6 +4480,37 @@ "node": ">= 0.6.0" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, "node_modules/readable-stream": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", @@ -3855,6 +4603,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rfdc": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", @@ -3933,6 +4692,30 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/sade": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", @@ -4215,6 +4998,15 @@ "node": ">=0.10.0" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/sprintf-js": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", @@ -4322,6 +5114,39 @@ "node": ">=8" } }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -4446,6 +5271,57 @@ } } }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/tarn": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz", @@ -4477,6 +5353,77 @@ "node": ">=18.17" } }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -4500,6 +5447,13 @@ "node": ">=6" } }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -4618,6 +5572,44 @@ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -4815,6 +5807,31 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yaml": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/zimmerframe": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", diff --git a/package.json b/package.json index 440f310..d7d4ce2 100644 --- a/package.json +++ b/package.json @@ -13,18 +13,27 @@ "@sveltejs/adapter-node": "^5.5.2", "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^4.0.0-next.0", + "@types/bcrypt": "^6.0.0", + "@types/jsonwebtoken": "^9.0.10", "@types/mssql": "^9.1.5", "@types/node": "^22.0.0", + "@types/pg": "^8.16.0", + "autoprefixer": "^10.4.24", + "postcss": "^8.5.6", "svelte": "^5.0.0-next.1", "svelte-check": "^3.6.0", + "tailwindcss": "^3.4.19", "tslib": "^2.4.1", "typescript": "^5.0.0", "vite": "^5.0.0" }, "dependencies": { + "bcrypt": "^6.0.0", "bootstrap": "^5.3.3", "dotenv": "^16.4.5", - "mssql": "^10.0.2" + "jsonwebtoken": "^9.0.3", + "mssql": "^10.0.2", + "pg": "^8.18.0" }, "type": "module" } diff --git a/postcss.config.cjs b/postcss.config.cjs new file mode 100644 index 0000000..5cbc2c7 --- /dev/null +++ b/postcss.config.cjs @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {} + } +}; diff --git a/scripts/generate-password-hash.js b/scripts/generate-password-hash.js new file mode 100644 index 0000000..233e232 --- /dev/null +++ b/scripts/generate-password-hash.js @@ -0,0 +1,18 @@ +// Script para generar hash de contraseña para el usuario admin +import bcrypt from 'bcrypt'; + +const password = 'Admin123!'; // Cambiar esta contraseña +const saltRounds = 10; + +bcrypt.hash(password, saltRounds, (err, hash) => { + if (err) { + console.error('Error generando hash:', err); + process.exit(1); + } + + console.log('\n=== Hash de Contraseña Generado ==='); + console.log('Contraseña:', password); + console.log('Hash:', hash); + console.log('\nUsa este hash en el archivo database/schema.sql'); + console.log('para actualizar la contraseña del usuario admin.\n'); +}); diff --git a/scripts/init-database.js b/scripts/init-database.js new file mode 100644 index 0000000..7d5bfd2 --- /dev/null +++ b/scripts/init-database.js @@ -0,0 +1,108 @@ +// Script para inicializar la base de datos PostgreSQL +import { Pool } from 'pg'; +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// Configuración de PostgreSQL (Docker local) +const pool = new Pool({ + host: 'localhost', + port: 5432, + database: 'CONTROLDESK', + user: 'postgres', + password: 'Control.', + max: 1 +}); + +async function initDatabase() { + console.log('================================='); + console.log('Inicializando Base de Datos'); + console.log('=================================\n'); + + try { + // Leer el archivo SQL + const schemaPath = path.join(__dirname, '..', 'database', 'schema.sql'); + console.log('1. Leyendo schema.sql...'); + const sql = fs.readFileSync(schemaPath, 'utf8'); + + // Conectar a PostgreSQL + console.log('2. Conectando a PostgreSQL...'); + const client = await pool.connect(); + console.log(' ✓ Conectado exitosamente\n'); + + // Ejecutar el script SQL + console.log('3. Ejecutando script SQL...'); + await client.query(sql); + console.log(' ✓ Tablas creadas exitosamente\n'); + + // Verificar las tablas creadas + console.log('4. Verificando tablas creadas...'); + const result = await client.query(` + SELECT table_name + FROM information_schema.tables + WHERE table_schema = 'public' + AND table_name IN ('usuarios', 'usuario_base_datos', 'sesiones') + ORDER BY table_name; + `); + + if (result.rows.length === 3) { + console.log(' ✓ Tablas verificadas:'); + result.rows.forEach(row => { + console.log(` - ${row.table_name}`); + }); + } else { + console.log(' ⚠ No todas las tablas fueron creadas'); + } + + // Verificar usuario admin + console.log('\n5. Verificando usuario admin...'); + const userResult = await client.query('SELECT username, email, es_admin FROM usuarios WHERE username = $1', ['admin']); + + if (userResult.rows.length > 0) { + console.log(' ✓ Usuario admin creado:'); + console.log(` - Username: ${userResult.rows[0].username}`); + console.log(` - Email: ${userResult.rows[0].email}`); + console.log(` - Es Admin: ${userResult.rows[0].es_admin}`); + } else { + console.log(' ⚠ Usuario admin no encontrado'); + } + + client.release(); + + console.log('\n================================='); + console.log('✓ Inicialización Completa'); + console.log('=================================\n'); + console.log('IMPORTANTE: Ahora debes actualizar la contraseña del admin:'); + console.log('1. Ejecuta: node scripts/generate-password-hash.js'); + console.log('2. Copia el hash generado'); + console.log('3. Ejecuta este SQL en PostgreSQL:'); + console.log(' UPDATE usuarios SET password_hash = \'TU_HASH_AQUI\' WHERE username = \'admin\';'); + console.log('\n4. Luego inicia el servidor: npm run dev'); + console.log('5. Ve a: http://localhost:5173/login'); + console.log(' Usuario: admin'); + console.log(' Contraseña: Admin123!\n'); + + } catch (error) { + console.error('\n❌ Error durante la inicialización:'); + console.error(error.message); + + if (error.code === 'ECONNREFUSED' || error.code === 'ETIMEDOUT') { + console.error('\n⚠ No se pudo conectar a PostgreSQL.'); + console.error('Verifica que:'); + console.error('1. PostgreSQL esté corriendo en Docker:'); + console.error(' docker-compose -f docker-compose.postgres.yml up -d'); + console.error('2. Verifica el estado: docker ps'); + console.error('3. Ve los logs: docker-compose -f docker-compose.postgres.yml logs postgres'); + } + + process.exit(1); + } finally { + await pool.end(); + } +} + +// Ejecutar +initDatabase(); diff --git a/scripts/init-users.sh b/scripts/init-users.sh new file mode 100644 index 0000000..062ce4c --- /dev/null +++ b/scripts/init-users.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +# Script de inicialización del sistema de usuarios +echo "===================================" +echo "Inicialización del Sistema de Usuarios" +echo "===================================" +echo "" + +# 1. Verificar si PostgreSQL está disponible +echo "1. Verificando conexión a PostgreSQL..." +if command -v psql &> /dev/null; then + echo " ✓ PostgreSQL CLI encontrado" +else + echo " ✗ PostgreSQL CLI no encontrado" + echo " Por favor, ejecuta el script SQL manualmente" + exit 1 +fi + +# 2. Generar hash de contraseña +echo "" +echo "2. Generando hash de contraseña para admin..." +node scripts/generate-password-hash.js + +# 3. Instrucciones +echo "" +echo "===================================" +echo "Pasos siguientes:" +echo "===================================" +echo "" +echo "1. Copia el hash generado arriba" +echo "" +echo "2. Edita el archivo database/schema.sql y reemplaza el hash temporal" +echo "" +echo "3. Ejecuta el script SQL en PostgreSQL:" +echo " psql -h 10.0.20.152 -U postgres -d CONTROLDESK -f database/schema.sql" +echo "" +echo "4. O ejecuta el SQL manualmente desde pgAdmin/DBeaver" +echo "" +echo "5. Copia .env.example a .env y configura las variables" +echo "" +echo "6. Inicia el servidor: npm run dev" +echo "" +echo "7. Ve a http://localhost:5173/login" +echo " Usuario: admin" +echo " Contraseña: Admin123! (o la que configuraste)" +echo "" +echo "===================================" diff --git a/src/app.css b/src/app.css index 2dac828..582da90 100644 --- a/src/app.css +++ b/src/app.css @@ -1,196 +1,8 @@ -:root { - /* Google Material Colors */ - --md-primary: #1a73e8; - --md-primary-hover: #1557b0; - --md-secondary: #5f6368; - --md-success: #1e8e3e; - --md-danger: #d93025; - --md-warning: #f9ab00; - - --md-bg: #f8f9fa; /* Light gray background */ - --md-surface: #ffffff; - - --md-text-primary: #202124; - --md-text-secondary: #5f6368; - - /* Elevation Shadows */ - --shadow-1: 0 1px 2px 0 rgba(60,64,67,0.3), 0 1px 3px 1px rgba(60,64,67,0.15); - --shadow-2: 0 1px 3px 0 rgba(60,64,67,0.3), 0 4px 8px 3px rgba(60,64,67,0.15); - - --border-radius: 8px; - --transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); -} - -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} +@tailwind base; +@tailwind components; +@tailwind utilities; body { - font-family: 'Inter', Roboto, sans-serif; - background-color: var(--md-bg); - color: var(--md-text-primary); - min-height: 100vh; -} - -h1, h2, h3, h4, h5, h6 { - color: var(--md-text-primary); - font-weight: 500; -} - -/* Navbar */ -.md-navbar { - background: var(--md-surface); - height: 64px; - box-shadow: var(--shadow-1); - z-index: 1040; -} - -.brand-text { - font-size: 1.25rem; - font-weight: 500; - color: var(--md-text-secondary); -} - -.brand-logo { - color: var(--md-primary); -} - -/* Sidebar */ -.md-sidebar { - background: var(--md-surface); - width: 260px; - height: 100vh; - position: fixed; - top: 0; - left: 0; - z-index: 1030; - padding-top: 64px; /* Matches navbar height */ - border-right: none; /* Shadow handles separation */ - box-shadow: 1px 0 0 rgba(0,0,0,0.12); /* Subtle divider */ - transition: transform 0.3s ease; -} - -.md-nav-link { - display: flex; - align-items: center; - padding: 0 24px; - height: 48px; - color: var(--md-text-secondary); - text-decoration: none; - font-weight: 500; - font-size: 0.875rem; - border-radius: 0 24px 24px 0; /* Material pill shape on one side or full pill */ - margin: 4px 12px 4px 0; - border: none; - background: transparent; - width: 100%; - text-align: left; - cursor: pointer; - transition: background 0.2s; -} - -.md-nav-link:hover { - background-color: #f1f3f4; - color: var(--md-text-primary); -} - -.md-nav-link.active { - background-color: #e8f0fe; - color: var(--md-primary); -} - -.md-nav-icon { - margin-right: 16px; - font-size: 24px; -} - -/* Main Content */ -.md-content-wrapper { - margin-left: 260px; - padding: 24px; - padding-top: 88px; /* 64px navbar + 24px spacing */ - min-height: 100vh; - transition: margin-left 0.3s ease; -} - -/* Cards */ -.md-card { - background: var(--md-surface); - border-radius: var(--border-radius); - padding: 24px; - box-shadow: var(--shadow-1); - margin-bottom: 24px; - border: none; -} - -.md-card-title { - font-size: 1.125rem; - color: var(--md-text-primary); - margin-bottom: 24px; -} - -/* Metrics */ -.md-metric-card { - background: var(--md-surface); - border-radius: var(--border-radius); - padding: 24px; - box-shadow: var(--shadow-1); - display: flex; - flex-direction: column; - height: 100%; -} - -.md-metric-value { - font-size: 2.25rem; - font-weight: 400; - color: var(--md-text-primary); - margin: 8px 0; -} - -.md-metric-label { - color: var(--md-text-secondary); - font-size: 0.875rem; -} - -.md-metric-icon { - color: var(--md-primary); - font-size: 32px; -} - -/* Tables */ -table.dataTable { - width: 100% !important; - border-collapse: collapse !important; -} - -table.dataTable thead th { - border-bottom: 1px solid rgba(0,0,0,0.12) !important; - color: var(--md-text-secondary); - font-weight: 500; - font-size: 0.75rem; - text-transform: uppercase; - padding: 16px !important; -} - -table.dataTable tbody td { - padding: 16px !important; - border-bottom: 1px solid rgba(0,0,0,0.06); - color: var(--md-text-primary); - font-size: 0.875rem; -} - -/* Responsive */ -@media (max-width: 992px) { - .md-sidebar { - transform: translateX(-100%); - } - .md-sidebar.show { - transform: translateX(0); - box-shadow: var(--shadow-2); - } - .md-content-wrapper { - margin-left: 0; - } + @apply bg-slate-100 text-slate-900 min-h-screen antialiased; + font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; } diff --git a/src/app.html b/src/app.html index a7d8143..24e5b48 100644 --- a/src/app.html +++ b/src/app.html @@ -2,42 +2,16 @@ - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + %sveltekit.head% diff --git a/src/lib/assets/icono.png b/src/lib/assets/icono.png new file mode 100644 index 0000000..7b4a555 Binary files /dev/null and b/src/lib/assets/icono.png differ diff --git a/src/lib/assets/logo.png b/src/lib/assets/logo.png new file mode 100644 index 0000000..853d117 Binary files /dev/null and b/src/lib/assets/logo.png differ diff --git a/src/lib/components/Navbar.svelte b/src/lib/components/Navbar.svelte index 96b5ebf..9ce79e0 100644 --- a/src/lib/components/Navbar.svelte +++ b/src/lib/components/Navbar.svelte @@ -2,36 +2,42 @@ import { page } from '$app/stores'; -