diff --git a/backend/socket-handlers/docker-socket-handler.ts b/backend/socket-handlers/docker-socket-handler.ts index 047d8f7..df6c01b 100644 --- a/backend/socket-handlers/docker-socket-handler.ts +++ b/backend/socket-handlers/docker-socket-handler.ts @@ -9,10 +9,10 @@ import composerize from "composerize"; export class DockerSocketHandler extends SocketHandler { create(socket : DockgeSocket, server : DockgeServer) { - socket.on("deployStack", async (name : unknown, composeYAML : unknown, isAdd : unknown, callback) => { + socket.on("deployStack", async (name : unknown, composeYAML : unknown, composeENV : unknown, isAdd : unknown, callback) => { try { checkLogin(socket); - const stack = this.saveStack(socket, server, name, composeYAML, isAdd); + const stack = this.saveStack(socket, server, name, composeYAML, composeENV, isAdd); await stack.deploy(socket); server.sendStackList(); callback({ @@ -25,10 +25,10 @@ export class DockerSocketHandler extends SocketHandler { } }); - socket.on("saveStack", async (name : unknown, composeYAML : unknown, isAdd : unknown, callback) => { + socket.on("saveStack", async (name : unknown, composeYAML : unknown, composeENV : unknown, isAdd : unknown, callback) => { try { checkLogin(socket); - this.saveStack(socket, server, name, composeYAML, isAdd); + this.saveStack(socket, server, name, composeYAML, composeENV, isAdd); callback({ ok: true, "msg": "Saved" @@ -264,7 +264,7 @@ export class DockerSocketHandler extends SocketHandler { }); } - saveStack(socket : DockgeSocket, server : DockgeServer, name : unknown, composeYAML : unknown, isAdd : unknown) : Stack { + saveStack(socket : DockgeSocket, server : DockgeServer, name : unknown, composeYAML : unknown, composeENV : unknown, isAdd : unknown) : Stack { // Check types if (typeof(name) !== "string") { throw new ValidationError("Name must be a string"); @@ -272,11 +272,14 @@ export class DockerSocketHandler extends SocketHandler { if (typeof(composeYAML) !== "string") { throw new ValidationError("Compose YAML must be a string"); } + if (typeof(composeENV) !== "string") { + throw new ValidationError("Compose ENV must be a string"); + } if (typeof(isAdd) !== "boolean") { throw new ValidationError("isAdd must be a boolean"); } - const stack = new Stack(server, name, composeYAML); + const stack = new Stack(server, name, composeYAML, composeENV, false); stack.save(isAdd); return stack; } diff --git a/backend/stack.ts b/backend/stack.ts index a9af442..cda53db 100644 --- a/backend/stack.ts +++ b/backend/stack.ts @@ -23,6 +23,7 @@ export class Stack { name: string; protected _status: number = UNKNOWN; protected _composeYAML?: string; + protected _composeENV?: string; protected _configFilePath?: string; protected _composeFileName: string = "compose.yaml"; protected server: DockgeServer; @@ -31,10 +32,11 @@ export class Stack { protected static managedStackList: Map = new Map(); - constructor(server : DockgeServer, name : string, composeYAML? : string, skipFSOperations = false) { + constructor(server : DockgeServer, name : string, composeYAML? : string, composeENV? : string, skipFSOperations = false) { this.name = name; this.server = server; this._composeYAML = composeYAML; + this._composeENV = composeENV; if (!skipFSOperations) { // Check if compose file name is different from compose.yaml @@ -53,6 +55,7 @@ export class Stack { return { ...obj, composeYAML: this.composeYAML, + composeENV: this.composeENV, }; } @@ -105,6 +108,17 @@ export class Stack { return this._composeYAML; } + get composeENV() : string { + if (this._composeENV === undefined) { + try { + this._composeENV = fs.readFileSync(path.join(this.path, ".env"), "utf-8"); + } catch (e) { + this._composeENV = ""; + } + } + return this._composeENV; + } + get path() : string { return path.join(this.server.stacksDir, this.name); } @@ -149,6 +163,8 @@ export class Stack { // Write or overwrite the compose.yaml fs.writeFileSync(path.join(dir, this._composeFileName), this.composeYAML); + // Write or overwrite the .env + fs.writeFileSync(path.join(dir, ".env"), this.composeENV); } async deploy(socket? : DockgeSocket) : Promise { @@ -306,7 +322,7 @@ export class Stack { if (!skipFSOperations) { stack = new Stack(server, stackName); } else { - stack = new Stack(server, stackName, undefined, true); + stack = new Stack(server, stackName, undefined, undefined, true); } stack._status = UNKNOWN; diff --git a/frontend/src/pages/Compose.vue b/frontend/src/pages/Compose.vue index af58138..9cc0a5c 100644 --- a/frontend/src/pages/Compose.vue +++ b/frontend/src/pages/Compose.vue @@ -154,7 +154,7 @@ ref="editor" v-model="stack.composeYAML" class="yaml-editor" - :highlight="highlighter" + :highlight="highlighterYAML" line-numbers :readonly="!isEditMode" @input="yamlCodeChange" @focus="editorFocus = true" @@ -165,6 +165,22 @@ {{ yamlError }} + +
+

.env

+
+ +
+
+
@@ -232,10 +248,16 @@ services: ports: - "8080:80" `; +const envDefault = "# VARIABLE=value #comment"; let yamlErrorTimeout = null; let serviceStatusTimeout = null; +let prismjsSymbolDefinition = { + "symbol": { + pattern: /(? { + this.$root.getSocket().emit("deployStack", this.stack.name, this.stack.composeYAML, this.stack.composeENV, this.isAdd, (res) => { this.processing = false; this.$root.toastRes(res); @@ -506,7 +535,7 @@ export default { saveStack() { this.processing = true; - this.$root.getSocket().emit("saveStack", this.stack.name, this.stack.composeYAML, this.isAdd, (res) => { + this.$root.getSocket().emit("saveStack", this.stack.name, this.stack.composeYAML, this.stack.composeENV, this.isAdd, (res) => { this.processing = false; this.$root.toastRes(res); @@ -576,8 +605,44 @@ export default { this.isEditMode = false; }, - highlighter(code) { - return highlight(code, languages.yaml); + highlighterYAML(code) { + if (!languages.yaml_with_symbols) { + languages.yaml_with_symbols = languages.insertBefore("yaml", "punctuation", { + "symbol": prismjsSymbolDefinition["symbol"] + }); + } + return highlight(code, languages.yaml_with_symbols); + }, + + highlighterENV(code) { + if (!languages.docker_env) { + languages.docker_env = { + "comment": { + pattern: /(^#| #).*$/m, + greedy: true + }, + "keyword": { + pattern: /^[^ :=]*(?=[:=])/m, + greedy: true + }, + "value": { + pattern: /(?<=[:=]).*?((?= #)|$)/m, + greedy: true, + inside: { + "string": [ + { + pattern: /^ *'.*?(?