UI for editing .env file (#218)

* Add .env file editing with syntax highlighting

* Add example env file to new compose

* Changed .env editing section title

* Better stack constuctor parameter order

* Minor

---------

Co-authored-by: Louis Lam <louislam@users.noreply.github.com>
This commit is contained in:
syko9000 2023-12-03 05:12:54 -05:00 committed by GitHub
parent e2c81bd3e0
commit a8d95d06b9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 98 additions and 14 deletions

View file

@ -9,10 +9,10 @@ import composerize from "composerize";
export class DockerSocketHandler extends SocketHandler { export class DockerSocketHandler extends SocketHandler {
create(socket : DockgeSocket, server : DockgeServer) { 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 { try {
checkLogin(socket); 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); await stack.deploy(socket);
server.sendStackList(); server.sendStackList();
callback({ 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 { try {
checkLogin(socket); checkLogin(socket);
this.saveStack(socket, server, name, composeYAML, isAdd); this.saveStack(socket, server, name, composeYAML, composeENV, isAdd);
callback({ callback({
ok: true, ok: true,
"msg": "Saved" "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 // Check types
if (typeof(name) !== "string") { if (typeof(name) !== "string") {
throw new ValidationError("Name must be a string"); throw new ValidationError("Name must be a string");
@ -272,11 +272,14 @@ export class DockerSocketHandler extends SocketHandler {
if (typeof(composeYAML) !== "string") { if (typeof(composeYAML) !== "string") {
throw new ValidationError("Compose YAML must be a 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") { if (typeof(isAdd) !== "boolean") {
throw new ValidationError("isAdd must be a 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); stack.save(isAdd);
return stack; return stack;
} }

View file

@ -23,6 +23,7 @@ export class Stack {
name: string; name: string;
protected _status: number = UNKNOWN; protected _status: number = UNKNOWN;
protected _composeYAML?: string; protected _composeYAML?: string;
protected _composeENV?: string;
protected _configFilePath?: string; protected _configFilePath?: string;
protected _composeFileName: string = "compose.yaml"; protected _composeFileName: string = "compose.yaml";
protected server: DockgeServer; protected server: DockgeServer;
@ -31,10 +32,11 @@ export class Stack {
protected static managedStackList: Map<string, Stack> = new Map(); protected static managedStackList: Map<string, Stack> = 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.name = name;
this.server = server; this.server = server;
this._composeYAML = composeYAML; this._composeYAML = composeYAML;
this._composeENV = composeENV;
if (!skipFSOperations) { if (!skipFSOperations) {
// Check if compose file name is different from compose.yaml // Check if compose file name is different from compose.yaml
@ -53,6 +55,7 @@ export class Stack {
return { return {
...obj, ...obj,
composeYAML: this.composeYAML, composeYAML: this.composeYAML,
composeENV: this.composeENV,
}; };
} }
@ -105,6 +108,17 @@ export class Stack {
return this._composeYAML; 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 { get path() : string {
return path.join(this.server.stacksDir, this.name); return path.join(this.server.stacksDir, this.name);
} }
@ -149,6 +163,8 @@ export class Stack {
// Write or overwrite the compose.yaml // Write or overwrite the compose.yaml
fs.writeFileSync(path.join(dir, this._composeFileName), this.composeYAML); 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<number> { async deploy(socket? : DockgeSocket) : Promise<number> {
@ -306,7 +322,7 @@ export class Stack {
if (!skipFSOperations) { if (!skipFSOperations) {
stack = new Stack(server, stackName); stack = new Stack(server, stackName);
} else { } else {
stack = new Stack(server, stackName, undefined, true); stack = new Stack(server, stackName, undefined, undefined, true);
} }
stack._status = UNKNOWN; stack._status = UNKNOWN;

View file

@ -154,7 +154,7 @@
ref="editor" ref="editor"
v-model="stack.composeYAML" v-model="stack.composeYAML"
class="yaml-editor" class="yaml-editor"
:highlight="highlighter" :highlight="highlighterYAML"
line-numbers :readonly="!isEditMode" line-numbers :readonly="!isEditMode"
@input="yamlCodeChange" @input="yamlCodeChange"
@focus="editorFocus = true" @focus="editorFocus = true"
@ -165,6 +165,22 @@
{{ yamlError }} {{ yamlError }}
</div> </div>
<!-- ENV editor -->
<div v-if="isEditMode">
<h4 class="mb-3">.env</h4>
<div class="shadow-box mb-3 editor-box" :class="{'edit-mode' : isEditMode}">
<prism-editor
ref="editor"
v-model="stack.composeENV"
class="env-editor"
:highlight="highlighterENV"
line-numbers :readonly="!isEditMode"
@focus="editorFocus = true"
@blur="editorFocus = false"
></prism-editor>
</div>
</div>
<div v-if="isEditMode"> <div v-if="isEditMode">
<!-- Volumes --> <!-- Volumes -->
<div v-if="false"> <div v-if="false">
@ -232,10 +248,16 @@ services:
ports: ports:
- "8080:80" - "8080:80"
`; `;
const envDefault = "# VARIABLE=value #comment";
let yamlErrorTimeout = null; let yamlErrorTimeout = null;
let serviceStatusTimeout = null; let serviceStatusTimeout = null;
let prismjsSymbolDefinition = {
"symbol": {
pattern: /(?<!\$)\$(\{[^{}]*\}|\w+)/,
}
};
export default { export default {
components: { components: {
@ -381,19 +403,26 @@ export default {
this.isEditMode = true; this.isEditMode = true;
let composeYAML; let composeYAML;
let composeENV;
if (this.$root.composeTemplate) { if (this.$root.composeTemplate) {
composeYAML = this.$root.composeTemplate; composeYAML = this.$root.composeTemplate;
this.$root.composeTemplate = ""; this.$root.composeTemplate = "";
} else { } else {
composeYAML = template; composeYAML = template;
} }
if (this.$root.envTemplate) {
composeENV = this.$root.envTemplate;
this.$root.envTemplate = "";
} else {
composeENV = envDefault;
}
// Default Values // Default Values
this.stack = { this.stack = {
name: "", name: "",
composeYAML, composeYAML,
composeENV,
isManagedByDockge: true, isManagedByDockge: true,
}; };
@ -492,7 +521,7 @@ export default {
this.bindTerminal(this.terminalName); this.bindTerminal(this.terminalName);
this.$root.getSocket().emit("deployStack", this.stack.name, this.stack.composeYAML, this.isAdd, (res) => { this.$root.getSocket().emit("deployStack", this.stack.name, this.stack.composeYAML, this.stack.composeENV, this.isAdd, (res) => {
this.processing = false; this.processing = false;
this.$root.toastRes(res); this.$root.toastRes(res);
@ -506,7 +535,7 @@ export default {
saveStack() { saveStack() {
this.processing = true; 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.processing = false;
this.$root.toastRes(res); this.$root.toastRes(res);
@ -576,8 +605,44 @@ export default {
this.isEditMode = false; this.isEditMode = false;
}, },
highlighter(code) { highlighterYAML(code) {
return highlight(code, languages.yaml); 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: /^ *'.*?(?<!\\)'/m,
},
{
pattern: /^ *".*?(?<!\\)"|^.*$/m,
inside: prismjsSymbolDefinition
},
],
},
},
};
}
return highlight(code, languages.docker_env);
}, },
yamlCodeChange() { yamlCodeChange() {