mysqlsyncr 1.0.12 → 1.0.16
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 +5 -1
- package/src/apply.js +41 -3
- package/src/socketServer.js +52 -1
- package/ui/dist/spa/assets/{MainLayout-D0eJ_5sV.js → MainLayout-CGvO4rsY.js} +1 -1
- package/ui/dist/spa/assets/{axios-CEU4Idnn.js → axios-CrRsmKCW.js} +1 -1
- package/ui/dist/spa/assets/{index-DbZFTc3Z.css → index-767_aev0.css} +1 -1
- package/ui/dist/spa/assets/index-8ohClfZU.js +30 -0
- package/ui/dist/spa/index.html +2 -2
- package/ui/src/components/FolderBrowser.vue +194 -0
- package/ui/src/pages/MainComponent.vue +41 -1
- package/ui/dist/spa/assets/index-C4-Ja7SU.js +0 -30
package/ui/dist/spa/index.html
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
<!DOCTYPE html><html><head><title>MySQLsyncr</title><meta charset=utf-8><meta name=description content="A simple ui for the sync features in mysqlsyncr"><meta name=format-detection content="telephone=no"><meta name=msapplication-tap-highlight content=no><meta name=viewport content="user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1,width=device-width"><link rel=icon type=image/png sizes=128x128 href=/icons/favicon-128x128.png><link rel=icon type=image/png sizes=96x96 href=/icons/favicon-96x96.png><link rel=icon type=image/png sizes=32x32 href=/icons/favicon-32x32.png><link rel=icon type=image/png sizes=16x16 href=/icons/favicon-16x16.png><link rel=icon type=image/ico href=/favicon.ico> <script type="module" crossorigin src="/assets/index-
|
|
2
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
1
|
+
<!DOCTYPE html><html><head><title>MySQLsyncr</title><meta charset=utf-8><meta name=description content="A simple ui for the sync features in mysqlsyncr"><meta name=format-detection content="telephone=no"><meta name=msapplication-tap-highlight content=no><meta name=viewport content="user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1,width=device-width"><link rel=icon type=image/png sizes=128x128 href=/icons/favicon-128x128.png><link rel=icon type=image/png sizes=96x96 href=/icons/favicon-96x96.png><link rel=icon type=image/png sizes=32x32 href=/icons/favicon-32x32.png><link rel=icon type=image/png sizes=16x16 href=/icons/favicon-16x16.png><link rel=icon type=image/ico href=/favicon.ico> <script type="module" crossorigin src="/assets/index-8ohClfZU.js"></script>
|
|
2
|
+
<link rel="stylesheet" crossorigin href="/assets/index-767_aev0.css">
|
|
3
3
|
</head><body><div id=q-app></div></body></html>
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<q-dialog v-model="isOpen" persistent>
|
|
3
|
+
<q-card class="folder-browser-card">
|
|
4
|
+
<q-card-section class="header-section row items-center justify-between">
|
|
5
|
+
<div class="text-h6">Select Folder</div>
|
|
6
|
+
<q-btn icon="close" flat round dense v-close-popup />
|
|
7
|
+
</q-card-section>
|
|
8
|
+
|
|
9
|
+
<q-card-section>
|
|
10
|
+
<div class="path-section">
|
|
11
|
+
<div class="text-subtitle2 q-mb-sm">Current Path:</div>
|
|
12
|
+
<q-input
|
|
13
|
+
v-model="currentPath"
|
|
14
|
+
dense
|
|
15
|
+
readonly
|
|
16
|
+
filled
|
|
17
|
+
class="current-path-input"
|
|
18
|
+
bg-color="grey-2"
|
|
19
|
+
>
|
|
20
|
+
<template v-slot:prepend>
|
|
21
|
+
<q-icon name="folder" color="primary" />
|
|
22
|
+
</template>
|
|
23
|
+
</q-input>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<q-list bordered separator class="directory-list">
|
|
27
|
+
<q-item
|
|
28
|
+
v-for="item in sortedItems"
|
|
29
|
+
:key="item.path"
|
|
30
|
+
clickable
|
|
31
|
+
v-ripple
|
|
32
|
+
@click="item.isDirectory ? navigateToFolder(item.path) : null"
|
|
33
|
+
class="directory-item"
|
|
34
|
+
>
|
|
35
|
+
<q-item-section avatar>
|
|
36
|
+
<q-icon
|
|
37
|
+
:name="getItemIcon(item)"
|
|
38
|
+
:color="getItemColor(item)"
|
|
39
|
+
size="sm"
|
|
40
|
+
/>
|
|
41
|
+
</q-item-section>
|
|
42
|
+
<q-item-section>
|
|
43
|
+
{{ item.name }}
|
|
44
|
+
</q-item-section>
|
|
45
|
+
<q-item-section side v-if="item.isDirectory">
|
|
46
|
+
<q-icon name="chevron_right" color="grey-6" size="xs" />
|
|
47
|
+
</q-item-section>
|
|
48
|
+
</q-item>
|
|
49
|
+
</q-list>
|
|
50
|
+
</q-card-section>
|
|
51
|
+
|
|
52
|
+
<q-card-actions align="right" class="action-buttons">
|
|
53
|
+
<q-btn
|
|
54
|
+
flat
|
|
55
|
+
label="Cancel"
|
|
56
|
+
color="grey-7"
|
|
57
|
+
v-close-popup
|
|
58
|
+
class="q-px-md"
|
|
59
|
+
/>
|
|
60
|
+
<q-btn
|
|
61
|
+
unelevated
|
|
62
|
+
label="Select"
|
|
63
|
+
color="primary"
|
|
64
|
+
@click="selectCurrentFolder"
|
|
65
|
+
class="q-px-md q-ml-sm"
|
|
66
|
+
/>
|
|
67
|
+
</q-card-actions>
|
|
68
|
+
</q-card>
|
|
69
|
+
</q-dialog>
|
|
70
|
+
</template>
|
|
71
|
+
|
|
72
|
+
<script>
|
|
73
|
+
import axios from 'axios';
|
|
74
|
+
|
|
75
|
+
export default {
|
|
76
|
+
name: 'FolderBrowser',
|
|
77
|
+
props: {
|
|
78
|
+
modelValue: {
|
|
79
|
+
type: Boolean,
|
|
80
|
+
required: true
|
|
81
|
+
},
|
|
82
|
+
initialPath: {
|
|
83
|
+
type: String,
|
|
84
|
+
default: ''
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
data() {
|
|
88
|
+
return {
|
|
89
|
+
currentPath: '',
|
|
90
|
+
items: []
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
computed: {
|
|
94
|
+
isOpen: {
|
|
95
|
+
get() {
|
|
96
|
+
return this.modelValue
|
|
97
|
+
},
|
|
98
|
+
set(value) {
|
|
99
|
+
this.$emit('update:modelValue', value)
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
sortedItems() {
|
|
103
|
+
return this.items;
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
methods: {
|
|
107
|
+
async navigateToFolder(folderPath) {
|
|
108
|
+
await this.loadFolderContents(folderPath);
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
async loadFolderContents(path) {
|
|
112
|
+
try {
|
|
113
|
+
const response = await axios.post('http://localhost:3000/api/browse-folders', {
|
|
114
|
+
currentPath: path
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
if (response.data) {
|
|
118
|
+
this.currentPath = response.data.currentPath;
|
|
119
|
+
this.items = response.data.items || [];
|
|
120
|
+
}
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.error('Error loading folder contents:', error);
|
|
123
|
+
this.$q.notify({
|
|
124
|
+
type: 'negative',
|
|
125
|
+
message: 'Error loading folder contents'
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
selectCurrentFolder() {
|
|
131
|
+
this.$emit('folder-selected', this.currentPath);
|
|
132
|
+
this.isOpen = false;
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
getItemIcon(item) {
|
|
136
|
+
if (item.isParent) return 'reply';
|
|
137
|
+
if (item.isDirectory) return 'folder';
|
|
138
|
+
return 'insert_drive_file';
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
getItemColor(item) {
|
|
142
|
+
if (item.isParent) return 'grey-7';
|
|
143
|
+
if (item.isDirectory) return 'primary';
|
|
144
|
+
return 'grey-6';
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
async mounted() {
|
|
148
|
+
await this.loadFolderContents(this.initialPath || process.cwd());
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
</script>
|
|
152
|
+
|
|
153
|
+
<style scoped>
|
|
154
|
+
.folder-browser-card {
|
|
155
|
+
min-width: 450px;
|
|
156
|
+
max-width: 700px;
|
|
157
|
+
border-radius: 8px;
|
|
158
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.header-section {
|
|
162
|
+
background-color: #f5f5f5;
|
|
163
|
+
border-bottom: 1px solid #e0e0e0;
|
|
164
|
+
padding: 12px 20px;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.path-section {
|
|
168
|
+
margin-bottom: 20px;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.current-path-input {
|
|
172
|
+
font-family: monospace;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.directory-list {
|
|
176
|
+
max-height: 400px;
|
|
177
|
+
overflow-y: auto;
|
|
178
|
+
border-radius: 4px;
|
|
179
|
+
border: 1px solid #e0e0e0;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.directory-item {
|
|
183
|
+
transition: background-color 0.2s;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.directory-item:hover {
|
|
187
|
+
background-color: #f0f4f8;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.action-buttons {
|
|
191
|
+
padding: 16px;
|
|
192
|
+
border-top: 1px solid #e0e0e0;
|
|
193
|
+
}
|
|
194
|
+
</style>
|
|
@@ -124,6 +124,7 @@
|
|
|
124
124
|
<q-input
|
|
125
125
|
v-model="host"
|
|
126
126
|
label="Database Host"
|
|
127
|
+
default="127.0.0.1"
|
|
127
128
|
filled
|
|
128
129
|
class="q-mb-md"
|
|
129
130
|
required
|
|
@@ -138,6 +139,7 @@
|
|
|
138
139
|
<q-input
|
|
139
140
|
v-model="port"
|
|
140
141
|
label="Database Port"
|
|
142
|
+
default="3306"
|
|
141
143
|
type="number"
|
|
142
144
|
filled
|
|
143
145
|
class="q-mb-md"
|
|
@@ -153,6 +155,7 @@
|
|
|
153
155
|
<q-input
|
|
154
156
|
v-model="user"
|
|
155
157
|
label="Database User"
|
|
158
|
+
default="root"
|
|
156
159
|
filled
|
|
157
160
|
class="q-mb-md"
|
|
158
161
|
required
|
|
@@ -197,6 +200,7 @@
|
|
|
197
200
|
<q-input
|
|
198
201
|
v-model="dbFolder"
|
|
199
202
|
label="Database Folder"
|
|
203
|
+
default="db-dump"
|
|
200
204
|
filled
|
|
201
205
|
class="q-mb-md"
|
|
202
206
|
required
|
|
@@ -204,6 +208,16 @@
|
|
|
204
208
|
<template v-slot:prepend>
|
|
205
209
|
<q-icon name="folder" />
|
|
206
210
|
</template>
|
|
211
|
+
<template v-slot:append>
|
|
212
|
+
<q-btn
|
|
213
|
+
flat
|
|
214
|
+
dense
|
|
215
|
+
icon="folder_open"
|
|
216
|
+
@click="showFolderBrowser = true"
|
|
217
|
+
>
|
|
218
|
+
<q-tooltip>Browse</q-tooltip>
|
|
219
|
+
</q-btn>
|
|
220
|
+
</template>
|
|
207
221
|
</q-input>
|
|
208
222
|
</div>
|
|
209
223
|
</div>
|
|
@@ -369,13 +383,24 @@
|
|
|
369
383
|
</q-card-actions>
|
|
370
384
|
</q-card>
|
|
371
385
|
</q-dialog>
|
|
386
|
+
|
|
387
|
+
<FolderBrowser v-if="showFolderBrowser"
|
|
388
|
+
v-model="showFolderBrowser"
|
|
389
|
+
:initial-path="dbFolder"
|
|
390
|
+
@folder-selected="onFolderSelected"
|
|
391
|
+
/>
|
|
372
392
|
</q-page>
|
|
373
393
|
</template>
|
|
374
394
|
|
|
375
395
|
<script>
|
|
376
396
|
import axios from 'axios'
|
|
397
|
+
import FolderBrowser from '../components/FolderBrowser.vue'
|
|
398
|
+
|
|
377
399
|
|
|
378
400
|
export default {
|
|
401
|
+
components: {
|
|
402
|
+
FolderBrowser
|
|
403
|
+
},
|
|
379
404
|
data() {
|
|
380
405
|
return {
|
|
381
406
|
isDbSetup: false,
|
|
@@ -402,6 +427,7 @@ export default {
|
|
|
402
427
|
selectedIssues: [],
|
|
403
428
|
selectedNode: null,
|
|
404
429
|
isNodeFixing: {}, // Tracks loading state for each node
|
|
430
|
+
showFolderBrowser: false,
|
|
405
431
|
}
|
|
406
432
|
},
|
|
407
433
|
methods: {
|
|
@@ -423,7 +449,8 @@ export default {
|
|
|
423
449
|
localStorage.setItem('dbConfig', JSON.stringify(dbConfig))
|
|
424
450
|
this.isDbSetup = true
|
|
425
451
|
this.closeSettings()
|
|
426
|
-
this.
|
|
452
|
+
this.getDatabasesFromExistingDumps()
|
|
453
|
+
this.fetchAllDifferences()
|
|
427
454
|
},
|
|
428
455
|
async refreshDifferences() {
|
|
429
456
|
if (!this.isDbSetup) return
|
|
@@ -983,6 +1010,19 @@ export default {
|
|
|
983
1010
|
this.isApplying = false
|
|
984
1011
|
}
|
|
985
1012
|
},
|
|
1013
|
+
onFolderSelected(path) {
|
|
1014
|
+
this.dbFolder = path;
|
|
1015
|
+
// If you want to save the setting immediately
|
|
1016
|
+
const dbConfig = {
|
|
1017
|
+
host: this.host,
|
|
1018
|
+
port: this.port,
|
|
1019
|
+
user: this.user,
|
|
1020
|
+
password: this.password,
|
|
1021
|
+
database: this.database,
|
|
1022
|
+
output: this.dbFolder,
|
|
1023
|
+
}
|
|
1024
|
+
localStorage.setItem('dbConfig', JSON.stringify(dbConfig));
|
|
1025
|
+
},
|
|
986
1026
|
},
|
|
987
1027
|
mounted() {
|
|
988
1028
|
const dbConfig = localStorage.getItem('dbConfig')
|