Compare commits

..

1 Commits

Author SHA1 Message Date
f6a75d08c3 Remove compose version 2023-11-25 03:05:58 +08:00
48 changed files with 250 additions and 1381 deletions

View File

@ -1,4 +1,5 @@
labels: [help] title: "❓ Ask for help"
labels: [help]
body: body:
- type: checkboxes - type: checkboxes
id: no-duplicate-issues id: no-duplicate-issues

View File

@ -1,3 +1,4 @@
title: 🚀 Feature Request
labels: [feature-request] labels: [feature-request]
body: body:
- type: checkboxes - type: checkboxes
@ -51,4 +52,4 @@ body:
attributes: attributes:
label: "📝 Additional Context" label: "📝 Additional Context"
description: "Add any other context or screenshots about the feature request here." description: "Add any other context or screenshots about the feature request here."
placeholder: "..." placeholder: "..."

View File

@ -1,14 +1,14 @@
name: "⚠️ Ask for help (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" description: "Please go to the Discussions tab to submit a Help Request"
body: body:
- type: markdown - type: markdown
attributes: attributes:
value: | 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 - type: checkboxes
id: no-duplicate-issues id: no-duplicate-issues
attributes: 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: options:
- label: "I understand" - label: "I understand"
required: true required: true

View File

@ -1,14 +1,14 @@
name: 🚀 Feature Request (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" description: "Please go to the Discussions tab to submit a Feature Request"
body: body:
- type: markdown - type: markdown
attributes: attributes:
value: | 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 - type: checkboxes
id: no-duplicate-issues id: no-duplicate-issues
attributes: 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: options:
- label: "I understand" - label: "I understand"
required: true required: true

View File

@ -14,8 +14,8 @@ jobs:
ci: ci:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, windows-latest, macos-latest, ARM64] os: [ubuntu-latest, windows-latest, macos-latest]
node: [18.17.1] # Can be changed node: [20.x] # Can be changed
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout Code - name: Checkout Code

View File

@ -1,16 +0,0 @@
name: Prevent File Change
on:
pull_request:
jobs:
check-file-changes:
runs-on: ubuntu-latest
steps:
- name: Prevent file change
uses: xalvarez/prevent-file-change-action@v1
with:
githubToken: ${{ secrets.GITHUB_TOKEN }}
# Regex, /src/lang/*.json is not allowed to be changed, except for /src/lang/en.json
pattern: '^(?!frontend/src/lang/en\.json$)frontend/src/lang/.*\.json$'
trustedAuthors: UptimeKumaBot

View File

@ -7,15 +7,13 @@ Here are some references:
### ✅ Usually accepted: ### ✅ Usually accepted:
- Bug fix - Bug fix
- Security fix - Security fix
- Adding new language files (see [these instructions](https://github.com/louislam/dockge/blob/master/frontend/src/lang/README.md)) - Translation
- Adding new language keys: `$t("...")`
### ⚠️ Discussion required: ### ⚠️ Discussion required:
- Large pull requests - Large pull requests
- New features - New features
### ❌ Won't be merged: ### ❌ Won't be merged:
- A dedicated PR for translating existing languages (see [these instructions](https://github.com/louislam/dockge/blob/master/frontend/src/lang/README.md))
- Do not pass the auto-test - Do not pass the auto-test
- Any breaking changes - Any breaking changes
- Duplicated pull requests - Duplicated pull requests

View File

@ -34,18 +34,12 @@ View Video: https://youtu.be/AWAlOQeNpgU?t=48
## 🔧 How to Install ## 🔧 How to Install
Requirements: Requirements:
- [Docker](https://docs.docker.com/engine/install/) 20+ / Podman - [Docker CE](https://docs.docker.com/engine/install/) 20+ is recommended / Podman
- (Docker only) [Docker Compose Plugin](https://docs.docker.com/compose/install/linux/)
- (Podman only) podman-docker (Debian: `apt install podman-docker`) - (Podman only) podman-docker (Debian: `apt install podman-docker`)
- OS: - OS:
- Major Linux distros that can run Docker/Podman such as: - As long as you can run Docker CE / Podman, it should be fine, but:
- ✅ Ubuntu - Debian/Raspbian Buster or lower is not supported, please upgrade to Bullseye or higher
- ✅ Debian (Bullseye or newer)
- ✅ Raspbian (Bullseye or newer)
- ✅ CentOS
- ✅ Fedora
- ✅ ArchLinux
- ❌ Debian/Raspbian Buster or lower is not supported
- ❌ Windows (Will be supported later)
- Arch: armv7, arm64, amd64 (a.k.a x86_64) - Arch: armv7, arm64, amd64 (a.k.a x86_64)
### Basic ### Basic
@ -54,14 +48,14 @@ Requirements:
- Default Port: 5001 - Default Port: 5001
``` ```
# Create directories that store your stacks and stores Dockge's stack # Create a directory that stores your stacks and stores dockge's compose.yaml
mkdir -p /opt/stacks /opt/dockge mkdir -p /opt/stacks /opt/dockge
cd /opt/dockge cd /opt/dockge
# Download the compose.yaml # Download the compose.yaml
curl https://raw.githubusercontent.com/louislam/dockge/master/compose.yaml --output compose.yaml curl https://raw.githubusercontent.com/louislam/dockge/master/compose.yaml --output compose.yaml
# Start the server # Start the Server
docker compose up -d docker compose up -d
# If you are using docker-compose V1 or Podman # If you are using docker-compose V1 or Podman
@ -72,24 +66,40 @@ Dockge is now running on http://localhost:5001
### Advanced ### Advanced
If you want to store your stacks in another directory, you can generate your compose.yaml file by using the following URL with custom query strings. If you want to store your stacks in another directory, you can change the `DOCKGE_STACKS_DIR` environment variable and volumes.
```yaml
version: "3.8"
services:
dockge:
image: louislam/dockge:1
restart: unless-stopped
ports:
# Host Port : Container Port
- 5001:5001
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./data:/app/data
# If you want to use private registries, you need to share the auth file with Dockge:
# - /root/.docker/:/root/.docker
# Your stacks directory in the host (The paths inside container must be the same as the host)
# ⚠️⚠️ If you did it wrong, your data could end up be written into a wrong path.
# ✔️✔️✔️✔️ CORRECT: - /my-stacks:/my-stacks (Both paths match)
# ❌❌❌❌ WRONG: - /docker:/my-stacks (Both paths do not match)
- /opt/stacks:/opt/stacks
environment:
# Tell Dockge where is your stacks directory
- DOCKGE_STACKS_DIR=/opt/stacks
``` ```
# Download your compose.yaml
curl "https://dockge.kuma.pet/compose.yaml?port=5001&stacksPath=/opt/stacks" --output compose.yaml
```
- port=`5001`
- stacksPath=`/opt/stacks`
Interactive compose.yaml generator is available on:
https://dockge.kuma.pet
## How to Update ## How to Update
```bash ```bash
cd /opt/dockge cd /opt/dockge
docker compose pull && docker compose up -d docker compose pull
docker compose up -d
``` ```
## Screenshots ## Screenshots
@ -112,7 +122,7 @@ docker compose pull && docker compose up -d
If you love this project, please consider giving it a ⭐. If you love this project, please consider giving it a ⭐.
## 🗣️ Community and Contribution ## 🗣️
### Bug Report ### Bug Report
https://github.com/louislam/dockge/issues https://github.com/louislam/dockge/issues
@ -120,13 +130,10 @@ https://github.com/louislam/dockge/issues
### Ask for Help / Discussions ### Ask for Help / Discussions
https://github.com/louislam/dockge/discussions https://github.com/louislam/dockge/discussions
### Translation ## Translation
If you want to translate Dockge into your language, please read [Translation Guide](https://github.com/louislam/dockge/blob/master/frontend/src/lang/README.md) If you want to translate Dockge into your language, please read [Translation Guide](https://github.com/louislam/dockge/blob/master/frontend/src/lang/README.md)
### Create a Pull Request
Be sure to read the [guide](https://github.com/louislam/dockge/blob/master/CONTRIBUTING.md), as we don't accept all types of pull requests and don't want to waste your time.
## FAQ ## FAQ
#### "Dockge"? #### "Dockge"?
@ -148,18 +155,17 @@ 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 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 4. Now you should see your stack in the list
#### Is Dockge a Portainer replcement? ## More Ideas?
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. - Stats
- File manager
- App store for yaml templates
- Get app icons
- Switch Docker context
- Support Dockerfile and build
- Support Docker swarm
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. # Others
#### Can I install both Dockge and Portainer?
Yes, you can.
## Others
Dockge is built on top of [Compose V2](https://docs.docker.com/compose/migrate/). `compose.yaml` also known as `docker-compose.yml`. Dockge is built on top of [Compose V2](https://docs.docker.com/compose/migrate/). `compose.yaml` also known as `docker-compose.yml`.

View File

@ -29,11 +29,9 @@ import { Stack } from "./stack";
import { Cron } from "croner"; import { Cron } from "croner";
import gracefulShutdown from "http-graceful-shutdown"; import gracefulShutdown from "http-graceful-shutdown";
import User from "./models/user"; import User from "./models/user";
import childProcessAsync from "promisify-child-process"; import childProcess from "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;
@ -152,6 +150,9 @@ export class DockgeServer {
} }
} }
// Create all the necessary directories
this.initDataDir();
// Create express // Create express
this.app = express(); this.app = express();
@ -254,9 +255,6 @@ export class DockgeServer {
* *
*/ */
async serve() { async serve() {
// Create all the necessary directories
this.initDataDir();
// Connect to database // Connect to database
try { try {
await Database.init(this); await Database.init(this);
@ -485,7 +483,7 @@ export class DockgeServer {
return jwtSecretBean; return jwtSecretBean;
} }
async sendStackList(useCache = false) { sendStackList(useCache = false) {
let roomList = this.io.sockets.adapter.rooms.keys(); let roomList = this.io.sockets.adapter.rooms.keys();
let map : Map<string, object> | undefined; let map : Map<string, object> | undefined;
@ -496,7 +494,7 @@ export class DockgeServer {
// Get the list only if there is a room // Get the list only if there is a room
if (!map) { if (!map) {
map = new Map(); map = new Map();
let stackList = await Stack.getStackList(this, useCache); let stackList = Stack.getStackList(this, useCache);
for (let [ stackName, stack ] of stackList) { for (let [ stackName, stack ] of stackList) {
map.set(stackName, stack.toSimpleJSON()); map.set(stackName, stack.toSimpleJSON());
@ -512,8 +510,8 @@ export class DockgeServer {
} }
} }
async sendStackStatusList() { sendStackStatusList() {
let statusList = await Stack.getStatusList(); let statusList = Stack.getStatusList();
let roomList = this.io.sockets.adapter.rooms.keys(); let roomList = this.io.sockets.adapter.rooms.keys();
@ -531,15 +529,8 @@ export class DockgeServer {
} }
} }
async getDockerNetworkList() : Promise<string[]> { getDockerNetworkList() : string[] {
let res = await childProcessAsync.spawn("docker", [ "network", "ls", "--format", "{{.Name}}" ], { let res = childProcess.spawnSync("docker", [ "network", "ls", "--format", "{{.Name}}" ]);
encoding: "utf-8",
});
if (!res.stdout) {
return [];
}
let list = res.stdout.toString().split("\n"); let list = res.stdout.toString().split("\n");
// Remove empty string item // Remove empty string item

View File

@ -9,10 +9,10 @@ import composerize from "composerize";
export class DockerSocketHandler extends SocketHandler { export class DockerSocketHandler extends SocketHandler {
create(socket : DockgeSocket, server : DockgeServer) { create(socket : DockgeSocket, server : DockgeServer) {
socket.on("deployStack", async (name : unknown, composeYAML : unknown, composeENV : unknown, isAdd : unknown, callback) => { socket.on("deployStack", async (name : unknown, composeYAML : unknown, isAdd : unknown, callback) => {
try { try {
checkLogin(socket); checkLogin(socket);
const stack = await this.saveStack(socket, server, name, composeYAML, composeENV, isAdd); const stack = this.saveStack(socket, server, name, composeYAML, isAdd);
await stack.deploy(socket); await stack.deploy(socket);
server.sendStackList(); server.sendStackList();
callback({ callback({
@ -25,10 +25,10 @@ export class DockerSocketHandler extends SocketHandler {
} }
}); });
socket.on("saveStack", async (name : unknown, composeYAML : unknown, composeENV : unknown, isAdd : unknown, callback) => { socket.on("saveStack", async (name : unknown, composeYAML : unknown, isAdd : unknown, callback) => {
try { try {
checkLogin(socket); checkLogin(socket);
this.saveStack(socket, server, name, composeYAML, composeENV, isAdd); this.saveStack(socket, server, name, composeYAML, isAdd);
callback({ callback({
ok: true, ok: true,
"msg": "Saved" "msg": "Saved"
@ -45,7 +45,7 @@ export class DockerSocketHandler extends SocketHandler {
if (typeof(name) !== "string") { if (typeof(name) !== "string") {
throw new ValidationError("Name must be a string"); throw new ValidationError("Name must be a string");
} }
const stack = await Stack.getStack(server, name); const stack = Stack.getStack(server, name);
try { try {
await stack.delete(socket); await stack.delete(socket);
@ -65,7 +65,7 @@ export class DockerSocketHandler extends SocketHandler {
} }
}); });
socket.on("getStack", async (stackName : unknown, callback) => { socket.on("getStack", (stackName : unknown, callback) => {
try { try {
checkLogin(socket); checkLogin(socket);
@ -73,7 +73,7 @@ export class DockerSocketHandler extends SocketHandler {
throw new ValidationError("Stack name must be a string"); throw new ValidationError("Stack name must be a string");
} }
const stack = await Stack.getStack(server, stackName); const stack = Stack.getStack(server, stackName);
if (stack.isManagedByDockge) { if (stack.isManagedByDockge) {
stack.joinCombinedTerminal(socket); stack.joinCombinedTerminal(socket);
@ -111,7 +111,7 @@ export class DockerSocketHandler extends SocketHandler {
throw new ValidationError("Stack name must be a string"); throw new ValidationError("Stack name must be a string");
} }
const stack = await Stack.getStack(server, stackName); const stack = Stack.getStack(server, stackName);
await stack.start(socket); await stack.start(socket);
callback({ callback({
ok: true, ok: true,
@ -135,7 +135,7 @@ export class DockerSocketHandler extends SocketHandler {
throw new ValidationError("Stack name must be a string"); throw new ValidationError("Stack name must be a string");
} }
const stack = await Stack.getStack(server, stackName); const stack = Stack.getStack(server, stackName);
await stack.stop(socket); await stack.stop(socket);
callback({ callback({
ok: true, ok: true,
@ -156,7 +156,7 @@ export class DockerSocketHandler extends SocketHandler {
throw new ValidationError("Stack name must be a string"); throw new ValidationError("Stack name must be a string");
} }
const stack = await Stack.getStack(server, stackName); const stack = Stack.getStack(server, stackName);
await stack.restart(socket); await stack.restart(socket);
callback({ callback({
ok: true, ok: true,
@ -177,7 +177,7 @@ export class DockerSocketHandler extends SocketHandler {
throw new ValidationError("Stack name must be a string"); throw new ValidationError("Stack name must be a string");
} }
const stack = await Stack.getStack(server, stackName); const stack = Stack.getStack(server, stackName);
await stack.update(socket); await stack.update(socket);
callback({ callback({
ok: true, ok: true,
@ -198,7 +198,7 @@ export class DockerSocketHandler extends SocketHandler {
throw new ValidationError("Stack name must be a string"); throw new ValidationError("Stack name must be a string");
} }
const stack = await Stack.getStack(server, stackName); const stack = Stack.getStack(server, stackName);
await stack.down(socket); await stack.down(socket);
callback({ callback({
ok: true, ok: true,
@ -219,7 +219,7 @@ export class DockerSocketHandler extends SocketHandler {
throw new ValidationError("Stack name must be a string"); throw new ValidationError("Stack name must be a string");
} }
const stack = await Stack.getStack(server, stackName, true); const stack = Stack.getStack(server, stackName, true);
const serviceStatusList = Object.fromEntries(await stack.getServiceStatusList()); const serviceStatusList = Object.fromEntries(await stack.getServiceStatusList());
callback({ callback({
ok: true, ok: true,
@ -234,7 +234,7 @@ export class DockerSocketHandler extends SocketHandler {
socket.on("getDockerNetworkList", async (callback) => { socket.on("getDockerNetworkList", async (callback) => {
try { try {
checkLogin(socket); checkLogin(socket);
const dockerNetworkList = await server.getDockerNetworkList(); const dockerNetworkList = server.getDockerNetworkList();
callback({ callback({
ok: true, ok: true,
dockerNetworkList, 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, isAdd : unknown) : 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");
@ -272,15 +272,12 @@ export class DockerSocketHandler extends SocketHandler {
if (typeof(composeYAML) !== "string") { if (typeof(composeYAML) !== "string") {
throw new ValidationError("Compose YAML must be a string"); throw new ValidationError("Compose YAML must be a string");
} }
if (typeof(composeENV) !== "string") {
throw new ValidationError("Compose ENV must be a string");
}
if (typeof(isAdd) !== "boolean") { if (typeof(isAdd) !== "boolean") {
throw new ValidationError("isAdd must be a boolean"); throw new ValidationError("isAdd must be a boolean");
} }
const stack = new Stack(server, name, composeYAML, composeENV, false); const stack = new Stack(server, name, composeYAML);
await stack.save(isAdd); stack.save(isAdd);
return stack; return stack;
} }

View File

@ -101,7 +101,7 @@ export class TerminalSocketHandler extends SocketHandler {
log.debug("interactiveTerminal", "Service name: " + serviceName); log.debug("interactiveTerminal", "Service name: " + serviceName);
// Get stack // Get stack
const stack = await Stack.getStack(server, stackName); const stack = Stack.getStack(server, stackName);
stack.joinContainerTerminal(socket, serviceName, shell); stack.joinContainerTerminal(socket, serviceName, shell);
callback({ callback({
@ -151,7 +151,7 @@ export class TerminalSocketHandler extends SocketHandler {
throw new ValidationError("Stack name must be a string."); throw new ValidationError("Stack name must be a string.");
} }
const stack = await Stack.getStack(server, stackName); const stack = Stack.getStack(server, stackName);
await stack.leaveCombinedTerminal(socket); await stack.leaveCombinedTerminal(socket);
callback({ callback({

View File

@ -1,8 +1,8 @@
import { DockgeServer } from "./dockge-server"; import { DockgeServer } from "./dockge-server";
import fs, { promises as fsAsync } from "fs"; import fs from "fs";
import { log } from "./log"; import { log } from "./log";
import yaml from "yaml"; import yaml from "yaml";
import { DockgeSocket, fileExists, ValidationError } from "./util-server"; import { DockgeSocket, ValidationError } from "./util-server";
import path from "path"; import path from "path";
import { import {
COMBINED_TERMINAL_COLS, COMBINED_TERMINAL_COLS,
@ -16,14 +16,13 @@ import {
UNKNOWN UNKNOWN
} from "./util-common"; } from "./util-common";
import { InteractiveTerminal, Terminal } from "./terminal"; import { InteractiveTerminal, Terminal } from "./terminal";
import childProcessAsync from "promisify-child-process"; import childProcess from "child_process";
export class Stack { export class Stack {
name: string; name: string;
protected _status: number = UNKNOWN; protected _status: number = UNKNOWN;
protected _composeYAML?: string; protected _composeYAML?: string;
protected _composeENV?: string;
protected _configFilePath?: string; protected _configFilePath?: string;
protected _composeFileName: string = "compose.yaml"; protected _composeFileName: string = "compose.yaml";
protected server: DockgeServer; protected server: DockgeServer;
@ -32,11 +31,10 @@ export class Stack {
protected static managedStackList: Map<string, Stack> = new Map(); protected static managedStackList: Map<string, Stack> = new Map();
constructor(server : DockgeServer, name : string, composeYAML? : string, composeENV? : string, skipFSOperations = false) { constructor(server : DockgeServer, name : string, composeYAML? : string, skipFSOperations = false) {
this.name = name; this.name = name;
this.server = server; this.server = server;
this._composeYAML = composeYAML; this._composeYAML = composeYAML;
this._composeENV = composeENV;
if (!skipFSOperations) { if (!skipFSOperations) {
// Check if compose file name is different from compose.yaml // Check if compose file name is different from compose.yaml
@ -55,7 +53,6 @@ export class Stack {
return { return {
...obj, ...obj,
composeYAML: this.composeYAML, composeYAML: this.composeYAML,
composeENV: this.composeENV,
}; };
} }
@ -72,15 +69,11 @@ export class Stack {
/** /**
* Get the status of the stack from `docker compose ps --format json` * Get the status of the stack from `docker compose ps --format json`
*/ */
async ps() : Promise<object> { ps() : object {
let res = await childProcessAsync.spawn("docker", [ "compose", "ps", "--format", "json" ], { let res = childProcess.execSync("docker compose ps --format json", {
cwd: this.path, cwd: this.path
encoding: "utf-8",
}); });
if (!res.stdout) { return JSON.parse(res.toString());
return {};
}
return JSON.parse(res.stdout.toString());
} }
get isManagedByDockge() : boolean { get isManagedByDockge() : boolean {
@ -99,15 +92,6 @@ 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 {
@ -121,17 +105,6 @@ export class Stack {
return this._composeYAML; return this._composeYAML;
} }
get composeENV() : string {
if (this._composeENV === undefined) {
try {
this._composeENV = fs.readFileSync(path.join(this.path, ".env"), "utf-8");
} catch (e) {
this._composeENV = "";
}
}
return this._composeENV;
}
get path() : string { get path() : string {
return path.join(this.server.stacksDir, this.name); return path.join(this.server.stacksDir, this.name);
} }
@ -155,35 +128,27 @@ export class Stack {
* Save the stack to the disk * Save the stack to the disk
* @param isAdd * @param isAdd
*/ */
async save(isAdd : boolean) { 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 (await fileExists(dir)) { if (fs.existsSync(dir)) {
throw new ValidationError("Stack name already exists"); throw new ValidationError("Stack name already exists");
} }
// Create the stack folder // Create the stack folder
await fsAsync.mkdir(dir); fs.mkdirSync(dir);
} else { } else {
if (!await fileExists(dir)) { if (!fs.existsSync(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
await fsAsync.writeFile(path.join(dir, this._composeFileName), this.composeYAML); fs.writeFileSync(path.join(dir, this._composeFileName), this.composeYAML);
const envPath = path.join(dir, ".env");
// 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);
}
} }
async deploy(socket? : DockgeSocket) : Promise<number> { async deploy(socket? : DockgeSocket) : Promise<number> {
@ -203,7 +168,7 @@ export class Stack {
} }
// Remove the stack folder // Remove the stack folder
await fsAsync.rm(this.path, { fs.rmSync(this.path, {
recursive: true, recursive: true,
force: true force: true
}); });
@ -211,8 +176,8 @@ export class Stack {
return exitCode; return exitCode;
} }
async updateStatus() { updateStatus() {
let statusList = await Stack.getStatusList(); let statusList = Stack.getStatusList();
let status = statusList.get(this.name); let status = statusList.get(this.name);
if (status) { if (status) {
@ -222,7 +187,7 @@ export class Stack {
} }
} }
static async getStackList(server : DockgeServer, useCacheForManaged = false) : Promise<Map<string, Stack>> { static getStackList(server : DockgeServer, useCacheForManaged = false) : Map<string, Stack> {
let stacksDir = server.stacksDir; let stacksDir = server.stacksDir;
let stackList : Map<string, Stack>; let stackList : Map<string, Stack>;
@ -233,16 +198,16 @@ 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 = await fsAsync.readdir(stacksDir); let filenameList = fs.readdirSync(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 = await fsAsync.stat(path.join(stacksDir, filename)); let stat = fs.statSync(path.join(stacksDir, filename));
if (!stat.isDirectory()) { if (!stat.isDirectory()) {
continue; continue;
} }
let stack = await this.getStack(server, filename); let stack = this.getStack(server, filename);
stack._status = CREATED_FILE; stack._status = CREATED_FILE;
stackList.set(filename, stack); stackList.set(filename, stack);
} catch (e) { } catch (e) {
@ -257,15 +222,8 @@ export class Stack {
} }
// Get status from docker compose ls // Get status from docker compose ls
let res = await childProcessAsync.spawn("docker", [ "compose", "ls", "--all", "--format", "json" ], { let res = childProcess.execSync("docker compose ls --all --format json");
encoding: "utf-8", let composeList = JSON.parse(res.toString());
});
if (!res.stdout) {
return stackList;
}
let composeList = JSON.parse(res.stdout.toString());
for (let composeStack of composeList) { for (let composeStack of composeList) {
let stack = stackList.get(composeStack.Name); let stack = stackList.get(composeStack.Name);
@ -291,12 +249,10 @@ export class Stack {
* Get the status list, it will be used to update the status of the stacks * Get the status list, it will be used to update the status of the stacks
* Not all status will be returned, only the stack that is deployed or created to `docker compose` will be returned * Not all status will be returned, only the stack that is deployed or created to `docker compose` will be returned
*/ */
static async getStatusList() : Promise<Map<string, number>> { static getStatusList() : Map<string, number> {
let statusList = new Map<string, number>(); let statusList = new Map<string, number>();
let res = await childProcessAsync.spawn("docker", [ "compose", "ls", "--all", "--format", "json" ], { let res = childProcess.execSync("docker compose ls --all --format json");
encoding: "utf-8",
});
let composeList = JSON.parse(res.toString()); let composeList = JSON.parse(res.toString());
for (let composeStack of composeList) { for (let composeStack of composeList) {
@ -325,13 +281,13 @@ export class Stack {
} }
} }
static async getStack(server: DockgeServer, stackName: string, skipFSOperations = false) : Promise<Stack> { static getStack(server: DockgeServer, stackName: string, skipFSOperations = false) : Stack {
let dir = path.join(server.stacksDir, stackName); let dir = path.join(server.stacksDir, stackName);
if (!skipFSOperations) { 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 // Maybe it is a stack managed by docker compose directly
let stackList = await this.getStackList(server, true); let stackList = this.getStackList(server, true);
let stack = stackList.get(stackName); let stack = stackList.get(stackName);
if (stack) { if (stack) {
@ -350,7 +306,7 @@ export class Stack {
if (!skipFSOperations) { if (!skipFSOperations) {
stack = new Stack(server, stackName); stack = new Stack(server, stackName);
} else { } else {
stack = new Stack(server, stackName, undefined, undefined, true); stack = new Stack(server, stackName, undefined, true);
} }
stack._status = UNKNOWN; stack._status = UNKNOWN;
@ -402,7 +358,7 @@ export class Stack {
} }
// If the stack is not running, we don't need to restart it // If the stack is not running, we don't need to restart it
await this.updateStatus(); this.updateStatus();
log.debug("update", "Status: " + this.status); log.debug("update", "Status: " + this.status);
if (this.status !== RUNNING) { if (this.status !== RUNNING) {
return exitCode; return exitCode;
@ -450,35 +406,24 @@ export class Stack {
async getServiceStatusList() { async getServiceStatusList() {
let statusList = new Map<string, number>(); let statusList = new Map<string, number>();
try { let res = childProcess.spawnSync("docker", [ "compose", "ps", "--format", "json" ], {
let res = await childProcessAsync.spawn("docker", [ "compose", "ps", "--format", "json" ], { cwd: this.path,
cwd: this.path, });
encoding: "utf-8",
});
if (!res.stdout) { let lines = res.stdout.toString().split("\n");
return statusList;
}
let lines = res.stdout?.toString().split("\n"); for (let line of lines) {
try {
for (let line of lines) { let obj = JSON.parse(line);
try { if (obj.Health === "") {
let obj = JSON.parse(line); statusList.set(obj.Service, obj.State);
if (obj.Health === "") { } else {
statusList.set(obj.Service, obj.State); statusList.set(obj.Service, obj.Health);
} else {
statusList.set(obj.Service, obj.Health);
}
} catch (e) {
} }
} catch (e) {
} }
return statusList;
} catch (e) {
log.error("getServiceStatusList", e);
return statusList;
} }
return statusList;
} }
} }

View File

@ -5,6 +5,8 @@ import { LimitQueue } from "./utils/limit-queue";
import { DockgeSocket } from "./util-server"; import { DockgeSocket } from "./util-server";
import { import {
allowedCommandList, allowedRawKeys, allowedCommandList, allowedRawKeys,
getComposeTerminalName,
getCryptoRandomInt,
PROGRESS_TERMINAL_ROWS, PROGRESS_TERMINAL_ROWS,
TERMINAL_COLS, TERMINAL_COLS,
TERMINAL_ROWS TERMINAL_ROWS
@ -205,20 +207,14 @@ export class Terminal {
} }
public static exec(server : DockgeServer, socket : DockgeSocket | undefined, terminalName : string, file : string, args : string | string[], cwd : string) : Promise<number> { public static exec(server : DockgeServer, socket : DockgeSocket | undefined, terminalName : string, file : string, args : string | string[], cwd : string) : Promise<number> {
return new Promise((resolve, reject) => { const terminal = new Terminal(server, terminalName, file, args, cwd);
// check if terminal exists terminal.rows = PROGRESS_TERMINAL_ROWS;
if (Terminal.terminalMap.has(terminalName)) {
reject("Another operation is already running, please try again later.");
return;
}
let terminal = new Terminal(server, terminalName, file, args, cwd); if (socket) {
terminal.rows = PROGRESS_TERMINAL_ROWS; terminal.join(socket);
}
if (socket) {
terminal.join(socket);
}
return new Promise((resolve) => {
terminal.onExit((exitCode : number) => { terminal.onExit((exitCode : number) => {
resolve(exitCode); resolve(exitCode);
}); });

View File

@ -203,7 +203,7 @@ export function getContainerTerminalName(container : string) {
} }
export function getContainerExecTerminalName(stackName : string, container : string, index : number) { export function getContainerExecTerminalName(stackName : string, container : string, index : number) {
return "container-exec-" + stackName + "-" + container + "-" + index; return "container-exec-" + container + "-" + index;
} }
export function copyYAMLComments(doc : Document, src : Document) { export function copyYAMLComments(doc : Document, src : Document) {

View File

@ -5,7 +5,6 @@ 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;
@ -83,9 +82,3 @@ 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);
}

View File

@ -13,10 +13,10 @@ services:
# If you want to use private registries, you need to share the auth file with Dockge: # If you want to use private registries, you need to share the auth file with Dockge:
# - /root/.docker/:/root/.docker # - /root/.docker/:/root/.docker
# Stacks Directory # Your stacks directory in the host (The paths inside container must be the same as the host)
# ⚠️ READ IT CAREFULLY. If you did it wrong, your data could end up writing into a WRONG PATH. # ⚠️⚠️ If you did it wrong, your data could end up be written into a wrong path.
# ⚠️ 1. FULL path only. No relative path (MUST) # ✔️✔️✔️✔️ CORRECT: - /my-stacks:/my-stacks (Both paths match)
# ⚠️ 2. Left Stacks Path === Right Stacks Path (MUST) # ❌❌❌❌ WRONG: - /docker:/my-stacks (Both paths do not match)
- /opt/stacks:/opt/stacks - /opt/stacks:/opt/stacks
environment: environment:
# Tell Dockge where is your stacks directory # Tell Dockge where is your stacks directory

View File

@ -1,7 +1,9 @@
# Due to the bug of #145, Node.js's version cannot be changed, unless upstream is fixed. FROM node:20-bookworm-slim
FROM node:18.17.1-bookworm-slim
ENV PNPM_HOME="/pnpm" ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH" ENV PATH="$PNPM_HOME:$PATH"
# COPY --from=docker:dind /usr/local/bin/docker /usr/local/bin/
RUN apt update && apt install --yes --no-install-recommends \ RUN apt update && apt install --yes --no-install-recommends \
curl \ curl \
ca-certificates \ ca-certificates \
@ -22,3 +24,16 @@ RUN apt update && apt install --yes --no-install-recommends \
&& rm -rf /var/lib/apt/lists/* \ && rm -rf /var/lib/apt/lists/* \
&& npm install pnpm -g \ && npm install pnpm -g \
&& pnpm install -g tsx && pnpm install -g tsx
# ensures that /var/run/docker.sock exists
# changes the ownership of /var/run/docker.sock
RUN touch /var/run/docker.sock && chown node:node /var/run/docker.sock
# Full Base Image
# MariaDB, Chromium and fonts
#FROM base-slim AS base
#ENV DOCKGE_ENABLE_EMBEDDED_MARIADB=1
#RUN apt update && \
# apt --yes --no-install-recommends install mariadb-server && \
# rm -rf /var/lib/apt/lists/* && \
# apt --yes autoremove

View File

@ -1,10 +0,0 @@
############################################
# Build in Golang
############################################
FROM golang:1.21.4-bookworm
WORKDIR /app
ARG TARGETPLATFORM
COPY ./extra/healthcheck.go ./extra/healthcheck.go
# Compile healthcheck.go
RUN go build -x -o ./extra/healthcheck ./extra/healthcheck.go

View File

@ -1,8 +1,3 @@
############################################
# Healthcheck Binary
############################################
FROM louislam/dockge:build-healthcheck AS build_healthcheck
############################################ ############################################
# Build # Build
############################################ ############################################
@ -17,17 +12,16 @@ RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-l
############################################ ############################################
FROM louislam/dockge:base AS release FROM louislam/dockge:base AS release
WORKDIR /app WORKDIR /app
COPY --chown=node:node --from=build_healthcheck /app/extra/healthcheck /app/extra/healthcheck
COPY --from=build /app/node_modules /app/node_modules
COPY --chown=node:node . . COPY --chown=node:node . .
COPY --from=build /app/node_modules /app/node_modules
RUN mkdir ./data RUN mkdir ./data
VOLUME /app/data VOLUME /app/data
EXPOSE 5001 EXPOSE 5001
HEALTHCHECK --interval=60s --timeout=30s --start-period=60s --retries=5 CMD extra/healthcheck
ENTRYPOINT ["/usr/bin/dumb-init", "--"] ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["tsx", "./backend/index.ts"] CMD ["tsx", "./backend/index.ts"]
############################################ ############################################
# Mark as Nightly # Mark as Nightly
############################################ ############################################

View File

@ -1,74 +0,0 @@
/*
* If changed, have to run `npm run build-docker-builder-go`.
* This script should be run after a period of time (180s), because the server may need some time to prepare.
*/
package main
import (
"crypto/tls"
"io/ioutil"
"log"
"net/http"
"os"
"strings"
"time"
)
func main() {
// Is K8S + "dockge" as the container name
// See https://github.com/louislam/uptime-kuma/pull/2083
isK8s := strings.HasPrefix(os.Getenv("DOCKGE_PORT"), "tcp://")
// process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{
InsecureSkipVerify: true,
}
client := http.Client{
Timeout: 28 * time.Second,
}
sslKey := os.Getenv("DOCKGE_SSL_KEY")
sslCert := os.Getenv("DOCKGE_SSL_CERT")
hostname := os.Getenv("DOCKGE_HOST")
if len(hostname) == 0 {
hostname = "127.0.0.1"
}
port := ""
// DOCKGE_PORT is override by K8S unexpectedly,
if !isK8s {
port = os.Getenv("DOCKGE_PORT")
}
if len(port) == 0 {
port = "5001"
}
protocol := ""
if len(sslKey) != 0 && len(sslCert) != 0 {
protocol = "https"
} else {
protocol = "http"
}
url := protocol + "://" + hostname + ":" + port
log.Println("Checking " + url)
resp, err := client.Get(url)
if err != nil {
log.Fatalln(err)
}
defer resp.Body.Close()
_, err = ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatalln(err)
}
log.Printf("Health Check OK [Res Code: %d]\n", resp.StatusCode)
}

View File

@ -1,84 +0,0 @@
import { Database } from "../backend/database";
import { R } from "redbean-node";
import readline from "readline";
import { User } from "../backend/models/user";
import { DockgeServer } from "../backend/dockge-server";
import { log } from "../backend/log";
console.log("== Dockge Reset Password Tool ==");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
export const main = async () => {
const server = new DockgeServer();
// Check if
console.log("Connecting the database");
try {
await Database.init(server);
} catch (e) {
if (e instanceof Error) {
log.error("server", "Failed to connect to your database: " + e.message);
}
process.exit(1);
}
try {
// No need to actually reset the password for testing, just make sure no connection problem. It is ok for now.
if (!process.env.TEST_BACKEND) {
const user = await R.findOne("user");
if (! user) {
throw new Error("user not found, have you installed?");
}
console.log("Found user: " + user.username);
while (true) {
let password = await question("New Password: ");
let confirmPassword = await question("Confirm New Password: ");
if (password === confirmPassword) {
await User.resetPassword(user.id, password);
// Reset all sessions by reset jwt secret
await server.initJWTSecret();
break;
} else {
console.log("Passwords do not match, please try again.");
}
}
console.log("Password reset successfully.");
}
} catch (e) {
if (e instanceof Error) {
console.error("Error: " + e.message);
}
}
await Database.close();
rl.close();
console.log("Finished.");
};
/**
* Ask question of user
* @param question Question to ask
* @returns Users response
*/
function question(question : string) : Promise<string> {
return new Promise((resolve) => {
rl.question(question, (answer) => {
resolve(answer);
});
});
}
if (!process.env.TEST_BACKEND) {
main();
}

View File

@ -30,10 +30,6 @@ export default {
displayName: { displayName: {
type: String, type: String,
required: true, required: true,
},
objectType: {
type: String,
default: "service",
} }
}, },
data() { data() {
@ -45,7 +41,8 @@ export default {
array() { array() {
// Create the array if not exists, it should be safe. // Create the array if not exists, it should be safe.
if (!this.service[this.name]) { if (!this.service[this.name]) {
return []; // eslint-disable-next-line vue/no-side-effects-in-computed-properties
this.service[this.name] = [];
} }
return this.service[this.name]; return this.service[this.name];
}, },
@ -59,24 +56,8 @@ export default {
return this.service[this.name] !== undefined; return this.service[this.name] !== undefined;
}, },
/**
* Not a good name, but it is used to get the object.
*/
service() { service() {
if (this.objectType === "service") { return this.$parent.$parent.service;
// Used in Container.vue
return this.$parent.$parent.service;
} else if (this.objectType === "x-dockge") {
if (!this.$parent.$parent.jsonConfig["x-dockge"]) {
return {};
}
// Used in Compose.vue
return this.$parent.$parent.jsonConfig["x-dockge"];
} else {
return {};
}
}, },
valid() { valid() {
@ -100,19 +81,6 @@ export default {
}, },
methods: { methods: {
addField() { addField() {
// Create the object if not exists.
if (this.objectType === "x-dockge") {
if (!this.$parent.$parent.jsonConfig["x-dockge"]) {
this.$parent.$parent.jsonConfig["x-dockge"] = {};
}
}
// Create the array if not exists.
if (!this.service[this.name]) {
this.service[this.name] = [];
}
this.array.push(""); this.array.push("");
}, },
remove(index) { remove(index) {

View File

@ -49,7 +49,8 @@ export default {
array() { array() {
// Create the array if not exists, it should be safe. // Create the array if not exists, it should be safe.
if (!this.service[this.name]) { if (!this.service[this.name]) {
return []; // eslint-disable-next-line vue/no-side-effects-in-computed-properties
this.service[this.name] = [];
} }
return this.service[this.name]; return this.service[this.name];
}, },
@ -88,10 +89,6 @@ export default {
}, },
methods: { methods: {
addField() { addField() {
// Create the array if not exists.
if (!this.service[this.name]) {
this.service[this.name] = [];
}
this.array.push(""); this.array.push("");
}, },
remove(index) { remove(index) {

View File

@ -152,14 +152,6 @@ export default {
}); });
result.sort((m1, m2) => { 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 (m1.status !== m2.status) {
if (m2.status === RUNNING) { if (m2.status === RUNNING) {
return 1; return 1;

View File

@ -45,12 +45,11 @@ export default {
<style scoped> <style scoped>
.badge { .badge {
min-width: 62px; min-width: 62px;
overflow: hidden;
text-overflow: ellipsis;
} }
.fixed-width { .fixed-width {
width: 62px; width: 62px;
overflow: hidden;
text-overflow: ellipsis;
} }
</style> </style>

View File

@ -13,20 +13,14 @@ const languageList = {
"sl": "Slovenščina", "sl": "Slovenščina",
"tr": "Türkçe", "tr": "Türkçe",
"zh-CN": "简体中文", "zh-CN": "简体中文",
"zh-TW": "繁體中文(台灣)",
"ur": "Urdu", "ur": "Urdu",
"ko-KR": "한국어", "ko-KR": "한국어",
"ru": "Русский", "ru": "Русский",
"cs-CZ": "Čeština", "cs-CZ": "Čeština",
"ar": "العربية", "ar": "العربية",
"th": "ไทย", "th":"ไทย",
"it-IT": "Italiano", "it-IT":"Italiano",
"sv-SE": "Svenska", "sv-SE":"Svenska"
"uk-UA": "Українська",
"da": "Dansk",
"ja": "日本語",
"nl": "Nederlands",
"ro": "Română",
}; };
let messages = { let messages = {

View File

@ -2,18 +2,13 @@
A simple guide on how to translate `Dockge` in your native language. A simple guide on how to translate `Dockge` in your native language.
## How to Translate
(11-26-2023) Updated
1. Go to <https://weblate.kuma.pet>
2. Register an account on Weblate
3. Make sure your GitHub email is matched with Weblate's account, so that it could show you as a contributor on GitHub
4. Choose your language on Weblate and start translating.
## How to add a new language in the dropdown ## How to add a new language in the dropdown
1. Add your Language at <https://weblate.kuma.pet/projects/dockge/dockge/>. (11-21-2023) Updated
2. Find the language code (You can find it at the end of the URL)
1. Add your Language at `frontend/src/lang/` by creating a new file with your language Code, format: `zh-TW.json` .
2. Copy the content from `en.json` and make translations from that.
3. Add your language at the end of `languageList` in `frontend/src/i18n.ts`, format: `"zh-TW": "繁體中文 (台灣)"`, 3. Add your language at the end of `languageList` in `frontend/src/i18n.ts`, format: `"zh-TW": "繁體中文 (台灣)"`,
4. Commit to new branch and make a new Pull Request for me to approve. 4. Commit to new branch and make a new Pull Request for me to approve.
*Note:* Currently we are only accepting one Pull Request per Language Translate.

View File

@ -12,8 +12,8 @@
"registry": "السجل", "registry": "السجل",
"compose": "أنشاء كمبوز", "compose": "أنشاء كمبوز",
"addFirstStackMsg": "أنشيء أول كمبوز!", "addFirstStackMsg": "أنشيء أول كمبوز!",
"stackName": "اسم المكدسة", "stackName" : "اسم المكدسة",
"deployStack": "نشر", "deployStack": نر",
"deleteStack": "حذف", "deleteStack": "حذف",
"stopStack": "إيقاف", "stopStack": "إيقاف",
"restartStack": "إعادة تشغيل", "restartStack": "إعادة تشغيل",
@ -23,7 +23,7 @@
"editStack": "تعديل", "editStack": "تعديل",
"discardStack": "إهمال", "discardStack": "إهمال",
"saveStackDraft": "حفظ", "saveStackDraft": "حفظ",
"notAvailableShort": "غير متوفر", "notAvailableShort" : "غير متوفر",
"deleteStackMsg": "هل أنت متأكد أنك تريد حذف هذه المكدسة؟", "deleteStackMsg": "هل أنت متأكد أنك تريد حذف هذه المكدسة؟",
"stackNotManagedByDockgeMsg": "لا يتم إدارة هذه المكدس بواسطة Dockge.", "stackNotManagedByDockgeMsg": "لا يتم إدارة هذه المكدس بواسطة Dockge.",
"primaryHostname": "اسم المضيف الرئيسي", "primaryHostname": "اسم المضيف الرئيسي",
@ -46,7 +46,7 @@
"deleteContainer": "حذف", "deleteContainer": "حذف",
"addContainer": "أضافة حاوية", "addContainer": "أضافة حاوية",
"addNetwork": "أضافة شبكة", "addNetwork": "أضافة شبكة",
"disableauth.message1": "هل أنت متأكد أنك تريد <strong>تعطيل المصادقة</strong>؟", "disableauth.message1": "هل أنت متأكد أنك تريد <strong>تعطيل المصادقة</strong>?",
"disableauth.message2": "إنه مصمم للحالات <strong>التي تنوي فيها مصادقة الطرف الثالث</strong> أمام Dockge مثل Cloudflare Access, Authelia أو أي من آليات المصادقة الأخرى.", "disableauth.message2": "إنه مصمم للحالات <strong>التي تنوي فيها مصادقة الطرف الثالث</strong> أمام Dockge مثل Cloudflare Access, Authelia أو أي من آليات المصادقة الأخرى.",
"passwordNotMatchMsg": "كلمة المرور المكررة غير متطابقة.", "passwordNotMatchMsg": "كلمة المرور المكررة غير متطابقة.",
"autoGet": "الجلب التلقائي", "autoGet": "الجلب التلقائي",
@ -91,12 +91,5 @@
"Allowed commands:": "الأوامر المسموح بها:", "Allowed commands:": "الأوامر المسموح بها:",
"Internal Networks": "الشبكات الداخلية", "Internal Networks": "الشبكات الداخلية",
"External Networks": "الشبكات الخارجية", "External Networks": "الشبكات الخارجية",
"No External Networks": "لا توجد شبكات خارجية", "No External Networks": "لا توجد شبكات خارجية"
"reverseProxyMsg2": "تحقق كيف يتم إعداده لمقبس ويب",
"Cannot connect to the socket server.": "تعذر الاتصال بخادم المقبس.",
"reconnecting...": "إعادة الاتصال…",
"url": "رابط | روابط",
"extra": "إضافات",
"reverseProxyMsg1": "هل تستدخم خادم عكسي؟",
"connecting...": "جاري الاتصال بخادم المقبس…"
} }

View File

@ -1,102 +0,0 @@
{
"languageName": "Dansk",
"authIncorrectCreds": "Forkert brugernavn eller adgangskode.",
"PasswordsDoNotMatch": "Adgangskode stemmer ikke overens.",
"Repeat Password": "Gentag adgangskode",
"Create": "Opret",
"signedInDisp": "Logget ind som {0}",
"signedInDispDisabled": "Auth Deaktiveret.",
"home": "Hjem",
"console": "Konsol",
"registry": "Registry",
"compose": "Compose",
"stackName": "Stack-navn",
"deployStack": "Udrulle",
"deleteStack": "Slet",
"stopStack": "Stop",
"restartStack": "Genstart",
"updateStack": "Opdatere",
"startStack": "Start",
"downStack": "Stop & Sluk",
"editStack": "Editere",
"discardStack": "Annuller",
"saveStackDraft": "Gem",
"notAvailableShort": "Ugyldig",
"stackNotManagedByDockgeMsg": "Denne stack administreres ikke af Dockge.",
"primaryHostname": "Primært værtsnavn",
"general": "Generelt",
"container": "Container | Containere",
"scanFolder": "Scan Stack-mappe",
"dockerImage": "Billede",
"restartPolicyUnlessStopped": "Medmindre stoppet",
"restartPolicyAlways": "Altid",
"restartPolicyOnFailure": "Ved fejl",
"restartPolicyNo": "Nej",
"restartPolicy": "Genstart politik",
"containerName": "Container navn",
"port": "Port | Porte",
"volume": "Volumen | Voluminer",
"network": "Netværk | Netværker",
"dependsOn": "Container Dependency | Container Dependencies",
"addListItem": "Tilføj {0}",
"deleteContainer": "Slet",
"addNetwork": "Tilføj Netværk",
"passwordNotMatchMsg": "Koden du gentog stemmer ikke overens.",
"autoGet": "Auto Get",
"add": "Tilføj",
"Edit": "Redigere",
"applyToYAML": "Anvend til YAML",
"createExternalNetwork": "Skabe",
"addInternalNetwork": "Tilføj",
"Save": "Gem",
"Language": "Sprog",
"Current User": "Nuværende bruger",
"Change Password": "Ændre adgangskode",
"Current Password": "Nuværende adgangskode",
"New Password": "Ny adgangskode",
"Repeat New Password": "Gentag ny adgangskode",
"Update Password": "Opdater adgangskode",
"Advanced": "Avanceret",
"Please use this option carefully!": "Brug venligst denne indstilling forsigtigt!",
"Enable Auth": "Aktiver Auth",
"Disable Auth": "Deaktiver Auth",
"I understand, please disable": "Jeg forstår, venligst deaktiver",
"Leave": "Forlad",
"Frontend Version": "Frontend Version",
"Check Update On GitHub": "Tjek opdatering på GitHub",
"Also check beta release": "Tjek også betaversionen",
"Remember me": "Husk mig",
"Login": "Login",
"Username": "Brugernavn",
"Password": "Adgangskode",
"Settings": "Indstillinger",
"Logout": "Log ud",
"Convert to Compose": "Konverter til Compose",
"active": "aktive",
"exited": "forladt",
"inactive": "inaktive",
"Appearance": "Udseende",
"Security": "Sikkerhed",
"Docker Run": "Docker Kør",
"About": "Om",
"Allowed commands:": "Tilladte kommandoer:",
"Internal Networks": "Interne netværk",
"External Networks": "Eksterne netværk",
"No External Networks": "Ingen eksterne netværk",
"reverseProxyMsg1": "Bruger du en Reverse-Proxy?",
"reverseProxyMsg2": "Tjek, hvordan du konfigurerer det til WebSocket",
"Cannot connect to the socket server.": "Kan ikke oprette forbindelse til socket-serveren.",
"reconnecting...": "Genopretter forbindelse…",
"connecting...": "Opretter forbindelse til socket-serveren…",
"url": "URL | URL'er",
"extra": "Ekstra",
"Create your admin account": "Opret din administratorkonto",
"addFirstStackMsg": "Compose din første stack!",
"deleteStackMsg": "Er du sikker på, at du vil slette denne stack?",
"environmentVariable": "Miljøvariabel | miljøvariabler",
"addContainer": "Tilføj Container",
"disableauth.message1": "Er du sikker på, at du vil <strong>deaktivere godkendelse</strong>?",
"disableauth.message2": "Det er designet til scenarier <strong>hvor du har til hensigt at implementere tredjepartsgodkendelse</strong> foran Dockge såsom Cloudflare Access, Authelia eller andre godkendelsesmekanismer.",
"Show update if available": "Vis opdatering, hvis tilgængelig",
"Lowercase only": "Kun små bogstaver"
}

View File

@ -6,14 +6,14 @@
"Repeat Password": "Passwort wiederholen", "Repeat Password": "Passwort wiederholen",
"Create": "Erstellen", "Create": "Erstellen",
"signedInDisp": "Angemeldet als {0}", "signedInDisp": "Angemeldet als {0}",
"signedInDispDisabled": "Anmeldung deaktiviert.", "signedInDispDisabled": "Authentifizierung deaktiviert.",
"home": "Startseite", "home": "Startseite",
"console": "Konsole", "console": "Konsole",
"registry": "Container Registry", "registry": "Register",
"compose": "", "compose": "Zusammenstellen",
"addFirstStackMsg": "Stelle deinen ersten Stack zusammen!", "addFirstStackMsg": "Stelle deinen ersten Stack zusammen!",
"stackName": "Stack-Name", "stackName" : "Stack-Name",
"deployStack": "Deployen", "deployStack": "Bereitstellen",
"deleteStack": "Löschen", "deleteStack": "Löschen",
"stopStack": "Anhalten", "stopStack": "Anhalten",
"restartStack": "Neustarten", "restartStack": "Neustarten",
@ -22,33 +22,33 @@
"editStack": "Bearbeiten", "editStack": "Bearbeiten",
"discardStack": "Verwerfen", "discardStack": "Verwerfen",
"saveStackDraft": "Speichern", "saveStackDraft": "Speichern",
"notAvailableShort": "N/V", "notAvailableShort" : "N/A",
"deleteStackMsg": "Möchtest du diesen Stack wirklich löschen?", "deleteStackMsg": "Möchtest du diesen Stack wirklich löschen?",
"stackNotManagedByDockgeMsg": "Dieser Stack wird nicht von Dockge verwaltet.", "stackNotManagedByDockgeMsg": "Dieser Stack wird nicht von Dockge verwaltet.",
"primaryHostname": "Primärer Hostname", "primaryHostname": "Primärer Hostname",
"general": "Allgemein", "general": "Allgemein",
"container": "Container", "container": "Container | Container",
"scanFolder": "Stacks-Ordner durchsuchen", "scanFolder": "Stacks-Ordner durchsuchen",
"dockerImage": "Image", "dockerImage": "Image",
"restartPolicyUnlessStopped": "Falls nicht gestoppt", "restartPolicyUnlessStopped": "Falls nicht gestoppt",
"restartPolicyAlways": "Immer", "restartPolicyAlways": "Immer",
"restartPolicyOnFailure": "Bei Fehler", "restartPolicyOnFailure": "Bei Fehler",
"restartPolicyNo": "Kein Neustart", "restartPolicyNo": "Kein Neustart",
"environmentVariable": "Umgebungsvariable/n", "environmentVariable": "Umgebungsvariable | Umgebungsvariablen",
"restartPolicy": "Neustart Richtlinie", "restartPolicy": "Neustart Richtlinie",
"containerName": "Container-Name", "containerName": "Container-Name",
"port": "Port / Ports", "port": "Port | Ports",
"volume": "Volume / Volumes", "volume": "Volume | Volumes",
"network": "Netzwerk | Netzwerke", "network": "Netzwerk | Netzwerke",
"dependsOn": "Container-Abhängigkeit/en", "dependsOn": "Container-Abhängigkeit | Container-Abhängigkeiten",
"addListItem": "{0} hinzufügen", "addListItem": "{0} hinzufügen",
"deleteContainer": "Löschen", "deleteContainer": "Löschen",
"addContainer": "Container hinzufügen", "addContainer": "Container hinzufügen",
"addNetwork": "Netzwerk hinzufügen", "addNetwork": "Netzwerk hinzufügen",
"disableauth.message1": "Bist du sicher, dass du die <strong>Anmeldung deaktivieren</strong> möchtest?", "disableauth.message1": "Bist du sicher, dass du die <strong>Authentifizierung deaktivieren</strong> möchtest?",
"disableauth.message2": "Es ist für Szenarien vorgesehen, <strong>in denen du beabsichtigst, eine Drittanbieter-Authentifizierung</strong> vor Dockge zu implementieren, wie zum Beispiel Cloudflare Access, Authelia oder andere Authentifizierungsmechanismen.", "disableauth.message2": "Es ist für Szenarien vorgesehen, <strong>in denen du beabsichtigst, eine Drittanbieter-Authentifizierung</strong> vor Dockge zu implementieren, wie zum Beispiel Cloudflare Access, Authelia oder andere Authentifizierungsmechanismen.",
"passwordNotMatchMsg": "Das wiederholte Passwort stimmt nicht überein.", "passwordNotMatchMsg": "Das wiederholte Passwort stimmt nicht überein.",
"autoGet": "Automatisch laden", "autoGet": "Automatisch holen",
"add": "Hinzufügen", "add": "Hinzufügen",
"Edit": "Bearbeiten", "Edit": "Bearbeiten",
"applyToYAML": "Auf YAML anwenden", "applyToYAML": "Auf YAML anwenden",
@ -64,8 +64,8 @@
"Update Password": "Passwort aktualisieren", "Update Password": "Passwort aktualisieren",
"Advanced": "Erweitert", "Advanced": "Erweitert",
"Please use this option carefully!": "Bitte verwende diese Option sorgfältig!", "Please use this option carefully!": "Bitte verwende diese Option sorgfältig!",
"Enable Auth": "Anmeldung aktivieren", "Enable Auth": "Authentifizierung aktivieren",
"Disable Auth": "Anmeldung deaktivieren", "Disable Auth": "Authentifizierung deaktivieren",
"I understand, please disable": "Ich verstehe, bitte deaktivieren", "I understand, please disable": "Ich verstehe, bitte deaktivieren",
"Leave": "Verlassen", "Leave": "Verlassen",
"Frontend Version": "Frontend Version", "Frontend Version": "Frontend Version",
@ -90,13 +90,5 @@
"Allowed commands:": "Zugelassene Befehle:", "Allowed commands:": "Zugelassene Befehle:",
"Internal Networks": "Interne Netzwerke", "Internal Networks": "Interne Netzwerke",
"External Networks": "Externe Netzwerke", "External Networks": "Externe Netzwerke",
"No External Networks": "Keine externen Netzwerke", "No External Networks": "Keine externen Netzwerke"
"Cannot connect to the socket server.": "Keine Verbindung zum Socket Server.", }
"reverseProxyMsg1": "Wird ein Reverse Proxy genutzt?",
"reconnecting...": "Erneuter Verbindungsaufbau…",
"downStack": "Stoppen & Aus",
"extra": "Extra",
"url": "URL / URLs",
"reverseProxyMsg2": "Lerne wie dieser für WebSockets zu konfigurieren ist.",
"connecting...": "Verbindungsaufbau zum Socket Server…"
}

View File

@ -12,7 +12,7 @@
"registry": "Registry", "registry": "Registry",
"compose": "Compose", "compose": "Compose",
"addFirstStackMsg": "Compose your first stack!", "addFirstStackMsg": "Compose your first stack!",
"stackName": "Stack Name", "stackName" : "Stack Name",
"deployStack": "Deploy", "deployStack": "Deploy",
"deleteStack": "Delete", "deleteStack": "Delete",
"stopStack": "Stop", "stopStack": "Stop",
@ -23,7 +23,7 @@
"editStack": "Edit", "editStack": "Edit",
"discardStack": "Discard", "discardStack": "Discard",
"saveStackDraft": "Save", "saveStackDraft": "Save",
"notAvailableShort": "N/A", "notAvailableShort" : "N/A",
"deleteStackMsg": "Are you sure you want to delete this stack?", "deleteStackMsg": "Are you sure you want to delete this stack?",
"stackNotManagedByDockgeMsg": "This stack is not managed by Dockge.", "stackNotManagedByDockgeMsg": "This stack is not managed by Dockge.",
"primaryHostname": "Primary Hostname", "primaryHostname": "Primary Hostname",
@ -91,12 +91,5 @@
"Allowed commands:": "Allowed commands:", "Allowed commands:": "Allowed commands:",
"Internal Networks": "Internal Networks", "Internal Networks": "Internal Networks",
"External Networks": "External Networks", "External Networks": "External Networks",
"No External Networks": "No External Networks", "No External Networks": "No External Networks"
"reverseProxyMsg1": "Using a Reverse Proxy?",
"reverseProxyMsg2": "Check how to config it for WebSocket",
"Cannot connect to the socket server.": "Cannot connect to the socket server.",
"reconnecting...": "Reconnecting…",
"connecting...": "Connecting to the socket server…",
"url": "URL | URLs",
"extra": "Extra"
} }

View File

@ -1,5 +1,5 @@
{ {
"languageName": "Français", "languageName": "Francais",
"Create your admin account": "Créez votre compte administrateur", "Create your admin account": "Créez votre compte administrateur",
"authIncorrectCreds": "identifiant ou mot de passe incorrect.", "authIncorrectCreds": "identifiant ou mot de passe incorrect.",
"Repeat Password": "Répéter le mot de passe", "Repeat Password": "Répéter le mot de passe",
@ -11,8 +11,8 @@
"console": "Console", "console": "Console",
"registry": "Registre", "registry": "Registre",
"compose": "Compose", "compose": "Compose",
"addFirstStackMsg": "Créez votre première pile !", "addFirstStackMsg": "Créez votre première pile!",
"stackName": "Nom de la pile", "stackName" : "Nom de la pile",
"deployStack": "Déployer", "deployStack": "Déployer",
"deleteStack": "Supprimer", "deleteStack": "Supprimer",
"stopStack": "Arrêter", "stopStack": "Arrêter",
@ -22,11 +22,11 @@
"editStack": "Modifier", "editStack": "Modifier",
"discardStack": "Ignorer", "discardStack": "Ignorer",
"saveStackDraft": "Sauvegarder", "saveStackDraft": "Sauvegarder",
"notAvailableShort": "N/A", "notAvailableShort" : "N/A",
"deleteStackMsg": "Êtes-vous sûr de vouloir supprimer cette pile ?", "deleteStackMsg": "Êtes-vous sûr de vouloir supprimer cette pile ?",
"stackNotManagedByDockgeMsg": "Cette pile n'est pas gérée par Dockge.", "stackNotManagedByDockgeMsg": "Cette pile n'est pas gérée par Dockge.",
"primaryHostname": "Nom d'hôte principal", "primaryHostname": "Nom d'hôte principal",
"general": "Général", "general": "Générale",
"container": "Conteneur | Conteneurs", "container": "Conteneur | Conteneurs",
"scanFolder": "Analyser le dossier des piles", "scanFolder": "Analyser le dossier des piles",
"dockerImage": "Image", "dockerImage": "Image",
@ -51,7 +51,7 @@
"autoGet": "Obtention automatique", "autoGet": "Obtention automatique",
"add": "Ajouter", "add": "Ajouter",
"Edit": "Modifier", "Edit": "Modifier",
"applyToYAML": "Appliquer au YAML", "applyToYAML": "Appliquer à YAML",
"createExternalNetwork": "Créer", "createExternalNetwork": "Créer",
"addInternalNetwork": "Ajouter", "addInternalNetwork": "Ajouter",
"Save": "Enregistrer", "Save": "Enregistrer",
@ -87,16 +87,8 @@
"Appearance": "Apparence", "Appearance": "Apparence",
"Security": "Sécurité", "Security": "Sécurité",
"About": "À propos", "About": "À propos",
"Allowed commands:": "Commandes autorisées :", "Allowed commands:": "Commandes autorisées:",
"Internal Networks": "Réseaux Internes", "Internal Networks": "Réseaux Internes",
"External Networks": "Réseaux Externes", "External Networks": "Réseaux Externes",
"No External Networks": "Aucun Réseau Externe", "No External Networks": "Aucun Réseau Externe"
"reverseProxyMsg2": "Vérifier comment le configurer pour WebSocket",
"connecting...": "Connexion au serveur socket…",
"url": "URL | URLs",
"extra": "Supplémentaire",
"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…"
} }

View File

@ -12,7 +12,7 @@
"registry": "Registro", "registry": "Registro",
"compose": "Compose", "compose": "Compose",
"addFirstStackMsg": "Componi il tuo primo stack!", "addFirstStackMsg": "Componi il tuo primo stack!",
"stackName": "Nome dello stack", "stackName" : "Nome dello stack",
"deployStack": "Deploy", "deployStack": "Deploy",
"deleteStack": "Cancella", "deleteStack": "Cancella",
"stopStack": "Stop", "stopStack": "Stop",
@ -23,7 +23,7 @@
"editStack": "Modifica", "editStack": "Modifica",
"discardStack": "Annulla", "discardStack": "Annulla",
"saveStackDraft": "Salva", "saveStackDraft": "Salva",
"notAvailableShort": "N/D", "notAvailableShort" : "N/D",
"deleteStackMsg": "Sei sicuro di voler eliminare questo stack?", "deleteStackMsg": "Sei sicuro di voler eliminare questo stack?",
"stackNotManagedByDockgeMsg": "Questo stack non è gestito da Dockge.", "stackNotManagedByDockgeMsg": "Questo stack non è gestito da Dockge.",
"primaryHostname": "Hostname primario", "primaryHostname": "Hostname primario",
@ -91,11 +91,5 @@
"Allowed commands:": "Comandi permessi:", "Allowed commands:": "Comandi permessi:",
"Internal Networks": "Reti interne", "Internal Networks": "Reti interne",
"External Networks": "Reti esterne", "External Networks": "Reti esterne",
"No External Networks": "Nessuna rete esterna", "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…"
} }

View File

@ -1,98 +0,0 @@
{
"authIncorrectCreds": "ユーザーネームまたはパスワードが正しくありません。",
"PasswordsDoNotMatch": "パスワードが一致しません。",
"Repeat Password": "パスワードを再度入力してください",
"Create": "作成",
"signedInDispDisabled": "認証が無効化されています。",
"home": "ホーム",
"console": "コンソール",
"registry": "レジストリ",
"stackName": "スタック名",
"deployStack": "デプロイ",
"deleteStack": "削除",
"stopStack": "停止",
"restartStack": "再起動",
"updateStack": "更新",
"startStack": "起動",
"editStack": "編集",
"discardStack": "破棄",
"saveStackDraft": "保存",
"stackNotManagedByDockgeMsg": "このスタックはDockgeによって管理されていません。",
"general": "一般",
"scanFolder": "スタックフォルダをスキャン",
"dockerImage": "イメージ",
"environmentVariable": "環境変数",
"restartPolicy": "再起動ポリシー",
"containerName": "コンテナ名",
"port": "ポート",
"volume": "ボリューム",
"network": "ネットワーク",
"addListItem": "{0} を追加",
"addContainer": "コンテナを追加",
"addNetwork": "ネットワークを追加",
"compose": "Compose",
"primaryHostname": "主ホスト名",
"container": "コンテナ",
"dependsOn": "コンテナ依存関係",
"downStack": "停止して削除",
"notAvailableShort": "N/A",
"restartPolicyUnlessStopped": "手動で停止されるまで",
"restartPolicyAlways": "常時",
"restartPolicyOnFailure": "失敗時",
"restartPolicyNo": "しない",
"passwordNotMatchMsg": "繰り返しのパスワードが一致しません。",
"autoGet": "自動取得",
"add": "追加",
"Edit": "編集",
"applyToYAML": "YAMLに適用",
"createExternalNetwork": "作成",
"addInternalNetwork": "追加",
"Save": "保存",
"Language": "言語",
"Change Password": "パスワードを変更する",
"Current Password": "現在のパスワード",
"New Password": "新しいパスワード",
"Update Password": "パスワードを更新",
"Advanced": "高度",
"Please use this option carefully!": "このオプションは注意して使用してください!",
"Enable Auth": "認証を有効化",
"Disable Auth": "認証を無効化",
"Check Update On GitHub": "GitHubで更新を確認",
"Show update if available": "アップデートがある場合表示",
"Also check beta release": "ベータ版のリリースも確認する",
"Login": "ログイン",
"Username": "ユーザー名",
"Password": "パスワード",
"Settings": "設定",
"Logout": "ログアウト",
"Convert to Compose": "Composeに変換",
"Appearance": "外観",
"Security": "セキュリティ",
"Allowed commands:": "許可されたコマンド:",
"Internal Networks": "内部ネットワーク",
"External Networks": "外部ネットワーク",
"reverseProxyMsg2": "WebSocketの設定方法を確認",
"Cannot connect to the socket server.": "ソケットサーバーに接続できません。",
"reconnecting...": "再接続中…",
"Leave": "やめる",
"Frontend Version": "フロントエンドバージョン",
"Remember me": "覚えておく",
"No External Networks": "外部ネットワークなし",
"exited": "終了済み",
"inactive": "非アクティブ",
"active": "アクティブ",
"languageName": "日本語",
"Create your admin account": "管理者アカウントを作成してください",
"signedInDisp": "{0} としてログイン中",
"addFirstStackMsg": "最初のスタックを組み立てましょう!",
"deleteStackMsg": "本当にこのスタックを削除しますか?",
"deleteContainer": "削除",
"disableauth.message1": "本当に<strong>認証を無効化</strong>しますか?",
"disableauth.message2": "これはCloudflare AccessやAutheliaなどの認証手段をDockgeの前段に置いて<strong>サードパーティー認証を実装することをあなたが意図している</strong>場合のために設計されています。",
"Current User": "現在のユーザー",
"Repeat New Password": "新しいパスワードを繰り返してください",
"I understand, please disable": "理解しました。無効化してください",
"Lowercase only": "小文字のみ",
"reverseProxyMsg1": "リバースプロキシを使用していますか?",
"connecting...": "ソケットサーバーに接続中…"
}

View File

@ -90,13 +90,5 @@
"Allowed commands:": "허용된 명령어:", "Allowed commands:": "허용된 명령어:",
"Internal Networks": "내부 네트워크", "Internal Networks": "내부 네트워크",
"External Networks": "외부 네트워크", "External Networks": "외부 네트워크",
"No External Networks": "외부 네트워크 없음", "No External Networks": "외부 네트워크 없음"
"reverseProxyMsg2": "여기서 WebSocket을 위한 설정을 확인해 보세요",
"downStack": "정지 & Down",
"reverseProxyMsg1": "리버스 프록시를 사용하고 계신가요?",
"Cannot connect to the socket server.": "소켓 서버에 연결하지 못했습니다.",
"connecting...": "소켓 서버에 연결하는 중…",
"extra": "기타",
"url": "URL | URL",
"reconnecting...": "재연결 중…"
} }

View File

@ -1,102 +0,0 @@
{
"languageName": "Nederlands",
"authIncorrectCreds": "Onjuiste gebruikersnaam of wachtwoord.",
"PasswordsDoNotMatch": "Paswoorden komen niet overeen.",
"Repeat Password": "Herhaal wachtwoord",
"Create": "Aanmaken",
"signedInDisp": "Ingelogd als {0}",
"home": "Startpagina",
"console": "Console",
"registry": "Register",
"compose": "Samenstellen",
"stackName": "Stack naam",
"deployStack": "Opzetten",
"deleteStack": "Verwijder",
"stopStack": "Stop",
"restartStack": "Herstart",
"updateStack": "Update",
"startStack": "Start",
"downStack": "Stop & Down",
"editStack": "Bewerken",
"discardStack": "Verwijderen",
"saveStackDraft": "Opslaan",
"notAvailableShort": "NVT",
"stackNotManagedByDockgeMsg": "Deze stack wordt niet beheerd door Dockge.",
"primaryHostname": "Primaire hostnaam",
"general": "Algemeen",
"scanFolder": "Scan stacks folder",
"dockerImage": "Image",
"restartPolicyUnlessStopped": "Tenzij gestopt",
"restartPolicyAlways": "Altijd",
"restartPolicyOnFailure": "Bij fout",
"restartPolicyNo": "Neen",
"environmentVariable": "Omgevings variabele(n)",
"restartPolicy": "Herstart policy",
"containerName": "Containernaam",
"port": "Poort(en)",
"volume": "Volume(s)",
"network": "Netwerk(en)",
"addListItem": "Voeg {0} toe",
"deleteContainer": "Verwijder",
"addContainer": "Container toevoegen",
"addNetwork": "Netwerk toevoegen",
"signedInDispDisabled": "Aanmelden uitgeschakeld.",
"container": "Container(s)",
"autoGet": "Auto ophalen",
"add": "Toevoegen",
"Edit": "Bewerken",
"applyToYAML": "Toevoegen aan YAML",
"createExternalNetwork": "Aanmaken",
"addInternalNetwork": "Toevoegen",
"Save": "Opslaan",
"Language": "Taal",
"Change Password": "Verander wachtwoord",
"Current Password": "Huidig wachtwoord",
"New Password": "Nieuw wachtwoord",
"Repeat New Password": "Herhaal nieuw wachtwoord",
"Update Password": "Update wachtwoord",
"Advanced": "Geavanceerd",
"I understand, please disable": "Begrepen, dit uitschakelen",
"Disable Auth": "Aanmelden uitschakelen",
"Enable Auth": "Aanmelden inschakelen",
"Leave": "Afmelden",
"Frontend Version": "Frontend versie",
"Check Update On GitHub": "Controleer via GitHub op updates",
"Show update if available": "Toon update indien beschikbaar",
"Remember me": "Onthoud mij",
"Login": "Inloggen",
"Username": "Gebruikersnaam",
"Password": "Wachtwoord",
"Settings": "Instellingen",
"Logout": "Uitloggen",
"Lowercase only": "Geen hoofdletters",
"Docker Run": "Docker run",
"active": "actief",
"exited": "gestopt",
"inactive": "inactief",
"Appearance": "Uiterlijk",
"Security": "Beveiliging",
"About": "Over",
"Allowed commands:": "Toegelaten commando's:",
"Internal Networks": "Interne netwerken",
"No External Networks": "Geen externe netwerken",
"reverseProxyMsg1": "Reverse proxy in gebruik?",
"reverseProxyMsg2": "Controleer hoe te configureren voor WebSocket",
"Cannot connect to the socket server.": "Kan geen verbinding maken met de socket server.",
"reconnecting...": "Herverbinden...",
"connecting...": "Verbinden met de socket server...",
"url": "Url(s)",
"extra": "Extra",
"Create your admin account": "Creëer je beheerders-account",
"addFirstStackMsg": "Maak je eerste stack!",
"deleteStackMsg": "Zeker dat je deze stack wilt verwijderen?",
"dependsOn": "Container afhankelijkheid | afhankelijkheden",
"disableauth.message1": "Zeker dat u <strong>aanmelden</strong> wilt uitschakelen?",
"disableauth.message2": "Dit is enkel bedoeld om te gebruiken wanneer je<strong> third-party autorisatie wilt gebruiken voor Dockge</strong>, zoals Cloudflare Acces, Authelia, ...",
"passwordNotMatchMsg": "De wachtwoorden komen niet overeen.",
"Current User": "Huidige gebruiker",
"Please use this option carefully!": "Wees voorzichtig met deze optie!",
"Also check beta release": "Controleer ook op beta releases",
"Convert to Compose": "Converteer naar compose",
"External Networks": "Externe netwerken"
}

View File

@ -1,102 +0,0 @@
{
"Create your admin account": "Creați-vă contul de administrator",
"PasswordsDoNotMatch": "Parolele nu se potrivesc.",
"Repeat Password": "Repetați parola",
"signedInDisp": "Conectat ca {0}",
"signedInDispDisabled": "Autentificare dezactivată.",
"Create": "Creează",
"home": "Acasă",
"console": "Consolă",
"registry": "Registru",
"compose": "Compune",
"addFirstStackMsg": "Compune prima ta stivă!",
"stackName": "Nume stivă",
"deployStack": "Lansează",
"deleteStack": "Șterge",
"stopStack": "Oprește",
"restartStack": "Repornire",
"updateStack": "Actualizare",
"languageName": "Română",
"authIncorrectCreds": "Numele de utilizator sau parola incorectă.",
"startStack": "Pornește",
"editStack": "Editați",
"discardStack": "Renunţa",
"notAvailableShort": "N/A",
"deleteStackMsg": "Sigur doriți să ștergeți această stivă?",
"stackNotManagedByDockgeMsg": "Această stivă nu este gestionată de Dockge.",
"primaryHostname": "Numele gazdei principale",
"general": "General",
"container": "Container | Containere",
"scanFolder": "Scanează folderul cu stive",
"dockerImage": "Imagine",
"restartPolicyOnFailure": "La Defecţiune",
"restartPolicyNo": "Nu",
"restartPolicy": "Politica de repornire",
"restartPolicyAlways": "Mereu",
"containerName": "Numele Containerului",
"port": "Port | Porturi",
"volume": "Volum | Volume",
"network": "Reţea | Reţele",
"dependsOn": "Dependența containerului | Dependențele containerelor",
"addListItem": "Adaugă {0}",
"deleteContainer": "Șterge",
"addContainer": "Adaugă Container",
"addNetwork": "Adaugă Rețea",
"addInternalNetwork": "Adaugă",
"Save": "Salvează",
"Current User": "Utilizator Curent",
"Change Password": "Schimbă Parola",
"Current Password": "Parolă Curenta",
"New Password": "Parolă Nouă",
"Repeat New Password": "Repetă Parola Nouă",
"Update Password": "Actualizează Parola",
"Advanced": "Avansat",
"Enable Auth": "Activați Autentificarea",
"Disable Auth": "Dezactivați Autentificarea",
"I understand, please disable": "Am înțeles, vă rog dezactivați",
"Leave": "Părăsiți",
"Frontend Version": "Versiunea Frontend",
"Check Update On GitHub": "Verificați actualizarea pe GitHub",
"Also check beta release": "Verificați și versiunea beta",
"Remember me": "Ține-mă minte",
"Login": "Autentificare",
"Username": "Nume de utilizator",
"Password": "Parolă",
"passwordNotMatchMsg": "Parola repetată nu se potrivește.",
"autoGet": "Obținere automată",
"add": "Adăuga",
"Edit": "Editați",
"applyToYAML": "Aplicați la YAML",
"createExternalNetwork": "Creează",
"Settings": "Setări",
"Logout": "Deconectare",
"Lowercase only": "Doar litere mici",
"Convert to Compose": "Convertiți în Compose",
"Docker Run": "Docker Run",
"active": "activ",
"exited": "ieșit",
"inactive": "inactiv",
"Appearance": "Aspect",
"Security": "Securitate",
"About": "Despre",
"Allowed commands:": "Comenzi permise:",
"Internal Networks": "Rețele interne",
"External Networks": "Rețele externe",
"No External Networks": "Fără rețele externe",
"reverseProxyMsg1": "Folosești un proxy invers?",
"reverseProxyMsg2": "Verificați cum să-l configurați pentru WebSocket",
"Cannot connect to the socket server.": "Nu se poate conecta la serverul socket.",
"reconnecting...": "Reconectare...",
"connecting...": "Se conectează la serverul socket...",
"url": "URL | URLs",
"extra": "Suplimentar",
"downStack": "Opriți & Coborâți",
"saveStackDraft": "Salvați",
"restartPolicyUnlessStopped": "Dacă nu este oprit",
"environmentVariable": "Variabila de mediu | Variabile de mediu",
"Language": "Limbă",
"Please use this option carefully!": "Vă rugăm să utilizați această opțiune cu atenție!",
"Show update if available": "Afișează actualizarea dacă este disponibilă",
"disableauth.message1": "Sigur doriți să <strong>dezactivați autentificarea</strong>?",
"disableauth.message2": "Este conceput pentru scenarii <strong>în care intenționați să implementați autentificarea terță</strong> în fața Dockge-lui, cum ar fi Cloudflare Access, Authelia sau alte mecanisme de autentificare."
}

View File

@ -5,14 +5,14 @@
"PasswordsDoNotMatch": "Пароль не совпадает.", "PasswordsDoNotMatch": "Пароль не совпадает.",
"Repeat Password": "Повторите пароль", "Repeat Password": "Повторите пароль",
"Create": "Создать", "Create": "Создать",
"signedInDisp": "Авторизован как", "signedInDisp": "Авторизлван как {0}",
"signedInDispDisabled": "Авторизация выключена.", "signedInDispDisabled": "Авторизация выключена.",
"home": "Главная", "home": "Главная",
"console": "Консоль", "console": "Консоль",
"registry": "Registry", "registry": "Registry",
"compose": "Compose", "compose": "Compose",
"addFirstStackMsg": "Создайте свой первый стек!", "addFirstStackMsg": "Создайте свой первый стек!",
"stackName": "Имя стека", "stackName" : "Имя стека",
"deployStack": "Развернуть", "deployStack": "Развернуть",
"deleteStack": "Удалить", "deleteStack": "Удалить",
"stopStack": "Остановить", "stopStack": "Остановить",
@ -22,7 +22,7 @@
"editStack": "Изменить", "editStack": "Изменить",
"discardStack": "Отменить", "discardStack": "Отменить",
"saveStackDraft": "Сохранить", "saveStackDraft": "Сохранить",
"notAvailableShort": "Н/Д", "notAvailableShort" : "Н/Д",
"deleteStackMsg": "Вы уверены что хотите удалить этот стек?", "deleteStackMsg": "Вы уверены что хотите удалить этот стек?",
"stackNotManagedByDockgeMsg": "Данный стек не обслуживается Dockge.", "stackNotManagedByDockgeMsg": "Данный стек не обслуживается Dockge.",
"primaryHostname": "Имя хоста", "primaryHostname": "Имя хоста",
@ -79,7 +79,7 @@
"Settings": "Настройки", "Settings": "Настройки",
"Logout": "Выйти", "Logout": "Выйти",
"Lowercase only": "Только нижний регистр", "Lowercase only": "Только нижний регистр",
"Convert to Compose": "Преобразовать в Compose", "Convert to Compose": "Преобразовать вCompose",
"Docker Run": "Запустить Docker", "Docker Run": "Запустить Docker",
"active": "активный", "active": "активный",
"exited": "завершенный", "exited": "завершенный",

View File

@ -12,7 +12,7 @@
"registry": "Kayıt Defteri", "registry": "Kayıt Defteri",
"compose": "Compose", "compose": "Compose",
"addFirstStackMsg": "İlk yığınınızı oluşturun!", "addFirstStackMsg": "İlk yığınınızı oluşturun!",
"stackName": "Yığın Adı", "stackName" : "Yığın Adı",
"deployStack": "Dağıtmak", "deployStack": "Dağıtmak",
"deleteStack": "Sil", "deleteStack": "Sil",
"stopStack": "Dudur", "stopStack": "Dudur",
@ -22,7 +22,7 @@
"editStack": "Düzenle", "editStack": "Düzenle",
"discardStack": ıkar", "discardStack": ıkar",
"saveStackDraft": "Kaydet", "saveStackDraft": "Kaydet",
"notAvailableShort": "N/A", "notAvailableShort" : "N/A",
"deleteStackMsg": "Bu yığını silmek istediğinizden emin misiniz?", "deleteStackMsg": "Bu yığını silmek istediğinizden emin misiniz?",
"stackNotManagedByDockgeMsg": "Bu yığın Dockge tarafından yönetilmemektedir.", "stackNotManagedByDockgeMsg": "Bu yığın Dockge tarafından yönetilmemektedir.",
"primaryHostname": "Birincil Ana Bilgisayar Adı", "primaryHostname": "Birincil Ana Bilgisayar Adı",
@ -90,13 +90,5 @@
"Allowed commands:": "İzin verilen komutlar:", "Allowed commands:": "İzin verilen komutlar:",
"Internal Networks": "İç Ağlar", "Internal Networks": "İç Ağlar",
"External Networks": "Dış Ağlar", "External Networks": "Dış Ağlar",
"No External Networks": "Dış Ağ Yok", "No External Networks": "Dış Ağ Yok"
"extra": "Ekstra",
"reverseProxyMsg1": "Ters Proxy mi kullanıyorsunuz?",
"reverseProxyMsg2": "WebSocket için nasıl yapılandırma yapılacağını kontrol edin",
"reconnecting...": "Yeniden bağlanıyor…",
"connecting...": "Soket sunucusuna bağlanıyor…",
"url": "URL | URLler",
"Cannot connect to the socket server.": "Soket sunucusuna bağlanılamıyor.",
"downStack": "Durdur & Kapat"
} }

View File

@ -1,102 +0,0 @@
{
"languageName": "Українська",
"Create your admin account": "Створити акаунт адміністратора",
"authIncorrectCreds": "Неправильне ім'я користувача або пароль.",
"PasswordsDoNotMatch": "Паролі не збігаються.",
"Repeat Password": "Повторіть пароль",
"Create": "Створити",
"signedInDisp": "Авторизовано як {0}",
"signedInDispDisabled": "Авторизацію вимкнено.",
"home": "Головна",
"console": "Консоль",
"registry": "Registry",
"compose": "Compose",
"addFirstStackMsg": "Додайте свій перший стек!",
"stackName": "Назва стеку",
"deployStack": "Розгорнути",
"deleteStack": "Видалити",
"stopStack": "Зупинити",
"restartStack": "Перезапустити",
"updateStack": "Оновити",
"startStack": "Запустити",
"editStack": "Редагувати",
"discardStack": "Відмінити",
"saveStackDraft": "Зберегти",
"notAvailableShort": "Н/Д",
"deleteStackMsg": "Ви впевнені що хочете видалити цей стек?",
"stackNotManagedByDockgeMsg": "Даний стек не управляється Dockge.",
"primaryHostname": "Назва хосту",
"general": "Загальне",
"container": "Контейнер | Контейнери",
"scanFolder": "Сканувати папку зі стеками",
"dockerImage": "Образ",
"restartPolicyUnlessStopped": "Доки не буде зупинено",
"restartPolicyAlways": "Завжди",
"restartPolicyOnFailure": "При падінні",
"restartPolicyNo": "Ніколи",
"environmentVariable": "Змінна середовища | змінні середовища",
"restartPolicy": "Перезапуск",
"containerName": "Назва контейнеру",
"port": "Порт | Порти",
"volume": "Сховище | Сховища",
"network": "Мережа | Мережі",
"dependsOn": "Залежність контейнера | Залежності контейнеру",
"addListItem": "Додати {0}",
"deleteContainer": "Видалити",
"addContainer": "Додати Контейнер",
"addNetwork": "Додати Мережу",
"disableauth.message1": "Ви впевнені що хочете <strong>вимкнути авторизацію</strong>?",
"disableauth.message2": "Це призначено для сценаріїв, <strong>де ви збираєтесь використати сторонню авторизацію</strong> перед Dockge, наприклад Cloudflare Access, Authelia чи інші.",
"passwordNotMatchMsg": "Повторення паролю не збігається.",
"autoGet": "Отримати",
"add": "Додати",
"Edit": "Змінити",
"applyToYAML": "Застосувати для YAML",
"createExternalNetwork": "Створити",
"addInternalNetwork": "Додати",
"Save": "Зберегти",
"Language": "Мова",
"Current User": "Користувач",
"Change Password": "Змінити пароль",
"Current Password": "Поточний пароль",
"New Password": "Новий пароль",
"Repeat New Password": "Повторіть новий пароль",
"Update Password": "Оновити пароль",
"Advanced": "Розширені опції",
"Please use this option carefully!": "Будь ласка, використовуйте цю опцію з обережністю!",
"Enable Auth": "Увімкнути автентифікацію",
"Disable Auth": "Вимкнути автентифікацію",
"I understand, please disable": "Зрозуміло, все одно вимкнути",
"Leave": "Покинути",
"Frontend Version": "Версія інтерфейсу",
"Check Update On GitHub": "Перевірити оновлення на GitHub",
"Show update if available": "Показати оновлення, якщо доступно",
"Also check beta release": "Перевіряти оновлення до бета-версії",
"Remember me": "Запамʼятати мене",
"Login": "Логін",
"Username": "Імʼя користувача",
"Password": "Пароль",
"Settings": "Налаштування",
"Logout": "Вийти",
"Lowercase only": "Тільки нижній регістр",
"Convert to Compose": "Конвертувати в Compose",
"Docker Run": "Запустити Docker",
"active": "активно",
"exited": "завершено",
"inactive": "неактивно",
"Appearance": "Зовнішній вигляд",
"Security": "Безпека",
"About": "Про продукт",
"Allowed commands:": "Дозволені команди:",
"Internal Networks": "Внутрішні мережі",
"External Networks": "Зовнішні мережі",
"No External Networks": "Немає зовнішніх мереж",
"downStack": "Зупинити і вимкнути",
"reverseProxyMsg1": "Використовувати зворотній проксі?",
"Cannot connect to the socket server.": "Не вдається підключитися до сервера сокетів.",
"reconnecting...": "Повторне підключення…",
"connecting...": "Підключення до сервера сокетів…",
"url": "URL-адреса | URL-адреси",
"reverseProxyMsg2": "Перевірте, як налаштувати його для WebSocket",
"extra": "Додатково"
}

View File

@ -12,23 +12,23 @@
"registry": "رجسٹری", "registry": "رجسٹری",
"compose": "تحریر", "compose": "تحریر",
"addFirstStackMsg": "اپنا پہلا اسٹیک کمپوز کریں!", "addFirstStackMsg": "اپنا پہلا اسٹیک کمپوز کریں!",
"stackName": "اسٹیک کا نام", "stackName" : "اسٹیک کا نام",
"deployStack": "تعینات", "deployStack": "تعینات",
"deleteStack": "حذف کریں", "deleteStack": "حذف کریں",
"stopStack": "روکو", "stopStack": "روکو",
"restartStack": "دوبارہ شروع کریں", "restartStack": "دوبارہ شروع کریں",
"updateStack": "اپ ڈیٹ", "updateStack": "اپ ڈیٹ",
"startStack": "شروع کریں", "startStack": "شروع کریں۔",
"editStack": "ترمیم", "editStack": "ترمیم",
"discardStack": "رد کر دیں", "discardStack": "رد کر دیں۔",
"saveStackDraft": "محفوظ کریں۔", "saveStackDraft": "محفوظ کریں۔",
"notAvailableShort": "N / A", "notAvailableShort" : "N / A",
"deleteStackMsg": "کیا آپ واقعی اس اسٹیک کو حذف کرنا چاہتے ہیں؟", "deleteStackMsg": "کیا آپ واقعی اس اسٹیک کو حذف کرنا چاہتے ہیں؟",
"stackNotManagedByDockgeMsg": "یہ اسٹیک Dockge کے زیر انتظام نہیں ہے۔", "stackNotManagedByDockgeMsg": "یہ اسٹیک Dockge کے زیر انتظام نہیں ہے۔",
"primaryHostname": "بنیادی میزبان نام", "primaryHostname": "بنیادی میزبان نام",
"general": "جنرل", "general": "جنرل",
"container": "کنٹینر | کنٹینرز", "container": "کنٹینر | کنٹینرز",
"scanFolder": "اسٹیک فولڈر کو اسکین کریں", "scanFolder": "اسٹیک فولڈر کو اسکین کریں۔",
"dockerImage": "تصویر", "dockerImage": "تصویر",
"restartPolicyUnlessStopped": "جب تک روکا نہیں جاتا", "restartPolicyUnlessStopped": "جب تک روکا نہیں جاتا",
"restartPolicyAlways": "ہمیشہ", "restartPolicyAlways": "ہمیشہ",
@ -51,7 +51,7 @@
"autoGet": "آٹو حاصل کریں", "autoGet": "آٹو حاصل کریں",
"add": "شامل کریں", "add": "شامل کریں",
"Edit": "ترمیم", "Edit": "ترمیم",
"applyToYAML": "YAML پر درخواست دیں", "applyToYAML": "YAML پر درخواست دیں۔",
"createExternalNetwork": "بنانا", "createExternalNetwork": "بنانا",
"addInternalNetwork": "شامل کریں", "addInternalNetwork": "شامل کریں",
"Save": "محفوظ کریں", "Save": "محفوظ کریں",
@ -64,12 +64,12 @@
"Update Password": "پاس ورڈ اپ ڈیٹ کریں", "Update Password": "پاس ورڈ اپ ڈیٹ کریں",
"Advanced": "ترقی یافتہ", "Advanced": "ترقی یافتہ",
"Please use this option carefully!": "براہ کرم اس اختیار کو احتیاط سے استعمال کریں!", "Please use this option carefully!": "براہ کرم اس اختیار کو احتیاط سے استعمال کریں!",
"Enable Auth": "تصدیق کو فعال کریں", "Enable Auth": "تصدیق کو فعال کریں۔",
"Disable Auth": "توثیق کو غیر فعال کریں", "Disable Auth": "توثیق کو غیر فعال کریں۔",
"I understand, please disable": "میں سمجھتا ہوں، براہ کرم غیر فعال کریں", "I understand, please disable": "میں سمجھتا ہوں، براہ کرم غیر فعال کریں۔",
"Leave": "چھوڑ دو", "Leave": "چھوڑ دو",
"Frontend Version": "فرنٹ اینڈ ورژن", "Frontend Version": "فرنٹ اینڈ ورژن",
"Check Update On GitHub": "گیتوب پر اپ ڈیٹ چیک کریں", "Check Update On GitHub": "گیتوب پر اپ ڈیٹ چیک کریں۔",
"Show update if available": "اگر دستیاب ہو تو اپ ڈیٹ دکھائیں", "Show update if available": "اگر دستیاب ہو تو اپ ڈیٹ دکھائیں",
"Also check beta release": "بیٹا ریلیز بھی چیک کریں", "Also check beta release": "بیٹا ریلیز بھی چیک کریں",
"Remember me": "مجھے پہچانتے ہو", "Remember me": "مجھے پہچانتے ہو",
@ -90,13 +90,5 @@
"Allowed commands:": "اجازت شدہ احکامات:", "Allowed commands:": "اجازت شدہ احکامات:",
"Internal Networks": "اندرونی نیٹ ورکس", "Internal Networks": "اندرونی نیٹ ورکس",
"External Networks": "بیرونی نیٹ ورکس", "External Networks": "بیرونی نیٹ ورکس",
"No External Networks": "کوئی بیرونی نیٹ ورک نہیں", "No External Networks": "کوئی بیرونی نیٹ ورک نہیں"
"reverseProxyMsg1": "ایک ریورس پراکسی کا استعمال کرتے ہوئے؟",
"Cannot connect to the socket server.": "ساکٹ سرور سے منسلک نہیں ہو سکتا۔",
"reconnecting...": "دوبارہ منسلک ہو رہا ہے…",
"connecting...": "ساکٹ سرور سے منسلک ہو رہا ہے…",
"url": "یو آر ایل | یو آر ایل",
"extra": "اضافی",
"downStack": "اسٹاپ اینڈ ڈاؤن",
"reverseProxyMsg2": "اسے WebSocket کے لیے ترتیب دینے کا طریقہ چیک کریں"
} }

View File

@ -1,18 +1,18 @@
{ {
"languageName": "简体中文", "languageName": "简体中文",
"Create your admin account": "创建你的管理员账号", "Create your admin account": "创建你的管理员账号",
"authIncorrectCreds": "用户名或密码错误", "authIncorrectCreds": "用户名或密码错误",
"PasswordsDoNotMatch": "两次输入的密码不一致。", "PasswordsDoNotMatch": "两次输入的密码不一致。",
"Repeat Password": "重复以确认密码", "Repeat Password": "重复以确认密码",
"Create": "创建", "Create": "创建",
"signedInDisp": "当前用户: {0}", "signedInDisp": "当前用户: {0}",
"signedInDispDisabled": "已禁用身份验证", "signedInDispDisabled": "已禁用身份验证",
"home": "主页", "home": "主页",
"console": "终端", "console": "终端",
"registry": "镜像仓库", "registry": "镜像仓库",
"compose": "Compose", "compose": "Compose",
"addFirstStackMsg": "组合你的第一个堆栈!", "addFirstStackMsg": "组合你的第一个堆栈!",
"stackName": "堆栈名称", "stackName" : "堆栈名称",
"deployStack": "部署", "deployStack": "部署",
"deleteStack": "删除", "deleteStack": "删除",
"stopStack": "停止", "stopStack": "停止",
@ -22,9 +22,9 @@
"editStack": "编辑", "editStack": "编辑",
"discardStack": "放弃", "discardStack": "放弃",
"saveStackDraft": "保存", "saveStackDraft": "保存",
"notAvailableShort": "不可用", "notAvailableShort" : "不可用",
"deleteStackMsg": "你确定要删除这个堆栈吗?", "deleteStackMsg": "你确定要删除这个堆栈吗?",
"stackNotManagedByDockgeMsg": "这个堆栈不由Dockge管理", "stackNotManagedByDockgeMsg": "这个堆栈不由Dockge管理",
"primaryHostname": "主机名", "primaryHostname": "主机名",
"general": "常规", "general": "常规",
"container": "容器 | 容器组", "container": "容器 | 容器组",
@ -90,13 +90,5 @@
"Allowed commands:": "允许使用的指令:", "Allowed commands:": "允许使用的指令:",
"Internal Networks": "内部网络", "Internal Networks": "内部网络",
"External Networks": "外部网络", "External Networks": "外部网络",
"No External Networks": "无外部网络", "No External Networks": "无外部网络"
"reconnecting...": "重连中…",
"reverseProxyMsg2": "检查如何配置WebSocket",
"reverseProxyMsg1": "正在使用反向代理?",
"connecting...": "正在连接到socket服务器…",
"Cannot connect to the socket server.": "无法连接到socket服务器。",
"url": "网址 | 网址",
"extra": "额外",
"downStack": "停止并删除"
} }

View File

@ -1,102 +0,0 @@
{
"languageName": "繁體中文(台灣)",
"Create your admin account": "建立您的管理員帳號",
"authIncorrectCreds": "使用者名稱或密碼錯誤。",
"PasswordsDoNotMatch": "兩次輸入的密碼不一致。",
"Repeat Password": "重複以確認密碼",
"Create": "建立",
"signedInDisp": "目前使用者:{0}",
"signedInDispDisabled": "已停用身份驗證。",
"home": "首頁",
"console": "主控台",
"registry": "映像倉庫",
"compose": "Compose",
"addFirstStackMsg": "組合您的第一個堆疊!",
"stackName": "堆疊名稱",
"deployStack": "部署",
"deleteStack": "刪除",
"stopStack": "停止",
"restartStack": "重啟",
"updateStack": "更新",
"startStack": "啟動",
"editStack": "編輯",
"discardStack": "捨棄",
"saveStackDraft": "儲存",
"notAvailableShort": "不可用",
"deleteStackMsg": "您確定要刪除這個堆疊嗎?",
"stackNotManagedByDockgeMsg": "這個堆疊不由 Dockge 管理。",
"primaryHostname": "主機名稱",
"general": "一般",
"container": "容器 | 容器群組",
"scanFolder": "掃描堆疊資料夾",
"dockerImage": "映像",
"restartPolicyUnlessStopped": "除非手動停止",
"restartPolicyAlways": "始終",
"restartPolicyOnFailure": "在失敗時",
"restartPolicyNo": "不重啟",
"environmentVariable": "環境變數 | 環境變數群組",
"restartPolicy": "重啟策略",
"containerName": "容器名稱",
"port": "連接埠 | 連接埠群組",
"volume": "資料卷 | 資料卷群組",
"network": "網路 | 網路群組",
"dependsOn": "容器依賴 | 容器依賴關係",
"addListItem": "新增 {0}",
"deleteContainer": "刪除容器",
"addContainer": "新增容器",
"addNetwork": "新增網路",
"disableauth.message1": "您確定要<strong>停用身份驗證</strong>嗎?",
"disableauth.message2": "該選項設計用於某些場景,<strong>例如在 Dockge 之上接入第三方認證</strong>,如 Cloudflare Access、Authelia 或其他認證機制。如果您不清楚這個選項的作用,請不要停用驗證!",
"passwordNotMatchMsg": "兩次輸入的密碼不一致。",
"autoGet": "自動取得",
"add": "新增",
"Edit": "編輯",
"applyToYAML": "應用到YAML",
"createExternalNetwork": "建立",
"addInternalNetwork": "新增",
"Save": "儲存",
"Language": "語言",
"Current User": "目前使用者",
"Change Password": "更換密碼",
"Current Password": "目前密碼",
"New Password": "新密碼",
"Repeat New Password": "重複以確認新密碼",
"Update Password": "更新密碼",
"Advanced": "進階",
"Please use this option carefully!": "請謹慎使用該選項!",
"Enable Auth": "啟用驗證",
"Disable Auth": "停用驗證",
"I understand, please disable": "我已了解風險,確認停用",
"Leave": "離開",
"Frontend Version": "前端版本",
"Check Update On GitHub": "在 GitHub 上檢查更新",
"Show update if available": "有更新時提醒我",
"Also check beta release": "同時檢查 Beta 渠道更新",
"Remember me": "記住我",
"Login": "登入",
"Username": "使用者名稱",
"Password": "密碼",
"Settings": "設定",
"Logout": "登出",
"Lowercase only": "僅小寫字母",
"Convert to Compose": "轉換為 Compose 格式",
"Docker Run": "Docker 啟動",
"active": "已啟動",
"exited": "已退出",
"inactive": "未啟動",
"Appearance": "外觀",
"Security": "安全",
"About": "關於",
"Allowed commands:": "允許使用的指令:",
"Internal Networks": "內部網路",
"External Networks": "外部網路",
"No External Networks": "無外部網路",
"downStack": "停止",
"reverseProxyMsg1": "在使用反向代理吗?",
"reverseProxyMsg2": "點擊這裡了解如何為 WebSocket 配置反向代理",
"Cannot connect to the socket server.": "無法連接到 Socket 伺服器。",
"reconnecting...": "重新連線中…",
"connecting...": "連線至 Socket 伺服器中…",
"url": "網址 | 網址",
"extra": "額外"
}

View File

@ -3,9 +3,6 @@
<div v-if="! $root.socketIO.connected && ! $root.socketIO.firstConnect" class="lost-connection"> <div v-if="! $root.socketIO.connected && ! $root.socketIO.firstConnect" class="lost-connection">
<div class="container-fluid"> <div class="container-fluid">
{{ $root.socketIO.connectionErrorMsg }} {{ $root.socketIO.connectionErrorMsg }}
<div v-if="$root.socketIO.showReverseProxyGuide">
{{ $t("reverseProxyMsg1") }} <a href="https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy" target="_blank">{{ $t("reverseProxyMsg2") }}</a>
</div>
</div> </div>
</div> </div>
@ -85,10 +82,6 @@
</header> </header>
<main> <main>
<div v-if="$root.socketIO.connecting" class="container mt-5">
<h4>{{ $t("connecting...") }}</h4>
</div>
<router-view v-if="$root.loggedIn" /> <router-view v-if="$root.loggedIn" />
<Login v-if="! $root.loggedIn && $root.allowLoginDialog" /> <Login v-if="! $root.loggedIn && $root.allowLoginDialog" />
</main> </main>

View File

@ -19,7 +19,6 @@ export default defineComponent({
initedSocketIO: false, initedSocketIO: false,
connectionErrorMsg: `${this.$t("Cannot connect to the socket server.")} ${this.$t("Reconnecting...")}`, connectionErrorMsg: `${this.$t("Cannot connect to the socket server.")} ${this.$t("Reconnecting...")}`,
showReverseProxyGuide: true, showReverseProxyGuide: true,
connecting: false,
}, },
info: { info: {
@ -104,10 +103,6 @@ export default defineComponent({
url = location.protocol + "//" + location.host; url = location.protocol + "//" + location.host;
} }
let connectingMsgTimeout = setTimeout(() => {
this.socketIO.connecting = true;
}, 1500);
socket = io(url, { socket = io(url, {
transports: [ "websocket", "polling" ] transports: [ "websocket", "polling" ]
}); });
@ -115,9 +110,6 @@ export default defineComponent({
socket.on("connect", () => { socket.on("connect", () => {
console.log("Connected to the socket server"); console.log("Connected to the socket server");
clearTimeout(connectingMsgTimeout);
this.socketIO.connecting = false;
this.socketIO.connectCount++; this.socketIO.connectCount++;
this.socketIO.connected = true; this.socketIO.connected = true;
this.socketIO.showReverseProxyGuide = false; this.socketIO.showReverseProxyGuide = false;
@ -151,11 +143,10 @@ export default defineComponent({
socket.on("connect_error", (err) => { socket.on("connect_error", (err) => {
console.error(`Failed to connect to the backend. Socket.io connect_error: ${err.message}`); console.error(`Failed to connect to the backend. Socket.io connect_error: ${err.message}`);
this.socketIO.connectionErrorMsg = `${this.$t("Cannot connect to the socket server.")} [${err}] ${this.$t("reconnecting...")}`; this.socketIO.connectionErrorMsg = `${this.$t("Cannot connect to the socket server.")} [${err}] ${this.$t("Reconnecting...")}`;
this.socketIO.showReverseProxyGuide = true; this.socketIO.showReverseProxyGuide = true;
this.socketIO.connected = false; this.socketIO.connected = false;
this.socketIO.firstConnect = false; this.socketIO.firstConnect = false;
this.socketIO.connecting = false;
}); });
// Custom Events // Custom Events

View File

@ -41,7 +41,7 @@
{{ $t("stopStack") }} {{ $t("stopStack") }}
</button> </button>
<BDropdown right text="" variant="normal"> <BDropdown v-if="!isEditMode && active" right text="" variant="normal">
<BDropdownItem @click="downStack"> <BDropdownItem @click="downStack">
<font-awesome-icon icon="stop" class="me-1" /> <font-awesome-icon icon="stop" class="me-1" />
{{ $t("downStack") }} {{ $t("downStack") }}
@ -56,13 +56,6 @@
</button> </button>
</div> </div>
<!-- URLs -->
<div v-if="urls.length > 0" class="mb-3">
<a v-for="(url, index) in urls" :key="index" target="_blank" :href="url.url">
<span class="badge bg-secondary me-2">{{ url.display }}</span>
</a>
</div>
<!-- Progress Terminal --> <!-- Progress Terminal -->
<transition name="slide-fade" appear> <transition name="slide-fade" appear>
<Terminal <Terminal
@ -118,20 +111,6 @@
<button v-if="false && isEditMode && jsonConfig.services && Object.keys(jsonConfig.services).length > 0" class="btn btn-normal mb-3" @click="addContainer">{{ $t("addContainer") }}</button> <button v-if="false && isEditMode && jsonConfig.services && Object.keys(jsonConfig.services).length > 0" class="btn btn-normal mb-3" @click="addContainer">{{ $t("addContainer") }}</button>
<!-- General -->
<div v-if="isEditMode">
<h4 class="mb-3">{{ $t("extra") }}</h4>
<div class="shadow-box big-padding mb-3">
<!-- URLs -->
<div class="mb-4">
<label class="form-label">
{{ $tc("url", 2) }}
</label>
<ArrayInput name="urls" :display-name="$t('url')" placeholder="https://" object-type="x-dockge" />
</div>
</div>
</div>
<!-- Combined Terminal Output --> <!-- Combined Terminal Output -->
<div v-show="!isEditMode"> <div v-show="!isEditMode">
<h4 class="mb-3">Terminal</h4> <h4 class="mb-3">Terminal</h4>
@ -154,7 +133,7 @@
ref="editor" ref="editor"
v-model="stack.composeYAML" v-model="stack.composeYAML"
class="yaml-editor" class="yaml-editor"
:highlight="highlighterYAML" :highlight="highlighter"
line-numbers :readonly="!isEditMode" line-numbers :readonly="!isEditMode"
@input="yamlCodeChange" @input="yamlCodeChange"
@focus="editorFocus = true" @focus="editorFocus = true"
@ -165,22 +144,6 @@
{{ yamlError }} {{ yamlError }}
</div> </div>
<!-- ENV editor -->
<div v-if="isEditMode">
<h4 class="mb-3">.env</h4>
<div class="shadow-box mb-3 editor-box" :class="{'edit-mode' : isEditMode}">
<prism-editor
ref="editor"
v-model="stack.composeENV"
class="env-editor"
:highlight="highlighterENV"
line-numbers :readonly="!isEditMode"
@focus="editorFocus = true"
@blur="editorFocus = false"
></prism-editor>
</div>
</div>
<div v-if="isEditMode"> <div v-if="isEditMode">
<!-- Volumes --> <!-- Volumes -->
<div v-if="false"> <div v-if="false">
@ -248,16 +211,10 @@ services:
ports: ports:
- "8080:80" - "8080:80"
`; `;
const envDefault = "# VARIABLE=value #comment";
let yamlErrorTimeout = null; let yamlErrorTimeout = null;
let serviceStatusTimeout = null; let serviceStatusTimeout = null;
let prismjsSymbolDefinition = {
"symbol": {
pattern: /(?<!\$)\$(\{[^{}]*\}|\w+)/,
}
};
export default { export default {
components: { components: {
@ -295,34 +252,6 @@ export default {
}; };
}, },
computed: { computed: {
urls() {
if (!this.jsonConfig["x-dockge"] || !this.jsonConfig["x-dockge"].urls || !Array.isArray(this.jsonConfig["x-dockge"].urls)) {
return [];
}
let urls = [];
for (const url of this.jsonConfig["x-dockge"].urls) {
let display;
try {
let obj = new URL(url);
let pathname = obj.pathname;
if (pathname === "/") {
pathname = "";
}
display = obj.host + pathname + obj.search;
} catch (e) {
display = url;
}
urls.push({
display,
url,
});
}
return urls;
},
isAdd() { isAdd() {
return this.$route.path === "/compose" && !this.submitted; return this.$route.path === "/compose" && !this.submitted;
}, },
@ -403,26 +332,19 @@ export default {
this.isEditMode = true; this.isEditMode = true;
let composeYAML; let composeYAML;
let composeENV;
if (this.$root.composeTemplate) { if (this.$root.composeTemplate) {
composeYAML = this.$root.composeTemplate; composeYAML = this.$root.composeTemplate;
this.$root.composeTemplate = ""; this.$root.composeTemplate = "";
} else { } else {
composeYAML = template; composeYAML = template;
} }
if (this.$root.envTemplate) {
composeENV = this.$root.envTemplate;
this.$root.envTemplate = "";
} else {
composeENV = envDefault;
}
// Default Values // Default Values
this.stack = { this.stack = {
name: "", name: "",
composeYAML, composeYAML,
composeENV,
isManagedByDockge: true, isManagedByDockge: true,
}; };
@ -521,7 +443,7 @@ export default {
this.bindTerminal(this.terminalName); this.bindTerminal(this.terminalName);
this.$root.getSocket().emit("deployStack", this.stack.name, this.stack.composeYAML, this.stack.composeENV, this.isAdd, (res) => { this.$root.getSocket().emit("deployStack", this.stack.name, this.stack.composeYAML, this.isAdd, (res) => {
this.processing = false; this.processing = false;
this.$root.toastRes(res); this.$root.toastRes(res);
@ -535,7 +457,7 @@ export default {
saveStack() { saveStack() {
this.processing = true; this.processing = true;
this.$root.getSocket().emit("saveStack", this.stack.name, this.stack.composeYAML, this.stack.composeENV, this.isAdd, (res) => { this.$root.getSocket().emit("saveStack", this.stack.name, this.stack.composeYAML, this.isAdd, (res) => {
this.processing = false; this.processing = false;
this.$root.toastRes(res); this.$root.toastRes(res);
@ -605,44 +527,8 @@ export default {
this.isEditMode = false; this.isEditMode = false;
}, },
highlighterYAML(code) { highlighter(code) {
if (!languages.yaml_with_symbols) { return highlight(code, languages.yaml);
languages.yaml_with_symbols = languages.insertBefore("yaml", "punctuation", {
"symbol": prismjsSymbolDefinition["symbol"]
});
}
return highlight(code, languages.yaml_with_symbols);
},
highlighterENV(code) {
if (!languages.docker_env) {
languages.docker_env = {
"comment": {
pattern: /(^#| #).*$/m,
greedy: true
},
"keyword": {
pattern: /^[^ :=]*(?=[:=])/m,
greedy: true
},
"value": {
pattern: /(?<=[:=]).*?((?= #)|$)/m,
greedy: true,
inside: {
"string": [
{
pattern: /^ *'.*?(?<!\\)'/m,
},
{
pattern: /^ *".*?(?<!\\)"|^.*$/m,
inside: prismjsSymbolDefinition
},
],
},
},
};
}
return highlight(code, languages.docker_env);
}, },
yamlCodeChange() { yamlCodeChange() {

View File

@ -1,10 +1,7 @@
{ {
"name": "dockge", "name": "dockge",
"version": "1.3.1", "version": "1.1.1",
"type": "module", "type": "module",
"engines": {
"node": ">= 18.0.0 && <= 18.17.1"
},
"scripts": { "scripts": {
"fmt": "eslint \"**/*.{ts,vue}\" --fix", "fmt": "eslint \"**/*.{ts,vue}\" --fix",
"lint": "eslint \"**/*.{ts,vue}\"", "lint": "eslint \"**/*.{ts,vue}\"",
@ -17,11 +14,9 @@
"build:docker-base": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:base -f ./docker/Base.Dockerfile . --push", "build:docker-base": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:base -f ./docker/Base.Dockerfile . --push",
"build:docker": "node ./extra/env2arg.js docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:latest -t louislam/dockge:1 -t louislam/dockge:$VERSION --target release -f ./docker/Dockerfile . --push", "build:docker": "node ./extra/env2arg.js docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:latest -t louislam/dockge:1 -t louislam/dockge:$VERSION --target release -f ./docker/Dockerfile . --push",
"build:docker-nightly": "pnpm run build:frontend && docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:nightly --target nightly -f ./docker/Dockerfile . --push", "build:docker-nightly": "pnpm run build:frontend && docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:nightly --target nightly -f ./docker/Dockerfile . --push",
"build:healthcheck": "docker buildx build -f docker/BuildHealthCheck.Dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:build-healthcheck . --push",
"start-docker": "docker run --rm -p 5001:5001 --name dockge louislam/dockge:latest", "start-docker": "docker run --rm -p 5001:5001 --name dockge louislam/dockge:latest",
"mark-as-nightly": "tsx ./extra/mark-as-nightly.ts", "mark-as-nightly": "tsx ./extra/mark-as-nightly.ts",
"reformat-changelog": "tsx ./extra/reformat-changelog.ts", "reformat-changelog": "tsx ./extra/reformat-changelog.ts"
"reset-password": "tsx ./extra/reset-password.ts"
}, },
"dependencies": { "dependencies": {
"@homebridge/node-pty-prebuilt-multiarch": "~0.11.11", "@homebridge/node-pty-prebuilt-multiarch": "~0.11.11",
@ -33,7 +28,6 @@
"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",
@ -42,7 +36,6 @@
"knex": "~2.5.1", "knex": "~2.5.1",
"limiter-es6-compat": "~2.1.2", "limiter-es6-compat": "~2.1.2",
"mysql2": "~3.6.3", "mysql2": "~3.6.3",
"promisify-child-process": "~4.1.2",
"redbean-node": "~0.3.3", "redbean-node": "~0.3.3",
"socket.io": "~4.7.2", "socket.io": "~4.7.2",
"socket.io-client": "~4.7.2", "socket.io-client": "~4.7.2",

16
pnpm-lock.yaml generated
View File

@ -32,9 +32,6 @@ 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
@ -59,9 +56,6 @@ dependencies:
mysql2: mysql2:
specifier: ~3.6.3 specifier: ~3.6.3
version: 3.6.3 version: 3.6.3
promisify-child-process:
specifier: ~4.1.2
version: 4.1.2
redbean-node: redbean-node:
specifier: ~0.3.3 specifier: ~0.3.3
version: 0.3.3(mysql2@3.6.3) version: 0.3.3(mysql2@3.6.3)
@ -2157,11 +2151,6 @@ 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
@ -3898,11 +3887,6 @@ packages:
dev: false dev: false
optional: true optional: true
/promisify-child-process@4.1.2:
resolution: {integrity: sha512-APnkIgmaHNJpkAn7k+CrJSi9WMuff5ctYFbD0CO2XIPkM8yO7d/ShouU2clywbpHV/DUsyc4bpJCsNgddNtx4g==}
engines: {node: '>=8'}
dev: false
/proxy-addr@2.0.7: /proxy-addr@2.0.7:
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
engines: {node: '>= 0.10'} engines: {node: '>= 0.10'}