mirror of
https://github.com/louislam/dockge.git
synced 2025-08-13 06:37:50 +02:00
Compare commits
150 Commits
multiple
...
fix-consol
Author | SHA1 | Date | |
---|---|---|---|
8902db0ae4 | |||
cdbf7f1843 | |||
c62b91682e | |||
970a826d5a | |||
86c7dfdb5b | |||
6c357fb603 | |||
90255f05cb | |||
c40c463788 | |||
9fedd8790d | |||
65c719d95d | |||
fb349e06b1 | |||
322f4ccee8 | |||
38d424d8bc | |||
4438adc04a | |||
053ea3643c | |||
b7b1435d62 | |||
9830bc345a | |||
9d8fbf1af2 | |||
fb366cbf24 | |||
fd3e4910e2 | |||
6c0d8da11e | |||
a2f96913c2 | |||
40fc0ebb06 | |||
be562ce66e | |||
3fff0dbd51 | |||
71e773ae9f | |||
74e9efd471 | |||
d451e06e84 | |||
a65a9f5549 | |||
9b73e44cd9 | |||
81818a19d1 | |||
1372bd2ce1 | |||
01906205f0 | |||
28337c5430 | |||
5baf48db63 | |||
b2c8fdab75 | |||
e12525fa42 | |||
3e3f67c6b7 | |||
020faa49d2 | |||
df95d7ce9d | |||
7a2524c542 | |||
6ceaa70cdd | |||
caea8996da | |||
39e3d5a07c | |||
723afb5bc2 | |||
3b3b3a7940 | |||
f9309a0650 | |||
54c2be7abe | |||
48db1c73a8 | |||
88f696d9b1 | |||
f80cfca64b | |||
1ddd70791a | |||
5f01347d2f | |||
04c9a8669d | |||
91b7c18c52 | |||
9cef4ad9ee | |||
e7dd099f17 | |||
d27fd2919b | |||
e2f5796470 | |||
88f26f53c5 | |||
ccd9d96227 | |||
a8dcfe4ccd | |||
941ec0056a | |||
1bb6f2532c | |||
6fb24adc66 | |||
c4fe952121 | |||
59bfe79c40 | |||
9e89f49e38 | |||
19beb02b1e | |||
9dd68372c2 | |||
109222f024 | |||
5ad42a6dab | |||
74c8baef93 | |||
c7ea2f9ee9 | |||
4a9173f5dc | |||
3d641090c0 | |||
32527100a0 | |||
30c69583a7 | |||
69cbe16745 | |||
f5df9a777c | |||
c33a469972 | |||
f667467091 | |||
2ff27b4073 | |||
8ad6702932 | |||
6a7d7b5e43 | |||
66747b7a73 | |||
051cc11eaa | |||
2e22f95720 | |||
4d10dc75a7 | |||
af2d40eeac | |||
5420c73960 | |||
2e6e2bda38 | |||
36c3f01d00 | |||
7e05f51676 | |||
f9baa9180f | |||
3204020749 | |||
6d8487c879 | |||
cb72629596 | |||
658c2828e2 | |||
22b9f04426 | |||
0992408fa0 | |||
a24b2199fa | |||
b7b1708696 | |||
17566fcd95 | |||
ec6bdea711 | |||
f8ad8c45fd | |||
c239f40acc | |||
8efa58e0d0 | |||
dbbefa6c09 | |||
a253f8ab25 | |||
701b0158b1 | |||
c94eb4805d | |||
4670121dfa | |||
9d5d062420 | |||
21f7a677a3 | |||
25026b1ed5 | |||
afe433dbfa | |||
480c498974 | |||
8fe75feb69 | |||
17046b500b | |||
75ff8e1d5c | |||
660da44938 | |||
193f975c4c | |||
7f1b03edab | |||
900ab8978f | |||
11e71f373d | |||
8bd432a4b6 | |||
7795bcab03 | |||
3ad7302e03 | |||
3140947174 | |||
2c7b938f69 | |||
d3a595b02f | |||
4b93794cda | |||
8b8a9d0f1f | |||
d4546e1a85 | |||
b8cff4cc51 | |||
cc02eee50c | |||
5578f28456 | |||
11f9302e62 | |||
012f7f1116 | |||
ea2b02587b | |||
91ba7761f9 | |||
91b3165ea8 | |||
f621d9c4c3 | |||
3e486008bb | |||
4049ae22f0 | |||
df58de180e | |||
07e259db5c | |||
1b84531b31 | |||
de2de0573b |
37
.github/workflows/ci.yml
vendored
37
.github/workflows/ci.yml
vendored
@ -14,50 +14,31 @@ jobs:
|
|||||||
ci:
|
ci:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest, ARM64]
|
os: [ubuntu-latest, windows-latest, macos-latest, ARM, ARM64]
|
||||||
node: [18.17.1] # Can be changed
|
node: [22] # Can be changed
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Code
|
- name: Checkout Code
|
||||||
|
run: | # Mainly for Windows
|
||||||
|
git config --global core.autocrlf false
|
||||||
|
git config --global core.eol lf
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- run: git config --global core.autocrlf false # Mainly for Windows
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{matrix.node}}
|
node-version: ${{matrix.node}}
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v2
|
|
||||||
name: Install pnpm
|
|
||||||
with:
|
|
||||||
version: 8
|
|
||||||
run_install: false
|
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
|
||||||
name: Setup pnpm cache
|
|
||||||
with:
|
|
||||||
path: ${{ env.STORE_PATH }}
|
|
||||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pnpm-store-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: npm install
|
||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: pnpm run lint
|
run: npm run lint
|
||||||
|
|
||||||
- name: Check Typescript
|
- name: Check Typescript
|
||||||
run: pnpm run check-ts
|
run: npm run check-ts
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm run build:frontend
|
run: npm run build:frontend
|
||||||
# more things can be add later like tests etc..
|
# more things can be add later like tests etc..
|
||||||
|
|
||||||
|
22
.github/workflows/close-incorrect-issue.yml
vendored
22
.github/workflows/close-incorrect-issue.yml
vendored
@ -16,27 +16,5 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v2
|
|
||||||
name: Install pnpm
|
|
||||||
with:
|
|
||||||
version: 8
|
|
||||||
run_install: false
|
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
|
||||||
name: Setup pnpm cache
|
|
||||||
with:
|
|
||||||
path: ${{ env.STORE_PATH }}
|
|
||||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pnpm-store-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install
|
|
||||||
|
|
||||||
- name: Close Incorrect Issue
|
- name: Close Incorrect Issue
|
||||||
run: node extra/close-incorrect-issue.js ${{ secrets.GITHUB_TOKEN }} ${{ github.event.issue.number }} ${{ github.event.issue.user.login }}
|
run: node extra/close-incorrect-issue.js ${{ secrets.GITHUB_TOKEN }} ${{ github.event.issue.number }} ${{ github.event.issue.user.login }}
|
||||||
|
4
.github/workflows/json-yaml-validate.yml
vendored
4
.github/workflows/json-yaml-validate.yml
vendored
@ -17,11 +17,11 @@ jobs:
|
|||||||
json-yaml-validate:
|
json-yaml-validate:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: json-yaml-validate
|
- name: json-yaml-validate
|
||||||
id: json-yaml-validate
|
id: json-yaml-validate
|
||||||
uses: GrantBirki/json-yaml-validate@v1.3.0
|
uses: GrantBirki/json-yaml-validate@v2.6.1
|
||||||
with:
|
with:
|
||||||
comment: "false" # enable comment mode
|
comment: "false" # enable comment mode
|
||||||
exclude_file: ".github/config/exclude.txt" # gitignore style file for exclusions
|
exclude_file: ".github/config/exclude.txt" # gitignore style file for exclusions
|
||||||
|
@ -58,8 +58,7 @@ I personally do not like something that requires so many configurations before y
|
|||||||
|
|
||||||
## Tools
|
## Tools
|
||||||
|
|
||||||
- [`Node.js`](https://nodejs.org/) >= 20
|
- [`Node.js`](https://nodejs.org/) >= 22.14.0
|
||||||
- [`pnpm`](https://pnpm.io/)
|
|
||||||
- [`git`](https://git-scm.com/)
|
- [`git`](https://git-scm.com/)
|
||||||
- IDE that supports [`ESLint`](https://eslint.org/) and EditorConfig (I am using [`IntelliJ IDEA`](https://www.jetbrains.com/idea/))
|
- IDE that supports [`ESLint`](https://eslint.org/) and EditorConfig (I am using [`IntelliJ IDEA`](https://www.jetbrains.com/idea/))
|
||||||
- A SQLite GUI tool (f.ex. [`SQLite Expert Personal`](https://www.sqliteexpert.com/download.html) or [`DBeaver Community`](https://dbeaver.io/download/))
|
- A SQLite GUI tool (f.ex. [`SQLite Expert Personal`](https://www.sqliteexpert.com/download.html) or [`DBeaver Community`](https://dbeaver.io/download/))
|
||||||
@ -67,14 +66,14 @@ I personally do not like something that requires so many configurations before y
|
|||||||
## Install Dependencies for Development
|
## Install Dependencies for Development
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm install
|
npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
## Dev Server
|
## Dev Server
|
||||||
|
|
||||||
```
|
```
|
||||||
pnpm run dev:frontend
|
npm run dev:frontend
|
||||||
pnpm run dev:backend
|
npm run dev:backend
|
||||||
```
|
```
|
||||||
|
|
||||||
## Backend Dev Server
|
## Backend Dev Server
|
||||||
@ -94,7 +93,7 @@ You can use Vue.js devtools Chrome extension for debugging.
|
|||||||
### Build the frontend
|
### Build the frontend
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm run build
|
npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
## Database Migration
|
## Database Migration
|
||||||
@ -117,7 +116,7 @@ Both frontend and backend share the same package.json. However, the frontend dep
|
|||||||
Should only be done by the maintainer.
|
Should only be done by the maintainer.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm update
|
npm update
|
||||||
````
|
````
|
||||||
|
|
||||||
It should update the patch release version only.
|
It should update the patch release version only.
|
||||||
|
23
README.md
23
README.md
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
A fancy, easy-to-use and reactive self-hosted docker compose.yaml stack-oriented manager.
|
A fancy, easy-to-use and reactive self-hosted docker compose.yaml stack-oriented manager.
|
||||||
|
|
||||||
   
|
[](https://github.com/louislam/dockge) [](https://hub.docker.com/r/louislam/dockge/tags) [](https://hub.docker.com/r/louislam/dockge/tags) [](https://github.com/louislam/dockge/commits/master/)
|
||||||
|
|
||||||
<img src="https://github.com/louislam/dockge/assets/1336778/26a583e1-ecb1-4a8d-aedf-76157d714ad7" width="900" alt="" />
|
<img src="https://github.com/louislam/dockge/assets/1336778/26a583e1-ecb1-4a8d-aedf-76157d714ad7" width="900" alt="" />
|
||||||
|
|
||||||
@ -14,20 +14,19 @@ View Video: https://youtu.be/AWAlOQeNpgU?t=48
|
|||||||
|
|
||||||
## ⭐ Features
|
## ⭐ Features
|
||||||
|
|
||||||
- Manage `compose.yaml`
|
- 🧑💼 Manage your `compose.yaml` files
|
||||||
- Create/Edit/Start/Stop/Restart/Delete
|
- Create/Edit/Start/Stop/Restart/Delete
|
||||||
- Update Docker Images
|
- Update Docker Images
|
||||||
- Interactive Editor for `compose.yaml`
|
- ⌨️ Interactive Editor for `compose.yaml`
|
||||||
- Interactive Web Terminal
|
- 🦦 Interactive Web Terminal
|
||||||
- Reactive
|
- 🕷️ (1.4.0 🆕) Multiple agents support - You can manage multiple stacks from different Docker hosts in one single interface
|
||||||
- Everything is just responsive. Progress (Pull/Up/Down) and terminal output are in real-time
|
- 🏪 Convert `docker run ...` commands into `compose.yaml`
|
||||||
- Easy-to-use & fancy UI
|
- 📙 File based structure - Dockge won't kidnap your compose files, they are stored on your drive as usual. You can interact with them using normal `docker compose` commands
|
||||||
- If you love Uptime Kuma's UI/UX, you will love this one too
|
|
||||||
- Convert `docker run ...` commands into `compose.yaml`
|
|
||||||
- File based structure
|
|
||||||
- Dockge won't kidnap your compose files, they are stored on your drive as usual. You can interact with them using normal `docker compose` commands
|
|
||||||
<img src="https://github.com/louislam/dockge/assets/1336778/cc071864-592e-4909-b73a-343a57494002" width=300 />
|
|
||||||
|
|
||||||
|
<img src="https://github.com/louislam/dockge/assets/1336778/cc071864-592e-4909-b73a-343a57494002" width=300 />
|
||||||
|
|
||||||
|
- 🚄 Reactive - Everything is just responsive. Progress (Pull/Up/Down) and terminal output are in real-time
|
||||||
|
- 🐣 Easy-to-use & fancy UI - If you love Uptime Kuma's UI/UX, you will love this one too
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
293
backend/agent-manager.ts
Normal file
293
backend/agent-manager.ts
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
import { DockgeSocket } from "./util-server";
|
||||||
|
import { io, Socket as SocketClient } from "socket.io-client";
|
||||||
|
import { log } from "./log";
|
||||||
|
import { Agent } from "./models/agent";
|
||||||
|
import { isDev, LooseObject, sleep } from "../common/util-common";
|
||||||
|
import semver from "semver";
|
||||||
|
import { R } from "redbean-node";
|
||||||
|
import dayjs, { Dayjs } from "dayjs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dockge Instance Manager
|
||||||
|
* One AgentManager per Socket connection
|
||||||
|
*/
|
||||||
|
export class AgentManager {
|
||||||
|
|
||||||
|
protected socket : DockgeSocket;
|
||||||
|
protected agentSocketList : Record<string, SocketClient> = {};
|
||||||
|
protected agentLoggedInList : Record<string, boolean> = {};
|
||||||
|
protected _firstConnectTime : Dayjs = dayjs();
|
||||||
|
|
||||||
|
constructor(socket: DockgeSocket) {
|
||||||
|
this.socket = socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
get firstConnectTime() : Dayjs {
|
||||||
|
return this._firstConnectTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
test(url : string, username : string, password : string) : Promise<void> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let obj = new URL(url);
|
||||||
|
let endpoint = obj.host;
|
||||||
|
|
||||||
|
if (!endpoint) {
|
||||||
|
reject(new Error("Invalid Dockge URL"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.agentSocketList[endpoint]) {
|
||||||
|
reject(new Error("The Dockge URL already exists"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let client = io(url, {
|
||||||
|
reconnection: false,
|
||||||
|
extraHeaders: {
|
||||||
|
endpoint,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("connect", () => {
|
||||||
|
client.emit("login", {
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
}, (res : LooseObject) => {
|
||||||
|
if (res.ok) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject(new Error(res.msg));
|
||||||
|
}
|
||||||
|
client.disconnect();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("connect_error", (err) => {
|
||||||
|
if (err.message === "xhr poll error") {
|
||||||
|
reject(new Error("Unable to connect to the Dockge instance"));
|
||||||
|
} else {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
client.disconnect();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @param username
|
||||||
|
* @param password
|
||||||
|
*/
|
||||||
|
async add(url : string, username : string, password : string) : Promise<Agent> {
|
||||||
|
let bean = R.dispense("agent") as Agent;
|
||||||
|
bean.url = url;
|
||||||
|
bean.username = username;
|
||||||
|
bean.password = password;
|
||||||
|
await R.store(bean);
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
*/
|
||||||
|
async remove(url : string) {
|
||||||
|
let bean = await R.findOne("agent", " url = ? ", [
|
||||||
|
url,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (bean) {
|
||||||
|
await R.trash(bean);
|
||||||
|
let endpoint = bean.endpoint;
|
||||||
|
this.disconnect(endpoint);
|
||||||
|
this.sendAgentList();
|
||||||
|
delete this.agentSocketList[endpoint];
|
||||||
|
} else {
|
||||||
|
throw new Error("Agent not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(url : string, username : string, password : string) {
|
||||||
|
let obj = new URL(url);
|
||||||
|
let endpoint = obj.host;
|
||||||
|
|
||||||
|
this.socket.emit("agentStatus", {
|
||||||
|
endpoint: endpoint,
|
||||||
|
status: "connecting",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!endpoint) {
|
||||||
|
log.error("agent-manager", "Invalid endpoint: " + endpoint + " URL: " + url);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.agentSocketList[endpoint]) {
|
||||||
|
log.debug("agent-manager", "Already connected to the socket server: " + endpoint);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("agent-manager", "Connecting to the socket server: " + endpoint);
|
||||||
|
let client = io(url, {
|
||||||
|
extraHeaders: {
|
||||||
|
endpoint,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("connect", () => {
|
||||||
|
log.info("agent-manager", "Connected to the socket server: " + endpoint);
|
||||||
|
|
||||||
|
client.emit("login", {
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
}, (res : LooseObject) => {
|
||||||
|
if (res.ok) {
|
||||||
|
log.info("agent-manager", "Logged in to the socket server: " + endpoint);
|
||||||
|
this.agentLoggedInList[endpoint] = true;
|
||||||
|
this.socket.emit("agentStatus", {
|
||||||
|
endpoint: endpoint,
|
||||||
|
status: "online",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
log.error("agent-manager", "Failed to login to the socket server: " + endpoint);
|
||||||
|
this.agentLoggedInList[endpoint] = false;
|
||||||
|
this.socket.emit("agentStatus", {
|
||||||
|
endpoint: endpoint,
|
||||||
|
status: "offline",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("connect_error", (err) => {
|
||||||
|
log.error("agent-manager", "Error from the socket server: " + endpoint);
|
||||||
|
this.socket.emit("agentStatus", {
|
||||||
|
endpoint: endpoint,
|
||||||
|
status: "offline",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("disconnect", () => {
|
||||||
|
log.info("agent-manager", "Disconnected from the socket server: " + endpoint);
|
||||||
|
this.socket.emit("agentStatus", {
|
||||||
|
endpoint: endpoint,
|
||||||
|
status: "offline",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("agent", (...args : unknown[]) => {
|
||||||
|
this.socket.emit("agent", ...args);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("info", (res) => {
|
||||||
|
log.debug("agent-manager", res);
|
||||||
|
|
||||||
|
// Disconnect if the version is lower than 1.4.0
|
||||||
|
if (!isDev && semver.satisfies(res.version, "< 1.4.0")) {
|
||||||
|
this.socket.emit("agentStatus", {
|
||||||
|
endpoint: endpoint,
|
||||||
|
status: "offline",
|
||||||
|
msg: `${endpoint}: Unsupported version: ` + res.version,
|
||||||
|
});
|
||||||
|
client.disconnect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.agentSocketList[endpoint] = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect(endpoint : string) {
|
||||||
|
let client = this.agentSocketList[endpoint];
|
||||||
|
client?.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
async connectAll() {
|
||||||
|
this._firstConnectTime = dayjs();
|
||||||
|
|
||||||
|
if (this.socket.endpoint) {
|
||||||
|
log.info("agent-manager", "This connection is connected as an agent, skip connectAll()");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let list : Record<string, Agent> = await Agent.getAgentList();
|
||||||
|
|
||||||
|
if (Object.keys(list).length !== 0) {
|
||||||
|
log.info("agent-manager", "Connecting to all instance socket server(s)...");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let endpoint in list) {
|
||||||
|
let agent = list[endpoint];
|
||||||
|
this.connect(agent.url, agent.username, agent.password);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectAll() {
|
||||||
|
for (let endpoint in this.agentSocketList) {
|
||||||
|
this.disconnect(endpoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async emitToEndpoint(endpoint: string, eventName: string, ...args : unknown[]) {
|
||||||
|
log.debug("agent-manager", "Emitting event to endpoint: " + endpoint);
|
||||||
|
let client = this.agentSocketList[endpoint];
|
||||||
|
|
||||||
|
if (!client) {
|
||||||
|
log.error("agent-manager", "Socket client not found for endpoint: " + endpoint);
|
||||||
|
throw new Error("Socket client not found for endpoint: " + endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!client.connected || !this.agentLoggedInList[endpoint]) {
|
||||||
|
// Maybe the request is too quick, the socket is not connected yet, check firstConnectTime
|
||||||
|
// If it is within 10 seconds, we should apply retry logic here
|
||||||
|
let diff = dayjs().diff(this.firstConnectTime, "second");
|
||||||
|
log.debug("agent-manager", endpoint + ": diff: " + diff);
|
||||||
|
let ok = false;
|
||||||
|
while (diff < 10) {
|
||||||
|
if (client.connected && this.agentLoggedInList[endpoint]) {
|
||||||
|
log.debug("agent-manager", `${endpoint}: Connected & Logged in`);
|
||||||
|
ok = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
log.debug("agent-manager", endpoint + ": not ready yet, retrying in 1 second...");
|
||||||
|
await sleep(1000);
|
||||||
|
diff = dayjs().diff(this.firstConnectTime, "second");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
log.error("agent-manager", `${endpoint}: Socket client not connected`);
|
||||||
|
throw new Error("Socket client not connected for endpoint: " + endpoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client.emit("agent", endpoint, eventName, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
emitToAllEndpoints(eventName: string, ...args : unknown[]) {
|
||||||
|
log.debug("agent-manager", "Emitting event to all endpoints");
|
||||||
|
for (let endpoint in this.agentSocketList) {
|
||||||
|
this.emitToEndpoint(endpoint, eventName, ...args).catch((e) => {
|
||||||
|
log.warn("agent-manager", e.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendAgentList() {
|
||||||
|
let list = await Agent.getAgentList();
|
||||||
|
let result : Record<string, LooseObject> = {};
|
||||||
|
|
||||||
|
// Myself
|
||||||
|
result[""] = {
|
||||||
|
url: "",
|
||||||
|
username: "",
|
||||||
|
endpoint: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let endpoint in list) {
|
||||||
|
let agent = list[endpoint];
|
||||||
|
result[endpoint] = agent.toJSON();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.socket.emit("agentList", {
|
||||||
|
ok: true,
|
||||||
|
agentList: result,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
7
backend/agent-socket-handler.ts
Normal file
7
backend/agent-socket-handler.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { DockgeServer } from "./dockge-server";
|
||||||
|
import { AgentSocket } from "../common/agent-socket";
|
||||||
|
import { DockgeSocket } from "./util-server";
|
||||||
|
|
||||||
|
export abstract class AgentSocketHandler {
|
||||||
|
abstract create(socket : DockgeSocket, server : DockgeServer, agentSocket : AgentSocket): void;
|
||||||
|
}
|
@ -1,45 +1,46 @@
|
|||||||
import { SocketHandler } from "../socket-handler.js";
|
import { AgentSocketHandler } from "../agent-socket-handler";
|
||||||
import { DockgeServer } from "../dockge-server";
|
import { DockgeServer } from "../dockge-server";
|
||||||
import { callbackError, checkLogin, DockgeSocket, ValidationError } from "../util-server";
|
import { callbackError, callbackResult, checkLogin, DockgeSocket, ValidationError } from "../util-server";
|
||||||
import { Stack } from "../stack";
|
import { Stack } from "../stack";
|
||||||
|
import { AgentSocket } from "../../common/agent-socket";
|
||||||
|
|
||||||
// @ts-ignore
|
export class DockerSocketHandler extends AgentSocketHandler {
|
||||||
import composerize from "composerize";
|
create(socket : DockgeSocket, server : DockgeServer, agentSocket : AgentSocket) {
|
||||||
|
// Do not call super.create()
|
||||||
|
|
||||||
export class DockerSocketHandler extends SocketHandler {
|
agentSocket.on("deployStack", async (name : unknown, composeYAML : unknown, composeENV : unknown, isAdd : unknown, callback) => {
|
||||||
create(socket : DockgeSocket, server : DockgeServer) {
|
|
||||||
|
|
||||||
socket.on("deployStack", async (name : unknown, composeYAML : unknown, composeENV : unknown, isAdd : unknown, callback) => {
|
|
||||||
try {
|
try {
|
||||||
checkLogin(socket);
|
checkLogin(socket);
|
||||||
const stack = await this.saveStack(socket, server, name, composeYAML, composeENV, isAdd);
|
const stack = await this.saveStack(server, name, composeYAML, composeENV, isAdd);
|
||||||
await stack.deploy(socket);
|
await stack.deploy(socket);
|
||||||
server.sendStackList();
|
server.sendStackList();
|
||||||
callback({
|
callbackResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
msg: "Deployed",
|
msg: "Deployed",
|
||||||
});
|
msgi18n: true,
|
||||||
|
}, callback);
|
||||||
stack.joinCombinedTerminal(socket);
|
stack.joinCombinedTerminal(socket);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
callbackError(e, callback);
|
callbackError(e, callback);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("saveStack", async (name : unknown, composeYAML : unknown, composeENV : unknown, isAdd : unknown, callback) => {
|
agentSocket.on("saveStack", async (name : unknown, composeYAML : unknown, composeENV : unknown, isAdd : unknown, callback) => {
|
||||||
try {
|
try {
|
||||||
checkLogin(socket);
|
checkLogin(socket);
|
||||||
this.saveStack(socket, server, name, composeYAML, composeENV, isAdd);
|
await this.saveStack(server, name, composeYAML, composeENV, isAdd);
|
||||||
callback({
|
callbackResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
"msg": "Saved"
|
msg: "Saved",
|
||||||
});
|
msgi18n: true,
|
||||||
|
}, callback);
|
||||||
server.sendStackList();
|
server.sendStackList();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
callbackError(e, callback);
|
callbackError(e, callback);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("deleteStack", async (name : unknown, callback) => {
|
agentSocket.on("deleteStack", async (name : unknown, callback) => {
|
||||||
try {
|
try {
|
||||||
checkLogin(socket);
|
checkLogin(socket);
|
||||||
if (typeof(name) !== "string") {
|
if (typeof(name) !== "string") {
|
||||||
@ -55,17 +56,18 @@ export class DockerSocketHandler extends SocketHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
server.sendStackList();
|
server.sendStackList();
|
||||||
callback({
|
callbackResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
msg: "Deleted"
|
msg: "Deleted",
|
||||||
});
|
msgi18n: true,
|
||||||
|
}, callback);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
callbackError(e, callback);
|
callbackError(e, callback);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("getStack", async (stackName : unknown, callback) => {
|
agentSocket.on("getStack", async (stackName : unknown, callback) => {
|
||||||
try {
|
try {
|
||||||
checkLogin(socket);
|
checkLogin(socket);
|
||||||
|
|
||||||
@ -79,31 +81,32 @@ export class DockerSocketHandler extends SocketHandler {
|
|||||||
stack.joinCombinedTerminal(socket);
|
stack.joinCombinedTerminal(socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
callback({
|
callbackResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
stack: stack.toJSON(),
|
stack: await stack.toJSON(socket.endpoint),
|
||||||
});
|
}, callback);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
callbackError(e, callback);
|
callbackError(e, callback);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// requestStackList
|
// requestStackList
|
||||||
socket.on("requestStackList", async (callback) => {
|
agentSocket.on("requestStackList", async (callback) => {
|
||||||
try {
|
try {
|
||||||
checkLogin(socket);
|
checkLogin(socket);
|
||||||
server.sendStackList();
|
server.sendStackList();
|
||||||
callback({
|
callbackResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
msg: "Updated"
|
msg: "Updated",
|
||||||
});
|
msgi18n: true,
|
||||||
|
}, callback);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
callbackError(e, callback);
|
callbackError(e, callback);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// startStack
|
// startStack
|
||||||
socket.on("startStack", async (stackName : unknown, callback) => {
|
agentSocket.on("startStack", async (stackName : unknown, callback) => {
|
||||||
try {
|
try {
|
||||||
checkLogin(socket);
|
checkLogin(socket);
|
||||||
|
|
||||||
@ -113,10 +116,11 @@ export class DockerSocketHandler extends SocketHandler {
|
|||||||
|
|
||||||
const stack = await Stack.getStack(server, stackName);
|
const stack = await Stack.getStack(server, stackName);
|
||||||
await stack.start(socket);
|
await stack.start(socket);
|
||||||
callback({
|
callbackResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
msg: "Started"
|
msg: "Started",
|
||||||
});
|
msgi18n: true,
|
||||||
|
}, callback);
|
||||||
server.sendStackList();
|
server.sendStackList();
|
||||||
|
|
||||||
stack.joinCombinedTerminal(socket);
|
stack.joinCombinedTerminal(socket);
|
||||||
@ -127,7 +131,7 @@ export class DockerSocketHandler extends SocketHandler {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// stopStack
|
// stopStack
|
||||||
socket.on("stopStack", async (stackName : unknown, callback) => {
|
agentSocket.on("stopStack", async (stackName : unknown, callback) => {
|
||||||
try {
|
try {
|
||||||
checkLogin(socket);
|
checkLogin(socket);
|
||||||
|
|
||||||
@ -137,10 +141,11 @@ export class DockerSocketHandler extends SocketHandler {
|
|||||||
|
|
||||||
const stack = await Stack.getStack(server, stackName);
|
const stack = await Stack.getStack(server, stackName);
|
||||||
await stack.stop(socket);
|
await stack.stop(socket);
|
||||||
callback({
|
callbackResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
msg: "Stopped"
|
msg: "Stopped",
|
||||||
});
|
msgi18n: true,
|
||||||
|
}, callback);
|
||||||
server.sendStackList();
|
server.sendStackList();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
callbackError(e, callback);
|
callbackError(e, callback);
|
||||||
@ -148,7 +153,7 @@ export class DockerSocketHandler extends SocketHandler {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// restartStack
|
// restartStack
|
||||||
socket.on("restartStack", async (stackName : unknown, callback) => {
|
agentSocket.on("restartStack", async (stackName : unknown, callback) => {
|
||||||
try {
|
try {
|
||||||
checkLogin(socket);
|
checkLogin(socket);
|
||||||
|
|
||||||
@ -158,10 +163,11 @@ export class DockerSocketHandler extends SocketHandler {
|
|||||||
|
|
||||||
const stack = await Stack.getStack(server, stackName);
|
const stack = await Stack.getStack(server, stackName);
|
||||||
await stack.restart(socket);
|
await stack.restart(socket);
|
||||||
callback({
|
callbackResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
msg: "Restarted"
|
msg: "Restarted",
|
||||||
});
|
msgi18n: true,
|
||||||
|
}, callback);
|
||||||
server.sendStackList();
|
server.sendStackList();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
callbackError(e, callback);
|
callbackError(e, callback);
|
||||||
@ -169,7 +175,7 @@ export class DockerSocketHandler extends SocketHandler {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// updateStack
|
// updateStack
|
||||||
socket.on("updateStack", async (stackName : unknown, callback) => {
|
agentSocket.on("updateStack", async (stackName : unknown, callback) => {
|
||||||
try {
|
try {
|
||||||
checkLogin(socket);
|
checkLogin(socket);
|
||||||
|
|
||||||
@ -179,10 +185,11 @@ export class DockerSocketHandler extends SocketHandler {
|
|||||||
|
|
||||||
const stack = await Stack.getStack(server, stackName);
|
const stack = await Stack.getStack(server, stackName);
|
||||||
await stack.update(socket);
|
await stack.update(socket);
|
||||||
callback({
|
callbackResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
msg: "Updated"
|
msg: "Updated",
|
||||||
});
|
msgi18n: true,
|
||||||
|
}, callback);
|
||||||
server.sendStackList();
|
server.sendStackList();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
callbackError(e, callback);
|
callbackError(e, callback);
|
||||||
@ -190,7 +197,7 @@ export class DockerSocketHandler extends SocketHandler {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// down stack
|
// down stack
|
||||||
socket.on("downStack", async (stackName : unknown, callback) => {
|
agentSocket.on("downStack", async (stackName : unknown, callback) => {
|
||||||
try {
|
try {
|
||||||
checkLogin(socket);
|
checkLogin(socket);
|
||||||
|
|
||||||
@ -200,10 +207,11 @@ export class DockerSocketHandler extends SocketHandler {
|
|||||||
|
|
||||||
const stack = await Stack.getStack(server, stackName);
|
const stack = await Stack.getStack(server, stackName);
|
||||||
await stack.down(socket);
|
await stack.down(socket);
|
||||||
callback({
|
callbackResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
msg: "Downed"
|
msg: "Downed",
|
||||||
});
|
msgi18n: true,
|
||||||
|
}, callback);
|
||||||
server.sendStackList();
|
server.sendStackList();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
callbackError(e, callback);
|
callbackError(e, callback);
|
||||||
@ -211,7 +219,7 @@ export class DockerSocketHandler extends SocketHandler {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Services status
|
// Services status
|
||||||
socket.on("serviceStatusList", async (stackName : unknown, callback) => {
|
agentSocket.on("serviceStatusList", async (stackName : unknown, callback) => {
|
||||||
try {
|
try {
|
||||||
checkLogin(socket);
|
checkLogin(socket);
|
||||||
|
|
||||||
@ -221,50 +229,31 @@ export class DockerSocketHandler extends SocketHandler {
|
|||||||
|
|
||||||
const stack = await Stack.getStack(server, stackName, true);
|
const stack = await Stack.getStack(server, stackName, true);
|
||||||
const serviceStatusList = Object.fromEntries(await stack.getServiceStatusList());
|
const serviceStatusList = Object.fromEntries(await stack.getServiceStatusList());
|
||||||
callback({
|
callbackResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
serviceStatusList,
|
serviceStatusList,
|
||||||
});
|
}, callback);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
callbackError(e, callback);
|
callbackError(e, callback);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// getExternalNetworkList
|
// getExternalNetworkList
|
||||||
socket.on("getDockerNetworkList", async (callback) => {
|
agentSocket.on("getDockerNetworkList", async (callback) => {
|
||||||
try {
|
try {
|
||||||
checkLogin(socket);
|
checkLogin(socket);
|
||||||
const dockerNetworkList = await server.getDockerNetworkList();
|
const dockerNetworkList = await server.getDockerNetworkList();
|
||||||
callback({
|
callbackResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
dockerNetworkList,
|
dockerNetworkList,
|
||||||
});
|
}, callback);
|
||||||
} catch (e) {
|
|
||||||
callbackError(e, callback);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// composerize
|
|
||||||
socket.on("composerize", async (dockerRunCommand : unknown, callback) => {
|
|
||||||
try {
|
|
||||||
checkLogin(socket);
|
|
||||||
|
|
||||||
if (typeof(dockerRunCommand) !== "string") {
|
|
||||||
throw new ValidationError("dockerRunCommand must be a string");
|
|
||||||
}
|
|
||||||
|
|
||||||
const composeTemplate = composerize(dockerRunCommand);
|
|
||||||
callback({
|
|
||||||
ok: true,
|
|
||||||
composeTemplate,
|
|
||||||
});
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
callbackError(e, callback);
|
callbackError(e, callback);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveStack(socket : DockgeSocket, server : DockgeServer, name : unknown, composeYAML : unknown, composeENV : unknown, isAdd : unknown) : Promise<Stack> {
|
async saveStack(server : DockgeServer, name : unknown, composeYAML : unknown, composeENV : unknown, isAdd : unknown) : Promise<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");
|
@ -1,24 +1,15 @@
|
|||||||
import { SocketHandler } from "../socket-handler.js";
|
|
||||||
import { DockgeServer } from "../dockge-server";
|
import { DockgeServer } from "../dockge-server";
|
||||||
import { callbackError, checkLogin, DockgeSocket, ValidationError } from "../util-server";
|
import { callbackError, callbackResult, checkLogin, DockgeSocket, ValidationError } from "../util-server";
|
||||||
import { log } from "../log";
|
import { log } from "../log";
|
||||||
import yaml from "yaml";
|
|
||||||
import path from "path";
|
|
||||||
import fs from "fs";
|
|
||||||
import {
|
|
||||||
allowedCommandList,
|
|
||||||
allowedRawKeys,
|
|
||||||
getComposeTerminalName, getContainerExecTerminalName,
|
|
||||||
isDev,
|
|
||||||
PROGRESS_TERMINAL_ROWS
|
|
||||||
} from "../util-common";
|
|
||||||
import { InteractiveTerminal, MainTerminal, Terminal } from "../terminal";
|
import { InteractiveTerminal, MainTerminal, Terminal } from "../terminal";
|
||||||
import { Stack } from "../stack";
|
import { Stack } from "../stack";
|
||||||
|
import { AgentSocketHandler } from "../agent-socket-handler";
|
||||||
|
import { AgentSocket } from "../../common/agent-socket";
|
||||||
|
|
||||||
export class TerminalSocketHandler extends SocketHandler {
|
export class TerminalSocketHandler extends AgentSocketHandler {
|
||||||
create(socket : DockgeSocket, server : DockgeServer) {
|
create(socket : DockgeSocket, server : DockgeServer, agentSocket : AgentSocket) {
|
||||||
|
|
||||||
socket.on("terminalInput", async (terminalName : unknown, cmd : unknown, errorCallback) => {
|
agentSocket.on("terminalInput", async (terminalName : unknown, cmd : unknown, callback) => {
|
||||||
try {
|
try {
|
||||||
checkLogin(socket);
|
checkLogin(socket);
|
||||||
|
|
||||||
@ -38,20 +29,20 @@ export class TerminalSocketHandler extends SocketHandler {
|
|||||||
throw new Error("Terminal not found or it is not a Interactive Terminal.");
|
throw new Error("Terminal not found or it is not a Interactive Terminal.");
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
callbackError(e, callback);
|
||||||
errorCallback({
|
|
||||||
ok: false,
|
|
||||||
msg: e.message,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Main Terminal
|
// Main Terminal
|
||||||
socket.on("mainTerminal", async (terminalName : unknown, callback) => {
|
agentSocket.on("mainTerminal", async (terminalName : unknown, callback) => {
|
||||||
try {
|
try {
|
||||||
checkLogin(socket);
|
checkLogin(socket);
|
||||||
|
|
||||||
|
// Throw an error if console is not enabled
|
||||||
|
if (!server.config.enableConsole) {
|
||||||
|
throw new ValidationError("Console is not enabled.");
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Reset the name here, force one main terminal for now
|
// TODO: Reset the name here, force one main terminal for now
|
||||||
terminalName = "console";
|
terminalName = "console";
|
||||||
|
|
||||||
@ -59,29 +50,41 @@ export class TerminalSocketHandler extends SocketHandler {
|
|||||||
throw new ValidationError("Terminal name must be a string.");
|
throw new ValidationError("Terminal name must be a string.");
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug("deployStack", "Terminal name: " + terminalName);
|
log.debug("mainTerminal", "Terminal name: " + terminalName);
|
||||||
|
|
||||||
let terminal = Terminal.getTerminal(terminalName);
|
let terminal = Terminal.getTerminal(terminalName);
|
||||||
|
|
||||||
if (!terminal) {
|
if (!terminal) {
|
||||||
terminal = new MainTerminal(server, terminalName);
|
terminal = new MainTerminal(server, terminalName);
|
||||||
terminal.rows = 50;
|
terminal.rows = 50;
|
||||||
log.debug("deployStack", "Terminal created");
|
log.debug("mainTerminal", "Terminal created");
|
||||||
}
|
}
|
||||||
|
|
||||||
terminal.join(socket);
|
terminal.join(socket);
|
||||||
terminal.start();
|
terminal.start();
|
||||||
|
|
||||||
callback({
|
callbackResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
});
|
}, callback);
|
||||||
|
} catch (e) {
|
||||||
|
callbackError(e, callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if MainTerminal is enabled
|
||||||
|
agentSocket.on("checkMainTerminal", async (callback) => {
|
||||||
|
try {
|
||||||
|
checkLogin(socket);
|
||||||
|
callbackResult({
|
||||||
|
ok: server.config.enableConsole,
|
||||||
|
}, callback);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
callbackError(e, callback);
|
callbackError(e, callback);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Interactive Terminal for containers
|
// Interactive Terminal for containers
|
||||||
socket.on("interactiveTerminal", async (stackName : unknown, serviceName : unknown, shell : unknown, callback) => {
|
agentSocket.on("interactiveTerminal", async (stackName : unknown, serviceName : unknown, shell : unknown, callback) => {
|
||||||
try {
|
try {
|
||||||
checkLogin(socket);
|
checkLogin(socket);
|
||||||
|
|
||||||
@ -104,16 +107,16 @@ export class TerminalSocketHandler extends SocketHandler {
|
|||||||
const stack = await Stack.getStack(server, stackName);
|
const stack = await Stack.getStack(server, stackName);
|
||||||
stack.joinContainerTerminal(socket, serviceName, shell);
|
stack.joinContainerTerminal(socket, serviceName, shell);
|
||||||
|
|
||||||
callback({
|
callbackResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
});
|
}, callback);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
callbackError(e, callback);
|
callbackError(e, callback);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Join Output Terminal
|
// Join Output Terminal
|
||||||
socket.on("terminalJoin", async (terminalName : unknown, callback) => {
|
agentSocket.on("terminalJoin", async (terminalName : unknown, callback) => {
|
||||||
if (typeof(callback) !== "function") {
|
if (typeof(callback) !== "function") {
|
||||||
log.debug("console", "Callback is not a function.");
|
log.debug("console", "Callback is not a function.");
|
||||||
return;
|
return;
|
||||||
@ -141,7 +144,7 @@ export class TerminalSocketHandler extends SocketHandler {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Leave Combined Terminal
|
// Leave Combined Terminal
|
||||||
socket.on("leaveCombinedTerminal", async (stackName : unknown, callback) => {
|
agentSocket.on("leaveCombinedTerminal", async (stackName : unknown, callback) => {
|
||||||
try {
|
try {
|
||||||
checkLogin(socket);
|
checkLogin(socket);
|
||||||
|
|
||||||
@ -154,52 +157,48 @@ export class TerminalSocketHandler extends SocketHandler {
|
|||||||
const stack = await Stack.getStack(server, stackName);
|
const stack = await Stack.getStack(server, stackName);
|
||||||
await stack.leaveCombinedTerminal(socket);
|
await stack.leaveCombinedTerminal(socket);
|
||||||
|
|
||||||
callback({
|
callbackResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
});
|
}, callback);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
callbackError(e, callback);
|
callbackError(e, callback);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Resize Terminal
|
// Resize Terminal
|
||||||
socket.on(
|
agentSocket.on("terminalResize", async (terminalName: unknown, rows: unknown, cols: unknown) => {
|
||||||
"terminalResize",
|
log.info("terminalResize", `Terminal: ${terminalName}`);
|
||||||
async (terminalName: unknown, rows: unknown, cols: unknown) => {
|
try {
|
||||||
log.info("terminalResize", `Terminal: ${terminalName}`);
|
checkLogin(socket);
|
||||||
try {
|
if (typeof terminalName !== "string") {
|
||||||
checkLogin(socket);
|
throw new Error("Terminal name must be a string.");
|
||||||
if (typeof terminalName !== "string") {
|
}
|
||||||
throw new Error("Terminal name must be a string.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof rows !== "number") {
|
if (typeof rows !== "number") {
|
||||||
throw new Error("Command must be a number.");
|
throw new Error("Command must be a number.");
|
||||||
}
|
}
|
||||||
if (typeof cols !== "number") {
|
if (typeof cols !== "number") {
|
||||||
throw new Error("Command must be a number.");
|
throw new Error("Command must be a number.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let terminal = Terminal.getTerminal(terminalName);
|
let terminal = Terminal.getTerminal(terminalName);
|
||||||
|
|
||||||
// log.info("terminal", terminal);
|
// log.info("terminal", terminal);
|
||||||
if (terminal instanceof Terminal) {
|
if (terminal instanceof Terminal) {
|
||||||
//log.debug("terminalInput", "Terminal found, writing to terminal.");
|
//log.debug("terminalInput", "Terminal found, writing to terminal.");
|
||||||
terminal.rows = rows;
|
terminal.rows = rows;
|
||||||
terminal.cols = cols;
|
terminal.cols = cols;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`${terminalName} Terminal not found.`);
|
throw new Error(`${terminalName} Terminal not found.`);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.debug(
|
log.debug("terminalResize",
|
||||||
"terminalResize",
|
|
||||||
// Added to prevent the lint error when adding the type
|
// Added to prevent the lint error when adding the type
|
||||||
// and ts type checker saying type is unknown.
|
// and ts type checker saying type is unknown.
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
`Error on ${terminalName}: ${e.message}`
|
`Error on ${terminalName}: ${e.message}`
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,7 +9,7 @@ import knex from "knex";
|
|||||||
import Dialect from "knex/lib/dialects/sqlite3/index.js";
|
import Dialect from "knex/lib/dialects/sqlite3/index.js";
|
||||||
|
|
||||||
import sqlite from "@louislam/sqlite3";
|
import sqlite from "@louislam/sqlite3";
|
||||||
import { sleep } from "./util-common";
|
import { sleep } from "../common/util-common";
|
||||||
|
|
||||||
interface DBConfig {
|
interface DBConfig {
|
||||||
type?: "sqlite" | "mysql";
|
type?: "sqlite" | "mysql";
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import "dotenv/config";
|
||||||
import { MainRouter } from "./routers/main-router";
|
import { MainRouter } from "./routers/main-router";
|
||||||
import * as fs from "node:fs";
|
import * as fs from "node:fs";
|
||||||
import { PackageJson } from "type-fest";
|
import { PackageJson } from "type-fest";
|
||||||
@ -17,23 +18,26 @@ import { Settings } from "./settings";
|
|||||||
import checkVersion from "./check-version";
|
import checkVersion from "./check-version";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { R } from "redbean-node";
|
import { R } from "redbean-node";
|
||||||
import { genSecret, isDev } from "./util-common";
|
import { genSecret, isDev, LooseObject } from "../common/util-common";
|
||||||
import { generatePasswordHash } from "./password-hash";
|
import { generatePasswordHash } from "./password-hash";
|
||||||
import { Bean } from "redbean-node/dist/bean";
|
import { Bean } from "redbean-node/dist/bean";
|
||||||
import { Arguments, Config, DockgeSocket } from "./util-server";
|
import { Arguments, Config, DockgeSocket } from "./util-server";
|
||||||
import { DockerSocketHandler } from "./socket-handlers/docker-socket-handler";
|
import { DockerSocketHandler } from "./agent-socket-handlers/docker-socket-handler";
|
||||||
import expressStaticGzip from "express-static-gzip";
|
import expressStaticGzip from "express-static-gzip";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { TerminalSocketHandler } from "./socket-handlers/terminal-socket-handler";
|
import { TerminalSocketHandler } from "./agent-socket-handlers/terminal-socket-handler";
|
||||||
import { Stack } from "./stack";
|
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 childProcessAsync from "promisify-child-process";
|
||||||
|
import { AgentManager } from "./agent-manager";
|
||||||
|
import { AgentProxySocketHandler } from "./socket-handlers/agent-proxy-socket-handler";
|
||||||
|
import { AgentSocketHandler } from "./agent-socket-handler";
|
||||||
|
import { AgentSocket } from "../common/agent-socket";
|
||||||
|
import { ManageAgentSocketHandler } from "./socket-handlers/manage-agent-socket-handler";
|
||||||
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;
|
||||||
@ -50,10 +54,19 @@ export class DockgeServer {
|
|||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of socket handlers
|
* List of socket handlers (no agent support)
|
||||||
*/
|
*/
|
||||||
socketHandlerList : SocketHandler[] = [
|
socketHandlerList : SocketHandler[] = [
|
||||||
new MainSocketHandler(),
|
new MainSocketHandler(),
|
||||||
|
new ManageAgentSocketHandler(),
|
||||||
|
];
|
||||||
|
|
||||||
|
agentProxySocketHandler = new AgentProxySocketHandler();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of socket handlers (support agent)
|
||||||
|
*/
|
||||||
|
agentSocketHandlerList : AgentSocketHandler[] = [
|
||||||
new DockerSocketHandler(),
|
new DockerSocketHandler(),
|
||||||
new TerminalSocketHandler(),
|
new TerminalSocketHandler(),
|
||||||
];
|
];
|
||||||
@ -123,6 +136,11 @@ export class DockgeServer {
|
|||||||
stacksDir: {
|
stacksDir: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
},
|
||||||
|
enableConsole: {
|
||||||
|
type: Boolean,
|
||||||
|
optional: true,
|
||||||
|
defaultValue: false,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -136,6 +154,7 @@ export class DockgeServer {
|
|||||||
this.config.hostname = args.hostname || process.env.DOCKGE_HOSTNAME || undefined;
|
this.config.hostname = args.hostname || process.env.DOCKGE_HOSTNAME || undefined;
|
||||||
this.config.dataDir = args.dataDir || process.env.DOCKGE_DATA_DIR || "./data/";
|
this.config.dataDir = args.dataDir || process.env.DOCKGE_DATA_DIR || "./data/";
|
||||||
this.config.stacksDir = args.stacksDir || process.env.DOCKGE_STACKS_DIR || defaultStacksDir;
|
this.config.stacksDir = args.stacksDir || process.env.DOCKGE_STACKS_DIR || defaultStacksDir;
|
||||||
|
this.config.enableConsole = args.enableConsole || process.env.DOCKGE_ENABLE_CONSOLE === "true" || false;
|
||||||
this.stacksDir = this.config.stacksDir;
|
this.stacksDir = this.config.stacksDir;
|
||||||
|
|
||||||
log.debug("server", this.config);
|
log.debug("server", this.config);
|
||||||
@ -196,7 +215,7 @@ export class DockgeServer {
|
|||||||
cors,
|
cors,
|
||||||
allowRequest: (req, callback) => {
|
allowRequest: (req, callback) => {
|
||||||
let isOriginValid = true;
|
let isOriginValid = true;
|
||||||
const bypass = isDev;
|
const bypass = isDev || process.env.UPTIME_KUMA_WS_ORIGIN_CHECK === "bypass";
|
||||||
|
|
||||||
if (!bypass) {
|
if (!bypass) {
|
||||||
let host = req.headers.host;
|
let host = req.headers.host;
|
||||||
@ -230,20 +249,52 @@ export class DockgeServer {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.io.on("connection", async (socket: Socket) => {
|
this.io.on("connection", async (socket: Socket) => {
|
||||||
log.info("server", "Socket connected!");
|
let dockgeSocket = socket as DockgeSocket;
|
||||||
|
dockgeSocket.instanceManager = new AgentManager(dockgeSocket);
|
||||||
|
dockgeSocket.emitAgent = (event : string, ...args : unknown[]) => {
|
||||||
|
let obj = args[0];
|
||||||
|
if (typeof(obj) === "object") {
|
||||||
|
let obj2 = obj as LooseObject;
|
||||||
|
obj2.endpoint = dockgeSocket.endpoint;
|
||||||
|
}
|
||||||
|
dockgeSocket.emit("agent", event, ...args);
|
||||||
|
};
|
||||||
|
|
||||||
this.sendInfo(socket, true);
|
if (typeof(socket.request.headers.endpoint) === "string") {
|
||||||
|
dockgeSocket.endpoint = socket.request.headers.endpoint;
|
||||||
|
} else {
|
||||||
|
dockgeSocket.endpoint = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dockgeSocket.endpoint) {
|
||||||
|
log.info("server", "Socket connected (agent), as endpoint " + dockgeSocket.endpoint);
|
||||||
|
} else {
|
||||||
|
log.info("server", "Socket connected (direct)");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sendInfo(dockgeSocket, true);
|
||||||
|
|
||||||
if (this.needSetup) {
|
if (this.needSetup) {
|
||||||
log.info("server", "Redirect to setup page");
|
log.info("server", "Redirect to setup page");
|
||||||
socket.emit("setup");
|
dockgeSocket.emit("setup");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create socket handlers
|
// Create socket handlers (original, no agent support)
|
||||||
for (const socketHandler of this.socketHandlerList) {
|
for (const socketHandler of this.socketHandlerList) {
|
||||||
socketHandler.create(socket as DockgeSocket, this);
|
socketHandler.create(dockgeSocket, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create Agent Socket
|
||||||
|
let agentSocket = new AgentSocket();
|
||||||
|
|
||||||
|
// Create agent socket handlers
|
||||||
|
for (const socketHandler of this.agentSocketHandlerList) {
|
||||||
|
socketHandler.create(dockgeSocket, this, agentSocket);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create agent proxy socket handlers
|
||||||
|
this.agentProxySocketHandler.create2(dockgeSocket, this, agentSocket);
|
||||||
|
|
||||||
// ***************************
|
// ***************************
|
||||||
// Better do anything after added all socket handlers here
|
// Better do anything after added all socket handlers here
|
||||||
// ***************************
|
// ***************************
|
||||||
@ -251,12 +302,18 @@ export class DockgeServer {
|
|||||||
log.debug("auth", "check auto login");
|
log.debug("auth", "check auto login");
|
||||||
if (await Settings.get("disableAuth")) {
|
if (await Settings.get("disableAuth")) {
|
||||||
log.info("auth", "Disabled Auth: auto login to admin");
|
log.info("auth", "Disabled Auth: auto login to admin");
|
||||||
this.afterLogin(socket as DockgeSocket, await R.findOne("user") as User);
|
this.afterLogin(dockgeSocket, await R.findOne("user") as User);
|
||||||
socket.emit("autoLogin");
|
dockgeSocket.emit("autoLogin");
|
||||||
} else {
|
} else {
|
||||||
log.debug("auth", "need auth");
|
log.debug("auth", "need auth");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Socket disconnect
|
||||||
|
dockgeSocket.on("disconnect", () => {
|
||||||
|
log.info("server", "Socket disconnected!");
|
||||||
|
dockgeSocket.instanceManager.disconnectAll();
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.io.on("disconnect", () => {
|
this.io.on("disconnect", () => {
|
||||||
@ -281,6 +338,11 @@ export class DockgeServer {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.error("server", e);
|
log.error("server", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
socket.instanceManager.sendAgentList();
|
||||||
|
|
||||||
|
// Also connect to other dockge instances
|
||||||
|
socket.instanceManager.connectAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -519,26 +581,34 @@ export class DockgeServer {
|
|||||||
return jwtSecretBean;
|
return jwtSecretBean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send stack list to all connected sockets
|
||||||
|
* @param useCache
|
||||||
|
*/
|
||||||
async sendStackList(useCache = false) {
|
async sendStackList(useCache = false) {
|
||||||
let roomList = this.io.sockets.adapter.rooms.keys();
|
let socketList = this.io.sockets.sockets.values();
|
||||||
let map : Map<string, object> | undefined;
|
|
||||||
|
let stackList;
|
||||||
|
|
||||||
|
for (let socket of socketList) {
|
||||||
|
let dockgeSocket = socket as DockgeSocket;
|
||||||
|
|
||||||
for (let room of roomList) {
|
|
||||||
// Check if the room is a number (user id)
|
// Check if the room is a number (user id)
|
||||||
if (Number(room)) {
|
if (dockgeSocket.userID) {
|
||||||
|
|
||||||
// Get the list only if there is a room
|
// Get the list only if there is a logged in user
|
||||||
if (!map) {
|
if (!stackList) {
|
||||||
map = new Map();
|
stackList = await Stack.getStackList(this, useCache);
|
||||||
let stackList = await Stack.getStackList(this, useCache);
|
|
||||||
|
|
||||||
for (let [ stackName, stack ] of stackList) {
|
|
||||||
map.set(stackName, stack.toSimpleJSON());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug("server", "Send stack list to room " + room);
|
let map : Map<string, object> = new Map();
|
||||||
this.io.to(room).emit("stackList", {
|
|
||||||
|
for (let [ stackName, stack ] of stackList) {
|
||||||
|
map.set(stackName, stack.toSimpleJSON(dockgeSocket.endpoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("server", "Send stack list to user: " + dockgeSocket.id + " (" + dockgeSocket.endpoint + ")");
|
||||||
|
dockgeSocket.emitAgent("stackList", {
|
||||||
ok: true,
|
ok: true,
|
||||||
stackList: Object.fromEntries(map),
|
stackList: Object.fromEntries(map),
|
||||||
});
|
});
|
||||||
@ -546,25 +616,6 @@ export class DockgeServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendStackStatusList() {
|
|
||||||
let statusList = await Stack.getStatusList();
|
|
||||||
|
|
||||||
let roomList = this.io.sockets.adapter.rooms.keys();
|
|
||||||
|
|
||||||
for (let room of roomList) {
|
|
||||||
// Check if the room is a number (user id)
|
|
||||||
if (Number(room)) {
|
|
||||||
log.debug("server", "Send stack status list to room " + room);
|
|
||||||
this.io.to(room).emit("stackStatusList", {
|
|
||||||
ok: true,
|
|
||||||
stackStatusList: Object.fromEntries(statusList),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
log.debug("server", "Skip sending stack status list to room " + room);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getDockerNetworkList() : Promise<string[]> {
|
async getDockerNetworkList() : Promise<string[]> {
|
||||||
let res = await childProcessAsync.spawn("docker", [ "network", "ls", "--format", "{{.Name}}" ], {
|
let res = await childProcessAsync.spawn("docker", [ "network", "ls", "--format", "{{.Name}}" ], {
|
||||||
encoding: "utf-8",
|
encoding: "utf-8",
|
||||||
@ -618,10 +669,10 @@ export class DockgeServer {
|
|||||||
* @param {string} userID
|
* @param {string} userID
|
||||||
* @param {string?} currentSocketID
|
* @param {string?} currentSocketID
|
||||||
*/
|
*/
|
||||||
disconnectAllSocketClients(userID: number, currentSocketID? : string) {
|
disconnectAllSocketClients(userID: number | undefined, currentSocketID? : string) {
|
||||||
for (const rawSocket of this.io.sockets.sockets.values()) {
|
for (const rawSocket of this.io.sockets.sockets.values()) {
|
||||||
let socket = rawSocket as DockgeSocket;
|
let socket = rawSocket as DockgeSocket;
|
||||||
if (socket.userID === userID && socket.id !== currentSocketID) {
|
if ((!userID || socket.userID === userID) && socket.id !== currentSocketID) {
|
||||||
try {
|
try {
|
||||||
socket.emit("refresh");
|
socket.emit("refresh");
|
||||||
socket.disconnect();
|
socket.disconnect();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Console colors
|
// Console colors
|
||||||
// https://stackoverflow.com/questions/9781218/how-to-change-node-jss-console-font-color
|
// https://stackoverflow.com/questions/9781218/how-to-change-node-jss-console-font-color
|
||||||
import { intHash, isDev } from "./util-common";
|
import { intHash, isDev } from "../common/util-common";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
export const CONSOLE_STYLE_Reset = "\x1b[0m";
|
export const CONSOLE_STYLE_Reset = "\x1b[0m";
|
||||||
|
16
backend/migrations/2023-12-20-2117-agent-table.ts
Normal file
16
backend/migrations/2023-12-20-2117-agent-table.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
// Create the user table
|
||||||
|
return knex.schema.createTable("agent", (table) => {
|
||||||
|
table.increments("id");
|
||||||
|
table.string("url", 255).notNullable().unique();
|
||||||
|
table.string("username", 255).notNullable();
|
||||||
|
table.string("password", 255).notNullable();
|
||||||
|
table.boolean("active").notNullable().defaultTo(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
return knex.schema.dropTable("agent");
|
||||||
|
}
|
31
backend/models/agent.ts
Normal file
31
backend/models/agent.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { BeanModel } from "redbean-node/dist/bean-model";
|
||||||
|
import { R } from "redbean-node";
|
||||||
|
import { LooseObject } from "../../common/util-common";
|
||||||
|
|
||||||
|
export class Agent extends BeanModel {
|
||||||
|
|
||||||
|
static async getAgentList() : Promise<Record<string, Agent>> {
|
||||||
|
let list = await R.findAll("agent") as Agent[];
|
||||||
|
let result : Record<string, Agent> = {};
|
||||||
|
for (let agent of list) {
|
||||||
|
result[agent.endpoint] = agent;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
get endpoint() : string {
|
||||||
|
let obj = new URL(this.url);
|
||||||
|
return obj.host;
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() : LooseObject {
|
||||||
|
return {
|
||||||
|
url: this.url,
|
||||||
|
username: this.username,
|
||||||
|
endpoint: this.endpoint,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Agent;
|
@ -1,6 +1,6 @@
|
|||||||
import { R } from "redbean-node";
|
import { R } from "redbean-node";
|
||||||
import { log } from "./log";
|
import { log } from "./log";
|
||||||
import { LooseObject } from "./util-common";
|
import { LooseObject } from "../common/util-common";
|
||||||
|
|
||||||
export class Settings {
|
export class Settings {
|
||||||
|
|
||||||
|
47
backend/socket-handlers/agent-proxy-socket-handler.ts
Normal file
47
backend/socket-handlers/agent-proxy-socket-handler.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { SocketHandler } from "../socket-handler.js";
|
||||||
|
import { DockgeServer } from "../dockge-server";
|
||||||
|
import { log } from "../log";
|
||||||
|
import { checkLogin, DockgeSocket } from "../util-server";
|
||||||
|
import { AgentSocket } from "../../common/agent-socket";
|
||||||
|
import { ALL_ENDPOINTS } from "../../common/util-common";
|
||||||
|
|
||||||
|
export class AgentProxySocketHandler extends SocketHandler {
|
||||||
|
|
||||||
|
create2(socket : DockgeSocket, server : DockgeServer, agentSocket : AgentSocket) {
|
||||||
|
// Agent - proxying requests if needed
|
||||||
|
socket.on("agent", async (endpoint : unknown, eventName : unknown, ...args : unknown[]) => {
|
||||||
|
try {
|
||||||
|
checkLogin(socket);
|
||||||
|
|
||||||
|
// Check Type
|
||||||
|
if (typeof(endpoint) !== "string") {
|
||||||
|
throw new Error("Endpoint must be a string: " + endpoint);
|
||||||
|
}
|
||||||
|
if (typeof(eventName) !== "string") {
|
||||||
|
throw new Error("Event name must be a string");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endpoint === ALL_ENDPOINTS) { // Send to all endpoints
|
||||||
|
log.debug("agent", "Sending to all endpoints: " + eventName);
|
||||||
|
socket.instanceManager.emitToAllEndpoints(eventName, ...args);
|
||||||
|
|
||||||
|
} else if (!endpoint || endpoint === socket.endpoint) { // Direct connection or matching endpoint
|
||||||
|
log.debug("agent", "Matched endpoint: " + eventName);
|
||||||
|
agentSocket.call(eventName, ...args);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
log.debug("agent", "Proxying request to " + endpoint + " for " + eventName);
|
||||||
|
await socket.instanceManager.emitToEndpoint(endpoint, eventName, ...args);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
log.warn("agent", e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
create(socket : DockgeSocket, server : DockgeServer) {
|
||||||
|
throw new Error("Method not implemented. Please use create2 instead.");
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
// @ts-ignore
|
||||||
|
import composerize from "composerize";
|
||||||
import { SocketHandler } from "../socket-handler.js";
|
import { SocketHandler } from "../socket-handler.js";
|
||||||
import { DockgeServer } from "../dockge-server";
|
import { DockgeServer } from "../dockge-server";
|
||||||
import { log } from "../log";
|
import { log } from "../log";
|
||||||
@ -5,7 +7,14 @@ import { R } from "redbean-node";
|
|||||||
import { loginRateLimiter, twoFaRateLimiter } from "../rate-limiter";
|
import { loginRateLimiter, twoFaRateLimiter } from "../rate-limiter";
|
||||||
import { generatePasswordHash, needRehashPassword, shake256, SHAKE256_LENGTH, verifyPassword } from "../password-hash";
|
import { generatePasswordHash, needRehashPassword, shake256, SHAKE256_LENGTH, verifyPassword } from "../password-hash";
|
||||||
import { User } from "../models/user";
|
import { User } from "../models/user";
|
||||||
import { checkLogin, DockgeSocket, doubleCheckPassword, JWTDecoded } from "../util-server";
|
import {
|
||||||
|
callbackError,
|
||||||
|
checkLogin,
|
||||||
|
DockgeSocket,
|
||||||
|
doubleCheckPassword,
|
||||||
|
JWTDecoded,
|
||||||
|
ValidationError
|
||||||
|
} from "../util-server";
|
||||||
import { passwordStrength } from "check-password-strength";
|
import { passwordStrength } from "check-password-strength";
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
import { Settings } from "../settings";
|
import { Settings } from "../settings";
|
||||||
@ -262,8 +271,6 @@ export class MainSocketHandler extends SocketHandler {
|
|||||||
await doubleCheckPassword(socket, currentPassword);
|
await doubleCheckPassword(socket, currentPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(data);
|
|
||||||
|
|
||||||
await Settings.setSettings("general", data);
|
await Settings.setSettings("general", data);
|
||||||
|
|
||||||
callback({
|
callback({
|
||||||
@ -294,6 +301,25 @@ export class MainSocketHandler extends SocketHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// composerize
|
||||||
|
socket.on("composerize", async (dockerRunCommand : unknown, callback) => {
|
||||||
|
try {
|
||||||
|
checkLogin(socket);
|
||||||
|
|
||||||
|
if (typeof(dockerRunCommand) !== "string") {
|
||||||
|
throw new ValidationError("dockerRunCommand must be a string");
|
||||||
|
}
|
||||||
|
|
||||||
|
const composeTemplate = composerize(dockerRunCommand);
|
||||||
|
callback({
|
||||||
|
ok: true,
|
||||||
|
composeTemplate,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
callbackError(e, callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async login(username : string, password : string) : Promise<User | null> {
|
async login(username : string, password : string) : Promise<User | null> {
|
||||||
|
70
backend/socket-handlers/manage-agent-socket-handler.ts
Normal file
70
backend/socket-handlers/manage-agent-socket-handler.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import { SocketHandler } from "../socket-handler.js";
|
||||||
|
import { DockgeServer } from "../dockge-server";
|
||||||
|
import { log } from "../log";
|
||||||
|
import { callbackError, callbackResult, checkLogin, DockgeSocket } from "../util-server";
|
||||||
|
import { LooseObject } from "../../common/util-common";
|
||||||
|
|
||||||
|
export class ManageAgentSocketHandler extends SocketHandler {
|
||||||
|
|
||||||
|
create(socket : DockgeSocket, server : DockgeServer) {
|
||||||
|
// addAgent
|
||||||
|
socket.on("addAgent", async (requestData : unknown, callback : unknown) => {
|
||||||
|
try {
|
||||||
|
log.debug("manage-agent-socket-handler", "addAgent");
|
||||||
|
checkLogin(socket);
|
||||||
|
|
||||||
|
if (typeof(requestData) !== "object") {
|
||||||
|
throw new Error("Data must be an object");
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = requestData as LooseObject;
|
||||||
|
let manager = socket.instanceManager;
|
||||||
|
await manager.test(data.url, data.username, data.password);
|
||||||
|
await manager.add(data.url, data.username, data.password);
|
||||||
|
|
||||||
|
// connect to the agent
|
||||||
|
manager.connect(data.url, data.username, data.password);
|
||||||
|
|
||||||
|
// Refresh another sockets
|
||||||
|
// It is a bit difficult to control another browser sessions to connect/disconnect agents, so force them to refresh the page will be easier.
|
||||||
|
server.disconnectAllSocketClients(undefined, socket.id);
|
||||||
|
manager.sendAgentList();
|
||||||
|
|
||||||
|
callbackResult({
|
||||||
|
ok: true,
|
||||||
|
msg: "agentAddedSuccessfully",
|
||||||
|
msgi18n: true,
|
||||||
|
}, callback);
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
callbackError(e, callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// removeAgent
|
||||||
|
socket.on("removeAgent", async (url : unknown, callback : unknown) => {
|
||||||
|
try {
|
||||||
|
log.debug("manage-agent-socket-handler", "removeAgent");
|
||||||
|
checkLogin(socket);
|
||||||
|
|
||||||
|
if (typeof(url) !== "string") {
|
||||||
|
throw new Error("URL must be a string");
|
||||||
|
}
|
||||||
|
|
||||||
|
let manager = socket.instanceManager;
|
||||||
|
await manager.remove(url);
|
||||||
|
|
||||||
|
server.disconnectAllSocketClients(undefined, socket.id);
|
||||||
|
manager.sendAgentList();
|
||||||
|
|
||||||
|
callbackResult({
|
||||||
|
ok: true,
|
||||||
|
msg: "agentRemovedSuccessfully",
|
||||||
|
msgi18n: true,
|
||||||
|
}, callback);
|
||||||
|
} catch (e) {
|
||||||
|
callbackError(e, callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -15,9 +15,10 @@ import {
|
|||||||
PROGRESS_TERMINAL_ROWS,
|
PROGRESS_TERMINAL_ROWS,
|
||||||
RUNNING, TERMINAL_ROWS,
|
RUNNING, TERMINAL_ROWS,
|
||||||
UNKNOWN
|
UNKNOWN
|
||||||
} from "./util-common";
|
} from "../common/util-common";
|
||||||
import { InteractiveTerminal, Terminal } from "./terminal";
|
import { InteractiveTerminal, Terminal } from "./terminal";
|
||||||
import childProcessAsync from "promisify-child-process";
|
import childProcessAsync from "promisify-child-process";
|
||||||
|
import { Settings } from "./settings";
|
||||||
|
|
||||||
export class Stack {
|
export class Stack {
|
||||||
|
|
||||||
@ -50,22 +51,41 @@ export class Stack {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSON() : object {
|
async toJSON(endpoint : string) : Promise<object> {
|
||||||
let obj = this.toSimpleJSON();
|
|
||||||
|
// Since we have multiple agents now, embed primary hostname in the stack object too.
|
||||||
|
let primaryHostname = await Settings.get("primaryHostname");
|
||||||
|
if (!primaryHostname) {
|
||||||
|
if (!endpoint) {
|
||||||
|
primaryHostname = "localhost";
|
||||||
|
} else {
|
||||||
|
// Use the endpoint as the primary hostname
|
||||||
|
try {
|
||||||
|
primaryHostname = (new URL("https://" + endpoint).hostname);
|
||||||
|
} catch (e) {
|
||||||
|
// Just in case if the endpoint is in a incorrect format
|
||||||
|
primaryHostname = "localhost";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let obj = this.toSimpleJSON(endpoint);
|
||||||
return {
|
return {
|
||||||
...obj,
|
...obj,
|
||||||
composeYAML: this.composeYAML,
|
composeYAML: this.composeYAML,
|
||||||
composeENV: this.composeENV,
|
composeENV: this.composeENV,
|
||||||
|
primaryHostname,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
toSimpleJSON() : object {
|
toSimpleJSON(endpoint : string) : object {
|
||||||
return {
|
return {
|
||||||
name: this.name,
|
name: this.name,
|
||||||
status: this._status,
|
status: this._status,
|
||||||
tags: [],
|
tags: [],
|
||||||
isManagedByDockge: this.isManagedByDockge,
|
isManagedByDockge: this.isManagedByDockge,
|
||||||
composeFileName: this._composeFileName,
|
composeFileName: this._composeFileName,
|
||||||
|
endpoint,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,8 +206,8 @@ export class Stack {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async deploy(socket? : DockgeSocket) : Promise<number> {
|
async deploy(socket : DockgeSocket) : Promise<number> {
|
||||||
const terminalName = getComposeTerminalName(this.name);
|
const terminalName = getComposeTerminalName(socket.endpoint, this.name);
|
||||||
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "up", "-d", "--remove-orphans" ], this.path);
|
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "up", "-d", "--remove-orphans" ], this.path);
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
throw new Error("Failed to deploy, please check the terminal output for more information.");
|
throw new Error("Failed to deploy, please check the terminal output for more information.");
|
||||||
@ -195,8 +215,8 @@ export class Stack {
|
|||||||
return exitCode;
|
return exitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(socket?: DockgeSocket) : Promise<number> {
|
async delete(socket: DockgeSocket) : Promise<number> {
|
||||||
const terminalName = getComposeTerminalName(this.name);
|
const terminalName = getComposeTerminalName(socket.endpoint, this.name);
|
||||||
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "down", "--remove-orphans" ], this.path);
|
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "down", "--remove-orphans" ], this.path);
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
throw new Error("Failed to delete, please check the terminal output for more information.");
|
throw new Error("Failed to delete, please check the terminal output for more information.");
|
||||||
@ -388,7 +408,7 @@ export class Stack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async start(socket: DockgeSocket) {
|
async start(socket: DockgeSocket) {
|
||||||
const terminalName = getComposeTerminalName(this.name);
|
const terminalName = getComposeTerminalName(socket.endpoint, this.name);
|
||||||
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "up", "-d", "--remove-orphans" ], this.path);
|
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "up", "-d", "--remove-orphans" ], this.path);
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
throw new Error("Failed to start, please check the terminal output for more information.");
|
throw new Error("Failed to start, please check the terminal output for more information.");
|
||||||
@ -397,7 +417,7 @@ export class Stack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async stop(socket: DockgeSocket) : Promise<number> {
|
async stop(socket: DockgeSocket) : Promise<number> {
|
||||||
const terminalName = getComposeTerminalName(this.name);
|
const terminalName = getComposeTerminalName(socket.endpoint, this.name);
|
||||||
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "stop" ], this.path);
|
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "stop" ], this.path);
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
throw new Error("Failed to stop, please check the terminal output for more information.");
|
throw new Error("Failed to stop, please check the terminal output for more information.");
|
||||||
@ -406,7 +426,7 @@ export class Stack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async restart(socket: DockgeSocket) : Promise<number> {
|
async restart(socket: DockgeSocket) : Promise<number> {
|
||||||
const terminalName = getComposeTerminalName(this.name);
|
const terminalName = getComposeTerminalName(socket.endpoint, this.name);
|
||||||
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "restart" ], this.path);
|
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "restart" ], this.path);
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
throw new Error("Failed to restart, please check the terminal output for more information.");
|
throw new Error("Failed to restart, please check the terminal output for more information.");
|
||||||
@ -415,7 +435,7 @@ export class Stack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async down(socket: DockgeSocket) : Promise<number> {
|
async down(socket: DockgeSocket) : Promise<number> {
|
||||||
const terminalName = getComposeTerminalName(this.name);
|
const terminalName = getComposeTerminalName(socket.endpoint, this.name);
|
||||||
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "down" ], this.path);
|
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "down" ], this.path);
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
throw new Error("Failed to down, please check the terminal output for more information.");
|
throw new Error("Failed to down, please check the terminal output for more information.");
|
||||||
@ -424,7 +444,7 @@ export class Stack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async update(socket: DockgeSocket) {
|
async update(socket: DockgeSocket) {
|
||||||
const terminalName = getComposeTerminalName(this.name);
|
const terminalName = getComposeTerminalName(socket.endpoint, this.name);
|
||||||
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "pull" ], this.path);
|
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "pull" ], this.path);
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
throw new Error("Failed to pull, please check the terminal output for more information.");
|
throw new Error("Failed to pull, please check the terminal output for more information.");
|
||||||
@ -445,7 +465,7 @@ export class Stack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async joinCombinedTerminal(socket: DockgeSocket) {
|
async joinCombinedTerminal(socket: DockgeSocket) {
|
||||||
const terminalName = getCombinedTerminalName(this.name);
|
const terminalName = getCombinedTerminalName(socket.endpoint, this.name);
|
||||||
const terminal = Terminal.getOrCreateTerminal(this.server, terminalName, "docker", [ "compose", "logs", "-f", "--tail", "100" ], this.path);
|
const terminal = Terminal.getOrCreateTerminal(this.server, terminalName, "docker", [ "compose", "logs", "-f", "--tail", "100" ], this.path);
|
||||||
terminal.enableKeepAlive = true;
|
terminal.enableKeepAlive = true;
|
||||||
terminal.rows = COMBINED_TERMINAL_ROWS;
|
terminal.rows = COMBINED_TERMINAL_ROWS;
|
||||||
@ -455,7 +475,7 @@ export class Stack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async leaveCombinedTerminal(socket: DockgeSocket) {
|
async leaveCombinedTerminal(socket: DockgeSocket) {
|
||||||
const terminalName = getCombinedTerminalName(this.name);
|
const terminalName = getCombinedTerminalName(socket.endpoint, this.name);
|
||||||
const terminal = Terminal.getTerminal(terminalName);
|
const terminal = Terminal.getTerminal(terminalName);
|
||||||
if (terminal) {
|
if (terminal) {
|
||||||
terminal.leave(socket);
|
terminal.leave(socket);
|
||||||
@ -463,7 +483,7 @@ export class Stack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async joinContainerTerminal(socket: DockgeSocket, serviceName: string, shell : string = "sh", index: number = 0) {
|
async joinContainerTerminal(socket: DockgeSocket, serviceName: string, shell : string = "sh", index: number = 0) {
|
||||||
const terminalName = getContainerExecTerminalName(this.name, serviceName, index);
|
const terminalName = getContainerExecTerminalName(socket.endpoint, this.name, serviceName, index);
|
||||||
let terminal = Terminal.getTerminal(terminalName);
|
let terminal = Terminal.getTerminal(terminalName);
|
||||||
|
|
||||||
if (!terminal) {
|
if (!terminal) {
|
||||||
|
@ -4,11 +4,10 @@ import * as pty from "@homebridge/node-pty-prebuilt-multiarch";
|
|||||||
import { LimitQueue } from "./utils/limit-queue";
|
import { LimitQueue } from "./utils/limit-queue";
|
||||||
import { DockgeSocket } from "./util-server";
|
import { DockgeSocket } from "./util-server";
|
||||||
import {
|
import {
|
||||||
allowedCommandList, allowedRawKeys,
|
|
||||||
PROGRESS_TERMINAL_ROWS,
|
PROGRESS_TERMINAL_ROWS,
|
||||||
TERMINAL_COLS,
|
TERMINAL_COLS,
|
||||||
TERMINAL_ROWS
|
TERMINAL_ROWS
|
||||||
} from "./util-common";
|
} from "../common/util-common";
|
||||||
import { sync as commandExistsSync } from "command-exists";
|
import { sync as commandExistsSync } from "command-exists";
|
||||||
import { log } from "./log";
|
import { log } from "./log";
|
||||||
|
|
||||||
@ -16,7 +15,6 @@ import { log } from "./log";
|
|||||||
* Terminal for running commands, no user interaction
|
* Terminal for running commands, no user interaction
|
||||||
*/
|
*/
|
||||||
export class Terminal {
|
export class Terminal {
|
||||||
|
|
||||||
protected static terminalMap : Map<string, Terminal> = new Map();
|
protected static terminalMap : Map<string, Terminal> = new Map();
|
||||||
|
|
||||||
protected _ptyProcess? : pty.IPty;
|
protected _ptyProcess? : pty.IPty;
|
||||||
@ -34,6 +32,9 @@ export class Terminal {
|
|||||||
|
|
||||||
public enableKeepAlive : boolean = false;
|
public enableKeepAlive : boolean = false;
|
||||||
protected keepAliveInterval? : NodeJS.Timeout;
|
protected keepAliveInterval? : NodeJS.Timeout;
|
||||||
|
protected kickDisconnectedClientsInterval? : NodeJS.Timeout;
|
||||||
|
|
||||||
|
protected socketList : Record<string, DockgeSocket> = {};
|
||||||
|
|
||||||
constructor(server : DockgeServer, name : string, file : string, args : string | string[], cwd : string) {
|
constructor(server : DockgeServer, name : string, file : string, args : string | string[], cwd : string) {
|
||||||
this.server = server;
|
this.server = server;
|
||||||
@ -82,13 +83,22 @@ export class Terminal {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.kickDisconnectedClientsInterval = setInterval(() => {
|
||||||
|
for (const socketID in this.socketList) {
|
||||||
|
const socket = this.socketList[socketID];
|
||||||
|
if (!socket.connected) {
|
||||||
|
log.debug("Terminal", "Kicking disconnected client " + socket.id + " from terminal " + this.name);
|
||||||
|
this.leave(socket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 60 * 1000);
|
||||||
|
|
||||||
if (this.enableKeepAlive) {
|
if (this.enableKeepAlive) {
|
||||||
log.debug("Terminal", "Keep alive enabled for terminal " + this.name);
|
log.debug("Terminal", "Keep alive enabled for terminal " + this.name);
|
||||||
|
|
||||||
// Close if there is no clients
|
// Close if there is no clients
|
||||||
this.keepAliveInterval = setInterval(() => {
|
this.keepAliveInterval = setInterval(() => {
|
||||||
const clients = this.server.io.sockets.adapter.rooms.get(this.name);
|
const numClients = Object.keys(this.socketList).length;
|
||||||
const numClients = clients ? clients.size : 0;
|
|
||||||
|
|
||||||
if (numClients === 0) {
|
if (numClients === 0) {
|
||||||
log.debug("Terminal", "Terminal " + this.name + " has no client, closing...");
|
log.debug("Terminal", "Terminal " + this.name + " has no client, closing...");
|
||||||
@ -112,8 +122,10 @@ export class Terminal {
|
|||||||
// On Data
|
// On Data
|
||||||
this._ptyProcess.onData((data) => {
|
this._ptyProcess.onData((data) => {
|
||||||
this.buffer.pushItem(data);
|
this.buffer.pushItem(data);
|
||||||
if (this.server.io) {
|
|
||||||
this.server.io.to(this.name).emit("terminalWrite", this.name, data);
|
for (const socketID in this.socketList) {
|
||||||
|
const socket = this.socketList[socketID];
|
||||||
|
socket.emitAgent("terminalWrite", this.name, data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -137,15 +149,19 @@ export class Terminal {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
protected exit = (res : {exitCode: number, signal?: number | undefined}) => {
|
protected exit = (res : {exitCode: number, signal?: number | undefined}) => {
|
||||||
this.server.io.to(this.name).emit("terminalExit", this.name, res.exitCode);
|
for (const socketID in this.socketList) {
|
||||||
|
const socket = this.socketList[socketID];
|
||||||
|
socket.emitAgent("terminalExit", this.name, res.exitCode);
|
||||||
|
}
|
||||||
|
|
||||||
// Remove room
|
// Remove all clients
|
||||||
this.server.io.in(this.name).socketsLeave(this.name);
|
this.socketList = {};
|
||||||
|
|
||||||
Terminal.terminalMap.delete(this.name);
|
Terminal.terminalMap.delete(this.name);
|
||||||
log.debug("Terminal", "Terminal " + this.name + " exited with code " + res.exitCode);
|
log.debug("Terminal", "Terminal " + this.name + " exited with code " + res.exitCode);
|
||||||
|
|
||||||
clearInterval(this.keepAliveInterval);
|
clearInterval(this.keepAliveInterval);
|
||||||
|
clearInterval(this.kickDisconnectedClientsInterval);
|
||||||
|
|
||||||
if (this.callback) {
|
if (this.callback) {
|
||||||
this.callback(res.exitCode);
|
this.callback(res.exitCode);
|
||||||
@ -157,11 +173,11 @@ export class Terminal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public join(socket : DockgeSocket) {
|
public join(socket : DockgeSocket) {
|
||||||
socket.join(this.name);
|
this.socketList[socket.id] = socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
public leave(socket : DockgeSocket) {
|
public leave(socket : DockgeSocket) {
|
||||||
socket.leave(this.name);
|
delete this.socketList[socket.id];
|
||||||
}
|
}
|
||||||
|
|
||||||
public get ptyProcess() {
|
public get ptyProcess() {
|
||||||
@ -254,6 +270,11 @@ export class MainTerminal extends InteractiveTerminal {
|
|||||||
constructor(server : DockgeServer, name : string) {
|
constructor(server : DockgeServer, name : string) {
|
||||||
let shell;
|
let shell;
|
||||||
|
|
||||||
|
// Throw an error if console is not enabled
|
||||||
|
if (!server.config.enableConsole) {
|
||||||
|
throw new Error("Console is not enabled.");
|
||||||
|
}
|
||||||
|
|
||||||
if (os.platform() === "win32") {
|
if (os.platform() === "win32") {
|
||||||
if (commandExistsSync("pwsh.exe")) {
|
if (commandExistsSync("pwsh.exe")) {
|
||||||
shell = "pwsh.exe";
|
shell = "pwsh.exe";
|
||||||
@ -267,21 +288,6 @@ export class MainTerminal extends InteractiveTerminal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public write(input : string) {
|
public write(input : string) {
|
||||||
// For like Ctrl + C
|
|
||||||
if (allowedRawKeys.includes(input)) {
|
|
||||||
super.write(input);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the command is allowed
|
|
||||||
const cmdParts = input.split(" ");
|
|
||||||
const executable = cmdParts[0].trim();
|
|
||||||
log.debug("console", "Executable: " + executable);
|
|
||||||
log.debug("console", "Executable length: " + executable.length);
|
|
||||||
|
|
||||||
if (!allowedCommandList.includes(executable)) {
|
|
||||||
throw new Error("Command not allowed.");
|
|
||||||
}
|
|
||||||
super.write(input);
|
super.write(input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,11 @@ import { Socket } from "socket.io";
|
|||||||
import { Terminal } from "./terminal";
|
import { Terminal } from "./terminal";
|
||||||
import { randomBytes } from "crypto";
|
import { randomBytes } from "crypto";
|
||||||
import { log } from "./log";
|
import { log } from "./log";
|
||||||
import { ERROR_TYPE_VALIDATION } from "./util-common";
|
import { ERROR_TYPE_VALIDATION } from "../common/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";
|
import fs from "fs";
|
||||||
|
import { AgentManager } from "./agent-manager";
|
||||||
|
|
||||||
export interface JWTDecoded {
|
export interface JWTDecoded {
|
||||||
username : string;
|
username : string;
|
||||||
@ -15,6 +16,9 @@ export interface JWTDecoded {
|
|||||||
export interface DockgeSocket extends Socket {
|
export interface DockgeSocket extends Socket {
|
||||||
userID: number;
|
userID: number;
|
||||||
consoleTerminal? : Terminal;
|
consoleTerminal? : Terminal;
|
||||||
|
instanceManager : AgentManager;
|
||||||
|
endpoint : string;
|
||||||
|
emitAgent : (eventName : string, ...args : unknown[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For command line arguments, so they are nullable
|
// For command line arguments, so they are nullable
|
||||||
@ -26,6 +30,7 @@ export interface Arguments {
|
|||||||
hostname? : string;
|
hostname? : string;
|
||||||
dataDir? : string;
|
dataDir? : string;
|
||||||
stacksDir? : string;
|
stacksDir? : string;
|
||||||
|
enableConsole? : boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some config values are required
|
// Some config values are required
|
||||||
@ -56,18 +61,28 @@ export function callbackError(error : unknown, callback : unknown) {
|
|||||||
callback({
|
callback({
|
||||||
ok: false,
|
ok: false,
|
||||||
msg: error.message,
|
msg: error.message,
|
||||||
|
msgi18n: true,
|
||||||
});
|
});
|
||||||
} else if (error instanceof ValidationError) {
|
} else if (error instanceof ValidationError) {
|
||||||
callback({
|
callback({
|
||||||
ok: false,
|
ok: false,
|
||||||
type: ERROR_TYPE_VALIDATION,
|
type: ERROR_TYPE_VALIDATION,
|
||||||
msg: error.message,
|
msg: error.message,
|
||||||
|
msgi18n: true,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
log.debug("console", "Unknown error: " + error);
|
log.debug("console", "Unknown error: " + error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function callbackResult(result : unknown, callback : unknown) {
|
||||||
|
if (typeof(callback) !== "function") {
|
||||||
|
log.error("console", "Callback is not a function");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback(result);
|
||||||
|
}
|
||||||
|
|
||||||
export async function doubleCheckPassword(socket : DockgeSocket, currentPassword : unknown) {
|
export async function doubleCheckPassword(socket : DockgeSocket, currentPassword : unknown) {
|
||||||
if (typeof currentPassword !== "string") {
|
if (typeof currentPassword !== "string") {
|
||||||
throw new Error("Wrong data type?");
|
throw new Error("Wrong data type?");
|
||||||
|
15
common/agent-socket.ts
Normal file
15
common/agent-socket.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
export class AgentSocket {
|
||||||
|
|
||||||
|
eventList : Map<string, (...args : unknown[]) => void> = new Map();
|
||||||
|
|
||||||
|
on(event : string, callback : (...args : unknown[]) => void) {
|
||||||
|
this.eventList.set(event, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
call(eventName : string, ...args : unknown[]) {
|
||||||
|
const callback = this.eventList.get(eventName);
|
||||||
|
if (callback) {
|
||||||
|
callback(...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -43,6 +43,8 @@ async function initRandomBytes() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const ALL_ENDPOINTS = "##ALL_DOCKGE_ENDPOINTS##";
|
||||||
|
|
||||||
// Stack Status
|
// Stack Status
|
||||||
export const UNKNOWN = 0;
|
export const UNKNOWN = 0;
|
||||||
export const CREATED_FILE = 1;
|
export const CREATED_FILE = 1;
|
||||||
@ -105,17 +107,6 @@ export const COMBINED_TERMINAL_ROWS = 20;
|
|||||||
|
|
||||||
export const ERROR_TYPE_VALIDATION = 1;
|
export const ERROR_TYPE_VALIDATION = 1;
|
||||||
|
|
||||||
export const allowedCommandList : string[] = [
|
|
||||||
"docker",
|
|
||||||
"ls",
|
|
||||||
"cd",
|
|
||||||
"dir",
|
|
||||||
];
|
|
||||||
|
|
||||||
export const allowedRawKeys = [
|
|
||||||
"\u0003", // Ctrl + C
|
|
||||||
];
|
|
||||||
|
|
||||||
export const acceptedComposeFileNames = [
|
export const acceptedComposeFileNames = [
|
||||||
"compose.yaml",
|
"compose.yaml",
|
||||||
"docker-compose.yaml",
|
"docker-compose.yaml",
|
||||||
@ -206,20 +197,20 @@ export function getCryptoRandomInt(min: number, max: number):number {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getComposeTerminalName(stack : string) {
|
export function getComposeTerminalName(endpoint : string, stack : string) {
|
||||||
return "compose-" + stack;
|
return "compose-" + endpoint + "-" + stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCombinedTerminalName(stack : string) {
|
export function getCombinedTerminalName(endpoint : string, stack : string) {
|
||||||
return "combined-" + stack;
|
return "combined-" + endpoint + "-" + stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getContainerTerminalName(container : string) {
|
export function getContainerTerminalName(endpoint : string, container : string) {
|
||||||
return "container-" + container;
|
return "container-" + endpoint + "-" + container;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getContainerExecTerminalName(stackName : string, container : string, index : number) {
|
export function getContainerExecTerminalName(endpoint : string, stackName : string, container : string, index : number) {
|
||||||
return "container-exec-" + stackName + "-" + container + "-" + index;
|
return "container-exec-" + endpoint + "-" + stackName + "-" + container + "-" + index;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function copyYAMLComments(doc : Document, src : Document) {
|
export function copyYAMLComments(doc : Document, src : Document) {
|
||||||
@ -234,42 +225,63 @@ export function copyYAMLComments(doc : Document, src : Document) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy yaml comments from srcItems to items
|
* Copy yaml comments from srcItems to items
|
||||||
* Typescript is super annoying here, so I have to use any here
|
* Attempts to preserve comments by matching content rather than just array indices
|
||||||
* TODO: Since comments are belong to the array index, the comments will be lost if the order of the items is changed or removed or added.
|
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
function copyYAMLCommentsItems(items : any, srcItems : any) {
|
function copyYAMLCommentsItems(items: any, srcItems: any) {
|
||||||
if (!items || !srcItems) {
|
if (!items || !srcItems) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// First pass - try to match items by their content
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const item : any = items[i];
|
const item: any = items[i];
|
||||||
|
|
||||||
|
// Try to find matching source item by content
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const srcItem : any = srcItems[i];
|
const srcIndex = srcItems.findIndex((srcItem: any) =>
|
||||||
|
JSON.stringify(srcItem.value) === JSON.stringify(item.value) &&
|
||||||
|
JSON.stringify(srcItem.key) === JSON.stringify(item.key)
|
||||||
|
);
|
||||||
|
|
||||||
if (!srcItem) {
|
if (srcIndex !== -1) {
|
||||||
continue;
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
}
|
const srcItem: any = srcItems[srcIndex];
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const nextSrcItem: any = srcItems[srcIndex + 1];
|
||||||
|
|
||||||
if (item.key && srcItem.key) {
|
if (item.key && srcItem.key) {
|
||||||
item.key.comment = srcItem.key.comment;
|
item.key.comment = srcItem.key.comment;
|
||||||
item.key.commentBefore = srcItem.key.commentBefore;
|
item.key.commentBefore = srcItem.key.commentBefore;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (srcItem.comment) {
|
if (srcItem.comment) {
|
||||||
item.comment = srcItem.comment;
|
item.comment = srcItem.comment;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.value && srcItem.value) {
|
// Handle comments between array items
|
||||||
if (typeof item.value === "object" && typeof srcItem.value === "object") {
|
if (nextSrcItem && nextSrcItem.commentBefore) {
|
||||||
item.value.comment = srcItem.value.comment;
|
if (items[i + 1]) {
|
||||||
item.value.commentBefore = srcItem.value.commentBefore;
|
items[i + 1].commentBefore = nextSrcItem.commentBefore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (item.value.items && srcItem.value.items) {
|
// Handle trailing comments after array items
|
||||||
copyYAMLCommentsItems(item.value.items, srcItem.value.items);
|
if (srcItem.value && srcItem.value.comment) {
|
||||||
|
if (item.value) {
|
||||||
|
item.value.comment = srcItem.value.comment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.value && srcItem.value) {
|
||||||
|
if (typeof item.value === "object" && typeof srcItem.value === "object") {
|
||||||
|
item.value.comment = srcItem.value.comment;
|
||||||
|
item.value.commentBefore = srcItem.value.commentBefore;
|
||||||
|
|
||||||
|
if (item.value.items && srcItem.value.items) {
|
||||||
|
copyYAMLCommentsItems(item.value.items, srcItem.value.items);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -289,10 +301,9 @@ function copyYAMLCommentsItems(items : any, srcItems : any) {
|
|||||||
* - "127.0.0.1:5000-5010:5000-5010"
|
* - "127.0.0.1:5000-5010:5000-5010"
|
||||||
* - "6060:6060/udp"
|
* - "6060:6060/udp"
|
||||||
* @param input
|
* @param input
|
||||||
* @param defaultHostname
|
* @param hostname
|
||||||
*/
|
*/
|
||||||
export function parseDockerPort(input : string, defaultHostname : string = "localhost") {
|
export function parseDockerPort(input : string, hostname : string) {
|
||||||
let hostname = defaultHostname;
|
|
||||||
let port;
|
let port;
|
||||||
let display;
|
let display;
|
||||||
|
|
||||||
@ -405,3 +416,4 @@ function traverseYAML(pair : Pair, env : DotenvParseOutput) : void {
|
|||||||
pair.value.value = envsubst(pair.value.value, env);
|
pair.value.value = envsubst(pair.value.value, env);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,3 @@
|
|||||||
version: "3.8"
|
|
||||||
services:
|
services:
|
||||||
dockge:
|
dockge:
|
||||||
image: louislam/dockge:1
|
image: louislam/dockge:1
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
# Due to the bug of #145, Node.js's version cannot be changed, unless upstream is fixed.
|
FROM node:22-bookworm-slim
|
||||||
FROM node:18.17.1-bookworm-slim
|
|
||||||
ENV PNPM_HOME="/pnpm"
|
|
||||||
ENV PATH="$PNPM_HOME:$PATH"
|
|
||||||
RUN apt update && apt install --yes --no-install-recommends \
|
RUN apt update && apt install --yes --no-install-recommends \
|
||||||
curl \
|
curl \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
@ -20,5 +17,4 @@ RUN apt update && apt install --yes --no-install-recommends \
|
|||||||
docker-ce-cli \
|
docker-ce-cli \
|
||||||
docker-compose-plugin \
|
docker-compose-plugin \
|
||||||
&& rm -rf /var/lib/apt/lists/* \
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
&& npm install pnpm -g \
|
&& npm install -g tsx
|
||||||
&& pnpm install -g tsx
|
|
||||||
|
@ -9,8 +9,8 @@ FROM louislam/dockge:build-healthcheck AS build_healthcheck
|
|||||||
FROM louislam/dockge:base AS build
|
FROM louislam/dockge:base AS build
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --chown=node:node ./package.json ./package.json
|
COPY --chown=node:node ./package.json ./package.json
|
||||||
COPY --chown=node:node ./pnpm-lock.yaml ./pnpm-lock.yaml
|
COPY --chown=node:node ./package-lock.json ./package-lock.json
|
||||||
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile
|
RUN npm ci
|
||||||
|
|
||||||
############################################
|
############################################
|
||||||
# ⭐ Main Image
|
# ⭐ Main Image
|
||||||
@ -22,6 +22,13 @@ COPY --from=build /app/node_modules /app/node_modules
|
|||||||
COPY --chown=node:node . .
|
COPY --chown=node:node . .
|
||||||
RUN mkdir ./data
|
RUN mkdir ./data
|
||||||
|
|
||||||
|
|
||||||
|
# It is just for safe, as by default, it is disabled in the latest Node.js now.
|
||||||
|
# Read more:
|
||||||
|
# - https://github.com/sagemathinc/cocalc/issues/6963
|
||||||
|
# - https://github.com/microsoft/node-pty/issues/630#issuecomment-1987212447
|
||||||
|
ENV UV_USE_IO_URING=0
|
||||||
|
|
||||||
VOLUME /app/data
|
VOLUME /app/data
|
||||||
EXPOSE 5001
|
EXPOSE 5001
|
||||||
HEALTHCHECK --interval=60s --timeout=30s --start-period=60s --retries=5 CMD extra/healthcheck
|
HEALTHCHECK --interval=60s --timeout=30s --start-period=60s --retries=5 CMD extra/healthcheck
|
||||||
@ -32,4 +39,4 @@ CMD ["tsx", "./backend/index.ts"]
|
|||||||
# Mark as Nightly
|
# Mark as Nightly
|
||||||
############################################
|
############################################
|
||||||
FROM release AS nightly
|
FROM release AS nightly
|
||||||
RUN pnpm run mark-as-nightly
|
RUN npm run mark-as-nightly
|
||||||
|
@ -5,7 +5,7 @@ import { User } from "../backend/models/user";
|
|||||||
import { DockgeServer } from "../backend/dockge-server";
|
import { DockgeServer } from "../backend/dockge-server";
|
||||||
import { log } from "../backend/log";
|
import { log } from "../backend/log";
|
||||||
import { io } from "socket.io-client";
|
import { io } from "socket.io-client";
|
||||||
import { BaseRes } from "../backend/util-common";
|
import { BaseRes } from "../common/util-common";
|
||||||
|
|
||||||
console.log("== Dockge Reset Password Tool ==");
|
console.log("== Dockge Reset Password Tool ==");
|
||||||
|
|
||||||
@ -92,7 +92,6 @@ function disconnectAllSocketClients(username : string, password : string) : Prom
|
|||||||
|
|
||||||
// Disconnect all socket connections
|
// Disconnect all socket connections
|
||||||
const socket = io(url, {
|
const socket = io(url, {
|
||||||
transports: [ "websocket" ],
|
|
||||||
reconnection: false,
|
reconnection: false,
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
version: "3.8"
|
|
||||||
services:
|
services:
|
||||||
mariadb:
|
mariadb:
|
||||||
image: mariadb:latest
|
image: mariadb:latest
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
version: '3.8'
|
|
||||||
services:
|
services:
|
||||||
nginx-proxy-manager:
|
nginx-proxy-manager:
|
||||||
image: 'jc21/nginx-proxy-manager:latest'
|
image: 'jc21/nginx-proxy-manager:latest'
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
version: '3.8'
|
|
||||||
services:
|
services:
|
||||||
uptime-kuma:
|
uptime-kuma:
|
||||||
image: louislam/uptime-kuma:1
|
image: louislam/uptime-kuma:1
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
<button class="btn btn-normal btn-sm mt-3" @click="addField">{{ $t("addListItem", [ displayName ]) }}</button>
|
<button class="btn btn-normal btn-sm mt-3" @click="addField">{{ $t("addListItem", [ displayName ]) }}</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
Long syntax is not supported here. Please use the YAML editor.
|
{{ $t("LongSyntaxNotSupported") }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<ul v-if="isArrayInited" class="list-group">
|
<ul v-if="isArrayInited" class="list-group">
|
||||||
<li v-for="(value, index) in array" :key="index" class="list-group-item">
|
<li v-for="(value, index) in array" :key="index" class="list-group-item">
|
||||||
<select v-model="array[index]" class="no-bg domain-input">
|
<select v-model="array[index]" class="no-bg domain-input">
|
||||||
<option value="">Select a network...</option>
|
<option value="">{{ $t(`Select a network...`) }}</option>
|
||||||
<option v-for="option in options" :key="option" :value="option">{{ option }}</option>
|
<option v-for="option in options" :key="option" :value="option">{{ option }}</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@
|
|||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div v-if="networkList.length === 0 && service.networks && service.networks.length > 0" class="text-warning mb-3">
|
<div v-if="networkList.length === 0 && service.networks && service.networks.length > 0" class="text-warning mb-3">
|
||||||
No networks available. You need to add internal networks or enable external networks in the right side first.
|
{{ $t("NoNetworksAvailable") }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ArraySelect name="networks" :display-name="$t('network')" placeholder="Network Name" :options="networkList" />
|
<ArraySelect name="networks" :display-name="$t('network')" placeholder="Network Name" :options="networkList" />
|
||||||
@ -127,7 +127,7 @@
|
|||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t("dependsOn") }}
|
{{ $t("dependsOn") }}
|
||||||
</label>
|
</label>
|
||||||
<ArrayInput name="depends_on" :display-name="$t('dependsOn')" placeholder="Container Name" />
|
<ArrayInput name="depends_on" :display-name="$t('dependsOn')" :placeholder="$t(`containerName`)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
@ -137,7 +137,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||||
import { parseDockerPort } from "../../../backend/util-common";
|
import { parseDockerPort } from "../../../common/util-common";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
@ -189,14 +189,34 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
terminalRouteLink() {
|
terminalRouteLink() {
|
||||||
return {
|
if (this.endpoint) {
|
||||||
name: "containerTerminal",
|
return {
|
||||||
params: {
|
name: "containerTerminalEndpoint",
|
||||||
stackName: this.stackName,
|
params: {
|
||||||
serviceName: this.name,
|
endpoint: this.endpoint,
|
||||||
type: "bash",
|
stackName: this.stackName,
|
||||||
},
|
serviceName: this.name,
|
||||||
};
|
type: "bash",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
name: "containerTerminal",
|
||||||
|
params: {
|
||||||
|
stackName: this.stackName,
|
||||||
|
serviceName: this.name,
|
||||||
|
type: "bash",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
endpoint() {
|
||||||
|
return this.$parent.$parent.endpoint;
|
||||||
|
},
|
||||||
|
|
||||||
|
stack() {
|
||||||
|
return this.$parent.$parent.stack;
|
||||||
},
|
},
|
||||||
|
|
||||||
stackName() {
|
stackName() {
|
||||||
@ -254,8 +274,12 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
parsePort(port) {
|
parsePort(port) {
|
||||||
let hostname = this.$root.info.primaryHostname || location.hostname;
|
if (this.stack.endpoint) {
|
||||||
return parseDockerPort(port, hostname);
|
return parseDockerPort(port, this.stack.primaryHostname);
|
||||||
|
} else {
|
||||||
|
let hostname = this.$root.info.primaryHostname || location.hostname;
|
||||||
|
return parseDockerPort(port, hostname);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
remove() {
|
remove() {
|
||||||
delete this.jsonObject.services[this.name];
|
delete this.jsonObject.services[this.name];
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<h5>{{ $t("Internal Networks") }}</h5>
|
<h5>{{ $t("Internal Networks") }}</h5>
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
<li v-for="(networkRow, index) in networkList" :key="index" class="list-group-item">
|
<li v-for="(networkRow, index) in networkList" :key="index" class="list-group-item">
|
||||||
<input v-model="networkRow.key" type="text" class="no-bg domain-input" placeholder="Network name..." />
|
<input v-model="networkRow.key" type="text" class="no-bg domain-input" :placeholder="$t(`Network name...`)" />
|
||||||
<font-awesome-icon icon="times" class="action remove ms-2 me-3 text-danger" @click="remove(index)" />
|
<font-awesome-icon icon="times" class="action remove ms-2 me-3 text-danger" @click="remove(index)" />
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -65,6 +65,10 @@ export default {
|
|||||||
editorFocus() {
|
editorFocus() {
|
||||||
return this.$parent.$parent.editorFocus;
|
return this.$parent.$parent.editorFocus;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
endpoint() {
|
||||||
|
return this.$parent.$parent.endpoint;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
"jsonConfig.networks": {
|
"jsonConfig.networks": {
|
||||||
@ -134,7 +138,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
loadExternalNetworkList() {
|
loadExternalNetworkList() {
|
||||||
this.$root.getSocket().emit("getDockerNetworkList", (res) => {
|
this.$root.emitAgent(this.endpoint, "getDockerNetworkList", (res) => {
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
this.externalNetworkList = res.dockerNetworkList.filter((n) => {
|
this.externalNetworkList = res.dockerNetworkList.filter((n) => {
|
||||||
// Filter out this stack networks
|
// Filter out this stack networks
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div ref="stackList" class="stack-list" :class="{ scrollbar: scrollbar }" :style="stackListStyle">
|
<div ref="stackList" class="stack-list" :class="{ scrollbar: scrollbar }" :style="stackListStyle">
|
||||||
<div v-if="Object.keys($root.stackList).length === 0" class="text-center mt-3">
|
<div v-if="Object.keys(sortedStackList).length === 0" class="text-center mt-3">
|
||||||
<router-link to="/compose">{{ $t("addFirstStackMsg") }}</router-link>
|
<router-link to="/compose">{{ $t("addFirstStackMsg") }}</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -67,7 +67,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import Confirm from "../components/Confirm.vue";
|
import Confirm from "../components/Confirm.vue";
|
||||||
import StackListItem from "../components/StackListItem.vue";
|
import StackListItem from "../components/StackListItem.vue";
|
||||||
import { CREATED_FILE, CREATED_STACK, EXITED, RUNNING, UNKNOWN } from "../../../backend/util-common";
|
import { CREATED_FILE, CREATED_STACK, EXITED, RUNNING, UNKNOWN } from "../../../common/util-common";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -120,7 +120,7 @@ export default {
|
|||||||
* @returns {Array} The sorted list of stacks.
|
* @returns {Array} The sorted list of stacks.
|
||||||
*/
|
*/
|
||||||
sortedStackList() {
|
sortedStackList() {
|
||||||
let result = Object.values(this.$root.stackList);
|
let result = Object.values(this.$root.completeStackList);
|
||||||
|
|
||||||
result = result.filter(stack => {
|
result = result.filter(stack => {
|
||||||
// filter by search text
|
// filter by search text
|
||||||
@ -160,6 +160,7 @@ export default {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sort by status
|
||||||
if (m1.status !== m2.status) {
|
if (m1.status !== m2.status) {
|
||||||
if (m2.status === RUNNING) {
|
if (m2.status === RUNNING) {
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<router-link :to="`/compose/${stack.name}`" :class="{ 'dim' : !stack.isManagedByDockge }" class="item">
|
<router-link :to="url" :class="{ 'dim' : !stack.isManagedByDockge }" class="item">
|
||||||
<Uptime :stack="stack" :fixed-width="true" class="me-2" />
|
<Uptime :stack="stack" :fixed-width="true" class="me-2" />
|
||||||
<span class="title">{{ stackName }}</span>
|
<div class="title">
|
||||||
|
<span>{{ stackName }}</span>
|
||||||
|
<div v-if="$root.agentCount > 1" class="endpoint">{{ endpointDisplay }}</div>
|
||||||
|
</div>
|
||||||
</router-link>
|
</router-link>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import Uptime from "./Uptime.vue";
|
import Uptime from "./Uptime.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -51,6 +53,16 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
endpointDisplay() {
|
||||||
|
return this.$root.endpointDisplayFunction(this.stack.endpoint);
|
||||||
|
},
|
||||||
|
url() {
|
||||||
|
if (this.stack.endpoint) {
|
||||||
|
return `/compose/${this.stack.name}/${this.stack.endpoint}`;
|
||||||
|
} else {
|
||||||
|
return `/compose/${this.stack.name}`;
|
||||||
|
}
|
||||||
|
},
|
||||||
depthMargin() {
|
depthMargin() {
|
||||||
return {
|
return {
|
||||||
marginLeft: `${31 * this.depth}px`,
|
marginLeft: `${31 * this.depth}px`,
|
||||||
@ -117,16 +129,31 @@ export default {
|
|||||||
padding-right: 2px !important;
|
padding-right: 2px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
// .stack-item {
|
.item {
|
||||||
// width: 100%;
|
text-decoration: none;
|
||||||
// }
|
|
||||||
|
|
||||||
.tags {
|
|
||||||
margin-top: 4px;
|
|
||||||
padding-left: 67px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
align-items: center;
|
||||||
gap: 0;
|
min-height: 52px;
|
||||||
|
border-radius: 10px;
|
||||||
|
transition: all ease-in-out 0.15s;
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px 8px;
|
||||||
|
&.disabled {
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
background-color: $highlight-white;
|
||||||
|
}
|
||||||
|
&.active {
|
||||||
|
background-color: #cdf8f4;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
margin-top: -4px;
|
||||||
|
}
|
||||||
|
.endpoint {
|
||||||
|
font-size: 12px;
|
||||||
|
color: $dark-font-color3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.collapsed {
|
.collapsed {
|
||||||
|
@ -7,8 +7,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { Terminal } from "@xterm/xterm";
|
import { Terminal } from "@xterm/xterm";
|
||||||
import { FitAddon } from "@xterm/addon-fit";
|
import { FitAddon } from "@xterm/addon-fit";
|
||||||
import { WebLinksAddon } from "xterm-addon-web-links";
|
import { TERMINAL_COLS, TERMINAL_ROWS } from "../../../common/util-common";
|
||||||
import { TERMINAL_COLS, TERMINAL_ROWS } from "../../../backend/util-common";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
/**
|
/**
|
||||||
@ -24,6 +23,11 @@ export default {
|
|||||||
require: true,
|
require: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
endpoint: {
|
||||||
|
type: String,
|
||||||
|
require: true,
|
||||||
|
},
|
||||||
|
|
||||||
// Require if mode is interactive
|
// Require if mode is interactive
|
||||||
stackName: {
|
stackName: {
|
||||||
type: String,
|
type: String,
|
||||||
@ -110,14 +114,14 @@ export default {
|
|||||||
|
|
||||||
// Create a new Terminal
|
// Create a new Terminal
|
||||||
if (this.mode === "mainTerminal") {
|
if (this.mode === "mainTerminal") {
|
||||||
this.$root.getSocket().emit("mainTerminal", this.name, (res) => {
|
this.$root.emitAgent(this.endpoint, "mainTerminal", this.name, (res) => {
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
this.$root.toastRes(res);
|
this.$root.toastRes(res);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (this.mode === "interactive") {
|
} else if (this.mode === "interactive") {
|
||||||
console.debug("Create Interactive terminal:", this.name);
|
console.debug("Create Interactive terminal:", this.name);
|
||||||
this.$root.getSocket().emit("interactiveTerminal", this.stackName, this.serviceName, this.shell, (res) => {
|
this.$root.emitAgent(this.endpoint, "interactiveTerminal", this.stackName, this.serviceName, this.shell, (res) => {
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
this.$root.toastRes(res);
|
this.$root.toastRes(res);
|
||||||
}
|
}
|
||||||
@ -134,15 +138,15 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
bind(name) {
|
bind(endpoint, name) {
|
||||||
// Workaround: normally this.name should be set, but it is not sometimes, so we use the parameter, but eventually this.name and name must be the same name
|
// Workaround: normally this.name should be set, but it is not sometimes, so we use the parameter, but eventually this.name and name must be the same name
|
||||||
if (name) {
|
if (name) {
|
||||||
this.$root.unbindTerminal(name);
|
this.$root.unbindTerminal(name);
|
||||||
this.$root.bindTerminal(name, this.terminal);
|
this.$root.bindTerminal(endpoint, name, this.terminal);
|
||||||
console.debug("Terminal bound via parameter: " + name);
|
console.debug("Terminal bound via parameter: " + name);
|
||||||
} else if (this.name) {
|
} else if (this.name) {
|
||||||
this.$root.unbindTerminal(this.name);
|
this.$root.unbindTerminal(this.name);
|
||||||
this.$root.bindTerminal(this.name, this.terminal);
|
this.$root.bindTerminal(this.endpoint, this.name, this.terminal);
|
||||||
console.debug("Terminal bound: " + this.name);
|
console.debug("Terminal bound: " + this.name);
|
||||||
} else {
|
} else {
|
||||||
console.debug("Terminal name not set");
|
console.debug("Terminal name not set");
|
||||||
@ -173,7 +177,7 @@ export default {
|
|||||||
// Remove the input from the terminal
|
// Remove the input from the terminal
|
||||||
this.removeInput();
|
this.removeInput();
|
||||||
|
|
||||||
this.$root.getSocket().emit("terminalInput", this.name, buffer + e.key, (err) => {
|
this.$root.emitAgent(this.endpoint, "terminalInput", this.name, buffer + e.key, (err) => {
|
||||||
this.$root.toastError(err.msg);
|
this.$root.toastError(err.msg);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -192,12 +196,11 @@ export default {
|
|||||||
// TODO
|
// TODO
|
||||||
} else if (e.key === "\u0003") { // Ctrl + C
|
} else if (e.key === "\u0003") { // Ctrl + C
|
||||||
console.debug("Ctrl + C");
|
console.debug("Ctrl + C");
|
||||||
this.$root.getSocket().emit("terminalInput", this.name, e.key);
|
this.$root.emitAgent(this.endpoint, "terminalInput", this.name, e.key);
|
||||||
this.removeInput();
|
this.removeInput();
|
||||||
} else {
|
} else {
|
||||||
this.cursorPosition++;
|
this.cursorPosition++;
|
||||||
this.terminalInputBuffer += e.key;
|
this.terminalInputBuffer += e.key;
|
||||||
console.log(this.terminalInputBuffer);
|
|
||||||
this.terminal.write(e.key);
|
this.terminal.write(e.key);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -205,7 +208,7 @@ export default {
|
|||||||
|
|
||||||
interactiveTerminalConfig() {
|
interactiveTerminalConfig() {
|
||||||
this.terminal.onKey(e => {
|
this.terminal.onKey(e => {
|
||||||
this.$root.getSocket().emit("terminalInput", this.name, e.key, (res) => {
|
this.$root.emitAgent(this.endpoint, "terminalInput", this.name, e.key, (res) => {
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
this.$root.toastRes(res);
|
this.$root.toastRes(res);
|
||||||
}
|
}
|
||||||
@ -234,7 +237,7 @@ export default {
|
|||||||
this.terminalFitAddOn.fit();
|
this.terminalFitAddOn.fit();
|
||||||
let rows = this.terminal.rows;
|
let rows = this.terminal.rows;
|
||||||
let cols = this.terminal.cols;
|
let cols = this.terminal.cols;
|
||||||
this.$root.getSocket().emit("terminalResize", this.name, rows, cols);
|
this.$root.emitAgent(this.endpoint, "terminalResize", this.name, rows, cols);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -243,13 +246,12 @@ export default {
|
|||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.main-terminal {
|
.main-terminal {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow-x: scroll;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.terminal {
|
.terminal {
|
||||||
padding: 10px 15px;
|
background-color: black !important;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { statusColor, statusNameShort } from "../../../backend/util-common";
|
import { statusColor, statusNameShort } from "../../../common/util-common";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
<input
|
<input
|
||||||
v-model="settings.primaryHostname"
|
v-model="settings.primaryHostname"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
placeholder="(Unset: Follow current hostname)"
|
:placeholder="$t(`CurrentHostname`)"
|
||||||
/>
|
/>
|
||||||
<button class="btn btn-outline-primary" type="button" @click="autoGetPrimaryHostname">
|
<button class="btn btn-outline-primary" type="button" @click="autoGetPrimaryHostname">
|
||||||
{{ $t("autoGet") }}
|
{{ $t("autoGet") }}
|
||||||
|
@ -27,6 +27,11 @@ const languageList = {
|
|||||||
"ja": "日本語",
|
"ja": "日本語",
|
||||||
"nl": "Nederlands",
|
"nl": "Nederlands",
|
||||||
"ro": "Română",
|
"ro": "Română",
|
||||||
|
"id": "Bahasa Indonesia (Indonesian)",
|
||||||
|
"vi": "Tiếng Việt",
|
||||||
|
"hu": "Magyar",
|
||||||
|
"ca": "Català",
|
||||||
|
"ga": "Gaeilge",
|
||||||
};
|
};
|
||||||
|
|
||||||
let messages = {
|
let messages = {
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
"restartPolicyAlways": "دائماً",
|
"restartPolicyAlways": "دائماً",
|
||||||
"restartPolicyOnFailure": "عند الفشل",
|
"restartPolicyOnFailure": "عند الفشل",
|
||||||
"restartPolicyNo": "لا",
|
"restartPolicyNo": "لا",
|
||||||
"environmentVariable": "متغير البيئة | متغيرات البيئة",
|
"environmentVariable": "متغير | متغيرات",
|
||||||
"restartPolicy": "سياسة إعادة التشغيل",
|
"restartPolicy": "سياسة إعادة التشغيل",
|
||||||
"containerName": "اسم الحاوية",
|
"containerName": "اسم الحاوية",
|
||||||
"port": "منفذ | منافذ",
|
"port": "منفذ | منافذ",
|
||||||
@ -98,5 +98,16 @@
|
|||||||
"url": "رابط | روابط",
|
"url": "رابط | روابط",
|
||||||
"extra": "إضافات",
|
"extra": "إضافات",
|
||||||
"reverseProxyMsg1": "هل تستدخم خادم عكسي؟",
|
"reverseProxyMsg1": "هل تستدخم خادم عكسي؟",
|
||||||
"connecting...": "جاري الاتصال بخادم المقبس…"
|
"connecting...": "جاري الاتصال بخادم المقبس…",
|
||||||
|
"newUpdate": "تحديث جديد",
|
||||||
|
"currentEndpoint": "السياق: الوكيل الحالي",
|
||||||
|
"dockgeURL": "رابط Dockge (مثلا http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "متصل",
|
||||||
|
"agentOffline": "غير متصل",
|
||||||
|
"connecting": "جاري الإتصال",
|
||||||
|
"connect": "ارتبط",
|
||||||
|
"dockgeAgent": "سيرفر Dockge",
|
||||||
|
"removeAgent": "حذف الوكيل",
|
||||||
|
"removeAgentMsg": "هل انت متأكد من حذف هذا الوكيل؟",
|
||||||
|
"LongSyntaxNotSupported": "كتابة النصوص المدعومة غير المدعومة هنا. الرجاء استخدام محرر YAML."
|
||||||
}
|
}
|
||||||
|
116
frontend/src/lang/be.json
Normal file
116
frontend/src/lang/be.json
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
{
|
||||||
|
"active": "акт.",
|
||||||
|
"LongSyntaxNotSupported": "Доўгі сінтаксіс тут не падтрымліваецца. Выкарыстоўвайце рэдактар YAML.",
|
||||||
|
"removeAgentMsg": "Вы ўпэўнены, што хочаце выдаліць гэтага агента?",
|
||||||
|
"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": "Запусціць",
|
||||||
|
"downStack": "Спыніць і дэактываваць",
|
||||||
|
"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>?",
|
||||||
|
"Show update if available": "Паказаць абнаўленне, калі яно даступна",
|
||||||
|
"Also check beta release": "Атрымліваць бэта-версіі",
|
||||||
|
"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",
|
||||||
|
"Remember me": "Запомніць мяне",
|
||||||
|
"Login": "Лагін",
|
||||||
|
"Username": "Імя карыстальніка",
|
||||||
|
"Password": "Пароль",
|
||||||
|
"Settings": "Налады",
|
||||||
|
"Logout": "Выйсці",
|
||||||
|
"Lowercase only": "Толькі ніжні рэгістр",
|
||||||
|
"Convert to Compose": "Пераўтварыць у Compose",
|
||||||
|
"Docker Run": "Docker Run",
|
||||||
|
"exited": "спын.",
|
||||||
|
"inactive": "неакт.",
|
||||||
|
"Appearance": "Знешні выгляд",
|
||||||
|
"Security": "Бяспека",
|
||||||
|
"About": "Аб праграме",
|
||||||
|
"Allowed commands:": "Дазволеныя каманды:",
|
||||||
|
"Internal Networks": "Унутраныя сеткі",
|
||||||
|
"External Networks": "Знешнія сеткі",
|
||||||
|
"No External Networks": "Няма знешніх сетак",
|
||||||
|
"reverseProxyMsg1": "Выкарыстоўваеце зваротны проксі?",
|
||||||
|
"reverseProxyMsg2": "Праверце, як наладзіць яго для WebSocket",
|
||||||
|
"Cannot connect to the socket server.": "Не ўдалося падключыцца да сокет-сервера.",
|
||||||
|
"reconnecting...": "Перападключэнне…",
|
||||||
|
"connecting...": "Падключэнне да сокет-сервера…",
|
||||||
|
"url": "URL-адрас | URL-адрасы",
|
||||||
|
"extra": "Дадаткова",
|
||||||
|
"newUpdate": "Даступна абнаўленне",
|
||||||
|
"dockgeAgent": "Агент Dockge | Агенты Dockge",
|
||||||
|
"currentEndpoint": "Бягучы",
|
||||||
|
"dockgeURL": "URL-адрас Dockge (напрыклад: http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "У сетцы",
|
||||||
|
"agentOffline": "Не ў сетцы",
|
||||||
|
"connecting": "Падключэнне",
|
||||||
|
"connect": "Падключыць",
|
||||||
|
"addAgent": "Дадаць Агента",
|
||||||
|
"agentAddedSuccessfully": "Агент паспяхова дададзены.",
|
||||||
|
"agentRemovedSuccessfully": "Агент паспяхова выдалены.",
|
||||||
|
"removeAgent": "Выдаліць агента"
|
||||||
|
}
|
@ -92,11 +92,41 @@
|
|||||||
"External Networks": "Външни мрежи",
|
"External Networks": "Външни мрежи",
|
||||||
"No External Networks": "Не са налични външни мрежи",
|
"No External Networks": "Не са налични външни мрежи",
|
||||||
"reverseProxyMsg2": "Проверете как да го конфигурирате за WebSocket",
|
"reverseProxyMsg2": "Проверете как да го конфигурирате за WebSocket",
|
||||||
"downStack": "Спри и изключи",
|
"downStack": "Спри & Неактивен",
|
||||||
"reverseProxyMsg1": "Използвате ревърс прокси?",
|
"reverseProxyMsg1": "Използвате ревърс прокси?",
|
||||||
"Cannot connect to the socket server.": "Не може да се свърже със сокет сървъра.",
|
"Cannot connect to the socket server.": "Не може да се свърже със сокет сървъра.",
|
||||||
"url": "URL адрес | URL адреси",
|
"url": "URL адрес | URL адреси",
|
||||||
"extra": "Допълнително",
|
"extra": "Допълнително",
|
||||||
"reconnecting...": "Повторно свързване…",
|
"reconnecting...": "Повторно свързване…",
|
||||||
"connecting...": "Свързване със сокет сървъра…"
|
"connecting...": "Свързване със сокет сървъра…",
|
||||||
|
"newUpdate": "Нова актуализация",
|
||||||
|
"currentEndpoint": "Текущ",
|
||||||
|
"dockgeURL": "Dockge URL адрес (напр. http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "Онлайн",
|
||||||
|
"agentOffline": "Офлайн",
|
||||||
|
"connect": "Свържи",
|
||||||
|
"addAgent": "Добави агент",
|
||||||
|
"agentAddedSuccessfully": "Агентът е добавен успешно.",
|
||||||
|
"removeAgent": "Премахни агент",
|
||||||
|
"removeAgentMsg": "Сигурни ли сте, че желаете да премахнете този агент?",
|
||||||
|
"dockgeAgent": "Dockge агент | Dockge агенти",
|
||||||
|
"connecting": "Свързване",
|
||||||
|
"agentRemovedSuccessfully": "Агентът е премахнат успешно.",
|
||||||
|
"LongSyntaxNotSupported": "Дългият синтаксис не се поддържа тук. Моля, използвайте YAML редактора.",
|
||||||
|
"Started": "Стартиран",
|
||||||
|
"Updated": "Актуализиран",
|
||||||
|
"Deleted": "Изтрит",
|
||||||
|
"Deployed": "Внедрен",
|
||||||
|
"Stopped": "Спрян",
|
||||||
|
"Restarted": "Рестартиран",
|
||||||
|
"Switch to sh": "Превключи на \"sh\"",
|
||||||
|
"terminal": "Терминал",
|
||||||
|
"New Container Name...": "Ново име на контейнер...",
|
||||||
|
"Network name...": "Име на мрежата...",
|
||||||
|
"Select a network...": "Изберете мрежа...",
|
||||||
|
"Lost connection to the socket server. Reconnecting...": "Изгубена връзка със сокет сървъра. Повторно свързване...",
|
||||||
|
"Saved": "Запазено",
|
||||||
|
"Downed": "Свален",
|
||||||
|
"CurrentHostname": "(Не е зададено: Следвай текущото име на хост)",
|
||||||
|
"NoNetworksAvailable": "Няма налични мрежи. Първо трябва да добавите вътрешни мрежи или да активирате външни мрежи в дясната страна."
|
||||||
}
|
}
|
||||||
|
116
frontend/src/lang/ca.json
Normal file
116
frontend/src/lang/ca.json
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
{
|
||||||
|
"Create your admin account": "Crea el teu compte d'administrador",
|
||||||
|
"Repeat Password": "Repeteix la contrasenya",
|
||||||
|
"Create": "Crea",
|
||||||
|
"signedInDisp": "S'ha iniciat sessió com a {0}",
|
||||||
|
"home": "Inici",
|
||||||
|
"console": "Consola",
|
||||||
|
"registry": "Registre",
|
||||||
|
"compose": "Compondre",
|
||||||
|
"addFirstStackMsg": "Compondre la teva primera pila!",
|
||||||
|
"stackName": "Nom de la pila",
|
||||||
|
"deployStack": "Desplegar",
|
||||||
|
"deleteStack": "Eliminar",
|
||||||
|
"stopStack": "Aturar",
|
||||||
|
"restartStack": "Reiniciar",
|
||||||
|
"updateStack": "Actualitzar",
|
||||||
|
"startStack": "Inicia",
|
||||||
|
"downStack": "Atura i inactiva",
|
||||||
|
"languageName": "Català",
|
||||||
|
"authIncorrectCreds": "Usuari o contrasenya incorrecte.",
|
||||||
|
"PasswordsDoNotMatch": "Les contrasenyes no coincideixen.",
|
||||||
|
"signedInDispDisabled": "Autenticació deshabilitada.",
|
||||||
|
"discardStack": "Descartar",
|
||||||
|
"saveStackDraft": "Guardar",
|
||||||
|
"notAvailableShort": "N/D",
|
||||||
|
"primaryHostname": "Nom del host primari",
|
||||||
|
"general": "General",
|
||||||
|
"container": "Contenidor | Contenidors",
|
||||||
|
"scanFolder": "Escaneja la carpeta de piles",
|
||||||
|
"dockerImage": "Imatge",
|
||||||
|
"restartPolicyAlways": "Sempre",
|
||||||
|
"restartPolicyOnFailure": "En cas de fallada",
|
||||||
|
"restartPolicyNo": "No",
|
||||||
|
"environmentVariable": "Variable d'entorn | Variables d'entorn",
|
||||||
|
"restartPolicy": "Política de reinici",
|
||||||
|
"containerName": "Nom del contenidor",
|
||||||
|
"port": "Port | Ports",
|
||||||
|
"volume": "Volum | Volums",
|
||||||
|
"network": "Xarxa | Xarxes",
|
||||||
|
"addListItem": "Afegir {0}",
|
||||||
|
"deleteContainer": "Eliminar",
|
||||||
|
"addContainer": "Afegir contenidor",
|
||||||
|
"addNetwork": "Afegir xarxa",
|
||||||
|
"passwordNotMatchMsg": "La contrasenya repetida no coincideix.",
|
||||||
|
"autoGet": "Obtenir automàticament",
|
||||||
|
"add": "Afegir",
|
||||||
|
"Edit": "Editar",
|
||||||
|
"applyToYAML": "Aplicar a YAML",
|
||||||
|
"createExternalNetwork": "Crear",
|
||||||
|
"addInternalNetwork": "Afegir",
|
||||||
|
"Save": "Guardar",
|
||||||
|
"Language": "Idioma",
|
||||||
|
"Current User": "Usuari actual",
|
||||||
|
"Change Password": "Canviar la contrasenya",
|
||||||
|
"Current Password": "Contrasenya actual",
|
||||||
|
"New Password": "Nova contrasenya",
|
||||||
|
"stackNotManagedByDockgeMsg": "Aquesta pila no està gestionada per Dockge.",
|
||||||
|
"Update Password": "Actualitzar contrasenya",
|
||||||
|
"Advanced": "Avançat",
|
||||||
|
"Disable Auth": "Deshabilitar autenticació",
|
||||||
|
"Leave": "Sortir",
|
||||||
|
"Frontend Version": "Versió del frontend",
|
||||||
|
"Check Update On GitHub": "Comprova les actualitzacions a GitHub",
|
||||||
|
"Show update if available": "Mostra si hi ha disponible una nova actualització",
|
||||||
|
"Also check beta release": "Comprovar també la versió beta",
|
||||||
|
"Remember me": "Recorda'm",
|
||||||
|
"Login": "Inici de sesió",
|
||||||
|
"Username": "Usuari",
|
||||||
|
"Settings": "Configuració",
|
||||||
|
"Logout": "Tanca sessió",
|
||||||
|
"Lowercase only": "Només minúscules",
|
||||||
|
"Convert to Compose": "Convertir a Compose",
|
||||||
|
"Docker Run": "Executar Docker",
|
||||||
|
"active": "actiu",
|
||||||
|
"exited": "finalitzat",
|
||||||
|
"inactive": "inactiu",
|
||||||
|
"Appearance": "Aparença",
|
||||||
|
"Security": "Seguretat",
|
||||||
|
"About": "Sobre",
|
||||||
|
"Allowed commands:": "Comandes permeses:",
|
||||||
|
"Internal Networks": "Xarxes internes",
|
||||||
|
"External Networks": "Xarxes externes",
|
||||||
|
"No External Networks": "No hi ha xarxes externes",
|
||||||
|
"reverseProxyMsg1": "Estàs fent servir un proxy invers?",
|
||||||
|
"reverseProxyMsg2": "Comproveu com configurar-lo per a WebSocket",
|
||||||
|
"Cannot connect to the socket server.": "No es pot connectar al servidor del socket.",
|
||||||
|
"reconnecting...": "S'està tornant a connectar…",
|
||||||
|
"connecting...": "S'està connectant al servidor del socket…",
|
||||||
|
"url": "URL | URLs",
|
||||||
|
"extra": "Extra",
|
||||||
|
"newUpdate": "Nova actualització",
|
||||||
|
"dockgeAgent": "Agent Dockge | Agents Dockge",
|
||||||
|
"currentEndpoint": "Actual",
|
||||||
|
"dockgeURL": "URL de Dockge (ex. http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "En línia",
|
||||||
|
"agentOffline": "Fora de línia",
|
||||||
|
"connecting": "Connectant",
|
||||||
|
"connect": "Connectar",
|
||||||
|
"addAgent": "Afegir agent",
|
||||||
|
"agentAddedSuccessfully": "Agent afegit correctament.",
|
||||||
|
"agentRemovedSuccessfully": "Agent eliminat correctament.",
|
||||||
|
"removeAgent": "Eliminar agent",
|
||||||
|
"removeAgentMsg": "Esteu segur que voleu eliminar aquest agent?",
|
||||||
|
"editStack": "Editar",
|
||||||
|
"deleteStackMsg": "Estàs segur que vols eliminar aquesta pila?",
|
||||||
|
"restartPolicyUnlessStopped": "A menys que s'aturi",
|
||||||
|
"dependsOn": "Dependència del contenidor | Dependències del contenidor",
|
||||||
|
"disableauth.message1": "Esteu segur que voleu <strong>desactivar l'autenticació</strong>?",
|
||||||
|
"disableauth.message2": "Està dissenyat per a escenaris <strong>on voleu implementar l'autenticació de tercers</strong> per davant de Dockge, com ara Cloudflare Access, Authelia o altres mecanismes d'autenticació.",
|
||||||
|
"Repeat New Password": "Repetiu la nova contrasenya",
|
||||||
|
"Please use this option carefully!": "Si us plau, utilitzeu aquesta opció amb cura!",
|
||||||
|
"Enable Auth": "Habilitar autenticació",
|
||||||
|
"I understand, please disable": "Ho entenc, si us plau deshabilita",
|
||||||
|
"Password": "Contrasenya",
|
||||||
|
"LongSyntaxNotSupported": "La sintaxi llarga no està suportada aquí. Si us plau, fes servir l'editor YAML."
|
||||||
|
}
|
@ -19,7 +19,7 @@
|
|||||||
"restartStack": "Restartovat",
|
"restartStack": "Restartovat",
|
||||||
"updateStack": "Aktualizovat",
|
"updateStack": "Aktualizovat",
|
||||||
"startStack": "Spustit",
|
"startStack": "Spustit",
|
||||||
"downStack": "Zastavit & Vypnout",
|
"downStack": "Zastavit & Zneaktivnit",
|
||||||
"editStack": "Upravit",
|
"editStack": "Upravit",
|
||||||
"discardStack": "Zahodit",
|
"discardStack": "Zahodit",
|
||||||
"saveStackDraft": "Uložit",
|
"saveStackDraft": "Uložit",
|
||||||
@ -97,5 +97,33 @@
|
|||||||
"extra": "Extra",
|
"extra": "Extra",
|
||||||
"reverseProxyMsg1": "Používáte Reverzní proxy server?",
|
"reverseProxyMsg1": "Používáte Reverzní proxy server?",
|
||||||
"reverseProxyMsg2": "Podívat se jak to nastavit pro WebSocket",
|
"reverseProxyMsg2": "Podívat se jak to nastavit pro WebSocket",
|
||||||
"Cannot connect to the socket server.": "Nelze se připojit k serveru ."
|
"Cannot connect to the socket server.": "Nelze se připojit k serveru .",
|
||||||
|
"Lost connection to the socket server. Reconnecting...": "Ztraceno spojení se serverem. Obnovuji spojení...",
|
||||||
|
"newUpdate": "Nová aktualizace",
|
||||||
|
"dockgeAgent": "Dockge Agent | Dockge Agenti",
|
||||||
|
"agentOnline": "Online",
|
||||||
|
"connecting": "Připojování",
|
||||||
|
"agentOffline": "Offline",
|
||||||
|
"dockgeURL": "Dockge URL (např. http://127.0.0.1:5001)",
|
||||||
|
"LongSyntaxNotSupported": "Dlouhá syntaxe zde není podporována. Použijte, prosím, YAML editor.",
|
||||||
|
"connecting...": "Připojování k socket serveru…",
|
||||||
|
"connect": "Připojit",
|
||||||
|
"addAgent": "Přidat Agenta",
|
||||||
|
"agentAddedSuccessfully": "Agent byl úspěšně přidán.",
|
||||||
|
"agentRemovedSuccessfully": "Agend byl úspěšně odebrán.",
|
||||||
|
"removeAgent": "Odebrat Agenta",
|
||||||
|
"removeAgentMsg": "Opravdu chcete tohoto agenta odebrat?",
|
||||||
|
"Saved": "Uloženo",
|
||||||
|
"Deployed": "Nasazeno",
|
||||||
|
"Deleted": "Odstraněno",
|
||||||
|
"Updated": "Aktualizovat",
|
||||||
|
"Started": "Spuštěno",
|
||||||
|
"Stopped": "Zastaveno",
|
||||||
|
"Restarted": "Restartováno",
|
||||||
|
"Switch to sh": "Přepnout na sh shell",
|
||||||
|
"terminal": "Terminál",
|
||||||
|
"New Container Name...": "Název nového kontejneru...",
|
||||||
|
"Network name...": "Název sítě...",
|
||||||
|
"Select a network...": "Vyberte síť...",
|
||||||
|
"NoNetworksAvailable": "Žádná síť není dostupná. Musíte přidat interní síť nebo povolit externí sítě v pravé části."
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,32 @@
|
|||||||
{
|
{
|
||||||
"languageName": "Dansk",
|
"languageName": "Dansk",
|
||||||
"authIncorrectCreds": "Forkert brugernavn eller adgangskode.",
|
"authIncorrectCreds": "Forkert brugernavn eller adgangskode.",
|
||||||
"PasswordsDoNotMatch": "Adgangskode stemmer ikke overens.",
|
"PasswordsDoNotMatch": "Adgangskoder stemmer ikke overens.",
|
||||||
"Repeat Password": "Gentag adgangskode",
|
"Repeat Password": "Gentag adgangskode",
|
||||||
"Create": "Opret",
|
"Create": "Opret",
|
||||||
"signedInDisp": "Logget ind som {0}",
|
"signedInDisp": "Logget ind som {0}",
|
||||||
"signedInDispDisabled": "Auth Deaktiveret.",
|
"signedInDispDisabled": "Godkendelse deaktiveret.",
|
||||||
"home": "Hjem",
|
"home": "Hjem",
|
||||||
"console": "Konsol",
|
"console": "Konsol",
|
||||||
"registry": "Registry",
|
"registry": "Register",
|
||||||
"compose": "Compose",
|
"compose": "Komponer",
|
||||||
"stackName": "Stack-navn",
|
"stackName": "Stak-navn",
|
||||||
"deployStack": "Udrulle",
|
"deployStack": "Udrulle",
|
||||||
"deleteStack": "Slet",
|
"deleteStack": "Slet",
|
||||||
"stopStack": "Stop",
|
"stopStack": "Stop",
|
||||||
"restartStack": "Genstart",
|
"restartStack": "Genstart",
|
||||||
"updateStack": "Opdatere",
|
"updateStack": "Opdater",
|
||||||
"startStack": "Start",
|
"startStack": "Start",
|
||||||
"downStack": "Stop & Sluk",
|
"downStack": "Stop & Deaktiver",
|
||||||
"editStack": "Editere",
|
"editStack": "Rediger",
|
||||||
"discardStack": "Annuller",
|
"discardStack": "Kassér",
|
||||||
"saveStackDraft": "Gem",
|
"saveStackDraft": "Gem",
|
||||||
"notAvailableShort": "Ugyldig",
|
"notAvailableShort": "N/A",
|
||||||
"stackNotManagedByDockgeMsg": "Denne stack administreres ikke af Dockge.",
|
"stackNotManagedByDockgeMsg": "Denne stak administreres ikke af Dockge.",
|
||||||
"primaryHostname": "Primært værtsnavn",
|
"primaryHostname": "Primært værtsnavn",
|
||||||
"general": "Generelt",
|
"general": "Generelt",
|
||||||
"container": "Container | Containere",
|
"container": "Container | Containere",
|
||||||
"scanFolder": "Scan Stack-mappe",
|
"scanFolder": "Scan Stak-mappe",
|
||||||
"dockerImage": "Billede",
|
"dockerImage": "Billede",
|
||||||
"restartPolicyUnlessStopped": "Medmindre stoppet",
|
"restartPolicyUnlessStopped": "Medmindre stoppet",
|
||||||
"restartPolicyAlways": "Altid",
|
"restartPolicyAlways": "Altid",
|
||||||
@ -37,7 +37,7 @@
|
|||||||
"port": "Port | Porte",
|
"port": "Port | Porte",
|
||||||
"volume": "Volumen | Voluminer",
|
"volume": "Volumen | Voluminer",
|
||||||
"network": "Netværk | Netværker",
|
"network": "Netværk | Netværker",
|
||||||
"dependsOn": "Container Dependency | Container Dependencies",
|
"dependsOn": "Containerafhængighed | Containerafhængigheder",
|
||||||
"addListItem": "Tilføj {0}",
|
"addListItem": "Tilføj {0}",
|
||||||
"deleteContainer": "Slet",
|
"deleteContainer": "Slet",
|
||||||
"addNetwork": "Tilføj Netværk",
|
"addNetwork": "Tilføj Netværk",
|
||||||
@ -46,7 +46,7 @@
|
|||||||
"add": "Tilføj",
|
"add": "Tilføj",
|
||||||
"Edit": "Redigere",
|
"Edit": "Redigere",
|
||||||
"applyToYAML": "Anvend til YAML",
|
"applyToYAML": "Anvend til YAML",
|
||||||
"createExternalNetwork": "Skabe",
|
"createExternalNetwork": "Skab",
|
||||||
"addInternalNetwork": "Tilføj",
|
"addInternalNetwork": "Tilføj",
|
||||||
"Save": "Gem",
|
"Save": "Gem",
|
||||||
"Language": "Sprog",
|
"Language": "Sprog",
|
||||||
@ -58,11 +58,11 @@
|
|||||||
"Update Password": "Opdater adgangskode",
|
"Update Password": "Opdater adgangskode",
|
||||||
"Advanced": "Avanceret",
|
"Advanced": "Avanceret",
|
||||||
"Please use this option carefully!": "Brug venligst denne indstilling forsigtigt!",
|
"Please use this option carefully!": "Brug venligst denne indstilling forsigtigt!",
|
||||||
"Enable Auth": "Aktiver Auth",
|
"Enable Auth": "Aktiver godkendelse",
|
||||||
"Disable Auth": "Deaktiver Auth",
|
"Disable Auth": "Deaktiver godkendelse",
|
||||||
"I understand, please disable": "Jeg forstår, venligst deaktiver",
|
"I understand, please disable": "Jeg forstår, venligst deaktiver",
|
||||||
"Leave": "Forlad",
|
"Leave": "Forlad",
|
||||||
"Frontend Version": "Frontend Version",
|
"Frontend Version": "Version",
|
||||||
"Check Update On GitHub": "Tjek opdatering på GitHub",
|
"Check Update On GitHub": "Tjek opdatering på GitHub",
|
||||||
"Also check beta release": "Tjek også betaversionen",
|
"Also check beta release": "Tjek også betaversionen",
|
||||||
"Remember me": "Husk mig",
|
"Remember me": "Husk mig",
|
||||||
@ -72,7 +72,7 @@
|
|||||||
"Settings": "Indstillinger",
|
"Settings": "Indstillinger",
|
||||||
"Logout": "Log ud",
|
"Logout": "Log ud",
|
||||||
"Convert to Compose": "Konverter til Compose",
|
"Convert to Compose": "Konverter til Compose",
|
||||||
"active": "aktive",
|
"active": "aktiv",
|
||||||
"exited": "forladt",
|
"exited": "forladt",
|
||||||
"inactive": "inaktive",
|
"inactive": "inaktive",
|
||||||
"Appearance": "Udseende",
|
"Appearance": "Udseende",
|
||||||
@ -91,12 +91,36 @@
|
|||||||
"url": "URL | URL'er",
|
"url": "URL | URL'er",
|
||||||
"extra": "Ekstra",
|
"extra": "Ekstra",
|
||||||
"Create your admin account": "Opret din administratorkonto",
|
"Create your admin account": "Opret din administratorkonto",
|
||||||
"addFirstStackMsg": "Compose din første stack!",
|
"addFirstStackMsg": "Komponer din første stak!",
|
||||||
"deleteStackMsg": "Er du sikker på, at du vil slette denne stack?",
|
"deleteStackMsg": "Er du sikker på, at du vil slette denne stak?",
|
||||||
"environmentVariable": "Miljøvariabel | miljøvariabler",
|
"environmentVariable": "Miljøvariabel | miljøvariabler",
|
||||||
"addContainer": "Tilføj Container",
|
"addContainer": "Tilføj Container",
|
||||||
"disableauth.message1": "Er du sikker på, at du vil <strong>deaktivere godkendelse</strong>?",
|
"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.",
|
"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",
|
"Show update if available": "Vis opdatering, hvis tilgængelig",
|
||||||
"Lowercase only": "Kun små bogstaver"
|
"Lowercase only": "Kun små bogstaver",
|
||||||
|
"newUpdate": "Ny Opdatering",
|
||||||
|
"dockgeAgent": "Dockge Agent | Dockge Agenter",
|
||||||
|
"currentEndpoint": "Nuværende",
|
||||||
|
"dockgeURL": "Dockge URL (f.eks. http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "Online",
|
||||||
|
"agentOffline": "Offline",
|
||||||
|
"connecting": "Tilslutter",
|
||||||
|
"connect": "Tilslut",
|
||||||
|
"addAgent": "Tilføj agent",
|
||||||
|
"agentAddedSuccessfully": "Agent succesfuld tilføjet.",
|
||||||
|
"agentRemovedSuccessfully": "Agent succesfuld fjernet.",
|
||||||
|
"removeAgent": "Fjern agent",
|
||||||
|
"removeAgentMsg": "Er du sikker på at du vil fjerne denne agent?",
|
||||||
|
"LongSyntaxNotSupported": "Langt syntaks er ikke understøttet her. Forsøg venligst med YAML-editoren.",
|
||||||
|
"Saved": "Gemt",
|
||||||
|
"Deleted": "Slettet",
|
||||||
|
"Updated": "Opdateret",
|
||||||
|
"Started": "Startet",
|
||||||
|
"Stopped": "Stoppet",
|
||||||
|
"Restarted": "Genstartet",
|
||||||
|
"terminal": "Terminal",
|
||||||
|
"Network name...": "Netværksnavn...",
|
||||||
|
"Select a network...": "Vælg et netværk...",
|
||||||
|
"Deployed": "Udrullet"
|
||||||
}
|
}
|
||||||
|
@ -94,9 +94,39 @@
|
|||||||
"Cannot connect to the socket server.": "Keine Verbindung zum Socket Server.",
|
"Cannot connect to the socket server.": "Keine Verbindung zum Socket Server.",
|
||||||
"reverseProxyMsg1": "Wird ein Reverse Proxy genutzt?",
|
"reverseProxyMsg1": "Wird ein Reverse Proxy genutzt?",
|
||||||
"reconnecting...": "Erneuter Verbindungsaufbau…",
|
"reconnecting...": "Erneuter Verbindungsaufbau…",
|
||||||
"downStack": "Stoppen & Aus",
|
"downStack": "Stoppen & Deaktivieren",
|
||||||
"extra": "Extra",
|
"extra": "Extra",
|
||||||
"url": "URL / URLs",
|
"url": "URL / URLs",
|
||||||
"reverseProxyMsg2": "Lerne wie dieser für WebSockets zu konfigurieren ist.",
|
"reverseProxyMsg2": "Lerne wie dieser für WebSockets zu konfigurieren ist.",
|
||||||
"connecting...": "Verbindungsaufbau zum Socket Server…"
|
"connecting...": "Verbindungsaufbau zum Socket Server…",
|
||||||
|
"newUpdate": "Neues Update",
|
||||||
|
"dockgeAgent": "Dockge Agent | Dockge Agenten",
|
||||||
|
"currentEndpoint": "Aktuell",
|
||||||
|
"dockgeURL": "Dockge URL (z. B. http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "Online",
|
||||||
|
"agentOffline": "Offline",
|
||||||
|
"connecting": "Verbinden",
|
||||||
|
"connect": "Verbinden",
|
||||||
|
"addAgent": "Agent Hinzufügen",
|
||||||
|
"agentAddedSuccessfully": "Agent erfolgreich hinzugefügt.",
|
||||||
|
"agentRemovedSuccessfully": "Agent erfolgreich entfernt.",
|
||||||
|
"removeAgent": "Agent Entfernen",
|
||||||
|
"removeAgentMsg": "Bist Du sicher, dass Du diesen Agent entfernen möchtest?",
|
||||||
|
"LongSyntaxNotSupported": "Lange Syntax wird nicht unterstützt. Bitte verwende den YAML-Editor.",
|
||||||
|
"Lost connection to the socket server. Reconnecting...": "Verbindung zu Socket Server verloren. Verbinden...",
|
||||||
|
"Saved": "Gespeichert",
|
||||||
|
"Deleted": "Gelöscht",
|
||||||
|
"Started": "Gestartet",
|
||||||
|
"Stopped": "Gestoppt",
|
||||||
|
"Restarted": "Neugestartet",
|
||||||
|
"New Container Name...": "Neuer Container Name...",
|
||||||
|
"Network name...": "Netzwerkname...",
|
||||||
|
"Select a network...": "Netzwerk auswählen...",
|
||||||
|
"Updated": "Aktualisiert",
|
||||||
|
"Deployed": "Deployed",
|
||||||
|
"Switch to sh": "Zu sh wechseln",
|
||||||
|
"terminal": "Terminal",
|
||||||
|
"CurrentHostname": "(nicht gesetzt: verwende aktuellen Hostname)",
|
||||||
|
"Downed": "Heruntergefahren",
|
||||||
|
"NoNetworksAvailable": "Keine Netzwerke verfügbar. Du musst zunächst interne Netzwerke hinzufügen oder externe Netzwerke auf der rechten Seite aktivieren."
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
"restartStack": "Restart",
|
"restartStack": "Restart",
|
||||||
"updateStack": "Update",
|
"updateStack": "Update",
|
||||||
"startStack": "Start",
|
"startStack": "Start",
|
||||||
"downStack": "Stop & Down",
|
"downStack": "Stop & Inactive",
|
||||||
"editStack": "Edit",
|
"editStack": "Edit",
|
||||||
"discardStack": "Discard",
|
"discardStack": "Discard",
|
||||||
"saveStackDraft": "Save",
|
"saveStackDraft": "Save",
|
||||||
@ -95,9 +95,38 @@
|
|||||||
"reverseProxyMsg1": "Using a Reverse Proxy?",
|
"reverseProxyMsg1": "Using a Reverse Proxy?",
|
||||||
"reverseProxyMsg2": "Check how to config it for WebSocket",
|
"reverseProxyMsg2": "Check how to config it for WebSocket",
|
||||||
"Cannot connect to the socket server.": "Cannot connect to the socket server.",
|
"Cannot connect to the socket server.": "Cannot connect to the socket server.",
|
||||||
|
"Lost connection to the socket server. Reconnecting...": "Lost connection to the socket server. Reconnecting...",
|
||||||
"reconnecting...": "Reconnecting…",
|
"reconnecting...": "Reconnecting…",
|
||||||
"connecting...": "Connecting to the socket server…",
|
"connecting...": "Connecting to the socket server…",
|
||||||
"url": "URL | URLs",
|
"url": "URL | URLs",
|
||||||
"extra": "Extra",
|
"extra": "Extra",
|
||||||
"newUpdate": "New Update"
|
"newUpdate": "New Update",
|
||||||
|
"dockgeAgent": "Dockge Agent | Dockge Agents",
|
||||||
|
"currentEndpoint": "Current",
|
||||||
|
"dockgeURL": "Dockge URL (e.g. http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "Online",
|
||||||
|
"agentOffline": "Offline",
|
||||||
|
"connecting": "Connecting",
|
||||||
|
"connect": "Connect",
|
||||||
|
"addAgent": "Add Agent",
|
||||||
|
"agentAddedSuccessfully": "Agent added successfully.",
|
||||||
|
"agentRemovedSuccessfully": "Agent removed successfully.",
|
||||||
|
"removeAgent": "Remove Agent",
|
||||||
|
"removeAgentMsg": "Are you sure you want to remove this agent?",
|
||||||
|
"LongSyntaxNotSupported": "Long syntax is not supported here. Please use the YAML editor.",
|
||||||
|
"Saved": "Saved",
|
||||||
|
"Deployed": "Deployed",
|
||||||
|
"Deleted": "Deleted",
|
||||||
|
"Updated": "Updated",
|
||||||
|
"Started": "Started",
|
||||||
|
"Stopped": "Stopped",
|
||||||
|
"Restarted": "Restarted",
|
||||||
|
"Downed": "Downed",
|
||||||
|
"Switch to sh": "Switch to sh",
|
||||||
|
"terminal": "Terminal",
|
||||||
|
"CurrentHostname": "(Unset: Follow current hostname)",
|
||||||
|
"New Container Name...": "New Container Name...",
|
||||||
|
"Network name...": "Network name...",
|
||||||
|
"Select a network...": "Select a network...",
|
||||||
|
"NoNetworksAvailable": "No networks available. You need to add internal networks or enable external networks in the right side first."
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"languageName": "Español",
|
"languageName": "Inglés",
|
||||||
"Create your admin account": "Crea tu cuenta de administrador",
|
"Create your admin account": "Crea tu cuenta de administrador",
|
||||||
"authIncorrectCreds": "Nombre de usuario o contraseña incorrectos.",
|
"authIncorrectCreds": "Nombre de usuario o contraseña incorrectos.",
|
||||||
"PasswordsDoNotMatch": "Las contraseñas no coinciden.",
|
"PasswordsDoNotMatch": "Las contraseñas no coinciden.",
|
||||||
@ -12,7 +12,7 @@
|
|||||||
"registry": "Registro",
|
"registry": "Registro",
|
||||||
"compose": "Componer",
|
"compose": "Componer",
|
||||||
"addFirstStackMsg": "¡Compón tu primera pila!",
|
"addFirstStackMsg": "¡Compón tu primera pila!",
|
||||||
"stackName" : "Nombre de la Pila",
|
"stackName": "Nombre de la Pila",
|
||||||
"deployStack": "Desplegar",
|
"deployStack": "Desplegar",
|
||||||
"deleteStack": "Eliminar",
|
"deleteStack": "Eliminar",
|
||||||
"stopStack": "Detener",
|
"stopStack": "Detener",
|
||||||
@ -22,7 +22,7 @@
|
|||||||
"editStack": "Editar",
|
"editStack": "Editar",
|
||||||
"discardStack": "Descartar",
|
"discardStack": "Descartar",
|
||||||
"saveStackDraft": "Guardar",
|
"saveStackDraft": "Guardar",
|
||||||
"notAvailableShort" : "N/D",
|
"notAvailableShort": "N/D",
|
||||||
"deleteStackMsg": "¿Estás seguro de que quieres eliminar esta pila?",
|
"deleteStackMsg": "¿Estás seguro de que quieres eliminar esta pila?",
|
||||||
"stackNotManagedByDockgeMsg": "Esta pila no está gestionada por Dockge.",
|
"stackNotManagedByDockgeMsg": "Esta pila no está gestionada por Dockge.",
|
||||||
"primaryHostname": "Nombre de Host Primario",
|
"primaryHostname": "Nombre de Host Primario",
|
||||||
@ -90,5 +90,43 @@
|
|||||||
"Allowed commands:": "Comandos permitidos:",
|
"Allowed commands:": "Comandos permitidos:",
|
||||||
"Internal Networks": "Redes Internas",
|
"Internal Networks": "Redes Internas",
|
||||||
"External Networks": "Redes Externas",
|
"External Networks": "Redes Externas",
|
||||||
"No External Networks": "Sin Redes Externas"
|
"No External Networks": "Sin Redes Externas",
|
||||||
|
"reverseProxyMsg1": "¿Usando un proxy inverso?",
|
||||||
|
"reverseProxyMsg2": "Compruebe cómo configurarlo para WebSocket",
|
||||||
|
"newUpdate": "Nueva actualización",
|
||||||
|
"downStack": "Detener y desactivar",
|
||||||
|
"Cannot connect to the socket server.": "No se puede conectar al servidor del socket.",
|
||||||
|
"reconnecting...": "Reconectando…",
|
||||||
|
"connecting...": "Conectando al servidor del socket…",
|
||||||
|
"url": "Dirección URL | Direcciones URLs",
|
||||||
|
"extra": "Addicional",
|
||||||
|
"currentEndpoint": "Actual",
|
||||||
|
"dockgeURL": "URL de Dockge (ej. http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "En línea",
|
||||||
|
"agentOffline": "Desconectado",
|
||||||
|
"connect": "Conectar",
|
||||||
|
"addAgent": "Añadir Agente",
|
||||||
|
"agentAddedSuccessfully": "Agente añadido satisfactoriamente.",
|
||||||
|
"removeAgent": "Eliminar Agente",
|
||||||
|
"removeAgentMsg": "¿Estás seguro que deseas eliminar este agente?",
|
||||||
|
"dockgeAgent": "Agentes Dockge",
|
||||||
|
"connecting": "Conectando",
|
||||||
|
"agentRemovedSuccessfully": "Agente eliminado satisfactoriamente.",
|
||||||
|
"LongSyntaxNotSupported": "No hay soporte para la sintaxis larga. Por favor use el editor de YAML.",
|
||||||
|
"Lost connection to the socket server. Reconnecting...": "Se ha perdido la conexión con el servidor de socket. Reconectando...",
|
||||||
|
"Saved": "Guardado",
|
||||||
|
"Deployed": "Desplegado",
|
||||||
|
"Deleted": "Eliminado",
|
||||||
|
"Updated": "Actualizado",
|
||||||
|
"Started": "Arrancado",
|
||||||
|
"Stopped": "Parado",
|
||||||
|
"Restarted": "Reseteado",
|
||||||
|
"Downed": "Caído",
|
||||||
|
"Switch to sh": "Cambiar a sh",
|
||||||
|
"terminal": "Terminal",
|
||||||
|
"CurrentHostname": "(Vacío: Seguir hostname actual)",
|
||||||
|
"New Container Name...": "Nombre del nuevo Container...",
|
||||||
|
"Network name...": "Nombre de la red...",
|
||||||
|
"Select a network...": "Selecciona una red...",
|
||||||
|
"NoNetworksAvailable": "No hay redes disponibles. Primero debes agregar redes internas o habilitar redes externas en el lado derecho."
|
||||||
}
|
}
|
||||||
|
@ -95,9 +95,38 @@
|
|||||||
"connecting...": "Connexion au serveur socket…",
|
"connecting...": "Connexion au serveur socket…",
|
||||||
"url": "URL | URLs",
|
"url": "URL | URLs",
|
||||||
"extra": "Supplémentaire",
|
"extra": "Supplémentaire",
|
||||||
"downStack": "Arrêter et désactiver",
|
"downStack": "Arrêtez et rendre inactif",
|
||||||
"reverseProxyMsg1": "Utilisez vous un proxy inverse ?",
|
"reverseProxyMsg1": "Utilisez vous un proxy inverse ?",
|
||||||
"Cannot connect to the socket server.": "Impossible de se connecter au serveur socket.",
|
"Cannot connect to the socket server.": "Impossible de se connecter au serveur socket.",
|
||||||
"reconnecting...": "Reconnexion…",
|
"reconnecting...": "Reconnexion…",
|
||||||
"newUpdate": "Nouvelle mise à jour"
|
"newUpdate": "Nouvelle mise à jour",
|
||||||
|
"dockgeURL": "URL de Dockge (e.g. http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "En ligne",
|
||||||
|
"agentOffline": "Hors ligne",
|
||||||
|
"connecting": "Connexion",
|
||||||
|
"addAgent": "Ajouter un agent",
|
||||||
|
"agentAddedSuccessfully": "Agent ajouté avec succès.",
|
||||||
|
"agentRemovedSuccessfully": "Agent supprimé avec succès.",
|
||||||
|
"removeAgent": "Supprimer l'agent",
|
||||||
|
"dockgeAgent": "Dockge Agent | Dockge Agents",
|
||||||
|
"currentEndpoint": "Actuel",
|
||||||
|
"connect": "Connecter",
|
||||||
|
"removeAgentMsg": "Êtes-vous sûr de vouloir supprimer cet agent ?",
|
||||||
|
"LongSyntaxNotSupported": "La syntaxe longue n'est pas prise en charge ici. Veuillez utiliser l'éditeur YAML.",
|
||||||
|
"Saved": "Enregistré",
|
||||||
|
"Deployed": "Déployé",
|
||||||
|
"Deleted": "Supprimé",
|
||||||
|
"Updated": "Mis à jour",
|
||||||
|
"Started": "démarrer",
|
||||||
|
"Stopped": "Arrêté",
|
||||||
|
"Restarted": "Redémarré",
|
||||||
|
"Switch to sh": "Passer à sh",
|
||||||
|
"terminal": "Terminal",
|
||||||
|
"New Container Name...": "Nouveau nom du conteneur...",
|
||||||
|
"Network name...": "Nom du réseau...",
|
||||||
|
"Select a network...": "Sélectionnez un réseau...",
|
||||||
|
"Downed": "Abattu",
|
||||||
|
"Lost connection to the socket server. Reconnecting...": "Connexion au serveur socket perdue. Reconnexion...",
|
||||||
|
"CurrentHostname": "(Non défini : suivre le nom d'hôte actuel)",
|
||||||
|
"NoNetworksAvailable": "Aucun réseau disponible. Vous devez d'abord ajouter des réseaux internes ou activer les réseaux externes sur le côté droit."
|
||||||
}
|
}
|
||||||
|
132
frontend/src/lang/ga.json
Normal file
132
frontend/src/lang/ga.json
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
{
|
||||||
|
"Create your admin account": "Cruthaigh do chuntas riaracháin",
|
||||||
|
"authIncorrectCreds": "Ainm úsáideora nó pasfhocal mícheart.",
|
||||||
|
"PasswordsDoNotMatch": "Níl na pasfhocail comhthráthacha.",
|
||||||
|
"Repeat Password": "Athscríobh an Pasfhocal",
|
||||||
|
"Create": "Cruthaigh",
|
||||||
|
"signedInDisp": "Sínithe isteach mar {0}",
|
||||||
|
"languageName": "Gaeilge",
|
||||||
|
"console": "Consól",
|
||||||
|
"registry": "Clárlann",
|
||||||
|
"compose": "Scríobh",
|
||||||
|
"stackName": "Ainm an Staca",
|
||||||
|
"deployStack": "Deighil",
|
||||||
|
"deleteStack": "Scrios",
|
||||||
|
"stopStack": "Stad",
|
||||||
|
"restartStack": "Atosaigh",
|
||||||
|
"updateStack": "Nuashonraigh",
|
||||||
|
"startStack": "Tosaigh",
|
||||||
|
"downStack": "Stad & Neamhghníomhach",
|
||||||
|
"editStack": "Cuir in Eagar",
|
||||||
|
"discardStack": "Caith amach",
|
||||||
|
"saveStackDraft": "Sábháil",
|
||||||
|
"deleteStackMsg": "An bhfuil tú cinnte go bhfuil tú ag iarraidh an staca seo a scriosadh?",
|
||||||
|
"primaryHostname": "Príomhainm óstáin",
|
||||||
|
"general": "Ginearálta",
|
||||||
|
"container": "Coimeádán | Coimeádáin",
|
||||||
|
"scanFolder": "Scanáil Fillteáin na dStacanna",
|
||||||
|
"dockerImage": "Íomha",
|
||||||
|
"restartPolicyUnlessStopped": "Mura Stadfar",
|
||||||
|
"restartPolicyAlways": "I gcónaí",
|
||||||
|
"restartPolicyOnFailure": "Ar theip",
|
||||||
|
"restartPolicyNo": "Níl",
|
||||||
|
"environmentVariable": "Athróg Timpeallacht | Athróga Timpeallacht",
|
||||||
|
"restartPolicy": "Polasaí Atosaigh",
|
||||||
|
"port": "Port | Portanna",
|
||||||
|
"volume": "Toirt | Toirteanna",
|
||||||
|
"network": "Líonra | Líonraí",
|
||||||
|
"dependsOn": "Spleáchas Coimeádán | Spleáchais Coimeádán",
|
||||||
|
"addListItem": "Cuir {0}",
|
||||||
|
"deleteContainer": "Scrios",
|
||||||
|
"addContainer": "Cuir Coimeádán leis",
|
||||||
|
"addNetwork": "Cuir Líonra leis",
|
||||||
|
"add": "Cuir",
|
||||||
|
"Edit": "Cuir in eagar",
|
||||||
|
"applyToYAML": "Déan iarratas ar YAML",
|
||||||
|
"createExternalNetwork": "Cruthaigh",
|
||||||
|
"disableauth.message1": "An bhfuil tú cinnte gur mhaith leat <strong>fíordheimhniú a dhíchumasú</strong>?",
|
||||||
|
"passwordNotMatchMsg": "Ní hionann an pasfhocal athfhillteach.",
|
||||||
|
"autoGet": "Faigh Uathoibríoch",
|
||||||
|
"addInternalNetwork": "Cuir",
|
||||||
|
"Save": "Sábháil",
|
||||||
|
"Language": "Teanga",
|
||||||
|
"Current User": "Úsáideoir Reatha",
|
||||||
|
"New Password": "Pasfhocal Nua",
|
||||||
|
"Current Password": "Pasfhocal Reatha",
|
||||||
|
"Change Password": "Athraigh do Phasfhocal",
|
||||||
|
"Repeat New Password": "Déan Pasfhocal Nua arís",
|
||||||
|
"Update Password": "Nuashonraigh Pasfhocal",
|
||||||
|
"Advanced": "Ardleibhéal",
|
||||||
|
"Please use this option carefully!": "Bain úsáid as an rogha seo go cúramach, le do thoil!",
|
||||||
|
"Enable Auth": "Cumasaigh Auth",
|
||||||
|
"Disable Auth": "Auth dhíchumasú",
|
||||||
|
"I understand, please disable": "Tuigim, le do thoil, múch",
|
||||||
|
"Leave": "Fág",
|
||||||
|
"Frontend Version": "Leagan Frontend",
|
||||||
|
"Check Update On GitHub": "Seiceáil an Nuashonrú ar GitHub",
|
||||||
|
"Show update if available": "Taispeáin an Nuashonrú más ar fáil",
|
||||||
|
"Also check beta release": "Seiceáil an scaoileadh beta freisin",
|
||||||
|
"Remember me": "Cuimhnigh orm",
|
||||||
|
"Login": "Logáil isteach",
|
||||||
|
"Username": "Ainm úsáideora",
|
||||||
|
"Password": "Pasfhocal",
|
||||||
|
"Logout": "Logáil Amach",
|
||||||
|
"Lowercase only": "Cás íochtair amháin",
|
||||||
|
"Convert to Compose": "Tiontaigh go Compóidh",
|
||||||
|
"Docker Run": "Docker Rith",
|
||||||
|
"exited": "scoir",
|
||||||
|
"inactive": "neamhghníomhach",
|
||||||
|
"Appearance": "Dealramh",
|
||||||
|
"Security": "Slándáil",
|
||||||
|
"About": "Maidir le",
|
||||||
|
"Allowed commands:": "Orduithe ceadaithe:",
|
||||||
|
"Internal Networks": "Líonraí Inmheánacha",
|
||||||
|
"External Networks": "Líonraí Seachtracha",
|
||||||
|
"No External Networks": "Gan Líonraí Seachtracha",
|
||||||
|
"reverseProxyMsg1": "Ag Úsáid Seachfhreastalaí Réabhlóideach?",
|
||||||
|
"reverseProxyMsg2": "Seiceáil conas é a shocraigh don WebSocket",
|
||||||
|
"Cannot connect to the socket server.": "Ní féidir ceangal a dhéanamh leis an freastalaí soicéad.",
|
||||||
|
"reconnecting...": "Ag athcheangal…",
|
||||||
|
"connecting...": "Ag nascadh leis an freastalaí soicéad…",
|
||||||
|
"url": "URL | URLanna",
|
||||||
|
"extra": "Breise",
|
||||||
|
"newUpdate": "Nuashonrú Nua",
|
||||||
|
"dockgeAgent": "Aighne Dockge | Aighnithe Dockge",
|
||||||
|
"currentEndpoint": "Reatha",
|
||||||
|
"dockgeURL": "Dockge URL (e.g. http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "Ar Líne",
|
||||||
|
"agentOffline": "As Líne",
|
||||||
|
"connecting": "Ag Nascadh",
|
||||||
|
"connect": "Ceangail",
|
||||||
|
"addAgent": "Cuir Aighne",
|
||||||
|
"agentAddedSuccessfully": "Aighne curtha leis go rathúil.",
|
||||||
|
"agentRemovedSuccessfully": "Aighne bhaint as go rathúil.",
|
||||||
|
"removeAgent": "Bain Aighne",
|
||||||
|
"removeAgentMsg": "An bhfuil tú cinnte gur mhaith leat an t-aighne seo a bhaint?",
|
||||||
|
"LongSyntaxNotSupported": "Ní thacaítear leis an níochán fada anseo. Úsáid an Eagarthóir YAML, le do thoil.",
|
||||||
|
"signedInDispDisabled": "Auth Díchumasaithe.",
|
||||||
|
"home": "Abhaile",
|
||||||
|
"addFirstStackMsg": "Scríobh do chéad stac!",
|
||||||
|
"notAvailableShort": "Níl ar Fáil",
|
||||||
|
"stackNotManagedByDockgeMsg": "Ní bhainistítear an staca seo ag Dockge.",
|
||||||
|
"containerName": "Ainm na gCoimeádán",
|
||||||
|
"disableauth.message2": "Tá sé deartha do chúinsí <strong>ina bhfuil sé beartaithe agat tríú páirtí athbhreithniú a chur i bhfeidhm</strong> os comhair Dockge cosúil le Rochtain Cloudflare, Authelia nó múnlaí deimhniú eile.",
|
||||||
|
"Settings": "Socruithe",
|
||||||
|
"active": "gníomhach",
|
||||||
|
"Lost connection to the socket server. Reconnecting...": "Ceangal caillte leis an bhfreastalaí soicéad. Ag athcheangal...",
|
||||||
|
"Saved": "Shábháil",
|
||||||
|
"Deployed": "Imlonnaithe",
|
||||||
|
"Deleted": "Scriosta",
|
||||||
|
"Updated": "Nuashonraithe",
|
||||||
|
"Started": "Thosaigh",
|
||||||
|
"Stopped": "Stopadh",
|
||||||
|
"Restarted": "Atosaigh",
|
||||||
|
"Downed": "Tugtha anuas",
|
||||||
|
"Switch to sh": "Athraigh go sh",
|
||||||
|
"terminal": "Teirminéal",
|
||||||
|
"CurrentHostname": "(Díshuiteáil: Lean an t-óstainm reatha)",
|
||||||
|
"New Container Name...": "Ainm Gabhdáin Nua...",
|
||||||
|
"Network name...": "Ainm líonra...",
|
||||||
|
"Select a network...": "Roghnaigh líonra...",
|
||||||
|
"NoNetworksAvailable": "Níl líonraí ar fáil. Ní mór duit líonraí inmheánacha a chur leis nó líonraí seachtracha a chumasú ar an taobh deas ar dtús."
|
||||||
|
}
|
132
frontend/src/lang/hu.json
Normal file
132
frontend/src/lang/hu.json
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
{
|
||||||
|
"languageName": "Angol",
|
||||||
|
"Repeat Password": "Jelszó Ismétlése",
|
||||||
|
"Create": "Létrehozás",
|
||||||
|
"signedInDisp": "Bejelentkezve {0}-ként",
|
||||||
|
"home": "Főképernyő",
|
||||||
|
"registry": "Bejegyzések",
|
||||||
|
"compose": "Összeállít",
|
||||||
|
"addFirstStackMsg": "Állítsd össze az első stack-odat!",
|
||||||
|
"stackName": "Stack Neve",
|
||||||
|
"deployStack": "Telepítés",
|
||||||
|
"deleteStack": "Törlés",
|
||||||
|
"stopStack": "Leállítás",
|
||||||
|
"restartStack": "Újraindítás",
|
||||||
|
"downStack": "Leállítva",
|
||||||
|
"editStack": "Szerkesztés",
|
||||||
|
"discardStack": "Eldobás",
|
||||||
|
"saveStackDraft": "Mentés",
|
||||||
|
"notAvailableShort": "N/A",
|
||||||
|
"stackNotManagedByDockgeMsg": "Ez a stack nem a Dockge kezelése alatt áll.",
|
||||||
|
"primaryHostname": "Elsődleges Gazdagépnév",
|
||||||
|
"general": "Általános",
|
||||||
|
"container": "Konténer | Konténerek",
|
||||||
|
"scanFolder": "Stack Mappa Beolvasása",
|
||||||
|
"dockerImage": "Applikáció-kép",
|
||||||
|
"restartPolicyNo": "Nem",
|
||||||
|
"environmentVariable": "Környezeti Változó | Környezeti Változók",
|
||||||
|
"containerName": "Konténer Neve",
|
||||||
|
"port": "Port | Portok",
|
||||||
|
"volume": "Tárhely | Tárhelyek",
|
||||||
|
"network": "Hálózat | Hálózatok",
|
||||||
|
"addListItem": "{0} Hozzáadása",
|
||||||
|
"deleteContainer": "Törlés",
|
||||||
|
"addContainer": "Konténer Hozzáadása",
|
||||||
|
"addNetwork": "Hálózat Hozzáadása",
|
||||||
|
"add": "Hozzáadás",
|
||||||
|
"Edit": "Szerkesztés",
|
||||||
|
"applyToYAML": "Alkalmazás YAML Formátumba",
|
||||||
|
"addInternalNetwork": "Hozzáadás",
|
||||||
|
"Save": "Mentés",
|
||||||
|
"Language": "Nyelv",
|
||||||
|
"Current User": "Jelenlegi Felhasználó",
|
||||||
|
"Change Password": "Jelszó Módosítása",
|
||||||
|
"Current Password": "Jelenlegi Jelszó",
|
||||||
|
"New Password": "Új Jelszó",
|
||||||
|
"Update Password": "Jelszó Cserélése",
|
||||||
|
"Advanced": "Fejlett",
|
||||||
|
"Enable Auth": "Hitelesítés Bekapcsolása",
|
||||||
|
"Disable Auth": "Hitelesítés Kikapcsolása",
|
||||||
|
"I understand, please disable": "Megértettem, kérem kapcsolja ki",
|
||||||
|
"Leave": "Kilépés",
|
||||||
|
"Frontend Version": "Frontend Verzió",
|
||||||
|
"Also check beta release": "Béta kiadás is",
|
||||||
|
"Remember me": "Emlékezz rám",
|
||||||
|
"Login": "Belépés",
|
||||||
|
"Username": "Felhasználónév",
|
||||||
|
"Password": "Jelszó",
|
||||||
|
"Settings": "Beállítások",
|
||||||
|
"Convert to Compose": "Átalakítás Compose-ra",
|
||||||
|
"Docker Run": "Docker Futtatása",
|
||||||
|
"active": "aktív",
|
||||||
|
"inactive": "inaktív",
|
||||||
|
"Appearance": "Megjelenés",
|
||||||
|
"Security": "Biztonság",
|
||||||
|
"Allowed commands:": "Megengedett parancsok:",
|
||||||
|
"Internal Networks": "Belső Hálózatok",
|
||||||
|
"External Networks": "Kölső Hálózatok",
|
||||||
|
"No External Networks": "Nincs Külső Hálózat",
|
||||||
|
"reverseProxyMsg1": "Proxy-t használ?",
|
||||||
|
"reverseProxyMsg2": "Javasolt WebSocket konfiguráció megtekintése",
|
||||||
|
"reconnecting...": "Újracsatlakozás…",
|
||||||
|
"extra": "Extra",
|
||||||
|
"newUpdate": "Új Frissítés",
|
||||||
|
"currentEndpoint": "Jelenlegi",
|
||||||
|
"agentOnline": "Online",
|
||||||
|
"dockgeAgent": "Dockge Felügyelő | Dockge Felügyelők",
|
||||||
|
"agentOffline": "Offline",
|
||||||
|
"connecting": "Csatlakozás",
|
||||||
|
"connect": "Csatlakozás",
|
||||||
|
"agentAddedSuccessfully": "Felügyelő hozzáadva.",
|
||||||
|
"agentRemovedSuccessfully": "Felügyelő törölve.",
|
||||||
|
"removeAgent": "Felügyelő Törlése",
|
||||||
|
"addAgent": "Felügyelő Hozzáadása",
|
||||||
|
"removeAgentMsg": "Biztos hogy törli ezt a Felügyelőt?",
|
||||||
|
"Create your admin account": "Adminisztrátor felhasználó létrehozása",
|
||||||
|
"authIncorrectCreds": "Helytelen felhasználónév vagy jelszó.",
|
||||||
|
"PasswordsDoNotMatch": "Jelszavak nem eggyeznek.",
|
||||||
|
"signedInDispDisabled": "Hitelesítés Kikapcsolva.",
|
||||||
|
"console": "Konzol",
|
||||||
|
"updateStack": "Frissítés",
|
||||||
|
"startStack": "Indítás",
|
||||||
|
"deleteStackMsg": "Biztos hogy törli ezt a stack-ot?",
|
||||||
|
"restartPolicyUnlessStopped": "Ha Nincs Leállítva",
|
||||||
|
"restartPolicyAlways": "Mindig",
|
||||||
|
"restartPolicyOnFailure": "Hibába Futáskor",
|
||||||
|
"restartPolicy": "Újraindítási Elv",
|
||||||
|
"dependsOn": "Konténer Függés | Konténer Függései",
|
||||||
|
"disableauth.message1": "Biztos hogy szeretné a <strong>hitelesítést kikapcsolni</strong>?",
|
||||||
|
"disableauth.message2": "Olyan esetekre ahol <strong>harmadfél általi hitelesítést szeretne alkalmazni</strong> a Dockge elött, mint például Cloudflare Access, Authelia vagy egyéb hitelesítő program.",
|
||||||
|
"passwordNotMatchMsg": "Az ismételt jelszó nem eggyezik.",
|
||||||
|
"autoGet": "Automatikus Megszerzés",
|
||||||
|
"createExternalNetwork": "Készítés",
|
||||||
|
"Repeat New Password": "Új Jelszó Megerősítése",
|
||||||
|
"Please use this option carefully!": "Ezt a lehetőséget használja óvatosan!",
|
||||||
|
"Check Update On GitHub": "Fríssítés Keresése Github-on",
|
||||||
|
"Show update if available": "Elérhető frissítések megjelenítése",
|
||||||
|
"Logout": "Kilépés",
|
||||||
|
"Lowercase only": "Csak kisbetűvel",
|
||||||
|
"exited": "végzett",
|
||||||
|
"About": "Az Alkalmazásról",
|
||||||
|
"Cannot connect to the socket server.": "A Socket csatlakozás nem elérhető.",
|
||||||
|
"connecting...": "Csatlakozás a socket szerver-hez…",
|
||||||
|
"url": "URL | URL-ek",
|
||||||
|
"dockgeURL": "Dockge URL (pl. http://127.0.0.1:5001)",
|
||||||
|
"LongSyntaxNotSupported": "A hosszú szintaxis itt nem támogatott. Használja a YAML szerkesztőt.",
|
||||||
|
"Lost connection to the socket server. Reconnecting...": "Megszakadt a kapcsolat a socket szerverrel. Újracsatlakozás...",
|
||||||
|
"Saved": "Elmentve",
|
||||||
|
"Deployed": "Telepítve",
|
||||||
|
"Deleted": "Törölve",
|
||||||
|
"Updated": "Frissítve",
|
||||||
|
"Started": "Indult",
|
||||||
|
"Stopped": "Megállítva",
|
||||||
|
"Restarted": "Újraindítva",
|
||||||
|
"Downed": "Leállított",
|
||||||
|
"Switch to sh": "Váltás shell-re",
|
||||||
|
"terminal": "Terminál",
|
||||||
|
"CurrentHostname": "(Nincs beállítva: Az aktuális gazdagépnév követése)",
|
||||||
|
"New Container Name...": "Új konténer név...",
|
||||||
|
"Network name...": "Hálózat név...",
|
||||||
|
"Select a network...": "Válasszon ki egy hálózatot...",
|
||||||
|
"NoNetworksAvailable": "Nincs elérhető hálózat. Először hozzá kell adnia belső hálózatokat, vagy engedélyeznie kell a külső hálózatokat a jobb oldalon."
|
||||||
|
}
|
122
frontend/src/lang/id.json
Normal file
122
frontend/src/lang/id.json
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
{
|
||||||
|
"Create your admin account": "Buat akun admin Anda",
|
||||||
|
"PasswordsDoNotMatch": "Kata sandi tidak sama.",
|
||||||
|
"Repeat Password": "Ulangi Kata Sandi",
|
||||||
|
"Create": "Buat",
|
||||||
|
"signedInDisp": "Masuk sebagai {0}",
|
||||||
|
"signedInDispDisabled": "Otentikasi Dinonaktifkan.",
|
||||||
|
"home": "Beranda",
|
||||||
|
"console": "Konsol",
|
||||||
|
"registry": "Registri",
|
||||||
|
"compose": "Menyusun",
|
||||||
|
"addFirstStackMsg": "Buat tumpukan pertama Anda!",
|
||||||
|
"stackName": "Nama Tumpukan",
|
||||||
|
"deployStack": "Terapkan",
|
||||||
|
"stopStack": "Hentikan",
|
||||||
|
"restartStack": "Mulai ulang",
|
||||||
|
"updateStack": "Pembaruan",
|
||||||
|
"downStack": "Hentikan & Tidak aktif",
|
||||||
|
"editStack": "Sunting",
|
||||||
|
"discardStack": "Buang",
|
||||||
|
"saveStackDraft": "Simpan",
|
||||||
|
"notAvailableShort": "T/A",
|
||||||
|
"stackNotManagedByDockgeMsg": "Tumpukan ini tidak dikelola oleh Dockge.",
|
||||||
|
"primaryHostname": "Nama Host Utama",
|
||||||
|
"general": "Umum",
|
||||||
|
"container": "Kontainer | Wadah",
|
||||||
|
"scanFolder": "Pindai Folder Tumpukan",
|
||||||
|
"restartPolicyUnlessStopped": "Kecuali Dihentikan",
|
||||||
|
"restartPolicyAlways": "Selalu",
|
||||||
|
"restartPolicyNo": "Tidak",
|
||||||
|
"environmentVariable": "Variabel Lingkungan | Variabel Lingkungan",
|
||||||
|
"dockerImage": "Image",
|
||||||
|
"startStack": "Mulai",
|
||||||
|
"restartPolicy": "Kebijakan Mulai Ulang",
|
||||||
|
"containerName": "Nama Kontainer",
|
||||||
|
"network": "Jaringan",
|
||||||
|
"dependsOn": "Ketergantungan Kontainer",
|
||||||
|
"addListItem": "Tambah {0}",
|
||||||
|
"deleteContainer": "Hapus",
|
||||||
|
"addContainer": "Tambah Kontainer",
|
||||||
|
"addNetwork": "Tambah Jaringan",
|
||||||
|
"disableauth.message1": "Apakah Anda yakin untuk <strong>mematikan otentikasi</strong>?",
|
||||||
|
"passwordNotMatchMsg": "Kata sandi berulang tidak cocok.",
|
||||||
|
"autoGet": "Otomatis Dapatkan",
|
||||||
|
"add": "Tambah",
|
||||||
|
"Edit": "Sunting",
|
||||||
|
"port": "Port",
|
||||||
|
"volume": "Volume",
|
||||||
|
"createExternalNetwork": "Buat",
|
||||||
|
"addInternalNetwork": "Tambah",
|
||||||
|
"Save": "Simpan",
|
||||||
|
"Language": "Bahasa",
|
||||||
|
"Change Password": "Ubah Kata Sandi",
|
||||||
|
"Current Password": "Ubah Kata Sandi",
|
||||||
|
"New Password": "Kata Sandi Baru",
|
||||||
|
"Repeat New Password": "Ulangi Kata Sandi",
|
||||||
|
"Update Password": "Perbarui Kata Sandi",
|
||||||
|
"Advanced": "Lanjutan",
|
||||||
|
"Enable Auth": "Hidupkan Otentikasi",
|
||||||
|
"Disable Auth": "Matikan Otentikasi",
|
||||||
|
"I understand, please disable": "Saya mengerti, tolong nonaktifkan",
|
||||||
|
"Leave": "Pergi",
|
||||||
|
"Frontend Version": "Versi Antarmuka",
|
||||||
|
"Check Update On GitHub": "Cek pembaruan di Github",
|
||||||
|
"Show update if available": "Tampilkan pembaruan jika tersedia",
|
||||||
|
"Remember me": "Ingat saya",
|
||||||
|
"Login": "Masuk",
|
||||||
|
"Username": "Nama Pengguna",
|
||||||
|
"Password": "Kata Sandi",
|
||||||
|
"Settings": "Pengaturan",
|
||||||
|
"Logout": "Keluar",
|
||||||
|
"Lowercase only": "Huruf kecil saja",
|
||||||
|
"Convert to Compose": "Ubah ke Tumpukan",
|
||||||
|
"active": "aktif",
|
||||||
|
"exited": "keluar",
|
||||||
|
"inactive": "nonaktif",
|
||||||
|
"Appearance": "Tampilan",
|
||||||
|
"Security": "Keamanan",
|
||||||
|
"About": "Tentang",
|
||||||
|
"Internal Networks": "Jaringan internal",
|
||||||
|
"External Networks": "Jaringan eksternal",
|
||||||
|
"No External Networks": "Tanpa Jaringan Eksternal",
|
||||||
|
"reverseProxyMsg1": "Menggunakan Reverse Proxy ?",
|
||||||
|
"Cannot connect to the socket server.": "Tidak bisa terhubung dengan server socket.",
|
||||||
|
"reconnecting...": "Menghubungkan kembali…",
|
||||||
|
"connecting...": "Menyambungkan ke server socket…",
|
||||||
|
"url": "TAUTAN",
|
||||||
|
"extra": "Ekstra",
|
||||||
|
"Docker Run": "Jalankan Docker",
|
||||||
|
"newUpdate": "Pembaruan Baru",
|
||||||
|
"languageName": "Bahasa Indonesia (Indonesian)",
|
||||||
|
"authIncorrectCreds": "Nama pengguna atau sandi salah.",
|
||||||
|
"deleteStack": "Hapus",
|
||||||
|
"deleteStackMsg": "Apakah Anda yakin Anda ingin menghapus tumpukan ini ?",
|
||||||
|
"restartPolicyOnFailure": "Ketika Gagal",
|
||||||
|
"disableauth.message2": "Ini dirancang untuk skenario <strong>di mana Anda bermaksud untuk mengimplementasikan otentikasi pihak ketiga</strong> di depan Dockge seperti Cloudflare Access, Authelia, atau mekanisme otentikasi lainnya.",
|
||||||
|
"applyToYAML": "Aplikasikan ke YAML",
|
||||||
|
"Current User": "Pengguna Saat Ini",
|
||||||
|
"Please use this option carefully!": "Mohon berhati - hati menggunakan opsi ini!",
|
||||||
|
"Also check beta release": "Juga cek keluaran beta",
|
||||||
|
"Allowed commands:": "Perintah yang diperbolehkan:",
|
||||||
|
"reverseProxyMsg2": "Lihat cara mengonfigurasinya untuk WebSocket",
|
||||||
|
"dockgeURL": "Alamat Dockge (cth. http://127.0.0.1:5001)",
|
||||||
|
"connecting": "Menghubungkan",
|
||||||
|
"addAgent": "Tambah Agen",
|
||||||
|
"agentAddedSuccessfully": "Agen sukses ditambahkan.",
|
||||||
|
"agentRemovedSuccessfully": "Agen sukses dihapus.",
|
||||||
|
"removeAgent": "Hapus Agen",
|
||||||
|
"connect": "Hubungkan",
|
||||||
|
"dockgeAgent": "Agen Dockge",
|
||||||
|
"currentEndpoint": "Sekarang",
|
||||||
|
"agentOnline": "Terhubung",
|
||||||
|
"agentOffline": "Terputus",
|
||||||
|
"removeAgentMsg": "Apakah anda yakin untuk menghapus agen ini?",
|
||||||
|
"LongSyntaxNotSupported": "Sintaks yang panjang tidak didukung di sini. Silakan gunakan editor YAML.",
|
||||||
|
"Saved": "Tersimpan",
|
||||||
|
"Deleted": "Terhapus",
|
||||||
|
"Updated": "Telah diperbaharui",
|
||||||
|
"Started": "Aplikasi dimulai",
|
||||||
|
"Stopped": "Aplikasi dihentikan",
|
||||||
|
"Restarted": "Aplikasi memuat kembali"
|
||||||
|
}
|
@ -19,7 +19,7 @@
|
|||||||
"restartStack": "Riavvia",
|
"restartStack": "Riavvia",
|
||||||
"updateStack": "Aggiorna",
|
"updateStack": "Aggiorna",
|
||||||
"startStack": "Avvia",
|
"startStack": "Avvia",
|
||||||
"downStack": "Stop & Down",
|
"downStack": "Stop & Inattivo",
|
||||||
"editStack": "Modifica",
|
"editStack": "Modifica",
|
||||||
"discardStack": "Annulla",
|
"discardStack": "Annulla",
|
||||||
"saveStackDraft": "Salva",
|
"saveStackDraft": "Salva",
|
||||||
@ -92,11 +92,25 @@
|
|||||||
"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?",
|
"reverseProxyMsg1": "Stai usando Reverse Proxy?",
|
||||||
"reverseProxyMsg2": "Controlla come configurarlo per WebSocket",
|
"reverseProxyMsg2": "Verifica come configurarlo per il WebSocket",
|
||||||
"Cannot connect to the socket server.": "Impossibile connettersi al server socket.",
|
"Cannot connect to the socket server.": "impossibile collegarsi al socket server",
|
||||||
"connecting...": "Connessione al server socket…",
|
"connecting...": "connettendosi al socket server…",
|
||||||
"extra": "Extra",
|
"extra": "Extra",
|
||||||
"reconnecting...": "Riconnessione…",
|
"reconnecting...": "Riconnessione…",
|
||||||
"url": "Indirizzo | Indirizzi"
|
"url": "URL | URLs",
|
||||||
|
"newUpdate": "Nuovo aggiornamento",
|
||||||
|
"dockgeAgent": "Agente Dockge | Agenti Dockge",
|
||||||
|
"currentEndpoint": "Corrente",
|
||||||
|
"agentOnline": "Online",
|
||||||
|
"agentOffline": "Offline",
|
||||||
|
"connecting": "In connessione",
|
||||||
|
"connect": "Connetti",
|
||||||
|
"dockgeURL": "Dockge URL (ad esempio http://127.0.0.1:5001)",
|
||||||
|
"agentRemovedSuccessfully": "Agente rimosso con successo.",
|
||||||
|
"removeAgent": "Rimuovi Agente",
|
||||||
|
"removeAgentMsg": "Sei sicuro di voler rimuovere questo agente?",
|
||||||
|
"addAgent": "Aggungi Agente",
|
||||||
|
"agentAddedSuccessfully": "Agente aggiunto correttamente.",
|
||||||
|
"LongSyntaxNotSupported": "La sintassi lunga non è supportata qui. Utilizzare l'editor YAML."
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
"primaryHostname": "主ホスト名",
|
"primaryHostname": "主ホスト名",
|
||||||
"container": "コンテナ",
|
"container": "コンテナ",
|
||||||
"dependsOn": "コンテナ依存関係",
|
"dependsOn": "コンテナ依存関係",
|
||||||
"downStack": "停止して削除",
|
"downStack": "停止して非アクティブ化",
|
||||||
"notAvailableShort": "N/A",
|
"notAvailableShort": "N/A",
|
||||||
"restartPolicyUnlessStopped": "手動で停止されるまで",
|
"restartPolicyUnlessStopped": "手動で停止されるまで",
|
||||||
"restartPolicyAlways": "常時",
|
"restartPolicyAlways": "常時",
|
||||||
@ -94,5 +94,36 @@
|
|||||||
"I understand, please disable": "理解しました。無効化してください",
|
"I understand, please disable": "理解しました。無効化してください",
|
||||||
"Lowercase only": "小文字のみ",
|
"Lowercase only": "小文字のみ",
|
||||||
"reverseProxyMsg1": "リバースプロキシを使用していますか?",
|
"reverseProxyMsg1": "リバースプロキシを使用していますか?",
|
||||||
"connecting...": "ソケットサーバーに接続中…"
|
"connecting...": "ソケットサーバーに接続中…",
|
||||||
|
"newUpdate": "新しいバージョン",
|
||||||
|
"dockgeAgent": "Dockge エージェント",
|
||||||
|
"dockgeURL": "DockgeのURL (例: http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "オンライン",
|
||||||
|
"agentOffline": "オフライン",
|
||||||
|
"connecting": "接続中",
|
||||||
|
"connect": "接続",
|
||||||
|
"addAgent": "エージェントを追加",
|
||||||
|
"agentAddedSuccessfully": "エージェントが正常に追加されました。",
|
||||||
|
"agentRemovedSuccessfully": "エージェントは正常に削除されました。",
|
||||||
|
"removeAgent": "エージェントを削除",
|
||||||
|
"removeAgentMsg": "本当にこのエージェントを削除しますか?",
|
||||||
|
"url": "URL | URL",
|
||||||
|
"About": "Dockgeについて",
|
||||||
|
"Docker Run": "Docker Run to Compose",
|
||||||
|
"LongSyntaxNotSupported": "長い構文はここではサポートされていません。YAMLエディタを使用してください。",
|
||||||
|
"Lost connection to the socket server. Reconnecting...": "ソケットサーバーとの接続が失われました。再接続中です...",
|
||||||
|
"extra": "追加設定",
|
||||||
|
"Saved": "保存済み",
|
||||||
|
"Deployed": "デプロイ済み",
|
||||||
|
"Deleted": "削除済み",
|
||||||
|
"Updated": "アップデート済み",
|
||||||
|
"Started": "開始済み",
|
||||||
|
"Stopped": "停止済み",
|
||||||
|
"Restarted": "再起動済み",
|
||||||
|
"Switch to sh": "shに切り替え",
|
||||||
|
"terminal": "ターミナル",
|
||||||
|
"New Container Name...": "新しいコンテナ名...",
|
||||||
|
"Network name...": "ネットワーク名...",
|
||||||
|
"Select a network...": "ネットワークを選択...",
|
||||||
|
"NoNetworksAvailable": "利用可能なネットワークがありません。まず右側の内部ネットワークを追加するか、外部ネットワークを有効にする必要があります。"
|
||||||
}
|
}
|
||||||
|
@ -92,11 +92,25 @@
|
|||||||
"External Networks": "외부 네트워크",
|
"External Networks": "외부 네트워크",
|
||||||
"No External Networks": "외부 네트워크 없음",
|
"No External Networks": "외부 네트워크 없음",
|
||||||
"reverseProxyMsg2": "여기서 WebSocket을 위한 설정을 확인해 보세요",
|
"reverseProxyMsg2": "여기서 WebSocket을 위한 설정을 확인해 보세요",
|
||||||
"downStack": "정지 & Down",
|
"downStack": "정지 & 비활성화",
|
||||||
"reverseProxyMsg1": "리버스 프록시를 사용하고 계신가요?",
|
"reverseProxyMsg1": "리버스 프록시를 사용하고 계신가요?",
|
||||||
"Cannot connect to the socket server.": "소켓 서버에 연결하지 못했습니다.",
|
"Cannot connect to the socket server.": "소켓 서버에 연결하지 못했습니다.",
|
||||||
"connecting...": "소켓 서버에 연결하는 중…",
|
"connecting...": "소켓 서버에 연결하는 중…",
|
||||||
"extra": "기타",
|
"extra": "기타",
|
||||||
"url": "URL | URL",
|
"url": "URL | URL",
|
||||||
"reconnecting...": "재연결 중…"
|
"reconnecting...": "재연결 중…",
|
||||||
|
"newUpdate": "새 업데이트",
|
||||||
|
"dockgeURL": "Dockge URL (예. http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "온라인",
|
||||||
|
"agentOffline": "오프라인",
|
||||||
|
"connect": "연결",
|
||||||
|
"addAgent": "에이전트 추가",
|
||||||
|
"agentAddedSuccessfully": "에이전트를 성공적으로 추가했습니다.",
|
||||||
|
"removeAgent": "에이전트 삭제",
|
||||||
|
"removeAgentMsg": "정말로 이 에이전트를 삭제하시겠습니까?",
|
||||||
|
"dockgeAgent": "Dockge 에이전트",
|
||||||
|
"currentEndpoint": "현재",
|
||||||
|
"connecting": "연결 중",
|
||||||
|
"agentRemovedSuccessfully": "에이전트를 성공적으로 삭제했습니다.",
|
||||||
|
"LongSyntaxNotSupported": "긴 문법은 여기서 지원되지 않습니다. YAML 에디터를 사용하세요."
|
||||||
}
|
}
|
||||||
|
34
frontend/src/lang/nb_NO.json
Normal file
34
frontend/src/lang/nb_NO.json
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"Create your admin account": "Lag din administrator konto",
|
||||||
|
"authIncorrectCreds": "Brukernavn eller passord stemmer ikke.",
|
||||||
|
"PasswordsDoNotMatch": "Passord stemmer ikke.",
|
||||||
|
"Repeat Password": "Gjenta passord",
|
||||||
|
"Create": "Lag",
|
||||||
|
"signedInDisp": "Logg in som {0}",
|
||||||
|
"signedInDispDisabled": "Auth deaktivert.",
|
||||||
|
"home": "Hjem",
|
||||||
|
"console": "Konsoll",
|
||||||
|
"registry": "Register",
|
||||||
|
"compose": "Skriv",
|
||||||
|
"addFirstStackMsg": "Lag din første stack!",
|
||||||
|
"stackName": "Navn på stack",
|
||||||
|
"deployStack": "Utplassere",
|
||||||
|
"deleteStack": "Slett",
|
||||||
|
"stopStack": "Stoppe",
|
||||||
|
"restartStack": "Omstart",
|
||||||
|
"updateStack": "Oppdater",
|
||||||
|
"downStack": "Stop & Inaktiver",
|
||||||
|
"editStack": "Rediger",
|
||||||
|
"discardStack": "Kast",
|
||||||
|
"saveStackDraft": "Lagre",
|
||||||
|
"notAvailableShort": "N/A",
|
||||||
|
"deleteStackMsg": "Er du sikker på at du vil slette denne stacken?",
|
||||||
|
"stackNotManagedByDockgeMsg": "Denne stacken er ikke styrt av Dockge.",
|
||||||
|
"primaryHostname": "Primært vertsnavn",
|
||||||
|
"general": "Generell",
|
||||||
|
"container": "Container | Containers",
|
||||||
|
"scanFolder": "Skann Stacks mappe",
|
||||||
|
"dockerImage": "Bilde",
|
||||||
|
"languageName": "Engelsk",
|
||||||
|
"startStack": "Start"
|
||||||
|
}
|
@ -1,14 +1,14 @@
|
|||||||
{
|
{
|
||||||
"languageName": "Nederlands",
|
"languageName": "Nederlands",
|
||||||
"authIncorrectCreds": "Onjuiste gebruikersnaam of wachtwoord.",
|
"authIncorrectCreds": "Onjuiste gebruikersnaam of wachtwoord.",
|
||||||
"PasswordsDoNotMatch": "Paswoorden komen niet overeen.",
|
"PasswordsDoNotMatch": "Wachtwoorden komen niet overeen.",
|
||||||
"Repeat Password": "Herhaal wachtwoord",
|
"Repeat Password": "Herhaal wachtwoord",
|
||||||
"Create": "Aanmaken",
|
"Create": "Aanmaken",
|
||||||
"signedInDisp": "Ingelogd als {0}",
|
"signedInDisp": "Ingelogd als {0}",
|
||||||
"home": "Startpagina",
|
"home": "Home",
|
||||||
"console": "Console",
|
"console": "Console",
|
||||||
"registry": "Register",
|
"registry": "Register",
|
||||||
"compose": "Samenstellen",
|
"compose": "Nieuwe stack",
|
||||||
"stackName": "Stack naam",
|
"stackName": "Stack naam",
|
||||||
"deployStack": "Opzetten",
|
"deployStack": "Opzetten",
|
||||||
"deleteStack": "Verwijder",
|
"deleteStack": "Verwijder",
|
||||||
@ -16,11 +16,11 @@
|
|||||||
"restartStack": "Herstart",
|
"restartStack": "Herstart",
|
||||||
"updateStack": "Update",
|
"updateStack": "Update",
|
||||||
"startStack": "Start",
|
"startStack": "Start",
|
||||||
"downStack": "Stop & Down",
|
"downStack": "Stop & Afsluiten",
|
||||||
"editStack": "Bewerken",
|
"editStack": "Bewerken",
|
||||||
"discardStack": "Verwijderen",
|
"discardStack": "Verwijderen",
|
||||||
"saveStackDraft": "Opslaan",
|
"saveStackDraft": "Opslaan",
|
||||||
"notAvailableShort": "NVT",
|
"notAvailableShort": "n.v.t.",
|
||||||
"stackNotManagedByDockgeMsg": "Deze stack wordt niet beheerd door Dockge.",
|
"stackNotManagedByDockgeMsg": "Deze stack wordt niet beheerd door Dockge.",
|
||||||
"primaryHostname": "Primaire hostnaam",
|
"primaryHostname": "Primaire hostnaam",
|
||||||
"general": "Algemeen",
|
"general": "Algemeen",
|
||||||
@ -83,9 +83,9 @@
|
|||||||
"reverseProxyMsg1": "Reverse proxy in gebruik?",
|
"reverseProxyMsg1": "Reverse proxy in gebruik?",
|
||||||
"reverseProxyMsg2": "Controleer hoe te configureren voor WebSocket",
|
"reverseProxyMsg2": "Controleer hoe te configureren voor WebSocket",
|
||||||
"Cannot connect to the socket server.": "Kan geen verbinding maken met de socket server.",
|
"Cannot connect to the socket server.": "Kan geen verbinding maken met de socket server.",
|
||||||
"reconnecting...": "Herverbinden...",
|
"reconnecting...": "Herverbinden…",
|
||||||
"connecting...": "Verbinden met de socket server...",
|
"connecting...": "Verbinden met de socket server…",
|
||||||
"url": "Url(s)",
|
"url": "Adres(sen)",
|
||||||
"extra": "Extra",
|
"extra": "Extra",
|
||||||
"Create your admin account": "Creëer je beheerders-account",
|
"Create your admin account": "Creëer je beheerders-account",
|
||||||
"addFirstStackMsg": "Maak je eerste stack!",
|
"addFirstStackMsg": "Maak je eerste stack!",
|
||||||
@ -98,5 +98,19 @@
|
|||||||
"Please use this option carefully!": "Wees voorzichtig met deze optie!",
|
"Please use this option carefully!": "Wees voorzichtig met deze optie!",
|
||||||
"Also check beta release": "Controleer ook op beta releases",
|
"Also check beta release": "Controleer ook op beta releases",
|
||||||
"Convert to Compose": "Converteer naar compose",
|
"Convert to Compose": "Converteer naar compose",
|
||||||
"External Networks": "Externe netwerken"
|
"External Networks": "Externe netwerken",
|
||||||
|
"newUpdate": "Nieuwe update",
|
||||||
|
"dockgeAgent": "Dockge Agent | Dockge Agenten",
|
||||||
|
"currentEndpoint": "Huidige",
|
||||||
|
"dockgeURL": "Dockge Adres (bijv. http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "Online",
|
||||||
|
"agentOffline": "Offline",
|
||||||
|
"connecting": "Verbinden",
|
||||||
|
"connect": "Verbind",
|
||||||
|
"addAgent": "Agent toevoegen",
|
||||||
|
"agentAddedSuccessfully": "Agent toegevoegd.",
|
||||||
|
"agentRemovedSuccessfully": "Agent verwijderd.",
|
||||||
|
"removeAgent": "Verwijder agent",
|
||||||
|
"removeAgentMsg": "Weet je zeker dat je deze agent wilt verwijderen?",
|
||||||
|
"LongSyntaxNotSupported": "Lange syntax wordt hier niet ondersteund. Gebruik de YAML editor."
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
"registry": "Rejestr",
|
"registry": "Rejestr",
|
||||||
"compose": "Stwórz",
|
"compose": "Stwórz",
|
||||||
"addFirstStackMsg": "Stwórz swój pierwszy stos!",
|
"addFirstStackMsg": "Stwórz swój pierwszy stos!",
|
||||||
"stackName" : "Nazwa stosu",
|
"stackName": "Nazwa stosu",
|
||||||
"deployStack": "Wdroż",
|
"deployStack": "Wdroż",
|
||||||
"deleteStack": "Usuń",
|
"deleteStack": "Usuń",
|
||||||
"stopStack": "Zatrzymaj",
|
"stopStack": "Zatrzymaj",
|
||||||
@ -22,7 +22,7 @@
|
|||||||
"editStack": "Edytuj",
|
"editStack": "Edytuj",
|
||||||
"discardStack": "Odrzuć",
|
"discardStack": "Odrzuć",
|
||||||
"saveStackDraft": "Zapisz",
|
"saveStackDraft": "Zapisz",
|
||||||
"notAvailableShort" : "N/A",
|
"notAvailableShort": "N/A",
|
||||||
"deleteStackMsg": "Czy na pewno chcesz usunąć ten stos?",
|
"deleteStackMsg": "Czy na pewno chcesz usunąć ten stos?",
|
||||||
"stackNotManagedByDockgeMsg": "Ten stos nie jest zarządzany przez Dockge.",
|
"stackNotManagedByDockgeMsg": "Ten stos nie jest zarządzany przez Dockge.",
|
||||||
"primaryHostname": "Podstawowa nazwa hosta",
|
"primaryHostname": "Podstawowa nazwa hosta",
|
||||||
@ -90,5 +90,43 @@
|
|||||||
"Allowed commands:": "Dozwolone polecenia:",
|
"Allowed commands:": "Dozwolone polecenia:",
|
||||||
"Internal Networks": "Sieci wewnętrzne",
|
"Internal Networks": "Sieci wewnętrzne",
|
||||||
"External Networks": "Sieci zewnętrzne",
|
"External Networks": "Sieci zewnętrzne",
|
||||||
"No External Networks": "Brak sieci zewnętrznych"
|
"No External Networks": "Brak sieci zewnętrznych",
|
||||||
|
"newUpdate": "Nowa Aktualizacja",
|
||||||
|
"dockgeAgent": "Agent Dockge | Agenci Dockge",
|
||||||
|
"currentEndpoint": "Obecny",
|
||||||
|
"connecting": "Łączenie",
|
||||||
|
"connect": "Połącz",
|
||||||
|
"addAgent": "Dodaj Agenta",
|
||||||
|
"agentAddedSuccessfully": "Agent pomyślnie dodany.",
|
||||||
|
"agentRemovedSuccessfully": "Agent pomyślnie usunięty.",
|
||||||
|
"removeAgent": "Usuń Agenta",
|
||||||
|
"removeAgentMsg": "Czy na pewno usunąć tego Agenta?",
|
||||||
|
"dockgeURL": "Dockge URL (e.g. http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "Online",
|
||||||
|
"agentOffline": "Offline",
|
||||||
|
"downStack": "Zatrzymaj i Dezaktywuj",
|
||||||
|
"reverseProxyMsg1": "Używasz Serwer Proxy?",
|
||||||
|
"reverseProxyMsg2": "Sprawdź w jaki sposób skonfigurować dla WebSocketów",
|
||||||
|
"Cannot connect to the socket server.": "Brak połączenia z socket serwera.",
|
||||||
|
"connecting...": "Łączenie z socketem serwera…",
|
||||||
|
"extra": "Ekstra",
|
||||||
|
"url": "URL | URLe",
|
||||||
|
"reconnecting...": "Wznawianie połączenia…",
|
||||||
|
"LongSyntaxNotSupported": "Nieobsługiwana składnia. Użyj edytora YAML.",
|
||||||
|
"Saved": "Zapisano",
|
||||||
|
"Switch to sh": "Przełącz na sh",
|
||||||
|
"terminal": "Terminal",
|
||||||
|
"Restarted": "Zrestartowano",
|
||||||
|
"Deployed": "Wdrożono",
|
||||||
|
"Deleted": "Usunięto",
|
||||||
|
"Updated": "Zaktualizowano",
|
||||||
|
"Started": "Uruchomiono",
|
||||||
|
"Stopped": "Zatrzymano",
|
||||||
|
"Downed": "Położono",
|
||||||
|
"Lost connection to the socket server. Reconnecting...": "Utracono połączenie z socketem serwera. Ponawiam połączenie...",
|
||||||
|
"New Container Name...": "Nazwa nowego kontenera...",
|
||||||
|
"Network name...": "Nazwa sieci...",
|
||||||
|
"Select a network...": "Wybierz sieć...",
|
||||||
|
"NoNetworksAvailable": "Brak dostępnych sieci. Musisz najpierw dodać sieć wewnętrzną lub włączyć sieci zewnętrzne po prawej stronie.",
|
||||||
|
"CurrentHostname": "(Odznacze: Podążaj za aktualnym hostem)"
|
||||||
}
|
}
|
||||||
|
@ -92,11 +92,41 @@
|
|||||||
"External Networks": "Redes externas",
|
"External Networks": "Redes externas",
|
||||||
"No External Networks": "Sem redes externas",
|
"No External Networks": "Sem redes externas",
|
||||||
"reverseProxyMsg2": "Veja como configurar para WebSocket",
|
"reverseProxyMsg2": "Veja como configurar para WebSocket",
|
||||||
"downStack": "Parar & Encerrar",
|
"downStack": "Parar & Inativar",
|
||||||
"reverseProxyMsg1": "Utiliza proxy reverso?",
|
"reverseProxyMsg1": "Utiliza proxy reverso?",
|
||||||
"Cannot connect to the socket server.": "Não é possível conectar ao socket server.",
|
"Cannot connect to the socket server.": "Não é possível conectar ao socket server.",
|
||||||
"connecting...": "Conectando ao socket server…",
|
"connecting...": "Conectando ao socket server…",
|
||||||
"url": "URL | URLs",
|
"url": "URL | URLs",
|
||||||
"extra": "Extra",
|
"extra": "Extra",
|
||||||
"reconnecting...": "Reconectando…"
|
"reconnecting...": "Reconectando…",
|
||||||
|
"newUpdate": "Nova Atualização",
|
||||||
|
"dockgeAgent": "Agente Dockge | Agentes Dockge",
|
||||||
|
"currentEndpoint": "Atual",
|
||||||
|
"dockgeURL": "Dockge URL (ex. http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "Online",
|
||||||
|
"agentOffline": "Offline",
|
||||||
|
"connecting": "Conectando",
|
||||||
|
"connect": "Conectar",
|
||||||
|
"addAgent": "Adicionar agente",
|
||||||
|
"agentAddedSuccessfully": "Agente adicionado com sucesso.",
|
||||||
|
"agentRemovedSuccessfully": "Agente removido com sucesso.",
|
||||||
|
"removeAgent": "Remover Agente",
|
||||||
|
"removeAgentMsg": "Tem certeza de que deseja remover este agente?",
|
||||||
|
"LongSyntaxNotSupported": "Sintaxe longa não é suportada aqui. Por favor, use o editor de YAML.",
|
||||||
|
"Lost connection to the socket server. Reconnecting...": "Conexão perdida com o servidor de socket. Reconectando...",
|
||||||
|
"Saved": "Salvo",
|
||||||
|
"Deployed": "Implantado",
|
||||||
|
"Deleted": "Excluído",
|
||||||
|
"Updated": "Alterado",
|
||||||
|
"Started": "Iniciado",
|
||||||
|
"Stopped": "Parado",
|
||||||
|
"Restarted": "Reiniciado",
|
||||||
|
"Downed": "Finalizado",
|
||||||
|
"Switch to sh": "Mudar para sh",
|
||||||
|
"terminal": "Terminal",
|
||||||
|
"CurrentHostname": "(Não definido: seguir o nome do host atual)",
|
||||||
|
"New Container Name...": "Nome do novo container...",
|
||||||
|
"Network name...": "Nome da rede...",
|
||||||
|
"Select a network...": "Selecione uma rede...",
|
||||||
|
"NoNetworksAvailable": "Nenhuma rede disponível. Você precisa adicionar redes internas ou habilitar redes externas no lado direito primeiro."
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
"registry": "Registro",
|
"registry": "Registro",
|
||||||
"compose": "Compor",
|
"compose": "Compor",
|
||||||
"addFirstStackMsg": "Componha sua primeira pilha!",
|
"addFirstStackMsg": "Componha sua primeira pilha!",
|
||||||
"stackName" : "Nome da Pilha",
|
"stackName": "Nome da Pilha",
|
||||||
"deployStack": "Implantar",
|
"deployStack": "Implantar",
|
||||||
"deleteStack": "Excluir",
|
"deleteStack": "Excluir",
|
||||||
"stopStack": "Parar",
|
"stopStack": "Parar",
|
||||||
@ -22,7 +22,7 @@
|
|||||||
"editStack": "Editar",
|
"editStack": "Editar",
|
||||||
"discardStack": "Descartar",
|
"discardStack": "Descartar",
|
||||||
"saveStackDraft": "Salvar",
|
"saveStackDraft": "Salvar",
|
||||||
"notAvailableShort" : "N/D",
|
"notAvailableShort": "N/D",
|
||||||
"deleteStackMsg": "Tem certeza de que deseja excluir esta pilha?",
|
"deleteStackMsg": "Tem certeza de que deseja excluir esta pilha?",
|
||||||
"stackNotManagedByDockgeMsg": "Esta pilha não é gerenciada pelo Dockge.",
|
"stackNotManagedByDockgeMsg": "Esta pilha não é gerenciada pelo Dockge.",
|
||||||
"primaryHostname": "Nome do Host Primário",
|
"primaryHostname": "Nome do Host Primário",
|
||||||
@ -90,5 +90,27 @@
|
|||||||
"Allowed commands:": "Comandos permitidos:",
|
"Allowed commands:": "Comandos permitidos:",
|
||||||
"Internal Networks": "Redes Internas",
|
"Internal Networks": "Redes Internas",
|
||||||
"External Networks": "Redes Externas",
|
"External Networks": "Redes Externas",
|
||||||
"No External Networks": "Sem Redes Externas"
|
"No External Networks": "Sem Redes Externas",
|
||||||
|
"newUpdate": "Nova Atualização",
|
||||||
|
"currentEndpoint": "Atual",
|
||||||
|
"dockgeURL": "Dockge URL (e.g. http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "Online",
|
||||||
|
"agentOffline": "Offline",
|
||||||
|
"connecting": "Conectando",
|
||||||
|
"addAgent": "Adicionar Agente",
|
||||||
|
"agentAddedSuccessfully": "Agente adicionado com sucesso.",
|
||||||
|
"agentRemovedSuccessfully": "Agente removido com sucesso.",
|
||||||
|
"removeAgent": "Remover Agente",
|
||||||
|
"downStack": "Parar & Desativar",
|
||||||
|
"dockgeAgent": "Dockge Agente | Dockge Agentes",
|
||||||
|
"connect": "Conectar",
|
||||||
|
"removeAgentMsg": "Tem certeza de que deseja remover este agente?",
|
||||||
|
"reverseProxyMsg1": "Usando um Proxy Reverso?",
|
||||||
|
"reverseProxyMsg2": "Verifique para configurá-lo como WebSocket",
|
||||||
|
"Cannot connect to the socket server.": "Não é possível se conectar ao servidor socket.",
|
||||||
|
"url": "URL | URL's",
|
||||||
|
"extra": "Extra",
|
||||||
|
"reconnecting...": "Reconectando…",
|
||||||
|
"connecting...": "Conectando ao servidor de socket…",
|
||||||
|
"LongSyntaxNotSupported": "Sintaxes longas não são suportadas aqui. Por favor, utilize um editor YAML."
|
||||||
}
|
}
|
||||||
|
@ -86,11 +86,11 @@
|
|||||||
"reverseProxyMsg1": "Folosești un proxy invers?",
|
"reverseProxyMsg1": "Folosești un proxy invers?",
|
||||||
"reverseProxyMsg2": "Verificați cum să-l configurați pentru WebSocket",
|
"reverseProxyMsg2": "Verificați cum să-l configurați pentru WebSocket",
|
||||||
"Cannot connect to the socket server.": "Nu se poate conecta la serverul socket.",
|
"Cannot connect to the socket server.": "Nu se poate conecta la serverul socket.",
|
||||||
"reconnecting...": "Reconectare...",
|
"reconnecting...": "Reconectare…",
|
||||||
"connecting...": "Se conectează la serverul socket...",
|
"connecting...": "Se conectează la serverul socket…",
|
||||||
"url": "URL | URLs",
|
"url": "URL | URLs",
|
||||||
"extra": "Suplimentar",
|
"extra": "Suplimentar",
|
||||||
"downStack": "Opriți & Coborâți",
|
"downStack": "Opriți & Inactiv",
|
||||||
"saveStackDraft": "Salvați",
|
"saveStackDraft": "Salvați",
|
||||||
"restartPolicyUnlessStopped": "Dacă nu este oprit",
|
"restartPolicyUnlessStopped": "Dacă nu este oprit",
|
||||||
"environmentVariable": "Variabila de mediu | Variabile de mediu",
|
"environmentVariable": "Variabila de mediu | Variabile de mediu",
|
||||||
@ -98,5 +98,35 @@
|
|||||||
"Please use this option carefully!": "Vă rugăm să utilizați această opțiune cu atenție!",
|
"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ă",
|
"Show update if available": "Afișează actualizarea dacă este disponibilă",
|
||||||
"disableauth.message1": "Sigur doriți să <strong>dezactivați autentificarea</strong>?",
|
"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."
|
"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.",
|
||||||
|
"newUpdate": "Actualizare nouă",
|
||||||
|
"dockgeAgent": "Agent Dockge | Agenții Dockge",
|
||||||
|
"currentEndpoint": "Actual",
|
||||||
|
"dockgeURL": "Dockge URL (de ex. http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "Online",
|
||||||
|
"agentOffline": "Offline",
|
||||||
|
"connecting": "Se conectează",
|
||||||
|
"addAgent": "Adaugă Agent",
|
||||||
|
"connect": "Conectează",
|
||||||
|
"agentRemovedSuccessfully": "Agentul a fost eliminat cu succes.",
|
||||||
|
"removeAgent": "Șterge Agentul",
|
||||||
|
"removeAgentMsg": "Ești sigur că vrei să elimini acest agent?",
|
||||||
|
"LongSyntaxNotSupported": "Sintaxa lungă nu este acceptată aici. Vă rugăm să utilizați editorul YAML.",
|
||||||
|
"agentAddedSuccessfully": "Agentul a fost adăugat cu succes.",
|
||||||
|
"Lost connection to the socket server. Reconnecting...": "S-a pierdut conexiunea cu serverul socket. Se reconectează...",
|
||||||
|
"Saved": "Salvat",
|
||||||
|
"Deployed": "Lansat",
|
||||||
|
"Deleted": "Șters",
|
||||||
|
"Updated": "Actualizat",
|
||||||
|
"Started": "Pornit",
|
||||||
|
"Stopped": "Oprit",
|
||||||
|
"Restarted": "Repornit",
|
||||||
|
"Downed": "Coborât",
|
||||||
|
"Switch to sh": "Schimbă la sh",
|
||||||
|
"terminal": "Terminal",
|
||||||
|
"CurrentHostname": "(Nesetat: Urmăriți numele de host curent)",
|
||||||
|
"New Container Name...": "Nume nou de container...",
|
||||||
|
"Network name...": "Numele rețelei...",
|
||||||
|
"Select a network...": "Selectați o rețea...",
|
||||||
|
"NoNetworksAvailable": "Nu există rețele disponibile. Mai întâi trebuie să adăugați rețele interne sau să activați rețele externe în partea dreaptă."
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"languageName": "Русский",
|
"languageName": "Русский",
|
||||||
"Create your admin account": "Создайте учетку администратора",
|
"Create your admin account": "Создайте учетную запись администратора",
|
||||||
"authIncorrectCreds": "Неверный логин или пароль.",
|
"authIncorrectCreds": "Неверный логин или пароль.",
|
||||||
"PasswordsDoNotMatch": "Пароль не совпадает.",
|
"PasswordsDoNotMatch": "Пароли не совпадают.",
|
||||||
"Repeat Password": "Повторите пароль",
|
"Repeat Password": "Повторите пароль",
|
||||||
"Create": "Создать",
|
"Create": "Создать",
|
||||||
"signedInDisp": "Авторизован как {0}",
|
"signedInDisp": "Авторизован как {0}",
|
||||||
@ -10,7 +10,7 @@
|
|||||||
"home": "Главная",
|
"home": "Главная",
|
||||||
"console": "Консоль",
|
"console": "Консоль",
|
||||||
"registry": "Реестр (Registry)",
|
"registry": "Реестр (Registry)",
|
||||||
"compose": "Составить (Compose)",
|
"compose": "Compose",
|
||||||
"addFirstStackMsg": "Создайте свой первый стек!",
|
"addFirstStackMsg": "Создайте свой первый стек!",
|
||||||
"stackName": "Имя стека",
|
"stackName": "Имя стека",
|
||||||
"deployStack": "Развернуть",
|
"deployStack": "Развернуть",
|
||||||
@ -24,9 +24,9 @@
|
|||||||
"saveStackDraft": "Сохранить",
|
"saveStackDraft": "Сохранить",
|
||||||
"notAvailableShort": "Н/Д",
|
"notAvailableShort": "Н/Д",
|
||||||
"deleteStackMsg": "Вы уверены что хотите удалить этот стек?",
|
"deleteStackMsg": "Вы уверены что хотите удалить этот стек?",
|
||||||
"stackNotManagedByDockgeMsg": "Данный стек не обслуживается Dockge.",
|
"stackNotManagedByDockgeMsg": "Данный стек не управляется Dockge.",
|
||||||
"primaryHostname": "Имя хоста",
|
"primaryHostname": "Имя хоста",
|
||||||
"general": "Главное",
|
"general": "Основные",
|
||||||
"container": "Контейнер | Контейнеры",
|
"container": "Контейнер | Контейнеры",
|
||||||
"scanFolder": "Сканировать папку стеков",
|
"scanFolder": "Сканировать папку стеков",
|
||||||
"dockerImage": "Образ",
|
"dockerImage": "Образ",
|
||||||
@ -43,12 +43,12 @@
|
|||||||
"dependsOn": "Зависимость контейнера | Зависимости контейнера",
|
"dependsOn": "Зависимость контейнера | Зависимости контейнера",
|
||||||
"addListItem": "Добавить {0}",
|
"addListItem": "Добавить {0}",
|
||||||
"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": "Auto Get",
|
"autoGet": "Авто",
|
||||||
"add": "Добавить",
|
"add": "Добавить",
|
||||||
"Edit": "Изменить",
|
"Edit": "Изменить",
|
||||||
"applyToYAML": "Применить к YAML",
|
"applyToYAML": "Применить к YAML",
|
||||||
@ -62,16 +62,16 @@
|
|||||||
"New Password": "Новый пароль",
|
"New Password": "Новый пароль",
|
||||||
"Repeat New Password": "Повторите новый пароль",
|
"Repeat New Password": "Повторите новый пароль",
|
||||||
"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": "Проверьте обновление на GitHub",
|
"Check Update On GitHub": "Проверить обновления на GitHub",
|
||||||
"Show update if available": "Показать обновление, если оно доступно",
|
"Show update if available": "Показать обновление, если оно доступно",
|
||||||
"Also check beta release": "Также проверьте бета-версию",
|
"Also check beta release": "Получать бета-версии",
|
||||||
"Remember me": "Запомнить меня",
|
"Remember me": "Запомнить меня",
|
||||||
"Login": "Логин",
|
"Login": "Логин",
|
||||||
"Username": "Имя пользователя",
|
"Username": "Имя пользователя",
|
||||||
@ -80,10 +80,10 @@
|
|||||||
"Logout": "Выйти",
|
"Logout": "Выйти",
|
||||||
"Lowercase only": "Только нижний регистр",
|
"Lowercase only": "Только нижний регистр",
|
||||||
"Convert to Compose": "Преобразовать в Compose",
|
"Convert to Compose": "Преобразовать в Compose",
|
||||||
"Docker Run": "Запустить Docker",
|
"Docker Run": "Docker Run",
|
||||||
"active": "активный",
|
"active": "акт.",
|
||||||
"exited": "завершенный",
|
"exited": "ост.",
|
||||||
"inactive": "неактинвый",
|
"inactive": "неакт.",
|
||||||
"Appearance": "Внешний вид",
|
"Appearance": "Внешний вид",
|
||||||
"Security": "Безопасность",
|
"Security": "Безопасность",
|
||||||
"About": "О продукте",
|
"About": "О продукте",
|
||||||
@ -91,12 +91,26 @@
|
|||||||
"Internal Networks": "Внутренние сети",
|
"Internal Networks": "Внутренние сети",
|
||||||
"External Networks": "Внешние сети",
|
"External Networks": "Внешние сети",
|
||||||
"No External Networks": "Нет внешних сетей",
|
"No External Networks": "Нет внешних сетей",
|
||||||
"downStack": "Остановить и выключить",
|
"downStack": "Остановить и деактивировать",
|
||||||
"reverseProxyMsg1": "Использовать Реверс Прокси?",
|
"reverseProxyMsg1": "Используете обратный прокси?",
|
||||||
"reconnecting...": "Переподключение…",
|
"reconnecting...": "Переподключение…",
|
||||||
"Cannot connect to the socket server.": "Не удается подключиться к серверу сокетов.",
|
"Cannot connect to the socket server.": "Не удается подключиться к сокет-серверу.",
|
||||||
"url": "URL адрес(а)",
|
"url": "URL-адрес | URL-адреса",
|
||||||
"extra": "Дополнительно",
|
"extra": "Дополнительно",
|
||||||
"reverseProxyMsg2": "Проверьте, как настроить его для WebSocket",
|
"reverseProxyMsg2": "Проверьте, как настроить его для WebSocket",
|
||||||
"connecting...": "Подключение к серверу сокетов…"
|
"connecting...": "Подключение к сокет-серверу…",
|
||||||
|
"newUpdate": "Доступно обновление",
|
||||||
|
"currentEndpoint": "Текущий",
|
||||||
|
"agentOnline": "В сети",
|
||||||
|
"agentOffline": "Не в сети",
|
||||||
|
"connecting": "Подключение",
|
||||||
|
"connect": "Подключить",
|
||||||
|
"addAgent": "Добавить Агента",
|
||||||
|
"agentAddedSuccessfully": "Агент успешно добавлен.",
|
||||||
|
"removeAgent": "Удалить агента",
|
||||||
|
"removeAgentMsg": "Вы уверены, что хотите удалить этого агента?",
|
||||||
|
"dockgeAgent": "Агент Dockge | Агенты Dockge",
|
||||||
|
"dockgeURL": "URL-адрес Dockge (например: http://127.0.0.1:5001)",
|
||||||
|
"agentRemovedSuccessfully": "Агент успешно удален.",
|
||||||
|
"LongSyntaxNotSupported": "Длинный синтаксис здесь не поддерживается. Пожалуйста, используйте редактор YAML."
|
||||||
}
|
}
|
||||||
|
@ -91,9 +91,26 @@
|
|||||||
"Internal Networks": "Notranja omrežja",
|
"Internal Networks": "Notranja omrežja",
|
||||||
"External Networks": "Zunanja omrežja",
|
"External Networks": "Zunanja omrežja",
|
||||||
"No External Networks": "Ni zunanjih omrežij",
|
"No External Networks": "Ni zunanjih omrežij",
|
||||||
"downStack": "Ustavi & Odstrani",
|
"downStack": "Ustavi & Deaktiviraj",
|
||||||
"connecting...": "Povezovanje s strežnikom…",
|
"connecting...": "Povezovanje s strežnikom…",
|
||||||
"reverseProxyMsg1": "Uporabljate obratni proxy?",
|
"reverseProxyMsg1": "Uporabljate obratni proxy?",
|
||||||
"extra": "Dodatno",
|
"extra": "Dodatno",
|
||||||
"reconnecting...": "Ponovna povezava …"
|
"reconnecting...": "Ponovna povezava …",
|
||||||
|
"newUpdate": "Nova posodobitev",
|
||||||
|
"reverseProxyMsg2": "Preverite, kako ga konfigurirati za WebSocket",
|
||||||
|
"Cannot connect to the socket server.": "Ni mogoče vzpostaviti povezave s strežnikom vtičnic.",
|
||||||
|
"url": "URL | URL-ji",
|
||||||
|
"currentEndpoint": "Trenutni",
|
||||||
|
"dockgeURL": "Dockge URL (npr. http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "Aktivno",
|
||||||
|
"agentOffline": "Neaktivno",
|
||||||
|
"connecting": "Povezujem",
|
||||||
|
"connect": "Poveži",
|
||||||
|
"addAgent": "Dodaj agenta",
|
||||||
|
"dockgeAgent": "Dockge agent | Dockge agenti",
|
||||||
|
"agentAddedSuccessfully": "Agent dodan uspešno.",
|
||||||
|
"agentRemovedSuccessfully": "Agent uspešno odstranjen.",
|
||||||
|
"removeAgent": "Odstrani agent",
|
||||||
|
"removeAgentMsg": "Ali ste prepričani, da želite odstraniti agenta?",
|
||||||
|
"LongSyntaxNotSupported": "Long syntax-a ni podprta tukaj. Prosim uporabite YAML urejevalnik."
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
"restartStack": "Starta om",
|
"restartStack": "Starta om",
|
||||||
"updateStack": "Uppdatera",
|
"updateStack": "Uppdatera",
|
||||||
"startStack": "Starta",
|
"startStack": "Starta",
|
||||||
"downStack": "Stoppa & Ner",
|
"downStack": "Stoppa & Inaktivera",
|
||||||
"editStack": "Redigera",
|
"editStack": "Redigera",
|
||||||
"discardStack": "Kasta",
|
"discardStack": "Kasta",
|
||||||
"saveStackDraft": "Spara",
|
"saveStackDraft": "Spara",
|
||||||
@ -98,5 +98,35 @@
|
|||||||
"reverseProxyMsg2": "Kontrollera hur man konfigurerar webbsocket",
|
"reverseProxyMsg2": "Kontrollera hur man konfigurerar webbsocket",
|
||||||
"url": "URL | URLer",
|
"url": "URL | URLer",
|
||||||
"extra": "Extra",
|
"extra": "Extra",
|
||||||
"reconnecting...": "Återansluter…"
|
"reconnecting...": "Återansluter…",
|
||||||
|
"newUpdate": "Ny uppdatering",
|
||||||
|
"currentEndpoint": "Nuvarande",
|
||||||
|
"dockgeURL": "Dockge URL (ex. http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "On-line",
|
||||||
|
"agentOffline": "Off-line",
|
||||||
|
"connecting": "Ansluter",
|
||||||
|
"connect": "Ansluten",
|
||||||
|
"addAgent": "Lägg till agent",
|
||||||
|
"agentRemovedSuccessfully": "Agent borttagen.",
|
||||||
|
"removeAgent": "Ta bort agent",
|
||||||
|
"removeAgentMsg": "Är du säker att du vill ta bort denna agent?",
|
||||||
|
"dockgeAgent": "Dockge agenter | Dockge agenter",
|
||||||
|
"agentAddedSuccessfully": "Agent tillagd.",
|
||||||
|
"LongSyntaxNotSupported": "Lång syntax stöds inte här. Använd YAML-redigeraren.",
|
||||||
|
"Lost connection to the socket server. Reconnecting...": "Tappade anslutning till socket-server. Återansluter...",
|
||||||
|
"Saved": "Sparad",
|
||||||
|
"Deployed": "Uppsatt",
|
||||||
|
"Deleted": "Borttagen",
|
||||||
|
"Updated": "Uppdaterad",
|
||||||
|
"Started": "Startad",
|
||||||
|
"Stopped": "Stoppad",
|
||||||
|
"Restarted": "Omstartad",
|
||||||
|
"Downed": "Nedstängd",
|
||||||
|
"Switch to sh": "Byt till sh",
|
||||||
|
"terminal": "Terminal",
|
||||||
|
"CurrentHostname": "(Ej angedd: Följ nuvarande värdnamn)",
|
||||||
|
"New Container Name...": "Nytt kontainernamn...",
|
||||||
|
"Network name...": "Nätverksnamn...",
|
||||||
|
"Select a network...": "Välj ett nätverk...",
|
||||||
|
"NoNetworksAvailable": "Inga nätverk tillgängliga. Du måste lägga till interna nätverk eller aktivera externa nätverk på högra sidan först."
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
{
|
{
|
||||||
"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": "ปิดใช้งาน Auth",
|
"signedInDispDisabled": "ปิดใช้งาน Auth",
|
||||||
"home": "หน้าหลักe",
|
"home": "หน้าหลัก",
|
||||||
"console": "คอนโซล",
|
"console": "คอนโซล",
|
||||||
"registry": "Registry",
|
"registry": "Registry",
|
||||||
"compose": "Compose",
|
"compose": "Compose",
|
||||||
"addFirstStackMsg": "Compose stack แรกของคุณ",
|
"addFirstStackMsg": "Compose stack แรกของคุณ!",
|
||||||
"stackName": "ชื่อ Stack",
|
"stackName": "ชื่อ Stack",
|
||||||
"deployStack": "ปรับใช้",
|
"deployStack": "ปรับใช้",
|
||||||
"deleteStack": "ลบ",
|
"deleteStack": "ลบ",
|
||||||
@ -19,7 +19,7 @@
|
|||||||
"restartStack": "เริ่มใหม่",
|
"restartStack": "เริ่มใหม่",
|
||||||
"updateStack": "อัปเดต",
|
"updateStack": "อัปเดต",
|
||||||
"startStack": "เริ่มต้น",
|
"startStack": "เริ่มต้น",
|
||||||
"downStack": "หยุดและปิด",
|
"downStack": "หยุดการทำงาน",
|
||||||
"editStack": "แก้ไข",
|
"editStack": "แก้ไข",
|
||||||
"discardStack": "ยกเลิก",
|
"discardStack": "ยกเลิก",
|
||||||
"saveStackDraft": "บันทึก",
|
"saveStackDraft": "บันทึก",
|
||||||
@ -98,5 +98,19 @@
|
|||||||
"connecting...": "กำลังเชื่อมต่อกับเซิร์ฟเวอร์ socket…",
|
"connecting...": "กำลังเชื่อมต่อกับเซิร์ฟเวอร์ socket…",
|
||||||
"url": "URL | URLs",
|
"url": "URL | URLs",
|
||||||
"extra": "พิเศษ",
|
"extra": "พิเศษ",
|
||||||
"reconnecting...": "กำลังเชื่อมต่อใหม่…"
|
"reconnecting...": "กำลังเชื่อมต่อใหม่…",
|
||||||
|
"newUpdate": "อัปเดตใหม่",
|
||||||
|
"dockgeAgent": "เอเย่นต์ Dockge | เอเย่นต์ Dockge",
|
||||||
|
"currentEndpoint": "ปัจุบัน",
|
||||||
|
"agentOnline": "ออนไลน์",
|
||||||
|
"agentOffline": "ออฟไลน์",
|
||||||
|
"connecting": "กำลังเชื่อมต่อ",
|
||||||
|
"connect": "เชื่อมต่อ",
|
||||||
|
"addAgent": "เพิ่มเอเย่นต์",
|
||||||
|
"agentAddedSuccessfully": "เพิ่มเอเย่นต์สำเร็จ",
|
||||||
|
"agentRemovedSuccessfully": "ลบเอเย่นต์สำเร็จ",
|
||||||
|
"removeAgent": "ลบเอเย่นต์",
|
||||||
|
"removeAgentMsg": "คุณแน่ใจหรือไม่ที่จะลบเอเย่นต์นี้?",
|
||||||
|
"dockgeURL": "ลิ้งก์ Dockge (เช่น http://127.0.0.1:5001)",
|
||||||
|
"LongSyntaxNotSupported": "Syntax แบบยาสไม่รองรับที่นี่ กรุณาใช้ตัวแก้ไข YAML"
|
||||||
}
|
}
|
||||||
|
@ -7,22 +7,22 @@
|
|||||||
"Create": "Oluştur",
|
"Create": "Oluştur",
|
||||||
"signedInDisp": "{0} olarak oturum açıldı",
|
"signedInDisp": "{0} olarak oturum açıldı",
|
||||||
"signedInDispDisabled": "Yetkilendirme Devre Dışı.",
|
"signedInDispDisabled": "Yetkilendirme Devre Dışı.",
|
||||||
"home": "Anasayfa",
|
"home": "Ana Sayfa",
|
||||||
"console": "Konsol",
|
"console": "Konsol",
|
||||||
"registry": "Kayıt Defteri",
|
"registry": "Kayıt Defteri",
|
||||||
"compose": "Compose",
|
"compose": "Oluştur",
|
||||||
"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ğıt",
|
||||||
"deleteStack": "Sil",
|
"deleteStack": "Sil",
|
||||||
"stopStack": "Dudur",
|
"stopStack": "Durdur",
|
||||||
"restartStack": "Yeniden Başlat",
|
"restartStack": "Yeniden Başlat",
|
||||||
"updateStack": "Güncelle",
|
"updateStack": "Güncelle",
|
||||||
"startStack": "Başlat",
|
"startStack": "Başlat",
|
||||||
"editStack": "Düzenle",
|
"editStack": "Düzenle",
|
||||||
"discardStack": "Çıkar",
|
"discardStack": "İptal Et",
|
||||||
"saveStackDraft": "Kaydet",
|
"saveStackDraft": "Kaydet",
|
||||||
"notAvailableShort": "N/A",
|
"notAvailableShort": "YOK",
|
||||||
"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ı",
|
||||||
@ -45,19 +45,19 @@
|
|||||||
"deleteContainer": "Sil",
|
"deleteContainer": "Sil",
|
||||||
"addContainer": "Konteyner Ekle",
|
"addContainer": "Konteyner Ekle",
|
||||||
"addNetwork": "Ağ Ekle",
|
"addNetwork": "Ağ Ekle",
|
||||||
"disableauth.message1": "<strong>Kimlik doğrulamayı devre dışı</strong> bırakmak istediğinizden emin misiniz?",
|
"disableauth.message1": "<strong>Kimlik doğrulamayı devre dışı bırakmak</strong> istediğinizden emin misiniz?",
|
||||||
"disableauth.message2": "Cloudflare Access, Authelia veya diğer kimlik doğrulama mekanizmaları gibi Uptime Kuma'nın önünde <strong>üçüncü taraf kimlik doğrulaması uygulamak</strong> istediğiniz senaryolar için tasarlanmıştır.",
|
"disableauth.message2": "Cloudflare Access, Authelia veya diğer kimlik doğrulama mekanizmaları Dockge önünde <strong>üçüncü taraf kimlik doğrulaması uygulamak</strong> istediğiniz senaryolar için tasarlanmıştır.",
|
||||||
"passwordNotMatchMsg": "Tekrarlanan parola eşleşmiyor.",
|
"passwordNotMatchMsg": "Tekrarlanan parola eşleşmiyor.",
|
||||||
"autoGet": "Otomatik Al",
|
"autoGet": "Otomatik Al",
|
||||||
"add": "Ekle",
|
"add": "Ekle",
|
||||||
"Edit": "Düzenle",
|
"Edit": "Düzenle",
|
||||||
"applyToYAML": "YAML'ye uygulayın",
|
"applyToYAML": "YAML dosyasına uygula",
|
||||||
"createExternalNetwork": "Oluştur",
|
"createExternalNetwork": "Oluştur",
|
||||||
"addInternalNetwork": "Ekle",
|
"addInternalNetwork": "Ekle",
|
||||||
"Save": "Kaydet",
|
"Save": "Kaydet",
|
||||||
"Language": "Dil",
|
"Language": "Dil",
|
||||||
"Current User": "Mevcut Kullanıcı",
|
"Current User": "Mevcut Kullanıcı",
|
||||||
"Change Password": "Mevcut Parola",
|
"Change Password": "Parolayı Değiştir",
|
||||||
"Current Password": "Mevcut Parola",
|
"Current Password": "Mevcut Parola",
|
||||||
"New Password": "Yeni Parola",
|
"New Password": "Yeni Parola",
|
||||||
"Repeat New Password": "Yeni Parolayı Tekrarla",
|
"Repeat New Password": "Yeni Parolayı Tekrarla",
|
||||||
@ -66,31 +66,31 @@
|
|||||||
"Please use this option carefully!": "Lütfen bu seçeneği dikkatli kullanın!",
|
"Please use this option carefully!": "Lütfen bu seçeneği dikkatli kullanın!",
|
||||||
"Enable Auth": "Kimlik Doğrulamayı Etkinleştir",
|
"Enable Auth": "Kimlik Doğrulamayı Etkinleştir",
|
||||||
"Disable Auth": "Kimlik Doğrulamayı Devre Dışı Bırak",
|
"Disable Auth": "Kimlik Doğrulamayı Devre Dışı Bırak",
|
||||||
"I understand, please disable": "Anlıyorum, lütfen devre dışı bırakın",
|
"I understand, please disable": "Anlıyorum, lütfen devre dışı bırak",
|
||||||
"Leave": "Ayrıl",
|
"Leave": "Ayrıl",
|
||||||
"Frontend Version": "Frontend Versiyon",
|
"Frontend Version": "Ön Uç Sürümü",
|
||||||
"Check Update On GitHub": "GitHub'da Güncellemeyi Kontrol Edin",
|
"Check Update On GitHub": "GitHub'da Güncellemeyi Kontrol Edin",
|
||||||
"Show update if available": "Varsa güncellemeyi göster",
|
"Show update if available": "Varsa güncellemeyi göster",
|
||||||
"Also check beta release": "Ayrıca beta sürümünü kontrol edin",
|
"Also check beta release": "Ayrıca beta sürümünü kontrol edin",
|
||||||
"Remember me": "Beni Hatırla",
|
"Remember me": "Beni hatırla",
|
||||||
"Login": "Oturum Aç",
|
"Login": "Oturum Aç",
|
||||||
"Username": "Kullanıcı Adı",
|
"Username": "Kullanıcı Adı",
|
||||||
"Password": "Parola",
|
"Password": "Parola",
|
||||||
"Settings": "Ayarlar",
|
"Settings": "Ayarlar",
|
||||||
"Logout": "Oturumu Kapat",
|
"Logout": "Oturumu Kapat",
|
||||||
"Lowercase only": "Yalnızca küçük harf",
|
"Lowercase only": "Yalnızca küçük harf",
|
||||||
"Convert to Compose": "Compose'a Dönüştür",
|
"Convert to Compose": "Compose dosyasına dönüştür",
|
||||||
"Docker Run": "Docker Run",
|
"Docker Run": "Docker Run",
|
||||||
"active": "aktif",
|
"active": "etkin",
|
||||||
"exited": "çıkış yaptı",
|
"exited": "çıktı",
|
||||||
"inactive": "aktif değil",
|
"inactive": "devre dışı",
|
||||||
"Appearance": "Görünüm",
|
"Appearance": "Görünüm",
|
||||||
"Security": "Güvenlik",
|
"Security": "Güvenlik",
|
||||||
"About": "Hakkında",
|
"About": "Hakkında",
|
||||||
"Allowed commands:": "İzin verilen komutlar:",
|
"Allowed commands:": "İzin verilen komutlar:",
|
||||||
"Internal Networks": "İç Ağlar",
|
"Internal Networks": "Dahili Ağlar",
|
||||||
"External Networks": "Dış Ağlar",
|
"External Networks": "Harici Ağlar",
|
||||||
"No External Networks": "Dış Ağ Yok",
|
"No External Networks": "Harici Ağ Yok",
|
||||||
"extra": "Ekstra",
|
"extra": "Ekstra",
|
||||||
"reverseProxyMsg1": "Ters Proxy mi kullanıyorsunuz?",
|
"reverseProxyMsg1": "Ters Proxy mi kullanıyorsunuz?",
|
||||||
"reverseProxyMsg2": "WebSocket için nasıl yapılandırma yapılacağını kontrol edin",
|
"reverseProxyMsg2": "WebSocket için nasıl yapılandırma yapılacağını kontrol edin",
|
||||||
@ -98,5 +98,35 @@
|
|||||||
"connecting...": "Soket sunucusuna bağlanıyor…",
|
"connecting...": "Soket sunucusuna bağlanıyor…",
|
||||||
"url": "URL | URL’ler",
|
"url": "URL | URL’ler",
|
||||||
"Cannot connect to the socket server.": "Soket sunucusuna bağlanılamıyor.",
|
"Cannot connect to the socket server.": "Soket sunucusuna bağlanılamıyor.",
|
||||||
"downStack": "Durdur & Kapat"
|
"downStack": "Durdur ve Devre Dışı Bırak",
|
||||||
|
"newUpdate": "Yeni Güncelleme",
|
||||||
|
"dockgeAgent": "Dockge Aracısı | Dockge Aracıları",
|
||||||
|
"currentEndpoint": "Varsayılan",
|
||||||
|
"dockgeURL": "Dockge URL (ör. http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "Çevrimiçi",
|
||||||
|
"agentOffline": "Çevrimdışı",
|
||||||
|
"connecting": "Bağlanıyor",
|
||||||
|
"connect": "Bağlan",
|
||||||
|
"addAgent": "Aracı Ekle",
|
||||||
|
"agentAddedSuccessfully": "Aracı başarıyla eklendi.",
|
||||||
|
"agentRemovedSuccessfully": "Aracı başarıyla kaldırıldı.",
|
||||||
|
"removeAgent": "Aracıyı Kaldır",
|
||||||
|
"removeAgentMsg": "Bu aracıyı kaldırmak istediğinize emin misiniz?",
|
||||||
|
"LongSyntaxNotSupported": "Uzun syntax burada desteklenmiyor. Lütfen YAML editörünü kullanın.",
|
||||||
|
"Lost connection to the socket server. Reconnecting...": "Soket sunucusuna bağlantı kesildi. Yeniden bağlanılıyor...",
|
||||||
|
"NoNetworksAvailable": "Kullanılabilir ağ yok. Önce dahili ağları eklemeniz veya sağ tarafta harici ağları etkinleştirmeniz gerekir.",
|
||||||
|
"Saved": "Kayıtlı",
|
||||||
|
"Deployed": "Deploy Edildi",
|
||||||
|
"Deleted": "Silindi",
|
||||||
|
"Updated": "Güncellendi",
|
||||||
|
"Started": "Başladı",
|
||||||
|
"Stopped": "Durdu",
|
||||||
|
"Restarted": "Yeniden başlatıldı",
|
||||||
|
"Downed": "Düştü",
|
||||||
|
"Switch to sh": "sh'ye çevir",
|
||||||
|
"terminal": "Terminal",
|
||||||
|
"CurrentHostname": "(Ayarlanmamış: Mevcut hostname'i takip et)",
|
||||||
|
"New Container Name...": "Yeni Konteyner Adı...",
|
||||||
|
"Network name...": "Ağ adı...",
|
||||||
|
"Select a network...": "Bir ağ seçin..."
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@
|
|||||||
"Logout": "Вийти",
|
"Logout": "Вийти",
|
||||||
"Lowercase only": "Тільки нижній регістр",
|
"Lowercase only": "Тільки нижній регістр",
|
||||||
"Convert to Compose": "Конвертувати в Compose",
|
"Convert to Compose": "Конвертувати в Compose",
|
||||||
"Docker Run": "Запустити Docker",
|
"Docker Run": "Docker Run",
|
||||||
"active": "активно",
|
"active": "активно",
|
||||||
"exited": "завершено",
|
"exited": "завершено",
|
||||||
"inactive": "неактивно",
|
"inactive": "неактивно",
|
||||||
@ -98,5 +98,35 @@
|
|||||||
"connecting...": "Підключення до сервера сокетів…",
|
"connecting...": "Підключення до сервера сокетів…",
|
||||||
"url": "URL-адреса | URL-адреси",
|
"url": "URL-адреса | URL-адреси",
|
||||||
"reverseProxyMsg2": "Перевірте, як налаштувати його для WebSocket",
|
"reverseProxyMsg2": "Перевірте, як налаштувати його для WebSocket",
|
||||||
"extra": "Додатково"
|
"extra": "Додатково",
|
||||||
|
"newUpdate": "Оновлення",
|
||||||
|
"currentEndpoint": "Поточний",
|
||||||
|
"agentOnline": "Онлайн",
|
||||||
|
"agentOffline": "Офлайн",
|
||||||
|
"connecting": "Підключення",
|
||||||
|
"connect": "Підключитися",
|
||||||
|
"addAgent": "Додати агент",
|
||||||
|
"removeAgent": "Видалити агент",
|
||||||
|
"dockgeAgent": "Dockge-агент | Dockge-агенти",
|
||||||
|
"dockgeURL": "Dockge URL (напр. http://127.0.0.1:5001)",
|
||||||
|
"agentRemovedSuccessfully": "Агент успішно видалено.",
|
||||||
|
"agentAddedSuccessfully": "Агент успішно додано.",
|
||||||
|
"removeAgentMsg": "Ви впевнені, що хочете видалити цей агент?",
|
||||||
|
"LongSyntaxNotSupported": "Довгий синтаксис тут не підтримується. Будь ласка, використовуйте редактор YAML.",
|
||||||
|
"Saved": "Збережено",
|
||||||
|
"Deployed": "Розгорнуто",
|
||||||
|
"Deleted": "Видалено",
|
||||||
|
"Updated": "Оновлено",
|
||||||
|
"Started": "Запущено",
|
||||||
|
"Stopped": "Зупинено",
|
||||||
|
"Downed": "Вимкнено",
|
||||||
|
"Switch to sh": "Перемкнути на sh",
|
||||||
|
"terminal": "Термінал",
|
||||||
|
"New Container Name...": "Нова назва контейнера...",
|
||||||
|
"Network name...": "Назва мережі...",
|
||||||
|
"Select a network...": "Вибрати мережу...",
|
||||||
|
"Lost connection to the socket server. Reconnecting...": "Втрачено зв'язок з сервером сокетів. Повторне підключення...",
|
||||||
|
"Restarted": "Перезапущено",
|
||||||
|
"CurrentHostname": "(Не встановлено: використовувати поточну назву хосту)",
|
||||||
|
"NoNetworksAvailable": "Немає доступних мереж. Спочатку потрібно додати внутрішні мережі або увімкнути зовнішні мережі в правій частині."
|
||||||
}
|
}
|
||||||
|
@ -97,6 +97,20 @@
|
|||||||
"connecting...": "ساکٹ سرور سے منسلک ہو رہا ہے…",
|
"connecting...": "ساکٹ سرور سے منسلک ہو رہا ہے…",
|
||||||
"url": "یو آر ایل | یو آر ایل",
|
"url": "یو آر ایل | یو آر ایل",
|
||||||
"extra": "اضافی",
|
"extra": "اضافی",
|
||||||
"downStack": "اسٹاپ اینڈ ڈاؤن",
|
"downStack": "روکیں اور غیر فعال",
|
||||||
"reverseProxyMsg2": "اسے WebSocket کے لیے ترتیب دینے کا طریقہ چیک کریں"
|
"reverseProxyMsg2": "اسے WebSocket کے لیے ترتیب دینے کا طریقہ چیک کریں",
|
||||||
|
"newUpdate": "نئی تازہ کاری",
|
||||||
|
"dockgeAgent": "ڈاکج ایجنٹ | ڈاکج ایجنٹس",
|
||||||
|
"currentEndpoint": "کرنٹ",
|
||||||
|
"dockgeURL": "Dockge URL (جیسے http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "آن لائن",
|
||||||
|
"agentOffline": "آف لائن",
|
||||||
|
"connecting": "جڑ رہا ہے",
|
||||||
|
"connect": "جڑیں",
|
||||||
|
"addAgent": "ایجنٹ شامل کریں",
|
||||||
|
"agentAddedSuccessfully": "ایجنٹ کامیابی کے ساتھ شامل ہو گیا۔",
|
||||||
|
"agentRemovedSuccessfully": "ایجنٹ کو کامیابی سے ہٹا دیا گیا۔",
|
||||||
|
"removeAgent": "ایجنٹ کو ہٹا دیں",
|
||||||
|
"removeAgentMsg": "کیا آپ واقعی اس ایجنٹ کو ہٹانا چاہتے ہیں؟",
|
||||||
|
"LongSyntaxNotSupported": "لمبا نحو یہاں تعاون یافتہ نہیں ہے۔ براہ کرم YAML ایڈیٹر استعمال کریں۔"
|
||||||
}
|
}
|
||||||
|
115
frontend/src/lang/vi.json
Normal file
115
frontend/src/lang/vi.json
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
{
|
||||||
|
"authIncorrectCreds": "Sai tên người dùng hoặc mật khẩu.",
|
||||||
|
"PasswordsDoNotMatch": "Mật khẩu không khớp.",
|
||||||
|
"Repeat Password": "Lặp Lại Mật Khẩu",
|
||||||
|
"Create": "Tạo",
|
||||||
|
"signedInDisp": "Đã đăng nhập với tư cách {0}",
|
||||||
|
"home": "Trang chủ",
|
||||||
|
"console": "Console",
|
||||||
|
"compose": "Compose",
|
||||||
|
"registry": "Registry",
|
||||||
|
"stackName": "Tên Stack",
|
||||||
|
"deployStack": "Triển khai",
|
||||||
|
"deleteStack": "Xoá",
|
||||||
|
"stopStack": "Dừng",
|
||||||
|
"restartStack": "Khởi động lại",
|
||||||
|
"signedInDispDisabled": "Đã Tắt Xác Thực Đăng Nhập.",
|
||||||
|
"startStack": "Bắt đầu",
|
||||||
|
"downStack": "Dừng & Ngưng hoạt động",
|
||||||
|
"editStack": "Chỉnh sửa",
|
||||||
|
"saveStackDraft": "Lưu",
|
||||||
|
"notAvailableShort": "N/A",
|
||||||
|
"deleteStackMsg": "Bạn có chắc chắn muốn xoá stack này?",
|
||||||
|
"primaryHostname": "Tên Host Chính",
|
||||||
|
"scanFolder": "Quét Thư Mục Stack",
|
||||||
|
"restartPolicyAlways": "Luôn Luôn",
|
||||||
|
"restartPolicyOnFailure": "Khi Có Lỗi",
|
||||||
|
"restartPolicyNo": "Không",
|
||||||
|
"environmentVariable": "Biến Môi Trường | Các Biến Môi Trường",
|
||||||
|
"restartPolicy": "Chính Sách Khởi Động Lại",
|
||||||
|
"containerName": "Tên Container",
|
||||||
|
"port": "Cổng | Cổng",
|
||||||
|
"addListItem": "Thêm {0}",
|
||||||
|
"deleteContainer": "Xoá",
|
||||||
|
"addContainer": "Thêm Container",
|
||||||
|
"addNetwork": "Thêm Mạng",
|
||||||
|
"passwordNotMatchMsg": "Mật khẩu nhập lại không khớp.",
|
||||||
|
"autoGet": "Tự Động Lấy",
|
||||||
|
"add": "Thêm",
|
||||||
|
"Edit": "Chỉnh sửa",
|
||||||
|
"applyToYAML": "Áp dụng cho YAML",
|
||||||
|
"createExternalNetwork": "Tạo",
|
||||||
|
"addInternalNetwork": "Thêm",
|
||||||
|
"Save": "Lưu",
|
||||||
|
"Language": "Ngôn ngữ",
|
||||||
|
"Current User": "Người Dùng Hiện Tại",
|
||||||
|
"Change Password": "Đổi Mật Khẩu",
|
||||||
|
"Current Password": "Mật Khẩu Hiện Tại",
|
||||||
|
"New Password": "Mật Khẩu Mới",
|
||||||
|
"Repeat New Password": "Nhập Lại Mật Khẩu Mới",
|
||||||
|
"Update Password": "Cập Nhật Mật Khẩu",
|
||||||
|
"Advanced": "Nâng cao",
|
||||||
|
"Please use this option carefully!": "Vui lòng sử dụng tuỳ chọn này cẩn thận!",
|
||||||
|
"Enable Auth": "Kích Hoạt Xác Thực Đăng Nhập",
|
||||||
|
"Disable Auth": "Vô Hiệu Xác Thực Đăng Nhập",
|
||||||
|
"I understand, please disable": "Tôi hiểu, vui lòng vô hiệu",
|
||||||
|
"Leave": "Rời",
|
||||||
|
"Frontend Version": "Phiên Bản Giao Diện Người Dùng",
|
||||||
|
"Check Update On GitHub": "Kiểm Tra Cập Nhật Trên Github",
|
||||||
|
"Also check beta release": "Kiểm tra cả bản phát hành beta",
|
||||||
|
"Remember me": "Ghi nhớ tôi",
|
||||||
|
"Login": "Đăng nhập",
|
||||||
|
"Username": "Tên người dùng",
|
||||||
|
"Password": "Mật khẩu",
|
||||||
|
"Settings": "Cài đặt",
|
||||||
|
"Logout": "Đăng xuất",
|
||||||
|
"Lowercase only": "Chỉ viết thường",
|
||||||
|
"Convert to Compose": "Chuyển đổi sang Compose",
|
||||||
|
"Docker Run": "Chạy Docker",
|
||||||
|
"active": "hoạt động",
|
||||||
|
"exited": "đã thoát",
|
||||||
|
"inactive": "không hoạt động",
|
||||||
|
"Security": "Bảo Mật",
|
||||||
|
"Appearance": "Giao Diện",
|
||||||
|
"About": "Về",
|
||||||
|
"Allowed commands:": "Các lệnh được cho phép:",
|
||||||
|
"Internal Networks": "Mạng Nội Bộ",
|
||||||
|
"External Networks": "Mạng Ngoại Vi",
|
||||||
|
"No External Networks": "Không Có Mạng Ngoại Vi",
|
||||||
|
"reverseProxyMsg1": "Đang sử dụng Reverse Proxy?",
|
||||||
|
"reverseProxyMsg2": "Xem cách để cấu hình nó cho WebSocket",
|
||||||
|
"Cannot connect to the socket server.": "Không thể kết nối tới máy chủ socket.",
|
||||||
|
"reconnecting...": "Đang kết nối lại…",
|
||||||
|
"connecting...": "Đang kết nối tới máy chủ socket…",
|
||||||
|
"url": "URL",
|
||||||
|
"extra": "Bổ sung",
|
||||||
|
"newUpdate": "Cập Nhật Mới",
|
||||||
|
"dockgeAgent": "Dockge Agent",
|
||||||
|
"currentEndpoint": "Đang sử dụng",
|
||||||
|
"dockgeURL": "URL của Dockge (v.d. http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "Trực tuyến",
|
||||||
|
"agentOffline": "Ngoại tuyến",
|
||||||
|
"connecting": "Đang kết nối",
|
||||||
|
"connect": "Kết nối",
|
||||||
|
"addAgent": "Thêm Agent",
|
||||||
|
"agentAddedSuccessfully": "Agent đã được thêm thành công.",
|
||||||
|
"agentRemovedSuccessfully": "Agent đã được xoá thành công.",
|
||||||
|
"removeAgent": "Xoá Agent",
|
||||||
|
"removeAgentMsg": "Bạn có chắc chắn muốn xoá agent này?",
|
||||||
|
"languageName": "Tiếng Việt",
|
||||||
|
"Create your admin account": "Tạo tài khoản admin của bạn",
|
||||||
|
"addFirstStackMsg": "Tạo stack đầu tiên của bạn!",
|
||||||
|
"volume": "Volume | Volume",
|
||||||
|
"updateStack": "Cập nhật",
|
||||||
|
"network": "Mạng | Mạng",
|
||||||
|
"discardStack": "Huỷ",
|
||||||
|
"stackNotManagedByDockgeMsg": "Stack này không được quản lý bởi Dockge.",
|
||||||
|
"dependsOn": "Container Phụ Thuộc | Các Container Phụ Thuộc",
|
||||||
|
"general": "Tổng Quan",
|
||||||
|
"disableauth.message1": "Bạn có chắc chắn muốn <strong>tắt xác thực đăng nhập</strong>?",
|
||||||
|
"container": "Container",
|
||||||
|
"disableauth.message2": "Nó được thiết kế trong hoàn cảnh <strong>mà bạn dự định triển khai xác thực đăng nhập bên thứ ba</strong> trước Dockge như là Cloudflare Access, Authelia hay các phương thức xác minh đăng nhập khác.",
|
||||||
|
"dockerImage": "Image",
|
||||||
|
"Show update if available": "Hiển thị cập nhật nếu có",
|
||||||
|
"restartPolicyUnlessStopped": "Trừ Khi Dừng Lại"
|
||||||
|
}
|
@ -98,5 +98,35 @@
|
|||||||
"Cannot connect to the socket server.": "无法连接到socket服务器。",
|
"Cannot connect to the socket server.": "无法连接到socket服务器。",
|
||||||
"url": "网址 | 网址",
|
"url": "网址 | 网址",
|
||||||
"extra": "额外",
|
"extra": "额外",
|
||||||
"downStack": "停止并删除"
|
"downStack": "停止并置于非活动状态",
|
||||||
|
"newUpdate": "新版本",
|
||||||
|
"dockgeURL": "Dockge地址 (例如 http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "在线",
|
||||||
|
"agentOffline": "离线",
|
||||||
|
"connecting": "连接中",
|
||||||
|
"connect": "连接",
|
||||||
|
"dockgeAgent": "Dockge代理",
|
||||||
|
"currentEndpoint": "当前",
|
||||||
|
"addAgent": "添加代理",
|
||||||
|
"agentRemovedSuccessfully": "代理移除成功。",
|
||||||
|
"removeAgent": "移除代理",
|
||||||
|
"removeAgentMsg": "您确定要移除此代理?",
|
||||||
|
"agentAddedSuccessfully": "代理添加成功。",
|
||||||
|
"LongSyntaxNotSupported": "此处不支持Long syntax,请使用YAML编辑器。",
|
||||||
|
"Lost connection to the socket server. Reconnecting...": "已断开socket服务器连接,重新连接中...",
|
||||||
|
"Saved": "已保存",
|
||||||
|
"Deployed": "已部署",
|
||||||
|
"Deleted": "已删除",
|
||||||
|
"Updated": "已更新",
|
||||||
|
"Started": "已启动",
|
||||||
|
"Stopped": "已停止",
|
||||||
|
"Restarted": "已重启",
|
||||||
|
"Switch to sh": "切换至sh",
|
||||||
|
"terminal": "终端",
|
||||||
|
"CurrentHostname": "未设置:沿用当前主机名",
|
||||||
|
"New Container Name...": "新的容器名称...",
|
||||||
|
"Network name...": "网络名称...",
|
||||||
|
"Select a network...": "选择网络...",
|
||||||
|
"NoNetworksAvailable": "网络不可用.你需要在正确的方向先添加内部网络或者启用外部网络.",
|
||||||
|
"Downed": "已宕机"
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"languageName": "繁體中文(台灣)",
|
"languageName": "繁體中文 (台灣)",
|
||||||
"Create your admin account": "建立您的管理員帳號",
|
"Create your admin account": "建立您的管理員帳號",
|
||||||
"authIncorrectCreds": "使用者名稱或密碼錯誤。",
|
"authIncorrectCreds": "使用者名稱或密碼錯誤。",
|
||||||
"PasswordsDoNotMatch": "兩次輸入的密碼不一致。",
|
"PasswordsDoNotMatch": "兩次輸入的密碼不一致。",
|
||||||
@ -10,7 +10,7 @@
|
|||||||
"home": "首頁",
|
"home": "首頁",
|
||||||
"console": "主控台",
|
"console": "主控台",
|
||||||
"registry": "映像倉庫",
|
"registry": "映像倉庫",
|
||||||
"compose": "Compose",
|
"compose": "撰寫",
|
||||||
"addFirstStackMsg": "組合您的第一個堆疊!",
|
"addFirstStackMsg": "組合您的第一個堆疊!",
|
||||||
"stackName": "堆疊名稱",
|
"stackName": "堆疊名稱",
|
||||||
"deployStack": "部署",
|
"deployStack": "部署",
|
||||||
@ -20,7 +20,7 @@
|
|||||||
"updateStack": "更新",
|
"updateStack": "更新",
|
||||||
"startStack": "啟動",
|
"startStack": "啟動",
|
||||||
"editStack": "編輯",
|
"editStack": "編輯",
|
||||||
"discardStack": "捨棄",
|
"discardStack": "丟棄",
|
||||||
"saveStackDraft": "儲存",
|
"saveStackDraft": "儲存",
|
||||||
"notAvailableShort": "不可用",
|
"notAvailableShort": "不可用",
|
||||||
"deleteStackMsg": "您確定要刪除這個堆疊嗎?",
|
"deleteStackMsg": "您確定要刪除這個堆疊嗎?",
|
||||||
@ -46,7 +46,7 @@
|
|||||||
"addContainer": "新增容器",
|
"addContainer": "新增容器",
|
||||||
"addNetwork": "新增網路",
|
"addNetwork": "新增網路",
|
||||||
"disableauth.message1": "您確定要<strong>停用身份驗證</strong>嗎?",
|
"disableauth.message1": "您確定要<strong>停用身份驗證</strong>嗎?",
|
||||||
"disableauth.message2": "該選項設計用於某些場景,<strong>例如在 Dockge 之上接入第三方認證</strong>,如 Cloudflare Access、Authelia 或其他認證機制。如果您不清楚這個選項的作用,請不要停用驗證!",
|
"disableauth.message2": "該選項設計用於某些場景,<strong>例如在 Dockge 之介接接第三方身份驗證</strong>,例如 Cloudflare Access、Authelia 或其他身份驗證機制。",
|
||||||
"passwordNotMatchMsg": "兩次輸入的密碼不一致。",
|
"passwordNotMatchMsg": "兩次輸入的密碼不一致。",
|
||||||
"autoGet": "自動取得",
|
"autoGet": "自動取得",
|
||||||
"add": "新增",
|
"add": "新增",
|
||||||
@ -91,12 +91,42 @@
|
|||||||
"Internal Networks": "內部網路",
|
"Internal Networks": "內部網路",
|
||||||
"External Networks": "外部網路",
|
"External Networks": "外部網路",
|
||||||
"No External Networks": "無外部網路",
|
"No External Networks": "無外部網路",
|
||||||
"downStack": "停止",
|
"downStack": "停止及未啟動化",
|
||||||
"reverseProxyMsg1": "在使用反向代理嗎?",
|
"reverseProxyMsg1": "在使用反向代理嗎?",
|
||||||
"reverseProxyMsg2": "點擊這裡了解如何為 WebSocket 配置反向代理",
|
"reverseProxyMsg2": "點擊這裡了解如何為 WebSocket 配置反向代理",
|
||||||
"Cannot connect to the socket server.": "無法連接到 Socket 伺服器。",
|
"Cannot connect to the socket server.": "無法連接到 Socket 伺服器。",
|
||||||
"reconnecting...": "重新連線中…",
|
"reconnecting...": "重新連線中…",
|
||||||
"connecting...": "連線至 Socket 伺服器中…",
|
"connecting...": "連線至 Socket 伺服器中…",
|
||||||
"url": "網址 | 網址",
|
"url": "網址 | 網址",
|
||||||
"extra": "額外"
|
"extra": "額外",
|
||||||
|
"newUpdate": "新版本",
|
||||||
|
"currentEndpoint": "目前",
|
||||||
|
"dockgeURL": "Dockge URL(例如:http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "線上",
|
||||||
|
"connecting": "正在連線",
|
||||||
|
"agentOffline": "離線",
|
||||||
|
"Lost connection to the socket server. Reconnecting...": "與伺服器斷線。正在重新連線...",
|
||||||
|
"dockgeAgent": "Dockge代理 | Dockge代理",
|
||||||
|
"Saved": "已儲存",
|
||||||
|
"Switch to sh": "切換到 sh",
|
||||||
|
"NoNetworksAvailable": "沒有可以使用的網路。您需要先在右側新增內部網路或啟用外部網路。",
|
||||||
|
"LongSyntaxNotSupported": "這裡不支援長語法。請使用 YAML 編輯器。",
|
||||||
|
"connect": "連接",
|
||||||
|
"addAgent": "新增代理",
|
||||||
|
"agentAddedSuccessfully": "代理新增成功。",
|
||||||
|
"agentRemovedSuccessfully": "代理刪除成功。",
|
||||||
|
"Deployed": "已佈署",
|
||||||
|
"Deleted": "已刪除",
|
||||||
|
"Updated": "已更新",
|
||||||
|
"Started": "開始",
|
||||||
|
"Stopped": "已停止",
|
||||||
|
"Restarted": "重新啟動",
|
||||||
|
"Downed": "斷線",
|
||||||
|
"terminal": "終端",
|
||||||
|
"CurrentHostname": "(取消設定:依據目前主機名稱)",
|
||||||
|
"New Container Name...": "新容器名稱...",
|
||||||
|
"Network name...": "網路名稱...",
|
||||||
|
"Select a network...": "選擇網路...",
|
||||||
|
"removeAgent": "刪除代理",
|
||||||
|
"removeAgentMsg": "您確定要刪除這個代理嗎?"
|
||||||
}
|
}
|
||||||
|
@ -98,6 +98,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import Login from "../components/Login.vue";
|
import Login from "../components/Login.vue";
|
||||||
import { compareVersions } from "compare-versions";
|
import { compareVersions } from "compare-versions";
|
||||||
|
import { ALL_ENDPOINTS } from "../../../common/util-common";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
||||||
@ -145,7 +146,7 @@ export default {
|
|||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
scanFolder() {
|
scanFolder() {
|
||||||
this.$root.getSocket().emit("requestStackList", (res) => {
|
this.$root.emitAgent(ALL_ENDPOINTS, "requestStackList", (res) => {
|
||||||
this.$root.toastRes(res);
|
this.$root.toastRes(res);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Dayjs init inside this, so it has to be the first import
|
// Dayjs init inside this, so it has to be the first import
|
||||||
import "../../backend/util-common";
|
import "../../common/util-common";
|
||||||
|
|
||||||
import { createApp, defineComponent, h } from "vue";
|
import { createApp, defineComponent, h } from "vue";
|
||||||
import App from "./App.vue";
|
import App from "./App.vue";
|
||||||
|
@ -3,6 +3,7 @@ import { Socket } from "socket.io-client";
|
|||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
import jwtDecode from "jwt-decode";
|
import jwtDecode from "jwt-decode";
|
||||||
import { Terminal } from "@xterm/xterm";
|
import { Terminal } from "@xterm/xterm";
|
||||||
|
import { AgentSocket } from "../../../common/agent-socket";
|
||||||
|
|
||||||
let socket : Socket;
|
let socket : Socket;
|
||||||
|
|
||||||
@ -28,16 +29,51 @@ export default defineComponent({
|
|||||||
loggedIn: false,
|
loggedIn: false,
|
||||||
allowLoginDialog: false,
|
allowLoginDialog: false,
|
||||||
username: null,
|
username: null,
|
||||||
stackList: {},
|
|
||||||
composeTemplate: "",
|
composeTemplate: "",
|
||||||
|
|
||||||
|
stackList: {},
|
||||||
|
|
||||||
|
// All stack list from all agents
|
||||||
|
allAgentStackList: {} as Record<string, object>,
|
||||||
|
|
||||||
|
// online / offline / connecting
|
||||||
|
agentStatusList: {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
// Agent List
|
||||||
|
agentList: {
|
||||||
|
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
||||||
|
agentCount() {
|
||||||
|
return Object.keys(this.agentList).length;
|
||||||
|
},
|
||||||
|
|
||||||
|
completeStackList() {
|
||||||
|
let list : Record<string, object> = {};
|
||||||
|
|
||||||
|
for (let stackName in this.stackList) {
|
||||||
|
list[stackName + "_"] = this.stackList[stackName];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let endpoint in this.allAgentStackList) {
|
||||||
|
let instance = this.allAgentStackList[endpoint];
|
||||||
|
for (let stackName in instance.stackList) {
|
||||||
|
list[stackName + "_" + endpoint] = instance.stackList[stackName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
},
|
||||||
|
|
||||||
usernameFirstChar() {
|
usernameFirstChar() {
|
||||||
if (typeof this.username == "string" && this.username.length >= 1) {
|
if (typeof this.username == "string" && this.username.length >= 1) {
|
||||||
return this.username.charAt(0).toUpperCase();
|
return this.username.charAt(0).toUpperCase();
|
||||||
} else {
|
} else {
|
||||||
return "🐻";
|
return "🐬";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -65,6 +101,15 @@ export default defineComponent({
|
|||||||
|
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|
||||||
|
"socketIO.connected"() {
|
||||||
|
if (this.socketIO.connected) {
|
||||||
|
this.agentStatusList[""] = "online";
|
||||||
|
} else {
|
||||||
|
this.agentStatusList[""] = "offline";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
remember() {
|
remember() {
|
||||||
localStorage.remember = (this.remember) ? "1" : "0";
|
localStorage.remember = (this.remember) ? "1" : "0";
|
||||||
},
|
},
|
||||||
@ -84,6 +129,15 @@ export default defineComponent({
|
|||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
||||||
|
endpointDisplayFunction(endpoint : string) {
|
||||||
|
if (endpoint) {
|
||||||
|
return endpoint;
|
||||||
|
} else {
|
||||||
|
return this.$t("currentEndpoint");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize connection to socket server
|
* Initialize connection to socket server
|
||||||
* @param bypass Should the check for if we
|
* @param bypass Should the check for if we
|
||||||
@ -108,8 +162,12 @@ export default defineComponent({
|
|||||||
this.socketIO.connecting = true;
|
this.socketIO.connecting = true;
|
||||||
}, 1500);
|
}, 1500);
|
||||||
|
|
||||||
socket = io(url, {
|
socket = io(url);
|
||||||
transports: [ "websocket", "polling" ]
|
|
||||||
|
// Handling events from agents
|
||||||
|
let agentSocket = new AgentSocket();
|
||||||
|
socket.on("agent", (eventName : unknown, ...args : unknown[]) => {
|
||||||
|
agentSocket.call(eventName, ...args);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("connect", () => {
|
socket.on("connect", () => {
|
||||||
@ -145,7 +203,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
socket.on("disconnect", () => {
|
socket.on("disconnect", () => {
|
||||||
console.log("disconnect");
|
console.log("disconnect");
|
||||||
this.socketIO.connectionErrorMsg = "Lost connection to the socket server. Reconnecting...";
|
this.socketIO.connectionErrorMsg = `${this.$t("Lost connection to the socket server. Reconnecting...")}`;
|
||||||
this.socketIO.connected = false;
|
this.socketIO.connected = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -177,7 +235,7 @@ export default defineComponent({
|
|||||||
this.$router.push("/setup");
|
this.$router.push("/setup");
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("terminalWrite", (terminalName, data) => {
|
agentSocket.on("terminalWrite", (terminalName, data) => {
|
||||||
const terminal = terminalMap.get(terminalName);
|
const terminal = terminalMap.get(terminalName);
|
||||||
if (!terminal) {
|
if (!terminal) {
|
||||||
//console.error("Terminal not found: " + terminalName);
|
//console.error("Terminal not found: " + terminalName);
|
||||||
@ -186,9 +244,18 @@ export default defineComponent({
|
|||||||
terminal.write(data);
|
terminal.write(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("stackList", (res) => {
|
agentSocket.on("stackList", (res) => {
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
this.stackList = res.stackList;
|
if (!res.endpoint) {
|
||||||
|
this.stackList = res.stackList;
|
||||||
|
} else {
|
||||||
|
if (!this.allAgentStackList[res.endpoint]) {
|
||||||
|
this.allAgentStackList[res.endpoint] = {
|
||||||
|
stackList: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
this.allAgentStackList[res.endpoint].stackList = res.stackList;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -203,6 +270,20 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on("agentStatus", (res) => {
|
||||||
|
this.agentStatusList[res.endpoint] = res.status;
|
||||||
|
|
||||||
|
if (res.msg) {
|
||||||
|
this.toastError(res.msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("agentList", (res) => {
|
||||||
|
if (res.ok) {
|
||||||
|
this.agentList = res.agentList;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
socket.on("refresh", () => {
|
socket.on("refresh", () => {
|
||||||
location.reload();
|
location.reload();
|
||||||
});
|
});
|
||||||
@ -220,6 +301,10 @@ export default defineComponent({
|
|||||||
return socket;
|
return socket;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
emitAgent(endpoint : string, eventName : string, ...args : unknown[]) {
|
||||||
|
this.getSocket().emit("agent", endpoint, eventName, ...args);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get payload of JWT cookie
|
* Get payload of JWT cookie
|
||||||
* @returns {(object | undefined)} JWT payload
|
* @returns {(object | undefined)} JWT payload
|
||||||
@ -310,9 +395,9 @@ export default defineComponent({
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
bindTerminal(terminalName : string, terminal : Terminal) {
|
bindTerminal(endpoint : string, terminalName : string, terminal : Terminal) {
|
||||||
// Load terminal, get terminal screen
|
// Load terminal, get terminal screen
|
||||||
socket.emit("terminalJoin", terminalName, (res) => {
|
this.emitAgent(endpoint, "terminalJoin", terminalName, (res) => {
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
terminal.write(res.buffer);
|
terminal.write(res.buffer);
|
||||||
terminalMap.set(terminalName, terminal);
|
terminalMap.set(terminalName, terminal);
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<transition name="slide-fade" appear>
|
<transition name="slide-fade" appear>
|
||||||
<div>
|
<div>
|
||||||
<h1 v-if="isAdd" class="mb-3">Compose</h1>
|
<h1 v-if="isAdd" class="mb-3">{{$t("compose")}}</h1>
|
||||||
<h1 v-else class="mb-3"><Uptime :stack="globalStack" :pill="true" /> {{ stack.name }}</h1>
|
<h1 v-else class="mb-3">
|
||||||
|
<Uptime :stack="globalStack" :pill="true" /> {{ stack.name }}
|
||||||
|
<span v-if="$root.agentCount > 1" class="agent-name">
|
||||||
|
({{ endpointDisplay }})
|
||||||
|
</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
<div v-if="stack.isManagedByDockge" class="mb-3">
|
<div v-if="stack.isManagedByDockge" class="mb-3">
|
||||||
<div class="btn-group me-2" role="group">
|
<div class="btn-group me-2" role="group">
|
||||||
@ -70,6 +75,7 @@
|
|||||||
ref="progressTerminal"
|
ref="progressTerminal"
|
||||||
class="mb-3 terminal"
|
class="mb-3 terminal"
|
||||||
:name="terminalName"
|
:name="terminalName"
|
||||||
|
:endpoint="endpoint"
|
||||||
:rows="progressTerminalRows"
|
:rows="progressTerminalRows"
|
||||||
@has-data="showProgressTerminal = true; submitted = true;"
|
@has-data="showProgressTerminal = true; submitted = true;"
|
||||||
></Terminal>
|
></Terminal>
|
||||||
@ -87,6 +93,16 @@
|
|||||||
<input id="name" v-model="stack.name" type="text" class="form-control" required @blur="stackNameToLowercase">
|
<input id="name" v-model="stack.name" type="text" class="form-control" required @blur="stackNameToLowercase">
|
||||||
<div class="form-text">{{ $t("Lowercase only") }}</div>
|
<div class="form-text">{{ $t("Lowercase only") }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Endpoint -->
|
||||||
|
<div class="mt-3">
|
||||||
|
<label for="name" class="form-label">{{ $t("dockgeAgent") }}</label>
|
||||||
|
<select v-model="stack.endpoint" class="form-select">
|
||||||
|
<option v-for="(agent, endpoint) in $root.agentList" :key="endpoint" :value="endpoint" :disabled="$root.agentStatusList[endpoint] != 'online'">
|
||||||
|
({{ $root.agentStatusList[endpoint] }}) {{ (endpoint) ? endpoint : $t("currentEndpoint") }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -96,7 +112,7 @@
|
|||||||
<div v-if="isEditMode" class="input-group mb-3">
|
<div v-if="isEditMode" class="input-group mb-3">
|
||||||
<input
|
<input
|
||||||
v-model="newContainerName"
|
v-model="newContainerName"
|
||||||
placeholder="New Container Name..."
|
:placeholder="$t(`New Container Name...`)"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
@keyup.enter="addContainer"
|
@keyup.enter="addContainer"
|
||||||
/>
|
/>
|
||||||
@ -134,14 +150,15 @@
|
|||||||
|
|
||||||
<!-- Combined Terminal Output -->
|
<!-- Combined Terminal Output -->
|
||||||
<div v-show="!isEditMode">
|
<div v-show="!isEditMode">
|
||||||
<h4 class="mb-3">Terminal</h4>
|
<h4 class="mb-3">{{$t("terminal")}}</h4>
|
||||||
<Terminal
|
<Terminal
|
||||||
ref="combinedTerminal"
|
ref="combinedTerminal"
|
||||||
class="mb-3 terminal"
|
class="mb-3 terminal"
|
||||||
:name="combinedTerminalName"
|
:name="combinedTerminalName"
|
||||||
|
:endpoint="endpoint"
|
||||||
:rows="combinedTerminalRows"
|
:rows="combinedTerminalRows"
|
||||||
:cols="combinedTerminalCols"
|
:cols="combinedTerminalCols"
|
||||||
style="height: 350px;"
|
style="height: 315px;"
|
||||||
></Terminal>
|
></Terminal>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -236,12 +253,12 @@ import {
|
|||||||
getComposeTerminalName,
|
getComposeTerminalName,
|
||||||
PROGRESS_TERMINAL_ROWS,
|
PROGRESS_TERMINAL_ROWS,
|
||||||
RUNNING
|
RUNNING
|
||||||
} from "../../../backend/util-common";
|
} from "../../../common/util-common";
|
||||||
import { BModal } from "bootstrap-vue-next";
|
import { BModal } from "bootstrap-vue-next";
|
||||||
import NetworkInput from "../components/NetworkInput.vue";
|
import NetworkInput from "../components/NetworkInput.vue";
|
||||||
import dotenv from "dotenv";
|
import dotenv from "dotenv";
|
||||||
|
|
||||||
const template = `version: "3.8"
|
const template = `
|
||||||
services:
|
services:
|
||||||
nginx:
|
nginx:
|
||||||
image: nginx:latest
|
image: nginx:latest
|
||||||
@ -298,6 +315,10 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
||||||
|
endpointDisplay() {
|
||||||
|
return this.$root.endpointDisplayFunction(this.endpoint);
|
||||||
|
},
|
||||||
|
|
||||||
urls() {
|
urls() {
|
||||||
if (!this.envsubstJSONConfig["x-dockge"] || !this.envsubstJSONConfig["x-dockge"].urls || !Array.isArray(this.envsubstJSONConfig["x-dockge"].urls)) {
|
if (!this.envsubstJSONConfig["x-dockge"] || !this.envsubstJSONConfig["x-dockge"].urls || !Array.isArray(this.envsubstJSONConfig["x-dockge"].urls)) {
|
||||||
return [];
|
return [];
|
||||||
@ -334,7 +355,7 @@ export default {
|
|||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
globalStack() {
|
globalStack() {
|
||||||
return this.$root.stackList[this.stack.name];
|
return this.$root.completeStackList[this.stack.name + "_" + this.endpoint];
|
||||||
},
|
},
|
||||||
|
|
||||||
status() {
|
status() {
|
||||||
@ -349,20 +370,31 @@ export default {
|
|||||||
if (!this.stack.name) {
|
if (!this.stack.name) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return getComposeTerminalName(this.stack.name);
|
return getComposeTerminalName(this.endpoint, this.stack.name);
|
||||||
},
|
},
|
||||||
|
|
||||||
combinedTerminalName() {
|
combinedTerminalName() {
|
||||||
if (!this.stack.name) {
|
if (!this.stack.name) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return getCombinedTerminalName(this.stack.name);
|
return getCombinedTerminalName(this.endpoint, this.stack.name);
|
||||||
},
|
},
|
||||||
|
|
||||||
networks() {
|
networks() {
|
||||||
return this.jsonConfig.networks;
|
return this.jsonConfig.networks;
|
||||||
}
|
},
|
||||||
|
|
||||||
|
endpoint() {
|
||||||
|
return this.stack.endpoint || this.$route.params.endpoint || "";
|
||||||
|
},
|
||||||
|
|
||||||
|
url() {
|
||||||
|
if (this.stack.endpoint) {
|
||||||
|
return `/compose/${this.stack.name}/${this.stack.endpoint}`;
|
||||||
|
} else {
|
||||||
|
return `/compose/${this.stack.name}`;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
"stack.composeYAML": {
|
"stack.composeYAML": {
|
||||||
@ -405,9 +437,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
$route(to, from) {
|
$route(to, from) {
|
||||||
// Leave Combined Terminal
|
|
||||||
console.debug("leaveCombinedTerminal", from.params.stackName);
|
|
||||||
this.$root.getSocket().emit("leaveCombinedTerminal", this.stack.name, () => {});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@ -437,6 +467,7 @@ export default {
|
|||||||
composeYAML,
|
composeYAML,
|
||||||
composeENV,
|
composeENV,
|
||||||
isManagedByDockge: true,
|
isManagedByDockge: true,
|
||||||
|
endpoint: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
this.yamlCodeChange();
|
this.yamlCodeChange();
|
||||||
@ -449,11 +480,9 @@ export default {
|
|||||||
this.requestServiceStatus();
|
this.requestServiceStatus();
|
||||||
},
|
},
|
||||||
unmounted() {
|
unmounted() {
|
||||||
this.stopServiceStatusTimeout = true;
|
|
||||||
clearTimeout(serviceStatusTimeout);
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
||||||
startServiceStatusTimeout() {
|
startServiceStatusTimeout() {
|
||||||
clearTimeout(serviceStatusTimeout);
|
clearTimeout(serviceStatusTimeout);
|
||||||
serviceStatusTimeout = setTimeout(async () => {
|
serviceStatusTimeout = setTimeout(async () => {
|
||||||
@ -462,7 +491,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
requestServiceStatus() {
|
requestServiceStatus() {
|
||||||
this.$root.getSocket().emit("serviceStatusList", this.stack.name, (res) => {
|
this.$root.emitAgent(this.endpoint, "serviceStatusList", this.stack.name, (res) => {
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
this.serviceStatusList = res.serviceStatusList;
|
this.serviceStatusList = res.serviceStatusList;
|
||||||
}
|
}
|
||||||
@ -475,22 +504,34 @@ export default {
|
|||||||
exitConfirm(next) {
|
exitConfirm(next) {
|
||||||
if (this.isEditMode) {
|
if (this.isEditMode) {
|
||||||
if (confirm("You are currently editing a stack. Are you sure you want to leave?")) {
|
if (confirm("You are currently editing a stack. Are you sure you want to leave?")) {
|
||||||
|
this.exitAction();
|
||||||
next();
|
next();
|
||||||
} else {
|
} else {
|
||||||
next(false);
|
next(false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
this.exitAction();
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
exitAction() {
|
||||||
|
console.log("exitAction");
|
||||||
|
this.stopServiceStatusTimeout = true;
|
||||||
|
clearTimeout(serviceStatusTimeout);
|
||||||
|
|
||||||
|
// Leave Combined Terminal
|
||||||
|
console.debug("leaveCombinedTerminal", this.endpoint, this.stack.name);
|
||||||
|
this.$root.emitAgent(this.endpoint, "leaveCombinedTerminal", this.stack.name, () => {});
|
||||||
|
},
|
||||||
|
|
||||||
bindTerminal() {
|
bindTerminal() {
|
||||||
this.$refs.progressTerminal?.bind(this.terminalName);
|
this.$refs.progressTerminal?.bind(this.endpoint, this.terminalName);
|
||||||
},
|
},
|
||||||
|
|
||||||
loadStack() {
|
loadStack() {
|
||||||
this.processing = true;
|
this.processing = true;
|
||||||
this.$root.getSocket().emit("getStack", this.stack.name, (res) => {
|
this.$root.emitAgent(this.endpoint, "getStack", this.stack.name, (res) => {
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
this.stack = res.stack;
|
this.stack = res.stack;
|
||||||
this.yamlCodeChange();
|
this.yamlCodeChange();
|
||||||
@ -532,15 +573,15 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.bindTerminal(this.terminalName);
|
this.bindTerminal();
|
||||||
|
|
||||||
this.$root.getSocket().emit("deployStack", this.stack.name, this.stack.composeYAML, this.stack.composeENV, this.isAdd, (res) => {
|
this.$root.emitAgent(this.stack.endpoint, "deployStack", this.stack.name, this.stack.composeYAML, this.stack.composeENV, this.isAdd, (res) => {
|
||||||
this.processing = false;
|
this.processing = false;
|
||||||
this.$root.toastRes(res);
|
this.$root.toastRes(res);
|
||||||
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
this.isEditMode = false;
|
this.isEditMode = false;
|
||||||
this.$router.push("/compose/" + this.stack.name);
|
this.$router.push(this.url);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -548,13 +589,13 @@ 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.emitAgent(this.stack.endpoint, "saveStack", this.stack.name, this.stack.composeYAML, this.stack.composeENV, this.isAdd, (res) => {
|
||||||
this.processing = false;
|
this.processing = false;
|
||||||
this.$root.toastRes(res);
|
this.$root.toastRes(res);
|
||||||
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
this.isEditMode = false;
|
this.isEditMode = false;
|
||||||
this.$router.push("/compose/" + this.stack.name);
|
this.$router.push(this.url);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -562,7 +603,7 @@ export default {
|
|||||||
startStack() {
|
startStack() {
|
||||||
this.processing = true;
|
this.processing = true;
|
||||||
|
|
||||||
this.$root.getSocket().emit("startStack", this.stack.name, (res) => {
|
this.$root.emitAgent(this.endpoint, "startStack", this.stack.name, (res) => {
|
||||||
this.processing = false;
|
this.processing = false;
|
||||||
this.$root.toastRes(res);
|
this.$root.toastRes(res);
|
||||||
});
|
});
|
||||||
@ -571,7 +612,7 @@ export default {
|
|||||||
stopStack() {
|
stopStack() {
|
||||||
this.processing = true;
|
this.processing = true;
|
||||||
|
|
||||||
this.$root.getSocket().emit("stopStack", this.stack.name, (res) => {
|
this.$root.emitAgent(this.endpoint, "stopStack", this.stack.name, (res) => {
|
||||||
this.processing = false;
|
this.processing = false;
|
||||||
this.$root.toastRes(res);
|
this.$root.toastRes(res);
|
||||||
});
|
});
|
||||||
@ -580,7 +621,7 @@ export default {
|
|||||||
downStack() {
|
downStack() {
|
||||||
this.processing = true;
|
this.processing = true;
|
||||||
|
|
||||||
this.$root.getSocket().emit("downStack", this.stack.name, (res) => {
|
this.$root.emitAgent(this.endpoint, "downStack", this.stack.name, (res) => {
|
||||||
this.processing = false;
|
this.processing = false;
|
||||||
this.$root.toastRes(res);
|
this.$root.toastRes(res);
|
||||||
});
|
});
|
||||||
@ -589,7 +630,7 @@ export default {
|
|||||||
restartStack() {
|
restartStack() {
|
||||||
this.processing = true;
|
this.processing = true;
|
||||||
|
|
||||||
this.$root.getSocket().emit("restartStack", this.stack.name, (res) => {
|
this.$root.emitAgent(this.endpoint, "restartStack", this.stack.name, (res) => {
|
||||||
this.processing = false;
|
this.processing = false;
|
||||||
this.$root.toastRes(res);
|
this.$root.toastRes(res);
|
||||||
});
|
});
|
||||||
@ -598,14 +639,14 @@ export default {
|
|||||||
updateStack() {
|
updateStack() {
|
||||||
this.processing = true;
|
this.processing = true;
|
||||||
|
|
||||||
this.$root.getSocket().emit("updateStack", this.stack.name, (res) => {
|
this.$root.emitAgent(this.endpoint, "updateStack", this.stack.name, (res) => {
|
||||||
this.processing = false;
|
this.processing = false;
|
||||||
this.$root.toastRes(res);
|
this.$root.toastRes(res);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteDialog() {
|
deleteDialog() {
|
||||||
this.$root.getSocket().emit("deleteStack", this.stack.name, (res) => {
|
this.$root.emitAgent(this.endpoint, "deleteStack", this.stack.name, (res) => {
|
||||||
this.$root.toastRes(res);
|
this.$root.toastRes(res);
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
this.$router.push("/");
|
this.$router.push("/");
|
||||||
@ -750,6 +791,8 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@import "../styles/vars.scss";
|
||||||
|
|
||||||
.terminal {
|
.terminal {
|
||||||
height: 200px;
|
height: 200px;
|
||||||
}
|
}
|
||||||
@ -761,4 +804,9 @@ export default {
|
|||||||
background-color: #2c2f38 !important;
|
background-color: #2c2f38 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.agent-name {
|
||||||
|
font-size: 13px;
|
||||||
|
color: $dark-font-color3;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,39 +1,48 @@
|
|||||||
<template>
|
<template>
|
||||||
<transition name="slide-fade" appear>
|
<transition name="slide-fade" appear>
|
||||||
<div>
|
<div v-if="!processing">
|
||||||
<h1 class="mb-3">Console</h1>
|
<h1 class="mb-3">Console</h1>
|
||||||
|
|
||||||
<div>
|
<Terminal v-if="enableConsole" class="terminal" :rows="20" mode="mainTerminal" name="console" :endpoint="endpoint"></Terminal>
|
||||||
<p>
|
|
||||||
{{ $t("Allowed commands:") }}
|
|
||||||
<template v-for="(command, index) in allowedCommandList" :key="command">
|
|
||||||
<code>{{ command }}</code>
|
|
||||||
|
|
||||||
<!-- No comma at the end -->
|
<div v-else class="alert alert-warning shadow-box" role="alert">
|
||||||
<span v-if="index !== allowedCommandList.length - 1">, </span>
|
<h4 class="alert-heading">Console is not enabled</h4>
|
||||||
</template>
|
<p>
|
||||||
|
Console is a powerful tool that allows you to execute any commands such as <code>docker</code>, <code>rm</code> within the Dockge's container in this Web UI.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
It might be dangerous since this Dockge container is connecting to the host's Docker daemon. Also Dockge could be possibly taken down by commands like <code>rm -rf</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
If you understand the risk, you can enable it by setting <code>DOCKGE_ENABLE_CONSOLE=true</code> in the environment variables.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Terminal class="terminal" :rows="20" mode="mainTerminal" name="console"></Terminal>
|
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import { allowedCommandList } from "../../../backend/util-common";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
allowedCommandList,
|
processing: true,
|
||||||
|
enableConsole: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
endpoint() {
|
||||||
|
return this.$route.params.endpoint || "";
|
||||||
|
},
|
||||||
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.$root.emitAgent(this.endpoint, "checkMainTerminal", (res) => {
|
||||||
|
this.enableConsole = res.ok;
|
||||||
|
this.processing = false;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<transition name="slide-fade" appear>
|
<transition name="slide-fade" appear>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="mb-3">Terminal - {{ serviceName }} ({{ stackName }})</h1>
|
<h1 class="mb-3">{{$t("terminal")}} - {{ serviceName }} ({{ stackName }})</h1>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<router-link :to="sh" class="btn btn-normal me-2">Switch to sh</router-link>
|
<router-link :to="sh" class="btn btn-normal me-2">{{ $t("Switch to sh") }}</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Terminal class="terminal" :rows="20" mode="interactive" :name="terminalName" :stack-name="stackName" :service-name="serviceName" :shell="shell"></Terminal>
|
<Terminal class="terminal" :rows="20" mode="interactive" :name="terminalName" :stack-name="stackName" :service-name="serviceName" :shell="shell" :endpoint="endpoint"></Terminal>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getContainerExecTerminalName } from "../../../backend/util-common";
|
import { getContainerExecTerminalName } from "../../../common/util-common";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -27,6 +27,9 @@ export default {
|
|||||||
stackName() {
|
stackName() {
|
||||||
return this.$route.params.stackName;
|
return this.$route.params.stackName;
|
||||||
},
|
},
|
||||||
|
endpoint() {
|
||||||
|
return this.$route.params.endpoint || "";
|
||||||
|
},
|
||||||
shell() {
|
shell() {
|
||||||
return this.$route.params.type;
|
return this.$route.params.type;
|
||||||
},
|
},
|
||||||
@ -34,10 +37,12 @@ export default {
|
|||||||
return this.$route.params.serviceName;
|
return this.$route.params.serviceName;
|
||||||
},
|
},
|
||||||
terminalName() {
|
terminalName() {
|
||||||
return getContainerExecTerminalName(this.stackName, this.serviceName, 0);
|
return getContainerExecTerminalName(this.endpoint, this.stackName, this.serviceName, 0);
|
||||||
},
|
},
|
||||||
sh() {
|
sh() {
|
||||||
return {
|
let endpoint = this.$route.params.endpoint;
|
||||||
|
|
||||||
|
let data = {
|
||||||
name: "containerTerminal",
|
name: "containerTerminal",
|
||||||
params: {
|
params: {
|
||||||
stackName: this.stackName,
|
stackName: this.stackName,
|
||||||
@ -45,6 +50,13 @@ export default {
|
|||||||
type: "sh",
|
type: "sh",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (endpoint) {
|
||||||
|
data.name = "containerTerminalEndpoint";
|
||||||
|
data.params.endpoint = endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -5,36 +5,97 @@
|
|||||||
{{ $t("home") }}
|
{{ $t("home") }}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<div class="shadow-box big-padding text-center mb-4">
|
<div class="row first-row">
|
||||||
<div class="row">
|
<!-- Left -->
|
||||||
<div class="col">
|
<div class="col-md-7">
|
||||||
<h3>{{ $t("active") }}</h3>
|
<!-- Stats -->
|
||||||
<span class="num active">{{ activeNum }}</span>
|
<div class="shadow-box big-padding text-center mb-4">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<h3>{{ $t("active") }}</h3>
|
||||||
|
<span class="num active">{{ activeNum }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<h3>{{ $t("exited") }}</h3>
|
||||||
|
<span class="num exited">{{ exitedNum }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<h3>{{ $t("inactive") }}</h3>
|
||||||
|
<span class="num inactive">{{ inactiveNum }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
|
||||||
<h3>{{ $t("exited") }}</h3>
|
<!-- Docker Run -->
|
||||||
<span class="num exited">{{ exitedNum }}</span>
|
<h2 class="mb-3">{{ $t("Docker Run") }}</h2>
|
||||||
|
<div class="mb-3">
|
||||||
|
<textarea id="name" v-model="dockerRunCommand" type="text" class="form-control docker-run" required placeholder="docker run ..."></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
|
||||||
<h3>{{ $t("inactive") }}</h3>
|
<button class="btn-normal btn mb-4" @click="convertDockerRun">{{ $t("Convert to Compose") }}</button>
|
||||||
<span class="num inactive">{{ inactiveNum }}</span>
|
</div>
|
||||||
|
<!-- Right -->
|
||||||
|
<div class="col-md-5">
|
||||||
|
<!-- Agent List -->
|
||||||
|
<div class="shadow-box big-padding">
|
||||||
|
<h4 class="mb-3">{{ $tc("dockgeAgent", 2) }} <span class="badge bg-warning" style="font-size: 12px;">beta</span></h4>
|
||||||
|
|
||||||
|
<div v-for="(agent, endpoint) in $root.agentList" :key="endpoint" class="mb-3 agent">
|
||||||
|
<!-- Agent Status -->
|
||||||
|
<template v-if="$root.agentStatusList[endpoint]">
|
||||||
|
<span v-if="$root.agentStatusList[endpoint] === 'online'" class="badge bg-primary me-2">{{ $t("agentOnline") }}</span>
|
||||||
|
<span v-else-if="$root.agentStatusList[endpoint] === 'offline'" class="badge bg-danger me-2">{{ $t("agentOffline") }}</span>
|
||||||
|
<span v-else class="badge bg-secondary me-2">{{ $t($root.agentStatusList[endpoint]) }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Agent Display Name -->
|
||||||
|
<span v-if="endpoint === ''">{{ $t("currentEndpoint") }}</span>
|
||||||
|
<a v-else :href="agent.url" target="_blank">{{ endpoint }}</a>
|
||||||
|
|
||||||
|
<!-- Remove Button -->
|
||||||
|
<font-awesome-icon v-if="endpoint !== ''" class="ms-2 remove-agent" icon="trash" @click="showRemoveAgentDialog[agent.url] = !showRemoveAgentDialog[agent.url]" />
|
||||||
|
|
||||||
|
<!-- Remoe Agent Dialog -->
|
||||||
|
<BModal v-model="showRemoveAgentDialog[agent.url]" :okTitle="$t('removeAgent')" okVariant="danger" @ok="removeAgent(agent.url)">
|
||||||
|
<p>{{ agent.url }}</p>
|
||||||
|
{{ $t("removeAgentMsg") }}
|
||||||
|
</BModal>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button v-if="!showAgentForm" class="btn btn-normal" @click="showAgentForm = !showAgentForm">{{ $t("addAgent") }}</button>
|
||||||
|
|
||||||
|
<!-- Add Agent Form -->
|
||||||
|
<form v-if="showAgentForm" @submit.prevent="addAgent">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="url" class="form-label">{{ $t("dockgeURL") }}</label>
|
||||||
|
<input id="url" v-model="agent.url" type="url" class="form-control" required placeholder="http://">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="username" class="form-label">{{ $t("Username") }}</label>
|
||||||
|
<input id="username" v-model="agent.username" type="text" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">{{ $t("Password") }}</label>
|
||||||
|
<input id="password" v-model="agent.password" type="password" class="form-control" required autocomplete="new-password">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary" :disabled="connectingAgent">
|
||||||
|
<template v-if="connectingAgent">{{ $t("connecting") }}</template>
|
||||||
|
<template v-else>{{ $t("connect") }}</template>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 class="mb-3">{{ $t("Docker Run") }}</h2>
|
|
||||||
<div class="mb-3">
|
|
||||||
<textarea id="name" v-model="dockerRunCommand" type="text" class="form-control docker-run" required placeholder="docker run ..."></textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button class="btn-normal btn" @click="convertDockerRun">{{ $t("Convert to Compose") }}</button>
|
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
<router-view ref="child" />
|
<router-view ref="child" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { statusNameShort } from "../../../backend/util-common";
|
import { statusNameShort } from "../../../common/util-common";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -58,6 +119,14 @@ export default {
|
|||||||
importantHeartBeatListLength: 0,
|
importantHeartBeatListLength: 0,
|
||||||
displayedRecords: [],
|
displayedRecords: [],
|
||||||
dockerRunCommand: "",
|
dockerRunCommand: "",
|
||||||
|
showAgentForm: false,
|
||||||
|
showRemoveAgentDialog: {},
|
||||||
|
connectingAgent: false,
|
||||||
|
agent: {
|
||||||
|
url: "http://",
|
||||||
|
username: "",
|
||||||
|
password: "",
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -98,11 +167,43 @@ export default {
|
|||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
|
||||||
|
addAgent() {
|
||||||
|
this.connectingAgent = true;
|
||||||
|
this.$root.getSocket().emit("addAgent", this.agent, (res) => {
|
||||||
|
this.$root.toastRes(res);
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
this.showAgentForm = false;
|
||||||
|
this.agent = {
|
||||||
|
url: "http://",
|
||||||
|
username: "",
|
||||||
|
password: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.connectingAgent = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
removeAgent(url) {
|
||||||
|
this.$root.getSocket().emit("removeAgent", url, (res) => {
|
||||||
|
if (res.ok) {
|
||||||
|
this.$root.toastRes(res);
|
||||||
|
|
||||||
|
let urlObj = new URL(url);
|
||||||
|
let endpoint = urlObj.host;
|
||||||
|
|
||||||
|
// Remove the stack list and status list of the removed agent
|
||||||
|
delete this.$root.allAgentStackList[endpoint];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
getStatusNum(statusName) {
|
getStatusNum(statusName) {
|
||||||
let num = 0;
|
let num = 0;
|
||||||
|
|
||||||
for (let stackName in this.$root.stackList) {
|
for (let stackName in this.$root.completeStackList) {
|
||||||
const stack = this.$root.stackList[stackName];
|
const stack = this.$root.completeStackList[stackName];
|
||||||
if (statusNameShort(stack.status) === statusName) {
|
if (statusNameShort(stack.status) === statusName) {
|
||||||
num += 1;
|
num += 1;
|
||||||
}
|
}
|
||||||
@ -230,4 +331,20 @@ table {
|
|||||||
font-family: 'JetBrains Mono', monospace;
|
font-family: 'JetBrains Mono', monospace;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.first-row .shadow-box {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove-agent {
|
||||||
|
cursor: pointer;
|
||||||
|
color: rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent {
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -35,22 +35,33 @@ const routes = [
|
|||||||
component: Compose,
|
component: Compose,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/compose/:stackName",
|
path: "/compose/:stackName/:endpoint",
|
||||||
name: "compose",
|
component: Compose,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/compose/:stackName",
|
||||||
component: Compose,
|
component: Compose,
|
||||||
props: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/terminal/:stackName/:serviceName/:type",
|
path: "/terminal/:stackName/:serviceName/:type",
|
||||||
component: ContainerTerminal,
|
component: ContainerTerminal,
|
||||||
name: "containerTerminal",
|
name: "containerTerminal",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/terminal/:stackName/:serviceName/:type/:endpoint",
|
||||||
|
component: ContainerTerminal,
|
||||||
|
name: "containerTerminalEndpoint",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/console",
|
path: "/console",
|
||||||
component: Console,
|
component: Console,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/console/:endpoint",
|
||||||
|
component: Console,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/settings",
|
path: "/settings",
|
||||||
component: Settings,
|
component: Settings,
|
||||||
|
@ -36,7 +36,7 @@ textarea.form-control {
|
|||||||
}
|
}
|
||||||
|
|
||||||
::placeholder {
|
::placeholder {
|
||||||
color: $dark-font-color3 !important;
|
color: $dark-font-color3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.incident a,
|
.incident a,
|
||||||
@ -422,9 +422,8 @@ optgroup {
|
|||||||
// Floating Label
|
// Floating Label
|
||||||
.form-floating > .form-control:focus ~ label::after, .form-floating > .form-control:not(:placeholder-shown) ~ label::after, .form-floating > .form-control-plaintext ~ label::after, .form-floating > .form-select ~ label::after {
|
.form-floating > .form-control:focus ~ label::after, .form-floating > .form-control:not(:placeholder-shown) ~ label::after, .form-floating > .form-control-plaintext ~ label::after, .form-floating > .form-select ~ label::after {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-floating > label {
|
.form-floating > label {
|
||||||
.dark & {
|
.dark & {
|
||||||
color: $dark-font-color3 !important;
|
color: $dark-font-color3 !important;
|
||||||
@ -657,13 +656,6 @@ $shadow-box-padding: 20px;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-terminal {
|
|
||||||
.xterm-viewport {
|
|
||||||
border-radius: 10px;
|
|
||||||
background-color: $dark-bg !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
code {
|
||||||
padding: .2em .4em;
|
padding: .2em .4em;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
10520
package-lock.json
generated
Normal file
10520
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
66
package.json
66
package.json
@ -1,23 +1,25 @@
|
|||||||
{
|
{
|
||||||
"name": "dockge",
|
"name": "dockge",
|
||||||
"version": "1.3.5",
|
"version": "1.4.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 18.0.0 && <= 18.17.1"
|
"node": ">= 22.14.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"fmt": "eslint \"**/*.{ts,vue}\" --fix",
|
"fmt": "eslint \"**/*.{ts,vue}\" --fix",
|
||||||
"lint": "eslint \"**/*.{ts,vue}\"",
|
"lint": "eslint \"**/*.{ts,vue}\"",
|
||||||
"check-ts": "tsc --noEmit",
|
"check-ts": "tsc --noEmit",
|
||||||
"start": "tsx ./backend/index.ts",
|
"start": "tsx ./backend/index.ts",
|
||||||
"dev": "concurrently -k -r \"wait-on tcp:5000 && pnpm run dev:backend \" \"pnpm run dev:frontend\"",
|
"dev": "concurrently -k -r \"wait-on tcp:5000 && npm run dev:backend \" \"npm run dev:frontend\"",
|
||||||
"dev:backend": "cross-env NODE_ENV=development tsx watch --inspect ./backend/index.ts",
|
"dev:backend": "cross-env NODE_ENV=development tsx watch --inspect ./backend/index.ts",
|
||||||
"dev:frontend": "cross-env NODE_ENV=development vite --host --config ./frontend/vite.config.ts",
|
"dev:frontend": "cross-env NODE_ENV=development vite --host --config ./frontend/vite.config.ts",
|
||||||
"release-final": "tsx ./extra/test-docker.ts && tsx extra/update-version.ts && pnpm run build:frontend && npm run build:docker",
|
"release-final": "tsx ./extra/test-docker.ts && tsx extra/update-version.ts && npm run build:frontend && npm run build:docker",
|
||||||
|
"release-beta": "tsx ./extra/test-docker.ts && tsx extra/update-version.ts && npm run build:frontend && npm run build:docker-beta",
|
||||||
"build:frontend": "vite build --config ./frontend/vite.config.ts",
|
"build:frontend": "vite build --config ./frontend/vite.config.ts",
|
||||||
"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 -t louislam/dockge:beta -t louislam/dockge:nightly --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-beta": "node ./extra/env2arg.js docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:beta -t louislam/dockge:$VERSION --target release -f ./docker/Dockerfile . --push",
|
||||||
|
"build:docker-nightly": "npm 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",
|
"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",
|
||||||
@ -25,38 +27,39 @@
|
|||||||
"reset-password": "tsx ./extra/reset-password.ts"
|
"reset-password": "tsx ./extra/reset-password.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@homebridge/node-pty-prebuilt-multiarch": "~0.11.12",
|
"@homebridge/node-pty-prebuilt-multiarch": "0.11.14",
|
||||||
"@inventage/envsubst": "^0.16.0",
|
"@inventage/envsubst": "^0.16.0",
|
||||||
"@louislam/sqlite3": "~15.1.6",
|
"@louislam/sqlite3": "~15.1.6",
|
||||||
"bcryptjs": "~2.4.3",
|
"bcryptjs": "~2.4.3",
|
||||||
"check-password-strength": "~2.0.7",
|
"check-password-strength": "~2.0.10",
|
||||||
"command-exists": "~1.2.9",
|
"command-exists": "~1.2.9",
|
||||||
"compare-versions": "~6.1.0",
|
"compare-versions": "~6.1.1",
|
||||||
"composerize": "~1.4.1",
|
"composerize": "~1.7.1",
|
||||||
"croner": "~7.0.5",
|
"croner": "~8.1.2",
|
||||||
"dayjs": "~1.11.10",
|
"dayjs": "~1.11.13",
|
||||||
"dotenv": "~16.3.1",
|
"dotenv": "~16.3.2",
|
||||||
"express": "~4.18.2",
|
"express": "~4.21.2",
|
||||||
"express-static-gzip": "~2.1.7",
|
"express-static-gzip": "~2.1.8",
|
||||||
"http-graceful-shutdown": "~3.1.13",
|
"http-graceful-shutdown": "~3.1.14",
|
||||||
"jsonwebtoken": "~9.0.2",
|
"jsonwebtoken": "~9.0.2",
|
||||||
"jwt-decode": "~3.1.2",
|
"jwt-decode": "~3.1.2",
|
||||||
"knex": "~2.5.1",
|
"knex": "~2.5.1",
|
||||||
"limiter-es6-compat": "~2.1.2",
|
"limiter-es6-compat": "~2.1.2",
|
||||||
"mysql2": "~3.6.5",
|
"mysql2": "~3.12.0",
|
||||||
"promisify-child-process": "~4.1.2",
|
"promisify-child-process": "~4.1.2",
|
||||||
"redbean-node": "~0.3.3",
|
"redbean-node": "~0.3.3",
|
||||||
"socket.io": "~4.7.2",
|
"semver": "^7.7.1",
|
||||||
"socket.io-client": "~4.7.2",
|
"socket.io": "~4.8.1",
|
||||||
"timezones-list": "~3.0.2",
|
"socket.io-client": "~4.8.1",
|
||||||
|
"timezones-list": "~3.0.3",
|
||||||
"ts-command-line-args": "~2.5.1",
|
"ts-command-line-args": "~2.5.1",
|
||||||
"tsx": "~4.6.2",
|
"tsx": "~4.19.3",
|
||||||
"type-fest": "~4.3.3",
|
"type-fest": "~4.3.3",
|
||||||
"yaml": "~2.3.4"
|
"yaml": "~2.3.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@actions/github": "^6.0.0",
|
"@actions/github": "^6.0.0",
|
||||||
"@fontsource/jetbrains-mono": "^5.0.18",
|
"@fontsource/jetbrains-mono": "^5.2.5",
|
||||||
"@fortawesome/fontawesome-svg-core": "6.4.2",
|
"@fortawesome/fontawesome-svg-core": "6.4.2",
|
||||||
"@fortawesome/free-regular-svg-icons": "6.4.2",
|
"@fortawesome/free-regular-svg-icons": "6.4.2",
|
||||||
"@fortawesome/free-solid-svg-icons": "6.4.2",
|
"@fortawesome/free-solid-svg-icons": "6.4.2",
|
||||||
@ -65,10 +68,11 @@
|
|||||||
"@types/bootstrap": "~5.2.10",
|
"@types/bootstrap": "~5.2.10",
|
||||||
"@types/command-exists": "~1.2.3",
|
"@types/command-exists": "~1.2.3",
|
||||||
"@types/express": "~4.17.21",
|
"@types/express": "~4.17.21",
|
||||||
"@types/jsonwebtoken": "~9.0.5",
|
"@types/jsonwebtoken": "~9.0.9",
|
||||||
|
"@types/semver": "^7.7.0",
|
||||||
"@typescript-eslint/eslint-plugin": "~6.8.0",
|
"@typescript-eslint/eslint-plugin": "~6.8.0",
|
||||||
"@typescript-eslint/parser": "~6.8.0",
|
"@typescript-eslint/parser": "~6.8.0",
|
||||||
"@vitejs/plugin-vue": "~4.5.2",
|
"@vitejs/plugin-vue": "~5.2.3",
|
||||||
"@xterm/addon-fit": "beta",
|
"@xterm/addon-fit": "beta",
|
||||||
"@xterm/xterm": "beta",
|
"@xterm/xterm": "beta",
|
||||||
"bootstrap": "5.3.2",
|
"bootstrap": "5.3.2",
|
||||||
@ -77,19 +81,19 @@
|
|||||||
"cross-env": "~7.0.3",
|
"cross-env": "~7.0.3",
|
||||||
"eslint": "~8.50.0",
|
"eslint": "~8.50.0",
|
||||||
"eslint-plugin-jsdoc": "~46.8.2",
|
"eslint-plugin-jsdoc": "~46.8.2",
|
||||||
"eslint-plugin-vue": "~9.17.0",
|
"eslint-plugin-vue": "~9.32.0",
|
||||||
"prismjs": "~1.29.0",
|
"prismjs": "~1.30.0",
|
||||||
"sass": "~1.68.0",
|
"sass": "~1.68.0",
|
||||||
"typescript": "~5.2.2",
|
"typescript": "~5.2.2",
|
||||||
"unplugin-vue-components": "~0.25.2",
|
"unplugin-vue-components": "~0.25.2",
|
||||||
"vite": "~5.0.7",
|
"vite": "~5.4.15",
|
||||||
"vite-plugin-compression": "~0.5.1",
|
"vite-plugin-compression": "~0.5.1",
|
||||||
"vue": "~3.3.11",
|
"vue": "~3.5.13",
|
||||||
"vue-eslint-parser": "~9.3.2",
|
"vue-eslint-parser": "~9.3.2",
|
||||||
"vue-i18n": "~9.5.0",
|
"vue-i18n": "~10.0.6",
|
||||||
"vue-prism-editor": "2.0.0-alpha.2",
|
"vue-prism-editor": "2.0.0-alpha.2",
|
||||||
"vue-qrcode": "~2.2.0",
|
"vue-qrcode": "~2.2.2",
|
||||||
"vue-router": "~4.2.5",
|
"vue-router": "~4.5.0",
|
||||||
"vue-toastification": "2.0.0-rc.5",
|
"vue-toastification": "2.0.0-rc.5",
|
||||||
"wait-on": "^7.2.0",
|
"wait-on": "^7.2.0",
|
||||||
"xterm-addon-web-links": "~0.9.0"
|
"xterm-addon-web-links": "~0.9.0"
|
||||||
|
5633
pnpm-lock.yaml
generated
5633
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -7,6 +7,7 @@
|
|||||||
"skipLibCheck": true
|
"skipLibCheck": true
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"backend/**/*"
|
"backend/**/*",
|
||||||
|
"common/**/*"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user