Skip to content

Commit e2ab3be

Browse files
committed
refactor: migrate database layer from SQLite to TypeORM with PostgreSQL support
1 parent 5e502dc commit e2ab3be

43 files changed

Lines changed: 1857 additions & 922 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/AGENTS.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,20 @@ This file documents workspace-specific rules, patterns, and guidelines that all
1515
* **Metrics Security**: The metrics endpoints under `/api/metrics` must always require a valid `METRICS_KEY`. If `METRICS_KEY` is not set in the environment, the endpoints must respond with `403 Forbidden` rather than falling back to public access.
1616
* **Rate Limiting**: Rate limiting is configured at `/api/` using `express-rate-limit`. Do not bypass or remove this unless instructed. If adding new endpoints, ensure they are protected by the rate limiter.
1717
* **Security Headers**: `helmet` is used to enforce secure headers. Keep `contentSecurityPolicy: false` to allow inline CSS inside the generated SVG cards.
18+
* **Environment Secrets**: Do NOT attempt to read, write, or modify the `.env` file (or any other local environment files containing secrets/configurations) directly. Always output or present the required environment key templates to the user so they can configure them manually.
1819

1920
## Docker Guidelines
2021
* **Non-Root Execution**: The runner stage must run as the non-privileged `node` user (`USER node`).
2122
* **Precise COPY**: Avoid using trailing wildcards/globs in `COPY` commands (e.g. `COPY package.json pnpm-lock.yaml* ...`) if the files are known to exist. List them explicitly to avoid matching unintended files.
22-
* **Volume Mounts**: The persistent SQLite database is stored in `/usr/src/app/data`. Ensure this directory is declared as a `VOLUME` and has its owner set to `node` (`chown -R node:node /usr/src/app`).
2323

2424
## Codebase Patterns
25-
* **SQLite Database**: We use the native `sqlite3` driver in WAL mode for high concurrency. Run database modifications inside `db.serialize()` to ensure proper ordering.
25+
* **PostgreSQL Database**: We use PostgreSQL managed through TypeORM for security and concurrency. Avoid using raw SQL queries; instead, use the Active Record / Data Mapper repositories or QueryBuilder parameterized bindings to prevent SQL Injection.
2626
* **SVG Cards**: Cards are rendered directly as SVG strings in server code and cached for 2 hours. If returning an error on a card endpoint, always send the response as `Content-Type: image/svg+xml` containing an SVG representation of the error card (e.g., using `renderErrorCard(message)`), so it renders correctly inside `<img>` tags on GitHub.
2727
* **Package Manager**: Use `pnpm` exclusively. Never run `npm install` or `yarn` inside this workspace. Run test suite using `pnpm test`.
2828

2929
## Release & Version Management
3030
* **release-it**: The repository uses `release-it` to manage semantic versioning and automate changelog generation.
3131
* **Commands**: Run `pnpm release` from the `main` branch to trigger a new release.
3232
* **Conventional Commits**: Commit messages must follow the Conventional Commits specification (e.g. `feat: ...`, `fix: ...`, `chore: ...`) to enable automatic changelog generation.
33+
* **Documentation Synchronization**: AI coding agents MUST update all relevant markdown files (`README.md`, `CHANGELOG.md`, etc.) on every modification that changes architecture, configuration keys, or deployment steps to ensure documentation is always synchronized.
3334

.env.example

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,17 @@ PORT=3000
44
GITHUB_TOKEN=
55
# Key to protect access to the /api/metrics and /api/metrics/users endpoints
66
METRICS_KEY=
7+
8+
DB_HOST=
9+
DB_PORT=
10+
DB_DATABASE=
11+
DB_USERNAME=
12+
DB_PASSWORD=
13+
DB_SSL=
14+
DB_SYNCHRONIZE=
15+
16+
# Private statistics configuration status (set to false to enable/activate the feature, defaults to true)
17+
PRIVATE_STATS_COMING_SOON=true
18+
19+
# Frecuencia en horas para guardar instantáneas del historial de estadísticas del usuario
20+
STATS_HISTORY_FREQUENCY_HOURS=12

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ Thumbs.db
3737
# Testing
3838
coverage/
3939
.nyc_output/
40-
data/metrics.sqlite-shm
41-
data/metrics.sqlite-wal
4240

4341
# Agents Customization Skills
4442
.agents/skills/
43+
design-system/
44+
skills-lock.json

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@
22

33
Todos los cambios notables en este proyecto serán documentados en este archivo.
44

5+
## [Unreleased]
6+
7+
### 🏗️ Base de Datos
8+
- **Migración a PostgreSQL con TypeORM**: Reemplazo completo de la base de datos SQLite y su controlador de bajo nivel `sqlite3` por una arquitectura basada en TypeORM con PostgreSQL para mejorar la seguridad (prevención de inyección SQL), concurrencia y flexibilidad de despliegue.
9+
- **Entidades de Dominio e Infraestructura**: Mapeo completo de las tablas `global_metrics`, `user_metrics`, `request_log` y `user_tokens` mediante decoradores TypeORM.
10+
11+
### 🐳 Docker & Despliegue
12+
- **Simplificación del Contenedor**: Eliminación del entrypoint script `docker-entrypoint.sh` y la dependencia de volumen persistente local. El contenedor ahora se inicia directamente con el usuario no privilegiado `node`.
13+
14+
### 🔒 Seguridad
15+
- **Políticas de Secretos**: Modificación de las directrices en `AGENTS.md` prohibiendo la lectura/escritura del archivo `.env` por parte de agentes de IA para salvaguardar secretos locales de producción.
16+
517
## [1.1.0] - 2026-07-17
618

719
### ✨ Características

Dockerfile

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@ RUN pnpm run build
2222
# Stage 2: Runtime (Production)
2323
FROM node:24-alpine AS runner
2424

25-
# Install pnpm and su-exec (lightweight privilege-drop tool for Alpine)
26-
# su-exec is used in docker-entrypoint.sh to drop from root → node user at startup
27-
RUN npm install -g pnpm && apk add --no-cache su-exec
25+
# Install pnpm
26+
RUN npm install -g pnpm
2827

2928
WORKDIR /usr/src/app
3029

@@ -40,25 +39,16 @@ RUN pnpm install --prod --frozen-lockfile
4039
COPY --from=builder /usr/src/app/dist ./dist
4140
COPY public ./public
4241

43-
# Copy the entrypoint script that fixes volume ownership at startup
44-
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
45-
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
46-
47-
# Pre-create the data directory so the volume mount point exists
48-
RUN mkdir -p /usr/src/app/data && chown -R node:node /usr/src/app
49-
50-
# Declare the persistent volume directory for SQLite metrics database
51-
VOLUME /usr/src/app/data
42+
# Ensure the app files are owned by the node user
43+
RUN chown -R node:node /usr/src/app
5244

53-
# NOTE: We intentionally do NOT set USER node here.
54-
# The entrypoint script runs as root to fix mounted-volume permissions,
55-
# then drops privileges to 'node' via su-exec before starting the app.
45+
# Drop privileges to non-root 'node' user
46+
USER node
5647

5748
# Health check — uses wget (built into Alpine) to probe the /health endpoint.
58-
# --start-period gives the app time to initialize SQLite before probes begin.
5949
HEALTHCHECK --interval=30s --timeout=10s --start-period=20s --retries=3 \
6050
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
6151

6252
EXPOSE 3000
6353

64-
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
54+
CMD ["node", "dist/server.js"]

README.md

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,10 @@ src/
3737
├── adapters/ # Adaptadores de Interfaz (Controladores, Repositorios y Presentadores)
3838
│ ├── controllers/ # CardController, TokenController, MetricsController
3939
│ ├── presenters/ # statsCard, languagesCard, theme (Renderizadores de SVGs)
40-
│ └── repositories/ # SQLiteTokenRepository, SQLiteMetricsRepository, ApiGitHubRepository, CachedGitHubRepository
40+
│ └── repositories/ # TypeORMTokenRepository, TypeORMMetricsRepository, ApiGitHubRepository, CachedGitHubRepository
4141
└── infrastructure/ # Detalles técnicos concretos (Base de datos, Servidor Express, Criptografía)
42-
├── database/ # Inicialización de SQLite y migraciones
42+
├── database/ # Configuración de TypeORM con PostgreSQL y Entidades
43+
│ └── entities/ # Entidades de base de datos (GlobalMetric, UserMetric, etc.)
4344
├── express/ # Enrutamiento, middlewares y arranque de servidor Express
4445
├── security/ # Criptografía AES-256-GCM y validación de scopes
4546
└── server.ts # Entrypoint principal (Wrapper de importación limpio y relativo)
@@ -73,6 +74,8 @@ El proyecto utiliza alias `@/` apuntando al directorio `src/`. Esto previene la
7374
- `GITHUB_TOKEN`: Tu token de acceso personal de GitHub para evitar límites de tasa.
7475
- `METRICS_KEY`: Clave secreta obligatoria para poder acceder a los endpoints de analíticas (`/api/metrics`).
7576
- `TRUST_PROXY`: Número de saltos del proxy (por defecto `1`), útil para que el rate limit identifique correctamente las IPs detrás de Cloudflare, Nginx, etc.
77+
- `PRIVATE_STATS_COMING_SOON`: Estado de configuración de estadísticas privadas. Establécelo en `false` para habilitar y activar completamente la funcionalidad de registro/revocación de tokens (por defecto `true`).
78+
- `STATS_HISTORY_FREQUENCY_HOURS`: Frecuencia mínima en horas entre tomas de instantáneas del historial de estadísticas del usuario (por defecto `12`).
7679

7780
### Scripts de Desarrollo
7881

@@ -152,25 +155,32 @@ Este microservicio implementa las siguientes medidas de seguridad para entornos
152155

153156
## 🐳 Despliegue en Docker y Coolify
154157

155-
Este proyecto incluye un `Dockerfile` optimizado con builds en multi-etapa y configuración para correr bajo el usuario no root `node`.
158+
Este proyecto incluye un `Dockerfile` optimizado con builds en multi-etapa y configuración segura que se ejecuta bajo el usuario no root `node`.
156159

157160
### Pruebas Locales con Docker
158-
Para evitar perder el histórico de métricas (base de datos SQLite) cuando se recrea o actualiza el contenedor, debes montar un volumen persistente apuntando al directorio `/usr/src/app/data`:
161+
162+
Dado que la base de datos se ha migrado a PostgreSQL, el contenedor de la aplicación no requiere almacenamiento persistente en disco. Puedes enlazarlo a tu servidor de PostgreSQL local:
159163

160164
1. Construir la imagen:
161165
```bash
162166
docker build -t github-helpers .
163167
```
164-
2. Ejecutar el contenedor con volumen persistente:
168+
2. Ejecutar el contenedor pasando las credenciales de la base de datos en las variables de entorno:
165169
```bash
166-
docker run -d -p 3000:3000 --name github-helpers-app -v github-helpers-db:/usr/src/app/data --env-file .env github-helpers
170+
docker run -d -p 3000:3000 --name github-helpers-app --env-file .env github-helpers
167171
```
168172

169173
### Despliegue en Coolify
170174
1. Crea un nuevo recurso de tipo **Application** en tu panel de Coolify.
171175
2. Selecciona **GitHub Repository** como fuente y apunta a este repositorio.
172176
3. En la configuración de construcción, selecciona **Dockerfile**.
173177
4. Configura el puerto de exposición en el puerto `3000`.
174-
5. **Persistencia**: En la pestaña **Storages**, crea un volumen para montar en la ruta `/usr/src/app/data` (ej. `db-data:/usr/src/app/data`). Esto asegurará que tu base de datos SQLite no se pierda en cada despliegue.
175-
6. Agrega las variables de entorno en la pestaña `Environment Variables` (ej. `GITHUB_TOKEN`, `METRICS_KEY`).
176-
7. Haz clic en **Deploy**. Coolify leerá el `Dockerfile`, construirá el contenedor seguro de producción y lo pondrá en marcha con SSL automático.
178+
5. **Base de Datos**: Añade un servicio de base de datos **PostgreSQL** en Coolify.
179+
6. **Variables de Entorno**: Agrega en la pestaña `Environment Variables` los datos de acceso de la base de datos y tus tokens de seguridad:
180+
* `DB_HOST`: Host de tu base de datos PostgreSQL de Coolify.
181+
* `DB_PORT`: `5432`
182+
* `DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD`: Datos de tu base de datos PostgreSQL.
183+
* `DB_SSL`: `'true'` (si la base de datos requiere SSL).
184+
* `DB_SYNCHRONIZE`: `'true'` (si deseas que cree las tablas al iniciar la primera vez).
185+
* `GITHUB_TOKEN`, `METRICS_KEY`, `TRUST_PROXY`.
186+
7. Haz clic en **Deploy**. Coolify construirá el contenedor seguro de producción y lo pondrá en marcha con SSL automático.

data/metrics.sqlite

-92 KB
Binary file not shown.

docker-entrypoint.sh

Lines changed: 0 additions & 19 deletions
This file was deleted.

package.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,18 @@
2222
"express": "5.2.1",
2323
"express-rate-limit": "8.6.0",
2424
"helmet": "8.3.0",
25-
"sqlite3": "6.0.1"
25+
"pg": "^8.22.0",
26+
"reflect-metadata": "^0.2.2",
27+
"typeorm": "^1.1.0"
2628
},
2729
"devDependencies": {
2830
"@release-it/conventional-changelog": "11.0.1",
2931
"@types/cors": "2.8.19",
3032
"@types/express": "5.0.6",
3133
"@types/node": "26.1.1",
32-
"@typescript-eslint/eslint-plugin": "8.64.0",
33-
"@typescript-eslint/parser": "8.64.0",
34+
"@types/pg": "^8.20.0",
35+
"@typescript-eslint/eslint-plugin": "8.65.0",
36+
"@typescript-eslint/parser": "8.65.0",
3437
"eslint": "10.7.0",
3538
"eslint-config-prettier": "10.1.8",
3639
"prettier": "3.9.5",

0 commit comments

Comments
 (0)