Compare commits
597 Commits
v4.4.5-bet
...
v4.8.8-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31edd48257 | ||
|
|
8e9a14665b | ||
|
|
793bc6b494 | ||
|
|
864b9c9333 | ||
|
|
fb4dd06578 | ||
|
|
3c8a0ebd22 | ||
|
|
3a20f24f7c | ||
|
|
aa8854da93 | ||
|
|
82ae8e23e0 | ||
|
|
4d7887a379 | ||
|
|
30b054dbec | ||
|
|
df743e8ade | ||
|
|
7cc70dc5f8 | ||
|
|
10491868a0 | ||
|
|
8f945dc13f | ||
|
|
d041f88a47 | ||
|
|
e5bbf5eca3 | ||
|
|
66da21804b | ||
|
|
5315a549b0 | ||
|
|
82304f13e7 | ||
|
|
5bcd5d57ba | ||
|
|
b6464dc119 | ||
|
|
4182132dde | ||
|
|
32228d542a | ||
|
|
72f5710dfd | ||
|
|
539c383b21 | ||
|
|
c3289d09c0 | ||
|
|
233fed30cb | ||
|
|
63521002cc | ||
|
|
21642e0a3e | ||
|
|
4945b71c58 | ||
|
|
ed0d63d135 | ||
|
|
2c25669bc7 | ||
|
|
0c94145b18 | ||
|
|
95fb5d51c5 | ||
|
|
a75e931fd5 | ||
|
|
c7c667bbe0 | ||
|
|
97eff2b113 | ||
|
|
0122b0accc | ||
|
|
6c718981d6 | ||
|
|
f78d37159e | ||
|
|
bf1881595c | ||
|
|
4e8c92eb36 | ||
|
|
34a0cb095b | ||
|
|
f606d0c41c | ||
|
|
8002d734bc | ||
|
|
be6d572ce2 | ||
|
|
c3baa88d9e | ||
|
|
5ef06abd1a | ||
|
|
e86a34ab53 | ||
|
|
c2b715e3f7 | ||
|
|
666f1a3159 | ||
|
|
1ee66047d4 | ||
|
|
8032bf272b | ||
|
|
d52cfadfc4 | ||
|
|
374c820567 | ||
|
|
cc639df566 | ||
|
|
5a42e8e963 | ||
|
|
0187bb74ee | ||
|
|
bf4b7beadb | ||
|
|
b254c90f33 | ||
|
|
89fdfbe8c1 | ||
|
|
a37c81be88 | ||
|
|
abe9694d05 | ||
|
|
c55c1f3aaf | ||
|
|
ce6b750a1c | ||
|
|
f83d45356f | ||
|
|
589ff1ad38 | ||
|
|
ba97f50273 | ||
|
|
e52fbd5034 | ||
|
|
e29d3a6143 | ||
|
|
7313fa16f6 | ||
|
|
18437d1be7 | ||
|
|
2b39c85918 | ||
|
|
5135b985c9 | ||
|
|
05619faa7a | ||
|
|
12a638af3b | ||
|
|
38e05bf8e0 | ||
|
|
6d92de6930 | ||
|
|
90f8d349fc | ||
|
|
5379e86d6e | ||
|
|
85e449953f | ||
|
|
30e52723dd | ||
|
|
467918bcbb | ||
|
|
73d17504c1 | ||
|
|
7a3007deb2 | ||
|
|
2a46ff78bb | ||
|
|
fa193f0e57 | ||
|
|
f702513bb9 | ||
|
|
72462376b1 | ||
|
|
a46ef7f0d0 | ||
|
|
9f5013c6da | ||
|
|
5ea6c56752 | ||
|
|
cbb38b8edc | ||
|
|
045f6c6a47 | ||
|
|
334ab504cf | ||
|
|
66db28010c | ||
|
|
807392fc57 | ||
|
|
1a32d88312 | ||
|
|
1a76cc0979 | ||
|
|
bcdaf84739 | ||
|
|
39296a852e | ||
|
|
1a035ca168 | ||
|
|
c0b365602b | ||
|
|
5aac142e4c | ||
|
|
25380ee0a8 | ||
|
|
0b3b18ceda | ||
|
|
9cecebe8bc | ||
|
|
c0227ecce1 | ||
|
|
1d37950b37 | ||
|
|
fe277f5ffa | ||
|
|
1b52a2a0fc | ||
|
|
a062073a5f | ||
|
|
44e52dfa9c | ||
|
|
0323264bbd | ||
|
|
aacedba450 | ||
|
|
aac768b158 | ||
|
|
d4a7ae13e1 | ||
|
|
b8206a7a02 | ||
|
|
4c2f6c9b65 | ||
|
|
d58d46feac | ||
|
|
760edc7eca | ||
|
|
7c0f33383f | ||
|
|
a20a34680d | ||
|
|
0e8166577f | ||
|
|
11c82b1aac | ||
|
|
61f24c3408 | ||
|
|
e25657bd43 | ||
|
|
4bd7cd26d0 | ||
|
|
e06894372f | ||
|
|
1f0ae98c88 | ||
|
|
c0fdcf2fd1 | ||
|
|
8d31130737 | ||
|
|
9e3991556a | ||
|
|
fc08353225 | ||
|
|
25556f0d3e | ||
|
|
0fb3817af6 | ||
|
|
d8e840f127 | ||
|
|
4270722557 | ||
|
|
b3cfff0ae8 | ||
|
|
2f5f0ab54c | ||
|
|
4c856c5e36 | ||
|
|
5c8ae85c54 | ||
|
|
5b39576e61 | ||
|
|
735c48902f | ||
|
|
613ac3f0e5 | ||
|
|
1aecda6d9f | ||
|
|
38dfad4dfc | ||
|
|
25ae5bf048 | ||
|
|
f3bfe58c58 | ||
|
|
ff714b1f8a | ||
|
|
93552585f7 | ||
|
|
ec7641dbd6 | ||
|
|
e2308f501b | ||
|
|
45277e34c9 | ||
|
|
ba77b32e9a | ||
|
|
c05ef42bf9 | ||
|
|
b0e0197346 | ||
|
|
4c411b048d | ||
|
|
a3bc1e577a | ||
|
|
ef533671a2 | ||
|
|
77612cc6fb | ||
|
|
26881a3e39 | ||
|
|
a1b7ad18af | ||
|
|
487d4afd70 | ||
|
|
39f7508136 | ||
|
|
153bc6ddde | ||
|
|
73d4c43571 | ||
|
|
a73168b7e1 | ||
|
|
f10f863940 | ||
|
|
5df0204450 | ||
|
|
2bec053809 | ||
|
|
6fb582249c | ||
|
|
1a81952ce7 | ||
|
|
04f25c4535 | ||
|
|
8824262395 | ||
|
|
ff661ec89b | ||
|
|
0221c30716 | ||
|
|
913896d8bc | ||
|
|
9b449527fd | ||
|
|
6dff481dbf | ||
|
|
649626975c | ||
|
|
c135a068a2 | ||
|
|
728a2c6a9f | ||
|
|
6e041e9eed | ||
|
|
6c8eccd369 | ||
|
|
bf1a89ee21 | ||
|
|
d888feeaf8 | ||
|
|
e181318e24 | ||
|
|
a9038984d8 | ||
|
|
4acce560ad | ||
|
|
29591a613a | ||
|
|
8f1d76fd2a | ||
|
|
7d196c7c62 | ||
|
|
267e687e2b | ||
|
|
34658e134f | ||
|
|
4efcef192a | ||
|
|
9c7a130ee4 | ||
|
|
0d7bfd5f90 | ||
|
|
5c4794deae | ||
|
|
074a75075d | ||
|
|
4ce2ed06e1 | ||
|
|
61c5ac6a47 | ||
|
|
4a0bd14dfe | ||
|
|
85bdbd503b | ||
|
|
aec9c796b2 | ||
|
|
6c317b0cf7 | ||
|
|
170f213024 | ||
|
|
050c15994b | ||
|
|
5e73c75bb5 | ||
|
|
09bb8f0874 | ||
|
|
651dd09b15 | ||
|
|
900fdc56f4 | ||
|
|
5bda092a51 | ||
|
|
dc34898cd8 | ||
|
|
6590830344 | ||
|
|
cf734a26ee | ||
|
|
32c06fdf4d | ||
|
|
cbe0ea7c9b | ||
|
|
b6cc77c7fe | ||
|
|
25015f35d5 | ||
|
|
ae719157c0 | ||
|
|
772a72dfd8 | ||
|
|
3ccb00854c | ||
|
|
cf047cb7b5 | ||
|
|
84725f0586 | ||
|
|
34dae68a62 | ||
|
|
750a37a27f | ||
|
|
cd7864b889 | ||
|
|
7efd4be401 | ||
|
|
0629123cc1 | ||
|
|
6138cfc2da | ||
|
|
730fd38b9e | ||
|
|
3d72df424f | ||
|
|
98a9859216 | ||
|
|
0b6042c3cb | ||
|
|
35792a024a | ||
|
|
ddff3d2b89 | ||
|
|
c26bc6d0e9 | ||
|
|
8c3708fc8c | ||
|
|
008a09cc81 | ||
|
|
7497e2684c | ||
|
|
c4b0b185e6 | ||
|
|
8b91c69f5f | ||
|
|
f57624ef24 | ||
|
|
64c8d4bdca | ||
|
|
9b396e7431 | ||
|
|
78faa94d77 | ||
|
|
7953186e03 | ||
|
|
fbaf6684de | ||
|
|
8d2f437640 | ||
|
|
ccfbe2cccb | ||
|
|
74ad345ba5 | ||
|
|
750e929d1e | ||
|
|
5eba93559d | ||
|
|
51942be0a6 | ||
|
|
425841bb38 | ||
|
|
2202ae5aab | ||
|
|
17d90c73cc | ||
|
|
886d920fcf | ||
|
|
e8d24e177b | ||
|
|
d7a2bf3ac0 | ||
|
|
8692283cb8 | ||
|
|
a4fde49c75 | ||
|
|
3c0dd13ae5 | ||
|
|
2fa46da7b6 | ||
|
|
caf9870990 | ||
|
|
8d7c7481b4 | ||
|
|
be90241091 | ||
|
|
fac78afa31 | ||
|
|
d37d2cc8d1 | ||
|
|
3f87be3f46 | ||
|
|
8d5db901de | ||
|
|
e683904a91 | ||
|
|
4c243f996b | ||
|
|
92d34d1ddd | ||
|
|
428bae3cd4 | ||
|
|
9ed53c8f47 | ||
|
|
b0e0cf1829 | ||
|
|
2a1e9a6ddd | ||
|
|
bfea77c56b | ||
|
|
50f9b9f025 | ||
|
|
1050760c1d | ||
|
|
80a3282d41 | ||
|
|
20c56a92ee | ||
|
|
7128a47f0a | ||
|
|
41193bcc28 | ||
|
|
fbae2341d5 | ||
|
|
7b8c0be044 | ||
|
|
9267ca326f | ||
|
|
2b61c8a21f | ||
|
|
ae7dccb5b7 | ||
|
|
1c977bb7ae | ||
|
|
c866a89dbb | ||
|
|
462f985406 | ||
|
|
6113547304 | ||
|
|
3d5e441994 | ||
|
|
e23b5b4124 | ||
|
|
db7370aef6 | ||
|
|
f384ddfb1f | ||
|
|
1afbf6049e | ||
|
|
eacb026603 | ||
|
|
7112f930ae | ||
|
|
07c7a0405a | ||
|
|
6a183a6263 | ||
|
|
3d395a0018 | ||
|
|
a55f8daef0 | ||
|
|
5bdc08d60f | ||
|
|
a8f1ff4013 | ||
|
|
b9cd12ab26 | ||
|
|
18c545a794 | ||
|
|
be0e5aa9cb | ||
|
|
10f34c9fe4 | ||
|
|
7b1a6ecdf2 | ||
|
|
b2c841e0cd | ||
|
|
8ca6c0ab3f | ||
|
|
08e03efea4 | ||
|
|
0e75f5ed0d | ||
|
|
f8da3c3476 | ||
|
|
256f97ad42 | ||
|
|
83ab3cb012 | ||
|
|
96136fe443 | ||
|
|
108a49b3e8 | ||
|
|
2946d8a1de | ||
|
|
29fa8445e2 | ||
|
|
ca79db53ee | ||
|
|
d3fca75277 | ||
|
|
1a4decd962 | ||
|
|
6a7afeff53 | ||
|
|
1b4bc427e3 | ||
|
|
2e410301bc | ||
|
|
288cab0840 | ||
|
|
11f2a2cb11 | ||
|
|
070a82333d | ||
|
|
d9e0b4fb90 | ||
|
|
8092da6a0a | ||
|
|
f23d974043 | ||
|
|
27900c5bc8 | ||
|
|
86baaba2c3 | ||
|
|
da6162309c | ||
|
|
84b546db70 | ||
|
|
e84b76e4d1 | ||
|
|
ef33ebe1eb | ||
|
|
92e9bb407f | ||
|
|
1a6a7a403d | ||
|
|
8b929f40d2 | ||
|
|
b8584db48f | ||
|
|
62f3c2bb3d | ||
|
|
0be3f7a6d4 | ||
|
|
4871d198aa | ||
|
|
bc593d25ae | ||
|
|
ada7ee7cab | ||
|
|
391c7a7b8f | ||
|
|
df52adc40f | ||
|
|
e0b8eb3e79 | ||
|
|
488b200fcb | ||
|
|
74cf073bfa | ||
|
|
1b77675ed7 | ||
|
|
a8265cebff | ||
|
|
e06b030707 | ||
|
|
5a88423f62 | ||
|
|
056fb6ef6a | ||
|
|
b459ee250c | ||
|
|
50984cae89 | ||
|
|
812557a964 | ||
|
|
a1f5d1f230 | ||
|
|
f11d3e134b | ||
|
|
0c1640a75a | ||
|
|
db6d930d0c | ||
|
|
0c951b4659 | ||
|
|
19a43b6fbc | ||
|
|
ecb1affd8d | ||
|
|
0bb904bc9f | ||
|
|
6306771a88 | ||
|
|
160b4dec9f | ||
|
|
966307fd3c | ||
|
|
2ec962e2f1 | ||
|
|
849eff9e5b | ||
|
|
edcc957e64 | ||
|
|
b3b7bd0f83 | ||
|
|
4e221ecd3a | ||
|
|
0debe66dd0 | ||
|
|
691bb0af4f | ||
|
|
c3c63da752 | ||
|
|
062edafbe5 | ||
|
|
daea6d0fe8 | ||
|
|
e8404114bb | ||
|
|
253ec934ed | ||
|
|
a768547e80 | ||
|
|
219e7445e4 | ||
|
|
3ee29fead7 | ||
|
|
fcb0f45a4b | ||
|
|
da44c36660 | ||
|
|
1da135fc4c | ||
|
|
bcefe65e54 | ||
|
|
fc5a6a37a6 | ||
|
|
b2ce9331e8 | ||
|
|
e86a1a8a74 | ||
|
|
def9f989dc | ||
|
|
1b5184d39f | ||
|
|
8592039b56 | ||
|
|
e14e55fd94 | ||
|
|
99d59a140c | ||
|
|
f1304bca24 | ||
|
|
97d600e805 | ||
|
|
1c2861f171 | ||
|
|
c14d0fa360 | ||
|
|
2958eb372a | ||
|
|
94ec41d95f | ||
|
|
aa5c88b5b2 | ||
|
|
2bc84cb80b | ||
|
|
d32ec5ecb1 | ||
|
|
7297976843 | ||
|
|
1d52e02107 | ||
|
|
9bd33a386c | ||
|
|
9ca6a052c0 | ||
|
|
8ee828dd21 | ||
|
|
354f50d43c | ||
|
|
45f03edd9e | ||
|
|
03a99583d9 | ||
|
|
0cd68d411f | ||
|
|
344329e5f3 | ||
|
|
3c83e7a69f | ||
|
|
953f58ef3d | ||
|
|
d437b00265 | ||
|
|
49222bef25 | ||
|
|
3adc8119df | ||
|
|
8bad6da348 | ||
|
|
91a77765b6 | ||
|
|
09f5e3e703 | ||
|
|
94351805d3 | ||
|
|
bb3c17d6d5 | ||
|
|
8a60e54d3b | ||
|
|
03a53b97c3 | ||
|
|
b214d68eca | ||
|
|
c051f4ee62 | ||
|
|
03c2a58557 | ||
|
|
a49296e165 | ||
|
|
157325f605 | ||
|
|
de3faf618f | ||
|
|
577ae653de | ||
|
|
8648fad38e | ||
|
|
70e0dd47a6 | ||
|
|
9a486c47b0 | ||
|
|
ae861ef1ae | ||
|
|
83f60f863c | ||
|
|
89b3477446 | ||
|
|
c89e3adb38 | ||
|
|
1c5a22a071 | ||
|
|
a47973d58d | ||
|
|
20938fb6ce | ||
|
|
81ec0d4909 | ||
|
|
f2dfe5f1cf | ||
|
|
a76ba60272 | ||
|
|
595c9424df | ||
|
|
82266ac0d2 | ||
|
|
162ea56aa9 | ||
|
|
535d360027 | ||
|
|
3dd3ad8925 | ||
|
|
366ac40e8d | ||
|
|
8e3d614fb8 | ||
|
|
b2d70bf728 | ||
|
|
53361e3ca3 | ||
|
|
8646cf7571 | ||
|
|
8dfbeecac0 | ||
|
|
67c30075ad | ||
|
|
f31fcb5088 | ||
|
|
b5f11109eb | ||
|
|
9b666caf20 | ||
|
|
f1ba04cf6b | ||
|
|
0f56efea2d | ||
|
|
5eed81cf9f | ||
|
|
8ad8d3f8cd | ||
|
|
53441d3e62 | ||
|
|
a0d7ade863 | ||
|
|
5be368bbf3 | ||
|
|
c0891af5c3 | ||
|
|
a530a353b6 | ||
|
|
fa759b2fb8 | ||
|
|
8935d36ea8 | ||
|
|
d85b9c9bb5 | ||
|
|
2303cf1611 | ||
|
|
e99a6a189f | ||
|
|
5faddc0dc8 | ||
|
|
7a2a1a16f1 | ||
|
|
8aa185135a | ||
|
|
9ee60d1d49 | ||
|
|
3e4e8ed350 | ||
|
|
7f750077dd | ||
|
|
5752eaa2b4 | ||
|
|
337bccb488 | ||
|
|
2022b4e5ea | ||
|
|
410d523f8a | ||
|
|
6e50979045 | ||
|
|
22054cad12 | ||
|
|
3f06e7fda3 | ||
|
|
09f98c2442 | ||
|
|
cca0a6900e | ||
|
|
1a8303ca76 | ||
|
|
9690ddb32b | ||
|
|
a18549cf5b | ||
|
|
3c30dd59e4 | ||
|
|
91c4757cf8 | ||
|
|
53687f2235 | ||
|
|
71b89431ae | ||
|
|
0795eab05b | ||
|
|
b8ac4a5a06 | ||
|
|
75e93ec11e | ||
|
|
3a810e5bb5 | ||
|
|
e2376f553a | ||
|
|
916eb697de | ||
|
|
2cff55b12e | ||
|
|
00f1204bf0 | ||
|
|
28db13c995 | ||
|
|
5970abc85e | ||
|
|
283eeb7de5 | ||
|
|
d52e7a3b9f | ||
|
|
1b551a8665 | ||
|
|
5843ef458d | ||
|
|
c9c962abce | ||
|
|
397fbb9832 | ||
|
|
ebaa4fe4a6 | ||
|
|
c0dc179140 | ||
|
|
c4aa63eab5 | ||
|
|
e61a672209 | ||
|
|
60faaf40d5 | ||
|
|
bcda12db90 | ||
|
|
232b52d939 | ||
|
|
b5f66309ae | ||
|
|
f4bfe234a1 | ||
|
|
008a9b4d6d | ||
|
|
b9517b8490 | ||
|
|
cb8d35c5c3 | ||
|
|
9250f2baaf | ||
|
|
1e2ca1e297 | ||
|
|
61aa257992 | ||
|
|
952d1d6343 | ||
|
|
1eb7a3271e | ||
|
|
bd0dcc979d | ||
|
|
13531c2f3a | ||
|
|
6765de0c38 | ||
|
|
1c5fce1be1 | ||
|
|
866b8fdc25 | ||
|
|
fb9069efe8 | ||
|
|
1494fe3078 | ||
|
|
d52c69d746 | ||
|
|
f6f0108e17 | ||
|
|
7ba8ef01c5 | ||
|
|
579d72f03a | ||
|
|
020382a153 | ||
|
|
dae7e38179 | ||
|
|
69b1cdc964 | ||
|
|
9f3f4bdbd4 | ||
|
|
250d52131c | ||
|
|
17dbc6cc67 | ||
|
|
d7ad9be560 | ||
|
|
0b81ea8f4e | ||
|
|
064a5f5564 | ||
|
|
0d7accc990 | ||
|
|
10e2d3c632 | ||
|
|
13c32d4063 | ||
|
|
df4c8d2698 | ||
|
|
3f0e6591fb | ||
|
|
cd48a1dfc5 | ||
|
|
168866743b | ||
|
|
40ad9805b6 | ||
|
|
8a6275250e | ||
|
|
5e08421c98 | ||
|
|
ed616130b8 | ||
|
|
c2e652adfc | ||
|
|
5dc5c34af3 | ||
|
|
88469e7366 | ||
|
|
15de3600c3 | ||
|
|
379166c66c | ||
|
|
e07e35c104 | ||
|
|
c0779f1260 | ||
|
|
1e59182fda | ||
|
|
fd54c176eb | ||
|
|
4a495377fd | ||
|
|
66cac0665d | ||
|
|
92d160b077 | ||
|
|
da536adca3 | ||
|
|
8b06d3be15 | ||
|
|
2ade7e9a68 | ||
|
|
8d3c8184ce | ||
|
|
7126eec4f0 | ||
|
|
0ff59e626e | ||
|
|
473f1b0e54 | ||
|
|
e5993136ab | ||
|
|
b323f9c322 | ||
|
|
1fcd13e9ff | ||
|
|
11f6b82b72 | ||
|
|
94553504a7 | ||
|
|
1ea9c23576 | ||
|
|
991b2fd3c1 | ||
|
|
c22a6b48f1 | ||
|
|
22295ceef2 |
3
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -28,6 +28,3 @@ If applicable, add screenshots to help explain your problem.
|
||||
- Install source [e.g. installer/SNAP/Docker/NPM]
|
||||
- Type - Web/Application
|
||||
- Database engine: [e.g. MySQL/PostgreSQL/SQL Server]
|
||||
|
||||
**Additional context**
|
||||
Anything else you think might be helpful
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -15,6 +15,3 @@ A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
||||
20
.github/workflows/build-app-beta.yaml
vendored
@@ -10,9 +10,10 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# os: [ubuntu-18.04, windows-2016]
|
||||
os: [macOS-10.15, windows-2016, ubuntu-18.04]
|
||||
os: [macOS-10.15, windows-2022, ubuntu-18.04]
|
||||
# os: [macOS-10.15]
|
||||
|
||||
steps:
|
||||
- name: Context
|
||||
@@ -42,20 +43,19 @@ jobs:
|
||||
if: matrix.os == 'ubuntu-18.04'
|
||||
uses: samuelmeuli/action-snapcraft@v1
|
||||
- name: Publish
|
||||
if: matrix.os != 'macOS-10.15'
|
||||
run: |
|
||||
yarn run build:app
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }} # token for electron publish
|
||||
|
||||
WIN_CSC_LINK: ${{ secrets.WINCERT_CERTIFICATE }}
|
||||
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_PASSWORD }}
|
||||
|
||||
- name: Publish Mac
|
||||
if: matrix.os == 'macOS-10.15'
|
||||
run: |
|
||||
yarn run build:app:mac
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }} # token for electron publish
|
||||
CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }}
|
||||
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||
|
||||
- name: Save snap login
|
||||
if: matrix.os == 'ubuntu-18.04'
|
||||
@@ -81,7 +81,7 @@ jobs:
|
||||
cp app/dist/*win*.exe artifacts/dbgate-beta.exe || true
|
||||
cp app/dist/*win_x64.zip artifacts/dbgate-windows-beta.zip || true
|
||||
cp app/dist/*win_arm64.zip artifacts/dbgate-windows-beta-arm64.zip || true
|
||||
cp app/dist/*-mac.dmg artifacts/dbgate-beta.dmg || true
|
||||
cp app/dist/*-mac_x64.dmg artifacts/dbgate-beta.dmg || true
|
||||
cp app/dist/*-mac_arm64.dmg artifacts/dbgate-beta-arm64.dmg || true
|
||||
|
||||
mv app/dist/*.exe artifacts/ || true
|
||||
|
||||
18
.github/workflows/build-app.yaml
vendored
@@ -14,9 +14,10 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# os: [ubuntu-18.04, windows-2016]
|
||||
os: [macOS-10.15, windows-2016, ubuntu-18.04]
|
||||
os: [macOS-10.15, windows-2022, ubuntu-18.04]
|
||||
|
||||
steps:
|
||||
- name: Context
|
||||
@@ -32,6 +33,8 @@ jobs:
|
||||
node-version: 14.x
|
||||
- name: yarn install
|
||||
run: |
|
||||
# yarn --version
|
||||
# yarn config set network-timeout 300000
|
||||
yarn install
|
||||
- name: setCurrentVersion
|
||||
run: |
|
||||
@@ -50,9 +53,16 @@ jobs:
|
||||
yarn run build:app
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }} # token for electron publish
|
||||
|
||||
WIN_CSC_LINK: ${{ secrets.WINCERT_CERTIFICATE }}
|
||||
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_PASSWORD }}
|
||||
|
||||
CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }}
|
||||
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||
|
||||
- name: generatePadFile
|
||||
run: |
|
||||
yarn generatePadFile
|
||||
@@ -81,8 +91,8 @@ jobs:
|
||||
cp app/dist/*.exe artifacts/dbgate-latest.exe || true
|
||||
cp app/dist/*win_x64.zip artifacts/dbgate-windows-latest.zip || true
|
||||
cp app/dist/*win_arm64.zip artifacts/dbgate-windows-latest-arm64.zip || true
|
||||
cp app/dist/*-mac.dmg artifacts/dbgate-latest.dmg || true
|
||||
cp app/dist/*-mac_arm64.dmg artifacts/dbgate-latest-arm64.dmg || true
|
||||
cp app/dist/*-mac_universal.dmg artifacts/dbgate-latest.dmg || true
|
||||
cp app/dist/*-mac_x64.dmg artifacts/dbgate-latest-x64.dmg || true
|
||||
|
||||
mv app/dist/*.exe artifacts/ || true
|
||||
mv app/dist/*.zip artifacts/ || true
|
||||
@@ -116,7 +126,7 @@ jobs:
|
||||
# mv app/dist/latest.yml artifacts/latest.yml || true
|
||||
|
||||
- name: Copy latest.yml (windows)
|
||||
if: matrix.os == 'windows-2016'
|
||||
if: matrix.os == 'windows-2022'
|
||||
run: |
|
||||
mv app/dist/latest.yml artifacts/latest.yml || true
|
||||
mv app/dist/dbgate-pad.xml artifacts/ || true
|
||||
|
||||
11
.github/workflows/build-docker-beta.yaml
vendored
@@ -6,6 +6,7 @@ on:
|
||||
push:
|
||||
tags:
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+'
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+-docker.[0-9]+'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -30,6 +31,8 @@ jobs:
|
||||
node-version: 14.x
|
||||
- name: yarn install
|
||||
run: |
|
||||
# yarn --version
|
||||
# yarn config set network-timeout 300000
|
||||
yarn install
|
||||
- name: setCurrentVersion
|
||||
run: |
|
||||
@@ -45,3 +48,11 @@ jobs:
|
||||
docker tag dbgate dbgate/dbgate:beta
|
||||
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
|
||||
docker push dbgate/dbgate:beta
|
||||
- name: Build alpine docker image
|
||||
run: |
|
||||
docker build ./docker -t dbgate -f docker/Dockerfile-alpine
|
||||
- name: Push alpine docker image
|
||||
run: |
|
||||
docker tag dbgate dbgate/dbgate:beta-alpine
|
||||
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
|
||||
docker push dbgate/dbgate:beta-alpine
|
||||
|
||||
8
.github/workflows/build-docker.yaml
vendored
@@ -51,3 +51,11 @@ jobs:
|
||||
docker tag dbgate dbgate/dbgate
|
||||
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
|
||||
docker push dbgate/dbgate
|
||||
- name: Build alpine docker image
|
||||
run: |
|
||||
docker build ./docker -t dbgate -f docker/Dockerfile-alpine
|
||||
- name: Push alpine docker image
|
||||
run: |
|
||||
docker tag dbgate dbgate/dbgate:alpine
|
||||
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
|
||||
docker push dbgate/dbgate:alpine
|
||||
|
||||
24
.github/workflows/build-npm.yaml
vendored
@@ -79,26 +79,31 @@ jobs:
|
||||
run: |
|
||||
npm publish
|
||||
|
||||
- name: Publish query-splitter
|
||||
working-directory: packages/query-splitter
|
||||
run: |
|
||||
npm publish
|
||||
|
||||
- name: Publish web
|
||||
working-directory: packages/web
|
||||
run: |
|
||||
npm publish
|
||||
|
||||
- name: Publish dbgate
|
||||
|
||||
- name: Publish dbgate (obsolete)
|
||||
working-directory: packages/dbgate
|
||||
run: |
|
||||
npm publish
|
||||
|
||||
- name: Publish dbgate-serve
|
||||
working-directory: packages/serve
|
||||
run: |
|
||||
npm publish
|
||||
|
||||
- name: Publish dbgate-plugin-csv
|
||||
working-directory: plugins/dbgate-plugin-csv
|
||||
run: |
|
||||
npm publish
|
||||
|
||||
- name: Publish dbgate-plugin-xml
|
||||
working-directory: plugins/dbgate-plugin-xml
|
||||
run: |
|
||||
npm publish
|
||||
|
||||
- name: Publish dbgate-plugin-excel
|
||||
working-directory: plugins/dbgate-plugin-excel
|
||||
run: |
|
||||
@@ -128,3 +133,8 @@ jobs:
|
||||
working-directory: plugins/dbgate-plugin-sqlite
|
||||
run: |
|
||||
npm publish
|
||||
|
||||
- name: Publish dbgate-plugin-redis
|
||||
working-directory: plugins/dbgate-plugin-redis
|
||||
run: |
|
||||
npm publish
|
||||
|
||||
11
.github/workflows/run-tests.yaml
vendored
@@ -31,11 +31,6 @@ jobs:
|
||||
run: |
|
||||
cd packages/filterparser
|
||||
yarn test:ci
|
||||
- name: Query spliiter tests
|
||||
if: always()
|
||||
run: |
|
||||
cd packages/query-splitter
|
||||
yarn test:ci
|
||||
- uses: tanmen/jest-reporter@v1
|
||||
if: always()
|
||||
with:
|
||||
@@ -48,12 +43,6 @@ jobs:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
result-file: packages/filterparser/result.json
|
||||
action-name: Filter parser test results
|
||||
- uses: tanmen/jest-reporter@v1
|
||||
if: always()
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
result-file: packages/query-splitter/result.json
|
||||
action-name: Query splitter test results
|
||||
|
||||
services:
|
||||
postgres:
|
||||
|
||||
176
CHANGELOG.md
@@ -1,5 +1,181 @@
|
||||
# ChangeLog
|
||||
|
||||
Builds:
|
||||
- docker - build
|
||||
- npm - npm package dbgate-serve
|
||||
- app - classic electron app
|
||||
- mac - application for macOS
|
||||
- linux - application for linux
|
||||
- win - application for Windows
|
||||
|
||||
### 4.8.7
|
||||
- ADDED: MySQL dump/backup database
|
||||
- ADDED: Import SQL dump from file or from URL
|
||||
- FIXED(mac): Fixed Cmd+C, Cmd+V, Cmd+X - shortcuts for copy/cut/paste #270
|
||||
- FIXED(mac): Some minor issues on macOS
|
||||
- FIXED: Analysing MS SQL nvarchar(max)
|
||||
- ADDED: Support for dockerhost network name under docker #271
|
||||
|
||||
### 4.8.4
|
||||
- FIXED(mac): Fixed build for macOS arm64 #259
|
||||
- FIXED(mac): Fixed opening SQLite files on macOS #243
|
||||
- FIXED(mac): Fixed opening PEM certificates on macOS #206
|
||||
- FIXED(mac): Fixed handling Command key on macOS
|
||||
- FIXED(mac): Fixed system menu on macOS
|
||||
- FIXED(mac): Fixed reopening main window on macOS
|
||||
- CHANGED: Shortcut for net query is now Ctrl+T or Command+T on macOS, former it was Ctrl+Q
|
||||
- FIXED: Fixed misplaced tab close icon #260
|
||||
- ADDED: Added menu command "Tools/Change to recent database"
|
||||
|
||||
### 4.8.3
|
||||
- FIXED: filters in query result and NDJSON/archive viewer
|
||||
- ADDED: Added select values from query result and NDJSON/archive viewer
|
||||
- ADDED: tab navigation in datagrid #254
|
||||
- ADDED: Keyboard shortcuts added to help menu #254
|
||||
- ADDED: API logging (run enableApiLog() in developers console to enable logging)
|
||||
- ADDED: SSH reconnect + moved SSH forward into separate fork #253
|
||||
- ADDED: Data type + reference link in column manager
|
||||
- FIXED(win,linux,mac): Unable to change theme after installing plugin #244
|
||||
|
||||
### 4.8.2
|
||||
- ADDED: implemented missing redis search key logic
|
||||
|
||||
### 4.8.1
|
||||
- FIXED: fixed crash after disconnecting from all DBs
|
||||
|
||||
### 4.8.0
|
||||
- ADDED: Redis support (support stream type), removed experimental status
|
||||
- ADDED: Redis readonly support
|
||||
- ADDED: Explicit NDJSON support, when opening NDJSON/JSON lines file, table data are immediately shown, without neccesarity to import
|
||||
- ADDED(win,linux,mac): Opening developer tools when crashing without reload app
|
||||
### 4.7.4
|
||||
- ADDED: Experimental Redis support (full support is planned to version 4.8.0)
|
||||
- ADDED: Read-only connections
|
||||
- FIXED: MongoDB filters
|
||||
- ADDED: MongoDB column value selection
|
||||
- ADDED: App related queries
|
||||
- ADDED: Fuzzy search #246
|
||||
- ADDED(docker, npm): New permissions
|
||||
- FIXED(npm): NPM build no longer allocates additonal ports
|
||||
- CHANGED(npm): renamed NPM package dbgate => dbgate-serve
|
||||
- CHANGED(docker): custom JavaScripts and connections defined in scripts are now prohibited by default, use SHELL_CONNECTION and SHELL_SCRIPTING environment variables for allowing this
|
||||
- ADDED(docker, npm): Better documentation of environment variables configuration, https://dbgate.org/docs/env-variables.html
|
||||
- ADDED(docker): support for multiple users with different permissions
|
||||
- ADDED(docker): logout operation
|
||||
|
||||
### 4.7.3
|
||||
- CHANGED: Export menu redesign, quick export menu merged with old export menu
|
||||
- REMOVED: Quick export menu
|
||||
- ADDED: Export column mapping
|
||||
- ADDED: Export invoked from data grid respects columns choosed in column manager
|
||||
- ADDED: Quick export (now merged in export menu) is now possible also in web app
|
||||
- FIXED: Virtual foreign key editor fixes
|
||||
- FIXED: Tabs panel style fix
|
||||
- ADDED: Find by schema in databases widget
|
||||
- FIXED: Column manager selection fix
|
||||
- FIXED: NPM dist - fixed error when loading plugins
|
||||
- CHANGED: NPN dist is now executed by dbgate-serve command
|
||||
- ADDED: NPM dist accepts .env configuration
|
||||
|
||||
### 4.7.2
|
||||
- CHANGED: documentation URL - https://dbgate.org/docs/
|
||||
- CHANGED: Close button available for all tab groups - #238
|
||||
- ADDED: Search function for the Keyboard Shortcuts overview - #239
|
||||
- ADDED: Editor font size settings - #229
|
||||
- ADDED: Rename MongoDB collection - #223
|
||||
- FIXED: bug in cache subsystem
|
||||
|
||||
### 4.7.1
|
||||
- FIXED: Fixed connecting to MS SQL server running in docker container from DbGate running in docker container #236
|
||||
- FIXED: Fixed export MongoDB collections into Excel and CSV #240
|
||||
- ADDED: Added support for docker volumes to persiste connections, when not using configuration via env variables #232
|
||||
- ADDED: DbGate in Docker can run in subdirectory #228
|
||||
- FIXED: DbGate in Docker can be proxied with nginx #228
|
||||
- FIDED: Theme persists when opening multiple windows #207
|
||||
- ADDED: Remember fullscreen state #230
|
||||
- ADDED: Improved fullscreen state, title bar with menu is hidden, menu is in hamburger menu, like in web version
|
||||
- ADDED: Theme choose dialog (added as tab in settings)
|
||||
- FIXED: Fixed crash when clicking on application layers #231
|
||||
### 4.7.0
|
||||
- CHANGED: Changed main menu style, menu and title bar is in one line (+ability to switch to system menu)
|
||||
- REMOVED: Removed main toolbar, use main menu or tab related bottom tool instead
|
||||
- ADDED: Added tab related context bottom toolbar
|
||||
- ADDED: Main menu is available also in web application, by clicking on hamburger menu
|
||||
- ADDED: Added support of SQLite to docker container #219
|
||||
- ADDED: Added Debian and Alpine docker distributions (default is Debian)
|
||||
- FIXED: Fixed performance problem of data grid, especially when there are cells with large data (eg. JSONs), now it is much faster
|
||||
- ADDED: Open JSON and array cell buttons
|
||||
- ADDED: Handle JSON in varchar cells
|
||||
- ADDED: Scroll tabs on mouse wheel
|
||||
- ADDED: Show edit edit MySQL column comments #218 #81
|
||||
- ADDED: Handle sparse (mssql), unsigned (mysql), zerofill (mysql) column flags
|
||||
- FIXED: Fixed same caching problems (eg. leading to indefinitely loading DB structure sometimes)
|
||||
- ADDED: Show estimated table row count for MySQL and MS SQL
|
||||
- FIXED: Fixed deleting rows from added rows in table data editor
|
||||
- ADDED: Better work with JSON lines file, added JSONL editor with preview
|
||||
|
||||
### 4.6.3
|
||||
- FIXED: Fixed Windows build
|
||||
- FIXED: Fixed crash, when there is invalid value in browser local storage
|
||||
- FIXED: Fixed plugin description display, where author name or description is not correctly filled
|
||||
|
||||
### 4.6.2
|
||||
- FIXED: Fixed issues of XML import plugin
|
||||
- ADDED: Split columns macro (available in data sheet editor)
|
||||
- CHANGED: Accepting non standard plugins names (which doesn't start with dbgate-plugin-)
|
||||
- ADDED: Support BLOB values #211
|
||||
- ADDED: Picture cell view
|
||||
- ADDED: HTML cell view
|
||||
- CHANGED: Code completion supports non-default schema names
|
||||
- FIXED: More robust MySQL analyser, when connecting to non-standard servers #214
|
||||
- FIXED: Fixed configuring connection to SQLite with environment variables #215
|
||||
|
||||
### 4.6.1
|
||||
- ADDED: Ability to configure SSH tunnel over environment variables #210 (for docker container)
|
||||
- ADDED: XML export and import
|
||||
- ADDED: Archive file - show and edit source text file
|
||||
- ADDED: Window title shows current tab and database
|
||||
- ADDED: DbGate documentation
|
||||
- ADDED: Introduced application layers
|
||||
- ADDED: Virtual foreign key editor
|
||||
- ADDED: Application commands (SQL scripts related to database)
|
||||
- ADDED: Theme can be implemented in plugin
|
||||
- CHANGED: Dictionary description is stored in app
|
||||
- FIXED: Unique and index editor
|
||||
- FIXED: Posibility to edit UNIQUE index flag
|
||||
- CHANGED: UX improvements of table editor
|
||||
|
||||
### 4.6.0
|
||||
- ADDED: ER diagrams #118
|
||||
- Generate diagram from table or for database
|
||||
- Automatic layout
|
||||
- Diagram styles - colors, select columns to display, optional displaying data type or nullability
|
||||
- Export diagram to HTML file
|
||||
- FIXED: Mac latest build link #204
|
||||
|
||||
### 4.5.1
|
||||
- FIXED: MongoId detection
|
||||
- FIXED: #203 disabled spellchecker
|
||||
- FIXED: Prevented display filters in form view twice
|
||||
- FIXED: Query designer fixes
|
||||
|
||||
### 4.5.0
|
||||
- ADDED: #220 functions, materialized views and stored procedures in code completion
|
||||
- ADDED: Query result in statusbar
|
||||
- ADDED: Highlight and execute current query
|
||||
- CHANGED: Code completion offers objects only from current query
|
||||
- CHANGED: Big optimalizations of electron app - removed embedded web server, removed remote module, updated electron to version 13
|
||||
- CHANGED: Removed dependency to electron-store module
|
||||
- FIXED: #201 fixed database URL definition, when running from Docvker container
|
||||
- FIXED: #192 Docker container stops in 1 second, ability to stop container with Ctrl+C
|
||||
- CHANGED: Web app - websocket replaced with SSE technology
|
||||
- CHANGED: Changed tab order, tabs are ordered by creation time
|
||||
- ADDED: Reorder tabs with drag & drop
|
||||
- CHANGED: Collapse left column in datagrid - removed from settings, remember last used state
|
||||
- ADDED: Ability to select multiple columns in column manager in datagrid + copy column names
|
||||
- ADDED: Show used filters in left datagrid column
|
||||
- FIXED: Fixed delete dependency cycle detection (delete didn't work for some tables)
|
||||
|
||||
### 4.4.4
|
||||
- FIXED: Database colors
|
||||
- CHANGED: Precise work with MongoDB ObjectId
|
||||
|
||||
50
README.md
@@ -1,4 +1,4 @@
|
||||
[](https://www.npmjs.com/package/dbgate)
|
||||
[](https://www.npmjs.com/package/dbgate-serve)
|
||||

|
||||
[](https://snapcraft.io/dbgate)
|
||||
[](https://snapcraft.io/dbgate)
|
||||
@@ -16,13 +16,14 @@ DbGate is licensed under MIT license and is completely free.
|
||||
|
||||
* Try it online - [demo.dbgate.org](https://demo.dbgate.org) - online demo application
|
||||
* **Download** application for Windows, Linux or Mac from [dbgate.org](https://dbgate.org/download/)
|
||||
* Run web version as [NPM package](https://www.npmjs.com/package/dbgate) or as [docker image](https://hub.docker.com/r/dbgate/dbgate)
|
||||
* Run web version as [NPM package](https://www.npmjs.com/package/dbgate-serve) or as [docker image](https://hub.docker.com/r/dbgate/dbgate)
|
||||
|
||||
## Supported databases
|
||||
* MySQL
|
||||
* PostgreSQL
|
||||
* SQL Server
|
||||
* MongoDB
|
||||
* Redis
|
||||
* SQLite
|
||||
* Amazon Redshift
|
||||
* CockroachDB
|
||||
@@ -50,6 +51,7 @@ DbGate is licensed under MIT license and is completely free.
|
||||
* Table data editing, with SQL change script preview
|
||||
* Edit table schema, indexes, primary and foreign keys
|
||||
* Compare and synchronize database structure
|
||||
* ER diagram
|
||||
* Light and dark theme
|
||||
* Master/detail views, foreign key lookups
|
||||
* Query designer
|
||||
@@ -62,8 +64,9 @@ DbGate is licensed under MIT license and is completely free.
|
||||
* SQL code completion
|
||||
* Add SQL LEFT/INNER/RIGHT join utility
|
||||
* Mongo JavaScript editor, execute Mongo script (with NodeJs syntax)
|
||||
* Redis tree view, generate script from keys, run Redis script
|
||||
* Runs as application for Windows, Linux and Mac. Or in Docker container on server and in web Browser on client.
|
||||
* Import, export from/to CSV, Excel, JSON
|
||||
* Import, export from/to CSV, Excel, JSON, XML
|
||||
* Free table editor - quick table data editing (cleanup data after import/before export, prototype tables etc.)
|
||||
* Archives - backup your data in JSON files on local filesystem (or on DbGate server, when using web application)
|
||||
* Charts, export chart to HTML page
|
||||
@@ -77,6 +80,7 @@ Any contributions are welcome. If you want to contribute without coding, conside
|
||||
* Write review on [Slant.co](https://www.slant.co/improve/options/41086/~dbgate-review) or [G2](https://www.g2.com/products/dbgate/reviews)
|
||||
* Create issue, if you find problem in app, or you have idea to new feature. If issue already exists, you could leave comment on it, to prioritise most wanted issues.
|
||||
* Become a backer on [Open collective](https://opencollective.com/dbgate)
|
||||
* Where a small coding is acceptable for you, you could [create plugin](https://dbgate.org/docs/plugin-development.html). Plugins for new themes can be created actually without JS coding.
|
||||
|
||||
Thank you!
|
||||
|
||||
@@ -108,27 +112,42 @@ Basic set of plugins is part of DbGate git repository and is installed with app.
|
||||
|
||||
## How to run development environment
|
||||
|
||||
Simple variant - runs WEB application:
|
||||
```sh
|
||||
yarn
|
||||
yarn start
|
||||
```
|
||||
|
||||
If you want to make modifications in libraries or plugins, run library compiler in watch mode in the second terminal:
|
||||
If you want more control, run WEB application:
|
||||
```sh
|
||||
yarn lib
|
||||
yarn # install NPM packages
|
||||
```
|
||||
|
||||
And than run following 3 commands concurrently in 3 terminals:
|
||||
```
|
||||
yarn start:api # run API on port 3000
|
||||
yarn start:web # run web on port 5000
|
||||
yarn lib # watch typescript libraries and plugins modifications
|
||||
```
|
||||
This runs API on port 3000 and web application on port 5000
|
||||
Open http://localhost:5000 in your browser
|
||||
|
||||
You could run electron app (requires running localhost:5000):
|
||||
If you want to run electron app:
|
||||
```sh
|
||||
yarn # install NPM packages
|
||||
cd app
|
||||
yarn
|
||||
yarn start
|
||||
yarn # install NPM packages for electron
|
||||
```
|
||||
|
||||
And than run following 3 commands concurrently in 3 terminals:
|
||||
```
|
||||
yarn start:web # run web on port 5000 (only static JS and HTML files)
|
||||
yarn lib # watch typescript libraries and plugins modifications
|
||||
yarn start:app # run electron app
|
||||
```
|
||||
|
||||
## How to run built electron app locally
|
||||
This mode is very similar to production run of electron app. Electron app forks process with API on dynamically allocated port, works with compiled javascript files and uses compiled version of plugins (doesn't use localhost:5000)
|
||||
This mode is very similar to production run of electron app. Electron doesn't use localhost:5000.
|
||||
|
||||
```sh
|
||||
cd app
|
||||
@@ -141,3 +160,16 @@ yarn build:app:local
|
||||
yarn start:app:local
|
||||
```
|
||||
|
||||
## How to create plugin
|
||||
Creating plugin is described in [documentation](https://github.com/dbgate/dbgate/wiki/Plugin-development)
|
||||
|
||||
But it is very simple:
|
||||
|
||||
```sh
|
||||
npm install -g yo # install yeoman
|
||||
npm install -g generator-dbgate # install dbgate generator
|
||||
cd dbgate-plugin-my-new-plugin # this directory is created by wizard, edit, what you need to change
|
||||
yarn plugin # this compiles plugin and copies it into existing DbGate installation
|
||||
```
|
||||
|
||||
After restarting DbGate, you could use your new plugin from DbGate.
|
||||
12
app/entitlements.mac.plist
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.disable-library-validation</key><true/>
|
||||
<key>com.apple.security.cs.allow-jit</key><true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key><true/>
|
||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key><true/>
|
||||
<key>com.apple.security.files.user-selected.read-only</key><true/>
|
||||
<key>com.apple.security.files.user-selected.read-write</key><true/>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
app/icon.ico
|
Before Width: | Height: | Size: 182 KiB After Width: | Height: | Size: 158 KiB |
BIN
app/icon.png
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 36 KiB |
BIN
app/icon32.png
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 15 KiB |
BIN
app/icon512.png
|
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 18 KiB |
@@ -6,8 +6,8 @@
|
||||
"description": "Opensource database administration tool",
|
||||
"dependencies": {
|
||||
"electron-log": "^4.4.1",
|
||||
"electron-store": "^8.0.1",
|
||||
"electron-updater": "^4.6.1",
|
||||
"lodash.clonedeepwith": "^4.5.0",
|
||||
"patch-package": "^6.4.7"
|
||||
},
|
||||
"repository": {
|
||||
@@ -15,18 +15,23 @@
|
||||
"url": "https://github.com/dbgate/dbgate.git"
|
||||
},
|
||||
"build": {
|
||||
"artifactName": "${productName}-${version}-${os}_${arch}.${ext}",
|
||||
"artifactName": "dbgate-${version}-${os}_${arch}.${ext}",
|
||||
"appId": "org.dbgate",
|
||||
"productName": "DbGate",
|
||||
"afterSign": "electron-builder-notarize",
|
||||
"mac": {
|
||||
"category": "database",
|
||||
"icon": "icon512.png",
|
||||
"hardenedRuntime": true,
|
||||
"entitlements": "entitlements.mac.plist",
|
||||
"entitlementsInherit": "entitlements.mac.plist",
|
||||
"publish": [
|
||||
"github"
|
||||
],
|
||||
"target": {
|
||||
"target": "default",
|
||||
"arch": [
|
||||
"arm64",
|
||||
"universal",
|
||||
"x64"
|
||||
]
|
||||
}
|
||||
@@ -93,17 +98,18 @@
|
||||
"start:local": "cross-env electron .",
|
||||
"dist": "electron-builder",
|
||||
"build": "cd ../packages/api && yarn build && cd ../web && yarn build && cd ../../app && yarn dist",
|
||||
"build:mac": "cd ../packages/api && yarn build && cd ../web && yarn build && cd ../../app && node setMacPlatform x64 && yarn dist && node setMacPlatform arm64 && yarn dist",
|
||||
"build:local": "cd ../packages/api && yarn build && cd ../web && yarn build && cd ../../app && yarn predist",
|
||||
"postinstall": "electron-builder install-app-deps && patch-package",
|
||||
"postinstall": "yarn rebuild && patch-package",
|
||||
"rebuild": "electron-builder install-app-deps",
|
||||
"predist": "copyfiles ../packages/api/dist/* packages && copyfiles \"../packages/web/public/*\" packages && copyfiles \"../packages/web/public/**/*\" packages && copyfiles --up 3 \"../plugins/dist/**/*\" packages/plugins"
|
||||
},
|
||||
"main": "src/electron.js",
|
||||
"devDependencies": {
|
||||
"copyfiles": "^2.2.0",
|
||||
"cross-env": "^6.0.3",
|
||||
"electron": "12.1.0",
|
||||
"electron-builder": "22.14.5"
|
||||
"electron": "13.6.3",
|
||||
"electron-builder": "22.14.5",
|
||||
"electron-builder-notarize": "^1.4.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"better-sqlite3": "7.4.5",
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
const fs = require('fs');
|
||||
|
||||
const text = fs.readFileSync('package.json', { encoding: 'utf-8' });
|
||||
const json = JSON.parse(text);
|
||||
|
||||
json.build.mac.target.arch = process.argv[2];
|
||||
|
||||
fs.writeFileSync('package.json', JSON.stringify(json, null, 2), { encoding: 'utf-8' });
|
||||
@@ -1,10 +1,10 @@
|
||||
const electron = require('electron');
|
||||
const os = require('os');
|
||||
const fs = require('fs');
|
||||
const { Menu, ipcMain } = require('electron');
|
||||
const { fork } = require('child_process');
|
||||
const { autoUpdater } = require('electron-updater');
|
||||
const Store = require('electron-store');
|
||||
const log = require('electron-log');
|
||||
const _cloneDeepWith = require('lodash.clonedeepwith');
|
||||
|
||||
// Module to control application life.
|
||||
const app = electron.app;
|
||||
@@ -13,15 +13,39 @@ const BrowserWindow = electron.BrowserWindow;
|
||||
|
||||
const path = require('path');
|
||||
const url = require('url');
|
||||
const mainMenuDefinition = require('./mainMenuDefinition');
|
||||
const { settings } = require('cluster');
|
||||
|
||||
// require('@electron/remote/main').initialize();
|
||||
|
||||
const store = new Store();
|
||||
const configRootPath = path.join(app.getPath('userData'), 'config-root.json');
|
||||
let initialConfig = {};
|
||||
let apiLoaded = false;
|
||||
let mainModule;
|
||||
|
||||
const isMac = () => os.platform() == 'darwin';
|
||||
|
||||
try {
|
||||
initialConfig = JSON.parse(fs.readFileSync(configRootPath, { encoding: 'utf-8' }));
|
||||
} catch (err) {
|
||||
console.log('Error loading config-root:', err.message);
|
||||
initialConfig = {};
|
||||
}
|
||||
|
||||
// let settingsJson = {};
|
||||
// try {
|
||||
// const datadir = path.join(os.homedir(), 'dbgate-data');
|
||||
// settingsJson = JSON.parse(fs.readFileSync(path.join(datadir, 'settings.json'), { encoding: 'utf-8' }));
|
||||
// } catch (err) {
|
||||
// console.log('Error loading settings.json:', err.message);
|
||||
// settingsJson = {};
|
||||
// }
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow;
|
||||
let mainMenu;
|
||||
let runCommandOnLoad = null;
|
||||
|
||||
log.transports.file.level = 'debug';
|
||||
autoUpdater.logger = log;
|
||||
@@ -30,103 +54,71 @@ autoUpdater.logger = log;
|
||||
|
||||
let commands = {};
|
||||
|
||||
function commandItem(id) {
|
||||
function formatKeyText(keyText) {
|
||||
if (!keyText) {
|
||||
return keyText;
|
||||
}
|
||||
if (os.platform() == 'darwin') {
|
||||
return keyText.replace('CtrlOrCommand+', 'Command+');
|
||||
}
|
||||
return keyText.replace('CtrlOrCommand+', 'Ctrl+');
|
||||
}
|
||||
|
||||
function commandItem(item) {
|
||||
const id = item.command;
|
||||
const command = commands[id];
|
||||
if (item.skipInApp) {
|
||||
return { skip: true };
|
||||
}
|
||||
return {
|
||||
id,
|
||||
label: command ? command.menuName || command.toolbarName || command.name : id,
|
||||
accelerator: command ? command.keyText : undefined,
|
||||
accelerator: formatKeyText(command ? command.keyText : undefined),
|
||||
enabled: command ? command.enabled : false,
|
||||
click() {
|
||||
mainWindow.webContents.send('run-command', id);
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('run-command', id);
|
||||
} else {
|
||||
runCommandOnLoad = id;
|
||||
createWindow();
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildMenu() {
|
||||
const template = [
|
||||
{
|
||||
label: 'File',
|
||||
submenu: [
|
||||
commandItem('new.connection'),
|
||||
commandItem('new.sqliteDatabase'),
|
||||
commandItem('new.modelCompare'),
|
||||
commandItem('new.freetable'),
|
||||
{ type: 'separator' },
|
||||
commandItem('file.open'),
|
||||
commandItem('file.openArchive'),
|
||||
{ type: 'separator' },
|
||||
commandItem('group.save'),
|
||||
commandItem('group.saveAs'),
|
||||
commandItem('database.search'),
|
||||
{ type: 'separator' },
|
||||
commandItem('tabs.closeTab'),
|
||||
commandItem('file.exit'),
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Window',
|
||||
submenu: [commandItem('new.query'), { type: 'separator' }, commandItem('tabs.closeAll'), { role: 'minimize' }],
|
||||
},
|
||||
let template = _cloneDeepWith(mainMenuDefinition({ editMenu: true }), item => {
|
||||
if (item.divider) {
|
||||
return { type: 'separator' };
|
||||
}
|
||||
|
||||
// {
|
||||
// label: 'Edit',
|
||||
// submenu: [
|
||||
// { role: 'undo' },
|
||||
// { role: 'redo' },
|
||||
// { type: 'separator' },
|
||||
// { role: 'cut' },
|
||||
// { role: 'copy' },
|
||||
// { role: 'paste' },
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
label: 'View',
|
||||
submenu: [
|
||||
{ role: 'reload' },
|
||||
{ role: 'forcereload' },
|
||||
{ role: 'toggledevtools' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'resetzoom' },
|
||||
{ role: 'zoomin' },
|
||||
{ role: 'zoomout' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'togglefullscreen' },
|
||||
commandItem('theme.changeTheme'),
|
||||
],
|
||||
},
|
||||
{
|
||||
role: 'help',
|
||||
submenu: [
|
||||
{
|
||||
label: 'dbgate.org',
|
||||
click() {
|
||||
electron.shell.openExternal('https://dbgate.org');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'DbGate on GitHub',
|
||||
click() {
|
||||
electron.shell.openExternal('https://github.com/dbgate/dbgate');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'DbGate on docker hub',
|
||||
click() {
|
||||
electron.shell.openExternal('https://hub.docker.com/r/dbgate/dbgate');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Report problem or feature request',
|
||||
click() {
|
||||
electron.shell.openExternal('https://github.com/dbgate/dbgate/issues/new');
|
||||
},
|
||||
},
|
||||
commandItem('tabs.changelog'),
|
||||
commandItem('about.show'),
|
||||
],
|
||||
},
|
||||
];
|
||||
if (item.command) {
|
||||
return commandItem(item);
|
||||
}
|
||||
});
|
||||
|
||||
template = _cloneDeepWith(template, item => {
|
||||
if (Array.isArray(item) && item.find(x => x.skip)) {
|
||||
return item.filter(x => x && !x.skip);
|
||||
}
|
||||
});
|
||||
|
||||
if (isMac()) {
|
||||
template = [
|
||||
{
|
||||
label: 'DbGate',
|
||||
submenu: [
|
||||
commandItem({ command: 'about.show' }),
|
||||
{ role: 'services' },
|
||||
{ role: 'hide' },
|
||||
{ role: 'hideOthers' },
|
||||
{ role: 'unhide' },
|
||||
{ role: 'quit' },
|
||||
],
|
||||
},
|
||||
...template,
|
||||
];
|
||||
}
|
||||
|
||||
return Menu.buildFromTemplate(template);
|
||||
}
|
||||
@@ -141,15 +133,94 @@ ipcMain.on('update-commands', async (event, arg) => {
|
||||
// rebuild menu
|
||||
if (menu.label != command.text || menu.accelerator != command.keyText) {
|
||||
mainMenu = buildMenu();
|
||||
mainWindow.setMenu(mainMenu);
|
||||
|
||||
Menu.setApplicationMenu(mainMenu);
|
||||
// mainWindow.setMenu(mainMenu);
|
||||
return;
|
||||
}
|
||||
|
||||
menu.enabled = command.enabled;
|
||||
}
|
||||
});
|
||||
ipcMain.on('close-window', async (event, arg) => {
|
||||
mainWindow.close();
|
||||
ipcMain.on('quit-app', async (event, arg) => {
|
||||
if (isMac()) {
|
||||
app.quit();
|
||||
} else {
|
||||
mainWindow.close();
|
||||
}
|
||||
});
|
||||
ipcMain.on('set-title', async (event, arg) => {
|
||||
mainWindow.setTitle(arg);
|
||||
});
|
||||
ipcMain.on('open-link', async (event, arg) => {
|
||||
electron.shell.openExternal(arg);
|
||||
});
|
||||
ipcMain.on('open-dev-tools', () => {
|
||||
mainWindow.webContents.openDevTools();
|
||||
});
|
||||
ipcMain.on('app-started', async (event, arg) => {
|
||||
if (runCommandOnLoad) {
|
||||
mainWindow.webContents.send('run-command', runCommandOnLoad);
|
||||
runCommandOnLoad = null;
|
||||
}
|
||||
});
|
||||
ipcMain.on('window-action', async (event, arg) => {
|
||||
if (!mainWindow) {
|
||||
return;
|
||||
}
|
||||
switch (arg) {
|
||||
case 'minimize':
|
||||
mainWindow.minimize();
|
||||
break;
|
||||
case 'maximize':
|
||||
if (mainWindow.isMaximized()) {
|
||||
mainWindow.unmaximize();
|
||||
} else {
|
||||
mainWindow.maximize();
|
||||
}
|
||||
break;
|
||||
case 'close':
|
||||
mainWindow.close();
|
||||
break;
|
||||
case 'fullscreen-on':
|
||||
mainWindow.setFullScreen(true);
|
||||
break;
|
||||
case 'fullscreen-off':
|
||||
mainWindow.setFullScreen(false);
|
||||
break;
|
||||
case 'devtools':
|
||||
mainWindow.webContents.toggleDevTools();
|
||||
break;
|
||||
case 'reload':
|
||||
mainWindow.webContents.reloadIgnoringCache();
|
||||
break;
|
||||
case 'zoomin':
|
||||
mainWindow.webContents.zoomLevel += 0.5;
|
||||
break;
|
||||
case 'zoomout':
|
||||
mainWindow.webContents.zoomLevel -= 0.5;
|
||||
break;
|
||||
case 'zoomreset':
|
||||
mainWindow.webContents.zoomLevel = 0;
|
||||
break;
|
||||
|
||||
// edit
|
||||
case 'undo':
|
||||
mainWindow.webContents.undo();
|
||||
break;
|
||||
case 'redo':
|
||||
mainWindow.webContents.redo();
|
||||
break;
|
||||
case 'cut':
|
||||
mainWindow.webContents.cut();
|
||||
break;
|
||||
case 'copy':
|
||||
mainWindow.webContents.copy();
|
||||
break;
|
||||
case 'paste':
|
||||
mainWindow.webContents.paste();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('showOpenDialog', async (event, options) => {
|
||||
@@ -167,24 +238,54 @@ ipcMain.handle('openExternal', async (event, url) => {
|
||||
electron.shell.openExternal(url);
|
||||
});
|
||||
|
||||
function fillMissingSettings(value) {
|
||||
const res = {
|
||||
...value,
|
||||
};
|
||||
if (value['app.useNativeMenu'] !== true && value['app.useNativeMenu'] !== false) {
|
||||
res['app.useNativeMenu'] = false;
|
||||
// res['app.useNativeMenu'] = os.platform() == 'darwin' ? true : false;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function createWindow() {
|
||||
const bounds = store.get('winBounds');
|
||||
let settingsJson = {};
|
||||
try {
|
||||
const datadir = path.join(os.homedir(), 'dbgate-data');
|
||||
settingsJson = fillMissingSettings(
|
||||
JSON.parse(fs.readFileSync(path.join(datadir, 'settings.json'), { encoding: 'utf-8' }))
|
||||
);
|
||||
} catch (err) {
|
||||
console.log('Error loading settings.json:', err.message);
|
||||
settingsJson = fillMissingSettings({});
|
||||
}
|
||||
|
||||
const bounds = initialConfig['winBounds'];
|
||||
useNativeMenu = settingsJson['app.useNativeMenu'];
|
||||
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 1200,
|
||||
height: 800,
|
||||
title: 'DbGate',
|
||||
frame: useNativeMenu,
|
||||
titleBarStyle: useNativeMenu ? undefined : 'hidden',
|
||||
...bounds,
|
||||
icon: os.platform() == 'win32' ? 'icon.ico' : path.resolve(__dirname, '../icon.png'),
|
||||
partition: 'persist:dbgate',
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false,
|
||||
spellcheck: false,
|
||||
},
|
||||
});
|
||||
|
||||
if (store.get('winIsMaximized')) {
|
||||
if (initialConfig['winIsMaximized']) {
|
||||
mainWindow.maximize();
|
||||
}
|
||||
if (settingsJson['app.fullscreen']) {
|
||||
mainWindow.setFullScreen(true);
|
||||
}
|
||||
|
||||
mainMenu = buildMenu();
|
||||
mainWindow.setMenu(mainMenu);
|
||||
@@ -198,33 +299,49 @@ function createWindow() {
|
||||
slashes: true,
|
||||
});
|
||||
mainWindow.on('close', () => {
|
||||
store.set('winBounds', mainWindow.getBounds());
|
||||
store.set('winIsMaximized', mainWindow.isMaximized());
|
||||
try {
|
||||
fs.writeFileSync(
|
||||
configRootPath,
|
||||
JSON.stringify({
|
||||
winBounds: mainWindow.getBounds(),
|
||||
winIsMaximized: mainWindow.isMaximized(),
|
||||
}),
|
||||
'utf-8'
|
||||
);
|
||||
} catch (err) {
|
||||
console.log('Error saving config-root:', err.message);
|
||||
}
|
||||
});
|
||||
mainWindow.loadURL(startUrl);
|
||||
if (os.platform() == 'linux') {
|
||||
mainWindow.setIcon(path.resolve(__dirname, '../icon.png'));
|
||||
}
|
||||
// mainWindow.webContents.toggleDevTools();
|
||||
}
|
||||
|
||||
const apiPackage = path.join(
|
||||
__dirname,
|
||||
process.env.DEVMODE ? '../../packages/api/src/index' : '../packages/api/dist/bundle.js'
|
||||
);
|
||||
if (!apiLoaded) {
|
||||
const apiPackage = path.join(
|
||||
__dirname,
|
||||
process.env.DEVMODE ? '../../packages/api/src/index' : '../packages/api/dist/bundle.js'
|
||||
);
|
||||
|
||||
global.API_PACKAGE = apiPackage;
|
||||
global.API_PACKAGE = apiPackage;
|
||||
global.NATIVE_MODULES = path.join(__dirname, 'nativeModules');
|
||||
|
||||
// console.log('global.API_PACKAGE', global.API_PACKAGE);
|
||||
const api = require(apiPackage);
|
||||
// console.log(
|
||||
// 'REQUIRED',
|
||||
// path.resolve(
|
||||
// path.join(__dirname, process.env.DEVMODE ? '../../packages/api/src/index' : '../packages/api/dist/bundle.js')
|
||||
// )
|
||||
// );
|
||||
const main = api.getMainModule();
|
||||
main.initializeElectronSender(mainWindow.webContents);
|
||||
main.useAllControllers(null, electron);
|
||||
// console.log('global.API_PACKAGE', global.API_PACKAGE);
|
||||
const api = require(apiPackage);
|
||||
// console.log(
|
||||
// 'REQUIRED',
|
||||
// path.resolve(
|
||||
// path.join(__dirname, process.env.DEVMODE ? '../../packages/api/src/index' : '../packages/api/dist/bundle.js')
|
||||
// )
|
||||
// );
|
||||
const main = api.getMainModule();
|
||||
main.useAllControllers(null, electron);
|
||||
mainModule = main;
|
||||
apiLoaded = true;
|
||||
}
|
||||
mainModule.setElectronSender(mainWindow.webContents);
|
||||
|
||||
loadMainWindow();
|
||||
|
||||
@@ -234,6 +351,7 @@ function createWindow() {
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null;
|
||||
mainModule.setElectronSender(null);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -253,7 +371,7 @@ app.on('ready', onAppReady);
|
||||
app.on('window-all-closed', function () {
|
||||
// On OS X it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
if (!isMac()) {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
99
app/src/mainMenuDefinition.js
Normal file
@@ -0,0 +1,99 @@
|
||||
module.exports = ({ editMenu }) => [
|
||||
{
|
||||
label: 'File',
|
||||
submenu: [
|
||||
{ command: 'new.connection', hideDisabled: true },
|
||||
{ command: 'new.sqliteDatabase', hideDisabled: true },
|
||||
{ divider: true },
|
||||
{ command: 'new.query', hideDisabled: true },
|
||||
{ command: 'new.freetable', hideDisabled: true },
|
||||
{ command: 'new.shell', hideDisabled: true },
|
||||
{ command: 'new.jsonl', hideDisabled: true },
|
||||
{ divider: true },
|
||||
{ command: 'file.open', hideDisabled: true },
|
||||
{ command: 'file.openArchive', hideDisabled: true },
|
||||
{ divider: true },
|
||||
{ command: 'group.save', hideDisabled: true },
|
||||
{ command: 'group.saveAs', hideDisabled: true },
|
||||
{ divider: true },
|
||||
{ command: 'file.exit', hideDisabled: true },
|
||||
{ command: 'app.logout', hideDisabled: true, skipInApp: true },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Window',
|
||||
submenu: [
|
||||
{ command: 'tabs.closeTab', hideDisabled: false },
|
||||
{ command: 'tabs.closeAll', hideDisabled: false },
|
||||
{ command: 'tabs.closeTabsWithCurrentDb', hideDisabled: false },
|
||||
{ command: 'tabs.closeTabsButCurrentDb', hideDisabled: false },
|
||||
{ divider: true },
|
||||
{ command: 'app.zoomIn', hideDisabled: true },
|
||||
{ command: 'app.zoomOut', hideDisabled: true },
|
||||
{ command: 'app.zoomReset', hideDisabled: true },
|
||||
],
|
||||
},
|
||||
|
||||
editMenu
|
||||
? {
|
||||
label: 'Edit',
|
||||
submenu: [
|
||||
{ command: 'edit.undo' },
|
||||
{ command: 'edit.redo' },
|
||||
{ divider: true },
|
||||
{ command: 'edit.cut' },
|
||||
{ command: 'edit.copy' },
|
||||
{ command: 'edit.paste' },
|
||||
],
|
||||
}
|
||||
: null,
|
||||
|
||||
// {
|
||||
// label: 'Edit',
|
||||
// submenu: [
|
||||
// { role: 'undo' },
|
||||
// { role: 'redo' },
|
||||
// { type: 'separator' },
|
||||
// { role: 'cut' },
|
||||
// { role: 'copy' },
|
||||
// { role: 'paste' },
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
label: 'View',
|
||||
submenu: [
|
||||
{ command: 'app.reload', hideDisabled: true },
|
||||
{ command: 'app.toggleDevTools', hideDisabled: true },
|
||||
{ command: 'app.toggleFullScreen', hideDisabled: true },
|
||||
{ command: 'app.minimize', hideDisabled: true },
|
||||
{ divider: true },
|
||||
{ command: 'theme.changeTheme', hideDisabled: true },
|
||||
{ command: 'settings.show' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Tools',
|
||||
submenu: [
|
||||
{ command: 'database.search', hideDisabled: true },
|
||||
{ command: 'commandPalette.show', hideDisabled: true },
|
||||
{ command: 'database.switch', hideDisabled: true },
|
||||
{ divider: true },
|
||||
{ command: 'sql.generator', hideDisabled: true },
|
||||
{ command: 'file.import', hideDisabled: true },
|
||||
{ command: 'new.modelCompare', hideDisabled: true },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Help',
|
||||
submenu: [
|
||||
{ command: 'app.openDocs', hideDisabled: true },
|
||||
{ command: 'app.openWeb', hideDisabled: true },
|
||||
{ command: 'app.openIssue', hideDisabled: true },
|
||||
{ command: 'app.openSponsoring', hideDisabled: true },
|
||||
{ divider: true },
|
||||
{ command: 'settings.commands', hideDisabled: true },
|
||||
{ command: 'tabs.changelog', hideDisabled: true },
|
||||
{ command: 'about.show', hideDisabled: true },
|
||||
],
|
||||
},
|
||||
];
|
||||
382
app/yarn.lock
@@ -7,6 +7,27 @@
|
||||
resolved "https://registry.yarnpkg.com/7zip-bin/-/7zip-bin-5.1.1.tgz#9274ec7460652f9c632c59addf24efb1684ef876"
|
||||
integrity sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ==
|
||||
|
||||
"@babel/code-frame@^7.0.0":
|
||||
version "7.16.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789"
|
||||
integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==
|
||||
dependencies:
|
||||
"@babel/highlight" "^7.16.7"
|
||||
|
||||
"@babel/helper-validator-identifier@^7.16.7":
|
||||
version "7.16.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad"
|
||||
integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==
|
||||
|
||||
"@babel/highlight@^7.16.7":
|
||||
version "7.17.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.17.9.tgz#61b2ee7f32ea0454612def4fccdae0de232b73e3"
|
||||
integrity sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==
|
||||
dependencies:
|
||||
"@babel/helper-validator-identifier" "^7.16.7"
|
||||
chalk "^2.0.0"
|
||||
js-tokens "^4.0.0"
|
||||
|
||||
"@develar/schema-utils@~2.6.5":
|
||||
version "2.6.5"
|
||||
resolved "https://registry.yarnpkg.com/@develar/schema-utils/-/schema-utils-2.6.5.tgz#3ece22c5838402419a6e0425f85742b961d9b6c6"
|
||||
@@ -113,6 +134,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.2.tgz#00fe4d1686d5f6cf3a2f2e9a0eef42594d06abfc"
|
||||
integrity sha512-fqtSN5xn/bBzDxMT77C1rJg6CsH/R49E7qsGuvdPJa20HtV5zSTuLJPNfnlyVH3wauKnkHdLggTVkOW/xP9oQg==
|
||||
|
||||
"@types/normalize-package-data@^2.4.0":
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301"
|
||||
integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==
|
||||
|
||||
"@types/plist@^3.0.1":
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/plist/-/plist-3.0.2.tgz#61b3727bba0f5c462fe333542534a0c3e19ccb01"
|
||||
@@ -148,13 +174,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31"
|
||||
integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==
|
||||
|
||||
ajv-formats@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520"
|
||||
integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==
|
||||
dependencies:
|
||||
ajv "^8.0.0"
|
||||
|
||||
ajv-keywords@^3.4.1:
|
||||
version "3.5.2"
|
||||
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
|
||||
@@ -170,16 +189,6 @@ ajv@^6.10.0, ajv@^6.12.0:
|
||||
json-schema-traverse "^0.4.1"
|
||||
uri-js "^4.2.2"
|
||||
|
||||
ajv@^8.0.0, ajv@^8.6.3:
|
||||
version "8.8.2"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.8.2.tgz#01b4fef2007a28bf75f0b7fc009f62679de4abbb"
|
||||
integrity sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==
|
||||
dependencies:
|
||||
fast-deep-equal "^3.1.1"
|
||||
json-schema-traverse "^1.0.0"
|
||||
require-from-string "^2.0.2"
|
||||
uri-js "^4.2.2"
|
||||
|
||||
ansi-align@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59"
|
||||
@@ -260,6 +269,13 @@ are-we-there-yet@~1.1.2:
|
||||
delegates "^1.0.0"
|
||||
readable-stream "^2.0.6"
|
||||
|
||||
argparse@^1.0.7:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
|
||||
integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
|
||||
dependencies:
|
||||
sprintf-js "~1.0.2"
|
||||
|
||||
argparse@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
|
||||
@@ -307,11 +323,6 @@ at-least-node@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
|
||||
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
|
||||
|
||||
atomically@^1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/atomically/-/atomically-1.7.0.tgz#c07a0458432ea6dbc9a3506fffa424b48bccaafe"
|
||||
integrity sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||
@@ -481,7 +492,7 @@ camelcase@^6.2.0:
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.1.tgz#250fd350cfd555d0d2160b1d51510eaf8326e86e"
|
||||
integrity sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==
|
||||
|
||||
chalk@^2.4.2:
|
||||
chalk@^2.0.0, chalk@^2.4.2:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
|
||||
@@ -625,22 +636,6 @@ concat-stream@^1.6.2:
|
||||
readable-stream "^2.2.2"
|
||||
typedarray "^0.0.6"
|
||||
|
||||
conf@^10.0.3:
|
||||
version "10.1.1"
|
||||
resolved "https://registry.yarnpkg.com/conf/-/conf-10.1.1.tgz#ff08046d5aeeee0eaff55d57f5b4319193c3dfda"
|
||||
integrity sha512-z2civwq/k8TMYtcn3SVP0Peso4otIWnHtcTuHhQ0zDZDdP4NTxqEc8owfkz4zBsdMYdn/LFcE+ZhbCeqkhtq3Q==
|
||||
dependencies:
|
||||
ajv "^8.6.3"
|
||||
ajv-formats "^2.1.1"
|
||||
atomically "^1.7.0"
|
||||
debounce-fn "^4.0.0"
|
||||
dot-prop "^6.0.1"
|
||||
env-paths "^2.2.1"
|
||||
json-schema-typed "^7.0.3"
|
||||
onetime "^5.1.2"
|
||||
pkg-up "^3.1.0"
|
||||
semver "^7.3.5"
|
||||
|
||||
config-chain@^1.1.11:
|
||||
version "1.1.13"
|
||||
resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4"
|
||||
@@ -728,13 +723,6 @@ crypto-random-string@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
|
||||
integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
|
||||
|
||||
debounce-fn@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/debounce-fn/-/debounce-fn-4.0.0.tgz#ed76d206d8a50e60de0dd66d494d82835ffe61c7"
|
||||
integrity sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==
|
||||
dependencies:
|
||||
mimic-fn "^3.0.0"
|
||||
|
||||
debug@^2.6.8, debug@^2.6.9:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
@@ -852,13 +840,6 @@ dot-prop@^5.2.0:
|
||||
dependencies:
|
||||
is-obj "^2.0.0"
|
||||
|
||||
dot-prop@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083"
|
||||
integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==
|
||||
dependencies:
|
||||
is-obj "^2.0.0"
|
||||
|
||||
dotenv-expand@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0"
|
||||
@@ -881,6 +862,15 @@ ejs@^3.1.6:
|
||||
dependencies:
|
||||
jake "^10.6.1"
|
||||
|
||||
electron-builder-notarize@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/electron-builder-notarize/-/electron-builder-notarize-1.4.0.tgz#9e3609935eb70bf08f64fba255c3e4c43b0058e7"
|
||||
integrity sha512-5CPVlzkG+SofK3VU3E6HKmdXW6Uu6q5WWvzXX6diLAlAy9qJsR0n99aNztVKKsPl6yjEbvT+MUl4ci0YCwOBRA==
|
||||
dependencies:
|
||||
electron-notarize "^1.1.1"
|
||||
js-yaml "^3.14.0"
|
||||
read-pkg-up "^7.0.0"
|
||||
|
||||
electron-builder@22.14.5:
|
||||
version "22.14.5"
|
||||
resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-22.14.5.tgz#3a25547bd4fe3728d4704da80956a794c5c31496"
|
||||
@@ -904,6 +894,14 @@ electron-log@^4.4.1:
|
||||
resolved "https://registry.yarnpkg.com/electron-log/-/electron-log-4.4.1.tgz#28ebeb474eccba2ebf194a96c40d6328e5353e4d"
|
||||
integrity sha512-nK/DwxPLtwWbggPCm27eMQhYHc3gzoZ+cokBK99diO4WsZJKrv5l44EUW8mRfWpmC8ZubnMyp6GTUIJyTc9AJA==
|
||||
|
||||
electron-notarize@^1.1.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/electron-notarize/-/electron-notarize-1.2.1.tgz#347c18eca8e29dddadadee511b870c13d4008baf"
|
||||
integrity sha512-u/ECWhIrhkSQpZM4cJzVZ5TsmkaqrRo5LDC/KMbGF0sPkm53Ng59+M0zp8QVaql0obfJy9vlVT+4iOkAi2UDlA==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
fs-extra "^9.0.1"
|
||||
|
||||
electron-osx-sign@^0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/electron-osx-sign/-/electron-osx-sign-0.5.0.tgz#fc258c5e896859904bbe3d01da06902c04b51c3a"
|
||||
@@ -929,14 +927,6 @@ electron-publish@22.14.5:
|
||||
lazy-val "^1.0.5"
|
||||
mime "^2.5.2"
|
||||
|
||||
electron-store@^8.0.1:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/electron-store/-/electron-store-8.0.1.tgz#9b598c1d2edeffebee9d8c1cd957ad368c528925"
|
||||
integrity sha512-ZyLvNywiqSpbwC/pp89O/AycVWY/UJIkmtyzF2Bd0Nm/rLmcFc0NTGuLdg6+LE8mS8qsiK5JMoe4PnrecLHH5w==
|
||||
dependencies:
|
||||
conf "^10.0.3"
|
||||
type-fest "^1.0.2"
|
||||
|
||||
electron-updater@^4.6.1:
|
||||
version "4.6.1"
|
||||
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-4.6.1.tgz#80ca805c4f51b2e682aac29d18fed75d6a533d32"
|
||||
@@ -951,10 +941,10 @@ electron-updater@^4.6.1:
|
||||
lodash.isequal "^4.5.0"
|
||||
semver "^7.3.5"
|
||||
|
||||
electron@12.1.0:
|
||||
version "12.1.0"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-12.1.0.tgz#615a7f9dbb2fc79cc72361fba9f39d005c697bca"
|
||||
integrity sha512-joQlYI/nTIrTUldO3GENZ2j225eKar9nTQBSEwSUSWN4h65QGDmXNQ7dbWPmLlkUQWtHhz8lXhFk30OLG9ZjLw==
|
||||
electron@13.6.3:
|
||||
version "13.6.3"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-13.6.3.tgz#c0217178807d3e0b2175c49dbe33ea8dac447e73"
|
||||
integrity sha512-kevgR6/RuEhchJQbgCKhHle9HvJhi2dOJlicFZJqbbqa9BVpZARqqFDlwTSatYxmUPUJwu09FvyMwJG2DMQIng==
|
||||
dependencies:
|
||||
"@electron/get" "^1.0.1"
|
||||
"@types/node" "^14.6.2"
|
||||
@@ -977,11 +967,18 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1:
|
||||
dependencies:
|
||||
once "^1.4.0"
|
||||
|
||||
env-paths@^2.2.0, env-paths@^2.2.1:
|
||||
env-paths@^2.2.0:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
|
||||
integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==
|
||||
|
||||
error-ex@^1.3.1:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
|
||||
integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
|
||||
dependencies:
|
||||
is-arrayish "^0.2.1"
|
||||
|
||||
es6-error@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
|
||||
@@ -1007,6 +1004,11 @@ escape-string-regexp@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
|
||||
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
|
||||
|
||||
esprima@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
|
||||
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
|
||||
|
||||
expand-template@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
|
||||
@@ -1063,12 +1065,13 @@ fill-range@^7.0.1:
|
||||
dependencies:
|
||||
to-regex-range "^5.0.1"
|
||||
|
||||
find-up@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
|
||||
integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
|
||||
find-up@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
|
||||
integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
|
||||
dependencies:
|
||||
locate-path "^3.0.0"
|
||||
locate-path "^5.0.0"
|
||||
path-exists "^4.0.0"
|
||||
|
||||
find-yarn-workspace-root@^2.0.0:
|
||||
version "2.0.0"
|
||||
@@ -1140,6 +1143,11 @@ fs.realpath@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
|
||||
|
||||
function-bind@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
||||
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
|
||||
|
||||
gauge@~2.7.3:
|
||||
version "2.7.4"
|
||||
resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
|
||||
@@ -1273,6 +1281,18 @@ has-yarn@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77"
|
||||
integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==
|
||||
|
||||
has@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
|
||||
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
|
||||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
|
||||
hosted-git-info@^2.1.4:
|
||||
version "2.8.9"
|
||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
|
||||
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
|
||||
|
||||
hosted-git-info@^4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.0.2.tgz#5e425507eede4fea846b7262f0838456c4209961"
|
||||
@@ -1338,6 +1358,11 @@ ini@^1.3.4, ini@~1.3.0:
|
||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
|
||||
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
|
||||
|
||||
is-arrayish@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
|
||||
integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
|
||||
|
||||
is-ci@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c"
|
||||
@@ -1352,6 +1377,13 @@ is-ci@^3.0.0:
|
||||
dependencies:
|
||||
ci-info "^3.2.0"
|
||||
|
||||
is-core-module@^2.8.1:
|
||||
version "2.8.1"
|
||||
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211"
|
||||
integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==
|
||||
dependencies:
|
||||
has "^1.0.3"
|
||||
|
||||
is-docker@^2.0.0:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa"
|
||||
@@ -1451,6 +1483,19 @@ jake@^10.6.1:
|
||||
filelist "^1.0.1"
|
||||
minimatch "^3.0.4"
|
||||
|
||||
js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
||||
|
||||
js-yaml@^3.14.0:
|
||||
version "3.14.1"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
|
||||
integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
|
||||
dependencies:
|
||||
argparse "^1.0.7"
|
||||
esprima "^4.0.0"
|
||||
|
||||
js-yaml@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
|
||||
@@ -1463,21 +1508,16 @@ json-buffer@3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898"
|
||||
integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=
|
||||
|
||||
json-parse-even-better-errors@^2.3.0:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
|
||||
integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
|
||||
|
||||
json-schema-traverse@^0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
|
||||
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
|
||||
|
||||
json-schema-traverse@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
|
||||
integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
|
||||
|
||||
json-schema-typed@^7.0.3:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/json-schema-typed/-/json-schema-typed-7.0.3.tgz#23ff481b8b4eebcd2ca123b4fa0409e66469a2d9"
|
||||
integrity sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==
|
||||
|
||||
json-stringify-safe@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
|
||||
@@ -1532,13 +1572,22 @@ lazy-val@^1.0.4, lazy-val@^1.0.5:
|
||||
resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.5.tgz#6cf3b9f5bc31cee7ee3e369c0832b7583dcd923d"
|
||||
integrity sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==
|
||||
|
||||
locate-path@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
|
||||
integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
|
||||
lines-and-columns@^1.1.6:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
|
||||
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
|
||||
|
||||
locate-path@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
|
||||
integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
|
||||
dependencies:
|
||||
p-locate "^3.0.0"
|
||||
path-exists "^3.0.0"
|
||||
p-locate "^4.1.0"
|
||||
|
||||
lodash.clonedeepwith@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.clonedeepwith/-/lodash.clonedeepwith-4.5.0.tgz#6ee30573a03a1a60d670a62ef33c10cf1afdbdd4"
|
||||
integrity sha1-buMFc6A6GmDWcKYu8zwQzxr9vdQ=
|
||||
|
||||
lodash.escaperegexp@^4.1.2:
|
||||
version "4.1.2"
|
||||
@@ -1611,16 +1660,6 @@ mime@^2.5.2:
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367"
|
||||
integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==
|
||||
|
||||
mimic-fn@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
|
||||
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
|
||||
|
||||
mimic-fn@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-3.1.0.tgz#65755145bbf3e36954b949c16450427451d5ca74"
|
||||
integrity sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==
|
||||
|
||||
mimic-response@^1.0.0, mimic-response@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
|
||||
@@ -1740,6 +1779,16 @@ noms@0.0.0:
|
||||
inherits "^2.0.1"
|
||||
readable-stream "~1.0.31"
|
||||
|
||||
normalize-package-data@^2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
|
||||
integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
|
||||
dependencies:
|
||||
hosted-git-info "^2.1.4"
|
||||
resolve "^1.10.0"
|
||||
semver "2 || 3 || 4 || 5"
|
||||
validate-npm-package-license "^3.0.1"
|
||||
|
||||
normalize-url@^4.1.0:
|
||||
version "4.5.1"
|
||||
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a"
|
||||
@@ -1785,13 +1834,6 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
||||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
onetime@^5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
|
||||
integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
|
||||
dependencies:
|
||||
mimic-fn "^2.1.0"
|
||||
|
||||
open@^7.4.2:
|
||||
version "7.4.2"
|
||||
resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321"
|
||||
@@ -1810,19 +1852,19 @@ p-cancelable@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc"
|
||||
integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==
|
||||
|
||||
p-limit@^2.0.0:
|
||||
p-limit@^2.2.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
|
||||
integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
|
||||
dependencies:
|
||||
p-try "^2.0.0"
|
||||
|
||||
p-locate@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
|
||||
integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
|
||||
p-locate@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
|
||||
integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
|
||||
dependencies:
|
||||
p-limit "^2.0.0"
|
||||
p-limit "^2.2.0"
|
||||
|
||||
p-try@^2.0.0:
|
||||
version "2.2.0"
|
||||
@@ -1839,6 +1881,16 @@ package-json@^6.3.0:
|
||||
registry-url "^5.0.0"
|
||||
semver "^6.2.0"
|
||||
|
||||
parse-json@^5.0.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
|
||||
integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.0.0"
|
||||
error-ex "^1.3.1"
|
||||
json-parse-even-better-errors "^2.3.0"
|
||||
lines-and-columns "^1.1.6"
|
||||
|
||||
patch-package@^6.4.7:
|
||||
version "6.4.7"
|
||||
resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-6.4.7.tgz#2282d53c397909a0d9ef92dae3fdeb558382b148"
|
||||
@@ -1858,10 +1910,10 @@ patch-package@^6.4.7:
|
||||
slash "^2.0.0"
|
||||
tmp "^0.0.33"
|
||||
|
||||
path-exists@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
|
||||
integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
|
||||
path-exists@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
|
||||
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
|
||||
|
||||
path-is-absolute@^1.0.0:
|
||||
version "1.0.1"
|
||||
@@ -1878,6 +1930,11 @@ path-key@^3.1.0:
|
||||
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
|
||||
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
|
||||
|
||||
path-parse@^1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
|
||||
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
|
||||
|
||||
pend@~1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
|
||||
@@ -1893,13 +1950,6 @@ pify@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
|
||||
integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
|
||||
|
||||
pkg-up@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5"
|
||||
integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==
|
||||
dependencies:
|
||||
find-up "^3.0.0"
|
||||
|
||||
plist@^3.0.1, plist@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.4.tgz#a62df837e3aed2bb3b735899d510c4f186019cbe"
|
||||
@@ -2007,6 +2057,25 @@ read-config-file@6.2.0:
|
||||
json5 "^2.2.0"
|
||||
lazy-val "^1.0.4"
|
||||
|
||||
read-pkg-up@^7.0.0:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507"
|
||||
integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==
|
||||
dependencies:
|
||||
find-up "^4.1.0"
|
||||
read-pkg "^5.2.0"
|
||||
type-fest "^0.8.1"
|
||||
|
||||
read-pkg@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc"
|
||||
integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==
|
||||
dependencies:
|
||||
"@types/normalize-package-data" "^2.4.0"
|
||||
normalize-package-data "^2.5.0"
|
||||
parse-json "^5.0.0"
|
||||
type-fest "^0.6.0"
|
||||
|
||||
readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@~2.3.6:
|
||||
version "2.3.7"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
|
||||
@@ -2058,10 +2127,14 @@ require-directory@^2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
|
||||
integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
|
||||
|
||||
require-from-string@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
|
||||
integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
|
||||
resolve@^1.10.0:
|
||||
version "1.22.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198"
|
||||
integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==
|
||||
dependencies:
|
||||
is-core-module "^2.8.1"
|
||||
path-parse "^1.0.7"
|
||||
supports-preserve-symlinks-flag "^1.0.0"
|
||||
|
||||
responselike@^1.0.2:
|
||||
version "1.0.2"
|
||||
@@ -2135,7 +2208,7 @@ semver-diff@^3.1.1:
|
||||
dependencies:
|
||||
semver "^6.3.0"
|
||||
|
||||
semver@^5.4.1, semver@^5.5.0, semver@^5.6.0:
|
||||
"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||
@@ -2248,11 +2321,42 @@ source-map@^0.6.0:
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||
|
||||
spdx-correct@^3.0.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9"
|
||||
integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==
|
||||
dependencies:
|
||||
spdx-expression-parse "^3.0.0"
|
||||
spdx-license-ids "^3.0.0"
|
||||
|
||||
spdx-exceptions@^2.1.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d"
|
||||
integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==
|
||||
|
||||
spdx-expression-parse@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679"
|
||||
integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==
|
||||
dependencies:
|
||||
spdx-exceptions "^2.1.0"
|
||||
spdx-license-ids "^3.0.0"
|
||||
|
||||
spdx-license-ids@^3.0.0:
|
||||
version "3.0.11"
|
||||
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95"
|
||||
integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==
|
||||
|
||||
sprintf-js@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673"
|
||||
integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==
|
||||
|
||||
sprintf-js@~1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
||||
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
|
||||
|
||||
stat-mode@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/stat-mode/-/stat-mode-1.0.0.tgz#68b55cb61ea639ff57136f36b216a291800d1465"
|
||||
@@ -2335,6 +2439,11 @@ supports-color@^7.1.0:
|
||||
dependencies:
|
||||
has-flag "^4.0.0"
|
||||
|
||||
supports-preserve-symlinks-flag@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||
|
||||
tar-fs@^2.0.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784"
|
||||
@@ -2446,10 +2555,15 @@ type-fest@^0.20.2:
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
|
||||
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
|
||||
|
||||
type-fest@^1.0.2:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1"
|
||||
integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==
|
||||
type-fest@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b"
|
||||
integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==
|
||||
|
||||
type-fest@^0.8.1:
|
||||
version "0.8.1"
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
|
||||
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
|
||||
|
||||
typedarray-to-buffer@^3.1.5:
|
||||
version "3.1.5"
|
||||
@@ -2529,6 +2643,14 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||
|
||||
validate-npm-package-license@^3.0.1:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
|
||||
integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
|
||||
dependencies:
|
||||
spdx-correct "^3.0.0"
|
||||
spdx-expression-parse "^3.0.0"
|
||||
|
||||
verror@^1.10.0:
|
||||
version "1.10.1"
|
||||
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.1.tgz#4bf09eeccf4563b109ed4b3d458380c972b0cdeb"
|
||||
|
||||
47
convert-icons.sh
Executable file
@@ -0,0 +1,47 @@
|
||||
# magick icon.svg -resize 1024x1024 -transparent white -background transparent app/icon1024.png
|
||||
|
||||
# magick app/icon1024.png -resize 512x512 app/icon512.png
|
||||
# magick app/icon1024.png -resize 256x256 app/icon.png
|
||||
# magick app/icon1024.png -resize 32x32 app/icon32.png
|
||||
# magick icon.svg -define icon:auto-resize="256,128,96,64,48,32,16" -transparent white -background transparent app/icon.ico
|
||||
|
||||
# # magick icon.svg -resize 512x512 -transparent white -background transparent app/icon512.png
|
||||
# # magick icon.svg -resize 256x256 -transparent white -background transparent app/icon.png
|
||||
# # magick icon.svg -resize 32x32 -transparent white -background transparent app/icon32.png
|
||||
# # magick icon.svg -define icon:auto-resize="256,128,96,64,48,32,16" -transparent white -background transparent app/icon.ico
|
||||
|
||||
# magick app/icon1024.png -resize 512x512 app/icons/512x512.png
|
||||
# magick app/icon1024.png -resize 256x256 app/icons/256x256.png
|
||||
# magick app/icon1024.png -resize 128x128 app/icons/128x128.png
|
||||
# magick app/icon1024.png -resize 64x64 app/icons/64x64.png
|
||||
# magick app/icon1024.png -resize 48x48 app/icons/48x48.png
|
||||
# magick app/icon1024.png -resize 32x32 app/icons/32x32.png
|
||||
# magick app/icon1024.png -resize 16x16 app/icons/16x16.png
|
||||
|
||||
# # magick icon.svg -resize 16x16 -transparent white -background transparent app/icons/16x16.png
|
||||
# # magick icon.svg -resize 32x32 -transparent white -background transparent app/icons/32x32.png
|
||||
# # magick icon.svg -resize 48x48 -transparent white -background transparent app/icons/48x48.png
|
||||
# # magick icon.svg -resize 64x64 -transparent white -background transparent app/icons/64x64.png
|
||||
# # magick icon.svg -resize 128x128 -transparent white -background transparent app/icons/128x128.png
|
||||
# # magick icon.svg -resize 256x256 -transparent white -background transparent app/icons/256x256.png
|
||||
# # magick icon.svg -resize 512x512 -transparent white -background transparent app/icons/512x512.png
|
||||
|
||||
# magick icon.svg -resize 1024x1024 icon.png
|
||||
# magick icon.svg -resize 1024x1024 -transparent white -background transparent icon.png
|
||||
|
||||
magick icon.png -resize 512x512 app/icon512.png
|
||||
magick icon.png -resize 256x256 app/icon.png
|
||||
magick icon.png -resize 32x32 app/icon32.png
|
||||
magick icon.png -define icon:auto-resize="256,128,96,64,48,32,16" app/icon.ico
|
||||
|
||||
magick icon.png -resize 512x512 app/icons/512x512.png
|
||||
magick icon.png -resize 256x256 app/icons/256x256.png
|
||||
magick icon.png -resize 128x128 app/icons/128x128.png
|
||||
magick icon.png -resize 64x64 app/icons/64x64.png
|
||||
magick icon.png -resize 48x48 app/icons/48x48.png
|
||||
magick icon.png -resize 32x32 app/icons/32x32.png
|
||||
magick icon.png -resize 16x16 app/icons/16x16.png
|
||||
|
||||
magick icon.png -resize 192x192 packages/web/public/logo192.png
|
||||
magick icon.png -resize 512x512 packages/web/public/logo512.png
|
||||
magick icon.png -define icon:auto-resize="256,128,96,64,48,32,16" packages/web/public/favicon.ico
|
||||
54
docker-compose.yaml
Normal file
@@ -0,0 +1,54 @@
|
||||
# this compose file is for testing purposes only
|
||||
# use it for testing docker containsers built on local machine
|
||||
version: "3"
|
||||
services:
|
||||
dbgate:
|
||||
build: docker
|
||||
# image: dbgate/dbgate:beta-alpine
|
||||
# image: dbgate/dbgate:alpine
|
||||
# image: dbgate/dbgate:beta
|
||||
restart: always
|
||||
ports:
|
||||
- 3100:3000
|
||||
# volumes:
|
||||
# - /home/jena/dbgate-data:/root/dbgate-data
|
||||
|
||||
volumes:
|
||||
- dbgate-data:/root/dbgate-data
|
||||
|
||||
# environment:
|
||||
# WEB_ROOT: /dbgate
|
||||
|
||||
# CONNECTIONS: mssql
|
||||
# LABEL_mssql: MS Sql
|
||||
# SERVER_mssql: mssql
|
||||
# USER_mssql: sa
|
||||
# PORT_mssql: 1433
|
||||
# PASSWORD_mssql: Pwd2020Db
|
||||
# ENGINE_mssql: mssql@dbgate-plugin-mssql
|
||||
# proxy:
|
||||
# # image: nginx
|
||||
# build: test/nginx
|
||||
# ports:
|
||||
# - 8082:80
|
||||
|
||||
# volumes:
|
||||
# - /home/jena/test/chinook:/mnt/sqt
|
||||
# environment:
|
||||
# CONNECTIONS: sqlite
|
||||
|
||||
# LABEL_sqlite: sqt
|
||||
# FILE_sqlite: /mnt/sqt/Chinook.db
|
||||
# ENGINE_sqlite: sqlite@dbgate-plugin-sqlite
|
||||
|
||||
# mssql:
|
||||
# image: mcr.microsoft.com/mssql/server
|
||||
# restart: always
|
||||
# environment:
|
||||
# - ACCEPT_EULA=Y
|
||||
# - SA_PASSWORD=Pwd2020Db
|
||||
# - MSSQL_PID=Express
|
||||
|
||||
volumes:
|
||||
dbgate-data:
|
||||
driver: local
|
||||
2
docker/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
package.json
|
||||
yarn.lock
|
||||
@@ -1,9 +1,18 @@
|
||||
FROM node:14-alpine
|
||||
FROM node:14
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
iputils-ping \
|
||||
iproute2 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /home/dbgate-docker
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN ["chmod", "+x", "/home/dbgate-docker/entrypoint.sh"]
|
||||
|
||||
WORKDIR /home/dbgate-docker
|
||||
EXPOSE 3000
|
||||
CMD node bundle.js
|
||||
VOLUME /root/dbgate-data
|
||||
|
||||
CMD ["/home/dbgate-docker/entrypoint.sh"]
|
||||
|
||||
17
docker/Dockerfile-alpine
Normal file
@@ -0,0 +1,17 @@
|
||||
FROM node:14-alpine
|
||||
|
||||
WORKDIR /home/dbgate-docker
|
||||
|
||||
RUN apk --no-cache upgrade \
|
||||
&& apk --no-cache add \
|
||||
iputils
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN ["chmod", "+x", "/home/dbgate-docker/entrypoint.sh"]
|
||||
|
||||
WORKDIR /home/dbgate-docker
|
||||
EXPOSE 3000
|
||||
VOLUME /root/dbgate-data
|
||||
|
||||
CMD ["/home/dbgate-docker/entrypoint.sh"]
|
||||
11
docker/entrypoint.sh
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
HOST_DOMAIN="dockerhost"
|
||||
ping -q -c1 $HOST_DOMAIN > /dev/null 2>&1
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
HOST_IP=$(ip route | awk 'NR==1 {print $3}')
|
||||
echo "$HOST_IP $HOST_DOMAIN" >> /etc/hosts
|
||||
fi
|
||||
|
||||
node bundle.js
|
||||
|
Before Width: | Height: | Size: 279 KiB After Width: | Height: | Size: 292 KiB |
|
Before Width: | Height: | Size: 228 KiB After Width: | Height: | Size: 202 KiB |
|
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 156 KiB |
|
Before Width: | Height: | Size: 204 KiB After Width: | Height: | Size: 192 KiB |
17
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"version": "4.4.5-beta.14",
|
||||
"version": "4.8.8-beta.3",
|
||||
"name": "dbgate-all",
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
@@ -11,21 +11,18 @@
|
||||
"start:api": "yarn workspace dbgate-api start",
|
||||
"start:app": "cd app && yarn start",
|
||||
"start:api:portal": "yarn workspace dbgate-api start:portal",
|
||||
"start:api:covid": "yarn workspace dbgate-api start:covid",
|
||||
"start:api:singledb": "yarn workspace dbgate-api start:singledb",
|
||||
"start:web": "yarn workspace dbgate-web dev",
|
||||
"start:sqltree": "yarn workspace dbgate-sqltree start",
|
||||
"start:tools": "yarn workspace dbgate-tools start",
|
||||
"start:datalib": "yarn workspace dbgate-datalib start",
|
||||
"start:filterparser": "yarn workspace dbgate-filterparser start",
|
||||
"start:querysplitter": "yarn workspace dbgate-query-splitter start",
|
||||
"build:sqltree": "yarn workspace dbgate-sqltree build",
|
||||
"build:datalib": "yarn workspace dbgate-datalib build",
|
||||
"build:filterparser": "yarn workspace dbgate-filterparser build",
|
||||
"build:querysplitter": "yarn workspace dbgate-query-splitter build",
|
||||
"build:tools": "yarn workspace dbgate-tools build",
|
||||
"build:lib": "yarn build:querysplitter && yarn build:tools && yarn build:sqltree && yarn build:filterparser && yarn build:datalib",
|
||||
"build:lib": "yarn build:sqltree && yarn build:tools && yarn build:filterparser && yarn build:datalib",
|
||||
"build:app": "yarn plugins:copydist && cd app && yarn install && yarn build",
|
||||
"build:app:mac": "yarn plugins:copydist && cd app && yarn install && yarn build:mac",
|
||||
"build:api": "yarn workspace dbgate-api build",
|
||||
"build:web:docker": "yarn workspace dbgate-web build",
|
||||
"build:plugins:frontend": "workspaces-run --only=\"dbgate-plugin-*\" -- yarn build:frontend",
|
||||
@@ -41,13 +38,15 @@
|
||||
"resetPackagedPlugins": "node resetPackagedPlugins",
|
||||
"prettier": "prettier --write packages/api/src && prettier --write packages/datalib/src && prettier --write packages/filterparser/src && prettier --write packages/sqltree/src && prettier --write packages/tools/src && prettier --write packages/types && prettier --write packages/web/src && prettier --write app/src",
|
||||
"copy:docker:build": "copyfiles packages/api/dist/* docker -f && copyfiles packages/web/public/* docker -u 2 && copyfiles \"packages/web/public/**/*\" docker -u 2 && copyfiles \"plugins/dist/**/*\" docker/plugins -u 2",
|
||||
"prepare:docker": "yarn plugins:copydist && yarn build:web:docker && yarn build:api && yarn copy:docker:build",
|
||||
"install:sqlite:docker": "cd docker && yarn init --yes && yarn add better-sqlite3 && cd ..",
|
||||
"prepare:docker": "yarn plugins:copydist && yarn build:web:docker && yarn build:api && yarn copy:docker:build && yarn install:sqlite:docker",
|
||||
"start": "concurrently --kill-others-on-fail \"yarn start:api\" \"yarn start:web\"",
|
||||
"lib": "concurrently --kill-others-on-fail \"yarn start:sqltree\" \"yarn start:filterparser\" \"yarn start:datalib\" \"yarn start:tools\" \"yarn start:querysplitter\" \"yarn build:plugins:frontend:watch\"",
|
||||
"lib": "concurrently --kill-others-on-fail \"yarn start:sqltree\" \"yarn start:filterparser\" \"yarn start:datalib\" \"yarn start:tools\" \"yarn build:plugins:frontend:watch\"",
|
||||
"ts:api": "yarn workspace dbgate-api ts",
|
||||
"ts:web": "yarn workspace dbgate-web ts",
|
||||
"ts": "yarn ts:api && yarn ts:web",
|
||||
"postinstall": "yarn resetPackagedPlugins && yarn build:lib && patch-package && yarn fillNativeModules && yarn build:plugins:frontend"
|
||||
"postinstall": "yarn resetPackagedPlugins && yarn build:lib && patch-package && yarn fillNativeModules && yarn build:plugins:frontend",
|
||||
"dbgate-serve": "node packages/dbgate/bin/dbgate-serve.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"concurrently": "^5.1.0",
|
||||
|
||||
@@ -1 +1,15 @@
|
||||
DEVMODE=1
|
||||
# PERMISSIONS=~widgets/app,~widgets/plugins
|
||||
# DISABLE_SHELL=1
|
||||
# HIDE_APP_EDITOR=1
|
||||
|
||||
|
||||
# DEVWEB=1
|
||||
# LOGINS=admin,test
|
||||
|
||||
# LOGIN_PASSWORD_admin=admin
|
||||
# LOGIN_PERMISSIONS_admin=*
|
||||
|
||||
# LOGIN_PASSWORD_test=test
|
||||
# LOGIN_PERMISSIONS_test=~*, widgets/database
|
||||
# WORKSPACE_DIR=/home/jena/dbgate-data-2
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
DEVMODE=1
|
||||
|
||||
CONNECTIONS=mysql,postgres
|
||||
|
||||
LABEL_mysql=MySql localhost
|
||||
SERVER_mysql=localhost
|
||||
USER_mysql=root
|
||||
PASSWORD_mysql=test
|
||||
PORT_mysql=3307
|
||||
ENGINE_mysql=mysql@dbgate-plugin-mysql
|
||||
|
||||
LABEL_postgres=Postgres localhost
|
||||
SERVER_postgres=localhost
|
||||
USER_postgres=postgres
|
||||
PASSWORD_postgres=test
|
||||
PORT_postgres=5433
|
||||
ENGINE_postgres=postgres@dbgate-plugin-postgres
|
||||
44
packages/api/env/portal/.env
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
DEVMODE=1
|
||||
|
||||
CONNECTIONS=mysql,postgres,mongo,mongo2,mysqlssh,sqlite
|
||||
|
||||
LABEL_mysql=MySql localhost
|
||||
SERVER_mysql=localhost
|
||||
USER_mysql=root
|
||||
PASSWORD_mysql=test
|
||||
PORT_mysql=3307
|
||||
ENGINE_mysql=mysql@dbgate-plugin-mysql
|
||||
|
||||
LABEL_postgres=Postgres localhost
|
||||
SERVER_postgres=localhost
|
||||
USER_postgres=postgres
|
||||
PASSWORD_postgres=test
|
||||
PORT_postgres=5433
|
||||
ENGINE_postgres=postgres@dbgate-plugin-postgres
|
||||
|
||||
LABEL_mongo=Mongo URL
|
||||
URL_mongo=mongodb://localhost:27017
|
||||
ENGINE_mongo=mongo@dbgate-plugin-mongo
|
||||
|
||||
LABEL_mongo2=Mongo Server
|
||||
SERVER_mongo2=localhost
|
||||
ENGINE_mongo2=mongo@dbgate-plugin-mongo
|
||||
|
||||
LABEL_mysqlssh=MySql SSH
|
||||
SERVER_mysqlssh=localhost
|
||||
USER_mysqlssh=root
|
||||
PASSWORD_mysqlssh=xxx
|
||||
PORT_mysqlssh=3316
|
||||
ENGINE_mysqlssh=mysql@dbgate-plugin-mysql
|
||||
USE_SSH_mysqlssh=1
|
||||
SSH_HOST_mysqlssh=demo.dbgate.org
|
||||
SSH_PORT_mysqlssh=22
|
||||
SSH_MODE_mysqlssh=userPassword
|
||||
SSH_LOGIN_mysqlssh=root
|
||||
SSH_PASSWORD_mysqlssh=xxx
|
||||
|
||||
LABEL_sqlite=sqlite
|
||||
FILE_sqlite=/home/jena/dbgate-data/files/sqlite/feeds.sqlite
|
||||
ENGINE_sqlite=sqlite@dbgate-plugin-sqlite
|
||||
|
||||
# docker run -p 3000:3000 -e CONNECTIONS=mongo -e URL_mongo=mongodb://localhost:27017 -e ENGINE_mongo=mongo@dbgate-plugin-mongo -e LABEL_mongo=mongo dbgate/dbgate:beta
|
||||
@@ -8,6 +8,8 @@ USER_mysql=root
|
||||
PASSWORD_mysql=test
|
||||
PORT_mysql=3307
|
||||
ENGINE_mysql=mysql@dbgate-plugin-mysql
|
||||
DBCONFIG_mysql=[{"name":"Chinook","connectionColor":"cyan"}]
|
||||
|
||||
|
||||
SINGLE_CONNECTION=mysql
|
||||
SINGLE_DATABASE=Chinook
|
||||
@@ -25,7 +25,7 @@
|
||||
"compare-versions": "^3.6.0",
|
||||
"cors": "^2.8.5",
|
||||
"cross-env": "^6.0.3",
|
||||
"dbgate-query-splitter": "^4.1.1",
|
||||
"dbgate-query-splitter": "^4.9.0",
|
||||
"dbgate-sqltree": "^4.1.1",
|
||||
"dbgate-tools": "^4.1.1",
|
||||
"diff": "^5.0.0",
|
||||
@@ -44,7 +44,6 @@
|
||||
"line-reader": "^0.4.0",
|
||||
"lodash": "^4.17.21",
|
||||
"ncp": "^2.0.0",
|
||||
"nedb-promises": "^4.0.1",
|
||||
"node-cron": "^2.0.3",
|
||||
"node-ssh-forward": "^0.7.2",
|
||||
"portfinder": "^1.0.28",
|
||||
@@ -54,8 +53,8 @@
|
||||
},
|
||||
"scripts": {
|
||||
"start": "env-cmd node src/index.js",
|
||||
"start:portal": "env-cmd -f .env-portal node src/index.js",
|
||||
"start:singledb": "env-cmd -f .env-singledb node src/index.js",
|
||||
"start:portal": "env-cmd -f env/portal/.env node src/index.js",
|
||||
"start:singledb": "env-cmd -f env/singledb/.env node src/index.js",
|
||||
"start:filedb": "env-cmd node src/index.js /home/jena/test/chinook/Chinook.db",
|
||||
"start:singleconn": "env-cmd node src/index.js --server localhost --user root --port 3307 --engine mysql@dbgate-plugin-mysql --password test",
|
||||
"ts": "tsc",
|
||||
|
||||
280
packages/api/src/controllers/apps.js
Normal file
@@ -0,0 +1,280 @@
|
||||
const fs = require('fs-extra');
|
||||
const _ = require('lodash');
|
||||
const path = require('path');
|
||||
const { appdir } = require('../utility/directories');
|
||||
const socket = require('../utility/socket');
|
||||
const connections = require('./connections');
|
||||
|
||||
module.exports = {
|
||||
folders_meta: true,
|
||||
async folders() {
|
||||
const folders = await fs.readdir(appdir());
|
||||
return [
|
||||
...folders.map(name => ({
|
||||
name,
|
||||
})),
|
||||
];
|
||||
},
|
||||
|
||||
createFolder_meta: true,
|
||||
async createFolder({ folder }) {
|
||||
const name = await this.getNewAppFolder({ name: folder });
|
||||
await fs.mkdir(path.join(appdir(), name));
|
||||
socket.emitChanged('app-folders-changed');
|
||||
this.emitChangedDbApp(folder);
|
||||
return name;
|
||||
},
|
||||
|
||||
files_meta: true,
|
||||
async files({ folder }) {
|
||||
if (!folder) return [];
|
||||
const dir = path.join(appdir(), folder);
|
||||
if (!(await fs.exists(dir))) return [];
|
||||
const files = await fs.readdir(dir);
|
||||
|
||||
function fileType(ext, type) {
|
||||
return files
|
||||
.filter(name => name.endsWith(ext))
|
||||
.map(name => ({
|
||||
name: name.slice(0, -ext.length),
|
||||
label: path.parse(name.slice(0, -ext.length)).base,
|
||||
type,
|
||||
}));
|
||||
}
|
||||
|
||||
return [
|
||||
...fileType('.command.sql', 'command.sql'),
|
||||
...fileType('.query.sql', 'query.sql'),
|
||||
...fileType('.config.json', 'config.json'),
|
||||
];
|
||||
},
|
||||
|
||||
async emitChangedDbApp(folder) {
|
||||
const used = await this.getUsedAppFolders();
|
||||
if (used.includes(folder)) {
|
||||
socket.emitChanged('used-apps-changed');
|
||||
}
|
||||
},
|
||||
|
||||
refreshFiles_meta: true,
|
||||
async refreshFiles({ folder }) {
|
||||
socket.emitChanged(`app-files-changed-${folder}`);
|
||||
},
|
||||
|
||||
refreshFolders_meta: true,
|
||||
async refreshFolders() {
|
||||
socket.emitChanged(`app-folders-changed`);
|
||||
},
|
||||
|
||||
deleteFile_meta: true,
|
||||
async deleteFile({ folder, file, fileType }) {
|
||||
await fs.unlink(path.join(appdir(), folder, `${file}.${fileType}`));
|
||||
socket.emitChanged(`app-files-changed-${folder}`);
|
||||
this.emitChangedDbApp(folder);
|
||||
},
|
||||
|
||||
renameFile_meta: true,
|
||||
async renameFile({ folder, file, newFile, fileType }) {
|
||||
await fs.rename(
|
||||
path.join(path.join(appdir(), folder), `${file}.${fileType}`),
|
||||
path.join(path.join(appdir(), folder), `${newFile}.${fileType}`)
|
||||
);
|
||||
socket.emitChanged(`app-files-changed-${folder}`);
|
||||
this.emitChangedDbApp(folder);
|
||||
},
|
||||
|
||||
renameFolder_meta: true,
|
||||
async renameFolder({ folder, newFolder }) {
|
||||
const uniqueName = await this.getNewAppFolder({ name: newFolder });
|
||||
await fs.rename(path.join(appdir(), folder), path.join(appdir(), uniqueName));
|
||||
socket.emitChanged(`app-folders-changed`);
|
||||
},
|
||||
|
||||
deleteFolder_meta: true,
|
||||
async deleteFolder({ folder }) {
|
||||
if (!folder) throw new Error('Missing folder parameter');
|
||||
await fs.rmdir(path.join(appdir(), folder), { recursive: true });
|
||||
socket.emitChanged(`app-folders-changed`);
|
||||
socket.emitChanged(`app-files-changed-${folder}`);
|
||||
socket.emitChanged('used-apps-changed');
|
||||
},
|
||||
|
||||
async getNewAppFolder({ name }) {
|
||||
if (!(await fs.exists(path.join(appdir(), name)))) return name;
|
||||
let index = 2;
|
||||
while (await fs.exists(path.join(appdir(), `${name}${index}`))) {
|
||||
index += 1;
|
||||
}
|
||||
return `${name}${index}`;
|
||||
},
|
||||
|
||||
getUsedAppFolders_meta: true,
|
||||
async getUsedAppFolders() {
|
||||
const list = await connections.list();
|
||||
const apps = [];
|
||||
|
||||
for (const connection of list) {
|
||||
for (const db of connection.databases || []) {
|
||||
for (const key of _.keys(db || {})) {
|
||||
if (key.startsWith('useApp:') && db[key]) {
|
||||
apps.push(key.substring('useApp:'.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _.intersection(_.uniq(apps), await fs.readdir(appdir()));
|
||||
},
|
||||
|
||||
getUsedApps_meta: true,
|
||||
async getUsedApps() {
|
||||
const apps = await this.getUsedAppFolders();
|
||||
const res = [];
|
||||
|
||||
for (const folder of apps) {
|
||||
res.push(await this.loadApp({ folder }));
|
||||
}
|
||||
return res;
|
||||
},
|
||||
|
||||
// getAppsForDb_meta: true,
|
||||
// async getAppsForDb({ conid, database }) {
|
||||
// const connection = await connections.get({ conid });
|
||||
// if (!connection) return [];
|
||||
// const db = (connection.databases || []).find(x => x.name == database);
|
||||
// const apps = [];
|
||||
// const res = [];
|
||||
// if (db) {
|
||||
// for (const key of _.keys(db || {})) {
|
||||
// if (key.startsWith('useApp:') && db[key]) {
|
||||
// apps.push(key.substring('useApp:'.length));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// for (const folder of apps) {
|
||||
// res.push(await this.loadApp({ folder }));
|
||||
// }
|
||||
// return res;
|
||||
// },
|
||||
|
||||
loadApp_meta: true,
|
||||
async loadApp({ folder }) {
|
||||
const res = {
|
||||
queries: [],
|
||||
commands: [],
|
||||
name: folder,
|
||||
};
|
||||
const dir = path.join(appdir(), folder);
|
||||
if (await fs.exists(dir)) {
|
||||
const files = await fs.readdir(dir);
|
||||
|
||||
async function processType(ext, field) {
|
||||
for (const file of files) {
|
||||
if (file.endsWith(ext)) {
|
||||
res[field].push({
|
||||
name: file.slice(0, -ext.length),
|
||||
sql: await fs.readFile(path.join(dir, file), { encoding: 'utf-8' }),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await processType('.command.sql', 'commands');
|
||||
await processType('.query.sql', 'queries');
|
||||
}
|
||||
|
||||
try {
|
||||
res.virtualReferences = JSON.parse(
|
||||
await fs.readFile(path.join(dir, 'virtual-references.config.json'), { encoding: 'utf-8' })
|
||||
);
|
||||
} catch (err) {
|
||||
res.virtualReferences = [];
|
||||
}
|
||||
try {
|
||||
res.dictionaryDescriptions = JSON.parse(
|
||||
await fs.readFile(path.join(dir, 'dictionary-descriptions.config.json'), { encoding: 'utf-8' })
|
||||
);
|
||||
} catch (err) {
|
||||
res.dictionaryDescriptions = [];
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
|
||||
async saveConfigFile(appFolder, filename, filterFunc, newItem) {
|
||||
const file = path.join(appdir(), appFolder, filename);
|
||||
|
||||
let json;
|
||||
try {
|
||||
json = JSON.parse(await fs.readFile(file, { encoding: 'utf-8' }));
|
||||
} catch (err) {
|
||||
json = [];
|
||||
}
|
||||
|
||||
if (filterFunc) {
|
||||
json = json.filter(filterFunc);
|
||||
}
|
||||
|
||||
json = [...json, newItem];
|
||||
|
||||
await fs.writeFile(file, JSON.stringify(json, undefined, 2));
|
||||
|
||||
socket.emitChanged(`app-files-changed-${appFolder}`);
|
||||
socket.emitChanged('used-apps-changed');
|
||||
},
|
||||
|
||||
saveVirtualReference_meta: true,
|
||||
async saveVirtualReference({ appFolder, schemaName, pureName, refSchemaName, refTableName, columns }) {
|
||||
await this.saveConfigFile(
|
||||
appFolder,
|
||||
'virtual-references.config.json',
|
||||
columns.length == 1
|
||||
? x =>
|
||||
!(
|
||||
x.schemaName == schemaName &&
|
||||
x.pureName == pureName &&
|
||||
x.columns.length == 1 &&
|
||||
x.columns[0].columnName == columns[0].columnName
|
||||
)
|
||||
: null,
|
||||
{
|
||||
schemaName,
|
||||
pureName,
|
||||
refSchemaName,
|
||||
refTableName,
|
||||
columns,
|
||||
}
|
||||
);
|
||||
return true;
|
||||
},
|
||||
|
||||
saveDictionaryDescription_meta: true,
|
||||
async saveDictionaryDescription({ appFolder, pureName, schemaName, expression, columns, delimiter }) {
|
||||
await this.saveConfigFile(
|
||||
appFolder,
|
||||
'dictionary-descriptions.config.json',
|
||||
x => !(x.schemaName == schemaName && x.pureName == pureName),
|
||||
{
|
||||
schemaName,
|
||||
pureName,
|
||||
expression,
|
||||
columns,
|
||||
delimiter,
|
||||
}
|
||||
);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
createConfigFile_meta: true,
|
||||
async createConfigFile({ appFolder, fileName, content }) {
|
||||
const file = path.join(appdir(), appFolder, fileName);
|
||||
if (!(await fs.exists(file))) {
|
||||
await fs.writeFile(file, JSON.stringify(content, undefined, 2));
|
||||
socket.emitChanged(`app-files-changed-${appFolder}`);
|
||||
socket.emitChanged('used-apps-changed');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
};
|
||||
@@ -141,6 +141,13 @@ module.exports = {
|
||||
});
|
||||
},
|
||||
|
||||
saveText_meta: true,
|
||||
async saveText({ folder, file, text }) {
|
||||
await fs.writeFile(path.join(resolveArchiveFolder(folder), `${file}.jsonl`), text);
|
||||
socket.emitChanged(`archive-files-changed-${folder}`);
|
||||
return true;
|
||||
},
|
||||
|
||||
async getNewArchiveFolder({ database }) {
|
||||
const isLink = database.endsWith(database);
|
||||
const name = isLink ? database.slice(0, -5) : database;
|
||||
|
||||
@@ -1,38 +1,57 @@
|
||||
const fs = require('fs-extra');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const axios = require('axios');
|
||||
const { datadir } = require('../utility/directories');
|
||||
const hasPermission = require('../utility/hasPermission');
|
||||
const { hasPermission, getLogins } = require('../utility/hasPermission');
|
||||
const socket = require('../utility/socket');
|
||||
const _ = require('lodash');
|
||||
const AsyncLock = require('async-lock');
|
||||
|
||||
const currentVersion = require('../currentVersion');
|
||||
const platformInfo = require('../utility/platformInfo');
|
||||
const connections = require('../controllers/connections');
|
||||
|
||||
module.exports = {
|
||||
settingsValue: {},
|
||||
const lock = new AsyncLock();
|
||||
|
||||
async _init() {
|
||||
try {
|
||||
this.settingsValue = JSON.parse(await fs.readFile(path.join(datadir(), 'settings.json'), { encoding: 'utf-8' }));
|
||||
} catch (err) {
|
||||
this.settingsValue = {};
|
||||
}
|
||||
},
|
||||
module.exports = {
|
||||
// settingsValue: {},
|
||||
|
||||
// async _init() {
|
||||
// try {
|
||||
// this.settingsValue = JSON.parse(await fs.readFile(path.join(datadir(), 'settings.json'), { encoding: 'utf-8' }));
|
||||
// } catch (err) {
|
||||
// this.settingsValue = {};
|
||||
// }
|
||||
// },
|
||||
|
||||
get_meta: true,
|
||||
async get() {
|
||||
const permissions = process.env.PERMISSIONS ? process.env.PERMISSIONS.split(',') : null;
|
||||
async get(_params, req) {
|
||||
const logins = getLogins();
|
||||
const login = logins ? logins.find(x => x.login == (req.auth && req.auth.user)) : null;
|
||||
const permissions = login ? login.permissions : null;
|
||||
|
||||
return {
|
||||
runAsPortal: !!connections.portalConnections,
|
||||
singleDatabase: connections.singleDatabase,
|
||||
// hideAppEditor: !!process.env.HIDE_APP_EDITOR,
|
||||
allowShellConnection: platformInfo.allowShellConnection,
|
||||
allowShellScripting: platformInfo.allowShellConnection,
|
||||
isDocker: platformInfo.isDocker,
|
||||
permissions,
|
||||
login,
|
||||
...currentVersion,
|
||||
};
|
||||
},
|
||||
|
||||
logout_meta: {
|
||||
method: 'get',
|
||||
raw: true,
|
||||
},
|
||||
logout(req, res) {
|
||||
res.status(401).send('Logged out<br><a href="../..">Back to DbGate</a>');
|
||||
},
|
||||
|
||||
platformInfo_meta: true,
|
||||
async platformInfo() {
|
||||
return platformInfo;
|
||||
@@ -40,24 +59,45 @@ module.exports = {
|
||||
|
||||
getSettings_meta: true,
|
||||
async getSettings() {
|
||||
return this.settingsValue;
|
||||
try {
|
||||
return this.fillMissingSettings(
|
||||
JSON.parse(await fs.readFile(path.join(datadir(), 'settings.json'), { encoding: 'utf-8' }))
|
||||
);
|
||||
} catch (err) {
|
||||
return this.fillMissingSettings({});
|
||||
}
|
||||
},
|
||||
|
||||
fillMissingSettings(value) {
|
||||
const res = {
|
||||
...value,
|
||||
};
|
||||
if (value['app.useNativeMenu'] !== true && value['app.useNativeMenu'] !== false) {
|
||||
res['app.useNativeMenu'] = os.platform() == 'darwin' ? true : false;
|
||||
}
|
||||
return res;
|
||||
},
|
||||
|
||||
updateSettings_meta: true,
|
||||
async updateSettings(values) {
|
||||
if (!hasPermission(`settings/change`)) return false;
|
||||
try {
|
||||
const updated = {
|
||||
...this.settingsValue,
|
||||
...values,
|
||||
};
|
||||
await fs.writeFile(path.join(datadir(), 'settings.json'), JSON.stringify(updated, undefined, 2));
|
||||
this.settingsValue = updated;
|
||||
socket.emitChanged(`settings-changed`);
|
||||
return updated;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
async updateSettings(values, req) {
|
||||
if (!hasPermission(`settings/change`, req)) return false;
|
||||
|
||||
const res = await lock.acquire('update', async () => {
|
||||
const currentValue = await this.getSettings();
|
||||
try {
|
||||
const updated = {
|
||||
...currentValue,
|
||||
...values,
|
||||
};
|
||||
await fs.writeFile(path.join(datadir(), 'settings.json'), JSON.stringify(updated, undefined, 2));
|
||||
// this.settingsValue = updated;
|
||||
socket.emitChanged(`settings-changed`);
|
||||
return updated;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return res;
|
||||
},
|
||||
|
||||
changelog_meta: true,
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
const path = require('path');
|
||||
const { fork } = require('child_process');
|
||||
const _ = require('lodash');
|
||||
const nedb = require('nedb-promises');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
const { datadir, filesdir } = require('../utility/directories');
|
||||
const socket = require('../utility/socket');
|
||||
const { encryptConnection } = require('../utility/crypting');
|
||||
const { encryptConnection, maskConnection } = require('../utility/crypting');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
const { pickSafeConnectionInfo } = require('../utility/crypting');
|
||||
const JsonLinesDatabase = require('../utility/JsonLinesDatabase');
|
||||
|
||||
const processArgs = require('../utility/processArgs');
|
||||
const { safeJsonParse } = require('dbgate-tools');
|
||||
const platformInfo = require('../utility/platformInfo');
|
||||
|
||||
function getNamedArgs() {
|
||||
const res = {};
|
||||
@@ -37,7 +42,7 @@ function getDatabaseFileLabel(databaseFile) {
|
||||
|
||||
function getPortalCollections() {
|
||||
if (process.env.CONNECTIONS) {
|
||||
return _.compact(process.env.CONNECTIONS.split(',')).map(id => ({
|
||||
const connections = _.compact(process.env.CONNECTIONS.split(',')).map(id => ({
|
||||
_id: id,
|
||||
engine: process.env[`ENGINE_${id}`],
|
||||
server: process.env[`SERVER_${id}`],
|
||||
@@ -45,11 +50,44 @@ function getPortalCollections() {
|
||||
password: process.env[`PASSWORD_${id}`],
|
||||
port: process.env[`PORT_${id}`],
|
||||
databaseUrl: process.env[`URL_${id}`],
|
||||
useDatabaseUrl: !!process.env[`URL_${id}`],
|
||||
databaseFile: process.env[`FILE_${id}`],
|
||||
defaultDatabase: process.env[`DATABASE_${id}`],
|
||||
singleDatabase: !!process.env[`DATABASE_${id}`],
|
||||
defaultDatabase:
|
||||
process.env[`DATABASE_${id}`] ||
|
||||
(process.env[`FILE_${id}`] ? getDatabaseFileLabel(process.env[`FILE_${id}`]) : null),
|
||||
singleDatabase: !!process.env[`DATABASE_${id}`] || !!process.env[`FILE_${id}`],
|
||||
displayName: process.env[`LABEL_${id}`],
|
||||
isReadOnly: process.env[`READONLY_${id}`],
|
||||
databases: process.env[`DBCONFIG_${id}`] ? safeJsonParse(process.env[`DBCONFIG_${id}`]) : null,
|
||||
|
||||
// SSH tunnel
|
||||
useSshTunnel: process.env[`USE_SSH_${id}`],
|
||||
sshHost: process.env[`SSH_HOST_${id}`],
|
||||
sshPort: process.env[`SSH_PORT_${id}`],
|
||||
sshMode: process.env[`SSH_MODE_${id}`],
|
||||
sshLogin: process.env[`SSH_LOGIN_${id}`],
|
||||
sshPassword: process.env[`SSH_PASSWORD_${id}`],
|
||||
sshKeyfile: process.env[`SSH_KEY_FILE_${id}`],
|
||||
sshKeyfilePassword: process.env[`SSH_KEY_FILE_PASSWORD_${id}`],
|
||||
|
||||
// SSL
|
||||
useSsl: process.env[`USE_SSL_${id}`],
|
||||
sslCaFile: process.env[`SSL_CA_FILE_${id}`],
|
||||
sslCertFile: process.env[`SSL_CERT_FILE_${id}`],
|
||||
sslCertFilePassword: process.env[`SSL_CERT_FILE_PASSWORD_${id}`],
|
||||
sslKeyFile: process.env[`SSL_KEY_FILE_${id}`],
|
||||
sslRejectUnauthorized: process.env[`SSL_REJECT_UNAUTHORIZED_${id}`],
|
||||
}));
|
||||
console.log('Using connections from ENV variables:');
|
||||
console.log(JSON.stringify(connections.map(pickSafeConnectionInfo), undefined, 2));
|
||||
const noengine = connections.filter(x => !x.engine);
|
||||
if (noengine.length > 0) {
|
||||
console.log(
|
||||
'Warning: Invalid CONNECTIONS configutation, missing ENGINE for connection ID:',
|
||||
noengine.map(x => x._id)
|
||||
);
|
||||
}
|
||||
return connections;
|
||||
}
|
||||
|
||||
const args = getNamedArgs();
|
||||
@@ -122,35 +160,37 @@ module.exports = {
|
||||
const dir = datadir();
|
||||
if (!portalConnections) {
|
||||
// @ts-ignore
|
||||
this.datastore = nedb.create(path.join(dir, 'connections.jsonl'));
|
||||
this.datastore = new JsonLinesDatabase(path.join(dir, 'connections.jsonl'));
|
||||
}
|
||||
},
|
||||
|
||||
list_meta: true,
|
||||
async list() {
|
||||
return portalConnections || this.datastore.find();
|
||||
return portalConnections && !platformInfo.allowShellConnection
|
||||
? portalConnections.map(maskConnection)
|
||||
: this.datastore.find();
|
||||
},
|
||||
|
||||
test_meta: {
|
||||
method: 'post',
|
||||
raw: true,
|
||||
},
|
||||
test(req, res) {
|
||||
test_meta: true,
|
||||
test(connection) {
|
||||
const subprocess = fork(global['API_PACKAGE'] || process.argv[1], [
|
||||
'--is-forked-api',
|
||||
'--start-process',
|
||||
'connectProcess',
|
||||
...process.argv.slice(3),
|
||||
...processArgs.getPassArgs(),
|
||||
// ...process.argv.slice(3),
|
||||
]);
|
||||
subprocess.on('message', resp => {
|
||||
if (handleProcessCommunication(resp, subprocess)) return;
|
||||
// @ts-ignore
|
||||
const { msgtype } = resp;
|
||||
if (msgtype == 'connected' || msgtype == 'error') {
|
||||
res.json(resp);
|
||||
}
|
||||
subprocess.send(connection);
|
||||
return new Promise(resolve => {
|
||||
subprocess.on('message', resp => {
|
||||
if (handleProcessCommunication(resp, subprocess)) return;
|
||||
// @ts-ignore
|
||||
const { msgtype } = resp;
|
||||
if (msgtype == 'connected' || msgtype == 'error') {
|
||||
resolve(resp);
|
||||
}
|
||||
});
|
||||
});
|
||||
subprocess.send(req.body);
|
||||
},
|
||||
|
||||
save_meta: true,
|
||||
@@ -159,18 +199,25 @@ module.exports = {
|
||||
let res;
|
||||
const encrypted = encryptConnection(connection);
|
||||
if (connection._id) {
|
||||
res = await this.datastore.update(_.pick(connection, '_id'), encrypted);
|
||||
res = await this.datastore.update(encrypted);
|
||||
} else {
|
||||
res = await this.datastore.insert(encrypted);
|
||||
}
|
||||
socket.emitChanged('connection-list-changed');
|
||||
socket.emitChanged('used-apps-changed');
|
||||
if (this._closeAll) {
|
||||
this._closeAll(connection._id);
|
||||
}
|
||||
// for (const db of connection.databases || []) {
|
||||
// socket.emitChanged(`db-apps-changed-${connection._id}-${db.name}`);
|
||||
// }
|
||||
return res;
|
||||
},
|
||||
|
||||
update_meta: true,
|
||||
async update({ _id, values }) {
|
||||
if (portalConnections) return;
|
||||
const res = await this.datastore.update({ _id }, { $set: values });
|
||||
const res = await this.datastore.patch(_id, values);
|
||||
socket.emitChanged('connection-list-changed');
|
||||
return res;
|
||||
},
|
||||
@@ -178,31 +225,41 @@ module.exports = {
|
||||
updateDatabase_meta: true,
|
||||
async updateDatabase({ conid, database, values }) {
|
||||
if (portalConnections) return;
|
||||
const conn = await this.datastore.find({ _id: conid });
|
||||
let databases = conn[0].databases || [];
|
||||
const conn = await this.datastore.get(conid);
|
||||
let databases = (conn && conn.databases) || [];
|
||||
if (databases.find(x => x.name == database)) {
|
||||
databases = databases.map(x => (x.name == database ? { ...x, ...values } : x));
|
||||
} else {
|
||||
databases = [...databases, { name: database, ...values }];
|
||||
}
|
||||
const res = await this.datastore.update({ _id: conid }, { $set: { databases } });
|
||||
const res = await this.datastore.patch(conid, { databases });
|
||||
socket.emitChanged('connection-list-changed');
|
||||
socket.emitChanged('used-apps-changed');
|
||||
// socket.emitChanged(`db-apps-changed-${conid}-${database}`);
|
||||
return res;
|
||||
},
|
||||
|
||||
delete_meta: true,
|
||||
async delete(connection) {
|
||||
if (portalConnections) return;
|
||||
const res = await this.datastore.remove(_.pick(connection, '_id'));
|
||||
const res = await this.datastore.remove(connection._id);
|
||||
socket.emitChanged('connection-list-changed');
|
||||
return res;
|
||||
},
|
||||
|
||||
async getCore({ conid, mask = false }) {
|
||||
if (!conid) return null;
|
||||
if (portalConnections) {
|
||||
const res = portalConnections.find(x => x._id == conid) || null;
|
||||
return mask && !platformInfo.allowShellConnection ? maskConnection(res) : res;
|
||||
}
|
||||
const res = await this.datastore.get(conid);
|
||||
return res || null;
|
||||
},
|
||||
|
||||
get_meta: true,
|
||||
async get({ conid }) {
|
||||
if (portalConnections) return portalConnections.find(x => x._id == conid);
|
||||
const res = await this.datastore.find({ _id: conid });
|
||||
return res[0] || null;
|
||||
return this.getCore({ conid, mask: true });
|
||||
},
|
||||
|
||||
newSqliteDatabase_meta: true,
|
||||
|
||||
@@ -25,6 +25,7 @@ const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||
const generateDeploySql = require('../shell/generateDeploySql');
|
||||
const { createTwoFilesPatch } = require('diff');
|
||||
const diff2htmlPage = require('../utility/diff2htmlPage');
|
||||
const processArgs = require('../utility/processArgs');
|
||||
|
||||
module.exports = {
|
||||
/** @type {import('dbgate-types').OpenedDatabaseConnection[]} */
|
||||
@@ -32,6 +33,10 @@ module.exports = {
|
||||
closed: {},
|
||||
requests: {},
|
||||
|
||||
async _init() {
|
||||
connections._closeAll = conid => this.closeAll(conid);
|
||||
},
|
||||
|
||||
handle_structure(conid, database, { structure }) {
|
||||
const existing = this.opened.find(x => x.conid == conid && x.database == database);
|
||||
if (!existing) return;
|
||||
@@ -61,9 +66,10 @@ module.exports = {
|
||||
delete this.requests[msgid];
|
||||
},
|
||||
handle_status(conid, database, { status }) {
|
||||
// console.log('HANDLE SET STATUS', status);
|
||||
const existing = this.opened.find(x => x.conid == conid && x.database == database);
|
||||
if (!existing) return;
|
||||
if (existing.status == status) return;
|
||||
if (existing.status && status && existing.status.counter > status.counter) return;
|
||||
existing.status = status;
|
||||
socket.emitChanged(`database-status-changed-${conid}-${database}`);
|
||||
},
|
||||
@@ -73,12 +79,13 @@ module.exports = {
|
||||
async ensureOpened(conid, database) {
|
||||
const existing = this.opened.find(x => x.conid == conid && x.database == database);
|
||||
if (existing) return existing;
|
||||
const connection = await connections.get({ conid });
|
||||
const connection = await connections.getCore({ conid });
|
||||
const subprocess = fork(global['API_PACKAGE'] || process.argv[1], [
|
||||
'--is-forked-api',
|
||||
'--start-process',
|
||||
'databaseConnectionProcess',
|
||||
...process.argv.slice(3),
|
||||
...processArgs.getPassArgs(),
|
||||
// ...process.argv.slice(3),
|
||||
]);
|
||||
const lastClosed = this.closed[`${conid}/${database}`];
|
||||
const newOpened = {
|
||||
@@ -107,7 +114,7 @@ module.exports = {
|
||||
msgtype: 'connect',
|
||||
connection: { ...connection, database },
|
||||
structure: lastClosed ? lastClosed.structure : null,
|
||||
globalSettings: config.settingsValue,
|
||||
globalSettings: await config.getSettings(),
|
||||
});
|
||||
return newOpened;
|
||||
},
|
||||
@@ -133,6 +140,13 @@ module.exports = {
|
||||
return res;
|
||||
},
|
||||
|
||||
sqlSelect_meta: true,
|
||||
async sqlSelect({ conid, database, select }) {
|
||||
const opened = await this.ensureOpened(conid, database);
|
||||
const res = await this.sendRequest(opened, { msgtype: 'sqlSelect', select });
|
||||
return res;
|
||||
},
|
||||
|
||||
runScript_meta: true,
|
||||
async runScript({ conid, database, sql }) {
|
||||
console.log(`Processing script, conid=${conid}, database=${database}, sql=${sql}`);
|
||||
@@ -145,14 +159,69 @@ module.exports = {
|
||||
async collectionData({ conid, database, options }) {
|
||||
const opened = await this.ensureOpened(conid, database);
|
||||
const res = await this.sendRequest(opened, { msgtype: 'collectionData', options });
|
||||
return res.result;
|
||||
return res.result || null;
|
||||
},
|
||||
|
||||
async loadDataCore(msgtype, { conid, database, ...args }) {
|
||||
const opened = await this.ensureOpened(conid, database);
|
||||
const res = await this.sendRequest(opened, { msgtype, ...args });
|
||||
if (res.errorMessage) {
|
||||
console.error(res.errorMessage);
|
||||
|
||||
return {
|
||||
errorMessage: res.errorMessage,
|
||||
};
|
||||
}
|
||||
return res.result || null;
|
||||
},
|
||||
|
||||
loadKeys_meta: true,
|
||||
async loadKeys({ conid, database, root, filter }) {
|
||||
return this.loadDataCore('loadKeys', { conid, database, root, filter });
|
||||
},
|
||||
|
||||
exportKeys_meta: true,
|
||||
async exportKeys({ conid, database, options }) {
|
||||
return this.loadDataCore('exportKeys', { conid, database, options });
|
||||
},
|
||||
|
||||
loadKeyInfo_meta: true,
|
||||
async loadKeyInfo({ conid, database, key }) {
|
||||
return this.loadDataCore('loadKeyInfo', { conid, database, key });
|
||||
},
|
||||
|
||||
loadKeyTableRange_meta: true,
|
||||
async loadKeyTableRange({ conid, database, key, cursor, count }) {
|
||||
return this.loadDataCore('loadKeyTableRange', { conid, database, key, cursor, count });
|
||||
},
|
||||
|
||||
loadFieldValues_meta: true,
|
||||
async loadFieldValues({ conid, database, schemaName, pureName, field, search }) {
|
||||
return this.loadDataCore('loadFieldValues', { conid, database, schemaName, pureName, field, search });
|
||||
},
|
||||
|
||||
callMethod_meta: true,
|
||||
async callMethod({ conid, database, method, args }) {
|
||||
return this.loadDataCore('callMethod', { conid, database, method, args });
|
||||
|
||||
// const opened = await this.ensureOpened(conid, database);
|
||||
// const res = await this.sendRequest(opened, { msgtype: 'callMethod', method, args });
|
||||
// if (res.errorMessage) {
|
||||
// console.error(res.errorMessage);
|
||||
// }
|
||||
// return res.result || null;
|
||||
},
|
||||
|
||||
updateCollection_meta: true,
|
||||
async updateCollection({ conid, database, changeSet }) {
|
||||
const opened = await this.ensureOpened(conid, database);
|
||||
const res = await this.sendRequest(opened, { msgtype: 'updateCollection', changeSet });
|
||||
return res.result;
|
||||
if (res.errorMessage) {
|
||||
return {
|
||||
errorMessage: res.errorMessage,
|
||||
};
|
||||
}
|
||||
return res.result || null;
|
||||
},
|
||||
|
||||
status_meta: true,
|
||||
@@ -225,6 +294,12 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
|
||||
closeAll(conid, kill = true) {
|
||||
for (const existing of this.opened.filter(x => x.conid == conid)) {
|
||||
this.close(conid, existing.database, kill);
|
||||
}
|
||||
},
|
||||
|
||||
disconnect_meta: true,
|
||||
async disconnect({ conid, database }) {
|
||||
await this.close(conid, database, true);
|
||||
@@ -250,6 +325,7 @@ module.exports = {
|
||||
|
||||
serverVersion_meta: true,
|
||||
async serverVersion({ conid, database }) {
|
||||
if (!conid) return null;
|
||||
const opened = await this.ensureOpened(conid, database);
|
||||
return opened.serverVersion || null;
|
||||
},
|
||||
@@ -321,8 +397,8 @@ module.exports = {
|
||||
const targetDb = generateDbPairingId(
|
||||
extendDatabaseInfo(await this.structure({ conid: targetConid, database: targetDatabase }))
|
||||
);
|
||||
// const sourceConnection = await connections.get({conid:sourceConid})
|
||||
const connection = await connections.get({ conid: targetConid });
|
||||
// const sourceConnection = await connections.getCore({conid:sourceConid})
|
||||
const connection = await connections.getCore({ conid: targetConid });
|
||||
const driver = requireEngineDriver(connection);
|
||||
const targetDbPaired = matchPairedObjects(sourceDb, targetDb, dbDiffOptions);
|
||||
const diffRows = computeDbDiffRows(sourceDb, targetDbPaired, dbDiffOptions, driver);
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
const uuidv1 = require('uuid/v1');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const { filesdir, archivedir, resolveArchiveFolder, uploadsdir } = require('../utility/directories');
|
||||
const { filesdir, archivedir, resolveArchiveFolder, uploadsdir, appdir } = require('../utility/directories');
|
||||
const getChartExport = require('../utility/getChartExport');
|
||||
const hasPermission = require('../utility/hasPermission');
|
||||
const { hasPermission } = require('../utility/hasPermission');
|
||||
const socket = require('../utility/socket');
|
||||
const scheduler = require('./scheduler');
|
||||
const getDiagramExport = require('../utility/getDiagramExport');
|
||||
const apps = require('./apps');
|
||||
|
||||
function serialize(format, data) {
|
||||
if (format == 'text') return data;
|
||||
@@ -21,8 +23,8 @@ function deserialize(format, text) {
|
||||
|
||||
module.exports = {
|
||||
list_meta: true,
|
||||
async list({ folder }) {
|
||||
if (!hasPermission(`files/${folder}/read`)) return [];
|
||||
async list({ folder }, req) {
|
||||
if (!hasPermission(`files/${folder}/read`, req)) return [];
|
||||
const dir = path.join(filesdir(), folder);
|
||||
if (!(await fs.exists(dir))) return [];
|
||||
const files = (await fs.readdir(dir)).map(file => ({ folder, file }));
|
||||
@@ -30,11 +32,11 @@ module.exports = {
|
||||
},
|
||||
|
||||
listAll_meta: true,
|
||||
async listAll() {
|
||||
async listAll(_params, req) {
|
||||
const folders = await fs.readdir(filesdir());
|
||||
const res = [];
|
||||
for (const folder of folders) {
|
||||
if (!hasPermission(`files/${folder}/read`)) continue;
|
||||
if (!hasPermission(`files/${folder}/read`, req)) continue;
|
||||
const dir = path.join(filesdir(), folder);
|
||||
const files = (await fs.readdir(dir)).map(file => ({ folder, file }));
|
||||
res.push(...files);
|
||||
@@ -43,51 +45,78 @@ module.exports = {
|
||||
},
|
||||
|
||||
delete_meta: true,
|
||||
async delete({ folder, file }) {
|
||||
if (!hasPermission(`files/${folder}/write`)) return;
|
||||
async delete({ folder, file }, req) {
|
||||
if (!hasPermission(`files/${folder}/write`, req)) return false;
|
||||
await fs.unlink(path.join(filesdir(), folder, file));
|
||||
socket.emitChanged(`files-changed-${folder}`);
|
||||
socket.emitChanged(`all-files-changed`);
|
||||
return true;
|
||||
},
|
||||
|
||||
rename_meta: true,
|
||||
async rename({ folder, file, newFile }) {
|
||||
if (!hasPermission(`files/${folder}/write`)) return;
|
||||
async rename({ folder, file, newFile }, req) {
|
||||
if (!hasPermission(`files/${folder}/write`, req)) return false;
|
||||
await fs.rename(path.join(filesdir(), folder, file), path.join(filesdir(), folder, newFile));
|
||||
socket.emitChanged(`files-changed-${folder}`);
|
||||
socket.emitChanged(`all-files-changed`);
|
||||
return true;
|
||||
},
|
||||
|
||||
refresh_meta: true,
|
||||
async refresh({ folders }, req) {
|
||||
for (const folder of folders) {
|
||||
socket.emitChanged(`files-changed-${folder}`);
|
||||
socket.emitChanged(`all-files-changed`);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
copy_meta: true,
|
||||
async copy({ folder, file, newFile }) {
|
||||
if (!hasPermission(`files/${folder}/write`)) return;
|
||||
async copy({ folder, file, newFile }, req) {
|
||||
if (!hasPermission(`files/${folder}/write`, req)) return false;
|
||||
await fs.copyFile(path.join(filesdir(), folder, file), path.join(filesdir(), folder, newFile));
|
||||
socket.emitChanged(`files-changed-${folder}`);
|
||||
socket.emitChanged(`all-files-changed`);
|
||||
return true;
|
||||
},
|
||||
|
||||
load_meta: true,
|
||||
async load({ folder, file, format }) {
|
||||
async load({ folder, file, format }, req) {
|
||||
if (folder.startsWith('archive:')) {
|
||||
const text = await fs.readFile(path.join(resolveArchiveFolder(folder.substring('archive:'.length)), file), {
|
||||
encoding: 'utf-8',
|
||||
});
|
||||
return deserialize(format, text);
|
||||
} else if (folder.startsWith('app:')) {
|
||||
const text = await fs.readFile(path.join(appdir(), folder.substring('app:'.length), file), {
|
||||
encoding: 'utf-8',
|
||||
});
|
||||
return deserialize(format, text);
|
||||
} else {
|
||||
if (!hasPermission(`files/${folder}/read`)) return null;
|
||||
if (!hasPermission(`files/${folder}/read`, req)) return null;
|
||||
const text = await fs.readFile(path.join(filesdir(), folder, file), { encoding: 'utf-8' });
|
||||
return deserialize(format, text);
|
||||
}
|
||||
},
|
||||
|
||||
save_meta: true,
|
||||
async save({ folder, file, data, format }) {
|
||||
async save({ folder, file, data, format }, req) {
|
||||
if (folder.startsWith('archive:')) {
|
||||
if (!hasPermission(`archive/write`, req)) return false;
|
||||
const dir = resolveArchiveFolder(folder.substring('archive:'.length));
|
||||
await fs.writeFile(path.join(dir, file), serialize(format, data));
|
||||
socket.emitChanged(`archive-files-changed-${folder.substring('archive:'.length)}`);
|
||||
return true;
|
||||
} else if (folder.startsWith('app:')) {
|
||||
if (!hasPermission(`apps/write`, req)) return false;
|
||||
const app = folder.substring('app:'.length);
|
||||
await fs.writeFile(path.join(appdir(), app, file), serialize(format, data));
|
||||
socket.emitChanged(`app-files-changed-${app}`);
|
||||
socket.emitChanged('used-apps-changed');
|
||||
apps.emitChangedDbApp(folder);
|
||||
return true;
|
||||
} else {
|
||||
if (!hasPermission(`files/${folder}/write`)) return;
|
||||
if (!hasPermission(`files/${folder}/write`, req)) return false;
|
||||
const dir = path.join(filesdir(), folder);
|
||||
if (!(await fs.exists(dir))) {
|
||||
await fs.mkdir(dir);
|
||||
@@ -98,6 +127,7 @@ module.exports = {
|
||||
if (folder == 'shell') {
|
||||
scheduler.reload();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -107,8 +137,8 @@ module.exports = {
|
||||
},
|
||||
|
||||
favorites_meta: true,
|
||||
async favorites() {
|
||||
if (!hasPermission(`files/favorites/read`)) return [];
|
||||
async favorites(_params, req) {
|
||||
if (!hasPermission(`files/favorites/read`, req)) return [];
|
||||
const dir = path.join(filesdir(), 'favorites');
|
||||
if (!(await fs.exists(dir))) return [];
|
||||
const files = await fs.readdir(dir);
|
||||
@@ -126,8 +156,8 @@ module.exports = {
|
||||
},
|
||||
|
||||
generateUploadsFile_meta: true,
|
||||
async generateUploadsFile() {
|
||||
const fileName = `${uuidv1()}.html`;
|
||||
async generateUploadsFile({ extension }) {
|
||||
const fileName = `${uuidv1()}.${extension || 'html'}`;
|
||||
return {
|
||||
fileName,
|
||||
filePath: path.join(uploadsdir(), fileName),
|
||||
@@ -150,4 +180,30 @@ module.exports = {
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
exportDiagram_meta: true,
|
||||
async exportDiagram({ filePath, html, css, themeType, themeClassName }) {
|
||||
await fs.writeFile(filePath, getDiagramExport(html, css, themeType, themeClassName));
|
||||
return true;
|
||||
},
|
||||
|
||||
getFileRealPath_meta: true,
|
||||
async getFileRealPath({ folder, file }, req) {
|
||||
if (folder.startsWith('archive:')) {
|
||||
if (!hasPermission(`archive/write`, req)) return false;
|
||||
const dir = resolveArchiveFolder(folder.substring('archive:'.length));
|
||||
return path.join(dir, file);
|
||||
} else if (folder.startsWith('app:')) {
|
||||
if (!hasPermission(`apps/write`, req)) return false;
|
||||
const app = folder.substring('app:'.length);
|
||||
return path.join(appdir(), app, file);
|
||||
} else {
|
||||
if (!hasPermission(`files/${folder}/write`, req)) return false;
|
||||
const dir = path.join(filesdir(), folder);
|
||||
if (!(await fs.exists(dir))) {
|
||||
await fs.mkdir(dir);
|
||||
}
|
||||
return path.join(dir, file);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
const { filterName } = require('dbgate-tools');
|
||||
const fs = require('fs');
|
||||
const lineReader = require('line-reader');
|
||||
const _ = require('lodash');
|
||||
const { __ } = require('lodash/fp');
|
||||
const DatastoreProxy = require('../utility/DatastoreProxy');
|
||||
const { saveFreeTableData } = require('../utility/freeTableStorage');
|
||||
const getJslFileName = require('../utility/getJslFileName');
|
||||
@@ -10,7 +12,10 @@ const socket = require('../utility/socket');
|
||||
function readFirstLine(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
lineReader.open(file, (err, reader) => {
|
||||
if (err) reject(err);
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
if (reader.hasNextLine()) {
|
||||
reader.nextLine((err, line) => {
|
||||
if (err) reject(err);
|
||||
@@ -107,9 +112,22 @@ module.exports = {
|
||||
getInfo_meta: true,
|
||||
async getInfo({ jslid }) {
|
||||
const file = getJslFileName(jslid);
|
||||
const firstLine = await readFirstLine(file);
|
||||
if (firstLine) return JSON.parse(firstLine);
|
||||
return null;
|
||||
try {
|
||||
const firstLine = await readFirstLine(file);
|
||||
if (firstLine) {
|
||||
const parsed = JSON.parse(firstLine);
|
||||
if (parsed.__isStreamHeader) {
|
||||
return parsed;
|
||||
}
|
||||
return {
|
||||
__isStreamHeader: true,
|
||||
__isDynamicStructure: true,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
getRows_meta: true,
|
||||
@@ -131,8 +149,21 @@ module.exports = {
|
||||
return {};
|
||||
},
|
||||
|
||||
loadFieldValues_meta: true,
|
||||
async loadFieldValues({ jslid, field, search }) {
|
||||
const datastore = await this.ensureDatastore(jslid);
|
||||
const res = new Set();
|
||||
await datastore.enumRows(row => {
|
||||
if (!filterName(search, row[field])) return true;
|
||||
res.add(row[field]);
|
||||
return res.size < 100;
|
||||
});
|
||||
// @ts-ignore
|
||||
return [...res].map(value => ({ value }));
|
||||
},
|
||||
|
||||
async notifyChangedStats(stats) {
|
||||
console.log('SENDING STATS', JSON.stringify(stats));
|
||||
// console.log('SENDING STATS', JSON.stringify(stats));
|
||||
const datastore = this.datastores[stats.jslid];
|
||||
if (datastore) await datastore.notifyChanged();
|
||||
socket.emit(`jsldata-stats-${stats.jslid}`, stats);
|
||||
@@ -151,4 +182,10 @@ module.exports = {
|
||||
saveFreeTableData(getJslFileName(jslid), data);
|
||||
return true;
|
||||
},
|
||||
|
||||
saveText_meta: true,
|
||||
async saveText({ jslid, text }) {
|
||||
await fs.promises.writeFile(getJslFileName(jslid), text);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ const socket = require('../utility/socket');
|
||||
const compareVersions = require('compare-versions');
|
||||
const requirePlugin = require('../shell/requirePlugin');
|
||||
const downloadPackage = require('../utility/downloadPackage');
|
||||
const hasPermission = require('../utility/hasPermission');
|
||||
const { hasPermission } = require('../utility/hasPermission');
|
||||
const _ = require('lodash');
|
||||
const packagedPluginsContent = require('../packagedPluginsContent');
|
||||
|
||||
@@ -72,6 +72,7 @@ module.exports = {
|
||||
|
||||
const res = [];
|
||||
for (const packageName of _.union(files1, files2)) {
|
||||
if (packageName == 'dist') continue;
|
||||
if (!/^dbgate-plugin-.*$/.test(packageName)) continue;
|
||||
try {
|
||||
if (packagedContent && packagedContent[packageName]) {
|
||||
@@ -88,6 +89,12 @@ module.exports = {
|
||||
encoding: 'utf-8',
|
||||
})
|
||||
.then(x => JSON.parse(x));
|
||||
if (!manifest.keywords) {
|
||||
continue;
|
||||
}
|
||||
if (!manifest.keywords.includes('dbgateplugin')) {
|
||||
continue;
|
||||
}
|
||||
const readmeFile = path.join(isPackaged ? packagedPluginsDir() : pluginsdir(), packageName, 'README.md');
|
||||
// @ts-ignore
|
||||
if (await fs.exists(readmeFile)) {
|
||||
@@ -108,8 +115,8 @@ module.exports = {
|
||||
// },
|
||||
|
||||
install_meta: true,
|
||||
async install({ packageName }) {
|
||||
if (!hasPermission(`plugins/install`)) return;
|
||||
async install({ packageName }, req) {
|
||||
if (!hasPermission(`plugins/install`, req)) return;
|
||||
const dir = path.join(pluginsdir(), packageName);
|
||||
// @ts-ignore
|
||||
if (!(await fs.exists(dir))) {
|
||||
@@ -118,21 +125,23 @@ module.exports = {
|
||||
socket.emitChanged(`installed-plugins-changed`);
|
||||
// this.removedPlugins = this.removedPlugins.filter(x => x != packageName);
|
||||
// await this.saveRemovePlugins();
|
||||
return true;
|
||||
},
|
||||
|
||||
uninstall_meta: true,
|
||||
async uninstall({ packageName }) {
|
||||
if (!hasPermission(`plugins/install`)) return;
|
||||
async uninstall({ packageName }, req) {
|
||||
if (!hasPermission(`plugins/install`, req)) return;
|
||||
const dir = path.join(pluginsdir(), packageName);
|
||||
await fs.rmdir(dir, { recursive: true });
|
||||
socket.emitChanged(`installed-plugins-changed`);
|
||||
// this.removedPlugins.push(packageName);
|
||||
await this.saveRemovePlugins();
|
||||
// await this.saveRemovePlugins();
|
||||
return true;
|
||||
},
|
||||
|
||||
upgrade_meta: true,
|
||||
async upgrade({ packageName }) {
|
||||
if (!hasPermission(`plugins/install`)) return;
|
||||
async upgrade({ packageName }, req) {
|
||||
if (!hasPermission(`plugins/install`, req)) return;
|
||||
const dir = path.join(pluginsdir(), packageName);
|
||||
// @ts-ignore
|
||||
if (await fs.exists(dir)) {
|
||||
@@ -141,6 +150,7 @@ module.exports = {
|
||||
}
|
||||
|
||||
socket.emitChanged(`installed-plugins-changed`);
|
||||
return true;
|
||||
},
|
||||
|
||||
command_meta: true,
|
||||
|
||||
@@ -48,7 +48,7 @@ module.exports = {
|
||||
async write({ data }) {
|
||||
const fileName = path.join(datadir(), 'query-history.jsonl');
|
||||
await fs.appendFile(fileName, JSON.stringify(data) + '\n');
|
||||
socket.emitChanged('query-history-changed');
|
||||
socket.emit('query-history-changed');
|
||||
return 'OK';
|
||||
},
|
||||
};
|
||||
|
||||
@@ -6,8 +6,10 @@ const byline = require('byline');
|
||||
const socket = require('../utility/socket');
|
||||
const { fork } = require('child_process');
|
||||
const { rundir, uploadsdir, pluginsdir, getPluginBackendPath, packagedPluginList } = require('../utility/directories');
|
||||
const { extractShellApiPlugins, extractShellApiFunctionName } = require('dbgate-tools');
|
||||
const { extractShellApiPlugins, extractShellApiFunctionName, jsonScriptToJavascript } = require('dbgate-tools');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
const processArgs = require('../utility/processArgs');
|
||||
const platformInfo = require('../utility/platformInfo');
|
||||
|
||||
function extractPlugins(script) {
|
||||
const requireRegex = /\s*\/\/\s*@require\s+([^\s]+)\s*\n/g;
|
||||
@@ -98,15 +100,23 @@ module.exports = {
|
||||
const pluginNames = _.union(fs.readdirSync(pluginsdir()), packagedPluginList);
|
||||
console.log(`RUNNING SCRIPT ${scriptFile}`);
|
||||
// const subprocess = fork(scriptFile, ['--checkParent', '--max-old-space-size=8192'], {
|
||||
const subprocess = fork(scriptFile, ['--checkParent', ...process.argv.slice(3)], {
|
||||
cwd: directory,
|
||||
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
|
||||
env: {
|
||||
...process.env,
|
||||
DBGATE_API: global['API_PACKAGE'] || global['dbgateApiModulePath'] || process.argv[1],
|
||||
..._.fromPairs(pluginNames.map(name => [`PLUGIN_${_.camelCase(name)}`, getPluginBackendPath(name)])),
|
||||
},
|
||||
});
|
||||
const subprocess = fork(
|
||||
scriptFile,
|
||||
[
|
||||
'--checkParent', // ...process.argv.slice(3)
|
||||
'--is-forked-api',
|
||||
...processArgs.getPassArgs(),
|
||||
],
|
||||
{
|
||||
cwd: directory,
|
||||
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
|
||||
env: {
|
||||
...process.env,
|
||||
DBGATE_API: global['API_PACKAGE'] || process.argv[1],
|
||||
..._.fromPairs(pluginNames.map(name => [`PLUGIN_${_.camelCase(name)}`, getPluginBackendPath(name)])),
|
||||
},
|
||||
}
|
||||
);
|
||||
const pipeDispatcher = severity => data =>
|
||||
this.dispatchMessage(runid, { severity, message: data.toString().trim() });
|
||||
|
||||
@@ -136,12 +146,22 @@ module.exports = {
|
||||
if (handleProcessCommunication(message, subprocess)) return;
|
||||
this[`handle_${msgtype}`](runid, message);
|
||||
});
|
||||
return newOpened;
|
||||
return _.pick(newOpened, ['runid']);
|
||||
},
|
||||
|
||||
start_meta: true,
|
||||
async start({ script }) {
|
||||
const runid = uuidv1();
|
||||
|
||||
if (script.type == 'json') {
|
||||
const js = jsonScriptToJavascript(script);
|
||||
return this.startCore(runid, scriptTemplate(js, false));
|
||||
}
|
||||
|
||||
if (!platformInfo.allowShellScripting) {
|
||||
return { errorMessage: 'Shell scripting is not allowed' };
|
||||
}
|
||||
|
||||
return this.startCore(runid, scriptTemplate(script, false));
|
||||
},
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const cron = require('node-cron');
|
||||
const runners = require('./runners');
|
||||
const hasPermission = require('../utility/hasPermission');
|
||||
const { hasPermission } = require('../utility/hasPermission');
|
||||
|
||||
const scheduleRegex = /\s*\/\/\s*@schedule\s+([^\n]+)\n/;
|
||||
|
||||
@@ -26,8 +26,8 @@ module.exports = {
|
||||
this.tasks.push(task);
|
||||
},
|
||||
|
||||
async reload() {
|
||||
if (!hasPermission('files/shell/read')) return;
|
||||
async reload(_params, req) {
|
||||
if (!hasPermission('files/shell/read', req)) return;
|
||||
const shellDir = path.join(filesdir(), 'shell');
|
||||
await this.unload();
|
||||
if (!(await fs.exists(shellDir))) return;
|
||||
|
||||
@@ -6,6 +6,7 @@ const AsyncLock = require('async-lock');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
const lock = new AsyncLock();
|
||||
const config = require('./config');
|
||||
const processArgs = require('../utility/processArgs');
|
||||
|
||||
module.exports = {
|
||||
opened: [],
|
||||
@@ -36,12 +37,13 @@ module.exports = {
|
||||
const res = await lock.acquire(conid, async () => {
|
||||
const existing = this.opened.find(x => x.conid == conid);
|
||||
if (existing) return existing;
|
||||
const connection = await connections.get({ conid });
|
||||
const connection = await connections.getCore({ conid });
|
||||
const subprocess = fork(global['API_PACKAGE'] || process.argv[1], [
|
||||
'--is-forked-api',
|
||||
'--start-process',
|
||||
'serverConnectionProcess',
|
||||
...process.argv.slice(3),
|
||||
...processArgs.getPassArgs(),
|
||||
// ...process.argv.slice(3),
|
||||
]);
|
||||
const newOpened = {
|
||||
conid,
|
||||
@@ -67,7 +69,7 @@ module.exports = {
|
||||
if (newOpened.disconnected) return;
|
||||
this.close(conid, false);
|
||||
});
|
||||
subprocess.send({ msgtype: 'connect', ...connection, globalSettings: config.settingsValue });
|
||||
subprocess.send({ msgtype: 'connect', ...connection, globalSettings: await config.getSettings() });
|
||||
return newOpened;
|
||||
});
|
||||
return res;
|
||||
@@ -140,6 +142,7 @@ module.exports = {
|
||||
createDatabase_meta: true,
|
||||
async createDatabase({ conid, name }) {
|
||||
const opened = await this.ensureOpened(conid);
|
||||
if (opened.connection.isReadOnly) return false;
|
||||
opened.subprocess.send({ msgtype: 'createDatabase', name });
|
||||
return { status: 'ok' };
|
||||
},
|
||||
|
||||
@@ -4,7 +4,10 @@ const connections = require('./connections');
|
||||
const socket = require('../utility/socket');
|
||||
const { fork } = require('child_process');
|
||||
const jsldata = require('./jsldata');
|
||||
const path = require('path');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
const processArgs = require('../utility/processArgs');
|
||||
const { appdir } = require('../utility/directories');
|
||||
|
||||
module.exports = {
|
||||
/** @type {import('dbgate-types').OpenedSession[]} */
|
||||
@@ -45,9 +48,18 @@ module.exports = {
|
||||
this.dispatchMessage(sesid, info);
|
||||
},
|
||||
|
||||
handle_done(sesid) {
|
||||
handle_done(sesid, props) {
|
||||
socket.emit(`session-done-${sesid}`);
|
||||
this.dispatchMessage(sesid, 'Query execution finished');
|
||||
if (!props.skipFinishedMessage) {
|
||||
this.dispatchMessage(sesid, 'Query execution finished');
|
||||
}
|
||||
const session = this.opened.find(x => x.sesid == sesid);
|
||||
if (session.loadingReader_jslid) {
|
||||
socket.emit(`session-jslid-done-${session.loadingReader_jslid}`);
|
||||
}
|
||||
if (session.killOnDone) {
|
||||
this.kill({ sesid });
|
||||
}
|
||||
},
|
||||
|
||||
handle_recordset(sesid, props) {
|
||||
@@ -59,17 +71,23 @@ module.exports = {
|
||||
jsldata.notifyChangedStats(stats);
|
||||
},
|
||||
|
||||
handle_initializeFile(sesid, props) {
|
||||
const { jslid } = props;
|
||||
socket.emit(`session-initialize-file-${jslid}`);
|
||||
},
|
||||
|
||||
handle_ping() {},
|
||||
|
||||
create_meta: true,
|
||||
async create({ conid, database }) {
|
||||
const sesid = uuidv1();
|
||||
const connection = await connections.get({ conid });
|
||||
const connection = await connections.getCore({ conid });
|
||||
const subprocess = fork(global['API_PACKAGE'] || process.argv[1], [
|
||||
'--is-forked-api',
|
||||
'--start-process',
|
||||
'sessionProcess',
|
||||
...process.argv.slice(3),
|
||||
...processArgs.getPassArgs(),
|
||||
// ...process.argv.slice(3),
|
||||
]);
|
||||
const newOpened = {
|
||||
conid,
|
||||
@@ -86,7 +104,7 @@ module.exports = {
|
||||
this[`handle_${msgtype}`](sesid, message);
|
||||
});
|
||||
subprocess.send({ msgtype: 'connect', ...connection, database });
|
||||
return newOpened;
|
||||
return _.pick(newOpened, ['conid', 'database', 'sesid']);
|
||||
},
|
||||
|
||||
executeQuery_meta: true,
|
||||
@@ -103,6 +121,29 @@ module.exports = {
|
||||
return { state: 'ok' };
|
||||
},
|
||||
|
||||
executeReader_meta: true,
|
||||
async executeReader({ conid, database, sql, queryName, appFolder }) {
|
||||
const { sesid } = await this.create({ conid, database });
|
||||
const session = this.opened.find(x => x.sesid == sesid);
|
||||
session.killOnDone = true;
|
||||
const jslid = uuidv1();
|
||||
session.loadingReader_jslid = jslid;
|
||||
const fileName = queryName && appFolder ? path.join(appdir(), appFolder, `${queryName}.query.sql`) : null;
|
||||
|
||||
session.subprocess.send({ msgtype: 'executeReader', sql, fileName, jslid });
|
||||
|
||||
return { jslid };
|
||||
},
|
||||
|
||||
stopLoadingReader_meta: true,
|
||||
async stopLoadingReader({ jslid }) {
|
||||
const session = this.opened.find(x => x.loadingReader_jslid == jslid);
|
||||
if (session) {
|
||||
this.kill({ sesid: session.sesid });
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
// cancel_meta: true,
|
||||
// async cancel({ sesid }) {
|
||||
// const session = this.opened.find((x) => x.sesid == sesid);
|
||||
|
||||
@@ -8,7 +8,7 @@ if (processArgs.startProcess) {
|
||||
const proc = require('./proc');
|
||||
const module = proc[processArgs.startProcess];
|
||||
module.start();
|
||||
} else if (!module['parent'] && !processArgs.checkParent) {
|
||||
} else if (!processArgs.checkParent && !global['API_PACKAGE']) {
|
||||
const main = require('./main');
|
||||
|
||||
main.start();
|
||||
|
||||
@@ -4,11 +4,8 @@ const bodyParser = require('body-parser');
|
||||
const fileUpload = require('express-fileupload');
|
||||
const http = require('http');
|
||||
const cors = require('cors');
|
||||
const fs = require('fs');
|
||||
const getPort = require('get-port');
|
||||
const childProcessChecker = require('./utility/childProcessChecker');
|
||||
const path = require('path');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const useController = require('./utility/useController');
|
||||
const socket = require('./utility/socket');
|
||||
@@ -22,6 +19,7 @@ const runners = require('./controllers/runners');
|
||||
const jsldata = require('./controllers/jsldata');
|
||||
const config = require('./controllers/config');
|
||||
const archive = require('./controllers/archive');
|
||||
const apps = require('./controllers/apps');
|
||||
const uploads = require('./controllers/uploads');
|
||||
const plugins = require('./controllers/plugins');
|
||||
const files = require('./controllers/files');
|
||||
@@ -30,11 +28,9 @@ const queryHistory = require('./controllers/queryHistory');
|
||||
|
||||
const { rundir } = require('./utility/directories');
|
||||
const platformInfo = require('./utility/platformInfo');
|
||||
const processArgs = require('./utility/processArgs');
|
||||
const timingSafeCheckToken = require('./utility/timingSafeCheckToken');
|
||||
|
||||
let authorization = null;
|
||||
let checkLocalhostOrigin = null;
|
||||
const getExpressPath = require('./utility/getExpressPath');
|
||||
const { getLogins } = require('./utility/hasPermission');
|
||||
const _ = require('lodash');
|
||||
|
||||
function start() {
|
||||
// console.log('process.argv', process.argv);
|
||||
@@ -43,47 +39,24 @@ function start() {
|
||||
|
||||
const server = http.createServer(app);
|
||||
|
||||
if (process.env.LOGIN && process.env.PASSWORD) {
|
||||
const logins = getLogins();
|
||||
if (logins) {
|
||||
app.use(
|
||||
basicAuth({
|
||||
users: {
|
||||
[process.env.LOGIN]: process.env.PASSWORD,
|
||||
},
|
||||
users: _.fromPairs(logins.map(x => [x.login, x.password])),
|
||||
challenge: true,
|
||||
realm: 'DbGate Web App',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
if (authorization && !timingSafeCheckToken(req.headers.authorization, authorization)) {
|
||||
return res.status(403).json({ error: 'Not authorized!' });
|
||||
}
|
||||
if (checkLocalhostOrigin) {
|
||||
if (
|
||||
req.headers.origin &&
|
||||
req.headers.origin != checkLocalhostOrigin &&
|
||||
req.headers.origin != `http://${checkLocalhostOrigin}`
|
||||
) {
|
||||
console.log('API origin check FAILED');
|
||||
console.log('HEADERS', { ...req.headers, authorization: '***' });
|
||||
return res.status(403).json({ error: 'Not authorized!' });
|
||||
}
|
||||
if (!req.headers.origin && req.headers.host != checkLocalhostOrigin) {
|
||||
console.log('API host check FAILED');
|
||||
console.log('HEADERS', { ...req.headers, authorization: '***' });
|
||||
return res.status(403).json({ error: 'Not authorized!' });
|
||||
}
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
app.use(cors());
|
||||
|
||||
app.get('/stream', async function (req, res) {
|
||||
app.get(getExpressPath('/stream'), async function (req, res) {
|
||||
res.set({
|
||||
'Cache-Control': 'no-cache',
|
||||
'Content-Type': 'text/event-stream',
|
||||
'X-Accel-Buffering': 'no',
|
||||
Connection: 'keep-alive',
|
||||
});
|
||||
res.flushHeaders();
|
||||
@@ -96,7 +69,7 @@ function start() {
|
||||
app.use(bodyParser.json({ limit: '50mb' }));
|
||||
|
||||
app.use(
|
||||
'/uploads',
|
||||
getExpressPath('/uploads'),
|
||||
fileUpload({
|
||||
limits: { fileSize: 4 * 1024 * 1024 },
|
||||
})
|
||||
@@ -108,29 +81,60 @@ function start() {
|
||||
// app.use('/pages', express.static(process.env.PAGES_DIRECTORY));
|
||||
// }
|
||||
|
||||
app.use('/runners/data', express.static(rundir()));
|
||||
app.use(getExpressPath('/runners/data'), express.static(rundir()));
|
||||
|
||||
if (platformInfo.isDocker) {
|
||||
// server static files inside docker container
|
||||
app.use(express.static('/home/dbgate-docker/public'));
|
||||
} else {
|
||||
if (!platformInfo.isNpmDist) {
|
||||
app.get('/', (req, res) => {
|
||||
res.send('DbGate API');
|
||||
});
|
||||
}
|
||||
}
|
||||
app.use(getExpressPath('/'), express.static('/home/dbgate-docker/public'));
|
||||
|
||||
if (platformInfo.isNpmDist) {
|
||||
app.use(express.static(path.join(__dirname, '../../dbgate-web/public')));
|
||||
getPort({ port: 5000 }).then(port => {
|
||||
const port = process.env.PORT || 3000;
|
||||
console.log('DbGate API listening on port (docker build)', port);
|
||||
server.listen(port);
|
||||
} else if (platformInfo.isNpmDist) {
|
||||
app.use(getExpressPath('/'), express.static(path.join(__dirname, '../../dbgate-web/public')));
|
||||
getPort({
|
||||
port: parseInt(
|
||||
// @ts-ignore
|
||||
process.env.PORT || 3000
|
||||
),
|
||||
}).then(port => {
|
||||
server.listen(port, () => {
|
||||
console.log(`DbGate API listening on port ${port}`);
|
||||
console.log(`DbGate API listening on port ${port} (NPM build)`);
|
||||
});
|
||||
});
|
||||
} else if (process.env.DEVWEB) {
|
||||
console.log('__dirname', __dirname);
|
||||
console.log(path.join(__dirname, '../../web/public/build'));
|
||||
app.use(getExpressPath('/'), express.static(path.join(__dirname, '../../web/public')));
|
||||
|
||||
const port = process.env.PORT || 3000;
|
||||
console.log('DbGate API & web listening on port (dev web build)', port);
|
||||
server.listen(port);
|
||||
} else {
|
||||
server.listen(process.env.PORT || 3000);
|
||||
app.get(getExpressPath('/'), (req, res) => {
|
||||
res.send('DbGate API');
|
||||
});
|
||||
|
||||
const port = process.env.PORT || 3000;
|
||||
console.log('DbGate API listening on port (dev API build)', port);
|
||||
server.listen(port);
|
||||
}
|
||||
|
||||
function shutdown() {
|
||||
console.log('\nShutting down DbGate API server');
|
||||
server.close(() => {
|
||||
console.log('Server shut down, terminating');
|
||||
process.exit(0);
|
||||
});
|
||||
setTimeout(() => {
|
||||
console.log('Server close timeout, terminating');
|
||||
process.exit(0);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
process.on('SIGINT', shutdown);
|
||||
process.on('SIGTERM', shutdown);
|
||||
process.on('SIGBREAK', shutdown);
|
||||
}
|
||||
|
||||
function useAllControllers(app, electron) {
|
||||
@@ -148,10 +152,11 @@ function useAllControllers(app, electron) {
|
||||
useController(app, electron, '/files', files);
|
||||
useController(app, electron, '/scheduler', scheduler);
|
||||
useController(app, electron, '/query-history', queryHistory);
|
||||
useController(app, electron, '/apps', apps);
|
||||
}
|
||||
|
||||
function initializeElectronSender(electronSender) {
|
||||
function setElectronSender(electronSender) {
|
||||
socket.setElectronSender(electronSender);
|
||||
}
|
||||
|
||||
module.exports = { start, useAllControllers, initializeElectronSender };
|
||||
module.exports = { start, useAllControllers, setElectronSender, configController: config };
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
const argIndex = process.argv.indexOf('--native-modules');
|
||||
const redirectFile = argIndex > 0 ? process.argv[argIndex + 1] : null;
|
||||
const redirectFile = global['NATIVE_MODULES'] || (argIndex > 0 ? process.argv[argIndex + 1] : null);
|
||||
|
||||
// @ts-ignore
|
||||
module.exports = redirectFile ? __non_webpack_require__(redirectFile) : require('./nativeModulesContent');
|
||||
function requireDynamic(file) {
|
||||
try {
|
||||
// @ts-ignore
|
||||
return __non_webpack_require__(redirectFile);
|
||||
} catch (err) {
|
||||
return require(redirectFile);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = redirectFile ? requireDynamic(redirectFile) : require('./nativeModulesContent');
|
||||
|
||||
@@ -2,17 +2,9 @@ const childProcessChecker = require('../utility/childProcessChecker');
|
||||
const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||
const connectUtility = require('../utility/connectUtility');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
const { pickSafeConnectionInfo } = require('../utility/crypting');
|
||||
const _ = require('lodash');
|
||||
|
||||
function pickSafeConnectionInfo(connection) {
|
||||
return _.mapValues(connection, (v, k) => {
|
||||
if (k == 'engine' || k == 'port' || k == 'authType' || k == 'sshMode' || k == 'passwordMode') return v;
|
||||
if (v === null || v === true || v === false) return v;
|
||||
if (v) return '***';
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
const formatErrorDetail = (e, connection) => `${e.stack}
|
||||
|
||||
Error JSON: ${JSON.stringify(e, undefined, 2)}
|
||||
@@ -28,7 +20,7 @@ function start() {
|
||||
if (handleProcessCommunication(connection)) return;
|
||||
try {
|
||||
const driver = requireEngineDriver(connection);
|
||||
const conn = await connectUtility(driver, connection);
|
||||
const conn = await connectUtility(driver, connection, 'app');
|
||||
const res = await driver.getVersion(conn);
|
||||
process.send({ msgtype: 'connected', ...res });
|
||||
} catch (e) {
|
||||
|
||||
@@ -7,6 +7,7 @@ const connectUtility = require('../utility/connectUtility');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
const { SqlGenerator } = require('dbgate-tools');
|
||||
const generateDeploySql = require('../shell/generateDeploySql');
|
||||
const { dumpSqlSelect } = require('dbgate-sqltree');
|
||||
|
||||
let systemConnection;
|
||||
let storedConnection;
|
||||
@@ -18,6 +19,12 @@ let lastStatus = null;
|
||||
let analysedTime = 0;
|
||||
let serverVersion;
|
||||
|
||||
let statusCounter = 0;
|
||||
function getStatusCounter() {
|
||||
statusCounter += 1;
|
||||
return statusCounter;
|
||||
}
|
||||
|
||||
async function checkedAsyncCall(promise) {
|
||||
try {
|
||||
const res = await promise;
|
||||
@@ -79,7 +86,7 @@ function handleSyncModel() {
|
||||
function setStatus(status) {
|
||||
const statusString = stableStringify(status);
|
||||
if (lastStatus != statusString) {
|
||||
process.send({ msgtype: 'status', status });
|
||||
process.send({ msgtype: 'status', status: { ...status, counter: getStatusCounter() } });
|
||||
lastStatus = statusString;
|
||||
}
|
||||
}
|
||||
@@ -101,7 +108,7 @@ async function handleConnect({ connection, structure, globalSettings }) {
|
||||
|
||||
if (!structure) setStatusName('pending');
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
systemConnection = await checkedAsyncCall(connectUtility(driver, storedConnection));
|
||||
systemConnection = await checkedAsyncCall(connectUtility(driver, storedConnection, 'app'));
|
||||
await checkedAsyncCall(readVersion());
|
||||
if (structure) {
|
||||
analysedStructure = structure;
|
||||
@@ -148,6 +155,7 @@ async function handleRunScript({ msgid, sql }) {
|
||||
await waitConnected();
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
try {
|
||||
ensureExecuteCustomScript(driver);
|
||||
await driver.script(systemConnection, sql);
|
||||
process.send({ msgtype: 'response', msgid });
|
||||
} catch (err) {
|
||||
@@ -159,6 +167,7 @@ async function handleQueryData({ msgid, sql }) {
|
||||
await waitConnected();
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
try {
|
||||
ensureExecuteCustomScript(driver);
|
||||
const res = await driver.query(systemConnection, sql);
|
||||
process.send({ msgtype: 'response', msgid, ...res });
|
||||
} catch (err) {
|
||||
@@ -166,21 +175,75 @@ async function handleQueryData({ msgid, sql }) {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCollectionData({ msgid, options }) {
|
||||
async function handleSqlSelect({ msgid, select }) {
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
const dmp = driver.createDumper();
|
||||
dumpSqlSelect(dmp, select);
|
||||
return handleQueryData({ msgid, sql: dmp.s });
|
||||
}
|
||||
|
||||
async function handleDriverDataCore(msgid, callMethod) {
|
||||
await waitConnected();
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
try {
|
||||
const result = await driver.readCollection(systemConnection, options);
|
||||
const result = await callMethod(driver);
|
||||
process.send({ msgtype: 'response', msgid, result });
|
||||
} catch (err) {
|
||||
process.send({ msgtype: 'response', msgid, errorMessage: err.message });
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCollectionData({ msgid, options }) {
|
||||
return handleDriverDataCore(msgid, driver => driver.readCollection(systemConnection, options));
|
||||
}
|
||||
|
||||
async function handleLoadKeys({ msgid, root, filter }) {
|
||||
return handleDriverDataCore(msgid, driver => driver.loadKeys(systemConnection, root, filter));
|
||||
}
|
||||
|
||||
async function handleExportKeys({ msgid, options }) {
|
||||
return handleDriverDataCore(msgid, driver => driver.exportKeys(systemConnection, options));
|
||||
}
|
||||
|
||||
async function handleLoadKeyInfo({ msgid, key }) {
|
||||
return handleDriverDataCore(msgid, driver => driver.loadKeyInfo(systemConnection, key));
|
||||
}
|
||||
|
||||
async function handleCallMethod({ msgid, method, args }) {
|
||||
return handleDriverDataCore(msgid, driver => {
|
||||
if (storedConnection.isReadOnly) {
|
||||
throw new Error('Connection is read only, cannot call custom methods');
|
||||
}
|
||||
|
||||
ensureExecuteCustomScript(driver);
|
||||
return driver.callMethod(systemConnection, method, args);
|
||||
});
|
||||
}
|
||||
|
||||
async function handleLoadKeyTableRange({ msgid, key, cursor, count }) {
|
||||
return handleDriverDataCore(msgid, driver => driver.loadKeyTableRange(systemConnection, key, cursor, count));
|
||||
}
|
||||
|
||||
async function handleLoadFieldValues({ msgid, schemaName, pureName, field, search }) {
|
||||
return handleDriverDataCore(msgid, driver =>
|
||||
driver.loadFieldValues(systemConnection, { schemaName, pureName }, field, search)
|
||||
);
|
||||
}
|
||||
|
||||
function ensureExecuteCustomScript(driver) {
|
||||
if (driver.readOnlySessions) {
|
||||
return;
|
||||
}
|
||||
if (storedConnection.isReadOnly) {
|
||||
throw new Error('Connection is read only');
|
||||
}
|
||||
}
|
||||
|
||||
async function handleUpdateCollection({ msgid, changeSet }) {
|
||||
await waitConnected();
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
try {
|
||||
ensureExecuteCustomScript(driver);
|
||||
const result = await driver.updateCollection(systemConnection, changeSet);
|
||||
process.send({ msgtype: 'response', msgid, result });
|
||||
} catch (err) {
|
||||
@@ -242,10 +305,17 @@ const messageHandlers = {
|
||||
runScript: handleRunScript,
|
||||
updateCollection: handleUpdateCollection,
|
||||
collectionData: handleCollectionData,
|
||||
loadKeys: handleLoadKeys,
|
||||
loadKeyInfo: handleLoadKeyInfo,
|
||||
callMethod: handleCallMethod,
|
||||
loadKeyTableRange: handleLoadKeyTableRange,
|
||||
sqlPreview: handleSqlPreview,
|
||||
ping: handlePing,
|
||||
syncModel: handleSyncModel,
|
||||
generateDeploySql: handleGenerateDeploySql,
|
||||
loadFieldValues: handleLoadFieldValues,
|
||||
sqlSelect: handleSqlSelect,
|
||||
exportKeys: handleExportKeys,
|
||||
// runCommand: handleRunCommand,
|
||||
};
|
||||
|
||||
@@ -270,6 +340,7 @@ function start() {
|
||||
try {
|
||||
await handleMessage(message);
|
||||
} catch (e) {
|
||||
console.error('Error in DB connection', e);
|
||||
process.send({ msgtype: 'error', error: e.message });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ const databaseConnectionProcess = require('./databaseConnectionProcess');
|
||||
const serverConnectionProcess = require('./serverConnectionProcess');
|
||||
const sessionProcess = require('./sessionProcess');
|
||||
const jslDatastoreProcess = require('./jslDatastoreProcess');
|
||||
const sshForwardProcess = require('./sshForwardProcess');
|
||||
|
||||
module.exports = {
|
||||
connectProcess,
|
||||
@@ -10,4 +11,5 @@ module.exports = {
|
||||
serverConnectionProcess,
|
||||
sessionProcess,
|
||||
jslDatastoreProcess,
|
||||
sshForwardProcess,
|
||||
};
|
||||
|
||||
@@ -58,11 +58,14 @@ async function handleConnect(connection) {
|
||||
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
try {
|
||||
systemConnection = await connectUtility(driver, storedConnection);
|
||||
systemConnection = await connectUtility(driver, storedConnection, 'app');
|
||||
readVersion();
|
||||
handleRefresh();
|
||||
if (extractBoolSettingsValue(globalSettings, 'connection.autoRefresh', false)) {
|
||||
setInterval(handleRefresh, extractIntSettingsValue(globalSettings, 'connection.autoRefreshInterval', 30, 5, 3600) * 1000);
|
||||
setInterval(
|
||||
handleRefresh,
|
||||
extractIntSettingsValue(globalSettings, 'connection.autoRefreshInterval', 30, 5, 3600) * 1000
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
setStatus({
|
||||
@@ -80,7 +83,7 @@ function handlePing() {
|
||||
|
||||
async function handleCreateDatabase({ name }) {
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
systemConnection = await connectUtility(driver, storedConnection);
|
||||
systemConnection = await connectUtility(driver, storedConnection, 'app');
|
||||
console.log(`RUNNING SCRIPT: CREATE DATABASE ${driver.dialect.quoteIdentifier(name)}`);
|
||||
if (driver.createDatabase) {
|
||||
await driver.createDatabase(systemConnection, name);
|
||||
|
||||
@@ -17,11 +17,15 @@ let afterConnectCallbacks = [];
|
||||
// let currentHandlers = [];
|
||||
|
||||
class TableWriter {
|
||||
constructor(structure, resultIndex) {
|
||||
this.jslid = uuidv1();
|
||||
this.currentFile = path.join(jsldir(), `${this.jslid}.jsonl`);
|
||||
constructor() {
|
||||
this.currentRowCount = 0;
|
||||
this.currentChangeIndex = 1;
|
||||
this.initializedFile = false;
|
||||
}
|
||||
|
||||
initializeFromQuery(structure, resultIndex) {
|
||||
this.jslid = uuidv1();
|
||||
this.currentFile = path.join(jsldir(), `${this.jslid}.jsonl`);
|
||||
fs.writeFileSync(
|
||||
this.currentFile,
|
||||
JSON.stringify({
|
||||
@@ -32,13 +36,21 @@ class TableWriter {
|
||||
this.currentStream = fs.createWriteStream(this.currentFile, { flags: 'a' });
|
||||
this.writeCurrentStats(false, false);
|
||||
this.resultIndex = resultIndex;
|
||||
this.initializedFile = true;
|
||||
process.send({ msgtype: 'recordset', jslid: this.jslid, resultIndex });
|
||||
}
|
||||
|
||||
initializeFromReader(jslid) {
|
||||
this.jslid = jslid;
|
||||
this.currentFile = path.join(jsldir(), `${this.jslid}.jsonl`);
|
||||
this.writeCurrentStats(false, false);
|
||||
}
|
||||
|
||||
row(row) {
|
||||
// console.log('ACCEPT ROW', row);
|
||||
this.currentStream.write(JSON.stringify(row) + '\n');
|
||||
this.currentRowCount += 1;
|
||||
|
||||
if (!this.plannedStats) {
|
||||
this.plannedStats = true;
|
||||
process.nextTick(() => {
|
||||
@@ -49,6 +61,21 @@ class TableWriter {
|
||||
}
|
||||
}
|
||||
|
||||
rowFromReader(row) {
|
||||
if (!this.initializedFile) {
|
||||
process.send({ msgtype: 'initializeFile', jslid: this.jslid });
|
||||
this.initializedFile = true;
|
||||
|
||||
fs.writeFileSync(this.currentFile, JSON.stringify(row) + '\n');
|
||||
this.currentStream = fs.createWriteStream(this.currentFile, { flags: 'a' });
|
||||
this.writeCurrentStats(false, false);
|
||||
this.initializedFile = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this.row(row);
|
||||
}
|
||||
|
||||
writeCurrentStats(isFinished = false, emitEvent = false) {
|
||||
const stats = {
|
||||
rowCount: this.currentRowCount,
|
||||
@@ -63,10 +90,11 @@ class TableWriter {
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
close(afterClose) {
|
||||
if (this.currentStream) {
|
||||
this.currentStream.end(() => {
|
||||
this.writeCurrentStats(true, true);
|
||||
if (afterClose) afterClose();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -98,7 +126,11 @@ class StreamHandler {
|
||||
|
||||
recordset(columns) {
|
||||
this.closeCurrentWriter();
|
||||
this.currentWriter = new TableWriter(Array.isArray(columns) ? { columns } : columns, this.resultIndexHolder.value);
|
||||
this.currentWriter = new TableWriter();
|
||||
this.currentWriter.initializeFromQuery(
|
||||
Array.isArray(columns) ? { columns } : columns,
|
||||
this.resultIndexHolder.value
|
||||
);
|
||||
this.resultIndexHolder.value += 1;
|
||||
|
||||
// this.writeCurrentStats();
|
||||
@@ -110,7 +142,6 @@ class StreamHandler {
|
||||
// }, 500);
|
||||
}
|
||||
row(row) {
|
||||
// console.log('ACCEPT ROW', row);
|
||||
if (this.currentWriter) this.currentWriter.row(row);
|
||||
else if (row.message) process.send({ msgtype: 'info', info: { message: row.message } });
|
||||
// this.onRow(this.jslid);
|
||||
@@ -135,11 +166,22 @@ function handleStream(driver, resultIndexHolder, sql) {
|
||||
});
|
||||
}
|
||||
|
||||
function allowExecuteCustomScript(driver) {
|
||||
if (driver.readOnlySessions) {
|
||||
return true;
|
||||
}
|
||||
if (storedConnection.isReadOnly) {
|
||||
return false;
|
||||
// throw new Error('Connection is read only');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function handleConnect(connection) {
|
||||
storedConnection = connection;
|
||||
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
systemConnection = await connectUtility(driver, storedConnection);
|
||||
systemConnection = await connectUtility(driver, storedConnection, 'app');
|
||||
for (const [resolve] of afterConnectCallbacks) {
|
||||
resolve();
|
||||
}
|
||||
@@ -163,6 +205,19 @@ async function handleExecuteQuery({ sql }) {
|
||||
await waitConnected();
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
|
||||
if (!allowExecuteCustomScript(driver)) {
|
||||
process.send({
|
||||
msgtype: 'info',
|
||||
info: {
|
||||
message: 'Connection without read-only sessions is read only',
|
||||
severity: 'error',
|
||||
},
|
||||
});
|
||||
process.send({ msgtype: 'done', skipFinishedMessage: true });
|
||||
return;
|
||||
//process.send({ msgtype: 'error', error: e.message });
|
||||
}
|
||||
|
||||
const resultIndexHolder = {
|
||||
value: 0,
|
||||
};
|
||||
@@ -176,9 +231,39 @@ async function handleExecuteQuery({ sql }) {
|
||||
process.send({ msgtype: 'done' });
|
||||
}
|
||||
|
||||
async function handleExecuteReader({ jslid, sql, fileName }) {
|
||||
await waitConnected();
|
||||
|
||||
const driver = requireEngineDriver(storedConnection);
|
||||
|
||||
if (fileName) {
|
||||
sql = fs.readFileSync(fileName, 'utf-8');
|
||||
} else {
|
||||
if (!allowExecuteCustomScript(driver)) {
|
||||
process.send({ msgtype: 'done' });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const writer = new TableWriter();
|
||||
writer.initializeFromReader(jslid);
|
||||
|
||||
const reader = await driver.readQuery(systemConnection, sql);
|
||||
|
||||
reader.on('data', data => {
|
||||
writer.rowFromReader(data);
|
||||
});
|
||||
reader.on('end', () => {
|
||||
writer.close(() => {
|
||||
process.send({ msgtype: 'done' });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const messageHandlers = {
|
||||
connect: handleConnect,
|
||||
executeQuery: handleExecuteQuery,
|
||||
executeReader: handleExecuteReader,
|
||||
// cancel: handleCancel,
|
||||
};
|
||||
|
||||
|
||||
68
packages/api/src/proc/sshForwardProcess.js
Normal file
@@ -0,0 +1,68 @@
|
||||
const fs = require('fs-extra');
|
||||
const platformInfo = require('../utility/platformInfo');
|
||||
const childProcessChecker = require('../utility/childProcessChecker');
|
||||
const { SSHConnection } = require('node-ssh-forward');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
|
||||
async function getSshConnection(connection) {
|
||||
const sshConfig = {
|
||||
endHost: connection.sshHost || '',
|
||||
endPort: connection.sshPort || 22,
|
||||
bastionHost: connection.sshBastionHost || '',
|
||||
agentForward: connection.sshMode == 'agent',
|
||||
passphrase: connection.sshMode == 'keyFile' ? connection.sshKeyfilePassword : undefined,
|
||||
username: connection.sshLogin,
|
||||
password: connection.sshMode == 'userPassword' ? connection.sshPassword : undefined,
|
||||
agentSocket: connection.sshMode == 'agent' ? platformInfo.sshAuthSock : undefined,
|
||||
privateKey:
|
||||
connection.sshMode == 'keyFile' && connection.sshKeyfile ? await fs.readFile(connection.sshKeyfile) : undefined,
|
||||
skipAutoPrivateKey: true,
|
||||
noReadline: true,
|
||||
};
|
||||
|
||||
const sshConn = new SSHConnection(sshConfig);
|
||||
return sshConn;
|
||||
}
|
||||
|
||||
async function handleStart({ connection, tunnelConfig }) {
|
||||
try {
|
||||
const sshConn = await getSshConnection(connection);
|
||||
await sshConn.forward(tunnelConfig);
|
||||
|
||||
process.send({
|
||||
msgtype: 'connected',
|
||||
connection,
|
||||
tunnelConfig,
|
||||
});
|
||||
} catch (err) {
|
||||
process.send({
|
||||
msgtype: 'error',
|
||||
connection,
|
||||
tunnelConfig,
|
||||
errorMessage: err.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const messageHandlers = {
|
||||
connect: handleStart,
|
||||
};
|
||||
|
||||
async function handleMessage({ msgtype, ...other }) {
|
||||
const handler = messageHandlers[msgtype];
|
||||
await handler(other);
|
||||
}
|
||||
|
||||
function start() {
|
||||
childProcessChecker();
|
||||
process.on('message', async message => {
|
||||
if (handleProcessCommunication(message)) return;
|
||||
try {
|
||||
await handleMessage(message);
|
||||
} catch (e) {
|
||||
console.error('sshForwardProcess - unhandled error', e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { start };
|
||||
@@ -1,13 +1,47 @@
|
||||
const EnsureStreamHeaderStream = require('../utility/EnsureStreamHeaderStream');
|
||||
const Stream = require('stream');
|
||||
const ColumnMapTransformStream = require('../utility/ColumnMapTransformStream');
|
||||
|
||||
function copyStream(input, output, options) {
|
||||
const { columns } = options || {};
|
||||
|
||||
const transforms = [];
|
||||
if (columns) {
|
||||
transforms.push(new ColumnMapTransformStream(columns));
|
||||
}
|
||||
if (output.requireFixedStructure) {
|
||||
transforms.push(new EnsureStreamHeaderStream());
|
||||
}
|
||||
|
||||
// return new Promise((resolve, reject) => {
|
||||
// Stream.pipeline(input, ...transforms, output, err => {
|
||||
// if (err) {
|
||||
// reject(err);
|
||||
// } else {
|
||||
// resolve();
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
|
||||
function copyStream(input, output) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const ensureHeader = new EnsureStreamHeaderStream();
|
||||
const finisher = output['finisher'] || output;
|
||||
finisher.on('finish', resolve);
|
||||
finisher.on('error', reject);
|
||||
input.pipe(ensureHeader);
|
||||
ensureHeader.pipe(output);
|
||||
|
||||
let lastStream = input;
|
||||
for (const tran of transforms) {
|
||||
lastStream.pipe(tran);
|
||||
lastStream = tran;
|
||||
}
|
||||
lastStream.pipe(output);
|
||||
|
||||
// if (output.requireFixedStructure) {
|
||||
// const ensureHeader = new EnsureStreamHeaderStream();
|
||||
// input.pipe(ensureHeader);
|
||||
// ensureHeader.pipe(output);
|
||||
// } else {
|
||||
// input.pipe(output);
|
||||
// }
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
38
packages/api/src/shell/dumpDatabase.js
Normal file
@@ -0,0 +1,38 @@
|
||||
const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||
const connectUtility = require('../utility/connectUtility');
|
||||
|
||||
function doDump(dumper) {
|
||||
return new Promise((resolve, reject) => {
|
||||
dumper.once('end', () => {
|
||||
resolve(true);
|
||||
});
|
||||
dumper.once('error', err => {
|
||||
reject(err);
|
||||
});
|
||||
dumper.run();
|
||||
});
|
||||
}
|
||||
|
||||
async function dumpDatabase({
|
||||
connection = undefined,
|
||||
systemConnection = undefined,
|
||||
driver = undefined,
|
||||
outputFile,
|
||||
databaseName,
|
||||
schemaName,
|
||||
}) {
|
||||
console.log(`Dumping database`);
|
||||
|
||||
if (!driver) driver = requireEngineDriver(connection);
|
||||
const pool = systemConnection || (await connectUtility(driver, connection, 'read', { forceRowsAsObjects: true }));
|
||||
console.log(`Connected.`);
|
||||
|
||||
const dumper = await driver.createBackupDumper(pool, {
|
||||
outputFile,
|
||||
databaseName,
|
||||
schemaName,
|
||||
});
|
||||
await doDump(dumper);
|
||||
}
|
||||
|
||||
module.exports = dumpDatabase;
|
||||
@@ -5,7 +5,7 @@ async function executeQuery({ connection = undefined, systemConnection = undefin
|
||||
console.log(`Execute query ${sql}`);
|
||||
|
||||
if (!driver) driver = requireEngineDriver(connection);
|
||||
const pool = systemConnection || (await connectUtility(driver, connection));
|
||||
const pool = systemConnection || (await connectUtility(driver, connection, 'script'));
|
||||
console.log(`Connected.`);
|
||||
|
||||
await driver.script(pool, sql);
|
||||
|
||||
@@ -21,7 +21,7 @@ async function generateDeploySql({
|
||||
}) {
|
||||
if (!driver) driver = requireEngineDriver(connection);
|
||||
|
||||
const pool = systemConnection || (await connectUtility(driver, connection));
|
||||
const pool = systemConnection || (await connectUtility(driver, connection, 'read'));
|
||||
if (!analysedStructure) {
|
||||
analysedStructure = await driver.analyseFull(pool);
|
||||
}
|
||||
|
||||
57
packages/api/src/shell/importDatabase.js
Normal file
@@ -0,0 +1,57 @@
|
||||
const fs = require('fs');
|
||||
const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||
const connectUtility = require('../utility/connectUtility');
|
||||
const { splitQueryStream } = require('dbgate-query-splitter/lib/splitQueryStream');
|
||||
const download = require('./download');
|
||||
const stream = require('stream');
|
||||
|
||||
class ImportStream extends stream.Transform {
|
||||
constructor(pool, driver) {
|
||||
super({ objectMode: true });
|
||||
this.pool = pool;
|
||||
this.driver = driver;
|
||||
}
|
||||
async _transform(chunk, encoding, cb) {
|
||||
try {
|
||||
await this.driver.script(this.pool, chunk);
|
||||
} catch (err) {
|
||||
this.emit('error', err.message);
|
||||
}
|
||||
cb();
|
||||
}
|
||||
_flush(cb) {
|
||||
this.push('finish');
|
||||
cb();
|
||||
this.emit('end');
|
||||
}
|
||||
}
|
||||
|
||||
function awaitStreamEnd(stream) {
|
||||
return new Promise((resolve, reject) => {
|
||||
stream.once('end', () => {
|
||||
resolve(true);
|
||||
});
|
||||
stream.once('error', err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function importDatabase({ connection = undefined, systemConnection = undefined, driver = undefined, inputFile }) {
|
||||
console.log(`Importing database`);
|
||||
|
||||
if (!driver) driver = requireEngineDriver(connection);
|
||||
const pool = systemConnection || (await connectUtility(driver, connection, 'write'));
|
||||
console.log(`Connected.`);
|
||||
|
||||
const downloadedFile = await download(inputFile);
|
||||
|
||||
const fileStream = fs.createReadStream(downloadedFile, 'utf-8');
|
||||
const splittedStream = splitQueryStream(fileStream, driver.getQuerySplitterOptions());
|
||||
const importStream = new ImportStream(pool, driver);
|
||||
// @ts-ignore
|
||||
splittedStream.pipe(importStream);
|
||||
await awaitStreamEnd(importStream);
|
||||
}
|
||||
|
||||
module.exports = importDatabase;
|
||||
@@ -21,6 +21,8 @@ const executeQuery = require('./executeQuery');
|
||||
const loadFile = require('./loadFile');
|
||||
const deployDb = require('./deployDb');
|
||||
const initializeApiEnvironment = require('./initializeApiEnvironment');
|
||||
const dumpDatabase = require('./dumpDatabase');
|
||||
const importDatabase = require('./importDatabase');
|
||||
|
||||
const dbgateApi = {
|
||||
queryReader,
|
||||
@@ -45,6 +47,8 @@ const dbgateApi = {
|
||||
loadFile,
|
||||
deployDb,
|
||||
initializeApiEnvironment,
|
||||
dumpDatabase,
|
||||
importDatabase,
|
||||
};
|
||||
|
||||
requirePlugin.initializeDbgateApi(dbgateApi);
|
||||
|
||||
@@ -11,10 +11,7 @@ class StringifyStream extends stream.Transform {
|
||||
let skip = false;
|
||||
|
||||
if (!this.wasHeader) {
|
||||
skip =
|
||||
chunk.__isStreamHeader ||
|
||||
// TODO remove isArray test
|
||||
Array.isArray(chunk.columns);
|
||||
skip = chunk.__isStreamHeader;
|
||||
this.wasHeader = true;
|
||||
}
|
||||
if (!skip) {
|
||||
|
||||
@@ -12,14 +12,14 @@ class ParseStream extends stream.Transform {
|
||||
_transform(chunk, encoding, done) {
|
||||
const obj = JSON.parse(chunk);
|
||||
if (!this.wasHeader) {
|
||||
if (
|
||||
!obj.__isStreamHeader &&
|
||||
// TODO remove isArray test
|
||||
!Array.isArray(obj.columns)
|
||||
) {
|
||||
this.push({ columns: Object.keys(obj).map(columnName => ({ columnName })) });
|
||||
if (!obj.__isStreamHeader) {
|
||||
this.push({
|
||||
__isStreamHeader: true,
|
||||
__isDynamicStructure: true,
|
||||
// columns: Object.keys(obj).map(columnName => ({ columnName })),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
this.wasHeader = true;
|
||||
}
|
||||
if (!this.limitRows || this.rowsWritten < this.limitRows) {
|
||||
|
||||
@@ -10,11 +10,7 @@ class StringifyStream extends stream.Transform {
|
||||
_transform(chunk, encoding, done) {
|
||||
let skip = false;
|
||||
if (!this.wasHeader) {
|
||||
skip =
|
||||
(chunk.__isStreamHeader ||
|
||||
// TODO remove isArray test
|
||||
Array.isArray(chunk.columns)) &&
|
||||
!this.header;
|
||||
skip = (chunk.__isStreamHeader && !this.header) || (chunk.__isStreamHeader && chunk.__isDynamicStructure);
|
||||
this.wasHeader = true;
|
||||
}
|
||||
if (!skip) {
|
||||
|
||||
@@ -1,14 +1,26 @@
|
||||
const requireEngineDriver = require('../utility/requireEngineDriver');
|
||||
const { decryptConnection } = require('../utility/crypting');
|
||||
const connectUtility = require('../utility/connectUtility');
|
||||
|
||||
async function queryReader({ connection, sql }) {
|
||||
console.log(`Reading query ${sql}`);
|
||||
async function queryReader({
|
||||
connection,
|
||||
query,
|
||||
queryType,
|
||||
// obsolete; use query instead
|
||||
sql,
|
||||
}) {
|
||||
// if (sql && json) {
|
||||
// throw new Error('Only one of sql or json could be set');
|
||||
// }
|
||||
// if (!sql && !json) {
|
||||
// throw new Error('One of sql or json must be set');
|
||||
// }
|
||||
console.log(`Reading query ${query || sql}`);
|
||||
// else console.log(`Reading query ${JSON.stringify(json)}`);
|
||||
|
||||
const driver = requireEngineDriver(connection);
|
||||
const pool = await connectUtility(driver, connection);
|
||||
const pool = await connectUtility(driver, connection, queryType == 'json' ? 'read' : 'script');
|
||||
console.log(`Connected.`);
|
||||
return await driver.readQuery(pool, sql);
|
||||
return queryType == 'json' ? await driver.readJsonQuery(pool, query) : await driver.readQuery(pool, query || sql);
|
||||
}
|
||||
|
||||
module.exports = queryReader;
|
||||
|
||||
@@ -14,11 +14,7 @@ class SqlizeStream extends stream.Transform {
|
||||
_transform(chunk, encoding, done) {
|
||||
let skip = false;
|
||||
if (!this.wasHeader) {
|
||||
if (
|
||||
chunk.__isStreamHeader ||
|
||||
// TODO remove isArray test
|
||||
Array.isArray(chunk.columns)
|
||||
) {
|
||||
if (chunk.__isStreamHeader) {
|
||||
skip = true;
|
||||
this.tableName = chunk.pureName;
|
||||
if (chunk.engine) {
|
||||
|
||||
@@ -4,12 +4,12 @@ const connectUtility = require('../utility/connectUtility');
|
||||
|
||||
async function tableReader({ connection, pureName, schemaName }) {
|
||||
const driver = requireEngineDriver(connection);
|
||||
const pool = await connectUtility(driver, connection);
|
||||
const pool = await connectUtility(driver, connection, 'read');
|
||||
console.log(`Connected.`);
|
||||
|
||||
const fullName = { pureName, schemaName };
|
||||
|
||||
if (driver.dialect.nosql) {
|
||||
if (driver.databaseEngineTypes.includes('document')) {
|
||||
// @ts-ignore
|
||||
console.log(`Reading collection ${fullNameToString(fullName)}`);
|
||||
// @ts-ignore
|
||||
|
||||
@@ -8,7 +8,7 @@ async function tableWriter({ connection, schemaName, pureName, driver, systemCon
|
||||
if (!driver) {
|
||||
driver = requireEngineDriver(connection);
|
||||
}
|
||||
const pool = systemConnection || (await connectUtility(driver, connection));
|
||||
const pool = systemConnection || (await connectUtility(driver, connection, 'write'));
|
||||
|
||||
console.log(`Connected.`);
|
||||
return await driver.writeTable(pool, { schemaName, pureName }, options);
|
||||
|
||||
21
packages/api/src/utility/ColumnMapTransformStream.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const stream = require('stream');
|
||||
const { transformRowUsingColumnMap } = require('dbgate-tools');
|
||||
|
||||
class ColumnMapTransformStream extends stream.Transform {
|
||||
constructor(columns) {
|
||||
super({ objectMode: true });
|
||||
this.columns = columns;
|
||||
}
|
||||
_transform(chunk, encoding, done) {
|
||||
if (chunk.__isStreamHeader) {
|
||||
// skip stream header
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
this.push(transformRowUsingColumnMap(chunk, this.columns));
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ColumnMapTransformStream;
|
||||
@@ -1,6 +1,7 @@
|
||||
const { fork } = require('child_process');
|
||||
const uuidv1 = require('uuid/v1');
|
||||
const { handleProcessCommunication } = require('./processComm');
|
||||
const processArgs = require('../utility/processArgs');
|
||||
|
||||
class DatastoreProxy {
|
||||
constructor(file) {
|
||||
@@ -33,7 +34,8 @@ class DatastoreProxy {
|
||||
'--is-forked-api',
|
||||
'--start-process',
|
||||
'jslDatastoreProcess',
|
||||
...process.argv.slice(3),
|
||||
...processArgs.getPassArgs(),
|
||||
// ...process.argv.slice(3),
|
||||
]);
|
||||
|
||||
this.subprocess.on('message', message => {
|
||||
|
||||
@@ -13,11 +13,7 @@ class EnsureStreamHeaderStream extends stream.Transform {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!chunk.__isStreamHeader &&
|
||||
// TODO remove isArray test
|
||||
!Array.isArray(chunk.columns)
|
||||
) {
|
||||
if (!chunk.__isStreamHeader) {
|
||||
this.push({
|
||||
__isStreamHeader: true,
|
||||
__isComputedStructure: true,
|
||||
|
||||
142
packages/api/src/utility/JsonLinesDatabase.js
Normal file
@@ -0,0 +1,142 @@
|
||||
const AsyncLock = require('async-lock');
|
||||
const fs = require('fs-extra');
|
||||
const uuidv1 = require('uuid/v1');
|
||||
|
||||
const lock = new AsyncLock();
|
||||
|
||||
// const lineReader = require('line-reader');
|
||||
// const { fetchNextLineFromReader } = require('./JsonLinesDatastore');
|
||||
|
||||
class JsonLinesDatabase {
|
||||
constructor(filename) {
|
||||
this.filename = filename;
|
||||
this.data = [];
|
||||
this.loadedOk = false;
|
||||
this.loadPerformed = false;
|
||||
}
|
||||
|
||||
async _save() {
|
||||
if (!this.loadedOk) {
|
||||
// don't override data
|
||||
return;
|
||||
}
|
||||
await fs.writeFile(this.filename, this.data.map(x => JSON.stringify(x)).join('\n'));
|
||||
}
|
||||
|
||||
async _ensureLoaded() {
|
||||
if (!this.loadPerformed) {
|
||||
await lock.acquire('reader', async () => {
|
||||
if (!this.loadPerformed) {
|
||||
if (!(await fs.exists(this.filename))) {
|
||||
this.loadedOk = true;
|
||||
this.loadPerformed = true;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const text = await fs.readFile(this.filename, { encoding: 'utf-8' });
|
||||
this.data = text
|
||||
.split('\n')
|
||||
.filter(x => x.trim())
|
||||
.map(x => JSON.parse(x));
|
||||
this.loadedOk = true;
|
||||
} catch (err) {
|
||||
console.error(`Error loading file ${this.filename}`, err);
|
||||
}
|
||||
this.loadPerformed = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async insert(obj) {
|
||||
await this._ensureLoaded();
|
||||
if (obj._id && (await this.get(obj._id))) {
|
||||
throw new Error(`Cannot insert duplicate ID ${obj._id} into ${this.filename}`);
|
||||
}
|
||||
const elem = obj._id
|
||||
? obj
|
||||
: {
|
||||
...obj,
|
||||
_id: uuidv1(),
|
||||
};
|
||||
this.data.push(elem);
|
||||
await this._save();
|
||||
return elem;
|
||||
}
|
||||
|
||||
async get(id) {
|
||||
await this._ensureLoaded();
|
||||
return this.data.find(x => x._id == id);
|
||||
}
|
||||
|
||||
async find(cond) {
|
||||
await this._ensureLoaded();
|
||||
if (cond) {
|
||||
return this.data.filter(x => {
|
||||
for (const key of Object.keys(cond)) {
|
||||
if (x[key] != cond[key]) return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
|
||||
async update(obj) {
|
||||
await this._ensureLoaded();
|
||||
this.data = this.data.map(x => (x._id == obj._id ? obj : x));
|
||||
await this._save();
|
||||
return obj;
|
||||
}
|
||||
|
||||
async patch(id, values) {
|
||||
await this._ensureLoaded();
|
||||
this.data = this.data.map(x => (x._id == id ? { ...x, ...values } : x));
|
||||
await this._save();
|
||||
return this.data.find(x => x._id == id);
|
||||
}
|
||||
|
||||
async remove(id) {
|
||||
await this._ensureLoaded();
|
||||
const removed = this.data.find(x => x._id == id);
|
||||
this.data = this.data.filter(x => x._id != id);
|
||||
await this._save();
|
||||
return removed;
|
||||
}
|
||||
|
||||
// async _openReader() {
|
||||
// return new Promise((resolve, reject) =>
|
||||
// lineReader.open(this.filename, (err, reader) => {
|
||||
// if (err) reject(err);
|
||||
// resolve(reader);
|
||||
// })
|
||||
// );
|
||||
// }
|
||||
|
||||
// async _read() {
|
||||
// this.data = [];
|
||||
// if (!(await fs.exists(this.filename))) return;
|
||||
// try {
|
||||
// const reader = await this._openReader();
|
||||
// for (;;) {
|
||||
// const line = await fetchNextLineFromReader(reader);
|
||||
// if (!line) break;
|
||||
// this.data.push(JSON.parse(line));
|
||||
// }
|
||||
// } catch (err) {
|
||||
// console.error(`Error loading file ${this.filename}`, err);
|
||||
// }
|
||||
// }
|
||||
|
||||
// async _write() {
|
||||
// const fw = fs.createWriteStream(this.filename);
|
||||
// for (const obj of this.data) {
|
||||
// await fw.write(JSON.stringify(obj));
|
||||
// await fw.write('\n');
|
||||
// }
|
||||
// await fw.end();
|
||||
// }
|
||||
}
|
||||
|
||||
module.exports = JsonLinesDatabase;
|
||||
@@ -4,7 +4,7 @@ const lock = new AsyncLock();
|
||||
const stableStringify = require('json-stable-stringify');
|
||||
const { evaluateCondition } = require('dbgate-sqltree');
|
||||
|
||||
async function fetchNextLine(reader) {
|
||||
function fetchNextLineFromReader(reader) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!reader.hasNextLine()) {
|
||||
resolve(null);
|
||||
@@ -27,6 +27,7 @@ class JsonLinesDatastore {
|
||||
this.reader = null;
|
||||
this.readedDataRowCount = 0;
|
||||
this.readedSchemaRow = false;
|
||||
// this.firstRowToBeReturned = null;
|
||||
this.notifyChangedCallback = null;
|
||||
this.currentFilter = null;
|
||||
}
|
||||
@@ -37,6 +38,7 @@ class JsonLinesDatastore {
|
||||
this.reader = null;
|
||||
this.readedDataRowCount = 0;
|
||||
this.readedSchemaRow = false;
|
||||
// this.firstRowToBeReturned = null;
|
||||
this.currentFilter = null;
|
||||
reader.close(() => {});
|
||||
}
|
||||
@@ -61,8 +63,13 @@ class JsonLinesDatastore {
|
||||
}
|
||||
|
||||
async _readLine(parse) {
|
||||
// if (this.firstRowToBeReturned) {
|
||||
// const res = this.firstRowToBeReturned;
|
||||
// this.firstRowToBeReturned = null;
|
||||
// return res;
|
||||
// }
|
||||
for (;;) {
|
||||
const line = await fetchNextLine(this.reader);
|
||||
const line = await fetchNextLineFromReader(this.reader);
|
||||
if (!line) {
|
||||
// EOF
|
||||
return null;
|
||||
@@ -70,7 +77,11 @@ class JsonLinesDatastore {
|
||||
|
||||
if (!this.readedSchemaRow) {
|
||||
this.readedSchemaRow = true;
|
||||
return true;
|
||||
const parsedLine = JSON.parse(line);
|
||||
if (parsedLine.__isStreamHeader) {
|
||||
// skip to next line
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (this.currentFilter) {
|
||||
const parsedLine = JSON.parse(line);
|
||||
@@ -130,25 +141,47 @@ class JsonLinesDatastore {
|
||||
this.reader = reader;
|
||||
this.currentFilter = filter;
|
||||
}
|
||||
if (!this.readedSchemaRow) {
|
||||
await this._readLine(false); // skip structure
|
||||
}
|
||||
// if (!this.readedSchemaRow) {
|
||||
// const line = await this._readLine(true); // skip structure
|
||||
// if (!line.__isStreamHeader) {
|
||||
// // line contains data
|
||||
// this.firstRowToBeReturned = line;
|
||||
// }
|
||||
// }
|
||||
while (this.readedDataRowCount < offset) {
|
||||
await this._readLine(false);
|
||||
const line = await this._readLine(false);
|
||||
if (line == null) break;
|
||||
// if (this.firstRowToBeReturned) {
|
||||
// this.firstRowToBeReturned = null;
|
||||
// } else {
|
||||
// await this._readLine(false);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
async enumRows(eachRow) {
|
||||
await lock.acquire('reader', async () => {
|
||||
await this._ensureReader(0, null);
|
||||
for (;;) {
|
||||
const line = await this._readLine(true);
|
||||
if (line == null) break;
|
||||
const shouldContinue = eachRow(line);
|
||||
if (!shouldContinue) break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async getRows(offset, limit, filter) {
|
||||
const res = [];
|
||||
await lock.acquire('reader', async () => {
|
||||
await this._ensureReader(offset, filter);
|
||||
// console.log(JSON.stringify(this.currentFilter, undefined, 2));
|
||||
for (let i = 0; i < limit; i += 1) {
|
||||
const line = await this._readLine(true);
|
||||
if (line == null) break;
|
||||
res.push(line);
|
||||
}
|
||||
});
|
||||
// console.log('RETURN', res.length);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,47 @@ const fs = require('fs-extra');
|
||||
const { decryptConnection } = require('./crypting');
|
||||
const { getSshTunnel } = require('./sshTunnel');
|
||||
const { getSshTunnelProxy } = require('./sshTunnelProxy');
|
||||
const platformInfo = require('../utility/platformInfo');
|
||||
const connections = require('../controllers/connections');
|
||||
|
||||
async function loadConnection(driver, storedConnection, connectionMode) {
|
||||
const { allowShellConnection } = platformInfo;
|
||||
|
||||
if (connectionMode == 'app') {
|
||||
return storedConnection;
|
||||
}
|
||||
|
||||
if (storedConnection._id || !allowShellConnection) {
|
||||
if (!storedConnection._id) {
|
||||
throw new Error('Missing connection _id');
|
||||
}
|
||||
|
||||
await connections._init();
|
||||
const loaded = await connections.getCore({ conid: storedConnection._id });
|
||||
const loadedWithDb = {
|
||||
...loaded,
|
||||
database: storedConnection.database,
|
||||
};
|
||||
|
||||
if (loaded.isReadOnly) {
|
||||
if (connectionMode == 'read') return loadedWithDb;
|
||||
if (connectionMode == 'write') throw new Error('Cannot write readonly connection');
|
||||
if (connectionMode == 'script') {
|
||||
if (driver.readOnlySessions) return loadedWithDb;
|
||||
throw new Error('Cannot write readonly connection');
|
||||
}
|
||||
}
|
||||
return loadedWithDb;
|
||||
}
|
||||
return storedConnection;
|
||||
}
|
||||
|
||||
async function connectUtility(driver, storedConnection, connectionMode, additionalOptions = null) {
|
||||
const connectionLoaded = await loadConnection(driver, storedConnection, connectionMode);
|
||||
|
||||
async function connectUtility(driver, storedConnection) {
|
||||
const connection = {
|
||||
database: storedConnection.defaultDatabase,
|
||||
...decryptConnection(storedConnection),
|
||||
database: connectionLoaded.defaultDatabase,
|
||||
...decryptConnection(connectionLoaded),
|
||||
};
|
||||
|
||||
if (!connection.port && driver.defaultPort) connection.port = driver.defaultPort.toString();
|
||||
@@ -57,7 +93,7 @@ async function connectUtility(driver, storedConnection) {
|
||||
}
|
||||
}
|
||||
|
||||
const conn = await driver.connect(connection);
|
||||
const conn = await driver.connect({ ...connection, ...additionalOptions });
|
||||
return conn;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ const crypto = require('crypto');
|
||||
const simpleEncryptor = require('simple-encryptor');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const _ = require('lodash');
|
||||
|
||||
const { datadir } = require('./directories');
|
||||
|
||||
@@ -54,7 +55,7 @@ function encryptPasswordField(connection, field) {
|
||||
[field]: 'crypt:' + getEncryptor().encrypt(connection[field]),
|
||||
};
|
||||
}
|
||||
return connection;
|
||||
return connection;
|
||||
}
|
||||
|
||||
function decryptPasswordField(connection, field) {
|
||||
@@ -74,6 +75,11 @@ function encryptConnection(connection) {
|
||||
return connection;
|
||||
}
|
||||
|
||||
function maskConnection(connection) {
|
||||
if (!connection) return connection;
|
||||
return _.omit(connection, ['password', 'sshPassword', 'sshKeyfilePassword']);
|
||||
}
|
||||
|
||||
function decryptConnection(connection) {
|
||||
connection = decryptPasswordField(connection, 'password');
|
||||
connection = decryptPasswordField(connection, 'sshPassword');
|
||||
@@ -81,8 +87,19 @@ function decryptConnection(connection) {
|
||||
return connection;
|
||||
}
|
||||
|
||||
function pickSafeConnectionInfo(connection) {
|
||||
return _.mapValues(connection, (v, k) => {
|
||||
if (k == 'engine' || k == 'port' || k == 'authType' || k == 'sshMode' || k == 'passwordMode') return v;
|
||||
if (v === null || v === true || v === false) return v;
|
||||
if (v) return '***';
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
loadEncryptionKey,
|
||||
encryptConnection,
|
||||
decryptConnection,
|
||||
maskConnection,
|
||||
pickSafeConnectionInfo,
|
||||
};
|
||||
|
||||
@@ -3,11 +3,12 @@ const path = require('path');
|
||||
const fs = require('fs');
|
||||
const cleanDirectory = require('./cleanDirectory');
|
||||
const platformInfo = require('./platformInfo');
|
||||
const processArgs = require('./processArgs');
|
||||
|
||||
const createDirectories = {};
|
||||
const ensureDirectory = (dir, clean) => {
|
||||
if (!createDirectories[dir]) {
|
||||
if (clean && fs.existsSync(dir)) {
|
||||
if (clean && fs.existsSync(dir) && !platformInfo.isForkedApi) {
|
||||
console.log(`Cleaning directory ${dir}`);
|
||||
cleanDirectory(dir);
|
||||
}
|
||||
@@ -19,8 +20,18 @@ const ensureDirectory = (dir, clean) => {
|
||||
}
|
||||
};
|
||||
|
||||
function datadirCore() {
|
||||
if (process.env.WORKSPACE_DIR) {
|
||||
return process.env.WORKSPACE_DIR;
|
||||
}
|
||||
if (processArgs.workspaceDir) {
|
||||
return processArgs.workspaceDir;
|
||||
}
|
||||
return path.join(os.homedir(), 'dbgate-data');
|
||||
}
|
||||
|
||||
function datadir() {
|
||||
const dir = path.join(os.homedir(), 'dbgate-data');
|
||||
const dir = datadirCore();
|
||||
ensureDirectory(dir);
|
||||
|
||||
return dir;
|
||||
@@ -38,6 +49,7 @@ const rundir = dirFunc('run', true);
|
||||
const uploadsdir = dirFunc('uploads', true);
|
||||
const pluginsdir = dirFunc('plugins');
|
||||
const archivedir = dirFunc('archive');
|
||||
const appdir = dirFunc('apps');
|
||||
const filesdir = dirFunc('files');
|
||||
|
||||
function packagedPluginsDir() {
|
||||
@@ -53,7 +65,10 @@ function packagedPluginsDir() {
|
||||
}
|
||||
if (platformInfo.isNpmDist) {
|
||||
// node_modules
|
||||
return global['dbgateApiPackagedPluginsPath'];
|
||||
return global['PLUGINS_DIR'];
|
||||
}
|
||||
if (processArgs.pluginsDir) {
|
||||
return processArgs.pluginsDir;
|
||||
}
|
||||
if (platformInfo.isElectronBundle) {
|
||||
return path.resolve(__dirname, '../../plugins');
|
||||
@@ -103,6 +118,7 @@ module.exports = {
|
||||
rundir,
|
||||
uploadsdir,
|
||||
archivedir,
|
||||
appdir,
|
||||
ensureDirectory,
|
||||
pluginsdir,
|
||||
filesdir,
|
||||
|
||||
@@ -3,7 +3,7 @@ const fs = require('fs-extra');
|
||||
async function saveFreeTableData(file, data) {
|
||||
const { structure, rows } = data;
|
||||
const fileStream = fs.createWriteStream(file);
|
||||
await fileStream.write(JSON.stringify(structure) + '\n');
|
||||
await fileStream.write(JSON.stringify({ __isStreamHeader: true, ...structure }) + '\n');
|
||||
for (const row of rows) {
|
||||
await fileStream.write(JSON.stringify(row) + '\n');
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ const getChartExport = (title, config, imageFile) => {
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.6.0/chart.min.js" integrity="sha512-GMGzUEevhWh8Tc/njS0bDpwgxdCJLQBWG3Z2Ct+JGOpVnEmjvNx6ts4v6A2XJf1HOrtOsfhv3hBKpK9kE5z8AQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js" integrity="sha512-qTXRIMyZIFb8iQcfjXWCO8+M5Tbc38Qi5WzdPOYZHIlZpzBHG3L3by84BBBOiRGiEb7KKtAOAs5qYdUiZiQNNQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-adapter-moment/1.0.0/chartjs-adapter-moment.min.js" integrity="sha512-oh5t+CdSBsaVVAvxcZKy3XJdP7ZbYUBSRCXDTVn0ODewMDDNnELsrG9eDm8rVZAQg7RsDD/8K3MjPAFB13o6eA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.min.js" integrity="sha512-UXumZrZNiOwnTcZSHLOfcTs0aos2MzBWHXOHOuB0J/R44QB0dwY5JgfbvljXcklVf65Gc4El6RjZ+lnwd2az2g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-zoom/1.2.0/chartjs-plugin-zoom.min.js" integrity="sha512-TT0wAMqqtjXVzpc48sI0G84rBP+oTkBZPgeRYIOVRGUdwJsyS3WPipsNh///ay2LJ+onCM23tipnz6EvEy2/UA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
|
||||
<style>
|
||||
a { text-decoration: none }
|
||||
|
||||
|
||||
25
packages/api/src/utility/getDiagramExport.js
Normal file
@@ -0,0 +1,25 @@
|
||||
const getDiagramExport = (html, css, themeType, themeClassName) => {
|
||||
return `<html>
|
||||
<meta charset='utf-8'>
|
||||
|
||||
<head>
|
||||
<style>
|
||||
${css}
|
||||
|
||||
body {
|
||||
background: var(--theme-bg-1);
|
||||
color: var(--theme-font-1);
|
||||
}
|
||||
</style>
|
||||
|
||||
<link rel="stylesheet" href='https://cdn.jsdelivr.net/npm/@mdi/font@6.5.95/css/materialdesignicons.css' />
|
||||
</head>
|
||||
|
||||
<body class='${themeType == 'dark' ? 'theme-type-dark' : 'theme-type-light'} ${themeClassName}'>
|
||||
${html}
|
||||
</body>
|
||||
|
||||
</html>`;
|
||||
};
|
||||
|
||||
module.exports = getDiagramExport;
|
||||
10
packages/api/src/utility/getExpressPath.js
Normal file
@@ -0,0 +1,10 @@
|
||||
function getExpressPath(path) {
|
||||
path = path.replace(/\/*$/, '').replace(/^\/*/, '');
|
||||
const root = (process.env.WEB_ROOT || '').replace(/^\/*/, '').replace(/\/*$/, '');
|
||||
if (root) {
|
||||
return `/${root}/${path}`;
|
||||
}
|
||||
return `/${path}`;
|
||||
}
|
||||
|
||||
module.exports = getExpressPath;
|
||||
@@ -6,6 +6,10 @@ function getJslFileName(jslid) {
|
||||
if (archiveMatch) {
|
||||
return path.join(resolveArchiveFolder(archiveMatch[1]), `${archiveMatch[2]}.jsonl`);
|
||||
}
|
||||
const fileMatch = jslid.match(/^file:\/\/(.*)$/);
|
||||
if (fileMatch) {
|
||||
return fileMatch[1];
|
||||
}
|
||||
return path.join(jsldir(), `${jslid}.jsonl`);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,56 @@
|
||||
const { compilePermissions, testPermission } = require('dbgate-tools');
|
||||
const _ = require('lodash');
|
||||
|
||||
let compiled = undefined;
|
||||
const userPermissions = {};
|
||||
|
||||
function hasPermission(tested) {
|
||||
if (compiled === undefined) {
|
||||
compiled = compilePermissions(process.env.PERMISSIONS);
|
||||
function hasPermission(tested, req) {
|
||||
const { user } = (req && req.auth) || {};
|
||||
const key = user || '';
|
||||
const logins = getLogins();
|
||||
if (!userPermissions[key] && logins) {
|
||||
const login = logins.find(x => x.login == user);
|
||||
userPermissions[key] = compilePermissions(login ? login.permissions : null);
|
||||
}
|
||||
return testPermission(tested, compiled);
|
||||
return testPermission(tested, userPermissions[key]);
|
||||
}
|
||||
|
||||
module.exports = hasPermission;
|
||||
let loginsCache = null;
|
||||
let loginsLoaded = false;
|
||||
|
||||
function getLogins() {
|
||||
if (loginsLoaded) {
|
||||
return loginsCache;
|
||||
}
|
||||
|
||||
const res = [];
|
||||
if (process.env.LOGIN && process.env.PASSWORD) {
|
||||
res.push({
|
||||
login: process.env.LOGIN,
|
||||
password: process.env.PASSWORD,
|
||||
permissions: process.env.PERMISSIONS,
|
||||
});
|
||||
}
|
||||
if (process.env.LOGINS) {
|
||||
const logins = _.compact(process.env.LOGINS.split(',').map(x => x.trim()));
|
||||
for (const login of logins) {
|
||||
const password = process.env[`LOGIN_PASSWORD_${login}`];
|
||||
const permissions = process.env[`LOGIN_PERMISSIONS_${login}`];
|
||||
if (password) {
|
||||
res.push({
|
||||
login,
|
||||
password,
|
||||
permissions,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loginsCache = res.length > 0 ? res : null;
|
||||
loginsLoaded = true;
|
||||
return loginsCache;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
hasPermission,
|
||||
getLogins,
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ const isMac = platform === 'darwin';
|
||||
const isLinux = platform === 'linux';
|
||||
const isDocker = fs.existsSync('/home/dbgate-docker/public');
|
||||
const isDevMode = process.env.DEVMODE == '1';
|
||||
const isNpmDist = !!global['dbgateApiModulePath'];
|
||||
const isNpmDist = !!global['IS_NPM_DIST'];
|
||||
const isForkedApi = processArgs.isForkedApi;
|
||||
|
||||
// function moduleAvailable(name) {
|
||||
@@ -39,6 +39,8 @@ const platformInfo = {
|
||||
environment: process.env.NODE_ENV,
|
||||
platform,
|
||||
runningInWebpack: !!process.env.WEBPACK_DEV_SERVER_URL,
|
||||
allowShellConnection: !!process.env.SHELL_CONNECTION || !!isElectron(),
|
||||
allowShellScripting: !!process.env.SHELL_SCRIPTING || !!isElectron(),
|
||||
defaultKeyfile: path.join(os.homedir(), '.ssh/id_rsa'),
|
||||
};
|
||||
|
||||
|
||||
@@ -7,13 +7,27 @@ function getNamedArg(name) {
|
||||
}
|
||||
|
||||
const checkParent = process.argv.includes('--checkParent');
|
||||
const nativeModules = getNamedArg('--native-modules');
|
||||
const startProcess = getNamedArg('--start-process');
|
||||
const isForkedApi = process.argv.includes('--is-forked-api');
|
||||
const pluginsDir = getNamedArg('--plugins-dir');
|
||||
const workspaceDir = getNamedArg('--workspace-dir');
|
||||
|
||||
function getPassArgs() {
|
||||
const res = [];
|
||||
if (global['NATIVE_MODULES']) {
|
||||
res.push('--native-modules', global['NATIVE_MODULES']);
|
||||
}
|
||||
if (global['PLUGINS_DIR']) {
|
||||
res.push('--plugins-dir', global['PLUGINS_DIR']);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
checkParent,
|
||||
nativeModules,
|
||||
startProcess,
|
||||
isForkedApi,
|
||||
getPassArgs,
|
||||
pluginsDir,
|
||||
workspaceDir,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const _ = require('lodash');
|
||||
const requirePlugin = require('../shell/requirePlugin');
|
||||
const { pickSafeConnectionInfo } = require('./crypting');
|
||||
|
||||
/** @returns {import('dbgate-types').EngineDriver} */
|
||||
function requireEngineDriver(connection) {
|
||||
@@ -10,14 +11,14 @@ function requireEngineDriver(connection) {
|
||||
engine = connection.engine;
|
||||
}
|
||||
if (!engine) {
|
||||
throw new Error('Could not get driver from connection');
|
||||
throw new Error(`Could not get driver from connection ${JSON.stringify(pickSafeConnectionInfo(connection))}`);
|
||||
}
|
||||
if (engine.includes('@')) {
|
||||
const [shortName, packageName] = engine.split('@');
|
||||
const plugin = requirePlugin(packageName);
|
||||
return plugin.drivers.find(x => x.engine == engine);
|
||||
}
|
||||
throw new Error(`Could not found engine driver ${engine}`);
|
||||
throw new Error(`Could not find engine driver ${engine}`);
|
||||
}
|
||||
|
||||
module.exports = requireEngineDriver;
|
||||
|
||||
@@ -1,29 +1,41 @@
|
||||
let sseResponse = null;
|
||||
let electronSender = null;
|
||||
let init = '';
|
||||
let init = [];
|
||||
|
||||
module.exports = {
|
||||
setSseResponse(value) {
|
||||
sseResponse = value;
|
||||
setInterval(() => this.emit('ping'), 29 * 1000);
|
||||
},
|
||||
setElectronSender(value) {
|
||||
electronSender = value;
|
||||
},
|
||||
emit(message, data) {
|
||||
if (electronSender) {
|
||||
if (init.length > 0) {
|
||||
for (const item of init) {
|
||||
electronSender.send(item.message, item.data == null ? null : item.data);
|
||||
}
|
||||
init = [];
|
||||
}
|
||||
electronSender.send(message, data == null ? null : data);
|
||||
} else if (sseResponse) {
|
||||
if (init) {
|
||||
sseResponse.write(init);
|
||||
init = '';
|
||||
if (init.length > 0) {
|
||||
for (const item of init) {
|
||||
sseResponse.write(
|
||||
`event: ${item.message}\ndata: ${JSON.stringify(item.data == null ? null : item.data)}\n\n`
|
||||
);
|
||||
}
|
||||
init = [];
|
||||
}
|
||||
sseResponse.write(`event: ${message}\ndata: ${JSON.stringify(data == null ? null : data)}\n\n`);
|
||||
} else {
|
||||
init += sseResponse;
|
||||
init.push([{ message, data }]);
|
||||
}
|
||||
},
|
||||
emitChanged(key) {
|
||||
this.emit('clean-cache', key);
|
||||
this.emit(key);
|
||||
// console.log('EMIT CHANGED', key);
|
||||
this.emit('changed-cache', key);
|
||||
// this.emit(key);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
const { SSHConnection } = require('node-ssh-forward');
|
||||
const fs = require('fs-extra');
|
||||
const portfinder = require('portfinder');
|
||||
const stableStringify = require('json-stable-stringify');
|
||||
const _ = require('lodash');
|
||||
const platformInfo = require('./platformInfo');
|
||||
const AsyncLock = require('async-lock');
|
||||
const lock = new AsyncLock();
|
||||
const { fork } = require('child_process');
|
||||
const processArgs = require('../utility/processArgs');
|
||||
|
||||
const sshConnectionCache = {};
|
||||
const sshTunnelCache = {};
|
||||
|
||||
const CONNECTION_FIELDS = [
|
||||
@@ -22,37 +20,42 @@ const CONNECTION_FIELDS = [
|
||||
];
|
||||
const TUNNEL_FIELDS = [...CONNECTION_FIELDS, 'server', 'port'];
|
||||
|
||||
async function getSshConnection(connection) {
|
||||
const connectionCacheKey = stableStringify(_.pick(connection, CONNECTION_FIELDS));
|
||||
if (sshConnectionCache[connectionCacheKey]) return sshConnectionCache[connectionCacheKey];
|
||||
function callForwardProcess(connection, tunnelConfig, tunnelCacheKey) {
|
||||
let subprocess = fork(global['API_PACKAGE'] || process.argv[1], [
|
||||
'--is-forked-api',
|
||||
'--start-process',
|
||||
'sshForwardProcess',
|
||||
...processArgs.getPassArgs(),
|
||||
]);
|
||||
|
||||
const sshConfig = {
|
||||
endHost: connection.sshHost || '',
|
||||
endPort: connection.sshPort || 22,
|
||||
bastionHost: connection.sshBastionHost || '',
|
||||
agentForward: connection.sshMode == 'agent',
|
||||
passphrase: connection.sshMode == 'keyFile' ? connection.sshKeyfilePassword : undefined,
|
||||
username: connection.sshLogin,
|
||||
password: connection.sshMode == 'userPassword' ? connection.sshPassword : undefined,
|
||||
agentSocket: connection.sshMode == 'agent' ? platformInfo.sshAuthSock : undefined,
|
||||
privateKey:
|
||||
connection.sshMode == 'keyFile' && connection.sshKeyfile ? await fs.readFile(connection.sshKeyfile) : undefined,
|
||||
skipAutoPrivateKey: true,
|
||||
noReadline: true,
|
||||
};
|
||||
|
||||
const sshConn = new SSHConnection(sshConfig);
|
||||
sshConnectionCache[connectionCacheKey] = sshConn;
|
||||
return sshConn;
|
||||
subprocess.send({
|
||||
msgtype: 'connect',
|
||||
connection,
|
||||
tunnelConfig,
|
||||
});
|
||||
return new Promise((resolve, reject) => {
|
||||
subprocess.on('message', resp => {
|
||||
// @ts-ignore
|
||||
const { msgtype, errorMessage } = resp;
|
||||
if (msgtype == 'connected') {
|
||||
resolve(subprocess);
|
||||
}
|
||||
if (msgtype == 'error') {
|
||||
reject(errorMessage);
|
||||
}
|
||||
});
|
||||
subprocess.on('exit', code => {
|
||||
console.log('SSH forward process exited');
|
||||
delete sshTunnelCache[tunnelCacheKey];
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function getSshTunnel(connection) {
|
||||
const tunnelCacheKey = stableStringify(_.pick(connection, TUNNEL_FIELDS));
|
||||
|
||||
return await lock.acquire(tunnelCacheKey, async () => {
|
||||
const sshConn = await getSshConnection(connection);
|
||||
if (sshTunnelCache[tunnelCacheKey]) return sshTunnelCache[tunnelCacheKey];
|
||||
|
||||
const localPort = await portfinder.getPortPromise({ port: 10000, stopPort: 60000 });
|
||||
// workaround for `getPortPromise` not releasing the port quickly enough
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
@@ -66,7 +69,8 @@ async function getSshTunnel(connection) {
|
||||
`Creating SSH tunnel to ${connection.sshHost}-${connection.server}:${connection.port}, using local port ${localPort}`
|
||||
);
|
||||
|
||||
const tunnel = await sshConn.forward(tunnelConfig);
|
||||
const subprocess = await callForwardProcess(connection, tunnelConfig, tunnelCacheKey);
|
||||
|
||||
console.log(
|
||||
`Created SSH tunnel to ${connection.sshHost}-${connection.server}:${connection.port}, using local port ${localPort}`
|
||||
);
|
||||
@@ -74,6 +78,7 @@ async function getSshTunnel(connection) {
|
||||
sshTunnelCache[tunnelCacheKey] = {
|
||||
state: 'ok',
|
||||
localPort,
|
||||
subprocess,
|
||||
};
|
||||
return sshTunnelCache[tunnelCacheKey];
|
||||
} catch (err) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const _ = require('lodash');
|
||||
const express = require('express');
|
||||
const getExpressPath = require('./getExpressPath');
|
||||
|
||||
/**
|
||||
* @param {string} route
|
||||
@@ -30,8 +31,14 @@ module.exports = function useController(app, electron, route, controller) {
|
||||
const handler = `${route.substring(1)}-${_.kebabCase(key)}`;
|
||||
// console.log('REGISTERING HANDLER', handler);
|
||||
electron.ipcMain.handle(handler, async (event, args) => {
|
||||
const data = await controller[key](args);
|
||||
return data;
|
||||
try {
|
||||
const data = await controller[key](args);
|
||||
// console.log('HANDLED API', handler, data);
|
||||
if (data === undefined) return null;
|
||||
return data;
|
||||
} catch (err) {
|
||||
return { apiErrorMessage: err.message };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -60,19 +67,19 @@ module.exports = function useController(app, electron, route, controller) {
|
||||
// controller._init_called = true;
|
||||
// }
|
||||
try {
|
||||
let params = [{ ...req.body, ...req.query }];
|
||||
let params = [{ ...req.body, ...req.query }, req];
|
||||
if (rawParams) params = [req, res];
|
||||
const data = await controller[key](...params);
|
||||
res.json(data);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
res.status(500).json({ error: e.message });
|
||||
res.status(500).json({ apiErrorMessage: e.message });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (app) {
|
||||
app.use(route, router);
|
||||
app.use(getExpressPath(route), router);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -10,16 +10,16 @@ var config = {
|
||||
target: 'node',
|
||||
node: {
|
||||
__dirname: false,
|
||||
},
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'bundle.js',
|
||||
libraryTarget: 'commonjs2',
|
||||
},
|
||||
|
||||
// optimization: {
|
||||
// minimize: false,
|
||||
// },
|
||||
// optimization: {
|
||||
// minimize: false,
|
||||
// },
|
||||
|
||||
module: {
|
||||
rules: [
|
||||
@@ -45,6 +45,9 @@ var config = {
|
||||
},
|
||||
}),
|
||||
],
|
||||
externals: {
|
||||
'better-sqlite3': 'commonjs better-sqlite3',
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
|
||||