@nitronjs/framework 0.2.2 → 0.2.4
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 +3 -1
- package/cli/create.js +88 -72
- package/cli/njs.js +17 -19
- package/lib/Auth/Auth.js +167 -0
- package/lib/Build/CssBuilder.js +9 -0
- package/lib/Build/FileAnalyzer.js +16 -0
- package/lib/Build/HydrationBuilder.js +17 -0
- package/lib/Build/Manager.js +15 -0
- package/lib/Build/colors.js +4 -0
- package/lib/Build/plugins.js +84 -20
- package/lib/Console/Commands/DevCommand.js +13 -9
- package/lib/Console/Commands/MakeCommand.js +24 -10
- package/lib/Console/Commands/MigrateCommand.js +4 -3
- package/lib/Console/Commands/MigrateFreshCommand.js +22 -27
- package/lib/Console/Commands/MigrateRollbackCommand.js +8 -4
- package/lib/Console/Commands/MigrateStatusCommand.js +8 -4
- package/lib/Console/Commands/SeedCommand.js +8 -28
- package/lib/Console/Commands/StorageLinkCommand.js +20 -5
- package/lib/Console/Output.js +143 -0
- package/lib/Core/Config.js +2 -1
- package/lib/Core/Paths.js +8 -8
- package/lib/Database/DB.js +141 -51
- package/lib/Database/Drivers/MySQLDriver.js +102 -157
- package/lib/Database/Migration/Checksum.js +3 -8
- package/lib/Database/Migration/MigrationRepository.js +25 -35
- package/lib/Database/Migration/MigrationRunner.js +59 -67
- package/lib/Database/Model.js +165 -75
- package/lib/Database/QueryBuilder.js +43 -0
- package/lib/Database/QueryValidation.js +51 -30
- package/lib/Database/Schema/Blueprint.js +25 -36
- package/lib/Database/Schema/Manager.js +31 -68
- package/lib/Database/Seeder/SeederRunner.js +24 -145
- package/lib/Date/DateTime.js +9 -0
- package/lib/Encryption/Encryption.js +52 -0
- package/lib/Faker/Faker.js +11 -0
- package/lib/Filesystem/Storage.js +120 -0
- package/lib/HMR/Server.js +79 -9
- package/lib/Hashing/Hash.js +41 -0
- package/lib/Http/Server.js +179 -151
- package/lib/Logging/{Manager.js → Log.js} +68 -80
- package/lib/Mail/Mail.js +187 -0
- package/lib/Route/Router.js +416 -0
- package/lib/Session/File.js +135 -233
- package/lib/Session/Manager.js +117 -171
- package/lib/Session/Memory.js +28 -38
- package/lib/Session/Session.js +71 -107
- package/lib/Support/Str.js +103 -0
- package/lib/Translation/Lang.js +54 -0
- package/lib/View/Client/hmr-client.js +87 -51
- package/lib/View/Client/nitronjs-icon.png +0 -0
- package/lib/View/{Manager.js → View.js} +44 -29
- package/lib/index.d.ts +49 -27
- package/lib/index.js +19 -13
- package/package.json +1 -1
- package/skeleton/app/Controllers/HomeController.js +7 -1
- package/skeleton/package.json +2 -0
- package/skeleton/resources/css/global.css +1 -0
- package/skeleton/resources/views/Site/Home.tsx +456 -79
- package/skeleton/tsconfig.json +6 -1
- package/lib/Auth/Manager.js +0 -111
- package/lib/Database/Connection.js +0 -61
- package/lib/Database/Manager.js +0 -162
- 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/lib/Encryption/Manager.js +0 -47
- package/lib/Filesystem/Manager.js +0 -74
- package/lib/Hashing/Manager.js +0 -25
- package/lib/Mail/Manager.js +0 -120
- package/lib/Route/Loader.js +0 -80
- package/lib/Route/Manager.js +0 -286
- package/lib/Translation/Manager.js +0 -49
|
@@ -1,33 +1,37 @@
|
|
|
1
|
-
import mysql from
|
|
2
|
-
import Environment from
|
|
1
|
+
import mysql from "mysql2/promise";
|
|
2
|
+
import Environment from "../../Core/Environment.js";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* MySQL database driver using connection pooling.
|
|
6
|
+
* Handles query execution, transactions, and error management.
|
|
7
|
+
*/
|
|
4
8
|
class MySQLDriver {
|
|
5
9
|
#pool = null;
|
|
6
10
|
#config = null;
|
|
7
11
|
#credentials = null;
|
|
8
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Creates a new MySQL driver instance with connection pool.
|
|
15
|
+
* @param {Object} config - Database configuration from config/database.js
|
|
16
|
+
* @param {Object|null} credentials - Optional credential override for reconfiguration
|
|
17
|
+
*/
|
|
9
18
|
constructor(config, credentials = null) {
|
|
10
19
|
this.#config = config;
|
|
11
20
|
this.#credentials = credentials;
|
|
12
|
-
this.#createPool();
|
|
13
|
-
}
|
|
14
21
|
|
|
15
|
-
#createPool() {
|
|
16
22
|
const creds = this.#credentials;
|
|
17
23
|
|
|
18
24
|
this.#pool = mysql.createPool({
|
|
19
|
-
host: creds?.host ?? process.env.DATABASE_HOST ??
|
|
20
|
-
port: parseInt(creds?.port ?? process.env.DATABASE_PORT ??
|
|
21
|
-
user: creds?.username ?? process.env.DATABASE_USERNAME ??
|
|
22
|
-
password: creds?.password ?? process.env.DATABASE_PASSWORD ??
|
|
23
|
-
database: creds?.database ?? process.env.DATABASE_NAME ??
|
|
24
|
-
charset: this.#config.charset ||
|
|
25
|
-
|
|
25
|
+
host: creds?.host ?? process.env.DATABASE_HOST ?? "127.0.0.1",
|
|
26
|
+
port: parseInt(creds?.port ?? process.env.DATABASE_PORT ?? "3306"),
|
|
27
|
+
user: creds?.username ?? process.env.DATABASE_USERNAME ?? "root",
|
|
28
|
+
password: creds?.password ?? process.env.DATABASE_PASSWORD ?? "",
|
|
29
|
+
database: creds?.database ?? process.env.DATABASE_NAME ?? "test",
|
|
30
|
+
charset: this.#config.charset || "utf8mb4",
|
|
26
31
|
waitForConnections: true,
|
|
27
32
|
connectionLimit: this.#config.pool?.max ?? 10,
|
|
28
33
|
queueLimit: this.#config.pool?.queueLimit ?? 100,
|
|
29
34
|
connectTimeout: 10000,
|
|
30
|
-
|
|
31
35
|
enableKeepAlive: true,
|
|
32
36
|
keepAliveInitialDelay: 10000,
|
|
33
37
|
namedPlaceholders: true,
|
|
@@ -35,201 +39,142 @@ class MySQLDriver {
|
|
|
35
39
|
});
|
|
36
40
|
}
|
|
37
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Executes a prepared SQL query with bindings.
|
|
44
|
+
* @param {string} sql - SQL query string
|
|
45
|
+
* @param {Array} bindings - Parameter bindings
|
|
46
|
+
* @returns {Promise<Array>} Query result [rows, fields]
|
|
47
|
+
* @throws {Error} On query failure
|
|
48
|
+
*/
|
|
38
49
|
async query(sql, bindings = []) {
|
|
39
|
-
const
|
|
50
|
+
const connection = await this.#pool.getConnection();
|
|
40
51
|
|
|
41
52
|
try {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
try {
|
|
45
|
-
await connection.query('SET SESSION MAX_EXECUTION_TIME = 60000');
|
|
46
|
-
const [rows, fields] = await connection.execute(sql, bindings);
|
|
53
|
+
await connection.query("SET SESSION MAX_EXECUTION_TIME = 60000");
|
|
47
54
|
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
finally {
|
|
51
|
-
connection.release();
|
|
52
|
-
}
|
|
55
|
+
return await connection.execute(sql, bindings);
|
|
53
56
|
}
|
|
54
|
-
|
|
55
57
|
catch (error) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
sqlState: error.sqlState
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
const sanitized = new Error('Database query failed');
|
|
64
|
-
sanitized.code = error.code;
|
|
65
|
-
sanitized.driver = 'mysql';
|
|
66
|
-
throw sanitized;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
error.sql = sql;
|
|
70
|
-
error.bindings = this.#maskSensitiveData(bindings);
|
|
71
|
-
error.driver = 'mysql';
|
|
72
|
-
throw error;
|
|
58
|
+
throw this.#handleError(error, sql, bindings);
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
connection.release();
|
|
73
62
|
}
|
|
74
63
|
}
|
|
75
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Executes a raw SQL query without parameter binding.
|
|
67
|
+
* @param {string} sql - Raw SQL string
|
|
68
|
+
* @returns {Promise<Array>} Query result
|
|
69
|
+
*/
|
|
76
70
|
async raw(sql) {
|
|
77
|
-
const isProduction = Environment.isProd;
|
|
78
|
-
|
|
79
71
|
try {
|
|
80
|
-
|
|
81
|
-
return [rows, fields];
|
|
72
|
+
return await this.#pool.query(sql);
|
|
82
73
|
}
|
|
83
|
-
|
|
84
74
|
catch (error) {
|
|
85
|
-
|
|
86
|
-
console.error('[MySQLDriver] Raw query failed', {
|
|
87
|
-
code: error.code,
|
|
88
|
-
errno: error.errno,
|
|
89
|
-
sqlState: error.sqlState
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
const sanitized = new Error('Database query failed');
|
|
93
|
-
sanitized.code = error.code;
|
|
94
|
-
sanitized.driver = 'mysql';
|
|
95
|
-
throw sanitized;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
error.sql = sql;
|
|
99
|
-
error.driver = 'mysql';
|
|
100
|
-
throw error;
|
|
75
|
+
throw this.#handleError(error, sql);
|
|
101
76
|
}
|
|
102
77
|
}
|
|
103
78
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const sensitivePatterns = /password|token|secret|key|auth|bearer|jwt|otp|pin|credential/i;
|
|
115
|
-
const valuePreview = value.substring(0, 30).toLowerCase();
|
|
116
|
-
const looksLikeBase64 = /^[A-Za-z0-9+/]+=*$/.test(value) && value.length > 16;
|
|
117
|
-
|
|
118
|
-
if (sensitivePatterns.test(valuePreview) || value.length > 20 || looksLikeBase64) {
|
|
119
|
-
return '***MASKED***';
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return value;
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
async beginTransaction() {
|
|
79
|
+
/**
|
|
80
|
+
* Executes callback within a database transaction.
|
|
81
|
+
* Automatically commits on success or rolls back on failure.
|
|
82
|
+
* @param {Function} callback - Async function receiving connection
|
|
83
|
+
* @param {Object} options - Transaction options
|
|
84
|
+
* @param {AbortSignal} options.signal - Abort signal for timeout
|
|
85
|
+
* @returns {Promise<*>} Callback result
|
|
86
|
+
*/
|
|
87
|
+
async withTransaction(callback, options = {}) {
|
|
127
88
|
const connection = await this.#pool.getConnection();
|
|
128
|
-
await connection.query('SET SESSION MAX_EXECUTION_TIME = 60000');
|
|
129
89
|
|
|
90
|
+
await connection.query("SET SESSION MAX_EXECUTION_TIME = 60000");
|
|
130
91
|
await connection.beginTransaction();
|
|
131
92
|
|
|
132
|
-
return connection;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
async commit(connection) {
|
|
136
93
|
try {
|
|
137
|
-
|
|
138
|
-
|
|
94
|
+
if (options.signal?.aborted) {
|
|
95
|
+
throw new Error("Transaction aborted");
|
|
96
|
+
}
|
|
139
97
|
|
|
140
|
-
|
|
141
|
-
connection.release();
|
|
142
|
-
}
|
|
143
|
-
}
|
|
98
|
+
const result = await callback(connection);
|
|
144
99
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
100
|
+
if (options.signal?.aborted) {
|
|
101
|
+
await connection.rollback();
|
|
102
|
+
connection.release();
|
|
149
103
|
|
|
150
|
-
|
|
151
|
-
|
|
104
|
+
throw new Error("Transaction aborted");
|
|
105
|
+
}
|
|
152
106
|
|
|
153
|
-
|
|
107
|
+
await connection.commit();
|
|
154
108
|
connection.release();
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
109
|
|
|
158
|
-
|
|
159
|
-
try {
|
|
160
|
-
const [rows, fields] = await connection.execute(sql, bindings);
|
|
161
|
-
return [rows, fields];
|
|
110
|
+
return result;
|
|
162
111
|
}
|
|
163
|
-
|
|
164
112
|
catch (error) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if (isProduction) {
|
|
168
|
-
console.error('[MySQLDriver] Transaction query failed', {
|
|
169
|
-
code: error.code,
|
|
170
|
-
errno: error.errno,
|
|
171
|
-
sqlState: error.sqlState,
|
|
172
|
-
inTransaction: true
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
const sanitized = new Error('Database query failed');
|
|
176
|
-
sanitized.code = error.code;
|
|
177
|
-
sanitized.driver = 'mysql';
|
|
178
|
-
sanitized.inTransaction = true;
|
|
179
|
-
throw sanitized;
|
|
113
|
+
try {
|
|
114
|
+
await connection.rollback();
|
|
180
115
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
error.bindings = this.#maskSensitiveData(bindings);
|
|
184
|
-
error.driver = 'mysql';
|
|
185
|
-
error.inTransaction = true;
|
|
186
|
-
throw error;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
async withTransaction(callback, options = {}) {
|
|
191
|
-
const signal = options?.signal;
|
|
192
|
-
const connection = await this.beginTransaction();
|
|
193
|
-
|
|
194
|
-
try {
|
|
195
|
-
if (signal?.aborted) {
|
|
196
|
-
throw new Error('Transaction aborted');
|
|
116
|
+
catch {
|
|
117
|
+
// Ignore rollback errors
|
|
197
118
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if (signal?.aborted) {
|
|
201
|
-
await this.rollback(connection);
|
|
202
|
-
throw new Error('Transaction aborted');
|
|
119
|
+
finally {
|
|
120
|
+
connection.release();
|
|
203
121
|
}
|
|
204
122
|
|
|
205
|
-
await this.commit(connection);
|
|
206
|
-
|
|
207
|
-
return result;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
catch (error) {
|
|
211
|
-
await this.rollback(connection);
|
|
212
123
|
throw error;
|
|
213
124
|
}
|
|
214
125
|
}
|
|
215
126
|
|
|
127
|
+
/**
|
|
128
|
+
* Checks if database connection is healthy.
|
|
129
|
+
* @returns {Promise<boolean>} True if connection is alive
|
|
130
|
+
*/
|
|
216
131
|
async healthCheck() {
|
|
217
132
|
try {
|
|
218
|
-
await this.#pool.query(
|
|
133
|
+
await this.#pool.query("SELECT 1");
|
|
134
|
+
|
|
219
135
|
return true;
|
|
220
136
|
}
|
|
221
|
-
|
|
222
|
-
catch (error) {
|
|
137
|
+
catch {
|
|
223
138
|
return false;
|
|
224
139
|
}
|
|
225
140
|
}
|
|
226
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Closes connection pool and releases resources.
|
|
144
|
+
* @returns {Promise<void>}
|
|
145
|
+
*/
|
|
227
146
|
async close() {
|
|
228
147
|
if (this.#pool) {
|
|
229
148
|
await this.#pool.end();
|
|
230
149
|
this.#pool = null;
|
|
231
150
|
}
|
|
232
151
|
}
|
|
152
|
+
|
|
153
|
+
/** @private */
|
|
154
|
+
#handleError(error, sql, bindings = null) {
|
|
155
|
+
if (Environment.isProd) {
|
|
156
|
+
console.error("[MySQLDriver] Query failed", {
|
|
157
|
+
code: error.code,
|
|
158
|
+
errno: error.errno,
|
|
159
|
+
sqlState: error.sqlState
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const sanitized = new Error("Database query failed");
|
|
163
|
+
sanitized.code = error.code;
|
|
164
|
+
sanitized.driver = "mysql";
|
|
165
|
+
|
|
166
|
+
return sanitized;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
error.sql = sql;
|
|
170
|
+
error.driver = "mysql";
|
|
171
|
+
|
|
172
|
+
if (bindings) {
|
|
173
|
+
error.bindings = bindings;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return error;
|
|
177
|
+
}
|
|
233
178
|
}
|
|
234
179
|
|
|
235
180
|
export default MySQLDriver;
|
|
@@ -2,22 +2,17 @@ import { createHash } from 'crypto';
|
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
|
|
4
4
|
class Checksum {
|
|
5
|
-
|
|
6
5
|
static fromFile(filePath) {
|
|
7
|
-
|
|
8
|
-
return this.fromContent(content);
|
|
6
|
+
return this.fromContent(fs.readFileSync(filePath, 'utf8'));
|
|
9
7
|
}
|
|
10
8
|
|
|
11
9
|
static fromContent(content) {
|
|
12
|
-
|
|
13
|
-
return createHash('sha256').update(normalized, 'utf8').digest('hex');
|
|
10
|
+
return createHash('sha256').update(content.replace(/\r\n/g, '\n').trim(), 'utf8').digest('hex');
|
|
14
11
|
}
|
|
15
12
|
|
|
16
13
|
static verify(filePath, expectedChecksum) {
|
|
17
|
-
|
|
18
|
-
return actualChecksum === expectedChecksum;
|
|
14
|
+
return this.fromFile(filePath) === expectedChecksum;
|
|
19
15
|
}
|
|
20
|
-
|
|
21
16
|
}
|
|
22
17
|
|
|
23
18
|
export default Checksum;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import DB from "../DB.js";
|
|
2
2
|
|
|
3
3
|
class MigrationRepository {
|
|
4
|
-
|
|
5
4
|
static table = 'migrations';
|
|
6
5
|
|
|
6
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
7
|
+
// Public Methods
|
|
8
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
9
|
+
|
|
7
10
|
static async tableExists() {
|
|
8
11
|
const [rows] = await DB.rawQuery(`SHOW TABLES LIKE '${this.table}'`);
|
|
9
12
|
return rows.length > 0;
|
|
@@ -11,67 +14,46 @@ class MigrationRepository {
|
|
|
11
14
|
|
|
12
15
|
static async getExecuted() {
|
|
13
16
|
if (!await this.tableExists()) return [];
|
|
14
|
-
return await DB.table(this.table)
|
|
15
|
-
.orderBy("batch", "asc")
|
|
16
|
-
.orderBy("id", "asc")
|
|
17
|
-
.get();
|
|
17
|
+
return await DB.table(this.table).orderBy('batch', 'asc').orderBy('id', 'asc').get();
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
static async getExecutedNames() {
|
|
21
|
-
|
|
22
|
-
return new Set(migrations.map(m => m.name));
|
|
21
|
+
return new Set((await this.getExecuted()).map(m => m.name));
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
static async getNextBatchNumber() {
|
|
26
|
-
|
|
27
|
-
const result = await DB.table(this.table)
|
|
28
|
-
.select(DB.rawExpr("MAX(batch) as max_batch"))
|
|
29
|
-
.first();
|
|
30
|
-
return (result?.max_batch || 0) + 1;
|
|
25
|
+
return (await this.#getMaxBatch()) + 1;
|
|
31
26
|
}
|
|
32
27
|
|
|
33
28
|
static async getLastBatchNumber() {
|
|
34
|
-
|
|
35
|
-
const result = await DB.table(this.table)
|
|
36
|
-
.select(DB.rawExpr("MAX(batch) as max_batch"))
|
|
37
|
-
.first();
|
|
38
|
-
return result?.max_batch || 0;
|
|
29
|
+
return await this.#getMaxBatch();
|
|
39
30
|
}
|
|
40
31
|
|
|
41
32
|
static async getByBatch(batch) {
|
|
42
|
-
return await DB.table(this.table)
|
|
43
|
-
.where("batch", batch)
|
|
44
|
-
.orderBy("id", "desc")
|
|
45
|
-
.get();
|
|
33
|
+
return await DB.table(this.table).where('batch', batch).orderBy('id', 'desc').get();
|
|
46
34
|
}
|
|
47
35
|
|
|
48
36
|
static async getLastBatches(steps = 1) {
|
|
49
37
|
const lastBatch = await this.getLastBatchNumber();
|
|
50
38
|
if (lastBatch === 0) return [];
|
|
51
39
|
|
|
52
|
-
const minBatch = Math.max(1, lastBatch - steps + 1);
|
|
53
40
|
return await DB.table(this.table)
|
|
54
|
-
.where(
|
|
55
|
-
.orderBy(
|
|
56
|
-
.orderBy(
|
|
41
|
+
.where('batch', '>=', Math.max(1, lastBatch - steps + 1))
|
|
42
|
+
.orderBy('batch', 'desc')
|
|
43
|
+
.orderBy('id', 'desc')
|
|
57
44
|
.get();
|
|
58
45
|
}
|
|
59
46
|
|
|
60
47
|
static async log(name, batch, checksum) {
|
|
61
|
-
await DB.table(this.table).insert({
|
|
62
|
-
name,
|
|
63
|
-
batch,
|
|
64
|
-
checksum,
|
|
65
|
-
executed_at: new Date()
|
|
66
|
-
});
|
|
48
|
+
await DB.table(this.table).insert({ name, batch, checksum, executed_at: new Date() });
|
|
67
49
|
}
|
|
68
50
|
|
|
69
51
|
static async delete(name) {
|
|
70
|
-
await DB.table(this.table).where(
|
|
52
|
+
await DB.table(this.table).where('name', name).delete();
|
|
71
53
|
}
|
|
72
54
|
|
|
73
55
|
static async find(name) {
|
|
74
|
-
return await DB.table(this.table).where(
|
|
56
|
+
return await DB.table(this.table).where('name', name).first();
|
|
75
57
|
}
|
|
76
58
|
|
|
77
59
|
static async exists(name) {
|
|
@@ -79,10 +61,18 @@ class MigrationRepository {
|
|
|
79
61
|
}
|
|
80
62
|
|
|
81
63
|
static async getChecksum(name) {
|
|
82
|
-
|
|
83
|
-
return migration?.checksum || null;
|
|
64
|
+
return (await this.find(name))?.checksum || null;
|
|
84
65
|
}
|
|
85
66
|
|
|
67
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
68
|
+
// Private Methods
|
|
69
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
static async #getMaxBatch() {
|
|
72
|
+
if (!await this.tableExists()) return 0;
|
|
73
|
+
const result = await DB.table(this.table).select(DB.rawExpr('MAX(batch) as max_batch')).first();
|
|
74
|
+
return result?.max_batch || 0;
|
|
75
|
+
}
|
|
86
76
|
}
|
|
87
77
|
|
|
88
78
|
export default MigrationRepository;
|