millas 0.2.28 → 0.2.30
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/bin/millas.js +12 -2
- package/package.json +2 -1
- package/src/cli.js +117 -20
- package/src/commands/call.js +1 -1
- package/src/commands/createsuperuser.js +137 -182
- package/src/commands/key.js +61 -83
- package/src/commands/lang.js +423 -515
- package/src/commands/make.js +88 -62
- package/src/commands/migrate.js +200 -279
- package/src/commands/new.js +55 -50
- package/src/commands/route.js +78 -80
- package/src/commands/schedule.js +52 -150
- package/src/commands/serve.js +158 -191
- package/src/console/AppCommand.js +106 -0
- package/src/console/BaseCommand.js +726 -0
- package/src/console/CommandContext.js +66 -0
- package/src/console/CommandRegistry.js +88 -0
- package/src/console/Style.js +123 -0
- package/src/console/index.js +12 -3
- package/src/container/AppInitializer.js +10 -0
- package/src/facades/DB.js +195 -0
- package/src/index.js +2 -1
- package/src/scaffold/maker.js +102 -42
- package/src/schematics/Collection.js +28 -0
- package/src/schematics/SchematicEngine.js +122 -0
- package/src/schematics/Template.js +99 -0
- package/src/schematics/index.js +7 -0
- package/src/templates/command/default.template.js +14 -0
- package/src/templates/command/schema.json +19 -0
- package/src/templates/controller/default.template.js +10 -0
- package/src/templates/controller/resource.template.js +59 -0
- package/src/templates/controller/schema.json +30 -0
- package/src/templates/job/default.template.js +11 -0
- package/src/templates/job/schema.json +19 -0
- package/src/templates/middleware/default.template.js +11 -0
- package/src/templates/middleware/schema.json +19 -0
- package/src/templates/migration/default.template.js +14 -0
- package/src/templates/migration/schema.json +19 -0
- package/src/templates/model/default.template.js +14 -0
- package/src/templates/model/migration.template.js +17 -0
- package/src/templates/model/schema.json +30 -0
- package/src/templates/service/default.template.js +12 -0
- package/src/templates/service/schema.json +19 -0
- package/src/templates/shape/default.template.js +11 -0
- package/src/templates/shape/schema.json +19 -0
- package/src/validation/BaseValidator.js +3 -0
- package/src/validation/types.js +3 -3
package/src/commands/new.js
CHANGED
|
@@ -1,65 +1,70 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const BaseCommand = require('../console/BaseCommand');
|
|
4
4
|
const fs = require('fs-extra');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const ora = require('ora');
|
|
7
7
|
const { generateProject } = require('../scaffold/generator');
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
.command('new <project-name>')
|
|
12
|
-
.description('Create a new Millas project')
|
|
13
|
-
.option('--no-install', 'Skip npm install')
|
|
14
|
-
.action(async (projectName, options) => {
|
|
15
|
-
console.log();
|
|
16
|
-
console.log(chalk.cyan(' ⚡ Millas Framework'));
|
|
17
|
-
console.log(chalk.gray(' Creating a new project...\n'));
|
|
9
|
+
class NewCommand extends BaseCommand {
|
|
10
|
+
static description = 'Create a new Millas project';
|
|
18
11
|
|
|
19
|
-
|
|
12
|
+
async onInit(register) {
|
|
13
|
+
register
|
|
14
|
+
.command(async (projectName, install) => {
|
|
15
|
+
this.logger.log('');
|
|
16
|
+
this.logger.log(this.style.info(' ⚡ Millas Framework'));
|
|
17
|
+
this.logger.log(this.style.muted(' Creating a new project...\n'));
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
console.error(chalk.red(` ✖ Directory "${projectName}" already exists.\n`));
|
|
23
|
-
process.exit(1);
|
|
24
|
-
}
|
|
19
|
+
const targetDir = path.resolve(this.cwd, projectName);
|
|
25
20
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
try {
|
|
29
|
-
await generateProject(projectName, targetDir);
|
|
30
|
-
spinner.succeed(chalk.green(` Project "${projectName}" created successfully!`));
|
|
31
|
-
|
|
32
|
-
if (options.install !== false) {
|
|
33
|
-
const installSpinner = ora(' Installing dependencies...').start();
|
|
34
|
-
const { execSync } = require('child_process');
|
|
35
|
-
execSync('npm install', { cwd: targetDir, stdio: 'ignore' });
|
|
36
|
-
installSpinner.succeed(chalk.green(' Dependencies installed!'));
|
|
21
|
+
if (fs.existsSync(targetDir)) {
|
|
22
|
+
throw new Error(`Directory "${projectName}" already exists.`);
|
|
37
23
|
}
|
|
38
24
|
|
|
39
|
-
|
|
25
|
+
const spinner = ora(` Scaffolding project ${this.style.bold(projectName)}`).start();
|
|
26
|
+
|
|
40
27
|
try {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
fs.writeFileSync(envPath, envContent, 'utf8');
|
|
50
|
-
console.log(chalk.green(' ✔ Application key generated.'));
|
|
28
|
+
await generateProject(projectName, targetDir);
|
|
29
|
+
spinner.succeed(this.style.success(` Project "${projectName}" created successfully!`));
|
|
30
|
+
|
|
31
|
+
if (install !== false) {
|
|
32
|
+
const installSpinner = ora(' Installing dependencies...').start();
|
|
33
|
+
const { execSync } = require('child_process');
|
|
34
|
+
execSync('npm install', { cwd: targetDir, stdio: 'ignore' });
|
|
35
|
+
installSpinner.succeed(this.style.success(' Dependencies installed!'));
|
|
51
36
|
}
|
|
52
|
-
} catch { /* non-fatal — developer can run millas key:generate */ }
|
|
53
37
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
38
|
+
// Auto-generate APP_KEY into the new project's .env
|
|
39
|
+
try {
|
|
40
|
+
const { Encrypter } = require('../encryption/Encrypter');
|
|
41
|
+
const envPath = path.join(targetDir, '.env');
|
|
42
|
+
const key = Encrypter.generateKey('AES-256-CBC');
|
|
43
|
+
if (fs.existsSync(envPath)) {
|
|
44
|
+
let envContent = fs.readFileSync(envPath, 'utf8');
|
|
45
|
+
envContent = /^APP_KEY=/m.test(envContent)
|
|
46
|
+
? envContent.replace(/^APP_KEY=.*$/m, `APP_KEY=${key}`)
|
|
47
|
+
: envContent + `\nAPP_KEY=${key}\n`;
|
|
48
|
+
fs.writeFileSync(envPath, envContent, 'utf8');
|
|
49
|
+
this.logger.log(this.style.success(' ✔ Application key generated.'));
|
|
50
|
+
}
|
|
51
|
+
} catch { /* non-fatal — developer can run millas key:generate */ }
|
|
52
|
+
|
|
53
|
+
this.logger.log('');
|
|
54
|
+
this.logger.log(this.style.bold(' Next steps:'));
|
|
55
|
+
this.logger.log(this.style.info(` cd ${projectName}`));
|
|
56
|
+
this.logger.log(this.style.info(' millas serve'));
|
|
57
|
+
this.logger.log('');
|
|
58
|
+
} catch (err) {
|
|
59
|
+
spinner.fail(this.style.danger(' Failed to create project.'));
|
|
60
|
+
throw err;
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
.name('new')
|
|
64
|
+
.str('projectName', 'Project name')
|
|
65
|
+
.bool('install', 'Install dependencies')
|
|
66
|
+
.description('Create a new Millas project');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
module.exports = NewCommand;
|
package/src/commands/route.js
CHANGED
|
@@ -1,82 +1,83 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
3
|
+
const BaseCommand = require('../console/BaseCommand');
|
|
4
|
+
|
|
5
|
+
class RouteCommand extends BaseCommand {
|
|
6
|
+
static description = 'Manage application routes';
|
|
7
|
+
|
|
8
|
+
async onInit(register) {
|
|
9
|
+
register
|
|
10
|
+
.command(this.list)
|
|
11
|
+
.description('List all registered routes');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async list() {
|
|
15
|
+
if (!this.hasAppBootstrap()) {
|
|
16
|
+
this.error('Not inside a Millas project.');
|
|
17
|
+
throw new Error('Not inside a Millas project');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
process.env.MILLAS_ROUTE_LIST = 'true';
|
|
21
|
+
|
|
22
|
+
let route;
|
|
23
|
+
try {
|
|
24
|
+
const app = await this.getApp();
|
|
25
|
+
|
|
26
|
+
route = app.route;
|
|
27
|
+
} catch (err) {
|
|
28
|
+
this.error(`Failed to load routes: ${err.message}`);
|
|
29
|
+
throw err;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!route) {
|
|
33
|
+
this.warn('Bootstrap did not export { route }.');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const rows = route.list();
|
|
38
|
+
|
|
39
|
+
if (rows.length === 0) {
|
|
40
|
+
this.warn('No routes registered.');
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.logger.log('');
|
|
45
|
+
this.logger.log(this.style.bold(' Registered Routes\n'));
|
|
46
|
+
|
|
47
|
+
const col = {
|
|
48
|
+
verb: 8,
|
|
49
|
+
path: Math.max(6, ...rows.map(r => r.path.length)) + 2,
|
|
50
|
+
handler: Math.max(8, ...rows.map(r => formatHandler(r).length)) + 2,
|
|
51
|
+
mw: Math.max(10, ...rows.map(r => (r.middleware || []).join(', ').length || 1)) + 2,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const header =
|
|
55
|
+
' ' +
|
|
56
|
+
this.style.bold(pad('METHOD', col.verb)) +
|
|
57
|
+
this.style.bold(pad('PATH', col.path)) +
|
|
58
|
+
this.style.bold(pad('HANDLER', col.handler)) +
|
|
59
|
+
this.style.bold(pad('MIDDLEWARE', col.mw)) +
|
|
60
|
+
this.style.bold('NAME');
|
|
61
|
+
|
|
62
|
+
this.logger.log(header);
|
|
63
|
+
this.logger.log(this.style.line(col.verb + col.path + col.handler + col.mw + 10, '─').padStart(col.verb + col.path + col.handler + col.mw + 12));
|
|
64
|
+
|
|
65
|
+
for (const r of rows) {
|
|
66
|
+
const mw = (r.middleware || []).join(', ') || this.style.secondary('—');
|
|
67
|
+
const name = r.name || this.style.secondary('—');
|
|
68
|
+
this.logger.log(
|
|
53
69
|
' ' +
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const name = r.name || chalk.gray('—');
|
|
66
|
-
console.log(
|
|
67
|
-
' ' +
|
|
68
|
-
verbChalk(r.verb)(pad(r.verb, col.verb)) +
|
|
69
|
-
chalk.cyan(pad(r.path, col.path)) +
|
|
70
|
-
chalk.white(pad(formatHandler(r), col.handler)) +
|
|
71
|
-
chalk.yellow(pad(mw, col.mw)) +
|
|
72
|
-
chalk.gray(name)
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
console.log(chalk.gray(`\n ${rows.length} route(s) total.\n`));
|
|
77
|
-
process.exit(0);
|
|
78
|
-
});
|
|
79
|
-
};
|
|
70
|
+
this.style.method(r.verb)(pad(r.verb, col.verb)) +
|
|
71
|
+
this.style.info(pad(r.path, col.path)) +
|
|
72
|
+
this.style.light(pad(formatHandler(r), col.handler)) +
|
|
73
|
+
this.style.warning(pad(mw, col.mw)) +
|
|
74
|
+
this.style.secondary(name)
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
this.logger.log(this.style.secondary(`\n ${rows.length} route(s) total.\n`));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
80
81
|
|
|
81
82
|
function pad(str, len) { return String(str).padEnd(len); }
|
|
82
83
|
|
|
@@ -87,7 +88,4 @@ function formatHandler(r) {
|
|
|
87
88
|
return r.method ? `${name}@${r.method}` : name;
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
return { GET: chalk.green, POST: chalk.blue, PUT: chalk.yellow,
|
|
92
|
-
PATCH: chalk.magenta, DELETE: chalk.red }[verb] || chalk.white;
|
|
93
|
-
}
|
|
91
|
+
module.exports = RouteCommand;
|
package/src/commands/schedule.js
CHANGED
|
@@ -1,176 +1,78 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
let app;
|
|
21
|
-
try {
|
|
22
|
-
app = await require(bootstrapPath);
|
|
23
|
-
} catch (e) {
|
|
24
|
-
console.error(chalk.red(`\n ✖ Failed to load app: ${e.message}\n`));
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const scheduler = app.make('scheduler');
|
|
29
|
-
const tasks = scheduler.getTasks();
|
|
3
|
+
const BaseCommand = require('../console/BaseCommand');
|
|
4
|
+
|
|
5
|
+
class ScheduleCommand extends BaseCommand {
|
|
6
|
+
static description = 'Manage scheduled tasks';
|
|
7
|
+
|
|
8
|
+
async onInit(register) {
|
|
9
|
+
register
|
|
10
|
+
.command(async () => {
|
|
11
|
+
const app = this.getApp();
|
|
12
|
+
const scheduler = app.make('scheduler');
|
|
13
|
+
const tasks = scheduler.getTasks();
|
|
14
|
+
|
|
15
|
+
if (tasks.length === 0) {
|
|
16
|
+
this.info('No scheduled tasks found.');
|
|
17
|
+
this.info('Create routes/schedule.js to define scheduled tasks.');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
30
20
|
|
|
31
|
-
|
|
32
|
-
if (tasks.length === 0) {
|
|
33
|
-
console.log(chalk.gray(' No scheduled tasks found.'));
|
|
34
|
-
console.log(chalk.gray(' Create routes/schedule.js to define scheduled tasks.'));
|
|
35
|
-
} else {
|
|
36
|
-
console.log(chalk.bold(' Scheduled Tasks\n'));
|
|
21
|
+
this.logger.log(this.style.bold('\n Scheduled Tasks\n'));
|
|
37
22
|
|
|
38
23
|
for (const task of tasks) {
|
|
39
|
-
const status = task.isRunning()
|
|
24
|
+
const status = task.isRunning()
|
|
25
|
+
? this.style.warning('RUNNING')
|
|
26
|
+
: this.style.success('READY');
|
|
40
27
|
const nextRun = task.lastRun
|
|
41
28
|
? `Last: ${task.lastRun.toLocaleString()}`
|
|
42
|
-
:
|
|
29
|
+
: this.style.muted('Never run');
|
|
43
30
|
|
|
44
|
-
|
|
31
|
+
this.logger.log(` ${this.style.info(task.jobClass.name.padEnd(30))} ${status.padEnd(15)} ${nextRun}`);
|
|
45
32
|
|
|
46
33
|
if (task.cronExpression) {
|
|
47
|
-
|
|
34
|
+
this.logger.log(` ${this.style.muted('Cron:')} ${task.cronExpression}`);
|
|
48
35
|
}
|
|
49
36
|
|
|
50
37
|
if (Object.keys(task.parameters).length > 0) {
|
|
51
|
-
|
|
38
|
+
this.logger.log(` ${this.style.muted('Params:')} ${JSON.stringify(task.parameters)}`);
|
|
52
39
|
}
|
|
53
40
|
|
|
54
41
|
if (task.failures.length > 0) {
|
|
55
42
|
const lastFailure = task.failures[task.failures.length - 1];
|
|
56
|
-
|
|
43
|
+
this.logger.log(` ${this.style.danger('Last failure:')} ${lastFailure.error} (${lastFailure.timestamp.toLocaleString()})`);
|
|
57
44
|
}
|
|
58
45
|
|
|
59
|
-
|
|
46
|
+
this.logger.log('');
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
.name('list')
|
|
50
|
+
.description('Show all scheduled tasks and their next run times');
|
|
51
|
+
|
|
52
|
+
register
|
|
53
|
+
.command(async (taskName) => {
|
|
54
|
+
await this.appBoot();
|
|
55
|
+
const app = this.getApp();
|
|
56
|
+
const scheduler = app.make('scheduler');
|
|
57
|
+
const tasks = scheduler.getTasks();
|
|
58
|
+
const task = tasks.find(t => t.jobClass.name === taskName);
|
|
59
|
+
|
|
60
|
+
if (!task) {
|
|
61
|
+
this.error(`Task "${taskName}" not found.`);
|
|
62
|
+
this.info('Available tasks:');
|
|
63
|
+
tasks.forEach(t => this.logger.log(this.style.muted(` - ${t.jobClass.name}`)));
|
|
64
|
+
throw new Error(`Task "${taskName}" not found`);
|
|
60
65
|
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
process.exit(0);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
program
|
|
67
|
-
.command('schedule:test <taskName>')
|
|
68
|
-
.description('Run a specific scheduled task immediately for testing')
|
|
69
|
-
.action(async (taskName) => {
|
|
70
|
-
const bootstrapPath = path.resolve(process.cwd(), 'bootstrap/app.js');
|
|
71
|
-
if (!fs.existsSync(bootstrapPath)) {
|
|
72
|
-
console.error(chalk.red('\n ✖ Not inside a Millas project.\n'));
|
|
73
|
-
process.exit(1);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Boot the app
|
|
77
|
-
let app;
|
|
78
|
-
try {
|
|
79
|
-
app = await require(bootstrapPath);
|
|
80
|
-
} catch (e) {
|
|
81
|
-
console.error(chalk.red(`\n ✖ Failed to load app: ${e.message}\n`));
|
|
82
|
-
process.exit(1);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const scheduler = app.make('scheduler');
|
|
86
|
-
const tasks = scheduler.getTasks();
|
|
87
|
-
const task = tasks.find(t => t.jobClass.name === taskName);
|
|
88
|
-
|
|
89
|
-
if (!task) {
|
|
90
|
-
console.error(chalk.red(`\n ✖ Task "${taskName}" not found.\n`));
|
|
91
|
-
console.log(chalk.gray(' Available tasks:'));
|
|
92
|
-
tasks.forEach(t => console.log(chalk.gray(` - ${t.jobClass.name}`)));
|
|
93
|
-
console.log();
|
|
94
|
-
process.exit(1);
|
|
95
|
-
}
|
|
96
66
|
|
|
97
|
-
|
|
67
|
+
this.logger.log(this.style.primary(`\n ▶ Running ${taskName}...\n`));
|
|
98
68
|
|
|
99
|
-
try {
|
|
100
69
|
await scheduler._executeTask(task, new Date());
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
process.exit(0);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
program
|
|
111
|
-
.command('make:task <name>')
|
|
112
|
-
.description('Generate a new scheduled task class')
|
|
113
|
-
.action(async (name) => {
|
|
114
|
-
const taskName = name.endsWith('Task') ? name : `${name}Task`;
|
|
115
|
-
const taskPath = path.resolve(process.cwd(), 'app', 'tasks', `${taskName}.js`);
|
|
116
|
-
|
|
117
|
-
// Ensure directory exists
|
|
118
|
-
await fs.ensureDir(path.dirname(taskPath));
|
|
119
|
-
|
|
120
|
-
// Check if file already exists
|
|
121
|
-
if (await fs.pathExists(taskPath)) {
|
|
122
|
-
console.error(chalk.red(`\n ✖ Task ${taskName} already exists.\n`));
|
|
123
|
-
process.exit(1);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Generate task class
|
|
127
|
-
const template = `'use strict';
|
|
128
|
-
|
|
129
|
-
const { Job } = require('millas/core/queue');
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* ${taskName}
|
|
133
|
-
*
|
|
134
|
-
* Scheduled task that runs automatically based on the schedule defined in routes/schedule.js
|
|
135
|
-
*
|
|
136
|
-
* Usage in routes/schedule.js:
|
|
137
|
-
* Schedule.job(${taskName}).daily().at('09:00');
|
|
138
|
-
*/
|
|
139
|
-
class ${taskName} extends Job {
|
|
140
|
-
/**
|
|
141
|
-
* Constructor - DI container will inject dependencies automatically
|
|
142
|
-
*/
|
|
143
|
-
constructor(/* inject dependencies here */) {
|
|
144
|
-
super();
|
|
145
|
-
// Store injected dependencies
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Execute the scheduled task
|
|
150
|
-
*/
|
|
151
|
-
async handle() {
|
|
152
|
-
// Implement your scheduled task logic here
|
|
153
|
-
console.log('${taskName} is running...');
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Handle task failure (optional)
|
|
158
|
-
*/
|
|
159
|
-
async failed(error) {
|
|
160
|
-
console.error('${taskName} failed:', error.message);
|
|
70
|
+
this.success(`${taskName} completed successfully.`);
|
|
71
|
+
})
|
|
72
|
+
.name('test')
|
|
73
|
+
.str('taskName', 'Task name to run')
|
|
74
|
+
.description('Run a specific scheduled task immediately for testing');
|
|
161
75
|
}
|
|
162
76
|
}
|
|
163
77
|
|
|
164
|
-
module.exports =
|
|
165
|
-
`;
|
|
166
|
-
|
|
167
|
-
await fs.writeFile(taskPath, template);
|
|
168
|
-
|
|
169
|
-
console.log(chalk.green(`\n ✔ Task created: ${taskPath}`));
|
|
170
|
-
console.log(chalk.gray('\n Next steps:'));
|
|
171
|
-
console.log(chalk.gray(` 1. Implement the handle() method in ${taskName}`));
|
|
172
|
-
console.log(chalk.gray(` 2. Add the task to routes/schedule.js:`));
|
|
173
|
-
console.log(chalk.gray(` Schedule.job(${taskName}).daily().at('09:00');`));
|
|
174
|
-
console.log();
|
|
175
|
-
});
|
|
176
|
-
};
|
|
78
|
+
module.exports = ScheduleCommand;
|