Commit 5b640397 authored by 王曜嵚 Wang Yaoqin's avatar 王曜嵚 Wang Yaoqin

dev: v2 接口

parent 14984ae5
import { useUserStore } from "~/store/user"
export default defineNuxtRouteMiddleware((to, from) => { export default defineNuxtRouteMiddleware((to, from) => {
const user = useUserStore() if (to.path === '/') {
if (!user.username && to.path !== '/login') {
return navigateTo('/login') return navigateTo('/login')
} }
}) })
\ No newline at end of file
...@@ -8,15 +8,21 @@ ...@@ -8,15 +8,21 @@
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@pinia/nuxt": "^0.5.1", "@pinia/nuxt": "^0.5.1",
"dockerode": "^3.3.5",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"nuxt": "^3.11.1", "nuxt": "^3.11.1",
"pg": "^8.11.5",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"tar-fs": "^3.0.5",
"telnet-client": "^2.2.0", "telnet-client": "^2.2.0",
"vue": "^3.4.21", "vue": "^3.4.21",
"vue-router": "^4.3.0" "vue-router": "^4.3.0"
}, },
"devDependencies": { "devDependencies": {
"@types/lodash": "^4.17.0" "@types/dockerode": "^3.3.28",
"@types/lodash": "^4.17.0",
"@types/pg": "^8.11.4",
"@types/tar-fs": "^2.0.4"
} }
}, },
"node_modules/@ampproject/remapping": { "node_modules/@ampproject/remapping": {
...@@ -648,6 +654,11 @@ ...@@ -648,6 +654,11 @@
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@balena/dockerignore": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/@balena/dockerignore/-/dockerignore-1.0.2.tgz",
"integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q=="
},
"node_modules/@cloudflare/kv-asset-handler": { "node_modules/@cloudflare/kv-asset-handler": {
"version": "0.3.1", "version": "0.3.1",
"resolved": "https://registry.npmmirror.com/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.3.1.tgz", "resolved": "https://registry.npmmirror.com/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.3.1.tgz",
...@@ -2502,6 +2513,27 @@ ...@@ -2502,6 +2513,27 @@
"node": ">=16 || 14 >=14.17" "node": ">=16 || 14 >=14.17"
} }
}, },
"node_modules/@types/docker-modem": {
"version": "3.0.6",
"resolved": "https://registry.npmmirror.com/@types/docker-modem/-/docker-modem-3.0.6.tgz",
"integrity": "sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==",
"dev": true,
"dependencies": {
"@types/node": "*",
"@types/ssh2": "*"
}
},
"node_modules/@types/dockerode": {
"version": "3.3.28",
"resolved": "https://registry.npmmirror.com/@types/dockerode/-/dockerode-3.3.28.tgz",
"integrity": "sha512-RjY96chW88t2QvSebCsec+mQYo3/nyOr+/tVcE+0ynlOg2m/i9wPE52DhptzF75QDlhv2uDYVPqKfHKeGTn6Fg==",
"dev": true,
"dependencies": {
"@types/docker-modem": "*",
"@types/node": "*",
"@types/ssh2": "*"
}
},
"node_modules/@types/estree": { "node_modules/@types/estree": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.5.tgz", "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.5.tgz",
...@@ -2529,11 +2561,116 @@ ...@@ -2529,11 +2561,116 @@
"undici-types": "~5.26.4" "undici-types": "~5.26.4"
} }
}, },
"node_modules/@types/pg": {
"version": "8.11.4",
"resolved": "https://registry.npmmirror.com/@types/pg/-/pg-8.11.4.tgz",
"integrity": "sha512-yw3Bwbda6vO+NvI1Ue/YKOwtl31AYvvd/e73O3V4ZkNzuGpTDndLSyc0dQRB2xrQqDePd20pEGIfqSp/GH3pRw==",
"dev": true,
"dependencies": {
"@types/node": "*",
"pg-protocol": "*",
"pg-types": "^4.0.1"
}
},
"node_modules/@types/pg/node_modules/pg-types": {
"version": "4.0.2",
"resolved": "https://registry.npmmirror.com/pg-types/-/pg-types-4.0.2.tgz",
"integrity": "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==",
"dev": true,
"dependencies": {
"pg-int8": "1.0.1",
"pg-numeric": "1.0.2",
"postgres-array": "~3.0.1",
"postgres-bytea": "~3.0.0",
"postgres-date": "~2.1.0",
"postgres-interval": "^3.0.0",
"postgres-range": "^1.1.1"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@types/pg/node_modules/postgres-array": {
"version": "3.0.2",
"resolved": "https://registry.npmmirror.com/postgres-array/-/postgres-array-3.0.2.tgz",
"integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==",
"dev": true,
"engines": {
"node": ">=12"
}
},
"node_modules/@types/pg/node_modules/postgres-bytea": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/postgres-bytea/-/postgres-bytea-3.0.0.tgz",
"integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==",
"dev": true,
"dependencies": {
"obuf": "~1.1.2"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/@types/pg/node_modules/postgres-date": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/postgres-date/-/postgres-date-2.1.0.tgz",
"integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==",
"dev": true,
"engines": {
"node": ">=12"
}
},
"node_modules/@types/pg/node_modules/postgres-interval": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/postgres-interval/-/postgres-interval-3.0.0.tgz",
"integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==",
"dev": true,
"engines": {
"node": ">=12"
}
},
"node_modules/@types/resolve": { "node_modules/@types/resolve": {
"version": "1.20.2", "version": "1.20.2",
"resolved": "https://registry.npmmirror.com/@types/resolve/-/resolve-1.20.2.tgz", "resolved": "https://registry.npmmirror.com/@types/resolve/-/resolve-1.20.2.tgz",
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==" "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="
}, },
"node_modules/@types/ssh2": {
"version": "1.15.0",
"resolved": "https://registry.npmmirror.com/@types/ssh2/-/ssh2-1.15.0.tgz",
"integrity": "sha512-YcT8jP5F8NzWeevWvcyrrLB3zcneVjzYY9ZDSMAMboI+2zR1qYWFhwsyOFVzT7Jorn67vqxC0FRiw8YyG9P1ww==",
"dev": true,
"dependencies": {
"@types/node": "^18.11.18"
}
},
"node_modules/@types/ssh2/node_modules/@types/node": {
"version": "18.19.30",
"resolved": "https://registry.npmmirror.com/@types/node/-/node-18.19.30.tgz",
"integrity": "sha512-453z1zPuJLVDbyahaa1sSD5C2sht6ZpHp5rgJNs+H8YGqhluCXcuOUmBYsAo0Tos0cHySJ3lVUGbGgLlqIkpyg==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@types/tar-fs": {
"version": "2.0.4",
"resolved": "https://registry.npmmirror.com/@types/tar-fs/-/tar-fs-2.0.4.tgz",
"integrity": "sha512-ipPec0CjTmVDWE+QKr9cTmIIoTl7dFG/yARCM5MqK8i6CNLIG1P8x4kwDsOQY1ChZOZjH0wO9nvfgBvWl4R3kA==",
"dev": true,
"dependencies": {
"@types/node": "*",
"@types/tar-stream": "*"
}
},
"node_modules/@types/tar-stream": {
"version": "3.1.3",
"resolved": "https://registry.npmmirror.com/@types/tar-stream/-/tar-stream-3.1.3.tgz",
"integrity": "sha512-Zbnx4wpkWBMBSu5CytMbrT5ZpMiF55qgM+EpHzR4yIDu7mv52cej8hTkOc6K+LzpkOAbxwn/m7j3iO+/l42YkQ==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/web-bluetooth": { "node_modules/@types/web-bluetooth": {
"version": "0.0.20", "version": "0.0.20",
"resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz", "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
...@@ -3678,6 +3815,14 @@ ...@@ -3678,6 +3815,14 @@
"resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
}, },
"node_modules/asn1": {
"version": "0.2.6",
"resolved": "https://registry.npmmirror.com/asn1/-/asn1-0.2.6.tgz",
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
"dependencies": {
"safer-buffer": "~2.1.0"
}
},
"node_modules/ast-kit": { "node_modules/ast-kit": {
"version": "0.12.1", "version": "0.12.1",
"resolved": "https://registry.npmmirror.com/ast-kit/-/ast-kit-0.12.1.tgz", "resolved": "https://registry.npmmirror.com/ast-kit/-/ast-kit-0.12.1.tgz",
...@@ -3763,11 +3908,45 @@ ...@@ -3763,11 +3908,45 @@
"integrity": "sha512-h7z00dWdG0PYOQEvChhOSWvOfkIKsdZGkWr083FgN/HyoQuebSew/cgirYqh9SCuy/hRvxc5Vy6Fw8xAmYHLkQ==", "integrity": "sha512-h7z00dWdG0PYOQEvChhOSWvOfkIKsdZGkWr083FgN/HyoQuebSew/cgirYqh9SCuy/hRvxc5Vy6Fw8xAmYHLkQ==",
"optional": true "optional": true
}, },
"node_modules/bare-fs": {
"version": "2.2.3",
"resolved": "https://registry.npmmirror.com/bare-fs/-/bare-fs-2.2.3.tgz",
"integrity": "sha512-amG72llr9pstfXOBOHve1WjiuKKAMnebcmMbPWDZ7BCevAoJLpugjuAPRsDINEyjT0a6tbaVx3DctkXIRbLuJw==",
"optional": true,
"dependencies": {
"bare-events": "^2.0.0",
"bare-path": "^2.0.0",
"streamx": "^2.13.0"
}
},
"node_modules/bare-os": {
"version": "2.2.1",
"resolved": "https://registry.npmmirror.com/bare-os/-/bare-os-2.2.1.tgz",
"integrity": "sha512-OwPyHgBBMkhC29Hl3O4/YfxW9n7mdTr2+SsO29XBWKKJsbgj3mnorDB80r5TiCQgQstgE5ga1qNYrpes6NvX2w==",
"optional": true
},
"node_modules/bare-path": {
"version": "2.1.1",
"resolved": "https://registry.npmmirror.com/bare-path/-/bare-path-2.1.1.tgz",
"integrity": "sha512-OHM+iwRDRMDBsSW7kl3dO62JyHdBKO3B25FB9vNQBPcGHMo4+eA8Yj41Lfbk3pS/seDY+siNge0LdRTulAau/A==",
"optional": true,
"dependencies": {
"bare-os": "^2.1.0"
}
},
"node_modules/base64-js": { "node_modules/base64-js": {
"version": "1.5.1", "version": "1.5.1",
"resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz", "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
}, },
"node_modules/bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
"dependencies": {
"tweetnacl": "^0.14.3"
}
},
"node_modules/binary-extensions": { "node_modules/binary-extensions": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz", "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz",
...@@ -3789,6 +3968,38 @@ ...@@ -3789,6 +3968,38 @@
"resolved": "https://registry.npmmirror.com/birpc/-/birpc-0.2.17.tgz", "resolved": "https://registry.npmmirror.com/birpc/-/birpc-0.2.17.tgz",
"integrity": "sha512-+hkTxhot+dWsLpp3gia5AkVHIsKlZybNT5gIYiDlNzJrmYPcTM9k5/w2uaj3IPpd7LlEYpmCj4Jj1nC41VhDFg==" "integrity": "sha512-+hkTxhot+dWsLpp3gia5AkVHIsKlZybNT5gIYiDlNzJrmYPcTM9k5/w2uaj3IPpd7LlEYpmCj4Jj1nC41VhDFg=="
}, },
"node_modules/bl": {
"version": "4.1.0",
"resolved": "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"dependencies": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
"readable-stream": "^3.4.0"
}
},
"node_modules/bl/node_modules/buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"node_modules/bl/node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/boolbase": { "node_modules/boolbase": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz", "resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz",
...@@ -3852,6 +4063,15 @@ ...@@ -3852,6 +4063,15 @@
"resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz", "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
}, },
"node_modules/buildcheck": {
"version": "0.0.6",
"resolved": "https://registry.npmmirror.com/buildcheck/-/buildcheck-0.0.6.tgz",
"integrity": "sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==",
"optional": true,
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/builtin-modules": { "node_modules/builtin-modules": {
"version": "3.3.0", "version": "3.3.0",
"resolved": "https://registry.npmmirror.com/builtin-modules/-/builtin-modules-3.3.0.tgz", "resolved": "https://registry.npmmirror.com/builtin-modules/-/builtin-modules-3.3.0.tgz",
...@@ -4253,6 +4473,20 @@ ...@@ -4253,6 +4473,20 @@
"resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz", "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
}, },
"node_modules/cpu-features": {
"version": "0.0.9",
"resolved": "https://registry.npmmirror.com/cpu-features/-/cpu-features-0.0.9.tgz",
"integrity": "sha512-AKjgn2rP2yJyfbepsmLfiYcmtNn/2eUvocUyM/09yB0YDiz39HteK/5/T4Onf0pmdYDMgkBoGvRLvEguzyL7wQ==",
"hasInstallScript": true,
"optional": true,
"dependencies": {
"buildcheck": "~0.0.6",
"nan": "^2.17.0"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/crc-32": { "node_modules/crc-32": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz", "resolved": "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz",
...@@ -4629,6 +4863,90 @@ ...@@ -4629,6 +4863,90 @@
"node": ">=0.3.1" "node": ">=0.3.1"
} }
}, },
"node_modules/docker-modem": {
"version": "3.0.8",
"resolved": "https://registry.npmmirror.com/docker-modem/-/docker-modem-3.0.8.tgz",
"integrity": "sha512-f0ReSURdM3pcKPNS30mxOHSbaFLcknGmQjwSfmbcdOw1XWKXVhukM3NJHhr7NpY9BIyyWQb0EBo3KQvvuU5egQ==",
"dependencies": {
"debug": "^4.1.1",
"readable-stream": "^3.5.0",
"split-ca": "^1.0.1",
"ssh2": "^1.11.0"
},
"engines": {
"node": ">= 8.0"
}
},
"node_modules/docker-modem/node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/dockerode": {
"version": "3.3.5",
"resolved": "https://registry.npmmirror.com/dockerode/-/dockerode-3.3.5.tgz",
"integrity": "sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA==",
"dependencies": {
"@balena/dockerignore": "^1.0.2",
"docker-modem": "^3.0.0",
"tar-fs": "~2.0.1"
},
"engines": {
"node": ">= 8.0"
}
},
"node_modules/dockerode/node_modules/chownr": {
"version": "1.1.4",
"resolved": "https://registry.npmmirror.com/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
},
"node_modules/dockerode/node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/dockerode/node_modules/tar-fs": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/tar-fs/-/tar-fs-2.0.1.tgz",
"integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==",
"dependencies": {
"chownr": "^1.1.1",
"mkdirp-classic": "^0.5.2",
"pump": "^3.0.0",
"tar-stream": "^2.0.0"
}
},
"node_modules/dockerode/node_modules/tar-stream": {
"version": "2.2.0",
"resolved": "https://registry.npmmirror.com/tar-stream/-/tar-stream-2.2.0.tgz",
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
"dependencies": {
"bl": "^4.0.3",
"end-of-stream": "^1.4.1",
"fs-constants": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.1.1"
},
"engines": {
"node": ">=6"
}
},
"node_modules/dom-serializer": { "node_modules/dom-serializer": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-2.0.0.tgz", "resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-2.0.0.tgz",
...@@ -4731,6 +5049,14 @@ ...@@ -4731,6 +5049,14 @@
"iconv-lite": "^0.6.2" "iconv-lite": "^0.6.2"
} }
}, },
"node_modules/end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"dependencies": {
"once": "^1.4.0"
}
},
"node_modules/enhanced-resolve": { "node_modules/enhanced-resolve": {
"version": "5.16.0", "version": "5.16.0",
"resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz", "resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz",
...@@ -5027,6 +5353,11 @@ ...@@ -5027,6 +5353,11 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
},
"node_modules/fs-extra": { "node_modules/fs-extra": {
"version": "11.2.0", "version": "11.2.0",
"resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.2.0.tgz", "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.2.0.tgz",
...@@ -6288,6 +6619,11 @@ ...@@ -6288,6 +6619,11 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/mkdirp-classic": {
"version": "0.5.3",
"resolved": "https://registry.npmmirror.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
},
"node_modules/mlly": { "node_modules/mlly": {
"version": "1.6.1", "version": "1.6.1",
"resolved": "https://registry.npmmirror.com/mlly/-/mlly-1.6.1.tgz", "resolved": "https://registry.npmmirror.com/mlly/-/mlly-1.6.1.tgz",
...@@ -6320,6 +6656,12 @@ ...@@ -6320,6 +6656,12 @@
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}, },
"node_modules/nan": {
"version": "2.19.0",
"resolved": "https://registry.npmmirror.com/nan/-/nan-2.19.0.tgz",
"integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==",
"optional": true
},
"node_modules/nanoid": { "node_modules/nanoid": {
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-4.0.2.tgz", "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-4.0.2.tgz",
...@@ -6928,6 +7270,12 @@ ...@@ -6928,6 +7270,12 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/obuf": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/obuf/-/obuf-1.1.2.tgz",
"integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
"dev": true
},
"node_modules/ofetch": { "node_modules/ofetch": {
"version": "1.3.4", "version": "1.3.4",
"resolved": "https://registry.npmmirror.com/ofetch/-/ofetch-1.3.4.tgz", "resolved": "https://registry.npmmirror.com/ofetch/-/ofetch-1.3.4.tgz",
...@@ -7207,6 +7555,96 @@ ...@@ -7207,6 +7555,96 @@
"resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz", "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
"integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==" "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="
}, },
"node_modules/pg": {
"version": "8.11.5",
"resolved": "https://registry.npmmirror.com/pg/-/pg-8.11.5.tgz",
"integrity": "sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==",
"dependencies": {
"pg-connection-string": "^2.6.4",
"pg-pool": "^3.6.2",
"pg-protocol": "^1.6.1",
"pg-types": "^2.1.0",
"pgpass": "1.x"
},
"engines": {
"node": ">= 8.0.0"
},
"optionalDependencies": {
"pg-cloudflare": "^1.1.1"
},
"peerDependencies": {
"pg-native": ">=3.0.1"
},
"peerDependenciesMeta": {
"pg-native": {
"optional": true
}
}
},
"node_modules/pg-cloudflare": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz",
"integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==",
"optional": true
},
"node_modules/pg-connection-string": {
"version": "2.6.4",
"resolved": "https://registry.npmmirror.com/pg-connection-string/-/pg-connection-string-2.6.4.tgz",
"integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA=="
},
"node_modules/pg-int8": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/pg-int8/-/pg-int8-1.0.1.tgz",
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/pg-numeric": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/pg-numeric/-/pg-numeric-1.0.2.tgz",
"integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==",
"dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/pg-pool": {
"version": "3.6.2",
"resolved": "https://registry.npmmirror.com/pg-pool/-/pg-pool-3.6.2.tgz",
"integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==",
"peerDependencies": {
"pg": ">=8.0"
}
},
"node_modules/pg-protocol": {
"version": "1.6.1",
"resolved": "https://registry.npmmirror.com/pg-protocol/-/pg-protocol-1.6.1.tgz",
"integrity": "sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg=="
},
"node_modules/pg-types": {
"version": "2.2.0",
"resolved": "https://registry.npmmirror.com/pg-types/-/pg-types-2.2.0.tgz",
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
"dependencies": {
"pg-int8": "1.0.1",
"postgres-array": "~2.0.0",
"postgres-bytea": "~1.0.0",
"postgres-date": "~1.0.4",
"postgres-interval": "^1.1.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/pgpass": {
"version": "1.0.5",
"resolved": "https://registry.npmmirror.com/pgpass/-/pgpass-1.0.5.tgz",
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
"dependencies": {
"split2": "^4.1.0"
}
},
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz", "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz",
...@@ -7695,6 +8133,47 @@ ...@@ -7695,6 +8133,47 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
} }
}, },
"node_modules/postgres-array": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/postgres-array/-/postgres-array-2.0.0.tgz",
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
"engines": {
"node": ">=4"
}
},
"node_modules/postgres-bytea": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-date": {
"version": "1.0.7",
"resolved": "https://registry.npmmirror.com/postgres-date/-/postgres-date-1.0.7.tgz",
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-interval": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/postgres-interval/-/postgres-interval-1.2.0.tgz",
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
"dependencies": {
"xtend": "^4.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-range": {
"version": "1.1.4",
"resolved": "https://registry.npmmirror.com/postgres-range/-/postgres-range-1.1.4.tgz",
"integrity": "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==",
"dev": true
},
"node_modules/pretty-bytes": { "node_modules/pretty-bytes": {
"version": "6.1.1", "version": "6.1.1",
"resolved": "https://registry.npmmirror.com/pretty-bytes/-/pretty-bytes-6.1.1.tgz", "resolved": "https://registry.npmmirror.com/pretty-bytes/-/pretty-bytes-6.1.1.tgz",
...@@ -7758,6 +8237,15 @@ ...@@ -7758,6 +8237,15 @@
"resolved": "https://registry.npmmirror.com/protocols/-/protocols-2.0.1.tgz", "resolved": "https://registry.npmmirror.com/protocols/-/protocols-2.0.1.tgz",
"integrity": "sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==" "integrity": "sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q=="
}, },
"node_modules/pump": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"dependencies": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"node_modules/queue-microtask": { "node_modules/queue-microtask": {
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz",
...@@ -8087,8 +8575,7 @@ ...@@ -8087,8 +8575,7 @@
"node_modules/safer-buffer": { "node_modules/safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
"optional": true
}, },
"node_modules/scule": { "node_modules/scule": {
"version": "1.3.0", "version": "1.3.0",
...@@ -8418,6 +8905,19 @@ ...@@ -8418,6 +8905,19 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/split-ca": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/split-ca/-/split-ca-1.0.1.tgz",
"integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ=="
},
"node_modules/split2": {
"version": "4.2.0",
"resolved": "https://registry.npmmirror.com/split2/-/split2-4.2.0.tgz",
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
"engines": {
"node": ">= 10.x"
}
},
"node_modules/splitpanes": { "node_modules/splitpanes": {
"version": "3.1.5", "version": "3.1.5",
"resolved": "https://registry.npmmirror.com/splitpanes/-/splitpanes-3.1.5.tgz", "resolved": "https://registry.npmmirror.com/splitpanes/-/splitpanes-3.1.5.tgz",
...@@ -8428,6 +8928,23 @@ ...@@ -8428,6 +8928,23 @@
"resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.1.3.tgz", "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.1.3.tgz",
"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="
}, },
"node_modules/ssh2": {
"version": "1.15.0",
"resolved": "https://registry.npmmirror.com/ssh2/-/ssh2-1.15.0.tgz",
"integrity": "sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==",
"hasInstallScript": true,
"dependencies": {
"asn1": "^0.2.6",
"bcrypt-pbkdf": "^1.0.2"
},
"engines": {
"node": ">=10.16.0"
},
"optionalDependencies": {
"cpu-features": "~0.0.9",
"nan": "^2.18.0"
}
},
"node_modules/ssri": { "node_modules/ssri": {
"version": "10.0.5", "version": "10.0.5",
"resolved": "https://registry.npmmirror.com/ssri/-/ssri-10.0.5.tgz", "resolved": "https://registry.npmmirror.com/ssri/-/ssri-10.0.5.tgz",
...@@ -8652,6 +9169,19 @@ ...@@ -8652,6 +9169,19 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/tar-fs": {
"version": "3.0.5",
"resolved": "https://registry.npmmirror.com/tar-fs/-/tar-fs-3.0.5.tgz",
"integrity": "sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==",
"dependencies": {
"pump": "^3.0.0",
"tar-stream": "^3.1.5"
},
"optionalDependencies": {
"bare-fs": "^2.1.1",
"bare-path": "^2.1.0"
}
},
"node_modules/tar-stream": { "node_modules/tar-stream": {
"version": "3.1.7", "version": "3.1.7",
"resolved": "https://registry.npmmirror.com/tar-stream/-/tar-stream-3.1.7.tgz", "resolved": "https://registry.npmmirror.com/tar-stream/-/tar-stream-3.1.7.tgz",
...@@ -8786,6 +9316,11 @@ ...@@ -8786,6 +9316,11 @@
"node": "^16.14.0 || >=18.0.0" "node": "^16.14.0 || >=18.0.0"
} }
}, },
"node_modules/tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmmirror.com/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="
},
"node_modules/type-fest": { "node_modules/type-fest": {
"version": "3.13.1", "version": "3.13.1",
"resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-3.13.1.tgz", "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-3.13.1.tgz",
...@@ -9808,6 +10343,14 @@ ...@@ -9808,6 +10343,14 @@
} }
} }
}, },
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"engines": {
"node": ">=0.4"
}
},
"node_modules/y18n": { "node_modules/y18n": {
"version": "5.0.8", "version": "5.0.8",
"resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz", "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",
......
...@@ -11,14 +11,20 @@ ...@@ -11,14 +11,20 @@
}, },
"dependencies": { "dependencies": {
"@pinia/nuxt": "^0.5.1", "@pinia/nuxt": "^0.5.1",
"dockerode": "^3.3.5",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"nuxt": "^3.11.1", "nuxt": "^3.11.1",
"pg": "^8.11.5",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"tar-fs": "^3.0.5",
"telnet-client": "^2.2.0", "telnet-client": "^2.2.0",
"vue": "^3.4.21", "vue": "^3.4.21",
"vue-router": "^4.3.0" "vue-router": "^4.3.0"
}, },
"devDependencies": { "devDependencies": {
"@types/lodash": "^4.17.0" "@types/dockerode": "^3.3.28",
"@types/lodash": "^4.17.0",
"@types/pg": "^8.11.4",
"@types/tar-fs": "^2.0.4"
} }
} }
...@@ -178,7 +178,7 @@ function handleInstall() { ...@@ -178,7 +178,7 @@ function handleInstall() {
logs.value = [] logs.value = []
loading.value = true loading.value = true
status.value = 'creating' status.value = 'creating'
$fetch('/api/installProject', { method: 'post' }) $fetch('/api/devops/v2/install', { method: 'post' })
.then(handleGetStatus, handleGetStatus) .then(handleGetStatus, handleGetStatus)
.finally(() => { .finally(() => {
loading.value = false loading.value = false
......
export default defineEventHandler((event) => {
let username = req.session.user as string || '1234'
let docker = createDockerFactory(username)
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
if (!container) {
// 不存在容器,则说明用户还没初始化
res.sendStatus(200)
return
}
let info = await container.inspect()
if (info.State.Running) {
// 正在运行,则认为正常
res.sendStatus(200)
} else {
LogUtil.print(username, `[progress] [[1;34mInfo[m] 服务器异常关闭,正在重启...... \n`)
await docker.startContainer({ container })
// 依次打开 nginx, 后端服务,vscode-web
try {
await docker.execContainerCommand({ container, cmd: 'lsof -i:80' })
} catch (err) {
try {
await docker.execContainerCommand({ container, cmd: 'nginx' })
LogUtil.print(username, `[progress] [[1;34mInfo[m] Nginx 重启成功...... \n`)
} catch (err) {
res.status(500).send(err)
}
}
try {
await docker.execContainerCommand({ container, cmd: 'lsof -i:8000' })
} catch (err) {
try {
docker.execContainerCommand({ container, cmd: 'code-server --bind-addr 127.0.0.1:8000 --auth none' })
LogUtil.print(username, `[progress] [[1;34mInfo[m] Vscode 重启成功...... \n`)
} catch (err) {
res.status(500).send(err)
}
}
try {
await docker.execContainerCommand({ container, cmd: 'pm2 delete backend' })
await docker.execContainerCommand({ container, cmd: 'pm2 delete gateway' })
} catch (err) {
}
await docker.execContainerCommand({ container, cmd: 'pm2 start --name gateway --no-autorestart java -- -Xms128m -Xmx128m -XX:+UseG1GC --add-opens java.base/sun.util.locale.provider=ALL-UNNAMED -jar logwire-gateway-starter.jar', dir: '/var/logwire-backend/build-output/gateway', quiet: true })
await docker.execContainerCommand({ container, cmd: `pm2 start --name backend --no-autorestart java -- -Xms128m -Xmx128m -XX:+UseG1GC -DVALIDATE_XML_ENABLED=false --add-opens java.base/sun.util.locale.provider=ALL-UNNAMED -jar logwire-backend-starter.jar`, dir: '/var/logwire-backend/build-output/backend', quiet: true })
LogUtil.print(username, `[progress] [[1;34mInfo[m] 服务重启成功...... \n`)
res.sendStatus(200)
}
})
\ No newline at end of file
import LogUtil from "~/server/utils/log"
import { copyAndCreateGatewayPropertiesV2InDocker, copyAndCreateServerPropertiesV2InDocker } from "~/server/utils/server"
export default defineEventHandler(async (event) => {
try {
let username = event.context.username
let docker = createDockerFactory(username)
let port = getUserConfig(username, 'node-port')
setUserConfig(username, 'status', 'compiling')
// 每次编译前把已有的 tenants_config 文件夹拷贝到一个地方,编译完成后,再拷贝回来
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
if (!container) {
throw new Error('没有创建容器,请先初始化容器')
}
await docker.startContainer({ container })
await docker.preStartSystem({ container, username, platform: 'v2'})
try {
await docker.execContainerCommand({ container, cmd: 'mv -bf build-output/backend/tenants_config/ ../tenants_config', dir: '/var/logwire-backend' })
} catch (err) {
}
LogUtil.print(username, `[progress] [[1;34mInfo[m] 编译中..... \n`)
await docker.execContainerCommand({ container, cmd: 'bash build-release.sh --module=logwire', dir: '/var/logwire-backend', quiet: true })
await docker.execContainerCommand({ container, cmd: 'bash build-release.sh --module=assemble', dir: '/var/logwire-backend', quiet: true })
try {
// 打包完成后,移动默认配置文件, 修改配置文件信息
await docker.execContainerCommand({ container, cmd: 'mv -f ../tenants_config build-output/backend/', dir: '/var/logwire-backend' })
await docker.execContainerCommand({ container, cmd: 'chmod 755 build-output/backend/transform-miniapp.sh', dir: '/var/logwire-backend' })
} catch (err) {
}
copyAndCreateServerPropertiesV2InDocker(username)
copyAndCreateGatewayPropertiesV2InDocker(username)
LogUtil.print(username, `[progress] [[1;34mInfo[m] 编译完成 \n`)
setUserConfig(username, 'status', 'created')
} catch (err) {
let username = event.context.username
LogUtil.print(username, '[error] [[1;31mError[m]] ' + (err instanceof Error ? err.message : JSON.stringify(err)))
setResponseStatus(event, 500)
}
})
\ No newline at end of file
import LogUtil from "~/server/utils/log"
export default defineEventHandler(async (event) => {
// 先判断服务是否启动,启动则关闭,然后调用新的命令启动 java 端
try {
let username = event.context.username
let docker = createDockerFactory(username)
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
if (!container) {
throw new Error('没有创建容器,请先初始化容器')
}
const status = getUserConfig(username, 'status')
const tenants = getUserConfig(username, 'tenants')
const port = getUserConfig(username, 'node-port')
const debug = getUserConfig(username, 'debug') || { host: '192.168.0.190' }
// 有可能本身就关闭了
try {
LogUtil.print(username, `[progress] [[1;34mLoading[m] 关闭已存在的 Nginx 服务 ...... \n`)
await docker.execContainerCommand({ container, cmd: 'nginx -s stop'})
} catch (err) {}
await LogUtil.run(username, '初始化调试功能', async () => {
let key = 'InstallSteps'
let steps: string[] = getUserConfig(username, key) || []
if (steps.includes('初始化调试功能')) return
try {
if (container) {
LogUtil.print(username, `[progress] [Loading] 下载 Nginx ...... \n`)
await docker.execContainerCommand({ container, cmd: 'curl -O https://nginx.org/download/nginx-1.18.0.tar.gz', dir: '/var/tmp' })
LogUtil.print(username, `[progress] [Loading] 解压 Nginx ...... \n`)
await docker.execContainerCommand({ container, cmd: 'tar -xvzf nginx-1.18.0.tar.gz', dir: '/var/tmp'})
LogUtil.print(username, `[progress] [Loading] 配置 Nginx ...... \n`)
await docker.execContainerCommand({ container, cmd: './configure --prefix=/usr/share/nginx/ --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-compat --with-debug --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_sub_module --with-stream=dynamic', dir: '/var/tmp/nginx-1.18.0' })
LogUtil.print(username, `[progress] [Loading] 编译 Nginx ...... \n`)
await docker.execContainerCommand({ container, cmd: 'make', dir: '/var/tmp/nginx-1.18.0'})
LogUtil.print(username, `[progress] [Loading] 替换 Nginx 文件 ...... \n`)
await docker.execContainerCommand({ container, cmd: 'cp /var/tmp/nginx-1.18.0/objs/nginx /usr/sbin/'})
}
} catch (err) {
console.log('err2', err)
throw err
}
})
LogUtil.print(username, `[progress] [[1;34mLoading[m] 替换 Nginx 配置 ...... \n`)
await copyAndCreateNginxConfInDocker(username)
LogUtil.print(username, `[progress] [[1;34mLoading[m] 启动 Nginx ...... \n`)
await docker.execContainerCommand({ container, cmd: 'nginx'})
LogUtil.print(username, `[progress] [[1;34mInfo[m] 程序启动中...... \n`)
try {
await docker.execContainerCommand({ container, cmd: 'pm2 delete backend' })
await docker.execContainerCommand({ container, cmd: 'pm2 delete gateway' })
} catch (err) {
}
await docker.execContainerCommand({ container, cmd: 'pm2 start --name gateway --no-autorestart java -- -Xms128m -Xmx128m -XX:+UseG1GC --add-opens java.base/sun.util.locale.provider=ALL-UNNAMED -jar logwire-gateway-starter.jar', dir: '/var/logwire-backend/build-output/gateway', quiet: true })
await docker.execContainerCommand({ container, cmd: 'pm2 start --name backend --no-autorestart java -- -Xms128m -Xmx128m -XX:+UseG1GC -DVALIDATE_XML_ENABLED=false --add-opens java.base/sun.util.locale.provider=ALL-UNNAMED -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=:6666,suspend=n -jar logwire-backend-starter.jar', dir: '/var/logwire-backend/build-output/backend', quiet: true })
LogUtil.print(username, `[progress] [[1;34mInfo[m] 程序运行中...... \n`)
LogUtil.print(username, `[progress] [[1;34mInfo[m] 请代理后端请求到 <strong>192.168.0.4:${port}</strong> 上 \n`)
LogUtil.print(username, `[progress] [[1;34mInfo[m] 请设置开发环境域名和端口号 <strong>${tenants.host}</strong> 上\n`)
LogUtil.print(username, `[progress] [[1;34mInfo[m] 调试程序已启动,请IP地址为 <strong>${debug?.host}</strong> 后端访问 <strong>192.168.0.4:${port}</strong> 进行调试\n`)
setUserConfig(username, 'status', 'running')
} catch (err) {
setResponseStatus(event, 500)
return err
}
})
\ No newline at end of file
import LogUtil from "~/server/utils/log"
export default defineEventHandler(async (event) => {
try {
let username = event.context.username
let docker = createDockerFactory(username)
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
let tenants = getUserConfig(username, 'tenants')
let port = getUserConfig(username, 'node-port')
LogUtil.printInfo(username, `程序初始化中......`)
if (!container) {
throw new Error('没有创建容器,请先初始化容器')
}
// 有可能本身就关闭了
try {
await docker.execContainerCommand({ container, cmd: 'nginx -s stop'})
} catch (err) {}
await docker.preStartSystem({ container, username, platform: 'v2'})
await copyAndCreateNginxConfInDocker(username)
await docker.execContainerCommand({ container, cmd: 'nginx'})
await docker.execContainerCommand({ container, cmd: 'pm2 start --name gateway --no-autorestart java -- -Xms128m -Xmx128m -XX:+UseG1GC --add-opens java.base/sun.util.locale.provider=ALL-UNNAMED -jar logwire-gateway-starter.jar', dir: '/var/logwire-backend/build-output/gateway', quiet: true })
await docker.execContainerCommand({ container, cmd: `pm2 start --name backend --no-autorestart java -- -Xms128m -Xmx128m -XX:+UseG1GC -DVALIDATE_XML_ENABLED=false --add-opens java.base/sun.util.locale.provider=ALL-UNNAMED -jar logwire-backend-starter.jar`, dir: '/var/logwire-backend/build-output/backend', quiet: true })
LogUtil.printInfo(username, `程序运行中......`)
LogUtil.printInfo(username, `请代理后端请求到 <strong>192.168.0.4:${port}</strong> 上`)
LogUtil.printInfo(username, `请设置开发环境域名和端口号 <strong>${tenants.host}</strong> 上`)
setUserConfig(username, 'status', 'running')
} catch (err) {
let username = event.context.username
LogUtil.printError(username, (err instanceof Error ? err.message : JSON.stringify(err)))
setResponseStatus(event, 500)
return err
}
})
\ No newline at end of file
import Dockerode from "dockerode"
import LogUtil from "~/server/utils/log"
import { createPgClient, executePgQuery } from "~/server/utils/postgres"
import { checkSshExists } from "~/server/utils/server"
import fs from 'fs'
import path from 'path'
function sleep (ms: number) {
return new Promise<void>((resolve, reject) => {
setTimeout(resolve, ms)
})
}
export default defineEventHandler(async (event) => {
try {
let username = event.context.username
let docker = createDockerFactory(username)
let host = docker.host
let port = getUserConfig(username, 'node-port').toString()
let container: Dockerode.Container = {} as Dockerode.Container
setUserConfig(username, 'status', 'creating')
// 必须要保证创建容器是首先执行
// node 容器根据 dev/prod 环境与否,决定在哪里创建,远程容器还是本机容器
await LogUtil.run(username, '创建 node 容器', async () => {
container = await docker.checkAndCreateContainer({
name: username + '.node',
img: 'node:16-bullseye',
exposedPorts: { '8080/tcp': {} },
portBindings: { '8080/tcp': [{ HostPort: port }] }
})
await docker.startContainer({ container })
})
await LogUtil.run(username, '检查 SSH Key', async () => {
let sshKey = await checkSshExists(username)
LogUtil.printWarning(username, `${username}@greaconsulting.com 用户 SSH key 为 <strong>${sshKey.replace(/\n/g, '')}</strong>, 若克隆仓库失败, 请检查是否放入 Gitlab SSH 设置中并重试\n`)
})
// 创建 postgres 不一定要在本机,即使是 dev 环境也可以联远程的容器
await LogUtil.run(username, '创建 postgres 容器', async () => {
if (getUserConfig(username, 'postgres.ip') !== '192.168.0.4') {
let postgres = await docker.checkAndCreateContainer({ name: 'postgres', img: 'postgres:12', env: ["POSTGRES_PASSWORD=postgres"], portBindings: { '5432/tcp': [{ HostPort: '30001' }] } })
let info = await postgres.inspect()
await docker.startContainer({ container: postgres })
if (info?.State.Status === 'created') {
info = await postgres.inspect()
await sleep(3000)
let client = await createPgClient({ host: host, port: 30001 })
await executePgQuery({ client, query: 'CREATE DATABASE logwirev2'})
await executePgQuery({ client, query: 'CREATE SCHEMA library' })
await client.end()
}
} else {
// 如果是服务器环境,检查是否存在对应的数据库表,根据不同用户创建不同的数据库
let postgres = await docker.checkContainer('postgres_12')
if (!postgres) {
throw new Error('没有创建服务器上的 Postgres 容器, 请先创建')
}
let client = await createPgClient({ host: host, port: 25556 })
let result = await executePgQuery({ client, query: "SELECT u.datname FROM pg_catalog.pg_database u where u.datname='" +username+ "';" })
if (result.rows.length === 0) {
await executePgQuery({ client, query: 'CREATE DATABASE ' + username})
client = await createPgClient({ host: host, port: 25556, database: username })
await executePgQuery({ client, query: 'CREATE SCHEMA library' })
await client.end()
}
}
})
await LogUtil.run(username, '创建 redis 容器', async () => {
if (getUserConfig(username, 'redis.ip') !== '192.168.0.4') {
let redis = await docker.checkAndCreateContainer({ name: 'redis', img: 'redis', portBindings: { '6379/tcp': [{ HostPort: '30002' }] } })
await docker.startContainer({ container: redis })
}
})
await LogUtil.run(username, '创建 zookeeper 容器', async () => {
if (getUserConfig(username, 'zookeeper.ip') !== '192.168.0.4') {
let zookeeper = await docker.checkAndCreateContainer({ name: 'zookeeper', img: 'zookeeper', portBindings: { '2181/tcp': [{ HostPort: '30003' }] } })
await docker.startContainer({ container: zookeeper })
}
})
// 检查本机 rocketmq 端口是否被占用,被占用说明已经有 rockqtmq 服务启动,这时候就不安装容器了
await LogUtil.run(username, '创建 rocketmq serv 容器', async () => {
if (getUserConfig(username, 'rocketmq.ip') !== '192.168.0.4') {
let rockermqsrv = await docker.checkAndCreateContainer({ name: 'rocketmq.srv', img: 'foxiswho/rocketmq:4.8.0', portBindings: { '9876/tcp': [{ 'HostPort': '9876' }] /** , '10909/tcp': [], '10911/tcp': [], '10912/tcp': []*/ }, env: ['JAVA_OPT_EXT=-Xms512M -Xmx512M -Xmn128m'] , cmd: ['bash', '-c', 'mqnamesrv'] })
await docker.startContainer({ container: rockermqsrv })
}
})
await LogUtil.run(username, '创建 rocketmq broker 容器', async () => {
if (getUserConfig(username, 'rocketmq.ip') !== '192.168.0.4') {
let rocketmqbroker = await docker.checkAndCreateContainer({ name: 'rocketmq.broker', img: 'foxiswho/rocketmq:4.8.0', portBindings: { '10909/tcp': [{ 'HostPort': '10909' }], '10911/tcp': [{ 'HostPort': '10911' }] /** , '9876/tcp': [],'10912/tcp': [] */ }, env: ['JAVA_OPT_EXT=-Xms512M -Xmx512M -Xmn128m'] })
await docker.startContainer({ container: rocketmqbroker })
let info = await rocketmqbroker.inspect()
if (info?.State.Status === 'created') {
let brokerText = fs.readFileSync(path.resolve('./server/files/v2/broker.conf'), { encoding: 'utf-8' })
await docker.execContainerCommand({ container: rocketmqbroker, cmd: 'mkdir /home/rocketmq/conf -p' })
await docker.execContainerCommand({ container: rocketmqbroker, cmd: 'touch /home/rocketmq/conf/broker.conf' })
await docker.writeFile({ container: rocketmqbroker, path: '/home/rocketmq/conf/broker.conf', text: "'" +brokerText + "'" })
await docker.execContainerCommand({ container: rocketmqbroker, cmd: 'mqbroker -c /home/rocketmq/conf/broker.conf' })
}
}
})
await LogUtil.run(username, '克隆仓库', async () => {
await docker.execContainerCommand({ container, cmd: ['git', 'config' ,'--global', 'core.sshCommand', 'ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'] })
await docker.execContainerCommand({ container, cmd: 'rm -rf /var/logwire-backend' })
await docker.execContainerCommand({ container, cmd: 'git clone ssh://git@gitlab.logwire.cn:13389/logwire2/logwire-backend.git', dir: '/var' })
})
// await LogUtil.run(username, '添加源', async () => {
// let txt = await docker.getFile({ container, path: '/etc/apt/sources.list' })
// if (txt && !txt.includes('deb http://security.debian.org/debian-security bullseye-security main')) {
// await docker.appendFile({ container, path: '/etc/apt/sources.list', text: '\'deb http://security.debian.org/debian-security bullseye-security main\'' })
// }
// })
await LogUtil.run(username, '更新源', async () => {
await docker.execContainerCommand({ container, cmd: 'apt-get update' })
})
await LogUtil.run(username, '安装 openjdk ', async () => {
await docker.putArchive({ container, tarPath: '/var/openlogic-openjdk-17.0.10+7-linux-x64.tar.gz', targetPath: '/var' })
await docker.execContainerCommand({ container, cmd: 'cp -r openlogic-openjdk-17.0.10+7-linux-x64/ java-17-openjdk', dir: '/var' })
await docker.execContainerCommand({ container, cmd: 'rm -rf openlogic-openjdk-17.0.10+7-linux-x64', dir: '/var' })
await docker.execContainerCommand({ container, cmd: 'ln -s /etc/alternatives/java /usr/bin/java' })
// await docker.execContainerCommand({ container, cmd: 'apt-get install -y openjdk-17-jdk' })
})
await LogUtil.run(username, '安装 maven ', async () => {
await docker.execContainerCommand({ container, cmd: 'apt-get install -y maven' })
})
await LogUtil.run(username, '修改 maven 源', async () => {
let mavenSettingXml = fs.readFileSync(path.resolve('./server/files/settings.xml'), { encoding: 'utf-8' })
await docker.writeFile({ container, path: '/etc/maven/settings.xml', text: '\'' + mavenSettingXml + '\'' })
let mavenSecurityXml = fs.readFileSync(path.resolve('./server/files/settings-security.xml'), { encoding: 'utf-8' })
try {
let xml = await docker.getFile({ container, path: '/root/.m2/settings-security.xml' })
} catch (err) {
await docker.execContainerCommand({ container, cmd: 'mkdir .m2', dir: '/root' })
await docker.writeFile({ container, path: '/root/.m2/settings-security.xml', text: '\'' + mavenSecurityXml + '\'' })
}
})
// wetty 不好用,去掉了,逃生通道留一个 VSCODE 即可
await LogUtil.run(username, '安装 code-server ', async () => {
try {
let result = await docker.execContainerCommand({ container, cmd: 'which code-server' })
} catch (err) {
await docker.execContainerCommand({ container, cmd: 'apt-get install -y build-essential pkg-config python3' })
await docker.execContainerCommand({ container, cmd: 'npm config set python python3' })
await docker.execContainerCommand({ container, cmd: 'yarn global add code-server@4.6.0' })
}
})
await LogUtil.run(username, '安装 nginx ', async () => {
await docker.execContainerCommand({ container, cmd: 'apt-get install -y nginx'})
})
await LogUtil.run(username, '安装 pm2 ', async () => {
await docker.execContainerCommand({ container, cmd: 'npm install --global pm2' })
})
await LogUtil.run(username, '安装 zip', async () => {
await docker.execContainerCommand({ container, cmd: 'apt-get install -y zip' })
})
await LogUtil.run(username, '配置 nginx ', async () => {
let nginxConfigText = fs.readFileSync(path.resolve('./server/files/nginx.conf'), { encoding: 'utf-8' })
await docker.writeFile({ container, path: '/etc/nginx/nginx.conf', text: '\'' + nginxConfigText + '\'' })
})
await LogUtil.run(username, '启动 code-server ', async () => {
try {
let result = await docker.execContainerCommand({ container, cmd: 'lsof -i:8000' })
} catch (err) {
try {
docker.execContainerCommand({ container, cmd: 'code-server --bind-addr 127.0.0.1:8000 --auth none' })
} catch (err) {
console.log('code-server err', err)
}
}
})
await LogUtil.run(username, '启动 nginx ', async () => {
try {
let result = await docker.execContainerCommand({ container, cmd: 'lsof -i:80' })
} catch (err) {
try {
await docker.execContainerCommand({ container, cmd: 'nginx' })
} catch (err) {
console.log('nginx err', err)
}
}
})
setUserConfig(username, 'status', 'created')
LogUtil.printSuccess(username, 'Installed')
} catch (err: any) {
let username = event.context.username
setUserConfig(username, 'status', 'null')
LogUtil.printError(username, (err instanceof Error ? err.message : JSON.stringify(err)))
setResponseStatus(event, 500)
}
})
\ No newline at end of file
import LogUtil from "~/server/utils/log"
export default defineEventHandler(async (event) => {
try {
let username = event.context.username
let docker = createDockerFactory(username)
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
if (!container) {
throw new Error('没有创建容器,请先初始化容器')
}
LogUtil.printInfo(username, `程序停止中......`)
await docker.execContainerCommand({ container, cmd: 'pm2 delete backend', quiet: true })
await docker.execContainerCommand({ container, cmd: 'pm2 delete gateway', quiet: true })
LogUtil.print(username, `程序已停止`)
setUserConfig(username, 'status', 'stopped')
} catch (err) {
setResponseStatus(event, 500)
return err
}
})
\ No newline at end of file
import LogUtil from "~/server/utils/log"
export default defineEventHandler(async (event) => {
try {
let username = event.context.username
let docker = createDockerFactory(username)
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
LogUtil.printInfo(username, '测试消息')
} catch (err) {
setResponseStatus(event, 500)
return err
}
})
\ No newline at end of file
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const session = await getSession(event, { password: '123456abcdefghijklmnopqrstuvwxyz'}) const username = event.context.username
const username = 'wyq' const configs = getUserAllConfigs(username)
console.log('session', session)
let configs = getUserAllConfigs(username)
return configs return configs
}) })
\ No newline at end of file
import { readJson, writeJson } from "../../utils" import { SESSION_PASSWORD, readJson, writeJson } from "../../utils"
import { getAvailableNodePort, getUserAllConfigs, getUserConfig } from "../../utils/server" import { getAvailableNodePort, getUserAllConfigs, getUserConfig } from "../../utils/server"
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
...@@ -17,7 +17,9 @@ export default defineEventHandler(async (event) => { ...@@ -17,7 +17,9 @@ export default defineEventHandler(async (event) => {
const userDataPath = `./server/data/${username}.json` const userDataPath = `./server/data/${username}.json`
writeJson(userDataPath, userSetting) writeJson(userDataPath, userSetting)
} }
return useSession(event, { name: username, password: '123456abcdefghijklmnopqrstuvwxyz' }) const session = await useSession(event, { password: SESSION_PASSWORD })
await session.update({ username })
return session.data
} catch (err) { } catch (err) {
console.log(err) console.log(err)
throw createError({ throw createError({
......
{
"postgres": {
"visible": false,
"username": "postgres",
"password": "postgres",
"ip": "192.168.0.4",
"port": "25556",
"database": {
"username": "wyq"
},
"schema": "library"
},
"redis": {
"visible": false,
"ip": "192.168.0.4",
"port": "25557"
},
"zookeeper": {
"visible": false,
"ip": "192.168.0.4",
"port": "2182"
},
"rocketmq": {
"visible": false,
"ip": "192.168.0.4",
"port": "9876"
},
"tenants": {
"visible": false,
"id": {
"username": "wyq"
},
"host": "a.test.com:23333,a.test.com:23334",
"database-schema": "library",
"primary-namespace": "library"
},
"postgresV1": {
"visible": false,
"username": "postgres",
"password": "postgres",
"ip": "192.168.0.4",
"port": "25556",
"database": "logwirev2",
"schema": "library"
},
"redisV1": {
"visible": false,
"ip": "192.168.0.4",
"port": "25557"
},
"tenantsV1": {
"visible": false,
"id": "wongyaqi",
"port": 8080
},
"debug": {
"host": "192.168.1.94"
},
"node-port": 30000
}
\ No newline at end of file
console
console
\ No newline at end of file
export default defineEventHandler((event) => { import { SESSION_PASSWORD } from "../utils"
// console.log(event)
declare module 'h3' {
interface H3EventContext {
username: string
}
}
export default defineEventHandler(async (event) => {
const session = await useSession(event, { password: SESSION_PASSWORD })
if (session.data && session.data.username) {
event.context.username = session.data.username
} else {
const nonAuthorizedUrls = ['/login', '/', '/api/user/login']
const url = event.node.req.url || '/'
if (!nonAuthorizedUrls.includes(url)) {
throw createError({
statusCode: 401,
message: '未授权'
})
}
}
}) })
\ No newline at end of file
/**
* TODOLIST
* * [ ] 根据操作系统的不同,获取到特定的 Docker 对象
* * [ ] 查询容器状态
* * [ ] 安装镜像容器
* * [ ] 在容器内操作命令, Docker API.exec
*/
import Dockerode from 'dockerode'
import { chmod, createReadStream, createWriteStream, mkdirSync, readFileSync, rmdirSync, rmSync } from 'fs'
import { resolve } from 'path'
import { getUserConfig, setUserConfig } from './server'
import { removeUnreadCharacter } from '.'
import LogUtil from './log'
import tar from 'tar-fs'
class Docker {
docker: Dockerode
username: string
env: string[]
host: string
constructor (host = 'host.docker.internal', username: string) {
this.host = host
this.docker = new Dockerode({ host, port: 2375 })
this.username = username
this.env = []
}
async checkContainer (name: string) {
let targetContainerName = name
let containers = await this.docker.listContainers({ filters: JSON.stringify({ status: ['created', 'restarting', 'running', 'removing', 'paused', 'exited', 'dead' ], name: [targetContainerName] }) })
if (containers.length) {
let container = await this.docker.getContainer(containers[0].Id)
return container
} else {
return
}
}
async checkAndCreateContainer ({ name, img, env, portBindings, cmd, exposedPorts }: { name: string, img: string, env?: string[], portBindings?: Record<string, any>, cmd?: string[], exposedPorts?: Record<string, any> }) {
let targetContainerName = 'logwire_backend_helper.' + name
let containers = await this.docker.listContainers({ filters: JSON.stringify({ status: ['created', 'restarting', 'running', 'removing', 'paused', 'exited', 'dead' ], name: [targetContainerName] }) })
if (containers.length) {
let container = await this.docker.getContainer(containers[0].Id)
return container
} else {
let images = await this.docker.listImages({ filters: `{ "reference": ["${img}"] }` })
if (images.length === 0) {
await new Promise<void>((resolve, reject) => {
this.docker.pull(img, (err: any, stream: any) => {
this.docker.modem.followProgress(stream, onFinished, onProgress);
function onFinished(err: any, output: any) {
if (!err) {
resolve()
} else {
reject(err)
}
}
function onProgress(event: any) {
}
})
})
}
let opt = {
name: targetContainerName,
"Tty": true,
Image: img,
Env: env,
Cmd: cmd,
ExposedPorts: exposedPorts,
HostConfig: {
PortBindings: portBindings || {}
}
}
return this.docker.createContainer(opt)
}
}
async startContainer({ container }: { container: Dockerode.Container }) {
let info = await container.inspect()
if (info.State.Running) {
} else {
await container.start()
}
return container
}
async execContainerCommand ({ container, cmd, dir, quiet }: {container: Dockerode.Container, cmd: string | string[], dir?: string, quiet?: boolean }) {
let exec = await container.exec({
Cmd: cmd instanceof Array ? cmd : cmd.split(' '),
AttachStdout: true,
AttachStderr: true,
WorkingDir: dir,
User: 'root',
Env: this.env
})
let result = await exec?.start({})
return new Promise<string>((resolve, reject) => {
let logs = ''
let data = ''
result?.on('data', chunk => {
let text = removeUnreadCharacter(chunk.toString())
!quiet && LogUtil.print(this.username, '[info] ' + text )
logs += text
data += text
logs = logs.substring(logs.length - 1200)
})
result?.on('error', error => {
logs += error.message + '\n' + error.stack
})
result?.on('end', async () => {
let info = await exec.inspect()
if (info?.ExitCode) {
reject({ command: cmd, message: 'ExitCode: ' + info.ExitCode, exitcode: info.ExitCode, logs })
} else {
resolve(data)
}
})
})
}
async putArchive ({ container, tarPath, targetPath }: { container: Dockerode.Container, tarPath: string, targetPath: string }) {
return new Promise((resolve) => {
container.putArchive(tarPath, { path: targetPath }).then(resolve)
})
}
async writeFile ({ container, path, text }: { container: Dockerode.Container, path: string, text: string }) {
await this.execContainerCommand({ container, cmd: ['bash', '-c', 'echo ' + text + ' > ' + path] })
}
async appendFile({ container, path, text }: { container: Dockerode.Container, path: string, text: string }) {
await this.execContainerCommand({ container, cmd: ['bash', '-c', 'echo ' + text + ' >> ' + path] })
}
async getFile ({ container, path }: { container: Dockerode.Container, path: string }): Promise<string> {
return new Promise(async (resolve, reject) => {
try {
let readstream = await container.getArchive({ path })
let tempFilePath = './temp_' + Math.random() + '.tar'
let tempUnpackFolder = tempFilePath.replace('.tar', '')
let tempUnpackFile = tempUnpackFolder + '/' + path.replace(/.*\//, '')
mkdirSync(tempUnpackFolder)
let writestream = createWriteStream(tempFilePath)
readstream.pipe(writestream).on('finish', () => {
createReadStream(tempFilePath).pipe(tar.extract(tempUnpackFolder)).on('finish', () => {
let content = readFileSync(tempUnpackFile, { encoding: 'utf-8' })
rmSync(tempFilePath)
rmSync(tempUnpackFolder, { recursive: true, force: true })
resolve(content)
})
})
} catch (err) {
reject(err)
}
})
}
async switchJavaVersion ({ container, username, version }: { container: Dockerode.Container, username: string, version: 17 | 8}) {
LogUtil.printInfo(username, '更换 Java 环境')
await this.execContainerCommand({ container, cmd: 'rm /etc/alternatives/java'})
if (version === 8) {
await this.execContainerCommand({ container, cmd: 'ln -s /var/java-8-openjdk/bin/java /etc/alternatives/java'})
} else {
await this.execContainerCommand({ container, cmd: 'ln -s /var/java-17-openjdk/bin/java /etc/alternatives/java'})
}
}
async preStartSystem ({ container, username, platform }: { container: Dockerode.Container, username: string, platform: 'v1' | 'v2' }) {
try {
await this.execContainerCommand({ container, cmd: 'rm /root/.pm2/logs/backend-out.log'})
await this.execContainerCommand({ container, cmd: 'rm /root/.pm2/logs/backend-error.log'})
await this.execContainerCommand({ container, cmd: 'rm /root/.pm2/logs/gateway-out.log'})
await this.execContainerCommand({ container, cmd: 'rm /root/.pm2/logs/gateway-out.log'})
await this.execContainerCommand({ container, cmd: 'rm /root/.pm2/logs/platform-out.log'})
await this.execContainerCommand({ container, cmd: 'rm /root/.pm2/logs/platform-out.log'})
} catch (err) {
}
try { await this.execContainerCommand({ container, cmd: 'pm2 delete backend' }) } catch (err) {}
try { await this.execContainerCommand({ container, cmd: 'pm2 delete gateway' }) } catch (err) {}
try { await this.execContainerCommand({ container, cmd: 'pm2 delete platform' }) } catch (err) {}
if (platform === 'v1') {
await this.switchJavaVersion({ container, username, version: 8 })
const statusV2 = getUserConfig(username, 'status')
if (statusV2) setUserConfig(username, 'status', 'created') // 如果系统在运行,回退到已创建的状态
} else {
await this.switchJavaVersion({ container, username, version: 17 })
const statusV1 = getUserConfig(username, 'v1-status')
if (statusV1) setUserConfig(username, 'v1-status', 'created') // 如果系统在运行,回退到已创建的状态
}
}
}
class DevDocker extends Docker {
constructor (username: string) {
super('localhost', username)
}
}
class ProductionDocker extends Docker {
constructor (username: string) {
super('192.168.0.4', username)
}
}
export function createDockerFactory (username: string) {
if (process.env.NODE_ENV?.trim() === 'production') {
return new ProductionDocker(username)
} else {
return new DevDocker(username)
}
}
\ No newline at end of file
...@@ -17,3 +17,10 @@ export function writeJson(path: string, obj: Record<string, string>) { ...@@ -17,3 +17,10 @@ export function writeJson(path: string, obj: Record<string, string>) {
JSON.stringify(obj, null, 2) JSON.stringify(obj, null, 2)
) )
} }
export function removeUnreadCharacter (str: string) {
// return str.replace(/[\u0000-\u0009]/g, '').replace(/[\u000B-\u001F]/g, '')
return str.replace(/\u0001\u0000\u0000\u0000\u0000\u0000\u0000\n/g, '').replace(/[\u0000-\u0009]/g, '').replace(/[\u000B-\u001F]/g, '')
}
export const SESSION_PASSWORD = '80d42cfb-1cd2-462c-8f17-e3237d9027e9'
\ No newline at end of file
/**
* Logs 模块
* 使用 LogsModule 包裹后,为某个安装流程增加日志处理,并且对外还是返回 Promise 对象
*/
import { setUserConfig, getUserConfig } from "./server"
// import { Socket } from "socket.io/dist/socket";
// let websocket: Map<string, Socket> = new Map()
// export function setWebsocketIo (username: string, io: Socket) {
// websocket.set(username, io)
// }
// export function getWebsocketIo (username: string) {
// return websocket.get(username) as Socket
// }
export default class LogUtil {
// 根据 log 判断用户是否已经执行过
static async run (username: string, log: string, cb: () => Promise<void>) {
let key = 'InstallSteps'
let steps: string[] = getUserConfig(username, key) || []
// let socket = getWebsocketIo(username)
// socket.emit('Log', '[progress] [1m[Loading][m ' + log + '中...\n')
await cb()
steps.push(log)
setUserConfig(username, key, steps)
// socket.emit('Log', '[progress] [1m[Info][m ' + log + '完成\n')
}
static async print(username: string, log: string) {
// let socket = getWebsocketIo(username)
// socket.emit('Log', removeUnreadCharacter(log))
}
static async printInfo(username: string, log: string) {
// let socket = getWebsocketIo(username)
// socket.emit('Log', removeUnreadCharacter(`[progress] [[1;34mInfo[m] ${log} \n`))
}
static async printError(username: string, log: string) {
// let socket = getWebsocketIo(username)
// socket.emit('Log', removeUnreadCharacter(`[error] [[1;31mError[m] ${log} \n`))
}
static async printSuccess(username: string, log: string) {
// let socket = getWebsocketIo(username)
// socket.emit('Log', removeUnreadCharacter(`[progress] [[1;31mSuccess[m] ${log} \n`))
}
static async printWarning (username: string, log: string) {
}
}
import pg from 'pg'
export async function createPgClient ({ host, port, database }: { host: string, port: number, database?: string }) {
let client = new pg.Client({
user: 'postgres',
password: 'postgres',
database,
host,
port
})
await client.connect()
return client
}
export function executePgQuery ({ client, query }: { client: pg.Client, query: string }) {
return client.query(query)
}
...@@ -41,3 +41,162 @@ export function getUserAllConfigs (username: string): Record<string, string> { ...@@ -41,3 +41,162 @@ export function getUserAllConfigs (username: string): Record<string, string> {
let json = JSON.parse(jsonStr) || {} let json = JSON.parse(jsonStr) || {}
return json return json
} }
export function setUserConfig (username: string, config: string, value: any) {
let jsonPath = path.resolve(__dirname, './database/' + username + '.json')
let jsonStr = fs.readFileSync(jsonPath, { encoding: 'utf-8' })
let json = JSON.parse(jsonStr) || {}
let newJson = lodash.set(json, config, value)
fs.writeFileSync(jsonPath, JSON.stringify(newJson, null, 2), { encoding: 'utf-8' })
}
export async function checkSshExists (username: string) {
let gitEmail = username + '@greaconsulting.com'
let docker = createDockerFactory(username)
// 创建 node 容器
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
if (!container) {
throw new Error('没有创建容器,请先初始化容器')
}
await docker.startContainer({ container })
let sshKey = ''
try {
sshKey = await docker.getFile({ container, path: '/root/.ssh/id_rsa.pub' })
} catch (err) {
await docker.execContainerCommand({ container, cmd: 'rm -rf /root/.ssh' })
await docker.execContainerCommand({ container, cmd: `ssh-keygen -f /root/.ssh/id_rsa -t ed25519 -C "${gitEmail}"` })
sshKey = await docker.getFile({ container, path: '/root/.ssh/id_rsa.pub' })
}
return sshKey
}
export async function copyAndCreateServerPropertiesV2InDocker (username: string) {
let docker = createDockerFactory(username)
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
if (!container) {
throw new Error('没有创建容器,请先初始化容器')
}
let backendText = fs.readFileSync(path.resolve(__dirname, './files/application-server.properties'), { encoding: 'utf-8' })
const userDefaultSettingStr = fs.readFileSync(path.resolve(__dirname, './files/default-user-setting.json'), { encoding: 'utf-8'})
const userConfigsStr = fs.readFileSync(path.resolve(__dirname, './database/' + username + '.json'), { encoding: 'utf-8'})
const userDefaultSetting = JSON.parse(userDefaultSettingStr)
const userConfigs = JSON.parse(userConfigsStr)
const fn = (f: (obj: Record<string, any>) => string) => f(userConfigs) || f(userDefaultSetting)
// postgres config
backendText = backendText.replace(/spring.datasource.url=(.*?)\n/, `spring.datasource.url=jdbc:postgresql://${fn(o => o.postgres?.ip)}:${fn(o => o.postgres?.port)}/${fn(o => o.postgres?.database)}\n`)
backendText = backendText.replace(/spring.datasource.username=(.*?)\n/, `spring.datasource.username=${fn(o => o.postgres?.username)}\n`)
backendText = backendText.replace(/spring.datasource.password=(.*?)\n/, `spring.datasource.password=${fn(o => o.postgres?.password)}\n`)
// redis config
backendText = backendText.replace(/spring.redis.port=(.*?)\n/, `spring.redis.port=${fn(o => o.redis?.port)}\n`)
backendText = backendText.replace(/spring.redis.host=(.*?)\n/, `spring.redis.host=${fn(o => o.redis?.ip)}\n`)
// zookeeper config
backendText = backendText.replace(/logwire.register-center-server-list=(.*?)\n/, `logwire.register-center-server-list=${fn(o => o.zookeeper?.ip)}:${fn(o => o.zookeeper?.port)}\n`)
// rocketmq config
backendText = backendText.replace(/logwire.mq.name-srv-address=(.*?)\n/, `logwire.mq.name-srv-address=${fn(o => o.rocketmq?.ip)}:${fn(o => o.rocketmq?.port)}\n`)
// tenants config
backendText = backendText.replace(/logwire.tenants\[0\].id=(.*?)\n/, `logwire.tenants[0].id=${fn(o => o.tenants?.id)}\n`)
backendText = backendText.replace(/logwire.tenants\[0\].host=(.*?)\n/, `logwire.tenants[0].host=${fn(o => o.tenants?.host)}\n`)
backendText = backendText.replace(/logwire.tenants\[0\].database-schema=(.*?)\n/, `logwire.tenants[0].database-schema=${fn(o => o.tenants?.["database-schema"])}\n`)
backendText = backendText.replace(/logwire.tenants\[0\].primary-namespace=(.*?)\n/, `logwire.tenants[0].primary-namespace=${fn(o => o.tenants?.["primary-namespace"])}\n`)
// user personal config
const customServerProperties: { key: string, value: string }[] = userConfigs.serverProperties || []
customServerProperties.forEach(item => {
const RegExpKey = item.key.replaceAll('[', '\\[').replaceAll(']', '\\]').replace('.', '\\.')
if (backendText.match(new RegExp(`#${RegExpKey}=(.*?)\n`))) {
backendText = backendText.replace(new RegExp(`#${RegExpKey}=(.*?)\n`), `${item.key}=${item.value}\n`)
} else if (backendText.match(new RegExp(`${RegExpKey}=(.*?)\n`))) {
backendText = backendText.replace(new RegExp(`${RegExpKey}=(.*?)\n`), `${item.key}=${item.value}\n`)
} else { // 如果完全没有匹配到内容,则直接追加到最后
backendText += `\n${item.key}=${item.value}\n`
}
})
await docker.writeFile({ container, path: '/var/logwire-backend/build-output/backend/config/application-server.properties', text: '\'' + backendText.replace(/'/g, '"') + '\'' })
}
export async function copyAndCreateGatewayPropertiesV2InDocker (username: string) {
let docker = createDockerFactory(username)
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
if (!container) {
throw new Error('没有创建容器,请先初始化容器')
}
let gatewayText = fs.readFileSync(path.resolve(__dirname, './files/application-gateway.properties'), { encoding: 'utf-8' })
const userDefaultSettingStr = fs.readFileSync(path.resolve(__dirname, './files/default-user-setting.json'), { encoding: 'utf-8'})
const userConfigsStr = fs.readFileSync(path.resolve(__dirname, './database/' + username + '.json'), { encoding: 'utf-8'})
const userDefaultSetting = JSON.parse(userDefaultSettingStr)
const userConfigs = JSON.parse(userConfigsStr)
const fn = (f: (obj: Record<string, any>) => string) => f(userConfigs) || f(userDefaultSetting)
// zookeeper config
gatewayText = gatewayText.replace(/gateway.register-center-server-list=(.*?)\n/, `gateway.register-center-server-list=${fn(o => o.zookeeper?.ip)}:${fn(o => o.zookeeper?.port)}\n`)
// rocketmq config
gatewayText = gatewayText.replace(/gateway.mq.name-srv-address=(.*?)\n/, `gateway.mq.name-srv-address=${fn(o => o.rocketmq?.ip)}:${fn(o => o.rocketmq?.port)}`)
await docker.writeFile({ container, path: '/var/logwire-backend/build-output/gateway/config/application-gateway.properties', text: '\'' + gatewayText.replace(/'/g, '"') + '\'' })
}
export async function copyAndCreateServerPropertiesV1InDocker (username: string) {
let docker = createDockerFactory(username)
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
if (!container) {
throw new Error('没有创建容器,请先初始化容器')
}
let backendText = fs.readFileSync(path.resolve(__dirname, './files/v1/application.properties'), { encoding: 'utf-8' })
const userDefaultSettingStr = fs.readFileSync(path.resolve(__dirname, './files/default-user-setting.json'), { encoding: 'utf-8'})
const userConfigsStr = fs.readFileSync(path.resolve(__dirname, './database/' + username + '.json'), { encoding: 'utf-8'})
const userDefaultSetting = JSON.parse(userDefaultSettingStr)
const userConfigs = JSON.parse(userConfigsStr)
const fn = (f: (obj: Record<string, any>) => string) => f(userConfigs) || f(userDefaultSetting)
// postgres config
backendText = backendText.replace(/spring.datasource.url=(.*?)\n/, `spring.datasource.url=jdbc:postgresql://${fn(o => o.postgresV1?.ip)}:${fn(o => o.postgresV1?.port)}/${fn(o => o.postgresV1?.database)}\n`)
backendText = backendText.replace(/spring.datasource.username=(.*?)\n/, `spring.datasource.username=${fn(o => o.postgresV1?.username)}\n`)
backendText = backendText.replace(/spring.datasource.password=(.*?)\n/, `spring.datasource.password=${fn(o => o.postgresV1?.password)}\n`)
// redis config
backendText = backendText.replace(/spring.redis.port=(.*?)\n/, `spring.redis.port=${fn(o => o.redisV1?.port)}\n`)
backendText = backendText.replace(/spring.redis.host=(.*?)\n/, `spring.redis.host=${fn(o => o.redisV1?.ip)}\n`)
// user personal config
const customServerProperties: { key: string, value: string }[] = userConfigs.serverPropertiesV1 || []
customServerProperties.forEach(item => {
const RegExpKey = item.key.replaceAll('[', '\\[').replaceAll(']', '\\]').replace('.', '\\.')
if (backendText.match(new RegExp(`#${RegExpKey}=(.*?)\n`))) {
backendText = backendText.replace(new RegExp(`#${RegExpKey}=(.*?)\n`), `${item.key}=${item.value}\n`)
} else if (backendText.match(new RegExp(`${RegExpKey}=(.*?)\n`))) {
backendText = backendText.replace(new RegExp(`${RegExpKey}=(.*?)\n`), `${item.key}=${item.value}\n`)
} else { // 如果完全没有匹配到内容,则直接追加到最后
backendText += `\n${item.key}=${item.value}\n`
}
})
await docker.writeFile({ container, path: '/var/logwire-platform/dist/config/application.properties', text: '\'' + backendText.replace(/'/g, '"') + '\'' })
}
export async function copyAndCreateNginxConfInDocker (username: string) {
let docker = createDockerFactory(username)
let container = await docker.checkContainer('logwire_backend_helper.' + username + '.node')
if (!container) {
throw new Error('没有创建容器,请先初始化容器')
}
const userDefaultSettingStr = fs.readFileSync(path.resolve(__dirname, './files/default-user-setting.json'), { encoding: 'utf-8'})
const userConfigsStr = fs.readFileSync(path.resolve(__dirname, './database/' + username + '.json'), { encoding: 'utf-8'})
const userDefaultSetting = JSON.parse(userDefaultSettingStr)
const userConfigs = JSON.parse(userConfigsStr)
const nginxConfigStr = fs.readFileSync(path.resolve(__dirname, './files/nginx.conf'), { encoding: 'utf-8' })
// 修改调试端口的 NGINX 配置
const replacedNginxConfig = nginxConfigStr.replace(/192.168.1.94/, userConfigs?.debug?.host || userDefaultSetting.debug.host)
await docker.writeFile({ container, path: '/etc/nginx/nginx.conf', text: '\'' + replacedNginxConfig + '\'' })
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment