mirror of
https://github.com/louislam/dockge.git
synced 2026-05-22 06:22:17 +00:00
Compare commits
6 Commits
s
...
build-wind
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4400ba5dd6 | ||
|
|
2b428e139c | ||
|
|
587d2dcaca | ||
|
|
5f1f3593fd | ||
|
|
e570374a48 | ||
|
|
316c566c76 |
@@ -194,39 +194,6 @@ export class DockgeServer {
|
|||||||
// Create Socket.io
|
// Create Socket.io
|
||||||
this.io = new socketIO.Server(this.httpServer, {
|
this.io = new socketIO.Server(this.httpServer, {
|
||||||
cors,
|
cors,
|
||||||
allowRequest: (req, callback) => {
|
|
||||||
let isOriginValid = true;
|
|
||||||
const bypass = isDev;
|
|
||||||
|
|
||||||
if (!bypass) {
|
|
||||||
let host = req.headers.host;
|
|
||||||
|
|
||||||
// If this is set, it means the request is from the browser
|
|
||||||
let origin = req.headers.origin;
|
|
||||||
|
|
||||||
// If this is from the browser, check if the origin is allowed
|
|
||||||
if (origin) {
|
|
||||||
try {
|
|
||||||
let originURL = new URL(origin);
|
|
||||||
|
|
||||||
if (host !== originURL.host) {
|
|
||||||
isOriginValid = false;
|
|
||||||
log.error("auth", `Origin (${origin}) does not match host (${host}), IP: ${req.socket.remoteAddress}`);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// Invalid origin url, probably not from browser
|
|
||||||
isOriginValid = false;
|
|
||||||
log.error("auth", `Invalid origin url (${origin}), IP: ${req.socket.remoteAddress}`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.info("auth", `Origin is not set, IP: ${req.socket.remoteAddress}`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.debug("auth", "Origin check is bypassed");
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, isOriginValid);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.io.on("connection", async (socket: Socket) => {
|
this.io.on("connection", async (socket: Socket) => {
|
||||||
@@ -611,35 +578,4 @@ export class DockgeServer {
|
|||||||
finalFunction() {
|
finalFunction() {
|
||||||
log.info("server", "Graceful shutdown successful!");
|
log.info("server", "Graceful shutdown successful!");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Force connected sockets of a user to refresh and disconnect.
|
|
||||||
* Used for resetting password.
|
|
||||||
* @param {string} userID
|
|
||||||
* @param {string?} currentSocketID
|
|
||||||
*/
|
|
||||||
disconnectAllSocketClients(userID: number, currentSocketID? : string) {
|
|
||||||
for (const rawSocket of this.io.sockets.sockets.values()) {
|
|
||||||
let socket = rawSocket as DockgeSocket;
|
|
||||||
if (socket.userID === userID && socket.id !== currentSocketID) {
|
|
||||||
try {
|
|
||||||
socket.emit("refresh");
|
|
||||||
socket.disconnect();
|
|
||||||
} catch (e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isSSL() {
|
|
||||||
return this.config.sslKey && this.config.sslCert;
|
|
||||||
}
|
|
||||||
|
|
||||||
getLocalWebSocketURL() {
|
|
||||||
const protocol = this.isSSL() ? "wss" : "ws";
|
|
||||||
const host = this.config.hostname || "localhost";
|
|
||||||
return `${protocol}://${host}:${this.config.port}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -211,8 +211,6 @@ export class MainSocketHandler extends SocketHandler {
|
|||||||
let user = await doubleCheckPassword(socket, password.currentPassword);
|
let user = await doubleCheckPassword(socket, password.currentPassword);
|
||||||
await user.resetPassword(password.newPassword);
|
await user.resetPassword(password.newPassword);
|
||||||
|
|
||||||
server.disconnectAllSocketClients(user.id, socket.id);
|
|
||||||
|
|
||||||
callback({
|
callback({
|
||||||
ok: true,
|
ok: true,
|
||||||
msg: "Password has been updated successfully.",
|
msg: "Password has been updated successfully.",
|
||||||
@@ -282,18 +280,6 @@ export class MainSocketHandler extends SocketHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Disconnect all other socket clients of the user
|
|
||||||
socket.on("disconnectOtherSocketClients", async () => {
|
|
||||||
try {
|
|
||||||
checkLogin(socket);
|
|
||||||
server.disconnectAllSocketClients(socket.userID, socket.id);
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof Error) {
|
|
||||||
log.warn("disconnectOtherSocketClients", e.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async login(username : string, password : string) : Promise<User | null> {
|
async login(username : string, password : string) : Promise<User | null> {
|
||||||
|
|||||||
@@ -21,11 +21,6 @@ export interface LooseObject {
|
|||||||
[key: string]: any
|
[key: string]: any
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BaseRes {
|
|
||||||
ok: boolean;
|
|
||||||
msg?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
let randomBytes : (numBytes: number) => Uint8Array;
|
let randomBytes : (numBytes: number) => Uint8Array;
|
||||||
initRandomBytes();
|
initRandomBytes();
|
||||||
|
|
||||||
|
|||||||
92
extra/build-windows.ts
Normal file
92
extra/build-windows.ts
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import fsAsync from "fs/promises";
|
||||||
|
import unzipper from "unzipper";
|
||||||
|
import stream from "node:stream";
|
||||||
|
import { fileExists } from "../backend/util-server";
|
||||||
|
|
||||||
|
const version = process.env.VERSION;
|
||||||
|
|
||||||
|
if (!version) {
|
||||||
|
console.error("VERSION env not set");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const output = `./private/build/dockgen-${version}-win-x64.zip`;
|
||||||
|
|
||||||
|
if (await fileExists(output)) {
|
||||||
|
console.error(`${output} already exists`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Building ${output}`);
|
||||||
|
|
||||||
|
const nodeVersion = "18.17.1";
|
||||||
|
const buildPath = "./private/build/windows";
|
||||||
|
const nodePath = `${buildPath}/node`;
|
||||||
|
const nodeTempPath = `${buildPath}/node-v${nodeVersion}-win-x64`;
|
||||||
|
const corePath = `${buildPath}/core`;
|
||||||
|
|
||||||
|
// Clear
|
||||||
|
await fsAsync.rm(`${buildPath}/dockge-${version}`, {
|
||||||
|
recursive: true,
|
||||||
|
force: true
|
||||||
|
});
|
||||||
|
|
||||||
|
await fsAsync.rm(corePath, {
|
||||||
|
recursive: true,
|
||||||
|
force: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// mkdir
|
||||||
|
await fsAsync.mkdir(buildPath, {
|
||||||
|
recursive: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Download Node.js if not exists
|
||||||
|
// Download,pipe to unzipper and extract to nodePath
|
||||||
|
if (!await fileExists(nodePath)) {
|
||||||
|
console.log(`Downloading Node.js ${nodeVersion}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await download(`https://nodejs.org/dist/v${nodeVersion}/node-v${nodeVersion}-win-x64.zip`);
|
||||||
|
// Rename folder
|
||||||
|
await fsAsync.rename(nodeTempPath, nodePath);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
console.error(e.message);
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(`Node.js ${nodeVersion} already exists, skipping download`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download Dockge from GitHub
|
||||||
|
console.log(`Downloading Dockge ${version} from GitHub`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await download(`https://github.com/louislam/dockge/archive/refs/tags/${version}.zip`);
|
||||||
|
// Rename folder
|
||||||
|
await fsAsync.rename(`${buildPath}/dockge-${version}`, corePath);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
console.error(e.message);
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function download(url : string) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
fetch(url).then((res) => {
|
||||||
|
if (res.body) {
|
||||||
|
// @ts-ignore
|
||||||
|
stream.Readable.fromWeb(res.body)
|
||||||
|
.pipe(unzipper.Extract({
|
||||||
|
path: buildPath,
|
||||||
|
}))
|
||||||
|
.on("close", resolve);
|
||||||
|
} else {
|
||||||
|
reject(new Error(`Unable to download ${url}`));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -4,8 +4,6 @@ import readline from "readline";
|
|||||||
import { User } from "../backend/models/user";
|
import { User } from "../backend/models/user";
|
||||||
import { DockgeServer } from "../backend/dockge-server";
|
import { DockgeServer } from "../backend/dockge-server";
|
||||||
import { log } from "../backend/log";
|
import { log } from "../backend/log";
|
||||||
import { io } from "socket.io-client";
|
|
||||||
import { BaseRes } from "../backend/util-common";
|
|
||||||
|
|
||||||
console.log("== Dockge Reset Password Tool ==");
|
console.log("== Dockge Reset Password Tool ==");
|
||||||
|
|
||||||
@@ -14,10 +12,11 @@ const rl = readline.createInterface({
|
|||||||
output: process.stdout
|
output: process.stdout
|
||||||
});
|
});
|
||||||
|
|
||||||
const server = new DockgeServer();
|
|
||||||
|
|
||||||
export const main = async () => {
|
export const main = async () => {
|
||||||
|
const server = new DockgeServer();
|
||||||
|
|
||||||
// Check if
|
// Check if
|
||||||
|
|
||||||
console.log("Connecting the database");
|
console.log("Connecting the database");
|
||||||
try {
|
try {
|
||||||
await Database.init(server);
|
await Database.init(server);
|
||||||
@@ -48,16 +47,12 @@ export const main = async () => {
|
|||||||
// Reset all sessions by reset jwt secret
|
// Reset all sessions by reset jwt secret
|
||||||
await server.initJWTSecret();
|
await server.initJWTSecret();
|
||||||
|
|
||||||
console.log("Password reset successfully.");
|
|
||||||
|
|
||||||
// Disconnect all other socket clients of the user
|
|
||||||
await disconnectAllSocketClients(user.username, password);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
console.log("Passwords do not match, please try again.");
|
console.log("Passwords do not match, please try again.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
console.log("Password reset successfully.");
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
@@ -84,47 +79,6 @@ function question(question : string) : Promise<string> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function disconnectAllSocketClients(username : string, password : string) : Promise<void> {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
const url = server.getLocalWebSocketURL();
|
|
||||||
|
|
||||||
console.log("Connecting to " + url + " to disconnect all other socket clients");
|
|
||||||
|
|
||||||
// Disconnect all socket connections
|
|
||||||
const socket = io(url, {
|
|
||||||
transports: [ "websocket" ],
|
|
||||||
reconnection: false,
|
|
||||||
timeout: 5000,
|
|
||||||
});
|
|
||||||
socket.on("connect", () => {
|
|
||||||
socket.emit("login", {
|
|
||||||
username,
|
|
||||||
password,
|
|
||||||
}, (res : BaseRes) => {
|
|
||||||
if (res.ok) {
|
|
||||||
console.log("Logged in.");
|
|
||||||
socket.emit("disconnectOtherSocketClients");
|
|
||||||
} else {
|
|
||||||
console.warn("Login failed.");
|
|
||||||
console.warn("Please restart the server to disconnect all sessions.");
|
|
||||||
}
|
|
||||||
socket.close();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on("connect_error", function () {
|
|
||||||
// The localWebSocketURL is not guaranteed to be working for some complicated Uptime Kuma setup
|
|
||||||
// Ask the user to restart the server manually
|
|
||||||
console.warn("Failed to connect to " + url);
|
|
||||||
console.warn("Please restart the server to disconnect all sessions manually.");
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
socket.on("disconnect", () => {
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!process.env.TEST_BACKEND) {
|
if (!process.env.TEST_BACKEND) {
|
||||||
main();
|
main();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -202,10 +202,6 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("refresh", () => {
|
|
||||||
location.reload();
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -299,12 +299,12 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
|
|
||||||
urls() {
|
urls() {
|
||||||
if (!this.jsonConfig["x-dockge"] || !this.jsonConfig["x-dockge"].urls || !Array.isArray(this.jsonConfig["x-dockge"].urls)) {
|
if (!this.envsubstJSONConfig["x-dockge"] || !this.envsubstJSONConfig["x-dockge"].urls || !Array.isArray(this.envsubstJSONConfig["x-dockge"].urls)) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
let urls = [];
|
let urls = [];
|
||||||
for (const url of this.jsonConfig["x-dockge"].urls) {
|
for (const url of this.envsubstJSONConfig["x-dockge"].urls) {
|
||||||
let display;
|
let display;
|
||||||
try {
|
try {
|
||||||
let obj = new URL(url);
|
let obj = new URL(url);
|
||||||
|
|||||||
23
package.json
23
package.json
@@ -10,13 +10,15 @@
|
|||||||
"lint": "eslint \"**/*.{ts,vue}\"",
|
"lint": "eslint \"**/*.{ts,vue}\"",
|
||||||
"check-ts": "tsc --noEmit",
|
"check-ts": "tsc --noEmit",
|
||||||
"start": "tsx ./backend/index.ts",
|
"start": "tsx ./backend/index.ts",
|
||||||
|
"dev": "concurrently -k -r \"wait-on tcp:5000 && pnpm run dev:backend \" \"pnpm run dev:frontend\"",
|
||||||
"dev:backend": "cross-env NODE_ENV=development tsx watch --inspect ./backend/index.ts",
|
"dev:backend": "cross-env NODE_ENV=development tsx watch --inspect ./backend/index.ts",
|
||||||
"dev:frontend": "cross-env NODE_ENV=development vite --host --config ./frontend/vite.config.ts",
|
"dev:frontend": "cross-env NODE_ENV=development vite --host --config ./frontend/vite.config.ts",
|
||||||
"release-final": "tsx ./extra/test-docker.ts && tsx extra/update-version.ts && pnpm run build:frontend && npm run build:docker",
|
"release-final": "tsx ./extra/test-docker.ts && tsx extra/update-version.ts && pnpm run build:frontend && pnpm run build:docker",
|
||||||
"build:frontend": "vite build --config ./frontend/vite.config.ts",
|
"build:frontend": "vite build --config ./frontend/vite.config.ts",
|
||||||
"build:docker-base": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:base -f ./docker/Base.Dockerfile . --push",
|
"build:docker-base": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:base -f ./docker/Base.Dockerfile . --push",
|
||||||
"build:docker": "node ./extra/env2arg.js docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:latest -t louislam/dockge:1 -t louislam/dockge:$VERSION --target release -f ./docker/Dockerfile . --push",
|
"build:docker": "node ./extra/env2arg.js docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:latest -t louislam/dockge:1 -t louislam/dockge:$VERSION --target release -f ./docker/Dockerfile . --push",
|
||||||
"build:docker-nightly": "pnpm run build:frontend && docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:nightly --target nightly -f ./docker/Dockerfile . --push",
|
"build:docker-nightly": "pnpm run build:frontend && docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:nightly --target nightly -f ./docker/Dockerfile . --push",
|
||||||
|
"build:windows": "tsx ./extra/build-windows.ts",
|
||||||
"build:healthcheck": "docker buildx build -f docker/BuildHealthCheck.Dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:build-healthcheck . --push",
|
"build:healthcheck": "docker buildx build -f docker/BuildHealthCheck.Dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:build-healthcheck . --push",
|
||||||
"start-docker": "docker run --rm -p 5001:5001 --name dockge louislam/dockge:latest",
|
"start-docker": "docker run --rm -p 5001:5001 --name dockge louislam/dockge:latest",
|
||||||
"mark-as-nightly": "tsx ./extra/mark-as-nightly.ts",
|
"mark-as-nightly": "tsx ./extra/mark-as-nightly.ts",
|
||||||
@@ -24,7 +26,7 @@
|
|||||||
"reset-password": "tsx ./extra/reset-password.ts"
|
"reset-password": "tsx ./extra/reset-password.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@homebridge/node-pty-prebuilt-multiarch": "~0.11.11",
|
"@homebridge/node-pty-prebuilt-multiarch": "~0.11.12",
|
||||||
"@inventage/envsubst": "^0.16.0",
|
"@inventage/envsubst": "^0.16.0",
|
||||||
"@louislam/sqlite3": "~15.1.6",
|
"@louislam/sqlite3": "~15.1.6",
|
||||||
"bcryptjs": "~2.4.3",
|
"bcryptjs": "~2.4.3",
|
||||||
@@ -42,7 +44,8 @@
|
|||||||
"jwt-decode": "~3.1.2",
|
"jwt-decode": "~3.1.2",
|
||||||
"knex": "~2.5.1",
|
"knex": "~2.5.1",
|
||||||
"limiter-es6-compat": "~2.1.2",
|
"limiter-es6-compat": "~2.1.2",
|
||||||
"mysql2": "~3.6.3",
|
"mysql2": "~3.6.5",
|
||||||
|
"node-windows": "1.0.0-beta.8",
|
||||||
"promisify-child-process": "~4.1.2",
|
"promisify-child-process": "~4.1.2",
|
||||||
"redbean-node": "~0.3.3",
|
"redbean-node": "~0.3.3",
|
||||||
"socket.io": "~4.7.2",
|
"socket.io": "~4.7.2",
|
||||||
@@ -55,21 +58,23 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@actions/github": "^6.0.0",
|
"@actions/github": "^6.0.0",
|
||||||
"@fontsource/jetbrains-mono": "^5.0.17",
|
"@fontsource/jetbrains-mono": "^5.0.18",
|
||||||
"@fortawesome/fontawesome-svg-core": "6.4.2",
|
"@fortawesome/fontawesome-svg-core": "6.4.2",
|
||||||
"@fortawesome/free-regular-svg-icons": "6.4.2",
|
"@fortawesome/free-regular-svg-icons": "6.4.2",
|
||||||
"@fortawesome/free-solid-svg-icons": "6.4.2",
|
"@fortawesome/free-solid-svg-icons": "6.4.2",
|
||||||
"@fortawesome/vue-fontawesome": "3.0.3",
|
"@fortawesome/vue-fontawesome": "3.0.3",
|
||||||
"@types/bcryptjs": "^2.4.6",
|
"@types/bcryptjs": "^2.4.6",
|
||||||
"@types/bootstrap": "~5.2.9",
|
"@types/bootstrap": "~5.2.10",
|
||||||
"@types/command-exists": "~1.2.3",
|
"@types/command-exists": "~1.2.3",
|
||||||
"@types/express": "~4.17.21",
|
"@types/express": "~4.17.21",
|
||||||
"@types/jsonwebtoken": "~9.0.5",
|
"@types/jsonwebtoken": "~9.0.5",
|
||||||
|
"@types/unzipper": "^0.10.9",
|
||||||
"@typescript-eslint/eslint-plugin": "~6.8.0",
|
"@typescript-eslint/eslint-plugin": "~6.8.0",
|
||||||
"@typescript-eslint/parser": "~6.8.0",
|
"@typescript-eslint/parser": "~6.8.0",
|
||||||
"@vitejs/plugin-vue": "~4.5.0",
|
"@vitejs/plugin-vue": "~4.5.2",
|
||||||
"bootstrap": "5.3.2",
|
"bootstrap": "5.3.2",
|
||||||
"bootstrap-vue-next": "~0.14.10",
|
"bootstrap-vue-next": "~0.14.10",
|
||||||
|
"concurrently": "^8.2.2",
|
||||||
"cross-env": "~7.0.3",
|
"cross-env": "~7.0.3",
|
||||||
"eslint": "~8.50.0",
|
"eslint": "~8.50.0",
|
||||||
"eslint-plugin-jsdoc": "~46.8.2",
|
"eslint-plugin-jsdoc": "~46.8.2",
|
||||||
@@ -78,15 +83,17 @@
|
|||||||
"sass": "~1.68.0",
|
"sass": "~1.68.0",
|
||||||
"typescript": "~5.2.2",
|
"typescript": "~5.2.2",
|
||||||
"unplugin-vue-components": "~0.25.2",
|
"unplugin-vue-components": "~0.25.2",
|
||||||
"vite": "~5.0.0",
|
"unzipper": "^0.10.14",
|
||||||
|
"vite": "~5.0.7",
|
||||||
"vite-plugin-compression": "~0.5.1",
|
"vite-plugin-compression": "~0.5.1",
|
||||||
"vue": "~3.3.8",
|
"vue": "~3.3.11",
|
||||||
"vue-eslint-parser": "~9.3.2",
|
"vue-eslint-parser": "~9.3.2",
|
||||||
"vue-i18n": "~9.5.0",
|
"vue-i18n": "~9.5.0",
|
||||||
"vue-prism-editor": "2.0.0-alpha.2",
|
"vue-prism-editor": "2.0.0-alpha.2",
|
||||||
"vue-qrcode": "~2.2.0",
|
"vue-qrcode": "~2.2.0",
|
||||||
"vue-router": "~4.2.5",
|
"vue-router": "~4.2.5",
|
||||||
"vue-toastification": "2.0.0-rc.5",
|
"vue-toastification": "2.0.0-rc.5",
|
||||||
|
"wait-on": "^7.2.0",
|
||||||
"xterm": "5.4.0-beta.37",
|
"xterm": "5.4.0-beta.37",
|
||||||
"xterm-addon-web-links": "~0.9.0"
|
"xterm-addon-web-links": "~0.9.0"
|
||||||
}
|
}
|
||||||
|
|||||||
1011
pnpm-lock.yaml
generated
1011
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user