@arikajs/cli 0.0.3 → 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.
- package/README.md +99 -6
- package/dist/bin/arika.js +2 -2
- package/dist/bin/arika.js.map +1 -1
- package/dist/src/ApplicationLoader.d.ts +5 -0
- package/dist/src/ApplicationLoader.d.ts.map +1 -1
- package/dist/src/ApplicationLoader.js +24 -2
- package/dist/src/ApplicationLoader.js.map +1 -1
- package/dist/src/Bootstrap.d.ts.map +1 -1
- package/dist/src/Bootstrap.js +35 -0
- package/dist/src/Bootstrap.js.map +1 -1
- package/dist/src/Commands/AuthApiInstallCommand.d.ts +24 -0
- package/dist/src/Commands/AuthApiInstallCommand.d.ts.map +1 -0
- package/dist/src/Commands/AuthApiInstallCommand.js +751 -0
- package/dist/src/Commands/AuthApiInstallCommand.js.map +1 -0
- package/dist/src/Commands/AuthInstallCommand.d.ts +7 -0
- package/dist/src/Commands/AuthInstallCommand.d.ts.map +1 -0
- package/dist/src/Commands/AuthInstallCommand.js +46 -0
- package/dist/src/Commands/AuthInstallCommand.js.map +1 -0
- package/dist/src/Commands/AuthWebInstallCommand.d.ts +31 -0
- package/dist/src/Commands/AuthWebInstallCommand.d.ts.map +1 -0
- package/dist/src/Commands/AuthWebInstallCommand.js +1100 -0
- package/dist/src/Commands/AuthWebInstallCommand.js.map +1 -0
- package/dist/src/Commands/BenchmarkCommand.d.ts +7 -0
- package/dist/src/Commands/BenchmarkCommand.d.ts.map +1 -0
- package/dist/src/Commands/BenchmarkCommand.js +25 -0
- package/dist/src/Commands/BenchmarkCommand.js.map +1 -0
- package/dist/src/Commands/CacheClearCommand.d.ts +7 -0
- package/dist/src/Commands/CacheClearCommand.d.ts.map +1 -0
- package/dist/src/Commands/CacheClearCommand.js +33 -0
- package/dist/src/Commands/CacheClearCommand.js.map +1 -0
- package/dist/src/Commands/CacheTableCommand.d.ts.map +1 -1
- package/dist/src/Commands/CacheTableCommand.js +21 -9
- package/dist/src/Commands/CacheTableCommand.js.map +1 -1
- package/dist/src/Commands/ConfigCacheCommand.d.ts +7 -0
- package/dist/src/Commands/ConfigCacheCommand.d.ts.map +1 -0
- package/dist/src/Commands/ConfigCacheCommand.js +86 -0
- package/dist/src/Commands/ConfigCacheCommand.js.map +1 -0
- package/dist/src/Commands/ConfigClearCommand.d.ts +7 -0
- package/dist/src/Commands/ConfigClearCommand.d.ts.map +1 -0
- package/dist/src/Commands/ConfigClearCommand.js +29 -0
- package/dist/src/Commands/ConfigClearCommand.js.map +1 -0
- package/dist/src/Commands/DocsGenerateCommand.d.ts +0 -1
- package/dist/src/Commands/DocsGenerateCommand.d.ts.map +1 -1
- package/dist/src/Commands/DocsGenerateCommand.js +68 -77
- package/dist/src/Commands/DocsGenerateCommand.js.map +1 -1
- package/dist/src/Commands/EnvValidateCommand.d.ts +7 -0
- package/dist/src/Commands/EnvValidateCommand.d.ts.map +1 -0
- package/dist/src/Commands/EnvValidateCommand.js +51 -0
- package/dist/src/Commands/EnvValidateCommand.js.map +1 -0
- package/dist/src/Commands/ListCommand.d.ts.map +1 -1
- package/dist/src/Commands/ListCommand.js +15 -8
- package/dist/src/Commands/ListCommand.js.map +1 -1
- package/dist/src/Commands/MakeCommand.d.ts +7 -0
- package/dist/src/Commands/MakeCommand.d.ts.map +1 -0
- package/dist/src/Commands/MakeCommand.js +110 -0
- package/dist/src/Commands/MakeCommand.js.map +1 -0
- package/dist/src/Commands/MakeCommandCommand.d.ts +20 -0
- package/dist/src/Commands/MakeCommandCommand.d.ts.map +1 -0
- package/dist/src/Commands/MakeCommandCommand.js +82 -0
- package/dist/src/Commands/MakeCommandCommand.js.map +1 -0
- package/dist/src/Commands/MakeControllerCommand.d.ts +17 -0
- package/dist/src/Commands/MakeControllerCommand.d.ts.map +1 -0
- package/dist/src/Commands/MakeControllerCommand.js +90 -0
- package/dist/src/Commands/MakeControllerCommand.js.map +1 -0
- package/dist/src/Commands/MakeEventCommand.d.ts +8 -0
- package/dist/src/Commands/MakeEventCommand.d.ts.map +1 -0
- package/dist/src/Commands/MakeEventCommand.js +48 -0
- package/dist/src/Commands/MakeEventCommand.js.map +1 -0
- package/dist/src/Commands/MakeJobCommand.d.ts +8 -0
- package/dist/src/Commands/MakeJobCommand.d.ts.map +1 -0
- package/dist/src/Commands/MakeJobCommand.js +55 -0
- package/dist/src/Commands/MakeJobCommand.js.map +1 -0
- package/dist/src/Commands/MakeListenerCommand.d.ts +8 -0
- package/dist/src/Commands/MakeListenerCommand.d.ts.map +1 -0
- package/dist/src/Commands/MakeListenerCommand.js +55 -0
- package/dist/src/Commands/MakeListenerCommand.js.map +1 -0
- package/dist/src/Commands/MakeMiddlewareCommand.d.ts +17 -0
- package/dist/src/Commands/MakeMiddlewareCommand.d.ts.map +1 -0
- package/dist/src/Commands/MakeMiddlewareCommand.js +69 -0
- package/dist/src/Commands/MakeMiddlewareCommand.js.map +1 -0
- package/dist/src/Commands/MakeMigrationCommand.js +4 -4
- package/dist/src/Commands/MakeMigrationCommand.js.map +1 -1
- package/dist/src/Commands/MakeModelCommand.d.ts +26 -0
- package/dist/src/Commands/MakeModelCommand.d.ts.map +1 -0
- package/dist/src/Commands/MakeModelCommand.js +193 -0
- package/dist/src/Commands/MakeModelCommand.js.map +1 -0
- package/dist/src/Commands/MakeProviderCommand.d.ts +17 -0
- package/dist/src/Commands/MakeProviderCommand.d.ts.map +1 -0
- package/dist/src/Commands/MakeProviderCommand.js +72 -0
- package/dist/src/Commands/MakeProviderCommand.js.map +1 -0
- package/dist/src/Commands/MakeViewCommand.d.ts +7 -0
- package/dist/src/Commands/MakeViewCommand.d.ts.map +1 -0
- package/dist/src/Commands/MakeViewCommand.js +37 -0
- package/dist/src/Commands/MakeViewCommand.js.map +1 -0
- package/dist/src/Commands/MigrateFreshCommand.d.ts +10 -0
- package/dist/src/Commands/MigrateFreshCommand.d.ts.map +1 -0
- package/dist/src/Commands/MigrateFreshCommand.js +129 -0
- package/dist/src/Commands/MigrateFreshCommand.js.map +1 -0
- package/dist/src/Commands/NewCommand.d.ts.map +1 -1
- package/dist/src/Commands/NewCommand.js +92 -24
- package/dist/src/Commands/NewCommand.js.map +1 -1
- package/dist/src/Commands/QueueFailedCommand.d.ts +7 -0
- package/dist/src/Commands/QueueFailedCommand.d.ts.map +1 -0
- package/dist/src/Commands/QueueFailedCommand.js +30 -0
- package/dist/src/Commands/QueueFailedCommand.js.map +1 -0
- package/dist/src/Commands/QueueFailedTableCommand.d.ts +7 -0
- package/dist/src/Commands/QueueFailedTableCommand.d.ts.map +1 -0
- package/dist/src/Commands/QueueFailedTableCommand.js +55 -0
- package/dist/src/Commands/QueueFailedTableCommand.js.map +1 -0
- package/dist/src/Commands/QueueFlushCommand.d.ts +7 -0
- package/dist/src/Commands/QueueFlushCommand.d.ts.map +1 -0
- package/dist/src/Commands/QueueFlushCommand.js +21 -0
- package/dist/src/Commands/QueueFlushCommand.js.map +1 -0
- package/dist/src/Commands/QueueRetryCommand.d.ts +7 -0
- package/dist/src/Commands/QueueRetryCommand.d.ts.map +1 -0
- package/dist/src/Commands/QueueRetryCommand.js +36 -0
- package/dist/src/Commands/QueueRetryCommand.js.map +1 -0
- package/dist/src/Commands/QueueTableCommand.d.ts.map +1 -1
- package/dist/src/Commands/QueueTableCommand.js +19 -13
- package/dist/src/Commands/QueueTableCommand.js.map +1 -1
- package/dist/src/Commands/QueueWorkCommand.d.ts +8 -0
- package/dist/src/Commands/QueueWorkCommand.d.ts.map +1 -0
- package/dist/src/Commands/QueueWorkCommand.js +88 -0
- package/dist/src/Commands/QueueWorkCommand.js.map +1 -0
- package/dist/src/Commands/RouteListCommand.d.ts +7 -0
- package/dist/src/Commands/RouteListCommand.d.ts.map +1 -0
- package/dist/src/Commands/RouteListCommand.js +108 -0
- package/dist/src/Commands/RouteListCommand.js.map +1 -0
- package/dist/src/Commands/ScheduleRunCommand.d.ts +9 -0
- package/dist/src/Commands/ScheduleRunCommand.d.ts.map +1 -0
- package/dist/src/Commands/ScheduleRunCommand.js +80 -0
- package/dist/src/Commands/ScheduleRunCommand.js.map +1 -0
- package/dist/src/Commands/ScheduleWorkCommand.d.ts +9 -0
- package/dist/src/Commands/ScheduleWorkCommand.d.ts.map +1 -0
- package/dist/src/Commands/ScheduleWorkCommand.js +81 -0
- package/dist/src/Commands/ScheduleWorkCommand.js.map +1 -0
- package/dist/src/Commands/ServeCommand.d.ts +1 -0
- package/dist/src/Commands/ServeCommand.d.ts.map +1 -1
- package/dist/src/Commands/ServeCommand.js +111 -21
- package/dist/src/Commands/ServeCommand.js.map +1 -1
- package/dist/src/TemplateManager.d.ts +4 -0
- package/dist/src/TemplateManager.d.ts.map +1 -1
- package/dist/src/TemplateManager.js +35 -6
- package/dist/src/TemplateManager.js.map +1 -1
- package/dist/templates/app/.env.example +47 -0
- package/dist/templates/app/README.md +62 -0
- package/dist/templates/app/app/Exceptions/Handler.ts +19 -0
- package/dist/templates/app/app/Http/Controllers/UserController.ts +90 -0
- package/dist/templates/app/app/Http/Kernel.ts +43 -0
- package/dist/templates/app/app/Http/Middleware/Authenticate.ts +10 -0
- package/dist/templates/app/app/Http/Middleware/EnsureEmailIsVerified.ts +18 -0
- package/dist/templates/app/app/Http/Middleware/ExampleMiddleware.ts +12 -0
- package/dist/templates/app/app/Http/Middleware/RedirectIfAuthenticated.ts +18 -0
- package/dist/templates/app/app/Http/Middleware/TrimStrings.ts +12 -0
- package/dist/templates/app/app/Http/Middleware/TrustProxies.ts +23 -0
- package/dist/templates/app/app/Http/Middleware/VerifyCsrfToken.ts +10 -0
- package/dist/templates/app/app/Models/User.ts +15 -0
- package/dist/templates/app/app/Providers/AppServiceProvider.ts +17 -0
- package/dist/templates/app/app/Providers/RouteServiceProvider.ts +35 -0
- package/dist/templates/app/bootstrap/app.ts +41 -0
- package/dist/templates/app/config/app.ts +10 -0
- package/dist/templates/app/config/cache.ts +44 -0
- package/dist/templates/app/config/database.ts +63 -0
- package/dist/templates/app/config/filesystems.ts +35 -0
- package/dist/templates/app/config/http.ts +6 -0
- package/dist/templates/app/config/logging.ts +37 -0
- package/dist/templates/app/config/mail.ts +29 -0
- package/dist/templates/app/config/queue.ts +42 -0
- 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
- package/dist/templates/app/database/migrations/2024_01_01_000002_create_password_resets_table.ts +22 -0
- package/dist/templates/app/database/migrations/2024_01_01_000003_create_failed_jobs_table.ts +25 -0
- package/dist/templates/app/package.json +22 -0
- package/dist/templates/app/public/assets/img/favicon.png +0 -0
- package/dist/templates/app/public/assets/img/logo.png +0 -0
- package/dist/templates/app/resources/views/errors/401.ark.html +320 -0
- package/dist/templates/app/resources/views/errors/403.ark.html +320 -0
- package/dist/templates/app/resources/views/errors/404.ark.html +320 -0
- package/dist/templates/app/resources/views/errors/419.ark.html +320 -0
- package/dist/templates/app/resources/views/errors/429.ark.html +320 -0
- package/dist/templates/app/resources/views/errors/500.ark.html +320 -0
- package/dist/templates/app/resources/views/errors/503.ark.html +320 -0
- package/dist/templates/app/resources/views/welcome.ark.html +846 -0
- package/dist/templates/app/routes/api.ts +39 -0
- package/dist/templates/app/routes/web.ts +10 -0
- package/dist/templates/app/server.ts +15 -0
- package/dist/templates/app/tsconfig.json +27 -0
- package/package.json +16 -10
- package/templates/app/.env.example +2 -2
- package/templates/app/README.md +62 -0
- package/templates/app/app/Exceptions/Handler.ts +19 -0
- package/templates/app/app/Http/Controllers/UserController.ts +90 -0
- package/templates/app/app/Http/Kernel.ts +39 -14
- package/templates/app/app/Http/Middleware/Authenticate.ts +10 -0
- package/templates/app/app/Http/Middleware/EnsureEmailIsVerified.ts +18 -0
- package/templates/app/app/Http/Middleware/ExampleMiddleware.ts +12 -0
- package/templates/app/app/Http/Middleware/RedirectIfAuthenticated.ts +18 -0
- package/templates/app/app/Http/Middleware/TrimStrings.ts +12 -0
- package/templates/app/app/Http/Middleware/TrustProxies.ts +23 -0
- package/templates/app/app/Http/Middleware/VerifyCsrfToken.ts +10 -0
- package/templates/app/app/Models/User.ts +5 -5
- package/templates/app/app/Providers/AppServiceProvider.ts +17 -0
- package/templates/app/app/Providers/RouteServiceProvider.ts +35 -0
- package/templates/app/bootstrap/app.ts +30 -2
- package/templates/app/config/app.ts +7 -6
- package/templates/app/config/cache.ts +14 -7
- package/templates/app/config/database.ts +48 -21
- package/templates/app/config/filesystems.ts +11 -9
- package/templates/app/config/http.ts +3 -2
- package/templates/app/config/logging.ts +6 -4
- package/templates/app/config/mail.ts +10 -8
- package/templates/app/config/queue.ts +6 -4
- package/templates/app/database/migrations/2024_01_01_000001_create_users_table.ts +25 -0
- package/templates/app/database/migrations/2024_01_01_000002_create_password_resets_table.ts +22 -0
- package/templates/app/database/migrations/2024_01_01_000003_create_failed_jobs_table.ts +25 -0
- package/templates/app/package.json +4 -3
- package/templates/app/public/assets/img/favicon.png +0 -0
- package/templates/app/public/assets/img/logo.png +0 -0
- package/templates/app/resources/views/welcome.ark.html +846 -0
- package/templates/app/routes/api.ts +8 -3
- package/templates/app/routes/web.ts +8 -4
- package/templates/app/server.ts +3 -4
- package/templates/app/tsconfig.json +12 -0
- package/dist/tests/Cli.test.d.ts +0 -2
- package/dist/tests/Cli.test.d.ts.map +0 -1
- package/dist/tests/Cli.test.js +0 -16
- package/dist/tests/Cli.test.js.map +0 -1
- package/templates/app/app/Controllers/UserController.ts +0 -88
- 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">© {{ 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">© {{ year }} {{ app_name }}. All rights reserved.</div>
|
|
745
|
+
</div>
|
|
746
|
+
</body>
|
|
747
|
+
</html>`;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
exports.AuthApiInstallCommand = AuthApiInstallCommand;
|
|
751
|
+
//# sourceMappingURL=AuthApiInstallCommand.js.map
|