@nitronjs/framework 0.2.1 → 0.2.3
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/cli/njs.js +5 -14
- package/lib/Console/Commands/MigrateCommand.js +4 -2
- package/lib/Console/Commands/MigrateFreshCommand.js +4 -2
- package/lib/Console/Commands/MigrateRollbackCommand.js +2 -1
- package/lib/Console/Commands/MigrateStatusCommand.js +2 -1
- package/lib/Console/Commands/SeedCommand.js +4 -26
- package/lib/Core/Paths.js +0 -8
- package/lib/Database/Migration/MigrationRepository.js +1 -1
- package/lib/Database/Migration/MigrationRunner.js +3 -6
- package/lib/Database/Model.js +16 -0
- package/lib/Database/QueryBuilder.js +12 -0
- package/lib/Database/QueryValidation.js +30 -1
- package/lib/Database/Seeder/SeederRunner.js +22 -124
- package/lib/Http/Server.js +3 -0
- package/lib/index.d.ts +7 -19
- package/lib/index.js +0 -1
- package/package.json +1 -1
- package/skeleton/package.json +2 -0
- package/lib/Database/Migration/migrations/0000_00_00_00_01_create_seeders_table.js +0 -20
- package/lib/Database/Seeder/SeederRepository.js +0 -45
package/cli/njs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const COLORS = {
|
|
4
4
|
reset: "\x1b[0m",
|
|
@@ -32,10 +32,8 @@ ${COLORS.bold}Database:${COLORS.reset}
|
|
|
32
32
|
${COLORS.cyan}njs migrate:status${COLORS.reset} Show the status of each migration
|
|
33
33
|
${COLORS.cyan}njs migrate:fresh${COLORS.reset} Drop all tables and re-migrate
|
|
34
34
|
${COLORS.cyan}njs migrate:fresh --seed${COLORS.reset} Fresh migrate with seeders
|
|
35
|
-
${COLORS.cyan}njs seed${COLORS.reset} Run
|
|
36
|
-
${COLORS.cyan}njs seed
|
|
37
|
-
${COLORS.cyan}njs seed --all${COLORS.reset} Run all seeders (prod + dev)
|
|
38
|
-
${COLORS.cyan}njs seed --status${COLORS.reset} Show the status of each seeder
|
|
35
|
+
${COLORS.cyan}njs seed${COLORS.reset} Run all seeders
|
|
36
|
+
${COLORS.cyan}njs seed <Name>${COLORS.reset} Run a specific seeder
|
|
39
37
|
|
|
40
38
|
${COLORS.bold}Generators:${COLORS.reset}
|
|
41
39
|
${COLORS.cyan}njs make:controller${COLORS.reset} Create a new controller
|
|
@@ -137,15 +135,8 @@ async function run() {
|
|
|
137
135
|
|
|
138
136
|
case "seed": {
|
|
139
137
|
const { default: Seed } = await import("../lib/Console/Commands/SeedCommand.js");
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
const isAll = additionalArgs.includes('--all');
|
|
143
|
-
|
|
144
|
-
let environment = 'prod';
|
|
145
|
-
if (isDev) environment = 'dev';
|
|
146
|
-
if (isAll) environment = 'all';
|
|
147
|
-
|
|
148
|
-
await Seed({ environment, status: showStatus });
|
|
138
|
+
const seederName = additionalArgs.find(a => !a.startsWith('--')) || null;
|
|
139
|
+
await Seed(seederName);
|
|
149
140
|
break;
|
|
150
141
|
}
|
|
151
142
|
|
|
@@ -12,7 +12,8 @@ export default async function migrate(options = {}) {
|
|
|
12
12
|
|
|
13
13
|
try {
|
|
14
14
|
await DB.setup();
|
|
15
|
-
}
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
16
17
|
console.error('❌ Database setup failed:', error.message);
|
|
17
18
|
console.error('Check your .env file and ensure the database exists and is accessible');
|
|
18
19
|
return false;
|
|
@@ -34,7 +35,8 @@ export default async function migrate(options = {}) {
|
|
|
34
35
|
await DB.close();
|
|
35
36
|
return true;
|
|
36
37
|
|
|
37
|
-
}
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
38
40
|
console.error('❌ Migration error:', error.message);
|
|
39
41
|
await DB.close();
|
|
40
42
|
return false;
|
|
@@ -20,7 +20,8 @@ export default async function migrateFresh(options = {}) {
|
|
|
20
20
|
|
|
21
21
|
try {
|
|
22
22
|
await DB.setup();
|
|
23
|
-
}
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
24
25
|
console.error('❌ Database setup failed:', error.message);
|
|
25
26
|
console.error('Check your .env file and ensure the database exists and is accessible');
|
|
26
27
|
return false;
|
|
@@ -59,7 +60,8 @@ export default async function migrateFresh(options = {}) {
|
|
|
59
60
|
await DB.close();
|
|
60
61
|
return true;
|
|
61
62
|
|
|
62
|
-
}
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
63
65
|
console.error('❌ Fresh migration error:', error.message);
|
|
64
66
|
await DB.close();
|
|
65
67
|
return false;
|
|
@@ -11,7 +11,8 @@ export default async function rollback(options = {}) {
|
|
|
11
11
|
|
|
12
12
|
try {
|
|
13
13
|
await DB.setup();
|
|
14
|
-
}
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
15
16
|
console.error('❌ Database setup failed:', error.message);
|
|
16
17
|
console.error('Check your .env file and ensure the database exists and is accessible');
|
|
17
18
|
return false;
|
|
@@ -9,7 +9,8 @@ export default async function status() {
|
|
|
9
9
|
|
|
10
10
|
try {
|
|
11
11
|
await DB.setup();
|
|
12
|
-
}
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
13
14
|
console.error('❌ Database setup failed:', error.message);
|
|
14
15
|
console.error('Check your .env file and ensure the database exists and is accessible');
|
|
15
16
|
return false;
|
|
@@ -3,9 +3,7 @@ import DB from '../../Database/DB.js';
|
|
|
3
3
|
import Config from '../../Core/Config.js';
|
|
4
4
|
import SeederRunner from '../../Database/Seeder/SeederRunner.js';
|
|
5
5
|
|
|
6
|
-
export default async function seed(
|
|
7
|
-
const { environment = 'prod', status = false } = options;
|
|
8
|
-
|
|
6
|
+
export default async function seed(seederName = null) {
|
|
9
7
|
dotenv.config({ quiet: true });
|
|
10
8
|
await Config.initialize();
|
|
11
9
|
|
|
@@ -18,20 +16,7 @@ export default async function seed(options = {}) {
|
|
|
18
16
|
}
|
|
19
17
|
|
|
20
18
|
try {
|
|
21
|
-
|
|
22
|
-
await SeederRunner.printStatus();
|
|
23
|
-
await DB.close();
|
|
24
|
-
return true;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
let result;
|
|
28
|
-
|
|
29
|
-
if (environment === 'all') {
|
|
30
|
-
result = await SeederRunner.runAll();
|
|
31
|
-
} else {
|
|
32
|
-
result = await SeederRunner.run(environment);
|
|
33
|
-
}
|
|
34
|
-
|
|
19
|
+
const result = await SeederRunner.run(seederName);
|
|
35
20
|
await DB.close();
|
|
36
21
|
return result.success;
|
|
37
22
|
|
|
@@ -45,16 +30,9 @@ export default async function seed(options = {}) {
|
|
|
45
30
|
const isMain = process.argv[1]?.endsWith("SeedCommand.js");
|
|
46
31
|
if (isMain) {
|
|
47
32
|
const args = process.argv.slice(2);
|
|
33
|
+
const seederName = args.find(a => !a.startsWith('--')) || null;
|
|
48
34
|
|
|
49
|
-
|
|
50
|
-
const isDev = args.includes('--dev');
|
|
51
|
-
const isAll = args.includes('--all');
|
|
52
|
-
|
|
53
|
-
let environment = 'prod';
|
|
54
|
-
if (isDev) environment = 'dev';
|
|
55
|
-
if (isAll) environment = 'all';
|
|
56
|
-
|
|
57
|
-
seed({ environment, status: showStatus })
|
|
35
|
+
seed(seederName)
|
|
58
36
|
.then(success => process.exit(success ? 0 : 1))
|
|
59
37
|
.catch(err => {
|
|
60
38
|
console.error(err);
|
package/lib/Core/Paths.js
CHANGED
|
@@ -92,14 +92,6 @@ class Paths {
|
|
|
92
92
|
return path.join(this.#project, "database/seeders");
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
static get seedersProd() {
|
|
96
|
-
return path.join(this.#project, "database/seeders/prod");
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
static get seedersDev() {
|
|
100
|
-
return path.join(this.#project, "database/seeders/dev");
|
|
101
|
-
}
|
|
102
|
-
|
|
103
95
|
static get storage() {
|
|
104
96
|
return path.join(this.#project, "storage");
|
|
105
97
|
}
|
|
@@ -3,7 +3,6 @@ import path from 'path';
|
|
|
3
3
|
import { pathToFileURL, fileURLToPath } from 'url';
|
|
4
4
|
import Checksum from './Checksum.js';
|
|
5
5
|
import MigrationRepository from './MigrationRepository.js';
|
|
6
|
-
import SeederRepository from '../Seeder/SeederRepository.js';
|
|
7
6
|
import Paths from '../../Core/Paths.js';
|
|
8
7
|
|
|
9
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -39,10 +38,7 @@ class MigrationRunner {
|
|
|
39
38
|
const ran = [];
|
|
40
39
|
|
|
41
40
|
for (const file of files) {
|
|
42
|
-
const
|
|
43
|
-
const tableExists = isMigrationsTable
|
|
44
|
-
? await MigrationRepository.tableExists()
|
|
45
|
-
: await SeederRepository.tableExists();
|
|
41
|
+
const tableExists = await MigrationRepository.tableExists();
|
|
46
42
|
|
|
47
43
|
if (tableExists) continue;
|
|
48
44
|
|
|
@@ -147,7 +143,8 @@ class MigrationRunner {
|
|
|
147
143
|
console.log(`${COLORS.green}${COLORS.bold}✅ All migrations completed successfully.${COLORS.reset}`);
|
|
148
144
|
return { success: true, ran: executedInBatch.map(e => e.file) };
|
|
149
145
|
|
|
150
|
-
}
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
151
148
|
console.error(`\n${COLORS.red}❌ Migration failed: ${error.message}${COLORS.reset}`);
|
|
152
149
|
|
|
153
150
|
if (executedInBatch.length > 0) {
|
package/lib/Database/Model.js
CHANGED
|
@@ -98,6 +98,22 @@ class Model {
|
|
|
98
98
|
return DB.table(this.table, null, this).select(...columns);
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
static orderBy(column, direction = 'ASC') {
|
|
102
|
+
if (!this.table) {
|
|
103
|
+
throw new Error(`Model ${this.name} must define a static 'table' property`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return DB.table(this.table, null, this).orderBy(column, direction);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
static limit(value) {
|
|
110
|
+
if (!this.table) {
|
|
111
|
+
throw new Error(`Model ${this.name} must define a static 'table' property`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return DB.table(this.table, null, this).limit(value);
|
|
115
|
+
}
|
|
116
|
+
|
|
101
117
|
static async first() {
|
|
102
118
|
if (!this.table) {
|
|
103
119
|
throw new Error(`Model ${this.name} must define a static 'table' property`);
|
|
@@ -159,6 +159,18 @@ class QueryBuilder {
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
where(column, operator, value) {
|
|
162
|
+
if (typeof column === 'object' && column !== null && !Array.isArray(column)) {
|
|
163
|
+
for (const [key, val] of Object.entries(column)) {
|
|
164
|
+
if (Array.isArray(val) && val.length === 2) {
|
|
165
|
+
this.where(key, val[0], val[1]);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
this.where(key, '=', val);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return this;
|
|
172
|
+
}
|
|
173
|
+
|
|
162
174
|
if (arguments.length === 2) {
|
|
163
175
|
value = operator;
|
|
164
176
|
operator = '=';
|
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
const RESERVED_KEYWORDS = new Set([
|
|
2
|
+
'order', 'key', 'group', 'index', 'table', 'column', 'select', 'insert',
|
|
3
|
+
'update', 'delete', 'from', 'where', 'join', 'left', 'right', 'inner',
|
|
4
|
+
'outer', 'on', 'and', 'or', 'not', 'null', 'true', 'false', 'like',
|
|
5
|
+
'in', 'between', 'is', 'as', 'by', 'asc', 'desc', 'limit', 'offset',
|
|
6
|
+
'having', 'distinct', 'all', 'any', 'exists', 'case', 'when', 'then',
|
|
7
|
+
'else', 'end', 'if', 'into', 'values', 'set', 'create', 'drop', 'alter',
|
|
8
|
+
'add', 'primary', 'foreign', 'references', 'constraint', 'default',
|
|
9
|
+
'unique', 'check', 'view', 'trigger', 'procedure', 'function', 'database',
|
|
10
|
+
'schema', 'use', 'show', 'describe', 'explain', 'grant', 'revoke',
|
|
11
|
+
'commit', 'rollback', 'transaction', 'lock', 'unlock', 'read', 'write',
|
|
12
|
+
'range', 'rows', 'rank', 'row', 'status', 'type', 'level', 'value', 'name'
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
function escapeIdentifier(identifier) {
|
|
16
|
+
if (RESERVED_KEYWORDS.has(identifier.toLowerCase())) {
|
|
17
|
+
return `\`${identifier}\``;
|
|
18
|
+
}
|
|
19
|
+
return identifier;
|
|
20
|
+
}
|
|
21
|
+
|
|
1
22
|
export function validateIdentifier(identifier) {
|
|
2
23
|
if (typeof identifier !== 'string' || identifier.length === 0) {
|
|
3
24
|
throw new Error('Identifier must be a non-empty string');
|
|
@@ -27,7 +48,15 @@ export function validateIdentifier(identifier) {
|
|
|
27
48
|
throw new Error(`Invalid identifier: "${identifier}". Must be alphanumeric with underscores, optionally qualified with table name.`);
|
|
28
49
|
}
|
|
29
50
|
|
|
30
|
-
|
|
51
|
+
if (identifier.includes('.')) {
|
|
52
|
+
const [table, column] = identifier.split('.');
|
|
53
|
+
if (column === '*') {
|
|
54
|
+
return `${escapeIdentifier(table)}.*`;
|
|
55
|
+
}
|
|
56
|
+
return `${escapeIdentifier(table)}.${escapeIdentifier(column)}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return escapeIdentifier(identifier);
|
|
31
60
|
}
|
|
32
61
|
|
|
33
62
|
export function validateWhereOperator(operator) {
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { pathToFileURL } from 'url';
|
|
4
|
-
import Checksum from '../Migration/Checksum.js';
|
|
5
|
-
import SeederRepository from './SeederRepository.js';
|
|
6
4
|
import Paths from '../../Core/Paths.js';
|
|
7
5
|
|
|
8
6
|
const COLORS = {
|
|
@@ -17,167 +15,67 @@ const COLORS = {
|
|
|
17
15
|
|
|
18
16
|
class SeederRunner {
|
|
19
17
|
|
|
20
|
-
static async run(
|
|
21
|
-
const seedersDir =
|
|
18
|
+
static async run(seederName = null) {
|
|
19
|
+
const seedersDir = Paths.seeders;
|
|
22
20
|
|
|
23
21
|
if (!fs.existsSync(seedersDir)) {
|
|
24
|
-
console.log(`${COLORS.yellow}⚠️ No seeders directory found
|
|
22
|
+
console.log(`${COLORS.yellow}⚠️ No seeders directory found${COLORS.reset}`);
|
|
25
23
|
return { success: true, ran: [] };
|
|
26
24
|
}
|
|
27
25
|
|
|
28
|
-
|
|
26
|
+
let files = fs.readdirSync(seedersDir)
|
|
29
27
|
.filter(f => f.endsWith('.js'))
|
|
30
28
|
.sort();
|
|
31
29
|
|
|
32
30
|
if (files.length === 0) {
|
|
33
|
-
console.log(`${COLORS.yellow}⚠️ No seeder files found
|
|
31
|
+
console.log(`${COLORS.yellow}⚠️ No seeder files found${COLORS.reset}`);
|
|
34
32
|
return { success: true, ran: [] };
|
|
35
33
|
}
|
|
36
34
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (currentChecksum !== storedChecksum) {
|
|
47
|
-
console.error(`${COLORS.red}❌ CHECKSUM MISMATCH: ${fullName}${COLORS.reset}`);
|
|
48
|
-
console.error(`${COLORS.dim} Stored: ${storedChecksum}${COLORS.reset}`);
|
|
49
|
-
console.error(`${COLORS.dim} Current: ${currentChecksum}${COLORS.reset}`);
|
|
50
|
-
console.error(`${COLORS.red} Seeder files must NEVER be modified after execution.${COLORS.reset}`);
|
|
51
|
-
console.error(`${COLORS.red} Create a NEW seeder for any data changes.${COLORS.reset}`);
|
|
52
|
-
return {
|
|
53
|
-
success: false,
|
|
54
|
-
ran: [],
|
|
55
|
-
error: new Error(`Checksum mismatch for seeder: ${fullName}`)
|
|
56
|
-
};
|
|
57
|
-
}
|
|
35
|
+
// If specific seeder name provided, filter to only that one
|
|
36
|
+
if (seederName) {
|
|
37
|
+
const targetFile = seederName.endsWith('.js') ? seederName : `${seederName}.js`;
|
|
38
|
+
files = files.filter(f => f === targetFile);
|
|
39
|
+
|
|
40
|
+
if (files.length === 0) {
|
|
41
|
+
console.log(`${COLORS.red}❌ Seeder not found: ${seederName}${COLORS.reset}`);
|
|
42
|
+
return { success: false, ran: [], error: new Error(`Seeder not found: ${seederName}`) };
|
|
58
43
|
}
|
|
59
44
|
}
|
|
60
45
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (pending.length === 0) {
|
|
64
|
-
console.log(`${COLORS.green}✅ Nothing to seed. All ${environment} seeders are up to date.${COLORS.reset}`);
|
|
65
|
-
return { success: true, ran: [] };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
console.log(`${COLORS.cyan}🌱 Running ${environment} seeders${COLORS.reset}\n`);
|
|
46
|
+
console.log(`${COLORS.cyan}🌱 Running seeders${COLORS.reset}\n`);
|
|
69
47
|
|
|
70
48
|
const executed = [];
|
|
71
49
|
|
|
72
50
|
try {
|
|
73
|
-
for (const file of
|
|
51
|
+
for (const file of files) {
|
|
74
52
|
const filePath = path.join(seedersDir, file);
|
|
75
53
|
const fileUrl = pathToFileURL(filePath).href;
|
|
76
|
-
const checksum = Checksum.fromFile(filePath);
|
|
77
|
-
const fullName = `${environment}/${file}`;
|
|
78
54
|
|
|
79
|
-
console.log(`${COLORS.dim}Seeding:${COLORS.reset} ${COLORS.cyan}${
|
|
55
|
+
console.log(`${COLORS.dim}Seeding:${COLORS.reset} ${COLORS.cyan}${file}${COLORS.reset}`);
|
|
80
56
|
|
|
81
|
-
const { default: seeder } = await import(fileUrl);
|
|
57
|
+
const { default: seeder } = await import(`${fileUrl}?t=${Date.now()}`);
|
|
82
58
|
|
|
83
59
|
if (typeof seeder.run !== 'function') {
|
|
84
60
|
throw new Error(`Seeder ${file} does not have a run() method`);
|
|
85
61
|
}
|
|
86
62
|
|
|
87
63
|
await seeder.run();
|
|
88
|
-
|
|
89
|
-
executed.push(fullName);
|
|
64
|
+
executed.push(file);
|
|
90
65
|
|
|
91
|
-
console.log(`${COLORS.green}✅ Seeded:${COLORS.reset} ${COLORS.cyan}${
|
|
66
|
+
console.log(`${COLORS.green}✅ Seeded:${COLORS.reset} ${COLORS.cyan}${file}${COLORS.reset}\n`);
|
|
92
67
|
}
|
|
93
68
|
|
|
94
|
-
console.log(`${COLORS.green}${COLORS.bold}✅ All
|
|
69
|
+
console.log(`${COLORS.green}${COLORS.bold}✅ All seeders completed successfully.${COLORS.reset}`);
|
|
95
70
|
return { success: true, ran: executed };
|
|
96
71
|
|
|
97
|
-
}
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
98
74
|
console.error(`\n${COLORS.red}❌ Seeding failed: ${error.message}${COLORS.reset}`);
|
|
99
75
|
return { success: false, ran: executed, error };
|
|
100
76
|
}
|
|
101
77
|
}
|
|
102
78
|
|
|
103
|
-
static async runAll() {
|
|
104
|
-
console.log(`${COLORS.bold}Running prod seeders...${COLORS.reset}\n`);
|
|
105
|
-
const prodResult = await this.run('prod');
|
|
106
|
-
|
|
107
|
-
if (!prodResult.success) {
|
|
108
|
-
return prodResult;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
console.log(`\n${COLORS.bold}Running dev seeders...${COLORS.reset}\n`);
|
|
112
|
-
const devResult = await this.run('dev');
|
|
113
|
-
|
|
114
|
-
return {
|
|
115
|
-
success: devResult.success,
|
|
116
|
-
ran: [...prodResult.ran, ...devResult.ran],
|
|
117
|
-
error: devResult.error
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
static async status() {
|
|
122
|
-
const prodDir = Paths.seedersProd;
|
|
123
|
-
const devDir = Paths.seedersDev;
|
|
124
|
-
|
|
125
|
-
const status = [];
|
|
126
|
-
|
|
127
|
-
if (fs.existsSync(prodDir)) {
|
|
128
|
-
const prodFiles = fs.readdirSync(prodDir).filter(f => f.endsWith('.js')).sort();
|
|
129
|
-
for (const file of prodFiles) {
|
|
130
|
-
const fullName = `prod/${file}`;
|
|
131
|
-
const record = await SeederRepository.find(fullName);
|
|
132
|
-
status.push({
|
|
133
|
-
name: fullName,
|
|
134
|
-
status: record ? 'Ran' : 'Pending',
|
|
135
|
-
executedAt: record?.executed_at || null
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (fs.existsSync(devDir)) {
|
|
141
|
-
const devFiles = fs.readdirSync(devDir).filter(f => f.endsWith('.js')).sort();
|
|
142
|
-
for (const file of devFiles) {
|
|
143
|
-
const fullName = `dev/${file}`;
|
|
144
|
-
const record = await SeederRepository.find(fullName);
|
|
145
|
-
status.push({
|
|
146
|
-
name: fullName,
|
|
147
|
-
status: record ? 'Ran' : 'Pending',
|
|
148
|
-
executedAt: record?.executed_at || null
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return status;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
static async printStatus() {
|
|
157
|
-
const status = await this.status();
|
|
158
|
-
|
|
159
|
-
if (status.length === 0) {
|
|
160
|
-
console.log(`${COLORS.yellow}⚠️ No seeders found.${COLORS.reset}`);
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
console.log(`\n${COLORS.bold}Seeder Status${COLORS.reset}\n`);
|
|
165
|
-
console.log(`${COLORS.dim}${'─'.repeat(80)}${COLORS.reset}`);
|
|
166
|
-
|
|
167
|
-
for (const seeder of status) {
|
|
168
|
-
const statusColor = seeder.status === 'Ran' ? COLORS.green : COLORS.yellow;
|
|
169
|
-
const statusIcon = seeder.status === 'Ran' ? '✅' : '⏳';
|
|
170
|
-
|
|
171
|
-
console.log(`${statusIcon} ${statusColor}${seeder.status.padEnd(7)}${COLORS.reset} ${seeder.name}`);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
console.log(`${COLORS.dim}${'─'.repeat(80)}${COLORS.reset}\n`);
|
|
175
|
-
|
|
176
|
-
const ran = status.filter(s => s.status === 'Ran').length;
|
|
177
|
-
const pending = status.filter(s => s.status === 'Pending').length;
|
|
178
|
-
console.log(`${COLORS.dim}Total: ${status.length} | Ran: ${ran} | Pending: ${pending}${COLORS.reset}\n`);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
79
|
}
|
|
182
80
|
|
|
183
81
|
export default SeederRunner;
|
package/lib/Http/Server.js
CHANGED
|
@@ -7,6 +7,7 @@ import fastifyStatic from "@fastify/static";
|
|
|
7
7
|
import fastifyCookie from "@fastify/cookie";
|
|
8
8
|
import fastifyHelmet from "@fastify/helmet";
|
|
9
9
|
import fastifyMultipart from "@fastify/multipart";
|
|
10
|
+
import fastifyFormbody from "@fastify/formbody";
|
|
10
11
|
import Paths from "../Core/Paths.js";
|
|
11
12
|
import Config from "../Core/Config.js";
|
|
12
13
|
import Environment from "../Core/Environment.js";
|
|
@@ -111,6 +112,8 @@ class Server {
|
|
|
111
112
|
},
|
|
112
113
|
attachFieldsToBody: this.#serverConfigs.web_server.multipart.attachFieldsToBody
|
|
113
114
|
});
|
|
115
|
+
|
|
116
|
+
this.#server.register(fastifyFormbody);
|
|
114
117
|
}
|
|
115
118
|
|
|
116
119
|
static #getCorsOrigin(requestOrigin, cb) {
|
package/lib/index.d.ts
CHANGED
|
@@ -187,24 +187,13 @@ export class Lang {
|
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
export class DateTime {
|
|
190
|
-
static
|
|
191
|
-
static
|
|
192
|
-
static
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
addMonths(months: number): DateTime;
|
|
198
|
-
addYears(years: number): DateTime;
|
|
199
|
-
subDays(days: number): DateTime;
|
|
200
|
-
subMonths(months: number): DateTime;
|
|
201
|
-
subYears(years: number): DateTime;
|
|
202
|
-
diffInDays(other: DateTime): number;
|
|
203
|
-
diffInMonths(other: DateTime): number;
|
|
204
|
-
diffInYears(other: DateTime): number;
|
|
205
|
-
isBefore(other: DateTime): boolean;
|
|
206
|
-
isAfter(other: DateTime): boolean;
|
|
207
|
-
isSame(other: DateTime): boolean;
|
|
190
|
+
static toSQL(timestamp?: number | null): string;
|
|
191
|
+
static getTime(sqlDateTime?: string | null): number;
|
|
192
|
+
static getDate(timestamp?: number | null, format?: string): string;
|
|
193
|
+
static addDays(days: number): string;
|
|
194
|
+
static addHours(hours: number): string;
|
|
195
|
+
static subDays(days: number): string;
|
|
196
|
+
static subHours(hours: number): string;
|
|
208
197
|
}
|
|
209
198
|
|
|
210
199
|
export class Str {
|
|
@@ -396,7 +385,6 @@ export class View {
|
|
|
396
385
|
export const MigrationRunner: any;
|
|
397
386
|
export const MigrationRepository: any;
|
|
398
387
|
export const SeederRunner: any;
|
|
399
|
-
export const SeederRepository: any;
|
|
400
388
|
export const Checksum: any;
|
|
401
389
|
export const DatabaseManager: any;
|
|
402
390
|
export const SessionManager: any;
|
package/lib/index.js
CHANGED
|
@@ -23,7 +23,6 @@ export { default as Checksum } from "./Database/Migration/Checksum.js";
|
|
|
23
23
|
|
|
24
24
|
// Seeder
|
|
25
25
|
export { default as SeederRunner } from "./Database/Seeder/SeederRunner.js";
|
|
26
|
-
export { default as SeederRepository } from "./Database/Seeder/SeederRepository.js";
|
|
27
26
|
|
|
28
27
|
// Authentication
|
|
29
28
|
export { default as Auth } from "./Auth/Manager.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nitronjs/framework",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "NitronJS is a modern and extensible Node.js MVC framework built on Fastify. It focuses on clean architecture, modular structure, and developer productivity, offering built-in routing, middleware, configuration management, CLI tooling, and native React integration for scalable full-stack applications.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"njs": "./cli/njs.js"
|
package/skeleton/package.json
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
"migrate:seed": "njs migrate --seed",
|
|
11
11
|
"migrate:fresh": "njs migrate:fresh",
|
|
12
12
|
"migrate:fresh:seed": "njs migrate:fresh --seed",
|
|
13
|
+
"migrate:status": "njs migrate:status",
|
|
14
|
+
"migrate:rollback": "njs migrate:rollback",
|
|
13
15
|
"seed": "njs seed",
|
|
14
16
|
"storage:link": "njs storage:link",
|
|
15
17
|
"make:controller": "njs make:controller",
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import Schema from "../../Schema/Manager.js";
|
|
2
|
-
|
|
3
|
-
class CreateSeedersTable {
|
|
4
|
-
|
|
5
|
-
static async up() {
|
|
6
|
-
await Schema.create("seeders", (table) => {
|
|
7
|
-
table.id();
|
|
8
|
-
table.string("name").unique();
|
|
9
|
-
table.string("checksum", 64);
|
|
10
|
-
table.timestamp("executed_at");
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
static async down() {
|
|
15
|
-
await Schema.dropIfExists("seeders");
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export default CreateSeedersTable;
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import DB from "../DB.js";
|
|
2
|
-
|
|
3
|
-
class SeederRepository {
|
|
4
|
-
|
|
5
|
-
static table = 'seeders';
|
|
6
|
-
|
|
7
|
-
static async tableExists() {
|
|
8
|
-
const [rows] = await DB.raw(`SHOW TABLES LIKE '${this.table}'`);
|
|
9
|
-
return rows.length > 0;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
static async getExecuted() {
|
|
13
|
-
if (!await this.tableExists()) return [];
|
|
14
|
-
return await DB.table(this.table).orderBy("id", "asc").get();
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
static async getExecutedNames() {
|
|
18
|
-
const seeders = await this.getExecuted();
|
|
19
|
-
return new Set(seeders.map(s => s.name));
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
static async log(name, checksum) {
|
|
23
|
-
await DB.table(this.table).insert({
|
|
24
|
-
name,
|
|
25
|
-
checksum,
|
|
26
|
-
executed_at: new Date()
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
static async find(name) {
|
|
31
|
-
return await DB.table(this.table).where("name", name).first();
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
static async exists(name) {
|
|
35
|
-
return (await this.find(name)) !== null;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
static async getChecksum(name) {
|
|
39
|
-
const seeder = await this.find(name);
|
|
40
|
-
return seeder?.checksum || null;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export default SeederRepository;
|