@arikajs/cli 0.0.4 → 0.0.5

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 (228) hide show
  1. package/README.md +99 -6
  2. package/dist/bin/arika.js +2 -2
  3. package/dist/bin/arika.js.map +1 -1
  4. package/dist/src/ApplicationLoader.d.ts +5 -0
  5. package/dist/src/ApplicationLoader.d.ts.map +1 -1
  6. package/dist/src/ApplicationLoader.js +24 -2
  7. package/dist/src/ApplicationLoader.js.map +1 -1
  8. package/dist/src/Bootstrap.d.ts.map +1 -1
  9. package/dist/src/Bootstrap.js +35 -0
  10. package/dist/src/Bootstrap.js.map +1 -1
  11. package/dist/src/Commands/AuthApiInstallCommand.d.ts +24 -0
  12. package/dist/src/Commands/AuthApiInstallCommand.d.ts.map +1 -0
  13. package/dist/src/Commands/AuthApiInstallCommand.js +751 -0
  14. package/dist/src/Commands/AuthApiInstallCommand.js.map +1 -0
  15. package/dist/src/Commands/AuthInstallCommand.d.ts +7 -0
  16. package/dist/src/Commands/AuthInstallCommand.d.ts.map +1 -0
  17. package/dist/src/Commands/AuthInstallCommand.js +46 -0
  18. package/dist/src/Commands/AuthInstallCommand.js.map +1 -0
  19. package/dist/src/Commands/AuthWebInstallCommand.d.ts +31 -0
  20. package/dist/src/Commands/AuthWebInstallCommand.d.ts.map +1 -0
  21. package/dist/src/Commands/AuthWebInstallCommand.js +1100 -0
  22. package/dist/src/Commands/AuthWebInstallCommand.js.map +1 -0
  23. package/dist/src/Commands/BenchmarkCommand.d.ts +7 -0
  24. package/dist/src/Commands/BenchmarkCommand.d.ts.map +1 -0
  25. package/dist/src/Commands/BenchmarkCommand.js +25 -0
  26. package/dist/src/Commands/BenchmarkCommand.js.map +1 -0
  27. package/dist/src/Commands/CacheClearCommand.d.ts +7 -0
  28. package/dist/src/Commands/CacheClearCommand.d.ts.map +1 -0
  29. package/dist/src/Commands/CacheClearCommand.js +33 -0
  30. package/dist/src/Commands/CacheClearCommand.js.map +1 -0
  31. package/dist/src/Commands/CacheTableCommand.d.ts.map +1 -1
  32. package/dist/src/Commands/CacheTableCommand.js +21 -9
  33. package/dist/src/Commands/CacheTableCommand.js.map +1 -1
  34. package/dist/src/Commands/ConfigCacheCommand.d.ts +7 -0
  35. package/dist/src/Commands/ConfigCacheCommand.d.ts.map +1 -0
  36. package/dist/src/Commands/ConfigCacheCommand.js +86 -0
  37. package/dist/src/Commands/ConfigCacheCommand.js.map +1 -0
  38. package/dist/src/Commands/ConfigClearCommand.d.ts +7 -0
  39. package/dist/src/Commands/ConfigClearCommand.d.ts.map +1 -0
  40. package/dist/src/Commands/ConfigClearCommand.js +29 -0
  41. package/dist/src/Commands/ConfigClearCommand.js.map +1 -0
  42. package/dist/src/Commands/DocsGenerateCommand.d.ts +0 -1
  43. package/dist/src/Commands/DocsGenerateCommand.d.ts.map +1 -1
  44. package/dist/src/Commands/DocsGenerateCommand.js +68 -77
  45. package/dist/src/Commands/DocsGenerateCommand.js.map +1 -1
  46. package/dist/src/Commands/EnvValidateCommand.d.ts +7 -0
  47. package/dist/src/Commands/EnvValidateCommand.d.ts.map +1 -0
  48. package/dist/src/Commands/EnvValidateCommand.js +51 -0
  49. package/dist/src/Commands/EnvValidateCommand.js.map +1 -0
  50. package/dist/src/Commands/ListCommand.d.ts.map +1 -1
  51. package/dist/src/Commands/ListCommand.js +15 -8
  52. package/dist/src/Commands/ListCommand.js.map +1 -1
  53. package/dist/src/Commands/MakeCommand.d.ts +7 -0
  54. package/dist/src/Commands/MakeCommand.d.ts.map +1 -0
  55. package/dist/src/Commands/MakeCommand.js +110 -0
  56. package/dist/src/Commands/MakeCommand.js.map +1 -0
  57. package/dist/src/Commands/MakeCommandCommand.d.ts +20 -0
  58. package/dist/src/Commands/MakeCommandCommand.d.ts.map +1 -0
  59. package/dist/src/Commands/MakeCommandCommand.js +82 -0
  60. package/dist/src/Commands/MakeCommandCommand.js.map +1 -0
  61. package/dist/src/Commands/MakeControllerCommand.d.ts +17 -0
  62. package/dist/src/Commands/MakeControllerCommand.d.ts.map +1 -0
  63. package/dist/src/Commands/MakeControllerCommand.js +90 -0
  64. package/dist/src/Commands/MakeControllerCommand.js.map +1 -0
  65. package/dist/src/Commands/MakeEventCommand.d.ts +8 -0
  66. package/dist/src/Commands/MakeEventCommand.d.ts.map +1 -0
  67. package/dist/src/Commands/MakeEventCommand.js +48 -0
  68. package/dist/src/Commands/MakeEventCommand.js.map +1 -0
  69. package/dist/src/Commands/MakeJobCommand.d.ts +8 -0
  70. package/dist/src/Commands/MakeJobCommand.d.ts.map +1 -0
  71. package/dist/src/Commands/MakeJobCommand.js +55 -0
  72. package/dist/src/Commands/MakeJobCommand.js.map +1 -0
  73. package/dist/src/Commands/MakeListenerCommand.d.ts +8 -0
  74. package/dist/src/Commands/MakeListenerCommand.d.ts.map +1 -0
  75. package/dist/src/Commands/MakeListenerCommand.js +55 -0
  76. package/dist/src/Commands/MakeListenerCommand.js.map +1 -0
  77. package/dist/src/Commands/MakeMiddlewareCommand.d.ts +17 -0
  78. package/dist/src/Commands/MakeMiddlewareCommand.d.ts.map +1 -0
  79. package/dist/src/Commands/MakeMiddlewareCommand.js +69 -0
  80. package/dist/src/Commands/MakeMiddlewareCommand.js.map +1 -0
  81. package/dist/src/Commands/MakeMigrationCommand.js +4 -4
  82. package/dist/src/Commands/MakeMigrationCommand.js.map +1 -1
  83. package/dist/src/Commands/MakeModelCommand.d.ts +26 -0
  84. package/dist/src/Commands/MakeModelCommand.d.ts.map +1 -0
  85. package/dist/src/Commands/MakeModelCommand.js +193 -0
  86. package/dist/src/Commands/MakeModelCommand.js.map +1 -0
  87. package/dist/src/Commands/MakeProviderCommand.d.ts +17 -0
  88. package/dist/src/Commands/MakeProviderCommand.d.ts.map +1 -0
  89. package/dist/src/Commands/MakeProviderCommand.js +72 -0
  90. package/dist/src/Commands/MakeProviderCommand.js.map +1 -0
  91. package/dist/src/Commands/MakeViewCommand.d.ts +7 -0
  92. package/dist/src/Commands/MakeViewCommand.d.ts.map +1 -0
  93. package/dist/src/Commands/MakeViewCommand.js +37 -0
  94. package/dist/src/Commands/MakeViewCommand.js.map +1 -0
  95. package/dist/src/Commands/MigrateFreshCommand.d.ts +10 -0
  96. package/dist/src/Commands/MigrateFreshCommand.d.ts.map +1 -0
  97. package/dist/src/Commands/MigrateFreshCommand.js +129 -0
  98. package/dist/src/Commands/MigrateFreshCommand.js.map +1 -0
  99. package/dist/src/Commands/NewCommand.d.ts.map +1 -1
  100. package/dist/src/Commands/NewCommand.js +92 -24
  101. package/dist/src/Commands/NewCommand.js.map +1 -1
  102. package/dist/src/Commands/QueueFailedCommand.d.ts +7 -0
  103. package/dist/src/Commands/QueueFailedCommand.d.ts.map +1 -0
  104. package/dist/src/Commands/QueueFailedCommand.js +30 -0
  105. package/dist/src/Commands/QueueFailedCommand.js.map +1 -0
  106. package/dist/src/Commands/QueueFailedTableCommand.d.ts +7 -0
  107. package/dist/src/Commands/QueueFailedTableCommand.d.ts.map +1 -0
  108. package/dist/src/Commands/QueueFailedTableCommand.js +55 -0
  109. package/dist/src/Commands/QueueFailedTableCommand.js.map +1 -0
  110. package/dist/src/Commands/QueueFlushCommand.d.ts +7 -0
  111. package/dist/src/Commands/QueueFlushCommand.d.ts.map +1 -0
  112. package/dist/src/Commands/QueueFlushCommand.js +21 -0
  113. package/dist/src/Commands/QueueFlushCommand.js.map +1 -0
  114. package/dist/src/Commands/QueueRetryCommand.d.ts +7 -0
  115. package/dist/src/Commands/QueueRetryCommand.d.ts.map +1 -0
  116. package/dist/src/Commands/QueueRetryCommand.js +36 -0
  117. package/dist/src/Commands/QueueRetryCommand.js.map +1 -0
  118. package/dist/src/Commands/QueueTableCommand.d.ts.map +1 -1
  119. package/dist/src/Commands/QueueTableCommand.js +19 -13
  120. package/dist/src/Commands/QueueTableCommand.js.map +1 -1
  121. package/dist/src/Commands/QueueWorkCommand.d.ts +8 -0
  122. package/dist/src/Commands/QueueWorkCommand.d.ts.map +1 -0
  123. package/dist/src/Commands/QueueWorkCommand.js +88 -0
  124. package/dist/src/Commands/QueueWorkCommand.js.map +1 -0
  125. package/dist/src/Commands/RouteListCommand.d.ts +7 -0
  126. package/dist/src/Commands/RouteListCommand.d.ts.map +1 -0
  127. package/dist/src/Commands/RouteListCommand.js +108 -0
  128. package/dist/src/Commands/RouteListCommand.js.map +1 -0
  129. package/dist/src/Commands/ScheduleRunCommand.d.ts +9 -0
  130. package/dist/src/Commands/ScheduleRunCommand.d.ts.map +1 -0
  131. package/dist/src/Commands/ScheduleRunCommand.js +80 -0
  132. package/dist/src/Commands/ScheduleRunCommand.js.map +1 -0
  133. package/dist/src/Commands/ScheduleWorkCommand.d.ts +9 -0
  134. package/dist/src/Commands/ScheduleWorkCommand.d.ts.map +1 -0
  135. package/dist/src/Commands/ScheduleWorkCommand.js +81 -0
  136. package/dist/src/Commands/ScheduleWorkCommand.js.map +1 -0
  137. package/dist/src/Commands/ServeCommand.d.ts +1 -0
  138. package/dist/src/Commands/ServeCommand.d.ts.map +1 -1
  139. package/dist/src/Commands/ServeCommand.js +111 -21
  140. package/dist/src/Commands/ServeCommand.js.map +1 -1
  141. package/dist/src/TemplateManager.d.ts +4 -0
  142. package/dist/src/TemplateManager.d.ts.map +1 -1
  143. package/dist/src/TemplateManager.js +35 -6
  144. package/dist/src/TemplateManager.js.map +1 -1
  145. package/dist/templates/app/.env.example +47 -0
  146. package/dist/templates/app/README.md +62 -0
  147. package/dist/templates/app/app/Exceptions/Handler.ts +19 -0
  148. package/dist/templates/app/app/Http/Controllers/UserController.ts +90 -0
  149. package/dist/templates/app/app/Http/Kernel.ts +43 -0
  150. package/dist/templates/app/app/Http/Middleware/Authenticate.ts +10 -0
  151. package/dist/templates/app/app/Http/Middleware/EnsureEmailIsVerified.ts +18 -0
  152. package/dist/templates/app/app/Http/Middleware/ExampleMiddleware.ts +12 -0
  153. package/dist/templates/app/app/Http/Middleware/RedirectIfAuthenticated.ts +18 -0
  154. package/dist/templates/app/app/Http/Middleware/TrimStrings.ts +12 -0
  155. package/dist/templates/app/app/Http/Middleware/TrustProxies.ts +23 -0
  156. package/dist/templates/app/app/Http/Middleware/VerifyCsrfToken.ts +10 -0
  157. package/dist/templates/app/app/Models/User.ts +15 -0
  158. package/dist/templates/app/app/Providers/AppServiceProvider.ts +17 -0
  159. package/dist/templates/app/app/Providers/RouteServiceProvider.ts +35 -0
  160. package/dist/templates/app/bootstrap/app.ts +41 -0
  161. package/dist/templates/app/config/app.ts +10 -0
  162. package/dist/templates/app/config/cache.ts +44 -0
  163. package/dist/templates/app/config/database.ts +63 -0
  164. package/dist/templates/app/config/filesystems.ts +35 -0
  165. package/dist/templates/app/config/http.ts +6 -0
  166. package/dist/templates/app/config/logging.ts +37 -0
  167. package/dist/templates/app/config/mail.ts +29 -0
  168. package/dist/templates/app/config/queue.ts +42 -0
  169. package/{templates/app/database/migrations/0001_create_users_table.ts → dist/templates/app/database/migrations/2024_01_01_000001_create_users_table.ts} +3 -1
  170. package/dist/templates/app/database/migrations/2024_01_01_000002_create_password_resets_table.ts +22 -0
  171. package/dist/templates/app/database/migrations/2024_01_01_000003_create_failed_jobs_table.ts +25 -0
  172. package/dist/templates/app/package.json +22 -0
  173. package/dist/templates/app/public/assets/img/favicon.png +0 -0
  174. package/dist/templates/app/public/assets/img/logo.png +0 -0
  175. package/dist/templates/app/resources/views/errors/401.ark.html +320 -0
  176. package/dist/templates/app/resources/views/errors/403.ark.html +320 -0
  177. package/dist/templates/app/resources/views/errors/404.ark.html +320 -0
  178. package/dist/templates/app/resources/views/errors/419.ark.html +320 -0
  179. package/dist/templates/app/resources/views/errors/429.ark.html +320 -0
  180. package/dist/templates/app/resources/views/errors/500.ark.html +320 -0
  181. package/dist/templates/app/resources/views/errors/503.ark.html +320 -0
  182. package/dist/templates/app/resources/views/welcome.ark.html +846 -0
  183. package/dist/templates/app/routes/api.ts +39 -0
  184. package/dist/templates/app/routes/web.ts +10 -0
  185. package/dist/templates/app/server.ts +15 -0
  186. package/dist/templates/app/tsconfig.json +27 -0
  187. package/package.json +16 -10
  188. package/templates/app/.env.example +2 -2
  189. package/templates/app/README.md +62 -0
  190. package/templates/app/app/Exceptions/Handler.ts +19 -0
  191. package/templates/app/app/Http/Controllers/UserController.ts +90 -0
  192. package/templates/app/app/Http/Kernel.ts +39 -14
  193. package/templates/app/app/Http/Middleware/Authenticate.ts +10 -0
  194. package/templates/app/app/Http/Middleware/EnsureEmailIsVerified.ts +18 -0
  195. package/templates/app/app/Http/Middleware/ExampleMiddleware.ts +12 -0
  196. package/templates/app/app/Http/Middleware/RedirectIfAuthenticated.ts +18 -0
  197. package/templates/app/app/Http/Middleware/TrimStrings.ts +12 -0
  198. package/templates/app/app/Http/Middleware/TrustProxies.ts +23 -0
  199. package/templates/app/app/Http/Middleware/VerifyCsrfToken.ts +10 -0
  200. package/templates/app/app/Models/User.ts +5 -5
  201. package/templates/app/app/Providers/AppServiceProvider.ts +17 -0
  202. package/templates/app/app/Providers/RouteServiceProvider.ts +35 -0
  203. package/templates/app/bootstrap/app.ts +30 -2
  204. package/templates/app/config/app.ts +7 -6
  205. package/templates/app/config/cache.ts +14 -7
  206. package/templates/app/config/database.ts +48 -21
  207. package/templates/app/config/filesystems.ts +11 -9
  208. package/templates/app/config/http.ts +3 -2
  209. package/templates/app/config/logging.ts +6 -4
  210. package/templates/app/config/mail.ts +10 -8
  211. package/templates/app/config/queue.ts +6 -4
  212. package/templates/app/database/migrations/2024_01_01_000001_create_users_table.ts +25 -0
  213. package/templates/app/database/migrations/2024_01_01_000002_create_password_resets_table.ts +22 -0
  214. package/templates/app/database/migrations/2024_01_01_000003_create_failed_jobs_table.ts +25 -0
  215. package/templates/app/package.json +4 -3
  216. package/templates/app/public/assets/img/favicon.png +0 -0
  217. package/templates/app/public/assets/img/logo.png +0 -0
  218. package/templates/app/resources/views/welcome.ark.html +846 -0
  219. package/templates/app/routes/api.ts +8 -3
  220. package/templates/app/routes/web.ts +8 -4
  221. package/templates/app/server.ts +3 -4
  222. package/templates/app/tsconfig.json +12 -0
  223. package/dist/tests/Cli.test.d.ts +0 -2
  224. package/dist/tests/Cli.test.d.ts.map +0 -1
  225. package/dist/tests/Cli.test.js +0 -16
  226. package/dist/tests/Cli.test.js.map +0 -1
  227. package/templates/app/app/Controllers/UserController.ts +0 -88
  228. package/templates/app/resources/views/welcome.html +0 -470
@@ -0,0 +1,751 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.AuthApiInstallCommand = void 0;
7
+ const console_1 = require("@arikajs/console");
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const node_crypto_1 = __importDefault(require("node:crypto"));
11
+ class AuthApiInstallCommand extends console_1.Command {
12
+ constructor() {
13
+ super(...arguments);
14
+ this.signature = 'auth:install:api {--force}';
15
+ this.description = 'Scaffold API authentication routes (JWT-based)';
16
+ }
17
+ async handle() {
18
+ this.info('Scaffolding authentication system...');
19
+ const force = this.option('force');
20
+ const cwd = process.cwd();
21
+ // Target Paths
22
+ const configPath = path_1.default.join(cwd, 'config', 'auth.ts');
23
+ const modelPath = path_1.default.join(cwd, 'app', 'Models', 'User.ts');
24
+ const authControllerDir = path_1.default.join(cwd, 'app', 'Http', 'Controllers', 'Auth');
25
+ const langEnDir = path_1.default.join(cwd, 'resources', 'lang', 'en');
26
+ const authLangPath = path_1.default.join(langEnDir, 'auth.json');
27
+ const validationLangPath = path_1.default.join(langEnDir, 'validation.json');
28
+ // Generate timestamped migration name
29
+ const date = new Date();
30
+ const baseTimestamp = date.getFullYear().toString() + '_' +
31
+ (date.getMonth() + 1).toString().padStart(2, '0') + '_' +
32
+ date.getDate().toString().padStart(2, '0') + '_' +
33
+ date.getHours().toString().padStart(2, '0') +
34
+ date.getMinutes().toString().padStart(2, '0');
35
+ const timestamp1 = baseTimestamp + date.getSeconds().toString().padStart(2, '0');
36
+ const timestamp2 = baseTimestamp + (date.getSeconds() + 1).toString().padStart(2, '0');
37
+ const migrationDir = path_1.default.join(cwd, 'database', 'migrations');
38
+ const usersMigrationPath = path_1.default.join(migrationDir, `${timestamp1}_create_users_table.ts`);
39
+ const passwordMigrationPath = path_1.default.join(migrationDir, `${timestamp2}_create_password_resets_table.ts`);
40
+ // Check if any file exists unless --force is used
41
+ if (!force) {
42
+ const existingFiles = [configPath, modelPath]
43
+ .filter(p => fs_1.default.existsSync(p));
44
+ if (existingFiles.length > 0) {
45
+ this.error('Authentication scaffold failed. The following files already exist:');
46
+ existingFiles.forEach(f => this.writeln(` - ${f}`));
47
+ this.info('Use the --force flag to overwrite them.');
48
+ return;
49
+ }
50
+ }
51
+ // Ensure directories exist
52
+ [
53
+ path_1.default.dirname(configPath),
54
+ path_1.default.dirname(modelPath),
55
+ authControllerDir,
56
+ migrationDir,
57
+ langEnDir
58
+ ].forEach(dir => {
59
+ if (!fs_1.default.existsSync(dir))
60
+ fs_1.default.mkdirSync(dir, { recursive: true });
61
+ });
62
+ // Publish configuration and core files
63
+ fs_1.default.writeFileSync(configPath, this.getConfigStub());
64
+ fs_1.default.writeFileSync(modelPath, this.getModelStub());
65
+ // Publish Controllers
66
+ fs_1.default.writeFileSync(path_1.default.join(authControllerDir, 'LoginController.ts'), this.getLoginControllerStub());
67
+ fs_1.default.writeFileSync(path_1.default.join(authControllerDir, 'RegisterController.ts'), this.getRegisterControllerStub());
68
+ fs_1.default.writeFileSync(path_1.default.join(authControllerDir, 'ForgotPasswordController.ts'), this.getForgotPasswordControllerStub());
69
+ fs_1.default.writeFileSync(path_1.default.join(authControllerDir, 'ResetPasswordController.ts'), this.getResetPasswordControllerStub());
70
+ fs_1.default.writeFileSync(path_1.default.join(authControllerDir, 'VerifyEmailController.ts'), this.getVerifyEmailControllerStub());
71
+ // Publish Mailables
72
+ const mailAuthDir = path_1.default.join(cwd, 'app', 'Mail', 'Auth');
73
+ if (!fs_1.default.existsSync(mailAuthDir))
74
+ fs_1.default.mkdirSync(mailAuthDir, { recursive: true });
75
+ fs_1.default.writeFileSync(path_1.default.join(mailAuthDir, 'VerifyEmail.ts'), this.getVerifyEmailMailableStub());
76
+ fs_1.default.writeFileSync(path_1.default.join(mailAuthDir, 'ResetPassword.ts'), this.getResetPasswordMailableStub());
77
+ // Publish Email Views
78
+ const emailViewDir = path_1.default.join(cwd, 'resources', 'views', 'emails', 'auth');
79
+ if (!fs_1.default.existsSync(emailViewDir))
80
+ fs_1.default.mkdirSync(emailViewDir, { recursive: true });
81
+ const verifyViewPath = path_1.default.join(emailViewDir, 'verify_email.ark.html');
82
+ const resetViewPath = path_1.default.join(emailViewDir, 'reset_password.ark.html');
83
+ if (!fs_1.default.existsSync(verifyViewPath) || force) {
84
+ fs_1.default.writeFileSync(verifyViewPath, this.getVerifyEmailViewStub());
85
+ }
86
+ if (!fs_1.default.existsSync(resetViewPath) || force) {
87
+ fs_1.default.writeFileSync(resetViewPath, this.getResetPasswordViewStub());
88
+ }
89
+ // Publish Localization files
90
+ if (!fs_1.default.existsSync(authLangPath) || force) {
91
+ fs_1.default.writeFileSync(authLangPath, this.getAuthLangStub());
92
+ }
93
+ if (!fs_1.default.existsSync(validationLangPath) || force) {
94
+ fs_1.default.writeFileSync(validationLangPath, this.getValidationLangStub());
95
+ }
96
+ // Check if migrations already exist
97
+ const migrations = fs_1.default.existsSync(migrationDir) ? fs_1.default.readdirSync(migrationDir) : [];
98
+ if (!migrations.some(file => file.includes('create_users_table'))) {
99
+ fs_1.default.writeFileSync(usersMigrationPath, this.getUsersMigrationStub());
100
+ }
101
+ if (!migrations.some(file => file.includes('create_password_resets_table'))) {
102
+ fs_1.default.writeFileSync(passwordMigrationPath, this.getPasswordResetMigrationStub());
103
+ }
104
+ // Update Routes
105
+ this.appendRoutes();
106
+ this.updateEnv();
107
+ this.writeln('');
108
+ this.success('Premium API Authentication scaffolding generated successfully!');
109
+ this.info('Next steps:');
110
+ this.writeln(' 1. Run "arika migrate" to create the users table');
111
+ this.writeln(' 2. Use the "api" guard in your auth configuration');
112
+ }
113
+ updateEnv() {
114
+ const cwd = process.cwd();
115
+ const envPath = path_1.default.join(cwd, '.env');
116
+ if (!fs_1.default.existsSync(envPath))
117
+ return;
118
+ let content = fs_1.default.readFileSync(envPath, 'utf8');
119
+ if (!content.includes('JWT_SECRET=')) {
120
+ const secret = node_crypto_1.default.randomBytes(32).toString('hex');
121
+ content += `\n# Authentication\nJWT_SECRET=${secret}\n`;
122
+ fs_1.default.writeFileSync(envPath, content);
123
+ this.success(' ✅ JWT_SECRET has been automatically added to your .env file.');
124
+ }
125
+ }
126
+ getConfigStub() {
127
+ return `import { User } from '@Models/User';
128
+
129
+ export default {
130
+ /*
131
+ |--------------------------------------------------------------------------
132
+ | Authentication Defaults
133
+ |--------------------------------------------------------------------------
134
+ |
135
+ | This option controls the default authentication "guard" and password
136
+ | reset options for your application.
137
+ |
138
+ */
139
+ default: 'api',
140
+
141
+ /*
142
+ |--------------------------------------------------------------------------
143
+ | Authentication Guards
144
+ |--------------------------------------------------------------------------
145
+ |
146
+ | Next, you may define every authentication guard for your application.
147
+ | Supported Drivers: "session", "jwt", "token", "basic"
148
+ |
149
+ */
150
+ guards: {
151
+ web: {
152
+ driver: 'session',
153
+ provider: 'users',
154
+ },
155
+ api: {
156
+ driver: 'jwt',
157
+ provider: 'users',
158
+ secret: process.env.JWT_SECRET || 'your-super-secret-jwt-key',
159
+ options: {
160
+ expiresIn: '24h',
161
+ },
162
+ },
163
+ },
164
+
165
+ /*
166
+ |--------------------------------------------------------------------------
167
+ | User Providers
168
+ |--------------------------------------------------------------------------
169
+ |
170
+ | All authentication drivers have a user provider. This defines how the
171
+ | users are actually retrieved out of your database.
172
+ |
173
+ */
174
+ providers: {
175
+ users: {
176
+ driver: 'eloquent',
177
+ model: User,
178
+ },
179
+ },
180
+
181
+ /*
182
+ |--------------------------------------------------------------------------
183
+ | Authentication Throttling & Locking
184
+ |--------------------------------------------------------------------------
185
+ |
186
+ */
187
+ lockout: {
188
+ maxAttempts: 5,
189
+ decayMinutes: 15,
190
+ },
191
+ };
192
+ `;
193
+ }
194
+ getModelStub() {
195
+ return `import { Model } from 'arikajs';
196
+
197
+ export class User extends Model {
198
+ protected static table = 'users';
199
+
200
+ public declare id: number;
201
+ public declare name: string;
202
+ public declare email: string;
203
+ public declare password: string;
204
+ public declare email_verified_at: string | Date | null;
205
+
206
+ protected fillable: string[] = [
207
+ 'name',
208
+ 'email',
209
+ 'password',
210
+ 'email_verified_at',
211
+ ];
212
+
213
+ protected hidden: string[] = [
214
+ 'password',
215
+ ];
216
+
217
+ public hasVerifiedEmail(): boolean {
218
+ return this.email_verified_at !== null;
219
+ }
220
+ }
221
+ `;
222
+ }
223
+ getUsersMigrationStub() {
224
+ return `import { Migration, SchemaBuilder } from 'arikajs';
225
+
226
+ export default class CreateUsersTable extends Migration {
227
+ public async up(schema: SchemaBuilder): Promise<void> {
228
+ await schema.dropIfExists('users');
229
+ await schema.create('users', (table: any) => {
230
+ table.id();
231
+ table.string('name');
232
+ table.string('email').unique();
233
+ table.timestamp('email_verified_at').nullable();
234
+ table.string('password');
235
+ table.string('remember_token', 100).nullable();
236
+ table.string('refresh_token', 100).nullable();
237
+ table.timestamps();
238
+ });
239
+ }
240
+
241
+ public async down(schema: SchemaBuilder): Promise<void> {
242
+ await schema.dropIfExists('users');
243
+ }
244
+ }
245
+ `;
246
+ }
247
+ getPasswordResetMigrationStub() {
248
+ return `import { Migration, SchemaBuilder } from 'arikajs';
249
+
250
+ export default class CreatePasswordResetsTable extends Migration {
251
+ public async up(schema: SchemaBuilder): Promise<void> {
252
+ await schema.dropIfExists('password_resets');
253
+ await schema.create('password_resets', (table: any) => {
254
+ table.string('email').index();
255
+ table.string('token');
256
+ table.timestamp('created_at').nullable();
257
+ });
258
+ }
259
+
260
+ public async down(schema: SchemaBuilder): Promise<void> {
261
+ await schema.dropIfExists('password_resets');
262
+ }
263
+ }
264
+ `;
265
+ }
266
+ getLoginControllerStub() {
267
+ return `import { Request, Response, Log, lang, app } from 'arikajs';
268
+ import { User } from '@Models/User';
269
+
270
+ export class LoginController {
271
+ /**
272
+ * Handle an incoming authentication request.
273
+ */
274
+ public async login(req: Request, res: Response) {
275
+ const credentials = await req.validate({
276
+ email: 'required|email',
277
+ password: 'required|string',
278
+ });
279
+
280
+ const guard = req.auth.guard('api');
281
+ const result = await guard.attempt(credentials);
282
+
283
+ if (!result) {
284
+ Log.info('Failed login attempt', { email: credentials.email });
285
+ return res.json({ error: lang('auth.failed') }, 401);
286
+ }
287
+
288
+ const user = await guard.user();
289
+
290
+ // JwtGuard.attempt() returns { access_token, refresh_token? } on success
291
+ const access_token = typeof result === 'object' && result.access_token
292
+ ? result.access_token
293
+ : (typeof result === 'string' ? result : undefined);
294
+
295
+ return res.json({
296
+ message: lang('auth.login_success'),
297
+ access_token,
298
+ user
299
+ });
300
+ }
301
+
302
+ /**
303
+ * Get the authenticated user.
304
+ */
305
+ public async me(req: Request, res: Response) {
306
+ const user = await req.auth.guard('api').user() as User | null;
307
+ if (!user) {
308
+ return res.json({ error: lang('auth.unauthenticated') }, 401);
309
+ }
310
+ return res.json({ user });
311
+ }
312
+
313
+ /**
314
+ * Log the user out of the application.
315
+ */
316
+ public async logout(req: Request, res: Response) {
317
+ try {
318
+ await req.auth.guard('api').logout();
319
+ } catch (e) {
320
+ Log.error('Logout error', { error: (e as Error).message });
321
+ }
322
+
323
+ return res.json({ message: lang('auth.logout_success') });
324
+ }
325
+ }
326
+ `;
327
+ }
328
+ getRegisterControllerStub() {
329
+ return `import { Request, Response, Mail, Hasher, config, Log, lang, app } from 'arikajs';
330
+ import { User } from '@Models/User';
331
+ import { VerifyEmail } from '@Mail/Auth/VerifyEmail';
332
+
333
+ export class RegisterController {
334
+ /**
335
+ * Handle a registration request for the application.
336
+ */
337
+ public async register(req: Request, res: Response) {
338
+ const { name, email, password } = await req.validate({
339
+ name: 'required|string|min:2',
340
+ email: 'required|email',
341
+ password: 'required|string|min:8|confirmed',
342
+ });
343
+
344
+ const existingUser = await User.where('email', email).first();
345
+ if (existingUser) {
346
+ return res.json({ error: lang('auth.email_taken') }, 409);
347
+ }
348
+
349
+ const hashedPassword = await Hasher.make(password);
350
+
351
+ const user = await User.create({
352
+ name,
353
+ email,
354
+ password: hashedPassword,
355
+ email_verified_at: null
356
+ });
357
+
358
+ // Send Verification Email
359
+ const appName = config('app.name', 'ArikaJS App');
360
+ const appUrl = config('app.url', 'http://localhost:3000');
361
+ const verificationUrl = \`\${appUrl}/api/auth/verify?email=\${encodeURIComponent(email)}&token=\${Buffer.from(email).toString('base64')}\`;
362
+
363
+ try {
364
+ await Mail.to(email).send(new VerifyEmail(name, verificationUrl, appName));
365
+ } catch (e) {
366
+ Log.error('Failed to send verification email', { error: (e as Error).message, email });
367
+ }
368
+
369
+ // Return response (JWT logic)
370
+ let token = undefined;
371
+ const guard = req.auth.guard('api');
372
+ if (guard && (guard.constructor.name === 'JwtGuard' || (guard as any).issueTokens)) {
373
+ const result = await (guard as any).issueTokens(user);
374
+ token = result.access_token;
375
+ }
376
+
377
+ return res.json({
378
+ message: lang('auth.register_success'),
379
+ access_token: token,
380
+ user
381
+ });
382
+ }
383
+ }
384
+ `;
385
+ }
386
+ getForgotPasswordControllerStub() {
387
+ return `import { Request, Response, Mail, config, Log, lang, DB } from 'arikajs';
388
+ import { User } from '@Models/User';
389
+ import { ResetPassword } from '@Mail/Auth/ResetPassword';
390
+ import { VerifyEmail } from '@Mail/Auth/VerifyEmail';
391
+ import * as crypto from 'crypto';
392
+
393
+ export class ForgotPasswordController {
394
+ /**
395
+ * Send a reset link to the given user.
396
+ */
397
+ public async sendResetLinkEmail(req: Request, res: Response) {
398
+ const { email } = await req.validate({
399
+ email: 'required|email',
400
+ });
401
+ const user = await User.where('email', email).first() as User | null;
402
+
403
+ if (!user) {
404
+ return res.json({ error: lang('auth.user_not_found') }, 404);
405
+ }
406
+
407
+ const appName = config('app.name', 'ArikaJS App');
408
+ const appUrl = config('app.url', 'http://localhost:3000');
409
+
410
+ // Check if user is verified
411
+ if (user.hasVerifiedEmail && !user.hasVerifiedEmail()) {
412
+ const verificationUrl = \`\${appUrl}/api/auth/verify?email=\${encodeURIComponent(email)}&token=\${Buffer.from(email).toString('base64')}\`;
413
+
414
+ try {
415
+ // Send verification link instead
416
+ await Mail.to(email).send(new VerifyEmail(user.name, verificationUrl, appName));
417
+ return res.json({
418
+ message: "Account unverified. A new verification link has been sent to your email."
419
+ });
420
+ } catch (e) {
421
+ Log.error('Failed to send verification email', { error: (e as Error).message, email });
422
+ }
423
+ }
424
+
425
+ // Proceed with Password Reset
426
+ const token = crypto.randomBytes(32).toString('hex');
427
+
428
+ await DB.table('password_resets').where('email', email).delete();
429
+ await DB.table('password_resets').insert({
430
+ email,
431
+ token,
432
+ created_at: new Date()
433
+ });
434
+
435
+ const resetUrl = \`\${appUrl}/api/auth/password/reset?email=\${encodeURIComponent(email)}&token=\${token}\`;
436
+
437
+ try {
438
+ await Mail.to(email).send(new ResetPassword(resetUrl, appName));
439
+ } catch (e) {
440
+ Log.error('Failed to send reset email', { error: (e as Error).message, email });
441
+ }
442
+
443
+ return res.json({ message: lang('auth.reset_link_sent') });
444
+ }
445
+ }
446
+ `;
447
+ }
448
+ getResetPasswordControllerStub() {
449
+ return `import { Request, Response, Hasher, lang, DB } from 'arikajs';
450
+ import { User } from '@Models/User';
451
+
452
+ export class ResetPasswordController {
453
+ /**
454
+ * Reset the given user's password.
455
+ */
456
+ public async reset(req: Request, res: Response) {
457
+ const { token, email, password } = await req.validate({
458
+ token: 'required|string',
459
+ email: 'required|email',
460
+ password: 'required|string|min:8|confirmed'
461
+ });
462
+
463
+ // Verify token against database
464
+ const resetRecord = await DB.table('password_resets').where('email', email).where('token', token).first();
465
+ if (!resetRecord) {
466
+ return res.json({ error: lang('auth.invalid_reset_token') }, 403);
467
+ }
468
+
469
+ const user = await User.where('email', email).first() as User | null;
470
+ if (!user) {
471
+ return res.json({ error: lang('auth.user_not_found') }, 404);
472
+ }
473
+
474
+ await user.update({
475
+ password: await Hasher.make(password)
476
+ });
477
+
478
+ // Delete the used token
479
+ await DB.table('password_resets').where('email', email).delete();
480
+
481
+ return res.json({ message: lang('auth.password_reset_success') });
482
+ }
483
+ }
484
+ `;
485
+ }
486
+ getVerifyEmailControllerStub() {
487
+ return `import { Request, Response, Mail, config, Log, lang } from 'arikajs';
488
+ import { User } from '@Models/User';
489
+ import { VerifyEmail } from '@Mail/Auth/VerifyEmail';
490
+
491
+ export class VerifyEmailController {
492
+ /**
493
+ * Mark the authenticated user's email address as verified.
494
+ */
495
+ public async verify(req: Request, res: Response) {
496
+ const { email, token } = req.all();
497
+
498
+ if (!email || !token) {
499
+ return res.json({ error: lang('auth.missing_verification_data') }, 400);
500
+ }
501
+
502
+ const expectedToken = Buffer.from(email).toString('base64');
503
+ if (token !== expectedToken) {
504
+ Log.warning('Invalid email verification attempt', { email, token });
505
+ return res.json({ error: lang('auth.invalid_verification_token') }, 403);
506
+ }
507
+
508
+ const user = await User.where('email', email).first() as User | null;
509
+ if (!user) {
510
+ return res.json({ error: lang('auth.user_not_found') }, 404);
511
+ }
512
+
513
+ if (user.email_verified_at) {
514
+ return res.json({ message: lang('auth.email_already_verified') });
515
+ }
516
+
517
+ try {
518
+ await user.update({
519
+ email_verified_at: new Date()
520
+ });
521
+ } catch (e) {
522
+ Log.error('Email verification update failed', { error: (e as Error).message, email });
523
+ return res.json({ error: lang('auth.verification_failed') }, 500);
524
+ }
525
+
526
+ return res.json({ message: lang('auth.email_verified') });
527
+ }
528
+
529
+ /**
530
+ * Resend the email verification notification.
531
+ * The user is already authenticated via the auth middleware.
532
+ */
533
+ public async resend(req: Request, res: Response) {
534
+ // Get the authenticated user directly from the JWT guard
535
+ const user = await req.auth.guard('api').user() as User | null;
536
+
537
+ if (!user) {
538
+ return res.json({ error: lang('auth.unauthenticated') }, 401);
539
+ }
540
+
541
+ if (user.email_verified_at) {
542
+ return res.json({ message: lang('auth.email_already_verified') });
543
+ }
544
+
545
+ const appName = config('app.name', 'ArikaJS App');
546
+ const appUrl = config('app.url', 'http://localhost:3000');
547
+ const verificationUrl = \`\${appUrl}/api/auth/verify?email=\${encodeURIComponent(user.email)}&token=\${Buffer.from(user.email).toString('base64')}\`;
548
+
549
+ try {
550
+ await Mail.to(user.email).send(new VerifyEmail(user.name, verificationUrl, appName));
551
+ } catch (e) {
552
+ Log.error('Failed to resend verification email', { error: (e as Error).message, email: user.email });
553
+ return res.json({ error: lang('auth.verification_failed') }, 500);
554
+ }
555
+
556
+ return res.json({ message: lang('auth.verification_resent') });
557
+ }
558
+ }
559
+ `;
560
+ }
561
+ getVerifyEmailMailableStub() {
562
+ return `import { Mailable } from 'arikajs';
563
+
564
+ export class VerifyEmail extends Mailable {
565
+ constructor(
566
+ private name: string,
567
+ private verificationUrl: string,
568
+ private appName: string
569
+ ) {
570
+ super();
571
+ }
572
+
573
+ public build() {
574
+ return this.subject('Verify Your Email Address')
575
+ .view('emails/auth/verify_email', {
576
+ name: this.name,
577
+ verification_url: this.verificationUrl,
578
+ app_name: this.appName,
579
+ year: new Date().getFullYear()
580
+ });
581
+ }
582
+ }
583
+ `;
584
+ }
585
+ getResetPasswordMailableStub() {
586
+ return `import { Mailable } from 'arikajs';
587
+
588
+ export class ResetPassword extends Mailable {
589
+ constructor(
590
+ private resetUrl: string,
591
+ private appName: string
592
+ ) {
593
+ super();
594
+ }
595
+
596
+ public build() {
597
+ return this.subject('Reset Password Notification')
598
+ .view('emails/auth/reset_password', {
599
+ reset_url: this.resetUrl,
600
+ app_name: this.appName,
601
+ year: new Date().getFullYear()
602
+ });
603
+ }
604
+ }
605
+ `;
606
+ }
607
+ appendRoutes() {
608
+ const cwd = process.cwd();
609
+ const apiRoutesPath = path_1.default.join(cwd, 'routes', 'api.ts');
610
+ if (!fs_1.default.existsSync(apiRoutesPath))
611
+ return;
612
+ let content = fs_1.default.readFileSync(apiRoutesPath, 'utf8');
613
+ // Add imports if they don't exist
614
+ const imports = [
615
+ "import { LoginController } from '@Controllers/Auth/LoginController';",
616
+ "import { RegisterController } from '@Controllers/Auth/RegisterController';",
617
+ "import { ForgotPasswordController } from '@Controllers/Auth/ForgotPasswordController';",
618
+ "import { ResetPasswordController } from '@Controllers/Auth/ResetPasswordController';",
619
+ "import { VerifyEmailController } from '@Controllers/Auth/VerifyEmailController';"
620
+ ];
621
+ imports.forEach(imp => {
622
+ if (!content.includes(imp)) {
623
+ content = imp + "\n" + content;
624
+ }
625
+ });
626
+ if (!content.includes("Route.group({ prefix: 'auth'")) {
627
+ const routeDefinitions = `
628
+ // Authentication Routes (API)
629
+ Route.group({ prefix: 'auth' }, () => {
630
+ Route.post('/register', [RegisterController, 'register']);
631
+ Route.post('/login', [LoginController, 'login']);
632
+ Route.post('/logout', [LoginController, 'logout']).withMiddleware('auth:api');
633
+
634
+ // Email Verification
635
+ Route.get('/verify', [VerifyEmailController, 'verify']);
636
+ Route.post('/verification-notification', [VerifyEmailController, 'resend']).withMiddleware('auth:api');
637
+
638
+ // Password Reset
639
+ Route.post('/password/email', [ForgotPasswordController, 'sendResetLinkEmail']);
640
+ Route.post('/password/reset', [ResetPasswordController, 'reset']);
641
+ });
642
+
643
+ Route.get('/user', [LoginController, 'me']).withMiddleware('auth:api');
644
+ `;
645
+ content += routeDefinitions;
646
+ }
647
+ fs_1.default.writeFileSync(apiRoutesPath, content);
648
+ }
649
+ getAuthLangStub() {
650
+ return JSON.stringify({
651
+ "failed": "These credentials do not match our records.",
652
+ "failed_required": "Email and password are required.",
653
+ "throttle": "Too many login attempts. Please try again in :seconds seconds.",
654
+ "login_success": "Successfully logged in.",
655
+ "logout_success": "Successfully logged out.",
656
+ "register_success": "Registration successful! Please check your email to verify your account.",
657
+ "email_taken": "This email is already registered.",
658
+ "unauthenticated": "You must be logged in to access this resource.",
659
+ "email_verified": "Email address verified successfully!",
660
+ "email_already_verified": "Email address is already verified.",
661
+ "verification_failed": "Email verification failed. The link may be expired or invalid.",
662
+ "verification_resent": "Verification link has been resent to your email address.",
663
+ "missing_verification_data": "Missing verification data (email or token).",
664
+ "invalid_verification_token": "The verification token is invalid.",
665
+ "user_not_found": "We could not find a user with that email address.",
666
+ "reset_link_sent": "If that email is registered, a password reset link has been sent.",
667
+ "invalid_reset_token": "The password reset token is invalid.",
668
+ "password_reset_success": "Your password has been reset successfully. You can now log in.",
669
+ "email_not_verified": "Your email address is not verified. Please verify your email before attempting to reset your password."
670
+ }, null, 4);
671
+ }
672
+ getValidationLangStub() {
673
+ return JSON.stringify({
674
+ "failed": "The given data was invalid.",
675
+ "required": "The :attribute field is required.",
676
+ "email": "The :attribute must be a valid email address.",
677
+ "min": {
678
+ "string": "The :attribute must be at least :min characters."
679
+ },
680
+ "confirmed": "The :attribute confirmation does not match."
681
+ }, null, 4);
682
+ }
683
+ getVerifyEmailViewStub() {
684
+ return `<!DOCTYPE html>
685
+ <html lang="en">
686
+ <head>
687
+ <meta charset="UTF-8">
688
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
689
+ <title>Verify Your Email - {{ config('app.name', 'ArikaJS') }}</title>
690
+ <style>
691
+ body { margin: 0; padding: 0; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: #f4f4f7; color: #51545e; }
692
+ .wrapper { width: 100%; margin: 0; padding: 0; -webkit-text-size-adjust: none; background-color: #f4f4f7; }
693
+ .content { width: 100%; max-width: 570px; margin: 0 auto; padding: 35px; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 0 rgba(0, 0, 150, 0.025), 2px 4px 0 rgba(0, 0, 150, 0.015); }
694
+ .header { padding: 25px 0; text-align: center; }
695
+ .header a { font-size: 19px; font-weight: bold; color: #333; text-decoration: none; }
696
+ .button { display: inline-block; background-color: #22bc66; color: #FFF !important; padding: 12px 25px; text-decoration: none; border-radius: 3px; font-weight: bold; }
697
+ .footer { text-align: center; padding: 25px; font-size: 12px; color: #b0adc5; }
698
+ </style>
699
+ </head>
700
+ <body>
701
+ <div class="wrapper">
702
+ <div class="header"><a href="#">{{ app_name }}</a></div>
703
+ <div class="content">
704
+ <h1>Verify your email address</h1>
705
+ <p>Hi {{ name }},</p>
706
+ <p>Thanks for signing up! Please confirm your email address by clicking the button below.</p>
707
+ <p style="text-align: center;"><a href="{{ verification_url }}" class="button">Verify Email</a></p>
708
+ <p>If you did not create an account, no further action is required.</p>
709
+ <p>Regards,<br>{{ app_name }} Team</p>
710
+ </div>
711
+ <div class="footer">&copy; {{ year }} {{ app_name }}. All rights reserved.</div>
712
+ </div>
713
+ </body>
714
+ </html>`;
715
+ }
716
+ getResetPasswordViewStub() {
717
+ return `<!DOCTYPE html>
718
+ <html lang="en">
719
+ <head>
720
+ <meta charset="UTF-8">
721
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
722
+ <title>Reset Password - {{ config('app.name', 'ArikaJS') }}</title>
723
+ <style>
724
+ body { margin: 0; padding: 0; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: #f4f4f7; color: #51545e; }
725
+ .wrapper { width: 100%; margin: 0; padding: 0; -webkit-text-size-adjust: none; background-color: #f4f4f7; }
726
+ .content { width: 100%; max-width: 570px; margin: 0 auto; padding: 35px; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 0 rgba(0, 0, 150, 0.025), 2px 4px 0 rgba(0, 0, 150, 0.015); }
727
+ .header { padding: 25px 0; text-align: center; }
728
+ .header a { font-size: 19px; font-weight: bold; color: #333; text-decoration: none; }
729
+ .button { display: inline-block; background-color: #3869d4; color: #FFF !important; padding: 12px 25px; text-decoration: none; border-radius: 3px; font-weight: bold; }
730
+ .footer { text-align: center; padding: 25px; font-size: 12px; color: #b0adc5; }
731
+ </style>
732
+ </head>
733
+ <body>
734
+ <div class="wrapper">
735
+ <div class="header"><a href="#">{{ app_name }}</a></div>
736
+ <div class="content">
737
+ <h1>Reset your password</h1>
738
+ <p>You are receiving this email because we received a password reset request for your account.</p>
739
+ <p style="text-align: center;"><a href="{{ reset_url }}" class="button">Reset Password</a></p>
740
+ <p>This password reset link will expire in 60 minutes.</p>
741
+ <p>If you did not request a password reset, no further action is required.</p>
742
+ <p>Regards,<br>{{ app_name }} Team</p>
743
+ </div>
744
+ <div class="footer">&copy; {{ year }} {{ app_name }}. All rights reserved.</div>
745
+ </div>
746
+ </body>
747
+ </html>`;
748
+ }
749
+ }
750
+ exports.AuthApiInstallCommand = AuthApiInstallCommand;
751
+ //# sourceMappingURL=AuthApiInstallCommand.js.map