Compare commits
2 commits
master
...
child-proc
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b0e7f5aff5 | ||
![]() |
95c958ba9f |
16 changed files with 37 additions and 95 deletions
3
.github/DISCUSSION_TEMPLATE/ask-for-help.yml
vendored
3
.github/DISCUSSION_TEMPLATE/ask-for-help.yml
vendored
|
@ -1,4 +1,5 @@
|
|||
labels: [help]
|
||||
title: "❓ Ask for help"
|
||||
labels: [help]
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: no-duplicate-issues
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
title: 🚀 Feature Request
|
||||
labels: [feature-request]
|
||||
body:
|
||||
- type: checkboxes
|
||||
|
@ -51,4 +52,4 @@ body:
|
|||
attributes:
|
||||
label: "📝 Additional Context"
|
||||
description: "Add any other context or screenshots about the feature request here."
|
||||
placeholder: "..."
|
||||
placeholder: "..."
|
8
.github/ISSUE_TEMPLATE/ask-for-help.yaml
vendored
8
.github/ISSUE_TEMPLATE/ask-for-help.yaml
vendored
|
@ -1,14 +1,14 @@
|
|||
name: "⚠️ Ask for help (Please go to the \"Discussions\" tab to submit a Help Request)"
|
||||
description: "⚠️ Please go to the \"Discussions\" tab to submit a Help Request"
|
||||
name: "❓ Ask for help"
|
||||
description: "Please go to the Discussions tab to submit a Help Request"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ Please go to https://github.com/louislam/dockge/discussions/new?category=ask-for-help
|
||||
Please go to https://github.com/louislam/dockge/discussions/new?category=ask-for-help
|
||||
- type: checkboxes
|
||||
id: no-duplicate-issues
|
||||
attributes:
|
||||
label: "Issues are for bug reports only, please go to the \"Discussions\" tab to submit a Feature Request"
|
||||
label: "Issues are for bug reports only"
|
||||
options:
|
||||
- label: "I understand"
|
||||
required: true
|
||||
|
|
8
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
8
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
|
@ -1,14 +1,14 @@
|
|||
name: 🚀 Feature Request (Please go to the "Discussions" tab to submit a Feature Request)
|
||||
description: "⚠️ Please go to the \"Discussions\" tab to submit a Feature Request"
|
||||
name: 🚀 Feature Request
|
||||
description: "Please go to the Discussions tab to submit a Feature Request"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ Please go to https://github.com/louislam/dockge/discussions/new?category=ask-for-help
|
||||
Please go to https://github.com/louislam/dockge/discussions/new?category=feature-request
|
||||
- type: checkboxes
|
||||
id: no-duplicate-issues
|
||||
attributes:
|
||||
label: "Issues are for bug reports only, please go to the \"Discussions\" tab to submit a Feature Request"
|
||||
label: "Issues are for bug reports only"
|
||||
options:
|
||||
- label: "I understand"
|
||||
required: true
|
||||
|
|
|
@ -131,7 +131,7 @@ Be sure to read the [guide](https://github.com/louislam/dockge/blob/master/CONTR
|
|||
|
||||
#### "Dockge"?
|
||||
|
||||
"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.
|
||||
"Dockge" is a coinage word which is created by myself. I hope it sounds like `Dodge`.
|
||||
|
||||
The naming idea came from Twitch emotes like `sadge`, `bedge` or `wokege`. They all end in `-ge`.
|
||||
|
||||
|
|
|
@ -32,8 +32,6 @@ import User from "./models/user";
|
|||
import childProcessAsync from "promisify-child-process";
|
||||
import { Terminal } from "./terminal";
|
||||
|
||||
import "dotenv/config";
|
||||
|
||||
export class DockgeServer {
|
||||
app : Express;
|
||||
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) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
const stack = await this.saveStack(socket, server, name, composeYAML, composeENV, isAdd);
|
||||
const stack = this.saveStack(socket, server, name, composeYAML, composeENV, isAdd);
|
||||
await stack.deploy(socket);
|
||||
server.sendStackList();
|
||||
callback({
|
||||
|
@ -234,7 +234,7 @@ export class DockerSocketHandler extends SocketHandler {
|
|||
socket.on("getDockerNetworkList", async (callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
const dockerNetworkList = await server.getDockerNetworkList();
|
||||
const dockerNetworkList = server.getDockerNetworkList();
|
||||
callback({
|
||||
ok: true,
|
||||
dockerNetworkList,
|
||||
|
@ -264,7 +264,7 @@ export class DockerSocketHandler extends SocketHandler {
|
|||
});
|
||||
}
|
||||
|
||||
async saveStack(socket : DockgeSocket, server : DockgeServer, name : unknown, composeYAML : unknown, composeENV : unknown, isAdd : unknown) : Promise<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");
|
||||
|
@ -280,7 +280,7 @@ export class DockerSocketHandler extends SocketHandler {
|
|||
}
|
||||
|
||||
const stack = new Stack(server, name, composeYAML, composeENV, false);
|
||||
await stack.save(isAdd);
|
||||
stack.save(isAdd);
|
||||
return stack;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { DockgeServer } from "./dockge-server";
|
||||
import fs, { promises as fsAsync } from "fs";
|
||||
import fs from "fs";
|
||||
import { log } from "./log";
|
||||
import yaml from "yaml";
|
||||
import { DockgeSocket, fileExists, ValidationError } from "./util-server";
|
||||
import { DockgeSocket, ValidationError } from "./util-server";
|
||||
import path from "path";
|
||||
import {
|
||||
COMBINED_TERMINAL_COLS,
|
||||
|
@ -99,15 +99,6 @@ export class Stack {
|
|||
|
||||
// Check YAML format
|
||||
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 {
|
||||
|
@ -155,35 +146,29 @@ export class Stack {
|
|||
* Save the stack to the disk
|
||||
* @param isAdd
|
||||
*/
|
||||
async save(isAdd : boolean) {
|
||||
save(isAdd : boolean) {
|
||||
this.validate();
|
||||
|
||||
let dir = this.path;
|
||||
|
||||
// Check if the name is used if isAdd
|
||||
if (isAdd) {
|
||||
if (await fileExists(dir)) {
|
||||
if (fs.existsSync(dir)) {
|
||||
throw new ValidationError("Stack name already exists");
|
||||
}
|
||||
|
||||
// Create the stack folder
|
||||
await fsAsync.mkdir(dir);
|
||||
fs.mkdirSync(dir);
|
||||
} else {
|
||||
if (!await fileExists(dir)) {
|
||||
if (!fs.existsSync(dir)) {
|
||||
throw new ValidationError("Stack not found");
|
||||
}
|
||||
}
|
||||
|
||||
// Write or overwrite the compose.yaml
|
||||
await fsAsync.writeFile(path.join(dir, this._composeFileName), this.composeYAML);
|
||||
|
||||
const envPath = path.join(dir, ".env");
|
||||
|
||||
fs.writeFileSync(path.join(dir, this._composeFileName), this.composeYAML);
|
||||
// Write or overwrite the .env
|
||||
// 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);
|
||||
}
|
||||
fs.writeFileSync(path.join(dir, ".env"), this.composeENV);
|
||||
}
|
||||
|
||||
async deploy(socket? : DockgeSocket) : Promise<number> {
|
||||
|
@ -203,7 +188,7 @@ export class Stack {
|
|||
}
|
||||
|
||||
// Remove the stack folder
|
||||
await fsAsync.rm(this.path, {
|
||||
fs.rmSync(this.path, {
|
||||
recursive: true,
|
||||
force: true
|
||||
});
|
||||
|
@ -233,12 +218,12 @@ export class Stack {
|
|||
stackList = new Map<string, Stack>();
|
||||
|
||||
// Scan the stacks directory, and get the stack list
|
||||
let filenameList = await fsAsync.readdir(stacksDir);
|
||||
let filenameList = fs.readdirSync(stacksDir);
|
||||
|
||||
for (let filename of filenameList) {
|
||||
try {
|
||||
// Check if it is a directory
|
||||
let stat = await fsAsync.stat(path.join(stacksDir, filename));
|
||||
let stat = fs.statSync(path.join(stacksDir, filename));
|
||||
if (!stat.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
|
@ -297,12 +282,7 @@ export class Stack {
|
|||
let res = await childProcessAsync.spawn("docker", [ "compose", "ls", "--all", "--format", "json" ], {
|
||||
encoding: "utf-8",
|
||||
});
|
||||
|
||||
if (!res.stdout) {
|
||||
return statusList;
|
||||
}
|
||||
|
||||
let composeList = JSON.parse(res.stdout.toString());
|
||||
let composeList = JSON.parse(res.toString());
|
||||
|
||||
for (let composeStack of composeList) {
|
||||
statusList.set(composeStack.Name, this.statusConvert(composeStack.Status));
|
||||
|
@ -334,7 +314,7 @@ export class Stack {
|
|||
let dir = path.join(server.stacksDir, stackName);
|
||||
|
||||
if (!skipFSOperations) {
|
||||
if (!await fileExists(dir) || !(await fsAsync.stat(dir)).isDirectory()) {
|
||||
if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
|
||||
// Maybe it is a stack managed by docker compose directly
|
||||
let stackList = await this.getStackList(server, true);
|
||||
let stack = stackList.get(stackName);
|
||||
|
|
|
@ -5,7 +5,6 @@ import { log } from "./log";
|
|||
import { ERROR_TYPE_VALIDATION } from "./util-common";
|
||||
import { R } from "redbean-node";
|
||||
import { verifyPassword } from "./password-hash";
|
||||
import fs from "fs";
|
||||
|
||||
export interface JWTDecoded {
|
||||
username : string;
|
||||
|
@ -83,9 +82,3 @@ export async function doubleCheckPassword(socket : DockgeSocket, currentPassword
|
|||
|
||||
return user;
|
||||
}
|
||||
|
||||
export function fileExists(file : string) {
|
||||
return fs.promises.access(file, fs.constants.F_OK)
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
}
|
||||
|
|
|
@ -152,14 +152,6 @@ export default {
|
|||
});
|
||||
|
||||
result.sort((m1, m2) => {
|
||||
|
||||
// sort by managed by dockge
|
||||
if (m1.isManagedByDockge && !m2.isManagedByDockge) {
|
||||
return -1;
|
||||
} else if (!m1.isManagedByDockge && m2.isManagedByDockge) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (m1.status !== m2.status) {
|
||||
if (m2.status === RUNNING) {
|
||||
return 1;
|
||||
|
|
|
@ -39,7 +39,7 @@ for (let lang in languageList) {
|
|||
};
|
||||
}
|
||||
|
||||
const rtlLangs = [ "fa", "ar-SY", "ur", "ar" ];
|
||||
const rtlLangs = [ "fa", "ar-SY", "ur" ];
|
||||
|
||||
export const currentLocale = () => localStorage.locale
|
||||
|| languageList[navigator.language] && navigator.language
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"registry": "Registro",
|
||||
"compose": "Compose",
|
||||
"addFirstStackMsg": "Componi il tuo primo stack!",
|
||||
"stackName": "Nome dello stack",
|
||||
"stackName" : "Nome dello stack",
|
||||
"deployStack": "Deploy",
|
||||
"deleteStack": "Cancella",
|
||||
"stopStack": "Stop",
|
||||
|
@ -23,7 +23,7 @@
|
|||
"editStack": "Modifica",
|
||||
"discardStack": "Annulla",
|
||||
"saveStackDraft": "Salva",
|
||||
"notAvailableShort": "N/D",
|
||||
"notAvailableShort" : "N/D",
|
||||
"deleteStackMsg": "Sei sicuro di voler eliminare questo stack?",
|
||||
"stackNotManagedByDockgeMsg": "Questo stack non è gestito da Dockge.",
|
||||
"primaryHostname": "Hostname primario",
|
||||
|
@ -91,11 +91,5 @@
|
|||
"Allowed commands:": "Comandi permessi:",
|
||||
"Internal Networks": "Reti interne",
|
||||
"External Networks": "Reti esterne",
|
||||
"No External Networks": "Nessuna rete esterna",
|
||||
"reverseProxyMsg1": "Utilizzando un proxy inverso?",
|
||||
"reverseProxyMsg2": "Controlla come configurarlo per WebSocket",
|
||||
"Cannot connect to the socket server.": "Impossibile connettersi al server socket.",
|
||||
"connecting...": "Connessione al server socket…",
|
||||
"extra": "Extra",
|
||||
"reconnecting...": "Riconnessione…"
|
||||
"No External Networks": "Nessuna rete esterna"
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
"editStack": "編集",
|
||||
"discardStack": "破棄",
|
||||
"saveStackDraft": "保存",
|
||||
"stackNotManagedByDockgeMsg": "このスタックはDockgeによって管理されていません。",
|
||||
"stackNotManagedByDockgeMsg": "このスタックはDockageによって管理されていません。",
|
||||
"general": "一般",
|
||||
"scanFolder": "スタックフォルダをスキャン",
|
||||
"dockerImage": "イメージ",
|
||||
|
|
|
@ -90,13 +90,5 @@
|
|||
"Allowed commands:": "허용된 명령어:",
|
||||
"Internal Networks": "내부 네트워크",
|
||||
"External Networks": "외부 네트워크",
|
||||
"No External Networks": "외부 네트워크 없음",
|
||||
"reverseProxyMsg2": "여기서 WebSocket을 위한 설정을 확인해 보세요",
|
||||
"downStack": "정지 & Down",
|
||||
"reverseProxyMsg1": "리버스 프록시를 사용하고 계신가요?",
|
||||
"Cannot connect to the socket server.": "소켓 서버에 연결하지 못했습니다.",
|
||||
"connecting...": "소켓 서버에 연결하는 중…",
|
||||
"extra": "기타",
|
||||
"url": "URL | URL",
|
||||
"reconnecting...": "재연결 중…"
|
||||
"No External Networks": "외부 네트워크 없음"
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "dockge",
|
||||
"version": "1.3.2",
|
||||
"version": "1.2.0",
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">= 18.0.0 && <= 18.17.1"
|
||||
|
@ -33,7 +33,6 @@
|
|||
"composerize": "~1.4.1",
|
||||
"croner": "~7.0.5",
|
||||
"dayjs": "~1.11.10",
|
||||
"dotenv": "~16.3.1",
|
||||
"express": "~4.18.2",
|
||||
"express-static-gzip": "~2.1.7",
|
||||
"http-graceful-shutdown": "~3.1.13",
|
||||
|
|
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
|
@ -32,9 +32,6 @@ dependencies:
|
|||
dayjs:
|
||||
specifier: ~1.11.10
|
||||
version: 1.11.10
|
||||
dotenv:
|
||||
specifier: ~16.3.1
|
||||
version: 16.3.1
|
||||
express:
|
||||
specifier: ~4.18.2
|
||||
version: 4.18.2
|
||||
|
@ -2157,11 +2154,6 @@ packages:
|
|||
esutils: 2.0.3
|
||||
dev: true
|
||||
|
||||
/dotenv@16.3.1:
|
||||
resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/eastasianwidth@0.2.0:
|
||||
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
||||
dev: false
|
||||
|
|
Loading…
Add table
Reference in a new issue