mirror of
https://github.com/louislam/dockge.git
synced 2025-08-13 13:17:50 +02:00
Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
f3dd323daa | |||
3848e8cbb9 | |||
d32bd3937f | |||
007eac7b58 | |||
b945ddea55 | |||
5bbd3a906a | |||
fd7f539089 | |||
16a5f2a175 | |||
bd58de535e | |||
7a0d4fc62a | |||
d0712d77ab |
@ -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?
|
||||
|
||||
|
@ -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;
|
||||
class CheckVersion {
|
||||
version = packageJSON.version;
|
||||
latestVersion? : string;
|
||||
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();
|
||||
|
||||
// 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();
|
||||
|
||||
// For debug
|
||||
if (process.env.TEST_CHECK_VERSION === "1") {
|
||||
data.slow = "1000.0.0";
|
||||
}
|
||||
|
||||
const checkBeta = await Settings.get("checkBeta");
|
||||
|
||||
if (checkBeta && data.beta) {
|
||||
if (compareVersions.compare(data.beta, data.slow, ">")) {
|
||||
this.latestVersion = data.beta;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (data.slow) {
|
||||
this.latestVersion = data.slow;
|
||||
}
|
||||
|
||||
} catch (_) {
|
||||
log.info("update-checker", "Failed to check for new versions");
|
||||
}
|
||||
|
||||
if (data.slow) {
|
||||
obj.latestVersion = data.slow;
|
||||
}
|
||||
};
|
||||
|
||||
} catch (_) {
|
||||
log.info("update-checker", "Failed to check for new versions");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
check();
|
||||
interval = setInterval(check, UPDATE_CHECKER_INTERVAL_MS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
clearInterval(interval);
|
||||
|
||||
if (value) {
|
||||
startInterval();
|
||||
await check();
|
||||
this.interval = setInterval(check, UPDATE_CHECKER_INTERVAL_MS);
|
||||
}
|
||||
}
|
||||
|
||||
const checkVersion = new CheckVersion();
|
||||
export default checkVersion;
|
||||
|
@ -1,3 +0,0 @@
|
||||
export class Docker {
|
||||
|
||||
}
|
@ -194,6 +194,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) => {
|
||||
@ -308,6 +341,7 @@ export class DockgeServer {
|
||||
this.sendStackList(true);
|
||||
});
|
||||
|
||||
checkVersion.startInterval();
|
||||
});
|
||||
|
||||
gracefulShutdown(this.httpServer, {
|
||||
@ -577,4 +611,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}`;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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.",
|
||||
@ -280,6 +282,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> {
|
||||
|
@ -21,6 +21,11 @@ export interface LooseObject {
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export interface BaseRes {
|
||||
ok: boolean;
|
||||
msg?: string;
|
||||
}
|
||||
|
||||
let randomBytes : (numBytes: number) => Uint8Array;
|
||||
initRandomBytes();
|
||||
|
||||
|
@ -4,6 +4,8 @@ import readline from "readline";
|
||||
import { User } from "../backend/models/user";
|
||||
import { DockgeServer } from "../backend/dockge-server";
|
||||
import { log } from "../backend/log";
|
||||
import { io } from "socket.io-client";
|
||||
import { BaseRes } from "../backend/util-common";
|
||||
|
||||
console.log("== Dockge Reset Password Tool ==");
|
||||
|
||||
@ -12,11 +14,10 @@ const rl = readline.createInterface({
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
const server = new DockgeServer();
|
||||
|
||||
export const main = async () => {
|
||||
const server = new DockgeServer();
|
||||
|
||||
// Check if
|
||||
|
||||
console.log("Connecting the database");
|
||||
try {
|
||||
await Database.init(server);
|
||||
@ -47,12 +48,16 @@ export const main = async () => {
|
||||
// Reset all sessions by reset jwt secret
|
||||
await server.initJWTSecret();
|
||||
|
||||
console.log("Password reset successfully.");
|
||||
|
||||
// Disconnect all other socket clients of the user
|
||||
await disconnectAllSocketClients(user.username, password);
|
||||
|
||||
break;
|
||||
} else {
|
||||
console.log("Passwords do not match, please try again.");
|
||||
}
|
||||
}
|
||||
console.log("Password reset successfully.");
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
@ -79,6 +84,47 @@ function question(question : string) : Promise<string> {
|
||||
});
|
||||
}
|
||||
|
||||
function disconnectAllSocketClients(username : string, password : string) : Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
const url = server.getLocalWebSocketURL();
|
||||
|
||||
console.log("Connecting to " + url + " to disconnect all other socket clients");
|
||||
|
||||
// Disconnect all socket connections
|
||||
const socket = io(url, {
|
||||
transports: [ "websocket" ],
|
||||
reconnection: false,
|
||||
timeout: 5000,
|
||||
});
|
||||
socket.on("connect", () => {
|
||||
socket.emit("login", {
|
||||
username,
|
||||
password,
|
||||
}, (res : BaseRes) => {
|
||||
if (res.ok) {
|
||||
console.log("Logged in.");
|
||||
socket.emit("disconnectOtherSocketClients");
|
||||
} else {
|
||||
console.warn("Login failed.");
|
||||
console.warn("Please restart the server to disconnect all sessions.");
|
||||
}
|
||||
socket.close();
|
||||
});
|
||||
});
|
||||
|
||||
socket.on("connect_error", function () {
|
||||
// The localWebSocketURL is not guaranteed to be working for some complicated Uptime Kuma setup
|
||||
// Ask the user to restart the server manually
|
||||
console.warn("Failed to connect to " + url);
|
||||
console.warn("Please restart the server to disconnect all sessions manually.");
|
||||
resolve();
|
||||
});
|
||||
socket.on("disconnect", () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (!process.env.TEST_BACKEND) {
|
||||
main();
|
||||
}
|
||||
|
@ -98,5 +98,6 @@
|
||||
"reconnecting...": "Reconnecting…",
|
||||
"connecting...": "Connecting to the socket server…",
|
||||
"url": "URL | URLs",
|
||||
"extra": "Extra"
|
||||
"extra": "Extra",
|
||||
"newUpdate": "New Update"
|
||||
}
|
||||
|
@ -16,8 +16,8 @@
|
||||
<span class="fs-4 title">Dockge</span>
|
||||
</router-link>
|
||||
|
||||
<a v-if="hasNewVersion" target="_blank" href="https://github.com/louislam/dockge/releases" class="btn btn-info me-3">
|
||||
<font-awesome-icon icon="arrow-alt-circle-up" /> {{ $t("New Update") }}
|
||||
<a v-if="hasNewVersion" target="_blank" href="https://github.com/louislam/dockge/releases" class="btn btn-warning me-3">
|
||||
<font-awesome-icon icon="arrow-alt-circle-up" /> {{ $t("newUpdate") }}
|
||||
</a>
|
||||
|
||||
<ul class="nav nav-pills">
|
||||
|
@ -202,6 +202,10 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("refresh", () => {
|
||||
location.reload();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -374,6 +374,17 @@ export default {
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
|
||||
"stack.composeENV": {
|
||||
handler() {
|
||||
if (this.editorFocus) {
|
||||
console.debug("env code changed");
|
||||
this.yamlCodeChange();
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
|
||||
jsonConfig: {
|
||||
handler() {
|
||||
if (!this.editorFocus) {
|
||||
|
Reference in New Issue
Block a user