feat: initialize an app level db for storing workflow state

This commit is contained in:
linonetwo 2023-09-24 22:51:48 +08:00
parent 94c23a597b
commit e2f6e00f8f
14 changed files with 408 additions and 42 deletions

2
.nvmrc
View file

@ -1 +1 @@
v16 v18

View file

@ -101,6 +101,7 @@
"strip-ansi": "^7.1.0", "strip-ansi": "^7.1.0",
"threads": "1.7.0", "threads": "1.7.0",
"type-fest": "3.13.0", "type-fest": "3.13.0",
"typeorm": "^0.3.17",
"typescript-styled-is": "^2.1.0", "typescript-styled-is": "^2.1.0",
"v8-compile-cache-lib": "^3.0.1", "v8-compile-cache-lib": "^3.0.1",
"winston": "3.10.0", "winston": "3.10.0",

163
pnpm-lock.yaml generated
View file

@ -229,6 +229,9 @@ dependencies:
type-fest: type-fest:
specifier: 3.13.0 specifier: 3.13.0
version: 3.13.0 version: 3.13.0
typeorm:
specifier: ^0.3.17
version: 0.3.17(better-sqlite3@8.5.1)(ts-node@10.9.1)
typescript-styled-is: typescript-styled-is:
specifier: ^2.1.0 specifier: ^2.1.0
version: 2.1.0(react-dom@18.2.0)(react@18.2.0)(styled-components@6.0.7) version: 2.1.0(react-dom@18.2.0)(react@18.2.0)(styled-components@6.0.7)
@ -1931,7 +1934,6 @@ packages:
engines: {node: '>=12'} engines: {node: '>=12'}
dependencies: dependencies:
'@jridgewell/trace-mapping': 0.3.9 '@jridgewell/trace-mapping': 0.3.9
dev: true
/@cucumber/ci-environment@9.2.0: /@cucumber/ci-environment@9.2.0:
resolution: {integrity: sha512-jLzRtVwdtNt+uAmTwvXwW9iGYLEOJFpDSmnx/dgoMGKXUWRx1UHT86Q696CLdgXO8kyTwsgJY0c6n5SW9VitAA==} resolution: {integrity: sha512-jLzRtVwdtNt+uAmTwvXwW9iGYLEOJFpDSmnx/dgoMGKXUWRx1UHT86Q696CLdgXO8kyTwsgJY0c6n5SW9VitAA==}
@ -3385,7 +3387,6 @@ packages:
/@jridgewell/resolve-uri@3.1.1: /@jridgewell/resolve-uri@3.1.1:
resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
dev: true
/@jridgewell/set-array@1.1.2: /@jridgewell/set-array@1.1.2:
resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
@ -3415,7 +3416,6 @@ packages:
dependencies: dependencies:
'@jridgewell/resolve-uri': 3.1.1 '@jridgewell/resolve-uri': 3.1.1
'@jridgewell/sourcemap-codec': 1.4.15 '@jridgewell/sourcemap-codec': 1.4.15
dev: true
/@leichtgewicht/ip-codec@2.0.4: /@leichtgewicht/ip-codec@2.0.4:
resolution: {integrity: sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==} resolution: {integrity: sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==}
@ -3970,6 +3970,10 @@ packages:
resolution: {integrity: sha512-aywhxHNb6l7COooF3m439eT/6QN8E/RSl5IVboSKthMHcp0GlZYMSoS7546rqDLmFRxTD8f1tu/NIS9vtDwYAg==} resolution: {integrity: sha512-aywhxHNb6l7COooF3m439eT/6QN8E/RSl5IVboSKthMHcp0GlZYMSoS7546rqDLmFRxTD8f1tu/NIS9vtDwYAg==}
dev: true dev: true
/@sqltools/formatter@1.2.5:
resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==}
dev: false
/@szmarczak/http-timer@4.0.6: /@szmarczak/http-timer@4.0.6:
resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -3999,19 +4003,15 @@ packages:
/@tsconfig/node10@1.0.9: /@tsconfig/node10@1.0.9:
resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==}
dev: true
/@tsconfig/node12@1.0.11: /@tsconfig/node12@1.0.11:
resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==}
dev: true
/@tsconfig/node14@1.0.3: /@tsconfig/node14@1.0.3:
resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==}
dev: true
/@tsconfig/node16@1.0.4: /@tsconfig/node16@1.0.4:
resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==}
dev: true
/@types/better-sqlite3@7.6.4: /@types/better-sqlite3@7.6.4:
resolution: {integrity: sha512-dzrRZCYPXIXfSR1/surNbJ/grU3scTaygS0OMzjlGf71i9sc2fGyHPXXiXmEvNIoE0cGwsanEFMVJxPXmco9Eg==} resolution: {integrity: sha512-dzrRZCYPXIXfSR1/surNbJ/grU3scTaygS0OMzjlGf71i9sc2fGyHPXXiXmEvNIoE0cGwsanEFMVJxPXmco9Eg==}
@ -4851,7 +4851,6 @@ packages:
/acorn-walk@8.2.0: /acorn-walk@8.2.0:
resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==}
engines: {node: '>=0.4.0'} engines: {node: '>=0.4.0'}
dev: true
/acorn@8.9.0: /acorn@8.9.0:
resolution: {integrity: sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==} resolution: {integrity: sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==}
@ -4988,7 +4987,6 @@ packages:
/any-promise@1.3.0: /any-promise@1.3.0:
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
dev: true
/anymatch@3.1.3: /anymatch@3.1.3:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
@ -5004,6 +5002,11 @@ packages:
execa: 5.1.1 execa: 5.1.1
dev: false dev: false
/app-root-path@3.1.0:
resolution: {integrity: sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==}
engines: {node: '>= 6.0.0'}
dev: false
/aproba@1.2.0: /aproba@1.2.0:
resolution: {integrity: sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==} resolution: {integrity: sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==}
dev: false dev: false
@ -5029,7 +5032,6 @@ packages:
/arg@4.1.3: /arg@4.1.3:
resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==}
dev: true
/argparse@1.0.10: /argparse@1.0.10:
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
@ -5397,7 +5399,6 @@ packages:
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
dependencies: dependencies:
balanced-match: 1.0.2 balanced-match: 1.0.2
dev: true
/braces@3.0.2: /braces@3.0.2:
resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
@ -5511,7 +5512,6 @@ packages:
dependencies: dependencies:
base64-js: 1.5.1 base64-js: 1.5.1
ieee754: 1.2.1 ieee754: 1.2.1
dev: true
/bufferutil@4.0.7: /bufferutil@4.0.7:
resolution: {integrity: sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==} resolution: {integrity: sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==}
@ -5655,7 +5655,6 @@ packages:
dependencies: dependencies:
ansi-styles: 4.3.0 ansi-styles: 4.3.0
supports-color: 7.2.0 supports-color: 7.2.0
dev: true
/chalk@5.3.0: /chalk@5.3.0:
resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==}
@ -5776,6 +5775,19 @@ packages:
restore-cursor: 3.1.0 restore-cursor: 3.1.0
dev: true dev: true
/cli-highlight@2.1.11:
resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==}
engines: {node: '>=8.0.0', npm: '>=5.0.0'}
hasBin: true
dependencies:
chalk: 4.1.2
highlight.js: 10.7.3
mz: 2.7.0
parse5: 5.1.1
parse5-htmlparser2-tree-adapter: 6.0.1
yargs: 16.2.0
dev: false
/cli-spinners@2.9.0: /cli-spinners@2.9.0:
resolution: {integrity: sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==} resolution: {integrity: sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -5813,8 +5825,6 @@ packages:
string-width: 4.2.3 string-width: 4.2.3
strip-ansi: 6.0.1 strip-ansi: 6.0.1
wrap-ansi: 7.0.0 wrap-ansi: 7.0.0
dev: true
optional: true
/cliui@8.0.1: /cliui@8.0.1:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
@ -6144,7 +6154,6 @@ packages:
/create-require@1.1.1: /create-require@1.1.1:
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
dev: true
/cross-env@7.0.3: /cross-env@7.0.3:
resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
@ -6325,7 +6334,6 @@ packages:
engines: {node: '>=0.11'} engines: {node: '>=0.11'}
dependencies: dependencies:
'@babel/runtime': 7.22.6 '@babel/runtime': 7.22.6
dev: true
/debug@2.6.9: /debug@2.6.9:
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
@ -6520,7 +6528,6 @@ packages:
/diff@4.0.2: /diff@4.0.2:
resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
engines: {node: '>=0.3.1'} engines: {node: '>=0.3.1'}
dev: true
/diffie-hellman@5.0.3: /diffie-hellman@5.0.3:
resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==}
@ -6646,6 +6653,11 @@ packages:
tslib: 2.6.0 tslib: 2.6.0
dev: true dev: true
/dotenv@16.3.1:
resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==}
engines: {node: '>=12'}
dev: false
/dprint@0.37.1: /dprint@0.37.1:
resolution: {integrity: sha512-MVKIVOvk5FSiZQFFZlLv1KiB4zZXAirlZ/m8vtyKu2PTFnkD5d6jVv4aNHvd7M6f1ToganNZ/CqhSwpgadn8Ug==} resolution: {integrity: sha512-MVKIVOvk5FSiZQFFZlLv1KiB4zZXAirlZ/m8vtyKu2PTFnkD5d6jVv4aNHvd7M6f1ToganNZ/CqhSwpgadn8Ug==}
hasBin: true hasBin: true
@ -8528,7 +8540,6 @@ packages:
inherits: 2.0.4 inherits: 2.0.4
minimatch: 5.1.6 minimatch: 5.1.6
once: 1.4.0 once: 1.4.0
dev: true
/global-agent@3.0.0: /global-agent@3.0.0:
resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==} resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==}
@ -8760,6 +8771,10 @@ packages:
hasBin: true hasBin: true
dev: true dev: true
/highlight.js@10.7.3:
resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==}
dev: false
/hmac-drbg@1.0.1: /hmac-drbg@1.0.1:
resolution: {integrity: sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=} resolution: {integrity: sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=}
dependencies: dependencies:
@ -10052,7 +10067,6 @@ packages:
/make-error@1.3.6: /make-error@1.3.6:
resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
dev: true
/make-fetch-happen@11.1.1: /make-fetch-happen@11.1.1:
resolution: {integrity: sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==} resolution: {integrity: sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==}
@ -10263,7 +10277,6 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
dependencies: dependencies:
brace-expansion: 2.0.1 brace-expansion: 2.0.1
dev: true
/minimatch@9.0.2: /minimatch@9.0.2:
resolution: {integrity: sha512-PZOT9g5v2ojiTL7r1xF6plNHLtOeTpSlDI007As2NlA2aYBMfVom17yqa6QzhmDP8QOhn7LjHTg7DFCVSSa6yg==} resolution: {integrity: sha512-PZOT9g5v2ojiTL7r1xF6plNHLtOeTpSlDI007As2NlA2aYBMfVom17yqa6QzhmDP8QOhn7LjHTg7DFCVSSa6yg==}
@ -10373,7 +10386,6 @@ packages:
resolution: {integrity: sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==} resolution: {integrity: sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==}
engines: {node: '>=10'} engines: {node: '>=10'}
hasBin: true hasBin: true
dev: true
/moment@2.29.4: /moment@2.29.4:
resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==} resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==}
@ -10407,7 +10419,6 @@ packages:
any-promise: 1.3.0 any-promise: 1.3.0
object-assign: 4.1.1 object-assign: 4.1.1
thenify-all: 1.6.0 thenify-all: 1.6.0
dev: true
/nanoclone@0.2.1: /nanoclone@0.2.1:
resolution: {integrity: sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==} resolution: {integrity: sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==}
@ -11307,6 +11318,12 @@ packages:
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
dev: true dev: true
/parse5-htmlparser2-tree-adapter@6.0.1:
resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==}
dependencies:
parse5: 6.0.1
dev: false
/parse5-htmlparser2-tree-adapter@7.0.0: /parse5-htmlparser2-tree-adapter@7.0.0:
resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==} resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==}
dependencies: dependencies:
@ -11314,6 +11331,14 @@ packages:
parse5: 7.1.2 parse5: 7.1.2
dev: true dev: true
/parse5@5.1.1:
resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==}
dev: false
/parse5@6.0.1:
resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==}
dev: false
/parse5@7.1.2: /parse5@7.1.2:
resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==}
dependencies: dependencies:
@ -12087,7 +12112,7 @@ packages:
dev: true dev: true
/require-directory@2.1.1: /require-directory@2.1.1:
resolution: {integrity: sha1-jGStX9MNqxyXbiNE/+f3kqam30I=} resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
/require-from-string@2.0.2: /require-from-string@2.0.2:
@ -13088,7 +13113,6 @@ packages:
engines: {node: '>=8'} engines: {node: '>=8'}
dependencies: dependencies:
has-flag: 4.0.0 has-flag: 4.0.0
dev: true
/supports-color@8.1.1: /supports-color@8.1.1:
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
@ -13224,13 +13248,11 @@ packages:
engines: {node: '>=0.8'} engines: {node: '>=0.8'}
dependencies: dependencies:
thenify: 3.3.1 thenify: 3.3.1
dev: true
/thenify@3.3.1: /thenify@3.3.1:
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
dependencies: dependencies:
any-promise: 1.3.0 any-promise: 1.3.0
dev: true
/threads-plugin@1.4.0(@babel/types@7.22.5)(webpack@5.88.1): /threads-plugin@1.4.0(@babel/types@7.22.5)(webpack@5.88.1):
resolution: {integrity: sha512-lQENPueZLsD+6Cvxvj/QaQyUskwnFZO+2ZGDMnPIvtytSeywWvYzete8paZ9L+5IR4v8jnSYNZPlIQrEhSK1EA==} resolution: {integrity: sha512-lQENPueZLsD+6Cvxvj/QaQyUskwnFZO+2ZGDMnPIvtytSeywWvYzete8paZ9L+5IR4v8jnSYNZPlIQrEhSK1EA==}
@ -13414,7 +13436,6 @@ packages:
typescript: 5.1.6 typescript: 5.1.6
v8-compile-cache-lib: 3.0.1 v8-compile-cache-lib: 3.0.1
yn: 3.1.1 yn: 3.1.1
dev: true
/tslib@1.14.1: /tslib@1.14.1:
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
@ -13538,6 +13559,85 @@ packages:
is-typedarray: 1.0.0 is-typedarray: 1.0.0
dev: false dev: false
/typeorm@0.3.17(better-sqlite3@8.5.1)(ts-node@10.9.1):
resolution: {integrity: sha512-UDjUEwIQalO9tWw9O2A4GU+sT3oyoUXheHJy4ft+RFdnRdQctdQ34L9SqE2p7LdwzafHx1maxT+bqXON+Qnmig==}
engines: {node: '>= 12.9.0'}
hasBin: true
peerDependencies:
'@google-cloud/spanner': ^5.18.0
'@sap/hana-client': ^2.12.25
better-sqlite3: ^7.1.2 || ^8.0.0
hdb-pool: ^0.1.6
ioredis: ^5.0.4
mongodb: ^5.2.0
mssql: ^9.1.1
mysql2: ^2.2.5 || ^3.0.1
oracledb: ^5.1.0
pg: ^8.5.1
pg-native: ^3.0.0
pg-query-stream: ^4.0.0
redis: ^3.1.1 || ^4.0.0
sql.js: ^1.4.0
sqlite3: ^5.0.3
ts-node: ^10.7.0
typeorm-aurora-data-api-driver: ^2.0.0
peerDependenciesMeta:
'@google-cloud/spanner':
optional: true
'@sap/hana-client':
optional: true
better-sqlite3:
optional: true
hdb-pool:
optional: true
ioredis:
optional: true
mongodb:
optional: true
mssql:
optional: true
mysql2:
optional: true
oracledb:
optional: true
pg:
optional: true
pg-native:
optional: true
pg-query-stream:
optional: true
redis:
optional: true
sql.js:
optional: true
sqlite3:
optional: true
ts-node:
optional: true
typeorm-aurora-data-api-driver:
optional: true
dependencies:
'@sqltools/formatter': 1.2.5
app-root-path: 3.1.0
better-sqlite3: 8.5.1
buffer: 6.0.3
chalk: 4.1.2
cli-highlight: 2.1.11
date-fns: 2.30.0
debug: 4.3.4(supports-color@8.1.1)
dotenv: 16.3.1
glob: 8.1.0
mkdirp: 2.1.6
reflect-metadata: 0.1.13
sha.js: 2.4.11
ts-node: 10.9.1(@types/node@20.5.1)(typescript@5.1.6)
tslib: 2.6.0
uuid: 9.0.0
yargs: 17.7.2
transitivePeerDependencies:
- supports-color
dev: false
/typescript-plugin-styled-components@3.0.0(typescript@5.1.6): /typescript-plugin-styled-components@3.0.0(typescript@5.1.6):
resolution: {integrity: sha512-QWlhTl6NqsFxtJyxn7pJjm3RhgzXSByUftZ3AoQClrMMpa4yAaHuJKTN1gFpH3Ti+Rwm56fNUfG9pXSBU+WW3A==} resolution: {integrity: sha512-QWlhTl6NqsFxtJyxn7pJjm3RhgzXSByUftZ3AoQClrMMpa4yAaHuJKTN1gFpH3Ti+Rwm56fNUfG9pXSBU+WW3A==}
peerDependencies: peerDependencies:
@ -13564,7 +13664,6 @@ packages:
resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==} resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==}
engines: {node: '>=14.17'} engines: {node: '>=14.17'}
hasBin: true hasBin: true
dev: true
/typesync@0.11.1: /typesync@0.11.1:
resolution: {integrity: sha512-sMoD2oBqrmUZPX1jAmRd75N07qPG8gTSocfJSfe09otfuoVx4rFNcOreOriUW+hp6Fh01dBuh42yD2NCgZD2dA==} resolution: {integrity: sha512-sMoD2oBqrmUZPX1jAmRd75N07qPG8gTSocfJSfe09otfuoVx4rFNcOreOriUW+hp6Fh01dBuh42yD2NCgZD2dA==}
@ -13759,7 +13858,6 @@ packages:
/uuid@9.0.0: /uuid@9.0.0:
resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==}
hasBin: true hasBin: true
dev: true
/v8-compile-cache-lib@3.0.1: /v8-compile-cache-lib@3.0.1:
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
@ -14346,8 +14444,6 @@ packages:
resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
engines: {node: '>=10'} engines: {node: '>=10'}
requiresBuild: true requiresBuild: true
dev: true
optional: true
/yargs-parser@21.1.1: /yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
@ -14388,8 +14484,6 @@ packages:
string-width: 4.2.3 string-width: 4.2.3
y18n: 5.0.8 y18n: 5.0.8
yargs-parser: 20.2.9 yargs-parser: 20.2.9
dev: true
optional: true
/yargs@17.7.2: /yargs@17.7.2:
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
@ -14421,7 +14515,6 @@ packages:
/yn@3.1.1: /yn@3.1.1:
resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==}
engines: {node: '>=6'} engines: {node: '>=6'}
dev: true
/yocto-queue@0.1.0: /yocto-queue@0.1.0:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}

View file

@ -147,6 +147,10 @@ export enum MetaDataChannel {
name = 'MetaDataChannel', name = 'MetaDataChannel',
} }
export enum WorkflowChannel {
name = 'WorkflowChannel',
}
export type Channels = export type Channels =
| MainChannel | MainChannel
| AuthenticationChannel | AuthenticationChannel

View file

@ -22,6 +22,7 @@ import { bindServiceAndProxy } from '@services/libs/bindServiceAndProxy';
import serviceIdentifier from '@services/serviceIdentifier'; import serviceIdentifier from '@services/serviceIdentifier';
import { WindowNames } from '@services/windows/WindowProperties'; import { WindowNames } from '@services/windows/WindowProperties';
import { IDatabaseService } from '@services/database/interface';
import { reportErrorToGithubWithTemplates } from '@services/native/reportError'; import { reportErrorToGithubWithTemplates } from '@services/native/reportError';
import type { IUpdaterService } from '@services/updater/interface'; import type { IUpdaterService } from '@services/updater/interface';
import { IWikiService } from '@services/wiki/interface'; import { IWikiService } from '@services/wiki/interface';
@ -58,6 +59,7 @@ const wikiGitWorkspaceService = container.get<IWikiGitWorkspaceService>(serviceI
const wikiService = container.get<IWikiService>(serviceIdentifier.Wiki); const wikiService = container.get<IWikiService>(serviceIdentifier.Wiki);
const windowService = container.get<IWindowService>(serviceIdentifier.Window); const windowService = container.get<IWindowService>(serviceIdentifier.Window);
const workspaceViewService = container.get<IWorkspaceViewService>(serviceIdentifier.WorkspaceView); const workspaceViewService = container.get<IWorkspaceViewService>(serviceIdentifier.WorkspaceView);
const databaseService = container.get<IDatabaseService>(serviceIdentifier.Database);
app.on('second-instance', () => { app.on('second-instance', () => {
// Someone tried to run a second instance, we should focus our window. // Someone tried to run a second instance, we should focus our window.
const mainWindow = windowService.get(WindowNames.main); const mainWindow = windowService.get(WindowNames.main);
@ -95,6 +97,7 @@ const commonInit = async (): Promise<void> => {
preferenceService.get('attachToMenubar').then(async (attachToMenubar) => { preferenceService.get('attachToMenubar').then(async (attachToMenubar) => {
attachToMenubar && await windowService.open(WindowNames.menuBar); attachToMenubar && await windowService.open(WindowNames.menuBar);
}), }),
databaseService.initializeForApp(),
]); ]);
// perform wiki startup and git sync for each workspace // perform wiki startup and git sync for each workspace
await workspaceViewService.initializeAllWorkspaceView(); await workspaceViewService.initializeAllWorkspaceView();

View file

@ -0,0 +1,9 @@
# Database Service
We have workspace level db, work as cache for tiddlers, and change logs (to record deletion, for sync deletion with mobile clients).
And we have app level db, to store things for pages like workflow pages. They are also regarded as temporary cache, and user can toggle a switch to store something inside cache to a wiki.
## App level DB
`src/services/database/entity` and `src/services/database/migration` are for app level db.

View file

@ -0,0 +1,14 @@
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
import { WorkflowNetwork } from './WorkflowNetwork';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id!: number;
@Column('text')
username!: string;
@OneToMany(() => WorkflowNetwork, workflowNetwork => workflowNetwork.user)
workflowNetworks!: WorkflowNetwork[];
}

View file

@ -0,0 +1,26 @@
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
import { User } from './User';
export enum WorkflowRunningState {
Idle = 'idle',
Running = 'running',
Stopped = 'stopped',
}
@Entity()
export class WorkflowNetwork {
@PrimaryGeneratedColumn()
id!: number;
@Column({
type: 'varchar',
default: WorkflowRunningState.Idle,
})
runningState!: WorkflowRunningState;
@Column('text') // use text for large JSON string
serializedState!: string;
@ManyToOne(() => User, user => user.workflowNetworks)
user!: User;
}

View file

@ -0,0 +1,4 @@
import { User } from './User';
import { WorkflowNetwork } from './WorkflowNetwork';
export const entities = [User, WorkflowNetwork];

View file

@ -11,6 +11,9 @@ import { lazyInject } from '@services/container';
import { logger } from '@services/libs/log'; import { logger } from '@services/libs/log';
import fs from 'fs-extra'; import fs from 'fs-extra';
import path from 'path'; import path from 'path';
import { DataSource } from 'typeorm';
import { entities } from './entity';
import { migrations } from './migration';
import { loadSqliteVss } from './sqlite-vss'; import { loadSqliteVss } from './sqlite-vss';
@injectable() @injectable()
@ -23,9 +26,9 @@ export class DatabaseService implements IDatabaseService {
// private readonly dbWorker?: ModuleThread<GitWorker>; // private readonly dbWorker?: ModuleThread<GitWorker>;
async initializeForWorkspace(workspaceID: string): Promise<void> { async initializeForWorkspace(workspaceID: string): Promise<void> {
const destinationFilePath = this.getDataBasePath(workspaceID); const destinationFilePath = this.getWorkspaceDataBasePath(workspaceID);
// only create db file for this workspace's wiki if it doesn't exist // only create db file for this workspace's wiki if it doesn't exist
if (await fs.exists(this.getDataBasePath(workspaceID))) { if (await fs.exists(this.getWorkspaceDataBasePath(workspaceID))) {
logger.debug(`DatabaseService.initializeForWorkspace skip, there already has sqlite database for workspace ${workspaceID} in ${destinationFilePath}`); logger.debug(`DatabaseService.initializeForWorkspace skip, there already has sqlite database for workspace ${workspaceID} in ${destinationFilePath}`);
return; return;
} }
@ -74,7 +77,130 @@ export class DatabaseService implements IDatabaseService {
} }
} }
getDataBasePath(workspaceID: string): string { async initializeForApp(): Promise<void> {
const destinationFilePath = this.getAppDataBasePath();
// only create db file for app if it doesn't exist
if (await fs.exists(destinationFilePath)) {
logger.debug(`DatabaseService.initializeForApp skip, there already has sqlite database for app in ${destinationFilePath}`);
return;
}
await fs.ensureDir(CACHE_DATABASE_FOLDER);
try {
logger.debug(`DatabaseService.initializeForApp create a sqlite database for app`, { SQLITE_BINARY_PATH });
// Initialize TypeORM Connection using DataSource
const appDataSource = new DataSource({
type: 'better-sqlite3',
nativeBinding: SQLITE_BINARY_PATH,
database: destinationFilePath,
entities,
synchronize: false,
migrationsRun: true,
logging: true,
migrations,
});
await appDataSource.initialize();
await appDataSource.runMigrations();
await appDataSource.destroy();
logger.info(`DatabaseService.initializeForApp TypeORM connection initialized and migrations ran for app`);
} catch (error) {
logger.error(`DatabaseService.initializeForApp error when initializing TypeORM connection and running migrations for app: ${(error as Error).message}`);
}
}
private readonly dataSources = new Map<string, DataSource>();
async getAppDatabase(isRetry = false): Promise<DataSource> {
const name = 'app-tidgi';
if (!this.dataSources.has(name)) {
try {
const dataSource = new DataSource({
type: 'sqlite',
database: this.getAppDataBasePath(),
entities,
synchronize: false,
migrationsRun: false,
logging: true,
migrations,
});
/**
* Error `TypeError: Cannot read property 'transaction' of undefined` will show if run any query without initialize.
*/
await dataSource.initialize();
this.dataSources.set(name, dataSource);
return dataSource;
} catch (error) {
console.error(`Failed to getDatabase ${name}: ${(error as Error).message} ${(error as Error).stack ?? ''}`);
if (!isRetry) {
try {
await this.#fixAppDbLock();
return await this.getAppDatabase(true);
} catch (error) {
console.error(`Failed to retry getDatabase ${name}: ${(error as Error).message} ${(error as Error).stack ?? ''}`);
}
}
try {
await this.dataSources.get(name)?.destroy();
} catch (error) {
console.error(`Failed to destroy in getDatabase ${name}: ${(error as Error).message} ${(error as Error).stack ?? ''}`);
}
throw error;
}
}
return this.dataSources.get(name)!;
}
async closeAppDatabase(drop?: boolean) {
const name = 'app-tidgi';
if (this.dataSources.has(name)) {
try {
const dataSource = this.dataSources.get(name)!;
this.dataSources.delete(name);
if (drop === true) {
await dataSource.dropDatabase();
// need to delete the file. May encounter SQLITE_BUSY error if not deleted.
await fs.unlink(this.getAppDataBasePath());
} else {
await dataSource.destroy();
console.log(`closeDatabase ${name}`);
}
} catch (error) {
console.error(`Failed to closeDatabase ${name}: ${(error as Error).message} ${(error as Error).stack ?? ''}`);
throw error;
}
}
}
/**
* Fix SQLite busy by move the file.
* @url https://stackoverflow.com/a/1226850
*
* Fixes this:
*
* ```error
* [Error: Error getting skinny tiddlers list from SQLite: Call to function 'ExpoSQLite.exec' has been rejected.
* Caused by: android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5 SQLITE_BUSY): , while compiling: PRAGMA journal_mode] Error: Error getting skinny tiddlers list from SQLite: Call to function 'ExpoSQLite.exec' has been rejected.
* Caused by: android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5 SQLITE_BUSY): , while compiling: PRAGMA journal_mode
* ```
*/
async #fixAppDbLock() {
const oldSqlitePath = this.getAppDataBasePath();
const temporarySqlitePath = `${oldSqlitePath}.temp`;
await fs.copy(oldSqlitePath, temporarySqlitePath);
await fs.unlink(oldSqlitePath);
await fs.copy(temporarySqlitePath, oldSqlitePath);
await fs.unlink(temporarySqlitePath);
}
getWorkspaceDataBasePath(workspaceID: string): string {
return path.resolve(CACHE_DATABASE_FOLDER, `${workspaceID}-sqlite3-cache.db`); return path.resolve(CACHE_DATABASE_FOLDER, `${workspaceID}-sqlite3-cache.db`);
} }
getAppDataBasePath(): string {
return path.resolve(CACHE_DATABASE_FOLDER, `app-tidgi-sqlite3-cache.db`);
}
} }

View file

@ -1,11 +1,19 @@
import { DatabaseChannel } from '@/constants/channels'; import { DatabaseChannel } from '@/constants/channels';
import { ProxyPropertyType } from 'electron-ipc-cat/common'; import { ProxyPropertyType } from 'electron-ipc-cat/common';
import { DataSource } from 'typeorm';
/** /**
* Allow wiki or external app to save/search tiddlers cache from database like sqlite+sqlite-vss (vector storage) * Allow wiki or external app to save/search tiddlers cache from database like sqlite+sqlite-vss (vector storage)
*/ */
export interface IDatabaseService { export interface IDatabaseService {
getDataBasePath(workspaceID: string): string; closeAppDatabase(drop?: boolean): void;
getAppDataBasePath(): string;
/**
* Get a database connection for the app db, which is a sqlite manages by TypeORM for all app level data
*/
getAppDatabase(isRetry?: boolean): Promise<DataSource>;
getWorkspaceDataBasePath(workspaceID: string): string;
initializeForApp(): Promise<void>;
/** /**
* Create a database file for a workspace, store it in the appData folder, and load it in a worker_thread to execute SQL. * * Create a database file for a workspace, store it in the appData folder, and load it in a worker_thread to execute SQL. *
* (not store `.db` file in the workspace wiki's folder, because this cache file shouldn't not by Database committed) * (not store `.db` file in the workspace wiki's folder, because this cache file shouldn't not by Database committed)
@ -15,7 +23,9 @@ export interface IDatabaseService {
export const DatabaseServiceIPCDescriptor = { export const DatabaseServiceIPCDescriptor = {
channel: DatabaseChannel.name, channel: DatabaseChannel.name,
properties: { properties: {
initializeForWorkspace: ProxyPropertyType.Function, getAppDataBasePath: ProxyPropertyType.Function,
getDataBasePath: ProxyPropertyType.Function, getDataBasePath: ProxyPropertyType.Function,
initializeForApp: ProxyPropertyType.Function,
initializeForWorkspace: ProxyPropertyType.Function,
}, },
}; };

View file

@ -0,0 +1,73 @@
import { MigrationInterface, QueryRunner, Table, TableForeignKey } from 'typeorm';
export class Init1695276132349 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
// User table
await queryRunner.createTable(
new Table({
name: 'user',
columns: [
{
name: 'id',
type: 'integer',
isPrimary: true,
isGenerated: true,
generationStrategy: 'increment',
},
{
name: 'username',
type: 'varchar',
},
],
}),
true,
);
// WorkflowNetwork table
await queryRunner.createTable(
new Table({
name: 'workflowNetwork',
columns: [
{
name: 'id',
type: 'integer',
isPrimary: true,
isGenerated: true,
generationStrategy: 'increment',
},
{
name: 'runningState',
type: 'varchar',
// SQLite does not natively support the ENUM data type, and it seems like better-sqlite3 also does not support it. Error: Data type \"enum\" in \"WorkflowNetwork.runningState\" is not supported by \"better-sqlite3\" database.",
// enum: ['idle', 'running', 'stopped'],
default: "'idle'",
},
{
name: 'serializedState',
type: 'text',
},
{
name: 'userId',
type: 'integer',
},
],
}),
true,
);
await queryRunner.createForeignKey(
'workflowNetwork',
new TableForeignKey({
columnNames: ['userId'],
referencedColumnNames: ['id'],
referencedTableName: 'user',
onDelete: 'CASCADE',
}),
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropTable('workflowNetwork');
await queryRunner.dropTable('user');
}
}

View file

@ -0,0 +1,3 @@
import { Init1695276132349 } from './1695276132349-init';
export const migrations = [Init1695276132349];

View file

@ -176,7 +176,7 @@ export class Wiki implements IWikiService {
logger.debug('startWiki calling initCacheDatabase in the main process', { function: 'wikiWorker.initCacheDatabase' }); logger.debug('startWiki calling initCacheDatabase in the main process', { function: 'wikiWorker.initCacheDatabase' });
worker.initCacheDatabase({ worker.initCacheDatabase({
databaseFile: this.databaseService.getDataBasePath(workspaceID), databaseFile: this.databaseService.getWorkspaceDataBasePath(workspaceID),
sqliteBinary: SQLITE_BINARY_PATH, sqliteBinary: SQLITE_BINARY_PATH,
packagePathBase: PACKAGE_PATH_BASE, packagePathBase: PACKAGE_PATH_BASE,
}).subscribe(async (message) => { }).subscribe(async (message) => {