mysqlsyncr 1.0.4 → 1.0.7
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 +7 -2
- package/src/apply.js +16 -11
- package/src/cli.js +3 -2
- package/src/differences.js +45 -22
- package/src/dump.js +37 -1
- package/src/index.js +58 -40
- package/src/parsers.js +2 -1
- package/src/socketServer.js +96 -0
- package/ui/.editorconfig +7 -0
- package/ui/.prettierrc.json +6 -0
- package/ui/README.md +40 -0
- package/ui/dist/spa/assets/KFOkCnqEu92Fr1MmgVxIIzQ-Y2mpUUkw.woff +0 -0
- package/ui/dist/spa/assets/KFOlCnqEu92Fr1MmEU9fBBc--ViM7Ag4Q.woff +0 -0
- package/ui/dist/spa/assets/KFOlCnqEu92Fr1MmSU5fBBc--CrFzpgl6.woff +0 -0
- package/ui/dist/spa/assets/KFOlCnqEu92Fr1MmWUlfBBc--CW3_XjG_.woff +0 -0
- package/ui/dist/spa/assets/KFOlCnqEu92Fr1MmYUtfBBc--Dr8fnyGc.woff +0 -0
- package/ui/dist/spa/assets/KFOmCnqEu92Fr1Mu4mxM-DAYh6T4l.woff +0 -0
- package/ui/dist/spa/assets/MainLayout-C27l9N4F.js +1 -0
- package/ui/dist/spa/assets/axios-Y2H2Igoj.js +1 -0
- package/ui/dist/spa/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNa-Dr0goTwe.woff +0 -0
- package/ui/dist/spa/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ-D-x-0Q06.woff2 +0 -0
- package/ui/dist/spa/assets/index-DKK0oGss.js +30 -0
- package/ui/dist/spa/assets/index-DME6fUxJ.css +5 -0
- package/ui/dist/spa/favicon.ico +0 -0
- package/ui/dist/spa/icons/favicon-128x128.png +0 -0
- package/ui/dist/spa/icons/favicon-16x16.png +0 -0
- package/ui/dist/spa/icons/favicon-32x32.png +0 -0
- package/ui/dist/spa/icons/favicon-96x96.png +0 -0
- package/ui/dist/spa/index.html +3 -0
- package/ui/eslint.config.js +73 -0
- package/ui/index.html +21 -0
- package/ui/jsconfig.json +3 -0
- package/ui/package.json +42 -0
- package/ui/postcss.config.js +29 -0
- package/ui/public/favicon.ico +0 -0
- package/ui/public/icons/favicon-128x128.png +0 -0
- package/ui/public/icons/favicon-16x16.png +0 -0
- package/ui/public/icons/favicon-32x32.png +0 -0
- package/ui/public/icons/favicon-96x96.png +0 -0
- package/ui/quasar.config.js +215 -0
- package/ui/src/App.vue +7 -0
- package/ui/src/assets/quasar-logo-vertical.svg +15 -0
- package/ui/src/boot/.gitkeep +0 -0
- package/ui/src/boot/axios.js +24 -0
- package/ui/src/components/EssentialLink.vue +44 -0
- package/ui/src/css/app.scss +1 -0
- package/ui/src/css/quasar.variables.scss +25 -0
- package/ui/src/layouts/MainLayout.vue +13 -0
- package/ui/src/pages/ApplyChanges.vue +46 -0
- package/ui/src/pages/CompareDatabase.vue +57 -0
- package/ui/src/pages/DatabaseManager.vue +42 -0
- package/ui/src/pages/DumpDatabase.vue +41 -0
- package/ui/src/pages/ErrorNotFound.vue +27 -0
- package/ui/src/pages/MainComponent copy.vue +322 -0
- package/ui/src/pages/MainComponent.vue +894 -0
- package/ui/src/router/index.js +30 -0
- package/ui/src/router/routes.js +20 -0
- package/ui/src/stores/example-store.js +21 -0
- package/ui/src/stores/index.js +20 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mysqlsyncr",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -15,7 +15,10 @@
|
|
|
15
15
|
"author": "",
|
|
16
16
|
"license": "ISC",
|
|
17
17
|
"dependencies": {
|
|
18
|
+
"cors": "^2.8.5",
|
|
19
|
+
"express": "^4.21.2",
|
|
18
20
|
"mysql2": "^3.12.0",
|
|
21
|
+
"open": "^10.1.0",
|
|
19
22
|
"yargs": "^17.7.2"
|
|
20
23
|
},
|
|
21
24
|
"devDependencies": {
|
|
@@ -24,7 +27,9 @@
|
|
|
24
27
|
"jest": "^29.0.0"
|
|
25
28
|
},
|
|
26
29
|
"babel": {
|
|
27
|
-
"presets": [
|
|
30
|
+
"presets": [
|
|
31
|
+
"@babel/preset-env"
|
|
32
|
+
]
|
|
28
33
|
},
|
|
29
34
|
"jest": {
|
|
30
35
|
"transform": {
|
package/src/apply.js
CHANGED
|
@@ -19,17 +19,17 @@ export const applyDifferences = async (connection, database, differences) => {
|
|
|
19
19
|
await connection.query(dropTableSQL);
|
|
20
20
|
|
|
21
21
|
} else if (diff.type === 'missing_field') {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
: diff.field.Default !== null
|
|
26
|
-
? `DEFAULT '${diff.field.Default}'`
|
|
27
|
-
: '';
|
|
28
|
-
const addFieldSQL = `ALTER TABLE \`${diff.tableName}\` ADD \`${diff.field.Field}\` ${diff.field.Type} ${diff.field.Null === 'NO' ? 'NOT NULL' : ''
|
|
29
|
-
} ${defaultValue} ${diff.field.Extra || ''};`;
|
|
30
|
-
logger(`Executing: ${addFieldSQL}`);
|
|
22
|
+
// Step 1: Add column with DEFAULT NULL to handle existing records
|
|
23
|
+
const addFieldSQL = `ALTER TABLE \`${diff.tableName}\` ADD \`${diff.field.Field}\` ${diff.field.Type} DEFAULT NULL;`;
|
|
24
|
+
console.log(`Executing: ${addFieldSQL}`);
|
|
31
25
|
await connection.query(addFieldSQL);
|
|
32
26
|
|
|
27
|
+
// Step 2: If NOT NULL is required and no default was specified, modify the column to remove DEFAULT NULL
|
|
28
|
+
if (diff.field.NotNull && !diff.field.Default) {
|
|
29
|
+
const modifyFieldSQL = `ALTER TABLE \`${diff.tableName}\` MODIFY \`${diff.field.Field}\` ${diff.field.Type} NOT NULL;`;
|
|
30
|
+
console.log(`Executing: ${modifyFieldSQL}`);
|
|
31
|
+
await connection.query(modifyFieldSQL);
|
|
32
|
+
}
|
|
33
33
|
|
|
34
34
|
} else if (diff.type === 'extra_field') {
|
|
35
35
|
const dropFieldSQL = `ALTER TABLE \`${diff.tableName}\` DROP COLUMN \`${diff.field.Field}\`;`;
|
|
@@ -38,8 +38,13 @@ export const applyDifferences = async (connection, database, differences) => {
|
|
|
38
38
|
|
|
39
39
|
} else if (diff.type === 'mismatched_field') {
|
|
40
40
|
const { tableName, field } = diff;
|
|
41
|
-
const
|
|
42
|
-
|
|
41
|
+
const defaultClause = field.Default === 'CURRENT_TIMESTAMP'
|
|
42
|
+
? 'DEFAULT CURRENT_TIMESTAMP'
|
|
43
|
+
: field.Default !== undefined && field.Default !== null
|
|
44
|
+
? `DEFAULT '${field.Default}'`
|
|
45
|
+
: '';
|
|
46
|
+
|
|
47
|
+
const dropFieldSQL = `ALTER TABLE \`${tableName}\` CHANGE \`${field.Field}\` \`${field.Field}\` ${field.Type} ${field.NotNull ? 'NOT NULL' : ''} ${defaultClause};`;
|
|
43
48
|
logger(`Executing: ${dropFieldSQL}`);
|
|
44
49
|
await connection.query(dropFieldSQL);
|
|
45
50
|
|
package/src/cli.js
CHANGED
|
@@ -7,14 +7,15 @@ export const cli = () => {
|
|
|
7
7
|
.command('dump', 'Dump the database structure to files')
|
|
8
8
|
.command('compare', 'Compare the database structure with JSON dumps (dry run)')
|
|
9
9
|
.command('update', 'Update the database structure based on JSON dumps')
|
|
10
|
+
.command('ui', 'Open the UI')
|
|
10
11
|
.option('host', { alias: 'h', describe: 'Database host', default: 'localhost' })
|
|
11
12
|
.option('port', { alias: 'P', describe: 'Database port', default: 3306 })
|
|
12
13
|
.option('user', { alias: 'u', describe: 'Database user', default: 'root' })
|
|
13
|
-
.option('password', { alias: 'p', describe: 'Database password',
|
|
14
|
+
.option('password', { alias: 'p', describe: 'Database password', default: '' })
|
|
14
15
|
.option('database', { alias: 'd', describe: 'Target database', default: null })
|
|
15
16
|
.option('output', { alias: 'o', describe: 'Output directory for dumping. default is one level up from the current directory in a new folder called db-dump', default: '../db-dump' })
|
|
16
17
|
.option('verbose', { alias: 'v', describe: 'Enable verbose logging', type: 'boolean', default: false })
|
|
17
|
-
.demandCommand(1)
|
|
18
|
+
// .demandCommand(1)
|
|
18
19
|
.help()
|
|
19
20
|
.argv;
|
|
20
21
|
|
package/src/differences.js
CHANGED
|
@@ -58,14 +58,19 @@ export const findDifferences = (expected, current) => {
|
|
|
58
58
|
logger('Verifying Type Null and Default for table', tableName);
|
|
59
59
|
for (const field of columns) {
|
|
60
60
|
const currentField = current.tables[tableName].columns.find(f => f.Field === field.Field);
|
|
61
|
+
if (!currentField) {
|
|
62
|
+
console.error('Current field not found for', field.Field, 'in table', tableName);
|
|
63
|
+
differences.push({ type: 'missing_field', tableName, field });
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
61
66
|
if (field.Type !== currentField.Type) {
|
|
62
|
-
differences.push({ type: 'mismatched_field', tableName, field });
|
|
67
|
+
differences.push({ type: 'mismatched_field', info: "Type", tableName, field, currentField });
|
|
63
68
|
}
|
|
64
69
|
if (field.Null !== currentField.Null) {
|
|
65
|
-
differences.push({ type: 'mismatched_field', tableName, field });
|
|
70
|
+
differences.push({ type: 'mismatched_field', info: "Null", tableName, field, currentField });
|
|
66
71
|
}
|
|
67
72
|
if (field.Default !== currentField.Default) {
|
|
68
|
-
differences.push({ type: 'mismatched_field', tableName, field });
|
|
73
|
+
differences.push({ type: 'mismatched_field', info: "Default", tableName, field, currentField });
|
|
69
74
|
}
|
|
70
75
|
}
|
|
71
76
|
|
|
@@ -130,14 +135,15 @@ export const findDifferences = (expected, current) => {
|
|
|
130
135
|
|
|
131
136
|
// Compare stored procedures
|
|
132
137
|
if (!expected.procedures) throw new Error('No procedures found in expected');
|
|
133
|
-
for (const [
|
|
134
|
-
|
|
138
|
+
for (const [position, contents] of Object.entries(expected.procedures)) {
|
|
139
|
+
let procName = contents.Name;
|
|
140
|
+
let currentProc = current.procedures.find(p => p.Name === procName);
|
|
141
|
+
if (!currentProc) {
|
|
135
142
|
console.log('missing_procedure', contents);
|
|
136
143
|
differences.push({ type: 'missing_procedure', ...contents });
|
|
137
144
|
continue;
|
|
138
145
|
}
|
|
139
146
|
|
|
140
|
-
const currentProc = current.procedures[procName];
|
|
141
147
|
|
|
142
148
|
// Check if the procedure definition matches
|
|
143
149
|
if (currentProc.Definition !== contents.Definition) {
|
|
@@ -153,24 +159,41 @@ export const findDifferences = (expected, current) => {
|
|
|
153
159
|
}
|
|
154
160
|
|
|
155
161
|
// Compare views
|
|
156
|
-
if (
|
|
157
|
-
|
|
158
|
-
if (!current.views
|
|
159
|
-
|
|
160
|
-
|
|
162
|
+
if (expected.views || current.views) {
|
|
163
|
+
// Handle missing views (when expected has views but current doesn't have them)
|
|
164
|
+
if (expected.views && !current.views) {
|
|
165
|
+
for (const [viewName, expectedDefinition] of Object.entries(expected.views)) {
|
|
166
|
+
differences.push({ type: 'missing_view', viewName, definition: expectedDefinition });
|
|
167
|
+
}
|
|
161
168
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
169
|
+
// Handle extra views (when current has views but expected doesn't have them)
|
|
170
|
+
else if (current.views && !expected.views) {
|
|
171
|
+
for (const viewName of Object.keys(current.views)) {
|
|
172
|
+
differences.push({ type: 'extra_view', viewName });
|
|
173
|
+
}
|
|
166
174
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
175
|
+
// Compare views when both exist
|
|
176
|
+
else if (expected.views && current.views) {
|
|
177
|
+
// Check for missing and mismatched views
|
|
178
|
+
for (const [viewName, expectedDefinition] of Object.entries(expected.views)) {
|
|
179
|
+
if (!current.views[viewName]) {
|
|
180
|
+
differences.push({ type: 'missing_view', viewName, definition: expectedDefinition });
|
|
181
|
+
} else if (current.views[viewName] !== expectedDefinition) {
|
|
182
|
+
differences.push({
|
|
183
|
+
type: 'mismatched_view',
|
|
184
|
+
viewName,
|
|
185
|
+
definition: expectedDefinition,
|
|
186
|
+
current: current.views[viewName]
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Check for extra views
|
|
192
|
+
for (const viewName of Object.keys(current.views)) {
|
|
193
|
+
if (!expected.views[viewName]) {
|
|
194
|
+
differences.push({ type: 'extra_view', viewName });
|
|
195
|
+
}
|
|
196
|
+
}
|
|
174
197
|
}
|
|
175
198
|
}
|
|
176
199
|
|
package/src/dump.js
CHANGED
|
@@ -3,10 +3,46 @@ import path from 'path';
|
|
|
3
3
|
import { getDatabases, getDatabaseStructure } from './getters.js';
|
|
4
4
|
|
|
5
5
|
export const dumpAllDatabases = async (connection, command, options) => {
|
|
6
|
+
let response = {}
|
|
6
7
|
const databases = await getDatabases(connection);
|
|
7
8
|
for (const database of databases) {
|
|
8
9
|
let dbStructure = await getDatabaseStructure(connection, database, command, options);
|
|
9
|
-
|
|
10
|
+
if(Object.keys(dbStructure.views).length === 0){
|
|
11
|
+
delete dbStructure.views
|
|
12
|
+
}
|
|
13
|
+
if(Object.keys(dbStructure.procedures).length === 0){
|
|
14
|
+
delete dbStructure.procedures
|
|
15
|
+
}
|
|
16
|
+
await fs.writeFile(path.join(options.output , database, `${database}.json`), JSON.stringify(dbStructure, null, 4), 'utf-8');
|
|
17
|
+
response[database] = dbStructure;
|
|
10
18
|
}
|
|
19
|
+
return response;
|
|
11
20
|
}
|
|
12
21
|
|
|
22
|
+
export const getDatabasesFromExistingDumps = async (options) => {
|
|
23
|
+
console.log('Getting databases from existing dumps in ', options.output);
|
|
24
|
+
//check if the folder exists
|
|
25
|
+
let exists = await fs.stat(options.output).catch(() => false);
|
|
26
|
+
if (!exists) {
|
|
27
|
+
console.log('Folder does not exist');
|
|
28
|
+
return {};
|
|
29
|
+
}
|
|
30
|
+
//read directory above to get the dbs. each db is in a named folder with a matching .json.
|
|
31
|
+
|
|
32
|
+
//combine all the .jsons into a single object
|
|
33
|
+
let response = {}
|
|
34
|
+
const databases = await fs.readdir(path.join(options.output));
|
|
35
|
+
for (const database of databases) {
|
|
36
|
+
if(database.includes('diffs')){
|
|
37
|
+
console.log('Skipping diffs folder')
|
|
38
|
+
continue
|
|
39
|
+
}
|
|
40
|
+
let dbStructure = await fs.readFile(path.join(options.output, database, `${database}.json`), 'utf8');
|
|
41
|
+
response[database] = JSON.parse(dbStructure);
|
|
42
|
+
}
|
|
43
|
+
return response;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
package/src/index.js
CHANGED
|
@@ -7,13 +7,20 @@ import path from 'path';
|
|
|
7
7
|
import { cli } from './cli.js';
|
|
8
8
|
import { dumpAllDatabases } from './dump.js';
|
|
9
9
|
import { compareAllDatabases } from './compare.js';
|
|
10
|
-
|
|
10
|
+
import { startApiServer } from './socketServer.js'; // Import the new socket server
|
|
11
|
+
import open from 'open';
|
|
11
12
|
|
|
12
13
|
class DBStructureChecker {
|
|
13
14
|
constructor(config, targetDatabase = null) {
|
|
14
15
|
this.config = config;
|
|
15
16
|
this.targetDatabase = targetDatabase;
|
|
16
17
|
this.connection = mysql.createConnection(this.config);
|
|
18
|
+
this.dumpAllDatabases = dumpAllDatabases;
|
|
19
|
+
this.compareAllDatabases = compareAllDatabases;
|
|
20
|
+
this.getDatabases = () => {
|
|
21
|
+
|
|
22
|
+
fs.readFileSync(path.join(__dirname, 'databases.json'), 'utf8');
|
|
23
|
+
}
|
|
17
24
|
this.connection.on('error', (err) => {
|
|
18
25
|
console.error('Database connection error:', err);
|
|
19
26
|
});
|
|
@@ -35,50 +42,61 @@ class DBStructureChecker {
|
|
|
35
42
|
|
|
36
43
|
}
|
|
37
44
|
|
|
38
|
-
//
|
|
45
|
+
// Export the DBStructureChecker class
|
|
46
|
+
export { DBStructureChecker };
|
|
47
|
+
|
|
48
|
+
// Start the server only if the 'ui' command is used
|
|
49
|
+
const PORT = process.env.PORT || 3000;
|
|
39
50
|
(async () => {
|
|
40
51
|
const { command, config, argv } = cli();
|
|
41
52
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
console.log('
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
let maxRuns = 6;
|
|
64
|
-
while (runCount < maxRuns && totalDiffs > 0 && (totalDiffs < previousDiffs || previousDiffs === Infinity)) {
|
|
65
|
-
console.log('Running update command. Run count:', runCount);
|
|
66
|
-
let diffs = await compareAllDatabases(checker.connection, argv.output, true, command, argv); // Apply updates
|
|
67
|
-
previousDiffs = totalDiffs; // Update previous differences
|
|
68
|
-
totalDiffs = Object.values(diffs).reduce((acc, arr) => acc + arr.length, 0);
|
|
53
|
+
if (command === 'ui') {
|
|
54
|
+
startApiServer(PORT); // Start the Socket.IO server
|
|
55
|
+
console.log('Server started on port', PORT);
|
|
56
|
+
console.log('Open http://localhost:' + PORT + ' in your browser to use the UI');
|
|
57
|
+
await open(`http://localhost:${PORT}`);
|
|
58
|
+
} else {
|
|
59
|
+
// Handle other commands (dump, compare, update) as before
|
|
60
|
+
const checker = new DBStructureChecker(config, argv.database);
|
|
61
|
+
checker.connection.on('connect', async () => {
|
|
62
|
+
console.log('Connected to the database');
|
|
63
|
+
checker.targetDatabase = argv.database;
|
|
64
|
+
checker.command = command;
|
|
65
|
+
checker.options = argv;
|
|
66
|
+
try {
|
|
67
|
+
if (command === 'dump') {
|
|
68
|
+
console.log('Dumping the database structure to files');
|
|
69
|
+
await dumpAllDatabases(checker.connection, command, argv);
|
|
70
|
+
} else if (command === 'compare') {
|
|
71
|
+
console.log('Comparing the database structure with JSON dumps (dry run)');
|
|
72
|
+
let diffs = await compareAllDatabases(checker.connection, argv.output, false, command, argv); // Dry run
|
|
73
|
+
let totalDiffs = Object.values(diffs).reduce((acc, arr) => acc + arr.length, 0);
|
|
69
74
|
console.log('Total differences count:', totalDiffs);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
75
|
+
fs.writeFileSync(path.join(argv.output, 'diffs.json'), JSON.stringify(diffs, null, 4));
|
|
76
|
+
} else if (command === 'update') {
|
|
77
|
+
console.log('Updating the database structure based on JSON dumps');
|
|
78
|
+
let totalDiffs = Infinity; // Initialize with a large number
|
|
79
|
+
let previousDiffs = totalDiffs; // Store previous differences
|
|
80
|
+
let runCount = 0; // Counter for the number of runs
|
|
81
|
+
let maxRuns = 6;
|
|
82
|
+
while (runCount < maxRuns && totalDiffs > 0 && (totalDiffs < previousDiffs || previousDiffs === Infinity)) {
|
|
83
|
+
console.log('Running update command. Run count:', runCount);
|
|
84
|
+
let diffs = await compareAllDatabases(checker.connection, argv.output, true, command, argv); // Apply updates
|
|
85
|
+
previousDiffs = totalDiffs; // Update previous differences
|
|
86
|
+
totalDiffs = Object.values(diffs).reduce((acc, arr) => acc + arr.length, 0);
|
|
87
|
+
console.log('Total differences count:', totalDiffs);
|
|
88
|
+
runCount++;
|
|
89
|
+
if(totalDiffs > 0 && runCount < maxRuns){
|
|
90
|
+
console.log('Total differences count is still greater than 0. Waiting 1 second before next run. Run count:', runCount);
|
|
91
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
92
|
+
}
|
|
74
93
|
}
|
|
75
94
|
}
|
|
95
|
+
} catch (err) {
|
|
96
|
+
console.error(err.message);
|
|
97
|
+
} finally {
|
|
98
|
+
await checker.closeConnection();
|
|
76
99
|
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
} finally {
|
|
80
|
-
await checker.closeConnection();
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
|
|
100
|
+
});
|
|
101
|
+
}
|
|
84
102
|
})();
|
package/src/parsers.js
CHANGED
|
@@ -44,7 +44,8 @@ export const parseCreateTableSQL = (createTableSQL) => {
|
|
|
44
44
|
|
|
45
45
|
// Set Default property if it exists
|
|
46
46
|
if (columnMatch[5]) {
|
|
47
|
-
|
|
47
|
+
// console.log('columnMatch[5]', columnMatch[5], columnMatch[5].replace(/'/g, ''))
|
|
48
|
+
column.Default = columnMatch[5].replace(/'/g, '');
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
// Set AutoIncrement property if it exists
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import http from 'http';
|
|
3
|
+
import { dumpAllDatabases, getDatabasesFromExistingDumps } from './dump.js'; // Import necessary functions
|
|
4
|
+
import { compareAllDatabases } from './compare.js';
|
|
5
|
+
import { DBStructureChecker } from './index.js'; // Import your DBStructureChecker class
|
|
6
|
+
import { applyDifferences } from './apply.js';
|
|
7
|
+
import cors from 'cors';
|
|
8
|
+
|
|
9
|
+
export const startApiServer = (port) => {
|
|
10
|
+
const app = express();
|
|
11
|
+
app.use(cors({origin:'*'}));
|
|
12
|
+
const server = http.createServer(app);
|
|
13
|
+
app.use(express.json()); // Middleware to parse JSON requests
|
|
14
|
+
|
|
15
|
+
app.post('/api/getDatabasesFromExistingDumps', async (req, res) => {
|
|
16
|
+
const { config } = req.body;
|
|
17
|
+
const databases = await getDatabasesFromExistingDumps(config);
|
|
18
|
+
res.json(databases);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// API endpoint to dump the database
|
|
22
|
+
app.post('/api/dump', async (req, res) => {
|
|
23
|
+
const { config, database } = req.body;
|
|
24
|
+
const checker = new DBStructureChecker(config, database);
|
|
25
|
+
checker.connection.on('connect', async () => {
|
|
26
|
+
console.log('Connected to the database');
|
|
27
|
+
checker.targetDatabase = database;
|
|
28
|
+
checker.command = 'dump';
|
|
29
|
+
checker.options = req.body;
|
|
30
|
+
try {
|
|
31
|
+
console.log('Dumping the database structure to files');
|
|
32
|
+
await dumpAllDatabases(checker.connection, 'dump', config);
|
|
33
|
+
res.json({ message: 'Dump completed successfully' });
|
|
34
|
+
} catch (err) {
|
|
35
|
+
res.status(500).json({ message: err.message });
|
|
36
|
+
} finally {
|
|
37
|
+
await checker.closeConnection();
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// API endpoint to compare the database
|
|
43
|
+
app.post('/api/compare', async (req, res) => {
|
|
44
|
+
const { config } = req.body;
|
|
45
|
+
const checker = new DBStructureChecker(config, config.database);
|
|
46
|
+
checker.connection.on('connect', async () => {
|
|
47
|
+
console.log('Connected to the database');
|
|
48
|
+
checker.targetDatabase = config.database;
|
|
49
|
+
checker.command = 'compare';
|
|
50
|
+
checker.options = req.body;
|
|
51
|
+
try {
|
|
52
|
+
delete req.body.output;
|
|
53
|
+
console.log('Comparing the database structure with JSON dumps (dry run)');
|
|
54
|
+
let diffs = await compareAllDatabases(checker.connection, config.output, false, 'compare', config);
|
|
55
|
+
res.json(diffs);
|
|
56
|
+
} catch (err) {
|
|
57
|
+
console.log(err);
|
|
58
|
+
console.log(req.body);
|
|
59
|
+
res.status(500).json({ message: err.message });
|
|
60
|
+
} finally {
|
|
61
|
+
await checker.closeConnection();
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// API endpoint to apply differences
|
|
68
|
+
app.post('/api/apply', async (req, res) => {
|
|
69
|
+
const { config, database, diffs } = req.body;
|
|
70
|
+
console.log('Applying differences:', diffs);
|
|
71
|
+
const checker = new DBStructureChecker(config, database);
|
|
72
|
+
checker.connection.on('connect', async () => {
|
|
73
|
+
console.log('Connected to the database');
|
|
74
|
+
checker.targetDatabase = database;
|
|
75
|
+
checker.command = 'apply';
|
|
76
|
+
checker.options = req.body;
|
|
77
|
+
try {
|
|
78
|
+
console.log('Applying the database structure based on JSON dumps');
|
|
79
|
+
await applyDifferences(checker.connection, database, diffs);
|
|
80
|
+
res.json({ message: 'Apply completed successfully' });
|
|
81
|
+
} catch (err) {
|
|
82
|
+
res.status(500).json({ message: err.message });
|
|
83
|
+
} finally {
|
|
84
|
+
await checker.closeConnection();
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
//serve the app from the ui folder
|
|
90
|
+
app.use(express.static('ui/dist/spa'));
|
|
91
|
+
|
|
92
|
+
// Start the server
|
|
93
|
+
server.listen(port, () => {
|
|
94
|
+
console.log(`Server is running on http://localhost:${port}`);
|
|
95
|
+
});
|
|
96
|
+
};
|
package/ui/.editorconfig
ADDED
package/ui/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# MySQLsyncr (mysqlsyncr)
|
|
2
|
+
|
|
3
|
+
A simple ui for the sync features in mysqlsyncr
|
|
4
|
+
|
|
5
|
+
## Install the dependencies
|
|
6
|
+
```bash
|
|
7
|
+
yarn
|
|
8
|
+
# or
|
|
9
|
+
npm install
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
### Start the app in development mode (hot-code reloading, error reporting, etc.)
|
|
13
|
+
```bash
|
|
14
|
+
quasar dev
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Lint the files
|
|
19
|
+
```bash
|
|
20
|
+
yarn lint
|
|
21
|
+
# or
|
|
22
|
+
npm run lint
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### Format the files
|
|
27
|
+
```bash
|
|
28
|
+
yarn format
|
|
29
|
+
# or
|
|
30
|
+
npm run format
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
### Build the app for production
|
|
35
|
+
```bash
|
|
36
|
+
quasar build
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Customize the configuration
|
|
40
|
+
See [Configuring quasar.config.js](https://v2.quasar.dev/quasar-cli-vite/quasar-config-js).
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{c as L,i as I,e as P,l as D,p as N,b as g,h as m,f as J,g as C,j as X,s as Y,w as _,k as Z,o as T,m as H,n as V,q,r as ee,t as te,u as y,v as A,x as F,y as R,z as x,A as ne,B as oe,_ as ie,C as le,D as re,E as ae,F as k,G as B}from"./index-DKK0oGss.js";const se=L({name:"QPageContainer",setup(e,{slots:p}){const{proxy:{$q:n}}=C(),t=I(D,P);if(t===P)return console.error("QPageContainer needs to be child of QLayout"),P;N(X,!0);const r=g(()=>{const a={};return t.header.space===!0&&(a.paddingTop=`${t.header.size}px`),t.right.space===!0&&(a[`padding${n.lang.rtl===!0?"Left":"Right"}`]=`${t.right.size}px`),t.footer.space===!0&&(a.paddingBottom=`${t.footer.size}px`),t.left.space===!0&&(a[`padding${n.lang.rtl===!0?"Right":"Left"}`]=`${t.left.size}px`),a});return()=>m("div",{class:"q-page-container",style:r.value},J(p.default))}}),{passive:M}=q,ce=["both","horizontal","vertical"],ue=L({name:"QScrollObserver",props:{axis:{type:String,validator:e=>ce.includes(e),default:"vertical"},debounce:[String,Number],scrollTarget:Y},emits:["scroll"],setup(e,{emit:p}){const n={position:{top:0,left:0},direction:"down",directionChanged:!1,delta:{top:0,left:0},inflectionPoint:{top:0,left:0}};let t=null,r,a;_(()=>e.scrollTarget,()=>{l(),v()});function s(){t!==null&&t();const c=Math.max(0,ee(r)),w=te(r),d={top:c-n.position.top,left:w-n.position.left};if(e.axis==="vertical"&&d.top===0||e.axis==="horizontal"&&d.left===0)return;const z=Math.abs(d.top)>=Math.abs(d.left)?d.top<0?"up":"down":d.left<0?"left":"right";n.position={top:c,left:w},n.directionChanged=n.direction!==z,n.delta=d,n.directionChanged===!0&&(n.direction=z,n.inflectionPoint=n.position),p("scroll",{...n})}function v(){r=Z(a,e.scrollTarget),r.addEventListener("scroll",i,M),i(!0)}function l(){r!==void 0&&(r.removeEventListener("scroll",i,M),r=void 0)}function i(c){if(c===!0||e.debounce===0||e.debounce==="0")s();else if(t===null){const[w,d]=e.debounce?[setTimeout(s,e.debounce),clearTimeout]:[requestAnimationFrame(s),cancelAnimationFrame];t=()=>{d(w),t=null}}}const{proxy:h}=C();return _(()=>h.$q.lang.rtl,s),T(()=>{a=h.$el.parentNode,v()}),H(()=>{t!==null&&t(),l()}),Object.assign(h,{trigger:i,getPosition:()=>n}),V}});function de(){const e=y(!A.value);return e.value===!1&&T(()=>{e.value=!0}),{isHydrated:e}}const K=typeof ResizeObserver<"u",W=K===!0?{}:{style:"display:block;position:absolute;top:0;left:0;right:0;bottom:0;height:100%;width:100%;overflow:hidden;pointer-events:none;z-index:-1;",url:"about:blank"},j=L({name:"QResizeObserver",props:{debounce:{type:[String,Number],default:100}},emits:["resize"],setup(e,{emit:p}){let n=null,t,r={width:-1,height:-1};function a(l){l===!0||e.debounce===0||e.debounce==="0"?s():n===null&&(n=setTimeout(s,e.debounce))}function s(){if(n!==null&&(clearTimeout(n),n=null),t){const{offsetWidth:l,offsetHeight:i}=t;(l!==r.width||i!==r.height)&&(r={width:l,height:i},p("resize",r))}}const{proxy:v}=C();if(v.trigger=a,K===!0){let l;const i=h=>{t=v.$el.parentNode,t?(l=new ResizeObserver(a),l.observe(t),s()):h!==!0&&F(()=>{i(!0)})};return T(()=>{i()}),H(()=>{n!==null&&clearTimeout(n),l!==void 0&&(l.disconnect!==void 0?l.disconnect():t&&l.unobserve(t))}),V}else{let l=function(){n!==null&&(clearTimeout(n),n=null),c!==void 0&&(c.removeEventListener!==void 0&&c.removeEventListener("resize",a,q.passive),c=void 0)},i=function(){l(),t&&t.contentDocument&&(c=t.contentDocument.defaultView,c.addEventListener("resize",a,q.passive),s())};const{isHydrated:h}=de();let c;return T(()=>{F(()=>{t=v.$el,t&&i()})}),H(l),()=>{if(h.value===!0)return m("object",{class:"q--avoid-card-border",style:W.style,tabindex:-1,type:"text/html",data:W.url,"aria-hidden":"true",onLoad:i})}}}}),fe=L({name:"QLayout",props:{container:Boolean,view:{type:String,default:"hhh lpr fff",validator:e=>/^(h|l)h(h|r) lpr (f|l)f(f|r)$/.test(e.toLowerCase())},onScroll:Function,onScrollHeight:Function,onResize:Function},setup(e,{slots:p,emit:n}){const{proxy:{$q:t}}=C(),r=y(null),a=y(t.screen.height),s=y(e.container===!0?0:t.screen.width),v=y({position:0,direction:"down",inflectionPoint:0}),l=y(0),i=y(A.value===!0?0:R()),h=g(()=>"q-layout q-layout--"+(e.container===!0?"containerized":"standard")),c=g(()=>e.container===!1?{minHeight:t.screen.height+"px"}:null),w=g(()=>i.value!==0?{[t.lang.rtl===!0?"left":"right"]:`${i.value}px`}:null),d=g(()=>i.value!==0?{[t.lang.rtl===!0?"right":"left"]:0,[t.lang.rtl===!0?"left":"right"]:`-${i.value}px`,width:`calc(100% + ${i.value}px)`}:null);function z(o){if(e.container===!0||document.qScrollPrevented!==!0){const u={position:o.position.top,direction:o.direction,directionChanged:o.directionChanged,inflectionPoint:o.inflectionPoint.top,delta:o.delta.top};v.value=u,e.onScroll!==void 0&&n("scroll",u)}}function U(o){const{height:u,width:b}=o;let f=!1;a.value!==u&&(f=!0,a.value=u,e.onScrollHeight!==void 0&&n("scrollHeight",u),E()),s.value!==b&&(f=!0,s.value=b),f===!0&&e.onResize!==void 0&&n("resize",o)}function G({height:o}){l.value!==o&&(l.value=o,E())}function E(){if(e.container===!0){const o=a.value>l.value?R():0;i.value!==o&&(i.value=o)}}let S=null;const O={instances:{},view:g(()=>e.view),isContainer:g(()=>e.container),rootRef:r,height:a,containerHeight:l,scrollbarWidth:i,totalWidth:g(()=>s.value+i.value),rows:g(()=>{const o=e.view.toLowerCase().split(" ");return{top:o[0].split(""),middle:o[1].split(""),bottom:o[2].split("")}}),header:x({size:0,offset:0,space:!1}),right:x({size:300,offset:0,space:!1}),footer:x({size:0,offset:0,space:!1}),left:x({size:300,offset:0,space:!1}),scroll:v,animate(){S!==null?clearTimeout(S):document.body.classList.add("q-body--layout-animate"),S=setTimeout(()=>{S=null,document.body.classList.remove("q-body--layout-animate")},155)},update(o,u,b){O[o][u]=b}};if(N(D,O),R()>0){let o=function(){f=null,$.classList.remove("hide-scrollbar")},u=function(){if(f===null){if($.scrollHeight>t.screen.height)return;$.classList.add("hide-scrollbar")}else clearTimeout(f);f=setTimeout(o,300)},b=function(Q){f!==null&&Q==="remove"&&(clearTimeout(f),o()),window[`${Q}EventListener`]("resize",u)},f=null;const $=document.body;_(()=>e.container!==!0?"add":"remove",b),e.container!==!0&&b("add"),ne(()=>{b("remove")})}return()=>{const o=oe(p.default,[m(ue,{onScroll:z}),m(j,{onResize:U})]),u=m("div",{class:h.value,style:c.value,ref:e.container===!0?void 0:r,tabindex:-1},o);return e.container===!0?m("div",{class:"q-layout-container overflow-hidden",ref:r},[m(j,{onResize:G}),m("div",{class:"absolute-full",style:w.value},[m("div",{class:"scroll",style:d.value},[u])])]):u}}}),ve={};function he(e,p){const n=le("router-view");return re(),ae(fe,{view:"lHh Lpr lFf"},{default:k(()=>[B(se,null,{default:k(()=>[B(n)]),_:1})]),_:1})}const me=ie(ve,[["render",he]]);export{me as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{d as e,a}from"./index-DKK0oGss.js";const i=a.create({baseURL:"https://api.example.com"}),t=e(({app:o})=>{o.config.globalProperties.$axios=a,o.config.globalProperties.$api=i});export{i as api,t as default};
|
|
Binary file
|