create-tigra 2.6.8 → 2.7.1

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 (27) hide show
  1. package/bin/create-tigra.js +153 -1
  2. package/lib/patchers/email-verification.patcher.js +576 -0
  3. package/modules/email-verification/client/hooks/useVerification.ts +70 -0
  4. package/modules/email-verification/client/services/verification.service.ts +25 -0
  5. package/modules/email-verification/server/verification.controller.ts +28 -0
  6. package/modules/email-verification/server/verification.service.ts +190 -0
  7. package/package.json +5 -2
  8. package/template/client/src/features/auth/components/AuthInitializer.tsx +7 -1
  9. package/template/client/src/features/auth/hooks/useAuth.ts +10 -1
  10. package/template/client/src/features/auth/hooks/usePasswordReset.ts +57 -0
  11. package/template/client/src/features/auth/services/auth.service.ts +2 -2
  12. package/template/client/src/lib/constants/api-endpoints.ts +1 -1
  13. package/template/client/src/lib/constants/routes.ts +1 -1
  14. package/template/client/src/lib/utils/error.ts +4 -0
  15. package/template/server/.env.example +29 -0
  16. package/template/server/.env.example.production +22 -0
  17. package/template/server/package-lock.json +6823 -0
  18. package/template/server/package.json +1 -0
  19. package/template/server/src/config/env.ts +18 -1
  20. package/template/server/src/config/rate-limit.config.ts +8 -0
  21. package/template/server/src/libs/auth.ts +4 -1
  22. package/template/server/src/libs/email.ts +40 -0
  23. package/template/server/src/modules/auth/auth.controller.ts +27 -1
  24. package/template/server/src/modules/auth/auth.repo.ts +1 -0
  25. package/template/server/src/modules/auth/auth.routes.ts +24 -0
  26. package/template/server/src/modules/auth/auth.schemas.ts +18 -0
  27. package/template/server/src/modules/auth/auth.service.ts +136 -4
@@ -123,13 +123,122 @@ function replaceVariables(content, variables) {
123
123
  return result;
124
124
  }
125
125
 
126
+ function registerAddCommand(program) {
127
+ program
128
+ .command('add <module>')
129
+ .description('Add a module to an existing Tigra project')
130
+ .action(async (moduleName) => {
131
+ console.log();
132
+ console.log(chalk.bold(' Create Tigra') + chalk.dim(` v${VERSION}`) + chalk.dim(' — add module'));
133
+ console.log();
134
+
135
+ const projectDir = process.cwd();
136
+
137
+ // Detect if we're inside a Tigra project
138
+ const hasServer = await fs.pathExists(path.join(projectDir, 'server', 'src', 'modules', 'auth'));
139
+ const hasClient = await fs.pathExists(path.join(projectDir, 'client', 'src', 'features', 'auth'));
140
+
141
+ if (!hasServer || !hasClient) {
142
+ console.error(chalk.red(' This does not appear to be a Tigra project.'));
143
+ console.error(chalk.dim(' Run this command from the root of your project (the folder containing server/ and client/).'));
144
+ console.log();
145
+ process.exit(1);
146
+ }
147
+
148
+ const availableModules = ['email-verification'];
149
+
150
+ if (!availableModules.includes(moduleName)) {
151
+ console.error(chalk.red(` Unknown module: "${moduleName}"`));
152
+ console.log();
153
+ console.log(chalk.dim(' Available modules:'));
154
+ for (const m of availableModules) {
155
+ console.log(chalk.cyan(` - ${m}`));
156
+ }
157
+ console.log();
158
+ process.exit(1);
159
+ }
160
+
161
+ if (moduleName === 'email-verification') {
162
+ // Check if already applied
163
+ const alreadyApplied = await fs.pathExists(
164
+ path.join(projectDir, 'server', 'src', 'modules', 'auth', 'verification.service.ts'),
165
+ );
166
+ if (alreadyApplied) {
167
+ console.log(chalk.yellow(' Email verification is already installed in this project.'));
168
+ console.log();
169
+ process.exit(0);
170
+ }
171
+
172
+ const spinner = ora('Adding email verification module...').start();
173
+
174
+ try {
175
+ const { applyEmailVerificationModule } = await import(
176
+ '../lib/patchers/email-verification.patcher.js'
177
+ );
178
+ await applyEmailVerificationModule(projectDir);
179
+
180
+ // Set REQUIRE_USER_VERIFICATION=true in .env if it's currently false
181
+ for (const envFile of ['server/.env.example', 'server/.env']) {
182
+ const envPath = path.join(projectDir, envFile);
183
+ if (await fs.pathExists(envPath)) {
184
+ const content = await fs.readFile(envPath, 'utf-8');
185
+ if (content.includes('REQUIRE_USER_VERIFICATION=false')) {
186
+ await fs.writeFile(
187
+ envPath,
188
+ content.replace('REQUIRE_USER_VERIFICATION=false', 'REQUIRE_USER_VERIFICATION=true'),
189
+ 'utf-8',
190
+ );
191
+ }
192
+ }
193
+ }
194
+
195
+ spinner.succeed('Email verification module added!');
196
+ } catch (error) {
197
+ spinner.fail('Failed to add email verification module');
198
+ console.error(chalk.red(`\n ${error.message}\n`));
199
+ process.exit(1);
200
+ }
201
+
202
+ const dim = chalk.dim;
203
+ const cyan = chalk.cyan;
204
+ const green = chalk.green;
205
+
206
+ console.log();
207
+ console.log(green(' ✓ ') + 'Files added:');
208
+ console.log(dim(' server/src/modules/auth/verification.service.ts'));
209
+ console.log(dim(' server/src/modules/auth/verification.controller.ts'));
210
+ console.log(dim(' client/src/features/auth/services/verification.service.ts'));
211
+ console.log(dim(' client/src/features/auth/hooks/useVerification.ts'));
212
+ console.log();
213
+ console.log(green(' ✓ ') + 'Files patched:');
214
+ console.log(dim(' auth.routes.ts, auth.schemas.ts, auth.service.ts, auth.repo.ts'));
215
+ console.log(dim(' rate-limit.config.ts, api-endpoints.ts, error.ts, useAuth.ts'));
216
+ console.log(dim(' postman/collection.json'));
217
+ console.log();
218
+ console.log(green(' ✓ ') + 'REQUIRE_USER_VERIFICATION=true in server/.env');
219
+ console.log();
220
+ console.log(dim(' Next steps:'));
221
+ console.log(cyan(' 1 ') + 'Set ' + chalk.bold('RESEND_API_KEY') + ' in server/.env');
222
+ console.log(cyan(' 2 ') + 'Restart the server');
223
+ console.log();
224
+ }
225
+ });
226
+ }
227
+
126
228
  async function main() {
127
229
  const program = new Command();
128
230
 
231
+ // Define the add subcommand BEFORE the default command so Commander recognizes it
129
232
  program
130
233
  .name('create-tigra')
131
234
  .description('Create a production-ready full-stack app with Next.js + Fastify + Prisma + Redis')
132
- .version(VERSION)
235
+ .version(VERSION);
236
+
237
+ // ─── Add module subcommand ────────────────────────────────────
238
+ registerAddCommand(program);
239
+
240
+ // ─── Default: scaffold new project ────────────────────────────
241
+ program
133
242
  .argument('[project-name]', 'Name for your new project')
134
243
  .action(async (projectNameArg) => {
135
244
  console.log();
@@ -176,6 +285,25 @@ async function main() {
176
285
  }
177
286
  }
178
287
 
288
+ // Ask about email verification
289
+ const { enableVerification } = await prompts(
290
+ {
291
+ type: 'toggle',
292
+ name: 'enableVerification',
293
+ message: 'Enable email verification for new users?',
294
+ initial: false,
295
+ active: 'Yes',
296
+ inactive: 'No',
297
+ hint: 'Users must verify email before accessing the app',
298
+ },
299
+ {
300
+ onCancel: () => {
301
+ console.log(chalk.red('\n Cancelled.\n'));
302
+ process.exit(1);
303
+ },
304
+ }
305
+ );
306
+
179
307
  // Generate random port offset (1-200) so multiple projects don't conflict
180
308
  const portOffset = crypto.randomInt(1, 201);
181
309
 
@@ -218,6 +346,25 @@ async function main() {
218
346
  }
219
347
  }
220
348
 
349
+ // Apply email verification module if selected
350
+ if (enableVerification) {
351
+ const { applyEmailVerificationModule } = await import('../lib/patchers/email-verification.patcher.js');
352
+ await applyEmailVerificationModule(targetDir);
353
+ } else {
354
+ // Disable verification requirement in .env files
355
+ for (const envFile of ['server/.env.example', 'server/.env']) {
356
+ const envPath = path.join(targetDir, envFile);
357
+ if (await fs.pathExists(envPath)) {
358
+ const content = await fs.readFile(envPath, 'utf-8');
359
+ await fs.writeFile(
360
+ envPath,
361
+ content.replace('REQUIRE_USER_VERIFICATION=true', 'REQUIRE_USER_VERIFICATION=false'),
362
+ 'utf-8',
363
+ );
364
+ }
365
+ }
366
+ }
367
+
221
368
  // Create .developer-role file (default: fullstack = no restrictions)
222
369
  const developerRoleContent = [
223
370
  'fullstack',
@@ -281,6 +428,11 @@ async function main() {
281
428
  console.log();
282
429
  console.log(line);
283
430
  console.log();
431
+ if (enableVerification) {
432
+ console.log(dim(' Email verification: ') + green('enabled'));
433
+ console.log(dim(' Set RESEND_API_KEY in server/.env to send emails'));
434
+ console.log();
435
+ }
284
436
  console.log(dim(' Tip: ') + 'npm run docker:down' + dim(' to stop infrastructure'));
285
437
  console.log();
286
438
  console.log(dim(' Happy coding! 🚀'));