@arikajs/cli 0.0.4 → 0.0.6

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 (230) 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/package.json.backup +22 -0
  174. package/dist/templates/app/public/assets/img/favicon.png +0 -0
  175. package/dist/templates/app/public/assets/img/logo.png +0 -0
  176. package/dist/templates/app/resources/views/errors/401.ark.html +320 -0
  177. package/dist/templates/app/resources/views/errors/403.ark.html +320 -0
  178. package/dist/templates/app/resources/views/errors/404.ark.html +320 -0
  179. package/dist/templates/app/resources/views/errors/419.ark.html +320 -0
  180. package/dist/templates/app/resources/views/errors/429.ark.html +320 -0
  181. package/dist/templates/app/resources/views/errors/500.ark.html +320 -0
  182. package/dist/templates/app/resources/views/errors/503.ark.html +320 -0
  183. package/dist/templates/app/resources/views/welcome.ark.html +846 -0
  184. package/dist/templates/app/routes/api.ts +39 -0
  185. package/dist/templates/app/routes/web.ts +10 -0
  186. package/dist/templates/app/server.ts +15 -0
  187. package/dist/templates/app/tsconfig.json +27 -0
  188. package/package.json +16 -10
  189. package/templates/app/.env.example +2 -2
  190. package/templates/app/README.md +62 -0
  191. package/templates/app/app/Exceptions/Handler.ts +19 -0
  192. package/templates/app/app/Http/Controllers/UserController.ts +90 -0
  193. package/templates/app/app/Http/Kernel.ts +39 -14
  194. package/templates/app/app/Http/Middleware/Authenticate.ts +10 -0
  195. package/templates/app/app/Http/Middleware/EnsureEmailIsVerified.ts +18 -0
  196. package/templates/app/app/Http/Middleware/ExampleMiddleware.ts +12 -0
  197. package/templates/app/app/Http/Middleware/RedirectIfAuthenticated.ts +18 -0
  198. package/templates/app/app/Http/Middleware/TrimStrings.ts +12 -0
  199. package/templates/app/app/Http/Middleware/TrustProxies.ts +23 -0
  200. package/templates/app/app/Http/Middleware/VerifyCsrfToken.ts +10 -0
  201. package/templates/app/app/Models/User.ts +5 -5
  202. package/templates/app/app/Providers/AppServiceProvider.ts +17 -0
  203. package/templates/app/app/Providers/RouteServiceProvider.ts +35 -0
  204. package/templates/app/bootstrap/app.ts +30 -2
  205. package/templates/app/config/app.ts +7 -6
  206. package/templates/app/config/cache.ts +14 -7
  207. package/templates/app/config/database.ts +48 -21
  208. package/templates/app/config/filesystems.ts +11 -9
  209. package/templates/app/config/http.ts +3 -2
  210. package/templates/app/config/logging.ts +6 -4
  211. package/templates/app/config/mail.ts +10 -8
  212. package/templates/app/config/queue.ts +6 -4
  213. package/templates/app/database/migrations/2024_01_01_000001_create_users_table.ts +25 -0
  214. package/templates/app/database/migrations/2024_01_01_000002_create_password_resets_table.ts +22 -0
  215. package/templates/app/database/migrations/2024_01_01_000003_create_failed_jobs_table.ts +25 -0
  216. package/templates/app/package.json +4 -3
  217. package/templates/app/package.json.backup +22 -0
  218. package/templates/app/public/assets/img/favicon.png +0 -0
  219. package/templates/app/public/assets/img/logo.png +0 -0
  220. package/templates/app/resources/views/welcome.ark.html +846 -0
  221. package/templates/app/routes/api.ts +8 -3
  222. package/templates/app/routes/web.ts +8 -4
  223. package/templates/app/server.ts +3 -4
  224. package/templates/app/tsconfig.json +12 -0
  225. package/dist/tests/Cli.test.d.ts +0 -2
  226. package/dist/tests/Cli.test.d.ts.map +0 -1
  227. package/dist/tests/Cli.test.js +0 -16
  228. package/dist/tests/Cli.test.js.map +0 -1
  229. package/templates/app/app/Controllers/UserController.ts +0 -88
  230. package/templates/app/resources/views/welcome.html +0 -470
@@ -0,0 +1,1100 @@
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.AuthWebInstallCommand = void 0;
7
+ const console_1 = require("@arikajs/console");
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ class AuthWebInstallCommand extends console_1.Command {
11
+ constructor() {
12
+ super(...arguments);
13
+ this.signature = 'auth:install:web {--force}';
14
+ this.description = 'Scaffold web authentication views and routes (Session-based)';
15
+ }
16
+ async handle() {
17
+ this.info('Scaffolding authentication system...');
18
+ const force = this.option('force');
19
+ const cwd = process.cwd();
20
+ // Target Paths
21
+ const configPath = path_1.default.join(cwd, 'config', 'auth.ts');
22
+ const sessionConfigPath = path_1.default.join(cwd, 'config', 'session.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
+ // Generate timestamped migration name
26
+ const date = new Date();
27
+ const baseTimestamp = date.getFullYear().toString() + '_' +
28
+ (date.getMonth() + 1).toString().padStart(2, '0') + '_' +
29
+ date.getDate().toString().padStart(2, '0') + '_' +
30
+ date.getHours().toString().padStart(2, '0') +
31
+ date.getMinutes().toString().padStart(2, '0');
32
+ const timestamp1 = baseTimestamp + date.getSeconds().toString().padStart(2, '0');
33
+ const timestamp2 = baseTimestamp + (date.getSeconds() + 1).toString().padStart(2, '0');
34
+ const migrationDir = path_1.default.join(cwd, 'database', 'migrations');
35
+ const usersMigrationPath = path_1.default.join(migrationDir, `${timestamp1}_create_users_table.ts`);
36
+ const passwordMigrationPath = path_1.default.join(migrationDir, `${timestamp2}_create_password_resets_table.ts`);
37
+ // Check if any file exists unless --force is used
38
+ if (!force) {
39
+ const existingFiles = [configPath, sessionConfigPath, modelPath]
40
+ .filter(p => fs_1.default.existsSync(p));
41
+ if (existingFiles.length > 0) {
42
+ this.error('Authentication scaffold failed. The following files already exist:');
43
+ existingFiles.forEach(f => this.writeln(` - ${f}`));
44
+ this.info('Use the --force flag to overwrite them.');
45
+ return;
46
+ }
47
+ }
48
+ // Ensure directories exist
49
+ [
50
+ path_1.default.dirname(configPath),
51
+ path_1.default.dirname(modelPath),
52
+ authControllerDir,
53
+ migrationDir
54
+ ].forEach(dir => {
55
+ if (!fs_1.default.existsSync(dir))
56
+ fs_1.default.mkdirSync(dir, { recursive: true });
57
+ });
58
+ // Publish configuration and core files
59
+ fs_1.default.writeFileSync(configPath, this.getConfigStub());
60
+ fs_1.default.writeFileSync(sessionConfigPath, this.getSessionConfigStub());
61
+ fs_1.default.writeFileSync(modelPath, this.getModelStub());
62
+ // Publish Controllers
63
+ fs_1.default.writeFileSync(path_1.default.join(authControllerDir, 'LoginController.ts'), this.getLoginControllerStub());
64
+ fs_1.default.writeFileSync(path_1.default.join(authControllerDir, 'RegisterController.ts'), this.getRegisterControllerStub());
65
+ fs_1.default.writeFileSync(path_1.default.join(authControllerDir, 'ForgotPasswordController.ts'), this.getForgotPasswordControllerStub());
66
+ fs_1.default.writeFileSync(path_1.default.join(authControllerDir, 'ResetPasswordController.ts'), this.getResetPasswordControllerStub());
67
+ fs_1.default.writeFileSync(path_1.default.join(authControllerDir, 'VerifyEmailController.ts'), this.getVerifyEmailControllerStub());
68
+ // Publish Mailables
69
+ const mailAuthDir = path_1.default.join(cwd, 'app', 'Mail', 'Auth');
70
+ if (!fs_1.default.existsSync(mailAuthDir))
71
+ fs_1.default.mkdirSync(mailAuthDir, { recursive: true });
72
+ fs_1.default.writeFileSync(path_1.default.join(mailAuthDir, 'VerifyEmail.ts'), this.getVerifyEmailMailableStub());
73
+ fs_1.default.writeFileSync(path_1.default.join(mailAuthDir, 'ResetPassword.ts'), this.getResetPasswordMailableStub());
74
+ // Check if migrations already exist
75
+ const migrations = fs_1.default.existsSync(migrationDir) ? fs_1.default.readdirSync(migrationDir) : [];
76
+ if (!migrations.some(file => file.includes('create_users_table'))) {
77
+ fs_1.default.writeFileSync(usersMigrationPath, this.getUsersMigrationStub());
78
+ }
79
+ if (!migrations.some(file => file.includes('create_password_resets_table'))) {
80
+ fs_1.default.writeFileSync(passwordMigrationPath, this.getPasswordResetMigrationStub());
81
+ }
82
+ // Update Routes and Views
83
+ this.publishViews();
84
+ this.appendWebRoutes();
85
+ this.publishTranslations();
86
+ this.updateWelcomeHeader();
87
+ this.writeln('');
88
+ this.success('Premium Web Authentication scaffolding generated successfully!');
89
+ }
90
+ getConfigStub() {
91
+ return `import { User } from '@Models/User';
92
+
93
+ export default {
94
+ default: 'web',
95
+ guards: {
96
+ web: {
97
+ driver: 'session',
98
+ provider: 'users',
99
+ },
100
+ api: {
101
+ driver: 'jwt',
102
+ provider: 'users',
103
+ secret: process.env.JWT_SECRET || 'your-super-secret-jwt-key',
104
+ options: {
105
+ expiresIn: '24h',
106
+ },
107
+ },
108
+ },
109
+ providers: {
110
+ users: {
111
+ driver: 'eloquent',
112
+ model: User,
113
+ },
114
+ },
115
+ lockout: {
116
+ maxAttempts: 5,
117
+ decayMinutes: 15,
118
+ },
119
+ };
120
+ `;
121
+ }
122
+ getSessionConfigStub() {
123
+ return `export default {
124
+ /**
125
+ * Default session driver.
126
+ * Supported: "memory", "file"
127
+ * Set SESSION_DRIVER in your .env file.
128
+ */
129
+ driver: process.env.SESSION_DRIVER || 'file',
130
+
131
+ /**
132
+ * Session lifetime in minutes.
133
+ * After this period, idle sessions are expired.
134
+ */
135
+ lifetime: Number(process.env.SESSION_LIFETIME) || 120,
136
+
137
+ /**
138
+ * Session cookie name.
139
+ */
140
+ cookie: process.env.SESSION_COOKIE || 'arika_session',
141
+
142
+ /**
143
+ * Cookie path — typically '/' for all routes.
144
+ */
145
+ path: '/',
146
+
147
+ /**
148
+ * Storage path for the file driver.
149
+ */
150
+ storagePath: './storage/sessions',
151
+
152
+ /**
153
+ * Mark the session cookie as Secure (HTTPS only).
154
+ * Enable this in production.
155
+ */
156
+ secure: process.env.SESSION_SECURE === 'true',
157
+
158
+ /**
159
+ * Mark the session cookie as HttpOnly (not accessible to JS).
160
+ */
161
+ httpOnly: true,
162
+
163
+ /**
164
+ * SameSite cookie attribute.
165
+ * Options: 'Lax', 'Strict', 'None'
166
+ */
167
+ sameSite: 'Lax',
168
+
169
+ /**
170
+ * Enable session locking to prevent race conditions.
171
+ */
172
+ locking: false,
173
+
174
+ /**
175
+ * Lock timeout in seconds.
176
+ */
177
+ lockTimeout: 10,
178
+
179
+ /**
180
+ * Garbage collection probability.
181
+ * 0.01 = 1% of requests will trigger GC.
182
+ */
183
+ gcProbability: 0.01,
184
+ };
185
+ `;
186
+ }
187
+ getModelStub() {
188
+ return `import { Model } from 'arikajs';
189
+
190
+ export class User extends Model {
191
+ protected static table = 'users';
192
+
193
+ public declare id: number;
194
+ public declare name: string;
195
+ public declare email: string;
196
+ public declare password: string;
197
+ public declare email_verified_at: string | Date | null;
198
+
199
+ protected fillable: string[] = ['name', 'email', 'password', 'email_verified_at'];
200
+ protected hidden: string[] = ['password'];
201
+
202
+ public hasVerifiedEmail(): boolean {
203
+ return this.email_verified_at !== null;
204
+ }
205
+ }
206
+ `;
207
+ }
208
+ getUsersMigrationStub() {
209
+ return `import { Migration, SchemaBuilder } from 'arikajs';
210
+
211
+ export default class CreateUsersTable extends Migration {
212
+ public async up(schema: SchemaBuilder): Promise<void> {
213
+ await schema.create('users', (table: any) => {
214
+ table.id();
215
+ table.string('name');
216
+ table.string('email').unique();
217
+ table.timestamp('email_verified_at').nullable();
218
+ table.string('password');
219
+ table.string('remember_token', 100).nullable();
220
+ table.timestamps();
221
+ });
222
+ }
223
+ public async down(schema: SchemaBuilder): Promise<void> {
224
+ await schema.dropIfExists('users');
225
+ }
226
+ }
227
+ `;
228
+ }
229
+ getPasswordResetMigrationStub() {
230
+ return `import { Migration, SchemaBuilder } from 'arikajs';
231
+
232
+ export default class CreatePasswordResetsTable extends Migration {
233
+ public async up(schema: SchemaBuilder): Promise<void> {
234
+ await schema.create('password_resets', (table: any) => {
235
+ table.string('email').index();
236
+ table.string('token');
237
+ table.timestamp('created_at').nullable();
238
+ });
239
+ }
240
+ public async down(schema: SchemaBuilder): Promise<void> {
241
+ await schema.dropIfExists('password_resets');
242
+ }
243
+ }
244
+ `;
245
+ }
246
+ getLoginControllerStub() {
247
+ return `import { Request, Response, Log, lang, app, view } from 'arikajs';
248
+ import { User } from '@Models/User';
249
+
250
+ export class LoginController {
251
+ /**
252
+ * Show the application's login form.
253
+ */
254
+ public async showLogin(req: Request, res: Response) {
255
+ return await view('auth.login', {
256
+ error: req.query('error'),
257
+ success: req.query('success') || req.query('message'),
258
+ email: req.query('email') || ''
259
+ });
260
+ }
261
+
262
+ /**
263
+ * Handle an incoming authentication request.
264
+ */
265
+ public async login(req: Request, res: Response) {
266
+ const { email, password } = await req.validate({
267
+ email: 'required|email',
268
+ password: 'required|string',
269
+ });
270
+
271
+ const credentials = { email, password };
272
+ const result = await req.auth.attempt(credentials);
273
+
274
+ if (!result) {
275
+ Log.info('Failed login attempt', { email: credentials.email });
276
+ return await view('auth.login', {
277
+ error: lang('auth.failed'),
278
+ email: credentials.email
279
+ });
280
+ }
281
+
282
+ return res.redirect('/dashboard');
283
+ }
284
+
285
+ /**
286
+ * Show the application dashboard.
287
+ */
288
+ public async showDashboard(req: Request, res: Response) {
289
+ const user = await req.auth.user() as User | null;
290
+ return await view('dashboard', { user: user || { name: 'User' } });
291
+ }
292
+
293
+ /**
294
+ * Log the user out of the application.
295
+ */
296
+ public async logout(req: Request, res: Response) {
297
+ await req.auth.logout();
298
+ return res.redirect('/auth/login');
299
+ }
300
+ }
301
+ `;
302
+ }
303
+ getRegisterControllerStub() {
304
+ return `import { Request, Response, Mail, Hasher, config, Log, app, view } from 'arikajs';
305
+ import { User } from '@Models/User';
306
+ import { VerifyEmail } from '@Mail/Auth/VerifyEmail';
307
+
308
+ export class RegisterController {
309
+ public async showRegister(req: Request, res: Response) {
310
+ return await view('auth.register', { error: null, success: null, old: {} });
311
+ }
312
+
313
+ public async register(req: Request, res: Response) {
314
+ const { name, email, password } = await req.validate({
315
+ name: 'required|string|min:2',
316
+ email: 'required|email',
317
+ password: 'required|string|min:8|confirmed',
318
+ });
319
+
320
+ const hashedPassword = await Hasher.make(password);
321
+ const user = await User.create({ name, email, password: hashedPassword });
322
+
323
+ const appName = config('app.name', 'ArikaJS App');
324
+ const appUrl = config('app.url', 'http://localhost:3000');
325
+ const verificationUrl = appUrl + '/auth/verify?email=' + encodeURIComponent(email) + '&token=' + Buffer.from(email).toString('base64');
326
+
327
+ try {
328
+ await Mail.to(email).send(new VerifyEmail(name, verificationUrl, appName));
329
+ } catch (e) {
330
+ Log.error('Failed to send verification email', { error: (e as Error).message, email });
331
+ }
332
+
333
+ await req.auth.login(user);
334
+
335
+ return await view('auth.register', {
336
+ success: 'Registration successful! You are now logged in.',
337
+ user,
338
+ old: {}
339
+ });
340
+ }
341
+ }`;
342
+ }
343
+ getForgotPasswordControllerStub() {
344
+ return `import { Request, Response, Mail, config, Log, lang, DB, view } from 'arikajs';
345
+ import { User } from '@Models/User';
346
+ import { ResetPassword } from '@Mail/Auth/ResetPassword';
347
+ import * as crypto from 'crypto';
348
+
349
+ export class ForgotPasswordController {
350
+ /**
351
+ * Show the form to request a password reset link.
352
+ */
353
+ public async showLinkRequestForm(req: Request, res: Response) {
354
+ return await view('auth.passwords.email', { error: null, success: null });
355
+ }
356
+
357
+ /**
358
+ * Send a reset link to the given user.
359
+ */
360
+ public async sendResetLinkEmail(req: Request, res: Response) {
361
+ const { email } = await req.validate({
362
+ email: 'required|email'
363
+ });
364
+
365
+ const user = await User.where('email', email).first() as User | null;
366
+
367
+ if (!user) {
368
+ return await view('auth.passwords.email', {
369
+ error: lang('auth.user_not_found'),
370
+ success: null
371
+ });
372
+ }
373
+
374
+ if (!user.email_verified_at) {
375
+ return await view('auth.passwords.email', {
376
+ error: lang('auth.email_not_verified'),
377
+ success: null
378
+ });
379
+ }
380
+
381
+ const token = crypto.randomBytes(32).toString('hex');
382
+ await DB.table('password_resets').where('email', email).delete();
383
+ await DB.table('password_resets').insert({
384
+ email,
385
+ token,
386
+ created_at: new Date()
387
+ });
388
+
389
+ const appName = config('app.name', 'ArikaJS App');
390
+ const appUrl = config('app.url', 'http://localhost:3000');
391
+ const resetUrl = appUrl + '/auth/password/reset/' + token + '?email=' + encodeURIComponent(email);
392
+ try {
393
+ await Mail.to(email).send(new ResetPassword(resetUrl, appName));
394
+ } catch (e) {
395
+ Log.error('Failed to send reset email', { error: (e as Error).message, email });
396
+ }
397
+
398
+ return await view('auth.passwords.email', {
399
+ success: lang('auth.reset_link_sent')
400
+ });
401
+ }
402
+ }`;
403
+ }
404
+ getResetPasswordControllerStub() {
405
+ return `import { Request, Response, Hasher, lang, DB, view } from 'arikajs';
406
+ import { User } from '@Models/User';
407
+
408
+ export class ResetPasswordController {
409
+ /**
410
+ * Display the password reset view for the given token.
411
+ */
412
+ public async showResetForm(req: Request, res: Response) {
413
+ return await view('auth.passwords.reset', {
414
+ token: req.param('token', ''),
415
+ error: null
416
+ });
417
+ }
418
+
419
+ /**
420
+ * Reset the given user's password.
421
+ */
422
+ public async reset(req: Request, res: Response) {
423
+ const { email, password, token } = await req.validate({
424
+ token: 'required|string',
425
+ email: 'required|email',
426
+ password: 'required|string|min:8|confirmed'
427
+ });
428
+
429
+ const resetRecord = await DB.table('password_resets').where('email', email).where('token', token).first();
430
+ if (!resetRecord) {
431
+ return await view('auth.passwords.reset', {
432
+ error: lang('auth.invalid_reset_token'),
433
+ token
434
+ });
435
+ }
436
+
437
+ const user = await User.where('email', email).first() as User | null;
438
+ if (!user) {
439
+ return await view('auth.passwords.reset', {
440
+ error: lang('auth.user_not_found'),
441
+ token
442
+ });
443
+ }
444
+
445
+ if (!user.email_verified_at) {
446
+ return await view('auth.passwords.reset', {
447
+ error: lang('auth.email_not_verified'),
448
+ token
449
+ });
450
+ }
451
+
452
+ await user.update({ password: await Hasher.make(password) });
453
+ await DB.table('password_resets').where('email', email).delete();
454
+
455
+ return res.redirect(\`/auth/login?success=\${encodeURIComponent(lang('auth.password_reset_success'))}\`);
456
+ }
457
+ }
458
+ `;
459
+ }
460
+ getVerifyEmailControllerStub() {
461
+ return `import { Request, Response, Log, lang, Mail, config, app, view } from 'arikajs';
462
+ import { User } from '@Models/User';
463
+ import { VerifyEmail } from '@Mail/Auth/VerifyEmail';
464
+
465
+ export class VerifyEmailController {
466
+ /**
467
+ * Mark the authenticated user's email address as verified.
468
+ */
469
+ public async verify(req: Request, res: Response) {
470
+ const { email, token } = req.all();
471
+
472
+ if (!email || !token) {
473
+ return res.redirect(\`/auth/login?error=\${encodeURIComponent(lang('auth.missing_verification_data'))}\`);
474
+ }
475
+
476
+ const expectedToken = Buffer.from(email).toString('base64');
477
+ if (token !== expectedToken) {
478
+ Log.warning('Invalid email verification attempt', { email, token });
479
+ return res.redirect(\`/auth/login?error=\${encodeURIComponent(lang('auth.invalid_verification_token'))}\`);
480
+ }
481
+
482
+ const user = await User.where('email', email).first() as User | null;
483
+ if (!user) {
484
+ return res.redirect(\`/auth/login?error=\${encodeURIComponent(lang('auth.user_not_found'))}\`);
485
+ }
486
+
487
+ if (user.email_verified_at) {
488
+ return res.redirect(\`/auth/login?success=\${encodeURIComponent(lang('auth.email_already_verified'))}&email=\${encodeURIComponent(email)}\`);
489
+ }
490
+
491
+ try {
492
+ await user.update({
493
+ email_verified_at: new Date()
494
+ });
495
+ } catch (e) {
496
+ Log.error('Email verification update failed', { error: (e as Error).message, email });
497
+ return res.redirect(\`/auth/login?error=\${encodeURIComponent(lang('auth.verification_failed'))}\`);
498
+ }
499
+
500
+ return res.redirect(\`/auth/login?success=\${encodeURIComponent(lang('auth.email_verified'))}&email=\${encodeURIComponent(email)}\`);
501
+ }
502
+
503
+ /**
504
+ * Resend the email verification notification.
505
+ */
506
+ public async resend(req: Request, res: Response) {
507
+
508
+ const user = await req.auth.user() as User | null;
509
+ if (!user) {
510
+ return res.redirect('/auth/login');
511
+ }
512
+
513
+ if (typeof user.hasVerifiedEmail === 'function' ? user.hasVerifiedEmail() : user.email_verified_at) {
514
+ return res.redirect('/dashboard');
515
+ }
516
+
517
+ const appName = config('app.name', 'ArikaJS App');
518
+ const appUrl = config('app.url', 'http://localhost:3000');
519
+ const verificationUrl = appUrl + '/auth/verify?email=' + encodeURIComponent(user.email) + '&token=' + Buffer.from(user.email).toString('base64');
520
+
521
+ try {
522
+ await Mail.to(user.email).send(new VerifyEmail(user.name, verificationUrl, appName));
523
+ } catch (e) {
524
+ Log.error('Failed to send verification email', { error: (e as Error).message, email: user.email });
525
+ }
526
+
527
+ return res.redirect('/dashboard?success=' + encodeURIComponent('Verification link resent!'));
528
+ }
529
+ }
530
+ `;
531
+ }
532
+ getVerifyEmailMailableStub() {
533
+ return `import { Mailable } from 'arikajs';
534
+
535
+ export class VerifyEmail extends Mailable {
536
+ constructor(
537
+ private name: string,
538
+ private verificationUrl: string,
539
+ private appName: string
540
+ ) {
541
+ super();
542
+ }
543
+
544
+ public build() {
545
+ return this.subject('Verify Your Email Address')
546
+ .view('emails.auth.verify', {
547
+ name: this.name,
548
+ verification_url: this.verificationUrl,
549
+ app_name: this.appName,
550
+ year: new Date().getFullYear()
551
+ });
552
+ }
553
+ }
554
+ `;
555
+ }
556
+ getResetPasswordMailableStub() {
557
+ return `import { Mailable } from 'arikajs';
558
+
559
+ export class ResetPassword extends Mailable {
560
+ constructor(
561
+ private resetUrl: string,
562
+ private appName: string
563
+ ) {
564
+ super();
565
+ }
566
+
567
+ public build() {
568
+ return this.subject('Reset Password Notification')
569
+ .view('emails.auth.reset', {
570
+ reset_url: this.resetUrl,
571
+ app_name: this.appName,
572
+ year: new Date().getFullYear()
573
+ });
574
+ }
575
+ }
576
+ `;
577
+ }
578
+ publishViews() {
579
+ const cwd = process.cwd();
580
+ const viewsDir = path_1.default.join(cwd, 'resources', 'views');
581
+ const authViewsDir = path_1.default.join(viewsDir, 'auth');
582
+ const passwordDir = path_1.default.join(authViewsDir, 'passwords');
583
+ if (!fs_1.default.existsSync(passwordDir))
584
+ fs_1.default.mkdirSync(passwordDir, { recursive: true });
585
+ fs_1.default.writeFileSync(path_1.default.join(authViewsDir, 'login.ark.html'), this.getLoginViewStub());
586
+ fs_1.default.writeFileSync(path_1.default.join(authViewsDir, 'register.ark.html'), this.getRegisterViewStub());
587
+ fs_1.default.writeFileSync(path_1.default.join(passwordDir, 'email.ark.html'), this.getForgotPasswordViewStub());
588
+ fs_1.default.writeFileSync(path_1.default.join(passwordDir, 'reset.ark.html'), this.getResetPasswordViewStub());
589
+ fs_1.default.writeFileSync(path_1.default.join(viewsDir, 'dashboard.ark.html'), this.getDashboardViewStub());
590
+ // Email templates
591
+ const emailDir = path_1.default.join(viewsDir, 'emails', 'auth');
592
+ if (!fs_1.default.existsSync(emailDir))
593
+ fs_1.default.mkdirSync(emailDir, { recursive: true });
594
+ fs_1.default.writeFileSync(path_1.default.join(emailDir, 'reset.ark.html'), this.getEmailTemplateStub());
595
+ fs_1.default.writeFileSync(path_1.default.join(emailDir, 'verify.ark.html'), this.getVerifyEmailViewStub());
596
+ }
597
+ appendWebRoutes() {
598
+ const cwd = process.cwd();
599
+ const routesPath = path_1.default.join(cwd, 'routes', 'web.ts');
600
+ if (!fs_1.default.existsSync(routesPath))
601
+ return;
602
+ let content = fs_1.default.readFileSync(routesPath, 'utf8');
603
+ // Add imports if they don't exist
604
+ const imports = [
605
+ "import { LoginController } from '@Controllers/Auth/LoginController';",
606
+ "import { RegisterController } from '@Controllers/Auth/RegisterController';",
607
+ "import { ForgotPasswordController } from '@Controllers/Auth/ForgotPasswordController';",
608
+ "import { ResetPasswordController } from '@Controllers/Auth/ResetPasswordController';",
609
+ "import { VerifyEmailController } from '@Controllers/Auth/VerifyEmailController';"
610
+ ];
611
+ imports.forEach(imp => {
612
+ if (!content.includes(imp)) {
613
+ content = imp + "\n" + content;
614
+ }
615
+ });
616
+ if (!content.includes("Route.group({ prefix: 'auth'")) {
617
+ const routeDefinitions = `
618
+ // Authentication Routes (Web)
619
+ Route.group({ prefix: 'auth', middleware: 'web' }, () => {
620
+ Route.get('/login', [LoginController, 'showLogin']).as('login');
621
+ Route.post('/login', [LoginController, 'login']);
622
+ Route.get('/register', [RegisterController, 'showRegister']);
623
+ Route.post('/register', [RegisterController, 'register']);
624
+ Route.post('/logout', [LoginController, 'logout']).withMiddleware('auth:web');
625
+
626
+ // Password Reset
627
+ Route.get('/password/reset', [ForgotPasswordController, 'showLinkRequestForm']);
628
+ Route.post('/password/email', [ForgotPasswordController, 'sendResetLinkEmail']);
629
+ Route.get('/password/reset/:token', [ResetPasswordController, 'showResetForm']);
630
+ Route.post('/password/reset', [ResetPasswordController, 'reset']);
631
+
632
+ // Email Verification
633
+ Route.get('/verify', [VerifyEmailController, 'verify']);
634
+ Route.post('/verification-notification', [VerifyEmailController, 'resend']).withMiddleware('auth:web');
635
+ });
636
+
637
+ Route.get('/dashboard', [LoginController, 'showDashboard']).withMiddleware('auth:web');
638
+ `;
639
+ content += routeDefinitions;
640
+ }
641
+ fs_1.default.writeFileSync(routesPath, content);
642
+ }
643
+ updateWelcomeHeader() {
644
+ const cwd = process.cwd();
645
+ const welcomePath = path_1.default.join(cwd, 'resources', 'views', 'welcome.ark.html');
646
+ if (fs_1.default.existsSync(welcomePath)) {
647
+ let content = fs_1.default.readFileSync(welcomePath, 'utf8');
648
+ // Add CSS if not already present
649
+ if (!content.includes('.auth-link')) {
650
+ const css = `
651
+ .auth-link {
652
+ color: var(--text-main);
653
+ text-decoration: none;
654
+ font-weight: 600;
655
+ font-size: 0.95rem;
656
+ transition: color 0.3s ease;
657
+ }
658
+
659
+ .auth-link:hover {
660
+ color: var(--primary);
661
+ }
662
+
663
+ .auth-btn {
664
+ background: var(--primary);
665
+ color: white;
666
+ padding: 0.6rem 1.5rem;
667
+ border-radius: 99px;
668
+ text-decoration: none;
669
+ font-weight: 600;
670
+ font-size: 0.95rem;
671
+ box-shadow: 0 4px 15px var(--primary-glow);
672
+ transition: all 0.3s ease;
673
+ }
674
+
675
+ .auth-btn:hover {
676
+ transform: translateY(-2px);
677
+ box-shadow: 0 6px 20px var(--primary-glow);
678
+ filter: brightness(1.1);
679
+ }
680
+ `;
681
+ content = content.replace('</style>', `${css}\n </style>`);
682
+ }
683
+ // Add Buttons if not already present
684
+ if (!content.includes('/auth/login')) {
685
+ const buttons = `
686
+ <div class="auth-links" style="display: flex; gap: 1.5rem; align-items: center; margin-right: 0.5rem;">
687
+ <a href="/auth/login" class="auth-link">Login</a>
688
+ <a href="/auth/register" class="auth-btn">Register</a>
689
+ </div>`;
690
+ content = content.replace('<button class="theme-toggle"', `${buttons}\n <button class="theme-toggle"`);
691
+ }
692
+ fs_1.default.writeFileSync(welcomePath, content);
693
+ }
694
+ }
695
+ getLoginViewStub() {
696
+ return `<!DOCTYPE html>
697
+ <html lang="en">
698
+ <head>
699
+ <meta charset="UTF-8">
700
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
701
+ <title>Login - {{ config('app.name', 'ArikaJS') }}</title>
702
+ <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;600;800&display=swap" rel="stylesheet">
703
+ <link rel="icon" type="image/png" href="/assets/img/favicon.png">
704
+ <style>
705
+ :root { --primary: #8b5cf6; --primary-glow: rgba(139, 92, 246, 0.4); --bg: #f8fafc; --text: #0f172a; --text-muted: #64748b; }
706
+ * { box-sizing: border-box; transition: all 0.3s ease; }
707
+ body { font-family: 'Outfit', sans-serif; background: var(--bg); display: flex; align-items: center; justify-content: center; min-height: 100vh; margin: 0; color: var(--text); }
708
+ .card { background: white; padding: 2.5rem; border-radius: 24px; box-shadow: 0 20px 50px rgba(0,0,0,0.05); width: 100%; max-width: 420px; border: 1px solid rgba(0,0,0,0.02); }
709
+ .logo { width: 120px; margin-bottom: 2rem; }
710
+ h1 { margin-bottom: 0.5rem; font-size: 2rem; font-weight: 800; letter-spacing: -0.02em; }
711
+ p { color: var(--text-muted); margin-bottom: 2rem; font-weight: 400; }
712
+ .form-group { margin-bottom: 1.5rem; }
713
+ label { display: block; margin-bottom: 0.6rem; font-weight: 600; font-size: 0.9rem; }
714
+ input { width: 100%; padding: 0.9rem 1.2rem; border: 1px solid #e2e8f0; border-radius: 12px; font-family: inherit; font-size: 1rem; }
715
+ input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 4px var(--primary-glow); }
716
+ .btn { background: var(--primary); color: white; border: none; padding: 1rem; width: 100%; border-radius: 12px; font-weight: 700; cursor: pointer; font-size: 1rem; box-shadow: 0 4px 15px var(--primary-glow); }
717
+ .btn:hover { transform: translateY(-2px); box-shadow: 0 8px 25px var(--primary-glow); filter: brightness(1.1); }
718
+ .footer { margin-top: 2rem; text-align: center; font-size: 0.95rem; color: var(--text-muted); }
719
+ .footer a { color: var(--primary); text-decoration: none; font-weight: 700; }
720
+ .alert { padding: 1rem; border-radius: 12px; margin-bottom: 1.5rem; font-weight: 600; font-size: 0.9rem; }
721
+ .alert-error { background: #fee2e2; color: #dc2626; border: 1px solid #fecaca; }
722
+ .alert-success { background: #dcfce7; color: #16a34a; border: 1px solid #bbf7d0; }
723
+ .forgot-link { display: block; text-align: right; margin-top: 0.5rem; font-size: 0.85rem; color: var(--text-muted); text-decoration: none; }
724
+ .forgot-link:hover { color: var(--primary); }
725
+ </style>
726
+ </head>
727
+ <body>
728
+ <div class="card">
729
+ <a href="/"><img src="/assets/img/logo.png" alt="Logo" class="logo"></a>
730
+ <h1>Welcome Back</h1>
731
+ <p>Login to manage your application</p>
732
+
733
+ @isset(error)
734
+ <div class="alert alert-error">{{ error }}</div>
735
+ @endisset
736
+
737
+ @isset(success)
738
+ <div class="alert alert-success">{{ success }}</div>
739
+ @endisset
740
+
741
+ <form action="/auth/login" method="POST">
742
+ @csrf
743
+ <div class="form-group">
744
+ <label>Email Address</label>
745
+ <input type="email" name="email" required placeholder="email@example.com" value="{{ email }}">
746
+ </div>
747
+ <div class="form-group">
748
+ <label>Password</label>
749
+ <input type="password" name="password" required placeholder="••••••••">
750
+ <a href="/auth/password/reset" class="forgot-link">Forgot Password?</a>
751
+ </div>
752
+ <button type="submit" class="btn">Sign In</button>
753
+ </form>
754
+ <div class="footer">
755
+ Don't have an account? <a href="/auth/register">Create one</a>
756
+ </div>
757
+ </div>
758
+ </body>
759
+ </html>`;
760
+ }
761
+ getRegisterViewStub() {
762
+ return `<!DOCTYPE html>
763
+ <html lang="en">
764
+ <head>
765
+ <meta charset="UTF-8">
766
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
767
+ <title>Register - {{ config('app.name', 'ArikaJS') }}</title>
768
+ <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;600;800&display=swap" rel="stylesheet">
769
+ <link rel="icon" type="image/png" href="/assets/img/favicon.png">
770
+ <style>
771
+ :root { --primary: #8b5cf6; --primary-glow: rgba(139, 92, 246, 0.4); --bg: #f8fafc; --text: #0f172a; --text-muted: #64748b; }
772
+ * { box-sizing: border-box; transition: all 0.3s ease; }
773
+ body { font-family: 'Outfit', sans-serif; background: var(--bg); display: flex; align-items: center; justify-content: center; min-height: 100vh; margin: 0; color: var(--text); }
774
+ .card { background: white; padding: 2.5rem; border-radius: 24px; box-shadow: 0 20px 50px rgba(0,0,0,0.05); width: 100%; max-width: 440px; border: 1px solid rgba(0,0,0,0.02); }
775
+ .logo { width: 120px; margin-bottom: 2rem; }
776
+ h1 { margin-bottom: 0.5rem; font-size: 2rem; font-weight: 800; letter-spacing: -0.02em; }
777
+ p { color: var(--text-muted); margin-bottom: 2rem; font-weight: 400; }
778
+ .form-group { margin-bottom: 1.2rem; }
779
+ label { display: block; margin-bottom: 0.6rem; font-weight: 600; font-size: 0.9rem; }
780
+ input { width: 100%; padding: 0.9rem 1.2rem; border: 1px solid #e2e8f0; border-radius: 12px; font-family: inherit; font-size: 1rem; }
781
+ input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 4px var(--primary-glow); }
782
+ .btn { background: var(--primary); color: white; border: none; padding: 1rem; width: 100%; border-radius: 12px; font-weight: 700; cursor: pointer; font-size: 1rem; box-shadow: 0 4px 15px var(--primary-glow); }
783
+ .btn:hover { transform: translateY(-2px); box-shadow: 0 8px 25px var(--primary-glow); filter: brightness(1.1); }
784
+ .footer { margin-top: 2rem; text-align: center; font-size: 0.95rem; color: var(--text-muted); }
785
+ .footer a { color: var(--primary); text-decoration: none; font-weight: 700; }
786
+ .alert { padding: 1rem; border-radius: 12px; margin-bottom: 1.5rem; font-weight: 600; font-size: 0.9rem; }
787
+ .alert-error { background: #fee2e2; color: #dc2626; border: 1px solid #fecaca; }
788
+ .alert-success { background: #dcfce7; color: #16a34a; border: 1px solid #bbf7d0; }
789
+ </style>
790
+ </head>
791
+ <body>
792
+ <div class="card">
793
+ <a href="/"><img src="/assets/img/logo.png" alt="Logo" class="logo"></a>
794
+ <h1>Create Account</h1>
795
+ <p>Join our premium platform</p>
796
+
797
+ @isset(error)
798
+ <div class="alert alert-error">{{ error }}</div>
799
+ @endisset
800
+
801
+ @isset(success)
802
+ <div class="alert alert-success">{{ success }}</div>
803
+ @endisset
804
+
805
+ <form action="/auth/register" method="POST">
806
+ @csrf
807
+ <div class="form-group">
808
+ <label>Full Name</label>
809
+ <input type="text" name="name" required placeholder="John Doe" value="{{ old.name }}">
810
+ </div>
811
+ <div class="form-group">
812
+ <label>Email Address</label>
813
+ <input type="email" name="email" required placeholder="email@example.com" value="{{ old.email }}">
814
+ </div>
815
+ <div class="form-group">
816
+ <label>Password</label>
817
+ <input type="password" name="password" required placeholder="••••••••">
818
+ </div>
819
+ <div class="form-group">
820
+ <label>Confirm Password</label>
821
+ <input type="password" name="password_confirmation" required placeholder="••••••••">
822
+ </div>
823
+ <button type="submit" class="btn">Register</button>
824
+ </form>
825
+ <div class="footer">
826
+ Already have an account? <a href="/auth/login">Sign In</a>
827
+ </div>
828
+ </div>
829
+ </body>
830
+ </html>`;
831
+ }
832
+ getForgotPasswordViewStub() {
833
+ return `<!DOCTYPE html>
834
+ <html lang="en">
835
+ <head>
836
+ <meta charset="UTF-8">
837
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
838
+ <title>Forgot Password - {{ config('app.name', 'ArikaJS') }}</title>
839
+ <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;600;800&display=swap" rel="stylesheet">
840
+ <link rel="icon" type="image/png" href="/assets/img/favicon.png">
841
+ <style>
842
+ :root { --primary: #8b5cf6; --primary-glow: rgba(139, 92, 246, 0.4); --bg: #f8fafc; --text: #0f172a; --text-muted: #64748b; }
843
+ * { box-sizing: border-box; transition: all 0.3s ease; }
844
+ body { font-family: 'Outfit', sans-serif; background: var(--bg); display: flex; align-items: center; justify-content: center; min-height: 100vh; margin: 0; color: var(--text); }
845
+ .card { background: white; padding: 2.5rem; border-radius: 24px; box-shadow: 0 20px 50px rgba(0,0,0,0.05); width: 100%; max-width: 420px; border: 1px solid rgba(0,0,0,0.02); }
846
+ .logo { width: 120px; margin-bottom: 2rem; }
847
+ h1 { margin-bottom: 0.5rem; font-size: 2rem; font-weight: 800; letter-spacing: -0.02em; }
848
+ p { color: var(--text-muted); margin-bottom: 2rem; font-weight: 400; }
849
+ .form-group { margin-bottom: 1.5rem; }
850
+ label { display: block; margin-bottom: 0.6rem; font-weight: 600; font-size: 0.9rem; }
851
+ input { width: 100%; padding: 0.9rem 1.2rem; border: 1px solid #e2e8f0; border-radius: 12px; font-family: inherit; font-size: 1rem; }
852
+ input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 4px var(--primary-glow); }
853
+ .btn { background: var(--primary); color: white; border: none; padding: 1rem; width: 100%; border-radius: 12px; font-weight: 700; cursor: pointer; font-size: 1rem; box-shadow: 0 4px 15px var(--primary-glow); }
854
+ .btn:hover { transform: translateY(-2px); box-shadow: 0 8px 25px var(--primary-glow); filter: brightness(1.1); }
855
+ .footer { margin-top: 2rem; text-align: center; font-size: 0.95rem; color: var(--text-muted); }
856
+ .footer a { color: var(--primary); text-decoration: none; font-weight: 700; }
857
+ .alert { padding: 1rem; border-radius: 12px; margin-bottom: 1.5rem; font-weight: 600; font-size: 0.9rem; }
858
+ .alert-error { background: #fee2e2; color: #dc2626; border: 1px solid #fecaca; }
859
+ .alert-success { background: #dcfce7; color: #16a34a; border: 1px solid #bbf7d0; }
860
+ </style>
861
+ </head>
862
+ <body>
863
+ <div class="card">
864
+ <a href="/"><img src="/assets/img/logo.png" alt="Logo" class="logo"></a>
865
+ <h1>Reset Password</h1>
866
+ <p>Enter your email to receive a reset link.</p>
867
+
868
+ @isset(success)
869
+ <div class="alert alert-success">{{ success }}</div>
870
+ @endisset
871
+
872
+ @isset(error)
873
+ <div class="alert alert-error">{{ error }}</div>
874
+ @endisset
875
+
876
+ <form action="/auth/password/email" method="POST">
877
+ @csrf
878
+ <div class="form-group">
879
+ <label>Email Address</label>
880
+ <input type="email" name="email" required placeholder="email@example.com">
881
+ </div>
882
+ <button type="submit" class="btn">Send Reset Link</button>
883
+ </form>
884
+ <div class="footer">
885
+ Remembered? <a href="/auth/login">Back to Sign In</a>
886
+ </div>
887
+ </div>
888
+ </body>
889
+ </html>`;
890
+ }
891
+ getResetPasswordViewStub() {
892
+ return `<!DOCTYPE html>
893
+ <html lang="en">
894
+ <head>
895
+ <meta charset="UTF-8">
896
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
897
+ <title>Reset Password - {{ config('app.name', 'ArikaJS') }}</title>
898
+ <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;600;800&display=swap" rel="stylesheet">
899
+ <link rel="icon" type="image/png" href="/assets/img/favicon.png">
900
+ <style>
901
+ :root { --primary: #8b5cf6; --primary-glow: rgba(139, 92, 246, 0.4); --bg: #f8fafc; --text: #0f172a; --text-muted: #64748b; }
902
+ * { box-sizing: border-box; transition: all 0.3s ease; }
903
+ body { font-family: 'Outfit', sans-serif; background: var(--bg); display: flex; align-items: center; justify-content: center; min-height: 100vh; margin: 0; color: var(--text); }
904
+ .card { background: white; padding: 2.5rem; border-radius: 24px; box-shadow: 0 20px 50px rgba(0,0,0,0.05); width: 100%; max-width: 420px; border: 1px solid rgba(0,0,0,0.02); }
905
+ .logo { width: 120px; margin-bottom: 2rem; }
906
+ h1 { margin-bottom: 0.5rem; font-size: 2rem; font-weight: 800; letter-spacing: -0.02em; }
907
+ p { color: var(--text-muted); margin-bottom: 2rem; font-weight: 400; }
908
+ .form-group { margin-bottom: 1.5rem; }
909
+ label { display: block; margin-bottom: 0.6rem; font-weight: 600; font-size: 0.9rem; }
910
+ input { width: 100%; padding: 0.9rem 1.2rem; border: 1px solid #e2e8f0; border-radius: 12px; font-family: inherit; font-size: 1rem; }
911
+ input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 4px var(--primary-glow); }
912
+ .btn { background: var(--primary); color: white; border: none; padding: 1rem; width: 100%; border-radius: 12px; font-weight: 700; cursor: pointer; font-size: 1rem; box-shadow: 0 4px 15px var(--primary-glow); }
913
+ .btn:hover { transform: translateY(-2px); box-shadow: 0 8px 25px var(--primary-glow); filter: brightness(1.1); }
914
+ .alert { padding: 1rem; border-radius: 12px; margin-bottom: 1.5rem; font-weight: 600; font-size: 0.9rem; }
915
+ .alert-error { background: #fee2e2; color: #dc2626; border: 1px solid #fecaca; }
916
+ </style>
917
+ </head>
918
+ <body>
919
+ <div class="card">
920
+ <a href="/"><img src="/assets/img/logo.png" alt="Logo" class="logo"></a>
921
+ <h1>Set New Password</h1>
922
+ <p>Secure your account with a new password.</p>
923
+
924
+ @isset(error)
925
+ <div class="alert alert-error">{{ error }}</div>
926
+ @endisset
927
+
928
+ <form action="/auth/password/reset" method="POST">
929
+ @csrf
930
+ <input type="hidden" name="token" value="{{ token }}">
931
+ <div class="form-group">
932
+ <label>Email Address</label>
933
+ <input type="email" name="email" required placeholder="email@example.com">
934
+ </div>
935
+ <div class="form-group">
936
+ <label>New Password</label>
937
+ <input type="password" name="password" required placeholder="••••••••">
938
+ </div>
939
+ <div class="form-group">
940
+ <label>Confirm Password</label>
941
+ <input type="password" name="password_confirmation" required placeholder="••••••••">
942
+ </div>
943
+ <button type="submit" class="btn">Reset Password</button>
944
+ </form>
945
+ </div>
946
+ </body>
947
+ </html>`;
948
+ }
949
+ getDashboardViewStub() {
950
+ return `<!DOCTYPE html>
951
+ <html lang="en">
952
+ <head>
953
+ <meta charset="UTF-8">
954
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
955
+ <title>Dashboard - {{ config('app.name', 'ArikaJS') }}</title>
956
+ <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;600;800&display=swap" rel="stylesheet">
957
+ <link rel="icon" type="image/png" href="/assets/img/favicon.png">
958
+ <style>
959
+ :root { --primary: #8b5cf6; --bg: #f8fafc; --text: #0f172a; --text-muted: #64748b; }
960
+ body { font-family: 'Outfit', sans-serif; margin: 0; background: var(--bg); color: var(--text); }
961
+ nav { background: white; padding: 1.2rem 5%; border-bottom: 1px solid rgba(0,0,0,0.05); display: flex; justify-content: space-between; align-items: center; box-shadow: 0 4px 10px rgba(0,0,0,0.01); }
962
+ .logo-container { display: flex; align-items: center; text-decoration: none; }
963
+ .logo { width: 120px; height: auto; transition: filter 0.3s; }
964
+ .logo:hover { filter: brightness(1.1); }
965
+ .logout-btn { color: #ef4444; background: none; border: 1px solid rgba(239, 68, 68, 0.1); padding: 0.6rem 1.2rem; border-radius: 10px; cursor: pointer; font-weight: 600; font-size: 0.9rem; transition: all 0.3s ease; }
966
+ .logout-btn:hover { background: #fee2e2; }
967
+ main { padding: 4rem 5%; max-width: 1200px; margin: 0 auto; }
968
+ .welcome-card { background: white; padding: 4rem; border-radius: 32px; box-shadow: 0 20px 40px rgba(0,0,0,0.03); border: 1px solid rgba(0,0,0,0.02); text-align: center; position: relative; overflow: hidden; }
969
+ .welcome-card h1 { font-size: 3rem; margin-bottom: 1rem; font-weight: 800; }
970
+ .welcome-card p { font-size: 1.2rem; color: var(--text-muted); max-width: 600px; margin: 0 auto; line-height: 1.6; }
971
+ .aurora { position: absolute; top: -50%; left: -50%; width: 200%; height: 200%; background: radial-gradient(circle, rgba(139, 92, 246, 0.05) 0%, transparent 60%); z-index: -1; animation: rotate 20s linear infinite; }
972
+ @keyframes rotate { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
973
+ </style>
974
+ </head>
975
+ <body>
976
+ <nav>
977
+ <a href="/" class="logo-container">
978
+ <img src="/assets/img/logo.png" alt="ArikaJS Logo" class="logo">
979
+ </a>
980
+ <form action="/auth/logout" method="POST" style="margin: 0;">
981
+ @csrf
982
+ <button type="submit" class="logout-btn">Sign Out</button>
983
+ </form>
984
+ </nav>
985
+ <main>
986
+ <div class="welcome-card">
987
+ <div class="aurora"></div>
988
+ <h1>Hello, {{ user.name }}!</h1>
989
+ <p>Welcome to your premium application dashboard. You have successfully authenticated using the ArikaJS Next-Gen Authentication system.</p>
990
+
991
+ @if(!user.email_verified_at)
992
+ <div style="margin-top: 2.5rem; padding: 1.5rem; background: rgba(245, 158, 11, 0.1); border: 1px solid rgba(245, 158, 11, 0.3); border-radius: 16px; color: #b45309; text-align: left;">
993
+ <h3 style="margin-top: 0; margin-bottom: 0.5rem; font-size: 1.2rem;">Email Not Verified</h3>
994
+ <p style="margin-bottom: 1.5rem; color: #92400e; font-size: 0.95rem;">Please verify your email address to access all features. Didn't receive the email?</p>
995
+ <form action="/auth/verify/resend" method="POST" style="margin: 0;">
996
+ @csrf
997
+ <button type="submit" style="background: #f59e0b; color: white; border: none; padding: 0.8rem 1.5rem; border-radius: 10px; font-weight: 600; font-family: inherit; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 4px 10px rgba(245, 158, 11, 0.2);">
998
+ Resend Verification Link
999
+ </button>
1000
+ </form>
1001
+ </div>
1002
+ @endif
1003
+ </div>
1004
+ </main>
1005
+ </body>
1006
+ </html>`;
1007
+ }
1008
+ getEmailTemplateStub() {
1009
+ return `<!DOCTYPE html>
1010
+ <html lang="en">
1011
+ <head>
1012
+ <meta charset="UTF-8">
1013
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1014
+ <title>Reset Password - {{ config('app.name', 'ArikaJS') }}</title>
1015
+ <style>
1016
+ body { margin: 0; padding: 0; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: #f4f4f7; color: #51545e; }
1017
+ .wrapper { width: 100%; margin: 0; padding: 0; -webkit-text-size-adjust: none; background-color: #f4f4f7; }
1018
+ .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); }
1019
+ .header { padding: 25px 0; text-align: center; }
1020
+ .header a { font-size: 19px; font-weight: bold; color: #333; text-decoration: none; }
1021
+ .button { display: inline-block; background-color: #3869d4; color: #FFF !important; padding: 12px 25px; text-decoration: none; border-radius: 3px; font-weight: bold; }
1022
+ .footer { text-align: center; padding: 25px; font-size: 12px; color: #b0adc5; }
1023
+ </style>
1024
+ </head>
1025
+ <body>
1026
+ <div class="wrapper">
1027
+ <div class="header"><a href="#">{{ app_name }}</a></div>
1028
+ <div class="content">
1029
+ <h1>Reset your password</h1>
1030
+ <p>You are receiving this email because we received a password reset request for your account.</p>
1031
+ <p style="text-align: center;"><a href="{{ reset_url }}" class="button">Reset Password</a></p>
1032
+ <p>This password reset link will expire in 60 minutes.</p>
1033
+ <p>If you did not request a password reset, no further action is required.</p>
1034
+ <p>Regards,<br>{{ app_name }} Team</p>
1035
+ </div>
1036
+ <div class="footer">&copy; {{ year }} {{ app_name }}. All rights reserved.</div>
1037
+ </div>
1038
+ </body>
1039
+ </html>`;
1040
+ }
1041
+ getVerifyEmailViewStub() {
1042
+ return `<!DOCTYPE html>
1043
+ <html lang="en">
1044
+ <head>
1045
+ <meta charset="UTF-8">
1046
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1047
+ <title>Verify Your Email - {{ config('app.name', 'ArikaJS') }}</title>
1048
+ <style>
1049
+ body { margin: 0; padding: 0; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: #f4f4f7; color: #51545e; }
1050
+ .wrapper { width: 100%; margin: 0; padding: 0; -webkit-text-size-adjust: none; background-color: #f4f4f7; }
1051
+ .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); }
1052
+ .header { padding: 25px 0; text-align: center; }
1053
+ .header a { font-size: 19px; font-weight: bold; color: #333; text-decoration: none; }
1054
+ .button { display: inline-block; background-color: #22bc66; color: #FFF !important; padding: 12px 25px; text-decoration: none; border-radius: 3px; font-weight: bold; }
1055
+ .footer { text-align: center; padding: 25px; font-size: 12px; color: #b0adc5; }
1056
+ </style>
1057
+ </head>
1058
+ <body>
1059
+ <div class="wrapper">
1060
+ <div class="header"><a href="#">{{ app_name }}</a></div>
1061
+ <div class="content">
1062
+ <h1>Verify your email address</h1>
1063
+ <p>Hi {{ name }},</p>
1064
+ <p>Thanks for signing up! Please confirm your email address by clicking the button below.</p>
1065
+ <p style="text-align: center;"><a href="{{ verification_url }}" class="button">Verify Email</a></p>
1066
+ <p>If you did not create an account, no further action is required.</p>
1067
+ <p>Regards,<br>{{ app_name }} Team</p>
1068
+ <div class="footer">&copy; {{ year }} {{ app_name }}. All rights reserved.</div>
1069
+ </div>
1070
+ </body>
1071
+ </html>`;
1072
+ }
1073
+ getAuthTranslationStub() {
1074
+ return `{
1075
+ "failed": "These credentials do not match our records.",
1076
+ "password": "The provided password is incorrect.",
1077
+ "throttle": "Too many login attempts. Please try again in :seconds seconds.",
1078
+ "email_verified": "Your email has been successfully verified! Please sign in to continue.",
1079
+ "email_already_verified": "Your email is already verified. Please sign in.",
1080
+ "user_not_found": "We can't find a user with that email address.",
1081
+ "missing_verification_data": "Verification data is missing from the request.",
1082
+ "invalid_verification_token": "The verification token is invalid.",
1083
+ "verification_failed": "Something went wrong during the verification process.",
1084
+ "verification_resent": "A new verification link has been sent to your email address.",
1085
+ "reset_link_sent": "We have emailed your password reset link!",
1086
+ "password_reset_success": "Your password has been reset!",
1087
+ "invalid_reset_token": "This password reset token is invalid.",
1088
+ "email_not_verified": "Your email address is not verified. Please verify your email before attempting to reset your password."
1089
+ }`;
1090
+ }
1091
+ publishTranslations() {
1092
+ const cwd = process.cwd();
1093
+ const langDir = path_1.default.join(cwd, 'resources', 'lang', 'en');
1094
+ if (!fs_1.default.existsSync(langDir))
1095
+ fs_1.default.mkdirSync(langDir, { recursive: true });
1096
+ fs_1.default.writeFileSync(path_1.default.join(langDir, 'auth.json'), this.getAuthTranslationStub());
1097
+ }
1098
+ }
1099
+ exports.AuthWebInstallCommand = AuthWebInstallCommand;
1100
+ //# sourceMappingURL=AuthWebInstallCommand.js.map