millas 0.2.12-beta-1 → 0.2.13-beta
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/package.json +3 -2
- package/src/admin/ActivityLog.js +153 -52
- package/src/admin/Admin.js +516 -199
- package/src/admin/AdminAuth.js +213 -98
- package/src/admin/FormGenerator.js +372 -0
- package/src/admin/HookRegistry.js +256 -0
- package/src/admin/QueryEngine.js +263 -0
- package/src/admin/ViewContext.js +318 -0
- package/src/admin/WidgetRegistry.js +406 -0
- package/src/admin/index.js +17 -0
- package/src/admin/resources/AdminResource.js +393 -97
- package/src/admin/static/admin.css +1422 -0
- package/src/admin/static/date-picker.css +157 -0
- package/src/admin/static/date-picker.js +316 -0
- package/src/admin/static/json-editor.css +649 -0
- package/src/admin/static/json-editor.js +1429 -0
- package/src/admin/static/ui.js +1044 -0
- package/src/admin/views/layouts/base.njk +87 -1046
- package/src/admin/views/pages/detail.njk +56 -21
- package/src/admin/views/pages/error.njk +65 -0
- package/src/admin/views/pages/form.njk +47 -599
- package/src/admin/views/pages/list.njk +270 -62
- package/src/admin/views/partials/form-field.njk +53 -0
- package/src/admin/views/partials/form-footer.njk +28 -0
- package/src/admin/views/partials/form-readonly.njk +114 -0
- package/src/admin/views/partials/form-scripts.njk +480 -0
- package/src/admin/views/partials/form-widget.njk +297 -0
- package/src/admin/views/partials/icons.njk +64 -0
- package/src/admin/views/partials/json-dialog.njk +80 -0
- package/src/admin/views/partials/json-editor.njk +37 -0
- package/src/ai/AIManager.js +954 -0
- package/src/ai/AITokenBudget.js +250 -0
- package/src/ai/PromptGuard.js +216 -0
- package/src/ai/agents.js +218 -0
- package/src/ai/conversation.js +213 -0
- package/src/ai/drivers.js +734 -0
- package/src/ai/files.js +249 -0
- package/src/ai/media.js +303 -0
- package/src/ai/pricing.js +152 -0
- package/src/ai/provider_tools.js +114 -0
- package/src/ai/types.js +356 -0
- package/src/auth/Auth.js +18 -2
- package/src/auth/AuthUser.js +65 -44
- package/src/cli.js +3 -1
- package/src/commands/createsuperuser.js +267 -0
- package/src/commands/lang.js +589 -0
- package/src/commands/migrate.js +154 -81
- package/src/commands/serve.js +3 -4
- package/src/container/AppInitializer.js +101 -20
- package/src/container/Application.js +31 -1
- package/src/container/MillasApp.js +10 -3
- package/src/container/MillasConfig.js +35 -6
- package/src/core/admin.js +5 -0
- package/src/core/db.js +2 -1
- package/src/core/foundation.js +2 -10
- package/src/core/lang.js +1 -0
- package/src/errors/HttpError.js +32 -16
- package/src/facades/AI.js +411 -0
- package/src/facades/Hash.js +67 -0
- package/src/facades/Process.js +144 -0
- package/src/hashing/Hash.js +262 -0
- package/src/http/HtmlEscape.js +162 -0
- package/src/http/MillasRequest.js +63 -7
- package/src/http/MillasResponse.js +70 -4
- package/src/http/ResponseDispatcher.js +21 -27
- package/src/http/SafeFilePath.js +195 -0
- package/src/http/SafeRedirect.js +62 -0
- package/src/http/SecurityBootstrap.js +70 -0
- package/src/http/helpers.js +40 -125
- package/src/http/index.js +10 -1
- package/src/http/middleware/CsrfMiddleware.js +258 -0
- package/src/http/middleware/RateLimiter.js +314 -0
- package/src/http/middleware/SecurityHeaders.js +281 -0
- package/src/i18n/I18nServiceProvider.js +91 -0
- package/src/i18n/Translator.js +643 -0
- package/src/i18n/defaults.js +122 -0
- package/src/i18n/index.js +164 -0
- package/src/i18n/locales/en.js +55 -0
- package/src/i18n/locales/sw.js +48 -0
- package/src/logger/LogRedactor.js +247 -0
- package/src/logger/Logger.js +1 -1
- package/src/logger/formatters/JsonFormatter.js +11 -4
- package/src/logger/formatters/PrettyFormatter.js +103 -65
- package/src/logger/formatters/SimpleFormatter.js +14 -3
- package/src/middleware/ThrottleMiddleware.js +27 -4
- package/src/migrations/system/0001_users.js +21 -0
- package/src/migrations/system/0002_admin_log.js +25 -0
- package/src/migrations/system/0003_sessions.js +23 -0
- package/src/orm/fields/index.js +210 -188
- package/src/orm/migration/DefaultValueParser.js +325 -0
- package/src/orm/migration/InteractiveResolver.js +191 -0
- package/src/orm/migration/Makemigrations.js +312 -0
- package/src/orm/migration/MigrationGraph.js +227 -0
- package/src/orm/migration/MigrationRunner.js +202 -108
- package/src/orm/migration/MigrationWriter.js +463 -0
- package/src/orm/migration/ModelInspector.js +143 -74
- package/src/orm/migration/ModelScanner.js +225 -0
- package/src/orm/migration/ProjectState.js +213 -0
- package/src/orm/migration/RenameDetector.js +175 -0
- package/src/orm/migration/SchemaBuilder.js +8 -81
- package/src/orm/migration/operations/base.js +57 -0
- package/src/orm/migration/operations/column.js +191 -0
- package/src/orm/migration/operations/fields.js +252 -0
- package/src/orm/migration/operations/index.js +55 -0
- package/src/orm/migration/operations/models.js +152 -0
- package/src/orm/migration/operations/registry.js +131 -0
- package/src/orm/migration/operations/special.js +51 -0
- package/src/orm/migration/utils.js +208 -0
- package/src/orm/model/Model.js +81 -13
- package/src/process/Process.js +333 -0
- package/src/providers/AdminServiceProvider.js +66 -9
- package/src/providers/AuthServiceProvider.js +40 -5
- package/src/providers/CacheStorageServiceProvider.js +2 -2
- package/src/providers/DatabaseServiceProvider.js +3 -2
- package/src/providers/LogServiceProvider.js +4 -1
- package/src/providers/MailServiceProvider.js +1 -1
- package/src/providers/QueueServiceProvider.js +1 -1
- package/src/router/MiddlewareRegistry.js +27 -2
- package/src/scaffold/templates.js +80 -21
- package/src/validation/Validator.js +348 -607
|
@@ -1,146 +1,263 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const fs = require('fs-extra');
|
|
4
3
|
const path = require('path');
|
|
4
|
+
const MigrationGraph = require('./MigrationGraph');
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* MigrationRunner
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
* - run pending migrations (migrate)
|
|
11
|
-
* - rollback last batch (migrate:rollback)
|
|
12
|
-
* - show status table (migrate:status)
|
|
13
|
-
* - drop all + re-run (migrate:fresh)
|
|
14
|
-
* - rollback all (migrate:reset)
|
|
15
|
-
* - rollback all + re-run (migrate:refresh)
|
|
9
|
+
* Implements `millas migrate` and related commands.
|
|
16
10
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
11
|
+
* Critical separation:
|
|
12
|
+
* - NEVER generates migrations
|
|
13
|
+
* - NEVER reads model files
|
|
14
|
+
* - Only applies existing migration files to the database
|
|
15
|
+
*
|
|
16
|
+
* Tracking table: millas_migrations
|
|
17
|
+
* - app_name (source: 'system' | 'app')
|
|
18
|
+
* - name (migration name without .js)
|
|
19
|
+
* - applied_at (timestamp)
|
|
20
|
+
* - batch (integer, for rollback grouping)
|
|
21
|
+
*
|
|
22
|
+
* Execution order: topological sort of the DAG — dependencies always run first.
|
|
19
23
|
*/
|
|
20
24
|
class MigrationRunner {
|
|
21
25
|
/**
|
|
22
|
-
* @param {object}
|
|
23
|
-
* @param {string}
|
|
26
|
+
* @param {object} db — live knex connection
|
|
27
|
+
* @param {string} appMigPath — abs path to database/migrations/
|
|
28
|
+
* @param {string} systemMigPath — abs path to millas/src/migrations/system/
|
|
24
29
|
*/
|
|
25
|
-
constructor(
|
|
26
|
-
this._db
|
|
27
|
-
this.
|
|
30
|
+
constructor(db, appMigPath, systemMigPath) {
|
|
31
|
+
this._db = db;
|
|
32
|
+
this._appMigPath = appMigPath;
|
|
33
|
+
this._systemMigPath = systemMigPath || path.join(__dirname, '../../../migrations/system');
|
|
28
34
|
}
|
|
29
35
|
|
|
30
|
-
// ─── Public commands
|
|
36
|
+
// ─── Public commands ───────────────────────────────────────────────────────
|
|
31
37
|
|
|
32
|
-
/** Run all pending migrations. */
|
|
33
38
|
async migrate() {
|
|
34
39
|
await this._ensureTable();
|
|
35
|
-
const
|
|
40
|
+
const graph = this._buildGraph();
|
|
41
|
+
const applied = await this._appliedSet();
|
|
42
|
+
const pending = graph.topoSort().filter(n => !applied.has(n.key));
|
|
36
43
|
|
|
37
|
-
if (pending.length === 0) {
|
|
38
|
-
return { ran: [], message: 'Nothing to migrate.' };
|
|
39
|
-
}
|
|
44
|
+
if (pending.length === 0) return { ran: [], message: 'Nothing to migrate.' };
|
|
40
45
|
|
|
41
46
|
const batch = await this._nextBatch();
|
|
42
47
|
const ran = [];
|
|
43
48
|
|
|
44
|
-
for (const
|
|
45
|
-
|
|
46
|
-
await
|
|
47
|
-
|
|
48
|
-
ran.push(file);
|
|
49
|
+
for (const node of pending) {
|
|
50
|
+
await this._applyNode(node);
|
|
51
|
+
await this._record(node, batch);
|
|
52
|
+
ran.push({ label: node.key, source: node.source, name: node.name });
|
|
49
53
|
}
|
|
50
54
|
|
|
51
55
|
return { ran, batch, message: `Ran ${ran.length} migration(s).` };
|
|
52
56
|
}
|
|
53
57
|
|
|
54
|
-
/** Rollback the last batch of migrations. */
|
|
55
58
|
async rollback(steps = 1) {
|
|
56
59
|
await this._ensureTable();
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
60
|
+
const rows = await this._lastBatchRows(steps);
|
|
61
|
+
if (rows.length === 0) return { rolledBack: [], message: 'Nothing to rollback.' };
|
|
62
|
+
|
|
63
|
+
const graph = this._buildGraph();
|
|
64
|
+
const rolledBack = [];
|
|
65
|
+
|
|
66
|
+
// Reverse the topo order for rollback
|
|
67
|
+
const topoKeys = graph.topoSort().map(n => n.key);
|
|
68
|
+
rows.sort((a, b) => topoKeys.indexOf(`${b.app_name}:${b.name}`) - topoKeys.indexOf(`${a.app_name}:${a.name}`));
|
|
69
|
+
|
|
70
|
+
for (const row of rows) {
|
|
71
|
+
const key = `${row.app_name}:${row.name}`;
|
|
72
|
+
const node = graph.get(key);
|
|
73
|
+
if (!node) {
|
|
74
|
+
process.stderr.write(` ⚠ Migration "${key}" not found — skipping rollback\n`);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
await this._revertNode(node);
|
|
78
|
+
await this._db('millas_migrations')
|
|
79
|
+
.where('app_name', row.app_name)
|
|
80
|
+
.where('name', row.name)
|
|
81
|
+
.delete();
|
|
82
|
+
rolledBack.push({ label: key, source: node.source, name: node.name });
|
|
70
83
|
}
|
|
71
84
|
|
|
72
85
|
return { rolledBack, message: `Rolled back ${rolledBack.length} migration(s).` };
|
|
73
86
|
}
|
|
74
87
|
|
|
75
|
-
/** Drop all tables and re-run every migration. */
|
|
76
88
|
async fresh() {
|
|
77
89
|
await this._dropAllTables();
|
|
78
90
|
await this._ensureTable();
|
|
79
91
|
return this.migrate();
|
|
80
92
|
}
|
|
81
93
|
|
|
82
|
-
/** Rollback ALL migrations. */
|
|
83
94
|
async reset() {
|
|
84
95
|
await this._ensureTable();
|
|
85
|
-
const all = await this._db('millas_migrations').orderBy('id', 'desc');
|
|
86
|
-
|
|
87
|
-
if (all.length === 0) {
|
|
88
|
-
return { rolledBack: [], message: 'Nothing to reset.' };
|
|
89
|
-
}
|
|
96
|
+
const all = await this._db('millas_migrations').select('*').orderBy('id', 'desc');
|
|
97
|
+
if (all.length === 0) return { rolledBack: [], message: 'Nothing to reset.' };
|
|
90
98
|
|
|
99
|
+
const graph = this._buildGraph();
|
|
91
100
|
const rolledBack = [];
|
|
101
|
+
|
|
92
102
|
for (const row of all) {
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
103
|
+
const key = `${row.app_name}:${row.name}`;
|
|
104
|
+
const node = graph.get(key);
|
|
105
|
+
if (node) {
|
|
106
|
+
try { await this._revertNode(node); } catch { /* already gone */ }
|
|
107
|
+
}
|
|
108
|
+
await this._db('millas_migrations')
|
|
109
|
+
.where('app_name', row.app_name).where('name', row.name).delete();
|
|
110
|
+
rolledBack.push({ label: key });
|
|
97
111
|
}
|
|
98
112
|
|
|
99
113
|
return { rolledBack, message: `Reset ${rolledBack.length} migration(s).` };
|
|
100
114
|
}
|
|
101
115
|
|
|
102
|
-
/** Rollback all then re-run all. */
|
|
103
116
|
async refresh() {
|
|
104
117
|
await this.reset();
|
|
105
118
|
return this.migrate();
|
|
106
119
|
}
|
|
107
120
|
|
|
108
|
-
/** Return status of all migration files. */
|
|
109
121
|
async status() {
|
|
110
122
|
await this._ensureTable();
|
|
111
|
-
const files = this._files();
|
|
112
|
-
const ran = await this._ranNames();
|
|
113
123
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
124
|
+
const graph = this._buildGraph();
|
|
125
|
+
const applied = await this._appliedMap();
|
|
126
|
+
const rows = [];
|
|
127
|
+
|
|
128
|
+
for (const node of graph.topoSort()) {
|
|
129
|
+
const rec = applied.get(node.key);
|
|
130
|
+
rows.push({
|
|
131
|
+
key: node.key,
|
|
132
|
+
source: node.source,
|
|
133
|
+
name: node.name,
|
|
134
|
+
status: rec ? 'Applied' : 'Pending',
|
|
135
|
+
batch: rec?.batch ?? null,
|
|
136
|
+
appliedAt: rec?.applied_at ?? null,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return rows;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Mark a migration as applied without running it (--fake).
|
|
145
|
+
*/
|
|
146
|
+
async fake(source, name) {
|
|
147
|
+
await this._ensureTable();
|
|
148
|
+
const key = `${source}:${name}`;
|
|
149
|
+
const already = await this._db('millas_migrations')
|
|
150
|
+
.where('app_name', source).where('name', name).first();
|
|
151
|
+
if (already) throw new Error(`Migration "${key}" is already applied.`);
|
|
152
|
+
const batch = await this._nextBatch();
|
|
153
|
+
await this._record({ source, name }, batch);
|
|
154
|
+
return { key };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Show which migrations WOULD run (plan preview, no DB changes).
|
|
159
|
+
*/
|
|
160
|
+
async plan() {
|
|
161
|
+
await this._ensureTable();
|
|
162
|
+
const graph = this._buildGraph();
|
|
163
|
+
const applied = await this._appliedSet();
|
|
164
|
+
return graph.topoSort()
|
|
165
|
+
.filter(n => !applied.has(n.key))
|
|
166
|
+
.map(n => ({ key: n.key, source: n.source, name: n.name }));
|
|
119
167
|
}
|
|
120
168
|
|
|
121
169
|
// ─── Internal ─────────────────────────────────────────────────────────────
|
|
122
170
|
|
|
171
|
+
_buildGraph() {
|
|
172
|
+
const graph = new MigrationGraph()
|
|
173
|
+
.addSource('system', this._systemMigPath)
|
|
174
|
+
.addSource('app', this._appMigPath);
|
|
175
|
+
graph.loadAll();
|
|
176
|
+
return graph;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async _applyNode(node) {
|
|
180
|
+
if (node.legacy) {
|
|
181
|
+
// Legacy up/down migration — no operations array, just call up()
|
|
182
|
+
await node.raw.up(this._db);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const ops = node.operations || [];
|
|
187
|
+
|
|
188
|
+
// ── FK-safe two-phase execution ───────────────────────────────────────────
|
|
189
|
+
//
|
|
190
|
+
// When a migration contains multiple CreateModel ops, running them one by
|
|
191
|
+
// one with inline FK constraints fails whenever a table references another
|
|
192
|
+
// table that appears later in the op list (or in a circular relationship).
|
|
193
|
+
//
|
|
194
|
+
// Strategy:
|
|
195
|
+
// Phase 1 — run all CreateModel ops without FK constraints
|
|
196
|
+
// (plain integer columns only)
|
|
197
|
+
// Phase 2 — attach all FK constraints in a single alterTable per table,
|
|
198
|
+
// now that every referenced table is guaranteed to exist
|
|
199
|
+
// Phase 3 — run all remaining ops (AddField, AlterField, etc.) normally
|
|
200
|
+
//
|
|
201
|
+
// This costs exactly the same number of DB round-trips as the naive approach
|
|
202
|
+
// for the common case (one CreateModel per migration = one CREATE TABLE +
|
|
203
|
+
// one ALTER TABLE if it has FKs, vs one CREATE TABLE inline). For migrations
|
|
204
|
+
// with multiple CreateModel ops it is strictly cheaper than per-op ALTER TABLE
|
|
205
|
+
// calls because FK constraints are batched per table in phase 2.
|
|
206
|
+
|
|
207
|
+
const createOps = ops.filter(op => op.type === 'CreateModel');
|
|
208
|
+
const otherOps = ops.filter(op => op.type !== 'CreateModel');
|
|
209
|
+
|
|
210
|
+
// Phase 1: create all tables, FK columns as plain integers
|
|
211
|
+
for (const op of createOps) {
|
|
212
|
+
await op.upWithoutFKs(this._db);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Phase 2: attach all FK constraints — one alterTable per table
|
|
216
|
+
for (const op of createOps) {
|
|
217
|
+
await op.applyFKConstraints(this._db);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Phase 3: remaining ops (AddField, RemoveField, AlterField, RunSQL, etc.)
|
|
221
|
+
for (const op of otherOps) {
|
|
222
|
+
await op.up(this._db);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async _revertNode(node) {
|
|
227
|
+
if (node.legacy) {
|
|
228
|
+
await node.raw.down(this._db);
|
|
229
|
+
} else {
|
|
230
|
+
const ops = [...(node.operations || [])].reverse();
|
|
231
|
+
for (const op of ops) {
|
|
232
|
+
await op.down(this._db);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
123
237
|
async _ensureTable() {
|
|
124
238
|
const exists = await this._db.schema.hasTable('millas_migrations');
|
|
125
239
|
if (exists) return;
|
|
126
240
|
|
|
127
241
|
await this._db.schema.createTable('millas_migrations', (t) => {
|
|
128
242
|
t.increments('id');
|
|
129
|
-
t.string('
|
|
243
|
+
t.string('app_name', 50).notNullable();
|
|
244
|
+
t.string('name', 200).notNullable();
|
|
130
245
|
t.integer('batch').notNullable();
|
|
131
|
-
t.timestamp('
|
|
246
|
+
t.timestamp('applied_at').defaultTo(this._db.fn.now());
|
|
247
|
+
t.unique(['app_name', 'name']);
|
|
132
248
|
});
|
|
133
249
|
}
|
|
134
250
|
|
|
135
|
-
async
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
return files.filter(f => !ran.has(f));
|
|
251
|
+
async _appliedSet() {
|
|
252
|
+
const rows = await this._db('millas_migrations').select('app_name', 'name');
|
|
253
|
+
return new Set(rows.map(r => `${r.app_name}:${r.name}`));
|
|
139
254
|
}
|
|
140
255
|
|
|
141
|
-
async
|
|
142
|
-
const rows = await this._db('millas_migrations').select('
|
|
143
|
-
|
|
256
|
+
async _appliedMap() {
|
|
257
|
+
const rows = await this._db('millas_migrations').select('*');
|
|
258
|
+
const map = new Map();
|
|
259
|
+
for (const r of rows) map.set(`${r.app_name}:${r.name}`, r);
|
|
260
|
+
return map;
|
|
144
261
|
}
|
|
145
262
|
|
|
146
263
|
async _nextBatch() {
|
|
@@ -148,59 +265,36 @@ class MigrationRunner {
|
|
|
148
265
|
return (result?.max || 0) + 1;
|
|
149
266
|
}
|
|
150
267
|
|
|
151
|
-
async
|
|
152
|
-
const
|
|
153
|
-
if (!
|
|
268
|
+
async _lastBatchRows(steps) {
|
|
269
|
+
const result = await this._db('millas_migrations').max('batch as max').first();
|
|
270
|
+
if (!result?.max) return [];
|
|
271
|
+
const fromBatch = result.max - steps + 1;
|
|
272
|
+
return this._db('millas_migrations')
|
|
273
|
+
.where('batch', '>=', fromBatch)
|
|
274
|
+
.orderBy('id', 'desc');
|
|
275
|
+
}
|
|
154
276
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
277
|
+
async _record(node, batch) {
|
|
278
|
+
await this._db('millas_migrations').insert({
|
|
279
|
+
app_name: node.source,
|
|
280
|
+
name: node.name,
|
|
281
|
+
batch,
|
|
282
|
+
applied_at: new Date().toISOString(),
|
|
283
|
+
});
|
|
158
284
|
}
|
|
159
285
|
|
|
160
|
-
/**
|
|
161
|
-
* Drop all user tables — dialect-aware.
|
|
162
|
-
* Resolves the knex client name and delegates to the right helper.
|
|
163
|
-
*/
|
|
164
286
|
async _dropAllTables() {
|
|
165
287
|
const clientName = this._db.client.config.client || 'sqlite3';
|
|
166
|
-
|
|
167
288
|
let dialect;
|
|
168
289
|
if (clientName.includes('pg') || clientName.includes('postgres')) {
|
|
169
290
|
dialect = require('./dialects/postgres');
|
|
170
291
|
} else if (clientName.includes('mysql') || clientName.includes('maria')) {
|
|
171
292
|
dialect = require('./dialects/mysql');
|
|
172
293
|
} else {
|
|
173
|
-
// Default: sqlite / sqlite3
|
|
174
294
|
dialect = require('./dialects/sqlite');
|
|
175
295
|
}
|
|
176
|
-
|
|
177
296
|
await dialect.dropAllTables(this._db);
|
|
178
297
|
}
|
|
179
|
-
|
|
180
|
-
_files() {
|
|
181
|
-
if (!fs.existsSync(this._path)) return [];
|
|
182
|
-
return fs.readdirSync(this._path)
|
|
183
|
-
.filter(f => f.endsWith('.js') && !f.startsWith('.'))
|
|
184
|
-
.sort();
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
_load(name) {
|
|
188
|
-
const filePath = path.join(this._path, name);
|
|
189
|
-
delete require.cache[require.resolve(filePath)];
|
|
190
|
-
const migration = require(filePath);
|
|
191
|
-
if (typeof migration.up !== 'function' || typeof migration.down !== 'function') {
|
|
192
|
-
throw new Error(`Migration "${name}" must export { up(db), down(db) }`);
|
|
193
|
-
}
|
|
194
|
-
return migration;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
async _record(name, batch) {
|
|
198
|
-
await this._db('millas_migrations').insert({
|
|
199
|
-
name,
|
|
200
|
-
batch,
|
|
201
|
-
ran_at: new Date().toISOString(),
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
298
|
}
|
|
205
299
|
|
|
206
|
-
module.exports = MigrationRunner;
|
|
300
|
+
module.exports = MigrationRunner;
|