mirror of
https://github.com/louislam/dockge.git
synced 2025-08-12 16:27:03 +02:00
Compare commits
27 Commits
build-wind
...
1.3.5
Author | SHA1 | Date | |
---|---|---|---|
80e885e85d | |||
e54ede3f1c | |||
ac2a62abb1 | |||
e77ff3622d | |||
b5bd9a711a | |||
442c7fce67 | |||
7d55a84aa2 | |||
22bbba9652 | |||
3bc6779af4 | |||
3ef2be1c11 | |||
f6f7283f09 | |||
69e237a676 | |||
6a3eebfd57 | |||
3c137122b6 | |||
e2819afce1 | |||
94ca8a152a | |||
db0add7e4c | |||
0f52bb78b8 | |||
03c7815b58 | |||
80d5c685e5 | |||
a8482ec8ac | |||
07c52ccebb | |||
9b6b49947c | |||
3ba267a3dc | |||
01411f2d7e | |||
ba51031db6 | |||
daa8d12eee |
@ -6,7 +6,7 @@
|
||||
|
||||
A fancy, easy-to-use and reactive self-hosted docker compose.yaml stack-oriented manager.
|
||||
|
||||
      
|
||||
   
|
||||
|
||||
<img src="https://github.com/louislam/dockge/assets/1336778/26a583e1-ecb1-4a8d-aedf-76157d714ad7" width="900" alt="" />
|
||||
|
||||
|
@ -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) => {
|
||||
@ -578,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> {
|
||||
|
@ -162,9 +162,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}`
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
CREATED_FILE,
|
||||
@ -40,8 +41,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;
|
||||
@ -222,6 +222,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>;
|
||||
@ -242,6 +262,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);
|
||||
|
@ -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) {
|
||||
|
@ -21,6 +21,11 @@ export interface LooseObject {
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export interface BaseRes {
|
||||
ok: boolean;
|
||||
msg?: string;
|
||||
}
|
||||
|
||||
let randomBytes : (numBytes: number) => Uint8Array;
|
||||
initRandomBytes();
|
||||
|
||||
@ -111,6 +116,13 @@ export const allowedRawKeys = [
|
||||
"\u0003", // Ctrl + C
|
||||
];
|
||||
|
||||
export const acceptedComposeFileNames = [
|
||||
"compose.yaml",
|
||||
"docker-compose.yaml",
|
||||
"docker-compose.yml",
|
||||
"compose.yml",
|
||||
];
|
||||
|
||||
/**
|
||||
* Generate a decimal integer number from a string
|
||||
* @param str Input
|
||||
@ -380,7 +392,11 @@ function traverseYAML(pair : Pair, env : DotenvParseOutput) : void {
|
||||
if (item instanceof Pair) {
|
||||
traverseYAML(item, env);
|
||||
} else if (item instanceof Scalar) {
|
||||
item.value = envsubst(item.value, env);
|
||||
let value = item.value as unknown;
|
||||
|
||||
if (typeof(value) === "string") {
|
||||
item.value = envsubst(value, env);
|
||||
}
|
||||
}
|
||||
}
|
||||
// @ts-ignore
|
||||
|
@ -1,19 +1,37 @@
|
||||
// Generate on GitHub
|
||||
const input = `
|
||||
* Add Korean translation by @Alanimdeo in https://github.com/louislam/dockge/pull/86
|
||||
* Fixed envsubst issue by @louislam in https://github.com/louislam/dockge/pull/301
|
||||
* Fix: Only adding folders to stack with a compose file. by @Ozy-Viking in https://github.com/louislam/dockge/pull/299
|
||||
* Terminal text cols adjusts to terminal container. by @Ozy-Viking in https://github.com/louislam/dockge/pull/285
|
||||
* Update Docker Dompose plugin to 2.23.3 by @louislam in https://github.com/louislam/dockge/pull/303
|
||||
* Translations update from Kuma Weblate by @UptimeKumaBot in https://github.com/louislam/dockge/pull/302
|
||||
`;
|
||||
|
||||
const template = `
|
||||
|
||||
> [!WARNING]
|
||||
>
|
||||
|
||||
### 🆕 New Features
|
||||
-
|
||||
|
||||
### Improvements
|
||||
### ⬆️ Improvements
|
||||
-
|
||||
|
||||
### 🐞 Bug Fixes
|
||||
### 🐛 Bug Fixes
|
||||
-
|
||||
|
||||
### 🦎 Translation Contributions
|
||||
-
|
||||
|
||||
### ⬆️ Security Fixes
|
||||
-
|
||||
|
||||
### Others
|
||||
- Other small changes, code refactoring and comment/doc updates in this repo:
|
||||
-
|
||||
|
||||
Please let me know if your username is missing, if your pull request has been merged in this version, or your commit has been included in one of the pull requests.
|
||||
`;
|
||||
|
||||
const lines = input.split("\n").filter((line) => line.trim() !== "");
|
||||
@ -37,6 +55,12 @@ for (const line of lines) {
|
||||
}
|
||||
|
||||
message = message.split("* ").pop();
|
||||
console.log("-", pullRequestID, message, `(Thanks ${username})`);
|
||||
|
||||
let thanks = "";
|
||||
if (username != "@louislam") {
|
||||
thanks = `(Thanks ${username})`;
|
||||
}
|
||||
|
||||
console.log(pullRequestID, message, thanks);
|
||||
}
|
||||
console.log(template);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -5,7 +5,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Terminal } from "xterm";
|
||||
import { Terminal } from "@xterm/xterm";
|
||||
import { FitAddon } from "@xterm/addon-fit";
|
||||
import { WebLinksAddon } from "xterm-addon-web-links";
|
||||
import { TERMINAL_COLS, TERMINAL_ROWS } from "../../../backend/util-common";
|
||||
|
||||
@ -122,10 +123,12 @@ export default {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Fit the terminal width to the div container size after terminal is created.
|
||||
this.updateTerminalSize();
|
||||
},
|
||||
|
||||
unmounted() {
|
||||
window.removeEventListener("resize", this.onResizeEvent); // Remove the resize event listener from the window object.
|
||||
this.$root.unbindTerminal(this.name);
|
||||
this.terminal.dispose();
|
||||
},
|
||||
@ -208,6 +211,30 @@ export default {
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the terminal size to fit the container size.
|
||||
*
|
||||
* If the terminalFitAddOn is not created, creates it, loads it and then fits the terminal to the appropriate size.
|
||||
* It then addes an event listener to the window object to listen for resize events and calls the fit method of the terminalFitAddOn.
|
||||
*/
|
||||
updateTerminalSize() {
|
||||
if (!Object.hasOwn(this, "terminalFitAddOn")) {
|
||||
this.terminalFitAddOn = new FitAddon();
|
||||
this.terminal.loadAddon(this.terminalFitAddOn);
|
||||
window.addEventListener("resize", this.onResizeEvent);
|
||||
}
|
||||
this.terminalFitAddOn.fit();
|
||||
},
|
||||
/**
|
||||
* Handles the resize event of the terminal component.
|
||||
*/
|
||||
onResizeEvent() {
|
||||
this.terminalFitAddOn.fit();
|
||||
let rows = this.terminal.rows;
|
||||
let cols = this.terminal.cols;
|
||||
this.$root.getSocket().emit("terminalResize", this.name, rows, cols);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -12,7 +12,7 @@
|
||||
"registry": "Регистър",
|
||||
"compose": "Compose",
|
||||
"addFirstStackMsg": "Създайте вашия първи стак!",
|
||||
"stackName" : "Име на стак",
|
||||
"stackName": "Име на стак",
|
||||
"deployStack": "Разположи",
|
||||
"deleteStack": "Изтрий",
|
||||
"stopStack": "Спри",
|
||||
@ -22,7 +22,7 @@
|
||||
"editStack": "Редактирай",
|
||||
"discardStack": "Отхвърли",
|
||||
"saveStackDraft": "Запази",
|
||||
"notAvailableShort" : "N/A",
|
||||
"notAvailableShort": "N/A",
|
||||
"deleteStackMsg": "Сигурни ли сте, че желаете да изтриете този стак?",
|
||||
"stackNotManagedByDockgeMsg": "Този стак не се управлява от Dockge.",
|
||||
"primaryHostname": "Основно име на хост",
|
||||
@ -90,5 +90,13 @@
|
||||
"Allowed commands:": "Позволени команди:",
|
||||
"Internal Networks": "Вътрешни мрежи",
|
||||
"External Networks": "Външни мрежи",
|
||||
"No External Networks": "Не са налични външни мрежи"
|
||||
"No External Networks": "Не са налични външни мрежи",
|
||||
"reverseProxyMsg2": "Проверете как да го конфигурирате за WebSocket",
|
||||
"downStack": "Спри и изключи",
|
||||
"reverseProxyMsg1": "Използвате ревърс прокси?",
|
||||
"Cannot connect to the socket server.": "Не може да се свърже със сокет сървъра.",
|
||||
"url": "URL адрес | URL адреси",
|
||||
"extra": "Допълнително",
|
||||
"reconnecting...": "Повторно свързване…",
|
||||
"connecting...": "Свързване със сокет сървъра…"
|
||||
}
|
||||
|
@ -3,39 +3,39 @@
|
||||
"Create your admin account": "Vytvořit účet administrátora",
|
||||
"authIncorrectCreds": "Nesprávné uživatelské jméno nebo heslo.",
|
||||
"PasswordsDoNotMatch": "Hesla se neshodují.",
|
||||
"Repeat Password": "Opakujte heslo",
|
||||
"Repeat Password": "Napište Heslo Znovu",
|
||||
"Create": "Vytvořit",
|
||||
"signedInDisp": "Přihlášen jako {0}",
|
||||
"signedInDispDisabled": "Ověření zakázáno.",
|
||||
"signedInDispDisabled": "Ověření Zakázáno.",
|
||||
"home": "Domů",
|
||||
"console": "Konzole",
|
||||
"registry": "Registry",
|
||||
"compose": "Compose",
|
||||
"addFirstStackMsg": "Vytvořte svůj první stack!",
|
||||
"stackName": "Název stacku",
|
||||
"compose": "Komponovat",
|
||||
"addFirstStackMsg": "Vytvořte svůj první zásobník!",
|
||||
"stackName": "Název Zásobníku",
|
||||
"deployStack": "Nainstalovat",
|
||||
"deleteStack": "Smazat",
|
||||
"stopStack": "Zastavit",
|
||||
"restartStack": "Restartovat",
|
||||
"updateStack": "Aktualizovat",
|
||||
"startStack": "Spustit",
|
||||
"downStack": "Zastavit a vypnout",
|
||||
"downStack": "Zastavit & Vypnout",
|
||||
"editStack": "Upravit",
|
||||
"discardStack": "Zahodit",
|
||||
"saveStackDraft": "Uložit",
|
||||
"notAvailableShort": "N/A",
|
||||
"deleteStackMsg": "Opravdu chcete smazat tento stack?",
|
||||
"deleteStackMsg": "Opravdu chcete smazat tento zásobník?",
|
||||
"stackNotManagedByDockgeMsg": "Tento stack není spravován systémem Dockge.",
|
||||
"primaryHostname": "Primární název hostitele",
|
||||
"general": "Obecné",
|
||||
"container": "Kontejner | Kontejnery",
|
||||
"scanFolder": "Prohledat složku se stacky",
|
||||
"scanFolder": "Prohledat složku se zásobníky",
|
||||
"dockerImage": "Obrázek",
|
||||
"restartPolicyUnlessStopped": "Pokud není zastaveno",
|
||||
"restartPolicyAlways": "Vždy",
|
||||
"restartPolicyOnFailure": "Při selhání",
|
||||
"restartPolicyOnFailure": "Při Selhání",
|
||||
"restartPolicyNo": "Ne",
|
||||
"environmentVariable": "Proměnná prostředí | Proměnné prostředí",
|
||||
"environmentVariable": "Proměnná Prostředí | Proměnné Prostředí",
|
||||
"restartPolicy": "Politika restartu",
|
||||
"containerName": "Název kontejneru",
|
||||
"port": "Port | Porty",
|
||||
@ -91,5 +91,11 @@
|
||||
"Allowed commands:": "Povolené příkazy:",
|
||||
"Internal Networks": "Interní sítě",
|
||||
"External Networks": "Externí sítě",
|
||||
"No External Networks": "Žádné externí sítě"
|
||||
"No External Networks": "Žádné externí sítě",
|
||||
"reconnecting...": "Opětovné připojení…",
|
||||
"url": "Adresa URL | Adresy URL",
|
||||
"extra": "Extra",
|
||||
"reverseProxyMsg1": "Používáte Reverzní proxy server?",
|
||||
"reverseProxyMsg2": "Podívat se jak to nastavit pro WebSocket",
|
||||
"Cannot connect to the socket server.": "Nelze se připojit k serveru ."
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
"home": "Startseite",
|
||||
"console": "Konsole",
|
||||
"registry": "Container Registry",
|
||||
"compose": "",
|
||||
"compose": "Compose",
|
||||
"addFirstStackMsg": "Stelle deinen ersten Stack zusammen!",
|
||||
"stackName": "Stack-Name",
|
||||
"deployStack": "Deployen",
|
||||
@ -72,15 +72,15 @@
|
||||
"Check Update On GitHub": "Update auf GitHub überprüfen",
|
||||
"Show update if available": "Update anzeigen, wenn verfügbar",
|
||||
"Also check beta release": "Auch Beta-Version überprüfen",
|
||||
"Remember me": "Anmeldung beibehalten",
|
||||
"Remember me": "Angemeldet bleiben",
|
||||
"Login": "Anmelden",
|
||||
"Username": "Benutzername",
|
||||
"Password": "Passwort",
|
||||
"Settings": "Einstellungen",
|
||||
"Logout": "Abmelden",
|
||||
"Lowercase only": "Nur Kleinbuchstaben",
|
||||
"Convert to Compose": "In Compose Syntax umwandeln",
|
||||
"Docker Run": "Docker ausführen",
|
||||
"Convert to Compose": "In Compose-Syntax umwandeln",
|
||||
"Docker Run": "Docker Run",
|
||||
"active": "aktiv",
|
||||
"exited": "beendet",
|
||||
"inactive": "inaktiv",
|
||||
|
@ -98,5 +98,6 @@
|
||||
"downStack": "Arrêter et désactiver",
|
||||
"reverseProxyMsg1": "Utilisez vous un proxy inverse ?",
|
||||
"Cannot connect to the socket server.": "Impossible de se connecter au serveur socket.",
|
||||
"reconnecting...": "Reconnexion…"
|
||||
"reconnecting...": "Reconnexion…",
|
||||
"newUpdate": "Nouvelle mise à jour"
|
||||
}
|
||||
|
@ -10,10 +10,10 @@
|
||||
"home": "Home",
|
||||
"console": "Console",
|
||||
"registry": "Registro",
|
||||
"compose": "Compose",
|
||||
"compose": "Componi",
|
||||
"addFirstStackMsg": "Componi il tuo primo stack!",
|
||||
"stackName": "Nome dello stack",
|
||||
"deployStack": "Deploy",
|
||||
"deployStack": "Rilascia",
|
||||
"deleteStack": "Cancella",
|
||||
"stopStack": "Stop",
|
||||
"restartStack": "Riavvia",
|
||||
@ -75,7 +75,7 @@
|
||||
"Also check beta release": "Controlla anche le release in beta",
|
||||
"Remember me": "Ricordami",
|
||||
"Login": "Login",
|
||||
"Username": "Username",
|
||||
"Username": "Nome Utente",
|
||||
"Password": "Password",
|
||||
"Settings": "Impostazioni",
|
||||
"Logout": "Logout",
|
||||
@ -97,5 +97,6 @@
|
||||
"Cannot connect to the socket server.": "Impossibile connettersi al server socket.",
|
||||
"connecting...": "Connessione al server socket…",
|
||||
"extra": "Extra",
|
||||
"reconnecting...": "Riconnessione…"
|
||||
"reconnecting...": "Riconnessione…",
|
||||
"url": "Indirizzo | Indirizzi"
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
"registry": "Registro",
|
||||
"compose": "Compose",
|
||||
"addFirstStackMsg": "Crie sua primeira stack!",
|
||||
"stackName" : "Nome da stack",
|
||||
"stackName": "Nome da stack",
|
||||
"deployStack": "Deploy",
|
||||
"deleteStack": "Excluir",
|
||||
"stopStack": "Parar",
|
||||
@ -22,7 +22,7 @@
|
||||
"editStack": "Editar",
|
||||
"discardStack": "Descartar",
|
||||
"saveStackDraft": "Salvar",
|
||||
"notAvailableShort" : "N/D",
|
||||
"notAvailableShort": "N/D",
|
||||
"deleteStackMsg": "Tem certeza que deseja excluir esta stack?",
|
||||
"stackNotManagedByDockgeMsg": "Esta stack não é gerenciada pelo Dockge.",
|
||||
"primaryHostname": "Nome do Host Primário",
|
||||
@ -90,5 +90,13 @@
|
||||
"Allowed commands:": "Comandos permitidos:",
|
||||
"Internal Networks": "Redes internas",
|
||||
"External Networks": "Redes externas",
|
||||
"No External Networks": "Sem redes externas"
|
||||
"No External Networks": "Sem redes externas",
|
||||
"reverseProxyMsg2": "Veja como configurar para WebSocket",
|
||||
"downStack": "Parar & Encerrar",
|
||||
"reverseProxyMsg1": "Utiliza proxy reverso?",
|
||||
"Cannot connect to the socket server.": "Não é possível conectar ao socket server.",
|
||||
"connecting...": "Conectando ao socket server…",
|
||||
"url": "URL | URLs",
|
||||
"extra": "Extra",
|
||||
"reconnecting...": "Reconectando…"
|
||||
}
|
||||
|
@ -5,12 +5,12 @@
|
||||
"PasswordsDoNotMatch": "Пароль не совпадает.",
|
||||
"Repeat Password": "Повторите пароль",
|
||||
"Create": "Создать",
|
||||
"signedInDisp": "Авторизован как",
|
||||
"signedInDisp": "Авторизован как {0}",
|
||||
"signedInDispDisabled": "Авторизация выключена.",
|
||||
"home": "Главная",
|
||||
"console": "Консоль",
|
||||
"registry": "Registry",
|
||||
"compose": "Compose",
|
||||
"registry": "Реестр (Registry)",
|
||||
"compose": "Составить (Compose)",
|
||||
"addFirstStackMsg": "Создайте свой первый стек!",
|
||||
"stackName": "Имя стека",
|
||||
"deployStack": "Развернуть",
|
||||
@ -90,5 +90,13 @@
|
||||
"Allowed commands:": "Разрешенные команды:",
|
||||
"Internal Networks": "Внутренние сети",
|
||||
"External Networks": "Внешние сети",
|
||||
"No External Networks": "Нет внешних сетей"
|
||||
"No External Networks": "Нет внешних сетей",
|
||||
"downStack": "Остановить и выключить",
|
||||
"reverseProxyMsg1": "Использовать Реверс Прокси?",
|
||||
"reconnecting...": "Переподключение…",
|
||||
"Cannot connect to the socket server.": "Не удается подключиться к серверу сокетов.",
|
||||
"url": "URL адрес(а)",
|
||||
"extra": "Дополнительно",
|
||||
"reverseProxyMsg2": "Проверьте, как настроить его для WebSocket",
|
||||
"connecting...": "Подключение к серверу сокетов…"
|
||||
}
|
||||
|
@ -90,5 +90,10 @@
|
||||
"Allowed commands:": "Dovoljeni ukazi:",
|
||||
"Internal Networks": "Notranja omrežja",
|
||||
"External Networks": "Zunanja omrežja",
|
||||
"No External Networks": "Ni zunanjih omrežij"
|
||||
"No External Networks": "Ni zunanjih omrežij",
|
||||
"downStack": "Ustavi & Odstrani",
|
||||
"connecting...": "Povezovanje s strežnikom…",
|
||||
"reverseProxyMsg1": "Uporabljate obratni proxy?",
|
||||
"extra": "Dodatno",
|
||||
"reconnecting...": "Ponovna povezava …"
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"languageName": "Svenska",
|
||||
"Create your admin account": "Skapa ditt Admin-konto.",
|
||||
"Create your admin account": "Skapa ditt Admin-konto",
|
||||
"authIncorrectCreds": "Fel användarnamn eller lösenord.",
|
||||
"PasswordsDoNotMatch": "Lösenorden matchar inte.",
|
||||
"Repeat Password": "Repetera lösenord",
|
||||
@ -12,28 +12,28 @@
|
||||
"registry": "Register",
|
||||
"compose": "Komponera",
|
||||
"addFirstStackMsg": "Komponera din första stack!",
|
||||
"stackName" : "Stacknamn",
|
||||
"stackName": "Stacknamn",
|
||||
"deployStack": "Distribuera",
|
||||
"deleteStack": "Radera",
|
||||
"stopStack": "Stop",
|
||||
"stopStack": "Stoppa",
|
||||
"restartStack": "Starta om",
|
||||
"updateStack": "Uppdatera",
|
||||
"startStack": "Starta",
|
||||
"downStack": "Stop & Ner",
|
||||
"downStack": "Stoppa & Ner",
|
||||
"editStack": "Redigera",
|
||||
"discardStack": "Kasta",
|
||||
"saveStackDraft": "Spara",
|
||||
"notAvailableShort" : "N/A",
|
||||
"notAvailableShort": "N/A",
|
||||
"deleteStackMsg": "Är du säker på att du vill radera stacken?",
|
||||
"stackNotManagedByDockgeMsg": "Denna stacken hanteras inte av Dockge.",
|
||||
"primaryHostname": "Primärt värdnamn",
|
||||
"general": "Allmän",
|
||||
"container": "Container | Containrar",
|
||||
"scanFolder": "Scanna Stackfolder",
|
||||
"dockerImage": "Bild",
|
||||
"restartPolicyUnlessStopped": "Om inte stoppas",
|
||||
"scanFolder": "Skanna Stackmapp",
|
||||
"dockerImage": "Avbild",
|
||||
"restartPolicyUnlessStopped": "Om inte stoppad",
|
||||
"restartPolicyAlways": "Alltid",
|
||||
"restartPolicyOnFailure": "Vid Misslyckande",
|
||||
"restartPolicyOnFailure": "Vid misslyckande",
|
||||
"restartPolicyNo": "Nej",
|
||||
"environmentVariable": "Miljövariabel | Miljövariabler",
|
||||
"restartPolicy": "Omstartspolicy",
|
||||
@ -44,12 +44,12 @@
|
||||
"dependsOn": "Containerberoende | Containerberoenden",
|
||||
"addListItem": "Lägg till {0}",
|
||||
"deleteContainer": "Radera",
|
||||
"addContainer": "Lägg till Container",
|
||||
"addNetwork": "Lägg till Nätverk",
|
||||
"addContainer": "Lägg till container",
|
||||
"addNetwork": "Lägg till nätverk",
|
||||
"disableauth.message1": "Är du säker på att du vill <strong>inaktivera autentisering</strong>?",
|
||||
"disableauth.message2": "Det är designat för senarion <stong>när du ska implementera tredjeparts autentisering</strong> framör Dockge som Cloudflare Access, Authelia eller andra autentiseringsmekanismer.",
|
||||
"passwordNotMatchMsg": "Det upprepade lösenordet matchar inte",
|
||||
"autoGet": "Auto Hämta",
|
||||
"disableauth.message2": "Det är designat för scenarion <strong>där du ska implementera tredjepartsautentisering</strong> framför Dockge som Cloudflare Access, Authelia eller andra autentiseringsmekanismer.",
|
||||
"passwordNotMatchMsg": "Det upprepade lösenordet matchar inte.",
|
||||
"autoGet": "Auto-hämta",
|
||||
"add": "Lägg till",
|
||||
"Edit": "Redigera",
|
||||
"applyToYAML": "Lägg till i YAML",
|
||||
@ -57,8 +57,8 @@
|
||||
"addInternalNetwork": "Lägg till",
|
||||
"Save": "Spara",
|
||||
"Language": "Språk",
|
||||
"Current User": "Nuvarande användaren",
|
||||
"Change Password": "Byt lösenord",
|
||||
"Current User": "Nuvarande användare",
|
||||
"Change Password": "Ändra lösenord",
|
||||
"Current Password": "Nuvarande lösenord",
|
||||
"New Password": "Nytt lösenord",
|
||||
"Repeat New Password": "Upprepa nytt lösenord",
|
||||
@ -70,9 +70,9 @@
|
||||
"I understand, please disable": "Jag förstår, vänligen inaktivera",
|
||||
"Leave": "Lämna",
|
||||
"Frontend Version": "Frontendversion",
|
||||
"Check Update On GitHub": "Kontrollera Uppdatering på GitHub",
|
||||
"Check Update On GitHub": "Kontrollera uppdatering på GitHub",
|
||||
"Show update if available": "Visa uppdatering om tillgänglig",
|
||||
"Also check beta release": "Kontrollera även betaversionen",
|
||||
"Also check beta release": "Kontrollera även betaversioner",
|
||||
"Remember me": "Kom ihåg mig",
|
||||
"Login": "Logga in",
|
||||
"Username": "Användarnamn",
|
||||
@ -80,8 +80,8 @@
|
||||
"Settings": "Inställningar",
|
||||
"Logout": "Logga ut",
|
||||
"Lowercase only": "Endast små tecken",
|
||||
"Convert to Compose": "Omvandla till Compose",
|
||||
"Docker Run": "Docker Run",
|
||||
"Convert to Compose": "Omvandla till compose",
|
||||
"Docker Run": "Docker kör",
|
||||
"active": "aktiv",
|
||||
"exited": "avslutad",
|
||||
"inactive": "inaktiv",
|
||||
@ -89,7 +89,14 @@
|
||||
"Security": "Säkerhet",
|
||||
"About": "Om",
|
||||
"Allowed commands:": "Tillåtna kommandon:",
|
||||
"Internal Networks": "Interna Nätverk",
|
||||
"External Networks": "Externa Nätverk",
|
||||
"No External Networks": "Inga Externa Nätverk"
|
||||
"Internal Networks": "Interna nätverk",
|
||||
"External Networks": "Externa nätverk",
|
||||
"No External Networks": "Inga externa nätverk",
|
||||
"reverseProxyMsg1": "Används omvänd proxy?",
|
||||
"connecting...": "Ansluter till socketserver…",
|
||||
"Cannot connect to the socket server.": "Kan inte ansluta till socketservern.",
|
||||
"reverseProxyMsg2": "Kontrollera hur man konfigurerar webbsocket",
|
||||
"url": "URL | URLer",
|
||||
"extra": "Extra",
|
||||
"reconnecting...": "Återansluter…"
|
||||
}
|
||||
|
@ -91,5 +91,12 @@
|
||||
"Allowed commands:": "คำสั่งที่อนุญาต:",
|
||||
"Internal Networks": "เครือข่ายภายใน",
|
||||
"External Networks": "เครือข่ายภายนอก",
|
||||
"No External Networks": "ไม่มีเครือข่ายภายนอก"
|
||||
"No External Networks": "ไม่มีเครือข่ายภายนอก",
|
||||
"reverseProxyMsg2": "ตรวจสอบวิธีกำหนดค่าสำหรับ WebSocket",
|
||||
"Cannot connect to the socket server.": "ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ socket ได้",
|
||||
"reverseProxyMsg1": "ใช้ Reverse Proxy หรือไม่?",
|
||||
"connecting...": "กำลังเชื่อมต่อกับเซิร์ฟเวอร์ socket…",
|
||||
"url": "URL | URLs",
|
||||
"extra": "พิเศษ",
|
||||
"reconnecting...": "กำลังเชื่อมต่อใหม่…"
|
||||
}
|
@ -92,7 +92,7 @@
|
||||
"External Networks": "Зовнішні мережі",
|
||||
"No External Networks": "Немає зовнішніх мереж",
|
||||
"downStack": "Зупинити і вимкнути",
|
||||
"reverseProxyMsg1": "Використовувати зворотній проксі?",
|
||||
"reverseProxyMsg1": "Використовуєте зворотній проксі?",
|
||||
"Cannot connect to the socket server.": "Не вдається підключитися до сервера сокетів.",
|
||||
"reconnecting...": "Повторне підключення…",
|
||||
"connecting...": "Підключення до сервера сокетів…",
|
||||
|
@ -51,7 +51,7 @@
|
||||
"autoGet": "自動取得",
|
||||
"add": "新增",
|
||||
"Edit": "編輯",
|
||||
"applyToYAML": "應用到YAML",
|
||||
"applyToYAML": "套用到 YAML",
|
||||
"createExternalNetwork": "建立",
|
||||
"addInternalNetwork": "新增",
|
||||
"Save": "儲存",
|
||||
@ -71,7 +71,7 @@
|
||||
"Frontend Version": "前端版本",
|
||||
"Check Update On GitHub": "在 GitHub 上檢查更新",
|
||||
"Show update if available": "有更新時提醒我",
|
||||
"Also check beta release": "同時檢查 Beta 渠道更新",
|
||||
"Also check beta release": "同時檢查 Beta 版更新",
|
||||
"Remember me": "記住我",
|
||||
"Login": "登入",
|
||||
"Username": "使用者名稱",
|
||||
@ -92,7 +92,7 @@
|
||||
"External Networks": "外部網路",
|
||||
"No External Networks": "無外部網路",
|
||||
"downStack": "停止",
|
||||
"reverseProxyMsg1": "在使用反向代理吗?",
|
||||
"reverseProxyMsg1": "在使用反向代理嗎?",
|
||||
"reverseProxyMsg2": "點擊這裡了解如何為 WebSocket 配置反向代理",
|
||||
"Cannot connect to the socket server.": "無法連接到 Socket 伺服器。",
|
||||
"reconnecting...": "重新連線中…",
|
||||
|
@ -10,12 +10,12 @@ import { i18n } from "./i18n";
|
||||
// Dependencies
|
||||
import "bootstrap";
|
||||
import Toast, { POSITION, useToast } from "vue-toastification";
|
||||
import "xterm/lib/xterm.js";
|
||||
import "@xterm/xterm/lib/xterm.js";
|
||||
|
||||
// CSS
|
||||
import "@fontsource/jetbrains-mono";
|
||||
import "vue-toastification/dist/index.css";
|
||||
import "xterm/css/xterm.css";
|
||||
import "@xterm/xterm/css/xterm.css";
|
||||
import "./styles/main.scss";
|
||||
|
||||
// Minxins
|
||||
|
@ -2,7 +2,7 @@ import { io } from "socket.io-client";
|
||||
import { Socket } from "socket.io-client";
|
||||
import { defineComponent } from "vue";
|
||||
import jwtDecode from "jwt-decode";
|
||||
import { Terminal } from "xterm";
|
||||
import { Terminal } from "@xterm/xterm";
|
||||
|
||||
let socket : Socket;
|
||||
|
||||
@ -202,6 +202,10 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("refresh", () => {
|
||||
location.reload();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "dockge",
|
||||
"version": "1.3.2",
|
||||
"version": "1.3.5",
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">= 18.0.0 && <= 18.17.1"
|
||||
@ -55,8 +55,6 @@
|
||||
"yaml": "~2.3.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"concurrently": "^8.2.2",
|
||||
"wait-on": "^7.2.0",
|
||||
"@actions/github": "^6.0.0",
|
||||
"@fontsource/jetbrains-mono": "^5.0.18",
|
||||
"@fortawesome/fontawesome-svg-core": "6.4.2",
|
||||
@ -71,8 +69,11 @@
|
||||
"@typescript-eslint/eslint-plugin": "~6.8.0",
|
||||
"@typescript-eslint/parser": "~6.8.0",
|
||||
"@vitejs/plugin-vue": "~4.5.2",
|
||||
"@xterm/addon-fit": "beta",
|
||||
"@xterm/xterm": "beta",
|
||||
"bootstrap": "5.3.2",
|
||||
"bootstrap-vue-next": "~0.14.10",
|
||||
"concurrently": "^8.2.2",
|
||||
"cross-env": "~7.0.3",
|
||||
"eslint": "~8.50.0",
|
||||
"eslint-plugin-jsdoc": "~46.8.2",
|
||||
@ -90,7 +91,7 @@
|
||||
"vue-qrcode": "~2.2.0",
|
||||
"vue-router": "~4.2.5",
|
||||
"vue-toastification": "2.0.0-rc.5",
|
||||
"xterm": "5.4.0-beta.37",
|
||||
"wait-on": "^7.2.0",
|
||||
"xterm-addon-web-links": "~0.9.0"
|
||||
}
|
||||
}
|
||||
|
31
pnpm-lock.yaml
generated
31
pnpm-lock.yaml
generated
@ -133,6 +133,12 @@ devDependencies:
|
||||
'@vitejs/plugin-vue':
|
||||
specifier: ~4.5.2
|
||||
version: 4.5.2(vite@5.0.7)(vue@3.3.11)
|
||||
'@xterm/addon-fit':
|
||||
specifier: beta
|
||||
version: 0.9.0-beta.17(@xterm/xterm@5.4.0-beta.17)
|
||||
'@xterm/xterm':
|
||||
specifier: beta
|
||||
version: 5.4.0-beta.17
|
||||
bootstrap:
|
||||
specifier: 5.3.2
|
||||
version: 5.3.2(@popperjs/core@2.11.8)
|
||||
@ -196,12 +202,9 @@ devDependencies:
|
||||
wait-on:
|
||||
specifier: ^7.2.0
|
||||
version: 7.2.0
|
||||
xterm:
|
||||
specifier: 5.4.0-beta.37
|
||||
version: 5.4.0-beta.37
|
||||
xterm-addon-web-links:
|
||||
specifier: ~0.9.0
|
||||
version: 0.9.0(xterm@5.4.0-beta.37)
|
||||
version: 0.9.0(xterm@5.3.0)
|
||||
|
||||
packages:
|
||||
|
||||
@ -1569,6 +1572,18 @@ packages:
|
||||
- vue
|
||||
dev: true
|
||||
|
||||
/@xterm/addon-fit@0.9.0-beta.17(@xterm/xterm@5.4.0-beta.17):
|
||||
resolution: {integrity: sha512-4jYMsbyferF29hF0vCaQj6CQlT2A6ML6DhLZdi6cCZI5qn60mGUXZfDeO/Qoq2nmyK/PBrPyFYSOzWapSy45Zg==}
|
||||
peerDependencies:
|
||||
'@xterm/xterm': ^5.0.0
|
||||
dependencies:
|
||||
'@xterm/xterm': 5.4.0-beta.17
|
||||
dev: true
|
||||
|
||||
/@xterm/xterm@5.4.0-beta.17:
|
||||
resolution: {integrity: sha512-EhQsTHeO7VhCOnYLdrowWAqEbmZKf6k6Z/Rd3GriikB394jivux6OBcJbt7QdewIOAKqFah53d4rNXfz3/6mwQ==}
|
||||
dev: true
|
||||
|
||||
/abbrev@1.1.1:
|
||||
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
|
||||
requiresBuild: true
|
||||
@ -5524,16 +5539,16 @@ packages:
|
||||
engines: {node: '>=0.4.0'}
|
||||
dev: false
|
||||
|
||||
/xterm-addon-web-links@0.9.0(xterm@5.4.0-beta.37):
|
||||
/xterm-addon-web-links@0.9.0(xterm@5.3.0):
|
||||
resolution: {integrity: sha512-LIzi4jBbPlrKMZF3ihoyqayWyTXAwGfu4yprz1aK2p71e9UKXN6RRzVONR0L+Zd+Ik5tPVI9bwp9e8fDTQh49Q==}
|
||||
peerDependencies:
|
||||
xterm: ^5.0.0
|
||||
dependencies:
|
||||
xterm: 5.4.0-beta.37
|
||||
xterm: 5.3.0
|
||||
dev: true
|
||||
|
||||
/xterm@5.4.0-beta.37:
|
||||
resolution: {integrity: sha512-ys+mXqLFrJc7khmYN/MgBnfLv38NgXfkwkEXsCZKHGqn3h2xUBvTvsrSEWO3NQeDPLj4zMr1RwqTblMK9St3BA==}
|
||||
/xterm@5.3.0:
|
||||
resolution: {integrity: sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg==}
|
||||
dev: true
|
||||
|
||||
/y18n@4.0.3:
|
||||
|
Reference in New Issue
Block a user