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.
Files changed (114) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +338 -314
  3. package/dist/cli.js +19 -0
  4. package/dist/generator.js +166 -55
  5. package/package.json +52 -50
  6. package/templates/backend/.env.example +67 -67
  7. package/templates/backend/.prettierrc +4 -4
  8. package/templates/backend/README.md +249 -168
  9. package/templates/backend/eslint.config.mjs +35 -35
  10. package/templates/backend/nest-cli.json +8 -8
  11. package/templates/backend/package-lock.json +10979 -10979
  12. package/templates/backend/package.json +88 -88
  13. package/templates/backend/src/app.controller.spec.ts +24 -24
  14. package/templates/backend/src/app.controller.ts +15 -15
  15. package/templates/backend/src/app.module.ts +40 -40
  16. package/templates/backend/src/app.service.ts +11 -11
  17. package/templates/backend/src/common/base/base.repository.ts +221 -221
  18. package/templates/backend/src/common/base/base.schema.ts +24 -24
  19. package/templates/backend/src/common/decorators/cookie.decorator.ts +9 -9
  20. package/templates/backend/src/common/decorators/current-user.decorator.ts +20 -20
  21. package/templates/backend/src/common/decorators/workspace-id.decorator.ts +14 -14
  22. package/templates/backend/src/common/filters/global-exception.filter.ts +61 -61
  23. package/templates/backend/src/common/guards/jwt-auth.guard.ts +5 -5
  24. package/templates/backend/src/common/interceptors/workspace-tenant.interceptor.ts +45 -45
  25. package/templates/backend/src/main.ts +51 -51
  26. package/templates/backend/src/modules/auth/auth.controller.ts +158 -158
  27. package/templates/backend/src/modules/auth/auth.module.ts +20 -20
  28. package/templates/backend/src/modules/auth/auth.service.ts +257 -257
  29. package/templates/backend/src/modules/auth/dto/forgot-password.dto.ts +9 -9
  30. package/templates/backend/src/modules/auth/dto/login.dto.ts +14 -14
  31. package/templates/backend/src/modules/auth/dto/refresh-token.dto.ts +12 -12
  32. package/templates/backend/src/modules/auth/dto/register.dto.ts +26 -26
  33. package/templates/backend/src/modules/auth/dto/reset-password.dto.ts +16 -16
  34. package/templates/backend/src/modules/auth/dto/verify-email.dto.ts +9 -9
  35. package/templates/backend/src/modules/auth/strategies/jwt.strategy.ts +43 -43
  36. package/templates/backend/src/modules/mail/mail.module.ts +9 -9
  37. package/templates/backend/src/modules/mail/mail.service.ts +141 -141
  38. package/templates/backend/src/modules/users/schemas/user.schema.ts +54 -54
  39. package/templates/backend/src/modules/users/users.module.ts +14 -14
  40. package/templates/backend/src/modules/users/users.repository.ts +51 -51
  41. package/templates/backend/src/modules/users/users.service.ts +104 -104
  42. package/templates/backend/src/modules/workspaces/schemas/workspace.schema.ts +26 -26
  43. package/templates/backend/src/modules/workspaces/workspaces.module.ts +16 -16
  44. package/templates/backend/src/modules/workspaces/workspaces.repository.ts +34 -34
  45. package/templates/backend/src/modules/workspaces/workspaces.service.ts +42 -42
  46. package/templates/backend/test/app.e2e-spec.ts +25 -25
  47. package/templates/backend/test/jest-e2e.json +9 -9
  48. package/templates/backend/tsconfig.build.json +4 -4
  49. package/templates/backend/tsconfig.json +26 -26
  50. package/templates/frontend/.env.local.example +1 -1
  51. package/templates/frontend/README.md +152 -0
  52. package/templates/frontend/components.json +20 -20
  53. package/templates/frontend/eslint.config.mjs +14 -14
  54. package/templates/frontend/next.config.ts +5 -5
  55. package/templates/frontend/package-lock.json +6722 -6722
  56. package/templates/frontend/package.json +48 -48
  57. package/templates/frontend/pnpm-lock.yaml +5012 -5012
  58. package/templates/frontend/pnpm-workspace.yaml +3 -3
  59. package/templates/frontend/postcss.config.mjs +7 -7
  60. package/templates/frontend/src/app/(auth)/forgot-password/page.tsx +84 -84
  61. package/templates/frontend/src/app/(auth)/layout.tsx +28 -28
  62. package/templates/frontend/src/app/(auth)/login/page.tsx +111 -111
  63. package/templates/frontend/src/app/(auth)/register/page.tsx +161 -161
  64. package/templates/frontend/src/app/(auth)/reset-password/page.tsx +120 -120
  65. package/templates/frontend/src/app/(auth)/verify-email/page.tsx +78 -78
  66. package/templates/frontend/src/app/(dashboard)/billing/page.tsx +111 -111
  67. package/templates/frontend/src/app/(dashboard)/dashboard/page.tsx +105 -105
  68. package/templates/frontend/src/app/(dashboard)/layout.tsx +38 -38
  69. package/templates/frontend/src/app/(dashboard)/profile/page.tsx +226 -226
  70. package/templates/frontend/src/app/(dashboard)/settings/page.tsx +156 -156
  71. package/templates/frontend/src/app/(dashboard)/team/page.tsx +178 -178
  72. package/templates/frontend/src/app/(legal)/privacy/page.tsx +127 -127
  73. package/templates/frontend/src/app/(legal)/terms/page.tsx +118 -118
  74. package/templates/frontend/src/app/globals.css +81 -81
  75. package/templates/frontend/src/app/layout.tsx +26 -26
  76. package/templates/frontend/src/app/page.tsx +5 -45
  77. package/templates/frontend/src/app/setup/page.tsx +371 -275
  78. package/templates/frontend/src/components/dashboard/header.tsx +89 -89
  79. package/templates/frontend/src/components/dashboard/sidebar.tsx +71 -71
  80. package/templates/frontend/src/components/dashboard/stat-card.tsx +34 -34
  81. package/templates/frontend/src/components/landing/faq.tsx +39 -39
  82. package/templates/frontend/src/components/landing/features.tsx +54 -54
  83. package/templates/frontend/src/components/landing/footer.tsx +76 -76
  84. package/templates/frontend/src/components/landing/hero.tsx +72 -72
  85. package/templates/frontend/src/components/landing/navbar.tsx +78 -78
  86. package/templates/frontend/src/components/landing/pricing.tsx +90 -90
  87. package/templates/frontend/src/components/ui/accordion.tsx +52 -52
  88. package/templates/frontend/src/components/ui/avatar.tsx +46 -46
  89. package/templates/frontend/src/components/ui/badge.tsx +30 -30
  90. package/templates/frontend/src/components/ui/button.tsx +52 -52
  91. package/templates/frontend/src/components/ui/card.tsx +50 -50
  92. package/templates/frontend/src/components/ui/checkbox.tsx +27 -27
  93. package/templates/frontend/src/components/ui/dialog.tsx +100 -100
  94. package/templates/frontend/src/components/ui/dropdown-menu.tsx +173 -173
  95. package/templates/frontend/src/components/ui/form.tsx +158 -158
  96. package/templates/frontend/src/components/ui/input.tsx +21 -21
  97. package/templates/frontend/src/components/ui/label.tsx +22 -22
  98. package/templates/frontend/src/components/ui/separator.tsx +25 -25
  99. package/templates/frontend/src/components/ui/skeleton.tsx +7 -7
  100. package/templates/frontend/src/components/ui/switch.tsx +28 -28
  101. package/templates/frontend/src/components/ui/tabs.tsx +54 -54
  102. package/templates/frontend/src/components/ui/textarea.tsx +20 -20
  103. package/templates/frontend/src/components/ui/toast.tsx +109 -109
  104. package/templates/frontend/src/components/ui/toaster.tsx +30 -30
  105. package/templates/frontend/src/config/site.ts +197 -197
  106. package/templates/frontend/src/hooks/use-toast.ts +116 -116
  107. package/templates/frontend/src/lib/api/auth.ts +39 -39
  108. package/templates/frontend/src/lib/api/client.ts +66 -66
  109. package/templates/frontend/src/lib/hooks/use-auth.ts +1 -1
  110. package/templates/frontend/src/lib/utils.ts +6 -6
  111. package/templates/frontend/src/providers/auth-provider.tsx +60 -60
  112. package/templates/frontend/src/types/api.ts +12 -12
  113. package/templates/frontend/src/types/auth.ts +27 -27
  114. package/templates/frontend/tsconfig.json +23 -23
@@ -1,168 +1,249 @@
1
- <p align="center"><samp>
2
- ╔══════════════════════════════════╗<br/>
3
- ║&nbsp;&nbsp;create-saas-ar-backend&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>
4
- ║&nbsp;&nbsp;AFIP&nbsp;·&nbsp;Mercado&nbsp;Pago&nbsp;·&nbsp;Auth&nbsp;·&nbsp;Mail&nbsp;&nbsp;<br/>
5
- ║&nbsp;&nbsp;todo&nbsp;integrado&nbsp;en&nbsp;un&nbsp;comando&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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
- ```bash
73
- # 1. Clonar
74
- git clone <repo-url> mi-saas-backend
75
- cd mi-saas-backend
76
-
77
- # 2. Instalar dependencias
78
- npm install
79
-
80
- # 3. Configurar variables de entorno
81
- cp .env.example .env
82
- # Editar .env con tus valores (MONGODB_URI, JWT secrets, RESEND_API_KEY, etc.)
83
-
84
- # 4. Levantar en desarrollo
85
- npm run start:dev
86
- ```
87
-
88
- El servidor corre en `http://localhost:3000/api`. Swagger en `http://localhost:3000/api/docs`.
89
-
90
- ## Comandos disponibles
91
-
92
- ```bash
93
- npm run start:dev # Desarrollo con hot reload
94
- npm run start:prod # Producción (build + node dist/main)
95
- npm run build # Compilar TypeScript
96
- npm run test # Tests unitarios
97
- npm run test:e2e # Tests end-to-end
98
- npm run test:cov # Tests con coverage
99
- npm run lint # ESLint con fix automático
100
- npm run format # Prettier en todo el proyecto
101
- ```
102
-
103
- ## Estructura del proyecto
104
-
105
- ```
106
- src/
107
- ├── common/
108
- │ ├── base/
109
- │ │ ├── base.schema.ts # workspaceId, createdBy, deletedAt
110
- │ │ └── base.repository.ts # findAll, findById, create, update, softDelete, paginate, aggregate...
111
- │ ├── decorators/
112
- │ │ ├── workspace-id.decorator.ts # @WorkspaceId()
113
- │ │ └── current-user.decorator.ts # @CurrentUser()
114
- │ ├── filters/
115
- │ │ └── global-exception.filter.ts # 4xx/5xx → { statusCode, message, error, timestamp, path }
116
- │ ├── guards/
117
- │ │ └── jwt-auth.guard.ts # AuthGuard('jwt')
118
- │ └── interceptors/
119
- │ └── workspace-tenant.interceptor.ts # x-workspace-id header → request.workspaceId
120
- ├── modules/
121
- │ ├── auth/ # Registro, login, refresh, email verification, password reset
122
- │ ├── users/ # CRUD de usuarios
123
- │ ├── workspaces/ # CRUD de workspaces
124
- │ └── mail/ # Envío de emails con Resend (@Global)
125
- ├── app.module.ts
126
- └── main.ts # bootstrap: ValidationPipe, cookieParser, CORS, Swagger
127
- .ai-docs/ # Documentación compartida entre agentes AI y desarrolladores
128
- ├── architecture/ # overview, module-pattern, database-pattern
129
- ├── conventions/ # naming, error-handling, dto-validation
130
- ├── modules/ # auth, multi-tenancy, mail
131
- └── examples/ # full-module-example (módulo Clientes completo), repository-example
132
- .claude/ # Prompt y reglas para Claude Code
133
- .opencode/ # Prompt y config para OpenCode/DeepSeek
134
- ```
135
-
136
- ## Documentación para agentes AI
137
-
138
- Este proyecto está diseñado para que dos agentes de IA trabajen en paralelo:
139
-
140
- | Herramienta | Config | Ideal para |
141
- |---|---|---|
142
- | **Claude Code** | `.claude/CLAUDE.md` | Arquitectura, auth avanzado, decisiones de seguridad, refactors |
143
- | **OpenCode / DeepSeek** | `.opencode/config.json` + `system-prompt.md` | CRUD de módulos nuevos, DTOs, tests, renombrar archivos |
144
-
145
- Ambos leen `.ai-docs/` como fuente de verdad. Las **8 reglas absolutas** están en `.claude/CLAUDE.md` y `.opencode/system-prompt.md`.
146
-
147
- ## Cómo crear un módulo nuevo
148
-
149
- Seguí el checklist de 12 pasos en `.ai-docs/examples/full-module-example.md`. El resumen:
150
-
151
- 1. Crear carpeta `src/modules/{nombre}/` con `schemas/` y `dto/`
152
- 2. Schema → extiende `BaseSchema`
153
- 3. DTO Create → `class-validator` con mensajes en español
154
- 4. DTO Update `extends PartialType(CreateXxxDto)`
155
- 5. Repository → extiende `BaseRepository`, métodos con `workspaceId` primero
156
- 6. Service `create`, `findAll`, `findById`, `update`, `remove`
157
- 7. Controller → `@WorkspaceId()`, `@CurrentUser()`, nunca `@Req()`
158
- 8. Module `MongooseModule.forFeature(...)`, exporta el service
159
- 9. Registrar en `AppModule`
160
- 10. `npm run build` verificar que compila
161
- 11. Swagger → verificar en `/api/docs`
162
- 12. Tests → unitarios del service, e2e del controller
163
-
164
- El archivo `.ai-docs/examples/full-module-example.md` tiene el módulo Clientes completo para copiar y adaptar.
165
-
166
- ## Licencia
167
-
168
- Privado (UNLICENSED). Generado con [`create-saas-ar`](https://github.com/anomalyco/create-saas-ar).
1
+ <p align="center"><samp>
2
+ ╔══════════════════════════════════╗<br/>
3
+ ║&nbsp;&nbsp;create-saas-ar-backend&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>
4
+ ║&nbsp;&nbsp;AFIP&nbsp;·&nbsp;Mercado&nbsp;Pago&nbsp;·&nbsp;Auth&nbsp;·&nbsp;Mail&nbsp;&nbsp;<br/>
5
+ ║&nbsp;&nbsp;todo&nbsp;integrado&nbsp;en&nbsp;un&nbsp;comando&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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
+ }