Skip to content

Commit

Permalink
Merge branch 'master' into multiple
Browse files Browse the repository at this point in the history
# Conflicts:
#	backend/util-common.ts
#	frontend/src/pages/Compose.vue
#	package.json
#	pnpm-lock.yaml
  • Loading branch information
louislam committed Dec 17, 2023
2 parents 4d32398 + 80e885e commit 7118b52
Show file tree
Hide file tree
Showing 32 changed files with 1,379 additions and 556 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

A fancy, easy-to-use and reactive self-hosted docker compose.yaml stack-oriented manager.

![GitHub Repo stars](https://img.shields.io/github/stars/louislam/dockge?logo=github) ![GitHub issues](https://img.shields.io/github/issues/louislam/dockge?logo=github) ![GitHub pull requests](https://img.shields.io/github/issues-pr/louislam/dockge?logo=github) ![Docker Pulls](https://img.shields.io/docker/pulls/louislam/dockge?logo=docker) ![Docker Image Version (latest semver)](https://img.shields.io/docker/v/louislam/dockge/latest?label=docker%20image%20ver.) ![GitHub last commit (branch)](https://img.shields.io/github/last-commit/louislam/dockge/master?logo=github) ![GitHub](https://img.shields.io/github/license/louislam/dockge?logo=github)
![GitHub Repo stars](https://img.shields.io/github/stars/louislam/dockge?logo=github) ![Docker Pulls](https://img.shields.io/docker/pulls/louislam/dockge?logo=docker) ![Docker Image Version (latest semver)](https://img.shields.io/docker/v/louislam/dockge/latest?label=docker%20image%20ver.) ![GitHub last commit (branch)](https://img.shields.io/github/last-commit/louislam/dockge/master?logo=github)

<img src="https://github.com/louislam/dockge/assets/1336778/26a583e1-ecb1-4a8d-aedf-76157d714ad7" width="900" alt="" />

Expand Down Expand Up @@ -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 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`.

Expand All @@ -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
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.

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?

Expand Down
84 changes: 35 additions & 49 deletions backend/check-version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,69 +3,55 @@ import compareVersions from "compare-versions";
import packageJSON from "../package.json";
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
const UPDATE_CHECKER_INTERVAL_MS = 1000 * 60 * 60 * 48;
const CHECK_URL = "https://dockge.kuma.pet/version";

let interval : NodeJS.Timeout;

export function startInterval() {
const check = async () => {
if (await Settings.get("checkUpdate") === false) {
return;
}

log.debug("update-checker", "Retrieving latest versions");

try {
const res = await fetch(CHECK_URL);
const data = await res.json();
class CheckVersion {
version = packageJSON.version;
latestVersion? : string;
interval? : NodeJS.Timeout;

// For debug
if (process.env.TEST_CHECK_VERSION === "1") {
data.slow = "1000.0.0";
async startInterval() {
const check = async () => {
if (await Settings.get("checkUpdate") === false) {
return;
}

const checkBeta = await Settings.get("checkBeta");
log.debug("update-checker", "Retrieving latest versions");

if (checkBeta && data.beta) {
if (compareVersions.compare(data.beta, data.slow, ">")) {
obj.latestVersion = data.beta;
return;
}
}
try {
const res = await fetch(CHECK_URL);
const data = await res.json();

if (data.slow) {
obj.latestVersion = data.slow;
}
// For debug
if (process.env.TEST_CHECK_VERSION === "1") {
data.slow = "1000.0.0";
}

} catch (_) {
log.info("update-checker", "Failed to check for new versions");
}
const checkBeta = await Settings.get("checkBeta");

};
if (checkBeta && data.beta) {
if (compareVersions.compare(data.beta, data.slow, ">")) {
this.latestVersion = data.beta;
return;
}
}

check();
interval = setInterval(check, UPDATE_CHECKER_INTERVAL_MS);
}
if (data.slow) {
this.latestVersion = data.slow;
}

/**
* 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);
} catch (_) {
log.info("update-checker", "Failed to check for new versions");
}

clearInterval(interval);
};

if (value) {
startInterval();
await check();
this.interval = setInterval(check, UPDATE_CHECKER_INTERVAL_MS);
}
}

const checkVersion = new CheckVersion();
export default checkVersion;
3 changes: 0 additions & 3 deletions backend/docker.ts

This file was deleted.

65 changes: 65 additions & 0 deletions backend/dockge-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,39 @@ export class DockgeServer {
// Create Socket.io
this.io = new socketIO.Server(this.httpServer, {
cors,
allowRequest: (req, callback) => {
let isOriginValid = true;
const bypass = isDev;

if (!bypass) {
let host = req.headers.host;

// If this is set, it means the request is from the browser
let origin = req.headers.origin;

// If this is from the browser, check if the origin is allowed
if (origin) {
try {
let originURL = new URL(origin);

if (host !== originURL.host) {
isOriginValid = false;
log.error("auth", `Origin (${origin}) does not match host (${host}), IP: ${req.socket.remoteAddress}`);
}
} catch (e) {
// Invalid origin url, probably not from browser
isOriginValid = false;
log.error("auth", `Invalid origin url (${origin}), IP: ${req.socket.remoteAddress}`);
}
} else {
log.info("auth", `Origin is not set, IP: ${req.socket.remoteAddress}`);
}
} else {
log.debug("auth", "Origin check is bypassed");
}

callback(null, isOriginValid);
}
});

this.io.on("connection", async (socket: Socket) => {
Expand Down Expand Up @@ -325,6 +358,7 @@ export class DockgeServer {
this.sendStackList(true);
});

checkVersion.startInterval();
});

gracefulShutdown(this.httpServer, {
Expand Down Expand Up @@ -594,4 +628,35 @@ export class DockgeServer {
finalFunction() {
log.info("server", "Graceful shutdown successful!");
}

/**
* Force connected sockets of a user to refresh and disconnect.
* Used for resetting password.
* @param {string} userID
* @param {string?} currentSocketID
*/
disconnectAllSocketClients(userID: number, currentSocketID? : string) {
for (const rawSocket of this.io.sockets.sockets.values()) {
let socket = rawSocket as DockgeSocket;
if (socket.userID === userID && socket.id !== currentSocketID) {
try {
socket.emit("refresh");
socket.disconnect();
} catch (e) {

}
}
}
}

isSSL() {
return this.config.sslKey && this.config.sslCert;
}

getLocalWebSocketURL() {
const protocol = this.isSSL() ? "wss" : "ws";
const host = this.config.hostname || "localhost";
return `${protocol}://${host}:${this.config.port}`;
}

}
14 changes: 14 additions & 0 deletions backend/socket-handlers/main-socket-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,8 @@ export class MainSocketHandler extends SocketHandler {
let user = await doubleCheckPassword(socket, password.currentPassword);
await user.resetPassword(password.newPassword);

server.disconnectAllSocketClients(user.id, socket.id);

callback({
ok: true,
msg: "Password has been updated successfully.",
Expand Down Expand Up @@ -278,6 +280,18 @@ export class MainSocketHandler extends SocketHandler {
}
}
});

// Disconnect all other socket clients of the user
socket.on("disconnectOtherSocketClients", async () => {
try {
checkLogin(socket);
server.disconnectAllSocketClients(socket.userID, socket.id);
} catch (e) {
if (e instanceof Error) {
log.warn("disconnectOtherSocketClients", e.message);
}
}
});
}

async login(username : string, password : string) : Promise<User | null> {
Expand Down
43 changes: 39 additions & 4 deletions backend/socket-handlers/terminal-socket-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,44 @@ export class TerminalSocketHandler extends SocketHandler {
}
});

// TODO: Resize Terminal
socket.on("terminalResize", async (rows : unknown) => {

});
// Resize Terminal
socket.on(
"terminalResize",
async (terminalName: unknown, rows: unknown, cols: unknown) => {
log.info("terminalResize", `Terminal: ${terminalName}`);
try {
checkLogin(socket);
if (typeof terminalName !== "string") {
throw new Error("Terminal name must be a string.");
}

if (typeof rows !== "number") {
throw new Error("Command must be a number.");
}
if (typeof cols !== "number") {
throw new Error("Command must be a number.");
}

let terminal = Terminal.getTerminal(terminalName);

// log.info("terminal", terminal);
if (terminal instanceof Terminal) {
//log.debug("terminalInput", "Terminal found, writing to terminal.");
terminal.rows = rows;
terminal.cols = cols;
} else {
throw new Error(`${terminalName} Terminal not found.`);
}
} catch (e) {
log.debug(
"terminalResize",
// Added to prevent the lint error when adding the type
// and ts type checker saying type is unknown.
// @ts-ignore
`Error on ${terminalName}: ${e.message}`
);
}
}
);
}
}
28 changes: 26 additions & 2 deletions backend/stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import yaml from "yaml";
import { DockgeSocket, fileExists, ValidationError } from "./util-server";
import path from "path";
import {
acceptedComposeFileNames,
COMBINED_TERMINAL_COLS,
COMBINED_TERMINAL_ROWS, convertToRemoteStackID,
CREATED_FILE,
Expand Down Expand Up @@ -42,8 +43,7 @@ export class Stack {

if (!skipFSOperations) {
// Check if compose file name is different from compose.yaml
const supportedFileNames = [ "compose.yaml", "compose.yml", "docker-compose.yml", "docker-compose.yaml" ];
for (const filename of supportedFileNames) {
for (const filename of acceptedComposeFileNames) {
if (fs.existsSync(path.join(this.path, filename))) {
this._composeFileName = filename;
break;
Expand Down Expand Up @@ -234,6 +234,26 @@ export class Stack {
}
}

/**
* Checks if a compose file exists in the specified directory.
* @async
* @static
* @param {string} stacksDir - The directory of the stack.
* @param {string} filename - The name of the directory to check for the compose file.
* @returns {Promise<boolean>} A promise that resolves to a boolean indicating whether any compose file exists.
*/
static async composeFileExists(stacksDir : string, filename : string) : Promise<boolean> {
let filenamePath = path.join(stacksDir, filename);
// Check if any compose file exists
for (const filename of acceptedComposeFileNames) {
let composeFile = path.join(filenamePath, filename);
if (await fileExists(composeFile)) {
return true;
}
}
return false;
}

static async getStackList(server : DockgeServer, useCacheForManaged = false) : Promise<Map<string, Stack>> {
let stacksDir = server.stacksDir;
let stackList : Map<string, Stack>;
Expand All @@ -254,6 +274,10 @@ export class Stack {
if (!stat.isDirectory()) {
continue;
}
// If no compose file exists, skip it
if (!await Stack.composeFileExists(stacksDir, filename)) {
continue;
}
let stack = await this.getStack(server, filename);
stack._status = CREATED_FILE;
stackList.set(filename, stack);
Expand Down
1 change: 1 addition & 0 deletions backend/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export class Terminal {

set cols(cols : number) {
this._cols = cols;
log.debug("Terminal", `Terminal cols: ${this._cols}`); // Added to check if cols is being set when changing terminal size.
try {
this.ptyProcess?.resize(this.cols, this.rows);
} catch (e) {
Expand Down
Loading

0 comments on commit 7118b52

Please sign in to comment.