ar-saas 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +338 -314
- package/dist/cli.js +19 -0
- package/dist/generator.js +166 -55
- package/package.json +52 -50
- package/templates/backend/.env.example +67 -67
- package/templates/backend/.prettierrc +4 -4
- package/templates/backend/README.md +249 -168
- package/templates/backend/eslint.config.mjs +35 -35
- package/templates/backend/nest-cli.json +8 -8
- package/templates/backend/package-lock.json +10979 -10979
- package/templates/backend/package.json +88 -88
- package/templates/backend/src/app.controller.spec.ts +24 -24
- package/templates/backend/src/app.controller.ts +15 -15
- package/templates/backend/src/app.module.ts +40 -40
- package/templates/backend/src/app.service.ts +11 -11
- package/templates/backend/src/common/base/base.repository.ts +221 -221
- package/templates/backend/src/common/base/base.schema.ts +24 -24
- package/templates/backend/src/common/decorators/cookie.decorator.ts +9 -9
- package/templates/backend/src/common/decorators/current-user.decorator.ts +20 -20
- package/templates/backend/src/common/decorators/workspace-id.decorator.ts +14 -14
- package/templates/backend/src/common/filters/global-exception.filter.ts +61 -61
- package/templates/backend/src/common/guards/jwt-auth.guard.ts +5 -5
- package/templates/backend/src/common/interceptors/workspace-tenant.interceptor.ts +45 -45
- package/templates/backend/src/main.ts +51 -51
- package/templates/backend/src/modules/auth/auth.controller.ts +158 -158
- package/templates/backend/src/modules/auth/auth.module.ts +20 -20
- package/templates/backend/src/modules/auth/auth.service.ts +257 -257
- package/templates/backend/src/modules/auth/dto/forgot-password.dto.ts +9 -9
- package/templates/backend/src/modules/auth/dto/login.dto.ts +14 -14
- package/templates/backend/src/modules/auth/dto/refresh-token.dto.ts +12 -12
- package/templates/backend/src/modules/auth/dto/register.dto.ts +26 -26
- package/templates/backend/src/modules/auth/dto/reset-password.dto.ts +16 -16
- package/templates/backend/src/modules/auth/dto/verify-email.dto.ts +9 -9
- package/templates/backend/src/modules/auth/strategies/jwt.strategy.ts +43 -43
- package/templates/backend/src/modules/mail/mail.module.ts +9 -9
- package/templates/backend/src/modules/mail/mail.service.ts +141 -141
- package/templates/backend/src/modules/users/schemas/user.schema.ts +54 -54
- package/templates/backend/src/modules/users/users.module.ts +14 -14
- package/templates/backend/src/modules/users/users.repository.ts +51 -51
- package/templates/backend/src/modules/users/users.service.ts +104 -104
- package/templates/backend/src/modules/workspaces/schemas/workspace.schema.ts +26 -26
- package/templates/backend/src/modules/workspaces/workspaces.module.ts +16 -16
- package/templates/backend/src/modules/workspaces/workspaces.repository.ts +34 -34
- package/templates/backend/src/modules/workspaces/workspaces.service.ts +42 -42
- package/templates/backend/test/app.e2e-spec.ts +25 -25
- package/templates/backend/test/jest-e2e.json +9 -9
- package/templates/backend/tsconfig.build.json +4 -4
- package/templates/backend/tsconfig.json +26 -26
- package/templates/frontend/.env.local.example +1 -1
- package/templates/frontend/README.md +152 -0
- package/templates/frontend/components.json +20 -20
- package/templates/frontend/eslint.config.mjs +14 -14
- package/templates/frontend/next.config.ts +5 -5
- package/templates/frontend/package-lock.json +6722 -6722
- package/templates/frontend/package.json +48 -48
- package/templates/frontend/pnpm-lock.yaml +5012 -5012
- package/templates/frontend/pnpm-workspace.yaml +3 -3
- package/templates/frontend/postcss.config.mjs +7 -7
- package/templates/frontend/src/app/(auth)/forgot-password/page.tsx +84 -84
- package/templates/frontend/src/app/(auth)/layout.tsx +28 -28
- package/templates/frontend/src/app/(auth)/login/page.tsx +111 -111
- package/templates/frontend/src/app/(auth)/register/page.tsx +161 -161
- package/templates/frontend/src/app/(auth)/reset-password/page.tsx +120 -120
- package/templates/frontend/src/app/(auth)/verify-email/page.tsx +78 -78
- package/templates/frontend/src/app/(dashboard)/billing/page.tsx +111 -111
- package/templates/frontend/src/app/(dashboard)/dashboard/page.tsx +105 -105
- package/templates/frontend/src/app/(dashboard)/layout.tsx +38 -38
- package/templates/frontend/src/app/(dashboard)/profile/page.tsx +226 -226
- package/templates/frontend/src/app/(dashboard)/settings/page.tsx +156 -156
- package/templates/frontend/src/app/(dashboard)/team/page.tsx +178 -178
- package/templates/frontend/src/app/(legal)/privacy/page.tsx +127 -127
- package/templates/frontend/src/app/(legal)/terms/page.tsx +118 -118
- package/templates/frontend/src/app/globals.css +81 -81
- package/templates/frontend/src/app/layout.tsx +26 -26
- package/templates/frontend/src/app/page.tsx +5 -45
- package/templates/frontend/src/app/setup/page.tsx +371 -275
- package/templates/frontend/src/components/dashboard/header.tsx +89 -89
- package/templates/frontend/src/components/dashboard/sidebar.tsx +71 -71
- package/templates/frontend/src/components/dashboard/stat-card.tsx +34 -34
- package/templates/frontend/src/components/landing/faq.tsx +39 -39
- package/templates/frontend/src/components/landing/features.tsx +54 -54
- package/templates/frontend/src/components/landing/footer.tsx +76 -76
- package/templates/frontend/src/components/landing/hero.tsx +72 -72
- package/templates/frontend/src/components/landing/navbar.tsx +78 -78
- package/templates/frontend/src/components/landing/pricing.tsx +90 -90
- package/templates/frontend/src/components/ui/accordion.tsx +52 -52
- package/templates/frontend/src/components/ui/avatar.tsx +46 -46
- package/templates/frontend/src/components/ui/badge.tsx +30 -30
- package/templates/frontend/src/components/ui/button.tsx +52 -52
- package/templates/frontend/src/components/ui/card.tsx +50 -50
- package/templates/frontend/src/components/ui/checkbox.tsx +27 -27
- package/templates/frontend/src/components/ui/dialog.tsx +100 -100
- package/templates/frontend/src/components/ui/dropdown-menu.tsx +173 -173
- package/templates/frontend/src/components/ui/form.tsx +158 -158
- package/templates/frontend/src/components/ui/input.tsx +21 -21
- package/templates/frontend/src/components/ui/label.tsx +22 -22
- package/templates/frontend/src/components/ui/separator.tsx +25 -25
- package/templates/frontend/src/components/ui/skeleton.tsx +7 -7
- package/templates/frontend/src/components/ui/switch.tsx +28 -28
- package/templates/frontend/src/components/ui/tabs.tsx +54 -54
- package/templates/frontend/src/components/ui/textarea.tsx +20 -20
- package/templates/frontend/src/components/ui/toast.tsx +109 -109
- package/templates/frontend/src/components/ui/toaster.tsx +30 -30
- package/templates/frontend/src/config/site.ts +197 -197
- package/templates/frontend/src/hooks/use-toast.ts +116 -116
- package/templates/frontend/src/lib/api/auth.ts +39 -39
- package/templates/frontend/src/lib/api/client.ts +66 -66
- package/templates/frontend/src/lib/hooks/use-auth.ts +1 -1
- package/templates/frontend/src/lib/utils.ts +6 -6
- package/templates/frontend/src/providers/auth-provider.tsx +60 -60
- package/templates/frontend/src/types/api.ts +12 -12
- package/templates/frontend/src/types/auth.ts +27 -27
- package/templates/frontend/tsconfig.json +23 -23
|
@@ -1,168 +1,249 @@
|
|
|
1
|
-
<p align="center"><samp>
|
|
2
|
-
╔══════════════════════════════════╗<br/>
|
|
3
|
-
║ create-saas-ar-backend <br/>
|
|
4
|
-
║ AFIP · Mercado Pago · Auth · Mail <br/>
|
|
5
|
-
║ todo integrado en un comando <br/>
|
|
6
|
-
╚══════════════════════════════════╝<br/>
|
|
7
|
-
</samp></p>
|
|
8
|
-
|
|
9
|
-
<p align="center">
|
|
10
|
-
<strong>El backend SaaS que ojalá hubieras tenido cuando empezaste.</strong>
|
|
11
|
-
<br/>
|
|
12
|
-
AFIP, Mercado Pago, auth, multi-tenancy, emails — ya integrado. En vez de perder
|
|
13
|
-
3 semanas armando lo mismo de siempre, clonás esto y empezás a facturar.
|
|
14
|
-
</p>
|
|
15
|
-
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
## ¿Por qué existe esto?
|
|
19
|
-
|
|
20
|
-
Armar un SaaS en Argentina duele. Pasás semanas cableando autenticación con JWT,
|
|
21
|
-
aislamiento multi-tenant, envío de mails, validaciones, estructura de carpetas,
|
|
22
|
-
manejo de errores. Y cuando por fin terminás todo ese boilerplate, te das cuenta
|
|
23
|
-
de que todavía falta integrar AFIP para facturación electrónica y Mercado Pago
|
|
24
|
-
para cobrar. Otras 2 semanas.
|
|
25
|
-
|
|
26
|
-
`create-saas-ar` genera este repositorio completo **en un comando**. Viene con
|
|
27
|
-
todo lo genérico ya resuelto, más integraciones reales para operar en Argentina.
|
|
28
|
-
**Solo tenés que agregar tus módulos de negocio** siguiendo el patrón de
|
|
29
|
-
`.ai-docs/`.
|
|
30
|
-
|
|
31
|
-
## Stack
|
|
32
|
-
|
|
33
|
-
| Capa | Tecnología | Versión |
|
|
34
|
-
|---|---|---|
|
|
35
|
-
| Runtime | Node.js | 22 LTS |
|
|
36
|
-
| Lenguaje | TypeScript | 5.7 |
|
|
37
|
-
| Framework | NestJS | 11 |
|
|
38
|
-
| Base de datos | MongoDB | 8 |
|
|
39
|
-
| ODM | Mongoose | 9 |
|
|
40
|
-
| Auth (tokens) | JWT + cookies HttpOnly | — |
|
|
41
|
-
| Auth (passwords) | bcrypt | 5.x |
|
|
42
|
-
| Auth (passport) | passport + passport-jwt | 0.7 / 4.x |
|
|
43
|
-
| Validación | class-validator | 0.14 |
|
|
44
|
-
| Transformación | class-transformer | 0.5 |
|
|
45
|
-
| Emails | Resend | 4.x |
|
|
46
|
-
| Documentación API | Swagger | 11.x |
|
|
47
|
-
| Tareas programadas | @nestjs/schedule | 6.x |
|
|
48
|
-
| Testing | Jest | 30 |
|
|
49
|
-
| Formateo | Prettier | 3.x |
|
|
50
|
-
| Linting | ESLint | 9.x |
|
|
51
|
-
|
|
52
|
-
## Características principales
|
|
53
|
-
|
|
54
|
-
- **Multi-tenancy real** — Aislamiento por `workspaceId` garantizado en cada query. Un `WorkspaceTenantInterceptor` extrae el tenant del header `x-workspace-id` o del JWT. `BaseRepository` fuerza el filtro en toda operación. Imposible leakear datos entre workspaces por error humano.
|
|
55
|
-
|
|
56
|
-
- **Auth completo** — Registro, login, refresh token rotativo, email verification, password reset, change password. Access + refresh tokens en cookies `HttpOnly`, `Secure`, `SameSite=Strict`. Nunca en `localStorage` ni en el body.
|
|
57
|
-
|
|
58
|
-
- **BaseRepository genérico** — Soft delete, paginación, filtros dinámicos, agregaciones, conteo, upsert. Manejo automático de errores MongoDB (duplicate key, cast error). 12 métodos heredados por todos los repositorios.
|
|
59
|
-
|
|
60
|
-
- **DTOs con validación estricta** — `class-validator` con mensajes en español. `class-transformer` para coerción de tipos. `PartialType` de Swagger para updates. `forbidNonWhitelisted` + `whitelist` para rechazar campos no declarados.
|
|
61
|
-
|
|
62
|
-
- **Manejo de errores uniforme** — `GlobalExceptionFilter` transforma toda excepción a `{ statusCode, message, error, timestamp, path }`. Errores 5xx se loguean, 4xx no. Mensajes de validación concatenados y en español.
|
|
63
|
-
|
|
64
|
-
- **Emails con Resend** — Servicio de mail con métodos para verificación, reset de contraseña y bienvenida. Templates HTML inline. Preparado para reintentos con `@nestjs/schedule` y cola de emails fallidos.
|
|
65
|
-
|
|
66
|
-
- **Swagger automático** — `@ApiProperty` y `@ApiTags` en cada DTO y controller. Documentación interactiva en `/api/docs` sin configuración extra.
|
|
67
|
-
|
|
68
|
-
- **Soporte multi-agente IA** — El proyecto incluye `.ai-docs/` (fuente de verdad compartida entre herramientas), `.claude/CLAUDE.md` (leído automáticamente por Claude Code) y `.opencode/` (prompt + config para OpenCode/DeepSeek). Cada herramienta tiene tareas asignadas según su fortaleza: Claude Code para arquitectura y decisiones de seguridad, DeepSeek para tareas mecánicas como generar CRUD o tests.
|
|
69
|
-
|
|
70
|
-
## Cómo empezar
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
cd
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
1
|
+
<p align="center"><samp>
|
|
2
|
+
╔══════════════════════════════════╗<br/>
|
|
3
|
+
║ create-saas-ar-backend <br/>
|
|
4
|
+
║ AFIP · Mercado Pago · Auth · Mail <br/>
|
|
5
|
+
║ todo integrado en un comando <br/>
|
|
6
|
+
╚══════════════════════════════════╝<br/>
|
|
7
|
+
</samp></p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<strong>El backend SaaS que ojalá hubieras tenido cuando empezaste.</strong>
|
|
11
|
+
<br/>
|
|
12
|
+
AFIP, Mercado Pago, auth, multi-tenancy, emails — ya integrado. En vez de perder
|
|
13
|
+
3 semanas armando lo mismo de siempre, clonás esto y empezás a facturar.
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## ¿Por qué existe esto?
|
|
19
|
+
|
|
20
|
+
Armar un SaaS en Argentina duele. Pasás semanas cableando autenticación con JWT,
|
|
21
|
+
aislamiento multi-tenant, envío de mails, validaciones, estructura de carpetas,
|
|
22
|
+
manejo de errores. Y cuando por fin terminás todo ese boilerplate, te das cuenta
|
|
23
|
+
de que todavía falta integrar AFIP para facturación electrónica y Mercado Pago
|
|
24
|
+
para cobrar. Otras 2 semanas.
|
|
25
|
+
|
|
26
|
+
`create-saas-ar` genera este repositorio completo **en un comando**. Viene con
|
|
27
|
+
todo lo genérico ya resuelto, más integraciones reales para operar en Argentina.
|
|
28
|
+
**Solo tenés que agregar tus módulos de negocio** siguiendo el patrón de
|
|
29
|
+
`.ai-docs/`.
|
|
30
|
+
|
|
31
|
+
## Stack
|
|
32
|
+
|
|
33
|
+
| Capa | Tecnología | Versión |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| Runtime | Node.js | 22 LTS |
|
|
36
|
+
| Lenguaje | TypeScript | 5.7 |
|
|
37
|
+
| Framework | NestJS | 11 |
|
|
38
|
+
| Base de datos | MongoDB | 8 |
|
|
39
|
+
| ODM | Mongoose | 9 |
|
|
40
|
+
| Auth (tokens) | JWT + cookies HttpOnly | — |
|
|
41
|
+
| Auth (passwords) | bcrypt | 5.x |
|
|
42
|
+
| Auth (passport) | passport + passport-jwt | 0.7 / 4.x |
|
|
43
|
+
| Validación | class-validator | 0.14 |
|
|
44
|
+
| Transformación | class-transformer | 0.5 |
|
|
45
|
+
| Emails | Resend | 4.x |
|
|
46
|
+
| Documentación API | Swagger | 11.x |
|
|
47
|
+
| Tareas programadas | @nestjs/schedule | 6.x |
|
|
48
|
+
| Testing | Jest | 30 |
|
|
49
|
+
| Formateo | Prettier | 3.x |
|
|
50
|
+
| Linting | ESLint | 9.x |
|
|
51
|
+
|
|
52
|
+
## Características principales
|
|
53
|
+
|
|
54
|
+
- **Multi-tenancy real** — Aislamiento por `workspaceId` garantizado en cada query. Un `WorkspaceTenantInterceptor` extrae el tenant del header `x-workspace-id` o del JWT. `BaseRepository` fuerza el filtro en toda operación. Imposible leakear datos entre workspaces por error humano.
|
|
55
|
+
|
|
56
|
+
- **Auth completo** — Registro, login, refresh token rotativo, email verification, password reset, change password. Access + refresh tokens en cookies `HttpOnly`, `Secure`, `SameSite=Strict`. Nunca en `localStorage` ni en el body.
|
|
57
|
+
|
|
58
|
+
- **BaseRepository genérico** — Soft delete, paginación, filtros dinámicos, agregaciones, conteo, upsert. Manejo automático de errores MongoDB (duplicate key, cast error). 12 métodos heredados por todos los repositorios.
|
|
59
|
+
|
|
60
|
+
- **DTOs con validación estricta** — `class-validator` con mensajes en español. `class-transformer` para coerción de tipos. `PartialType` de Swagger para updates. `forbidNonWhitelisted` + `whitelist` para rechazar campos no declarados.
|
|
61
|
+
|
|
62
|
+
- **Manejo de errores uniforme** — `GlobalExceptionFilter` transforma toda excepción a `{ statusCode, message, error, timestamp, path }`. Errores 5xx se loguean, 4xx no. Mensajes de validación concatenados y en español.
|
|
63
|
+
|
|
64
|
+
- **Emails con Resend** — Servicio de mail con métodos para verificación, reset de contraseña y bienvenida. Templates HTML inline. Preparado para reintentos con `@nestjs/schedule` y cola de emails fallidos.
|
|
65
|
+
|
|
66
|
+
- **Swagger automático** — `@ApiProperty` y `@ApiTags` en cada DTO y controller. Documentación interactiva en `/api/docs` sin configuración extra.
|
|
67
|
+
|
|
68
|
+
- **Soporte multi-agente IA** — El proyecto incluye `.ai-docs/` (fuente de verdad compartida entre herramientas), `.claude/CLAUDE.md` (leído automáticamente por Claude Code) y `.opencode/` (prompt + config para OpenCode/DeepSeek). Cada herramienta tiene tareas asignadas según su fortaleza: Claude Code para arquitectura y decisiones de seguridad, DeepSeek para tareas mecánicas como generar CRUD o tests.
|
|
69
|
+
|
|
70
|
+
## Cómo empezar
|
|
71
|
+
|
|
72
|
+
### 1. Instalar dependencias
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
cd backend
|
|
76
|
+
npm install
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 2. Levantar MongoDB
|
|
80
|
+
|
|
81
|
+
El generador crea un `docker-compose.dev.yml` en la raíz del proyecto con MongoDB preconfigurado.
|
|
82
|
+
|
|
83
|
+
**Opción A — Docker (recomendado):**
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# Desde la raíz del proyecto (no desde /backend)
|
|
87
|
+
docker compose -f docker-compose.dev.yml up -d
|
|
88
|
+
|
|
89
|
+
# Verificar que está corriendo
|
|
90
|
+
docker ps
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
MongoDB queda disponible en `mongodb://localhost:27017`. Los datos persisten en un volumen Docker entre reinicios.
|
|
94
|
+
|
|
95
|
+
**Opción B — MongoDB Atlas (cloud gratuito):**
|
|
96
|
+
|
|
97
|
+
1. Crear cuenta en [mongodb.com/atlas](https://mongodb.com/atlas) (M0 Free Tier)
|
|
98
|
+
2. En **Database Access** → crear un usuario con contraseña
|
|
99
|
+
3. En **Network Access** → agregar tu IP (o `0.0.0.0/0` para desarrollo)
|
|
100
|
+
4. En **Connect** → "Connect your application" → copiar la URI de conexión
|
|
101
|
+
5. Reemplazar `<password>` en la URI con la contraseña del paso 2
|
|
102
|
+
6. Pegar la URI completa en `MONGODB_URI` del `.env`
|
|
103
|
+
|
|
104
|
+
**Opción C — MongoDB local sin Docker:**
|
|
105
|
+
|
|
106
|
+
Instalá MongoDB Community Edition siguiendo las instrucciones oficiales:
|
|
107
|
+
`docs.mongodb.com/manual/installation`
|
|
108
|
+
|
|
109
|
+
### 3. Configurar variables de entorno
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# El generador ya copió .env.example → .env automáticamente
|
|
113
|
+
# Completar los valores faltantes:
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Variables obligatorias:**
|
|
117
|
+
|
|
118
|
+
| Variable | Descripción |
|
|
119
|
+
|---|---|
|
|
120
|
+
| `MONGODB_URI` | URI de MongoDB. Local: `mongodb://localhost:27017/mi-saas` |
|
|
121
|
+
| `JWT_ACCESS_SECRET` | Secreto para access tokens — ver instrucción abajo |
|
|
122
|
+
| `JWT_REFRESH_SECRET` | Secreto para refresh tokens — usar valor distinto al anterior |
|
|
123
|
+
| `RESEND_API_KEY` | API key de Resend para enviar emails |
|
|
124
|
+
| `RESEND_FROM_EMAIL` | Email remitente verificado en Resend |
|
|
125
|
+
| `APP_URL` | URL del frontend: `http://localhost:3001` |
|
|
126
|
+
| `CORS_ORIGINS` | URL del frontend (misma que APP_URL): `http://localhost:3001` |
|
|
127
|
+
|
|
128
|
+
**Generar JWT secrets:**
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# Con Node.js
|
|
132
|
+
node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
|
|
133
|
+
|
|
134
|
+
# O con openssl
|
|
135
|
+
openssl rand -hex 64
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Ejecutar dos veces para obtener dos valores distintos: uno para `JWT_ACCESS_SECRET` y otro para `JWT_REFRESH_SECRET`.
|
|
139
|
+
|
|
140
|
+
**Configurar Resend (emails):**
|
|
141
|
+
|
|
142
|
+
1. Crear cuenta gratis en [resend.com](https://resend.com) (3 000 emails/mes gratis)
|
|
143
|
+
2. Ir a **API Keys** → "Create API Key" → copiar la key
|
|
144
|
+
3. Pegar en `RESEND_API_KEY`
|
|
145
|
+
4. Para desarrollo: podés usar el dominio sandbox de Resend (solo envía a tu propio email)
|
|
146
|
+
5. Para producción: agregar y verificar tu dominio en "Domains"
|
|
147
|
+
|
|
148
|
+
### 4. Levantar el servidor
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
npm run start:dev
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
El servidor corre en `http://localhost:3000/api`. Swagger en `http://localhost:3000/api/docs`.
|
|
155
|
+
|
|
156
|
+
**Verificar que todo funciona:**
|
|
157
|
+
|
|
158
|
+
- El log muestra `Connected to MongoDB successfully`
|
|
159
|
+
- `http://localhost:3000/api/docs` carga y lista los endpoints de auth
|
|
160
|
+
- `POST http://localhost:3000/api/auth/register` crea un usuario correctamente
|
|
161
|
+
|
|
162
|
+
**Errores frecuentes:**
|
|
163
|
+
|
|
164
|
+
| Error | Causa probable |
|
|
165
|
+
|---|---|
|
|
166
|
+
| `ECONNREFUSED 127.0.0.1:27017` | MongoDB no está corriendo — ejecutar `docker compose -f docker-compose.dev.yml up -d` |
|
|
167
|
+
| `secretOrPrivateKey must have a value` | `JWT_ACCESS_SECRET` o `JWT_REFRESH_SECRET` vacíos en `.env` |
|
|
168
|
+
| Error de CORS en el frontend | `CORS_ORIGINS` en `.env` no incluye `http://localhost:3001` |
|
|
169
|
+
| Emails no llegan | Verificar `RESEND_API_KEY` y que `RESEND_FROM_EMAIL` esté verificado en Resend |
|
|
170
|
+
|
|
171
|
+
## Comandos disponibles
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
npm run start:dev # Desarrollo con hot reload
|
|
175
|
+
npm run start:prod # Producción (build + node dist/main)
|
|
176
|
+
npm run build # Compilar TypeScript
|
|
177
|
+
npm run test # Tests unitarios
|
|
178
|
+
npm run test:e2e # Tests end-to-end
|
|
179
|
+
npm run test:cov # Tests con coverage
|
|
180
|
+
npm run lint # ESLint con fix automático
|
|
181
|
+
npm run format # Prettier en todo el proyecto
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Estructura del proyecto
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
src/
|
|
188
|
+
├── common/
|
|
189
|
+
│ ├── base/
|
|
190
|
+
│ │ ├── base.schema.ts # workspaceId, createdBy, deletedAt
|
|
191
|
+
│ │ └── base.repository.ts # findAll, findById, create, update, softDelete, paginate, aggregate...
|
|
192
|
+
│ ├── decorators/
|
|
193
|
+
│ │ ├── workspace-id.decorator.ts # @WorkspaceId()
|
|
194
|
+
│ │ └── current-user.decorator.ts # @CurrentUser()
|
|
195
|
+
│ ├── filters/
|
|
196
|
+
│ │ └── global-exception.filter.ts # 4xx/5xx → { statusCode, message, error, timestamp, path }
|
|
197
|
+
│ ├── guards/
|
|
198
|
+
│ │ └── jwt-auth.guard.ts # AuthGuard('jwt')
|
|
199
|
+
│ └── interceptors/
|
|
200
|
+
│ └── workspace-tenant.interceptor.ts # x-workspace-id header → request.workspaceId
|
|
201
|
+
├── modules/
|
|
202
|
+
│ ├── auth/ # Registro, login, refresh, email verification, password reset
|
|
203
|
+
│ ├── users/ # CRUD de usuarios
|
|
204
|
+
│ ├── workspaces/ # CRUD de workspaces
|
|
205
|
+
│ └── mail/ # Envío de emails con Resend (@Global)
|
|
206
|
+
├── app.module.ts
|
|
207
|
+
└── main.ts # bootstrap: ValidationPipe, cookieParser, CORS, Swagger
|
|
208
|
+
.ai-docs/ # Documentación compartida entre agentes AI y desarrolladores
|
|
209
|
+
├── architecture/ # overview, module-pattern, database-pattern
|
|
210
|
+
├── conventions/ # naming, error-handling, dto-validation
|
|
211
|
+
├── modules/ # auth, multi-tenancy, mail
|
|
212
|
+
└── examples/ # full-module-example (módulo Clientes completo), repository-example
|
|
213
|
+
.claude/ # Prompt y reglas para Claude Code
|
|
214
|
+
.opencode/ # Prompt y config para OpenCode/DeepSeek
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Documentación para agentes AI
|
|
218
|
+
|
|
219
|
+
Este proyecto está diseñado para que dos agentes de IA trabajen en paralelo:
|
|
220
|
+
|
|
221
|
+
| Herramienta | Config | Ideal para |
|
|
222
|
+
|---|---|---|
|
|
223
|
+
| **Claude Code** | `.claude/CLAUDE.md` | Arquitectura, auth avanzado, decisiones de seguridad, refactors |
|
|
224
|
+
| **OpenCode / DeepSeek** | `.opencode/config.json` + `system-prompt.md` | CRUD de módulos nuevos, DTOs, tests, renombrar archivos |
|
|
225
|
+
|
|
226
|
+
Ambos leen `.ai-docs/` como fuente de verdad. Las **8 reglas absolutas** están en `.claude/CLAUDE.md` y `.opencode/system-prompt.md`.
|
|
227
|
+
|
|
228
|
+
## Cómo crear un módulo nuevo
|
|
229
|
+
|
|
230
|
+
Seguí el checklist de 12 pasos en `.ai-docs/examples/full-module-example.md`. El resumen:
|
|
231
|
+
|
|
232
|
+
1. Crear carpeta `src/modules/{nombre}/` con `schemas/` y `dto/`
|
|
233
|
+
2. Schema → extiende `BaseSchema`
|
|
234
|
+
3. DTO Create → `class-validator` con mensajes en español
|
|
235
|
+
4. DTO Update → `extends PartialType(CreateXxxDto)`
|
|
236
|
+
5. Repository → extiende `BaseRepository`, métodos con `workspaceId` primero
|
|
237
|
+
6. Service → `create`, `findAll`, `findById`, `update`, `remove`
|
|
238
|
+
7. Controller → `@WorkspaceId()`, `@CurrentUser()`, nunca `@Req()`
|
|
239
|
+
8. Module → `MongooseModule.forFeature(...)`, exporta el service
|
|
240
|
+
9. Registrar en `AppModule`
|
|
241
|
+
10. `npm run build` → verificar que compila
|
|
242
|
+
11. Swagger → verificar en `/api/docs`
|
|
243
|
+
12. Tests → unitarios del service, e2e del controller
|
|
244
|
+
|
|
245
|
+
El archivo `.ai-docs/examples/full-module-example.md` tiene el módulo Clientes completo para copiar y adaptar.
|
|
246
|
+
|
|
247
|
+
## Licencia
|
|
248
|
+
|
|
249
|
+
Privado (UNLICENSED). Generado con [`create-saas-ar`](https://github.com/anomalyco/create-saas-ar).
|
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
import eslint from '@eslint/js';
|
|
3
|
-
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
|
|
4
|
-
import globals from 'globals';
|
|
5
|
-
import tseslint from 'typescript-eslint';
|
|
6
|
-
|
|
7
|
-
export default tseslint.config(
|
|
8
|
-
{
|
|
9
|
-
ignores: ['eslint.config.mjs'],
|
|
10
|
-
},
|
|
11
|
-
eslint.configs.recommended,
|
|
12
|
-
...tseslint.configs.recommendedTypeChecked,
|
|
13
|
-
eslintPluginPrettierRecommended,
|
|
14
|
-
{
|
|
15
|
-
languageOptions: {
|
|
16
|
-
globals: {
|
|
17
|
-
...globals.node,
|
|
18
|
-
...globals.jest,
|
|
19
|
-
},
|
|
20
|
-
sourceType: 'commonjs',
|
|
21
|
-
parserOptions: {
|
|
22
|
-
projectService: true,
|
|
23
|
-
tsconfigRootDir: import.meta.dirname,
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
rules: {
|
|
29
|
-
'@typescript-eslint/no-explicit-any': 'error',
|
|
30
|
-
'@typescript-eslint/no-floating-promises': 'warn',
|
|
31
|
-
'@typescript-eslint/no-unsafe-argument': 'warn',
|
|
32
|
-
'prettier/prettier': ['error', { endOfLine: 'auto' }],
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
);
|
|
1
|
+
// @ts-check
|
|
2
|
+
import eslint from '@eslint/js';
|
|
3
|
+
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
|
|
4
|
+
import globals from 'globals';
|
|
5
|
+
import tseslint from 'typescript-eslint';
|
|
6
|
+
|
|
7
|
+
export default tseslint.config(
|
|
8
|
+
{
|
|
9
|
+
ignores: ['eslint.config.mjs'],
|
|
10
|
+
},
|
|
11
|
+
eslint.configs.recommended,
|
|
12
|
+
...tseslint.configs.recommendedTypeChecked,
|
|
13
|
+
eslintPluginPrettierRecommended,
|
|
14
|
+
{
|
|
15
|
+
languageOptions: {
|
|
16
|
+
globals: {
|
|
17
|
+
...globals.node,
|
|
18
|
+
...globals.jest,
|
|
19
|
+
},
|
|
20
|
+
sourceType: 'commonjs',
|
|
21
|
+
parserOptions: {
|
|
22
|
+
projectService: true,
|
|
23
|
+
tsconfigRootDir: import.meta.dirname,
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
rules: {
|
|
29
|
+
'@typescript-eslint/no-explicit-any': 'error',
|
|
30
|
+
'@typescript-eslint/no-floating-promises': 'warn',
|
|
31
|
+
'@typescript-eslint/no-unsafe-argument': 'warn',
|
|
32
|
+
'prettier/prettier': ['error', { endOfLine: 'auto' }],
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json.schemastore.org/nest-cli",
|
|
3
|
-
"collection": "@nestjs/schematics",
|
|
4
|
-
"sourceRoot": "src",
|
|
5
|
-
"compilerOptions": {
|
|
6
|
-
"deleteOutDir": true
|
|
7
|
-
}
|
|
8
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/nest-cli",
|
|
3
|
+
"collection": "@nestjs/schematics",
|
|
4
|
+
"sourceRoot": "src",
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"deleteOutDir": true
|
|
7
|
+
}
|
|
8
|
+
}
|