mirror of
https://github.com/louislam/dockge.git
synced 2026-05-21 22:12:17 +00:00
Compare commits
110 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f809ae192b | ||
|
|
749e2c5c88 | ||
|
|
b02f5e092e | ||
|
|
078f762631 | ||
|
|
e589d4ec7e | ||
|
|
7e324d9015 | ||
|
|
72a941712d | ||
|
|
46ce4228a5 | ||
|
|
cc180562fc | ||
|
|
27bc4cd25c | ||
|
|
52605de5cd | ||
|
|
420c3af66d | ||
|
|
98cd537ba8 | ||
|
|
33fb84b4db | ||
|
|
f2575d5c05 | ||
|
|
65e2e26c43 | ||
|
|
cbb6b87a37 | ||
|
|
98cba39004 | ||
|
|
e31f766516 | ||
|
|
27bfe723d7 | ||
|
|
69818d665d | ||
|
|
bac498f97f | ||
|
|
3e37f38fc7 | ||
|
|
6dff52cc73 | ||
|
|
7fcc4c510c | ||
|
|
0ceb6336dd | ||
|
|
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 |
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
|
||||||
|
|||||||
52
.github/workflows/nightly-release.yml
vendored
Normal file
52
.github/workflows/nightly-release.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
name: Nightly Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# Runs at 2:00 AM UTC every day
|
||||||
|
- cron: "0 2 * * *"
|
||||||
|
workflow_dispatch: # Allow manual trigger
|
||||||
|
|
||||||
|
permissions: {}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release-nightly:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 120
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GHCR_TOKEN }}
|
||||||
|
|
||||||
|
- name: Use Node.js 22
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm clean-install --no-fund
|
||||||
|
|
||||||
|
- name: Run release-nightly
|
||||||
|
run: npm run release-nightly
|
||||||
@@ -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.
|
||||||
|
|||||||
45
README.md
45
README.md
@@ -81,9 +81,52 @@ curl "https://dockge.kuma.pet/compose.yaml?port=5001&stacksPath=/opt/stacks" --o
|
|||||||
- port=`5001`
|
- port=`5001`
|
||||||
- stacksPath=`/opt/stacks`
|
- stacksPath=`/opt/stacks`
|
||||||
|
|
||||||
|
Also, once compose is generated/downloaded, add the `PUID` and `PGID` section below to your compose `environment:` section to set stack ownership, otherwise default is `root`
|
||||||
|
|
||||||
|
```
|
||||||
|
# Both PUID and PGID must be set for it to do anything
|
||||||
|
- PUID=1000 # Set the stack file/dir ownership to this user
|
||||||
|
- PGID=1000 # Set the stack file/dir ownership to this group
|
||||||
|
```
|
||||||
|
|
||||||
Interactive compose.yaml generator is available on:
|
Interactive compose.yaml generator is available on:
|
||||||
https://dockge.kuma.pet
|
https://dockge.kuma.pet
|
||||||
|
|
||||||
|
### -OR-
|
||||||
|
Copy and paste your compose from the following:
|
||||||
|
|
||||||
|
If you want to store your stacks in another directory, you can change the `DOCKGE_STACKS_DIR` environment variable and volumes.
|
||||||
|
|
||||||
|
compose:
|
||||||
|
```
|
||||||
|
services:
|
||||||
|
dockge:
|
||||||
|
image: louislam/dockge:1
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
# Host Port:Container Port
|
||||||
|
- 5001:5001
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- ./data:/app/data
|
||||||
|
|
||||||
|
# If you want to use private registries, you need to share the auth file with Dockge:
|
||||||
|
# - /root/.docker/:/root/.docker
|
||||||
|
|
||||||
|
# Stacks Directory
|
||||||
|
# Your stacks directory in the host (The paths inside container must be the same as the host)
|
||||||
|
# ⚠️ If you did it wrong, your data could end up be written into a wrong path.
|
||||||
|
# ✔️ CORRECT EXAMPLE: - /my-stacks:/my-stacks (Both paths match)
|
||||||
|
# ❌ WRONG EXAMPLE: - /docker:/my-stacks (Both paths do not match)
|
||||||
|
- /opt/stacks:/opt/stacks
|
||||||
|
environment:
|
||||||
|
# Tell Dockge where your stacks directory is
|
||||||
|
- DOCKGE_STACKS_DIR=/opt/stacks
|
||||||
|
# Both PUID and PGID must be set for it to do anything
|
||||||
|
- PUID=1000 # Set the stack file/dir ownership to this user
|
||||||
|
- PGID=1000 # Set the stack file/dir ownership to this group
|
||||||
|
```
|
||||||
|
|
||||||
## How to Update
|
## How to Update
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -106,7 +149,7 @@ docker compose pull && docker compose up -d
|
|||||||
## Motivations
|
## Motivations
|
||||||
|
|
||||||
- I have been using Portainer for some time, but for the stack management, I am sometimes not satisfied with it. For example, sometimes when I try to deploy a stack, the loading icon keeps spinning for a few minutes without progress. And sometimes error messages are not clear.
|
- I have been using Portainer for some time, but for the stack management, I am sometimes not satisfied with it. For example, sometimes when I try to deploy a stack, the loading icon keeps spinning for a few minutes without progress. And sometimes error messages are not clear.
|
||||||
- Try to develop with ES Module + TypeScript (Originally, I planned to use Deno or Bun.js, but they don't have support for arm64, so I stepped back to Node.js)
|
- Try to develop with ES Module + TypeScript
|
||||||
|
|
||||||
If you love this project, please consider giving it a ⭐.
|
If you love this project, please consider giving it a ⭐.
|
||||||
|
|
||||||
|
|||||||
@@ -76,12 +76,14 @@ export class AgentManager {
|
|||||||
* @param url
|
* @param url
|
||||||
* @param username
|
* @param username
|
||||||
* @param password
|
* @param password
|
||||||
|
* @param name
|
||||||
*/
|
*/
|
||||||
async add(url : string, username : string, password : string) : Promise<Agent> {
|
async add(url: string, username: string, password: string, name: string): Promise<Agent> {
|
||||||
let bean = R.dispense("agent") as Agent;
|
let bean = R.dispense("agent") as Agent;
|
||||||
bean.url = url;
|
bean.url = url;
|
||||||
bean.username = username;
|
bean.username = username;
|
||||||
bean.password = password;
|
bean.password = password;
|
||||||
|
bean.name = name;
|
||||||
await R.store(bean);
|
await R.store(bean);
|
||||||
return bean;
|
return bean;
|
||||||
}
|
}
|
||||||
@@ -98,12 +100,31 @@ export class AgentManager {
|
|||||||
if (bean) {
|
if (bean) {
|
||||||
await R.trash(bean);
|
await R.trash(bean);
|
||||||
let endpoint = bean.endpoint;
|
let endpoint = bean.endpoint;
|
||||||
|
this.disconnect(endpoint);
|
||||||
|
this.sendAgentList();
|
||||||
delete this.agentSocketList[endpoint];
|
delete this.agentSocketList[endpoint];
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Agent not found");
|
throw new Error("Agent not found");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @param updatedName
|
||||||
|
*/
|
||||||
|
async update(url: string, updatedName: string) {
|
||||||
|
const agent = await R.findOne("agent", " url = ? ", [
|
||||||
|
url,
|
||||||
|
]);
|
||||||
|
if (agent) {
|
||||||
|
agent.name = updatedName;
|
||||||
|
await R.store(agent);
|
||||||
|
} else {
|
||||||
|
throw new Error("Agent not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
connect(url : string, username : string, password : string) {
|
connect(url : string, username : string, password : string) {
|
||||||
let obj = new URL(url);
|
let obj = new URL(url);
|
||||||
let endpoint = obj.host;
|
let endpoint = obj.host;
|
||||||
@@ -276,6 +297,8 @@ export class AgentManager {
|
|||||||
url: "",
|
url: "",
|
||||||
username: "",
|
username: "",
|
||||||
endpoint: "",
|
endpoint: "",
|
||||||
|
name: "",
|
||||||
|
updatedName: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
for (let endpoint in list) {
|
for (let endpoint in list) {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export class DockerSocketHandler extends AgentSocketHandler {
|
|||||||
callbackResult({
|
callbackResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
msg: "Deployed",
|
msg: "Deployed",
|
||||||
|
msgi18n: true,
|
||||||
}, callback);
|
}, callback);
|
||||||
stack.joinCombinedTerminal(socket);
|
stack.joinCombinedTerminal(socket);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -30,7 +31,8 @@ export class DockerSocketHandler extends AgentSocketHandler {
|
|||||||
await this.saveStack(server, name, composeYAML, composeENV, isAdd);
|
await this.saveStack(server, name, composeYAML, composeENV, isAdd);
|
||||||
callbackResult({
|
callbackResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
"msg": "Saved"
|
msg: "Saved",
|
||||||
|
msgi18n: true,
|
||||||
}, callback);
|
}, callback);
|
||||||
server.sendStackList();
|
server.sendStackList();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -56,7 +58,8 @@ export class DockerSocketHandler extends AgentSocketHandler {
|
|||||||
server.sendStackList();
|
server.sendStackList();
|
||||||
callbackResult({
|
callbackResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
msg: "Deleted"
|
msg: "Deleted",
|
||||||
|
msgi18n: true,
|
||||||
}, callback);
|
}, callback);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -94,7 +97,8 @@ export class DockerSocketHandler extends AgentSocketHandler {
|
|||||||
server.sendStackList();
|
server.sendStackList();
|
||||||
callbackResult({
|
callbackResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
msg: "Updated"
|
msg: "Updated",
|
||||||
|
msgi18n: true,
|
||||||
}, callback);
|
}, callback);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
callbackError(e, callback);
|
callbackError(e, callback);
|
||||||
@@ -114,7 +118,8 @@ export class DockerSocketHandler extends AgentSocketHandler {
|
|||||||
await stack.start(socket);
|
await stack.start(socket);
|
||||||
callbackResult({
|
callbackResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
msg: "Started"
|
msg: "Started",
|
||||||
|
msgi18n: true,
|
||||||
}, callback);
|
}, callback);
|
||||||
server.sendStackList();
|
server.sendStackList();
|
||||||
|
|
||||||
@@ -138,9 +143,12 @@ export class DockerSocketHandler extends AgentSocketHandler {
|
|||||||
await stack.stop(socket);
|
await stack.stop(socket);
|
||||||
callbackResult({
|
callbackResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
msg: "Stopped"
|
msg: "Stopped",
|
||||||
|
msgi18n: true,
|
||||||
}, callback);
|
}, callback);
|
||||||
server.sendStackList();
|
server.sendStackList();
|
||||||
|
|
||||||
|
stack.leaveCombinedTerminal(socket);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
callbackError(e, callback);
|
callbackError(e, callback);
|
||||||
}
|
}
|
||||||
@@ -159,7 +167,8 @@ export class DockerSocketHandler extends AgentSocketHandler {
|
|||||||
await stack.restart(socket);
|
await stack.restart(socket);
|
||||||
callbackResult({
|
callbackResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
msg: "Restarted"
|
msg: "Restarted",
|
||||||
|
msgi18n: true,
|
||||||
}, callback);
|
}, callback);
|
||||||
server.sendStackList();
|
server.sendStackList();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -180,7 +189,8 @@ export class DockerSocketHandler extends AgentSocketHandler {
|
|||||||
await stack.update(socket);
|
await stack.update(socket);
|
||||||
callbackResult({
|
callbackResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
msg: "Updated"
|
msg: "Updated",
|
||||||
|
msgi18n: true,
|
||||||
}, callback);
|
}, callback);
|
||||||
server.sendStackList();
|
server.sendStackList();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -201,7 +211,8 @@ export class DockerSocketHandler extends AgentSocketHandler {
|
|||||||
await stack.down(socket);
|
await stack.down(socket);
|
||||||
callbackResult({
|
callbackResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
msg: "Downed"
|
msg: "Downed",
|
||||||
|
msgi18n: true,
|
||||||
}, callback);
|
}, callback);
|
||||||
server.sendStackList();
|
server.sendStackList();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -229,6 +240,84 @@ export class DockerSocketHandler extends AgentSocketHandler {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Docker stats
|
||||||
|
agentSocket.on("dockerStats", async (callback) => {
|
||||||
|
try {
|
||||||
|
checkLogin(socket);
|
||||||
|
|
||||||
|
const dockerStats = Object.fromEntries(await server.getDockerStats());
|
||||||
|
callbackResult({
|
||||||
|
ok: true,
|
||||||
|
dockerStats,
|
||||||
|
}, callback);
|
||||||
|
server.sendStackList();
|
||||||
|
} catch (e) {
|
||||||
|
callbackError(e, callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start a service
|
||||||
|
agentSocket.on("startService", async (stackName: unknown, serviceName: unknown, callback) => {
|
||||||
|
try {
|
||||||
|
checkLogin(socket);
|
||||||
|
|
||||||
|
if (typeof (stackName) !== "string" || typeof (serviceName) !== "string") {
|
||||||
|
throw new ValidationError("Stack name and service name must be strings");
|
||||||
|
}
|
||||||
|
|
||||||
|
const stack = await Stack.getStack(server, stackName);
|
||||||
|
await stack.startService(socket, serviceName);
|
||||||
|
stack.joinCombinedTerminal(socket); // Ensure the combined terminal is joined
|
||||||
|
callbackResult({
|
||||||
|
ok: true,
|
||||||
|
msg: "Service " + serviceName + " started"
|
||||||
|
}, callback);
|
||||||
|
server.sendStackList();
|
||||||
|
} catch (e) {
|
||||||
|
callbackError(e, callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stop a service
|
||||||
|
agentSocket.on("stopService", async (stackName: unknown, serviceName: unknown, callback) => {
|
||||||
|
try {
|
||||||
|
checkLogin(socket);
|
||||||
|
|
||||||
|
if (typeof (stackName) !== "string" || typeof (serviceName) !== "string") {
|
||||||
|
throw new ValidationError("Stack name and service name must be strings");
|
||||||
|
}
|
||||||
|
|
||||||
|
const stack = await Stack.getStack(server, stackName);
|
||||||
|
await stack.stopService(socket, serviceName);
|
||||||
|
callbackResult({
|
||||||
|
ok: true,
|
||||||
|
msg: "Service " + serviceName + " stopped"
|
||||||
|
}, callback);
|
||||||
|
server.sendStackList();
|
||||||
|
} catch (e) {
|
||||||
|
callbackError(e, callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
agentSocket.on("restartService", async (stackName: unknown, serviceName: unknown, callback) => {
|
||||||
|
try {
|
||||||
|
checkLogin(socket);
|
||||||
|
|
||||||
|
if (typeof stackName !== "string" || typeof serviceName !== "string") {
|
||||||
|
throw new Error("Invalid stackName or serviceName");
|
||||||
|
}
|
||||||
|
|
||||||
|
const stack = await Stack.getStack(server, stackName, true);
|
||||||
|
await stack.restartService(socket, serviceName);
|
||||||
|
callbackResult({
|
||||||
|
ok: true,
|
||||||
|
msg: "Service " + serviceName + " restarted"
|
||||||
|
}, callback);
|
||||||
|
} catch (e) {
|
||||||
|
callbackError(e, callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// getExternalNetworkList
|
// getExternalNetworkList
|
||||||
agentSocket.on("getDockerNetworkList", async (callback) => {
|
agentSocket.on("getDockerNetworkList", async (callback) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -38,6 +38,11 @@ export class TerminalSocketHandler extends AgentSocketHandler {
|
|||||||
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";
|
||||||
|
|
||||||
@@ -66,6 +71,18 @@ export class TerminalSocketHandler extends AgentSocketHandler {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Check if MainTerminal is enabled
|
||||||
|
agentSocket.on("checkMainTerminal", async (callback) => {
|
||||||
|
try {
|
||||||
|
checkLogin(socket);
|
||||||
|
callbackResult({
|
||||||
|
ok: server.config.enableConsole,
|
||||||
|
}, callback);
|
||||||
|
} catch (e) {
|
||||||
|
callbackError(e, callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Interactive Terminal for containers
|
// Interactive Terminal for containers
|
||||||
agentSocket.on("interactiveTerminal", async (stackName : unknown, serviceName : unknown, shell : unknown, callback) => {
|
agentSocket.on("interactiveTerminal", async (stackName : unknown, serviceName : unknown, shell : unknown, callback) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -136,6 +136,11 @@ export class DockgeServer {
|
|||||||
stacksDir: {
|
stacksDir: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
},
|
||||||
|
enableConsole: {
|
||||||
|
type: Boolean,
|
||||||
|
optional: true,
|
||||||
|
defaultValue: false,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -149,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);
|
||||||
@@ -631,6 +637,35 @@ export class DockgeServer {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getDockerStats() : Promise<Map<string, object>> {
|
||||||
|
let stats = new Map<string, object>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
let res = await childProcessAsync.spawn("docker", [ "stats", "--format", "json", "--no-stream" ], {
|
||||||
|
encoding: "utf-8",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.stdout) {
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
let lines = res.stdout?.toString().split("\n");
|
||||||
|
|
||||||
|
for (let line of lines) {
|
||||||
|
try {
|
||||||
|
let obj = JSON.parse(line);
|
||||||
|
stats.set(obj.Name, obj);
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
} catch (e) {
|
||||||
|
log.error("getDockerStats", e);
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
get stackDirFullPath() {
|
get stackDirFullPath() {
|
||||||
return path.resolve(this.stacksDir);
|
return path.resolve(this.stacksDir);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
table.string("url", 255).notNullable().unique();
|
table.string("url", 255).notNullable().unique();
|
||||||
table.string("username", 255).notNullable();
|
table.string("username", 255).notNullable();
|
||||||
table.string("password", 255).notNullable();
|
table.string("password", 255).notNullable();
|
||||||
|
table.string("name", 255);
|
||||||
table.boolean("active").notNullable().defaultTo(true);
|
table.boolean("active").notNullable().defaultTo(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export class Agent extends BeanModel {
|
|||||||
url: this.url,
|
url: this.url,
|
||||||
username: this.username,
|
username: this.username,
|
||||||
endpoint: this.endpoint,
|
endpoint: this.endpoint,
|
||||||
|
name: this.name,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ import {
|
|||||||
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";
|
||||||
|
import fs, { promises as fsAsync } from "fs";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
export class MainSocketHandler extends SocketHandler {
|
export class MainSocketHandler extends SocketHandler {
|
||||||
create(socket : DockgeSocket, server : DockgeServer) {
|
create(socket : DockgeSocket, server : DockgeServer) {
|
||||||
@@ -242,6 +244,12 @@ export class MainSocketHandler extends SocketHandler {
|
|||||||
checkLogin(socket);
|
checkLogin(socket);
|
||||||
const data = await Settings.getSettings("general");
|
const data = await Settings.getSettings("general");
|
||||||
|
|
||||||
|
if (fs.existsSync(path.join(server.stacksDir, "global.env"))) {
|
||||||
|
data.globalENV = fs.readFileSync(path.join(server.stacksDir, "global.env"), "utf-8");
|
||||||
|
} else {
|
||||||
|
data.globalENV = "# VARIABLE=value #comment";
|
||||||
|
}
|
||||||
|
|
||||||
callback({
|
callback({
|
||||||
ok: true,
|
ok: true,
|
||||||
data: data,
|
data: data,
|
||||||
@@ -270,6 +278,16 @@ export class MainSocketHandler extends SocketHandler {
|
|||||||
if (!currentDisabledAuth && data.disableAuth) {
|
if (!currentDisabledAuth && data.disableAuth) {
|
||||||
await doubleCheckPassword(socket, currentPassword);
|
await doubleCheckPassword(socket, currentPassword);
|
||||||
}
|
}
|
||||||
|
// Handle global.env
|
||||||
|
if (data.globalENV && data.globalENV != "# VARIABLE=value #comment") {
|
||||||
|
await fsAsync.writeFile(path.join(server.stacksDir, "global.env"), data.globalENV);
|
||||||
|
} else {
|
||||||
|
await fsAsync.rm(path.join(server.stacksDir, "global.env"), {
|
||||||
|
recursive: true,
|
||||||
|
force: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
delete data.globalENV;
|
||||||
|
|
||||||
await Settings.setSettings("general", data);
|
await Settings.setSettings("general", data);
|
||||||
|
|
||||||
@@ -311,7 +329,12 @@ export class MainSocketHandler extends SocketHandler {
|
|||||||
throw new ValidationError("dockerRunCommand must be a string");
|
throw new ValidationError("dockerRunCommand must be a string");
|
||||||
}
|
}
|
||||||
|
|
||||||
const composeTemplate = composerize(dockerRunCommand);
|
// Option: 'latest' | 'v2x' | 'v3x'
|
||||||
|
let composeTemplate = composerize(dockerRunCommand, "", "latest");
|
||||||
|
|
||||||
|
// Remove the first line "name: <your project name>"
|
||||||
|
composeTemplate = composeTemplate.split("\n").slice(1).join("\n");
|
||||||
|
|
||||||
callback({
|
callback({
|
||||||
ok: true,
|
ok: true,
|
||||||
composeTemplate,
|
composeTemplate,
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export class ManageAgentSocketHandler extends SocketHandler {
|
|||||||
let data = requestData as LooseObject;
|
let data = requestData as LooseObject;
|
||||||
let manager = socket.instanceManager;
|
let manager = socket.instanceManager;
|
||||||
await manager.test(data.url, data.username, data.password);
|
await manager.test(data.url, data.username, data.password);
|
||||||
await manager.add(data.url, data.username, data.password);
|
await manager.add(data.url, data.username, data.password, data.name);
|
||||||
|
|
||||||
// connect to the agent
|
// connect to the agent
|
||||||
manager.connect(data.url, data.username, data.password);
|
manager.connect(data.url, data.username, data.password);
|
||||||
@@ -66,5 +66,27 @@ export class ManageAgentSocketHandler extends SocketHandler {
|
|||||||
callbackError(e, callback);
|
callbackError(e, callback);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// updateAgent
|
||||||
|
socket.on("updateAgent", async (name : string, updatedName : string, callback : unknown) => {
|
||||||
|
try {
|
||||||
|
log.debug("manage-agent-socket-handler", "updateAgent");
|
||||||
|
checkLogin(socket);
|
||||||
|
|
||||||
|
let manager = socket.instanceManager;
|
||||||
|
await manager.update(name, updatedName);
|
||||||
|
|
||||||
|
server.disconnectAllSocketClients(undefined, socket.id);
|
||||||
|
manager.sendAgentList();
|
||||||
|
|
||||||
|
callbackResult({
|
||||||
|
ok: true,
|
||||||
|
msg: "agentUpdatedSuccessfully",
|
||||||
|
msgi18n: true,
|
||||||
|
}, callback);
|
||||||
|
} catch (e) {
|
||||||
|
callbackError(e, callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ export class Stack {
|
|||||||
* Get the status of the stack from `docker compose ps --format json`
|
* Get the status of the stack from `docker compose ps --format json`
|
||||||
*/
|
*/
|
||||||
async ps() : Promise<object> {
|
async ps() : Promise<object> {
|
||||||
let res = await childProcessAsync.spawn("docker", [ "compose", "ps", "--format", "json" ], {
|
let res = await childProcessAsync.spawn("docker", this.getComposeOptions("ps", "--format", "json"), {
|
||||||
cwd: this.path,
|
cwd: this.path,
|
||||||
encoding: "utf-8",
|
encoding: "utf-8",
|
||||||
});
|
});
|
||||||
@@ -195,20 +195,18 @@ export class Stack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write or overwrite the compose.yaml
|
// Write or overwrite the compose.yaml
|
||||||
await fsAsync.writeFile(path.join(dir, this._composeFileName), this.composeYAML);
|
fs.writeFileSync(path.join(dir, this._composeFileName), this.composeYAML);
|
||||||
|
if (process.env.PUID && process.env.PGID) {
|
||||||
const envPath = path.join(dir, ".env");
|
const uid = Number(process.env.PUID);
|
||||||
|
const gid = Number(process.env.PGID);
|
||||||
// Write or overwrite the .env
|
fs.lchownSync(dir, uid, gid);
|
||||||
// If .env is not existing and the composeENV is empty, we don't need to write it
|
fs.chownSync(path.join(dir, this._composeFileName), uid, gid);
|
||||||
if (await fileExists(envPath) || this.composeENV.trim() !== "") {
|
|
||||||
await fsAsync.writeFile(envPath, this.composeENV);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async deploy(socket : DockgeSocket) : Promise<number> {
|
async deploy(socket : DockgeSocket) : Promise<number> {
|
||||||
const terminalName = getComposeTerminalName(socket.endpoint, 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", this.getComposeOptions("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.");
|
||||||
}
|
}
|
||||||
@@ -217,7 +215,7 @@ export class Stack {
|
|||||||
|
|
||||||
async delete(socket: DockgeSocket) : Promise<number> {
|
async delete(socket: DockgeSocket) : Promise<number> {
|
||||||
const terminalName = getComposeTerminalName(socket.endpoint, 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", this.getComposeOptions("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.");
|
||||||
}
|
}
|
||||||
@@ -407,9 +405,22 @@ export class Stack {
|
|||||||
return stack;
|
return stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getComposeOptions(command : string, ...extraOptions : string[]) {
|
||||||
|
//--env-file ./../global.env --env-file .env
|
||||||
|
let options = [ "compose", command, ...extraOptions ];
|
||||||
|
if (fs.existsSync(path.join(this.server.stacksDir, "global.env"))) {
|
||||||
|
if (fs.existsSync(path.join(this.path, ".env"))) {
|
||||||
|
options.splice(1, 0, "--env-file", "./.env");
|
||||||
|
}
|
||||||
|
options.splice(1, 0, "--env-file", "../global.env");
|
||||||
|
}
|
||||||
|
console.log(options);
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
async start(socket: DockgeSocket) {
|
async start(socket: DockgeSocket) {
|
||||||
const terminalName = getComposeTerminalName(socket.endpoint, 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", this.getComposeOptions("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.");
|
||||||
}
|
}
|
||||||
@@ -418,7 +429,7 @@ export class Stack {
|
|||||||
|
|
||||||
async stop(socket: DockgeSocket) : Promise<number> {
|
async stop(socket: DockgeSocket) : Promise<number> {
|
||||||
const terminalName = getComposeTerminalName(socket.endpoint, 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", this.getComposeOptions("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.");
|
||||||
}
|
}
|
||||||
@@ -427,7 +438,7 @@ export class Stack {
|
|||||||
|
|
||||||
async restart(socket: DockgeSocket) : Promise<number> {
|
async restart(socket: DockgeSocket) : Promise<number> {
|
||||||
const terminalName = getComposeTerminalName(socket.endpoint, 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", this.getComposeOptions("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.");
|
||||||
}
|
}
|
||||||
@@ -436,7 +447,7 @@ export class Stack {
|
|||||||
|
|
||||||
async down(socket: DockgeSocket) : Promise<number> {
|
async down(socket: DockgeSocket) : Promise<number> {
|
||||||
const terminalName = getComposeTerminalName(socket.endpoint, 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", this.getComposeOptions("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.");
|
||||||
}
|
}
|
||||||
@@ -445,7 +456,7 @@ export class Stack {
|
|||||||
|
|
||||||
async update(socket: DockgeSocket) {
|
async update(socket: DockgeSocket) {
|
||||||
const terminalName = getComposeTerminalName(socket.endpoint, 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", this.getComposeOptions("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.");
|
||||||
}
|
}
|
||||||
@@ -457,7 +468,7 @@ export class Stack {
|
|||||||
return exitCode;
|
return exitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "up", "-d", "--remove-orphans" ], this.path);
|
exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", this.getComposeOptions("up", "-d", "--remove-orphans"), 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.");
|
||||||
}
|
}
|
||||||
@@ -466,7 +477,7 @@ export class Stack {
|
|||||||
|
|
||||||
async joinCombinedTerminal(socket: DockgeSocket) {
|
async joinCombinedTerminal(socket: DockgeSocket) {
|
||||||
const terminalName = getCombinedTerminalName(socket.endpoint, 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", this.getComposeOptions("logs", "-f", "--tail", "100"), this.path);
|
||||||
terminal.enableKeepAlive = true;
|
terminal.enableKeepAlive = true;
|
||||||
terminal.rows = COMBINED_TERMINAL_ROWS;
|
terminal.rows = COMBINED_TERMINAL_ROWS;
|
||||||
terminal.cols = COMBINED_TERMINAL_COLS;
|
terminal.cols = COMBINED_TERMINAL_COLS;
|
||||||
@@ -487,7 +498,7 @@ export class Stack {
|
|||||||
let terminal = Terminal.getTerminal(terminalName);
|
let terminal = Terminal.getTerminal(terminalName);
|
||||||
|
|
||||||
if (!terminal) {
|
if (!terminal) {
|
||||||
terminal = new InteractiveTerminal(this.server, terminalName, "docker", [ "compose", "exec", serviceName, shell ], this.path);
|
terminal = new InteractiveTerminal(this.server, terminalName, "docker", this.getComposeOptions("exec", serviceName, shell), this.path);
|
||||||
terminal.rows = TERMINAL_ROWS;
|
terminal.rows = TERMINAL_ROWS;
|
||||||
log.debug("joinContainerTerminal", "Terminal created");
|
log.debug("joinContainerTerminal", "Terminal created");
|
||||||
}
|
}
|
||||||
@@ -497,10 +508,10 @@ export class Stack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getServiceStatusList() {
|
async getServiceStatusList() {
|
||||||
let statusList = new Map<string, number>();
|
let statusList = new Map<string, Array<object>>();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let res = await childProcessAsync.spawn("docker", [ "compose", "ps", "--format", "json" ], {
|
let res = await childProcessAsync.spawn("docker", this.getComposeOptions("ps", "--format", "json"), {
|
||||||
cwd: this.path,
|
cwd: this.path,
|
||||||
encoding: "utf-8",
|
encoding: "utf-8",
|
||||||
});
|
});
|
||||||
@@ -511,13 +522,23 @@ export class Stack {
|
|||||||
|
|
||||||
let lines = res.stdout?.toString().split("\n");
|
let lines = res.stdout?.toString().split("\n");
|
||||||
|
|
||||||
|
const addLine = (obj: { Service: string, State: string, Name: string, Health: string }) => {
|
||||||
|
if (!statusList.has(obj.Service)) {
|
||||||
|
statusList.set(obj.Service, []);
|
||||||
|
}
|
||||||
|
statusList.get(obj.Service)?.push({
|
||||||
|
status: obj.Health || obj.State,
|
||||||
|
name: obj.Name
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
for (let line of lines) {
|
for (let line of lines) {
|
||||||
try {
|
try {
|
||||||
let obj = JSON.parse(line);
|
let obj = JSON.parse(line);
|
||||||
if (obj.Health === "") {
|
if (obj instanceof Array) {
|
||||||
statusList.set(obj.Service, obj.State);
|
obj.forEach(addLine);
|
||||||
} else {
|
} else {
|
||||||
statusList.set(obj.Service, obj.Health);
|
addLine(obj);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
}
|
}
|
||||||
@@ -528,6 +549,35 @@ export class Stack {
|
|||||||
log.error("getServiceStatusList", e);
|
log.error("getServiceStatusList", e);
|
||||||
return statusList;
|
return statusList;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async startService(socket: DockgeSocket, serviceName: string) {
|
||||||
|
const terminalName = getComposeTerminalName(socket.endpoint, this.name);
|
||||||
|
const exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "up", "-d", serviceName ], this.path);
|
||||||
|
if (exitCode !== 0) {
|
||||||
|
throw new Error(`Failed to start service ${serviceName}, please check logs for more information.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return exitCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
async stopService(socket: DockgeSocket, serviceName: string): Promise<number> {
|
||||||
|
const terminalName = getComposeTerminalName(socket.endpoint, this.name);
|
||||||
|
const exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "stop", serviceName ], this.path);
|
||||||
|
if (exitCode !== 0) {
|
||||||
|
throw new Error(`Failed to stop service ${serviceName}, please check logs for more information.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return exitCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
async restartService(socket: DockgeSocket, serviceName: string): Promise<number> {
|
||||||
|
const terminalName = getComposeTerminalName(socket.endpoint, this.name);
|
||||||
|
const exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "restart", serviceName ], this.path);
|
||||||
|
if (exitCode !== 0) {
|
||||||
|
throw new Error(`Failed to restart service ${serviceName}, please check logs for more information.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return exitCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ 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
|
||||||
@@ -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;
|
||||||
@@ -272,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";
|
||||||
@@ -285,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,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
|
||||||
|
|||||||
@@ -107,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",
|
||||||
@@ -236,25 +225,31 @@ 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;
|
||||||
@@ -265,6 +260,20 @@ function copyYAMLCommentsItems(items : any, srcItems : any) {
|
|||||||
item.comment = srcItem.comment;
|
item.comment = srcItem.comment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle comments between array items
|
||||||
|
if (nextSrcItem && nextSrcItem.commentBefore) {
|
||||||
|
if (items[i + 1]) {
|
||||||
|
items[i + 1].commentBefore = nextSrcItem.commentBefore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle trailing comments after array items
|
||||||
|
if (srcItem.value && srcItem.value.comment) {
|
||||||
|
if (item.value) {
|
||||||
|
item.value.comment = srcItem.value.comment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (item.value && srcItem.value) {
|
if (item.value && srcItem.value) {
|
||||||
if (typeof item.value === "object" && typeof srcItem.value === "object") {
|
if (typeof item.value === "object" && typeof srcItem.value === "object") {
|
||||||
item.value.comment = srcItem.value.comment;
|
item.value.comment = srcItem.value.comment;
|
||||||
@@ -276,6 +285,7 @@ function copyYAMLCommentsItems(items : any, srcItems : any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -289,6 +299,7 @@ function copyYAMLCommentsItems(items : any, srcItems : any) {
|
|||||||
* - "8000-9000:80"
|
* - "8000-9000:80"
|
||||||
* - "127.0.0.1:8001:8001"
|
* - "127.0.0.1:8001:8001"
|
||||||
* - "127.0.0.1:5000-5010:5000-5010"
|
* - "127.0.0.1:5000-5010:5000-5010"
|
||||||
|
* - "0.0.0.0:8080->8080/tcp"
|
||||||
* - "6060:6060/udp"
|
* - "6060:6060/udp"
|
||||||
* @param input
|
* @param input
|
||||||
* @param hostname
|
* @param hostname
|
||||||
@@ -298,9 +309,19 @@ export function parseDockerPort(input : string, hostname : string) {
|
|||||||
let display;
|
let display;
|
||||||
|
|
||||||
const parts = input.split("/");
|
const parts = input.split("/");
|
||||||
const part1 = parts[0];
|
let part1 = parts[0];
|
||||||
let protocol = parts[1] || "tcp";
|
let protocol = parts[1] || "tcp";
|
||||||
|
|
||||||
|
// coming from docker ps, split host part
|
||||||
|
const arrow = part1.indexOf("->");
|
||||||
|
if (arrow >= 0) {
|
||||||
|
part1 = part1.split("->")[0];
|
||||||
|
const colon = part1.indexOf(":");
|
||||||
|
if (colon >= 0) {
|
||||||
|
part1 = part1.split(":")[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Split the last ":"
|
// Split the last ":"
|
||||||
const lastColon = part1.lastIndexOf(":");
|
const lastColon = part1.lastIndexOf(":");
|
||||||
|
|
||||||
|
|||||||
@@ -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 --omit=dev
|
||||||
|
|
||||||
############################################
|
############################################
|
||||||
# ⭐ 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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
8
frontend/components.d.ts
vendored
8
frontend/components.d.ts
vendored
@@ -11,12 +11,17 @@ declare module 'vue' {
|
|||||||
Appearance: typeof import('./src/components/settings/Appearance.vue')['default']
|
Appearance: typeof import('./src/components/settings/Appearance.vue')['default']
|
||||||
ArrayInput: typeof import('./src/components/ArrayInput.vue')['default']
|
ArrayInput: typeof import('./src/components/ArrayInput.vue')['default']
|
||||||
ArraySelect: typeof import('./src/components/ArraySelect.vue')['default']
|
ArraySelect: typeof import('./src/components/ArraySelect.vue')['default']
|
||||||
|
BButton: typeof import('bootstrap-vue-next')['BButton']
|
||||||
BDropdown: typeof import('bootstrap-vue-next')['BDropdown']
|
BDropdown: typeof import('bootstrap-vue-next')['BDropdown']
|
||||||
BDropdownItem: typeof import('bootstrap-vue-next')['BDropdownItem']
|
BDropdownItem: typeof import('bootstrap-vue-next')['BDropdownItem']
|
||||||
|
BFormGroup: typeof import('bootstrap-vue-next')['BFormGroup']
|
||||||
|
BFormInput: typeof import('bootstrap-vue-next')['BFormInput']
|
||||||
BModal: typeof import('bootstrap-vue-next')['BModal']
|
BModal: typeof import('bootstrap-vue-next')['BModal']
|
||||||
Confirm: typeof import('./src/components/Confirm.vue')['default']
|
Confirm: typeof import('./src/components/Confirm.vue')['default']
|
||||||
Container: typeof import('./src/components/Container.vue')['default']
|
Container: typeof import('./src/components/Container.vue')['default']
|
||||||
|
DockerStat: typeof import('./src/components/DockerStat.vue')['default']
|
||||||
General: typeof import('./src/components/settings/General.vue')['default']
|
General: typeof import('./src/components/settings/General.vue')['default']
|
||||||
|
GlobalEnv: typeof import('./src/components/settings/GlobalEnv.vue')['default']
|
||||||
HiddenInput: typeof import('./src/components/HiddenInput.vue')['default']
|
HiddenInput: typeof import('./src/components/HiddenInput.vue')['default']
|
||||||
Login: typeof import('./src/components/Login.vue')['default']
|
Login: typeof import('./src/components/Login.vue')['default']
|
||||||
NetworkInput: typeof import('./src/components/NetworkInput.vue')['default']
|
NetworkInput: typeof import('./src/components/NetworkInput.vue')['default']
|
||||||
@@ -29,4 +34,7 @@ declare module 'vue' {
|
|||||||
TwoFADialog: typeof import('./src/components/TwoFADialog.vue')['default']
|
TwoFADialog: typeof import('./src/components/TwoFADialog.vue')['default']
|
||||||
Uptime: typeof import('./src/components/Uptime.vue')['default']
|
Uptime: typeof import('./src/components/Uptime.vue')['default']
|
||||||
}
|
}
|
||||||
|
export interface ComponentCustomProperties {
|
||||||
|
vBModal: typeof import('bootstrap-vue-next')['vBModal']
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="shadow-box big-padding mb-3 container">
|
<div class="shadow-box big-padding mb-3 container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-7">
|
<div class="col-5">
|
||||||
<h4>{{ name }}</h4>
|
<h4>{{ name }}</h4>
|
||||||
<div class="image mb-2">
|
<div class="image mb-2">
|
||||||
<span class="me-1">{{ imageName }}:</span><span class="tag">{{ imageTag }}</span>
|
<span class="me-1">{{ imageName }}:</span><span class="tag">{{ imageTag }}</span>
|
||||||
@@ -9,17 +9,46 @@
|
|||||||
<div v-if="!isEditMode">
|
<div v-if="!isEditMode">
|
||||||
<span class="badge me-1" :class="bgStyle">{{ status }}</span>
|
<span class="badge me-1" :class="bgStyle">{{ status }}</span>
|
||||||
|
|
||||||
<a v-for="port in envsubstService.ports" :key="port" :href="parsePort(port).url" target="_blank">
|
<a v-for="port in (ports ?? envsubstService.ports)" :key="port" :href="parsePort(port).url" target="_blank">
|
||||||
<span class="badge me-1 bg-secondary">{{ parsePort(port).display }}</span>
|
<span class="badge me-1 bg-secondary">{{ parsePort(port).display }}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-5">
|
<div class="col-7">
|
||||||
<div class="function">
|
<div class="function">
|
||||||
<router-link v-if="!isEditMode" class="btn btn-normal" :to="terminalRouteLink" disabled="">
|
<div class="btn-group me-2" role="group">
|
||||||
|
<router-link v-if="!isEditMode && (status === 'running' || status === 'healthy')" class="btn btn-normal" :to="terminalRouteLink" disabled="">
|
||||||
<font-awesome-icon icon="terminal" />
|
<font-awesome-icon icon="terminal" />
|
||||||
Bash
|
Bash
|
||||||
</router-link>
|
</router-link>
|
||||||
|
<button
|
||||||
|
v-if="serviceCount > 1 && !isEditMode && status !== 'running' && status !== 'healthy'"
|
||||||
|
class="btn btn-primary"
|
||||||
|
:disabled="processing"
|
||||||
|
@click="startService"
|
||||||
|
>
|
||||||
|
<font-awesome-icon icon="play" class="me-1" />
|
||||||
|
{{ $t("startStack") }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="serviceCount > 1 && !isEditMode && (status === 'running' || status === 'healthy' || status === 'unhealthy')"
|
||||||
|
class="btn btn-normal"
|
||||||
|
:disabled="processing"
|
||||||
|
@click="restartService"
|
||||||
|
>
|
||||||
|
<font-awesome-icon icon="rotate" class="me-1" />
|
||||||
|
{{ $t("restartStack") }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="serviceCount > 1 && !isEditMode && (status === 'running' || status === 'healthy' || status === 'unhealthy')"
|
||||||
|
class="btn btn-normal"
|
||||||
|
:disabled="processing"
|
||||||
|
@click="stopService"
|
||||||
|
>
|
||||||
|
<font-awesome-icon icon="stop" class="me-1" />
|
||||||
|
{{ $t("stopStack") }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -35,6 +64,32 @@
|
|||||||
{{ $t("deleteContainer") }}
|
{{ $t("deleteContainer") }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else-if="statsInstances.length > 0" class="mt-2">
|
||||||
|
<div class="d-flex align-items-center gap-3">
|
||||||
|
<template v-if="!expandedStats">
|
||||||
|
<div class="stats">
|
||||||
|
{{ $t('CPU') }}: {{ statsInstances[0].CPUPerc }}
|
||||||
|
</div>
|
||||||
|
<div class="stats">
|
||||||
|
{{ $t('memoryAbbreviated') }}: {{ statsInstances[0].MemUsage }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="d-flex flex-grow-1 justify-content-end">
|
||||||
|
<button class="btn btn-sm btn-normal" @click="expandedStats = !expandedStats">
|
||||||
|
<font-awesome-icon :icon="expandedStats ? 'chevron-up' : 'chevron-down'" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<transition name="slide-fade" appear>
|
||||||
|
<div v-if="expandedStats" class="d-flex flex-column gap-3 mt-2">
|
||||||
|
<DockerStat
|
||||||
|
v-for="stat in statsInstances"
|
||||||
|
:key="stat.Name"
|
||||||
|
:stat="stat"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
|
||||||
<transition name="slide-fade" appear>
|
<transition name="slide-fade" appear>
|
||||||
<div v-if="isEditMode && showConfig" class="config mt-3">
|
<div v-if="isEditMode && showConfig" class="config mt-3">
|
||||||
@@ -116,7 +171,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 +182,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>
|
||||||
@@ -138,10 +193,12 @@
|
|||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||||
import { parseDockerPort } from "../../../common/util-common";
|
import { parseDockerPort } from "../../../common/util-common";
|
||||||
|
import DockerStat from "./DockerStat.vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
FontAwesomeIcon,
|
FontAwesomeIcon,
|
||||||
|
DockerStat
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
name: {
|
name: {
|
||||||
@@ -156,16 +213,24 @@ export default defineComponent({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
status: {
|
serviceStatus: {
|
||||||
type: String,
|
type: Object,
|
||||||
default: "N/A",
|
default: null,
|
||||||
|
},
|
||||||
|
dockerStats: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: [
|
emits: [
|
||||||
|
"start-service",
|
||||||
|
"stop-service",
|
||||||
|
"restart-service"
|
||||||
],
|
],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showConfig: false,
|
showConfig: false,
|
||||||
|
expandedStats: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -230,6 +295,10 @@ export default defineComponent({
|
|||||||
return this.jsonObject.services[this.name];
|
return this.jsonObject.services[this.name];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
serviceCount() {
|
||||||
|
return Object.keys(this.jsonObject.services).length;
|
||||||
|
},
|
||||||
|
|
||||||
jsonObject() {
|
jsonObject() {
|
||||||
return this.$parent.$parent.jsonConfig;
|
return this.$parent.$parent.jsonConfig;
|
||||||
},
|
},
|
||||||
@@ -266,6 +335,22 @@ export default defineComponent({
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
statsInstances() {
|
||||||
|
if (!this.serviceStatus) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.serviceStatus
|
||||||
|
.map(s => this.dockerStats[s.name])
|
||||||
|
.filter(s => !!s)
|
||||||
|
.sort((a, b) => a.Name.localeCompare(b.Name));
|
||||||
|
},
|
||||||
|
status() {
|
||||||
|
if (!this.serviceStatus) {
|
||||||
|
return "N/A";
|
||||||
|
}
|
||||||
|
return this.serviceStatus[0].status;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.first) {
|
if (this.first) {
|
||||||
@@ -284,6 +369,16 @@ export default defineComponent({
|
|||||||
remove() {
|
remove() {
|
||||||
delete this.jsonObject.services[this.name];
|
delete this.jsonObject.services[this.name];
|
||||||
},
|
},
|
||||||
|
startService() {
|
||||||
|
this.$emit("start-service", this.name);
|
||||||
|
},
|
||||||
|
stopService() {
|
||||||
|
this.$emit("stop-service", this.name);
|
||||||
|
},
|
||||||
|
restartService() {
|
||||||
|
this.$emit("restart-service", this.name);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@@ -308,5 +403,10 @@ export default defineComponent({
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stats {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
94
frontend/src/components/DockerStat.vue
Normal file
94
frontend/src/components/DockerStat.vue
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
<template>
|
||||||
|
<div class="stats-container">
|
||||||
|
<div class="stats-title">
|
||||||
|
{{ stat.Name }}
|
||||||
|
</div>
|
||||||
|
<div class="d-flex justify-content-between stats gap-2 mt-1">
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-label">
|
||||||
|
{{ $t('CPU') }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ stat.CPUPerc }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-label">
|
||||||
|
{{ $t('memory') }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ stat.MemUsage }} ({{ stat.MemPerc }})
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-label">
|
||||||
|
{{ $t('networkIO') }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ stat.NetIO }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-label">
|
||||||
|
{{ $t('blockIO') }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ stat.BlockIO }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
stat: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.stats-container {
|
||||||
|
container-type: inline-size;
|
||||||
|
|
||||||
|
.stats {
|
||||||
|
container-type: inline-size;
|
||||||
|
|
||||||
|
.stat {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@container (width < 420px) {
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.stat {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label::after {
|
||||||
|
content: ':'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-title {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--bs-heading-color);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -3,7 +3,10 @@
|
|||||||
<div class="list-header">
|
<div class="list-header">
|
||||||
<div class="header-top">
|
<div class="header-top">
|
||||||
<!-- TODO -->
|
<!-- TODO -->
|
||||||
<button v-if="false" class="btn btn-outline-normal ms-2" :class="{ 'active': selectMode }" type="button" @click="selectMode = !selectMode">
|
<button
|
||||||
|
v-if="false" class="btn btn-outline-normal ms-2" :class="{ 'active': selectMode }" type="button"
|
||||||
|
@click="selectMode = !selectMode"
|
||||||
|
>
|
||||||
{{ $t("Select") }}
|
{{ $t("Select") }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@@ -28,36 +31,46 @@
|
|||||||
|
|
||||||
<!-- TODO: Selection Controls -->
|
<!-- TODO: Selection Controls -->
|
||||||
<div v-if="selectMode && false" class="selection-controls px-2 pt-2">
|
<div v-if="selectMode && false" class="selection-controls px-2 pt-2">
|
||||||
<input
|
<input v-model="selectAll" class="form-check-input select-input" type="checkbox" />
|
||||||
v-model="selectAll"
|
|
||||||
class="form-check-input select-input"
|
|
||||||
type="checkbox"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<button class="btn-outline-normal" @click="pauseDialog"><font-awesome-icon icon="pause" size="sm" /> {{ $t("Pause") }}</button>
|
<button class="btn-outline-normal" @click="pauseDialog">
|
||||||
<button class="btn-outline-normal" @click="resumeSelected"><font-awesome-icon icon="play" size="sm" /> {{ $t("Resume") }}</button>
|
<font-awesome-icon icon="pause" size="sm" /> {{
|
||||||
|
$t("Pause") }}
|
||||||
|
</button>
|
||||||
|
<button class="btn-outline-normal" @click="resumeSelected">
|
||||||
|
<font-awesome-icon icon="play" size="sm" />
|
||||||
|
{{ $t("Resume") }}
|
||||||
|
</button>
|
||||||
|
|
||||||
<span v-if="selectedStackCount > 0">
|
<span v-if="selectedStackCount > 0">
|
||||||
{{ $t("selectedStackCount", [ selectedStackCount ]) }}
|
{{ $t("selectedStackCount", [selectedStackCount]) }}
|
||||||
</span>
|
</span>
|
||||||
</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(sortedStackList).length === 0" class="text-center mt-3">
|
<div v-if="agentStackList[0] && agentStackList[0].stacks.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>
|
||||||
|
<div v-for="(agent, agentIndex) in agentStackList" :key="agentIndex" class="stack-list-inner">
|
||||||
|
<div
|
||||||
|
v-if="$root.agentCount > 1" class="p-2 agent-select"
|
||||||
|
@click="closedAgents.set(agent.endpoint, !closedAgents.get(agent.endpoint))"
|
||||||
|
>
|
||||||
|
<span class="me-1">
|
||||||
|
<font-awesome-icon v-show="closedAgents.get(agent.endpoint)" icon="chevron-circle-right" />
|
||||||
|
<font-awesome-icon v-show="!closedAgents.get(agent.endpoint)" icon="chevron-circle-down" />
|
||||||
|
</span>
|
||||||
|
<span v-if="agent.endpoint === 'current'">{{ $t("currentEndpoint") }}</span>
|
||||||
|
<span v-else>{{ agent.endpoint }}</span>
|
||||||
|
</div>
|
||||||
<StackListItem
|
<StackListItem
|
||||||
v-for="(item, index) in sortedStackList"
|
v-for="(item, index) in agent.stacks"
|
||||||
:key="index"
|
v-show="$root.agentCount === 1 || !closedAgents.get(agent.endpoint)" :key="index" :stack="item" :isSelectMode="selectMode"
|
||||||
:stack="item"
|
:isSelected="isSelected" :select="select" :deselect="deselect"
|
||||||
:isSelectMode="selectMode"
|
|
||||||
:isSelected="isSelected"
|
|
||||||
:select="select"
|
|
||||||
:deselect="deselect"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Confirm ref="confirmPause" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="pauseSelected">
|
<Confirm ref="confirmPause" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="pauseSelected">
|
||||||
{{ $t("pauseStackMsg") }}
|
{{ $t("pauseStackMsg") }}
|
||||||
@@ -92,7 +105,8 @@ export default {
|
|||||||
status: null,
|
status: null,
|
||||||
active: null,
|
active: null,
|
||||||
tags: null,
|
tags: null,
|
||||||
}
|
},
|
||||||
|
closedAgents: new Map(),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -119,7 +133,7 @@ export default {
|
|||||||
* Returns a sorted list of stacks based on the applied filters and search text.
|
* Returns a sorted list of stacks based on the applied filters and search text.
|
||||||
* @returns {Array} The sorted list of stacks.
|
* @returns {Array} The sorted list of stacks.
|
||||||
*/
|
*/
|
||||||
sortedStackList() {
|
agentStackList() {
|
||||||
let result = Object.values(this.$root.completeStackList);
|
let result = Object.values(this.$root.completeStackList);
|
||||||
|
|
||||||
result = result.filter(stack => {
|
result = result.filter(stack => {
|
||||||
@@ -187,6 +201,29 @@ export default {
|
|||||||
return m1.name.localeCompare(m2.name);
|
return m1.name.localeCompare(m2.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Group stacks by endpoint, sorting them so the local endpoint is first
|
||||||
|
// and the rest are sorted alphabetically
|
||||||
|
result = [
|
||||||
|
...result.reduce((acc, stack) => {
|
||||||
|
const endpoint = stack.endpoint || "current";
|
||||||
|
if (!acc.has(endpoint)) {
|
||||||
|
acc.set(endpoint, []);
|
||||||
|
}
|
||||||
|
acc.get(endpoint).push(stack);
|
||||||
|
return acc;
|
||||||
|
}, new Map()).entries()
|
||||||
|
].map(([ endpoint, stacks ]) => ({
|
||||||
|
endpoint,
|
||||||
|
stacks
|
||||||
|
})).sort((a, b) => {
|
||||||
|
if (a.endpoint === "current" && b.endpoint !== "current") {
|
||||||
|
return -1;
|
||||||
|
} else if (a.endpoint !== "current" && b.endpoint === "current") {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return a.endpoint.localeCompare(b.endpoint);
|
||||||
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -221,7 +258,7 @@ export default {
|
|||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
searchText() {
|
searchText() {
|
||||||
for (let stack of this.sortedStackList) {
|
for (let stack of this.agentStackList) {
|
||||||
if (!this.selectedStacks[stack.id]) {
|
if (!this.selectedStacks[stack.id]) {
|
||||||
if (this.selectAll) {
|
if (this.selectAll) {
|
||||||
this.disableSelectAllWatcher = true;
|
this.disableSelectAllWatcher = true;
|
||||||
@@ -236,7 +273,7 @@ export default {
|
|||||||
this.selectedStacks = {};
|
this.selectedStacks = {};
|
||||||
|
|
||||||
if (this.selectAll) {
|
if (this.selectAll) {
|
||||||
this.sortedStackList.forEach((item) => {
|
this.agentStackList.forEach((item) => {
|
||||||
this.selectedStacks[item.id] = true;
|
this.selectedStacks[item.id] = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -331,7 +368,7 @@ export default {
|
|||||||
pauseSelected() {
|
pauseSelected() {
|
||||||
Object.keys(this.selectedStacks)
|
Object.keys(this.selectedStacks)
|
||||||
.filter(id => this.$root.stackList[id].active)
|
.filter(id => this.$root.stackList[id].active)
|
||||||
.forEach(id => this.$root.getSocket().emit("pauseStack", id, () => {}));
|
.forEach(id => this.$root.getSocket().emit("pauseStack", id, () => { }));
|
||||||
|
|
||||||
this.cancelSelectMode();
|
this.cancelSelectMode();
|
||||||
},
|
},
|
||||||
@@ -342,7 +379,7 @@ export default {
|
|||||||
resumeSelected() {
|
resumeSelected() {
|
||||||
Object.keys(this.selectedStacks)
|
Object.keys(this.selectedStacks)
|
||||||
.filter(id => !this.$root.stackList[id].active)
|
.filter(id => !this.$root.stackList[id].active)
|
||||||
.forEach(id => this.$root.getSocket().emit("resumeStack", id, () => {}));
|
.forEach(id => this.$root.getSocket().emit("resumeStack", id, () => { }));
|
||||||
|
|
||||||
this.cancelSelectMode();
|
this.cancelSelectMode();
|
||||||
},
|
},
|
||||||
@@ -444,4 +481,15 @@ export default {
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.agent-select {
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: $dark-font-color3;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
<Uptime :stack="stack" :fixed-width="true" class="me-2" />
|
<Uptime :stack="stack" :fixed-width="true" class="me-2" />
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<span>{{ stackName }}</span>
|
<span>{{ stackName }}</span>
|
||||||
<div v-if="$root.agentCount > 1" class="endpoint">{{ endpointDisplay }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</router-link>
|
</router-link>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -20,22 +20,24 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
require: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
endpoint: {
|
endpoint: {
|
||||||
type: String,
|
type: String,
|
||||||
require: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Require if mode is interactive
|
// Require if mode is interactive
|
||||||
stackName: {
|
stackName: {
|
||||||
type: String,
|
type: String,
|
||||||
|
default: "",
|
||||||
},
|
},
|
||||||
|
|
||||||
// Require if mode is interactive
|
// Require if mode is interactive
|
||||||
serviceName: {
|
serviceName: {
|
||||||
type: String,
|
type: String,
|
||||||
|
default: "",
|
||||||
},
|
},
|
||||||
|
|
||||||
// Require if mode is interactive
|
// Require if mode is interactive
|
||||||
@@ -101,6 +103,14 @@ export default {
|
|||||||
this.terminal.open(this.$refs.terminal);
|
this.terminal.open(this.$refs.terminal);
|
||||||
this.terminal.focus();
|
this.terminal.focus();
|
||||||
|
|
||||||
|
// Add right-click context menu handler for paste
|
||||||
|
this.$refs.terminal.addEventListener("contextmenu", this.handleContextMenu);
|
||||||
|
|
||||||
|
// Add selection handler for copy to clipboard
|
||||||
|
this.terminal.onSelectionChange(() => {
|
||||||
|
this.handleSelection();
|
||||||
|
});
|
||||||
|
|
||||||
// Notify parent component when data is received
|
// Notify parent component when data is received
|
||||||
this.terminal.onCursorMove(() => {
|
this.terminal.onCursorMove(() => {
|
||||||
console.debug("onData triggered");
|
console.debug("onData triggered");
|
||||||
@@ -135,6 +145,7 @@ export default {
|
|||||||
window.removeEventListener("resize", this.onResizeEvent); // Remove the resize event listener from the window object.
|
window.removeEventListener("resize", this.onResizeEvent); // Remove the resize event listener from the window object.
|
||||||
this.$root.unbindTerminal(this.name);
|
this.$root.unbindTerminal(this.name);
|
||||||
this.terminal.dispose();
|
this.terminal.dispose();
|
||||||
|
this.$refs.terminal?.removeEventListener("contextmenu", this.handleContextMenu);
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
@@ -154,17 +165,27 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
removeInput() {
|
removeInput() {
|
||||||
|
const textAfterCursorLength = this.terminalInputBuffer.length - this.cursorPosition;
|
||||||
|
const spaces = " ".repeat(textAfterCursorLength);
|
||||||
const backspaceCount = this.terminalInputBuffer.length;
|
const backspaceCount = this.terminalInputBuffer.length;
|
||||||
const backspaces = "\b \b".repeat(backspaceCount);
|
const backspaces = "\b \b".repeat(backspaceCount);
|
||||||
this.cursorPosition = 0;
|
this.cursorPosition = 0;
|
||||||
this.terminal.write(backspaces);
|
this.terminal.write(spaces + backspaces);
|
||||||
this.terminalInputBuffer = "";
|
this.terminalInputBuffer = "";
|
||||||
},
|
},
|
||||||
|
|
||||||
|
clearCurrentLine() {
|
||||||
|
// Move cursor to the beginning of the input and clear it
|
||||||
|
const backspaces = "\b".repeat(this.cursorPosition);
|
||||||
|
const spaces = " ".repeat(this.terminalInputBuffer.length);
|
||||||
|
const moreBackspaces = "\b".repeat(this.terminalInputBuffer.length);
|
||||||
|
this.terminal.write(backspaces + spaces + moreBackspaces);
|
||||||
|
},
|
||||||
|
|
||||||
mainTerminalConfig() {
|
mainTerminalConfig() {
|
||||||
this.terminal.onKey(e => {
|
this.terminal.onKey(e => {
|
||||||
const code = e.key.charCodeAt(0);
|
// Optional: keep for debugging
|
||||||
console.debug("Encode: " + JSON.stringify(e.key));
|
// console.debug("Encode: " + JSON.stringify(e.key));
|
||||||
|
|
||||||
if (e.key === "\r") {
|
if (e.key === "\r") {
|
||||||
// Return if no input
|
// Return if no input
|
||||||
@@ -180,35 +201,65 @@ export default {
|
|||||||
this.$root.emitAgent(this.endpoint, "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);
|
||||||
});
|
});
|
||||||
|
} else if (e.key === "\u007F") { // Backspace
|
||||||
} else if (code === 127) { // Backspace
|
|
||||||
if (this.cursorPosition > 0) {
|
if (this.cursorPosition > 0) {
|
||||||
this.terminal.write("\b \b");
|
// Remove character to the left of cursor
|
||||||
|
const beforeCursor = this.terminalInputBuffer.slice(0, this.cursorPosition - 1);
|
||||||
|
const afterCursor = this.terminalInputBuffer.slice(this.cursorPosition);
|
||||||
|
this.terminalInputBuffer = beforeCursor + afterCursor;
|
||||||
this.cursorPosition--;
|
this.cursorPosition--;
|
||||||
this.terminalInputBuffer = this.terminalInputBuffer.slice(0, -1);
|
|
||||||
|
// Redraw the line
|
||||||
|
this.terminal.write("\b" + afterCursor + " \b".repeat(afterCursor.length + 1));
|
||||||
|
}
|
||||||
|
} else if (e.key === "\u001B\u005B\u0033\u007E") { // Delete key
|
||||||
|
if (this.cursorPosition < this.terminalInputBuffer.length) {
|
||||||
|
// Remove character to the right of cursor
|
||||||
|
const beforeCursor = this.terminalInputBuffer.slice(0, this.cursorPosition);
|
||||||
|
const afterCursor = this.terminalInputBuffer.slice(this.cursorPosition + 1);
|
||||||
|
this.terminalInputBuffer = beforeCursor + afterCursor;
|
||||||
|
|
||||||
|
// Redraw the line from cursor position
|
||||||
|
this.terminal.write(afterCursor + " \b".repeat(afterCursor.length + 1));
|
||||||
}
|
}
|
||||||
} else if (e.key === "\u001B\u005B\u0041" || e.key === "\u001B\u005B\u0042") { // UP OR DOWN
|
} else if (e.key === "\u001B\u005B\u0041" || e.key === "\u001B\u005B\u0042") { // UP OR DOWN
|
||||||
// Do nothing
|
// Do nothing
|
||||||
|
|
||||||
} else if (e.key === "\u001B\u005B\u0043") { // RIGHT
|
} else if (e.key === "\u001B\u005B\u0043") { // RIGHT
|
||||||
// TODO
|
if (this.cursorPosition < this.terminalInputBuffer.length) {
|
||||||
|
this.terminal.write(this.terminalInputBuffer[this.cursorPosition]);
|
||||||
|
this.cursorPosition++;
|
||||||
|
}
|
||||||
} else if (e.key === "\u001B\u005B\u0044") { // LEFT
|
} else if (e.key === "\u001B\u005B\u0044") { // LEFT
|
||||||
// TODO
|
if (this.cursorPosition > 0) {
|
||||||
|
this.terminal.write("\b");
|
||||||
|
this.cursorPosition--;
|
||||||
|
}
|
||||||
} else if (e.key === "\u0003") { // Ctrl + C
|
} else if (e.key === "\u0003") { // Ctrl + C
|
||||||
console.debug("Ctrl + C");
|
console.debug("Ctrl + C");
|
||||||
this.$root.emitAgent(this.endpoint, "terminalInput", this.name, e.key);
|
this.$root.emitAgent(this.endpoint, "terminalInput", this.name, e.key);
|
||||||
this.removeInput();
|
this.removeInput();
|
||||||
|
} else if (e.key === "\u0016" || (e.domEvent?.ctrlKey && e.key.toLowerCase() === "v")) { // Ctrl + V
|
||||||
|
this.handlePaste();
|
||||||
|
} else if (e.key === "\u0009" || e.key.startsWith("\u001B")) { // TAB or other special keys
|
||||||
|
// Do nothing
|
||||||
} else {
|
} else {
|
||||||
|
const textBeforeCursor = this.terminalInputBuffer.slice(0, this.cursorPosition);
|
||||||
|
const textAfterCursor = this.terminalInputBuffer.slice(this.cursorPosition);
|
||||||
|
this.terminalInputBuffer = textBeforeCursor + e.key + textAfterCursor;
|
||||||
|
this.terminal.write(e.key + textAfterCursor + "\b".repeat(textAfterCursor.length));
|
||||||
this.cursorPosition++;
|
this.cursorPosition++;
|
||||||
this.terminalInputBuffer += e.key;
|
|
||||||
console.log(this.terminalInputBuffer);
|
|
||||||
this.terminal.write(e.key);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
interactiveTerminalConfig() {
|
interactiveTerminalConfig() {
|
||||||
this.terminal.onKey(e => {
|
this.terminal.onKey(e => {
|
||||||
|
// Handle Ctrl+V for paste
|
||||||
|
if (e.key === "\u0016" || (e.domEvent?.ctrlKey && e.key.toLowerCase() === "v")) {
|
||||||
|
this.handlePaste();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.$root.emitAgent(this.endpoint, "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);
|
||||||
@@ -239,7 +290,87 @@ export default {
|
|||||||
let rows = this.terminal.rows;
|
let rows = this.terminal.rows;
|
||||||
let cols = this.terminal.cols;
|
let cols = this.terminal.cols;
|
||||||
this.$root.emitAgent(this.endpoint, "terminalResize", this.name, rows, cols);
|
this.$root.emitAgent(this.endpoint, "terminalResize", this.name, rows, cols);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle clipboard paste operation
|
||||||
|
*/
|
||||||
|
async handlePaste() {
|
||||||
|
try {
|
||||||
|
const text = await navigator.clipboard.readText();
|
||||||
|
if (text) {
|
||||||
|
this.pasteText(text);
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to read from clipboard:", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paste text into the terminal based on current mode
|
||||||
|
*/
|
||||||
|
pasteText(text) {
|
||||||
|
if (this.mode === "mainTerminal") {
|
||||||
|
// For main terminal, insert text at current cursor position
|
||||||
|
const beforeCursor = this.terminalInputBuffer.slice(0, this.cursorPosition);
|
||||||
|
const afterCursor = this.terminalInputBuffer.slice(this.cursorPosition);
|
||||||
|
|
||||||
|
// Update the buffer with inserted text
|
||||||
|
this.terminalInputBuffer = beforeCursor + text + afterCursor;
|
||||||
|
|
||||||
|
// Clear the current line and rewrite it
|
||||||
|
this.clearCurrentLine();
|
||||||
|
this.terminal.write(this.terminalInputBuffer);
|
||||||
|
|
||||||
|
// Move cursor to the correct position (after the pasted text)
|
||||||
|
this.cursorPosition += text.length;
|
||||||
|
const backspaces = "\b".repeat(afterCursor.length);
|
||||||
|
this.terminal.write(backspaces);
|
||||||
|
|
||||||
|
} else if (this.mode === "interactive") {
|
||||||
|
// For interactive terminal, send directly to server
|
||||||
|
this.$root.emitAgent(this.endpoint, "terminalInput", this.name, text, (res) => {
|
||||||
|
if (!res.ok) {
|
||||||
|
this.$root.toastRes(res);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle right-click context menu for paste operation
|
||||||
|
*/
|
||||||
|
handleContextMenu(event) {
|
||||||
|
// Prevent default context menu
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
// Only handle paste for modes that support input
|
||||||
|
if (this.mode === "mainTerminal" || this.mode === "interactive") {
|
||||||
|
this.handlePaste();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle text selection in terminal - copy to clipboard
|
||||||
|
*/
|
||||||
|
handleSelection() {
|
||||||
|
const selectedText = this.terminal.getSelection();
|
||||||
|
if (selectedText && selectedText.length > 0) {
|
||||||
|
this.copyToClipboard(selectedText);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy text to clipboard
|
||||||
|
*/
|
||||||
|
async copyToClipboard(text) {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(text);
|
||||||
|
console.debug("Text copied to clipboard:", text);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to copy to clipboard:", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -247,13 +378,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>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="false" class="my-4">
|
<div v-show="true" class="my-4">
|
||||||
<label for="timezone" class="form-label">{{ $t("Theme") }}</label>
|
<label for="timezone" class="form-label">{{ $t("Theme") }}</label>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -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") }}
|
||||||
|
|||||||
98
frontend/src/components/settings/GlobalEnv.vue
Normal file
98
frontend/src/components/settings/GlobalEnv.vue
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-if="settingsLoaded" class="my-4">
|
||||||
|
<form class="my-4" autocomplete="off" @submit.prevent="saveGeneral">
|
||||||
|
<div class="shadow-box mb-3 editor-box edit-mode">
|
||||||
|
<code-mirror
|
||||||
|
ref="editor"
|
||||||
|
v-model="settings.globalENV"
|
||||||
|
:extensions="extensionsEnv"
|
||||||
|
minimal
|
||||||
|
wrap="true"
|
||||||
|
dark="true"
|
||||||
|
tab="true"
|
||||||
|
:hasFocus="editorFocus"
|
||||||
|
@change="onChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="my-4">
|
||||||
|
<!-- Save Button -->
|
||||||
|
<div>
|
||||||
|
<button class="btn btn-primary" type="submit">
|
||||||
|
{{ $t("Save") }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import CodeMirror from "vue-codemirror6";
|
||||||
|
import { python } from "@codemirror/lang-python"; // good enough for .env key=value highlighting
|
||||||
|
import { dracula as editorTheme } from "thememirror";
|
||||||
|
import { lineNumbers, EditorView } from "@codemirror/view";
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "GlobalEnv",
|
||||||
|
components: {
|
||||||
|
CodeMirror,
|
||||||
|
},
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
const editorFocus = ref(false);
|
||||||
|
|
||||||
|
const focusEffectHandler = (state, focusing) => {
|
||||||
|
editorFocus.value = focusing;
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const extensionsEnv = [
|
||||||
|
editorTheme,
|
||||||
|
python(),
|
||||||
|
lineNumbers(),
|
||||||
|
EditorView.focusChangeEffect.of(focusEffectHandler),
|
||||||
|
];
|
||||||
|
|
||||||
|
return { editorFocus,
|
||||||
|
extensionsEnv };
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
settings() {
|
||||||
|
return this.$parent.$parent.$parent.settings;
|
||||||
|
},
|
||||||
|
saveSettings() {
|
||||||
|
return this.$parent.$parent.$parent.saveSettings;
|
||||||
|
},
|
||||||
|
settingsLoaded() {
|
||||||
|
return this.$parent.$parent.$parent.settingsLoaded;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
/** Save the settings */
|
||||||
|
saveGeneral() {
|
||||||
|
this.saveSettings();
|
||||||
|
},
|
||||||
|
|
||||||
|
onChange() {
|
||||||
|
// hook for future live validation if desired
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.editor-box {
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
&.edit-mode {
|
||||||
|
background-color: #2c2f38 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -94,10 +94,18 @@
|
|||||||
<TwoFADialog ref="TwoFADialog" />
|
<TwoFADialog ref="TwoFADialog" />
|
||||||
|
|
||||||
<Confirm ref="confirmDisableAuth" btn-style="btn-danger" :yes-text="$t('I understand, please disable')" :no-text="$t('Leave')" @yes="disableAuth">
|
<Confirm ref="confirmDisableAuth" btn-style="btn-danger" :yes-text="$t('I understand, please disable')" :no-text="$t('Leave')" @yes="disableAuth">
|
||||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
<i18n-t keypath="disableauth.message1" tag="p">
|
||||||
<p v-html="$t('disableauth.message1')"></p>
|
<template #disableAuth>
|
||||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
<strong>{{ $t('disableAuth') }}</strong>
|
||||||
<p v-html="$t('disableauth.message2')"></p>
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
|
||||||
|
<i18n-t keypath="disableauth.message2" tag="p">
|
||||||
|
<template #scenarios>
|
||||||
|
<strong>{{ $t('scenarios') }}</strong>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
|
||||||
<p>{{ $t("Please use this option carefully!") }}</p>
|
<p>{{ $t("Please use this option carefully!") }}</p>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ const languageList = {
|
|||||||
"vi": "Tiếng Việt",
|
"vi": "Tiếng Việt",
|
||||||
"hu": "Magyar",
|
"hu": "Magyar",
|
||||||
"ca": "Català",
|
"ca": "Català",
|
||||||
|
"ga": "Gaeilge",
|
||||||
|
"de-CH": "Schwiizerdütsch",
|
||||||
|
"mag": "मगही",
|
||||||
|
"mai": "मैथिली",
|
||||||
};
|
};
|
||||||
|
|
||||||
let messages = {
|
let messages = {
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import {
|
|||||||
faAward,
|
faAward,
|
||||||
faLink,
|
faLink,
|
||||||
faChevronDown,
|
faChevronDown,
|
||||||
|
faChevronUp,
|
||||||
faSignOutAlt,
|
faSignOutAlt,
|
||||||
faPen,
|
faPen,
|
||||||
faExternalLinkSquareAlt,
|
faExternalLinkSquareAlt,
|
||||||
@@ -54,6 +55,8 @@ import {
|
|||||||
faTerminal, faWarehouse, faHome, faRocket,
|
faTerminal, faWarehouse, faHome, faRocket,
|
||||||
faRotate,
|
faRotate,
|
||||||
faCloudArrowDown, faArrowsRotate,
|
faCloudArrowDown, faArrowsRotate,
|
||||||
|
faChevronCircleRight,
|
||||||
|
faChevronCircleDown,
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
library.add(
|
library.add(
|
||||||
@@ -88,6 +91,7 @@ library.add(
|
|||||||
faAward,
|
faAward,
|
||||||
faLink,
|
faLink,
|
||||||
faChevronDown,
|
faChevronDown,
|
||||||
|
faChevronUp,
|
||||||
faSignOutAlt,
|
faSignOutAlt,
|
||||||
faPen,
|
faPen,
|
||||||
faExternalLinkSquareAlt,
|
faExternalLinkSquareAlt,
|
||||||
@@ -109,6 +113,8 @@ library.add(
|
|||||||
faRotate,
|
faRotate,
|
||||||
faCloudArrowDown,
|
faCloudArrowDown,
|
||||||
faArrowsRotate,
|
faArrowsRotate,
|
||||||
|
faChevronCircleRight,
|
||||||
|
faChevronCircleDown,
|
||||||
);
|
);
|
||||||
|
|
||||||
export { FontAwesomeIcon };
|
export { FontAwesomeIcon };
|
||||||
|
|||||||
@@ -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": "Выдаліць агента"
|
||||||
|
}
|
||||||
@@ -111,5 +111,22 @@
|
|||||||
"removeAgentMsg": "Сигурни ли сте, че желаете да премахнете този агент?",
|
"removeAgentMsg": "Сигурни ли сте, че желаете да премахнете този агент?",
|
||||||
"dockgeAgent": "Dockge агент | Dockge агенти",
|
"dockgeAgent": "Dockge агент | Dockge агенти",
|
||||||
"connecting": "Свързване",
|
"connecting": "Свързване",
|
||||||
"agentRemovedSuccessfully": "Агентът е премахнат успешно."
|
"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": "Няма налични мрежи. Първо трябва да добавите вътрешни мрежи или да активирате външни мрежи в дясната страна."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,5 +111,6 @@
|
|||||||
"Please use this option carefully!": "Si us plau, utilitzeu aquesta opció amb cura!",
|
"Please use this option carefully!": "Si us plau, utilitzeu aquesta opció amb cura!",
|
||||||
"Enable Auth": "Habilitar autenticació",
|
"Enable Auth": "Habilitar autenticació",
|
||||||
"I understand, please disable": "Ho entenc, si us plau deshabilita",
|
"I understand, please disable": "Ho entenc, si us plau deshabilita",
|
||||||
"Password": "Contrasenya"
|
"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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,8 +58,8 @@
|
|||||||
"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 godkændelse",
|
"Enable Auth": "Aktiver godkendelse",
|
||||||
"Disable Auth": "Deaktiver godkændelse",
|
"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": "Version",
|
"Frontend Version": "Version",
|
||||||
@@ -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",
|
||||||
@@ -111,5 +111,16 @@
|
|||||||
"agentAddedSuccessfully": "Agent succesfuld tilføjet.",
|
"agentAddedSuccessfully": "Agent succesfuld tilføjet.",
|
||||||
"agentRemovedSuccessfully": "Agent succesfuld fjernet.",
|
"agentRemovedSuccessfully": "Agent succesfuld fjernet.",
|
||||||
"removeAgent": "Fjern agent",
|
"removeAgent": "Fjern agent",
|
||||||
"removeAgentMsg": "Er du sikker på at du vil fjerne denne 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"
|
||||||
}
|
}
|
||||||
|
|||||||
132
frontend/src/lang/de-CH.json
Normal file
132
frontend/src/lang/de-CH.json
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
{
|
||||||
|
"languageName": "Schwiizerdütsch",
|
||||||
|
"Create your admin account": "Erstell dis Admin-Konto",
|
||||||
|
"authIncorrectCreds": "Falsche Benutzername oder falsches Passwort.",
|
||||||
|
"PasswordsDoNotMatch": "Passwörter stimmed nöd überein.",
|
||||||
|
"Repeat Password": "Passwort wiederhole",
|
||||||
|
"Create": "Erstelle",
|
||||||
|
"signedInDisp": "Agmeldet als {0}",
|
||||||
|
"signedInDispDisabled": "Ameldig deaktiviert.",
|
||||||
|
"home": "Startsiite",
|
||||||
|
"console": "Konsole",
|
||||||
|
"registry": "Container Registry",
|
||||||
|
"compose": "Compose",
|
||||||
|
"addFirstStackMsg": "Stell din erste Stack zämme!",
|
||||||
|
"stackName": "Stack-Name",
|
||||||
|
"deployStack": "Deploye",
|
||||||
|
"deleteStack": "Lösche",
|
||||||
|
"stopStack": "Ahalte",
|
||||||
|
"restartStack": "Neustarte",
|
||||||
|
"updateStack": "Aktualisiere",
|
||||||
|
"startStack": "Starte",
|
||||||
|
"editStack": "Bearbeite",
|
||||||
|
"discardStack": "Verwerfe",
|
||||||
|
"saveStackDraft": "Speicher",
|
||||||
|
"notAvailableShort": "N/V",
|
||||||
|
"deleteStackMsg": "Wotsch de Stack würklich lösche?",
|
||||||
|
"stackNotManagedByDockgeMsg": "De Stack wird nöd vo Dockge verwaltet.",
|
||||||
|
"primaryHostname": "Primäre Hostname",
|
||||||
|
"general": "Allgemein",
|
||||||
|
"container": "Container",
|
||||||
|
"scanFolder": "Stacks-Ordner durchsueche",
|
||||||
|
"dockerImage": "Image",
|
||||||
|
"restartPolicyUnlessStopped": "Falls nöd gstoppt",
|
||||||
|
"restartPolicyAlways": "Immer",
|
||||||
|
"restartPolicyOnFailure": "Bimene Fehler",
|
||||||
|
"restartPolicyNo": "Kein Neustart",
|
||||||
|
"environmentVariable": "Umgebigsvariable",
|
||||||
|
"restartPolicy": "Neustart Richtlinie",
|
||||||
|
"containerName": "Container-Name",
|
||||||
|
"port": "Port / Ports",
|
||||||
|
"volume": "Volume / Volumes",
|
||||||
|
"network": "Netzwerk | Netzwerke",
|
||||||
|
"dependsOn": "Container-Abhängigkeit/e",
|
||||||
|
"addListItem": "{0} hinzuefüege",
|
||||||
|
"deleteContainer": "Lösche",
|
||||||
|
"addContainer": "Container hinzuefüege",
|
||||||
|
"addNetwork": "Netzwerk hinzuefüege",
|
||||||
|
"disableauth.message1": "Bisch der sicher, dass du d'<strong>Ameldung deaktiviere</strong> wotsch?",
|
||||||
|
"disableauth.message2": "Es isch für Szenarien vorgseh, <strong>in dene du beabsichtigsch, e Drittabüter-Authentifizierig</strong> vor Dockge z'implementiere, wie zum Bispiel Cloudflare Access, Authelia oder anderi Authentifizierigsmechanisme.",
|
||||||
|
"passwordNotMatchMsg": "s'wiederholte Passwort stimmt nöd überein.",
|
||||||
|
"autoGet": "Automatisch lade",
|
||||||
|
"add": "Hinzuefüege",
|
||||||
|
"Edit": "Bearbeite",
|
||||||
|
"applyToYAML": "Uf s'YAML awende",
|
||||||
|
"createExternalNetwork": "Erstelle",
|
||||||
|
"addInternalNetwork": "Hinzuefüege",
|
||||||
|
"Save": "Speichere",
|
||||||
|
"Language": "Sprach",
|
||||||
|
"Current User": "Aktuelle Benutzer",
|
||||||
|
"Change Password": "Passwort ändere",
|
||||||
|
"Current Password": "Aktuells Passwort",
|
||||||
|
"New Password": "Neus Passwort",
|
||||||
|
"Repeat New Password": "Neus Passwort wiederhole",
|
||||||
|
"Update Password": "Passwort aktualisiere",
|
||||||
|
"Advanced": "Erwiitert",
|
||||||
|
"Please use this option carefully!": "Bitte verwend die Option sorgfältig!",
|
||||||
|
"Enable Auth": "Ameldig aktiviere",
|
||||||
|
"Disable Auth": "Ameldig deaktiviere",
|
||||||
|
"I understand, please disable": "Ich verstah, bitte deaktiviere",
|
||||||
|
"Leave": "Verlah",
|
||||||
|
"Frontend Version": "Frontend Version",
|
||||||
|
"Check Update On GitHub": "Update uf GitHub überprüefe",
|
||||||
|
"Show update if available": "Update azeige, wenn verfüegbar",
|
||||||
|
"Also check beta release": "Au Beta-Version überprüefe",
|
||||||
|
"Remember me": "Agmeldet blibe",
|
||||||
|
"Login": "Amelde",
|
||||||
|
"Username": "Benutzername",
|
||||||
|
"Password": "Passwort",
|
||||||
|
"Settings": "Istellige",
|
||||||
|
"Logout": "Abmelde",
|
||||||
|
"Lowercase only": "Nur Chliibuechstabe",
|
||||||
|
"Convert to Compose": "In Compose-Syntax umwandle",
|
||||||
|
"Docker Run": "Docker Run",
|
||||||
|
"active": "aktiv",
|
||||||
|
"exited": "beendet",
|
||||||
|
"inactive": "inaktiv",
|
||||||
|
"Appearance": "Erschiinigsbild",
|
||||||
|
"Security": "Sicherheit",
|
||||||
|
"About": "Über",
|
||||||
|
"Allowed commands:": "Zueglasseni Befehl:",
|
||||||
|
"Internal Networks": "Interni Netzwerk",
|
||||||
|
"External Networks": "Externi Netzwerk",
|
||||||
|
"No External Networks": "Kei externi Netzwerk",
|
||||||
|
"Cannot connect to the socket server.": "Kei Verbindig zum Socket Server.",
|
||||||
|
"reverseProxyMsg1": "Wird en Reverse Proxy benutzt?",
|
||||||
|
"reconnecting...": "Erneute Verbindigsufbau…",
|
||||||
|
"downStack": "Stoppe & Deaktiviere",
|
||||||
|
"extra": "Extra",
|
||||||
|
"url": "URL / URLs",
|
||||||
|
"reverseProxyMsg2": "Lern wie er für WebSockets z'konfiguriere isch.",
|
||||||
|
"connecting...": "Verbindigsufbau zum Socket Server…",
|
||||||
|
"newUpdate": "Neues Update",
|
||||||
|
"dockgeAgent": "Dockge Agent | Dockge Agente",
|
||||||
|
"currentEndpoint": "Aktuell",
|
||||||
|
"dockgeURL": "Dockge URL (z. B. http://127.0.0.1:5001)",
|
||||||
|
"agentOnline": "Online",
|
||||||
|
"agentOffline": "Offline",
|
||||||
|
"connecting": "Verbinde",
|
||||||
|
"connect": "Verbinde",
|
||||||
|
"addAgent": "Agent Hinzuefüege",
|
||||||
|
"agentAddedSuccessfully": "Agent erfolgriich hinzuegfüegt.",
|
||||||
|
"agentRemovedSuccessfully": "Agent erfolgriich entfernt.",
|
||||||
|
"removeAgent": "Agent Entferne",
|
||||||
|
"removeAgentMsg": "Bisch der sicher, dass du de Agent entferne wotsch?",
|
||||||
|
"LongSyntaxNotSupported": "Lange Syntax wird nöd unterstützt. Bitte verwend de YAML-Editor.",
|
||||||
|
"Lost connection to the socket server. Reconnecting...": "Verbindig zum Socket Server verlore. Verbinde...",
|
||||||
|
"Saved": "Gspeicheret",
|
||||||
|
"Deleted": "Glöscht",
|
||||||
|
"Started": "Gstartet",
|
||||||
|
"Stopped": "Gstoppt",
|
||||||
|
"Restarted": "Neugstartet",
|
||||||
|
"New Container Name...": "Neue Container Name...",
|
||||||
|
"Network name...": "Netzwerkname...",
|
||||||
|
"Select a network...": "Netzwerk uswähle...",
|
||||||
|
"Updated": "Aktualisiert",
|
||||||
|
"Deployed": "Deployed",
|
||||||
|
"Switch to sh": "Zu sh wechsle",
|
||||||
|
"terminal": "Terminal",
|
||||||
|
"CurrentHostname": "(nöd gsetzt: verwendet aktuelli Hostname)",
|
||||||
|
"Downed": "Abegfahre",
|
||||||
|
"NoNetworksAvailable": "Kei Netzwerk verfüegbar. Du muesch zersch interni Netzwerk hinzuefüege oder externi Netzwerk uf de rechte Siite aktiviere."
|
||||||
|
}
|
||||||
@@ -94,7 +94,7 @@
|
|||||||
"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": "Stopp & Inaktiv",
|
"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.",
|
||||||
@@ -111,5 +111,22 @@
|
|||||||
"agentAddedSuccessfully": "Agent erfolgreich hinzugefügt.",
|
"agentAddedSuccessfully": "Agent erfolgreich hinzugefügt.",
|
||||||
"agentRemovedSuccessfully": "Agent erfolgreich entfernt.",
|
"agentRemovedSuccessfully": "Agent erfolgreich entfernt.",
|
||||||
"removeAgent": "Agent Entfernen",
|
"removeAgent": "Agent Entfernen",
|
||||||
"removeAgentMsg": "Bist Du sicher, dass Du diesen Agent entfernen möchtest?"
|
"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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
"saveStackDraft": "Save",
|
"saveStackDraft": "Save",
|
||||||
"notAvailableShort": "N/A",
|
"notAvailableShort": "N/A",
|
||||||
"deleteStackMsg": "Are you sure you want to delete this stack?",
|
"deleteStackMsg": "Are you sure you want to delete this stack?",
|
||||||
|
"cancel": "Cancel",
|
||||||
"stackNotManagedByDockgeMsg": "This stack is not managed by Dockge.",
|
"stackNotManagedByDockgeMsg": "This stack is not managed by Dockge.",
|
||||||
"primaryHostname": "Primary Hostname",
|
"primaryHostname": "Primary Hostname",
|
||||||
"general": "General",
|
"general": "General",
|
||||||
@@ -46,8 +47,10 @@
|
|||||||
"deleteContainer": "Delete",
|
"deleteContainer": "Delete",
|
||||||
"addContainer": "Add Container",
|
"addContainer": "Add Container",
|
||||||
"addNetwork": "Add Network",
|
"addNetwork": "Add Network",
|
||||||
"disableauth.message1": "Are you sure want to <strong>disable authentication</strong>?",
|
"disableauth.message1": "Are you sure want to {disableAuth}?",
|
||||||
"disableauth.message2": "It is designed for scenarios <strong>where you intend to implement third-party authentication</strong> in front of Dockge such as Cloudflare Access, Authelia or other authentication mechanisms.",
|
"disableauth.message2": "It is designed for scenarios {scenarios}",
|
||||||
|
"disableAuth": "disable authentication",
|
||||||
|
"scenarios": "where you intend to implement third-party authentication",
|
||||||
"passwordNotMatchMsg": "The repeat password does not match.",
|
"passwordNotMatchMsg": "The repeat password does not match.",
|
||||||
"autoGet": "Auto Get",
|
"autoGet": "Auto Get",
|
||||||
"add": "Add",
|
"add": "Add",
|
||||||
@@ -95,6 +98,7 @@
|
|||||||
"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",
|
||||||
@@ -112,5 +116,37 @@
|
|||||||
"agentRemovedSuccessfully": "Agent removed successfully.",
|
"agentRemovedSuccessfully": "Agent removed successfully.",
|
||||||
"removeAgent": "Remove Agent",
|
"removeAgent": "Remove Agent",
|
||||||
"removeAgentMsg": "Are you sure you want to remove this agent?",
|
"removeAgentMsg": "Are you sure you want to remove this agent?",
|
||||||
"LongSyntaxNotSupported": "Long syntax is not supported here. Please use the YAML editor."
|
"GlobalEnv": "Global .env",
|
||||||
|
"LongSyntaxNotSupported": "Long syntax is not supported here. Please use the YAML editor.",
|
||||||
|
"name": "Dockge Agent Display name",
|
||||||
|
"updatedName": "New Dockge Agent Display name",
|
||||||
|
"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.",
|
||||||
|
"CPU": "CPU",
|
||||||
|
"memory": "Memory",
|
||||||
|
"memoryAbbreviated": "Mem",
|
||||||
|
"networkIO": "Network I/O",
|
||||||
|
"blockIO": "Block I/O",
|
||||||
|
"Console is not enabled": "Console is not enabled",
|
||||||
|
"ConsoleNotEnabledMSG1": "Console is a powerful tool that allows you to execute any commands such as {docker}, {rm} within the Dockge's container in this Web UI.",
|
||||||
|
"ConsoleNotEnabledMSG2": "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 {rmRf}",
|
||||||
|
"ConsoleNotEnabledMSG3": "If you understand the risk, you can enable it by setting {envVar} in the environment variables.",
|
||||||
|
"dockerCode": "docker",
|
||||||
|
"rmCode": "rm",
|
||||||
|
"rmRfCode": "rm -rf",
|
||||||
|
"envVarCode": "DOCKGE_ENABLE_CONSOLE=true",
|
||||||
|
"confirmLeaveStack": "You are currently editing a stack. Are you sure you want to leave?"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.",
|
||||||
@@ -98,18 +98,35 @@
|
|||||||
"Cannot connect to the socket server.": "No se puede conectar al servidor del socket.",
|
"Cannot connect to the socket server.": "No se puede conectar al servidor del socket.",
|
||||||
"reconnecting...": "Reconectando…",
|
"reconnecting...": "Reconectando…",
|
||||||
"connecting...": "Conectando al servidor del socket…",
|
"connecting...": "Conectando al servidor del socket…",
|
||||||
"url": "URL | URLs",
|
"url": "Dirección URL | Direcciones URLs",
|
||||||
"extra": "Addicional",
|
"extra": "Addicional",
|
||||||
"currentEndpoint": "Corriente",
|
"currentEndpoint": "Actual",
|
||||||
"dockgeURL": "URL de Dockge (ej. http://127.0.0.1:5001)",
|
"dockgeURL": "URL de Dockge (ej. http://127.0.0.1:5001)",
|
||||||
"agentOnline": "Conectado",
|
"agentOnline": "En línea",
|
||||||
"agentOffline": "Desconectado",
|
"agentOffline": "Desconectado",
|
||||||
"connect": "Conectar",
|
"connect": "Conectar",
|
||||||
"addAgent": "Añadir Agente",
|
"addAgent": "Añadir Agente",
|
||||||
"agentAddedSuccessfully": "Agente añadido satisfactoriamente.",
|
"agentAddedSuccessfully": "Agente añadido satisfactoriamente.",
|
||||||
"removeAgent": "Remover Agente",
|
"removeAgent": "Eliminar Agente",
|
||||||
"removeAgentMsg": "Estás seguro que deseas remover este agente?",
|
"removeAgentMsg": "¿Estás seguro que deseas eliminar este agente?",
|
||||||
"dockgeAgent": "Agentes Dockge",
|
"dockgeAgent": "Agentes Dockge",
|
||||||
"connecting": "Conectando",
|
"connecting": "Conectando",
|
||||||
"agentRemovedSuccessfully": "Agente removido satisfactoriamente."
|
"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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,5 +111,22 @@
|
|||||||
"dockgeAgent": "Dockge Agent | Dockge Agents",
|
"dockgeAgent": "Dockge Agent | Dockge Agents",
|
||||||
"currentEndpoint": "Actuel",
|
"currentEndpoint": "Actuel",
|
||||||
"connect": "Connecter",
|
"connect": "Connecter",
|
||||||
"removeAgentMsg": "Êtes-vous sûr de vouloir supprimer cet agent ?"
|
"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."
|
||||||
|
}
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
"Advanced": "Fejlett",
|
"Advanced": "Fejlett",
|
||||||
"Enable Auth": "Hitelesítés Bekapcsolása",
|
"Enable Auth": "Hitelesítés Bekapcsolása",
|
||||||
"Disable Auth": "Hitelesítés Kikapcsolása",
|
"Disable Auth": "Hitelesítés Kikapcsolása",
|
||||||
"I understand, please disable": "Megértetten, kapcsolja ki",
|
"I understand, please disable": "Megértettem, kérem kapcsolja ki",
|
||||||
"Leave": "Kilépés",
|
"Leave": "Kilépés",
|
||||||
"Frontend Version": "Frontend Verzió",
|
"Frontend Version": "Frontend Verzió",
|
||||||
"Also check beta release": "Béta kiadás is",
|
"Also check beta release": "Béta kiadás is",
|
||||||
@@ -111,5 +111,22 @@
|
|||||||
"Cannot connect to the socket server.": "A Socket csatlakozás nem elérhető.",
|
"Cannot connect to the socket server.": "A Socket csatlakozás nem elérhető.",
|
||||||
"connecting...": "Csatlakozás a socket szerver-hez…",
|
"connecting...": "Csatlakozás a socket szerver-hez…",
|
||||||
"url": "URL | URL-ek",
|
"url": "URL | URL-ek",
|
||||||
"dockgeURL": "Dockge URL (pl. http://127.0.0.1:5001)"
|
"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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,5 +111,12 @@
|
|||||||
"currentEndpoint": "Sekarang",
|
"currentEndpoint": "Sekarang",
|
||||||
"agentOnline": "Terhubung",
|
"agentOnline": "Terhubung",
|
||||||
"agentOffline": "Terputus",
|
"agentOffline": "Terputus",
|
||||||
"removeAgentMsg": "Apakah anda yakin untuk menghapus agen ini?"
|
"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"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,10 +95,10 @@
|
|||||||
"reverseProxyMsg1": "Stai usando Reverse Proxy?",
|
"reverseProxyMsg1": "Stai usando Reverse Proxy?",
|
||||||
"reverseProxyMsg2": "Verifica come configurarlo per il WebSocket",
|
"reverseProxyMsg2": "Verifica come configurarlo per il WebSocket",
|
||||||
"Cannot connect to the socket server.": "impossibile collegarsi al socket server",
|
"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",
|
"newUpdate": "Nuovo aggiornamento",
|
||||||
"dockgeAgent": "Agente Dockge | Agenti Dockge",
|
"dockgeAgent": "Agente Dockge | Agenti Dockge",
|
||||||
"currentEndpoint": "Corrente",
|
"currentEndpoint": "Corrente",
|
||||||
@@ -111,5 +111,6 @@
|
|||||||
"removeAgent": "Rimuovi Agente",
|
"removeAgent": "Rimuovi Agente",
|
||||||
"removeAgentMsg": "Sei sicuro di voler rimuovere questo agente?",
|
"removeAgentMsg": "Sei sicuro di voler rimuovere questo agente?",
|
||||||
"addAgent": "Aggungi Agente",
|
"addAgent": "Aggungi Agente",
|
||||||
"agentAddedSuccessfully": "Agente aggiunto correttamente."
|
"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": "停止してInactive",
|
"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": "利用可能なネットワークがありません。まず右側の内部ネットワークを追加するか、外部ネットワークを有効にする必要があります。"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,5 +111,6 @@
|
|||||||
"dockgeAgent": "Dockge 에이전트",
|
"dockgeAgent": "Dockge 에이전트",
|
||||||
"currentEndpoint": "현재",
|
"currentEndpoint": "현재",
|
||||||
"connecting": "연결 중",
|
"connecting": "연결 중",
|
||||||
"agentRemovedSuccessfully": "에이전트를 성공적으로 삭제했습니다."
|
"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"
|
||||||
|
}
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
"Convert to Compose": "Converteer naar compose",
|
"Convert to Compose": "Converteer naar compose",
|
||||||
"External Networks": "Externe netwerken",
|
"External Networks": "Externe netwerken",
|
||||||
"newUpdate": "Nieuwe update",
|
"newUpdate": "Nieuwe update",
|
||||||
"dockgeAgent": "Dockge Agent | Dockge Agents",
|
"dockgeAgent": "Dockge Agent | Dockge Agenten",
|
||||||
"currentEndpoint": "Huidige",
|
"currentEndpoint": "Huidige",
|
||||||
"dockgeURL": "Dockge Adres (bijv. http://127.0.0.1:5001)",
|
"dockgeURL": "Dockge Adres (bijv. http://127.0.0.1:5001)",
|
||||||
"agentOnline": "Online",
|
"agentOnline": "Online",
|
||||||
@@ -111,5 +111,6 @@
|
|||||||
"agentAddedSuccessfully": "Agent toegevoegd.",
|
"agentAddedSuccessfully": "Agent toegevoegd.",
|
||||||
"agentRemovedSuccessfully": "Agent verwijderd.",
|
"agentRemovedSuccessfully": "Agent verwijderd.",
|
||||||
"removeAgent": "Verwijder agent",
|
"removeAgent": "Verwijder agent",
|
||||||
"removeAgentMsg": "Weet je zeker dat je deze agent wilt verwijderen?"
|
"removeAgentMsg": "Weet je zeker dat je deze agent wilt verwijderen?",
|
||||||
|
"LongSyntaxNotSupported": "Lange syntax wordt hier niet ondersteund. Gebruik de YAML editor."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,5 +111,22 @@
|
|||||||
"connecting...": "Łączenie z socketem serwera…",
|
"connecting...": "Łączenie z socketem serwera…",
|
||||||
"extra": "Ekstra",
|
"extra": "Ekstra",
|
||||||
"url": "URL | URLe",
|
"url": "URL | URLe",
|
||||||
"reconnecting...": "Wznawianie połączenia…"
|
"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)"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,5 +111,22 @@
|
|||||||
"agentAddedSuccessfully": "Agente adicionado com sucesso.",
|
"agentAddedSuccessfully": "Agente adicionado com sucesso.",
|
||||||
"agentRemovedSuccessfully": "Agente removido com sucesso.",
|
"agentRemovedSuccessfully": "Agente removido com sucesso.",
|
||||||
"removeAgent": "Remover Agente",
|
"removeAgent": "Remover Agente",
|
||||||
"removeAgentMsg": "Tem certeza de que deseja remover este 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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,15 +101,16 @@
|
|||||||
"agentAddedSuccessfully": "Agente adicionado com sucesso.",
|
"agentAddedSuccessfully": "Agente adicionado com sucesso.",
|
||||||
"agentRemovedSuccessfully": "Agente removido com sucesso.",
|
"agentRemovedSuccessfully": "Agente removido com sucesso.",
|
||||||
"removeAgent": "Remover Agente",
|
"removeAgent": "Remover Agente",
|
||||||
"downStack": "Parar & Inativar",
|
"downStack": "Parar & Desativar",
|
||||||
"dockgeAgent": "Dockge Agente | Dockge Agentes",
|
"dockgeAgent": "Dockge Agente | Dockge Agentes",
|
||||||
"connect": "Conectar",
|
"connect": "Conectar",
|
||||||
"removeAgentMsg": "Tem certeza de que deseja remover este agente?",
|
"removeAgentMsg": "Tem certeza de que deseja remover este agente?",
|
||||||
"reverseProxyMsg1": "Usando um Proxy Reverso?",
|
"reverseProxyMsg1": "Usando um Proxy Reverso?",
|
||||||
"reverseProxyMsg2": "Verifique para configurá-lo como WebSocket",
|
"reverseProxyMsg2": "Verifique para configurá-lo como WebSocket",
|
||||||
"Cannot connect to the socket server.": "Não é possível se conectar ao servidor socket.",
|
"Cannot connect to the socket server.": "Não é possível se conectar ao servidor socket.",
|
||||||
"url": "URL | URLs",
|
"url": "URL | URL's",
|
||||||
"extra": "Extra",
|
"extra": "Extra",
|
||||||
"reconnecting...": "Reconectando…",
|
"reconnecting...": "Reconectando…",
|
||||||
"connecting...": "Conectando ao servidor de socket…"
|
"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": "О продукте",
|
||||||
@@ -92,24 +92,25 @@
|
|||||||
"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": "Доступно обновление",
|
"newUpdate": "Доступно обновление",
|
||||||
"currentEndpoint": "Текущий",
|
"currentEndpoint": "Текущий",
|
||||||
"agentOnline": "В сети",
|
"agentOnline": "В сети",
|
||||||
"agentOffline": "Не в сети",
|
"agentOffline": "Не в сети",
|
||||||
"connecting": "Подключение",
|
"connecting": "Подключение",
|
||||||
"connect": "Подключен",
|
"connect": "Подключить",
|
||||||
"addAgent": "Добавить Агента",
|
"addAgent": "Добавить Агента",
|
||||||
"agentAddedSuccessfully": "Агент добавлен успешно.",
|
"agentAddedSuccessfully": "Агент успешно добавлен.",
|
||||||
"removeAgent": "Удалить Агента",
|
"removeAgent": "Удалить агента",
|
||||||
"removeAgentMsg": "Вы уверены, что хотите удалить этого агента?",
|
"removeAgentMsg": "Вы уверены, что хотите удалить этого агента?",
|
||||||
"dockgeAgent": "Dockge Агент | Dockge Агенты",
|
"dockgeAgent": "Агент Dockge | Агенты Dockge",
|
||||||
"dockgeURL": "Dockge URL (например http://127.0.0.1:5001)",
|
"dockgeURL": "URL-адрес Dockge (например: http://127.0.0.1:5001)",
|
||||||
"agentRemovedSuccessfully": "Агент удален успешно."
|
"agentRemovedSuccessfully": "Агент успешно удален.",
|
||||||
|
"LongSyntaxNotSupported": "Длинный синтаксис здесь не поддерживается. Пожалуйста, используйте редактор YAML."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,7 +91,7 @@
|
|||||||
"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",
|
||||||
@@ -99,5 +99,18 @@
|
|||||||
"newUpdate": "Nova posodobitev",
|
"newUpdate": "Nova posodobitev",
|
||||||
"reverseProxyMsg2": "Preverite, kako ga konfigurirati za WebSocket",
|
"reverseProxyMsg2": "Preverite, kako ga konfigurirati za WebSocket",
|
||||||
"Cannot connect to the socket server.": "Ni mogoče vzpostaviti povezave s strežnikom vtičnic.",
|
"Cannot connect to the socket server.": "Ni mogoče vzpostaviti povezave s strežnikom vtičnic.",
|
||||||
"url": "URL | URL-ji"
|
"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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,5 +111,22 @@
|
|||||||
"removeAgent": "Ta bort agent",
|
"removeAgent": "Ta bort agent",
|
||||||
"removeAgentMsg": "Är du säker att du vill ta bort denna agent?",
|
"removeAgentMsg": "Är du säker att du vill ta bort denna agent?",
|
||||||
"dockgeAgent": "Dockge agenter | Dockge agenter",
|
"dockgeAgent": "Dockge agenter | Dockge agenter",
|
||||||
"agentAddedSuccessfully": "Agent tillagd."
|
"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"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,5 +111,22 @@
|
|||||||
"agentAddedSuccessfully": "Aracı başarıyla eklendi.",
|
"agentAddedSuccessfully": "Aracı başarıyla eklendi.",
|
||||||
"agentRemovedSuccessfully": "Aracı başarıyla kaldırıldı.",
|
"agentRemovedSuccessfully": "Aracı başarıyla kaldırıldı.",
|
||||||
"removeAgent": "Aracıyı Kaldır",
|
"removeAgent": "Aracıyı Kaldır",
|
||||||
"removeAgentMsg": "Bu aracıyı kaldırmak istediğinize emin misiniz?"
|
"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": "неактивно",
|
||||||
@@ -111,5 +111,22 @@
|
|||||||
"dockgeURL": "Dockge URL (напр. http://127.0.0.1:5001)",
|
"dockgeURL": "Dockge URL (напр. http://127.0.0.1:5001)",
|
||||||
"agentRemovedSuccessfully": "Агент успішно видалено.",
|
"agentRemovedSuccessfully": "Агент успішно видалено.",
|
||||||
"agentAddedSuccessfully": "Агент успішно додано.",
|
"agentAddedSuccessfully": "Агент успішно додано.",
|
||||||
"removeAgentMsg": "Ви впевнені, що хочете видалити цей агент?"
|
"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 ایڈیٹر استعمال کریں۔"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,5 +111,22 @@
|
|||||||
"agentRemovedSuccessfully": "代理移除成功。",
|
"agentRemovedSuccessfully": "代理移除成功。",
|
||||||
"removeAgent": "移除代理",
|
"removeAgent": "移除代理",
|
||||||
"removeAgentMsg": "您确定要移除此代理?",
|
"removeAgentMsg": "您确定要移除此代理?",
|
||||||
"agentAddedSuccessfully": "代理添加成功。"
|
"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": "已宕机"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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": "新增",
|
||||||
@@ -100,5 +100,33 @@
|
|||||||
"url": "網址 | 網址",
|
"url": "網址 | 網址",
|
||||||
"extra": "額外",
|
"extra": "額外",
|
||||||
"newUpdate": "新版本",
|
"newUpdate": "新版本",
|
||||||
"currentEndpoint": "目前"
|
"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": "您確定要刪除這個代理嗎?"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,10 +131,15 @@ export default defineComponent({
|
|||||||
methods: {
|
methods: {
|
||||||
|
|
||||||
endpointDisplayFunction(endpoint : string) {
|
endpointDisplayFunction(endpoint : string) {
|
||||||
|
for (const [ k, v ] of Object.entries(this.$data.agentList)) {
|
||||||
if (endpoint) {
|
if (endpoint) {
|
||||||
|
if (endpoint === v["endpoint"] && v["name"] !== "") {
|
||||||
|
return v["name"];
|
||||||
|
}
|
||||||
|
if (endpoint === v["endpoint"] && v["name"] === "" ) {
|
||||||
return endpoint;
|
return endpoint;
|
||||||
} else {
|
}
|
||||||
return this.$t("currentEndpoint");
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -203,7 +208,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;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -279,7 +284,6 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
|
|
||||||
socket.on("agentList", (res) => {
|
socket.on("agentList", (res) => {
|
||||||
console.log(res);
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
this.agentList = res.agentList;
|
this.agentList = res.agentList;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<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">
|
<h1 v-else class="mb-3">
|
||||||
<Uptime :stack="globalStack" :pill="true" /> {{ stack.name }}
|
<Uptime :stack="globalStack" :pill="true" /> {{ stack.name }}
|
||||||
<span v-if="$root.agentCount > 1" class="agent-name">
|
<span v-if="$root.agentCount > 1 && endpoint !== ''" class="agent-name">
|
||||||
({{ endpointDisplay }})
|
({{ endpointDisplay }})
|
||||||
</span>
|
</span>
|
||||||
</h1>
|
</h1>
|
||||||
@@ -63,8 +63,8 @@
|
|||||||
|
|
||||||
<!-- URLs -->
|
<!-- URLs -->
|
||||||
<div v-if="urls.length > 0" class="mb-3">
|
<div v-if="urls.length > 0" class="mb-3">
|
||||||
<a v-for="(url, index) in urls" :key="index" target="_blank" :href="url.url">
|
<a v-for="(urlItem, index) in urls" :key="index" target="_blank" :href="urlItem.url">
|
||||||
<span class="badge bg-secondary me-2">{{ url.display }}</span>
|
<span class="badge bg-secondary me-2">{{ urlItem.display }}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -98,8 +98,8 @@
|
|||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
<label for="name" class="form-label">{{ $t("dockgeAgent") }}</label>
|
<label for="name" class="form-label">{{ $t("dockgeAgent") }}</label>
|
||||||
<select v-model="stack.endpoint" class="form-select">
|
<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'">
|
<option v-for="(agent, agentEndpoint) in $root.agentList" :key="agentEndpoint" :value="agentEndpoint" :disabled="$root.agentStatusList[agentEndpoint] != 'online'">
|
||||||
({{ $root.agentStatusList[endpoint] }}) {{ (endpoint) ? endpoint : $t("currentEndpoint") }}
|
({{ $root.agentStatusList[agentEndpoint] }}) {{ (agent.name !== '') ? agent.name : agent.url || $t("Current") }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@@ -112,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"
|
||||||
/>
|
/>
|
||||||
@@ -128,7 +128,11 @@
|
|||||||
:name="name"
|
:name="name"
|
||||||
:is-edit-mode="isEditMode"
|
:is-edit-mode="isEditMode"
|
||||||
:first="name === Object.keys(jsonConfig.services)[0]"
|
:first="name === Object.keys(jsonConfig.services)[0]"
|
||||||
:status="serviceStatusList[name]"
|
:serviceStatus="serviceStatusList[name]"
|
||||||
|
:dockerStats="dockerStats"
|
||||||
|
@start-service="startService"
|
||||||
|
@stop-service="stopService"
|
||||||
|
@restart-service="restartService"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -150,7 +154,7 @@
|
|||||||
|
|
||||||
<!-- 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"
|
||||||
@@ -158,7 +162,7 @@
|
|||||||
:endpoint="endpoint"
|
:endpoint="endpoint"
|
||||||
:rows="combinedTerminalRows"
|
:rows="combinedTerminalRows"
|
||||||
:cols="combinedTerminalCols"
|
:cols="combinedTerminalCols"
|
||||||
style="height: 350px;"
|
style="height: 315px;"
|
||||||
></Terminal>
|
></Terminal>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -167,16 +171,18 @@
|
|||||||
|
|
||||||
<!-- YAML editor -->
|
<!-- YAML editor -->
|
||||||
<div class="shadow-box mb-3 editor-box" :class="{'edit-mode' : isEditMode}">
|
<div class="shadow-box mb-3 editor-box" :class="{'edit-mode' : isEditMode}">
|
||||||
<prism-editor
|
<code-mirror
|
||||||
ref="editor"
|
ref="editor"
|
||||||
v-model="stack.composeYAML"
|
v-model="stack.composeYAML"
|
||||||
class="yaml-editor"
|
:extensions="extensions"
|
||||||
:highlight="highlighterYAML"
|
minimal
|
||||||
line-numbers :readonly="!isEditMode"
|
wrap="true"
|
||||||
@input="yamlCodeChange"
|
dark="true"
|
||||||
@focus="editorFocus = true"
|
tab="true"
|
||||||
@blur="editorFocus = false"
|
:disabled="!isEditMode"
|
||||||
></prism-editor>
|
:hasFocus="editorFocus"
|
||||||
|
@change="yamlCodeChange"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="isEditMode" class="mb-3">
|
<div v-if="isEditMode" class="mb-3">
|
||||||
{{ yamlError }}
|
{{ yamlError }}
|
||||||
@@ -186,15 +192,18 @@
|
|||||||
<div v-if="isEditMode">
|
<div v-if="isEditMode">
|
||||||
<h4 class="mb-3">.env</h4>
|
<h4 class="mb-3">.env</h4>
|
||||||
<div class="shadow-box mb-3 editor-box" :class="{'edit-mode' : isEditMode}">
|
<div class="shadow-box mb-3 editor-box" :class="{'edit-mode' : isEditMode}">
|
||||||
<prism-editor
|
<code-mirror
|
||||||
ref="editor"
|
ref="editor"
|
||||||
v-model="stack.composeENV"
|
v-model="stack.composeENV"
|
||||||
class="env-editor"
|
:extensions="extensionsEnv"
|
||||||
:highlight="highlighterENV"
|
minimal
|
||||||
line-numbers :readonly="!isEditMode"
|
wrap="true"
|
||||||
@focus="editorFocus = true"
|
dark="true"
|
||||||
@blur="editorFocus = false"
|
tab="true"
|
||||||
></prism-editor>
|
:disabled="!isEditMode"
|
||||||
|
:hasFocus="editorFocus"
|
||||||
|
@change="yamlCodeChange"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -229,7 +238,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Delete Dialog -->
|
<!-- Delete Dialog -->
|
||||||
<BModal v-model="showDeleteDialog" :okTitle="$t('deleteStack')" okVariant="danger" @ok="deleteDialog">
|
<BModal v-model="showDeleteDialog" :cancelTitle="$t('cancel')" :okTitle="$t('deleteStack')" okVariant="danger" @ok="deleteDialog">
|
||||||
{{ $t("deleteStackMsg") }}
|
{{ $t("deleteStackMsg") }}
|
||||||
</BModal>
|
</BModal>
|
||||||
</div>
|
</div>
|
||||||
@@ -237,13 +246,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { highlight, languages } from "prismjs/components/prism-core";
|
import CodeMirror from "vue-codemirror6";
|
||||||
import { PrismEditor } from "vue-prism-editor";
|
import { yaml } from "@codemirror/lang-yaml";
|
||||||
import "prismjs/components/prism-yaml";
|
import { python } from "@codemirror/lang-python";
|
||||||
|
import { dracula as editorTheme } from "thememirror";
|
||||||
|
import { lineNumbers, EditorView } from "@codemirror/view";
|
||||||
import { parseDocument, Document } from "yaml";
|
import { parseDocument, Document } from "yaml";
|
||||||
|
|
||||||
import "prismjs/themes/prism-tomorrow.css";
|
|
||||||
import "vue-prism-editor/dist/prismeditor.min.css";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||||
import {
|
import {
|
||||||
COMBINED_TERMINAL_COLS,
|
COMBINED_TERMINAL_COLS,
|
||||||
@@ -257,8 +266,9 @@ import {
|
|||||||
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";
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
const template = `version: "3.8"
|
const template = `
|
||||||
services:
|
services:
|
||||||
nginx:
|
nginx:
|
||||||
image: nginx:latest
|
image: nginx:latest
|
||||||
@@ -271,17 +281,13 @@ const envDefault = "# VARIABLE=value #comment";
|
|||||||
let yamlErrorTimeout = null;
|
let yamlErrorTimeout = null;
|
||||||
|
|
||||||
let serviceStatusTimeout = null;
|
let serviceStatusTimeout = null;
|
||||||
let prismjsSymbolDefinition = {
|
let dockerStatsTimeout = null;
|
||||||
"symbol": {
|
|
||||||
pattern: /(?<!\$)\$(\{[^{}]*\}|\w+)/,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
NetworkInput,
|
NetworkInput,
|
||||||
FontAwesomeIcon,
|
FontAwesomeIcon,
|
||||||
PrismEditor,
|
CodeMirror,
|
||||||
BModal,
|
BModal,
|
||||||
},
|
},
|
||||||
beforeRouteUpdate(to, from, next) {
|
beforeRouteUpdate(to, from, next) {
|
||||||
@@ -290,10 +296,35 @@ export default {
|
|||||||
beforeRouteLeave(to, from, next) {
|
beforeRouteLeave(to, from, next) {
|
||||||
this.exitConfirm(next);
|
this.exitConfirm(next);
|
||||||
},
|
},
|
||||||
|
setup() {
|
||||||
|
const editorFocus = ref(false);
|
||||||
|
|
||||||
|
const focusEffectHandler = (state, focusing) => {
|
||||||
|
editorFocus.value = focusing;
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const extensions = [
|
||||||
|
editorTheme,
|
||||||
|
yaml(),
|
||||||
|
lineNumbers(),
|
||||||
|
EditorView.focusChangeEffect.of(focusEffectHandler)
|
||||||
|
];
|
||||||
|
|
||||||
|
const extensionsEnv = [
|
||||||
|
editorTheme,
|
||||||
|
python(),
|
||||||
|
lineNumbers(),
|
||||||
|
EditorView.focusChangeEffect.of(focusEffectHandler)
|
||||||
|
];
|
||||||
|
|
||||||
|
return { extensions,
|
||||||
|
extensionsEnv,
|
||||||
|
editorFocus };
|
||||||
|
},
|
||||||
yamlDoc: null, // For keeping the yaml comments
|
yamlDoc: null, // For keeping the yaml comments
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
editorFocus: false,
|
|
||||||
jsonConfig: {},
|
jsonConfig: {},
|
||||||
envsubstJSONConfig: {},
|
envsubstJSONConfig: {},
|
||||||
yamlError: "",
|
yamlError: "",
|
||||||
@@ -306,15 +337,16 @@ export default {
|
|||||||
|
|
||||||
},
|
},
|
||||||
serviceStatusList: {},
|
serviceStatusList: {},
|
||||||
|
dockerStats: {},
|
||||||
isEditMode: false,
|
isEditMode: false,
|
||||||
submitted: false,
|
submitted: false,
|
||||||
showDeleteDialog: false,
|
showDeleteDialog: false,
|
||||||
newContainerName: "",
|
newContainerName: "",
|
||||||
stopServiceStatusTimeout: false,
|
stopServiceStatusTimeout: false,
|
||||||
|
stopDockerStatsTimeout: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
||||||
endpointDisplay() {
|
endpointDisplay() {
|
||||||
return this.$root.endpointDisplayFunction(this.endpoint);
|
return this.$root.endpointDisplayFunction(this.endpoint);
|
||||||
},
|
},
|
||||||
@@ -478,6 +510,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.requestServiceStatus();
|
this.requestServiceStatus();
|
||||||
|
this.requestDockerStats();
|
||||||
},
|
},
|
||||||
unmounted() {
|
unmounted() {
|
||||||
|
|
||||||
@@ -490,7 +523,19 @@ export default {
|
|||||||
}, 5000);
|
}, 5000);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
startDockerStatsTimeout() {
|
||||||
|
clearTimeout(dockerStatsTimeout);
|
||||||
|
dockerStatsTimeout = setTimeout(async () => {
|
||||||
|
this.requestDockerStats();
|
||||||
|
}, 5000);
|
||||||
|
},
|
||||||
|
|
||||||
requestServiceStatus() {
|
requestServiceStatus() {
|
||||||
|
// Do not request if it is add mode
|
||||||
|
if (this.isAdd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.$root.emitAgent(this.endpoint, "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;
|
||||||
@@ -501,9 +546,20 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
requestDockerStats() {
|
||||||
|
this.$root.emitAgent(this.endpoint, "dockerStats", (res) => {
|
||||||
|
if (res.ok) {
|
||||||
|
this.dockerStats = res.dockerStats;
|
||||||
|
}
|
||||||
|
if (!this.stopDockerStatsTimeout) {
|
||||||
|
this.startDockerStatsTimeout();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
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(this.$t("confirmLeaveStack"))) {
|
||||||
this.exitAction();
|
this.exitAction();
|
||||||
next();
|
next();
|
||||||
} else {
|
} else {
|
||||||
@@ -518,7 +574,9 @@ export default {
|
|||||||
exitAction() {
|
exitAction() {
|
||||||
console.log("exitAction");
|
console.log("exitAction");
|
||||||
this.stopServiceStatusTimeout = true;
|
this.stopServiceStatusTimeout = true;
|
||||||
|
this.stopDockerStatsTimeout = true;
|
||||||
clearTimeout(serviceStatusTimeout);
|
clearTimeout(serviceStatusTimeout);
|
||||||
|
clearTimeout(dockerStatsTimeout);
|
||||||
|
|
||||||
// Leave Combined Terminal
|
// Leave Combined Terminal
|
||||||
console.debug("leaveCombinedTerminal", this.endpoint, this.stack.name);
|
console.debug("leaveCombinedTerminal", this.endpoint, this.stack.name);
|
||||||
@@ -659,46 +717,6 @@ export default {
|
|||||||
this.isEditMode = false;
|
this.isEditMode = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
highlighterYAML(code) {
|
|
||||||
if (!languages.yaml_with_symbols) {
|
|
||||||
languages.yaml_with_symbols = languages.insertBefore("yaml", "punctuation", {
|
|
||||||
"symbol": prismjsSymbolDefinition["symbol"]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return highlight(code, languages.yaml_with_symbols);
|
|
||||||
},
|
|
||||||
|
|
||||||
highlighterENV(code) {
|
|
||||||
if (!languages.docker_env) {
|
|
||||||
languages.docker_env = {
|
|
||||||
"comment": {
|
|
||||||
pattern: /(^#| #).*$/m,
|
|
||||||
greedy: true
|
|
||||||
},
|
|
||||||
"keyword": {
|
|
||||||
pattern: /^\w*(?=[:=])/m,
|
|
||||||
greedy: true
|
|
||||||
},
|
|
||||||
"value": {
|
|
||||||
pattern: /(?<=[:=]).*?((?= #)|$)/m,
|
|
||||||
greedy: true,
|
|
||||||
inside: {
|
|
||||||
"string": [
|
|
||||||
{
|
|
||||||
pattern: /^ *'.*?(?<!\\)'/m,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
pattern: /^ *".*?(?<!\\)"|^.*$/m,
|
|
||||||
inside: prismjsSymbolDefinition
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return highlight(code, languages.docker_env);
|
|
||||||
},
|
|
||||||
|
|
||||||
yamlToJSON(yaml) {
|
yamlToJSON(yaml) {
|
||||||
let doc = parseDocument(yaml);
|
let doc = parseDocument(yaml);
|
||||||
if (doc.errors.length > 0) {
|
if (doc.errors.length > 0) {
|
||||||
@@ -786,6 +804,44 @@ export default {
|
|||||||
this.stack.name = this.stack?.name?.toLowerCase();
|
this.stack.name = this.stack?.name?.toLowerCase();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
startService(serviceName) {
|
||||||
|
this.processing = true;
|
||||||
|
|
||||||
|
this.$root.emitAgent(this.endpoint, "startService", this.stack.name, serviceName, (res) => {
|
||||||
|
this.processing = false;
|
||||||
|
this.$root.toastRes(res);
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
this.requestServiceStatus(); // Refresh service status
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
stopService(serviceName) {
|
||||||
|
this.processing = true;
|
||||||
|
|
||||||
|
this.$root.emitAgent(this.endpoint, "stopService", this.stack.name, serviceName, (res) => {
|
||||||
|
this.processing = false;
|
||||||
|
this.$root.toastRes(res);
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
this.requestServiceStatus(); // Refresh service status
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
restartService(serviceName) {
|
||||||
|
this.processing = true;
|
||||||
|
|
||||||
|
this.$root.emitAgent(this.endpoint, "restartService", this.stack.name, serviceName, (res) => {
|
||||||
|
this.processing = false;
|
||||||
|
this.$root.toastRes(res);
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
this.requestServiceStatus(); // Refresh service status
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -800,9 +856,6 @@ export default {
|
|||||||
.editor-box {
|
.editor-box {
|
||||||
font-family: 'JetBrains Mono', monospace;
|
font-family: 'JetBrains Mono', monospace;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
&.edit-mode {
|
|
||||||
background-color: #2c2f38 !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.agent-name {
|
.agent-name {
|
||||||
|
|||||||
@@ -1,35 +1,41 @@
|
|||||||
<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">{{ $t("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">{{ $t("Console is not enabled") }}</h4>
|
||||||
|
<i18n-t keypath="ConsoleNotEnabledMSG1" tag="p">
|
||||||
|
<template #docker><code>{{ $t('dockerCode') }}</code></template>
|
||||||
|
<template #rm><code>{{ $t('rmCode') }}</code></template>
|
||||||
|
</i18n-t>
|
||||||
|
|
||||||
|
<i18n-t keypath="ConsoleNotEnabledMSG2" tag="p">
|
||||||
|
<template #rmRf>
|
||||||
|
<code>{{ $t('rmRfCode') }}</code>
|
||||||
</template>
|
</template>
|
||||||
</p>
|
</i18n-t>
|
||||||
</div>
|
|
||||||
|
|
||||||
<Terminal class="terminal" :rows="20" mode="mainTerminal" name="console" :endpoint="endpoint"></Terminal>
|
<i18n-t keypath="ConsoleNotEnabledMSG3" tag="p">
|
||||||
|
<template #envVar>
|
||||||
|
<code>{{ $t('envVarCode') }}</code>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import { allowedCommandList } from "../../../common/util-common";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
allowedCommandList,
|
processing: true,
|
||||||
|
enableConsole: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -38,7 +44,10 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.$root.emitAgent(this.endpoint, "checkMainTerminal", (res) => {
|
||||||
|
this.enableConsole = res.ok;
|
||||||
|
this.processing = false;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<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" :endpoint="endpoint"></Terminal>
|
<Terminal class="terminal" :rows="20" mode="interactive" :name="terminalName" :stack-name="stackName" :service-name="serviceName" :shell="shell" :endpoint="endpoint"></Terminal>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
<!-- Docker Run -->
|
<!-- Docker Run -->
|
||||||
<h2 class="mb-3">{{ $t("Docker Run") }}</h2>
|
<h2 class="mb-3">{{ $t("Docker Run") }}</h2>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<textarea id="name" v-model="dockerRunCommand" type="text" class="form-control docker-run" required placeholder="docker run ..."></textarea>
|
<textarea id="name" v-model="dockerRunCommand" type="text" class="form-control docker-run shadow-box" required placeholder="docker run ..."></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="btn-normal btn mb-4" @click="convertDockerRun">{{ $t("Convert to Compose") }}</button>
|
<button class="btn-normal btn mb-4" @click="convertDockerRun">{{ $t("Convert to Compose") }}</button>
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
<div class="shadow-box big-padding">
|
<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>
|
<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">
|
<div v-for="(agentItem, endpoint) in $root.agentList" :key="endpoint" class="mb-3 agent">
|
||||||
<!-- Agent Status -->
|
<!-- Agent Status -->
|
||||||
<template v-if="$root.agentStatusList[endpoint]">
|
<template v-if="$root.agentStatusList[endpoint]">
|
||||||
<span v-if="$root.agentStatusList[endpoint] === 'online'" class="badge bg-primary me-2">{{ $t("agentOnline") }}</span>
|
<span v-if="$root.agentStatusList[endpoint] === 'online'" class="badge bg-primary me-2">{{ $t("agentOnline") }}</span>
|
||||||
@@ -49,15 +49,27 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- Agent Display Name -->
|
<!-- Agent Display Name -->
|
||||||
<span v-if="endpoint === ''">{{ $t("currentEndpoint") }}</span>
|
<template v-if="$root.agentStatusList[endpoint]">
|
||||||
<a v-else :href="agent.url" target="_blank">{{ endpoint }}</a>
|
<span v-if="endpoint === '' && agentItem.name === ''" class="badge bg-secondary me-2">Current</span>
|
||||||
|
<span v-else-if="agentItem.name === ''" :href="agentItem.url" class="me-2">{{ endpoint }}</span>
|
||||||
|
<span v-else :href="agentItem.url" class="me-2">{{ agentItem.name }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Edit Name -->
|
||||||
|
<font-awesome-icon v-if="agentItem.name !== ''" icon="pen-to-square" @click="showEditAgentNameDialog[agentItem.name] = !showEditAgentNameDialog[agentItem.Name]" />
|
||||||
|
|
||||||
|
<!-- Edit Dialog -->
|
||||||
|
<BModal v-model="showEditAgentNameDialog[agentItem.name]" :no-close-on-backdrop="true" :close-on-esc="true" :okTitle="$t('Update Name')" okVariant="info" @ok="updateName(agentItem.url, agentItem.updatedName)">
|
||||||
|
<label for="Update Name" class="form-label">Current value: {{ $t(agentItem.name) }}</label>
|
||||||
|
<input id="updatedName" v-model="agentItem.updatedName" type="text" class="form-control" optional>
|
||||||
|
</BModal>
|
||||||
|
|
||||||
<!-- Remove Button -->
|
<!-- Remove Button -->
|
||||||
<font-awesome-icon v-if="endpoint !== ''" class="ms-2 remove-agent" icon="trash" @click="showRemoveAgentDialog[agent.url] = !showRemoveAgentDialog[agent.url]" />
|
<font-awesome-icon v-if="endpoint !== ''" class="ms-2 remove-agent" icon="trash" @click="showRemoveAgentDialog[agentItem.url] = !showRemoveAgentDialog[agentItem.url]" />
|
||||||
|
|
||||||
<!-- Remoe Agent Dialog -->
|
<!-- Remove Agent Dialog -->
|
||||||
<BModal v-model="showRemoveAgentDialog[agent.url]" :okTitle="$t('removeAgent')" okVariant="danger" @ok="removeAgent(agent.url)">
|
<BModal v-model="showRemoveAgentDialog[agentItem.url]" :okTitle="$t('removeAgent')" okVariant="danger" @ok="removeAgent(agentItem.url)">
|
||||||
<p>{{ agent.url }}</p>
|
<p>{{ agentItem.url }}</p>
|
||||||
{{ $t("removeAgentMsg") }}
|
{{ $t("removeAgentMsg") }}
|
||||||
</BModal>
|
</BModal>
|
||||||
</div>
|
</div>
|
||||||
@@ -81,6 +93,11 @@
|
|||||||
<input id="password" v-model="agent.password" type="password" class="form-control" required autocomplete="new-password">
|
<input id="password" v-model="agent.password" type="password" class="form-control" required autocomplete="new-password">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="name" class="form-label">{{ $t("Friendly Name") }}</label>
|
||||||
|
<input id="name" v-model="agent.name" type="text" class="form-control" optional>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary" :disabled="connectingAgent">
|
<button type="submit" class="btn btn-primary" :disabled="connectingAgent">
|
||||||
<template v-if="connectingAgent">{{ $t("connecting") }}</template>
|
<template v-if="connectingAgent">{{ $t("connecting") }}</template>
|
||||||
<template v-else>{{ $t("connect") }}</template>
|
<template v-else>{{ $t("connect") }}</template>
|
||||||
@@ -121,11 +138,14 @@ export default {
|
|||||||
dockerRunCommand: "",
|
dockerRunCommand: "",
|
||||||
showAgentForm: false,
|
showAgentForm: false,
|
||||||
showRemoveAgentDialog: {},
|
showRemoveAgentDialog: {},
|
||||||
|
showEditAgentNameDialog: {},
|
||||||
connectingAgent: false,
|
connectingAgent: false,
|
||||||
agent: {
|
agent: {
|
||||||
url: "http://",
|
url: "http://",
|
||||||
username: "",
|
username: "",
|
||||||
password: "",
|
password: "",
|
||||||
|
name: "",
|
||||||
|
updatedName: "",
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -199,6 +219,19 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateName(url, updatedName) {
|
||||||
|
this.$root.getSocket().emit("updateAgent", url, updatedName, (res) => {
|
||||||
|
this.$root.toastRes(res);
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
this.showAgentForm = false;
|
||||||
|
this.agent = {
|
||||||
|
updatedName: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
getStatusNum(statusName) {
|
getStatusNum(statusName) {
|
||||||
let num = 0;
|
let num = 0;
|
||||||
|
|
||||||
@@ -286,7 +319,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -326,7 +359,6 @@ table {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.docker-run {
|
.docker-run {
|
||||||
background-color: $dark-bg !important;
|
|
||||||
border: none;
|
border: none;
|
||||||
font-family: 'JetBrains Mono', monospace;
|
font-family: 'JetBrains Mono', monospace;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
|
|||||||
@@ -83,6 +83,9 @@ export default {
|
|||||||
security: {
|
security: {
|
||||||
title: this.$t("Security"),
|
title: this.$t("Security"),
|
||||||
},
|
},
|
||||||
|
globalEnv: {
|
||||||
|
title: this.$t("GlobalEnv"),
|
||||||
|
},
|
||||||
about: {
|
about: {
|
||||||
title: this.$t("About"),
|
title: this.$t("About"),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ const Settings = () => import("./pages/Settings.vue");
|
|||||||
import Appearance from "./components/settings/Appearance.vue";
|
import Appearance from "./components/settings/Appearance.vue";
|
||||||
import General from "./components/settings/General.vue";
|
import General from "./components/settings/General.vue";
|
||||||
const Security = () => import("./components/settings/Security.vue");
|
const Security = () => import("./components/settings/Security.vue");
|
||||||
|
const GlobalEnv = () => import("./components/settings/GlobalEnv.vue");
|
||||||
import About from "./components/settings/About.vue";
|
import About from "./components/settings/About.vue";
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
@@ -78,6 +79,10 @@ const routes = [
|
|||||||
path: "security",
|
path: "security",
|
||||||
component: Security,
|
component: Security,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "globalEnv",
|
||||||
|
component: GlobalEnv,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "about",
|
path: "about",
|
||||||
component: About,
|
component: About,
|
||||||
|
|||||||
@@ -593,9 +593,6 @@ optgroup {
|
|||||||
color: $primary;
|
color: $primary;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prism-editor__textarea {
|
|
||||||
outline: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
h5.settings-subheading::after {
|
h5.settings-subheading::after {
|
||||||
content: "";
|
content: "";
|
||||||
@@ -656,13 +653,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;
|
||||||
@@ -683,18 +673,25 @@ code {
|
|||||||
color: $dark-font-color3;
|
color: $dark-font-color3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vue Prism Editor bug - workaround
|
|
||||||
// https://github.com/koca/vue-prism-editor/issues/87
|
.cm-gutters {
|
||||||
/*
|
background-color: transparent !important;
|
||||||
.prism-editor__textarea {
|
|
||||||
width: 999999px !important;
|
|
||||||
}
|
}
|
||||||
.prism-editor__editor {
|
.dark [contenteditable="true"] {
|
||||||
white-space: pre !important;
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
.cm-editor {
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
.cm-activeLine, .cm-activeLineGutter {
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
.cm-selectionBackground {
|
||||||
|
background-color: #74c2ff3d !important;
|
||||||
|
}
|
||||||
|
.cm-focused {
|
||||||
|
outline: none !important;
|
||||||
}
|
}
|
||||||
.prism-editor__container {
|
|
||||||
overflow-x: scroll !important;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// Localization
|
// Localization
|
||||||
@import "localization.scss";
|
@import "localization.scss";
|
||||||
|
|||||||
10709
package-lock.json
generated
Normal file
10709
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
71
package.json
71
package.json
@@ -1,65 +1,67 @@
|
|||||||
{
|
{
|
||||||
"name": "dockge",
|
"name": "dockge",
|
||||||
"version": "1.4.0",
|
"version": "1.5.0",
|
||||||
"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 && pnpm run build:frontend && npm run build:docker-beta",
|
"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 -t louislam/dockge:beta -t louislam/dockge:nightly --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-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-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": "pnpm run build:frontend && docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:nightly --target nightly -f ./docker/Dockerfile . --push",
|
|
||||||
"build:healthcheck": "docker buildx build -f docker/BuildHealthCheck.Dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:build-healthcheck . --push",
|
"build:healthcheck": "docker buildx build -f docker/BuildHealthCheck.Dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:build-healthcheck . --push",
|
||||||
|
"release-nightly": "npm run build:frontend && docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:nightly -t ghcr.io/louislam/dockge:nightly --target nightly -f ./docker/Dockerfile . --push",
|
||||||
"start-docker": "docker run --rm -p 5001:5001 --name dockge louislam/dockge:latest",
|
"start-docker": "docker run --rm -p 5001:5001 --name dockge louislam/dockge:latest",
|
||||||
"mark-as-nightly": "tsx ./extra/mark-as-nightly.ts",
|
"mark-as-nightly": "tsx ./extra/mark-as-nightly.ts",
|
||||||
"reformat-changelog": "tsx ./extra/reformat-changelog.ts",
|
"reformat-changelog": "tsx ./extra/reformat-changelog.ts",
|
||||||
"reset-password": "tsx ./extra/reset-password.ts"
|
"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",
|
||||||
"semver": "^7.5.4",
|
"semver": "^7.7.1",
|
||||||
"socket.io": "~4.7.2",
|
"socket.io": "~4.8.1",
|
||||||
"socket.io-client": "~4.7.2",
|
"socket.io-client": "~4.8.1",
|
||||||
"timezones-list": "~3.0.2",
|
"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",
|
"@codemirror/lang-python": "^6.1.7",
|
||||||
|
"@codemirror/lang-yaml": "^6.1.2",
|
||||||
|
"@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",
|
||||||
@@ -68,32 +70,33 @@
|
|||||||
"@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.5.6",
|
"@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",
|
||||||
"bootstrap-vue-next": "~0.14.10",
|
"bootstrap-vue-next": "~0.14.10",
|
||||||
|
"codemirror": "^6.0.1",
|
||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
"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",
|
|
||||||
"sass": "~1.68.0",
|
"sass": "~1.68.0",
|
||||||
|
"thememirror": "^2.0.1",
|
||||||
"typescript": "~5.2.2",
|
"typescript": "~5.2.2",
|
||||||
"unplugin-vue-components": "~0.25.2",
|
"unplugin-vue-components": "~0.25.2",
|
||||||
"vite": "~5.0.10",
|
"vite": "~5.4.15",
|
||||||
"vite-plugin-compression": "~0.5.1",
|
"vite-plugin-compression": "~0.5.1",
|
||||||
"vue": "~3.3.13",
|
"vue": "~3.5.13",
|
||||||
|
"vue-codemirror6": "^1.3.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-qrcode": "~2.2.2",
|
||||||
"vue-qrcode": "~2.2.0",
|
"vue-router": "~4.5.0",
|
||||||
"vue-router": "~4.2.5",
|
|
||||||
"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"
|
||||||
|
|||||||
5649
pnpm-lock.yaml
generated
5649
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user