mirror of
https://github.com/louislam/dockge.git
synced 2025-08-12 16:27:03 +02:00
Compare commits
17 Commits
1.3.0
...
build-wind
Author | SHA1 | Date | |
---|---|---|---|
4400ba5dd6 | |||
2b428e139c | |||
587d2dcaca | |||
5f1f3593fd | |||
e570374a48 | |||
316c566c76 | |||
d32bd3937f | |||
007eac7b58 | |||
b945ddea55 | |||
bd58de535e | |||
8c6bcef987 | |||
bec5460395 | |||
4e899dcf21 | |||
8296c7b18f | |||
607c908f2d | |||
bd5dd3c3ad | |||
6eca6dc59f |
@ -131,7 +131,7 @@ Be sure to read the [guide](https://github.com/louislam/dockge/blob/master/CONTR
|
|||||||
|
|
||||||
#### "Dockge"?
|
#### "Dockge"?
|
||||||
|
|
||||||
"Dockge" is a coinage word which is created by myself. I hope it sounds like `Dodge`.
|
"Dockge" is a coinage word which is created by myself. I originally hoped it sounds like `Dodge`, but apparently many people called it `Dockage`, it is also acceptable.
|
||||||
|
|
||||||
The naming idea came from Twitch emotes like `sadge`, `bedge` or `wokege`. They all end in `-ge`.
|
The naming idea came from Twitch emotes like `sadge`, `bedge` or `wokege`. They all end in `-ge`.
|
||||||
|
|
||||||
@ -148,13 +148,13 @@ Yes, you can. However, you need to move your compose file into the stacks direct
|
|||||||
3. In Dockge, click the " Scan Stacks Folder" button in the top-right corner's dropdown menu
|
3. In Dockge, click the " Scan Stacks Folder" button in the top-right corner's dropdown menu
|
||||||
4. Now you should see your stack in the list
|
4. Now you should see your stack in the list
|
||||||
|
|
||||||
#### Is Dockge a Portainer replcement?
|
#### Is Dockge a Portainer replacement?
|
||||||
|
|
||||||
Yes or no. Portainer provides a lot of Docker features. While Dockge is currently only focusing on docker-compose with a better user interface and better user experience.
|
Yes or no. Portainer provides a lot of Docker features. While Dockge is currently only focusing on docker-compose with a better user interface and better user experience.
|
||||||
|
|
||||||
If you want to manage your container with docker-compose only, the answer may be yes.
|
If you want to manage your container with docker-compose only, the answer may be yes.
|
||||||
|
|
||||||
If you still need to manage something like docker networks, signle containers, the answer may be no.
|
If you still need to manage something like docker networks, single containers, the answer may be no.
|
||||||
|
|
||||||
#### Can I install both Dockge and Portainer?
|
#### Can I install both Dockge and Portainer?
|
||||||
|
|
||||||
|
@ -3,69 +3,55 @@ import compareVersions from "compare-versions";
|
|||||||
import packageJSON from "../package.json";
|
import packageJSON from "../package.json";
|
||||||
import { Settings } from "./settings";
|
import { Settings } from "./settings";
|
||||||
|
|
||||||
export const obj = {
|
|
||||||
version: packageJSON.version,
|
|
||||||
latestVersion: null,
|
|
||||||
};
|
|
||||||
export default obj;
|
|
||||||
|
|
||||||
// How much time in ms to wait between update checks
|
// How much time in ms to wait between update checks
|
||||||
const UPDATE_CHECKER_INTERVAL_MS = 1000 * 60 * 60 * 48;
|
const UPDATE_CHECKER_INTERVAL_MS = 1000 * 60 * 60 * 48;
|
||||||
const CHECK_URL = "https://dockge.kuma.pet/version";
|
const CHECK_URL = "https://dockge.kuma.pet/version";
|
||||||
|
|
||||||
let interval : NodeJS.Timeout;
|
class CheckVersion {
|
||||||
|
version = packageJSON.version;
|
||||||
|
latestVersion? : string;
|
||||||
|
interval? : NodeJS.Timeout;
|
||||||
|
|
||||||
export function startInterval() {
|
async startInterval() {
|
||||||
const check = async () => {
|
const check = async () => {
|
||||||
if (await Settings.get("checkUpdate") === false) {
|
if (await Settings.get("checkUpdate") === false) {
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
log.debug("update-checker", "Retrieving latest versions");
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await fetch(CHECK_URL);
|
|
||||||
const data = await res.json();
|
|
||||||
|
|
||||||
// For debug
|
|
||||||
if (process.env.TEST_CHECK_VERSION === "1") {
|
|
||||||
data.slow = "1000.0.0";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkBeta = await Settings.get("checkBeta");
|
log.debug("update-checker", "Retrieving latest versions");
|
||||||
|
|
||||||
if (checkBeta && data.beta) {
|
try {
|
||||||
if (compareVersions.compare(data.beta, data.slow, ">")) {
|
const res = await fetch(CHECK_URL);
|
||||||
obj.latestVersion = data.beta;
|
const data = await res.json();
|
||||||
return;
|
|
||||||
|
// For debug
|
||||||
|
if (process.env.TEST_CHECK_VERSION === "1") {
|
||||||
|
data.slow = "1000.0.0";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const checkBeta = await Settings.get("checkBeta");
|
||||||
|
|
||||||
|
if (checkBeta && data.beta) {
|
||||||
|
if (compareVersions.compare(data.beta, data.slow, ">")) {
|
||||||
|
this.latestVersion = data.beta;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.slow) {
|
||||||
|
this.latestVersion = data.slow;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (_) {
|
||||||
|
log.info("update-checker", "Failed to check for new versions");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.slow) {
|
};
|
||||||
obj.latestVersion = data.slow;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (_) {
|
await check();
|
||||||
log.info("update-checker", "Failed to check for new versions");
|
this.interval = setInterval(check, UPDATE_CHECKER_INTERVAL_MS);
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
check();
|
|
||||||
interval = setInterval(check, UPDATE_CHECKER_INTERVAL_MS);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable the check update feature
|
|
||||||
* @param value Should the check update feature be enabled?
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export async function enableCheckUpdate(value : boolean) {
|
|
||||||
await Settings.set("checkUpdate", value);
|
|
||||||
|
|
||||||
clearInterval(interval);
|
|
||||||
|
|
||||||
if (value) {
|
|
||||||
startInterval();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const checkVersion = new CheckVersion();
|
||||||
|
export default checkVersion;
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
export class Docker {
|
|
||||||
|
|
||||||
}
|
|
@ -308,6 +308,7 @@ export class DockgeServer {
|
|||||||
this.sendStackList(true);
|
this.sendStackList(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
checkVersion.startInterval();
|
||||||
});
|
});
|
||||||
|
|
||||||
gracefulShutdown(this.httpServer, {
|
gracefulShutdown(this.httpServer, {
|
||||||
|
@ -234,7 +234,7 @@ export class DockerSocketHandler extends SocketHandler {
|
|||||||
socket.on("getDockerNetworkList", async (callback) => {
|
socket.on("getDockerNetworkList", async (callback) => {
|
||||||
try {
|
try {
|
||||||
checkLogin(socket);
|
checkLogin(socket);
|
||||||
const dockerNetworkList = server.getDockerNetworkList();
|
const dockerNetworkList = await server.getDockerNetworkList();
|
||||||
callback({
|
callback({
|
||||||
ok: true,
|
ok: true,
|
||||||
dockerNetworkList,
|
dockerNetworkList,
|
||||||
|
@ -297,7 +297,12 @@ export class Stack {
|
|||||||
let res = await childProcessAsync.spawn("docker", [ "compose", "ls", "--all", "--format", "json" ], {
|
let res = await childProcessAsync.spawn("docker", [ "compose", "ls", "--all", "--format", "json" ], {
|
||||||
encoding: "utf-8",
|
encoding: "utf-8",
|
||||||
});
|
});
|
||||||
let composeList = JSON.parse(res.toString());
|
|
||||||
|
if (!res.stdout) {
|
||||||
|
return statusList;
|
||||||
|
}
|
||||||
|
|
||||||
|
let composeList = JSON.parse(res.stdout.toString());
|
||||||
|
|
||||||
for (let composeStack of composeList) {
|
for (let composeStack of composeList) {
|
||||||
statusList.set(composeStack.Name, this.statusConvert(composeStack.Status));
|
statusList.set(composeStack.Name, this.statusConvert(composeStack.Status));
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
/*
|
/*
|
||||||
* Common utilities for backend and frontend
|
* Common utilities for backend and frontend
|
||||||
*/
|
*/
|
||||||
import { Document } from "yaml";
|
import yaml, { Document, Pair, Scalar } from "yaml";
|
||||||
|
import { DotenvParseOutput } from "dotenv";
|
||||||
|
|
||||||
// Init dayjs
|
// Init dayjs
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import timezone from "dayjs/plugin/timezone";
|
import timezone from "dayjs/plugin/timezone";
|
||||||
import utc from "dayjs/plugin/utc";
|
import utc from "dayjs/plugin/utc";
|
||||||
import relativeTime from "dayjs/plugin/relativeTime";
|
import relativeTime from "dayjs/plugin/relativeTime";
|
||||||
|
// @ts-ignore
|
||||||
|
import { replaceVariablesSync } from "@inventage/envsubst";
|
||||||
|
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
dayjs.extend(timezone);
|
dayjs.extend(timezone);
|
||||||
dayjs.extend(relativeTime);
|
dayjs.extend(relativeTime);
|
||||||
@ -340,3 +344,48 @@ export function parseDockerPort(input : string, defaultHostname : string = "loca
|
|||||||
display: display,
|
display: display,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function envsubst(string : string, variables : LooseObject) : string {
|
||||||
|
return replaceVariablesSync(string, variables)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traverse all values in the yaml and for each value, if there are template variables, replace it environment variables
|
||||||
|
* Emulates the behavior of how docker-compose handles environment variables in yaml files
|
||||||
|
* @param content Yaml string
|
||||||
|
* @param env Environment variables
|
||||||
|
* @returns string Yaml string with environment variables replaced
|
||||||
|
*/
|
||||||
|
export function envsubstYAML(content : string, env : DotenvParseOutput) : string {
|
||||||
|
const doc = yaml.parseDocument(content);
|
||||||
|
if (doc.contents) {
|
||||||
|
// @ts-ignore
|
||||||
|
for (const item of doc.contents.items) {
|
||||||
|
traverseYAML(item, env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return doc.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for envsubstYAML(...)
|
||||||
|
* @param pair
|
||||||
|
* @param env
|
||||||
|
*/
|
||||||
|
function traverseYAML(pair : Pair, env : DotenvParseOutput) : void {
|
||||||
|
// @ts-ignore
|
||||||
|
if (pair.value && pair.value.items) {
|
||||||
|
// @ts-ignore
|
||||||
|
for (const item of pair.value.items) {
|
||||||
|
if (item instanceof Pair) {
|
||||||
|
traverseYAML(item, env);
|
||||||
|
} else if (item instanceof Scalar) {
|
||||||
|
item.value = envsubst(item.value, env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
|
} else if (pair.value && typeof(pair.value.value) === "string") {
|
||||||
|
// @ts-ignore
|
||||||
|
pair.value.value = envsubst(pair.value.value, env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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}`));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -9,7 +9,7 @@
|
|||||||
<div v-if="!isEditMode">
|
<div v-if="!isEditMode">
|
||||||
<span class="badge me-1" :class="bgStyle">{{ status }}</span>
|
<span class="badge me-1" :class="bgStyle">{{ status }}</span>
|
||||||
|
|
||||||
<a v-for="port in service.ports" :key="port" :href="parsePort(port).url" target="_blank">
|
<a v-for="port in envsubstService.ports" :key="port" :href="parsePort(port).url" target="_blank">
|
||||||
<span class="badge me-1 bg-secondary">{{ parsePort(port).display }}</span>
|
<span class="badge me-1 bg-secondary">{{ parsePort(port).display }}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -213,16 +213,29 @@ export default defineComponent({
|
|||||||
jsonObject() {
|
jsonObject() {
|
||||||
return this.$parent.$parent.jsonConfig;
|
return this.$parent.$parent.jsonConfig;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
envsubstJSONConfig() {
|
||||||
|
return this.$parent.$parent.envsubstJSONConfig;
|
||||||
|
},
|
||||||
|
|
||||||
|
envsubstService() {
|
||||||
|
if (!this.envsubstJSONConfig.services[this.name]) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return this.envsubstJSONConfig.services[this.name];
|
||||||
|
},
|
||||||
|
|
||||||
imageName() {
|
imageName() {
|
||||||
if (this.service.image) {
|
if (this.envsubstService.image) {
|
||||||
return this.service.image.split(":")[0];
|
return this.envsubstService.image.split(":")[0];
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
imageTag() {
|
imageTag() {
|
||||||
if (this.service.image) {
|
if (this.envsubstService.image) {
|
||||||
let tag = this.service.image.split(":")[1];
|
let tag = this.envsubstService.image.split(":")[1];
|
||||||
|
|
||||||
if (tag) {
|
if (tag) {
|
||||||
return tag;
|
return tag;
|
||||||
|
@ -39,7 +39,7 @@ for (let lang in languageList) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const rtlLangs = [ "fa", "ar-SY", "ur" ];
|
const rtlLangs = [ "fa", "ar-SY", "ur", "ar" ];
|
||||||
|
|
||||||
export const currentLocale = () => localStorage.locale
|
export const currentLocale = () => localStorage.locale
|
||||||
|| languageList[navigator.language] && navigator.language
|
|| languageList[navigator.language] && navigator.language
|
||||||
|
@ -98,5 +98,6 @@
|
|||||||
"reconnecting...": "Reconnecting…",
|
"reconnecting...": "Reconnecting…",
|
||||||
"connecting...": "Connecting to the socket server…",
|
"connecting...": "Connecting to the socket server…",
|
||||||
"url": "URL | URLs",
|
"url": "URL | URLs",
|
||||||
"extra": "Extra"
|
"extra": "Extra",
|
||||||
|
"newUpdate": "New Update"
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,8 @@
|
|||||||
<span class="fs-4 title">Dockge</span>
|
<span class="fs-4 title">Dockge</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
<a v-if="hasNewVersion" target="_blank" href="https://github.com/louislam/dockge/releases" class="btn btn-info me-3">
|
<a v-if="hasNewVersion" target="_blank" href="https://github.com/louislam/dockge/releases" class="btn btn-warning me-3">
|
||||||
<font-awesome-icon icon="arrow-alt-circle-up" /> {{ $t("New Update") }}
|
<font-awesome-icon icon="arrow-alt-circle-up" /> {{ $t("newUpdate") }}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<ul class="nav nav-pills">
|
<ul class="nav nav-pills">
|
||||||
|
@ -231,7 +231,7 @@ import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
|||||||
import {
|
import {
|
||||||
COMBINED_TERMINAL_COLS,
|
COMBINED_TERMINAL_COLS,
|
||||||
COMBINED_TERMINAL_ROWS,
|
COMBINED_TERMINAL_ROWS,
|
||||||
copyYAMLComments,
|
copyYAMLComments, envsubstYAML,
|
||||||
getCombinedTerminalName,
|
getCombinedTerminalName,
|
||||||
getComposeTerminalName,
|
getComposeTerminalName,
|
||||||
PROGRESS_TERMINAL_ROWS,
|
PROGRESS_TERMINAL_ROWS,
|
||||||
@ -239,6 +239,7 @@ import {
|
|||||||
} from "../../../backend/util-common";
|
} from "../../../backend/util-common";
|
||||||
import { BModal } from "bootstrap-vue-next";
|
import { BModal } from "bootstrap-vue-next";
|
||||||
import NetworkInput from "../components/NetworkInput.vue";
|
import NetworkInput from "../components/NetworkInput.vue";
|
||||||
|
import dotenv from "dotenv";
|
||||||
|
|
||||||
const template = `version: "3.8"
|
const template = `version: "3.8"
|
||||||
services:
|
services:
|
||||||
@ -277,6 +278,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
editorFocus: false,
|
editorFocus: false,
|
||||||
jsonConfig: {},
|
jsonConfig: {},
|
||||||
|
envsubstJSONConfig: {},
|
||||||
yamlError: "",
|
yamlError: "",
|
||||||
processing: true,
|
processing: true,
|
||||||
showProgressTerminal: false,
|
showProgressTerminal: false,
|
||||||
@ -297,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);
|
||||||
@ -372,6 +374,17 @@ export default {
|
|||||||
},
|
},
|
||||||
deep: true,
|
deep: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"stack.composeENV": {
|
||||||
|
handler() {
|
||||||
|
if (this.editorFocus) {
|
||||||
|
console.debug("env code changed");
|
||||||
|
this.yamlCodeChange();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
|
||||||
jsonConfig: {
|
jsonConfig: {
|
||||||
handler() {
|
handler() {
|
||||||
if (!this.editorFocus) {
|
if (!this.editorFocus) {
|
||||||
@ -622,7 +635,7 @@ export default {
|
|||||||
greedy: true
|
greedy: true
|
||||||
},
|
},
|
||||||
"keyword": {
|
"keyword": {
|
||||||
pattern: /^[^ :=]*(?=[:=])/m,
|
pattern: /^\w*(?=[:=])/m,
|
||||||
greedy: true
|
greedy: true
|
||||||
},
|
},
|
||||||
"value": {
|
"value": {
|
||||||
@ -645,28 +658,41 @@ export default {
|
|||||||
return highlight(code, languages.docker_env);
|
return highlight(code, languages.docker_env);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
yamlToJSON(yaml) {
|
||||||
|
let doc = parseDocument(yaml);
|
||||||
|
if (doc.errors.length > 0) {
|
||||||
|
throw doc.errors[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = doc.toJS() ?? {};
|
||||||
|
|
||||||
|
// Check data types
|
||||||
|
// "services" must be an object
|
||||||
|
if (!config.services) {
|
||||||
|
config.services = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(config.services) || typeof config.services !== "object") {
|
||||||
|
throw new Error("Services must be an object");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
config,
|
||||||
|
doc,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
yamlCodeChange() {
|
yamlCodeChange() {
|
||||||
try {
|
try {
|
||||||
let doc = parseDocument(this.stack.composeYAML);
|
let { config, doc } = this.yamlToJSON(this.stack.composeYAML);
|
||||||
if (doc.errors.length > 0) {
|
|
||||||
throw doc.errors[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
const config = doc.toJS() ?? {};
|
|
||||||
|
|
||||||
// Check data types
|
|
||||||
// "services" must be an object
|
|
||||||
if (!config.services) {
|
|
||||||
config.services = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(config.services) || typeof config.services !== "object") {
|
|
||||||
throw new Error("Services must be an object");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.yamlDoc = doc;
|
this.yamlDoc = doc;
|
||||||
this.jsonConfig = config;
|
this.jsonConfig = config;
|
||||||
|
|
||||||
|
let env = dotenv.parse(this.stack.composeENV);
|
||||||
|
let envYAML = envsubstYAML(this.stack.composeYAML, env);
|
||||||
|
this.envsubstJSONConfig = this.yamlToJSON(envYAML).config;
|
||||||
|
|
||||||
clearTimeout(yamlErrorTimeout);
|
clearTimeout(yamlErrorTimeout);
|
||||||
this.yamlError = "";
|
this.yamlError = "";
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
28
package.json
28
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "dockge",
|
"name": "dockge",
|
||||||
"version": "1.3.0",
|
"version": "1.3.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 18.0.0 && <= 18.17.1"
|
"node": ">= 18.0.0 && <= 18.17.1"
|
||||||
@ -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,8 @@
|
|||||||
"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",
|
||||||
"@louislam/sqlite3": "~15.1.6",
|
"@louislam/sqlite3": "~15.1.6",
|
||||||
"bcryptjs": "~2.4.3",
|
"bcryptjs": "~2.4.3",
|
||||||
"check-password-strength": "~2.0.7",
|
"check-password-strength": "~2.0.7",
|
||||||
@ -41,34 +44,37 @@
|
|||||||
"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",
|
||||||
"socket.io-client": "~4.7.2",
|
"socket.io-client": "~4.7.2",
|
||||||
"timezones-list": "~3.0.2",
|
"timezones-list": "~3.0.2",
|
||||||
"ts-command-line-args": "~2.5.1",
|
"ts-command-line-args": "~2.5.1",
|
||||||
"tsx": "~3.14.0",
|
"tsx": "~4.6.2",
|
||||||
"type-fest": "~4.3.3",
|
"type-fest": "~4.3.3",
|
||||||
"yaml": "~2.3.4"
|
"yaml": "~2.3.4"
|
||||||
},
|
},
|
||||||
"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",
|
||||||
@ -77,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"
|
||||||
}
|
}
|
||||||
|
1508
pnpm-lock.yaml
generated
1508
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user