mirror of
https://github.com/louislam/dockge.git
synced 2024-11-27 13:14:03 +00:00
Env follow up (#231)
* Create the env file only if not empty * Update * Check some fs operation to async
This commit is contained in:
parent
0df3fee3f4
commit
793a9de50d
6 changed files with 48 additions and 15 deletions
|
@ -32,6 +32,8 @@ import User from "./models/user";
|
||||||
import childProcessAsync from "promisify-child-process";
|
import childProcessAsync from "promisify-child-process";
|
||||||
import { Terminal } from "./terminal";
|
import { Terminal } from "./terminal";
|
||||||
|
|
||||||
|
import "dotenv/config";
|
||||||
|
|
||||||
export class DockgeServer {
|
export class DockgeServer {
|
||||||
app : Express;
|
app : Express;
|
||||||
httpServer : http.Server;
|
httpServer : http.Server;
|
||||||
|
|
|
@ -12,7 +12,7 @@ export class DockerSocketHandler extends SocketHandler {
|
||||||
socket.on("deployStack", async (name : unknown, composeYAML : unknown, composeENV : 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, composeENV, isAdd);
|
const stack = await this.saveStack(socket, server, name, composeYAML, composeENV, isAdd);
|
||||||
await stack.deploy(socket);
|
await stack.deploy(socket);
|
||||||
server.sendStackList();
|
server.sendStackList();
|
||||||
callback({
|
callback({
|
||||||
|
@ -264,7 +264,7 @@ export class DockerSocketHandler extends SocketHandler {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
saveStack(socket : DockgeSocket, server : DockgeServer, name : unknown, composeYAML : unknown, composeENV : unknown, isAdd : unknown) : Stack {
|
async saveStack(socket : DockgeSocket, server : DockgeServer, name : unknown, composeYAML : unknown, composeENV : unknown, isAdd : unknown) : Promise<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");
|
||||||
|
@ -280,7 +280,7 @@ export class DockerSocketHandler extends SocketHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
const stack = new Stack(server, name, composeYAML, composeENV, false);
|
const stack = new Stack(server, name, composeYAML, composeENV, false);
|
||||||
stack.save(isAdd);
|
await stack.save(isAdd);
|
||||||
return stack;
|
return stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { DockgeServer } from "./dockge-server";
|
import { DockgeServer } from "./dockge-server";
|
||||||
import fs from "fs";
|
import fs, { promises as fsAsync } from "fs";
|
||||||
import { log } from "./log";
|
import { log } from "./log";
|
||||||
import yaml from "yaml";
|
import yaml from "yaml";
|
||||||
import { DockgeSocket, ValidationError } from "./util-server";
|
import { DockgeSocket, fileExists, ValidationError } from "./util-server";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import {
|
import {
|
||||||
COMBINED_TERMINAL_COLS,
|
COMBINED_TERMINAL_COLS,
|
||||||
|
@ -99,6 +99,15 @@ export class Stack {
|
||||||
|
|
||||||
// Check YAML format
|
// Check YAML format
|
||||||
yaml.parse(this.composeYAML);
|
yaml.parse(this.composeYAML);
|
||||||
|
|
||||||
|
let lines = this.composeENV.split("\n");
|
||||||
|
|
||||||
|
// Check if the .env is able to pass docker-compose
|
||||||
|
// Prevent "setenv: The parameter is incorrect"
|
||||||
|
// It only happens when there is one line and it doesn't contain "="
|
||||||
|
if (lines.length === 1 && !lines[0].includes("=") && lines[0].length > 0) {
|
||||||
|
throw new ValidationError("Invalid .env format");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get composeYAML() : string {
|
get composeYAML() : string {
|
||||||
|
@ -146,29 +155,35 @@ export class Stack {
|
||||||
* Save the stack to the disk
|
* Save the stack to the disk
|
||||||
* @param isAdd
|
* @param isAdd
|
||||||
*/
|
*/
|
||||||
save(isAdd : boolean) {
|
async save(isAdd : boolean) {
|
||||||
this.validate();
|
this.validate();
|
||||||
|
|
||||||
let dir = this.path;
|
let dir = this.path;
|
||||||
|
|
||||||
// Check if the name is used if isAdd
|
// Check if the name is used if isAdd
|
||||||
if (isAdd) {
|
if (isAdd) {
|
||||||
if (fs.existsSync(dir)) {
|
if (await fileExists(dir)) {
|
||||||
throw new ValidationError("Stack name already exists");
|
throw new ValidationError("Stack name already exists");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the stack folder
|
// Create the stack folder
|
||||||
fs.mkdirSync(dir);
|
await fsAsync.mkdir(dir);
|
||||||
} else {
|
} else {
|
||||||
if (!fs.existsSync(dir)) {
|
if (!await fileExists(dir)) {
|
||||||
throw new ValidationError("Stack not found");
|
throw new ValidationError("Stack not found");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write or overwrite the compose.yaml
|
// Write or overwrite the compose.yaml
|
||||||
fs.writeFileSync(path.join(dir, this._composeFileName), this.composeYAML);
|
await fsAsync.writeFile(path.join(dir, this._composeFileName), this.composeYAML);
|
||||||
|
|
||||||
|
const envPath = path.join(dir, ".env");
|
||||||
|
|
||||||
// Write or overwrite the .env
|
// Write or overwrite the .env
|
||||||
fs.writeFileSync(path.join(dir, ".env"), this.composeENV);
|
// If .env is not existing and the composeENV is empty, we don't need to write it
|
||||||
|
if (await fileExists(envPath) || this.composeENV.trim() !== "") {
|
||||||
|
await fsAsync.writeFile(envPath, this.composeENV);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async deploy(socket? : DockgeSocket) : Promise<number> {
|
async deploy(socket? : DockgeSocket) : Promise<number> {
|
||||||
|
@ -188,7 +203,7 @@ export class Stack {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the stack folder
|
// Remove the stack folder
|
||||||
fs.rmSync(this.path, {
|
await fsAsync.rm(this.path, {
|
||||||
recursive: true,
|
recursive: true,
|
||||||
force: true
|
force: true
|
||||||
});
|
});
|
||||||
|
@ -218,12 +233,12 @@ export class Stack {
|
||||||
stackList = new Map<string, Stack>();
|
stackList = new Map<string, Stack>();
|
||||||
|
|
||||||
// Scan the stacks directory, and get the stack list
|
// Scan the stacks directory, and get the stack list
|
||||||
let filenameList = fs.readdirSync(stacksDir);
|
let filenameList = await fsAsync.readdir(stacksDir);
|
||||||
|
|
||||||
for (let filename of filenameList) {
|
for (let filename of filenameList) {
|
||||||
try {
|
try {
|
||||||
// Check if it is a directory
|
// Check if it is a directory
|
||||||
let stat = fs.statSync(path.join(stacksDir, filename));
|
let stat = await fsAsync.stat(path.join(stacksDir, filename));
|
||||||
if (!stat.isDirectory()) {
|
if (!stat.isDirectory()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -314,7 +329,7 @@ export class Stack {
|
||||||
let dir = path.join(server.stacksDir, stackName);
|
let dir = path.join(server.stacksDir, stackName);
|
||||||
|
|
||||||
if (!skipFSOperations) {
|
if (!skipFSOperations) {
|
||||||
if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
|
if (!await fileExists(dir) || !(await fsAsync.stat(dir)).isDirectory()) {
|
||||||
// Maybe it is a stack managed by docker compose directly
|
// Maybe it is a stack managed by docker compose directly
|
||||||
let stackList = await this.getStackList(server, true);
|
let stackList = await this.getStackList(server, true);
|
||||||
let stack = stackList.get(stackName);
|
let stack = stackList.get(stackName);
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { log } from "./log";
|
||||||
import { ERROR_TYPE_VALIDATION } from "./util-common";
|
import { ERROR_TYPE_VALIDATION } from "./util-common";
|
||||||
import { R } from "redbean-node";
|
import { R } from "redbean-node";
|
||||||
import { verifyPassword } from "./password-hash";
|
import { verifyPassword } from "./password-hash";
|
||||||
|
import fs from "fs";
|
||||||
|
|
||||||
export interface JWTDecoded {
|
export interface JWTDecoded {
|
||||||
username : string;
|
username : string;
|
||||||
|
@ -82,3 +83,9 @@ export async function doubleCheckPassword(socket : DockgeSocket, currentPassword
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function fileExists(file : string) {
|
||||||
|
return fs.promises.access(file, fs.constants.F_OK)
|
||||||
|
.then(() => true)
|
||||||
|
.catch(() => false);
|
||||||
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
"composerize": "~1.4.1",
|
"composerize": "~1.4.1",
|
||||||
"croner": "~7.0.5",
|
"croner": "~7.0.5",
|
||||||
"dayjs": "~1.11.10",
|
"dayjs": "~1.11.10",
|
||||||
|
"dotenv": "~16.3.1",
|
||||||
"express": "~4.18.2",
|
"express": "~4.18.2",
|
||||||
"express-static-gzip": "~2.1.7",
|
"express-static-gzip": "~2.1.7",
|
||||||
"http-graceful-shutdown": "~3.1.13",
|
"http-graceful-shutdown": "~3.1.13",
|
||||||
|
|
|
@ -32,6 +32,9 @@ dependencies:
|
||||||
dayjs:
|
dayjs:
|
||||||
specifier: ~1.11.10
|
specifier: ~1.11.10
|
||||||
version: 1.11.10
|
version: 1.11.10
|
||||||
|
dotenv:
|
||||||
|
specifier: ~16.3.1
|
||||||
|
version: 16.3.1
|
||||||
express:
|
express:
|
||||||
specifier: ~4.18.2
|
specifier: ~4.18.2
|
||||||
version: 4.18.2
|
version: 4.18.2
|
||||||
|
@ -2154,6 +2157,11 @@ packages:
|
||||||
esutils: 2.0.3
|
esutils: 2.0.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/dotenv@16.3.1:
|
||||||
|
resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/eastasianwidth@0.2.0:
|
/eastasianwidth@0.2.0:
|
||||||
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
Loading…
Reference in a new issue