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
package/dist/cli.js
CHANGED
|
@@ -85,6 +85,25 @@ async function runCli(defaultName) {
|
|
|
85
85
|
{ name: 'Después', value: 'later' },
|
|
86
86
|
],
|
|
87
87
|
},
|
|
88
|
+
{
|
|
89
|
+
type: 'list',
|
|
90
|
+
name: 'mongoMode',
|
|
91
|
+
message: '¿Dónde va a estar tu base de datos MongoDB?',
|
|
92
|
+
when: (answers) => answers.stack === 'backend' || answers.stack === 'backend-frontend',
|
|
93
|
+
choices: [
|
|
94
|
+
{ name: 'Local con Docker (recomendado para desarrollo)', value: 'docker' },
|
|
95
|
+
{ name: 'URL remota (MongoDB Atlas u otro)', value: 'remote' },
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
type: 'input',
|
|
100
|
+
name: 'mongoUri',
|
|
101
|
+
message: 'Ingresá la URI de MongoDB:',
|
|
102
|
+
when: (answers) => answers.mongoMode === 'remote',
|
|
103
|
+
validate: (input) => input.startsWith('mongodb://') || input.startsWith('mongodb+srv://')
|
|
104
|
+
? true
|
|
105
|
+
: 'La URI debe comenzar con mongodb:// o mongodb+srv://',
|
|
106
|
+
},
|
|
88
107
|
]);
|
|
89
108
|
return answers;
|
|
90
109
|
}
|
package/dist/generator.js
CHANGED
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.generate = generate;
|
|
7
7
|
const path_1 = __importDefault(require("path"));
|
|
8
8
|
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const child_process_1 = require("child_process");
|
|
9
10
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
10
11
|
const chalk_1 = __importDefault(require("chalk"));
|
|
11
12
|
const ora_1 = __importDefault(require("ora"));
|
|
@@ -62,13 +63,30 @@ async function generate(config) {
|
|
|
62
63
|
}
|
|
63
64
|
spinner.text = 'Generando configuración de deploy...';
|
|
64
65
|
generateDeployConfig(projectDir, config);
|
|
66
|
+
spinner.text = 'Generando entorno de desarrollo...';
|
|
67
|
+
generateDevConfig(projectDir, config);
|
|
65
68
|
spinner.text = 'Configurando variables de entorno...';
|
|
66
69
|
setupEnvFiles(projectDir, config);
|
|
70
|
+
let dockerStarted = false;
|
|
71
|
+
if (hasBackend) {
|
|
72
|
+
if (config.mongoMode === 'remote' && config.mongoUri) {
|
|
73
|
+
patchMongoUri(projectDir, config.mongoUri);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
const dockerAvailable = await detectDocker();
|
|
77
|
+
if (dockerAvailable) {
|
|
78
|
+
dockerStarted = await startDockerMongo(projectDir, config, spinner);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
printNoDockerWarning();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
67
85
|
spinner.succeed(chalk_1.default.green(`Proyecto ${chalk_1.default.bold(config.projectName)} creado`));
|
|
68
86
|
console.log();
|
|
69
87
|
console.log(chalk_1.default.cyan(` Sitio: ${chalk_1.default.bold(config.siteTitle)}`));
|
|
70
88
|
console.log(chalk_1.default.gray(` "${config.siteTagline}"`));
|
|
71
|
-
printNextSteps(config);
|
|
89
|
+
printNextSteps(config, dockerStarted);
|
|
72
90
|
}
|
|
73
91
|
function processDir(dir, config) {
|
|
74
92
|
for (const file of collectTextFiles(dir)) {
|
|
@@ -124,76 +142,101 @@ function generateDeployConfig(projectDir, config) {
|
|
|
124
142
|
fs_1.default.writeFileSync(path_1.default.join(projectDir, 'fly.toml'), buildFlyConfig(config), 'utf-8');
|
|
125
143
|
}
|
|
126
144
|
}
|
|
145
|
+
function generateDevConfig(projectDir, config) {
|
|
146
|
+
const hasBackend = config.stack === 'backend' || config.stack === 'backend-frontend';
|
|
147
|
+
if (!hasBackend)
|
|
148
|
+
return;
|
|
149
|
+
fs_1.default.writeFileSync(path_1.default.join(projectDir, 'docker-compose.dev.yml'), buildDockerComposeDev(config), 'utf-8');
|
|
150
|
+
}
|
|
151
|
+
function buildDockerComposeDev(config) {
|
|
152
|
+
return `# Base de datos local para desarrollo
|
|
153
|
+
# Levantá con: docker compose -f docker-compose.dev.yml up -d
|
|
154
|
+
# Para usar una BD remota (ej. MongoDB Atlas), cambiá MONGODB_URI en backend/.env
|
|
155
|
+
|
|
156
|
+
services:
|
|
157
|
+
mongodb:
|
|
158
|
+
image: mongo:7
|
|
159
|
+
container_name: ${config.projectName}-mongo-dev
|
|
160
|
+
ports:
|
|
161
|
+
- "27017:27017"
|
|
162
|
+
volumes:
|
|
163
|
+
- ${config.projectName}_mongo_dev:/data/db
|
|
164
|
+
restart: unless-stopped
|
|
165
|
+
|
|
166
|
+
volumes:
|
|
167
|
+
${config.projectName}_mongo_dev:
|
|
168
|
+
`;
|
|
169
|
+
}
|
|
127
170
|
function buildDockerCompose(config) {
|
|
128
171
|
const hasBackend = config.stack === 'backend' || config.stack === 'backend-frontend';
|
|
129
172
|
const hasFrontend = config.stack === 'frontend' || config.stack === 'backend-frontend';
|
|
130
173
|
const services = [];
|
|
131
174
|
if (hasBackend) {
|
|
132
|
-
services.push(` backend:
|
|
133
|
-
build: ./backend
|
|
134
|
-
ports:
|
|
135
|
-
- "3000:3000"
|
|
136
|
-
env_file:
|
|
137
|
-
- ./backend/.env
|
|
138
|
-
depends_on:
|
|
139
|
-
- mongodb
|
|
140
|
-
restart: unless-stopped
|
|
141
|
-
|
|
142
|
-
mongodb:
|
|
143
|
-
image: mongo:7
|
|
144
|
-
ports:
|
|
145
|
-
- "27017:27017"
|
|
146
|
-
volumes:
|
|
147
|
-
- mongodb_data:/data/db
|
|
175
|
+
services.push(` backend:
|
|
176
|
+
build: ./backend
|
|
177
|
+
ports:
|
|
178
|
+
- "3000:3000"
|
|
179
|
+
env_file:
|
|
180
|
+
- ./backend/.env
|
|
181
|
+
depends_on:
|
|
182
|
+
- mongodb
|
|
183
|
+
restart: unless-stopped
|
|
184
|
+
|
|
185
|
+
mongodb:
|
|
186
|
+
image: mongo:7
|
|
187
|
+
ports:
|
|
188
|
+
- "27017:27017"
|
|
189
|
+
volumes:
|
|
190
|
+
- mongodb_data:/data/db
|
|
148
191
|
restart: unless-stopped`);
|
|
149
192
|
}
|
|
150
193
|
if (hasFrontend) {
|
|
151
|
-
services.push(` frontend:
|
|
152
|
-
build: ./frontend
|
|
153
|
-
ports:
|
|
154
|
-
- "3001:3000"
|
|
155
|
-
env_file:
|
|
156
|
-
- ./frontend/.env.local
|
|
157
|
-
environment:
|
|
158
|
-
- NEXT_PUBLIC_API_URL=http://backend:3000
|
|
194
|
+
services.push(` frontend:
|
|
195
|
+
build: ./frontend
|
|
196
|
+
ports:
|
|
197
|
+
- "3001:3000"
|
|
198
|
+
env_file:
|
|
199
|
+
- ./frontend/.env.local
|
|
200
|
+
environment:
|
|
201
|
+
- NEXT_PUBLIC_API_URL=http://backend:3000
|
|
159
202
|
${hasBackend ? 'depends_on:\n - backend\n ' : ''}restart: unless-stopped`);
|
|
160
203
|
}
|
|
161
204
|
const volumes = hasBackend ? '\nvolumes:\n mongodb_data:' : '';
|
|
162
|
-
return `version: '3.8'
|
|
163
|
-
|
|
164
|
-
services:
|
|
165
|
-
${services.join('\n\n')}
|
|
166
|
-
${volumes}
|
|
205
|
+
return `version: '3.8'
|
|
206
|
+
|
|
207
|
+
services:
|
|
208
|
+
${services.join('\n\n')}
|
|
209
|
+
${volumes}
|
|
167
210
|
`;
|
|
168
211
|
}
|
|
169
212
|
function buildRailwayConfig() {
|
|
170
|
-
return `[build]
|
|
171
|
-
builder = "nixpacks"
|
|
172
|
-
|
|
173
|
-
[deploy]
|
|
174
|
-
startCommand = "npm run start:prod"
|
|
175
|
-
healthcheckPath = "/api/health"
|
|
176
|
-
healthcheckTimeout = 30
|
|
177
|
-
restartPolicyType = "on_failure"
|
|
213
|
+
return `[build]
|
|
214
|
+
builder = "nixpacks"
|
|
215
|
+
|
|
216
|
+
[deploy]
|
|
217
|
+
startCommand = "npm run start:prod"
|
|
218
|
+
healthcheckPath = "/api/health"
|
|
219
|
+
healthcheckTimeout = 30
|
|
220
|
+
restartPolicyType = "on_failure"
|
|
178
221
|
`;
|
|
179
222
|
}
|
|
180
223
|
function buildFlyConfig(config) {
|
|
181
|
-
return `app = "${config.projectName}"
|
|
182
|
-
primary_region = "gru"
|
|
183
|
-
|
|
184
|
-
[build]
|
|
185
|
-
|
|
186
|
-
[http_service]
|
|
187
|
-
internal_port = 3000
|
|
188
|
-
force_https = true
|
|
189
|
-
auto_stop_machines = true
|
|
190
|
-
auto_start_machines = true
|
|
191
|
-
min_machines_running = 0
|
|
192
|
-
|
|
193
|
-
[[vm]]
|
|
194
|
-
memory = "256mb"
|
|
195
|
-
cpu_kind = "shared"
|
|
196
|
-
cpus = 1
|
|
224
|
+
return `app = "${config.projectName}"
|
|
225
|
+
primary_region = "gru"
|
|
226
|
+
|
|
227
|
+
[build]
|
|
228
|
+
|
|
229
|
+
[http_service]
|
|
230
|
+
internal_port = 3000
|
|
231
|
+
force_https = true
|
|
232
|
+
auto_stop_machines = true
|
|
233
|
+
auto_start_machines = true
|
|
234
|
+
min_machines_running = 0
|
|
235
|
+
|
|
236
|
+
[[vm]]
|
|
237
|
+
memory = "256mb"
|
|
238
|
+
cpu_kind = "shared"
|
|
239
|
+
cpus = 1
|
|
197
240
|
`;
|
|
198
241
|
}
|
|
199
242
|
function setupEnvFiles(projectDir, config) {
|
|
@@ -205,6 +248,14 @@ function setupEnvFiles(projectDir, config) {
|
|
|
205
248
|
if (fs_1.default.existsSync(example) && !fs_1.default.existsSync(dest)) {
|
|
206
249
|
fs_extra_1.default.copySync(example, dest);
|
|
207
250
|
}
|
|
251
|
+
// Set default local URI; will be overwritten by patchMongoUri if remote mode
|
|
252
|
+
if (fs_1.default.existsSync(dest)) {
|
|
253
|
+
let envContent = fs_1.default.readFileSync(dest, 'utf-8');
|
|
254
|
+
if (!envContent.includes('MONGODB_URI')) {
|
|
255
|
+
envContent += `\n# MongoDB local (docker compose -f docker-compose.dev.yml up -d)\n# Para usar Atlas u otra BD remota, reemplazá esta URI\nMONGODB_URI=mongodb://localhost:27017/${config.projectName}\n`;
|
|
256
|
+
fs_1.default.writeFileSync(dest, envContent, 'utf-8');
|
|
257
|
+
}
|
|
258
|
+
}
|
|
208
259
|
}
|
|
209
260
|
if (hasFrontend) {
|
|
210
261
|
const example = path_1.default.join(projectDir, 'frontend', '.env.local.example');
|
|
@@ -214,13 +265,72 @@ function setupEnvFiles(projectDir, config) {
|
|
|
214
265
|
}
|
|
215
266
|
}
|
|
216
267
|
}
|
|
217
|
-
function
|
|
268
|
+
function patchMongoUri(projectDir, uri) {
|
|
269
|
+
const envPath = path_1.default.join(projectDir, 'backend', '.env');
|
|
270
|
+
if (!fs_1.default.existsSync(envPath))
|
|
271
|
+
return;
|
|
272
|
+
let content = fs_1.default.readFileSync(envPath, 'utf-8');
|
|
273
|
+
content = content.replace(/^MONGODB_URI=.*$/m, `MONGODB_URI=${uri}`);
|
|
274
|
+
if (!content.includes('MONGODB_URI')) {
|
|
275
|
+
content += `\nMONGODB_URI=${uri}\n`;
|
|
276
|
+
}
|
|
277
|
+
fs_1.default.writeFileSync(envPath, content, 'utf-8');
|
|
278
|
+
}
|
|
279
|
+
async function detectDocker() {
|
|
280
|
+
return new Promise((resolve) => {
|
|
281
|
+
(0, child_process_1.exec)('docker --version', (err) => resolve(!err));
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
async function startDockerMongo(projectDir, config, spinner) {
|
|
285
|
+
spinner.text = 'Levantando MongoDB local con Docker...';
|
|
286
|
+
return new Promise((resolve) => {
|
|
287
|
+
(0, child_process_1.exec)('docker compose -f docker-compose.dev.yml up -d', { cwd: projectDir }, (err, _stdout, stderr) => {
|
|
288
|
+
if (err) {
|
|
289
|
+
spinner.warn(chalk_1.default.yellow('No se pudo levantar MongoDB automáticamente'));
|
|
290
|
+
console.log(chalk_1.default.gray(` Podés levantarlo manualmente: cd ${config.projectName} && docker compose -f docker-compose.dev.yml up -d`));
|
|
291
|
+
if (stderr)
|
|
292
|
+
console.log(chalk_1.default.gray(` Error: ${stderr.trim()}`));
|
|
293
|
+
resolve(false);
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
resolve(true);
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
function printNoDockerWarning() {
|
|
302
|
+
console.log();
|
|
303
|
+
console.log(chalk_1.default.yellow(' ⚠ Docker no está instalado o no está corriendo.'));
|
|
304
|
+
console.log(chalk_1.default.gray(' Opciones para la base de datos:'));
|
|
305
|
+
console.log(chalk_1.default.gray(' 1. Instalá Docker Desktop → https://docs.docker.com/get-docker/'));
|
|
306
|
+
console.log(chalk_1.default.gray(' Luego: docker compose -f docker-compose.dev.yml up -d'));
|
|
307
|
+
console.log(chalk_1.default.gray(' 2. Usá MongoDB Atlas (gratis) → https://www.mongodb.com/atlas'));
|
|
308
|
+
console.log(chalk_1.default.gray(' Y editá MONGODB_URI en backend/.env'));
|
|
309
|
+
}
|
|
310
|
+
function printNextSteps(config, dockerStarted) {
|
|
218
311
|
const hasBackend = config.stack === 'backend' || config.stack === 'backend-frontend';
|
|
219
312
|
const hasFrontend = config.stack === 'frontend' || config.stack === 'backend-frontend';
|
|
220
313
|
console.log();
|
|
221
314
|
console.log(chalk_1.default.bold('Próximos pasos:'));
|
|
222
315
|
if (hasBackend) {
|
|
223
316
|
console.log();
|
|
317
|
+
if (config.mongoMode === 'remote') {
|
|
318
|
+
console.log(chalk_1.default.bold.white(' 1. Base de datos:'));
|
|
319
|
+
console.log(chalk_1.default.green(` ✔ MONGODB_URI configurada en backend/.env`));
|
|
320
|
+
console.log(chalk_1.default.gray(' # Verificá que la URI sea correcta antes de iniciar'));
|
|
321
|
+
}
|
|
322
|
+
else if (dockerStarted) {
|
|
323
|
+
console.log(chalk_1.default.bold.white(' 1. Base de datos:'));
|
|
324
|
+
console.log(chalk_1.default.green(' ✔ MongoDB local levantado con Docker'));
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
console.log(chalk_1.default.bold.white(' 1. Levantá la base de datos:'));
|
|
328
|
+
console.log(chalk_1.default.cyan(` cd ${config.projectName}`));
|
|
329
|
+
console.log(chalk_1.default.cyan(' docker compose -f docker-compose.dev.yml up -d'));
|
|
330
|
+
console.log(chalk_1.default.gray(' # O configurá una URL remota en backend/.env'));
|
|
331
|
+
}
|
|
332
|
+
console.log();
|
|
333
|
+
console.log(chalk_1.default.bold.white(' 2. Iniciá el backend:'));
|
|
224
334
|
console.log(chalk_1.default.cyan(` cd ${config.projectName}/backend`));
|
|
225
335
|
console.log(chalk_1.default.gray(' # Completar las variables en .env'));
|
|
226
336
|
console.log(chalk_1.default.cyan(' npm install'));
|
|
@@ -228,6 +338,7 @@ function printNextSteps(config) {
|
|
|
228
338
|
}
|
|
229
339
|
if (hasFrontend) {
|
|
230
340
|
console.log();
|
|
341
|
+
console.log(chalk_1.default.bold.white(` ${hasBackend ? '3' : '1'}. Iniciá el frontend:`));
|
|
231
342
|
console.log(chalk_1.default.cyan(` cd ${config.projectName}/frontend`));
|
|
232
343
|
console.log(chalk_1.default.gray(' # Completar .env.local con la URL del backend'));
|
|
233
344
|
console.log(chalk_1.default.gray(' # Personalizar contenido en src/config/site.ts'));
|
package/package.json
CHANGED
|
@@ -1,50 +1,52 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "ar-saas",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "Generador de proyectos SaaS multi-tenant para startups argentinas. Landing page, auth, dashboard y legal listos para producción.",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"bin": {
|
|
7
|
-
"ar-saas": "dist/index.js"
|
|
8
|
-
},
|
|
9
|
-
"files": [
|
|
10
|
-
"dist/",
|
|
11
|
-
"templates/"
|
|
12
|
-
],
|
|
13
|
-
"scripts": {
|
|
14
|
-
"build": "tsc",
|
|
15
|
-
"dev": "tsx src/index.ts",
|
|
16
|
-
"sync-templates": "tsx scripts/sync-templates.ts",
|
|
17
|
-
"prepublishOnly": "npm run sync-templates && npm run build"
|
|
18
|
-
},
|
|
19
|
-
"keywords": [
|
|
20
|
-
"saas",
|
|
21
|
-
"nestjs",
|
|
22
|
-
"nextjs",
|
|
23
|
-
"argentina",
|
|
24
|
-
"cli",
|
|
25
|
-
"generator",
|
|
26
|
-
"multi-tenant",
|
|
27
|
-
"landing-page",
|
|
28
|
-
"dashboard",
|
|
29
|
-
"boilerplate",
|
|
30
|
-
"starter"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"@types/
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "ar-saas",
|
|
3
|
+
"version": "0.3.2",
|
|
4
|
+
"description": "Generador de proyectos SaaS multi-tenant para startups argentinas. Landing page, auth, dashboard y legal listos para producción.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ar-saas": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/",
|
|
11
|
+
"templates/"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"dev": "tsx src/index.ts",
|
|
16
|
+
"sync-templates": "tsx scripts/sync-templates.ts",
|
|
17
|
+
"prepublishOnly": "npm run sync-templates && npm run build"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"saas",
|
|
21
|
+
"nestjs",
|
|
22
|
+
"nextjs",
|
|
23
|
+
"argentina",
|
|
24
|
+
"cli",
|
|
25
|
+
"generator",
|
|
26
|
+
"multi-tenant",
|
|
27
|
+
"landing-page",
|
|
28
|
+
"dashboard",
|
|
29
|
+
"boilerplate",
|
|
30
|
+
"starter",
|
|
31
|
+
"mongodb",
|
|
32
|
+
"docker"
|
|
33
|
+
],
|
|
34
|
+
"author": "ignacio.becher@gmail.com",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"chalk": "4.1.2",
|
|
41
|
+
"fs-extra": "^11.2.0",
|
|
42
|
+
"inquirer": "8.2.6",
|
|
43
|
+
"ora": "5.4.1"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/fs-extra": "^11.0.0",
|
|
47
|
+
"@types/inquirer": "^8.2.12",
|
|
48
|
+
"@types/node": "^20.0.0",
|
|
49
|
+
"tsx": "^4.7.0",
|
|
50
|
+
"typescript": "^5.4.0"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
# =========================================
|
|
2
|
-
# create-saas-ar Backend
|
|
3
|
-
# Variables de entorno
|
|
4
|
-
# =========================================
|
|
5
|
-
# Copiá este archivo a .env y completá los valores
|
|
6
|
-
# =========================================
|
|
7
|
-
|
|
8
|
-
# ===== Aplicación =====
|
|
9
|
-
# Entorno: development | production | test
|
|
10
|
-
NODE_ENV=development
|
|
11
|
-
|
|
12
|
-
# Puerto donde corre el servidor
|
|
13
|
-
PORT=3000
|
|
14
|
-
|
|
15
|
-
# Prefijo para todas las rutas de la API
|
|
16
|
-
API_PREFIX=api
|
|
17
|
-
|
|
18
|
-
# ===== MongoDB =====
|
|
19
|
-
# URI de conexión a MongoDB
|
|
20
|
-
# Desarrollo local: mongodb://localhost:27017/saas-ar
|
|
21
|
-
# Atlas: mongodb+srv://user:pass@cluster.xxx.mongodb.net/saas-ar
|
|
22
|
-
# Con replica set (para transacciones): mongodb://localhost:27017/saas-ar?replicaSet=rs0
|
|
23
|
-
MONGODB_URI=mongodb://localhost:27017/saas-ar
|
|
24
|
-
|
|
25
|
-
# ===== JWT =====
|
|
26
|
-
# Secreto para firmar access tokens (usar openssl rand -hex 64 para generar)
|
|
27
|
-
JWT_ACCESS_SECRET=cambiar-por-secreto-seguro
|
|
28
|
-
|
|
29
|
-
# Duración del access token (formato: 15m, 1h, 7d)
|
|
30
|
-
JWT_ACCESS_EXPIRES_IN=15m
|
|
31
|
-
|
|
32
|
-
# Secreto para firmar refresh tokens
|
|
33
|
-
JWT_REFRESH_SECRET=cambiar-por-secreto-seguro
|
|
34
|
-
|
|
35
|
-
# Duración del refresh token (formato: 15m, 1h, 7d)
|
|
36
|
-
JWT_REFRESH_EXPIRES_IN=7d
|
|
37
|
-
|
|
38
|
-
# ===== Email (Resend) =====
|
|
39
|
-
# API Key de Resend (https://resend.com/api-keys)
|
|
40
|
-
RESEND_API_KEY=re_cambiar_por_api_key
|
|
41
|
-
|
|
42
|
-
# Email remitente (debe estar verificado en Resend)
|
|
43
|
-
RESEND_FROM_EMAIL=noreply@tuapp.com
|
|
44
|
-
|
|
45
|
-
# Nombre del remitente que ve el destinatario
|
|
46
|
-
RESEND_FROM_NAME="Tu App"
|
|
47
|
-
|
|
48
|
-
# ===== App URL =====
|
|
49
|
-
# URL del frontend (para links en emails de verificación, reset, etc)
|
|
50
|
-
APP_URL=http://localhost:5173
|
|
51
|
-
|
|
52
|
-
# ===== Swagger =====
|
|
53
|
-
# Habilitar documentación Swagger en /api/docs (solo development)
|
|
54
|
-
SWAGGER_ENABLED=true
|
|
55
|
-
|
|
56
|
-
# ===== CORS =====
|
|
57
|
-
# Orígenes permitidos (separados por coma)
|
|
58
|
-
CORS_ORIGINS=http://localhost:3001
|
|
59
|
-
|
|
60
|
-
# ===== Rate Limiting =====
|
|
61
|
-
# Cantidad máxima de requests por ventana de tiempo
|
|
62
|
-
THROTTLE_TTL=60
|
|
63
|
-
THROTTLE_LIMIT=100
|
|
64
|
-
|
|
65
|
-
# ===== Cookies =====
|
|
66
|
-
# Secreto para firmar cookies (usar openssl rand -hex 32 para generar)
|
|
67
|
-
COOKIE_SECRET=cambiar-por-secreto-seguro
|
|
1
|
+
# =========================================
|
|
2
|
+
# create-saas-ar Backend
|
|
3
|
+
# Variables de entorno
|
|
4
|
+
# =========================================
|
|
5
|
+
# Copiá este archivo a .env y completá los valores
|
|
6
|
+
# =========================================
|
|
7
|
+
|
|
8
|
+
# ===== Aplicación =====
|
|
9
|
+
# Entorno: development | production | test
|
|
10
|
+
NODE_ENV=development
|
|
11
|
+
|
|
12
|
+
# Puerto donde corre el servidor
|
|
13
|
+
PORT=3000
|
|
14
|
+
|
|
15
|
+
# Prefijo para todas las rutas de la API
|
|
16
|
+
API_PREFIX=api
|
|
17
|
+
|
|
18
|
+
# ===== MongoDB =====
|
|
19
|
+
# URI de conexión a MongoDB
|
|
20
|
+
# Desarrollo local: mongodb://localhost:27017/saas-ar
|
|
21
|
+
# Atlas: mongodb+srv://user:pass@cluster.xxx.mongodb.net/saas-ar
|
|
22
|
+
# Con replica set (para transacciones): mongodb://localhost:27017/saas-ar?replicaSet=rs0
|
|
23
|
+
MONGODB_URI=mongodb://localhost:27017/saas-ar
|
|
24
|
+
|
|
25
|
+
# ===== JWT =====
|
|
26
|
+
# Secreto para firmar access tokens (usar openssl rand -hex 64 para generar)
|
|
27
|
+
JWT_ACCESS_SECRET=cambiar-por-secreto-seguro
|
|
28
|
+
|
|
29
|
+
# Duración del access token (formato: 15m, 1h, 7d)
|
|
30
|
+
JWT_ACCESS_EXPIRES_IN=15m
|
|
31
|
+
|
|
32
|
+
# Secreto para firmar refresh tokens
|
|
33
|
+
JWT_REFRESH_SECRET=cambiar-por-secreto-seguro
|
|
34
|
+
|
|
35
|
+
# Duración del refresh token (formato: 15m, 1h, 7d)
|
|
36
|
+
JWT_REFRESH_EXPIRES_IN=7d
|
|
37
|
+
|
|
38
|
+
# ===== Email (Resend) =====
|
|
39
|
+
# API Key de Resend (https://resend.com/api-keys)
|
|
40
|
+
RESEND_API_KEY=re_cambiar_por_api_key
|
|
41
|
+
|
|
42
|
+
# Email remitente (debe estar verificado en Resend)
|
|
43
|
+
RESEND_FROM_EMAIL=noreply@tuapp.com
|
|
44
|
+
|
|
45
|
+
# Nombre del remitente que ve el destinatario
|
|
46
|
+
RESEND_FROM_NAME="Tu App"
|
|
47
|
+
|
|
48
|
+
# ===== App URL =====
|
|
49
|
+
# URL del frontend (para links en emails de verificación, reset, etc)
|
|
50
|
+
APP_URL=http://localhost:5173
|
|
51
|
+
|
|
52
|
+
# ===== Swagger =====
|
|
53
|
+
# Habilitar documentación Swagger en /api/docs (solo development)
|
|
54
|
+
SWAGGER_ENABLED=true
|
|
55
|
+
|
|
56
|
+
# ===== CORS =====
|
|
57
|
+
# Orígenes permitidos (separados por coma)
|
|
58
|
+
CORS_ORIGINS=http://localhost:3001
|
|
59
|
+
|
|
60
|
+
# ===== Rate Limiting =====
|
|
61
|
+
# Cantidad máxima de requests por ventana de tiempo
|
|
62
|
+
THROTTLE_TTL=60
|
|
63
|
+
THROTTLE_LIMIT=100
|
|
64
|
+
|
|
65
|
+
# ===== Cookies =====
|
|
66
|
+
# Secreto para firmar cookies (usar openssl rand -hex 32 para generar)
|
|
67
|
+
COOKIE_SECRET=cambiar-por-secreto-seguro
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
{
|
|
2
|
-
"singleQuote": true,
|
|
3
|
-
"trailingComma": "all"
|
|
4
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"singleQuote": true,
|
|
3
|
+
"trailingComma": "all"
|
|
4
|
+
}
|