mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-09 16:00:29 +00:00
Compare commits
221
Commits
release-2.1.1
...
v2.1.7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e052b8ef2 | ||
|
|
1d40bdc11b | ||
|
|
3b62197f29 | ||
|
|
2d78ca5a86 | ||
|
|
a9b65b7ce5 | ||
|
|
291b40a45d | ||
|
|
a959da7d44 | ||
|
|
26c4e8323f | ||
|
|
0534badce7 | ||
|
|
594d818db0 | ||
|
|
04dceb93a7 | ||
|
|
7b554c0d61 | ||
|
|
bb642cd8a8 | ||
|
|
40249b527d | ||
|
|
74ff7ddc19 | ||
|
|
096ec8edc7 | ||
|
|
0d19069059 | ||
|
|
0da833302b | ||
|
|
4a69cf0ff5 | ||
|
|
39704ef4e5 | ||
|
|
e52cfeeb78 | ||
|
|
476429005c | ||
|
|
6fb41dc711 | ||
|
|
2cb2058cfa | ||
|
|
8533dd1d33 | ||
|
|
0cfbcd25fc | ||
|
|
b91ca8bdb3 | ||
|
|
72764863bc | ||
|
|
a7b9fd0181 | ||
|
|
2b3164fe82 | ||
|
|
2c6307049a | ||
|
|
acc1b9270f | ||
|
|
60e4143525 | ||
|
|
99bcd6d059 | ||
|
|
c4259dd004 | ||
|
|
1c5a4bc978 | ||
|
|
42f6e9a9a6 | ||
|
|
569363530a | ||
|
|
ec9eba0b9a | ||
|
|
5376d54089 | ||
|
|
c10e5747b0 | ||
|
|
5a80931a49 | ||
|
|
f541379daa | ||
|
|
0018335d64 | ||
|
|
df90286253 | ||
|
|
8dfa7945d7 | ||
|
|
1f4292d9cf | ||
|
|
e635a7beba | ||
|
|
59c657e403 | ||
|
|
ff8f79ed52 | ||
|
|
57a7edf481 | ||
|
|
bbd33c66f1 | ||
|
|
b05ed1a0ca | ||
|
|
b645c4edb4 | ||
|
|
9f61aac8be | ||
|
|
11d78644ee | ||
|
|
b96428e1d6 | ||
|
|
6432c35b1a | ||
|
|
fa843f0940 | ||
|
|
5c7ffc561b | ||
|
|
c2f0c8c6ac | ||
|
|
5bf6d17451 | ||
|
|
7f799004c3 | ||
|
|
c68251cada | ||
|
|
23e7d03d8d | ||
|
|
5085db7a2f | ||
|
|
a9cf7a7d1d | ||
|
|
cf18995182 | ||
|
|
24d7dc0786 | ||
|
|
142c9b9a71 | ||
|
|
f3c7603f6e | ||
|
|
db26da1700 | ||
|
|
d5ff59f4e7 | ||
|
|
85b88db99e | ||
|
|
f7053934b5 | ||
|
|
bf1339bb40 | ||
|
|
43c5e62c23 | ||
|
|
28634ce631 | ||
|
|
72263dfdda | ||
|
|
479d36e25f | ||
|
|
2f3ad3537e | ||
|
|
2dfe5096f0 | ||
|
|
4375722aef | ||
|
|
bde4d06842 | ||
|
|
17a486b2c4 | ||
|
|
8499c09571 | ||
|
|
7cb70f785a | ||
|
|
f3781320de | ||
|
|
869ee75020 | ||
|
|
1d5a26373d | ||
|
|
176fd0f8ad | ||
|
|
341a55ae63 | ||
|
|
fb87cf545f | ||
|
|
6db2a9352e | ||
|
|
21826bcc1a | ||
|
|
eedbc2e95c | ||
|
|
46763df7bb | ||
|
|
175162753e | ||
|
|
d114933fe9 | ||
|
|
432412e596 | ||
|
|
1b577e3d41 | ||
|
|
eda9641d35 | ||
|
|
6808e4892c | ||
|
|
0d71cd4db2 | ||
|
|
b6b3304a71 | ||
|
|
59a0a573ed | ||
|
|
e82d686940 | ||
|
|
b48bb88aa1 | ||
|
|
5ce9ec5b64 | ||
|
|
740f3549ad | ||
|
|
aa8a9e8b1e | ||
|
|
14218e3495 | ||
|
|
c526eb2962 | ||
|
|
1ff8292be4 | ||
|
|
d4394616af | ||
|
|
462ca6b373 | ||
|
|
657c3ad37a | ||
|
|
90d11a3b74 | ||
|
|
883f3db725 | ||
|
|
75da72c265 | ||
|
|
78eef15f5b | ||
|
|
735cf3cc71 | ||
|
|
99c5d9a8b2 | ||
|
|
3a0d2039d0 | ||
|
|
98e2d6390a | ||
|
|
975e7f9f58 | ||
|
|
77c90a26f8 | ||
|
|
17cb12df8b | ||
|
|
c986b04667 | ||
|
|
754e016f20 | ||
|
|
f67ce75492 | ||
|
|
e236ca5607 | ||
|
|
10e74c4fe4 | ||
|
|
473c0a5ff2 | ||
|
|
7fbe6e4091 | ||
|
|
65dbb53a07 | ||
|
|
c21910c203 | ||
|
|
1728511bad | ||
|
|
e63f6672f8 | ||
|
|
7fa9754ce7 | ||
|
|
475e4ca629 | ||
|
|
32dee74edf | ||
|
|
6715559925 | ||
|
|
2f380c9f2f | ||
|
|
0aebb0d2db | ||
|
|
b7cf9aab2b | ||
|
|
ea2f1875bc | ||
|
|
83524940ed | ||
|
|
8c33eb8409 | ||
|
|
644655af86 | ||
|
|
8082f4d2ca | ||
|
|
3c896bc5f8 | ||
|
|
5c7ce6d8d4 | ||
|
|
222702f66b | ||
|
|
b3b3feb1b9 | ||
|
|
133140040b | ||
|
|
d70f903432 | ||
|
|
f77f305316 | ||
|
|
13b5908442 | ||
|
|
d1335db986 | ||
|
|
1e8836ad95 | ||
|
|
d155217ce3 | ||
|
|
9ddb996a44 | ||
|
|
226eccf5f1 | ||
|
|
4b2d6f0ef2 | ||
|
|
f1c9ad45a7 | ||
|
|
93489c85fa | ||
|
|
8cead06d7b | ||
|
|
625cd9f32f | ||
|
|
9aab3b42af | ||
|
|
6684cd57d7 | ||
|
|
00d95d6e19 | ||
|
|
36f03e5daf | ||
|
|
7c395a6bf8 | ||
|
|
e9a7cac5fd | ||
|
|
9f00b431d4 | ||
|
|
1705d44378 | ||
|
|
537e2c730c | ||
|
|
b4dc6b1ab0 | ||
|
|
1f22285cf4 | ||
|
|
1d546236e4 | ||
|
|
982e0bbd3e | ||
|
|
5a67a62733 | ||
|
|
961ebd30a1 | ||
|
|
9819546694 | ||
|
|
5c8d391a73 | ||
|
|
0e84b9a2a7 | ||
|
|
1f58da501e | ||
|
|
0041184a3e | ||
|
|
112468949d | ||
|
|
7e03e94460 | ||
|
|
fd9b299b14 | ||
|
|
37932d7fb2 | ||
|
|
f0f2087f54 | ||
|
|
f3de09e5d7 | ||
|
|
1a8c1613a4 | ||
|
|
5209275f28 | ||
|
|
924e59d948 | ||
|
|
87a39cab3a | ||
|
|
f9a70757a1 | ||
|
|
741acee393 | ||
|
|
c964a02088 | ||
|
|
ba71613ae6 | ||
|
|
5493b41381 | ||
|
|
c3f45d4787 | ||
|
|
a8a92da483 | ||
|
|
a77e61a861 | ||
|
|
e589728987 | ||
|
|
cc3cdef5f3 | ||
|
|
7101af0594 | ||
|
|
df6cda7143 | ||
|
|
62beb534d2 | ||
|
|
9a70bf1c2d | ||
|
|
6f4559c7cd | ||
|
|
8b2bcdc814 | ||
|
|
0f6ddf26fd | ||
|
|
ebe472a9db | ||
|
|
c1a87baad7 | ||
|
|
c7f185a184 | ||
|
|
4ca2ba5482 | ||
|
|
727b74895d |
+25
-1
@@ -1,33 +1,43 @@
|
||||
name: Create Release And Upload Cloudflare R2
|
||||
name: Build And Publish (OSS + R2)
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
create-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22.19.0'
|
||||
|
||||
- name: Build Web
|
||||
run: |
|
||||
cd frontend && npm install && npm run build:pro
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8192
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.24'
|
||||
cache-dependency-path: |
|
||||
core/go.sum
|
||||
agent/go.sum
|
||||
|
||||
- name: Build Release
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
distribution: goreleaser
|
||||
version: '~> v2'
|
||||
args: release --skip=publish --clean
|
||||
|
||||
- name: Upload Assets
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
@@ -36,10 +46,24 @@ jobs:
|
||||
files: |
|
||||
dist/*.tar.gz
|
||||
dist/checksums.txt
|
||||
|
||||
- name: Setup OSSUTIL
|
||||
uses: yizhoumo/setup-ossutil@v2
|
||||
with:
|
||||
endpoint: ${{ secrets.OSS_ENDPOINT }}
|
||||
access-key-id: ${{ secrets.OSS_ACCESS_KEY_ID }}
|
||||
access-key-secret: ${{ secrets.OSS_ACCESS_KEY_SECRET }}
|
||||
ossutil-version: '1.7.18'
|
||||
|
||||
- name: Upload Assets to OSS
|
||||
run: |
|
||||
ossutil cp -r dist/ oss://resource-fit2cloud-com/1panel/package/v2/stable/${{ github.ref_name }}/release/ --include "*.tar.gz" --include "checksums.txt" --only-current-dir --force
|
||||
|
||||
- name: Setup Rclone
|
||||
uses: AnimMouse/setup-rclone@v1
|
||||
with:
|
||||
rclone_config: ${{ secrets.RCLONE_CONFIG }}
|
||||
|
||||
- name: Upload to Cloudflare R2
|
||||
run: |
|
||||
rclone copy dist/ cloudflare_r2:package/v2/stable/${{ github.ref_name }}/release/ --include "*.tar.gz" --include "checksums.txt" --progress
|
||||
@@ -1,47 +0,0 @@
|
||||
name: Create Release And Upload assets
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
jobs:
|
||||
create-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22.19.0'
|
||||
- name: Build Web
|
||||
run: |
|
||||
cd frontend && npm install && npm run build:pro
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8192
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.24'
|
||||
- name: Build Release
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
distribution: goreleaser
|
||||
version: '~> v2'
|
||||
args: release --skip=publish --clean
|
||||
- name: Upload Assets
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
draft: true
|
||||
files: |
|
||||
dist/*.tar.gz
|
||||
dist/checksums.txt
|
||||
- name: Setup OSSUTIL
|
||||
uses: yizhoumo/setup-ossutil@v2
|
||||
with:
|
||||
endpoint: ${{ secrets.OSS_ENDPOINT }}
|
||||
access-key-id: ${{ secrets.OSS_ACCESS_KEY_ID }}
|
||||
access-key-secret: ${{ secrets.OSS_ACCESS_KEY_SECRET }}
|
||||
ossutil-version: '1.7.18'
|
||||
- name: Upload Assets to OSS
|
||||
run: ossutil cp -r dist/ oss://resource-fit2cloud-com/1panel/package/v2/stable/${{ github.ref_name }}/release/ --include "*.tar.gz" --include "checksums.txt" --only-current-dir --force
|
||||
+3
-1
@@ -73,4 +73,6 @@ agent/.golangci.yml
|
||||
openspec
|
||||
CLAUDE.md
|
||||
AGENTS.md
|
||||
opencode.json
|
||||
opencode.json
|
||||
superpowers
|
||||
.worktrees/
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<p align="center"><a href="https://1panel.pro"><img src="https://resource.1panel.pro/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
|
||||
<h3 align="center">Take control of your VPS with 1Panel</h3>
|
||||
<p align="center">
|
||||
<a href="https://trendshift.io/repositories/2462" target="_blank"><img src="https://trendshift.io/api/badge/repositories/2462" alt="1Panel-dev%2F1Panel | Trendshift" style="width: 240px; height: auto;" /></a>
|
||||
</p>
|
||||
@@ -31,39 +32,28 @@
|
||||
------------------------------
|
||||
## What is 1Panel?
|
||||
|
||||
1Panel is a modern, open-source Linux server management panel that streamlines administration through an intuitive, clean web interface.
|
||||
1Panel is a modern, open-source VPS control panel that streamlines administration through an intuitive, clean web interface.
|
||||
|
||||
## Key Features
|
||||
|
||||
🔹 **AI Integration**
|
||||
Seamlessly deploy OpenClaw agents, host local LLMs via Ollama, and monitor GPU utilization—all within a unified workspace engineered for efficient AI development and inference.
|
||||
|
||||
🔹 **One-Click Website Deployment**
|
||||
Launch production-ready websites instantly with automatic domain binding, SSL certificate provisioning, and configuration—zero manual setup required.
|
||||
|
||||
🔹 **Curated App Marketplace**
|
||||
Discover, install, and update trusted open-source applications (WordPress, MySQL, Redis, and more) with a single click.
|
||||
|
||||
🔹 **Centralized Server Management**
|
||||
Monitor system resources and manage files, databases, containers, and services effortlessly through a unified, intuitive dashboard.
|
||||
|
||||
🔹 **Enterprise-Grade Security**
|
||||
Strengthen defenses with container isolation, firewall controls, audit logging, and secure deployment practices—ensuring compliance and peace of mind.
|
||||
|
||||
🔹 **Intelligent Backup & Restore**
|
||||
Schedule automated backups or perform instant restores to local storage or leading cloud platforms (AWS S3, Cloudflare R2, and more), with flexible retention and destination options.
|
||||
- **One-Click OpenClaw Deployment**: Seamlessly deploy OpenClaw agents, host local LLMs using Ollama, and monitor GPU utilization—all within a unified workspace.
|
||||
- **One-Click Website Deployment**: Launch production-ready websites instantly with automatic domain binding, SSL certificate provisioning, and configuration—zero manual setup required.
|
||||
- **Curated App Marketplace**: Discover, install, and update trusted open-source applications (Nextcloud, Bitwarden, Umami, and more) with a single click.
|
||||
- **Centralized Server Management**: Monitor system resources and manage files, databases, containers, and services effortlessly through a unified, intuitive dashboard.
|
||||
- **Enterprise-Grade Security**: Strengthen your defenses with container isolation, firewall controls, audit logging, and secure deployment practices—ensuring compliance and peace of mind.
|
||||
- **Intelligent Backup & Restore**: Schedule automated backups or perform instant restores to local storage or major cloud platforms (AWS S3, Cloudflare R2, and more), with flexible retention and destination options.
|
||||
|
||||
## Quick Start
|
||||
|
||||
Execute the script below and follow the prompts to install 1Panel:
|
||||
|
||||
```bash
|
||||
bash -c "$(curl -sSL https://resource.fit2cloud.com/1panel/package/v2/quick_start.sh)"
|
||||
bash -c "$(curl -sSL https://resource.1panel.pro/v2/quick_start.sh)"
|
||||
```
|
||||
|
||||
## Screenshot
|
||||
|
||||

|
||||

|
||||
|
||||
## Star History
|
||||
|
||||
|
||||
+581
-8
@@ -111,9 +111,30 @@ func (b *BaseApi) UpdateAgentModelConfig(c *gin.Context) {
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Get Agent overview
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentOverviewReq true "request"
|
||||
// @Success 200 {object} dto.AgentOverview
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/overview [post]
|
||||
func (b *BaseApi) GetAgentOverview(c *gin.Context) {
|
||||
var req dto.AgentOverviewReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
res, err := agentService.GetOverview(req)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Get Providers
|
||||
// @Success 200 {object} []dto.ProviderInfo
|
||||
// @Success 200 {array} dto.ProviderInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/providers [get]
|
||||
@@ -190,6 +211,87 @@ func (b *BaseApi) PageAgentAccounts(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary List Agent account models
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentAccountModelReq true "request"
|
||||
// @Success 200 {array} dto.AgentAccountModel
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/accounts/models [post]
|
||||
func (b *BaseApi) GetAgentAccountModels(c *gin.Context) {
|
||||
var req dto.AgentAccountModelReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
list, err := agentService.GetAccountModels(req)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Create Agent account model
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentAccountModelCreateReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/accounts/models/create [post]
|
||||
func (b *BaseApi) CreateAgentAccountModel(c *gin.Context) {
|
||||
var req dto.AgentAccountModelCreateReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := agentService.CreateAccountModel(req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Update Agent account model
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentAccountModelUpdateReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/accounts/models/update [post]
|
||||
func (b *BaseApi) UpdateAgentAccountModel(c *gin.Context) {
|
||||
var req dto.AgentAccountModelUpdateReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := agentService.UpdateAccountModel(req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Delete Agent account model
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentAccountModelDeleteReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/accounts/models/delete [post]
|
||||
func (b *BaseApi) DeleteAgentAccountModel(c *gin.Context) {
|
||||
var req dto.AgentAccountModelDeleteReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := agentService.DeleteAccountModel(req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Verify Agent account
|
||||
// @Accept json
|
||||
@@ -272,19 +374,490 @@ func (b *BaseApi) UpdateAgentFeishuConfig(c *gin.Context) {
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Approve Agent Feishu pairing code
|
||||
// @Summary Get Agent Telegram channel config
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentFeishuPairingApproveReq true "request"
|
||||
// @Success 200
|
||||
// @Param request body dto.AgentTelegramConfigReq true "request"
|
||||
// @Success 200 {object} dto.AgentTelegramConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/channel/feishu/approve [post]
|
||||
func (b *BaseApi) ApproveAgentFeishuPairing(c *gin.Context) {
|
||||
var req dto.AgentFeishuPairingApproveReq
|
||||
// @Router /ai/agents/channel/telegram/get [post]
|
||||
func (b *BaseApi) GetAgentTelegramConfig(c *gin.Context) {
|
||||
var req dto.AgentTelegramConfigReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := agentService.ApproveFeishuPairing(req); err != nil {
|
||||
data, err := agentService.GetTelegramConfig(req)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Update Agent Telegram channel config
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentTelegramConfigUpdateReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/channel/telegram/update [post]
|
||||
func (b *BaseApi) UpdateAgentTelegramConfig(c *gin.Context) {
|
||||
var req dto.AgentTelegramConfigUpdateReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := agentService.UpdateTelegramConfig(req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Get Agent Discord channel config
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentIDReq true "request"
|
||||
// @Success 200 {object} dto.AgentDiscordConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/channel/discord/get [post]
|
||||
func (b *BaseApi) GetAgentDiscordConfig(c *gin.Context) {
|
||||
var req dto.AgentIDReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
data, err := agentService.GetDiscordConfig(req)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Update Agent Discord channel config
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentDiscordConfigUpdateReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/channel/discord/update [post]
|
||||
func (b *BaseApi) UpdateAgentDiscordConfig(c *gin.Context) {
|
||||
var req dto.AgentDiscordConfigUpdateReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := agentService.UpdateDiscordConfig(req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Get Agent QQ Bot channel config
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentIDReq true "request"
|
||||
// @Success 200 {object} dto.AgentWecomConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/channel/wecom/get [post]
|
||||
func (b *BaseApi) GetAgentWecomConfig(c *gin.Context) {
|
||||
var req dto.AgentIDReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
data, err := agentService.GetWecomConfig(req)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Update Agent WeCom channel config
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentWecomConfigUpdateReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/channel/wecom/update [post]
|
||||
func (b *BaseApi) UpdateAgentWecomConfig(c *gin.Context) {
|
||||
var req dto.AgentWecomConfigUpdateReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := agentService.UpdateWecomConfig(req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Get Agent DingTalk channel config
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentIDReq true "request"
|
||||
// @Success 200 {object} dto.AgentDingTalkConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/channel/dingtalk/get [post]
|
||||
func (b *BaseApi) GetAgentDingTalkConfig(c *gin.Context) {
|
||||
var req dto.AgentIDReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
data, err := agentService.GetDingTalkConfig(req)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Update Agent DingTalk channel config
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentDingTalkConfigUpdateReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/channel/dingtalk/update [post]
|
||||
func (b *BaseApi) UpdateAgentDingTalkConfig(c *gin.Context) {
|
||||
var req dto.AgentDingTalkConfigUpdateReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := agentService.UpdateDingTalkConfig(req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Get Agent QQ Bot channel config
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentIDReq true "request"
|
||||
// @Success 200 {object} dto.AgentQQBotConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/channel/qqbot/get [post]
|
||||
func (b *BaseApi) GetAgentQQBotConfig(c *gin.Context) {
|
||||
var req dto.AgentIDReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
data, err := agentService.GetQQBotConfig(req)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Update Agent QQ Bot channel config
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentQQBotConfigUpdateReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/channel/qqbot/update [post]
|
||||
func (b *BaseApi) UpdateAgentQQBotConfig(c *gin.Context) {
|
||||
var req dto.AgentQQBotConfigUpdateReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := agentService.UpdateQQBotConfig(req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Install Agent plugin
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentPluginInstallReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/plugin/install [post]
|
||||
func (b *BaseApi) InstallAgentPlugin(c *gin.Context) {
|
||||
var req dto.AgentPluginInstallReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := agentService.InstallPlugin(req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Check Agent plugin installation status
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentPluginCheckReq true "request"
|
||||
// @Success 200 {object} dto.AgentPluginStatus
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/plugin/check [post]
|
||||
func (b *BaseApi) CheckAgentPlugin(c *gin.Context) {
|
||||
var req dto.AgentPluginCheckReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
data, err := agentService.CheckPlugin(req)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Get Agent Security config
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentIDReq true "request"
|
||||
// @Success 200 {object} dto.AgentSecurityConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/security/get [post]
|
||||
func (b *BaseApi) GetAgentSecurityConfig(c *gin.Context) {
|
||||
var req dto.AgentIDReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
data, err := agentService.GetSecurityConfig(req)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Update Agent Security config
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentSecurityConfigUpdateReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/security/update [post]
|
||||
func (b *BaseApi) UpdateAgentSecurityConfig(c *gin.Context) {
|
||||
var req dto.AgentSecurityConfigUpdateReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := agentService.UpdateSecurityConfig(req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Get Agent Other config
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentIDReq true "request"
|
||||
// @Success 200 {object} dto.AgentOtherConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/other/get [post]
|
||||
func (b *BaseApi) GetAgentOtherConfig(c *gin.Context) {
|
||||
var req dto.AgentIDReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
data, err := agentService.GetOtherConfig(req)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Update Agent Other config
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentOtherConfigUpdateReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/other/update [post]
|
||||
func (b *BaseApi) UpdateAgentOtherConfig(c *gin.Context) {
|
||||
var req dto.AgentOtherConfigUpdateReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := agentService.UpdateOtherConfig(req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Get Agent config file
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentConfigFileReq true "request"
|
||||
// @Success 200 {object} dto.AgentConfigFile
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/config-file/get [post]
|
||||
func (b *BaseApi) GetAgentConfigFile(c *gin.Context) {
|
||||
var req dto.AgentConfigFileReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
data, err := agentService.GetConfigFile(req)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Update Agent config file
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentConfigFileUpdateReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/config-file/update [post]
|
||||
func (b *BaseApi) UpdateAgentConfigFile(c *gin.Context) {
|
||||
var req dto.AgentConfigFileUpdateReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := agentService.UpdateConfigFile(req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary List Agent skills
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentIDReq true "request"
|
||||
// @Success 200 {array} dto.AgentSkillItem
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/skills/list [post]
|
||||
func (b *BaseApi) ListAgentSkills(c *gin.Context) {
|
||||
var req dto.AgentIDReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
data, err := agentService.ListSkills(req)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Search Agent skills
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentSkillSearchReq true "request"
|
||||
// @Success 200 {array} dto.AgentSkillSearchItem
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/skills/search [post]
|
||||
func (b *BaseApi) SearchAgentSkills(c *gin.Context) {
|
||||
var req dto.AgentSkillSearchReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
data, err := agentService.SearchSkills(req)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Update Agent skill status
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentSkillUpdateReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/skills/update [post]
|
||||
func (b *BaseApi) UpdateAgentSkill(c *gin.Context) {
|
||||
var req dto.AgentSkillUpdateReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := agentService.UpdateSkill(req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Install Agent skill
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentSkillInstallReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/skills/install [post]
|
||||
func (b *BaseApi) InstallAgentSkill(c *gin.Context) {
|
||||
var req dto.AgentSkillInstallReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := agentService.InstallSkill(req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Login Agent Weixin channel
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentWeixinLoginReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/channel/weixin/login [post]
|
||||
func (b *BaseApi) LoginAgentWeixinChannel(c *gin.Context) {
|
||||
var req dto.AgentWeixinLoginReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := agentService.LoginWeixinChannel(req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags AI
|
||||
// @Summary Approve Agent channel pairing code
|
||||
// @Accept json
|
||||
// @Param request body dto.AgentChannelPairingApproveReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /ai/agents/channel/pairing/approve [post]
|
||||
func (b *BaseApi) ApproveAgentChannelPairing(c *gin.Context) {
|
||||
var req dto.AgentChannelPairingApproveReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := agentService.ApproveChannelPairing(req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ func (b *BaseApi) LoadPort(c *gin.Context) {
|
||||
// @Success 200 {object} response.DatabaseConn
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /apps/installed/conninfo [POST]
|
||||
// @Router /apps/installed/conninfo [post]
|
||||
func (b *BaseApi) LoadConnInfo(c *gin.Context) {
|
||||
var req dto.OperationWithNameAndType
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -349,3 +349,15 @@ func (b *BaseApi) GetAppInstallInfo(c *gin.Context) {
|
||||
}
|
||||
helper.SuccessWithData(c, info)
|
||||
}
|
||||
|
||||
func (b *BaseApi) UpdateAppInstallSort(c *gin.Context) {
|
||||
var req request.AppInstallSort
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := appInstallService.UpdateSort(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
@@ -430,6 +430,16 @@ func (b *BaseApi) Backup(c *gin.Context) {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
case "container":
|
||||
if err := backupService.ContainerBackup(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
case "compose":
|
||||
if err := backupService.ComposeBackup(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
@@ -485,6 +495,16 @@ func (b *BaseApi) Recover(c *gin.Context) {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
case "container":
|
||||
if err := backupService.ContainerRecover(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
case "compose":
|
||||
if err := backupService.ComposeRecover(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
@@ -525,6 +545,16 @@ func (b *BaseApi) RecoverByUpload(c *gin.Context) {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
case "container":
|
||||
if err := backupService.ContainerRecover(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
case "compose":
|
||||
if err := backupService.ComposeRecover(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
+159
-21
@@ -1,6 +1,9 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||
@@ -53,6 +56,162 @@ func (b *BaseApi) LoadContainerUsers(c *gin.Context) {
|
||||
helper.SuccessWithData(c, containerService.LoadUsers(req))
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary List container files
|
||||
// @Accept json
|
||||
// @Param request body dto.ContainerFileReq true "request"
|
||||
// @Success 200 {array} dto.ContainerFileInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/files/search [post]
|
||||
func (b *BaseApi) ListContainerFiles(c *gin.Context) {
|
||||
var req dto.ContainerFileReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
files, err := containerService.ListContainerFiles(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, files)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Upload container file
|
||||
// @Accept multipart/form-data
|
||||
// @Param containerID formData string true "containerID"
|
||||
// @Param path formData string true "path"
|
||||
// @Param file formData file true "file"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/files/upload [post]
|
||||
// @x-panel-log {"bodyKeys":["containerID","path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"容器 [containerID] 上传文件到 [path]","formatEN":"Upload file to [path] in container [containerID]"}
|
||||
func (b *BaseApi) UploadContainerFile(c *gin.Context) {
|
||||
form, err := c.MultipartForm()
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
containerIDs := form.Value["containerID"]
|
||||
paths := form.Value["path"]
|
||||
uploadFiles := form.File["file"]
|
||||
if len(containerIDs) == 0 || len(paths) == 0 || len(uploadFiles) == 0 {
|
||||
helper.BadRequest(c, errors.New("invalid container file upload params"))
|
||||
return
|
||||
}
|
||||
req := dto.ContainerFileReq{
|
||||
ContainerID: containerIDs[0],
|
||||
Path: paths[0],
|
||||
}
|
||||
for _, uploadFile := range uploadFiles {
|
||||
file, err := uploadFile.Open()
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
err = containerService.UploadContainerFile(req, path.Base(uploadFile.Filename), uploadFile.Size, file)
|
||||
_ = file.Close()
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Get container file content
|
||||
// @Accept json
|
||||
// @Param request body dto.ContainerFileReq true "request"
|
||||
// @Success 200 {object} dto.ContainerFileContent
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/files/content [post]
|
||||
func (b *BaseApi) GetContainerFileContent(c *gin.Context) {
|
||||
var req dto.ContainerFileReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
content, err := containerService.GetContainerFileContent(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, content)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Get container file size
|
||||
// @Accept json
|
||||
// @Param request body dto.ContainerFileReq true "request"
|
||||
// @Success 200 {int} size
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/files/size [post]
|
||||
func (b *BaseApi) GetContainerFileSize(c *gin.Context) {
|
||||
var req dto.ContainerFileReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
size, err := containerService.GetContainerFileSize(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, size)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Delete container file
|
||||
// @Accept json
|
||||
// @Param request body dto.ContainerFileBatchDeleteReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/files/del [post]
|
||||
// @x-panel-log {"bodyKeys":["containerID","paths"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"删除容器 [containerID] 文件 [paths]","formatEN":"Delete files [paths] in container [containerID]"}
|
||||
func (b *BaseApi) DeleteContainerFile(c *gin.Context) {
|
||||
var req dto.ContainerFileBatchDeleteReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if err := containerService.DeleteContainerFile(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Download container file
|
||||
// @Accept json
|
||||
// @Param request body dto.ContainerFileReq true "request"
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/files/download [post]
|
||||
// @x-panel-log {"bodyKeys":["containerID","path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"下载容器 [containerID] 文件 [path]","formatEN":"Download file [path] from container [containerID]"}
|
||||
func (b *BaseApi) DownloadContainerFile(c *gin.Context) {
|
||||
var req dto.ContainerFileReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
if req.ContainerID == "" || req.Path == "" {
|
||||
helper.BadRequest(c, errors.New("invalid container file download params"))
|
||||
return
|
||||
}
|
||||
reader, fileName, contentType, err := containerService.DownloadContainerFile(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
defer reader.Close()
|
||||
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(fileName))
|
||||
c.DataFromReader(http.StatusOK, -1, contentType, reader, nil)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary List containers
|
||||
// @Accept json
|
||||
@@ -306,27 +465,6 @@ func (b *BaseApi) ContainerCreate(c *gin.Context) {
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Create container by command
|
||||
// @Accept json
|
||||
// @Param request body dto.ContainerCreateByCommand true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/command [post]
|
||||
func (b *BaseApi) ContainerCreateByCommand(c *gin.Context) {
|
||||
var req dto.ContainerCreateByCommand
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := containerService.ContainerCreateByCommand(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Upgrade container
|
||||
// @Accept json
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
// @Tags Container Docker
|
||||
// @Summary Load docker status
|
||||
// @Produce json
|
||||
// @Success 200 {string} dto.DockerStatus
|
||||
// @Success 200 {object} dto.DockerStatus
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/docker/status [get]
|
||||
@@ -24,7 +24,7 @@ func (b *BaseApi) LoadDockerStatus(c *gin.Context) {
|
||||
// @Tags Container Docker
|
||||
// @Summary Load docker daemon.json
|
||||
// @Produce json
|
||||
// @Success 200 {object} string
|
||||
// @Success 200 {string} string
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/daemonjson/file [get]
|
||||
|
||||
@@ -70,6 +70,7 @@ var (
|
||||
|
||||
recycleBinService = service.NewIRecycleBinService()
|
||||
favoriteService = service.NewIFavoriteService()
|
||||
hostService = service.NewIHostService()
|
||||
|
||||
websiteCAService = service.NewIWebsiteCAService()
|
||||
taskService = service.NewITaskService()
|
||||
|
||||
@@ -515,6 +515,29 @@ func (b *BaseApi) WgetFile(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags File
|
||||
// @Summary Stop wget file download
|
||||
// @Accept json
|
||||
// @Param request body request.FileProcessReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /files/wget/stop [post]
|
||||
// @x-panel-log {"bodyKeys":["key"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"停止下载任务 [key]","formatEN":"Stop wget task [key]"}
|
||||
func (b *BaseApi) StopWget(c *gin.Context) {
|
||||
var req request.FileProcessReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
if strings.TrimSpace(req.Key) == "" {
|
||||
helper.BadRequest(c, errors.New("key is required"))
|
||||
return
|
||||
}
|
||||
|
||||
files.CancelDownload(req.Key)
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags File
|
||||
// @Summary Move file
|
||||
// @Accept json
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (b *BaseApi) CreateHost(c *gin.Context) {
|
||||
var req dto.HostOperate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
host, err := hostService.Create(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, host)
|
||||
}
|
||||
|
||||
func (b *BaseApi) TestByInfo(c *gin.Context) {
|
||||
var req dto.HostConnTest
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, hostService.TestByInfo(req))
|
||||
}
|
||||
|
||||
func (b *BaseApi) TestByID(c *gin.Context) {
|
||||
var req dto.OperateByID
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, hostService.TestLocalConn(req.ID))
|
||||
}
|
||||
|
||||
func (b *BaseApi) HostTree(c *gin.Context) {
|
||||
var req dto.SearchForTree
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := hostService.SearchForTree(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
func (b *BaseApi) SearchHost(c *gin.Context) {
|
||||
var req dto.SearchPageWithGroup
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
total, list, err := hostService.SearchWithPage(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, dto.PageResult{Items: list, Total: total})
|
||||
}
|
||||
|
||||
func (b *BaseApi) DeleteHost(c *gin.Context) {
|
||||
var req dto.OperateByIDs
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := hostService.Delete(req.IDs); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
func (b *BaseApi) UpdateHost(c *gin.Context) {
|
||||
var req dto.HostOperate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
if len(req.Password) != 0 && req.AuthMode == "password" {
|
||||
req.Password, err = hostService.EncryptHost(req.Password)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
req.PrivateKey = ""
|
||||
req.PassPhrase = ""
|
||||
}
|
||||
if len(req.PrivateKey) != 0 && req.AuthMode == "key" {
|
||||
req.PrivateKey, err = hostService.EncryptHost(req.PrivateKey)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
if len(req.PassPhrase) != 0 {
|
||||
req.PassPhrase, err = encrypt.StringEncrypt(req.PassPhrase)
|
||||
if err != nil {
|
||||
helper.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
req.Password = ""
|
||||
}
|
||||
|
||||
upMap := map[string]interface{}{
|
||||
"name": req.Name,
|
||||
"group_id": req.GroupID,
|
||||
"addr": req.Addr,
|
||||
"port": req.Port,
|
||||
"user": req.User,
|
||||
"auth_mode": req.AuthMode,
|
||||
"remember_password": req.RememberPassword,
|
||||
"description": req.Description,
|
||||
}
|
||||
if req.AuthMode == "password" {
|
||||
upMap["password"] = req.Password
|
||||
upMap["private_key"] = ""
|
||||
upMap["pass_phrase"] = ""
|
||||
} else {
|
||||
upMap["password"] = ""
|
||||
upMap["private_key"] = req.PrivateKey
|
||||
upMap["pass_phrase"] = req.PassPhrase
|
||||
}
|
||||
hostItem, err := hostService.Update(req.ID, upMap)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, hostItem)
|
||||
}
|
||||
|
||||
func (b *BaseApi) UpdateHostGroup(c *gin.Context) {
|
||||
var req dto.ChangeGroup
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := hostService.Update(req.ID, map[string]interface{}{"group_id": req.GroupID}); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
func (b *BaseApi) GetHostByID(c *gin.Context) {
|
||||
var req dto.OperateByID
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
info, err := hostService.GetHostByID(req.ID)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, info)
|
||||
}
|
||||
@@ -27,6 +27,15 @@ func (b *BaseApi) GetSettingInfo(c *gin.Context) {
|
||||
helper.SuccessWithData(c, setting)
|
||||
}
|
||||
|
||||
func (b *BaseApi) GetTerminalAISettingInfo(c *gin.Context) {
|
||||
setting, err := settingService.GetTerminalAIInfo()
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, setting)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Load system available status
|
||||
// @Success 200
|
||||
@@ -59,6 +68,19 @@ func (b *BaseApi) UpdateSetting(c *gin.Context) {
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
func (b *BaseApi) UpdateTerminalAISetting(c *gin.Context) {
|
||||
var req dto.TerminalAIInfo
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := settingService.UpdateTerminalAI(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Load local backup dir
|
||||
// @Success 200 {string} path
|
||||
|
||||
@@ -32,7 +32,7 @@ func (b *BaseApi) PageTasks(c *gin.Context) {
|
||||
|
||||
// @Tags TaskLog
|
||||
// @Summary Get the number of executing tasks
|
||||
// @Success 200 {object} int64
|
||||
// @Success 200 {integer} int64
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /logs/tasks/executing/count [get]
|
||||
|
||||
@@ -9,8 +9,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/service"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ssh"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/terminal"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
@@ -40,9 +42,33 @@ func (b *BaseApi) WsSSH(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
client, err := loadLocalConn()
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "failed to set up the connection. Please check the host information")) {
|
||||
return
|
||||
hostID, _ := strconv.Atoi(c.DefaultQuery("id", "0"))
|
||||
var client *ssh.SSHClient
|
||||
if hostID > 0 {
|
||||
host, err := service.GetHostInfo(uint(hostID))
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "load host info by id failed")) {
|
||||
return
|
||||
}
|
||||
connInfo := ssh.ConnInfo{
|
||||
Addr: host.Addr,
|
||||
Port: int(host.Port),
|
||||
User: host.User,
|
||||
AuthMode: host.AuthMode,
|
||||
Password: host.Password,
|
||||
PrivateKey: []byte(host.PrivateKey),
|
||||
}
|
||||
if len(host.PassPhrase) != 0 {
|
||||
connInfo.PassPhrase = []byte(host.PassPhrase)
|
||||
}
|
||||
client, err = ssh.NewClient(connInfo)
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "failed to set up the connection. Please check the host information")) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
client, err = loadLocalConn()
|
||||
if wshandleError(wsConn, errors.WithMessage(err, "failed to set up the connection. Please check the host information")) {
|
||||
return
|
||||
}
|
||||
}
|
||||
defer client.Close()
|
||||
command := c.DefaultQuery("command", "")
|
||||
|
||||
@@ -506,7 +506,7 @@ func (b *BaseApi) GetProxyConfig(c *gin.Context) {
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/proxies/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"修改网站 [domain] 反向代理配置 ","formatEN":"Update domain [domain] proxy config"}
|
||||
// @x-panel-log {"bodyKeys":["id","name","operate"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"修改 [operate] 网站 [domain] 反向代理配置 [name] ","formatEN":"Update [operate] domain [domain] proxy config [name]"}
|
||||
func (b *BaseApi) UpdateProxyConfig(c *gin.Context) {
|
||||
var req request.WebsiteProxyConfig
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -520,6 +520,50 @@ func (b *BaseApi) UpdateProxyConfig(c *gin.Context) {
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Delete proxy config
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteProxyDel true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/proxies/delete [post]
|
||||
// @x-panel-log {"bodyKeys":["id","name"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"删除网站 [domain] 反向代理配置 [name] ","formatEN":"Delete domain [domain] proxy config [name]"}
|
||||
func (b *BaseApi) DeleteProxyConfig(c *gin.Context) {
|
||||
var req request.WebsiteProxyDel
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
err := websiteService.DeleteProxy(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Update proxy config status
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteProxyStatusUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/proxies/status [post]
|
||||
// @x-panel-log {"bodyKeys":["id","name","status"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"修改网站 [domain] 反向代理配置 [name] 状态 [status] ","formatEN":"Update domain [domain] proxy config [name] status [status]"}
|
||||
func (b *BaseApi) UpdateProxyConfigStatus(c *gin.Context) {
|
||||
var req request.WebsiteProxyStatusUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
err := websiteService.UpdateProxyStatus(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Update proxy file
|
||||
// @Accept json
|
||||
@@ -528,7 +572,7 @@ func (b *BaseApi) UpdateProxyConfig(c *gin.Context) {
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /websites/proxies/file [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteID"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"websiteID","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"更新反向代理文件 [domain]","formatEN":"Nginx conf proxy file update [domain]"}
|
||||
// @x-panel-log {"bodyKeys":["websiteID","name"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"websiteID","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"修改网站 [domain] 反向代理配置文件 [name] ","formatEN":"Update domain [domain] proxy config file [name]"}
|
||||
func (b *BaseApi) UpdateProxyConfigFile(c *gin.Context) {
|
||||
var req request.NginxProxyUpdate
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
|
||||
+316
-64
@@ -3,51 +3,54 @@ package dto
|
||||
import "time"
|
||||
|
||||
type AgentCreateReq struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
AppVersion string `json:"appVersion" validate:"required"`
|
||||
WebUIPort int `json:"webUIPort" validate:"required"`
|
||||
BridgePort int `json:"bridgePort" validate:"required"`
|
||||
Provider string `json:"provider" validate:"required"`
|
||||
Model string `json:"model" validate:"required"`
|
||||
AccountID uint `json:"accountId"`
|
||||
APIKey string `json:"apiKey"`
|
||||
BaseURL string `json:"baseURL"`
|
||||
Token string `json:"token"`
|
||||
TaskID string `json:"taskID"`
|
||||
Advanced bool `json:"advanced"`
|
||||
ContainerName string `json:"containerName"`
|
||||
AllowPort bool `json:"allowPort"`
|
||||
SpecifyIP string `json:"specifyIP"`
|
||||
RestartPolicy string `json:"restartPolicy"`
|
||||
CpuQuota float64 `json:"cpuQuota"`
|
||||
MemoryLimit float64 `json:"memoryLimit"`
|
||||
MemoryUnit string `json:"memoryUnit"`
|
||||
PullImage bool `json:"pullImage"`
|
||||
EditCompose bool `json:"editCompose"`
|
||||
DockerCompose string `json:"dockerCompose"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
AppVersion string `json:"appVersion" validate:"required"`
|
||||
WebUIPort int `json:"webUIPort" validate:"required"`
|
||||
BridgePort int `json:"bridgePort"`
|
||||
AllowedOrigins []string `json:"allowedOrigins"`
|
||||
AgentType string `json:"agentType" validate:"required,oneof=openclaw copaw"`
|
||||
Model string `json:"model"`
|
||||
AccountID uint `json:"accountId"`
|
||||
Token string `json:"token"`
|
||||
TaskID string `json:"taskID"`
|
||||
Advanced bool `json:"advanced"`
|
||||
ContainerName string `json:"containerName"`
|
||||
AllowPort bool `json:"allowPort"`
|
||||
SpecifyIP string `json:"specifyIP"`
|
||||
RestartPolicy string `json:"restartPolicy"`
|
||||
CpuQuota float64 `json:"cpuQuota"`
|
||||
MemoryLimit float64 `json:"memoryLimit"`
|
||||
MemoryUnit string `json:"memoryUnit"`
|
||||
PullImage bool `json:"pullImage"`
|
||||
EditCompose bool `json:"editCompose"`
|
||||
DockerCompose string `json:"dockerCompose"`
|
||||
}
|
||||
|
||||
type AgentItem struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Provider string `json:"provider"`
|
||||
ProviderName string `json:"providerName"`
|
||||
Model string `json:"model"`
|
||||
BaseURL string `json:"baseUrl"`
|
||||
APIKey string `json:"apiKey"`
|
||||
Token string `json:"token"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
AppInstallID uint `json:"appInstallId"`
|
||||
AccountID uint `json:"accountId"`
|
||||
AppVersion string `json:"appVersion"`
|
||||
Container string `json:"containerName"`
|
||||
WebUIPort int `json:"webUIPort"`
|
||||
BridgePort int `json:"bridgePort"`
|
||||
Path string `json:"path"`
|
||||
ConfigPath string `json:"configPath"`
|
||||
Upgradable bool `json:"upgradable"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
AgentType string `json:"agentType"`
|
||||
Provider string `json:"provider"`
|
||||
ProviderName string `json:"providerName"`
|
||||
Model string `json:"model"`
|
||||
APIType string `json:"apiType"`
|
||||
MaxTokens int `json:"maxTokens"`
|
||||
ContextWindow int `json:"contextWindow"`
|
||||
BaseURL string `json:"baseUrl"`
|
||||
APIKey string `json:"apiKey"`
|
||||
Token string `json:"token"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
AppInstallID uint `json:"appInstallId"`
|
||||
AccountID uint `json:"accountId"`
|
||||
AppVersion string `json:"appVersion"`
|
||||
Container string `json:"containerName"`
|
||||
WebUIPort int `json:"webUIPort"`
|
||||
BridgePort int `json:"bridgePort"`
|
||||
Path string `json:"path"`
|
||||
ConfigPath string `json:"configPath"`
|
||||
Upgradable bool `json:"upgradable"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
}
|
||||
|
||||
type AgentDeleteReq struct {
|
||||
@@ -66,21 +69,77 @@ type AgentModelConfigUpdateReq struct {
|
||||
Model string `json:"model" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentOverviewReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentIDReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentOverview struct {
|
||||
Snapshot AgentOverviewSnapshot `json:"snapshot"`
|
||||
}
|
||||
|
||||
type AgentOverviewSnapshot struct {
|
||||
ContainerStatus string `json:"containerStatus"`
|
||||
AppVersion string `json:"appVersion"`
|
||||
DefaultModel string `json:"defaultModel"`
|
||||
ChannelCount int `json:"channelCount"`
|
||||
SkillCount int `json:"skillCount"`
|
||||
JobCount int `json:"jobCount"`
|
||||
SessionCount int `json:"sessionCount"`
|
||||
}
|
||||
|
||||
type AgentAccountModel struct {
|
||||
RecordID uint `json:"recordId"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
ContextWindow int `json:"contextWindow"`
|
||||
MaxTokens int `json:"maxTokens"`
|
||||
Reasoning bool `json:"reasoning"`
|
||||
Input []string `json:"input"`
|
||||
}
|
||||
|
||||
type AgentAccountModelReq struct {
|
||||
AccountID uint `json:"accountId" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentAccountModelCreateReq struct {
|
||||
AccountID uint `json:"accountId" validate:"required"`
|
||||
Model AgentAccountModel `json:"model" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentAccountModelUpdateReq struct {
|
||||
AccountID uint `json:"accountId" validate:"required"`
|
||||
Model AgentAccountModel `json:"model" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentAccountModelDeleteReq struct {
|
||||
AccountID uint `json:"accountId" validate:"required"`
|
||||
RecordID uint `json:"recordId" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentAccountCreateReq struct {
|
||||
Provider string `json:"provider" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
APIKey string `json:"apiKey" validate:"required"`
|
||||
BaseURL string `json:"baseURL"`
|
||||
Remark string `json:"remark"`
|
||||
Provider string `json:"provider" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
APIKey string `json:"apiKey" validate:"required"`
|
||||
RememberAPIKey bool `json:"rememberApiKey"`
|
||||
BaseURL string `json:"baseURL"`
|
||||
Models []AgentAccountModel `json:"models"`
|
||||
APIType string `json:"apiType" validate:"required"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
type AgentAccountUpdateReq struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
APIKey string `json:"apiKey" validate:"required"`
|
||||
BaseURL string `json:"baseURL"`
|
||||
Remark string `json:"remark"`
|
||||
SyncAgents bool `json:"syncAgents"`
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
APIKey string `json:"apiKey" validate:"required"`
|
||||
RememberAPIKey bool `json:"rememberApiKey"`
|
||||
BaseURL string `json:"baseURL"`
|
||||
APIType string `json:"apiType" validate:"required"`
|
||||
Remark string `json:"remark"`
|
||||
SyncAgents bool `json:"syncAgents"`
|
||||
}
|
||||
|
||||
type AgentAccountVerifyReq struct {
|
||||
@@ -100,20 +159,27 @@ type AgentAccountSearch struct {
|
||||
}
|
||||
|
||||
type AgentAccountInfo struct {
|
||||
ID uint `json:"id"`
|
||||
Provider string `json:"provider"`
|
||||
ProviderName string `json:"providerName"`
|
||||
Name string `json:"name"`
|
||||
APIKey string `json:"apiKey"`
|
||||
BaseURL string `json:"baseUrl"`
|
||||
Verified bool `json:"verified"`
|
||||
Remark string `json:"remark"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
ID uint `json:"id"`
|
||||
Provider string `json:"provider"`
|
||||
ProviderName string `json:"providerName"`
|
||||
Name string `json:"name"`
|
||||
APIKey string `json:"apiKey"`
|
||||
RememberAPIKey bool `json:"rememberApiKey"`
|
||||
BaseURL string `json:"baseUrl"`
|
||||
Models []AgentAccountModel `json:"models"`
|
||||
APIType string `json:"apiType"`
|
||||
Verified bool `json:"verified"`
|
||||
Remark string `json:"remark"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
}
|
||||
|
||||
type ProviderModelInfo struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
ContextWindow int `json:"contextWindow"`
|
||||
MaxTokens int `json:"maxTokens"`
|
||||
Reasoning bool `json:"reasoning"`
|
||||
Input []string `json:"input"`
|
||||
}
|
||||
|
||||
type ProviderInfo struct {
|
||||
@@ -149,3 +215,189 @@ type AgentFeishuConfig struct {
|
||||
AppID string `json:"appId"`
|
||||
AppSecret string `json:"appSecret"`
|
||||
}
|
||||
|
||||
type AgentTelegramConfigReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentTelegramConfigUpdateReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
Enabled bool `json:"enabled"`
|
||||
DmPolicy string `json:"dmPolicy" validate:"required"`
|
||||
BotToken string `json:"botToken" validate:"required"`
|
||||
Proxy string `json:"proxy"`
|
||||
}
|
||||
|
||||
type AgentTelegramConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
DmPolicy string `json:"dmPolicy"`
|
||||
BotToken string `json:"botToken"`
|
||||
Proxy string `json:"proxy"`
|
||||
}
|
||||
|
||||
type AgentChannelPairingApproveReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
Type string `json:"type" validate:"required,oneof=feishu telegram discord wecom dingtalk-connector"`
|
||||
PairingCode string `json:"pairingCode" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentWecomConfigUpdateReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
Enabled bool `json:"enabled"`
|
||||
DmPolicy string `json:"dmPolicy" validate:"required,oneof=pairing open"`
|
||||
BotID string `json:"botId" validate:"required"`
|
||||
Secret string `json:"secret" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentWecomConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
DmPolicy string `json:"dmPolicy"`
|
||||
BotID string `json:"botId"`
|
||||
Secret string `json:"secret"`
|
||||
Installed bool `json:"installed"`
|
||||
}
|
||||
|
||||
type AgentDingTalkConfigUpdateReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
Enabled bool `json:"enabled"`
|
||||
ClientID string `json:"clientId" validate:"required"`
|
||||
ClientSecret string `json:"clientSecret" validate:"required"`
|
||||
DmPolicy string `json:"dmPolicy" validate:"required,oneof=pairing allowlist open disabled"`
|
||||
AllowFrom []string `json:"allowFrom"`
|
||||
GroupPolicy string `json:"groupPolicy" validate:"required,oneof=open allowlist disabled"`
|
||||
GroupAllowFrom []string `json:"groupAllowFrom"`
|
||||
}
|
||||
|
||||
type AgentDingTalkConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
ClientID string `json:"clientId"`
|
||||
ClientSecret string `json:"clientSecret"`
|
||||
DmPolicy string `json:"dmPolicy"`
|
||||
AllowFrom []string `json:"allowFrom"`
|
||||
GroupPolicy string `json:"groupPolicy"`
|
||||
GroupAllowFrom []string `json:"groupAllowFrom"`
|
||||
Installed bool `json:"installed"`
|
||||
}
|
||||
|
||||
type AgentWeixinLoginReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
TaskID string `json:"taskID" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentQQBotConfigUpdateReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
Enabled bool `json:"enabled"`
|
||||
AppID string `json:"appId" validate:"required"`
|
||||
ClientSecret string `json:"clientSecret" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentQQBotConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
AppID string `json:"appId"`
|
||||
ClientSecret string `json:"clientSecret"`
|
||||
Installed bool `json:"installed"`
|
||||
}
|
||||
|
||||
type AgentPluginInstallReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
Type string `json:"type" validate:"required,oneof=qqbot wecom dingtalk weixin"`
|
||||
TaskID string `json:"taskID" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentPluginCheckReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
Type string `json:"type" validate:"required,oneof=qqbot wecom dingtalk weixin"`
|
||||
}
|
||||
|
||||
type AgentPluginStatus struct {
|
||||
Installed bool `json:"installed"`
|
||||
}
|
||||
|
||||
type AgentDiscordConfigUpdateReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
Enabled bool `json:"enabled"`
|
||||
DmPolicy string `json:"dmPolicy" validate:"required"`
|
||||
GroupPolicy string `json:"groupPolicy" validate:"required,oneof=open allowlist disabled"`
|
||||
Token string `json:"token" validate:"required"`
|
||||
Proxy string `json:"proxy"`
|
||||
}
|
||||
|
||||
type AgentDiscordConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
DmPolicy string `json:"dmPolicy"`
|
||||
GroupPolicy string `json:"groupPolicy"`
|
||||
Token string `json:"token"`
|
||||
Proxy string `json:"proxy"`
|
||||
}
|
||||
|
||||
type AgentSecurityConfigUpdateReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
AllowedOrigins []string `json:"allowedOrigins"`
|
||||
}
|
||||
|
||||
type AgentSecurityConfig struct {
|
||||
AllowedOrigins []string `json:"allowedOrigins"`
|
||||
}
|
||||
|
||||
type AgentOtherConfigUpdateReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
UserTimezone string `json:"userTimezone" validate:"required"`
|
||||
BrowserEnabled bool `json:"browserEnabled"`
|
||||
NPMRegistry string `json:"npmRegistry" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentOtherConfig struct {
|
||||
UserTimezone string `json:"userTimezone"`
|
||||
BrowserEnabled bool `json:"browserEnabled"`
|
||||
NPMRegistry string `json:"npmRegistry"`
|
||||
}
|
||||
|
||||
type AgentConfigFileReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentConfigFileUpdateReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
Content string `json:"content" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentConfigFile struct {
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type AgentSkillSearchReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
Source string `json:"source" validate:"required,oneof=clawhub skillhub"`
|
||||
Keyword string `json:"keyword" validate:"required"`
|
||||
}
|
||||
|
||||
type AgentSkillItem struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Source string `json:"source"`
|
||||
Bundled bool `json:"bundled"`
|
||||
Disabled bool `json:"disabled"`
|
||||
}
|
||||
|
||||
type AgentSkillSearchItem struct {
|
||||
Slug string `json:"slug"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Summary string `json:"summary"`
|
||||
Version string `json:"version"`
|
||||
Source string `json:"source"`
|
||||
Score string `json:"score"`
|
||||
}
|
||||
|
||||
type AgentSkillUpdateReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
type AgentSkillInstallReq struct {
|
||||
AgentID uint `json:"agentId" validate:"required"`
|
||||
Source string `json:"source" validate:"required,oneof=clawhub skillhub"`
|
||||
Slug string `json:"slug" validate:"required"`
|
||||
TaskID string `json:"taskID" validate:"required"`
|
||||
}
|
||||
|
||||
@@ -65,10 +65,11 @@ type UploadForRecover struct {
|
||||
}
|
||||
|
||||
type CommonBackup struct {
|
||||
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql mysql-cluster postgresql-cluster redis-cluster"`
|
||||
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql mysql-cluster postgresql-cluster redis-cluster container compose"`
|
||||
Name string `json:"name"`
|
||||
DetailName string `json:"detailName"`
|
||||
Secret string `json:"secret"`
|
||||
StopBefore bool `json:"stopBefore"`
|
||||
TaskID string `json:"taskID"`
|
||||
FileName string `json:"fileName"`
|
||||
Args []string `json:"args"`
|
||||
@@ -77,7 +78,7 @@ type CommonBackup struct {
|
||||
}
|
||||
type CommonRecover struct {
|
||||
DownloadAccountID uint `json:"downloadAccountID" validate:"required"`
|
||||
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql mysql-cluster postgresql-cluster redis-cluster"`
|
||||
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql mysql-cluster postgresql-cluster redis-cluster container compose"`
|
||||
Name string `json:"name"`
|
||||
DetailName string `json:"detailName"`
|
||||
File string `json:"file"`
|
||||
|
||||
@@ -11,6 +11,12 @@ type SearchPageWithType struct {
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type SearchPageWithGroup struct {
|
||||
PageInfo
|
||||
GroupID uint `json:"groupID"`
|
||||
Info string `json:"info"`
|
||||
}
|
||||
|
||||
type PageInfo struct {
|
||||
Page int `json:"page" validate:"required,number"`
|
||||
PageSize int `json:"pageSize" validate:"required,number"`
|
||||
|
||||
+45
-10
@@ -48,6 +48,34 @@ type ContainerOptions struct {
|
||||
State string `json:"state"`
|
||||
}
|
||||
|
||||
type ContainerFileReq struct {
|
||||
ContainerID string `json:"containerID" validate:"required"`
|
||||
Path string `json:"path" validate:"required"`
|
||||
}
|
||||
|
||||
type ContainerFileBatchDeleteReq struct {
|
||||
ContainerID string `json:"containerID" validate:"required"`
|
||||
Paths []string `json:"paths" validate:"required,min=1,dive,required"`
|
||||
}
|
||||
|
||||
type ContainerFileInfo struct {
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
IsDir bool `json:"isDir"`
|
||||
IsLink bool `json:"isLink"`
|
||||
LinkTo string `json:"linkTo"`
|
||||
Size int64 `json:"size"`
|
||||
Mode string `json:"mode"`
|
||||
ModTime string `json:"modTime"`
|
||||
}
|
||||
|
||||
type ContainerFileContent struct {
|
||||
Content string `json:"content"`
|
||||
Size int64 `json:"size"`
|
||||
Truncated bool `json:"truncated"`
|
||||
IsBinary bool `json:"isBinary"`
|
||||
}
|
||||
|
||||
type ContainerStatus struct {
|
||||
Created int `json:"created"`
|
||||
Running int `json:"running"`
|
||||
@@ -95,10 +123,16 @@ type ContainerOperate struct {
|
||||
Privileged bool `json:"privileged"`
|
||||
AutoRemove bool `json:"autoRemove"`
|
||||
Volumes []VolumeHelper `json:"volumes"`
|
||||
ExtraHosts []ExtraHost `json:"extraHosts"`
|
||||
Labels []string `json:"labels"`
|
||||
Env []string `json:"env"`
|
||||
RestartPolicy string `json:"restartPolicy"`
|
||||
}
|
||||
|
||||
type ExtraHost struct {
|
||||
Hostname string `json:"hostname"`
|
||||
IP string `json:"ip"`
|
||||
}
|
||||
type ContainerNetwork struct {
|
||||
Network string `json:"network"`
|
||||
Ipv4 string `json:"ipv4"`
|
||||
@@ -253,16 +287,17 @@ type BatchDelete struct {
|
||||
}
|
||||
|
||||
type ComposeInfo struct {
|
||||
Name string `json:"name"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
CreatedBy string `json:"createdBy"`
|
||||
ContainerCount int `json:"containerCount"`
|
||||
RunningCount int `json:"runningCount"`
|
||||
ConfigFile string `json:"configFile"`
|
||||
Workdir string `json:"workdir"`
|
||||
Path string `json:"path"`
|
||||
Containers []ComposeContainer `json:"containers"`
|
||||
Env string `json:"env"`
|
||||
Name string `json:"name"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
CreatedBy string `json:"createdBy"`
|
||||
ContainerCount int `json:"containerCount"`
|
||||
RunningCount int `json:"runningCount"`
|
||||
ConfigFile string `json:"configFile"`
|
||||
Workdir string `json:"workdir"`
|
||||
ComposeFileExists bool `json:"composeFileExists"`
|
||||
Path string `json:"path"`
|
||||
Containers []ComposeContainer `json:"containers"`
|
||||
Env string `json:"env"`
|
||||
}
|
||||
type ComposeContainer struct {
|
||||
ContainerID string `json:"containerID"`
|
||||
|
||||
@@ -4,6 +4,7 @@ import "time"
|
||||
|
||||
type DashboardBase struct {
|
||||
WebsiteNumber int `json:"websiteNumber"`
|
||||
AgentNumber int `json:"agentNumber"`
|
||||
DatabaseNumber int `json:"databaseNumber"`
|
||||
CronjobNumber int `json:"cronjobNumber"`
|
||||
AppInstalledNumber int `json:"appInstalledNumber"`
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
import "time"
|
||||
|
||||
type HostOperate struct {
|
||||
ID uint `json:"id"`
|
||||
@@ -34,11 +32,6 @@ type SearchForTree struct {
|
||||
Info string `json:"info"`
|
||||
}
|
||||
|
||||
type ChangeHostGroup struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
GroupID uint `json:"groupID" validate:"required"`
|
||||
}
|
||||
|
||||
type HostInfo struct {
|
||||
ID uint `json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
@@ -138,3 +138,12 @@ type AppUpdateVersion struct {
|
||||
AppInstallID uint `json:"appInstallID" validate:"required"`
|
||||
UpdateVersion string `json:"updateVersion"`
|
||||
}
|
||||
|
||||
type AppInstallSortItem struct {
|
||||
InstallID uint `json:"installID"`
|
||||
SortOrder int `json:"sortOrder"`
|
||||
}
|
||||
|
||||
type AppInstallSort struct {
|
||||
Items []AppInstallSortItem `json:"items" validate:"required"`
|
||||
}
|
||||
|
||||
@@ -269,9 +269,21 @@ type WebsiteProxyConfig struct {
|
||||
Replaces map[string]string `json:"replaces"`
|
||||
SNI bool `json:"sni"`
|
||||
ProxySSLName string `json:"proxySSLName"`
|
||||
SSLVerify bool `json:"sslVerify"`
|
||||
CorsConfig
|
||||
}
|
||||
|
||||
type WebsiteProxyDelete struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsiteProxyStatusUpdate struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Status string `json:"status" validate:"required"`
|
||||
}
|
||||
|
||||
type CorsConfig struct {
|
||||
Cors bool `json:"cors"`
|
||||
AllowOrigins string `json:"allowOrigins"`
|
||||
|
||||
@@ -6,8 +6,8 @@ type WebsiteSSLSearch struct {
|
||||
dto.PageInfo
|
||||
AcmeAccountID string `json:"acmeAccountID"`
|
||||
Domain string `json:"domain"`
|
||||
OrderBy string `json:"orderBy" validate:"required,oneof=expire_date"`
|
||||
Order string `json:"order" validate:"required,oneof=null ascending descending"`
|
||||
OrderBy string `json:"orderBy" validate:"omitempty,oneof=created_at expire_date"`
|
||||
Order string `json:"order" validate:"omitempty,oneof=null ascending descending"`
|
||||
}
|
||||
|
||||
type WebsiteSSLListReq struct {
|
||||
|
||||
@@ -126,6 +126,7 @@ type AppInstallDTO struct {
|
||||
WebUI string `json:"webUI"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Favorite bool `json:"favorite"`
|
||||
SortOrder int `json:"sortOrder"`
|
||||
App AppDetail `json:"app"`
|
||||
Container string `json:"container"`
|
||||
IsEdit bool `json:"isEdit"`
|
||||
|
||||
@@ -87,6 +87,14 @@ type SystemProxy struct {
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type TerminalAIInfo struct {
|
||||
AIStatus string `json:"aiStatus"`
|
||||
AIAccountID string `json:"aiAccountId"`
|
||||
AIPrefix string `json:"aiPrefix" validate:"required,oneof=@ai #ai /ai"`
|
||||
AIRiskCommands string `json:"aiRiskCommands"`
|
||||
AIRiskCommandsDefault string `json:"aiRiskCommandsDefault"`
|
||||
}
|
||||
|
||||
type CommonDescription struct {
|
||||
ID string `json:"id" validate:"required"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
|
||||
+15
-11
@@ -2,15 +2,19 @@ package model
|
||||
|
||||
type Agent struct {
|
||||
BaseModel
|
||||
Name string `json:"name" gorm:"not null;unique"`
|
||||
Provider string `json:"provider"`
|
||||
Model string `json:"model"`
|
||||
BaseURL string `json:"baseUrl"`
|
||||
APIKey string `json:"apiKey"`
|
||||
Token string `json:"token"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
AppInstallID uint `json:"appInstallId"`
|
||||
AccountID uint `json:"accountId"`
|
||||
ConfigPath string `json:"configPath"`
|
||||
Name string `json:"name" gorm:"not null;unique"`
|
||||
AgentType string `json:"agentType" gorm:"default:openclaw"`
|
||||
Provider string `json:"provider"`
|
||||
Model string `json:"model"`
|
||||
APIType string `json:"apiType"`
|
||||
MaxTokens int `json:"maxTokens"`
|
||||
ContextWindow int `json:"contextWindow"`
|
||||
BaseURL string `json:"baseUrl"`
|
||||
APIKey string `json:"apiKey"`
|
||||
Token string `json:"token"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
AppInstallID uint `json:"appInstallId"`
|
||||
AccountID uint `json:"accountId"`
|
||||
ConfigPath string `json:"configPath"`
|
||||
}
|
||||
|
||||
@@ -2,12 +2,14 @@ package model
|
||||
|
||||
type AgentAccount struct {
|
||||
BaseModel
|
||||
Provider string `json:"provider"`
|
||||
Name string `json:"name"`
|
||||
APIKey string `json:"apiKey"`
|
||||
BaseURL string `json:"baseUrl"`
|
||||
Verified bool `json:"verified"`
|
||||
Remark string `json:"remark"`
|
||||
Provider string `json:"provider"`
|
||||
Name string `json:"name"`
|
||||
APIKey string `json:"apiKey"`
|
||||
BaseURL string `json:"baseUrl"`
|
||||
APIType string `json:"apiType"`
|
||||
RememberAPIKey bool `json:"rememberApiKey"`
|
||||
Verified bool `json:"verified"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
func (AgentAccount) TableName() string {
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package model
|
||||
|
||||
type AgentAccountModel struct {
|
||||
BaseModel
|
||||
AccountID uint `json:"accountId" gorm:"index"`
|
||||
Model string `json:"model" gorm:"index"`
|
||||
Name string `json:"name"`
|
||||
ContextWindow int `json:"contextWindow"`
|
||||
MaxTokens int `json:"maxTokens"`
|
||||
Reasoning bool `json:"reasoning"`
|
||||
Input string `json:"input" gorm:"type:text"`
|
||||
SortOrder int `json:"sortOrder" gorm:"index"`
|
||||
}
|
||||
|
||||
func (AgentAccountModel) TableName() string {
|
||||
return "agent_account_models"
|
||||
}
|
||||
@@ -26,6 +26,7 @@ type AppInstall struct {
|
||||
HttpsPort int `json:"httpsPort"`
|
||||
WebUI string `json:"webUI"`
|
||||
Favorite bool `json:"favorite"`
|
||||
SortOrder int `json:"sortOrder" gorm:"default:0"`
|
||||
|
||||
App App `json:"app" gorm:"-:migration"`
|
||||
}
|
||||
|
||||
+322
-83
@@ -1,10 +1,23 @@
|
||||
package provider
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Model struct {
|
||||
ID string
|
||||
Name string
|
||||
ID string
|
||||
Name string
|
||||
ContextWindow int
|
||||
MaxTokens int
|
||||
Reasoning bool
|
||||
Input []string
|
||||
}
|
||||
|
||||
type RuntimeDefault struct {
|
||||
APIType string
|
||||
ContextWindow int
|
||||
MaxTokens int
|
||||
Input []string
|
||||
}
|
||||
|
||||
type Meta struct {
|
||||
@@ -13,133 +26,295 @@ type Meta struct {
|
||||
Sort uint
|
||||
DefaultBaseURL string
|
||||
EnvKey string
|
||||
Default RuntimeDefault
|
||||
Models []Model
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
var catalog = map[string]Meta{
|
||||
"custom": {
|
||||
Key: "custom",
|
||||
DisplayName: "Custom",
|
||||
Sort: 10,
|
||||
DefaultBaseURL: "",
|
||||
EnvKey: "CUSTOM_API_KEY",
|
||||
Default: RuntimeDefault{
|
||||
APIType: "openai-completions",
|
||||
ContextWindow: 128000,
|
||||
MaxTokens: 8192,
|
||||
Input: []string{"text"},
|
||||
},
|
||||
Models: []Model{},
|
||||
},
|
||||
"ollama": {
|
||||
Key: "ollama",
|
||||
DisplayName: "Ollama",
|
||||
Sort: 1,
|
||||
Enabled: true,
|
||||
Sort: 15,
|
||||
Default: RuntimeDefault{
|
||||
APIType: "openai-responses",
|
||||
ContextWindow: 160000,
|
||||
MaxTokens: 8192,
|
||||
Input: []string{"text"},
|
||||
},
|
||||
},
|
||||
"vllm": {
|
||||
Key: "vllm",
|
||||
DisplayName: "vLLM",
|
||||
Sort: 20,
|
||||
DefaultBaseURL: "",
|
||||
EnvKey: "VLLM_API_KEY",
|
||||
Default: RuntimeDefault{
|
||||
APIType: "openai-completions",
|
||||
ContextWindow: 128000,
|
||||
MaxTokens: 8192,
|
||||
Input: []string{"text"},
|
||||
},
|
||||
Models: []Model{},
|
||||
},
|
||||
"deepseek": {
|
||||
Key: "deepseek",
|
||||
DisplayName: "DeepSeek",
|
||||
Sort: 2,
|
||||
Sort: 25,
|
||||
DefaultBaseURL: "https://api.deepseek.com/v1",
|
||||
EnvKey: "DEEPSEEK_API_KEY",
|
||||
Enabled: true,
|
||||
Default: RuntimeDefault{
|
||||
APIType: "openai-completions",
|
||||
ContextWindow: 128000,
|
||||
MaxTokens: 8192,
|
||||
Input: []string{"text"},
|
||||
},
|
||||
Models: []Model{
|
||||
{ID: "deepseek/deepseek-chat", Name: "DeepSeek Chat"},
|
||||
{ID: "deepseek/deepseek-reasoner", Name: "DeepSeek Reasoner"},
|
||||
{ID: "deepseek/deepseek-r1:1.5b", Name: "DeepSeek R1 1.5B"},
|
||||
{ID: "deepseek/deepseek-reasoner", Name: "DeepSeek Reasoner", Reasoning: true},
|
||||
{ID: "deepseek/deepseek-r1:1.5b", Name: "DeepSeek R1 1.5B", Reasoning: true},
|
||||
},
|
||||
},
|
||||
"openai": {
|
||||
Key: "openai",
|
||||
DisplayName: "OpenAI",
|
||||
Sort: 3,
|
||||
DefaultBaseURL: "https://api.openai.com/v1",
|
||||
EnvKey: "OPENAI_API_KEY",
|
||||
Enabled: true,
|
||||
"bailian-coding-plan": {
|
||||
Key: "bailian-coding-plan",
|
||||
DisplayName: "阿里云百炼 Coding Plan",
|
||||
Sort: 30,
|
||||
DefaultBaseURL: "https://coding.dashscope.aliyuncs.com/v1",
|
||||
EnvKey: "QWEN_API_KEY",
|
||||
Default: RuntimeDefault{
|
||||
APIType: "openai-completions",
|
||||
ContextWindow: 256000,
|
||||
MaxTokens: 8192,
|
||||
Input: []string{"text"},
|
||||
},
|
||||
Models: []Model{
|
||||
{ID: "openai/codex-mini-latest", Name: "Codex Mini"},
|
||||
{ID: "openai/gpt-4.1", Name: "GPT-4.1"},
|
||||
{ID: "openai/gpt-4o", Name: "GPT-4o"},
|
||||
{ID: "openai/gpt-4o-mini", Name: "GPT-4o Mini"},
|
||||
{ID: "openai/gpt-5", Name: "GPT-5"},
|
||||
{ID: "openai/gpt-5-mini", Name: "GPT-5 Mini"},
|
||||
{ID: "bailian-coding-plan/qwen3.5-plus", Name: "Qwen3.5-Plus", Reasoning: true},
|
||||
{ID: "bailian-coding-plan/qwen3-max", Name: "Qwen3-Max", Reasoning: true},
|
||||
{ID: "bailian-coding-plan/qwen3-coder-next", Name: "Qwen3-Coder-Next", Reasoning: true},
|
||||
{ID: "bailian-coding-plan/qwen3-coder-plus", Name: "Qwen3-Coder-Plus", Reasoning: true},
|
||||
{ID: "bailian-coding-plan/minimax-m2.5", Name: "MiniMax M2.5", Reasoning: true},
|
||||
{ID: "bailian-coding-plan/glm-5", Name: "GLM-5", Reasoning: true},
|
||||
{ID: "bailian-coding-plan/kimi-k2.5", Name: "Kimi-k2.5", Reasoning: true},
|
||||
{ID: "bailian-coding-plan/glm-4.7", Name: "GLM-4.7", Reasoning: true},
|
||||
},
|
||||
},
|
||||
"anthropic": {
|
||||
Key: "anthropic",
|
||||
DisplayName: "Anthropic",
|
||||
Sort: 4,
|
||||
DefaultBaseURL: "https://api.anthropic.com",
|
||||
EnvKey: "ANTHROPIC_API_KEY",
|
||||
Enabled: true,
|
||||
"ark-coding-plan": {
|
||||
Key: "ark-coding-plan",
|
||||
DisplayName: "方舟 Coding Plan",
|
||||
Sort: 35,
|
||||
DefaultBaseURL: "https://ark.cn-beijing.volces.com/api/coding/v3",
|
||||
EnvKey: "ARK_API_KEY",
|
||||
Default: RuntimeDefault{
|
||||
APIType: "openai-completions",
|
||||
ContextWindow: 256000,
|
||||
MaxTokens: 8192,
|
||||
Input: []string{"text"},
|
||||
},
|
||||
Models: []Model{
|
||||
{ID: "anthropic/claude-3-haiku-20240307", Name: "Claude 3 Haiku"},
|
||||
{ID: "anthropic/claude-3-5-haiku-latest", Name: "Claude 3.5 Haiku"},
|
||||
{ID: "anthropic/claude-3-5-sonnet-20241022", Name: "Claude 3.5 Sonnet"},
|
||||
{ID: "anthropic/claude-3-7-sonnet-20250219", Name: "Claude 3.7 Sonnet"},
|
||||
{ID: "anthropic/claude-opus-4-1", Name: "Claude Opus 4.1"},
|
||||
{ID: "ark-coding-plan/doubao-seed-2.0-code", Name: "Doubao-Seed-2.0-Code"},
|
||||
{ID: "ark-coding-plan/doubao-seed-code", Name: "Doubao-Seed-Code"},
|
||||
{ID: "ark-coding-plan/kimi-k2.5", Name: "Kimi-K2.5", Reasoning: true},
|
||||
{ID: "ark-coding-plan/glm-4.7", Name: "GLM-4.7", Reasoning: true},
|
||||
{ID: "ark-coding-plan/deepseek-v3.2", Name: "DeepSeek-V3.2", Reasoning: true},
|
||||
{ID: "ark-coding-plan/kimi-k2-thinking", Name: "Kimi-K2-thinking", Reasoning: true},
|
||||
},
|
||||
},
|
||||
"gemini": {
|
||||
Key: "gemini",
|
||||
DisplayName: "Gemini",
|
||||
Sort: 5,
|
||||
DefaultBaseURL: "https://generativelanguage.googleapis.com",
|
||||
EnvKey: "GEMINI_API_KEY",
|
||||
Enabled: true,
|
||||
"zai": {
|
||||
Key: "zai",
|
||||
DisplayName: "Z.ai",
|
||||
Sort: 40,
|
||||
DefaultBaseURL: "https://open.bigmodel.cn/api/paas/v4",
|
||||
EnvKey: "ZAI_API_KEY",
|
||||
Default: RuntimeDefault{
|
||||
APIType: "openai-completions",
|
||||
ContextWindow: 204800,
|
||||
MaxTokens: 131072,
|
||||
Input: []string{"text"},
|
||||
},
|
||||
Models: []Model{
|
||||
{ID: "google/gemini-1.5-flash", Name: "Gemini 1.5 Flash"},
|
||||
{ID: "google/gemini-1.5-pro", Name: "Gemini 1.5 Pro"},
|
||||
{ID: "google/gemini-2.0-flash", Name: "Gemini 2.0 Flash"},
|
||||
{ID: "google/gemini-2.5-flash", Name: "Gemini 2.5 Flash"},
|
||||
{ID: "google/gemini-2.5-pro", Name: "Gemini 2.5 Pro"},
|
||||
{ID: "google/gemini-3-flash-preview", Name: "Gemini 3 Flash Preview"},
|
||||
{ID: "zai/glm-5", Name: "GLM-5", Reasoning: true},
|
||||
{ID: "zai/glm-4.7", Name: "GLM-4.7", Reasoning: true},
|
||||
{ID: "zai/glm-4.7-flash", Name: "GLM-4.7-Flash", Reasoning: true},
|
||||
{ID: "zai/glm-4.7-flashx", Name: "GLM-4.7-FlashX", Reasoning: true},
|
||||
},
|
||||
},
|
||||
"minimax": {
|
||||
Key: "minimax",
|
||||
DisplayName: "MiniMax (CN)",
|
||||
Sort: 6,
|
||||
Sort: 45,
|
||||
DefaultBaseURL: "https://api.minimaxi.com/anthropic",
|
||||
EnvKey: "MINIMAX_API_KEY",
|
||||
Enabled: true,
|
||||
Default: RuntimeDefault{
|
||||
APIType: "anthropic-messages",
|
||||
ContextWindow: 200000,
|
||||
MaxTokens: 8192,
|
||||
Input: []string{"text"},
|
||||
},
|
||||
Models: []Model{
|
||||
{ID: "minimax/MiniMax-M2.1", Name: "MiniMax M2.1"},
|
||||
{ID: "minimax/MiniMax-M2.1-lightning", Name: "MiniMax M2.1 Lightning"},
|
||||
{ID: "minimax/MiniMax-M2.7", Name: "MiniMax M2.7"},
|
||||
{ID: "minimax/MiniMax-M2.7-highspeed", Name: "MiniMax M2.7 highspeed"},
|
||||
{ID: "minimax/MiniMax-M2.5", Name: "MiniMax M2.5", Reasoning: true},
|
||||
{ID: "minimax/MiniMax-M2.5-highspeed", Name: "MiniMax M2.5 highspeed"},
|
||||
},
|
||||
},
|
||||
"moonshot": {
|
||||
Key: "moonshot",
|
||||
DisplayName: "Moonshot (Global)",
|
||||
Sort: 7,
|
||||
DefaultBaseURL: "https://api.moonshot.ai/v1",
|
||||
EnvKey: "MOONSHOT_API_KEY",
|
||||
Enabled: true,
|
||||
"xiaomi": {
|
||||
Key: "xiaomi",
|
||||
DisplayName: "Xiaomi",
|
||||
Sort: 46,
|
||||
DefaultBaseURL: "https://api.xiaomimimo.com/anthropic",
|
||||
EnvKey: "XIAOMI_API_KEY",
|
||||
Default: RuntimeDefault{
|
||||
APIType: "anthropic-messages",
|
||||
ContextWindow: 262144,
|
||||
MaxTokens: 8192,
|
||||
Input: []string{"text"},
|
||||
},
|
||||
Models: []Model{
|
||||
{ID: "moonshot/kimi-k2.5", Name: "Kimi K2.5"},
|
||||
{ID: "moonshot/kimi-k2-0905-preview", Name: "Kimi K2 0905 Preview"},
|
||||
{ID: "moonshot/kimi-k2-thinking", Name: "Kimi K2 Thinking"},
|
||||
{ID: "xiaomi/mimo-v2-pro", Name: "Xiaomi MiMo V2 Pro"},
|
||||
{ID: "xiaomi/mimo-v2-omni", Name: "Xiaomi MiMo V2 Omni"},
|
||||
{ID: "xiaomi/mimo-v2-tts", Name: "Xiaomi MiMo V2 TTS"},
|
||||
{ID: "xiaomi/mimo-v2-flash", Name: "Xiaomi MiMo V2 Flash"},
|
||||
},
|
||||
},
|
||||
"kimi": {
|
||||
Key: "kimi",
|
||||
DisplayName: "Kimi (CN)",
|
||||
Sort: 8,
|
||||
Sort: 50,
|
||||
DefaultBaseURL: "https://api.moonshot.cn/v1",
|
||||
EnvKey: "KIMI_API_KEY",
|
||||
Enabled: true,
|
||||
Default: RuntimeDefault{
|
||||
APIType: "openai-completions",
|
||||
ContextWindow: 256000,
|
||||
MaxTokens: 8192,
|
||||
Input: []string{"text", "image"},
|
||||
},
|
||||
Models: []Model{
|
||||
{ID: "kimi/kimi-k2.5", Name: "Kimi K2.5"},
|
||||
{ID: "kimi/kimi-k2.5", Name: "Kimi K2.5", Reasoning: true},
|
||||
{ID: "kimi/kimi-k2-0905-preview", Name: "Kimi K2 0905 Preview"},
|
||||
{ID: "kimi/kimi-k2-thinking", Name: "Kimi K2 Thinking"},
|
||||
{ID: "kimi/kimi-k2-thinking", Name: "Kimi K2 Thinking", Reasoning: true},
|
||||
},
|
||||
},
|
||||
"kimi-coding": {
|
||||
Key: "kimi-coding",
|
||||
DisplayName: "Kimi Coding",
|
||||
Sort: 9,
|
||||
DefaultBaseURL: "https://api.moonshot.cn/anthropic/v1",
|
||||
Sort: 51,
|
||||
DefaultBaseURL: "https://api.kimi.com/coding/",
|
||||
EnvKey: "KIMI_API_KEY",
|
||||
Enabled: true,
|
||||
Default: RuntimeDefault{
|
||||
APIType: "openai-completions",
|
||||
ContextWindow: 262144,
|
||||
MaxTokens: 32768,
|
||||
Input: []string{"text", "image"},
|
||||
},
|
||||
Models: []Model{
|
||||
{ID: "kimi-coding/k2p5", Name: "Kimi K2.5"},
|
||||
{ID: "kimi-coding/k2p5", Name: "Kimi K2.5", Reasoning: true},
|
||||
},
|
||||
},
|
||||
"qwen": {
|
||||
Key: "qwen",
|
||||
DisplayName: "Qwen",
|
||||
Sort: 10,
|
||||
DefaultBaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
||||
EnvKey: "QWEN_API_KEY",
|
||||
Enabled: false,
|
||||
"openai": {
|
||||
Key: "openai",
|
||||
DisplayName: "OpenAI",
|
||||
Sort: 55,
|
||||
DefaultBaseURL: "https://api.openai.com/v1",
|
||||
EnvKey: "OPENAI_API_KEY",
|
||||
Default: RuntimeDefault{
|
||||
APIType: "openai-completions",
|
||||
ContextWindow: 256000,
|
||||
MaxTokens: 8192,
|
||||
Input: []string{"text", "image"},
|
||||
},
|
||||
Models: []Model{
|
||||
{ID: "openai/codex-mini-latest", Name: "Codex Mini", Reasoning: true},
|
||||
{ID: "openai/gpt-5", Name: "GPT-5", Reasoning: true},
|
||||
{ID: "openai/gpt-5-mini", Name: "GPT-5 Mini", Reasoning: true},
|
||||
{ID: "openai/gpt-5.4", Name: "GPT-5.4", Reasoning: true},
|
||||
{ID: "openai/gpt-5.3-codex", Name: "GPT-5.3-Codex", Reasoning: true},
|
||||
},
|
||||
},
|
||||
"openrouter": {
|
||||
Key: "openrouter",
|
||||
DisplayName: "OpenRouter",
|
||||
Sort: 56,
|
||||
DefaultBaseURL: "https://openrouter.ai/api/v1",
|
||||
EnvKey: "OPENROUTER_API_KEY",
|
||||
Default: RuntimeDefault{
|
||||
APIType: "openai-completions",
|
||||
ContextWindow: 128000,
|
||||
MaxTokens: 8192,
|
||||
Input: []string{"text"},
|
||||
},
|
||||
Models: []Model{
|
||||
{ID: "openrouter/free", Name: "openrouter/free"},
|
||||
{ID: "openrouter/auto", Name: "openrouter/auto"},
|
||||
},
|
||||
},
|
||||
"anthropic": {
|
||||
Key: "anthropic",
|
||||
DisplayName: "Anthropic",
|
||||
Sort: 60,
|
||||
DefaultBaseURL: "https://api.anthropic.com",
|
||||
EnvKey: "ANTHROPIC_API_KEY",
|
||||
Default: RuntimeDefault{
|
||||
APIType: "openai-completions",
|
||||
ContextWindow: 256000,
|
||||
MaxTokens: 8192,
|
||||
Input: []string{"text", "image"},
|
||||
},
|
||||
Models: []Model{
|
||||
{ID: "anthropic/claude-3-haiku-20240307", Name: "Claude 3 Haiku"},
|
||||
{ID: "anthropic/claude-3-5-haiku-latest", Name: "Claude 3.5 Haiku"},
|
||||
{ID: "anthropic/claude-3-5-sonnet-20241022", Name: "Claude 3.5 Sonnet"},
|
||||
{ID: "anthropic/claude-3-7-sonnet-20250219", Name: "Claude 3.7 Sonnet", Reasoning: true},
|
||||
{ID: "anthropic/claude-opus-4-1", Name: "Claude Opus 4.1", Reasoning: true},
|
||||
},
|
||||
},
|
||||
"gemini": {
|
||||
Key: "gemini",
|
||||
DisplayName: "Gemini",
|
||||
Sort: 65,
|
||||
DefaultBaseURL: "https://generativelanguage.googleapis.com",
|
||||
EnvKey: "GEMINI_API_KEY",
|
||||
Default: RuntimeDefault{
|
||||
APIType: "openai-completions",
|
||||
ContextWindow: 256000,
|
||||
MaxTokens: 8192,
|
||||
Input: []string{"text", "image"},
|
||||
},
|
||||
Models: []Model{
|
||||
{ID: "google/gemini-3-flash-preview", Name: "Gemini 3 Flash Preview", Reasoning: true},
|
||||
{ID: "google/gemini-flash-latest", Name: "Gemini Flash Latest"},
|
||||
{ID: "google/gemini-3-pro-preview", Name: "Gemini 3 Pro Preview", Reasoning: true},
|
||||
},
|
||||
},
|
||||
"moonshot": {
|
||||
Key: "moonshot",
|
||||
DisplayName: "Moonshot (Global)",
|
||||
Sort: 70,
|
||||
DefaultBaseURL: "https://api.moonshot.ai/v1",
|
||||
EnvKey: "MOONSHOT_API_KEY",
|
||||
Default: RuntimeDefault{
|
||||
APIType: "openai-completions",
|
||||
ContextWindow: 256000,
|
||||
MaxTokens: 8192,
|
||||
Input: []string{"text"},
|
||||
},
|
||||
Models: []Model{
|
||||
{ID: "moonshot/kimi-k2.5", Name: "Kimi K2.5", Reasoning: true},
|
||||
{ID: "moonshot/kimi-k2-0905-preview", Name: "Kimi K2 0905 Preview"},
|
||||
{ID: "moonshot/kimi-k2-thinking", Name: "Kimi K2 Thinking", Reasoning: true},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -159,11 +334,6 @@ func All() map[string]Meta {
|
||||
return result
|
||||
}
|
||||
|
||||
func IsEnabled(key string) bool {
|
||||
meta, ok := catalog[strings.ToLower(strings.TrimSpace(key))]
|
||||
return ok && meta.Enabled
|
||||
}
|
||||
|
||||
func DefaultBaseURL(key string) (string, bool) {
|
||||
meta, ok := catalog[strings.ToLower(strings.TrimSpace(key))]
|
||||
if !ok || strings.TrimSpace(meta.DefaultBaseURL) == "" {
|
||||
@@ -191,11 +361,80 @@ func DisplayName(key string) string {
|
||||
return meta.DisplayName
|
||||
}
|
||||
|
||||
func FindModel(key, modelID string) (Model, bool) {
|
||||
meta, ok := Get(key)
|
||||
if !ok {
|
||||
return Model{}, false
|
||||
}
|
||||
for _, item := range meta.Models {
|
||||
if item.ID == modelID {
|
||||
return item, true
|
||||
}
|
||||
}
|
||||
return Model{}, false
|
||||
}
|
||||
|
||||
func cloneMeta(meta Meta) Meta {
|
||||
clone := meta
|
||||
if len(meta.Default.Input) > 0 {
|
||||
clone.Default.Input = make([]string, len(meta.Default.Input))
|
||||
copy(clone.Default.Input, meta.Default.Input)
|
||||
}
|
||||
if len(meta.Models) > 0 {
|
||||
clone.Models = make([]Model, len(meta.Models))
|
||||
copy(clone.Models, meta.Models)
|
||||
for i, item := range meta.Models {
|
||||
clone.Models[i] = normalizeModel(meta, item)
|
||||
}
|
||||
}
|
||||
return clone
|
||||
}
|
||||
|
||||
func normalizeModel(meta Meta, model Model) Model {
|
||||
clone := model
|
||||
clone.ID = strings.TrimSpace(clone.ID)
|
||||
clone.Name = strings.TrimSpace(clone.Name)
|
||||
if clone.Name == "" {
|
||||
clone.Name = clone.ID
|
||||
}
|
||||
if clone.MaxTokens <= 0 {
|
||||
clone.MaxTokens = meta.Default.MaxTokens
|
||||
}
|
||||
if clone.ContextWindow <= 0 {
|
||||
clone.ContextWindow = meta.Default.ContextWindow
|
||||
}
|
||||
if len(clone.Input) == 0 && len(meta.Default.Input) > 0 {
|
||||
clone.Input = make([]string, len(meta.Default.Input))
|
||||
copy(clone.Input, meta.Default.Input)
|
||||
}
|
||||
return clone
|
||||
}
|
||||
|
||||
func ResolveRuntimeParams(provider, apiType string, maxTokens, contextWindow int) (string, int, int) {
|
||||
defaultAPIType := "openai-completions"
|
||||
defaultMaxTokens := 8192
|
||||
defaultContextWindow := 256000
|
||||
if meta, ok := Get(provider); ok {
|
||||
if meta.Default.APIType != "" {
|
||||
defaultAPIType = meta.Default.APIType
|
||||
}
|
||||
if meta.Default.MaxTokens > 0 {
|
||||
defaultMaxTokens = meta.Default.MaxTokens
|
||||
}
|
||||
if meta.Default.ContextWindow > 0 {
|
||||
defaultContextWindow = meta.Default.ContextWindow
|
||||
}
|
||||
}
|
||||
resolvedAPI := apiType
|
||||
if strings.TrimSpace(apiType) == "" {
|
||||
resolvedAPI = defaultAPIType
|
||||
}
|
||||
resolvedMaxTokens := defaultMaxTokens
|
||||
resolvedContextWindow := defaultContextWindow
|
||||
if maxTokens > 0 {
|
||||
resolvedMaxTokens = maxTokens
|
||||
}
|
||||
if contextWindow > 0 {
|
||||
resolvedContextWindow = contextWindow
|
||||
}
|
||||
return resolvedAPI, resolvedMaxTokens, resolvedContextWindow
|
||||
}
|
||||
|
||||
@@ -0,0 +1,338 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type OpenClawPatch struct {
|
||||
PrimaryModel string
|
||||
Models map[string]interface{}
|
||||
}
|
||||
|
||||
type openClawModelSpec struct {
|
||||
ID string
|
||||
Name string
|
||||
Reasoning bool
|
||||
Input []string
|
||||
ContextWindow int
|
||||
MaxTokens int
|
||||
}
|
||||
|
||||
type openClawPatchSpec struct {
|
||||
PrimaryModel string
|
||||
Provider string
|
||||
APIKey string
|
||||
BaseURL string
|
||||
APIType string
|
||||
AuthHeader bool
|
||||
Model openClawModelSpec
|
||||
}
|
||||
|
||||
func BuildOpenClawPatch(provider, modelName, apiType string, reasoning bool, maxTokens, contextWindow int, baseURL, apiKey string) (*OpenClawPatch, error) {
|
||||
provider = strings.ToLower(strings.TrimSpace(provider))
|
||||
modelName = strings.TrimSpace(modelName)
|
||||
if modelName == "" {
|
||||
return nil, fmt.Errorf("model is required")
|
||||
}
|
||||
apiType, maxTokens, contextWindow = ResolveRuntimeParams(provider, apiType, maxTokens, contextWindow)
|
||||
modelID := modelName
|
||||
if parts := strings.SplitN(modelName, "/", 2); len(parts) == 2 {
|
||||
modelID = parts[1]
|
||||
}
|
||||
|
||||
var spec openClawPatchSpec
|
||||
switch provider {
|
||||
case "deepseek":
|
||||
spec = buildDeepseekPatchSpec(modelName, reasoning, maxTokens, contextWindow, baseURL, apiKey)
|
||||
case "gemini":
|
||||
spec = buildGenericPatchSpec(provider, modelName, modelID, apiType, reasoning, maxTokens, contextWindow, baseURL, apiKey)
|
||||
case "moonshot", "kimi":
|
||||
spec = buildMoonshotPatchSpec(provider, modelName, modelID, reasoning, maxTokens, contextWindow, baseURL, apiKey)
|
||||
case "bailian-coding-plan":
|
||||
spec = buildBailianPatchSpec(modelID, reasoning, maxTokens, contextWindow, baseURL, apiKey)
|
||||
case "ark-coding-plan":
|
||||
spec = buildArkPatchSpec(modelID, reasoning, maxTokens, contextWindow, baseURL, apiKey)
|
||||
case "minimax":
|
||||
spec = buildMiniMaxPatchSpec(modelID, reasoning, maxTokens, contextWindow, baseURL, apiKey)
|
||||
case "xiaomi":
|
||||
spec = buildXiaomiPatchSpec(modelID, reasoning, maxTokens, contextWindow, baseURL, apiKey)
|
||||
case "custom", "vllm":
|
||||
spec = buildCustomPatchSpec(provider, modelName, apiType, reasoning, maxTokens, contextWindow, baseURL, apiKey)
|
||||
case "ollama":
|
||||
spec = buildOllamaPatchSpec(modelName, modelID, apiType, reasoning, maxTokens, contextWindow, baseURL)
|
||||
case "kimi-coding":
|
||||
spec = buildKimiCodingPatchSpec(modelName, modelID, reasoning, maxTokens, contextWindow, baseURL, apiKey)
|
||||
case "zai":
|
||||
spec = buildZaiPatchSpec(modelID, reasoning, maxTokens, contextWindow, baseURL, apiKey)
|
||||
default:
|
||||
spec = buildGenericPatchSpec(provider, modelName, modelID, apiType, reasoning, maxTokens, contextWindow, baseURL, apiKey)
|
||||
}
|
||||
return buildOpenClawPatch(spec), nil
|
||||
}
|
||||
|
||||
func buildOpenClawPatch(spec openClawPatchSpec) *OpenClawPatch {
|
||||
return &OpenClawPatch{
|
||||
PrimaryModel: spec.PrimaryModel,
|
||||
Models: providerModels(
|
||||
spec.Provider,
|
||||
spec.APIKey,
|
||||
spec.BaseURL,
|
||||
spec.APIType,
|
||||
spec.AuthHeader,
|
||||
buildOpenClawModel(spec.Model),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func buildOpenClawModel(spec openClawModelSpec) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"id": spec.ID,
|
||||
"name": spec.Name,
|
||||
"reasoning": spec.Reasoning,
|
||||
"input": spec.Input,
|
||||
"contextWindow": spec.ContextWindow,
|
||||
"maxTokens": spec.MaxTokens,
|
||||
"cost": map[string]interface{}{},
|
||||
}
|
||||
}
|
||||
|
||||
func buildDeepseekPatchSpec(modelName string, reasoning bool, maxTokens, contextWindow int, baseURL, apiKey string) openClawPatchSpec {
|
||||
return openClawPatchSpec{
|
||||
PrimaryModel: modelName,
|
||||
Provider: "deepseek",
|
||||
APIKey: strings.TrimSpace(apiKey),
|
||||
BaseURL: baseURL,
|
||||
APIType: "openai-completions",
|
||||
Model: openClawModelSpec{
|
||||
ID: "deepseek-chat",
|
||||
Name: "DeepSeek Chat",
|
||||
Reasoning: reasoning,
|
||||
Input: []string{"text"},
|
||||
ContextWindow: contextWindow,
|
||||
MaxTokens: maxTokens,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func buildMoonshotPatchSpec(provider, modelName, modelID string, reasoning bool, maxTokens, contextWindow int, baseURL, apiKey string) openClawPatchSpec {
|
||||
configProvider := provider
|
||||
primaryModel := modelName
|
||||
if provider == "kimi" {
|
||||
configProvider = "moonshot"
|
||||
primaryModel = "moonshot/" + modelID
|
||||
}
|
||||
return openClawPatchSpec{
|
||||
PrimaryModel: primaryModel,
|
||||
Provider: configProvider,
|
||||
APIKey: strings.TrimSpace(apiKey),
|
||||
BaseURL: baseURL,
|
||||
APIType: "openai-completions",
|
||||
Model: openClawModelSpec{
|
||||
ID: modelID,
|
||||
Name: modelID,
|
||||
Reasoning: reasoning,
|
||||
Input: []string{"text"},
|
||||
ContextWindow: contextWindow,
|
||||
MaxTokens: maxTokens,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func buildBailianPatchSpec(modelID string, reasoning bool, maxTokens, contextWindow int, baseURL, apiKey string) openClawPatchSpec {
|
||||
return openClawPatchSpec{
|
||||
PrimaryModel: "bailian-coding-plan/" + modelID,
|
||||
Provider: "bailian-coding-plan",
|
||||
APIKey: strings.TrimSpace(apiKey),
|
||||
BaseURL: baseURL,
|
||||
APIType: "openai-completions",
|
||||
Model: openClawModelSpec{
|
||||
ID: modelID,
|
||||
Name: modelID,
|
||||
Reasoning: reasoning,
|
||||
Input: []string{"text"},
|
||||
ContextWindow: contextWindow,
|
||||
MaxTokens: maxTokens,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func buildArkPatchSpec(modelID string, reasoning bool, maxTokens, contextWindow int, baseURL, apiKey string) openClawPatchSpec {
|
||||
return openClawPatchSpec{
|
||||
PrimaryModel: "ark-coding-plan/" + modelID,
|
||||
Provider: "ark-coding-plan",
|
||||
APIKey: strings.TrimSpace(apiKey),
|
||||
BaseURL: baseURL,
|
||||
APIType: "openai-completions",
|
||||
Model: openClawModelSpec{
|
||||
ID: modelID,
|
||||
Name: modelID,
|
||||
Reasoning: reasoning,
|
||||
Input: []string{"text"},
|
||||
ContextWindow: contextWindow,
|
||||
MaxTokens: maxTokens,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func buildMiniMaxPatchSpec(modelID string, reasoning bool, maxTokens, contextWindow int, baseURL, apiKey string) openClawPatchSpec {
|
||||
return openClawPatchSpec{
|
||||
PrimaryModel: "minimax/" + modelID,
|
||||
Provider: "minimax",
|
||||
APIKey: strings.TrimSpace(apiKey),
|
||||
BaseURL: baseURL,
|
||||
APIType: "anthropic-messages",
|
||||
AuthHeader: true,
|
||||
Model: openClawModelSpec{
|
||||
ID: modelID,
|
||||
Name: modelID,
|
||||
Reasoning: reasoning,
|
||||
Input: []string{"text"},
|
||||
ContextWindow: contextWindow,
|
||||
MaxTokens: maxTokens,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func buildXiaomiPatchSpec(modelID string, reasoning bool, maxTokens, contextWindow int, baseURL, apiKey string) openClawPatchSpec {
|
||||
normalizedID := strings.TrimSpace(modelID)
|
||||
return openClawPatchSpec{
|
||||
PrimaryModel: "xiaomi/" + normalizedID,
|
||||
Provider: "xiaomi",
|
||||
APIKey: strings.TrimSpace(apiKey),
|
||||
BaseURL: baseURL,
|
||||
APIType: "anthropic-messages",
|
||||
Model: openClawModelSpec{
|
||||
ID: normalizedID,
|
||||
Name: normalizedID,
|
||||
Reasoning: reasoning,
|
||||
Input: []string{"text"},
|
||||
ContextWindow: contextWindow,
|
||||
MaxTokens: maxTokens,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func buildCustomPatchSpec(provider, modelName, apiType string, reasoning bool, maxTokens, contextWindow int, baseURL, apiKey string) openClawPatchSpec {
|
||||
customModelID := normalizeCustomModel(modelName)
|
||||
return openClawPatchSpec{
|
||||
PrimaryModel: provider + "/" + customModelID,
|
||||
Provider: provider,
|
||||
APIKey: strings.TrimSpace(apiKey),
|
||||
BaseURL: strings.TrimSpace(baseURL),
|
||||
APIType: apiType,
|
||||
Model: openClawModelSpec{
|
||||
ID: customModelID,
|
||||
Name: customModelID,
|
||||
Reasoning: reasoning,
|
||||
Input: []string{"text"},
|
||||
ContextWindow: contextWindow,
|
||||
MaxTokens: maxTokens,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func buildOllamaPatchSpec(modelName, modelID, apiType string, reasoning bool, maxTokens, contextWindow int, baseURL string) openClawPatchSpec {
|
||||
return openClawPatchSpec{
|
||||
PrimaryModel: modelName,
|
||||
Provider: "ollama",
|
||||
APIKey: "ollama",
|
||||
BaseURL: strings.TrimSpace(baseURL),
|
||||
APIType: apiType,
|
||||
Model: openClawModelSpec{
|
||||
ID: modelID,
|
||||
Name: modelID,
|
||||
Reasoning: reasoning,
|
||||
Input: []string{"text"},
|
||||
ContextWindow: contextWindow,
|
||||
MaxTokens: maxTokens,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func buildKimiCodingPatchSpec(modelName, modelID string, reasoning bool, maxTokens, contextWindow int, baseURL, apiKey string) openClawPatchSpec {
|
||||
return openClawPatchSpec{
|
||||
PrimaryModel: modelName,
|
||||
Provider: "kimi-coding",
|
||||
APIKey: strings.TrimSpace(apiKey),
|
||||
BaseURL: baseURL,
|
||||
APIType: "anthropic-messages",
|
||||
Model: openClawModelSpec{
|
||||
ID: modelID,
|
||||
Name: "Kimi for Coding",
|
||||
Reasoning: reasoning,
|
||||
Input: []string{"text", "image"},
|
||||
ContextWindow: contextWindow,
|
||||
MaxTokens: maxTokens,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func buildZaiPatchSpec(modelID string, reasoning bool, maxTokens, contextWindow int, baseURL, apiKey string) openClawPatchSpec {
|
||||
return openClawPatchSpec{
|
||||
PrimaryModel: "zai/" + modelID,
|
||||
Provider: "zai",
|
||||
APIKey: strings.TrimSpace(apiKey),
|
||||
BaseURL: baseURL,
|
||||
APIType: "openai-completions",
|
||||
Model: openClawModelSpec{
|
||||
ID: modelID,
|
||||
Name: modelID,
|
||||
Reasoning: reasoning,
|
||||
Input: []string{"text"},
|
||||
ContextWindow: contextWindow,
|
||||
MaxTokens: maxTokens,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func buildGenericPatchSpec(provider, modelName, modelID, apiType string, reasoning bool, maxTokens, contextWindow int, baseURL, apiKey string) openClawPatchSpec {
|
||||
providerName := provider
|
||||
primaryModel := modelName
|
||||
if provider == "gemini" {
|
||||
providerName = "google"
|
||||
primaryModel = "google/" + modelID
|
||||
}
|
||||
return openClawPatchSpec{
|
||||
PrimaryModel: primaryModel,
|
||||
Provider: providerName,
|
||||
APIKey: strings.TrimSpace(apiKey),
|
||||
BaseURL: baseURL,
|
||||
APIType: apiType,
|
||||
Model: openClawModelSpec{
|
||||
ID: modelID,
|
||||
Name: modelID,
|
||||
Reasoning: reasoning,
|
||||
Input: []string{"text"},
|
||||
ContextWindow: contextWindow,
|
||||
MaxTokens: maxTokens,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func providerModels(provider, apiKey, baseURL, api string, authHeader bool, model map[string]interface{}) map[string]interface{} {
|
||||
providerConfig := map[string]interface{}{
|
||||
"apiKey": apiKey,
|
||||
"baseUrl": baseURL,
|
||||
"api": api,
|
||||
"models": []map[string]interface{}{model},
|
||||
}
|
||||
if authHeader {
|
||||
providerConfig["authHeader"] = true
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"mode": "merge",
|
||||
"providers": map[string]interface{}{
|
||||
provider: providerConfig,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeCustomModel(modelName string) string {
|
||||
trim := strings.TrimSpace(modelName)
|
||||
trim = strings.TrimLeft(trim, "/")
|
||||
if parts := strings.SplitN(trim, "/", 2); len(parts) == 2 && strings.EqualFold(parts[0], "custom") {
|
||||
return strings.TrimLeft(strings.TrimSpace(parts[1]), "/")
|
||||
}
|
||||
return trim
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
)
|
||||
|
||||
type VerifyRequest struct {
|
||||
Method string
|
||||
URL string
|
||||
Headers map[string]string
|
||||
Body []byte
|
||||
}
|
||||
|
||||
const (
|
||||
defaultVerifyTimeout = 30 * time.Second
|
||||
)
|
||||
|
||||
func SkipVerification(key string) bool {
|
||||
switch strings.ToLower(strings.TrimSpace(key)) {
|
||||
case "custom", "vllm", "ollama", "kimi-coding":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func VerifyAccount(provider, baseURL, apiKey string) error {
|
||||
req := BuildVerifyRequest(provider, baseURL, apiKey)
|
||||
var body *bytes.Buffer
|
||||
if len(req.Body) > 0 {
|
||||
body = bytes.NewBuffer(req.Body)
|
||||
} else {
|
||||
body = bytes.NewBuffer(nil)
|
||||
}
|
||||
httpReq, err := http.NewRequest(req.Method, req.URL, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for key, value := range req.Headers {
|
||||
httpReq.Header.Set(key, value)
|
||||
}
|
||||
resp, err := (&http.Client{Timeout: verifyTimeout()}).Do(httpReq)
|
||||
if err != nil {
|
||||
return buserr.WithErr("ErrAgentAccountUnavailable", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode >= 400 {
|
||||
return buserr.WithErr("ErrAgentAccountUnavailable", fmt.Errorf("verify failed: %s", resp.Status))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyTimeout() time.Duration {
|
||||
return defaultVerifyTimeout
|
||||
}
|
||||
|
||||
func BuildVerifyRequest(provider, baseURL, apiKey string) VerifyRequest {
|
||||
provider = strings.ToLower(strings.TrimSpace(provider))
|
||||
base := strings.TrimRight(strings.TrimSpace(baseURL), "/")
|
||||
headers := map[string]string{}
|
||||
request := VerifyRequest{Method: http.MethodGet, Headers: headers}
|
||||
|
||||
switch provider {
|
||||
case "anthropic", "kimi-coding":
|
||||
headers["x-api-key"] = apiKey
|
||||
headers["anthropic-version"] = "2023-06-01"
|
||||
if strings.Contains(base, "/v1") {
|
||||
request.URL = base + "/models"
|
||||
} else {
|
||||
request.URL = base + "/v1/models"
|
||||
}
|
||||
case "gemini":
|
||||
request.Method = http.MethodPost
|
||||
if strings.Contains(base, "/v1beta") {
|
||||
request.URL = base + "/models/gemini-3-flash-preview:generateContent"
|
||||
} else {
|
||||
request.URL = base + "/v1beta/models/gemini-3-flash-preview:generateContent"
|
||||
}
|
||||
headers["x-goog-api-key"] = apiKey
|
||||
headers["Content-Type"] = "application/json"
|
||||
request.Body = mustJSON(map[string]interface{}{
|
||||
"contents": []map[string]interface{}{{
|
||||
"parts": []map[string]string{{
|
||||
"text": "Explain how AI works in a few words",
|
||||
}},
|
||||
}},
|
||||
})
|
||||
case "zai":
|
||||
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
|
||||
request.URL = base + "/models"
|
||||
case "bailian-coding-plan":
|
||||
request.Method = http.MethodPost
|
||||
if !strings.Contains(base, "/v1") {
|
||||
base = base + "/v1"
|
||||
}
|
||||
request.URL = base + "/chat/completions"
|
||||
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
|
||||
headers["Content-Type"] = "application/json"
|
||||
request.Body = mustJSON(map[string]interface{}{
|
||||
"model": "qwen3.5-plus",
|
||||
"messages": []map[string]string{{"role": "user", "content": "test"}},
|
||||
"max_tokens": 1,
|
||||
})
|
||||
case "ark-coding-plan":
|
||||
request.Method = http.MethodPost
|
||||
if !strings.Contains(base, "/api/coding/v3") {
|
||||
base = "https://ark.cn-beijing.volces.com/api/coding/v3"
|
||||
}
|
||||
request.URL = base + "/chat/completions"
|
||||
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
|
||||
headers["Content-Type"] = "application/json"
|
||||
request.Body = mustJSON(map[string]interface{}{
|
||||
"model": "doubao-seed-2.0-code",
|
||||
"messages": []map[string]string{{"role": "user", "content": "test"}},
|
||||
"max_tokens": 1,
|
||||
})
|
||||
case "minimax":
|
||||
request.Method = http.MethodPost
|
||||
headers["x-api-key"] = apiKey
|
||||
headers["anthropic-version"] = "2023-06-01"
|
||||
headers["Content-Type"] = "application/json"
|
||||
if strings.Contains(base, "/v1") {
|
||||
request.URL = base + "/messages"
|
||||
} else {
|
||||
request.URL = base + "/v1/messages"
|
||||
}
|
||||
request.Body = mustJSON(map[string]interface{}{
|
||||
"model": "MiniMax-M2.5",
|
||||
"max_tokens": 1,
|
||||
"messages": []map[string]interface{}{{
|
||||
"role": "user",
|
||||
"content": []map[string]string{{
|
||||
"type": "text",
|
||||
"text": "test",
|
||||
}},
|
||||
}},
|
||||
})
|
||||
case "xiaomi":
|
||||
request.Method = http.MethodPost
|
||||
headers["x-api-key"] = apiKey
|
||||
headers["anthropic-version"] = "2023-06-01"
|
||||
headers["Content-Type"] = "application/json"
|
||||
if strings.Contains(base, "/v1") {
|
||||
request.URL = base + "/messages"
|
||||
} else {
|
||||
request.URL = base + "/v1/messages"
|
||||
}
|
||||
request.Body = mustJSON(map[string]interface{}{
|
||||
"model": "mimo-v2-flash",
|
||||
"max_tokens": 1,
|
||||
"messages": []map[string]interface{}{{
|
||||
"role": "user",
|
||||
"content": []map[string]string{{
|
||||
"type": "text",
|
||||
"text": "test",
|
||||
}},
|
||||
}},
|
||||
})
|
||||
case "openrouter":
|
||||
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
|
||||
if strings.Contains(base, "/v1") {
|
||||
request.URL = base + "/key"
|
||||
} else {
|
||||
request.URL = base + "/v1/key"
|
||||
}
|
||||
default:
|
||||
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
|
||||
if strings.Contains(base, "/v1") {
|
||||
request.URL = base + "/models"
|
||||
} else {
|
||||
request.URL = base + "/v1/models"
|
||||
}
|
||||
}
|
||||
return request
|
||||
}
|
||||
|
||||
func mustJSON(value interface{}) []byte {
|
||||
payload, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return []byte("{}")
|
||||
}
|
||||
return payload
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package repo
|
||||
|
||||
import "github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
|
||||
type AgentAccountModelRepo struct{}
|
||||
|
||||
type IAgentAccountModelRepo interface {
|
||||
List(opts ...DBOption) ([]model.AgentAccountModel, error)
|
||||
GetFirst(opts ...DBOption) (*model.AgentAccountModel, error)
|
||||
Create(item *model.AgentAccountModel) error
|
||||
Save(item *model.AgentAccountModel) error
|
||||
DeleteByID(id uint) error
|
||||
Delete(opts ...DBOption) error
|
||||
}
|
||||
|
||||
func NewIAgentAccountModelRepo() IAgentAccountModelRepo {
|
||||
return &AgentAccountModelRepo{}
|
||||
}
|
||||
|
||||
func (a AgentAccountModelRepo) List(opts ...DBOption) ([]model.AgentAccountModel, error) {
|
||||
var list []model.AgentAccountModel
|
||||
if err := getDb(opts...).Find(&list).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func (a AgentAccountModelRepo) GetFirst(opts ...DBOption) (*model.AgentAccountModel, error) {
|
||||
var item model.AgentAccountModel
|
||||
if err := getDb(opts...).First(&item).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &item, nil
|
||||
}
|
||||
|
||||
func (a AgentAccountModelRepo) Create(item *model.AgentAccountModel) error {
|
||||
return getDb().Create(item).Error
|
||||
}
|
||||
|
||||
func (a AgentAccountModelRepo) Save(item *model.AgentAccountModel) error {
|
||||
return getDb().Save(item).Error
|
||||
}
|
||||
|
||||
func (a AgentAccountModelRepo) DeleteByID(id uint) error {
|
||||
return getDb().Delete(&model.AgentAccountModel{}, id).Error
|
||||
}
|
||||
|
||||
func (a AgentAccountModelRepo) Delete(opts ...DBOption) error {
|
||||
return getDb(opts...).Delete(&model.AgentAccountModel{}).Error
|
||||
}
|
||||
@@ -80,9 +80,10 @@ func (u *LauncherRepo) ListQuickJump(withAll bool) []model.QuickJump {
|
||||
}
|
||||
if !withAll && len(quicks) == 0 {
|
||||
return []model.QuickJump{
|
||||
{Name: "Agent", Title: "aiTools.agents.agent", Recommend: 1, IsShow: true, Router: "/ai/agents/agent"},
|
||||
{Name: "Website", Title: "menu.website", Recommend: 10, IsShow: true, Router: "/websites"},
|
||||
{Name: "Database", Title: "menu.database", Recommend: 30, IsShow: true, Router: "/databases"},
|
||||
{Name: "Cronjob", Title: "menu.cronjob", Recommend: 50, IsShow: true, Router: "/cronjobs"},
|
||||
{Name: "Cronjob", Title: "menu.cronjob", Recommend: 50, IsShow: false, Router: "/cronjobs"},
|
||||
{Name: "AppInstalled", Title: "home.appInstalled", Recommend: 70, IsShow: true, Router: "/apps/installed"},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,12 @@ func WithByID(id uint) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func WithByGroupID(id uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("group_id = ?", id)
|
||||
}
|
||||
}
|
||||
|
||||
func WithByNOTID(id uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("id != ?", id)
|
||||
@@ -43,6 +49,12 @@ func WithByName(name string) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func WithByAddr(addr string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("addr = ?", addr)
|
||||
}
|
||||
}
|
||||
|
||||
func WithByKey(key string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("key = ?", key)
|
||||
|
||||
@@ -10,6 +10,18 @@ import (
|
||||
type HostRepo struct{}
|
||||
|
||||
type IHostRepo interface {
|
||||
Get(opts ...DBOption) (model.Host, error)
|
||||
GetList(opts ...DBOption) ([]model.Host, error)
|
||||
Page(limit, offset int, opts ...DBOption) (int64, []model.Host, error)
|
||||
Create(host *model.Host) error
|
||||
Update(id uint, vars map[string]interface{}) error
|
||||
UpdateGroup(group, newGroup uint) error
|
||||
Delete(opts ...DBOption) error
|
||||
|
||||
WithByInfo(info string) DBOption
|
||||
WithByPort(port uint) DBOption
|
||||
WithByUser(user string) DBOption
|
||||
|
||||
GetFirewallRecord(opts ...DBOption) (model.Firewall, error)
|
||||
ListFirewallRecord(opts ...DBOption) ([]model.Firewall, error)
|
||||
SaveFirewallRecord(firewall *model.Firewall) error
|
||||
@@ -30,6 +42,80 @@ func NewIHostRepo() IHostRepo {
|
||||
return &HostRepo{}
|
||||
}
|
||||
|
||||
func (h *HostRepo) Get(opts ...DBOption) (model.Host, error) {
|
||||
var host model.Host
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.First(&host).Error
|
||||
return host, err
|
||||
}
|
||||
|
||||
func (h *HostRepo) GetList(opts ...DBOption) ([]model.Host, error) {
|
||||
var hosts []model.Host
|
||||
db := global.DB.Model(&model.Host{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.Find(&hosts).Error
|
||||
return hosts, err
|
||||
}
|
||||
|
||||
func (h *HostRepo) Page(page, size int, opts ...DBOption) (int64, []model.Host, error) {
|
||||
var hosts []model.Host
|
||||
db := global.DB.Model(&model.Host{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Limit(size).Offset(size * (page - 1)).Find(&hosts).Error
|
||||
return count, hosts, err
|
||||
}
|
||||
|
||||
func (h *HostRepo) WithByInfo(info string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
if len(info) == 0 {
|
||||
return g
|
||||
}
|
||||
infoStr := "%" + info + "%"
|
||||
return g.Where("name LIKE ? OR addr LIKE ?", infoStr, infoStr)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HostRepo) WithByPort(port uint) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("port = ?", port)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HostRepo) WithByUser(user string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("user = ?", user)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HostRepo) Create(host *model.Host) error {
|
||||
return global.DB.Create(host).Error
|
||||
}
|
||||
|
||||
func (h *HostRepo) Update(id uint, vars map[string]interface{}) error {
|
||||
return global.DB.Model(&model.Host{}).Where("id = ?", id).Updates(vars).Error
|
||||
}
|
||||
|
||||
func (h *HostRepo) UpdateGroup(group, newGroup uint) error {
|
||||
return global.DB.Model(&model.Host{}).Where("group_id = ?", group).Updates(map[string]interface{}{"group_id": newGroup}).Error
|
||||
}
|
||||
|
||||
func (h *HostRepo) Delete(opts ...DBOption) error {
|
||||
db := global.DB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
return db.Delete(&model.Host{}).Error
|
||||
}
|
||||
|
||||
func (h *HostRepo) GetFirewallRecord(opts ...DBOption) (model.Firewall, error) {
|
||||
var firewall model.Firewall
|
||||
db := global.DB
|
||||
|
||||
+543
-952
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,619 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
)
|
||||
|
||||
func (a AgentService) GetFeishuConfig(req dto.AgentFeishuConfigReq) (*dto.AgentFeishuConfig, error) {
|
||||
_, _, conf, err := a.loadAgentConfig(req.AgentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := extractFeishuConfig(conf)
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (a AgentService) UpdateFeishuConfig(req dto.AgentFeishuConfigUpdateReq) error {
|
||||
return a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {
|
||||
setFeishuConfig(conf, dto.AgentFeishuConfig{
|
||||
Enabled: req.Enabled,
|
||||
DmPolicy: req.DmPolicy,
|
||||
BotName: req.BotName,
|
||||
AppID: req.AppID,
|
||||
AppSecret: req.AppSecret,
|
||||
})
|
||||
setFeishuPluginEnabled(conf, req.Enabled)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (a AgentService) GetTelegramConfig(req dto.AgentTelegramConfigReq) (*dto.AgentTelegramConfig, error) {
|
||||
_, _, conf, err := a.loadAgentConfig(req.AgentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := extractTelegramConfig(conf)
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (a AgentService) UpdateTelegramConfig(req dto.AgentTelegramConfigUpdateReq) error {
|
||||
return a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {
|
||||
setTelegramConfig(conf, dto.AgentTelegramConfig{
|
||||
Enabled: req.Enabled,
|
||||
DmPolicy: req.DmPolicy,
|
||||
BotToken: req.BotToken,
|
||||
Proxy: req.Proxy,
|
||||
})
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (a AgentService) GetDiscordConfig(req dto.AgentIDReq) (*dto.AgentDiscordConfig, error) {
|
||||
_, _, conf, err := a.loadAgentConfig(req.AgentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := extractDiscordConfig(conf)
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (a AgentService) UpdateDiscordConfig(req dto.AgentDiscordConfigUpdateReq) error {
|
||||
return a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {
|
||||
setDiscordConfig(conf, dto.AgentDiscordConfig{
|
||||
Enabled: req.Enabled,
|
||||
DmPolicy: req.DmPolicy,
|
||||
GroupPolicy: req.GroupPolicy,
|
||||
Token: req.Token,
|
||||
Proxy: req.Proxy,
|
||||
})
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (a AgentService) GetQQBotConfig(req dto.AgentIDReq) (*dto.AgentQQBotConfig, error) {
|
||||
_, install, conf, err := a.loadAgentConfig(req.AgentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := extractQQBotConfig(conf)
|
||||
installed, _ := checkPluginInstalled(install.ContainerName, "qqbot")
|
||||
result.Installed = installed
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (a AgentService) UpdateQQBotConfig(req dto.AgentQQBotConfigUpdateReq) error {
|
||||
return a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {
|
||||
setQQBotConfig(conf, dto.AgentQQBotConfig{
|
||||
Enabled: req.Enabled,
|
||||
AppID: req.AppID,
|
||||
ClientSecret: req.ClientSecret,
|
||||
})
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (a AgentService) GetWecomConfig(req dto.AgentIDReq) (*dto.AgentWecomConfig, error) {
|
||||
_, install, conf, err := a.loadAgentConfig(req.AgentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := extractWecomConfig(conf)
|
||||
installed, _ := checkPluginInstalled(install.ContainerName, "wecom")
|
||||
result.Installed = installed
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (a AgentService) UpdateWecomConfig(req dto.AgentWecomConfigUpdateReq) error {
|
||||
return a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {
|
||||
setWecomConfig(conf, dto.AgentWecomConfig{
|
||||
Enabled: req.Enabled,
|
||||
DmPolicy: req.DmPolicy,
|
||||
BotID: req.BotID,
|
||||
Secret: req.Secret,
|
||||
})
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (a AgentService) GetDingTalkConfig(req dto.AgentIDReq) (*dto.AgentDingTalkConfig, error) {
|
||||
_, install, conf, err := a.loadAgentConfig(req.AgentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := extractDingTalkConfig(conf)
|
||||
installed, _ := checkPluginInstalled(install.ContainerName, "dingtalk")
|
||||
result.Installed = installed
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (a AgentService) UpdateDingTalkConfig(req dto.AgentDingTalkConfigUpdateReq) error {
|
||||
return a.mutateAgentConfig(req.AgentID, func(_ *model.Agent, _ *model.AppInstall, conf map[string]interface{}) error {
|
||||
setDingTalkConfig(conf, dto.AgentDingTalkConfig{
|
||||
Enabled: req.Enabled,
|
||||
ClientID: req.ClientID,
|
||||
ClientSecret: req.ClientSecret,
|
||||
DmPolicy: req.DmPolicy,
|
||||
AllowFrom: req.AllowFrom,
|
||||
GroupPolicy: req.GroupPolicy,
|
||||
GroupAllowFrom: req.GroupAllowFrom,
|
||||
})
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (a AgentService) InstallPlugin(req dto.AgentPluginInstallReq) error {
|
||||
agent, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
spec, pluginID, err := resolvePluginMeta(req.Type)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
installTask, err := task.NewTaskWithOps(req.Type, task.TaskInstall, task.TaskScopeAI, req.TaskID, req.AgentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
installTask.AddSubTask("Install OpenClaw plugin", func(t *task.Task) error {
|
||||
mgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(10*time.Minute))
|
||||
if req.Type == "qqbot" {
|
||||
legacyPluginPath := path.Join(openclawPluginBaseDir, "qqbot")
|
||||
if err := mgr.RunBashCf("docker exec %s test -d %s", install.ContainerName, legacyPluginPath); err == nil {
|
||||
if err := mgr.Run("docker", "exec", "-i", install.ContainerName, "sh", "-c", "printf 'yes\\n' | openclaw plugins uninstall qqbot"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := mgr.Run("docker", "exec", install.ContainerName, "sh", "-c", buildOpenclawPluginInstallScript(spec, pluginID)); err != nil {
|
||||
return err
|
||||
}
|
||||
conf, err := readOpenclawConfig(agent.ConfigPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appendPluginAllow(conf, pluginID)
|
||||
return writeOpenclawConfigRaw(agent.ConfigPath, conf)
|
||||
}, nil)
|
||||
go func() {
|
||||
if err := installTask.Execute(); err != nil {
|
||||
global.LOG.Errorf("install openclaw plugin failed: %v", err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a AgentService) LoginWeixinChannel(req dto.AgentWeixinLoginReq) error {
|
||||
_, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
loginTask, err := task.NewTaskWithOps("weixin", task.TaskExec, task.TaskScopeAI, req.TaskID, req.AgentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
loginTask.AddSubTask("Login OpenClaw Weixin channel", func(t *task.Task) error {
|
||||
mgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(30*time.Minute))
|
||||
return mgr.RunBashCf("docker exec %s openclaw channels login --channel openclaw-weixin", install.ContainerName)
|
||||
}, nil)
|
||||
go func() {
|
||||
if err := loginTask.Execute(); err != nil {
|
||||
global.LOG.Errorf("login openclaw weixin channel failed: %v", err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a AgentService) CheckPlugin(req dto.AgentPluginCheckReq) (*dto.AgentPluginStatus, error) {
|
||||
_, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
installed, err := checkPluginInstalled(install.ContainerName, req.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &dto.AgentPluginStatus{Installed: installed}, nil
|
||||
}
|
||||
|
||||
func (a AgentService) ApproveChannelPairing(req dto.AgentChannelPairingApproveReq) error {
|
||||
_, install, err := a.loadAgentAndInstall(req.AgentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.RunDefaultBashCf(
|
||||
"docker exec %s openclaw pairing approve %s %q",
|
||||
install.ContainerName,
|
||||
req.Type,
|
||||
strings.TrimSpace(req.PairingCode),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func extractFeishuConfig(conf map[string]interface{}) dto.AgentFeishuConfig {
|
||||
result := dto.AgentFeishuConfig{Enabled: true, DmPolicy: "pairing"}
|
||||
channels, ok := conf["channels"].(map[string]interface{})
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
feishu, ok := channels["feishu"].(map[string]interface{})
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
if enabled, ok := feishu["enabled"].(bool); ok {
|
||||
result.Enabled = enabled
|
||||
}
|
||||
if dmPolicy, ok := feishu["dmPolicy"].(string); ok && strings.TrimSpace(dmPolicy) != "" {
|
||||
result.DmPolicy = dmPolicy
|
||||
}
|
||||
accounts, ok := feishu["accounts"].(map[string]interface{})
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
main, ok := accounts["main"].(map[string]interface{})
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
if appID, ok := main["appId"].(string); ok {
|
||||
result.AppID = appID
|
||||
}
|
||||
if appSecret, ok := main["appSecret"].(string); ok {
|
||||
result.AppSecret = appSecret
|
||||
}
|
||||
if botName, ok := main["botName"].(string); ok {
|
||||
result.BotName = botName
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func setFeishuConfig(conf map[string]interface{}, config dto.AgentFeishuConfig) {
|
||||
channels := ensureChildMap(conf, "channels")
|
||||
feishu := ensureChildMap(channels, "feishu")
|
||||
feishu["enabled"] = config.Enabled
|
||||
feishu["dmPolicy"] = config.DmPolicy
|
||||
|
||||
accounts := ensureChildMap(feishu, "accounts")
|
||||
main := ensureChildMap(accounts, "main")
|
||||
main["appId"] = config.AppID
|
||||
main["appSecret"] = config.AppSecret
|
||||
main["botName"] = config.BotName
|
||||
|
||||
if strings.EqualFold(config.DmPolicy, "open") {
|
||||
feishu["allowFrom"] = []string{"*"}
|
||||
}
|
||||
}
|
||||
|
||||
func setFeishuPluginEnabled(conf map[string]interface{}, enabled bool) {
|
||||
plugins := ensureChildMap(conf, "plugins")
|
||||
entries := ensureChildMap(plugins, "entries")
|
||||
feishu := ensureChildMap(entries, "feishu")
|
||||
feishu["enabled"] = enabled
|
||||
}
|
||||
|
||||
func extractTelegramConfig(conf map[string]interface{}) dto.AgentTelegramConfig {
|
||||
result := dto.AgentTelegramConfig{Enabled: true, DmPolicy: "pairing"}
|
||||
channels, ok := conf["channels"].(map[string]interface{})
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
telegram, ok := channels["telegram"].(map[string]interface{})
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
if enabled, ok := telegram["enabled"].(bool); ok {
|
||||
result.Enabled = enabled
|
||||
}
|
||||
if dmPolicy, ok := telegram["dmPolicy"].(string); ok && strings.TrimSpace(dmPolicy) != "" {
|
||||
result.DmPolicy = dmPolicy
|
||||
}
|
||||
if botToken, ok := telegram["botToken"].(string); ok {
|
||||
result.BotToken = botToken
|
||||
}
|
||||
if proxy, ok := telegram["proxy"].(string); ok {
|
||||
result.Proxy = proxy
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func setTelegramConfig(conf map[string]interface{}, config dto.AgentTelegramConfig) {
|
||||
channels := ensureChildMap(conf, "channels")
|
||||
telegram := map[string]interface{}{
|
||||
"enabled": config.Enabled,
|
||||
"dmPolicy": config.DmPolicy,
|
||||
"botToken": config.BotToken,
|
||||
}
|
||||
if strings.EqualFold(config.DmPolicy, "open") {
|
||||
telegram["allowFrom"] = []string{"*"}
|
||||
}
|
||||
if strings.TrimSpace(config.Proxy) != "" {
|
||||
telegram["proxy"] = strings.TrimSpace(config.Proxy)
|
||||
}
|
||||
channels["telegram"] = telegram
|
||||
}
|
||||
|
||||
func extractDiscordConfig(conf map[string]interface{}) dto.AgentDiscordConfig {
|
||||
result := dto.AgentDiscordConfig{Enabled: true, DmPolicy: "pairing", GroupPolicy: "open"}
|
||||
channels, ok := conf["channels"].(map[string]interface{})
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
discord, ok := channels["discord"].(map[string]interface{})
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
if enabled, ok := discord["enabled"].(bool); ok {
|
||||
result.Enabled = enabled
|
||||
}
|
||||
if token, ok := discord["token"].(string); ok {
|
||||
result.Token = token
|
||||
}
|
||||
if groupPolicy, ok := discord["groupPolicy"].(string); ok && strings.TrimSpace(groupPolicy) != "" {
|
||||
result.GroupPolicy = groupPolicy
|
||||
}
|
||||
if proxy, ok := discord["proxy"].(string); ok {
|
||||
result.Proxy = proxy
|
||||
}
|
||||
if policy, ok := discord["dmPolicy"].(string); ok && strings.TrimSpace(policy) != "" {
|
||||
result.DmPolicy = policy
|
||||
return result
|
||||
}
|
||||
dm, ok := discord["dm"].(map[string]interface{})
|
||||
if ok {
|
||||
if policy, ok := dm["policy"].(string); ok && strings.TrimSpace(policy) != "" {
|
||||
result.DmPolicy = policy
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func setDiscordConfig(conf map[string]interface{}, config dto.AgentDiscordConfig) {
|
||||
channels := ensureChildMap(conf, "channels")
|
||||
discord := ensureChildMap(channels, "discord")
|
||||
discord["enabled"] = config.Enabled
|
||||
discord["token"] = config.Token
|
||||
discord["dmPolicy"] = config.DmPolicy
|
||||
discord["groupPolicy"] = config.GroupPolicy
|
||||
if strings.EqualFold(config.DmPolicy, "open") {
|
||||
discord["allowFrom"] = []string{"*"}
|
||||
} else {
|
||||
delete(discord, "allowFrom")
|
||||
}
|
||||
if strings.TrimSpace(config.Proxy) != "" {
|
||||
discord["proxy"] = strings.TrimSpace(config.Proxy)
|
||||
} else {
|
||||
delete(discord, "proxy")
|
||||
}
|
||||
delete(discord, "dm")
|
||||
}
|
||||
|
||||
func extractQQBotConfig(conf map[string]interface{}) dto.AgentQQBotConfig {
|
||||
result := dto.AgentQQBotConfig{Enabled: true}
|
||||
channels, ok := conf["channels"].(map[string]interface{})
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
qqbot, ok := channels["qqbot"].(map[string]interface{})
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
if enabled, ok := qqbot["enabled"].(bool); ok {
|
||||
result.Enabled = enabled
|
||||
}
|
||||
if appID, ok := qqbot["appId"].(string); ok {
|
||||
result.AppID = appID
|
||||
}
|
||||
if clientSecret, ok := qqbot["clientSecret"].(string); ok {
|
||||
result.ClientSecret = clientSecret
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func extractWecomConfig(conf map[string]interface{}) dto.AgentWecomConfig {
|
||||
result := dto.AgentWecomConfig{Enabled: true, DmPolicy: "pairing"}
|
||||
channels, ok := conf["channels"].(map[string]interface{})
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
wecom, ok := channels["wecom"].(map[string]interface{})
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
if enabled, ok := wecom["enabled"].(bool); ok {
|
||||
result.Enabled = enabled
|
||||
}
|
||||
if dmPolicy, ok := wecom["dmPolicy"].(string); ok && strings.TrimSpace(dmPolicy) != "" {
|
||||
result.DmPolicy = strings.TrimSpace(dmPolicy)
|
||||
}
|
||||
if botID, ok := wecom["botId"].(string); ok {
|
||||
result.BotID = botID
|
||||
}
|
||||
if secret, ok := wecom["secret"].(string); ok {
|
||||
result.Secret = secret
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func extractDingTalkConfig(conf map[string]interface{}) dto.AgentDingTalkConfig {
|
||||
result := dto.AgentDingTalkConfig{
|
||||
Enabled: true,
|
||||
DmPolicy: "pairing",
|
||||
GroupPolicy: "disabled",
|
||||
AllowFrom: []string{},
|
||||
GroupAllowFrom: []string{},
|
||||
}
|
||||
channels, ok := conf["channels"].(map[string]interface{})
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
dingtalk, ok := channels["dingtalk-connector"].(map[string]interface{})
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
if enabled, ok := dingtalk["enabled"].(bool); ok {
|
||||
result.Enabled = enabled
|
||||
}
|
||||
if clientID, ok := dingtalk["clientId"].(string); ok {
|
||||
result.ClientID = clientID
|
||||
}
|
||||
if clientSecret, ok := dingtalk["clientSecret"].(string); ok {
|
||||
result.ClientSecret = clientSecret
|
||||
}
|
||||
if dmPolicy, ok := dingtalk["dmPolicy"].(string); ok && strings.TrimSpace(dmPolicy) != "" {
|
||||
result.DmPolicy = dmPolicy
|
||||
}
|
||||
if groupPolicy, ok := dingtalk["groupPolicy"].(string); ok && strings.TrimSpace(groupPolicy) != "" {
|
||||
result.GroupPolicy = groupPolicy
|
||||
}
|
||||
result.AllowFrom = extractStringList(dingtalk["allowFrom"])
|
||||
result.GroupAllowFrom = extractStringList(dingtalk["groupAllowFrom"])
|
||||
return result
|
||||
}
|
||||
|
||||
func setWecomConfig(conf map[string]interface{}, config dto.AgentWecomConfig) {
|
||||
channels := ensureChildMap(conf, "channels")
|
||||
wecom := ensureChildMap(channels, "wecom")
|
||||
wecom["enabled"] = config.Enabled
|
||||
wecom["botId"] = strings.TrimSpace(config.BotID)
|
||||
wecom["secret"] = strings.TrimSpace(config.Secret)
|
||||
wecom["dmPolicy"] = strings.TrimSpace(config.DmPolicy)
|
||||
if strings.EqualFold(config.DmPolicy, "open") {
|
||||
wecom["allowFrom"] = []string{"*"}
|
||||
} else {
|
||||
wecom["allowFrom"] = []string{}
|
||||
}
|
||||
|
||||
plugins := ensureChildMap(conf, "plugins")
|
||||
entries := ensureChildMap(plugins, "entries")
|
||||
wecomEntry := ensureChildMap(entries, "wecom-openclaw-plugin")
|
||||
wecomEntry["enabled"] = config.Enabled
|
||||
}
|
||||
|
||||
func setDingTalkConfig(conf map[string]interface{}, config dto.AgentDingTalkConfig) {
|
||||
channels := ensureChildMap(conf, "channels")
|
||||
dingtalk := ensureChildMap(channels, "dingtalk-connector")
|
||||
dingtalk["enabled"] = config.Enabled
|
||||
dingtalk["clientId"] = strings.TrimSpace(config.ClientID)
|
||||
dingtalk["clientSecret"] = strings.TrimSpace(config.ClientSecret)
|
||||
dingtalk["dmPolicy"] = config.DmPolicy
|
||||
dingtalk["groupPolicy"] = config.GroupPolicy
|
||||
dingtalk["gatewayToken"] = extractGatewayToken(conf)
|
||||
switch config.DmPolicy {
|
||||
case "open":
|
||||
dingtalk["allowFrom"] = []string{"*"}
|
||||
case "allowlist":
|
||||
dingtalk["allowFrom"] = append([]string(nil), config.AllowFrom...)
|
||||
default:
|
||||
delete(dingtalk, "allowFrom")
|
||||
}
|
||||
switch config.GroupPolicy {
|
||||
case "open":
|
||||
dingtalk["groupAllowFrom"] = []string{"*"}
|
||||
case "allowlist":
|
||||
dingtalk["groupAllowFrom"] = append([]string(nil), config.GroupAllowFrom...)
|
||||
default:
|
||||
delete(dingtalk, "groupAllowFrom")
|
||||
}
|
||||
|
||||
plugins := ensureChildMap(conf, "plugins")
|
||||
entries := ensureChildMap(plugins, "entries")
|
||||
dingtalkEntry := ensureChildMap(entries, "dingtalk-connector")
|
||||
dingtalkEntry["enabled"] = config.Enabled
|
||||
|
||||
gateway := ensureChildMap(conf, "gateway")
|
||||
httpMap := ensureChildMap(gateway, "http")
|
||||
endpoints := ensureChildMap(httpMap, "endpoints")
|
||||
chatCompletions := ensureChildMap(endpoints, "chatCompletions")
|
||||
chatCompletions["enabled"] = true
|
||||
}
|
||||
|
||||
func setQQBotConfig(conf map[string]interface{}, config dto.AgentQQBotConfig) {
|
||||
channels := ensureChildMap(conf, "channels")
|
||||
qqbot := ensureChildMap(channels, "qqbot")
|
||||
delete(qqbot, "dmPolicy")
|
||||
qqbot["enabled"] = config.Enabled
|
||||
qqbot["allowFrom"] = []string{"*"}
|
||||
qqbot["appId"] = strings.TrimSpace(config.AppID)
|
||||
qqbot["clientSecret"] = strings.TrimSpace(config.ClientSecret)
|
||||
|
||||
plugins := ensureChildMap(conf, "plugins")
|
||||
entries := ensureChildMap(plugins, "entries")
|
||||
delete(entries, "qqbot")
|
||||
qqbotEntry := ensureChildMap(entries, "openclaw-qqbot")
|
||||
qqbotEntry["enabled"] = config.Enabled
|
||||
}
|
||||
|
||||
func appendPluginAllow(conf map[string]interface{}, pluginID string) {
|
||||
plugins := ensureChildMap(conf, "plugins")
|
||||
allow := make([]string, 0, 4)
|
||||
seen := map[string]struct{}{}
|
||||
switch values := plugins["allow"].(type) {
|
||||
case []interface{}:
|
||||
for _, value := range values {
|
||||
text, ok := value.(string)
|
||||
if !ok || text == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[text]; ok {
|
||||
continue
|
||||
}
|
||||
seen[text] = struct{}{}
|
||||
allow = append(allow, text)
|
||||
}
|
||||
}
|
||||
if _, ok := seen[pluginID]; ok {
|
||||
plugins["allow"] = allow
|
||||
return
|
||||
}
|
||||
plugins["allow"] = append(allow, pluginID)
|
||||
}
|
||||
|
||||
func buildOpenclawPluginInstallScript(spec, pluginID string) string {
|
||||
return fmt.Sprintf(
|
||||
"set -e; workdir=%s/%s; rm -rf \"$workdir\"; mkdir -p \"$workdir\"; cd \"$workdir\"; npm pack --silent %q >/dev/null 2>&1; pkg=$(find \"$workdir\" -maxdepth 1 -type f -name '*.tgz' | head -n 1); printf '%%s\\n' \"$pkg\"; openclaw plugins install \"$pkg\"; rm -rf \"$workdir\"",
|
||||
openclawPluginPackageTmpDir,
|
||||
pluginID,
|
||||
spec,
|
||||
)
|
||||
}
|
||||
|
||||
func resolvePluginMeta(pluginType string) (string, string, error) {
|
||||
switch pluginType {
|
||||
case "qqbot":
|
||||
return "@tencent-connect/openclaw-qqbot@latest", "openclaw-qqbot", nil
|
||||
case "wecom":
|
||||
return "@wecom/wecom-openclaw-plugin", "wecom-openclaw-plugin", nil
|
||||
case "dingtalk":
|
||||
return "@dingtalk-real-ai/dingtalk-connector", "dingtalk-connector", nil
|
||||
case "weixin":
|
||||
return "@tencent-weixin/openclaw-weixin", "openclaw-weixin", nil
|
||||
default:
|
||||
return "", "", fmt.Errorf("unsupported plugin type")
|
||||
}
|
||||
}
|
||||
|
||||
func checkPluginInstalled(containerName, pluginType string) (bool, error) {
|
||||
_, pluginDir, err := resolvePluginMeta(pluginType)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if strings.TrimSpace(containerName) == "" {
|
||||
return false, buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
pluginPath := path.Join(openclawPluginBaseDir, pluginDir)
|
||||
mgr := cmd.NewCommandMgr(cmd.WithTimeout(20 * time.Second))
|
||||
if err := mgr.RunBashCf("docker exec %s test -d %s", containerName, pluginPath); err != nil {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
)
|
||||
|
||||
const (
|
||||
openclawCronJobsRelativePath = "data/conf/cron/jobs.json"
|
||||
openclawSessionStoreRelativeGlob = "data/conf/agents/*/sessions/sessions.json"
|
||||
openclawSessionFilesRelativeGlob = "data/conf/agents/*/sessions/*.jsonl"
|
||||
)
|
||||
|
||||
func (a AgentService) GetOverview(req dto.AgentOverviewReq) (*dto.AgentOverview, error) {
|
||||
agent, install, conf, err := a.loadOpenclawAgentConfig(req.AgentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
overview := &dto.AgentOverview{
|
||||
Snapshot: dto.AgentOverviewSnapshot{
|
||||
ContainerStatus: install.Status,
|
||||
AppVersion: install.Version,
|
||||
DefaultModel: extractOpenclawDefaultModel(conf),
|
||||
ChannelCount: countOpenclawConfiguredChannels(conf),
|
||||
},
|
||||
}
|
||||
if overview.Snapshot.DefaultModel == "" {
|
||||
overview.Snapshot.DefaultModel = agent.Model
|
||||
}
|
||||
if install.Status != constant.StatusRunning {
|
||||
return overview, nil
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(3)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
skillCount, err := loadOpenclawOverviewSkillStats(install.ContainerName)
|
||||
if err == nil {
|
||||
overview.Snapshot.SkillCount = skillCount
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
sessionCount, err := loadOpenclawOverviewSessionCount(install.GetPath())
|
||||
if err == nil {
|
||||
overview.Snapshot.SessionCount = sessionCount
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
jobCount, err := loadOpenclawOverviewJobCount(install.GetPath())
|
||||
if err == nil {
|
||||
overview.Snapshot.JobCount = jobCount
|
||||
}
|
||||
}()
|
||||
wg.Wait()
|
||||
|
||||
return overview, nil
|
||||
}
|
||||
|
||||
func extractOpenclawDefaultModel(conf map[string]interface{}) string {
|
||||
agents, ok := conf["agents"].(map[string]interface{})
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
defaults, ok := agents["defaults"].(map[string]interface{})
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
model, ok := defaults["model"].(map[string]interface{})
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
primary, _ := model["primary"].(string)
|
||||
return strings.TrimSpace(primary)
|
||||
}
|
||||
|
||||
func countOpenclawConfiguredChannels(conf map[string]interface{}) int {
|
||||
channels, ok := conf["channels"].(map[string]interface{})
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
count := 0
|
||||
for _, value := range channels {
|
||||
channel, ok := value.(map[string]interface{})
|
||||
if !ok || len(channel) == 0 {
|
||||
continue
|
||||
}
|
||||
count++
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func loadOpenclawOverviewSkillStats(containerName string) (int, error) {
|
||||
output, err := cmd.RunDefaultWithStdoutBashCfAndTimeOut(
|
||||
"docker exec %s openclaw skills list --json 2>&1",
|
||||
30*time.Second,
|
||||
containerName,
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if strings.TrimSpace(output) == "" {
|
||||
return 0, nil
|
||||
}
|
||||
skills, err := parseOpenclawSkillsList(output)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(skills), nil
|
||||
}
|
||||
|
||||
func loadOpenclawOverviewSessionCount(installPath string) (int, error) {
|
||||
sessionStorePaths, err := filepath.Glob(filepath.Join(installPath, openclawSessionStoreRelativeGlob))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
total := 0
|
||||
for _, sessionStorePath := range sessionStorePaths {
|
||||
count, err := loadOpenclawSessionCountFromFile(sessionStorePath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
total += count
|
||||
}
|
||||
if total > 0 {
|
||||
return total, nil
|
||||
}
|
||||
sessionFiles, err := filepath.Glob(filepath.Join(installPath, openclawSessionFilesRelativeGlob))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(sessionFiles), nil
|
||||
}
|
||||
|
||||
func loadOpenclawSessionCountFromFile(sessionStorePath string) (int, error) {
|
||||
content, err := os.ReadFile(sessionStorePath)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return parseOpenclawSessionCount(string(content))
|
||||
}
|
||||
|
||||
func loadOpenclawOverviewJobCount(installPath string) (int, error) {
|
||||
content, err := os.ReadFile(filepath.Join(installPath, openclawCronJobsRelativePath))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return 0, nil
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
return parseOpenclawCronCount(string(content))
|
||||
}
|
||||
|
||||
func parseOpenclawSessionCount(output string) (int, error) {
|
||||
if strings.TrimSpace(output) == "" {
|
||||
return 0, nil
|
||||
}
|
||||
var payload interface{}
|
||||
if err := json.Unmarshal([]byte(strings.TrimSpace(output)), &payload); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return countOpenclawSessions(payload), nil
|
||||
}
|
||||
|
||||
func parseOpenclawCronCount(output string) (int, error) {
|
||||
if strings.TrimSpace(output) == "" {
|
||||
return 0, nil
|
||||
}
|
||||
var payload interface{}
|
||||
if err := json.Unmarshal([]byte(strings.TrimSpace(output)), &payload); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
switch value := payload.(type) {
|
||||
case []interface{}:
|
||||
return len(value), nil
|
||||
case map[string]interface{}:
|
||||
if total, ok := value["total"].(float64); ok {
|
||||
return int(total), nil
|
||||
}
|
||||
if jobs, ok := value["jobs"].([]interface{}); ok {
|
||||
return len(jobs), nil
|
||||
}
|
||||
return len(value), nil
|
||||
default:
|
||||
return 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
func countOpenclawSessions(payload interface{}) int {
|
||||
switch value := payload.(type) {
|
||||
case []interface{}:
|
||||
return len(value)
|
||||
case map[string]interface{}:
|
||||
if count, ok := value["count"].(float64); ok {
|
||||
return int(count)
|
||||
}
|
||||
if sessions, ok := value["sessions"]; ok {
|
||||
return countOpenclawSessions(sessions)
|
||||
}
|
||||
count := 0
|
||||
for _, item := range value {
|
||||
switch typed := item.(type) {
|
||||
case map[string]interface{}:
|
||||
if looksLikeOpenclawSession(typed) {
|
||||
count++
|
||||
}
|
||||
case string:
|
||||
if strings.HasSuffix(strings.TrimSpace(typed), ".jsonl") {
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
return count
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func looksLikeOpenclawSession(item map[string]interface{}) bool {
|
||||
if _, ok := item["sessionFile"]; ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := item["agentId"]; ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := item["lastActivityTs"]; ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := item["updatedAt"]; ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
)
|
||||
|
||||
type openclawSkillsList struct {
|
||||
Skills []openclawSkillListItem `json:"skills"`
|
||||
}
|
||||
|
||||
type openclawSkillListItem struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Source string `json:"source"`
|
||||
Bundled bool `json:"bundled"`
|
||||
Disabled bool `json:"disabled"`
|
||||
}
|
||||
|
||||
type openclawSkillInfo struct {
|
||||
SkillKey string `json:"skillKey"`
|
||||
}
|
||||
|
||||
type skillhubSearchPayload struct {
|
||||
Skills []dto.AgentSkillSearchItem `json:"skills"`
|
||||
Results []dto.AgentSkillSearchItem `json:"results"`
|
||||
}
|
||||
|
||||
var clawhubSearchLinePattern = regexp.MustCompile(`^(\S+)\s+(.+?)\s+\(([\d.]+)\)$`)
|
||||
var ansiEscapePattern = regexp.MustCompile(`\x1b\[[0-9;?]*[ -/]*[@-~]`)
|
||||
|
||||
func (a AgentService) ListSkills(req dto.AgentIDReq) ([]dto.AgentSkillItem, error) {
|
||||
_, install, err := a.loadOpenclawAgentAndInstall(req.AgentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := ensureContainerRunning(install.ContainerName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
output, err := cmd.RunDefaultWithStdoutBashCfAndTimeOut(
|
||||
"docker exec %s openclaw skills list --json 2>&1",
|
||||
30*time.Second,
|
||||
install.ContainerName,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(output) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return parseOpenclawSkillsList(output)
|
||||
}
|
||||
|
||||
func (a AgentService) SearchSkills(req dto.AgentSkillSearchReq) ([]dto.AgentSkillSearchItem, error) {
|
||||
_, install, err := a.loadOpenclawAgentAndInstall(req.AgentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := ensureContainerRunning(install.ContainerName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
output, err := loadOpenclawSkillSearchOutput(install.ContainerName, req.Source, req.Keyword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(output) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
switch req.Source {
|
||||
case "skillhub":
|
||||
return parseSkillhubSearchResult(output)
|
||||
default:
|
||||
return parseClawhubSearchResult(output), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a AgentService) UpdateSkill(req dto.AgentSkillUpdateReq) error {
|
||||
agent, install, err := a.loadOpenclawAgentAndInstall(req.AgentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ensureContainerRunning(install.ContainerName); err != nil {
|
||||
return err
|
||||
}
|
||||
conf, err := readOpenclawConfig(agent.ConfigPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
skillKey, err := getOpenclawSkillKey(install.ContainerName, req.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
setOpenclawSkillEnabled(conf, skillKey, req.Enabled)
|
||||
return writeOpenclawConfigRaw(agent.ConfigPath, conf)
|
||||
}
|
||||
|
||||
func (a AgentService) InstallSkill(req dto.AgentSkillInstallReq) error {
|
||||
_, install, err := a.loadOpenclawAgentAndInstall(req.AgentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ensureContainerRunning(install.ContainerName); err != nil {
|
||||
return err
|
||||
}
|
||||
installTask, err := task.NewTaskWithOps(req.Slug, task.TaskInstall, task.TaskScopeAI, req.TaskID, req.AgentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
installTask.AddSubTask("Install OpenClaw skill", func(t *task.Task) error {
|
||||
mgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(10*time.Minute))
|
||||
return mgr.Run("docker", "exec", install.ContainerName, "sh", "-c", buildOpenclawSkillInstallCommand(req.Source, req.Slug))
|
||||
}, nil)
|
||||
go func() {
|
||||
if err := installTask.Execute(); err != nil {
|
||||
global.LOG.Errorf("install openclaw skill failed: %v", err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseOpenclawSkillsList(output string) ([]dto.AgentSkillItem, error) {
|
||||
payloadBytes, err := extractEmbeddedJSON(output)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(payloadBytes) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
var payload openclawSkillsList
|
||||
if err := json.Unmarshal(payloadBytes, &payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items := make([]dto.AgentSkillItem, 0, len(payload.Skills))
|
||||
for _, item := range payload.Skills {
|
||||
items = append(items, dto.AgentSkillItem{
|
||||
Name: item.Name,
|
||||
Description: item.Description,
|
||||
Source: item.Source,
|
||||
Bundled: item.Bundled,
|
||||
Disabled: item.Disabled,
|
||||
})
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func loadOpenclawSkillSearchOutput(containerName, source, keyword string) (string, error) {
|
||||
switch source {
|
||||
case "skillhub":
|
||||
return cmd.RunDefaultWithStdoutBashCfAndTimeOut(
|
||||
"docker exec %s skillhub search %q --json 2>&1",
|
||||
30*time.Second,
|
||||
containerName,
|
||||
keyword,
|
||||
)
|
||||
default:
|
||||
return cmd.RunDefaultWithStdoutBashCfAndTimeOut(
|
||||
"docker exec %s clawhub search %q 2>&1",
|
||||
30*time.Second,
|
||||
containerName,
|
||||
keyword,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func parseSkillhubSearchResult(output string) ([]dto.AgentSkillSearchItem, error) {
|
||||
trimmed := strings.TrimSpace(output)
|
||||
if trimmed == "" {
|
||||
return nil, nil
|
||||
}
|
||||
var list []dto.AgentSkillSearchItem
|
||||
if err := json.Unmarshal([]byte(trimmed), &list); err == nil {
|
||||
for i := range list {
|
||||
list[i].Source = "skillhub"
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
var payload skillhubSearchPayload
|
||||
if err := json.Unmarshal([]byte(trimmed), &payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items := payload.Skills
|
||||
if len(items) == 0 {
|
||||
items = payload.Results
|
||||
}
|
||||
for i := range items {
|
||||
items[i].Source = "skillhub"
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func parseClawhubSearchResult(output string) []dto.AgentSkillSearchItem {
|
||||
lines := strings.Split(strings.TrimSpace(output), "\n")
|
||||
items := make([]dto.AgentSkillSearchItem, 0, len(lines))
|
||||
for _, line := range lines {
|
||||
matches := clawhubSearchLinePattern.FindStringSubmatch(strings.TrimSpace(line))
|
||||
if len(matches) != 4 {
|
||||
continue
|
||||
}
|
||||
items = append(items, dto.AgentSkillSearchItem{
|
||||
Slug: matches[1],
|
||||
Name: matches[2],
|
||||
Score: matches[3],
|
||||
Source: "clawhub",
|
||||
})
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
func buildOpenclawSkillInstallCommand(source, slug string) string {
|
||||
switch source {
|
||||
case "clawhub":
|
||||
return fmt.Sprintf(
|
||||
"mkdir -p %s && clawhub --workdir /home/node/.openclaw --dir skills install %q",
|
||||
openclawManagedSkillsDir,
|
||||
slug,
|
||||
)
|
||||
default:
|
||||
return fmt.Sprintf(
|
||||
"mkdir -p %s && skillhub --dir %s install %q",
|
||||
openclawManagedSkillsDir,
|
||||
openclawManagedSkillsDir,
|
||||
slug,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func getOpenclawSkillKey(containerName, name string) (string, error) {
|
||||
output, err := cmd.RunDefaultWithStdoutBashCfAndTimeOut(
|
||||
"docker exec %s openclaw skills info %q --json 2>&1",
|
||||
30*time.Second,
|
||||
containerName,
|
||||
name,
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return parseOpenclawSkillKey(name, output)
|
||||
}
|
||||
|
||||
func parseOpenclawSkillKey(name, output string) (string, error) {
|
||||
payloadBytes, err := extractEmbeddedJSON(output)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var payload openclawSkillInfo
|
||||
if err := json.Unmarshal(payloadBytes, &payload); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if payload.SkillKey == "" {
|
||||
return "", fmt.Errorf("skill %s does not have a skillKey", name)
|
||||
}
|
||||
return payload.SkillKey, nil
|
||||
}
|
||||
|
||||
func extractEmbeddedJSON(output string) ([]byte, error) {
|
||||
trimmed := strings.TrimSpace(ansiEscapePattern.ReplaceAllString(output, ""))
|
||||
if trimmed == "" {
|
||||
return nil, nil
|
||||
}
|
||||
for i := 0; i < len(trimmed); i++ {
|
||||
if trimmed[i] != '{' && trimmed[i] != '[' {
|
||||
continue
|
||||
}
|
||||
decoder := json.NewDecoder(strings.NewReader(trimmed[i:]))
|
||||
var raw json.RawMessage
|
||||
if err := decoder.Decode(&raw); err == nil {
|
||||
return raw, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("json payload not found")
|
||||
}
|
||||
|
||||
func setOpenclawSkillEnabled(conf map[string]interface{}, skillKey string, enabled bool) {
|
||||
skills := ensureChildMap(conf, "skills")
|
||||
entries := ensureChildMap(skills, "entries")
|
||||
entry := ensureChildMap(entries, skillKey)
|
||||
entry["enabled"] = enabled
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/email"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
||||
"github.com/shirou/gopsutil/v4/disk"
|
||||
"mime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -483,13 +484,14 @@ func (a AlertService) TestAlertConfig(req dto.AlertConfigTest) (bool, error) {
|
||||
if username == "" {
|
||||
username = req.Sender
|
||||
}
|
||||
encodedDisplayName := mime.BEncoding.Encode("UTF-8", req.DisplayName)
|
||||
cfg := email.SMTPConfig{
|
||||
Host: req.Host,
|
||||
Port: req.Port,
|
||||
Sender: req.Sender,
|
||||
Username: username,
|
||||
Password: req.Password,
|
||||
From: fmt.Sprintf(`"%s" <%s>`, req.DisplayName, req.Sender),
|
||||
From: fmt.Sprintf(`"%s" <%s>`, encodedDisplayName, req.Sender),
|
||||
Encryption: req.Encryption,
|
||||
Recipient: req.Recipient,
|
||||
}
|
||||
|
||||
@@ -3,6 +3,12 @@ package service
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
@@ -18,11 +24,6 @@ import (
|
||||
"github.com/shirou/gopsutil/v4/load"
|
||||
"github.com/shirou/gopsutil/v4/mem"
|
||||
"github.com/shirou/gopsutil/v4/net"
|
||||
"math"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -423,25 +424,19 @@ func loadDiskUsage(alert dto.AlertDTO) {
|
||||
}
|
||||
if isAlertDue(newDate) {
|
||||
if strings.Contains(alert.Project, "all") {
|
||||
err = processAllDisks(alert)
|
||||
_ = processAllDisks(alert)
|
||||
} else {
|
||||
err = processSingleDisk(alert)
|
||||
_ = processSingleDisk(alert)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func loadPanelLogin(alert dto.AlertDTO) {
|
||||
count, isAlert, err := alertUtil.CountRecentFailedLoginLogs(alert.Cycle, alert.Count)
|
||||
alertType := alert.Type
|
||||
quota := strconv.Itoa(count)
|
||||
quotaType := strconv.Itoa(int(alert.Cycle))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("Failed to count recent failed login logs: %v", err)
|
||||
}
|
||||
if isAlert {
|
||||
alertType = "panelLogin"
|
||||
quota = strconv.Itoa(count)
|
||||
quotaType = "panelLogin"
|
||||
params := []dto.Param{
|
||||
{
|
||||
Index: "1",
|
||||
@@ -454,7 +449,7 @@ func loadPanelLogin(alert dto.AlertDTO) {
|
||||
Value: "",
|
||||
},
|
||||
}
|
||||
sendAlerts(alert, alertType, quota, quotaType, params)
|
||||
sendAlerts(alert, "panelLogin", strconv.Itoa(count), "panelLogin", params)
|
||||
}
|
||||
|
||||
whitelist := strings.Split(strings.TrimSpace(alert.AdvancedParams), "\n")
|
||||
@@ -463,15 +458,13 @@ func loadPanelLogin(alert dto.AlertDTO) {
|
||||
global.LOG.Errorf("Failed to check recent failed ip login logs: %v", err)
|
||||
}
|
||||
if len(records) > 0 {
|
||||
quota = strings.Join(func() []string {
|
||||
quota := strings.Join(func() []string {
|
||||
var ips []string
|
||||
for _, r := range records {
|
||||
ips = append(ips, r.IP)
|
||||
}
|
||||
return ips
|
||||
}(), "\n")
|
||||
alertType = "panelIpLogin"
|
||||
quotaType = "panelIpLogin"
|
||||
params := []dto.Param{
|
||||
{
|
||||
Index: "1",
|
||||
@@ -484,22 +477,16 @@ func loadPanelLogin(alert dto.AlertDTO) {
|
||||
Value: " IP ",
|
||||
},
|
||||
}
|
||||
sendAlerts(alert, alertType, quota, quotaType, params)
|
||||
sendAlerts(alert, "panelIpLogin", quota, "panelIpLogin", params)
|
||||
}
|
||||
}
|
||||
|
||||
func loadSSHLogin(alert dto.AlertDTO) {
|
||||
count, isAlert, err := alertUtil.CountRecentFailedSSHLog(alert.Cycle, alert.Count)
|
||||
alertType := alert.Type
|
||||
quota := strconv.Itoa(count)
|
||||
quotaType := strconv.Itoa(int(alert.Cycle))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("Failed to count recent failed ssh login logs: %v", err)
|
||||
}
|
||||
if isAlert {
|
||||
alertType = "sshLogin"
|
||||
quota = strconv.Itoa(count)
|
||||
quotaType = "sshLogin"
|
||||
params := []dto.Param{
|
||||
{
|
||||
Index: "1",
|
||||
@@ -512,7 +499,7 @@ func loadSSHLogin(alert dto.AlertDTO) {
|
||||
Value: "",
|
||||
},
|
||||
}
|
||||
sendAlerts(alert, alertType, quota, quotaType, params)
|
||||
sendAlerts(alert, "sshLogin", strconv.Itoa(count), "sshLogin", params)
|
||||
}
|
||||
whitelist := strings.Split(strings.TrimSpace(alert.AdvancedParams), "\n")
|
||||
records, err := alertUtil.FindRecentSuccessLoginNotInWhitelist(30, whitelist)
|
||||
@@ -520,9 +507,7 @@ func loadSSHLogin(alert dto.AlertDTO) {
|
||||
global.LOG.Errorf("Failed to check recent failed ip ssh login logs: %v", err)
|
||||
}
|
||||
if len(records) > 0 {
|
||||
quota = strings.Join(records, "\n")
|
||||
alertType = "sshIpLogin"
|
||||
quotaType = "sshIpLogin"
|
||||
quota := strings.Join(records, "\n")
|
||||
params := []dto.Param{
|
||||
{
|
||||
Index: "1",
|
||||
@@ -535,7 +520,7 @@ func loadSSHLogin(alert dto.AlertDTO) {
|
||||
Value: " IP ",
|
||||
},
|
||||
}
|
||||
sendAlerts(alert, alertType, quota, quotaType, params)
|
||||
sendAlerts(alert, "sshIpLogin", quota, "sshIpLogin", params)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -894,7 +879,7 @@ func processAllDisks(alert dto.AlertDTO) error {
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("disk path %s process failed: %v", item.Path, err)
|
||||
errMsgs = append(errMsgs, errMsg)
|
||||
global.LOG.Errorf(errMsg)
|
||||
global.LOG.Errorf("%s", errMsg)
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -907,7 +892,7 @@ func processAllDisks(alert dto.AlertDTO) error {
|
||||
func processSingleDisk(alert dto.AlertDTO) error {
|
||||
err := checkAndCreateDiskAlert(alert, alert.Project)
|
||||
if err != nil {
|
||||
global.LOG.Errorf(err.Error())
|
||||
global.LOG.Errorf("%s", err.Error())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -57,6 +57,10 @@ type IAppService interface {
|
||||
GetAppDetailByKey(appKey, version string) (response.AppDetailSimpleDTO, error)
|
||||
}
|
||||
|
||||
type appInstallHooks struct {
|
||||
AfterCopyData func(appInstall *model.AppInstall) error
|
||||
}
|
||||
|
||||
func NewIAppService() IAppService {
|
||||
return &AppService{}
|
||||
}
|
||||
@@ -340,6 +344,10 @@ func (a AppService) GetAppDetailByID(id uint) (*response.AppDetailDTO, error) {
|
||||
}
|
||||
|
||||
func (a AppService) Install(req request.AppInstallCreate, executeScript bool) (appInstall *model.AppInstall, err error) {
|
||||
return a.installWithHooks(req, executeScript, nil)
|
||||
}
|
||||
|
||||
func (a AppService) installWithHooks(req request.AppInstallCreate, executeScript bool, hooks *appInstallHooks) (appInstall *model.AppInstall, err error) {
|
||||
if err = docker.CreateDefaultDockerNetwork(); err != nil {
|
||||
err = buserr.WithDetail("Err1PanelNetworkFailed", err.Error(), nil)
|
||||
return
|
||||
@@ -416,7 +424,7 @@ func (a AppService) Install(req request.AppInstallCreate, executeScript bool) (a
|
||||
}
|
||||
} else {
|
||||
if appDetail.DockerCompose == "" {
|
||||
dockerComposeUrl := fmt.Sprintf("%s/%s/1panel/%s/%s/docker-compose.yml", global.CONF.RemoteURL.AppRepo, global.CONF.Base.Mode, app.Key, appDetail.Version)
|
||||
dockerComposeUrl := fmt.Sprintf("%s/%s/1panel/%s/%s/docker-compose.yml", global.AppRepoURL(), global.CONF.Base.Mode, app.Key, appDetail.Version)
|
||||
_, composeRes, err = req_helper.HandleRequest(dockerComposeUrl, http.MethodGet, constant.TimeOut20s)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -516,6 +524,10 @@ func (a AppService) Install(req request.AppInstallCreate, executeScript bool) (a
|
||||
}
|
||||
appInstall.Env = string(paramByte)
|
||||
|
||||
var maxSort int
|
||||
global.DB.Model(&model.AppInstall{}).Where("favorite = ?", false).Select("COALESCE(MAX(sort_order),0)").Scan(&maxSort)
|
||||
appInstall.SortOrder = maxSort + 1
|
||||
|
||||
if err = appInstallRepo.Create(context.Background(), appInstall); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -533,6 +545,11 @@ func (a AppService) Install(req request.AppInstallCreate, executeScript bool) (a
|
||||
if err = copyData(t, app, appDetail, appInstall, req); err != nil {
|
||||
return err
|
||||
}
|
||||
if hooks != nil && hooks.AfterCopyData != nil {
|
||||
if err = hooks.AfterCopyData(appInstall); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if executeScript {
|
||||
if err = runScript(t, appInstall, "init"); err != nil {
|
||||
return err
|
||||
@@ -817,7 +834,7 @@ func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
versionUrl := fmt.Sprintf("%s/%s/1panel.json.version.txt", global.CONF.RemoteURL.AppRepo, global.CONF.Base.Mode)
|
||||
versionUrl := fmt.Sprintf("%s/%s/1panel.json.version.txt", global.AppRepoURL(), global.CONF.Base.Mode)
|
||||
_, versionRes, err := req_helper.HandleRequest(versionUrl, http.MethodGet, constant.TimeOut20s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -889,7 +906,7 @@ func getAppFromRepo(downloadPath string) error {
|
||||
|
||||
func getAppList() (*dto.AppList, error) {
|
||||
list := &dto.AppList{}
|
||||
if err := getAppFromRepo(fmt.Sprintf("%s/%s/1panel.json.zip", global.CONF.RemoteURL.AppRepo, global.CONF.Base.Mode)); err != nil {
|
||||
if err := getAppFromRepo(fmt.Sprintf("%s/%s/1panel.json.zip", global.AppRepoURL(), global.CONF.Base.Mode)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listFile := filepath.Join(global.Dir.ResourceDir, "1panel.json")
|
||||
|
||||
@@ -57,6 +57,7 @@ type IAppInstallService interface {
|
||||
UpdateAppConfig(req request.AppConfigUpdate) error
|
||||
GetInstallList() ([]dto.AppInstallInfo, error)
|
||||
GetAppInstallInfo(appInstallID uint) (*response.AppInstallInfo, error)
|
||||
UpdateSort(req request.AppInstallSort) error
|
||||
}
|
||||
|
||||
func NewIAppInstalledService() IAppInstallService {
|
||||
@@ -83,6 +84,7 @@ func (a *AppInstallService) Page(req request.AppInstalledSearch) (int64, []respo
|
||||
err error
|
||||
)
|
||||
opts = append(opts, repo.WithOrderRuleBy("favorite", "descending"))
|
||||
opts = append(opts, repo.WithOrderRuleBy("sort_order", "ascending"))
|
||||
|
||||
if req.Name != "" {
|
||||
opts = append(opts, repo.WithByLikeName(req.Name))
|
||||
@@ -300,6 +302,9 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error {
|
||||
return opNginx(install.ContainerName, constant.NginxReload)
|
||||
case constant.Favorite:
|
||||
install.Favorite = req.Favorite
|
||||
var maxSort int
|
||||
global.DB.Model(&model.AppInstall{}).Where("favorite = ?", req.Favorite).Select("COALESCE(MAX(sort_order),0)").Scan(&maxSort)
|
||||
install.SortOrder = maxSort + 1
|
||||
return appInstallRepo.Save(context.Background(), &install)
|
||||
default:
|
||||
return errors.New("operate not support")
|
||||
@@ -318,17 +323,25 @@ func (a *AppInstallService) UpdateAppConfig(req request.AppConfigUpdate) error {
|
||||
return appInstallRepo.Save(context.Background(), &installed)
|
||||
}
|
||||
|
||||
func (a *AppInstallService) UpdateSort(req request.AppInstallSort) error {
|
||||
for _, item := range req.Items {
|
||||
if err := appInstallRepo.BatchUpdateBy(map[string]interface{}{"sort_order": item.SortOrder}, repo.WithByID(item.InstallID)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
|
||||
installed, err := appInstallRepo.GetFirst(repo.WithByID(req.InstallId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
changePort := false
|
||||
oldInstalled := installed
|
||||
port, ok := req.Params["PANEL_APP_PORT_HTTP"]
|
||||
if ok {
|
||||
portN := int(math.Ceil(port.(float64)))
|
||||
if portN != installed.HttpPort {
|
||||
changePort = true
|
||||
httpPort, err := checkPort("PANEL_APP_PORT_HTTP", req.Params)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -414,9 +427,17 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
|
||||
installed.Status = constant.StatusRunning
|
||||
_ = appInstallRepo.Save(context.Background(), &installed)
|
||||
|
||||
proxyChanged := hasAppInstallProxyPassChanged(&oldInstalled, &installed)
|
||||
currentProxy, currentProxyErr := getAppInstallProxyPass(&installed)
|
||||
website, _ := websiteRepo.GetFirst(websiteRepo.WithAppInstallId(installed.ID))
|
||||
if changePort && website.ID != 0 && website.Status == constant.StatusRunning {
|
||||
go func() {
|
||||
if proxyChanged && website.ID != 0 && currentProxyErr == nil {
|
||||
website.Proxy = currentProxy
|
||||
if err := websiteRepo.SaveWithoutCtx(&website); err != nil {
|
||||
global.LOG.Error(buserr.WithErr("ErrUpdateBuWebsite", err).Error())
|
||||
}
|
||||
}
|
||||
if proxyChanged && website.ID != 0 && website.Status == constant.StatusRunning && currentProxyErr == nil {
|
||||
go func(website model.Website, proxy string) {
|
||||
nginxInstall, err := getNginxFull(&website)
|
||||
if err != nil {
|
||||
global.LOG.Error(buserr.WithErr("ErrUpdateBuWebsite", err).Error())
|
||||
@@ -429,7 +450,6 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
|
||||
return
|
||||
}
|
||||
server := servers[0]
|
||||
proxy := fmt.Sprintf("http://127.0.0.1:%d", installed.HttpPort)
|
||||
server.UpdateRootProxy([]string{proxy})
|
||||
|
||||
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
@@ -440,7 +460,10 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
|
||||
global.LOG.Error(buserr.WithErr("ErrUpdateBuWebsite", err).Error())
|
||||
return
|
||||
}
|
||||
}()
|
||||
}(website, currentProxy)
|
||||
}
|
||||
if proxyChanged && website.ID != 0 && currentProxyErr != nil {
|
||||
global.LOG.Error(buserr.WithErr("ErrUpdateBuWebsite", currentProxyErr).Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
@@ -81,7 +83,7 @@ func (a AppService) createSyncAppStoreTask(sharedCtx **appSyncContext) func(t *t
|
||||
ctx := &appSyncContext{
|
||||
task: t,
|
||||
httpClient: http.Client{Timeout: time.Duration(constant.TimeOut20s) * time.Second, Transport: xpack.LoadRequestTransport()},
|
||||
baseRemoteUrl: fmt.Sprintf("%s/%s/1panel", global.CONF.RemoteURL.AppRepo, global.CONF.Base.Mode),
|
||||
baseRemoteUrl: fmt.Sprintf("%s/%s/1panel", global.AppRepoURL(), global.CONF.Base.Mode),
|
||||
systemVersion: setting.SystemVersion,
|
||||
settingService: settingService,
|
||||
list: list,
|
||||
@@ -127,90 +129,197 @@ func (a AppService) createSyncAppStoreTask(sharedCtx **appSyncContext) func(t *t
|
||||
}
|
||||
}
|
||||
|
||||
type appWorkItem struct {
|
||||
appDef dto.AppDefine
|
||||
app model.App
|
||||
iconUrl string
|
||||
hadIcon bool
|
||||
}
|
||||
|
||||
type appWorkResult struct {
|
||||
appKey string
|
||||
app model.App
|
||||
iconStatus int
|
||||
hadIcon bool
|
||||
httpFailed bool
|
||||
}
|
||||
|
||||
func (c *appSyncContext) processOneApp(item appWorkItem) appWorkResult {
|
||||
app := item.app
|
||||
l := item.appDef
|
||||
result := appWorkResult{
|
||||
appKey: l.AppProperty.Key,
|
||||
hadIcon: item.hadIcon,
|
||||
}
|
||||
|
||||
if item.hadIcon {
|
||||
status, iconField := c.downloadAppIcon(item.iconUrl, l.AppProperty.Key, app.Icon)
|
||||
result.iconStatus = status
|
||||
switch status {
|
||||
case http.StatusOK:
|
||||
app.Icon = iconField
|
||||
case http.StatusNotModified:
|
||||
default:
|
||||
result.httpFailed = true
|
||||
}
|
||||
}
|
||||
|
||||
app.TagsKey = l.AppProperty.Tags
|
||||
if l.AppProperty.Recommend > 0 {
|
||||
app.Recommend = l.AppProperty.Recommend
|
||||
} else {
|
||||
app.Recommend = 9999
|
||||
}
|
||||
app.ReadMe = l.ReadMe
|
||||
app.LastModified = l.LastModified
|
||||
|
||||
versions := l.Versions
|
||||
detailsMap := getAppDetails(app.Details, versions)
|
||||
for _, v := range versions {
|
||||
version := v.Name
|
||||
detail := detailsMap[version]
|
||||
versionUrl := fmt.Sprintf("%s/%s/%s", c.baseRemoteUrl, app.Key, version)
|
||||
|
||||
paramByte, _ := json.Marshal(v.AppForm)
|
||||
var appForm dto.AppForm
|
||||
_ = json.Unmarshal(paramByte, &appForm)
|
||||
|
||||
if appForm.SupportVersion > 0 && common.CompareVersion(strconv.FormatFloat(appForm.SupportVersion, 'f', -1, 64), c.systemVersion) {
|
||||
delete(detailsMap, version)
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := InitTypes[app.Type]; ok {
|
||||
dockerComposeUrl := fmt.Sprintf("%s/%s", versionUrl, "docker-compose.yml")
|
||||
_, composeRes, err := req_helper.HandleRequestWithClient(&c.httpClient, dockerComposeUrl, http.MethodGet, constant.TimeOut20s)
|
||||
if err == nil {
|
||||
detail.DockerCompose = string(composeRes)
|
||||
} else {
|
||||
result.httpFailed = true
|
||||
}
|
||||
} else {
|
||||
detail.DockerCompose = ""
|
||||
}
|
||||
|
||||
detail.Params = string(paramByte)
|
||||
detail.DownloadUrl = fmt.Sprintf("%s/%s", versionUrl, app.Key+"-"+version+".tar.gz")
|
||||
detail.DownloadCallBackUrl = v.DownloadCallBackUrl
|
||||
detail.Update = true
|
||||
detail.LastModified = v.LastModified
|
||||
detailsMap[version] = detail
|
||||
}
|
||||
|
||||
newDetails := make([]model.AppDetail, 0, len(detailsMap))
|
||||
for _, detail := range detailsMap {
|
||||
newDetails = append(newDetails, detail)
|
||||
}
|
||||
app.Details = newDetails
|
||||
|
||||
result.app = app
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *appSyncContext) syncAppIconsAndDetails() error {
|
||||
total := len(c.list.Apps)
|
||||
global.LOG.Infof("[AppStore] sync app detail start, total apps: %d", total)
|
||||
|
||||
var (
|
||||
icon200Count = 0
|
||||
icon304Count = 0
|
||||
iconFailCount = 0
|
||||
)
|
||||
|
||||
for i, l := range c.list.Apps {
|
||||
if (i+1)%10 == 0 {
|
||||
c.task.LogWithProgress(i18n.GetMsgByKey("SyncAppDetail"), i+1, total)
|
||||
}
|
||||
|
||||
workItems := make([]appWorkItem, 0, total)
|
||||
for _, l := range c.list.Apps {
|
||||
app, ok := c.appsMap[l.AppProperty.Key]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
iconUrl, hasPending := c.pendingIcons[l.AppProperty.Key]
|
||||
if hasPending {
|
||||
status, iconField := c.downloadAppIcon(iconUrl, l.AppProperty.Key, app.Icon)
|
||||
switch status {
|
||||
workItems = append(workItems, appWorkItem{
|
||||
appDef: l,
|
||||
app: app,
|
||||
iconUrl: iconUrl,
|
||||
hadIcon: hasPending,
|
||||
})
|
||||
}
|
||||
|
||||
totalWork := len(workItems)
|
||||
if totalWork == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
const maxWorkers = 4
|
||||
var (
|
||||
failFlag atomic.Bool
|
||||
workCh = make(chan int, maxWorkers)
|
||||
resultCh = make(chan appWorkResult, maxWorkers)
|
||||
wg sync.WaitGroup
|
||||
)
|
||||
|
||||
wg.Add(maxWorkers)
|
||||
for range maxWorkers {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for idx := range workCh {
|
||||
result := c.processOneApp(workItems[idx])
|
||||
resultCh <- result
|
||||
if result.httpFailed {
|
||||
failFlag.Store(true)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
go func() { wg.Wait(); close(resultCh) }()
|
||||
|
||||
var fed atomic.Int32
|
||||
go func() {
|
||||
for i := range workItems {
|
||||
if failFlag.Load() {
|
||||
break
|
||||
}
|
||||
workCh <- i
|
||||
fed.Store(int32(i + 1))
|
||||
}
|
||||
close(workCh)
|
||||
}()
|
||||
|
||||
var (
|
||||
completed int
|
||||
icon200Count int
|
||||
icon304Count int
|
||||
iconFailCount int
|
||||
)
|
||||
milestones := [4]int{totalWork / 4, totalWork / 2, totalWork * 3 / 4, totalWork}
|
||||
nextMS := 0
|
||||
checkProgress := func() {
|
||||
if nextMS < len(milestones) && completed >= milestones[nextMS] {
|
||||
c.task.LogWithProgress(i18n.GetMsgByKey("SyncAppDetail"), completed, totalWork)
|
||||
nextMS++
|
||||
}
|
||||
}
|
||||
|
||||
applyResult := func(result appWorkResult) {
|
||||
c.appsMap[result.appKey] = result.app
|
||||
if result.hadIcon {
|
||||
switch result.iconStatus {
|
||||
case http.StatusOK:
|
||||
app.Icon = iconField
|
||||
icon200Count++
|
||||
case http.StatusNotModified:
|
||||
icon304Count++
|
||||
default:
|
||||
global.LOG.Warnf("[AppStore] download icon failed url=%s, appKey=%s", iconUrl, l.AppProperty.Key)
|
||||
iconFailCount++
|
||||
}
|
||||
}
|
||||
completed++
|
||||
checkProgress()
|
||||
}
|
||||
|
||||
app.TagsKey = l.AppProperty.Tags
|
||||
if l.AppProperty.Recommend > 0 {
|
||||
app.Recommend = l.AppProperty.Recommend
|
||||
} else {
|
||||
app.Recommend = 9999
|
||||
for result := range resultCh {
|
||||
applyResult(result)
|
||||
}
|
||||
|
||||
fedCount := int(fed.Load())
|
||||
if failFlag.Load() && fedCount < len(workItems) {
|
||||
global.LOG.Warnf("[AppStore] HTTP failure detected, falling back to serial for remaining %d apps", len(workItems)-fedCount)
|
||||
for i := fedCount; i < len(workItems); i++ {
|
||||
result := c.processOneApp(workItems[i])
|
||||
applyResult(result)
|
||||
}
|
||||
app.ReadMe = l.ReadMe
|
||||
app.LastModified = l.LastModified
|
||||
|
||||
versions := l.Versions
|
||||
detailsMap := getAppDetails(app.Details, versions)
|
||||
for _, v := range versions {
|
||||
version := v.Name
|
||||
detail := detailsMap[version]
|
||||
versionUrl := fmt.Sprintf("%s/%s/%s", c.baseRemoteUrl, app.Key, version)
|
||||
|
||||
paramByte, _ := json.Marshal(v.AppForm)
|
||||
var appForm dto.AppForm
|
||||
_ = json.Unmarshal(paramByte, &appForm)
|
||||
|
||||
if appForm.SupportVersion > 0 && common.CompareVersion(strconv.FormatFloat(appForm.SupportVersion, 'f', -1, 64), c.systemVersion) {
|
||||
delete(detailsMap, version)
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := InitTypes[app.Type]; ok {
|
||||
dockerComposeUrl := fmt.Sprintf("%s/%s", versionUrl, "docker-compose.yml")
|
||||
_, composeRes, err := req_helper.HandleRequestWithClient(&c.httpClient, dockerComposeUrl, http.MethodGet, constant.TimeOut20s)
|
||||
if err == nil {
|
||||
detail.DockerCompose = string(composeRes)
|
||||
}
|
||||
} else {
|
||||
detail.DockerCompose = ""
|
||||
}
|
||||
|
||||
detail.Params = string(paramByte)
|
||||
detail.DownloadUrl = fmt.Sprintf("%s/%s", versionUrl, app.Key+"-"+version+".tar.gz")
|
||||
detail.DownloadCallBackUrl = v.DownloadCallBackUrl
|
||||
detail.Update = true
|
||||
detail.LastModified = v.LastModified
|
||||
detailsMap[version] = detail
|
||||
}
|
||||
|
||||
var newDetails []model.AppDetail
|
||||
for _, detail := range detailsMap {
|
||||
newDetails = append(newDetails, detail)
|
||||
}
|
||||
app.Details = newDetails
|
||||
c.appsMap[l.AppProperty.Key] = app
|
||||
}
|
||||
|
||||
global.LOG.Infof("[AppStore] icon download completed - total: %d, success(200): %d, cached(304): %d, failed: %d",
|
||||
|
||||
@@ -33,7 +33,6 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/compose"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
composeV2 "github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/env"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/nginx"
|
||||
@@ -363,7 +362,7 @@ func deleteAppInstall(deleteReq request.AppInstallDelete) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
images, err := composeV2.GetImagesFromDockerCompose(content, []byte(install.DockerCompose))
|
||||
images, err := docker.GetImagesFromDockerCompose(content, []byte(install.DockerCompose))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -393,7 +392,7 @@ func deleteAppInstall(deleteReq request.AppInstallDelete) error {
|
||||
return err
|
||||
}
|
||||
appKey := install.App.Key
|
||||
if appKey == constant.AppOpenclaw {
|
||||
if appKey == constant.AppOpenclaw || appKey == constant.AppCopaw {
|
||||
_ = agentRepo.DeleteByAppInstallIDWithCtx(ctx, install.ID)
|
||||
}
|
||||
|
||||
@@ -676,6 +675,7 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oldVersion := install.Version
|
||||
detail, err := appDetailRepo.GetFirst(repo.WithByID(req.DetailID))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -752,7 +752,7 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
||||
if req.DockerCompose != "" {
|
||||
composeContent = []byte(req.DockerCompose)
|
||||
}
|
||||
images, err := composeV2.GetImagesFromDockerCompose(content, composeContent)
|
||||
images, err := docker.GetImagesFromDockerCompose(content, composeContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -802,6 +802,9 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
||||
}
|
||||
|
||||
var newCompose string
|
||||
if err = migrateOpenclawProtocolUpgrade(&install, oldVersion, detail.Version); err != nil {
|
||||
return err
|
||||
}
|
||||
if req.DockerCompose == "" {
|
||||
newCompose, err = getUpgradeCompose(install, detail)
|
||||
if err != nil {
|
||||
@@ -911,7 +914,7 @@ func getContainerNames(install model.AppInstall) ([]string, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
project, err := composeV2.GetComposeProject(install.Name, install.GetPath(), []byte(install.DockerCompose), []byte(envStr), true)
|
||||
project, err := docker.GetComposeProject(install.Name, install.GetPath(), []byte(install.DockerCompose), []byte(envStr), true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1133,7 +1136,7 @@ func runScript(task *task.Task, appInstall *model.AppInstall, operate string) er
|
||||
}
|
||||
|
||||
func checkContainerNameIsExist(containerName, appDir string) (bool, error) {
|
||||
client, err := composeV2.NewDockerClient()
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -1169,7 +1172,7 @@ func upApp(task *task.Task, appInstall *model.AppInstall, pullImages bool) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
images, err := composeV2.GetImagesFromDockerCompose(envByte, []byte(appInstall.DockerCompose))
|
||||
images, err := docker.GetImagesFromDockerCompose(envByte, []byte(appInstall.DockerCompose))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1613,6 +1616,7 @@ func handleInstalled(appInstallList []model.AppInstall, updated, sync, checkUpda
|
||||
Document: installed.App.Document,
|
||||
},
|
||||
Favorite: installed.Favorite,
|
||||
SortOrder: installed.SortOrder,
|
||||
Container: installed.ContainerName,
|
||||
ServiceName: strings.ToLower(installed.ServiceName),
|
||||
}
|
||||
|
||||
@@ -57,6 +57,11 @@ type IBackupService interface {
|
||||
|
||||
AppBackup(db dto.CommonBackup) (*model.BackupRecord, error)
|
||||
AppRecover(req dto.CommonRecover) error
|
||||
|
||||
ContainerBackup(req dto.CommonBackup) error
|
||||
ContainerRecover(req dto.CommonRecover) error
|
||||
ComposeBackup(req dto.CommonBackup) error
|
||||
ComposeRecover(req dto.CommonRecover) error
|
||||
}
|
||||
|
||||
func NewIBackupService() IBackupService {
|
||||
|
||||
+153
-39
@@ -11,6 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
"github.com/subosito/gotenv"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
@@ -179,6 +180,9 @@ func handleAppRecover(install *model.AppInstall, parentTask *task.Task, recoverF
|
||||
return err
|
||||
}
|
||||
tmpPath := strings.ReplaceAll(recoverFile, ".tar.gz", "")
|
||||
if err := fileOp.TarGzExtractPro(tmpPath+"/app.tar.gz", tmpPath, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_, _ = compose.Up(install.GetComposePath())
|
||||
_ = os.RemoveAll(strings.ReplaceAll(recoverFile, ".tar.gz", ""))
|
||||
@@ -187,19 +191,26 @@ func handleAppRecover(install *model.AppInstall, parentTask *task.Task, recoverF
|
||||
if !fileOp.Stat(tmpPath+"/app.json") || !fileOp.Stat(tmpPath+"/app.tar.gz") {
|
||||
return errors.New(i18n.GetMsgByKey("AppBackupFileIncomplete"))
|
||||
}
|
||||
var oldInstall model.AppInstall
|
||||
var backupInstall model.AppInstall
|
||||
appJson, err := os.ReadFile(tmpPath + "/app.json")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := json.Unmarshal(appJson, &oldInstall); err != nil {
|
||||
if err := json.Unmarshal(appJson, &backupInstall); err != nil {
|
||||
return fmt.Errorf("unmarshal app.json failed, err: %v", err)
|
||||
}
|
||||
if oldInstall.App.Key != install.App.Key || oldInstall.Name != install.Name {
|
||||
if backupInstall.App.Key != install.App.Key || backupInstall.Name != install.Name {
|
||||
return errors.New(i18n.GetMsgByKey("AppAttributesNotMatch"))
|
||||
}
|
||||
|
||||
newEnvFile := ""
|
||||
backupEnvMap, err := getEnvMapByPath(path.Join(tmpPath, install.Name, ".env"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
installedEnvMap, err := getEnvMapByPath(install.GetEnvPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var mergedEnvContent string
|
||||
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
|
||||
for _, resource := range resources {
|
||||
var database model.Database
|
||||
@@ -225,11 +236,20 @@ func handleAppRecover(install *model.AppInstall, parentTask *task.Task, recoverF
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newDB, err := reCreatePostgresqlDB(db.ID, database, backupEnvMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
backupInstall.Env, mergedEnvContent, err = buildRecoverEnv(backupInstall.Env, backupEnvMap, installedEnvMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = appInstallResourceRepo.BatchUpdateBy(map[string]interface{}{"resource_id": newDB.ID}, repo.WithByID(resource.ID))
|
||||
taskName := task.GetTaskName(db.Name, task.TaskRecover, task.TaskScopeDatabase)
|
||||
t.LogStart(taskName)
|
||||
if err := handlePostgresqlRecover(dto.CommonRecover{
|
||||
Name: database.Name,
|
||||
DetailName: db.Name,
|
||||
Name: newDB.PostgresqlName,
|
||||
DetailName: newDB.Name,
|
||||
File: fmt.Sprintf("%s/%s.sql.gz", tmpPath, install.Name),
|
||||
}, parentTask, true); err != nil {
|
||||
t.LogFailedWithErr(taskName, err)
|
||||
@@ -241,15 +261,11 @@ func handleAppRecover(install *model.AppInstall, parentTask *task.Task, recoverF
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newDB, envMap, err := reCreateDB(db.ID, database, oldInstall.Env)
|
||||
newDB, err := reCreateDB(db.ID, database, backupEnvMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oldHost := fmt.Sprintf("\"PANEL_DB_HOST\":\"%v\"", envMap["PANEL_DB_HOST"].(string))
|
||||
newHost := fmt.Sprintf("\"PANEL_DB_HOST\":\"%v\"", database.Address)
|
||||
oldInstall.Env = strings.ReplaceAll(oldInstall.Env, oldHost, newHost)
|
||||
envMap["PANEL_DB_HOST"] = database.Address
|
||||
newEnvFile, err = coverEnvJsonToStr(oldInstall.Env)
|
||||
backupInstall.Env, mergedEnvContent, err = buildRecoverEnv(backupInstall.Env, backupEnvMap, installedEnvMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -284,22 +300,22 @@ func handleAppRecover(install *model.AppInstall, parentTask *task.Task, recoverF
|
||||
t.LogSuccess(deCompressName)
|
||||
_ = fileOp.DeleteDir(backPath)
|
||||
|
||||
if len(newEnvFile) != 0 {
|
||||
if len(mergedEnvContent) != 0 {
|
||||
envPath := fmt.Sprintf("%s/%s/.env", install.GetAppPath(), install.Name)
|
||||
file, err := os.OpenFile(envPath, os.O_WRONLY|os.O_TRUNC, 0640)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
_, _ = file.WriteString(newEnvFile)
|
||||
_, _ = file.WriteString(mergedEnvContent)
|
||||
}
|
||||
|
||||
oldInstall.ID = install.ID
|
||||
oldInstall.Status = constant.StatusRunning
|
||||
oldInstall.AppId = install.AppId
|
||||
oldInstall.AppDetailId = install.AppDetailId
|
||||
oldInstall.App.ID = install.AppId
|
||||
if err := appInstallRepo.Save(context.Background(), &oldInstall); err != nil {
|
||||
backupInstall.ID = install.ID
|
||||
backupInstall.Status = constant.StatusRunning
|
||||
backupInstall.AppId = install.AppId
|
||||
backupInstall.AppDetailId = install.AppDetailId
|
||||
backupInstall.App.ID = install.AppId
|
||||
if err := appInstallRepo.Save(context.Background(), &backupInstall); err != nil {
|
||||
global.LOG.Errorf("save db app install failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
@@ -373,33 +389,131 @@ func doAppBackup(install *model.AppInstall, parentTask *task.Task, backupDir, fi
|
||||
return nil
|
||||
}
|
||||
|
||||
func reCreateDB(dbID uint, database model.Database, oldEnv string) (*model.DatabaseMysql, map[string]interface{}, error) {
|
||||
func reCreateDB(dbID uint, database model.Database, envMap map[string]interface{}) (*model.DatabaseMysql, error) {
|
||||
mysqlService := NewIMysqlService()
|
||||
ctx := context.Background()
|
||||
_ = mysqlService.Delete(ctx, dto.MysqlDBDelete{ID: dbID, Database: database.Name, Type: database.Type, DeleteBackup: false, ForceDelete: true})
|
||||
|
||||
envMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(oldEnv), &envMap); err != nil {
|
||||
return nil, envMap, err
|
||||
}
|
||||
oldName, _ := envMap["PANEL_DB_NAME"].(string)
|
||||
oldUser, _ := envMap["PANEL_DB_USER"].(string)
|
||||
oldPassword, _ := envMap["PANEL_DB_USER_PASSWORD"].(string)
|
||||
dbInfo := getDBCreateInfoFromEnv(envMap, "utf8mb4")
|
||||
createDB, err := mysqlService.Create(context.Background(), dto.MysqlDBCreate{
|
||||
Name: oldName,
|
||||
Name: dbInfo.Name,
|
||||
From: database.From,
|
||||
Database: database.Name,
|
||||
Format: "utf8mb4",
|
||||
Username: oldUser,
|
||||
Password: oldPassword,
|
||||
Format: dbInfo.Format,
|
||||
Username: dbInfo.User,
|
||||
Password: dbInfo.Password,
|
||||
Permission: "%",
|
||||
})
|
||||
cronjobs, _ := cronjobRepo.List(cronjobRepo.WithByDbName(fmt.Sprintf("%v", dbID)))
|
||||
for _, job := range cronjobs {
|
||||
_ = cronjobRepo.Update(job.ID, map[string]interface{}{"db_name": fmt.Sprintf("%v", createDB.ID)})
|
||||
}
|
||||
if err != nil {
|
||||
return nil, envMap, err
|
||||
return nil, err
|
||||
}
|
||||
return createDB, envMap, nil
|
||||
updateCronjobsDBRef(dbID, createDB.ID)
|
||||
return createDB, nil
|
||||
}
|
||||
|
||||
func reCreatePostgresqlDB(dbID uint, database model.Database, envMap map[string]interface{}) (*model.DatabasePostgresql, error) {
|
||||
postgresqlService := NewIPostgresqlService()
|
||||
_ = postgresqlService.Delete(context.Background(), dto.PostgresqlDBDelete{
|
||||
ID: dbID,
|
||||
Type: database.Type,
|
||||
Database: database.Name,
|
||||
DeleteBackup: false,
|
||||
ForceDelete: true,
|
||||
})
|
||||
|
||||
dbInfo := getDBCreateInfoFromEnv(envMap, "UTF8")
|
||||
createDB, err := postgresqlService.Create(context.Background(), dto.PostgresqlDBCreate{
|
||||
Name: dbInfo.Name,
|
||||
From: database.From,
|
||||
Database: database.Name,
|
||||
Format: dbInfo.Format,
|
||||
Username: dbInfo.User,
|
||||
Password: dbInfo.Password,
|
||||
SuperUser: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
updateCronjobsDBRef(dbID, createDB.ID)
|
||||
return createDB, nil
|
||||
}
|
||||
|
||||
type dbRecreateInfo struct {
|
||||
Name string
|
||||
User string
|
||||
Password string
|
||||
Format string
|
||||
}
|
||||
|
||||
func getDBCreateInfoFromEnv(envMap map[string]interface{}, defaultFormat string) dbRecreateInfo {
|
||||
name, _ := envMap["PANEL_DB_NAME"].(string)
|
||||
user, _ := envMap["PANEL_DB_USER"].(string)
|
||||
password, _ := envMap["PANEL_DB_USER_PASSWORD"].(string)
|
||||
format, _ := envMap["format"].(string)
|
||||
if len(format) == 0 {
|
||||
format = defaultFormat
|
||||
}
|
||||
return dbRecreateInfo{
|
||||
Name: name,
|
||||
User: user,
|
||||
Password: password,
|
||||
Format: format,
|
||||
}
|
||||
}
|
||||
|
||||
func updateCronjobsDBRef(oldDBID, newDBID uint) {
|
||||
cronjobs, _ := cronjobRepo.List(cronjobRepo.WithByDbName(fmt.Sprintf("%v", oldDBID)))
|
||||
for _, job := range cronjobs {
|
||||
_ = cronjobRepo.Update(job.ID, map[string]interface{}{"db_name": fmt.Sprintf("%v", newDBID)})
|
||||
}
|
||||
}
|
||||
|
||||
func buildRecoverEnv(appEnv string, backupEnvMap, installedEnvMap map[string]interface{}) (string, string, error) {
|
||||
currentHostVal, hasCurrentHost := installedEnvMap["PANEL_DB_HOST"]
|
||||
if hasCurrentHost && fmt.Sprintf("%v", currentHostVal) != "" {
|
||||
backupHost := fmt.Sprintf("\"PANEL_DB_HOST\":\"%v\"", backupEnvMap["PANEL_DB_HOST"])
|
||||
currentHost := fmt.Sprintf("\"PANEL_DB_HOST\":\"%v\"", currentHostVal)
|
||||
appEnv = strings.ReplaceAll(appEnv, backupHost, currentHost)
|
||||
if _, ok := backupEnvMap["CASDOOR_DATASOURCE_NAME"]; ok {
|
||||
backupEnvMap["CASDOOR_DATASOURCE_NAME"] = strings.ReplaceAll(
|
||||
fmt.Sprintf("%v", backupEnvMap["CASDOOR_DATASOURCE_NAME"]),
|
||||
fmt.Sprintf("%v", backupEnvMap["PANEL_DB_HOST"]),
|
||||
fmt.Sprintf("%v", currentHostVal),
|
||||
)
|
||||
}
|
||||
backupEnvMap["PANEL_DB_HOST"] = currentHostVal
|
||||
}
|
||||
newEnvMap := make(map[string]string, len(backupEnvMap))
|
||||
handleMap(backupEnvMap, newEnvMap)
|
||||
mergedEnvContent, err := gotenv.Marshal(newEnvMap)
|
||||
if err != nil {
|
||||
return appEnv, "", err
|
||||
}
|
||||
return appEnv, mergedEnvContent, nil
|
||||
}
|
||||
|
||||
func getEnvMapByPath(envPath string) (map[string]interface{}, error) {
|
||||
envMap := make(map[string]interface{})
|
||||
envFile, err := os.ReadFile(envPath)
|
||||
if err != nil {
|
||||
return envMap, err
|
||||
}
|
||||
lines := strings.Split(string(envFile), "\n")
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
parts := strings.SplitN(line, "=", 2)
|
||||
if len(parts) == 2 {
|
||||
value := strings.TrimSpace(parts[1])
|
||||
if len(value) >= 2 {
|
||||
if (value[0] == '"' && value[len(value)-1] == '"') || (value[0] == '\'' && value[len(value)-1] == '\'') {
|
||||
value = value[1 : len(value)-1]
|
||||
}
|
||||
}
|
||||
envMap[parts[0]] = value
|
||||
}
|
||||
}
|
||||
return envMap, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,707 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/compose"
|
||||
dockerUtils "github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/client"
|
||||
)
|
||||
|
||||
type composeBackupFile struct {
|
||||
OriginalPath string `json:"originalPath"`
|
||||
FileName string `json:"fileName"`
|
||||
RelativePath string `json:"relativePath,omitempty"`
|
||||
BackupPath string `json:"backupPath"`
|
||||
}
|
||||
|
||||
type composeBackupMeta struct {
|
||||
ComposeName string `json:"composeName"`
|
||||
ComposePath string `json:"composePath"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
Files []composeBackupFile `json:"files"`
|
||||
Containers []string `json:"containers"`
|
||||
}
|
||||
|
||||
type composeBackupContext struct {
|
||||
req dto.CommonBackup
|
||||
composeName string
|
||||
composePath string
|
||||
composeFiles []string
|
||||
composeDir string
|
||||
fileOp files.FileOp
|
||||
dockerClient *client.Client
|
||||
stopped bool
|
||||
backupDir string
|
||||
fileName string
|
||||
filePath string
|
||||
tmpDir string
|
||||
meta composeBackupMeta
|
||||
}
|
||||
|
||||
type composeRecoverContext struct {
|
||||
req dto.CommonRecover
|
||||
fileOp files.FileOp
|
||||
tmpDir string
|
||||
meta composeBackupMeta
|
||||
composeName string
|
||||
targetDir string
|
||||
composePath string
|
||||
enqueued bool
|
||||
}
|
||||
|
||||
func (u *BackupService) ComposeBackup(req dto.CommonBackup) error {
|
||||
timeNow := time.Now().Format(constant.DateTimeSlimLayout) + common.RandStrAndNum(5)
|
||||
fileName := req.FileName
|
||||
if fileName == "" {
|
||||
fileName = fmt.Sprintf("%s_%s.tar.gz", req.Name, timeNow)
|
||||
}
|
||||
if !strings.HasSuffix(fileName, ".tar.gz") {
|
||||
fileName += ".tar.gz"
|
||||
}
|
||||
itemDir := fmt.Sprintf("compose/%s", req.Name)
|
||||
backupDir := path.Join(global.Dir.LocalBackupDir, itemDir)
|
||||
record := &model.BackupRecord{
|
||||
Type: req.Type,
|
||||
Name: req.Name,
|
||||
SourceAccountIDs: "1",
|
||||
DownloadAccountID: 1,
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
TaskID: req.TaskID,
|
||||
Status: constant.StatusWaiting,
|
||||
Description: req.Description,
|
||||
}
|
||||
if err := backupRepo.CreateRecord(record); err != nil {
|
||||
global.LOG.Errorf("save compose backup record failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
if err := handleComposeBackup(req, nil, record.ID, backupDir, fileName); err != nil {
|
||||
backupRepo.UpdateRecordByMap(record.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *BackupService) ComposeRecover(req dto.CommonRecover) error {
|
||||
return handleComposeRecover(req, nil)
|
||||
}
|
||||
|
||||
func handleComposeBackup(req dto.CommonBackup, parentTask *task.Task, recordID uint, backupDir, fileName string) error {
|
||||
composeCtx, err := newComposeBackupContext(req, backupDir, fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
containerNames, err := loadComposeContainerNames(composeCtx)
|
||||
if err != nil {
|
||||
composeCtx.close()
|
||||
return err
|
||||
}
|
||||
|
||||
backupTask := parentTask
|
||||
if backupTask == nil {
|
||||
backupTask, err = task.NewTaskWithOps(composeCtx.composeName, task.TaskBackup, task.TaskScopeBackup, req.TaskID, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if req.StopBefore {
|
||||
backupTask.AddSubTaskWithOps(i18n.GetMsgByKey("ComposeBackupStop"), func(t *task.Task) error {
|
||||
return stepStopComposeForBackup(composeCtx)
|
||||
}, func(t *task.Task) {
|
||||
_ = stepStartComposeAfterBackup(composeCtx)
|
||||
}, 3, time.Hour)
|
||||
}
|
||||
backupTask.AddSubTaskWithOps(i18n.GetMsgByKey("ComposeBackupPrepare"), func(t *task.Task) error { return stepPrepareComposeBackup(composeCtx) }, nil, 3, time.Hour)
|
||||
backupTask.AddSubTaskWithOps(i18n.GetMsgByKey("ComposeBackupFiles"), func(t *task.Task) error { return stepBackupComposeFiles(composeCtx) }, nil, 3, time.Hour)
|
||||
backupTask.AddSubTaskWithOps(i18n.GetMsgByKey("ComposeBackupContainers"), func(t *task.Task) error { return nil }, nil, 3, time.Hour)
|
||||
for _, containerName := range containerNames {
|
||||
backupFileName := fmt.Sprintf("%s.tar.gz", sanitizeComposeFileName(containerName))
|
||||
backupFile := path.Join(composeCtx.tmpDir, "containers", backupFileName)
|
||||
if err := handleContainerBackup(containerName, backupTask, 0, path.Dir(backupFile), path.Base(backupFile), "", "", false); err != nil {
|
||||
return err
|
||||
}
|
||||
composeCtx.meta.Containers = append(composeCtx.meta.Containers, path.Join("containers", backupFileName))
|
||||
}
|
||||
backupTask.AddSubTaskWithOps(i18n.GetMsgByKey("ComposeBackupMeta"), func(t *task.Task) error { return stepWriteComposeBackupMeta(composeCtx) }, nil, 3, time.Hour)
|
||||
backupTask.AddSubTaskWithOps(task.GetTaskName(composeCtx.composeName, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error {
|
||||
return stepPackComposeBackup(composeCtx)
|
||||
}, nil, 3, time.Hour)
|
||||
if req.StopBefore {
|
||||
backupTask.AddSubTaskWithOps(i18n.GetMsgByKey("ComposeBackupStart"), func(t *task.Task) error {
|
||||
return stepStartComposeAfterBackup(composeCtx)
|
||||
}, nil, 3, time.Hour)
|
||||
}
|
||||
backupTask.AddSubTaskWithOps(i18n.GetMsgByKey("ComposeBackupCleanup"), func(t *task.Task) error {
|
||||
composeCtx.close()
|
||||
return nil
|
||||
}, nil, 0, time.Hour)
|
||||
if parentTask != nil {
|
||||
return nil
|
||||
}
|
||||
go func() {
|
||||
defer composeCtx.close()
|
||||
if err := backupTask.Execute(); err != nil {
|
||||
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
|
||||
return
|
||||
}
|
||||
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusSuccess})
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadComposeContainerNames(composeCtx *composeBackupContext) ([]string, error) {
|
||||
options := container.ListOptions{All: true}
|
||||
options.Filters = filters.NewArgs(filters.Arg("label", composeProjectLabel+"="+composeCtx.composeName))
|
||||
containers, err := composeCtx.dockerClient.ContainerList(context.Background(), options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
names := make([]string, 0, len(containers))
|
||||
for _, item := range containers {
|
||||
if len(item.Names) == 0 {
|
||||
continue
|
||||
}
|
||||
names = append(names, strings.TrimPrefix(item.Names[0], "/"))
|
||||
}
|
||||
sort.Strings(names)
|
||||
return names, nil
|
||||
}
|
||||
|
||||
func handleComposeRecover(req dto.CommonRecover, parentTask *task.Task) error {
|
||||
var recoverCtx *composeRecoverContext
|
||||
|
||||
recoverTask := parentTask
|
||||
var err error
|
||||
if recoverTask == nil {
|
||||
if isImportRecover(req) {
|
||||
taskName := i18n.GetMsgByKey("TaskImport") + i18n.GetMsgByKey("Compose")
|
||||
recoverTask, err = task.NewTask(taskName, task.TaskImport, task.TaskScopeBackup, req.TaskID, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
taskName := req.Name
|
||||
if taskName == "" {
|
||||
taskName = "compose"
|
||||
}
|
||||
recoverTask, err = task.NewTaskWithOps(taskName, task.TaskRecover, task.TaskScopeBackup, req.TaskID, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
timeout := loadRecoverTimeout(req.Timeout)
|
||||
recoverTask.AddSubTaskWithOps(i18n.GetMsgByKey("ComposeRecoverPrepare"), func(t *task.Task) error {
|
||||
ctx, err := newComposeRecoverContext(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
recoverCtx = ctx
|
||||
if err := stepPrepareComposeRecover(recoverCtx); err != nil {
|
||||
recoverCtx.close()
|
||||
recoverCtx = nil
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, func(t *task.Task) {
|
||||
if recoverCtx != nil {
|
||||
recoverCtx.close()
|
||||
recoverCtx = nil
|
||||
}
|
||||
}, 3, timeout)
|
||||
recoverTask.AddSubTaskWithOps(i18n.GetMsgByKey("ComposeRecoverExtract"), func(t *task.Task) error { return stepExtractComposeRecover(recoverCtx) }, nil, 3, timeout)
|
||||
recoverTask.AddSubTaskWithOps(i18n.GetMsgByKey("ComposeRecoverMeta"), func(t *task.Task) error {
|
||||
if err := stepLoadComposeRecoverMeta(recoverCtx); err != nil {
|
||||
return err
|
||||
}
|
||||
t.Log(i18n.GetMsgWithMap("ComposeRecoverMetaLogName", map[string]interface{}{
|
||||
"name": recoverCtx.composeName,
|
||||
}))
|
||||
t.Log(i18n.GetMsgWithMap("ComposeRecoverMetaLogPath", map[string]interface{}{
|
||||
"backupPath": recoverCtx.meta.ComposePath,
|
||||
"targetDir": recoverCtx.targetDir,
|
||||
}))
|
||||
t.Log(i18n.GetMsgWithMap("ComposeRecoverMetaLogCount", map[string]interface{}{
|
||||
"files": len(recoverCtx.meta.Files),
|
||||
"containers": len(recoverCtx.meta.Containers),
|
||||
}))
|
||||
return nil
|
||||
}, nil, 3, timeout)
|
||||
recoverTask.AddSubTaskWithOps(i18n.GetMsgByKey("ComposeRecoverFiles"), func(t *task.Task) error { return stepRestoreComposeFiles(recoverCtx) }, nil, 3, timeout)
|
||||
recoverTask.AddSubTaskWithOps(i18n.GetMsgByKey("ComposeRecoverContainers"), func(t *task.Task) error {
|
||||
if recoverCtx.enqueued {
|
||||
return nil
|
||||
}
|
||||
containerItems := make([]string, 0, len(recoverCtx.meta.Containers))
|
||||
for _, item := range recoverCtx.meta.Containers {
|
||||
backupItem := item
|
||||
filePath, err := safeJoinWithinBase(recoverCtx.tmpDir, backupItem)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid container backup path %q, err: %v", backupItem, err)
|
||||
}
|
||||
if !recoverCtx.fileOp.Stat(filePath) {
|
||||
return fmt.Errorf("container backup file not found: %s", backupItem)
|
||||
}
|
||||
containerItems = append(containerItems, backupItem)
|
||||
}
|
||||
for _, backupItem := range containerItems {
|
||||
filePath, err := safeJoinWithinBase(recoverCtx.tmpDir, backupItem)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid container backup path %q, err: %v", backupItem, err)
|
||||
}
|
||||
containerLabel := strings.TrimSuffix(path.Base(backupItem), ".tar.gz")
|
||||
containerReq := recoverCtx.req
|
||||
containerReq.Type = "container"
|
||||
containerReq.Name = containerLabel
|
||||
containerReq.DetailName = ""
|
||||
containerReq.File = filePath
|
||||
containerReq.Secret = ""
|
||||
if err := handleContainerRecover(containerReq, recoverTask); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
recoverTask.AddSubTaskWithOps(i18n.GetMsgByKey("ComposeRecoverRecord"), func(t *task.Task) error {
|
||||
return stepSaveComposeRecord(recoverCtx)
|
||||
}, nil, 3, timeout)
|
||||
recoverTask.AddSubTaskWithOps(i18n.GetMsgByKey("ComposeRecoverCleanup"), func(t *task.Task) error {
|
||||
if recoverCtx != nil {
|
||||
recoverCtx.close()
|
||||
recoverCtx = nil
|
||||
}
|
||||
return nil
|
||||
}, nil, 0, timeout)
|
||||
recoverCtx.enqueued = true
|
||||
return nil
|
||||
}, nil, 3, timeout)
|
||||
if parentTask != nil {
|
||||
return nil
|
||||
}
|
||||
go func() {
|
||||
_ = recoverTask.Execute()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func newComposeBackupContext(req dto.CommonBackup, backupDir, fileName string) (*composeBackupContext, error) {
|
||||
if req.Name == "" {
|
||||
return nil, fmt.Errorf("compose name is required")
|
||||
}
|
||||
dockerClient, err := dockerUtils.NewDockerClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
composePath, composeFiles, err := loadComposePathAndFiles(req.Name, dockerClient)
|
||||
if err != nil {
|
||||
_ = dockerClient.Close()
|
||||
return nil, err
|
||||
}
|
||||
filePath := path.Join(backupDir, fileName)
|
||||
tmpDir := path.Join(path.Dir(filePath), strings.TrimSuffix(path.Base(filePath), ".tar.gz"))
|
||||
ctx := &composeBackupContext{
|
||||
req: req,
|
||||
composeName: req.Name,
|
||||
composePath: composePath,
|
||||
composeFiles: composeFiles,
|
||||
composeDir: path.Dir(composeFiles[0]),
|
||||
fileOp: files.NewFileOp(),
|
||||
dockerClient: dockerClient,
|
||||
backupDir: backupDir,
|
||||
fileName: fileName,
|
||||
filePath: filePath,
|
||||
tmpDir: tmpDir,
|
||||
meta: composeBackupMeta{
|
||||
ComposeName: req.Name,
|
||||
ComposePath: composePath,
|
||||
CreatedAt: time.Now().Format(constant.DateTimeLayout),
|
||||
Files: make([]composeBackupFile, 0),
|
||||
Containers: make([]string, 0),
|
||||
},
|
||||
}
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func loadComposePathAndFiles(composeName string, dockerClient *client.Client) (string, []string, error) {
|
||||
composeRecord, _ := composeRepo.GetRecord(repo.WithByName(composeName))
|
||||
if composeRecord.ID == 0 {
|
||||
composeRecord, _ = composeRepo.GetRecord(repo.WithByName(strings.ToLower(composeName)))
|
||||
}
|
||||
composePath := composeRecord.Path
|
||||
if composePath == "" {
|
||||
options := container.ListOptions{All: true}
|
||||
options.Filters = filters.NewArgs(filters.Arg("label", composeProjectLabel))
|
||||
list, err := dockerClient.ContainerList(context.Background(), options)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if len(list) == 0 {
|
||||
return "", nil, fmt.Errorf("compose %s not found", composeName)
|
||||
}
|
||||
var targetContainer *container.Summary
|
||||
for i := range list {
|
||||
if strings.EqualFold(list[i].Labels[composeProjectLabel], composeName) {
|
||||
targetContainer = &list[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
if targetContainer == nil {
|
||||
return "", nil, fmt.Errorf("compose %s not found", composeName)
|
||||
}
|
||||
config := targetContainer.Labels[composeConfigLabel]
|
||||
workdir := targetContainer.Labels[composeWorkdirLabel]
|
||||
if len(config) != 0 && len(workdir) != 0 && strings.Contains(config, workdir) {
|
||||
composePath = config
|
||||
} else {
|
||||
composePath = workdir
|
||||
}
|
||||
}
|
||||
composeFiles := normalizeComposeFiles(composePath)
|
||||
if len(composeFiles) == 0 {
|
||||
return "", nil, fmt.Errorf("compose file not found for %s", composeName)
|
||||
}
|
||||
return composePath, composeFiles, nil
|
||||
}
|
||||
|
||||
func normalizeComposeFiles(composePath string) []string {
|
||||
items := strings.Split(composePath, ",")
|
||||
result := make([]string, 0)
|
||||
seen := make(map[string]struct{})
|
||||
for _, item := range items {
|
||||
item = strings.TrimSpace(item)
|
||||
if item == "" {
|
||||
continue
|
||||
}
|
||||
stat, err := os.Stat(item)
|
||||
if err == nil && stat.IsDir() {
|
||||
item = path.Join(item, "docker-compose.yml")
|
||||
}
|
||||
if _, err := os.Stat(item); err != nil {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[item]; ok {
|
||||
continue
|
||||
}
|
||||
seen[item] = struct{}{}
|
||||
result = append(result, item)
|
||||
}
|
||||
sort.Strings(result)
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *composeBackupContext) close() {
|
||||
if c.dockerClient != nil {
|
||||
_ = c.dockerClient.Close()
|
||||
c.dockerClient = nil
|
||||
}
|
||||
if c.tmpDir != "" {
|
||||
_ = os.RemoveAll(c.tmpDir)
|
||||
c.tmpDir = ""
|
||||
}
|
||||
}
|
||||
|
||||
func stepPrepareComposeBackup(composeCtx *composeBackupContext) error {
|
||||
if err := os.MkdirAll(composeCtx.backupDir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("mkdir %s failed, err: %v", composeCtx.backupDir, err)
|
||||
}
|
||||
_ = os.RemoveAll(composeCtx.tmpDir)
|
||||
if err := os.MkdirAll(path.Join(composeCtx.tmpDir, "compose_files"), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.MkdirAll(path.Join(composeCtx.tmpDir, "containers"), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func stepStopComposeForBackup(composeCtx *composeBackupContext) error {
|
||||
if composeCtx.stopped {
|
||||
return nil
|
||||
}
|
||||
options := container.ListOptions{All: false}
|
||||
options.Filters = filters.NewArgs(filters.Arg("label", composeProjectLabel+"="+composeCtx.composeName))
|
||||
runningList, err := composeCtx.dockerClient.ContainerList(context.Background(), options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(runningList) == 0 {
|
||||
return nil
|
||||
}
|
||||
if stdout, err := compose.Operate(composeCtx.composePath, "stop"); err != nil {
|
||||
return fmt.Errorf("docker-compose stop failed, std: %s, err: %v", stdout, err)
|
||||
}
|
||||
composeCtx.stopped = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func stepStartComposeAfterBackup(composeCtx *composeBackupContext) error {
|
||||
if !composeCtx.stopped {
|
||||
return nil
|
||||
}
|
||||
if stdout, err := compose.Up(composeCtx.composePath); err != nil {
|
||||
return fmt.Errorf("docker-compose up failed, std: %s, err: %v", stdout, err)
|
||||
}
|
||||
composeCtx.stopped = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func stepBackupComposeFiles(composeCtx *composeBackupContext) error {
|
||||
for i, filePath := range composeCtx.composeFiles {
|
||||
backupName := fmt.Sprintf("%02d_%s", i, path.Base(filePath))
|
||||
backupPath := path.Join(composeCtx.tmpDir, "compose_files", backupName)
|
||||
content, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := composeCtx.fileOp.SaveFile(backupPath, string(content), fs.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
relativePath := path.Base(filePath)
|
||||
if composeCtx.composeDir != "" {
|
||||
rel, relErr := filepath.Rel(composeCtx.composeDir, filePath)
|
||||
if relErr == nil {
|
||||
rel = filepath.ToSlash(rel)
|
||||
if rel != "" && rel != "." && !strings.HasPrefix(rel, "../") {
|
||||
relativePath = rel
|
||||
}
|
||||
}
|
||||
}
|
||||
composeCtx.meta.Files = append(composeCtx.meta.Files, composeBackupFile{
|
||||
OriginalPath: filePath,
|
||||
FileName: path.Base(filePath),
|
||||
RelativePath: relativePath,
|
||||
BackupPath: path.Join("compose_files", backupName),
|
||||
})
|
||||
}
|
||||
if len(composeCtx.composeFiles) != 0 {
|
||||
envPath := path.Join(path.Dir(composeCtx.composeFiles[0]), ".env")
|
||||
if composeCtx.fileOp.Stat(envPath) {
|
||||
envContent, err := os.ReadFile(envPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := composeCtx.fileOp.SaveFile(path.Join(composeCtx.tmpDir, "compose_files", ".env"), string(envContent), fs.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func stepWriteComposeBackupMeta(composeCtx *composeBackupContext) error {
|
||||
metaBytes, err := json.MarshalIndent(composeCtx.meta, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return composeCtx.fileOp.SaveFile(path.Join(composeCtx.tmpDir, "compose_meta.json"), string(metaBytes), fs.ModePerm)
|
||||
}
|
||||
|
||||
func stepPackComposeBackup(composeCtx *composeBackupContext) error {
|
||||
return composeCtx.fileOp.TarGzCompressPro(true, composeCtx.tmpDir, composeCtx.filePath, composeCtx.req.Secret, "")
|
||||
}
|
||||
|
||||
func newComposeRecoverContext(req dto.CommonRecover) (*composeRecoverContext, error) {
|
||||
tmpDir := path.Join(path.Dir(req.File), strings.TrimSuffix(path.Base(req.File), ".tar.gz"))
|
||||
ctx := &composeRecoverContext{
|
||||
req: req,
|
||||
fileOp: files.NewFileOp(),
|
||||
tmpDir: tmpDir,
|
||||
meta: composeBackupMeta{
|
||||
Files: make([]composeBackupFile, 0),
|
||||
Containers: make([]string, 0),
|
||||
},
|
||||
}
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func (c *composeRecoverContext) close() {
|
||||
if c.tmpDir != "" {
|
||||
_ = os.RemoveAll(c.tmpDir)
|
||||
}
|
||||
}
|
||||
|
||||
func stepPrepareComposeRecover(recoverCtx *composeRecoverContext) error {
|
||||
if !recoverCtx.fileOp.Stat(recoverCtx.req.File) {
|
||||
return buserr.WithName("ErrFileNotFound", recoverCtx.req.File)
|
||||
}
|
||||
_ = os.RemoveAll(recoverCtx.tmpDir)
|
||||
return nil
|
||||
}
|
||||
|
||||
func stepExtractComposeRecover(recoverCtx *composeRecoverContext) error {
|
||||
return recoverCtx.fileOp.TarGzExtractPro(recoverCtx.req.File, path.Dir(recoverCtx.req.File), recoverCtx.req.Secret)
|
||||
}
|
||||
|
||||
func stepLoadComposeRecoverMeta(recoverCtx *composeRecoverContext) error {
|
||||
metaPath := path.Join(recoverCtx.tmpDir, "compose_meta.json")
|
||||
if !recoverCtx.fileOp.Stat(metaPath) {
|
||||
return fmt.Errorf("compose_meta.json not found in backup file")
|
||||
}
|
||||
metaBytes, err := os.ReadFile(metaPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := json.Unmarshal(metaBytes, &recoverCtx.meta); err != nil {
|
||||
return fmt.Errorf("unmarshal compose_meta.json failed, err: %v", err)
|
||||
}
|
||||
recoverCtx.composeName = strings.TrimSpace(recoverCtx.req.Name)
|
||||
if recoverCtx.composeName == "" {
|
||||
recoverCtx.composeName = strings.TrimSpace(recoverCtx.meta.ComposeName)
|
||||
}
|
||||
if recoverCtx.composeName == "" {
|
||||
return fmt.Errorf("compose name not found in recover request or backup file")
|
||||
}
|
||||
recoverCtx.targetDir = resolveComposeRecoverTargetDir(recoverCtx.meta, recoverCtx.composeName)
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveComposeRecoverTargetDir(meta composeBackupMeta, composeName string) string {
|
||||
composePath := strings.TrimSpace(meta.ComposePath)
|
||||
if composePath != "" {
|
||||
items := strings.Split(composePath, ",")
|
||||
for _, item := range items {
|
||||
p := strings.TrimSpace(item)
|
||||
if p == "" {
|
||||
continue
|
||||
}
|
||||
ext := strings.ToLower(path.Ext(p))
|
||||
if ext == ".yml" || ext == ".yaml" {
|
||||
return path.Dir(p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
}
|
||||
return path.Join(global.Dir.DataDir, "docker/compose", composeName)
|
||||
}
|
||||
|
||||
func safeJoinWithinBase(baseDir, name string) (string, error) {
|
||||
base := filepath.Clean(baseDir)
|
||||
candidate := strings.TrimSpace(name)
|
||||
candidate = strings.ReplaceAll(candidate, "\\", "/")
|
||||
candidate = filepath.Clean(filepath.FromSlash(candidate))
|
||||
if candidate == "" || candidate == "." {
|
||||
return "", fmt.Errorf("invalid path: empty")
|
||||
}
|
||||
if filepath.IsAbs(candidate) {
|
||||
return "", fmt.Errorf("invalid path %q: absolute path is not allowed", name)
|
||||
}
|
||||
if candidate == ".." || strings.HasPrefix(candidate, ".."+string(filepath.Separator)) {
|
||||
return "", fmt.Errorf("invalid path %q: path escapes base directory", name)
|
||||
}
|
||||
resolved := filepath.Clean(filepath.Join(base, candidate))
|
||||
rel, err := filepath.Rel(base, resolved)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("resolve path %q failed, err: %v", name, err)
|
||||
}
|
||||
if rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
|
||||
return "", fmt.Errorf("invalid path %q: path escapes base directory", name)
|
||||
}
|
||||
return resolved, nil
|
||||
}
|
||||
|
||||
func stepRestoreComposeFiles(recoverCtx *composeRecoverContext) error {
|
||||
if recoverCtx.targetDir != "" {
|
||||
_ = os.RemoveAll(recoverCtx.targetDir)
|
||||
}
|
||||
if err := os.MkdirAll(recoverCtx.targetDir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
restored := make([]string, 0, len(recoverCtx.meta.Files))
|
||||
for _, item := range recoverCtx.meta.Files {
|
||||
backupPath, err := safeJoinWithinBase(recoverCtx.tmpDir, item.BackupPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid compose backup path %q, err: %v", item.BackupPath, err)
|
||||
}
|
||||
if !recoverCtx.fileOp.Stat(backupPath) {
|
||||
continue
|
||||
}
|
||||
targetName := item.FileName
|
||||
if item.RelativePath != "" {
|
||||
targetName = item.RelativePath
|
||||
}
|
||||
if targetName == "" {
|
||||
targetName = path.Base(item.OriginalPath)
|
||||
}
|
||||
if targetName == "" {
|
||||
targetName = "docker-compose.yml"
|
||||
}
|
||||
targetPath, err := safeJoinWithinBase(recoverCtx.targetDir, targetName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid compose target path %q, err: %v", targetName, err)
|
||||
}
|
||||
if err := os.MkdirAll(path.Dir(targetPath), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
content, err := os.ReadFile(backupPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := recoverCtx.fileOp.SaveFile(targetPath, string(content), fs.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
restored = append(restored, targetPath)
|
||||
}
|
||||
envPath := path.Join(recoverCtx.tmpDir, "compose_files", ".env")
|
||||
if recoverCtx.fileOp.Stat(envPath) {
|
||||
envContent, err := os.ReadFile(envPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := recoverCtx.fileOp.SaveFile(path.Join(recoverCtx.targetDir, ".env"), string(envContent), fs.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(restored) == 0 {
|
||||
defaultPath := path.Join(recoverCtx.targetDir, "docker-compose.yml")
|
||||
if !recoverCtx.fileOp.Stat(defaultPath) {
|
||||
return fmt.Errorf("compose file not found in backup data")
|
||||
}
|
||||
restored = append(restored, defaultPath)
|
||||
}
|
||||
sort.Strings(restored)
|
||||
recoverCtx.composePath = strings.Join(restored, ",")
|
||||
return nil
|
||||
}
|
||||
|
||||
func stepSaveComposeRecord(recoverCtx *composeRecoverContext) error {
|
||||
if recoverCtx.composePath == "" {
|
||||
recoverCtx.composePath = path.Join(recoverCtx.targetDir, "docker-compose.yml")
|
||||
}
|
||||
recordName := strings.ToLower(recoverCtx.composeName)
|
||||
record, _ := composeRepo.GetRecord(repo.WithByName(recordName))
|
||||
if record.ID == 0 {
|
||||
return composeRepo.CreateRecord(&model.Compose{Name: recordName, Path: recoverCtx.composePath})
|
||||
}
|
||||
return composeRepo.UpdateRecord(recordName, map[string]interface{}{"path": recoverCtx.composePath})
|
||||
}
|
||||
|
||||
func sanitizeComposeFileName(in string) string {
|
||||
name := strings.TrimSpace(in)
|
||||
name = strings.ReplaceAll(name, "/", "_")
|
||||
name = strings.ReplaceAll(name, ":", "_")
|
||||
if name == "" {
|
||||
return "container"
|
||||
}
|
||||
return name
|
||||
}
|
||||
@@ -0,0 +1,914 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
dockerUtils "github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
"github.com/docker/docker/client"
|
||||
)
|
||||
|
||||
type containerBackupMeta struct {
|
||||
ContainerName string `json:"containerName"`
|
||||
ContainerID string `json:"containerID"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
Image string `json:"image"`
|
||||
HostConfig *container.HostConfig `json:"hostConfig,omitempty"`
|
||||
Config *container.Config `json:"config,omitempty"`
|
||||
Mounts []containerMountBackup `json:"mounts"`
|
||||
}
|
||||
|
||||
type containerMountBackup struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Source string `json:"source,omitempty"`
|
||||
Destination string `json:"destination"`
|
||||
Mode string `json:"mode,omitempty"`
|
||||
RW bool `json:"rw"`
|
||||
Propagation string `json:"propagation,omitempty"`
|
||||
BackupPath string `json:"backupPath,omitempty"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
type containerBackupContext struct {
|
||||
containerName string
|
||||
backupDir string
|
||||
fileName string
|
||||
secret string
|
||||
filePath string
|
||||
tmpDir string
|
||||
mountRoot string
|
||||
wasRunning bool
|
||||
stopped bool
|
||||
fileOp files.FileOp
|
||||
inspectInfo container.InspectResponse
|
||||
meta containerBackupMeta
|
||||
}
|
||||
|
||||
type containerRecoverContext struct {
|
||||
req dto.CommonRecover
|
||||
targetName string
|
||||
fileOp files.FileOp
|
||||
client *client.Client
|
||||
tmpDir string
|
||||
meta containerBackupMeta
|
||||
inspectInfo container.InspectResponse
|
||||
shouldStart bool
|
||||
createdContainerID string
|
||||
}
|
||||
|
||||
func (u *BackupService) ContainerBackup(req dto.CommonBackup) error {
|
||||
timeNow := time.Now().Format(constant.DateTimeSlimLayout) + common.RandStrAndNum(5)
|
||||
fileName := req.FileName
|
||||
if fileName == "" {
|
||||
fileName = fmt.Sprintf("%s_%s.tar.gz", req.Name, timeNow)
|
||||
}
|
||||
if !strings.HasSuffix(fileName, ".tar.gz") {
|
||||
fileName += ".tar.gz"
|
||||
}
|
||||
itemDir := fmt.Sprintf("container/%s", req.Name)
|
||||
backupDir := path.Join(global.Dir.LocalBackupDir, itemDir)
|
||||
record := &model.BackupRecord{
|
||||
Type: req.Type,
|
||||
Name: req.Name,
|
||||
SourceAccountIDs: "1",
|
||||
DownloadAccountID: 1,
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
TaskID: req.TaskID,
|
||||
Status: constant.StatusWaiting,
|
||||
Description: req.Description,
|
||||
}
|
||||
if err := backupRepo.CreateRecord(record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
if err := handleContainerBackup(req.Name, nil, record.ID, backupDir, fileName, req.TaskID, req.Secret, req.StopBefore); err != nil {
|
||||
backupRepo.UpdateRecordByMap(record.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *BackupService) ContainerRecover(req dto.CommonRecover) error {
|
||||
return handleContainerRecover(req, nil)
|
||||
}
|
||||
|
||||
func handleContainerBackup(containerName string, parentTask *task.Task, recordID uint, backupDir, fileName, taskID, secret string, stopBefore bool) error {
|
||||
var (
|
||||
err error
|
||||
backupTask *task.Task
|
||||
)
|
||||
backupCtx, err := newContainerBackupContext(containerName, backupDir, fileName, secret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
backupTask = parentTask
|
||||
if backupTask == nil {
|
||||
backupTask, err = task.NewTaskWithOps(containerName, task.TaskBackup, task.TaskScopeBackup, taskID, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if stopBefore {
|
||||
backupTask.AddSubTaskWithOps(i18n.GetMsgByKey("ContainerBackupStop"), func(t *task.Task) error {
|
||||
return stepStopContainerForBackup(backupCtx)
|
||||
}, func(t *task.Task) {
|
||||
_ = stepStartContainerAfterBackup(backupCtx)
|
||||
}, 3, time.Hour)
|
||||
}
|
||||
backupTask.AddSubTaskWithOps(i18n.GetMsgByKey("ContainerBackupPrepare"), func(t *task.Task) error {
|
||||
t.Logf("------------------ %s ------------------", containerName)
|
||||
return stepPrepareContainerBackup(backupCtx)
|
||||
}, nil, 3, time.Hour)
|
||||
backupTask.AddSubTaskWithOps(i18n.GetMsgByKey("ContainerBackupInspect"), func(t *task.Task) error { return stepBackupContainerInspect(backupCtx) }, nil, 3, time.Hour)
|
||||
backupTask.AddSubTaskWithOps(i18n.GetMsgByKey("ContainerBackupMounts"), func(t *task.Task) error { return stepBackupContainerMounts(backupCtx) }, nil, 3, time.Hour)
|
||||
backupTask.AddSubTaskWithOps(i18n.GetMsgByKey("ContainerBackupMeta"), func(t *task.Task) error { return stepWriteContainerMeta(backupCtx) }, nil, 3, time.Hour)
|
||||
backupTask.AddSubTaskWithOps(task.GetTaskName(containerName, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error { return stepPackContainerBackup(backupCtx) }, nil, 3, time.Hour)
|
||||
if stopBefore {
|
||||
backupTask.AddSubTaskWithOps(i18n.GetMsgByKey("ContainerBackupStart"), func(t *task.Task) error {
|
||||
return stepStartContainerAfterBackup(backupCtx)
|
||||
}, nil, 3, time.Hour)
|
||||
}
|
||||
if parentTask != nil {
|
||||
return nil
|
||||
}
|
||||
go func() {
|
||||
defer backupCtx.close()
|
||||
if err := backupTask.Execute(); err != nil {
|
||||
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
|
||||
return
|
||||
}
|
||||
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusSuccess})
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleContainerRecover(req dto.CommonRecover, parentTask *task.Task) error {
|
||||
var (
|
||||
err error
|
||||
recoverTask *task.Task
|
||||
recoverCtx *containerRecoverContext
|
||||
)
|
||||
recoverTask = parentTask
|
||||
if recoverTask == nil {
|
||||
if isImportRecover(req) {
|
||||
taskName := i18n.GetMsgByKey("TaskImport") + i18n.GetMsgByKey("Container")
|
||||
recoverTask, err = task.NewTask(taskName, task.TaskImport, task.TaskScopeBackup, req.TaskID, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
recoverTask, err = task.NewTaskWithOps("container", task.TaskRecover, task.TaskScopeBackup, req.TaskID, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
timeout := loadRecoverTimeout(req.Timeout)
|
||||
logName := strings.TrimSpace(req.Name)
|
||||
if logName == "" && req.File != "" {
|
||||
logName = strings.TrimSuffix(path.Base(req.File), ".tar.gz")
|
||||
}
|
||||
if logName == "" {
|
||||
logName = "container"
|
||||
}
|
||||
recoverTask.AddSubTaskWithOps(i18n.GetMsgByKey("ContainerRecoverPrepare"), func(t *task.Task) error {
|
||||
ctx, err := newContainerRecoverContext(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
recoverCtx = ctx
|
||||
t.Logf("------------------ %s ------------------", logName)
|
||||
if err := stepPrepareContainerRecover(recoverCtx); err != nil {
|
||||
recoverCtx.close()
|
||||
recoverCtx = nil
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, func(t *task.Task) {
|
||||
if recoverCtx != nil {
|
||||
recoverCtx.close()
|
||||
recoverCtx = nil
|
||||
}
|
||||
}, 3, timeout)
|
||||
recoverTask.AddSubTaskWithOps(i18n.GetMsgByKey("ContainerRecoverExtract"), func(t *task.Task) error { return stepExtractContainerRecover(recoverCtx) }, nil, 3, timeout)
|
||||
recoverTask.AddSubTaskWithOps(i18n.GetMsgByKey("ContainerRecoverParse"), func(t *task.Task) error { return stepLoadContainerRecoverData(recoverCtx) }, nil, 3, timeout)
|
||||
recoverTask.AddSubTaskWithOps(i18n.GetMsgByKey("ContainerRecoverCreate"), func(t *task.Task) error { return stepRecreateContainer(recoverCtx, t) }, nil, 3, timeout)
|
||||
recoverTask.AddSubTaskWithOps(i18n.GetMsgByKey("ContainerRecoverMounts"), func(t *task.Task) error { return stepRestoreContainerMounts(recoverCtx) }, nil, 3, timeout)
|
||||
recoverTask.AddSubTaskWithOps(i18n.GetMsgByKey("ContainerRecoverStart"), func(t *task.Task) error { return stepStartRecoveredContainer(recoverCtx) }, nil, 3, timeout)
|
||||
recoverTask.AddSubTaskWithOps(i18n.GetMsgByKey("ContainerRecoverCleanup"), func(t *task.Task) error {
|
||||
if recoverCtx != nil {
|
||||
recoverCtx.close()
|
||||
recoverCtx = nil
|
||||
}
|
||||
return nil
|
||||
}, nil, 0, timeout)
|
||||
if parentTask != nil {
|
||||
return nil
|
||||
}
|
||||
go func() {
|
||||
_ = recoverTask.Execute()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadRecoverTimeout(timeout int) time.Duration {
|
||||
switch timeout {
|
||||
case -1:
|
||||
return 0
|
||||
case 0:
|
||||
return 3 * time.Hour
|
||||
default:
|
||||
return time.Duration(timeout) * time.Second
|
||||
}
|
||||
}
|
||||
|
||||
func isImportRecover(req dto.CommonRecover) bool {
|
||||
return req.BackupRecordID == 0
|
||||
}
|
||||
|
||||
func newContainerBackupContext(containerName, backupDir, fileName, secret string) (*containerBackupContext, error) {
|
||||
dockerClient, err := dockerUtils.NewDockerClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
_ = dockerClient.Close()
|
||||
}()
|
||||
inspectInfo, err := dockerClient.ContainerInspect(context.Background(), containerName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
filePath := path.Join(backupDir, fileName)
|
||||
tmpDir := path.Join(path.Dir(filePath), strings.TrimSuffix(path.Base(filePath), ".tar.gz"))
|
||||
backupCtx := &containerBackupContext{
|
||||
containerName: containerName,
|
||||
backupDir: backupDir,
|
||||
fileName: fileName,
|
||||
secret: secret,
|
||||
filePath: filePath,
|
||||
tmpDir: tmpDir,
|
||||
mountRoot: path.Join(tmpDir, "mounts"),
|
||||
wasRunning: inspectInfo.State != nil && inspectInfo.State.Running,
|
||||
fileOp: files.NewFileOp(),
|
||||
inspectInfo: inspectInfo,
|
||||
meta: containerBackupMeta{
|
||||
ContainerName: containerName,
|
||||
ContainerID: inspectInfo.ID,
|
||||
CreatedAt: time.Now().Format(constant.DateTimeLayout),
|
||||
Image: inspectInfo.Config.Image,
|
||||
HostConfig: inspectInfo.HostConfig,
|
||||
Config: inspectInfo.Config,
|
||||
Mounts: make([]containerMountBackup, 0),
|
||||
},
|
||||
}
|
||||
return backupCtx, nil
|
||||
}
|
||||
|
||||
func newContainerRecoverContext(req dto.CommonRecover) (*containerRecoverContext, error) {
|
||||
dockerClient, err := dockerUtils.NewDockerClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tmpDir := path.Join(path.Dir(req.File), strings.TrimSuffix(path.Base(req.File), ".tar.gz"))
|
||||
ctx := &containerRecoverContext{
|
||||
req: req,
|
||||
targetName: req.Name,
|
||||
fileOp: files.NewFileOp(),
|
||||
client: dockerClient,
|
||||
tmpDir: tmpDir,
|
||||
meta: containerBackupMeta{
|
||||
Mounts: make([]containerMountBackup, 0),
|
||||
},
|
||||
}
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func (c *containerBackupContext) close() {
|
||||
if c.tmpDir != "" {
|
||||
_ = os.RemoveAll(c.tmpDir)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *containerRecoverContext) close() {
|
||||
if c.client != nil {
|
||||
_ = c.client.Close()
|
||||
}
|
||||
if c.tmpDir != "" {
|
||||
_ = os.RemoveAll(c.tmpDir)
|
||||
}
|
||||
}
|
||||
|
||||
func stepPrepareContainerBackup(backupCtx *containerBackupContext) error {
|
||||
if err := os.MkdirAll(backupCtx.backupDir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("mkdir %s failed, err: %v", backupCtx.backupDir, err)
|
||||
}
|
||||
_ = os.RemoveAll(backupCtx.tmpDir)
|
||||
if err := os.MkdirAll(backupCtx.mountRoot, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func stepStopContainerForBackup(backupCtx *containerBackupContext) error {
|
||||
if !backupCtx.wasRunning || backupCtx.stopped {
|
||||
return nil
|
||||
}
|
||||
dockerClient, err := dockerUtils.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = dockerClient.Close()
|
||||
}()
|
||||
if err := dockerClient.ContainerStop(context.Background(), backupCtx.inspectInfo.ID, container.StopOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
backupCtx.stopped = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func stepStartContainerAfterBackup(backupCtx *containerBackupContext) error {
|
||||
if !backupCtx.stopped {
|
||||
return nil
|
||||
}
|
||||
dockerClient, err := dockerUtils.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = dockerClient.Close()
|
||||
}()
|
||||
if err := dockerClient.ContainerStart(context.Background(), backupCtx.inspectInfo.ID, container.StartOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
backupCtx.stopped = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func stepBackupContainerInspect(backupCtx *containerBackupContext) error {
|
||||
inspectBytes, err := json.MarshalIndent(backupCtx.inspectInfo, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := backupCtx.fileOp.SaveFile(path.Join(backupCtx.tmpDir, "inspect.json"), string(inspectBytes), fs.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
if backupCtx.inspectInfo.NetworkSettings != nil {
|
||||
networkBytes, err := json.MarshalIndent(backupCtx.inspectInfo.NetworkSettings, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := backupCtx.fileOp.SaveFile(path.Join(backupCtx.tmpDir, "network.json"), string(networkBytes), fs.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func stepBackupContainerMounts(backupCtx *containerBackupContext) error {
|
||||
var (
|
||||
dockerClient *client.Client
|
||||
clientErr error
|
||||
)
|
||||
ensureClient := func() (*client.Client, error) {
|
||||
if dockerClient != nil || clientErr != nil {
|
||||
return dockerClient, clientErr
|
||||
}
|
||||
dockerClient, clientErr = dockerUtils.NewDockerClient()
|
||||
return dockerClient, clientErr
|
||||
}
|
||||
defer func() {
|
||||
if dockerClient != nil {
|
||||
_ = dockerClient.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
for i, item := range backupCtx.inspectInfo.Mounts {
|
||||
mountMeta := containerMountBackup{
|
||||
Type: string(item.Type),
|
||||
Name: item.Name,
|
||||
Source: item.Source,
|
||||
Destination: item.Destination,
|
||||
Mode: item.Mode,
|
||||
RW: item.RW,
|
||||
Propagation: string(item.Propagation),
|
||||
Status: "skipped",
|
||||
}
|
||||
|
||||
mountDirName := fmt.Sprintf("%02d_%s", i, sanitizeContainerMountName(item.Destination))
|
||||
mountDir := path.Join(backupCtx.mountRoot, mountDirName)
|
||||
mountMeta.BackupPath = path.Join("mounts", mountDirName, "data")
|
||||
|
||||
switch item.Type {
|
||||
case mount.TypeBind, mount.TypeVolume:
|
||||
if item.Source == "" {
|
||||
mountMeta.Message = "empty source"
|
||||
backupCtx.meta.Mounts = append(backupCtx.meta.Mounts, mountMeta)
|
||||
continue
|
||||
}
|
||||
sourceInfo, statErr := os.Stat(item.Source)
|
||||
if statErr != nil {
|
||||
mountMeta.Message = statErr.Error()
|
||||
backupCtx.meta.Mounts = append(backupCtx.meta.Mounts, mountMeta)
|
||||
continue
|
||||
}
|
||||
dataDir := path.Join(mountDir, "data")
|
||||
if err := os.MkdirAll(dataDir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
if sourceInfo.IsDir() {
|
||||
if err := backupCtx.fileOp.CopyDirWithNewName(item.Source, dataDir, "."); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := backupCtx.fileOp.CopyFile(item.Source, dataDir); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if item.Type == mount.TypeVolume && item.Name != "" {
|
||||
cli, err := ensureClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
volumeInfo, volumeErr := cli.VolumeInspect(context.Background(), item.Name)
|
||||
if volumeErr == nil {
|
||||
volumeBytes, volumeMarshalErr := json.MarshalIndent(volumeInfo, "", " ")
|
||||
if volumeMarshalErr == nil {
|
||||
_ = backupCtx.fileOp.SaveFile(path.Join(mountDir, "volume.json"), string(volumeBytes), fs.ModePerm)
|
||||
}
|
||||
}
|
||||
}
|
||||
mountMeta.Status = "backed_up"
|
||||
default:
|
||||
mountMeta.Message = "mount type not supported for data backup"
|
||||
}
|
||||
backupCtx.meta.Mounts = append(backupCtx.meta.Mounts, mountMeta)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func stepWriteContainerMeta(backupCtx *containerBackupContext) error {
|
||||
metaBytes, err := json.MarshalIndent(backupCtx.meta, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := backupCtx.fileOp.SaveFile(path.Join(backupCtx.tmpDir, "meta.json"), string(metaBytes), fs.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func stepPackContainerBackup(backupCtx *containerBackupContext) error {
|
||||
if err := backupCtx.fileOp.TarGzCompressPro(true, backupCtx.tmpDir, backupCtx.filePath, backupCtx.secret, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func stepPrepareContainerRecover(recoverCtx *containerRecoverContext) error {
|
||||
if !recoverCtx.fileOp.Stat(recoverCtx.req.File) {
|
||||
return buserr.WithName("ErrFileNotFound", recoverCtx.req.File)
|
||||
}
|
||||
_ = os.RemoveAll(recoverCtx.tmpDir)
|
||||
return nil
|
||||
}
|
||||
|
||||
func stepExtractContainerRecover(recoverCtx *containerRecoverContext) error {
|
||||
return recoverCtx.fileOp.TarGzExtractPro(recoverCtx.req.File, path.Dir(recoverCtx.req.File), recoverCtx.req.Secret)
|
||||
}
|
||||
|
||||
func stepLoadContainerRecoverData(recoverCtx *containerRecoverContext) error {
|
||||
if err := loadContainerRecoverMeta(recoverCtx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := loadContainerRecoverInspect(recoverCtx); err != nil {
|
||||
return err
|
||||
}
|
||||
if recoverCtx.targetName == "" {
|
||||
recoverCtx.targetName = strings.TrimPrefix(recoverCtx.inspectInfo.Name, "/")
|
||||
}
|
||||
if recoverCtx.targetName == "" {
|
||||
recoverCtx.targetName = recoverCtx.meta.ContainerName
|
||||
}
|
||||
if recoverCtx.targetName == "" {
|
||||
return fmt.Errorf("container name not found in recover request or backup file")
|
||||
}
|
||||
if recoverCtx.inspectInfo.Config == nil {
|
||||
recoverCtx.inspectInfo.Config = recoverCtx.meta.Config
|
||||
}
|
||||
if recoverCtx.inspectInfo.HostConfig == nil {
|
||||
recoverCtx.inspectInfo.HostConfig = recoverCtx.meta.HostConfig
|
||||
}
|
||||
if recoverCtx.inspectInfo.Config == nil {
|
||||
return fmt.Errorf("container config not found in backup file")
|
||||
}
|
||||
if recoverCtx.inspectInfo.HostConfig == nil {
|
||||
recoverCtx.inspectInfo.HostConfig = &container.HostConfig{}
|
||||
}
|
||||
recoverCtx.shouldStart = recoverCtx.inspectInfo.State != nil && recoverCtx.inspectInfo.State.Running
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadContainerRecoverMeta(recoverCtx *containerRecoverContext) error {
|
||||
metaPath := path.Join(recoverCtx.tmpDir, "meta.json")
|
||||
if !recoverCtx.fileOp.Stat(metaPath) {
|
||||
return nil
|
||||
}
|
||||
metaBytes, err := os.ReadFile(metaPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := json.Unmarshal(metaBytes, &recoverCtx.meta); err != nil {
|
||||
return fmt.Errorf("unmarshal meta.json failed, err: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadContainerRecoverInspect(recoverCtx *containerRecoverContext) error {
|
||||
inspectPath := path.Join(recoverCtx.tmpDir, "inspect.json")
|
||||
if !recoverCtx.fileOp.Stat(inspectPath) {
|
||||
return fmt.Errorf("inspect.json not found in backup file")
|
||||
}
|
||||
inspectBytes, err := os.ReadFile(inspectPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := json.Unmarshal(inspectBytes, &recoverCtx.inspectInfo); err != nil {
|
||||
return fmt.Errorf("unmarshal inspect.json failed, err: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func stepRecreateContainer(recoverCtx *containerRecoverContext, taskItem *task.Task) error {
|
||||
ctx := context.Background()
|
||||
if err := ensureContainerRecoverNetworks(recoverCtx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ensureContainerRecoverVolumes(recoverCtx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config := cloneContainerConfig(recoverCtx.inspectInfo.Config)
|
||||
hostConfig := cloneContainerHostConfig(recoverCtx.inspectInfo.HostConfig)
|
||||
if config.Image == "" {
|
||||
config.Image = recoverCtx.meta.Image
|
||||
}
|
||||
if config.Image == "" {
|
||||
return fmt.Errorf("container image not found in backup file")
|
||||
}
|
||||
if !checkImageExist(recoverCtx.client, config.Image) {
|
||||
if err := pullImages(taskItem, recoverCtx.client, config.Image); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := recoverCtx.client.ContainerInspect(ctx, recoverCtx.targetName); err == nil {
|
||||
if err := recoverCtx.client.ContainerRemove(ctx, recoverCtx.targetName, container.RemoveOptions{Force: true, RemoveVolumes: false}); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if !client.IsErrNotFound(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
networkConfig, extraNetworks := buildContainerRecoverNetworkConfig(recoverCtx.inspectInfo.NetworkSettings, hostConfig)
|
||||
removeBridgeDriverIPAM(recoverCtx.client, networkConfig, extraNetworks)
|
||||
createRes, err := recoverCtx.client.ContainerCreate(ctx, config, hostConfig, networkConfig, nil, recoverCtx.targetName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
recoverCtx.createdContainerID = createRes.ID
|
||||
|
||||
extraNames := make([]string, 0, len(extraNetworks))
|
||||
for name := range extraNetworks {
|
||||
extraNames = append(extraNames, name)
|
||||
}
|
||||
sort.Strings(extraNames)
|
||||
for _, item := range extraNames {
|
||||
if err := recoverCtx.client.NetworkConnect(ctx, item, recoverCtx.createdContainerID, extraNetworks[item]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeBridgeDriverIPAM(cli *client.Client, primary *network.NetworkingConfig, extras map[string]*network.EndpointSettings) {
|
||||
if primary != nil {
|
||||
removeBridgeDriverIPAMFromEndpoints(cli, primary.EndpointsConfig)
|
||||
}
|
||||
removeBridgeDriverIPAMFromEndpoints(cli, extras)
|
||||
}
|
||||
|
||||
func removeBridgeDriverIPAMFromEndpoints(cli *client.Client, endpoints map[string]*network.EndpointSettings) {
|
||||
for netName, endpoint := range endpoints {
|
||||
if endpoint == nil || endpoint.IPAMConfig == nil {
|
||||
continue
|
||||
}
|
||||
info, err := cli.NetworkInspect(context.Background(), netName, network.InspectOptions{})
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if info.Driver == "bridge" {
|
||||
endpoint.IPAMConfig = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ensureContainerRecoverNetworks(recoverCtx *containerRecoverContext) error {
|
||||
if recoverCtx.inspectInfo.NetworkSettings == nil {
|
||||
return nil
|
||||
}
|
||||
for netName := range recoverCtx.inspectInfo.NetworkSettings.Networks {
|
||||
if netName == "" || netName == "bridge" || netName == "host" || netName == "none" {
|
||||
continue
|
||||
}
|
||||
if _, err := recoverCtx.client.NetworkInspect(context.Background(), netName, network.InspectOptions{}); err != nil {
|
||||
if !client.IsErrNotFound(err) {
|
||||
return err
|
||||
}
|
||||
if _, err := recoverCtx.client.NetworkCreate(context.Background(), netName, network.CreateOptions{Driver: "bridge"}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ensureContainerRecoverVolumes(recoverCtx *containerRecoverContext) error {
|
||||
for _, item := range recoverCtx.meta.Mounts {
|
||||
if item.Type != string(mount.TypeVolume) || item.Name == "" {
|
||||
continue
|
||||
}
|
||||
if _, err := recoverCtx.client.VolumeInspect(context.Background(), item.Name); err == nil {
|
||||
continue
|
||||
} else if !client.IsErrNotFound(err) {
|
||||
return err
|
||||
}
|
||||
createOptions := volume.CreateOptions{Name: item.Name}
|
||||
if item.BackupPath != "" {
|
||||
volumeMetaPath := path.Join(recoverCtx.tmpDir, path.Dir(item.BackupPath), "volume.json")
|
||||
if recoverCtx.fileOp.Stat(volumeMetaPath) {
|
||||
volumeBytes, readErr := os.ReadFile(volumeMetaPath)
|
||||
if readErr != nil {
|
||||
return readErr
|
||||
}
|
||||
var volumeInfo volume.Volume
|
||||
if unmarshalErr := json.Unmarshal(volumeBytes, &volumeInfo); unmarshalErr != nil {
|
||||
return unmarshalErr
|
||||
}
|
||||
if volumeInfo.Driver != "" {
|
||||
createOptions.Driver = volumeInfo.Driver
|
||||
}
|
||||
if len(volumeInfo.Options) != 0 {
|
||||
createOptions.DriverOpts = volumeInfo.Options
|
||||
}
|
||||
if len(volumeInfo.Labels) != 0 {
|
||||
createOptions.Labels = volumeInfo.Labels
|
||||
}
|
||||
}
|
||||
}
|
||||
if _, err := recoverCtx.client.VolumeCreate(context.Background(), createOptions); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildContainerRecoverNetworkConfig(networkSettings *container.NetworkSettings, hostConfig *container.HostConfig) (*network.NetworkingConfig, map[string]*network.EndpointSettings) {
|
||||
extraNetworks := make(map[string]*network.EndpointSettings)
|
||||
if hostConfig != nil {
|
||||
networkMode := string(hostConfig.NetworkMode)
|
||||
if networkMode == "host" || networkMode == "none" {
|
||||
return nil, extraNetworks
|
||||
}
|
||||
}
|
||||
if networkSettings == nil || len(networkSettings.Networks) == 0 {
|
||||
return nil, extraNetworks
|
||||
}
|
||||
|
||||
primaryName := ""
|
||||
if hostConfig != nil {
|
||||
networkMode := string(hostConfig.NetworkMode)
|
||||
if networkMode != "" && networkMode != "default" && networkMode != "bridge" {
|
||||
if _, ok := networkSettings.Networks[networkMode]; ok {
|
||||
primaryName = networkMode
|
||||
}
|
||||
}
|
||||
}
|
||||
if primaryName == "" {
|
||||
if _, ok := networkSettings.Networks["bridge"]; ok {
|
||||
primaryName = "bridge"
|
||||
} else {
|
||||
names := make([]string, 0, len(networkSettings.Networks))
|
||||
for name := range networkSettings.Networks {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
if len(names) > 0 {
|
||||
primaryName = names[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config := &network.NetworkingConfig{EndpointsConfig: make(map[string]*network.EndpointSettings)}
|
||||
for name, endpoint := range networkSettings.Networks {
|
||||
if name == "host" || name == "none" {
|
||||
continue
|
||||
}
|
||||
endpointSetting := &network.EndpointSettings{Aliases: append([]string(nil), endpoint.Aliases...), MacAddress: endpoint.MacAddress}
|
||||
if endpoint.IPAMConfig != nil {
|
||||
endpointSetting.IPAMConfig = &network.EndpointIPAMConfig{
|
||||
IPv4Address: endpoint.IPAMConfig.IPv4Address,
|
||||
IPv6Address: endpoint.IPAMConfig.IPv6Address,
|
||||
}
|
||||
} else if endpoint.IPAddress != "" || endpoint.GlobalIPv6Address != "" {
|
||||
endpointSetting.IPAMConfig = &network.EndpointIPAMConfig{
|
||||
IPv4Address: endpoint.IPAddress,
|
||||
IPv6Address: endpoint.GlobalIPv6Address,
|
||||
}
|
||||
}
|
||||
if name == primaryName {
|
||||
config.EndpointsConfig[name] = endpointSetting
|
||||
} else {
|
||||
extraNetworks[name] = endpointSetting
|
||||
}
|
||||
}
|
||||
if len(config.EndpointsConfig) == 0 {
|
||||
return nil, extraNetworks
|
||||
}
|
||||
return config, extraNetworks
|
||||
}
|
||||
|
||||
func cloneContainerConfig(config *container.Config) *container.Config {
|
||||
if config == nil {
|
||||
return &container.Config{}
|
||||
}
|
||||
item := *config
|
||||
if len(config.Env) != 0 {
|
||||
item.Env = append([]string(nil), config.Env...)
|
||||
}
|
||||
if len(config.Cmd) != 0 {
|
||||
item.Cmd = append([]string(nil), config.Cmd...)
|
||||
}
|
||||
if len(config.Entrypoint) != 0 {
|
||||
item.Entrypoint = append([]string(nil), config.Entrypoint...)
|
||||
}
|
||||
if len(config.Labels) != 0 {
|
||||
labels := make(map[string]string, len(config.Labels))
|
||||
for key, val := range config.Labels {
|
||||
labels[key] = val
|
||||
}
|
||||
item.Labels = labels
|
||||
}
|
||||
if len(config.Volumes) != 0 {
|
||||
volumes := make(map[string]struct{}, len(config.Volumes))
|
||||
for key, val := range config.Volumes {
|
||||
volumes[key] = val
|
||||
}
|
||||
item.Volumes = volumes
|
||||
}
|
||||
return &item
|
||||
}
|
||||
|
||||
func cloneContainerHostConfig(hostConfig *container.HostConfig) *container.HostConfig {
|
||||
if hostConfig == nil {
|
||||
return &container.HostConfig{}
|
||||
}
|
||||
item := *hostConfig
|
||||
if len(hostConfig.Binds) != 0 {
|
||||
item.Binds = append([]string(nil), hostConfig.Binds...)
|
||||
}
|
||||
if len(hostConfig.DNS) != 0 {
|
||||
item.DNS = append([]string(nil), hostConfig.DNS...)
|
||||
}
|
||||
if len(hostConfig.ExtraHosts) != 0 {
|
||||
item.ExtraHosts = append([]string(nil), hostConfig.ExtraHosts...)
|
||||
}
|
||||
if len(hostConfig.Mounts) != 0 {
|
||||
item.Mounts = append([]mount.Mount(nil), hostConfig.Mounts...)
|
||||
}
|
||||
return &item
|
||||
}
|
||||
|
||||
func stepRestoreContainerMounts(recoverCtx *containerRecoverContext) error {
|
||||
currentContainer := recoverCtx.createdContainerID
|
||||
if currentContainer == "" {
|
||||
currentContainer = recoverCtx.targetName
|
||||
}
|
||||
currentInspect, err := recoverCtx.client.ContainerInspect(context.Background(), currentContainer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentMounts := make(map[string]container.MountPoint, len(currentInspect.Mounts))
|
||||
for _, item := range currentInspect.Mounts {
|
||||
currentMounts[item.Destination] = item
|
||||
}
|
||||
|
||||
for _, item := range recoverCtx.meta.Mounts {
|
||||
if item.Status != "backed_up" || item.BackupPath == "" || !item.RW {
|
||||
continue
|
||||
}
|
||||
backupPath := path.Join(recoverCtx.tmpDir, item.BackupPath)
|
||||
if !recoverCtx.fileOp.Stat(backupPath) {
|
||||
continue
|
||||
}
|
||||
sourcePath := item.Source
|
||||
if currentMount, ok := currentMounts[item.Destination]; ok {
|
||||
if currentMount.Source != "" {
|
||||
sourcePath = currentMount.Source
|
||||
}
|
||||
if item.Type == string(mount.TypeVolume) && item.Name == "" {
|
||||
item.Name = currentMount.Name
|
||||
}
|
||||
}
|
||||
if sourcePath == "" && item.Type == string(mount.TypeVolume) && item.Name != "" {
|
||||
volumeInfo, volumeErr := recoverCtx.client.VolumeInspect(context.Background(), item.Name)
|
||||
if volumeErr != nil {
|
||||
return volumeErr
|
||||
}
|
||||
sourcePath = volumeInfo.Mountpoint
|
||||
}
|
||||
if sourcePath == "" {
|
||||
continue
|
||||
}
|
||||
if err := restoreContainerMountData(recoverCtx.fileOp, backupPath, sourcePath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func restoreContainerMountData(fileOp files.FileOp, backupPath, sourcePath string) error {
|
||||
if sourcePath == "/" {
|
||||
return fmt.Errorf("invalid mount source path /")
|
||||
}
|
||||
entries, err := os.ReadDir(backupPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(entries) == 1 && !entries[0].IsDir() && entries[0].Name() == path.Base(sourcePath) {
|
||||
if err := os.MkdirAll(path.Dir(sourcePath), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = os.RemoveAll(sourcePath)
|
||||
if err := fileOp.CopyFile(path.Join(backupPath, entries[0].Name()), path.Dir(sourcePath)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_ = os.RemoveAll(sourcePath)
|
||||
if err := os.MkdirAll(sourcePath, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fileOp.CopyDirWithNewName(backupPath, sourcePath, "."); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func stepStartRecoveredContainer(recoverCtx *containerRecoverContext) error {
|
||||
if !recoverCtx.shouldStart {
|
||||
return nil
|
||||
}
|
||||
containerID := recoverCtx.createdContainerID
|
||||
if containerID == "" {
|
||||
containerID = recoverCtx.targetName
|
||||
}
|
||||
return recoverCtx.client.ContainerStart(context.Background(), containerID, container.StartOptions{})
|
||||
}
|
||||
|
||||
func sanitizeContainerMountName(in string) string {
|
||||
name := strings.TrimSpace(in)
|
||||
name = strings.Trim(name, "/")
|
||||
name = strings.ReplaceAll(name, "/", "_")
|
||||
name = strings.ReplaceAll(name, ":", "_")
|
||||
if name == "" {
|
||||
return "root"
|
||||
}
|
||||
return name
|
||||
}
|
||||
+478
-40
@@ -1,7 +1,9 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
@@ -11,6 +13,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -40,10 +43,10 @@ import (
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/gin-gonic/gin"
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/shirou/gopsutil/v4/cpu"
|
||||
"github.com/shirou/gopsutil/v4/mem"
|
||||
)
|
||||
@@ -69,7 +72,6 @@ type IContainerService interface {
|
||||
ComposeLogClean(req dto.ComposeLogClean) error
|
||||
|
||||
ContainerCreate(req dto.ContainerOperate, inThread bool) error
|
||||
ContainerCreateByCommand(req dto.ContainerCreateByCommand) error
|
||||
ContainerUpdate(req dto.ContainerOperate) error
|
||||
ContainerUpgrade(req dto.ContainerUpgrade) error
|
||||
ContainerInfo(req dto.OperationWithName) (*dto.ContainerOperate, error)
|
||||
@@ -91,6 +93,12 @@ type IContainerService interface {
|
||||
Prune(req dto.ContainerPrune) error
|
||||
|
||||
LoadUsers(req dto.OperationWithName) []string
|
||||
ListContainerFiles(req dto.ContainerFileReq) ([]dto.ContainerFileInfo, error)
|
||||
UploadContainerFile(req dto.ContainerFileReq, fileName string, fileSize int64, file io.Reader) error
|
||||
GetContainerFileContent(req dto.ContainerFileReq) (*dto.ContainerFileContent, error)
|
||||
GetContainerFileSize(req dto.ContainerFileReq) (int64, error)
|
||||
DeleteContainerFile(req dto.ContainerFileBatchDeleteReq) error
|
||||
DownloadContainerFile(req dto.ContainerFileReq) (io.ReadCloser, string, string, error)
|
||||
|
||||
StreamLogs(ctx *gin.Context, params dto.StreamLog)
|
||||
}
|
||||
@@ -104,7 +112,7 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer client.Close()
|
||||
defer func() { _ = client.Close() }()
|
||||
options := container.ListOptions{All: true}
|
||||
if len(req.Filters) != 0 {
|
||||
options.Filters = filters.NewArgs()
|
||||
@@ -302,42 +310,6 @@ func (u *ContainerService) ContainerListStats() ([]dto.ContainerListStats, error
|
||||
return datas, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerCreateByCommand(req dto.ContainerCreateByCommand) error {
|
||||
if cmd.CheckIllegal(req.Command) {
|
||||
return buserr.New("ErrCmdIllegal")
|
||||
}
|
||||
if !strings.HasPrefix(strings.TrimSpace(req.Command), "docker run ") {
|
||||
return errors.New("error command format")
|
||||
}
|
||||
containerName := ""
|
||||
commands := strings.Split(req.Command, " ")
|
||||
for index, val := range commands {
|
||||
if val == "--name" && len(commands) > index+1 {
|
||||
containerName = commands[index+1]
|
||||
}
|
||||
}
|
||||
if !strings.Contains(req.Command, " -d ") {
|
||||
req.Command = strings.ReplaceAll(req.Command, "docker run", "docker run -d")
|
||||
}
|
||||
if len(containerName) == 0 {
|
||||
containerName = fmt.Sprintf("1Panel-%s-%s", common.RandStr(5), common.RandStrAndNum(4))
|
||||
req.Command += fmt.Sprintf(" --name %s", containerName)
|
||||
}
|
||||
taskItem, err := task.NewTaskWithOps(containerName, task.TaskCreate, task.TaskScopeContainer, req.TaskID, 1)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("new task for create container failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
taskItem.AddSubTask(i18n.GetWithName("ContainerCreate", containerName), func(t *task.Task) error {
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem), cmd.WithTimeout(5*time.Minute))
|
||||
return cmdMgr.RunBashC(req.Command)
|
||||
}, nil)
|
||||
_ = taskItem.Execute()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) Inspect(req dto.InspectReq) (string, error) {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
@@ -621,6 +593,16 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai
|
||||
data.ExposedPorts = loadContainerPortForInfo(exposePorts)
|
||||
data.Hostname = oldContainer.Config.Hostname
|
||||
data.DNS = oldContainer.HostConfig.DNS
|
||||
for _, item := range oldContainer.HostConfig.ExtraHosts {
|
||||
parts := strings.SplitN(item, ":", 2)
|
||||
if len(parts) != 2 || len(parts[0]) == 0 || len(parts[1]) == 0 {
|
||||
continue
|
||||
}
|
||||
data.ExtraHosts = append(data.ExtraHosts, dto.ExtraHost{
|
||||
Hostname: parts[0],
|
||||
IP: parts[1],
|
||||
})
|
||||
}
|
||||
data.DomainName = oldContainer.Config.Domainname
|
||||
|
||||
data.Cmd = oldContainer.Config.Cmd
|
||||
@@ -1181,7 +1163,7 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error
|
||||
|
||||
func (u *ContainerService) LoadUsers(req dto.OperationWithName) []string {
|
||||
var users []string
|
||||
std, err := cmd.RunDefaultWithStdoutBashCf("docker exec %s cat /etc/passwd", req.Name)
|
||||
std, err := cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).RunWithStdout("docker", "exec", req.Name, "cat", "/etc/passwd")
|
||||
if err != nil {
|
||||
return users
|
||||
}
|
||||
@@ -1194,6 +1176,455 @@ func (u *ContainerService) LoadUsers(req dto.OperationWithName) []string {
|
||||
return users
|
||||
}
|
||||
|
||||
func (u *ContainerService) ListContainerFiles(req dto.ContainerFileReq) ([]dto.ContainerFileInfo, error) {
|
||||
if len(req.Path) == 0 {
|
||||
req.Path = "/"
|
||||
}
|
||||
cli, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
stat, err := cli.ContainerStatPath(ctx, req.ContainerID, req.Path)
|
||||
if err != nil {
|
||||
return nil, normalizeContainerFileError(err)
|
||||
}
|
||||
isDir := stat.Mode.IsDir()
|
||||
isLink := stat.Mode&os.ModeSymlink != 0
|
||||
if isLink && !isDir {
|
||||
linkDir, linkErr := isContainerDir(cli, req.ContainerID, req.Path)
|
||||
if linkErr == nil {
|
||||
isDir = linkDir
|
||||
}
|
||||
}
|
||||
if !isDir {
|
||||
return []dto.ContainerFileInfo{toContainerFileInfo(req.Path, stat, isDir)}, nil
|
||||
}
|
||||
|
||||
output, err := runContainerCommand(cli, req.ContainerID, []string{"ls", "-1A", "--", req.Path})
|
||||
if err != nil {
|
||||
return nil, normalizeContainerFileError(err)
|
||||
}
|
||||
lines := strings.Split(strings.TrimSpace(output), "\n")
|
||||
files := make([]dto.ContainerFileInfo, 0, len(lines))
|
||||
for _, line := range lines {
|
||||
name := strings.TrimSpace(line)
|
||||
if len(name) == 0 || name == "." || name == ".." {
|
||||
continue
|
||||
}
|
||||
childPath := req.Path
|
||||
if childPath == "/" {
|
||||
childPath = "/" + name
|
||||
} else {
|
||||
childPath = strings.TrimSuffix(childPath, "/") + "/" + name
|
||||
}
|
||||
childStat, statErr := cli.ContainerStatPath(ctx, req.ContainerID, childPath)
|
||||
if statErr != nil {
|
||||
continue
|
||||
}
|
||||
childIsDir := childStat.Mode.IsDir()
|
||||
if childStat.Mode&os.ModeSymlink != 0 && !childIsDir {
|
||||
linkDir, linkErr := isContainerDir(cli, req.ContainerID, childPath)
|
||||
if linkErr == nil {
|
||||
childIsDir = linkDir
|
||||
}
|
||||
}
|
||||
files = append(files, toContainerFileInfo(childPath, childStat, childIsDir))
|
||||
}
|
||||
sort.Slice(files, func(i, j int) bool {
|
||||
if files[i].IsDir != files[j].IsDir {
|
||||
return files[i].IsDir
|
||||
}
|
||||
return strings.ToLower(files[i].Name) < strings.ToLower(files[j].Name)
|
||||
})
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) DeleteContainerFile(req dto.ContainerFileBatchDeleteReq) error {
|
||||
for _, item := range req.Paths {
|
||||
if strings.TrimSpace(item) == "/" {
|
||||
return buserr.New("ErrPathNotDelete")
|
||||
}
|
||||
}
|
||||
cli, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
command := []string{"rm", "-rf", "--"}
|
||||
command = append(command, req.Paths...)
|
||||
_, err = runContainerCommand(cli, req.ContainerID, command)
|
||||
return err
|
||||
}
|
||||
|
||||
func (u *ContainerService) UploadContainerFile(req dto.ContainerFileReq, fileName string, fileSize int64, file io.Reader) error {
|
||||
if len(req.Path) == 0 {
|
||||
req.Path = "/"
|
||||
}
|
||||
safeName := path.Base(fileName)
|
||||
if safeName == "." || safeName == "/" || len(safeName) == 0 {
|
||||
return buserr.New("ErrInvalidChar")
|
||||
}
|
||||
|
||||
cli, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
stat, err := cli.ContainerStatPath(ctx, req.ContainerID, req.Path)
|
||||
if err != nil {
|
||||
if _, mkErr := runContainerCommand(cli, req.ContainerID, []string{"mkdir", "-p", "--", req.Path}); mkErr != nil {
|
||||
return mkErr
|
||||
}
|
||||
stat, err = cli.ContainerStatPath(ctx, req.ContainerID, req.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !stat.Mode.IsDir() {
|
||||
return fmt.Errorf("path %s is not directory", req.Path)
|
||||
}
|
||||
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
writeErr := make(chan error, 1)
|
||||
go func() {
|
||||
tw := tar.NewWriter(pipeWriter)
|
||||
header := &tar.Header{
|
||||
Name: safeName,
|
||||
Mode: 0644,
|
||||
Size: fileSize,
|
||||
ModTime: time.Now(),
|
||||
}
|
||||
if err := tw.WriteHeader(header); err != nil {
|
||||
_ = tw.Close()
|
||||
_ = pipeWriter.CloseWithError(err)
|
||||
writeErr <- err
|
||||
return
|
||||
}
|
||||
if _, err := io.Copy(tw, file); err != nil {
|
||||
_ = tw.Close()
|
||||
_ = pipeWriter.CloseWithError(err)
|
||||
writeErr <- err
|
||||
return
|
||||
}
|
||||
if err := tw.Close(); err != nil {
|
||||
_ = pipeWriter.CloseWithError(err)
|
||||
writeErr <- err
|
||||
return
|
||||
}
|
||||
_ = pipeWriter.Close()
|
||||
writeErr <- nil
|
||||
}()
|
||||
|
||||
err = cli.CopyToContainer(ctx, req.ContainerID, req.Path, pipeReader, container.CopyToContainerOptions{
|
||||
CopyUIDGID: true,
|
||||
})
|
||||
if err != nil {
|
||||
_ = pipeReader.CloseWithError(err)
|
||||
_ = pipeWriter.CloseWithError(err)
|
||||
<-writeErr
|
||||
return err
|
||||
}
|
||||
if err := <-writeErr; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) GetContainerFileContent(req dto.ContainerFileReq) (*dto.ContainerFileContent, error) {
|
||||
if len(req.Path) == 0 {
|
||||
return nil, buserr.New("ErrInvalidChar")
|
||||
}
|
||||
cli, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
stat, err := cli.ContainerStatPath(context.Background(), req.ContainerID, req.Path)
|
||||
if err != nil {
|
||||
return nil, normalizeContainerFileError(err)
|
||||
}
|
||||
if stat.Mode.IsDir() {
|
||||
return nil, fmt.Errorf("path %s is directory", req.Path)
|
||||
}
|
||||
|
||||
content := &dto.ContainerFileContent{Size: stat.Size}
|
||||
headBytes, err := runContainerCommandRaw(cli, req.ContainerID, []string{"head", "-c", "4096", "--", req.Path})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bytes.IndexByte(headBytes, 0) >= 0 {
|
||||
content.IsBinary = true
|
||||
return content, nil
|
||||
}
|
||||
|
||||
const inlinePreviewMax = 512 * 1024
|
||||
if stat.Size <= inlinePreviewMax {
|
||||
raw, err := runContainerCommandRaw(cli, req.ContainerID, []string{"cat", "--", req.Path})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
content.Content = string(raw)
|
||||
return content, nil
|
||||
}
|
||||
|
||||
raw, err := runContainerCommandRaw(cli, req.ContainerID, []string{"tail", "-n", "300", "--", req.Path})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
content.Content = string(raw)
|
||||
content.Truncated = true
|
||||
return content, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) GetContainerFileSize(req dto.ContainerFileReq) (int64, error) {
|
||||
if len(req.Path) == 0 {
|
||||
return 0, buserr.New("ErrInvalidChar")
|
||||
}
|
||||
cli, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
stat, err := cli.ContainerStatPath(context.Background(), req.ContainerID, req.Path)
|
||||
if err != nil {
|
||||
return 0, normalizeContainerFileError(err)
|
||||
}
|
||||
if !stat.Mode.IsDir() {
|
||||
return stat.Size, nil
|
||||
}
|
||||
output, err := runContainerCommand(cli, req.ContainerID, []string{"du", "-sb", "--", req.Path})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
parts := strings.Fields(output)
|
||||
if len(parts) == 0 {
|
||||
return 0, fmt.Errorf("invalid du output")
|
||||
}
|
||||
size, err := strconv.ParseInt(parts[0], 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return size, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) DownloadContainerFile(req dto.ContainerFileReq) (io.ReadCloser, string, string, error) {
|
||||
if len(req.Path) == 0 {
|
||||
req.Path = "/"
|
||||
}
|
||||
cli, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
stat, err := cli.ContainerStatPath(ctx, req.ContainerID, req.Path)
|
||||
if err != nil {
|
||||
_ = cli.Close()
|
||||
return nil, "", "", normalizeContainerFileError(err)
|
||||
}
|
||||
|
||||
fileName := stat.Name
|
||||
if len(fileName) == 0 {
|
||||
fileName = "container-file"
|
||||
}
|
||||
if stat.Mode.IsDir() {
|
||||
if _, err := runContainerCommand(cli, req.ContainerID, []string{"sh", "-c", "command -v tar >/dev/null 2>&1"}); err != nil {
|
||||
_ = cli.Close()
|
||||
return nil, "", "", fmt.Errorf("tar command not found in container")
|
||||
}
|
||||
|
||||
targetPath := path.Clean(req.Path)
|
||||
parentPath := path.Dir(targetPath)
|
||||
targetName := path.Base(targetPath)
|
||||
if parentPath == "." || parentPath == "" {
|
||||
parentPath = "/"
|
||||
}
|
||||
tarStream, err := runContainerCommandStream(cli, req.ContainerID, []string{
|
||||
"tar", "-czf", "-", "-C", parentPath, "--", targetName,
|
||||
})
|
||||
if err != nil {
|
||||
_ = cli.Close()
|
||||
return nil, "", "", err
|
||||
}
|
||||
if !strings.HasSuffix(fileName, ".tar.gz") {
|
||||
fileName += ".tar.gz"
|
||||
}
|
||||
return &closeHookReader{
|
||||
ReadCloser: tarStream,
|
||||
onClose: cli.Close,
|
||||
}, fileName, "application/gzip", nil
|
||||
}
|
||||
|
||||
fileStream, err := runContainerCommandStream(cli, req.ContainerID, []string{"cat", "--", req.Path})
|
||||
if err != nil {
|
||||
_ = cli.Close()
|
||||
return nil, "", "", err
|
||||
}
|
||||
return &closeHookReader{
|
||||
ReadCloser: fileStream,
|
||||
onClose: cli.Close,
|
||||
}, fileName, "application/octet-stream", nil
|
||||
}
|
||||
|
||||
func normalizeContainerFileError(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
message := strings.ToLower(err.Error())
|
||||
if strings.Contains(message, "no such file or directory") || strings.Contains(message, "not found") {
|
||||
return buserr.New("ErrPathNotFound")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func runContainerCommand(cli *client.Client, containerID string, command []string) (string, error) {
|
||||
raw, err := runContainerCommandRaw(cli, containerID, command)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(string(raw)), nil
|
||||
}
|
||||
|
||||
type closeHookReader struct {
|
||||
io.ReadCloser
|
||||
onClose func() error
|
||||
}
|
||||
|
||||
func (r *closeHookReader) Close() error {
|
||||
var closeErr error
|
||||
if r.ReadCloser != nil {
|
||||
closeErr = r.ReadCloser.Close()
|
||||
}
|
||||
if r.onClose != nil {
|
||||
if err := r.onClose(); err != nil && closeErr == nil {
|
||||
closeErr = err
|
||||
}
|
||||
}
|
||||
return closeErr
|
||||
}
|
||||
|
||||
func runContainerCommandRaw(cli *client.Client, containerID string, command []string) ([]byte, error) {
|
||||
ctx := context.Background()
|
||||
resp, err := cli.ContainerExecCreate(ctx, containerID, container.ExecOptions{
|
||||
Cmd: command,
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hijack, err := cli.ContainerExecAttach(ctx, resp.ID, container.ExecAttachOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer hijack.Close()
|
||||
|
||||
raw, err := io.ReadAll(hijack.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
if _, err := stdcopy.StdCopy(&stdout, &stderr, bytes.NewReader(raw)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
output := strings.TrimSpace(stdout.String())
|
||||
errorOutput := strings.TrimSpace(stderr.String())
|
||||
info, err := cli.ContainerExecInspect(ctx, resp.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if info.ExitCode != 0 {
|
||||
if len(errorOutput) != 0 {
|
||||
return nil, fmt.Errorf("%s", errorOutput)
|
||||
}
|
||||
if len(output) == 0 {
|
||||
return nil, fmt.Errorf("container command failed with exit code %d", info.ExitCode)
|
||||
}
|
||||
return nil, fmt.Errorf("%s", output)
|
||||
}
|
||||
return stdout.Bytes(), nil
|
||||
}
|
||||
|
||||
func runContainerCommandStream(cli *client.Client, containerID string, command []string) (io.ReadCloser, error) {
|
||||
ctx := context.Background()
|
||||
resp, err := cli.ContainerExecCreate(ctx, containerID, container.ExecOptions{
|
||||
Cmd: command,
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hijack, err := cli.ContainerExecAttach(ctx, resp.ID, container.ExecAttachOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
go func() {
|
||||
defer hijack.Close()
|
||||
var stderr bytes.Buffer
|
||||
_, copyErr := stdcopy.StdCopy(pipeWriter, &stderr, hijack.Reader)
|
||||
if copyErr != nil {
|
||||
_ = pipeWriter.CloseWithError(copyErr)
|
||||
return
|
||||
}
|
||||
info, inspectErr := cli.ContainerExecInspect(ctx, resp.ID)
|
||||
if inspectErr != nil {
|
||||
_ = pipeWriter.CloseWithError(inspectErr)
|
||||
return
|
||||
}
|
||||
if info.ExitCode != 0 {
|
||||
msg := strings.TrimSpace(stderr.String())
|
||||
if len(msg) == 0 {
|
||||
msg = fmt.Sprintf("container command failed with exit code %d", info.ExitCode)
|
||||
}
|
||||
_ = pipeWriter.CloseWithError(fmt.Errorf("%s", msg))
|
||||
return
|
||||
}
|
||||
_ = pipeWriter.Close()
|
||||
}()
|
||||
return pipeReader, nil
|
||||
}
|
||||
|
||||
func toContainerFileInfo(filePath string, stat container.PathStat, isDir bool) dto.ContainerFileInfo {
|
||||
name := stat.Name
|
||||
if len(name) == 0 {
|
||||
items := strings.Split(strings.TrimSuffix(filePath, "/"), "/")
|
||||
name = items[len(items)-1]
|
||||
}
|
||||
isLink := stat.Mode&os.ModeSymlink != 0
|
||||
return dto.ContainerFileInfo{
|
||||
Name: name,
|
||||
Path: filePath,
|
||||
IsDir: isDir,
|
||||
IsLink: isLink,
|
||||
LinkTo: stat.LinkTarget,
|
||||
Size: stat.Size,
|
||||
Mode: stat.Mode.String(),
|
||||
ModTime: stat.Mtime.Format(constant.DateTimeLayout),
|
||||
}
|
||||
}
|
||||
|
||||
func isContainerDir(cli *client.Client, containerID, targetPath string) (bool, error) {
|
||||
_, err := runContainerCommand(cli, containerID, []string{
|
||||
"sh", "-c", "[ -d \"$1\" ]", "sh", targetPath,
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func stringsToMap(list []string) map[string]string {
|
||||
var labelMap = make(map[string]string)
|
||||
for _, label := range list {
|
||||
@@ -1488,6 +1919,13 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *conta
|
||||
hostConf.Binds = []string{}
|
||||
hostConf.Mounts = []mount.Mount{}
|
||||
hostConf.DNS = req.DNS
|
||||
hostConf.ExtraHosts = []string{}
|
||||
for _, item := range req.ExtraHosts {
|
||||
if len(item.Hostname) == 0 || len(item.IP) == 0 {
|
||||
continue
|
||||
}
|
||||
hostConf.ExtraHosts = append(hostConf.ExtraHosts, fmt.Sprintf("%s:%s", item.Hostname, item.IP))
|
||||
}
|
||||
config.Volumes = make(map[string]struct{})
|
||||
for _, volume := range req.Volumes {
|
||||
item := mount.Mount{
|
||||
|
||||
@@ -135,6 +135,7 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
|
||||
|
||||
for key, value := range mergedMap {
|
||||
value.Name = key
|
||||
value.ComposeFileExists = composeFileExists(value.Workdir, value.ConfigFile)
|
||||
records = append(records, value)
|
||||
}
|
||||
if len(req.Info) != 0 {
|
||||
@@ -164,6 +165,29 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
|
||||
return int64(total), listItem, nil
|
||||
}
|
||||
|
||||
func composeFileExists(workdir, configFile string) bool {
|
||||
workdir = strings.TrimSpace(workdir)
|
||||
configFile = strings.TrimSpace(configFile)
|
||||
if configFile == "" {
|
||||
return false
|
||||
}
|
||||
for _, item := range strings.Split(configFile, ",") {
|
||||
file := strings.TrimSpace(item)
|
||||
if file == "" {
|
||||
continue
|
||||
}
|
||||
if !filepath.IsAbs(file) && workdir != "" {
|
||||
file = filepath.Join(workdir, file)
|
||||
}
|
||||
file = filepath.Clean(file)
|
||||
info, err := os.Stat(file)
|
||||
if err == nil && !info.IsDir() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (u *ContainerService) TestCompose(req dto.ComposeCreate) (bool, error) {
|
||||
if cmd.CheckIllegal(req.Path) {
|
||||
return false, buserr.New("ErrCmdIllegal")
|
||||
@@ -383,8 +407,11 @@ func (u *ContainerService) loadPath(req *dto.ComposeCreate) error {
|
||||
}
|
||||
|
||||
func removeContainerForCompose(composeName, composePath string) error {
|
||||
if stdout, err := compose.Operate(composePath, "down"); err != nil {
|
||||
return errors.New(stdout)
|
||||
if _, err := os.Stat(composePath); err == nil {
|
||||
if stdout, err := compose.Operate(composePath, "down"); err != nil {
|
||||
return errors.New(stdout)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
var options container.ListOptions
|
||||
options.All = true
|
||||
|
||||
@@ -284,7 +284,7 @@ func (u *DashboardService) LoadAppLauncher(ctx *gin.Context) ([]dto.AppLauncher,
|
||||
return data, err
|
||||
}
|
||||
|
||||
showList, err := launcherRepo.ListName()
|
||||
showList, _ := launcherRepo.ListName()
|
||||
defaultList, err := appRepo.GetTopRecommend()
|
||||
if err != nil {
|
||||
return data, nil
|
||||
@@ -361,6 +361,9 @@ func (u *DashboardService) LoadQuickOptions() []dto.QuickJump {
|
||||
_ = copier.Copy(&item, &quick)
|
||||
list = append(list, item)
|
||||
}
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
return list[i].Recommend < list[j].Recommend
|
||||
})
|
||||
return list
|
||||
}
|
||||
func (u *DashboardService) ChangeQuick(req dto.ChangeQuicks) error {
|
||||
@@ -612,6 +615,9 @@ func loadQuickJump(base *dto.DashboardBase) {
|
||||
website, _ := websiteRepo.GetBy()
|
||||
base.WebsiteNumber = len(website)
|
||||
|
||||
agents, _ := agentRepo.List()
|
||||
base.AgentNumber = len(agents)
|
||||
|
||||
postgresqlDbs, _ := postgresqlRepo.List()
|
||||
mysqlDbs, _ := mysqlRepo.List()
|
||||
base.DatabaseNumber = len(mysqlDbs) + len(postgresqlDbs)
|
||||
@@ -625,6 +631,8 @@ func loadQuickJump(base *dto.DashboardBase) {
|
||||
quicks := launcherRepo.ListQuickJump(false)
|
||||
for i := 0; i < len(quicks); i++ {
|
||||
switch quicks[i].Name {
|
||||
case "Agent":
|
||||
quicks[i].Detail = fmt.Sprintf("%d", base.AgentNumber)
|
||||
case "Website":
|
||||
quicks[i].Detail = fmt.Sprintf("%d", base.WebsiteNumber)
|
||||
case "Database":
|
||||
@@ -638,7 +646,7 @@ func loadQuickJump(base *dto.DashboardBase) {
|
||||
_ = copier.Copy(&item, quicks[i])
|
||||
base.QuickJumps = append(base.QuickJumps, item)
|
||||
}
|
||||
sort.Slice(quicks, func(i, j int) bool {
|
||||
return quicks[i].Recommend < quicks[j].Recommend
|
||||
sort.Slice(base.QuickJumps, func(i, j int) bool {
|
||||
return base.QuickJumps[i].Recommend < base.QuickJumps[j].Recommend
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
@@ -43,6 +44,10 @@ func (u *DBCommonService) LoadBaseInfo(req dto.OperationWithNameAndType) (*dto.D
|
||||
|
||||
func (u *DBCommonService) LoadDatabaseFile(req dto.OperationWithNameAndType) (string, error) {
|
||||
filePath := ""
|
||||
safeName := filepath.Base(req.Name)
|
||||
if safeName != req.Name || strings.Contains(safeName, "..") {
|
||||
return "", buserr.New("ErrInvalidParams")
|
||||
}
|
||||
switch req.Type {
|
||||
case "mysql-cluster-conf":
|
||||
filePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/mysql-cluster/%s/conf/my.cnf", req.Name))
|
||||
|
||||
@@ -109,7 +109,7 @@ func (u *DeviceService) CheckDNS(key, value string) (bool, error) {
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer conn.Close()
|
||||
defer func() { _ = conn.Close() }()
|
||||
|
||||
return true, nil
|
||||
}
|
||||
@@ -132,7 +132,7 @@ func (u *DeviceService) Update(key, value string) error {
|
||||
if cmd.CheckIllegal(value) {
|
||||
return buserr.New("ErrCmdIllegal")
|
||||
}
|
||||
if err := cmd.RunDefaultBashCf("%s hostnamectl set-hostname %s", cmd.SudoHandleCmd(), value); err != nil {
|
||||
if err := cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).Run("hostnamectl", "set-hostname", value); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = psutil.HOST.GetHostInfo(true)
|
||||
@@ -245,7 +245,7 @@ func (u *DeviceService) UpdateSwap(req dto.SwapHelper) error {
|
||||
}
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem))
|
||||
if !req.IsNew {
|
||||
if err := cmdMgr.RunBashCf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path); err != nil {
|
||||
if err := cmdMgr.Run("swapoff", req.Path); err != nil {
|
||||
return fmt.Errorf("handle swapoff %s failed, %v", req.Path, err)
|
||||
}
|
||||
}
|
||||
@@ -256,22 +256,22 @@ func (u *DeviceService) UpdateSwap(req dto.SwapHelper) error {
|
||||
return operateSwapWithFile(true, req)
|
||||
}
|
||||
taskItem.LogStart(i18n.GetMsgByKey("CreateSwap"))
|
||||
if err := cmdMgr.RunBashCf("%s dd if=/dev/zero of=%s bs=1024 count=%d", cmd.SudoHandleCmd(), req.Path, req.Size); err != nil {
|
||||
if err := cmdMgr.Run("dd", "if=/dev/zero", fmt.Sprintf("of=%s", req.Path), "bs=1024", fmt.Sprintf("count=%d", req.Size)); err != nil {
|
||||
return fmt.Errorf("handle dd %s failed, %v", req.Path, err)
|
||||
}
|
||||
|
||||
taskItem.Log("chmod 0600 " + req.Path)
|
||||
if err := cmdMgr.RunBashCf("%s chmod 0600 %s", cmd.SudoHandleCmd(), req.Path); err != nil {
|
||||
if err := cmdMgr.Run("chmod", "0600", req.Path); err != nil {
|
||||
return fmt.Errorf("handle chmod 0600 %s failed, %v", req.Path, err)
|
||||
}
|
||||
taskItem.LogStart(i18n.GetMsgByKey("FormatSwap"))
|
||||
if err := cmdMgr.RunBashCf("%s mkswap -f %s", cmd.SudoHandleCmd(), req.Path); err != nil {
|
||||
if err := cmdMgr.Run("mkswap", "-f", req.Path); err != nil {
|
||||
return fmt.Errorf("handle mkswap -f %s failed, %v", req.Path, err)
|
||||
}
|
||||
|
||||
taskItem.LogStart(i18n.GetMsgByKey("EnableSwap"))
|
||||
if err := cmdMgr.RunBashCf("%s swapon %s", cmd.SudoHandleCmd(), req.Path); err != nil {
|
||||
_ = cmdMgr.RunBashCf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path)
|
||||
if err := cmdMgr.Run("swapon", req.Path); err != nil {
|
||||
_ = cmdMgr.Run("swapoff", req.Path)
|
||||
return fmt.Errorf("handle swapoff %s failed, %v", req.Path, err)
|
||||
}
|
||||
return operateSwapWithFile(false, req)
|
||||
|
||||
@@ -2,13 +2,14 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
|
||||
@@ -65,19 +66,19 @@ func (s *DiskService) PartitionDisk(req request.DiskPartitionRequest) (string, e
|
||||
}
|
||||
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(10 * time.Second))
|
||||
if err := cmdMgr.RunBashC(fmt.Sprintf("partprobe %s", req.Device)); err != nil {
|
||||
if err := cmdMgr.Run("partprobe", req.Device); err != nil {
|
||||
return "", buserr.WithErr("PartitionDiskErr", err)
|
||||
}
|
||||
|
||||
if err := cmdMgr.RunBashC(fmt.Sprintf("parted -s %s mklabel gpt", req.Device)); err != nil {
|
||||
if err := cmdMgr.Run("parted", "-s", req.Device, "mklabel", "gpt"); err != nil {
|
||||
return "", buserr.WithErr("PartitionDiskErr", err)
|
||||
}
|
||||
|
||||
if err := cmdMgr.RunBashC(fmt.Sprintf("parted -s %s mkpart primary 1MiB 100%%", req.Device)); err != nil {
|
||||
if err := cmdMgr.Run("parted", "-s", req.Device, "mkpart", "primary", "1MiB", "100%"); err != nil {
|
||||
return "", buserr.WithErr("PartitionDiskErr", err)
|
||||
}
|
||||
|
||||
if err := cmdMgr.RunBashC(fmt.Sprintf("partprobe %s", req.Device)); err != nil {
|
||||
if err := cmdMgr.Run("partprobe", req.Device); err != nil {
|
||||
return "", buserr.WithErr("PartitionDiskErr", err)
|
||||
}
|
||||
partition := req.Device + "1"
|
||||
@@ -133,7 +134,7 @@ func (s *DiskService) MountDisk(req request.DiskMountRequest) error {
|
||||
}
|
||||
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(1 * time.Minute))
|
||||
if err := cmdMgr.RunBashC(fmt.Sprintf("mount -t %s %s %s", req.Filesystem, req.Device, req.MountPoint)); err != nil {
|
||||
if err := cmdMgr.Run("mount", "-t", req.Filesystem, req.Device, req.MountPoint); err != nil {
|
||||
return buserr.WithErr("MountDiskErr", err)
|
||||
}
|
||||
if req.AutoMount {
|
||||
@@ -156,7 +157,7 @@ func (s *DiskService) UnmountDisk(req request.DiskUnmountRequest) error {
|
||||
if !isPointMounted(req.MountPoint) {
|
||||
return buserr.New("MountDiskErr")
|
||||
}
|
||||
if err := cmd.RunDefaultBashC(fmt.Sprintf("umount -f %s", req.MountPoint)); err != nil {
|
||||
if err := cmd.NewCommandMgr(cmd.WithTimeout(20*time.Second)).Run("umount", "-f", req.MountPoint); err != nil {
|
||||
return buserr.WithErr("MountDiskErr", err)
|
||||
}
|
||||
if err := removeFromFstab(req.MountPoint); err != nil {
|
||||
|
||||
@@ -12,11 +12,12 @@ var (
|
||||
appInstallResourceRepo = repo.NewIAppInstallResourceRpo()
|
||||
appIgnoreUpgradeRepo = repo.NewIAppIgnoreUpgradeRepo()
|
||||
|
||||
aiRepo = repo.NewIAiRepo()
|
||||
mcpServerRepo = repo.NewIMcpServerRepo()
|
||||
tensorrtLLMRepo = repo.NewITensorRTLLMRepo()
|
||||
agentRepo = repo.NewIAgentRepo()
|
||||
agentAccountRepo = repo.NewIAgentAccountRepo()
|
||||
aiRepo = repo.NewIAiRepo()
|
||||
mcpServerRepo = repo.NewIMcpServerRepo()
|
||||
tensorrtLLMRepo = repo.NewITensorRTLLMRepo()
|
||||
agentRepo = repo.NewIAgentRepo()
|
||||
agentAccountRepo = repo.NewIAgentAccountRepo()
|
||||
agentAccountModelRepo = repo.NewIAgentAccountModelRepo()
|
||||
|
||||
mysqlRepo = repo.NewIMysqlRepo()
|
||||
postgresqlRepo = repo.NewIPostgresqlRepo()
|
||||
|
||||
@@ -263,7 +263,7 @@ func (f *FileService) Create(op request.FileCreate) error {
|
||||
func (f *FileService) Delete(op request.FileDelete) error {
|
||||
if op.IsDir {
|
||||
excludeDir := global.Dir.DataDir
|
||||
if filepath.Base(op.Path) == ".1panel_clash" || op.Path == excludeDir {
|
||||
if path.Base(op.Path) == ".1panel_clash" || op.Path == excludeDir {
|
||||
return buserr.New("ErrPathNotDelete")
|
||||
}
|
||||
}
|
||||
@@ -592,6 +592,12 @@ func (f *FileService) DepthDirSize(req request.DirSizeReq) ([]response.DepthDirS
|
||||
func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.FileLineContent, error) {
|
||||
logFilePath := ""
|
||||
taskStatus := ""
|
||||
if len(req.Name) != 0 {
|
||||
safeName := path.Base(req.Name)
|
||||
if safeName != req.Name || strings.Contains(safeName, "..") {
|
||||
return nil, buserr.New("ErrInvalidParams")
|
||||
}
|
||||
}
|
||||
switch req.Type {
|
||||
case constant.TypeWebsite:
|
||||
website, err := websiteRepo.GetFirst(repo.WithByID(req.ID))
|
||||
@@ -649,9 +655,9 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
|
||||
logFilePath = taskModel.LogFile
|
||||
taskStatus = taskModel.Status
|
||||
case "mysql-slow-logs":
|
||||
logFilePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/mysql/%s/data/1Panel-slow.log", req.Name))
|
||||
logFilePath = path.Join(global.Dir.DataDir, "apps", "mysql", req.Name, "data", "1Panel-slow.log")
|
||||
case "mariadb-slow-logs":
|
||||
logFilePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/mariadb/%s/db/data/1Panel-slow.log", req.Name))
|
||||
logFilePath = path.Join(global.Dir.DataDir, "apps", "mariadb", req.Name, "db", "data", "1Panel-slow.log")
|
||||
case "php-fpm-slow-logs":
|
||||
php, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
@@ -666,8 +672,7 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
|
||||
}
|
||||
logFilePath, _ = ini_conf.GetIniValue(configPath, "supervisord", "logfile")
|
||||
case constant.Supervisor:
|
||||
logDir := path.Join(global.Dir.DataDir, "tools", "supervisord", "log")
|
||||
logFilePath = path.Join(logDir, req.Name)
|
||||
logFilePath = path.Join(global.Dir.DataDir, "tools", "supervisord", "log", req.Name)
|
||||
}
|
||||
|
||||
file, err := os.Open(logFilePath)
|
||||
@@ -686,7 +691,7 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
|
||||
logFileRes *dto.LogFileRes
|
||||
)
|
||||
if stat.Size() > files.MaxReadFileSize {
|
||||
lines, err = files.TailFromEnd(logFilePath, req.PageSize)
|
||||
lines, _ = files.TailFromEnd(logFilePath, req.PageSize)
|
||||
isEndOfFile = true
|
||||
scope = "tail"
|
||||
} else {
|
||||
|
||||
@@ -4,13 +4,12 @@ import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/core/app/model"
|
||||
"github.com/1Panel-dev/1Panel/core/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/core/buserr"
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/encrypt"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/ssh"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ssh"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -75,11 +74,7 @@ func (u *HostService) TestByInfo(req dto.HostConnTest) bool {
|
||||
}
|
||||
|
||||
func (u *HostService) TestLocalConn(id uint) bool {
|
||||
var (
|
||||
host model.Host
|
||||
err error
|
||||
)
|
||||
host, err = hostRepo.Get(repo.WithByID(id))
|
||||
host, err := hostRepo.Get(repo.WithByID(id))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -113,12 +108,11 @@ func (u *HostService) TestLocalConn(id uint) bool {
|
||||
return false
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (u *HostService) SearchWithPage(req dto.SearchPageWithGroup) (int64, interface{}, error) {
|
||||
var options []global.DBOption
|
||||
var options []repo.DBOption
|
||||
if len(req.Info) != 0 {
|
||||
options = append(options, hostRepo.WithByInfo(req.Info))
|
||||
}
|
||||
@@ -198,8 +192,7 @@ func (u *HostService) SearchForTree(search dto.SearchForTree) ([]dto.HostTree, e
|
||||
|
||||
func (u *HostService) GetHostByID(id uint) (*dto.HostInfo, error) {
|
||||
var item dto.HostInfo
|
||||
var host model.Host
|
||||
host, _ = hostRepo.Get(repo.WithByID(id))
|
||||
host, _ := hostRepo.Get(repo.WithByID(id))
|
||||
if host.ID == 0 {
|
||||
return nil, buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
@@ -336,7 +329,6 @@ func GetHostInfo(id uint) (*model.Host, error) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(host.PassPhrase) != 0 {
|
||||
host.PassPhrase, err = encrypt.StringDecrypt(host.PassPhrase)
|
||||
if err != nil {
|
||||
@@ -278,6 +278,10 @@ func (h *HostToolService) OperateSupervisorProcess(req request.SupervisorProcess
|
||||
}
|
||||
|
||||
func handleProcess(supervisordDir string, req request.SupervisorProcessConfig, containerName string) error {
|
||||
safeName := path.Base(req.Name)
|
||||
if safeName != req.Name || strings.Contains(safeName, "..") {
|
||||
return buserr.New("ErrInvalidParams")
|
||||
}
|
||||
var (
|
||||
fileOp = files.NewFileOp()
|
||||
logDir = path.Join(supervisordDir, "log")
|
||||
|
||||
@@ -289,11 +289,11 @@ func (s *IptablesService) LoadChainStatus(req dto.OperationWithName) dto.Iptable
|
||||
}
|
||||
switch req.Name {
|
||||
case iptables.Chain1PanelBasic:
|
||||
data.IsBind, err = iptables.CheckChainBind(iptables.FilterTab, iptables.ChainInput, req.Name)
|
||||
data.IsBind, _ = iptables.CheckChainBind(iptables.FilterTab, iptables.ChainInput, req.Name)
|
||||
case iptables.Chain1PanelInput:
|
||||
data.IsBind, err = iptables.CheckChainBind(iptables.FilterTab, iptables.ChainInput, req.Name)
|
||||
data.IsBind, _ = iptables.CheckChainBind(iptables.FilterTab, iptables.ChainInput, req.Name)
|
||||
case iptables.Chain1PanelOutput:
|
||||
data.IsBind, err = iptables.CheckChainBind(iptables.FilterTab, iptables.ChainOutput, req.Name)
|
||||
data.IsBind, _ = iptables.CheckChainBind(iptables.FilterTab, iptables.ChainOutput, req.Name)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -4,6 +4,10 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
|
||||
@@ -23,9 +27,6 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser"
|
||||
"github.com/subosito/gotenv"
|
||||
"gopkg.in/yaml.v3"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type McpServerService struct{}
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
cmd2 "github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/subosito/gotenv"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/compose"
|
||||
@@ -241,7 +240,7 @@ func (n NginxService) Build(req request.NginxBuildReq) error {
|
||||
return err
|
||||
}
|
||||
buildTask.AddSubTaskWithOps("", func(t *task.Task) error {
|
||||
cmdMgr := cmd2.NewCommandMgr(cmd.WithTask(*buildTask), cmd.WithTimeout(120*time.Minute))
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*buildTask), cmd.WithTimeout(120*time.Minute))
|
||||
if err = cmdMgr.RunBashCf("docker compose -f %s build", nginxInstall.GetComposePath()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -677,7 +677,7 @@ func (r *RuntimeService) GetPHPExtensions(runtimeID uint) (response.PHPExtension
|
||||
return res, err
|
||||
}
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(20 * time.Second))
|
||||
out, err := cmdMgr.RunWithStdoutBashCf("docker exec -i %s php -m", runtime.ContainerName)
|
||||
out, err := cmdMgr.RunWithStdout("docker", "exec", "-i", runtime.ContainerName, "php", "-m")
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
@@ -722,7 +722,7 @@ func (r *RuntimeService) InstallPHPExtension(req request.PHPExtensionInstallReq)
|
||||
}
|
||||
installTask.AddSubTask("", func(t *task.Task) error {
|
||||
err = cmd.NewCommandMgr(cmd.WithTask(*installTask), cmd.WithTimeout(20*time.Minute)).
|
||||
RunBashCf("docker exec -i %s install-ext %s", runtime.ContainerName, req.Name)
|
||||
Run("docker", "exec", "-i", runtime.ContainerName, "install-ext", req.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -736,7 +736,7 @@ func (r *RuntimeService) InstallPHPExtension(req request.PHPExtensionInstallReq)
|
||||
return err
|
||||
}
|
||||
err = cmd.NewCommandMgr(cmd.WithTask(*installTask), cmd.WithTimeout(15*time.Minute)).
|
||||
RunBashCf("docker commit %s %s", runtime.ContainerName, runtime.Image)
|
||||
Run("docker", "commit", runtime.ContainerName, runtime.Image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package service
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
@@ -12,6 +14,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ssh"
|
||||
terminalai "github.com/1Panel-dev/1Panel/agent/utils/terminal/ai"
|
||||
"github.com/jinzhu/copier"
|
||||
)
|
||||
|
||||
@@ -19,7 +22,9 @@ type SettingService struct{}
|
||||
|
||||
type ISettingService interface {
|
||||
GetSettingInfo() (*dto.SettingInfo, error)
|
||||
GetTerminalAIInfo() (*dto.TerminalAIInfo, error)
|
||||
Update(key, value string) error
|
||||
UpdateTerminalAI(req dto.TerminalAIInfo) error
|
||||
|
||||
TestConnByInfo(req dto.SSHConnData) bool
|
||||
SaveConnInfo(req dto.SSHConnData) error
|
||||
@@ -57,10 +62,67 @@ func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) {
|
||||
return &info, err
|
||||
}
|
||||
|
||||
func (u *SettingService) GetTerminalAIInfo() (*dto.TerminalAIInfo, error) {
|
||||
info := &dto.TerminalAIInfo{
|
||||
AIStatus: constant.StatusDisable,
|
||||
AIAccountID: "",
|
||||
AIPrefix: constant.DefaultTerminalAIPrefix,
|
||||
AIRiskCommands: "[]",
|
||||
AIRiskCommandsDefault: constant.DefaultTerminalAIRiskCommands,
|
||||
}
|
||||
|
||||
if value, err := settingRepo.GetValueByKey("AIStatus"); err == nil && value != "" {
|
||||
info.AIStatus = value
|
||||
}
|
||||
if value, err := settingRepo.GetValueByKey("AIAccountID"); err == nil {
|
||||
info.AIAccountID = value
|
||||
}
|
||||
if value, err := settingRepo.GetValueByKey("AIPrefix"); err == nil {
|
||||
info.AIPrefix = value
|
||||
}
|
||||
if value, err := settingRepo.GetValueByKey("AIRiskCommands"); err == nil && value != "" {
|
||||
info.AIRiskCommands = value
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (u *SettingService) Update(key, value string) error {
|
||||
return settingRepo.UpdateOrCreate(key, value)
|
||||
}
|
||||
|
||||
func (u *SettingService) UpdateTerminalAI(req dto.TerminalAIInfo) error {
|
||||
if strings.EqualFold(strings.TrimSpace(req.AIStatus), constant.StatusEnable) {
|
||||
accountID, err := strconv.ParseUint(strings.TrimSpace(req.AIAccountID), 10, 64)
|
||||
if err != nil || accountID == 0 {
|
||||
return buserr.New("ErrAgentAccountIDRequired")
|
||||
}
|
||||
currentStatus, _ := settingRepo.GetValueByKey("AIStatus")
|
||||
currentAccountID, _ := settingRepo.GetValueByKey("AIAccountID")
|
||||
needValidate := !strings.EqualFold(strings.TrimSpace(currentStatus), constant.StatusEnable) ||
|
||||
strings.TrimSpace(currentAccountID) != strings.TrimSpace(req.AIAccountID)
|
||||
if needValidate {
|
||||
if err := terminalai.ValidateTerminalAccount(uint(accountID)); err != nil {
|
||||
return buserr.WithErr("ErrAgentAccountUnavailable", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := settingRepo.UpdateOrCreate("AIStatus", req.AIStatus); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.UpdateOrCreate("AIAccountID", req.AIAccountID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.UpdateOrCreate("AIPrefix", req.AIPrefix); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := settingRepo.UpdateOrCreate("AIRiskCommands", req.AIRiskCommands); err != nil {
|
||||
return err
|
||||
}
|
||||
terminalai.InvalidateTerminalRuntimeCache()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *SettingService) TestConnByInfo(req dto.SSHConnData) bool {
|
||||
if req.AuthMode == "password" && len(req.Password) != 0 {
|
||||
password, err := base64.StdEncoding.DecodeString(req.Password)
|
||||
|
||||
@@ -133,7 +133,7 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
|
||||
|
||||
var snapJson SnapshotJson
|
||||
taskItem.AddSubTaskWithAliasAndOps(
|
||||
"Readjson",
|
||||
"ReadJson",
|
||||
func(t *task.Task) error {
|
||||
snapJson, err = readFromJson(path.Join(rootDir, snap.Name), &itemHelper)
|
||||
return err
|
||||
@@ -156,14 +156,6 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
|
||||
)
|
||||
req.IsNew = true
|
||||
}
|
||||
if req.IsNew || snap.InterruptStep == "RecoverDBData" {
|
||||
taskItem.AddSubTaskWithAliasAndOps(
|
||||
"RecoverDBData",
|
||||
func(t *task.Task) error { return recoverDBData(path.Join(rootDir, snap.Name, "db"), &itemHelper) },
|
||||
nil, 0, 90*time.Minute,
|
||||
)
|
||||
req.IsNew = true
|
||||
}
|
||||
if req.IsNew || snap.InterruptStep == "RecoverBackups" {
|
||||
taskItem.AddSubTaskWithAliasAndOps(
|
||||
"RecoverBackups",
|
||||
@@ -222,13 +214,24 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
|
||||
)
|
||||
req.IsNew = true
|
||||
}
|
||||
taskItem.AddSubTaskWithAliasAndOps(
|
||||
"RecoverDBData",
|
||||
func(t *task.Task) error {
|
||||
return restartCompose(path.Join(snapJson.BaseDir, "1panel/docker/compose"), &itemHelper)
|
||||
},
|
||||
nil, 0, 90*time.Minute,
|
||||
)
|
||||
if req.IsNew || snap.InterruptStep == "RecoverCompose" {
|
||||
taskItem.AddSubTaskWithAliasAndOps(
|
||||
"RecoverCompose",
|
||||
func(t *task.Task) error {
|
||||
return restartCompose(path.Join(snapJson.BaseDir, "1panel/docker/compose"), &itemHelper)
|
||||
},
|
||||
nil, 0, 90*time.Minute,
|
||||
)
|
||||
req.IsNew = true
|
||||
}
|
||||
if req.IsNew || snap.InterruptStep == "RecoverDBData" {
|
||||
taskItem.AddSubTaskWithAliasAndOps(
|
||||
"RecoverDBData",
|
||||
func(t *task.Task) error { return recoverDBData(path.Join(rootDir, snap.Name, "db"), &itemHelper) },
|
||||
nil, 0, 90*time.Minute,
|
||||
)
|
||||
req.IsNew = true
|
||||
}
|
||||
|
||||
if err := taskItem.Execute(); err != nil {
|
||||
_ = settingRepo.Update("SystemStatus", "Free")
|
||||
@@ -343,22 +346,22 @@ func backupBeforeRecover(name string, itemHelper *snapRecoverHelper) error {
|
||||
|
||||
func readFromJson(rootDir string, itemHelper *snapRecoverHelper) (SnapshotJson, error) {
|
||||
itemHelper.Task.Log("---------------------- 4 / 11 ----------------------")
|
||||
itemHelper.Task.LogStart(i18n.GetMsgByKey("Readjson"))
|
||||
itemHelper.Task.LogStart(i18n.GetMsgByKey("ReadJson"))
|
||||
|
||||
snapJsonPath := path.Join(rootDir, "base/snapshot.json")
|
||||
var snap SnapshotJson
|
||||
_, err := os.Stat(snapJsonPath)
|
||||
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("ReadjsonPath"), err)
|
||||
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("ReadJsonPath"), err)
|
||||
if err != nil {
|
||||
return snap, err
|
||||
}
|
||||
fileByte, err := os.ReadFile(snapJsonPath)
|
||||
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("ReadjsonContent"), err)
|
||||
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("ReadJsonContent"), err)
|
||||
if err != nil {
|
||||
return snap, err
|
||||
}
|
||||
err = json.Unmarshal(fileByte, &snap)
|
||||
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("ReadjsonMarshal"), err)
|
||||
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("ReadJsonMarshal"), err)
|
||||
if err != nil {
|
||||
return snap, err
|
||||
}
|
||||
@@ -439,17 +442,8 @@ func recoverBaseData(src string, itemHelper *snapRecoverHelper) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func recoverDBData(src string, itemHelper *snapRecoverHelper) error {
|
||||
itemHelper.Task.Log("---------------------- 7 / 11 ----------------------")
|
||||
itemHelper.Task.LogStart(i18n.GetMsgByKey("RecoverDBData"))
|
||||
err := itemHelper.FileOp.CopyDirWithExclude(src, global.Dir.DataDir, nil)
|
||||
|
||||
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("RecoverDBData"), err)
|
||||
return err
|
||||
}
|
||||
|
||||
func restartCompose(composePath string, itemHelper *snapRecoverHelper) error {
|
||||
itemHelper.Task.Log("---------------------- 11 / 11 ----------------------")
|
||||
itemHelper.Task.Log("---------------------- 7 / 11 ----------------------")
|
||||
itemHelper.Task.LogStart(i18n.GetMsgByKey("RecoverCompose"))
|
||||
|
||||
composes, err := composeRepo.ListRecord()
|
||||
@@ -472,3 +466,64 @@ func restartCompose(composePath string, itemHelper *snapRecoverHelper) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func recoverDBData(src string, itemHelper *snapRecoverHelper) error {
|
||||
itemHelper.Task.Log("---------------------- 11 / 11 ----------------------")
|
||||
itemHelper.Task.LogStart(i18n.GetMsgByKey("RecoverDBData"))
|
||||
dbDir := path.Join(global.Dir.DataDir, "db")
|
||||
if err := os.RemoveAll(dbDir); err != nil {
|
||||
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("RecoverDBData"), err)
|
||||
return err
|
||||
}
|
||||
|
||||
err := itemHelper.FileOp.CopyDirWithExclude(src, global.Dir.DataDir, nil)
|
||||
if err != nil {
|
||||
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("RecoverDBData"), err)
|
||||
return err
|
||||
}
|
||||
if cleanErr := cleanOrphanSQLiteSidecars(dbDir); cleanErr != nil {
|
||||
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("RecoverDBData"), cleanErr)
|
||||
return cleanErr
|
||||
}
|
||||
|
||||
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("RecoverDBData"), nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func cleanOrphanSQLiteSidecars(dbDir string) error {
|
||||
entries, err := os.ReadDir(dbDir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
name := entry.Name()
|
||||
var mainDB string
|
||||
switch {
|
||||
case strings.HasSuffix(name, ".db-wal"):
|
||||
mainDB = strings.TrimSuffix(name, "-wal")
|
||||
case strings.HasSuffix(name, ".db-shm"):
|
||||
mainDB = strings.TrimSuffix(name, "-shm")
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
if _, statErr := os.Stat(path.Join(dbDir, mainDB)); statErr != nil {
|
||||
if !os.IsNotExist(statErr) {
|
||||
return statErr
|
||||
}
|
||||
if removeErr := os.Remove(path.Join(dbDir, name)); removeErr != nil && !os.IsNotExist(removeErr) {
|
||||
return removeErr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
+49
-13
@@ -269,16 +269,26 @@ func (u *SSHService) CreateRootCert(req dto.RootCertOperate) error {
|
||||
publicPath := fmt.Sprintf("%s/.ssh/%s.pub", currentUser.HomeDir, req.Name)
|
||||
authFilePath := currentUser.HomeDir + "/.ssh/authorized_keys"
|
||||
|
||||
authFileItem, _ := os.ReadFile(authFilePath)
|
||||
authFile := string(authFileItem)
|
||||
if authFile != "" && !strings.HasSuffix(authFile, "\n") {
|
||||
file, err := os.OpenFile(authFilePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if info, err := os.Stat(authFilePath); err == nil && info.Size() > 0 {
|
||||
f, err := os.Open(authFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
if _, err := file.WriteString("\n"); err != nil {
|
||||
return err
|
||||
defer func() { _ = f.Close() }()
|
||||
|
||||
if _, err := f.Seek(-1, 2); err == nil {
|
||||
buf := make([]byte, 1)
|
||||
if _, err := f.Read(buf); err == nil && buf[0] != '\n' {
|
||||
appendFile, err := os.OpenFile(authFilePath, os.O_APPEND|os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := appendFile.Write([]byte("\n")); err != nil {
|
||||
_ = appendFile.Close()
|
||||
return err
|
||||
}
|
||||
_ = appendFile.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,17 +300,43 @@ func (u *SSHService) CreateRootCert(req dto.RootCertOperate) error {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
command := fmt.Sprintf("ssh-keygen -t %s -f %s/.ssh/%s -N ''", req.EncryptionMode, currentUser.HomeDir, req.Name)
|
||||
if len(req.PassPhrase) != 0 {
|
||||
command = fmt.Sprintf("ssh-keygen -t %s -P %s -f %s/.ssh/%s | echo y", req.EncryptionMode, req.PassPhrase, currentUser.HomeDir, req.Name)
|
||||
tmpPrivatePath := privatePath + ".tmp"
|
||||
tmpPublicPath := privatePath + ".tmp.pub"
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(2 * time.Minute))
|
||||
args := []string{
|
||||
"-t", req.EncryptionMode,
|
||||
"-f", tmpPrivatePath,
|
||||
"-N", req.PassPhrase,
|
||||
"-q",
|
||||
}
|
||||
if err := cmd.RunDefaultBashC(command); err != nil {
|
||||
|
||||
if err := cmdMgr.Run("ssh-keygen", args...); err != nil {
|
||||
_ = os.Remove(tmpPrivatePath)
|
||||
_ = os.Remove(tmpPublicPath)
|
||||
return fmt.Errorf("generate failed, %v", err)
|
||||
}
|
||||
if err := os.Rename(tmpPrivatePath, privatePath); err != nil {
|
||||
return fmt.Errorf("replace private key failed, %v", err)
|
||||
}
|
||||
if err := os.Rename(tmpPublicPath, publicPath); err != nil {
|
||||
return fmt.Errorf("replace public key failed, %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := cmd.RunDefaultBashCf("cat %s >> %s", publicPath, authFilePath); err != nil {
|
||||
return fmt.Errorf("generate failed, %v", err)
|
||||
publicKeyBytes, err := os.ReadFile(publicPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read public key failed, %v", err)
|
||||
}
|
||||
|
||||
cleanKey := strings.TrimRight(string(publicKeyBytes), "\n") + "\n"
|
||||
authFile, err := os.OpenFile(authFilePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open authorized_keys failed, %v", err)
|
||||
}
|
||||
defer func() { _ = authFile.Close() }()
|
||||
|
||||
if _, err := authFile.Write([]byte(cleanKey)); err != nil {
|
||||
return fmt.Errorf("append authorized_keys failed, %v", err)
|
||||
}
|
||||
|
||||
cert.PrivateKeyPath = privatePath
|
||||
|
||||
@@ -110,6 +110,7 @@ type IWebsiteService interface {
|
||||
GetProxyCache(id uint) (res response.NginxProxyCache, err error)
|
||||
ClearProxyCache(req request.NginxCommonReq) error
|
||||
DeleteProxy(req request.WebsiteProxyDel) (err error)
|
||||
UpdateProxyStatus(req request.WebsiteProxyStatusUpdate) (err error)
|
||||
|
||||
CreateWebsiteDomain(create request.WebsiteDomainCreate) ([]model.WebsiteDomain, error)
|
||||
GetWebsiteDomain(websiteId uint) ([]model.WebsiteDomain, error)
|
||||
@@ -173,11 +174,10 @@ func (w WebsiteService) PageWebsite(req request.WebsiteSearch) (int64, []respons
|
||||
switch web.Type {
|
||||
case constant.Deployment:
|
||||
appInstall, err := appInstallRepo.GetFirst(repo.WithByID(web.AppInstallID))
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
if err == nil {
|
||||
appName = appInstall.Name
|
||||
appInstallID = appInstall.ID
|
||||
}
|
||||
appName = appInstall.Name
|
||||
appInstallID = appInstall.ID
|
||||
case constant.Runtime:
|
||||
runtime, _ := runtimeRepo.GetFirst(context.Background(), repo.WithByID(web.RuntimeID))
|
||||
if runtime != nil {
|
||||
@@ -389,7 +389,10 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
||||
}
|
||||
appInstall = install
|
||||
website.AppInstallID = install.ID
|
||||
website.Proxy = fmt.Sprintf("127.0.0.1:%d", appInstall.HttpPort)
|
||||
website.Proxy, err = getAppInstallProxyPass(appInstall)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
var install model.AppInstall
|
||||
install, err = appInstallRepo.GetFirst(repo.WithByID(create.AppInstallID))
|
||||
@@ -399,7 +402,10 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
|
||||
configApp := func(t *task.Task) error {
|
||||
appInstall = &install
|
||||
website.AppInstallID = appInstall.ID
|
||||
website.Proxy = fmt.Sprintf("127.0.0.1:%d", appInstall.HttpPort)
|
||||
website.Proxy, err = getAppInstallProxyPass(appInstall)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
createTask.AddSubTask(i18n.GetMsgByKey("ConfigApp"), configApp, nil)
|
||||
@@ -1325,7 +1331,7 @@ func (w WebsiteService) UpdateSitePermission(req request.WebsiteUpdateDirPermiss
|
||||
}
|
||||
absoluteIndexPath := GetSitePath(website, SiteIndexDir)
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(10 * time.Second))
|
||||
if err := cmdMgr.RunBashCf("%s chown -R %s:%s %s", cmd.SudoHandleCmd(), req.User, req.Group, absoluteIndexPath); err != nil {
|
||||
if err := cmdMgr.Run("chown", "-R", fmt.Sprintf("%s:%s", req.User, req.Group), absoluteIndexPath); err != nil {
|
||||
return err
|
||||
}
|
||||
website.User = req.User
|
||||
@@ -1488,23 +1494,11 @@ func (w WebsiteService) UpdateAntiLeech(req request.NginxAntiLeechUpdate) (err e
|
||||
newBlock.AppendDirectives(ifDir)
|
||||
}
|
||||
if website.Type == constant.Deployment {
|
||||
newBlock.AppendDirectives(
|
||||
&components.Directive{
|
||||
Name: "proxy_set_header",
|
||||
Parameters: []string{"Host", "$host"},
|
||||
},
|
||||
&components.Directive{
|
||||
Name: "proxy_set_header",
|
||||
Parameters: []string{"X-Real-IP", "$remote_addr"},
|
||||
},
|
||||
&components.Directive{
|
||||
Name: "proxy_set_header",
|
||||
Parameters: []string{"X-Forwarded-For", "$proxy_add_x_forwarded_for"},
|
||||
},
|
||||
&components.Directive{
|
||||
Name: "proxy_pass",
|
||||
Parameters: []string{fmt.Sprintf("http://%s", website.Proxy)},
|
||||
})
|
||||
proxyDirectives := getRootProxyDirectives(website.Proxy)
|
||||
if len(proxyDirectives) == 0 {
|
||||
return errors.New("failed to build deployment proxy directives")
|
||||
}
|
||||
newBlock.AppendDirectives(proxyDirectives...)
|
||||
}
|
||||
newDirective.Block = newBlock
|
||||
index := -1
|
||||
@@ -1750,7 +1744,7 @@ func (w WebsiteService) OperateRedirect(req request.NginxRedirectReq) (err error
|
||||
return buserr.WithErr("ErrUpdateBuWebsite", err)
|
||||
}
|
||||
|
||||
nginxInclude := fmt.Sprintf("/www/sites/%s/redirect/*.conf", website.Alias)
|
||||
nginxInclude := getWebsiteRedirectInclude(website)
|
||||
if err = updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: "include", Params: []string{nginxInclude}}}, &website); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -1906,6 +1900,10 @@ func (w WebsiteService) UpdateRedirectFile(req request.NginxRedirectUpdate) (err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
safeName := path.Base(req.Name)
|
||||
if safeName != req.Name || strings.Contains(safeName, "..") {
|
||||
return buserr.New("ErrInvalidParams")
|
||||
}
|
||||
absolutePath := path.Join(GetSitePath(website, SiteRedirectDir), req.Name+".conf")
|
||||
fileOp := files.NewFileOp()
|
||||
oldRewriteContent, err = fileOp.GetContent(absolutePath)
|
||||
@@ -2324,6 +2322,7 @@ func (w WebsiteService) ExecComposer(req request.ExecComposerReq) error {
|
||||
} else {
|
||||
command = req.ExtCommand
|
||||
}
|
||||
command = strings.TrimSpace(command)
|
||||
resourceName := fmt.Sprintf("composer %s", command)
|
||||
composerTask, err := task.NewTaskWithOps(resourceName, task.TaskExec, req.Command, req.TaskID, website.ID)
|
||||
if err != nil {
|
||||
@@ -2333,9 +2332,25 @@ func (w WebsiteService) ExecComposer(req request.ExecComposerReq) error {
|
||||
siteDir, _ := settingRepo.Get(settingRepo.WithByKey("WEBSITE_DIR"))
|
||||
execDir := strings.ReplaceAll(req.Dir, siteDir.Value, "/www")
|
||||
composerTask.AddSubTask("", func(t *task.Task) error {
|
||||
cmdStr := fmt.Sprintf("docker exec -u %s %s sh -c 'composer config -g repo.packagist composer %s && composer %s --working-dir=%s'", req.User, runtime.ContainerName, req.Mirror, command, execDir)
|
||||
err = cmdMgr.RunBashC(cmdStr)
|
||||
if err != nil {
|
||||
if err := cmdMgr.Run("docker", "exec",
|
||||
"-u", req.User,
|
||||
runtime.ContainerName,
|
||||
"composer",
|
||||
"config", "-g",
|
||||
"repo.packagist",
|
||||
"composer",
|
||||
req.Mirror,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := cmdMgr.Run("docker", "exec",
|
||||
"-u", req.User,
|
||||
runtime.ContainerName,
|
||||
"composer",
|
||||
command,
|
||||
"--working-dir="+execDir,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -3,6 +3,10 @@ package service
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
|
||||
@@ -16,9 +20,6 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/nginx/components"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (w WebsiteService) GetAuthBasics(req request.NginxAuthReq) (res response.NginxAuthRes, err error) {
|
||||
@@ -260,6 +261,10 @@ func (w WebsiteService) UpdatePathAuthBasic(req request.NginxPathAuthUpdate) err
|
||||
if !fileOp.Stat(passDir) {
|
||||
_ = fileOp.CreateDir(passDir, constant.DirPerm)
|
||||
}
|
||||
safeName := path.Base(req.Name)
|
||||
if safeName != req.Name || strings.Contains(safeName, "..") {
|
||||
return buserr.New("ErrInvalidParams")
|
||||
}
|
||||
confPath := path.Join(authDir, fmt.Sprintf("%s.conf", req.Name))
|
||||
passPath := path.Join(passDir, fmt.Sprintf("%s.pass", req.Name))
|
||||
var config *components.Config
|
||||
@@ -306,7 +311,7 @@ func (w WebsiteService) UpdatePathAuthBasic(req request.NginxPathAuthUpdate) err
|
||||
}
|
||||
nginxInclude := fmt.Sprintf("/www/sites/%s/path_auth/*.conf", website.Alias)
|
||||
if err = updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: "include", Params: []string{nginxInclude}}}, &website); err != nil {
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@ package service
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
||||
@@ -13,9 +17,6 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/nginx"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/nginx/components"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (w WebsiteService) GetLoadBalances(id uint) ([]dto.NginxUpstream, error) {
|
||||
@@ -87,6 +88,10 @@ func (w WebsiteService) CreateLoadBalance(req request.WebsiteLBCreate) error {
|
||||
if !fileOp.Stat(includeDir) {
|
||||
_ = fileOp.CreateDir(includeDir, constant.DirPerm)
|
||||
}
|
||||
safeName := path.Base(req.Name)
|
||||
if safeName != req.Name || strings.Contains(safeName, "..") {
|
||||
return buserr.New("ErrInvalidParams")
|
||||
}
|
||||
filePath := path.Join(includeDir, fmt.Sprintf("%s.conf", req.Name))
|
||||
if fileOp.Stat(filePath) {
|
||||
return buserr.New("ErrNameIsExist")
|
||||
@@ -133,6 +138,10 @@ func (w WebsiteService) UpdateLoadBalance(req request.WebsiteLBUpdate) error {
|
||||
}
|
||||
includeDir := GetSitePath(website, SiteUpstreamDir)
|
||||
fileOp := files.NewFileOp()
|
||||
safeName := path.Base(req.Name)
|
||||
if safeName != req.Name || strings.Contains(safeName, "..") {
|
||||
return buserr.New("ErrInvalidParams")
|
||||
}
|
||||
filePath := path.Join(includeDir, fmt.Sprintf("%s.conf", req.Name))
|
||||
if !fileOp.Stat(filePath) {
|
||||
return nil
|
||||
@@ -191,6 +200,10 @@ func (w WebsiteService) DeleteLoadBalance(req request.WebsiteLBDelete) error {
|
||||
|
||||
includeDir := GetSitePath(website, SiteUpstreamDir)
|
||||
fileOp := files.NewFileOp()
|
||||
safeName := path.Base(req.Name)
|
||||
if safeName != req.Name || strings.Contains(safeName, "..") {
|
||||
return buserr.New("ErrInvalidParams")
|
||||
}
|
||||
filePath := path.Join(includeDir, fmt.Sprintf("%s.conf", req.Name))
|
||||
if !fileOp.Stat(filePath) {
|
||||
return nil
|
||||
@@ -211,6 +224,10 @@ func (w WebsiteService) UpdateLoadBalanceFile(req request.WebsiteLBUpdateFile) e
|
||||
return err
|
||||
}
|
||||
includeDir := GetSitePath(website, SiteUpstreamDir)
|
||||
safeName := path.Base(req.Name)
|
||||
if safeName != req.Name || strings.Contains(safeName, "..") {
|
||||
return buserr.New("ErrInvalidParams")
|
||||
}
|
||||
filePath := path.Join(includeDir, fmt.Sprintf("%s.conf", req.Name))
|
||||
fileOp := files.NewFileOp()
|
||||
oldContent, err := fileOp.GetContent(filePath)
|
||||
|
||||
@@ -24,6 +24,21 @@ import (
|
||||
)
|
||||
|
||||
func (w WebsiteService) OperateProxy(req request.WebsiteProxyConfig) (err error) {
|
||||
switch req.Operate {
|
||||
case "delete":
|
||||
return w.DeleteProxy(request.WebsiteProxyDel{
|
||||
ID: req.ID,
|
||||
Name: req.Name,
|
||||
})
|
||||
case "disable":
|
||||
fallthrough
|
||||
case "enable":
|
||||
return w.UpdateProxyStatus(request.WebsiteProxyStatusUpdate{
|
||||
ID: req.ID,
|
||||
Name: req.Name,
|
||||
Status: req.Operate,
|
||||
})
|
||||
}
|
||||
var (
|
||||
website model.Website
|
||||
par *parser.Parser
|
||||
@@ -81,16 +96,8 @@ func (w WebsiteService) OperateProxy(req request.WebsiteProxyConfig) (err error)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case "delete":
|
||||
_ = fileOp.DeleteFile(includePath)
|
||||
_ = fileOp.DeleteFile(backPath)
|
||||
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
|
||||
case "disable":
|
||||
_ = fileOp.Rename(includePath, backPath)
|
||||
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
|
||||
case "enable":
|
||||
_ = fileOp.Rename(backPath, includePath)
|
||||
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
|
||||
default:
|
||||
return errors.New("unknown operate")
|
||||
}
|
||||
|
||||
config.FilePath = includePath
|
||||
@@ -107,7 +114,14 @@ func (w WebsiteService) OperateProxy(req request.WebsiteProxyConfig) (err error)
|
||||
err = errors.New("invalid proxy config, no location found")
|
||||
return
|
||||
}
|
||||
location.UpdateDirective("proxy_pass", []string{req.ProxyPass})
|
||||
applyLocationProxyPass(location, req.ProxyPass, &req.SNI, req.ProxySSLName)
|
||||
if isHTTPSProxyPass(req.ProxyPass) && req.SSLVerify {
|
||||
location.UpdateDirective("proxy_ssl_verify", []string{"on"})
|
||||
location.UpdateDirective("proxy_ssl_trusted_certificate", []string{"/etc/ssl/certs/ca-certificates.crt"})
|
||||
} else {
|
||||
location.RemoveDirective("proxy_ssl_verify", []string{})
|
||||
location.RemoveDirective("proxy_ssl_trusted_certificate", []string{})
|
||||
}
|
||||
location.UpdateDirective("proxy_set_header", []string{"Host", req.ProxyHost})
|
||||
location.ChangePath(req.Modifier, req.Match)
|
||||
// Server Cache Settings
|
||||
@@ -136,15 +150,7 @@ func (w WebsiteService) OperateProxy(req request.WebsiteProxyConfig) (err error)
|
||||
} else {
|
||||
location.RemoveSubFilter()
|
||||
}
|
||||
// SSL Settings
|
||||
if req.SNI {
|
||||
location.UpdateDirective("proxy_ssl_server_name", []string{"on"})
|
||||
if req.ProxySSLName != "" {
|
||||
location.UpdateDirective("proxy_ssl_name", []string{req.ProxySSLName})
|
||||
}
|
||||
} else {
|
||||
location.UpdateDirective("proxy_ssl_server_name", []string{"off"})
|
||||
}
|
||||
// Explicit SNI configuration still takes precedence over the automatic HTTPS upstream default.
|
||||
// CORS Settings
|
||||
if req.Cors {
|
||||
location.UpdateDirective("add_header", []string{"Access-Control-Allow-Origin", req.AllowOrigins, "always"})
|
||||
@@ -330,6 +336,9 @@ func (w WebsiteService) GetProxies(id uint) (res []request.WebsiteProxyConfig, e
|
||||
if directive.GetName() == "proxy_ssl_name" && len(directive.GetParameters()) > 0 {
|
||||
proxyConfig.ProxySSLName = directive.GetParameters()[0]
|
||||
}
|
||||
if directive.GetName() == "proxy_ssl_verify" {
|
||||
proxyConfig.SSLVerify = len(directive.GetParameters()) > 0 && directive.GetParameters()[0] == "on"
|
||||
}
|
||||
}
|
||||
proxyConfig.Cors = location.Cors
|
||||
proxyConfig.AllowCredentials = location.AllowCredentials
|
||||
@@ -391,24 +400,47 @@ func (w WebsiteService) ClearProxyCache(req request.NginxCommonReq) error {
|
||||
}
|
||||
|
||||
func (w WebsiteService) DeleteProxy(req request.WebsiteProxyDel) (err error) {
|
||||
fileOp := files.NewFileOp()
|
||||
website, err := websiteRepo.GetFirst(repo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
includeDir := path.Join(nginxInstall.GetPath(), "www", "sites", website.Alias, "proxy")
|
||||
includeDir := GetSitePath(website, SiteProxyDir)
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(includeDir) {
|
||||
_ = fileOp.CreateDir(includeDir, 0755)
|
||||
return
|
||||
}
|
||||
fileName := fmt.Sprintf("%s.conf", req.Name)
|
||||
includePath := path.Join(includeDir, fileName)
|
||||
backName := fmt.Sprintf("%s.bak", req.Name)
|
||||
backPath := path.Join(includeDir, backName)
|
||||
|
||||
_ = fileOp.DeleteFile(includePath)
|
||||
_ = fileOp.DeleteFile(backPath)
|
||||
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
|
||||
}
|
||||
|
||||
func (w WebsiteService) UpdateProxyStatus(req request.WebsiteProxyStatusUpdate) (err error) {
|
||||
website, err := websiteRepo.GetFirst(repo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
includeDir := GetSitePath(website, SiteProxyDir)
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(includeDir) {
|
||||
return
|
||||
}
|
||||
fileName := fmt.Sprintf("%s.conf", req.Name)
|
||||
includePath := path.Join(includeDir, fileName)
|
||||
backName := fmt.Sprintf("%s.bak", req.Name)
|
||||
backPath := path.Join(includeDir, backName)
|
||||
|
||||
switch req.Status {
|
||||
case "disable":
|
||||
_ = fileOp.Rename(includePath, backPath)
|
||||
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
|
||||
case "enable":
|
||||
_ = fileOp.Rename(backPath, includePath)
|
||||
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
|
||||
}
|
||||
return errors.New("unknown status")
|
||||
}
|
||||
|
||||
@@ -4,6 +4,10 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
|
||||
@@ -12,9 +16,6 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (w WebsiteService) UpdateRewriteConfig(req request.NginxRewriteUpdate) error {
|
||||
@@ -73,6 +74,10 @@ func (w WebsiteService) GetRewriteConfig(req request.NginxRewriteReq) (*response
|
||||
contentByte, _ = nginx_conf.Rewrites.ReadFile(rewriteFile)
|
||||
if contentByte == nil {
|
||||
customRewriteDir := GetOpenrestyDir(DefaultRewriteDir)
|
||||
safeName := path.Base(req.Name)
|
||||
if safeName != req.Name || strings.Contains(safeName, "..") {
|
||||
return nil, buserr.New("ErrInvalidParams")
|
||||
}
|
||||
customRewriteFile := path.Join(customRewriteDir, fmt.Sprintf("%s.conf", strings.ToLower(req.Name)))
|
||||
contentByte, err = files.NewFileOp().GetContent(customRewriteFile)
|
||||
}
|
||||
@@ -90,6 +95,10 @@ func (w WebsiteService) OperateCustomRewrite(req request.CustomRewriteOperate) e
|
||||
return err
|
||||
}
|
||||
}
|
||||
safeName := path.Base(req.Name)
|
||||
if safeName != req.Name || strings.Contains(safeName, "..") {
|
||||
return buserr.New("ErrInvalidParams")
|
||||
}
|
||||
rewriteFile := path.Join(rewriteDir, fmt.Sprintf("%s.conf", req.Name))
|
||||
switch req.Operate {
|
||||
case "create":
|
||||
|
||||
@@ -47,6 +47,82 @@ func handleChineseDomain(domain string) (string, error) {
|
||||
return domain, nil
|
||||
}
|
||||
|
||||
func isHTTPSProxyPass(proxyPass string) bool {
|
||||
return strings.HasPrefix(strings.ToLower(strings.TrimSpace(proxyPass)), "https://")
|
||||
}
|
||||
|
||||
func normalizeProxyPass(proxyPass string) string {
|
||||
proxyPass = strings.TrimSpace(proxyPass)
|
||||
if proxyPass == "" {
|
||||
return ""
|
||||
}
|
||||
if strings.Contains(proxyPass, "://") || strings.HasPrefix(proxyPass, "unix:") {
|
||||
return proxyPass
|
||||
}
|
||||
return fmt.Sprintf("http://%s", proxyPass)
|
||||
}
|
||||
|
||||
func getAppInstallProxyPass(appInstall *model.AppInstall) (string, error) {
|
||||
if appInstall == nil {
|
||||
return "", errors.New("app install is nil")
|
||||
}
|
||||
if appInstall.HttpPort > 0 {
|
||||
return fmt.Sprintf("http://127.0.0.1:%d", appInstall.HttpPort), nil
|
||||
}
|
||||
if appInstall.HttpsPort > 0 {
|
||||
return fmt.Sprintf("https://127.0.0.1:%d", appInstall.HttpsPort), nil
|
||||
}
|
||||
return "", fmt.Errorf("app %s has no available http or https port", appInstall.Name)
|
||||
}
|
||||
|
||||
func getAppInstallProxyPassOrEmpty(appInstall *model.AppInstall) string {
|
||||
proxyPass, err := getAppInstallProxyPass(appInstall)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return proxyPass
|
||||
}
|
||||
|
||||
func hasAppInstallProxyPassChanged(before *model.AppInstall, after *model.AppInstall) bool {
|
||||
return getAppInstallProxyPassOrEmpty(before) != getAppInstallProxyPassOrEmpty(after)
|
||||
}
|
||||
|
||||
func getRootProxyDirectives(proxyPass string) []components.IDirective {
|
||||
server := &components.Server{}
|
||||
server.UpdateRootProxy([]string{normalizeProxyPass(proxyPass)})
|
||||
|
||||
locations := server.FindDirectives("location")
|
||||
if len(locations) == 0 || locations[0].GetBlock() == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return append([]components.IDirective(nil), locations[0].GetBlock().GetDirectives()...)
|
||||
}
|
||||
|
||||
func getWebsiteRedirectInclude(website model.Website) string {
|
||||
return fmt.Sprintf("/www/sites/%s/redirect/*.conf", website.Alias)
|
||||
}
|
||||
|
||||
func applyLocationProxyPass(location *components.Location, proxyPass string, sni *bool, proxySSLName string) {
|
||||
location.UpdateDirective("proxy_pass", []string{proxyPass})
|
||||
|
||||
enableSNI := isHTTPSProxyPass(proxyPass)
|
||||
if sni != nil {
|
||||
enableSNI = enableSNI && *sni
|
||||
}
|
||||
if enableSNI {
|
||||
location.UpdateDirective("proxy_ssl_server_name", []string{"on"})
|
||||
} else {
|
||||
location.UpdateDirective("proxy_ssl_server_name", []string{"off"})
|
||||
}
|
||||
|
||||
sslName := "$proxy_host"
|
||||
if proxySSLName != "" {
|
||||
sslName = proxySSLName
|
||||
}
|
||||
location.UpdateDirective("proxy_ssl_name", []string{sslName})
|
||||
}
|
||||
|
||||
func createIndexFile(website *model.Website, runtime *model.Runtime) error {
|
||||
var (
|
||||
indexPath string
|
||||
@@ -122,7 +198,7 @@ func createProxyFile(website *model.Website) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
location.ChangePath("^~", "/")
|
||||
location.UpdateDirective("proxy_pass", []string{website.Proxy})
|
||||
applyLocationProxyPass(location, website.Proxy, nil, "")
|
||||
location.UpdateDirective("proxy_set_header", []string{"Host", "$host"})
|
||||
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
return buserr.WithErr("ErrUpdateBuWebsite", err)
|
||||
@@ -257,7 +333,10 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
|
||||
rootIndex := path.Join("/www/sites", website.Alias, "index")
|
||||
switch website.Type {
|
||||
case constant.Deployment:
|
||||
proxy := fmt.Sprintf("http://127.0.0.1:%d", appInstall.HttpPort)
|
||||
proxy, err := getAppInstallProxyPass(appInstall)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
server.UpdateRootProxy([]string{proxy})
|
||||
case constant.Static:
|
||||
server.UpdateRoot(rootIndex)
|
||||
@@ -279,7 +358,7 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
|
||||
server.UpdatePHPProxy([]string{website.Proxy}, "")
|
||||
}
|
||||
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython, constant.RuntimeDotNet:
|
||||
server.UpdateRootProxy([]string{fmt.Sprintf("http://%s", website.Proxy)})
|
||||
server.UpdateRootProxy([]string{normalizeProxyPass(website.Proxy)})
|
||||
}
|
||||
case constant.Subsite:
|
||||
parentWebsite, err := websiteRepo.GetFirst(repo.WithByID(website.ParentWebsiteID))
|
||||
@@ -977,7 +1056,7 @@ func opWebsite(website *model.Website, operate string) error {
|
||||
server.RemoveDirective("include", []string{proxyInclude})
|
||||
rewriteInclude := fmt.Sprintf("/www/sites/%s/rewrite/%s.conf", website.Alias, website.Alias)
|
||||
server.RemoveDirective("include", []string{rewriteInclude})
|
||||
redirectInclude := fmt.Sprintf("/www/sites/%s/redirect/%s.conf", website.Alias, website.Alias)
|
||||
redirectInclude := getWebsiteRedirectInclude(*website)
|
||||
server.RemoveDirective("include", []string{redirectInclude})
|
||||
|
||||
switch website.Type {
|
||||
@@ -1009,7 +1088,7 @@ func opWebsite(website *model.Website, operate string) error {
|
||||
if fileOp.Stat(absoluteRewritePath) {
|
||||
server.UpdateDirective("include", []string{rewriteInclude})
|
||||
}
|
||||
redirectInclude := fmt.Sprintf("/www/sites/%s/redirect/%s.conf", website.Alias, website.Alias)
|
||||
redirectInclude := getWebsiteRedirectInclude(*website)
|
||||
absoluteRedirectPath := GetSitePath(*website, SiteRedirectDir)
|
||||
if fileOp.Stat(absoluteRedirectPath) {
|
||||
server.UpdateDirective("include", []string{redirectInclude})
|
||||
@@ -1025,7 +1104,10 @@ func opWebsite(website *model.Website, operate string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
proxy := fmt.Sprintf("http://127.0.0.1:%d", appInstall.HttpPort)
|
||||
proxy, err := getAppInstallProxyPass(&appInstall)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
server.UpdateRootProxy([]string{proxy})
|
||||
case constant.Static:
|
||||
server.UpdateRoot(rootIndex)
|
||||
@@ -1045,9 +1127,34 @@ func opWebsite(website *model.Website, operate string) error {
|
||||
}
|
||||
server.UpdatePHPProxy([]string{website.Proxy}, localPath)
|
||||
} else {
|
||||
proxy := fmt.Sprintf("http://%s", website.Proxy)
|
||||
proxy := normalizeProxyPass(website.Proxy)
|
||||
server.UpdateRootProxy([]string{proxy})
|
||||
}
|
||||
case constant.Subsite:
|
||||
parentWebsite, err := websiteRepo.GetFirst(repo.WithByID(website.ParentWebsiteID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
website.Proxy = parentWebsite.Proxy
|
||||
rootIndex = path.Join("/www/sites", parentWebsite.Alias, "index", website.SiteDir)
|
||||
if parentWebsite.Type == constant.Runtime {
|
||||
parentRuntime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(parentWebsite.RuntimeID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
website.RuntimeID = parentRuntime.ID
|
||||
if parentRuntime.Type == constant.RuntimePHP {
|
||||
server.UpdateRoot(rootIndex)
|
||||
localPath := ""
|
||||
if parentRuntime.Resource == constant.ResourceLocal {
|
||||
localPath = path.Join(rootIndex, "index.php")
|
||||
}
|
||||
server.UpdatePHPProxy([]string{website.Proxy}, localPath)
|
||||
}
|
||||
}
|
||||
if parentWebsite.Type == constant.Static {
|
||||
server.UpdateRoot(rootIndex)
|
||||
}
|
||||
}
|
||||
website.Status = constant.WebRunning
|
||||
now := time.Now()
|
||||
|
||||
@@ -275,7 +275,8 @@ func (t *Task) Execute() error {
|
||||
}
|
||||
var err error
|
||||
t.Log(i18n.GetWithName("TaskStart", t.Name))
|
||||
for _, subTask := range t.SubTasks {
|
||||
for i := 0; i < len(t.SubTasks); i++ {
|
||||
subTask := t.SubTasks[i]
|
||||
t.Task.CurrentStep = subTask.StepAlias
|
||||
t.updateTask(t.Task)
|
||||
if err = subTask.Execute(); err == nil {
|
||||
|
||||
@@ -4,11 +4,6 @@ base:
|
||||
is_demo: false
|
||||
is_offline: false
|
||||
|
||||
remote_url:
|
||||
app_repo: https://apps-assets.fit2cloud.com
|
||||
repo_url: https://resource.fit2cloud.com/1panel/package/v2
|
||||
resource_url: https://resource.fit2cloud.com/1panel/resource/v2
|
||||
|
||||
log:
|
||||
level: debug
|
||||
time_zone: Asia/Shanghai
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package docs
|
||||
|
||||
import _ "embed"
|
||||
|
||||
//go:embed x-log.json
|
||||
var XLogJson []byte
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,6 @@ package nginx_conf
|
||||
|
||||
import (
|
||||
"embed"
|
||||
_ "embed"
|
||||
"io"
|
||||
)
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ const (
|
||||
|
||||
AppOpenresty = "openresty"
|
||||
AppOpenclaw = "openclaw"
|
||||
AppCopaw = "copaw"
|
||||
AppMysql = "mysql"
|
||||
AppMariaDB = "mariadb"
|
||||
AppPostgresql = "postgresql"
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package constant
|
||||
|
||||
const DefaultTerminalAIPrefix = "@ai"
|
||||
|
||||
const DefaultTerminalAIRiskCommands = "[\"rm\",\"mkfs\",\"dd if=\",\"dd of=/dev/\",\"wipefs\",\"fdisk\",\"parted\",\"sfdisk\",\"shred\",\"curl | sh\",\"wget | sh\",\"chmod -R 777 /\",\"shutdown\",\"reboot\",\"poweroff\",\"init 0\",\":(){ :|:& };:\"]"
|
||||
+13
-2
@@ -1,8 +1,9 @@
|
||||
package cron
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
mathRand "math/rand"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
@@ -43,7 +44,17 @@ func Run() {
|
||||
if _, err := global.Cron.AddJob("@daily", job.NewSSLJob()); err != nil {
|
||||
global.LOG.Errorf("can not add ssl corn job: %s", err.Error())
|
||||
}
|
||||
if _, err := global.Cron.AddJob(fmt.Sprintf("%v %v * * *", mathRand.Intn(60), mathRand.Intn(3)), job.NewAppStoreJob()); err != nil {
|
||||
minuteRand, err := rand.Int(rand.Reader, big.NewInt(60))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("generate random minute failed: %v", err)
|
||||
minuteRand = big.NewInt(0)
|
||||
}
|
||||
hourRand, err := rand.Int(rand.Reader, big.NewInt(3))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("generate random hour failed: %v", err)
|
||||
hourRand = big.NewInt(0)
|
||||
}
|
||||
if _, err := global.Cron.AddJob(fmt.Sprintf("%v %v * * *", minuteRand.Int64(), hourRand.Int64()), job.NewAppStoreJob()); err != nil {
|
||||
global.LOG.Errorf("can not add appstore corn job: %s", err.Error())
|
||||
}
|
||||
if _, err := global.Cron.AddJob("0 3 */31 * *", job.NewBackupJob()); err != nil {
|
||||
|
||||
@@ -2,13 +2,13 @@ package global
|
||||
|
||||
type ServerConfig struct {
|
||||
Base Base `mapstructure:"base"`
|
||||
RemoteURL RemoteURL `mapstructure:"remote_url"`
|
||||
Log LogConfig `mapstructure:"log"`
|
||||
DockerConfig DockerConfig
|
||||
}
|
||||
|
||||
type Base struct {
|
||||
Port string `mapstructure:"port"`
|
||||
Edition string `mapstructure:"edition"` // [ cn / intl]
|
||||
Version string `mapstructure:"version"`
|
||||
EncryptKey string `mapstructure:"encrypt_key"`
|
||||
Mode string `mapstructure:"mode"` // xpack [ Enable / Disable ]
|
||||
@@ -17,12 +17,6 @@ type Base struct {
|
||||
IsOffLine bool `mapstructure:"is_offline"`
|
||||
}
|
||||
|
||||
type RemoteURL struct {
|
||||
AppRepo string `mapstructure:"app_repo"`
|
||||
RepoUrl string `mapstructure:"repo_url"`
|
||||
ResourceUrl string `mapstructure:"resource_url"`
|
||||
}
|
||||
|
||||
type SystemDir struct {
|
||||
BaseDir string
|
||||
DbDir string
|
||||
|
||||
@@ -40,3 +40,22 @@ var (
|
||||
|
||||
TaskCtxMap = make(map[string]context.CancelFunc)
|
||||
)
|
||||
|
||||
func RepoURL() string {
|
||||
if CONF.Base.Edition != "intl" {
|
||||
return "https://resource.fit2cloud.com/1panel/package/v2"
|
||||
}
|
||||
return "https://resource.1panel.pro/v2"
|
||||
}
|
||||
func ResourceURL() string {
|
||||
if CONF.Base.Edition != "intl" {
|
||||
return "https://resource.fit2cloud.com/1panel/resource/v2"
|
||||
}
|
||||
return "https://resource.1panel.pro/v2/resource"
|
||||
}
|
||||
func AppRepoURL() string {
|
||||
if CONF.Base.Edition != "intl" {
|
||||
return "https://apps-assets.fit2cloud.com"
|
||||
}
|
||||
return "https://apps.1panel.pro"
|
||||
}
|
||||
|
||||
+54
-45
@@ -1,14 +1,17 @@
|
||||
module github.com/1Panel-dev/1Panel/agent
|
||||
|
||||
go 1.24.9
|
||||
go 1.25.7
|
||||
|
||||
replace github.com/moby/go-archive => github.com/moby/go-archive v0.1.0
|
||||
|
||||
require (
|
||||
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
|
||||
github.com/aws/aws-sdk-go v1.55.0
|
||||
github.com/compose-spec/compose-go/v2 v2.9.0
|
||||
github.com/compose-spec/compose-go/v2 v2.9.1
|
||||
github.com/creack/pty v1.1.24
|
||||
github.com/docker/compose/v2 v2.40.2
|
||||
github.com/docker/docker v28.5.1+incompatible
|
||||
github.com/docker/cli v29.2.0+incompatible
|
||||
github.com/docker/compose/v2 v2.40.3
|
||||
github.com/docker/docker v28.5.2+incompatible
|
||||
github.com/docker/go-connections v0.6.0
|
||||
github.com/fsnotify/fsnotify v1.9.0
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
@@ -18,15 +21,15 @@ require (
|
||||
github.com/go-playground/validator/v10 v10.26.0
|
||||
github.com/go-redis/redis v6.15.9+incompatible
|
||||
github.com/go-resty/resty/v2 v2.17.0
|
||||
github.com/go-sql-driver/mysql v1.8.1
|
||||
github.com/go-sql-driver/mysql v1.9.3
|
||||
github.com/goh-chunlin/go-onedrive v1.1.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674
|
||||
github.com/jackc/pgx/v5 v5.6.0
|
||||
github.com/jackc/pgx/v5 v5.7.5
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/jinzhu/gorm v1.9.16
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/klauspost/compress v1.18.1
|
||||
github.com/klauspost/compress v1.18.2
|
||||
github.com/mholt/archiver/v4 v4.0.0-alpha.8
|
||||
github.com/miekg/dns v1.1.69
|
||||
github.com/minio/minio-go/v7 v7.0.74
|
||||
@@ -36,12 +39,12 @@ require (
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pkg/sftp v1.13.6
|
||||
github.com/qiniu/go-sdk/v7 v7.21.1
|
||||
github.com/qiniu/go-sdk/v7 v7.26.4
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/shirou/gopsutil/v4 v4.25.11
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/sirupsen/logrus v1.9.4
|
||||
github.com/spf13/afero v1.11.0
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/spf13/viper v1.19.0
|
||||
github.com/subosito/gotenv v1.6.0
|
||||
github.com/tencentyun/cos-go-sdk-v5 v0.7.54
|
||||
@@ -50,7 +53,7 @@ require (
|
||||
golang.org/x/crypto v0.46.0
|
||||
golang.org/x/net v0.48.0
|
||||
golang.org/x/oauth2 v0.34.0
|
||||
golang.org/x/sys v0.39.0
|
||||
golang.org/x/sys v0.40.0
|
||||
golang.org/x/text v0.32.0
|
||||
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
@@ -59,7 +62,7 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
filippo.io/edwards25519 v1.1.1 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||
github.com/BurntSushi/toml v1.5.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
@@ -94,6 +97,7 @@ require (
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/clbanning/mxj v1.8.4 // indirect
|
||||
github.com/clbanning/mxj/v2 v2.7.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
@@ -101,7 +105,7 @@ require (
|
||||
github.com/connesc/cipherio v0.2.1 // indirect
|
||||
github.com/containerd/console v1.0.5 // indirect
|
||||
github.com/containerd/containerd/api v1.10.0 // indirect
|
||||
github.com/containerd/containerd/v2 v2.2.0 // indirect
|
||||
github.com/containerd/containerd/v2 v2.2.1 // indirect
|
||||
github.com/containerd/continuity v0.4.5 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
@@ -111,17 +115,17 @@ require (
|
||||
github.com/containerd/typeurl/v2 v2.2.3 // indirect
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/buildx v0.29.1 // indirect
|
||||
github.com/docker/cli v28.5.1+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.9.3 // indirect
|
||||
github.com/docker/buildx v0.31.1 // indirect
|
||||
github.com/docker/docker-credential-helpers v0.9.5 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dsnet/compress v0.0.1 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/ebitengine/purego v0.9.1 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fvbommel/sortorder v1.1.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
github.com/gammazero/toposort v0.1.1 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/glebarez/go-sqlite v1.22.0 // indirect
|
||||
github.com/go-acme/alidns-20150109/v4 v4.7.0 // indirect
|
||||
@@ -144,7 +148,7 @@ require (
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect
|
||||
github.com/h2non/filetype v1.1.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
@@ -154,8 +158,8 @@ require (
|
||||
github.com/in-toto/in-toto-golang v0.9.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
@@ -166,18 +170,19 @@ require (
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/magiconair/properties v1.8.9 // indirect
|
||||
github.com/matishsiao/goInfo v0.0.0-20210923090445-da2e3fa8d45f // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mattn/go-shellwords v1.0.12 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/moby/buildkit v0.25.1 // indirect
|
||||
github.com/moby/buildkit v0.27.0 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/go-archive v0.1.0 // indirect
|
||||
github.com/moby/go-archive v0.2.0 // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/moby/api v1.52.0 // indirect
|
||||
github.com/moby/moby/client v0.2.1 // indirect
|
||||
github.com/moby/patternmatcher v0.6.0 // indirect
|
||||
github.com/moby/sys/atomicwriter v0.1.0 // indirect
|
||||
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||
@@ -187,7 +192,7 @@ require (
|
||||
github.com/moby/term v0.5.2 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/morikuni/aec v1.1.0 // indirect
|
||||
github.com/mozillazg/go-httpheader v0.2.1 // indirect
|
||||
github.com/namedotcom/go/v4 v4.0.2 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
@@ -205,13 +210,15 @@ require (
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rs/xid v1.5.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.6.0 // indirect
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.9.1 // indirect
|
||||
github.com/shibumi/go-pathspec v1.3.0 // indirect
|
||||
github.com/sigstore/sigstore v1.10.0 // indirect
|
||||
github.com/sigstore/sigstore-go v1.1.4-0.20251124094504-b5fe07a5a7d7 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/cast v1.7.0 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
@@ -221,7 +228,7 @@ require (
|
||||
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 // indirect
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f // indirect
|
||||
github.com/tonistiigi/fsutil v0.0.0-20251211185533-a2aa163d723f // indirect
|
||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 // indirect
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
|
||||
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab // indirect
|
||||
@@ -231,34 +238,36 @@ require (
|
||||
github.com/volcengine/volc-sdk-golang v1.0.230 // indirect
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.mongodb.org/mongo-driver v1.13.1 // indirect
|
||||
go.mongodb.org/mongo-driver v1.17.6 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.63.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
|
||||
go.opentelemetry.io/otel v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.40.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
|
||||
golang.org/x/mod v0.30.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250911091902-df9299821621 // indirect
|
||||
golang.org/x/mod v0.31.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/term v0.38.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
golang.org/x/tools v0.39.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251103181224-f26f9409b101 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 // indirect
|
||||
google.golang.org/grpc v1.77.0 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
modernc.org/fileutil v1.3.3 // indirect
|
||||
modernc.org/libc v1.66.1 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
|
||||
+114
-34
@@ -31,14 +31,13 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
filippo.io/edwards25519 v1.1.1 h1:YpjwWWlNmGIDyXOn8zLzqiD+9TyIlPhGFG96P39uBpw=
|
||||
filippo.io/edwards25519 v1.1.1/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
@@ -117,6 +116,7 @@ github.com/aliyun/credentials-go v1.4.7 h1:T17dLqEtPUFvjDRRb5giVvLh6dFT8IcNFJJb7
|
||||
github.com/aliyun/credentials-go v1.4.7/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
|
||||
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc=
|
||||
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=
|
||||
github.com/anchore/go-struct-converter v0.1.0 h1:2rDRssAl6mgKBSLNiVCMADgZRhoqtw9dedlWa0OhD30=
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||
@@ -189,7 +189,6 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
@@ -216,16 +215,21 @@ github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUo
|
||||
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
|
||||
github.com/compose-spec/compose-go/v2 v2.9.0 h1:UHSv/QHlo6QJtrT4igF1rdORgIUhDo1gWuyJUoiNNIM=
|
||||
github.com/compose-spec/compose-go/v2 v2.9.0/go.mod h1:Oky9AZGTRB4E+0VbTPZTUu4Kp+oEMMuwZXZtPPVT1iE=
|
||||
github.com/compose-spec/compose-go/v2 v2.9.1 h1:8UwI+ujNU+9Ffkf/YgAm/qM9/eU7Jn8nHzWG721W4rs=
|
||||
github.com/compose-spec/compose-go/v2 v2.9.1/go.mod h1:Oky9AZGTRB4E+0VbTPZTUu4Kp+oEMMuwZXZtPPVT1iE=
|
||||
github.com/connesc/cipherio v0.2.1 h1:FGtpTPMbKNNWByNrr9aEBtaJtXjqOzkIXNYJp6OEycw=
|
||||
github.com/connesc/cipherio v0.2.1/go.mod h1:ukY0MWJDFnJEbXMQtOcn2VmTpRfzcTz4OoVrWGGJZcA=
|
||||
github.com/containerd/cgroups/v3 v3.1.0 h1:azxYVj+91ZgSnIBp2eI3k9y2iYQSR/ZQIgh9vKO+HSY=
|
||||
github.com/containerd/cgroups/v3 v3.1.0/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
|
||||
github.com/containerd/cgroups/v3 v3.1.2 h1:OSosXMtkhI6Qove637tg1XgK4q+DhR0mX8Wi8EhrHa4=
|
||||
github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc=
|
||||
github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
||||
github.com/containerd/containerd/api v1.10.0 h1:5n0oHYVBwN4VhoX9fFykCV9dF1/BvAXeg2F8W6UYq1o=
|
||||
github.com/containerd/containerd/api v1.10.0/go.mod h1:NBm1OAk8ZL+LG8R0ceObGxT5hbUYj7CzTmR3xh0DlMM=
|
||||
github.com/containerd/containerd/v2 v2.2.0 h1:K7TqcXy+LnFmZaui2DgHsnp2gAHhVNWYaHlx7HXfys8=
|
||||
github.com/containerd/containerd/v2 v2.2.0/go.mod h1:YCMjKjA4ZA7egdHNi3/93bJR1+2oniYlnS+c0N62HdE=
|
||||
github.com/containerd/containerd/v2 v2.2.1 h1:TpyxcY4AL5A+07dxETevunVS5zxqzuq7ZqJXknM11yk=
|
||||
github.com/containerd/containerd/v2 v2.2.1/go.mod h1:NR70yW1iDxe84F2iFWbR9xfAN0N2F0NcjTi1OVth4nU=
|
||||
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
|
||||
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
|
||||
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
||||
@@ -238,13 +242,17 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/nydus-snapshotter v0.15.2 h1:qsHI4M+Wwrf6Jr4eBqhNx8qh+YU0dSiJ+WPmcLFWNcg=
|
||||
github.com/containerd/nydus-snapshotter v0.15.2/go.mod h1:FfwH2KBkNYoisK/e+KsmNr7xTU53DmnavQHMFOcXwfM=
|
||||
github.com/containerd/nydus-snapshotter v0.15.4 h1:l59kGRVMtwMLDLh322HsWhEsBCkRKMkGWYV5vBeLYCE=
|
||||
github.com/containerd/nydus-snapshotter v0.15.10 h1:hphjuKOqSHLGznNJiAvmsOWkdu4qFXjf4DzGrWSuIsM=
|
||||
github.com/containerd/platforms v1.0.0-rc.2 h1:0SPgaNZPVWGEi4grZdV8VRYQn78y+nm6acgLGv/QzE4=
|
||||
github.com/containerd/platforms v1.0.0-rc.2/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4=
|
||||
github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y=
|
||||
github.com/containerd/plugin v1.0.0/go.mod h1:hQfJe5nmWfImiqT1q8Si3jLv3ynMUIBB47bQ+KexvO8=
|
||||
github.com/containerd/stargz-snapshotter v0.16.3 h1:zbQMm8dRuPHEOD4OqAYGajJJUwCeUzt4j7w9Iaw58u4=
|
||||
github.com/containerd/stargz-snapshotter v0.17.0 h1:djNS4KU8ztFhLdEDZ1bsfzOiYuVHT6TgSU5qwRk+cNc=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRccTampEyKpjpOnS3CyiV1Ebr8=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.17.0 h1:+TyQIsR/zSFI1Rm31EQBwpAA1ovYgIKHy7kctL3sLcE=
|
||||
github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ=
|
||||
github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
|
||||
github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
|
||||
@@ -256,7 +264,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6N
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
||||
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
||||
github.com/dave/jennifer v1.6.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
@@ -271,16 +278,28 @@ github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxK
|
||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/docker/buildx v0.29.1 h1:58hxM5Z4mnNje3G5NKfULT9xCr8ooM8XFtlfUK9bKaA=
|
||||
github.com/docker/buildx v0.29.1/go.mod h1:J4EFv6oxlPiV1MjO0VyJx2u5tLM7ImDEl9zyB8d4wPI=
|
||||
github.com/docker/buildx v0.30.1 h1:3vthfaTQOLt5QfN2nl7rKuPLUvx69nL5ZikFIXp//c8=
|
||||
github.com/docker/buildx v0.30.1/go.mod h1:8nwT0V6UNYNo9rXq6WO/BQd9KrJ0JYcY/QX6x0y1Oro=
|
||||
github.com/docker/buildx v0.31.1 h1:zbvbrb9nxBNVV8nnI33f2F+4aAZBA1gY+AmeBFflMqY=
|
||||
github.com/docker/buildx v0.31.1/go.mod h1:SD+jYLnt3S4SXqohVtV+8z+dihnOgwMJ8t+bLQvsaCk=
|
||||
github.com/docker/cli v28.5.1+incompatible h1:ESutzBALAD6qyCLqbQSEf1a/U8Ybms5agw59yGVc+yY=
|
||||
github.com/docker/cli v28.5.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli v29.2.0+incompatible h1:9oBd9+YM7rxjZLfyMGxjraKBKE4/nVyvVfN4qNl9XRM=
|
||||
github.com/docker/cli v29.2.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/compose/v2 v2.40.2 h1:h2bDBJkOuqmj93XvT2oI0ArPQonE0lGtWiILXdiXvbA=
|
||||
github.com/docker/compose/v2 v2.40.2/go.mod h1:CbSJpKGw20LInVsPjglZ8z7Squ3OBQOD7Ux5nkjGfIU=
|
||||
github.com/docker/compose/v2 v2.40.3 h1:XeYkQu1svDtyfZPv5nTwFryQ25ZJMkIlc4pz9HalMPI=
|
||||
github.com/docker/compose/v2 v2.40.3/go.mod h1:iNY1tvoHTyN3C3QHCuWAgj3OjR2T6mGkk/qxfbBF/4M=
|
||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v28.5.1+incompatible h1:Bm8DchhSD2J6PsFzxC35TZo4TLGR2PdW/E69rU45NhM=
|
||||
github.com/docker/docker v28.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
|
||||
github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
|
||||
github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
|
||||
github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY=
|
||||
github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q=
|
||||
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
|
||||
@@ -317,6 +336,8 @@ github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGE
|
||||
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
@@ -334,6 +355,8 @@ github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQ
|
||||
github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||
github.com/gammazero/toposort v0.1.1 h1:OivGxsWxF3U3+U80VoLJ+f50HcPU1MIqE1JlKzoJ2Eg=
|
||||
github.com/gammazero/toposort v0.1.1/go.mod h1:H2cozTnNpMw0hg2VHAYsAxmkHXBYroNangj2NTBQDvw=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
@@ -380,15 +403,12 @@ github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvSc
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/go-playground/validator/v10 v10.8.0/go.mod h1:9JhgTzTaE31GZDpH/HSvHiRJrJ3iKAgqqH0Bl/Ocjdk=
|
||||
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
|
||||
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
|
||||
@@ -398,6 +418,8 @@ github.com/go-resty/resty/v2 v2.17.0/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2m
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
||||
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
@@ -408,7 +430,6 @@ github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PU
|
||||
github.com/goccy/go-yaml v1.9.8 h1:5gMyLUeU1/6zl+WFfR1hN7D2kf+1/eRGa7DFtToiBvQ=
|
||||
github.com/goccy/go-yaml v1.9.8/go.mod h1:JubOolP3gh0HpiBc4BLRD4YmjEjHAmIIB2aaXKkTfoE=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
|
||||
github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
@@ -490,6 +511,7 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
@@ -516,6 +538,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4=
|
||||
github.com/h2non/filetype v1.1.1 h1:xvOwnXKAckvtLWsN398qS9QhlxlnVXBjXBydK2/UFB4=
|
||||
github.com/h2non/filetype v1.1.1/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
||||
github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
|
||||
@@ -557,7 +581,6 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.180 h1:uia+R3K1izQRGpxTV+bS4q3/ueMW7ProAMWqM6OlqOU=
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.180/go.mod h1:M+yna96Fx9o5GbIUnF3OvVvQGjgfVSyeJbV9Yb1z/wI=
|
||||
github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo=
|
||||
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU=
|
||||
github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo=
|
||||
@@ -568,10 +591,16 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
|
||||
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
|
||||
github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs=
|
||||
github.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=
|
||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc=
|
||||
github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
|
||||
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
||||
@@ -616,6 +645,8 @@ github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co=
|
||||
github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0=
|
||||
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
@@ -632,7 +663,6 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
@@ -640,7 +670,6 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
|
||||
@@ -649,14 +678,14 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
|
||||
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/matishsiao/goInfo v0.0.0-20210923090445-da2e3fa8d45f h1:B0OD7nYl2FPQEVrw8g2uyc1lGEzNbvrKh7fspGZcbvY=
|
||||
github.com/matishsiao/goInfo v0.0.0-20210923090445-da2e3fa8d45f/go.mod h1:aEt7p9Rvh67BYApmZwNDPpgircTO2kgdmDUoF/1QmwA=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
@@ -703,12 +732,22 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/moby/buildkit v0.25.1 h1:j7IlVkeNbEo+ZLoxdudYCHpmTsbwKvhgc/6UJ/mY/o8=
|
||||
github.com/moby/buildkit v0.25.1/go.mod h1:phM8sdqnvgK2y1dPDnbwI6veUCXHOZ6KFSl6E164tkc=
|
||||
github.com/moby/buildkit v0.26.3 h1:D+ruZVAk/3ipRq5XRxBH9/DIFpRjSlTtMbghT5gQP9g=
|
||||
github.com/moby/buildkit v0.26.3/go.mod h1:4T4wJzQS4kYWIfFRjsbJry4QoxDBjK+UGOEOs1izL7w=
|
||||
github.com/moby/buildkit v0.27.0 h1:1gtNaMcVE0XXCZrybC32L79A7Ga1JeB7V3PfpCt1bDc=
|
||||
github.com/moby/buildkit v0.27.0/go.mod h1:4STUkNc5t1nf03HS+01UmI2X6FdfOI3XaKt9QNoTsms=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=
|
||||
github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo=
|
||||
github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8=
|
||||
github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU=
|
||||
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
|
||||
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
|
||||
github.com/moby/moby/api v1.52.0 h1:00BtlJY4MXkkt84WhUZPRqt5TvPbgig2FZvTbe3igYg=
|
||||
github.com/moby/moby/api v1.52.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=
|
||||
github.com/moby/moby/client v0.2.1 h1:1Grh1552mvv6i+sYOdY+xKKVTvzJegcVMhuXocyDz/k=
|
||||
github.com/moby/moby/client v0.2.1/go.mod h1:O+/tw5d4a1Ha/ZA/tPxIZJapJRUS6LNZ1wiVRxYHyUE=
|
||||
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
|
||||
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
|
||||
@@ -736,6 +775,8 @@ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWu
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/morikuni/aec v1.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ=
|
||||
github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw=
|
||||
github.com/mozillazg/go-httpheader v0.2.1 h1:geV7TrjbL8KXSyvghnFm+NyTux/hxwueTSrwhe88TQQ=
|
||||
github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
@@ -788,8 +829,10 @@ github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJw
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww=
|
||||
github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-spec v1.3.0 h1:YZupQUdctfhpZy3TM39nN9Ika5CBWT5diQ8ibYCRkxg=
|
||||
github.com/opencontainers/selinux v1.12.0 h1:6n5JV4Cf+4y0KNXW48TLj5DwfXpvWlxXplUkdTrmPb8=
|
||||
github.com/opencontainers/selinux v1.12.0/go.mod h1:BTPX+bjVbWGXw7ZZWUbdENt8w0htPSrlgOOysQaU62U=
|
||||
github.com/opencontainers/selinux v1.13.1 h1:A8nNeceYngH9Ow++M+VVEwJVpdFmrlxsN22F+ISDCJE=
|
||||
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||
github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE=
|
||||
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
|
||||
@@ -809,7 +852,6 @@ github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0
|
||||
github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
|
||||
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@@ -854,22 +896,21 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||
github.com/qiniu/dyn v1.3.0/go.mod h1:E8oERcm8TtwJiZvkQPbcAh0RL8jO1G0VXJMW3FAWdkk=
|
||||
github.com/qiniu/go-sdk/v7 v7.21.1 h1:D/IjVOlg5pTw0jeDjqTo6H5QM73Obb1AYfPOHmIFN+Q=
|
||||
github.com/qiniu/go-sdk/v7 v7.21.1/go.mod h1:8EM2awITynlem2VML2dXGHkMYP2UyECsGLOdp6yMpco=
|
||||
github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs=
|
||||
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
|
||||
github.com/qiniu/go-sdk/v7 v7.26.4 h1:D/k6cPbhjKvSx9hVBQSh97GuPu1ZtM3OLPCclSpusjo=
|
||||
github.com/qiniu/go-sdk/v7 v7.26.4/go.mod h1:ri7fGwbio0pRDFr8EK5TUpx0DbnpIMJ2bMSDxGWfCbk=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
||||
@@ -887,18 +928,26 @@ github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCw
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.6.0 h1:T65atpAVCJQK14UA57LMdZGpHi4QYSH/9FZyNGqMYIA=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.6.0/go.mod h1:8Mtpo9JKks/qhPG4HGZ2LGMvrPbzuxwfz/f/zLfEWkk=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.9.1 h1:nZZaNz4DiERIQguNy0cL5qTdn9lR8XKHf4RUyG1Sx3g=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.9.1/go.mod h1:np53YzT0zXGMv6x4iEWc9Z59uR+x+ndLwCLqPYpLXVU=
|
||||
github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI=
|
||||
github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE=
|
||||
github.com/shirou/gopsutil/v4 v4.25.11 h1:X53gB7muL9Gnwwo2evPSE+SfOrltMoR6V3xJAXZILTY=
|
||||
github.com/shirou/gopsutil/v4 v4.25.11/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU=
|
||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sigstore/sigstore v1.10.0 h1:lQrmdzqlR8p9SCfWIpFoGUqdXEzJSZT2X+lTXOMPaQI=
|
||||
github.com/sigstore/sigstore v1.10.0/go.mod h1:Ygq+L/y9Bm3YnjpJTlQrOk/gXyrjkpn3/AEJpmk1n9Y=
|
||||
github.com/sigstore/sigstore-go v1.1.4-0.20251124094504-b5fe07a5a7d7 h1:94NLPmq4bxvdmslzcG670IOkrlS98CGpmob8cjpFHuI=
|
||||
github.com/sigstore/sigstore-go v1.1.4-0.20251124094504-b5fe07a5a7d7/go.mod h1:4r/PNX0G7uzkLpc3PSdYs5E2k4bWEJNXTK6kwAyw9TM=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
||||
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
@@ -908,12 +957,15 @@ github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIK
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spdx/tools-golang v0.5.5 h1:61c0KLfAcNqAjlg6UNMdkwpMernhw3zVRwDZ2x9XOmk=
|
||||
github.com/spdx/tools-golang v0.5.5/go.mod h1:MVIsXx8ZZzaRWNQpUDhC4Dud34edUYJYecciXgrw5vE=
|
||||
github.com/spdx/tools-golang v0.5.7 h1:+sWcKGnhwp3vLdMqPcLdA6QK679vd86cK9hQWH3AwCg=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
||||
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
||||
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
@@ -965,6 +1017,8 @@ github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 h1:r0p7fK5
|
||||
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323/go.mod h1:3Iuxbr0P7D3zUzBMAZB+ois3h/et0shEz0qApgHYGpY=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f h1:MoxeMfHAe5Qj/ySSBfL8A7l1V+hxuluj8owsIEEZipI=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f/go.mod h1:BKdcez7BiVtBvIcef90ZPc6ebqIWr4JWD7+EvLm6J98=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20251211185533-a2aa163d723f h1:Z4NEQ86qFl1mHuCu9gwcE+EYCwDKfXAYXZbdIXyxmEA=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20251211185533-a2aa163d723f/go.mod h1:BKdcez7BiVtBvIcef90ZPc6ebqIWr4JWD7+EvLm6J98=
|
||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 h1:2f304B10LaZdB8kkVEaoXvAMVan2tl9AiK4G0odjQtE=
|
||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0/go.mod h1:278M4p8WsNh3n4a1eqiFcV2FGk7wE5fwUpUom9mK9lE=
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0=
|
||||
@@ -984,6 +1038,7 @@ github.com/upyun/go-sdk v2.1.0+incompatible/go.mod h1:eu3F5Uz4b9ZE5bE5QsCL6mgSNW
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo=
|
||||
github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
|
||||
github.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4=
|
||||
github.com/volcengine/volc-sdk-golang v1.0.230 h1:84/MOF0zUPtAHt3e1+MsFq5qrnQRC+e3XzTUwIOzZxw=
|
||||
github.com/volcengine/volc-sdk-golang v1.0.230/go.mod h1:zHJlaqiMbIB+0mcrsZPTwOb3FB7S/0MCfqlnO8R7hlM=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
@@ -1009,6 +1064,8 @@ go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsX
|
||||
go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0=
|
||||
go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk=
|
||||
go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo=
|
||||
go.mongodb.org/mongo-driver v1.17.6 h1:87JUG1wZfWsr6rIz3ZmpH90rL5tea7O3IHuSwHUpsss=
|
||||
go.mongodb.org/mongo-driver v1.17.6/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
@@ -1021,31 +1078,46 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 h1:0tY123n7CdWMem7MOVdKOt0YfshufLCwfE5Bob+hQuM=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0/go.mod h1:CosX/aS4eHnG9D7nESYpV753l4j9q5j3SL/PUYd2lR8=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.63.0 h1:2pn7OzMewmYRiNtv1doZnLo3gONcnMHlFnmOR8Vgt+8=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.63.0/go.mod h1:rjbQTDEPQymPE0YnRQp9/NuPwwtL0sesz/fnqRW/v84=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
|
||||
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
|
||||
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 h1:QcFwRrZLc82r8wODjvyCbP7Ifp3UANaBSmhDSFjnqSc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0/go.mod h1:CXIWhUomyWBG/oY2/r/kLp6K/cmx9e/7DLpBuuGdLCA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 h1:vl9obrcoWVKp/lwl8tRE33853I8Xru9HFbw/skNeLs8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0/go.mod h1:GAXRxmLJcVM3u22IjTg74zWBrRCKq8BnOqUVLodpcpw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
|
||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4=
|
||||
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
|
||||
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
|
||||
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
|
||||
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
|
||||
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
|
||||
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
@@ -1059,6 +1131,7 @@ go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
|
||||
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
go4.org v0.0.0-20200411211856-f5505b9728dd h1:BNJlw5kRTzdmyfh5U8F93HA2OwkP7ZGwA51eJ/0wKOU=
|
||||
@@ -1083,7 +1156,6 @@ golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210915214749-c084706c2272/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210920023735-84f357641f63/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
@@ -1114,6 +1186,8 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
|
||||
golang.org/x/exp v0.0.0-20250911091902-df9299821621 h1:2id6c1/gto0kaHYyrixvknJ8tUK/Qs5IsmBtrc+FtgU=
|
||||
golang.org/x/exp v0.0.0-20250911091902-df9299821621/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk=
|
||||
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
@@ -1144,6 +1218,8 @@ golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
|
||||
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -1308,8 +1384,8 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -1478,6 +1554,8 @@ google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 h1:Q3nlH8iSQSRUwOs
|
||||
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38/go.mod h1:xBI+tzfqGGN2JBeSebfKXFSdBpWVQ7sLW40PTupVRm4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251103181224-f26f9409b101 h1:vk5TfqZHNn0obhPIYeS+cxIFKFQgser/M2jnI+9c6MM=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251103181224-f26f9409b101/go.mod h1:E17fc4PDhkr22dE3RgnH2hEubUaky6ZwW4VhANxyspg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 h1:Wgl1rcDNThT+Zn47YyCXOXyX/COgMTIdhJ717F0l4xk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
@@ -1515,6 +1593,8 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
+23
-3
@@ -27,13 +27,18 @@ var langFiles = map[string]string{
|
||||
}
|
||||
|
||||
func GetMsgWithMap(key string, maps map[string]interface{}) string {
|
||||
return GetMsgWithMapAndLang("", key, maps)
|
||||
}
|
||||
|
||||
func GetMsgWithMapAndLang(lang string, key string, maps map[string]interface{}) string {
|
||||
var content string
|
||||
localizer := newLocalizer(lang)
|
||||
if maps == nil {
|
||||
content, _ = global.I18n.Localize(&i18n.LocalizeConfig{
|
||||
content, _ = localizer.Localize(&i18n.LocalizeConfig{
|
||||
MessageID: key,
|
||||
})
|
||||
} else {
|
||||
content, _ = global.I18n.Localize(&i18n.LocalizeConfig{
|
||||
content, _ = localizer.Localize(&i18n.LocalizeConfig{
|
||||
MessageID: key,
|
||||
TemplateData: maps,
|
||||
})
|
||||
@@ -78,7 +83,11 @@ func GetErrMsg(key string, maps map[string]interface{}) string {
|
||||
}
|
||||
|
||||
func GetMsgByKey(key string) string {
|
||||
content, _ := global.I18n.Localize(&i18n.LocalizeConfig{
|
||||
return GetMsgByKeyAndLang("", key)
|
||||
}
|
||||
|
||||
func GetMsgByKeyAndLang(lang string, key string) string {
|
||||
content, _ := newLocalizer(lang).Localize(&i18n.LocalizeConfig{
|
||||
MessageID: key,
|
||||
})
|
||||
return content
|
||||
@@ -151,6 +160,17 @@ func Init() {
|
||||
global.I18n = i18n.NewLocalizer(bundle, lang)
|
||||
}
|
||||
|
||||
func newLocalizer(lang string) *i18n.Localizer {
|
||||
lang = strings.TrimSpace(lang)
|
||||
if lang == "" {
|
||||
if global.I18n != nil {
|
||||
return global.I18n
|
||||
}
|
||||
lang = GetLanguageFromDB()
|
||||
}
|
||||
return i18n.NewLocalizer(bundle, lang)
|
||||
}
|
||||
|
||||
func GetLanguageFromDB() string {
|
||||
if global.DB == nil {
|
||||
return "en"
|
||||
|
||||
+327
-295
@@ -1,115 +1,121 @@
|
||||
ErrInvalidParams: 'Request parameter error: {{ .detail }}'
|
||||
ErrTokenParse: 'Token generation error: {{ .detail }}'
|
||||
ErrInvalidParams: 'Bad request: {{ .detail }}'
|
||||
ErrTokenParse: 'Token error: {{ .detail }}'
|
||||
ErrInitialPassword: 'Original password is incorrect'
|
||||
ErrInternalServer: 'Internal server error: {{ .detail }}'
|
||||
ErrWgetRemoteFailed: 'Remote download failed or URL is unreachable: {{ .detail }}'
|
||||
ErrWgetInvalidContentType: 'Remote URL did not return a file (possibly an error page), {{ .detail }}'
|
||||
ErrRecordExist: 'Record already exists'
|
||||
ErrRecordNotFound: 'Record not found'
|
||||
ErrStructTransform: 'Type conversion failed: {{ .err }}'
|
||||
ErrNotLogin: 'User not logged in: {{ .detail }}'
|
||||
ErrPasswordExpired: 'The current password has expired: {{ .detail }}'
|
||||
ErrNotSupportType: 'The system does not support the current type: {{ .name }}'
|
||||
ErrProxy: 'Request error, please check the node status: {{ .detail }}'
|
||||
ErrApiConfigStatusInvalid: 'Access to the API interface is prohibited: {{ .detail }}'
|
||||
ErrApiConfigKeyInvalid: 'API interface key error: {{ .detail }}'
|
||||
ErrApiConfigIPInvalid: 'The IP used to call the API interface is not in the whitelist: {{ .detail }}'
|
||||
ErrApiConfigDisable: 'This interface prohibits the use of API interface calls: {{ .detail }}'
|
||||
ErrApiConfigKeyTimeInvalid: 'API interface timestamp error: {{ .detail }}'
|
||||
ErrShutDown: "Command manually terminated!"
|
||||
ErrNotLogin: 'Not logged in: {{ .detail }}'
|
||||
ErrPasswordExpired: 'Password expired: {{ .detail }}'
|
||||
ErrNotSupportType: 'Unsupported type: {{ .name }}'
|
||||
ErrProxy: 'Request failed: {{ .detail }}'
|
||||
ErrApiConfigStatusInvalid: 'API access disabled: {{ .detail }}'
|
||||
ErrApiConfigKeyInvalid: 'Invalid API key: {{ .detail }}'
|
||||
ErrApiConfigIPInvalid: 'IP not in API allowlist: {{ .detail }}'
|
||||
ErrApiConfigDisable: 'Endpoint not allowed via API: {{ .detail }}'
|
||||
ErrApiConfigKeyTimeInvalid: 'Invalid API timestamp: {{ .detail }}'
|
||||
ErrShutDown: 'Command stopped manually'
|
||||
|
||||
ErrMinQuickJump: "Please set at least one quick jump entry!"
|
||||
ErrMaxQuickJump: "You can set up to four quick jump entries!"
|
||||
ErrMinQuickJump: 'Set at least one quick jump entry'
|
||||
ErrMaxQuickJump: 'At most 4 quick jump entries'
|
||||
|
||||
#common
|
||||
ErrUsernameIsExist: 'Username already exists'
|
||||
ErrNameIsExist: 'Name already exists'
|
||||
ErrDemoEnvironment: 'Demo server, this operation is forbidden!'
|
||||
ErrCmdTimeout: 'Command execution timed out!'
|
||||
ErrCmdIllegal: 'There are illegal characters in the execution command, please modify it and try again!'
|
||||
ErrDemoEnvironment: 'Disabled in demo environment'
|
||||
ErrCmdTimeout: 'Command timed out'
|
||||
ErrCmdIllegal: 'Command has invalid characters'
|
||||
ErrPortExist: '{{ .port }} port is already occupied by {{ .type }} [{{ .name }}]'
|
||||
TYPE_APP: 'Application'
|
||||
TYPE_RUNTIME: 'Runtime environment'
|
||||
TYPE_DOMAIN: 'Domain Name'
|
||||
ErrTypePort: 'Port {{ .name }} format is incorrect'
|
||||
ErrTypePortRange: 'The port range needs to be between 1-65535'
|
||||
TYPE_RUNTIME: 'Runtime'
|
||||
TYPE_DOMAIN: 'Domain'
|
||||
ErrTypePort: 'Invalid port format: {{ .name }}'
|
||||
ErrTypePortRange: 'Port range must be 1-65535'
|
||||
Success: 'Success'
|
||||
Failed: 'Failed'
|
||||
SystemRestart: 'Task interrupted due to system restart'
|
||||
SystemRestart: 'Task interrupted by system restart'
|
||||
ErrGroupIsDefault: 'Default group, cannot be deleted'
|
||||
ErrGroupIsInWebsiteUse: 'The group is being used by another website and cannot be deleted.'
|
||||
ErrGroupIsInWebsiteUse: 'Group is in use by a website'
|
||||
Decrypt: "Decrypt"
|
||||
|
||||
#agent
|
||||
ErrAgentAccountBound: 'This account is bound to an agent and cannot be deleted. Please check and try again!'
|
||||
ErrAgentAccountUnavailable: 'The account connection information is unavailable. Error: {{ .err }}. Please check and try again!'
|
||||
ErrAgentProviderNotSupported: 'This agent provider is not supported. Please check and try again!'
|
||||
ErrAgentAccountRequired: 'Please select an agent account and try again.'
|
||||
ErrAgentAccountNotVerified: 'The account has not been verified. Please check and try again!'
|
||||
ErrAgentProviderMismatch: 'The account provider does not match. Please check and try again!'
|
||||
ErrAgentBaseURLRequired: 'Base URL cannot be empty. Please check and try again!'
|
||||
ErrAgentApiKeyRequired: 'API Key cannot be empty. Please check and try again!'
|
||||
ErrAgentComposeRequired: 'Custom compose content cannot be empty. Please check and try again!'
|
||||
ErrAgentIDRequired: 'Agent ID cannot be empty. Please check and try again!'
|
||||
ErrAgentAccountIDRequired: 'Account ID cannot be empty. Please check and try again!'
|
||||
ErrAgentAccountBound: 'Account is bound to an agent and cannot be deleted'
|
||||
ErrAgentAccountUnavailable: 'Account connection unavailable: {{ .err }}'
|
||||
ErrAgentProviderNotSupported: 'Unsupported agent provider'
|
||||
ErrAgentAccountRequired: 'Select an agent account first'
|
||||
ErrAgentAccountNotVerified: 'Agent account not verified'
|
||||
ErrAgentAccountModelsRequired: 'Please configure at least one model'
|
||||
ErrAgentAccountSingleInitialModel: 'Only one initial model can be configured during account creation'
|
||||
ErrAgentModelNotInAccount: 'Model is not configured in the selected account'
|
||||
ErrAgentModelInUse: 'A bound agent is still using this model: {{ .name }}'
|
||||
ErrAgentBaseURLRequired: 'Base URL is required'
|
||||
ErrAgentApiKeyRequired: 'API key is required'
|
||||
ErrAgentComposeRequired: 'Compose content is required'
|
||||
ErrAgentIDRequired: 'Agent ID is required'
|
||||
ErrAgentAccountIDRequired: 'Account ID is required'
|
||||
ErrAgentLimitReached: 'Community Edition supports up to {{ .max }} AI agents. Upgrade to Professional Edition for unlimited agents'
|
||||
|
||||
#backup
|
||||
Localhost: 'Local Machine'
|
||||
ErrBackupInUsed: 'The backup account has been used in the scheduled task and cannot be deleted.'
|
||||
ErrBackupCheck: 'Backup account test connection failed {{ .err }}'
|
||||
ErrBackupLocalDelete: 'Deleting the local server backup account is not supported yet'
|
||||
ErrBackupLocalCreate: 'Creating local server backup accounts is not supported yet'
|
||||
Localhost: 'Local'
|
||||
ErrBackupInUsed: 'Backup account is in use by a scheduled task'
|
||||
ErrBackupCheck: 'Backup account connectivity test failed: {{ .err }}'
|
||||
ErrBackupLocalDelete: 'Local backup accounts cannot be deleted'
|
||||
ErrBackupLocalCreate: 'Local backup accounts cannot be created'
|
||||
|
||||
#app
|
||||
ErrPortInUsed: '{{ .detail }} port is already occupied!'
|
||||
ErrAppLimit: 'The number of applications installed has exceeded the limit'
|
||||
ErrAppLimit: 'App install limit reached'
|
||||
ErrNotInstall: 'Application not installed'
|
||||
ErrPortInOtherApp: '{{ .port }} port is already occupied by application {{ .apps }}!'
|
||||
ErrDbUserNotValid: 'Existing database, username and password do not match!'
|
||||
ErrUpdateBuWebsite: 'The application was updated successfully, but the website configuration file modification failed. Please check the configuration! '
|
||||
ErrDbUserNotValid: 'Database username/password mismatch'
|
||||
ErrUpdateBuWebsite: 'App updated, but website config update failed'
|
||||
Err1PanelNetworkFailed: 'Default container network creation failed! {{ .detail }}'
|
||||
ErrFileParse: 'Application docker-compose file parsing failed!'
|
||||
ErrInstallDirNotFound: 'The installation directory does not exist. If you need to uninstall, please select Force Uninstall'
|
||||
ErrFileParse: 'Failed to parse app docker-compose file'
|
||||
ErrInstallDirNotFound: 'Install directory not found; use Force Uninstall'
|
||||
AppStoreIsUpToDate: 'The app store is already the latest version'
|
||||
LocalAppVersionNull: '{{ .name }} application is not synchronized to the version! Cannot be added to the application list'
|
||||
LocalAppVersionErr: '{{ .name }} sync version {{ .version }} failed! {{ .err }}'
|
||||
LocalAppVersionNull: 'App {{ .name }} has no synced version'
|
||||
LocalAppVersionErr: 'Failed to sync {{ .name }} version {{ .version }}: {{ .err }}'
|
||||
ErrFileNotFound: '{{ .name }} file does not exist'
|
||||
ErrFileParseApp: '{{ .name }} file parsing failed {{ .err }}'
|
||||
ErrFileParseApp: 'Failed to parse {{ .name }}: {{ .err }}'
|
||||
ErrAppDirNull: 'The version folder does not exist'
|
||||
LocalAppErr: 'Application {{ .name }} sync failed! {{ .err }}'
|
||||
LocalAppErr: 'Failed to sync app {{ .name }}: {{ .err }}'
|
||||
ErrContainerName: 'Container name already exists'
|
||||
ErrCreateHttpClient: 'Failed to create request {{ .err }}'
|
||||
ErrHttpReqTimeOut: 'Request timed out {{ .err }}'
|
||||
ErrHttpReqFailed: 'Request failed {{ .err }}'
|
||||
ErrNoSuchHost: 'Unable to find the requested server {{ .err }}'
|
||||
ErrHttpReqNotFound: 'The requested resource {{ .err }} could not be found'
|
||||
ErrCreateHttpClient: 'Failed to create HTTP request: {{ .err }}'
|
||||
ErrHttpReqTimeOut: 'HTTP request timed out: {{ .err }}'
|
||||
ErrHttpReqFailed: 'HTTP request failed: {{ .err }}'
|
||||
ErrNoSuchHost: 'Host resolve failed: {{ .err }}'
|
||||
ErrHttpReqNotFound: 'Requested resource not found: {{ .err }}'
|
||||
ErrContainerNotFound: '{{ .name }} container does not exist'
|
||||
ErrContainerMsg: '{{ .name }} container is abnormal. Please check the log on the container page for details'
|
||||
ErrAppBackup: '{{ .name }} application backup failed {{ .err }}'
|
||||
ErrVersionTooLow: 'The current 1Panel version is too low to update the App Store. Please upgrade the version before operating.'
|
||||
ErrContainerMsg: 'Container {{ .name }} is abnormal; check logs'
|
||||
ErrAppBackup: 'App backup failed: {{ .name }} {{ .err }}'
|
||||
ErrVersionTooLow: '1Panel version too low to update App Store'
|
||||
ErrAppNameExist: 'The application name already exists'
|
||||
AppStoreIsSyncing: 'The App Store is syncing, please try again later'
|
||||
ErrGetCompose: 'Failed to obtain the docker-compose.yml file! {{ .detail }}'
|
||||
ErrAppWarn: 'Abnormal status, please check the log'
|
||||
ErrAppParamKey: 'Parameter {{ .name }} field is abnormal'
|
||||
ErrAppUpgrade: 'Application {{ .name }} upgrade failed {{ .err }}'
|
||||
AppStoreIsSyncing: 'App Store is syncing; try again later'
|
||||
ErrGetCompose: 'Failed to read docker-compose.yml: {{ .detail }}'
|
||||
ErrAppWarn: 'App status abnormal; check logs'
|
||||
ErrAppParamKey: 'Invalid app parameter: {{ .name }}'
|
||||
ErrAppUpgrade: 'App upgrade failed: {{ .name }} {{ .err }}'
|
||||
AppRecover: 'Rollback application {{ .name }}'
|
||||
PullImageStart: 'Start pulling image [{{ .name }}]'
|
||||
PullImageSuccess: 'Image pull successful'
|
||||
AppStoreIsLastVersion: 'The App Store is already the latest version'
|
||||
AppStoreSyncSuccess: 'App Store synchronization successful'
|
||||
SyncAppDetail: 'Synchronize application configuration'
|
||||
AppVersionNotMatch: '{{ .name }} application requires a higher 1Panel version, skipping synchronization'
|
||||
MoveSiteDir: "Detected website directory change. Need to migrate OpenResty website directory to {{ .name }}"
|
||||
MoveSiteDirSuccess: 'Successful migration of website directory'
|
||||
PullImageStart: 'Pull image [{{ .name }}]'
|
||||
PullImageSuccess: 'Image pulled'
|
||||
AppStoreSyncSuccess: 'App Store synced'
|
||||
SyncAppDetail: 'Sync app config'
|
||||
AppVersionNotMatch: 'Skip {{ .name }}: requires newer 1Panel'
|
||||
MoveSiteDir: 'Website dir changed; migrate OpenResty dir to {{ .name }}'
|
||||
MoveSiteDirSuccess: 'Website dir migrated'
|
||||
DeleteRuntimePHP: 'Delete PHP runtime'
|
||||
CustomAppStoreFileValid: 'App store packages need to be in .tar.gz format'
|
||||
PullImageTimeout: 'Pull image timeout, please try to increase the image acceleration or change to another image acceleration'
|
||||
ErrAppIsDown: '{{ .name }} application status is abnormal, please check'
|
||||
ErrCustomApps: 'There is an installed application, please uninstall it first'
|
||||
ErrCustomRuntimes: 'There is an installed runtime environment, please delete it first'
|
||||
CustomAppStoreFileValid: 'App Store package must be .tar.gz'
|
||||
PullImageTimeout: 'Image pull timed out; try another mirror'
|
||||
ErrAppIsDown: 'App {{ .name }} is abnormal'
|
||||
ErrCustomApps: 'Installed apps detected; uninstall first'
|
||||
ErrCustomRuntimes: 'Installed runtimes detected; delete first'
|
||||
ErrAppVersionDeprecated: "The {{ .name }} application is not compatible with the current 1Panel version, skipped"
|
||||
ErrDockerFailed: "The state of Docker is abnormal, please check the service status"
|
||||
ErrDockerComposeCmdNotFound: "The Docker Compose command does not exist, please install this command on the host machine first"
|
||||
UseExistImage: 'Failed to pull image, using existing image'
|
||||
ErrDockerFailed: 'Docker is abnormal; check service status'
|
||||
ErrDockerComposeCmdNotFound: 'Docker Compose not found on host'
|
||||
UseExistImage: 'Image exists; using existing image'
|
||||
ErrDatabaseNotFound: 'Database not found: {{ .name }}'
|
||||
|
||||
#ssh
|
||||
ExportIP: "Login IP"
|
||||
@@ -121,42 +127,42 @@ ExportStatus: "Login Status"
|
||||
ExportDate: "Time"
|
||||
|
||||
#file
|
||||
ErrFileCanNotRead: 'This file does not support preview'
|
||||
ErrFileToLarge: 'The file is larger than 10M and cannot be opened'
|
||||
ErrFileCanNotRead: 'File preview is not supported'
|
||||
ErrFileToLarge: 'File is larger than 10 MB'
|
||||
ErrPathNotFound: 'Directory does not exist'
|
||||
ErrMovePathFailed: 'The target path cannot contain the original path!'
|
||||
ErrMovePathFailed: 'Target path cannot include source path'
|
||||
ErrLinkPathNotFound: 'The target path does not exist!'
|
||||
ErrFileIsExist: 'The file or folder already exists!'
|
||||
ErrFileUpload: '{{ .name }} failed to upload file {{ .detail }}'
|
||||
ErrFileDownloadDir: 'Download folder is not supported'
|
||||
ErrCmdNotFound: '{{ .name}} command does not exist, please install this command on the host first'
|
||||
ErrFileUpload: 'Upload failed: {{ .name }} {{ .detail }}'
|
||||
ErrFileDownloadDir: 'Directory download is not supported'
|
||||
ErrCmdNotFound: 'Command not found: {{ .name }}'
|
||||
ErrSourcePathNotFound: 'Source directory does not exist'
|
||||
ErrFavoriteExist: 'This path has already been favorited'
|
||||
ErrFavoriteExist: 'Path already in favorites'
|
||||
ErrInvalidChar: 'Illegal characters are not allowed'
|
||||
ErrPathNotDelete: 'The selected directory cannot be deleted'
|
||||
ErrLogFileToLarge: "The log file exceeds 500MB and cannot be opened"
|
||||
FileDropFailed: "Failed to clean up file {{ .name }}: {{ .err }}"
|
||||
FileDropSuccess: "Successfully cleaned up file {{ .name }}, cleaned {{ .count }} files, freed {{ .size }} disk space"
|
||||
FileDropSum: "File cleanup completed successfully, total {{ .count }} files cleaned, total {{ .size }} disk space freed"
|
||||
ErrBadDecrypt: "Compressed package decryption password error, decompression failed, please check and try again!"
|
||||
ErrLogFileToLarge: 'Log file exceeds 500 MB'
|
||||
FileDropFailed: 'File cleanup failed: {{ .name }} {{ .err }}'
|
||||
FileDropSuccess: 'Cleaned {{ .name }}; files: {{ .count }}, freed: {{ .size }}'
|
||||
FileDropSum: 'Cleanup done; files: {{ .count }}, freed: {{ .size }}'
|
||||
ErrBadDecrypt: 'Wrong archive password; extract failed'
|
||||
|
||||
#website
|
||||
ErrAliasIsExist: 'Alias already exists'
|
||||
ErrBackupMatch: 'The backup file does not match some of the current website data {{ .detail }}'
|
||||
ErrBackupExist: 'The corresponding part of the source data in the backup file does not exist {{ .detail }}'
|
||||
ErrPHPResource: 'The local operating environment does not support switching! '
|
||||
ErrPathPermission: 'A folder with non-1000:1000 permissions was detected in the index directory, which may cause an Access denied error on the website. Please click the Save button above'
|
||||
ErrBackupMatch: 'Backup data mismatch: {{ .detail }}'
|
||||
ErrBackupExist: 'Backup source data missing: {{ .detail }}'
|
||||
ErrPHPResource: 'Switching local runtime is not supported'
|
||||
ErrPathPermission: 'Non-1000:1000 folder in index dir may cause Access denied'
|
||||
ErrDomainIsUsed: 'The domain name is already used by the website [{{ .name }}]'
|
||||
ErrDomainFormat: '{{ .name }} domain name format is incorrect'
|
||||
ErrDefaultAlias: 'default is a reserved code, please use another code'
|
||||
ErrParentWebsite: 'You need to delete the subsite {{ .name }} first'
|
||||
ErrDomainFormat: 'Invalid domain format: {{ .name }}'
|
||||
ErrDefaultAlias: 'default is reserved; use another alias'
|
||||
ErrParentWebsite: 'Delete subsite {{ .name }} first'
|
||||
ErrBuildDirNotFound: 'The build directory does not exist'
|
||||
ErrImageNotExist: 'The operating environment {{ .name }} image does not exist, please re-edit the operating environment'
|
||||
ErrProxyIsUsed: "Load balancing has been used by reverse proxy, cannot be deleted"
|
||||
ErrSSLValid: 'Certificate file is abnormal, please check the certificate status!'
|
||||
ErrWebsiteDir: "Please select a directory within the website directory."
|
||||
ErrImageNotExist: 'Runtime image not found: {{ .name }}'
|
||||
ErrProxyIsUsed: 'Load balancer is used by reverse proxy'
|
||||
ErrSSLValid: 'Certificate file is invalid'
|
||||
ErrWebsiteDir: 'Select a subdirectory under the website directory'
|
||||
ErrComposerFileNotFound: "composer.json file does not exist"
|
||||
ErrRuntimeNoPort: "The runtime environment is not set with a port, please edit the runtime environment first."
|
||||
ErrRuntimeNoPort: 'Runtime port is not set'
|
||||
Status: 'Status'
|
||||
start: 'Start'
|
||||
stop: 'Stop'
|
||||
@@ -166,157 +172,187 @@ pause: 'Pause'
|
||||
unpause: 'Resume'
|
||||
remove: 'Delete'
|
||||
delete: 'Delete'
|
||||
ErrDefaultWebsite: 'Default website has been set, please cancel it before setting!'
|
||||
ErrDefaultWebsite: 'Default website already set; unset it first'
|
||||
SSL: 'Certificate'
|
||||
Set: 'Settings'
|
||||
|
||||
#ssl
|
||||
ErrSSLCannotDelete: 'The {{ .name }} certificate is being used by a website and cannot be deleted'
|
||||
ErrAccountCannotDelete: 'The account is associated with a certificate and cannot be deleted'
|
||||
ErrSSLApply: 'Certificate renewal successful, openresty reload failed, please check the configuration!'
|
||||
ErrEmailIsExist: 'The mailbox already exists'
|
||||
ErrSSLCannotDelete: 'Certificate {{ .name }} is in use by a website'
|
||||
ErrAccountCannotDelete: 'Account is linked to a certificate and cannot be deleted'
|
||||
ErrSSLApply: 'Certificate renewed; OpenResty reload failed'
|
||||
ErrEmailIsExist: 'Email already exists'
|
||||
ErrSSLKeyNotFound: 'The private key file does not exist'
|
||||
ErrSSLCertificateNotFound: 'The certificate file does not exist'
|
||||
ErrSSLKeyFormat: 'Private key file verification failed'
|
||||
ErrSSLCertificateFormat: 'The certificate file format is incorrect, please use pem format'
|
||||
ErrSSLCertificateFormat: 'Invalid certificate format; use PEM'
|
||||
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid or EabHmacKey cannot be blank'
|
||||
ErrOpenrestyNotFound: 'Http mode requires Openresty to be installed first'
|
||||
ApplySSLStart: 'Start applying for a certificate, domain name [{{ .domain }}] application method [{{ .type }}] '
|
||||
ErrOpenrestyNotFound: 'HTTP mode requires OpenResty'
|
||||
ApplySSLStart: 'Starting certificate request. Domain: [{{ .domain }}], method: [{{ .type }}]'
|
||||
dnsAccount: 'DNS Auto'
|
||||
dnsManual: 'DNS Manual'
|
||||
http: 'HTTP'
|
||||
ApplySSLFailed: 'Application for [{{ .domain }}] certificate failed, {{ .detail }} '
|
||||
ApplySSLSuccess: 'Apply for [{{ .domain }}] certificate successfully! !'
|
||||
DNSAccountName: 'DNS account [{{ .name }}] vendor [{{ .type }}]'
|
||||
ApplySSLFailed: 'Failed to request certificate for [{{ .domain }}]: {{ .detail }}'
|
||||
ApplySSLSuccess: 'Certificate issued for [{{ .domain }}]'
|
||||
DNSAccountName: 'DNS account [{{ .name }}], provider [{{ .type }}]'
|
||||
PushDirLog: 'Certificate pushed to directory [{{ .path }}] {{ .status }}'
|
||||
ErrDeleteCAWithSSL: 'The current organization has a certificate that has been issued and cannot be deleted.'
|
||||
ErrDeleteWithPanelSSL: 'Panel SSL configuration uses this certificate and cannot be deleted'
|
||||
ErrDeleteCAWithSSL: 'CA has issued certificates; cannot delete'
|
||||
ErrDeleteWithPanelSSL: 'Certificate is in use by Panel SSL settings'
|
||||
ErrDefaultCA: 'The default authority cannot be deleted'
|
||||
ApplyWebSiteSSLLog: 'Starting to renew the {{ .name }} website certificate'
|
||||
ApplyWebSiteSSLLog: 'Updating website certificate: {{ .name }}'
|
||||
ErrUpdateWebsiteSSL: '{{ .name }} website certificate update failed: {{ .err }}'
|
||||
ApplyWebSiteSSLSuccess: 'Update website certificate successfully'
|
||||
ApplyWebSiteSSLSuccess: 'Website certificate updated'
|
||||
ErrExecShell: 'Failed to execute script {{ .err }}'
|
||||
ExecShellStart: 'Start executing the script'
|
||||
ExecShellSuccess: 'Script execution successful'
|
||||
StartUpdateSystemSSL: 'Start updating system certificate'
|
||||
UpdateSystemSSLSuccess: 'Update system certificate successfully'
|
||||
ErrWildcardDomain: 'Unable to apply for wildcard domain name certificate in HTTP mode'
|
||||
ErrApplySSLCanNotDelete: "The certificate {{.name}} being applied for cannot be deleted, please try again later."
|
||||
StartPushSSLToNode: "Starting to push certificate to node"
|
||||
ExecShellStart: 'Run script'
|
||||
ExecShellSuccess: 'Script succeeded'
|
||||
StartUpdateSystemSSL: 'Update system certificate'
|
||||
UpdateSystemSSLSuccess: 'System certificate updated'
|
||||
ErrWildcardDomain: 'Wildcard certificates are not supported in HTTP mode'
|
||||
ErrApplySSLCanNotDelete: 'Certificate {{ .name }} is being issued and cannot be deleted yet'
|
||||
StartPushSSLToNode: 'Push certificate to node'
|
||||
PushSSLToNodeFailed: "Failed to push certificate to node: {{ .err }}"
|
||||
PushSSLToNodeSuccess: "Successfully pushed certificate to node"
|
||||
PushSSLToNodeSuccess: 'Certificate pushed to node'
|
||||
|
||||
#mysql
|
||||
ErrUserIsExist: 'The current user already exists, please re-enter'
|
||||
ErrDatabaseIsExist: 'The current database already exists, please re-enter'
|
||||
ErrExecTimeOut: 'SQL execution timed out, please check the database'
|
||||
ErrRemoteExist: 'The remote database already exists with this name, please modify it and try again'
|
||||
ErrLocalExist: 'The name already exists in the local database, please modify it and try again'
|
||||
RemoteBackup: "To back up the remote database, the local container database service needs to be started first using the image {{ .name }}, please wait..."
|
||||
RemoteRecover: "To restore the remote database, the local container database service needs to be started first using the image {{ .name }}, please wait..."
|
||||
ErrUserIsExist: 'User already exists'
|
||||
ErrDatabaseIsExist: 'Database already exists'
|
||||
ErrExecTimeOut: 'SQL execution timed out'
|
||||
ErrRemoteExist: 'Remote database name already exists'
|
||||
ErrLocalExist: 'Local database name already exists'
|
||||
RemoteBackup: 'Start local DB container with image {{ .name }} before backing up remote DB'
|
||||
RemoteRecover: 'Start local DB container with image {{ .name }} before restoring remote DB'
|
||||
Arg: "Argument"
|
||||
|
||||
#redis
|
||||
ErrTypeOfRedis: 'The recovery file type does not match the current persistence method, please modify it and try again'
|
||||
ErrTypeOfRedis: 'Backup file type does not match current persistence mode'
|
||||
|
||||
#container
|
||||
ErrInUsed: "{{ .detail }} is in use and cannot be deleted!"
|
||||
ErrObjectInUsed: "This object is in use and cannot be deleted!"
|
||||
ErrObjectBeDependent: "This image is depended on by other images and cannot be deleted!"
|
||||
ErrPortRules: 'Port number does not match, please re-enter!'
|
||||
ErrPgImagePull: 'Image pull timed out, please configure image acceleration or manually pull the {{ .name }} image and try again'
|
||||
PruneHelper: "This cleanup {{ .name }} freed disk space {{ .size }}"
|
||||
ImageRemoveHelper: "Deleted image {{ .name }}, freeing {{ .size }} disk space"
|
||||
BuildCache: "Build cache"
|
||||
Volume: "Storage volume"
|
||||
ErrObjectBeDependent: "This image is referenced by other images and cannot be deleted!"
|
||||
ErrPortRules: 'Port count does not match'
|
||||
ErrPgImagePull: 'Image pull timed out; configure acceleration or pull {{ .name }} manually'
|
||||
PruneHelper: "Cleanup {{ .name }} released {{ .size }} disk space"
|
||||
ImageRemoveHelper: "Removed image {{ .name }}, released {{ .size }} disk space"
|
||||
BuildCache: 'Build Cache'
|
||||
Volume: 'Volume'
|
||||
Network: "Network"
|
||||
PruneStart: "Cleaning in progress, please wait..."
|
||||
PruneStart: 'Cleaning in progress'
|
||||
ContainerBackupPrepare: 'Prepare container backup workspace'
|
||||
ContainerBackupStop: 'Stop container before backup'
|
||||
ContainerBackupInspect: 'Backup container inspect and network'
|
||||
ContainerBackupMounts: 'Backup container mounts'
|
||||
ContainerBackupMeta: 'Write container backup metadata'
|
||||
ContainerBackupStart: 'Start container after backup'
|
||||
ContainerRecoverPrepare: 'Prepare container restore workspace'
|
||||
ContainerRecoverExtract: 'Extract container backup archive'
|
||||
ContainerRecoverParse: 'Load container backup metadata'
|
||||
ContainerRecoverCreate: 'Recreate container'
|
||||
ContainerRecoverMounts: 'Restore container mounts'
|
||||
ContainerRecoverStart: 'Start restored container'
|
||||
ContainerRecoverCleanup: 'Cleanup container restore workspace'
|
||||
ComposeBackupPrepare: 'Prepare compose backup workspace'
|
||||
ComposeBackupStop: 'Stop compose before backup'
|
||||
ComposeBackupFiles: 'Backup compose files'
|
||||
ComposeBackupContainers: 'Backup compose containers'
|
||||
ComposeBackupMeta: 'Write compose backup metadata'
|
||||
ComposeBackupStart: 'Start compose after backup'
|
||||
ComposeBackupCleanup: 'Cleanup compose backup workspace'
|
||||
ComposeRecoverPrepare: 'Prepare compose restore workspace'
|
||||
ComposeRecoverExtract: 'Extract compose backup archive'
|
||||
ComposeRecoverMeta: 'Load compose backup metadata'
|
||||
ComposeRecoverMetaLogName: 'Compose restore meta: name={{ .name }}'
|
||||
ComposeRecoverMetaLogPath: 'Compose restore meta: backupPath={{ .backupPath }} targetDir={{ .targetDir }}'
|
||||
ComposeRecoverMetaLogCount: 'Compose restore meta: files={{ .files }} containers={{ .containers }}'
|
||||
ComposeRecoverFiles: 'Restore compose files'
|
||||
ComposeRecoverContainers: 'Restore compose containers'
|
||||
ComposeRecoverRecord: 'Save compose record'
|
||||
ComposeRecoverCleanup: 'Cleanup compose restore workspace'
|
||||
|
||||
#runtime
|
||||
ErrFileNotExist: '{{ .detail }} file does not exist! Please check the integrity of the source file!'
|
||||
ErrFileNotExist: 'Source file not found: {{ .detail }}'
|
||||
ErrImageBuildErr: 'Image build failed'
|
||||
ErrImageExist: "Image already exists! Please modify the image name."
|
||||
ErrDelWithWebsite: 'The operating environment is already associated with a website and cannot be deleted'
|
||||
ErrImageExist: "Image already exists. Use a different image name."
|
||||
ErrDelWithWebsite: 'Runtime is linked to a website and cannot be deleted'
|
||||
ErrRuntimeStart: 'Startup failed'
|
||||
ErrPackageJsonNotFound: 'package.json file does not exist'
|
||||
ErrScriptsNotFound: 'The scripts configuration item was not found in package.json'
|
||||
ErrContainerNameNotFound: 'Unable to get container name, please check .env file'
|
||||
ErrNodeModulesNotFound: 'The node_modules folder does not exist! Please edit the runtime environment or wait for the runtime environment to start successfully'
|
||||
ErrContainerNameIsNull: 'Container name does not exist'
|
||||
ErrPHPPortIsDefault: "Port 9000 is the default port, please modify and try again"
|
||||
ErrPHPRuntimePortFailed: "The port {{ .name }} is already used by the current runtime environment, please modify and try again"
|
||||
ErrScriptsNotFound: 'No scripts section found in package.json'
|
||||
ErrContainerNameNotFound: 'Container name not found; check .env'
|
||||
ErrNodeModulesNotFound: 'node_modules not found; edit runtime or wait for startup'
|
||||
ErrContainerNameIsNull: 'Container name is empty'
|
||||
ErrPHPPortIsDefault: 'Port 9000 is reserved as default; use another port'
|
||||
ErrPHPRuntimePortFailed: 'Port {{ .name }} is already used by this runtime'
|
||||
|
||||
#tool
|
||||
ErrConfigNotFound: 'Configuration file does not exist'
|
||||
ErrConfigParse: 'The configuration file format is incorrect'
|
||||
ErrConfigParse: 'Invalid configuration file format'
|
||||
ErrConfigIsNull: 'Configuration file cannot be empty'
|
||||
ErrConfigDirNotFound: 'The running directory does not exist'
|
||||
ErrConfigAlreadyExist: 'A configuration file with the same name already exists'
|
||||
ErrUserFindErr: 'User {{ .name }} search failed {{ .err }}'
|
||||
ErrConfigDirNotFound: 'Run directory not found'
|
||||
ErrConfigAlreadyExist: 'Configuration file with the same name already exists'
|
||||
ErrUserFindErr: 'Failed to find user {{ .name }}: {{ .err }}'
|
||||
|
||||
#cronjob
|
||||
CutWebsiteLogSuccess: '{{ .name }} website log cut successfully, backup path {{ .path }}'
|
||||
HandleShell: 'Execute script {{ .name }}'
|
||||
HandleCurl: "Access URL {{ .name }}"
|
||||
HandleNtpSync: 'System time synchronization'
|
||||
HandleSystemClean: 'System cache cleanup'
|
||||
CutWebsiteLogSuccess: 'Website log rotated: {{ .name }} (backup: {{ .path }})'
|
||||
HandleShell: 'Run script {{ .name }}'
|
||||
HandleCurl: 'Request URL {{ .name }}'
|
||||
HandleNtpSync: 'Sync system time'
|
||||
HandleSystemClean: 'Clean system cache'
|
||||
SystemLog: 'System Log'
|
||||
CutWebsiteLog: 'Rotate Website Log'
|
||||
FileOrDir: 'Directory / File'
|
||||
UploadFile: 'Uploading backup file {{ .file }} to {{ .backup }}'
|
||||
UploadFile: 'Upload backup {{ .file }} to {{ .backup }}'
|
||||
Upload: "Upload"
|
||||
IgnoreBackupErr: "Backup failed, error: {{ .detail }}, ignoring this error..."
|
||||
IgnoreUploadErr: "Upload failed, error: {{ .detail }}, ignoring this error..."
|
||||
LoadBackupFailed: "Failed to get backup account connection, error: {{ .detail }}"
|
||||
InExecuting: "The current task is being executed, please do not repeat the execution!"
|
||||
NoSuchResource: "No backup content found in the database, skipping..."
|
||||
CleanLog: "Clean Log"
|
||||
CleanLogByName: "Clean {{.name}} Log"
|
||||
WafIpGroupNotFound: "WAF IP group not found. Please go to [Advanced Features - WAF - Black/White List - IP Group] to add an IP group via remote download method."
|
||||
IgnoreBackupErr: 'Backup failed: {{ .detail }} (ignored)'
|
||||
IgnoreUploadErr: 'Upload failed: {{ .detail }} (ignored)'
|
||||
LoadBackupFailed: 'Failed to get backup account connection info: {{ .detail }}'
|
||||
InExecuting: 'Task is already running'
|
||||
NoSuchResource: 'No backup records found; skipped'
|
||||
CleanLog: 'Clean logs'
|
||||
CleanLogByName: 'Clean {{ .name }} logs'
|
||||
WafIpGroupNotFound: "WAF IP group not found. Go to [Advanced Features -> WAF -> Black/White List -> IP Group] and add one using Remote Download."
|
||||
|
||||
#toolbox
|
||||
ErrNotExistUser: 'The current user does not exist, please modify and try again!'
|
||||
ErrBanAction: 'Setting failed. The current {{ .name }} service is unavailable. Please check and try again!'
|
||||
ErrClamdscanNotFound: 'The clamdscan command was not detected, please refer to the documentation to install it!'
|
||||
TaskSwapSet: "Set Swap"
|
||||
ErrNotExistUser: 'User does not exist'
|
||||
ErrBanAction: 'Config failed: service {{ .name }} unavailable'
|
||||
ErrClamdscanNotFound: 'clamdscan command not found'
|
||||
TaskSwapSet: 'Set swap'
|
||||
SetSwap: "Set Swap {{ .path }} - {{ .size }}"
|
||||
CreateSwap: "Create Swap File"
|
||||
FormatSwap: "Format Swap File"
|
||||
EnableSwap: "Enable Swap"
|
||||
CreateSwap: 'Create swap file'
|
||||
FormatSwap: 'Format swap file'
|
||||
EnableSwap: 'Enable swap'
|
||||
|
||||
#tamper
|
||||
CleanTamperSetting: "Clean up historical protection settings"
|
||||
SetTamperWithRules: "Start directory protection according to rules"
|
||||
CleanTamperSetting: 'Clean historical protection settings'
|
||||
SetTamperWithRules: 'Enable directory protection by rules'
|
||||
ProtectDir: "Protect directory {{ .name }}"
|
||||
IgnoreFile: "Protect file {{ .name }}"
|
||||
TamperSetSuccessful: "Protection settings completed, start monitoring the following directories:"
|
||||
TamperSetSuccessful: 'Protection set. Monitoring these directories:'
|
||||
|
||||
#waf
|
||||
ErrScope: 'Modifying this configuration is not supported'
|
||||
ErrStateChange: 'State change failed'
|
||||
ErrScope: 'This config cannot be changed'
|
||||
ErrStateChange: 'Failed to change state'
|
||||
ErrRuleExist: 'Rule already exists'
|
||||
ErrRuleNotExist: 'Rule does not exist'
|
||||
ErrParseIP: 'Wrong IP format'
|
||||
ErrDefaultIP: 'default is a reserved name, please change it to another name'
|
||||
ErrGroupInUse: 'IP group is used by blacklist/whitelist and cannot be deleted'
|
||||
ErrIPGroupAclUse: "IP group is used by custom rules of website {{ .name }}, cannot be deleted"
|
||||
ErrParseIP: 'Invalid IP format'
|
||||
ErrDefaultIP: 'default is reserved; use another name'
|
||||
ErrGroupInUse: 'IP group is used by blacklist/whitelist rules'
|
||||
ErrIPGroupAclUse: 'IP group is used by website {{ .name }} custom rules'
|
||||
ErrGroupExist: 'IP group name already exists'
|
||||
ErrIPRange: 'Wrong IP range'
|
||||
ErrIPRange: 'Invalid IP range'
|
||||
ErrIPExist: 'IP already exists'
|
||||
urlDefense: 'URL rules'
|
||||
urlHelper: 'Prohibited URL'
|
||||
urlHelper: 'Blocked URL'
|
||||
dirFilter: 'Directory filter'
|
||||
xss: 'XSS'
|
||||
phpExec: 'PHP script execution'
|
||||
oneWordTrojan: 'One Word Trojan'
|
||||
appFilter: 'Apply dangerous directory filtering'
|
||||
webshell: 'Webshell'
|
||||
appFilter: 'App dangerous directory filtering'
|
||||
webShell: 'WebShell'
|
||||
args: 'Parameter rules'
|
||||
protocolFilter: 'Protocol filtering'
|
||||
javaFileter: 'Java dangerous file filter'
|
||||
javaFilter: 'Java dangerous file filtering'
|
||||
scannerFilter: 'Scanner filter'
|
||||
escapeFilter: 'escape filter'
|
||||
customRule: 'Custom rule'
|
||||
escapeFilter: 'Escape filtering'
|
||||
customRule: 'Custom rules'
|
||||
httpMethod: 'HTTP method filtering'
|
||||
fileExt: 'File upload restrictions'
|
||||
defaultIpBlack: 'Malicious IP group'
|
||||
@@ -331,8 +367,8 @@ notFoundCount: '404 Rate Limit'
|
||||
headerDefense: 'Header rules'
|
||||
defaultUaBlack: 'User-Agent rules'
|
||||
methodWhite: 'HTTP rules'
|
||||
captcha: 'human-machine verification'
|
||||
fiveSeconds: '5 seconds verification'
|
||||
captcha: 'Human verification'
|
||||
fiveSeconds: '5-second verification'
|
||||
vulnCheck: 'Supplementary rules'
|
||||
acl: 'Custom rules'
|
||||
sql: 'SQL injection'
|
||||
@@ -340,19 +376,20 @@ cc: 'Access frequency limit'
|
||||
defaultUrlBlack: 'URL rules'
|
||||
sqlInject: 'SQL injection'
|
||||
ErrDBNotExist: 'Database does not exist'
|
||||
allow: 'allow'
|
||||
deny: 'deny'
|
||||
OpenrestyNotFound: 'Openresty is not installed'
|
||||
allow: 'Allow'
|
||||
deny: 'Deny'
|
||||
OpenrestyNotFound: 'OpenResty is not installed'
|
||||
remoteIpIsNull: "IP list is empty"
|
||||
OpenrestyVersionErr: "Openresty version is too low, please upgrade Openresty to 1.27.1.2-2-2-focal"
|
||||
OpenrestyVersionErr: 'OpenResty version too low; upgrade to 1.27.1.2-2-2-focal'
|
||||
ErrFileTooLarge: 'File is larger than 1 MB and cannot be uploaded'
|
||||
|
||||
#task
|
||||
TaskStart: '{{ .name }} task starts [START]'
|
||||
TaskEnd: '{{ .name }} Task completed [COMPLETED]'
|
||||
TaskStart: '{{ .name }} [START]'
|
||||
TaskEnd: '{{ .name }} [DONE]'
|
||||
TaskFailed: '{{ .name }} task failed'
|
||||
TaskTimeout: '{{ .name }} timed out'
|
||||
TaskSuccess: '{{ .name }} Task succeeded'
|
||||
TaskRetry: 'Start {{ .name }}th retry'
|
||||
TaskSuccess: '{{ .name }} succeeded'
|
||||
TaskRetry: 'Retry #{{ .name }}'
|
||||
SubTaskSuccess: '{{ .name }} succeeded'
|
||||
SubTaskFailed: '{{ .name }} failed: {{ .err }}'
|
||||
TaskInstall: 'Install'
|
||||
@@ -376,34 +413,34 @@ TaskImport: "Import"
|
||||
TaskExport: "Export"
|
||||
Website: 'Website'
|
||||
App: 'Application'
|
||||
Runtime: 'Runtime environment'
|
||||
Runtime: 'Runtime'
|
||||
Database: 'Database'
|
||||
ConfigFTP: 'Create FTP user {{ .name }}'
|
||||
ConfigOpenresty: 'Create Openresty configuration file'
|
||||
InstallAppSuccess: 'Application {{ .name }} installed successfully'
|
||||
ConfigRuntime: 'Configure the runtime environment'
|
||||
ConfigApp: 'Configuration Application'
|
||||
ConfigOpenresty: 'Create OpenResty config file'
|
||||
InstallAppSuccess: 'App {{ .name }} installed'
|
||||
ConfigRuntime: 'Configure runtime'
|
||||
ConfigApp: 'Configure app'
|
||||
SuccessStatus: '{{ .name }} succeeded'
|
||||
FailedStatus: '{{ .name }} failed {{ .err }}'
|
||||
HandleLink: 'Handle application association'
|
||||
HandleDatabaseApp: 'Handling application parameters'
|
||||
ExecShell: 'Execute {{ .name }} script'
|
||||
FailedStatus: '{{ .name }} failed: {{ .err }}'
|
||||
HandleLink: 'Process app links'
|
||||
HandleDatabaseApp: 'Process app parameters'
|
||||
ExecShell: 'Run script {{ .name }}'
|
||||
PullImage: 'Pull image'
|
||||
Start: 'Start'
|
||||
Run: 'Start'
|
||||
Stop: 'Stop'
|
||||
Image: 'Mirror'
|
||||
Compose: 'Orchestration'
|
||||
Image: 'Image'
|
||||
Compose: 'Compose'
|
||||
Container: 'Container'
|
||||
AppLink: 'Linked Application'
|
||||
EnableSSL: 'Enable HTTPS'
|
||||
AppStore: 'App Store'
|
||||
TaskSync: 'Synchronize'
|
||||
TaskSync: 'Sync'
|
||||
LocalApp: 'Local Application'
|
||||
SubTask: 'Subtask'
|
||||
RuntimeExtension: 'Runtime Environment Extension'
|
||||
RuntimeExtension: 'Runtime extension'
|
||||
TaskIsExecuting: 'Task is running'
|
||||
CustomAppstore: 'Custom application warehouse'
|
||||
CustomAppstore: 'Custom application repository'
|
||||
TaskExec: 'Execute'
|
||||
TaskBatch: "Batch Operation"
|
||||
FileConvert: 'File Conversion'
|
||||
@@ -414,130 +451,125 @@ TaskScan: "Scan"
|
||||
|
||||
# task - ai
|
||||
OllamaModelPull: 'Pull Ollama model {{ .name }}'
|
||||
OllamaModelSize: 'Get the size of Ollama model {{ .name }}'
|
||||
OllamaModelSize: 'Get Ollama model size: {{ .name }}'
|
||||
|
||||
# task-snapshot
|
||||
Snapshot: 'Snapshot'
|
||||
SnapDBInfo: 'Write 1Panel database information'
|
||||
SnapCopy: 'Copy files & directories {{ .name }}'
|
||||
SnapNewDB: 'Initialize database {{ .name }} connection'
|
||||
SnapDeleteOperationLog: 'Delete operation log'
|
||||
SnapDeleteLoginLog: 'Delete access log'
|
||||
SnapDBInfo: 'Write 1Panel DB info'
|
||||
SnapCopy: 'Copy files/directories {{ .name }}'
|
||||
SnapNewDB: 'Init DB connection {{ .name }}'
|
||||
SnapDeleteOperationLog: 'Delete operation logs'
|
||||
SnapDeleteLoginLog: 'Delete access logs'
|
||||
SnapDeleteMonitor: 'Delete monitoring data'
|
||||
SnapRemoveSystemIP: 'Remove system IP'
|
||||
SnapBaseInfo: 'Write 1Panel basic information'
|
||||
SnapInstallAppImageEmpty: 'No application images selected, skipping...'
|
||||
SnapInstallApp: 'Backup 1Panel installed applications'
|
||||
SnapBaseInfo: 'Write 1Panel base info'
|
||||
SnapInstallAppImageEmpty: 'No app images selected; skipped'
|
||||
SnapInstallApp: 'Backup installed apps'
|
||||
SnapDockerSave: 'Compress installed applications'
|
||||
SnapLocalBackup: 'Backup 1Panel local backup directory'
|
||||
SnapLocalBackup: 'Backup local backup directory'
|
||||
SnapCompressBackup: 'Compress local backup directory'
|
||||
SnapPanelData: 'Backup 1Panel data directory'
|
||||
SnapCompressPanel: 'Compressed Data Directory'
|
||||
SnapWebsite: 'Backup 1Panel website directory'
|
||||
SnapCloseDBConn: 'Close the database connection'
|
||||
SnapCompress: 'Make snapshot files'
|
||||
SnapCompressPanel: 'Compress data directory'
|
||||
SnapWebsite: 'Backup website directory'
|
||||
SnapCloseDBConn: 'Close DB connection'
|
||||
SnapCompress: 'Create snapshot file'
|
||||
SnapCompressFile: 'Compress snapshot file'
|
||||
SnapCheckCompress: 'Check snapshot compression file'
|
||||
SnapCompressSize: 'Snapshot file size {{ .name }}'
|
||||
SnapCheckCompress: 'Check snapshot archive'
|
||||
SnapCompressSize: 'Snapshot size {{ .name }}'
|
||||
SnapUpload: 'Upload snapshot file'
|
||||
SnapUploadTo: 'Upload snapshot file to {{ .name }}'
|
||||
SnapUploadRes: 'Upload snapshot file to {{ .name }}'
|
||||
SnapUploadTo: 'Upload snapshot to {{ .name }}'
|
||||
SnapUploadRes: 'Upload snapshot to {{ .name }}'
|
||||
|
||||
SnapshotRecover: 'Snapshot Restore'
|
||||
SnapshotRecover: 'Recover snapshot'
|
||||
RecoverDownload: 'Download snapshot file'
|
||||
Download: 'Download'
|
||||
RecoverDownloadAccount: 'Get snapshot download backup account {{ .name }}'
|
||||
RecoverDecompress: 'Decompress snapshot compressed files'
|
||||
RecoverDownloadAccount: 'Get snapshot download account {{ .name }}'
|
||||
RecoverDecompress: 'Decompress snapshot archive'
|
||||
Decompress: 'Decompression'
|
||||
BackupBeforeRecover: 'Backup system-related data before snapshot'
|
||||
Readjson: 'Read the Json file in the snapshot'
|
||||
ReadjsonPath: 'Get the Json file path in the snapshot'
|
||||
ReadjsonContent: 'Read Json file'
|
||||
ReadjsonMarshal: 'Json escape processing'
|
||||
BackupBeforeRecover: 'Backup system data before recovery'
|
||||
ReadJson: 'Read snapshot JSON file'
|
||||
ReadJsonPath: 'Get snapshot JSON path'
|
||||
ReadJsonContent: 'Read JSON content'
|
||||
ReadJsonMarshal: 'Json escape processing'
|
||||
RecoverApp: 'Restore installed apps'
|
||||
RecoverWebsite: 'Recover website directory'
|
||||
RecoverWebsite: 'Restore website directory'
|
||||
RecoverAppImage: 'Restore snapshot image backup'
|
||||
RecoverCompose: 'Restore other composer content'
|
||||
RecoverComposeList: 'Get all composers to be restored'
|
||||
RecoverCompose: 'Restore other compose projects'
|
||||
RecoverComposeList: 'Get all compose projects to restore'
|
||||
RecoverComposeItem: 'Recover compose {{ .name }}'
|
||||
RecoverAppEmpty: 'No application image backup was found in the snapshot file'
|
||||
RecoverBaseData: 'Recover base data and files'
|
||||
RecoverDaemonJsonEmpty: 'Both the snapshot file and the current machine do not have the container configuration daemon.json file'
|
||||
RecoverDaemonJson: 'Restore container configuration daemon.json file'
|
||||
RecoverAppEmpty: 'No app image backup found in snapshot'
|
||||
RecoverBaseData: 'Restore base data and files'
|
||||
RecoverDaemonJsonEmpty: 'daemon.json not found in snapshot or current host'
|
||||
RecoverDaemonJson: 'Restore daemon.json'
|
||||
RecoverDBData: 'Recover database data'
|
||||
RecoverBackups: 'Restore local backup directory'
|
||||
RecoverPanelData: 'Recovery data directory'
|
||||
RecoverPanelData: 'Restore data directory'
|
||||
|
||||
# task - container
|
||||
ContainerNewCliet: 'Initialize Docker Client'
|
||||
ContainerImagePull: 'Pull container image {{ .name }}'
|
||||
ContainerRemoveOld: 'Remove the original container {{ .name }}'
|
||||
ContainerImageCheck: 'Check if the image is pulled normally'
|
||||
ContainerLoadInfo: 'Get basic container information'
|
||||
ContainerRecreate: 'Container update failed, now starting to restore the original container'
|
||||
ContainerImageCheck: 'Check image pull result'
|
||||
ContainerLoadInfo: 'Load container info'
|
||||
ContainerRecreate: 'Container update failed; restoring original container'
|
||||
ContainerCreate: 'Create a new container {{ .name }}'
|
||||
ContainerCreateFailed: 'Container creation failed, delete the failed container'
|
||||
ContainerStartCheck: 'Check if the container has been started'
|
||||
ContainerCreateFailed: 'Container create failed; deleting failed container'
|
||||
ContainerStartCheck: 'Check container started'
|
||||
|
||||
# task - image
|
||||
ImageBuild: 'Image Build'
|
||||
ImageBuildStdoutCheck: 'Parse image output content'
|
||||
ImageBuildRes: 'Image build output: {{ .name }}'
|
||||
ImageBuild: 'Build image'
|
||||
ImagePull: 'Pull image'
|
||||
ImageRepoAuthFromDB: 'Get repository authentication information from the database'
|
||||
ImaegPullRes: 'Image pull output: {{ .name }}'
|
||||
ImageRepoAuthFromDB: 'Load registry auth from DB'
|
||||
ImagePush: 'Push image'
|
||||
ImageRenameTag: 'Modify image tag'
|
||||
ImageRenameTag: 'Update image tag'
|
||||
ImageNewTag: 'New image tag {{ .name }}'
|
||||
ImaegPushRes: 'Image push output: {{ .name }}'
|
||||
ComposeCreate: 'Create a composition'
|
||||
ComposeCreateRes: 'Compose create output: {{ .name }}'
|
||||
ComposeCreate: 'Create compose project'
|
||||
|
||||
# task - website
|
||||
BackupNginxConfig: 'Backup website OpenResty configuration file'
|
||||
CompressFileSuccess: 'Compress directory successfully, compressed to {{ .name }}'
|
||||
CompressDir: 'Compression directory'
|
||||
BackupNginxConfig: 'Backup website OpenResty config'
|
||||
CompressFileSuccess: 'Directory compressed to {{ .name }}'
|
||||
CompressDir: 'Compress directory'
|
||||
DeCompressFile: 'Decompress file {{ .name }}'
|
||||
ErrCheckValid: 'Backup file verification failed, {{ .name }}'
|
||||
ErrCheckValid: 'Backup file validation failed: {{ .name }}'
|
||||
Rollback: 'Rollback'
|
||||
websiteDir: 'Website Directory'
|
||||
RecoverFailedStartRollBack: 'Recovery failed, start rollback'
|
||||
AppBackupFileIncomplete: 'The backup file is incomplete and lacks app.json or app.tar.gz files'
|
||||
websiteDir: 'Website directory'
|
||||
RecoverFailedStartRollBack: 'Recovery failed; starting rollback'
|
||||
AppBackupFileIncomplete: 'Backup is incomplete: missing app.json or app.tar.gz'
|
||||
AppAttributesNotMatch: 'Application type or name does not match'
|
||||
|
||||
#alert
|
||||
ErrAlert: 'The format of the warning message is incorrect, please check and try again!'
|
||||
ErrAlertPush: 'Error in pushing alert information, please check and try again!'
|
||||
ErrAlertSave: 'Error saving the alarm information, please check and try again!'
|
||||
ErrAlertSync: 'Alarm information synchronization error, please check and try again!'
|
||||
ErrAlertRemote: 'Alarm message remote error, please check and try again!'
|
||||
ErrAlertSync: 'Failed to sync alert message'
|
||||
|
||||
#task - runtime
|
||||
ErrInstallExtension: "An installation task is already in progress, please wait for the task to finish"
|
||||
ErrInstallExtension: 'Another extension install task is running'
|
||||
|
||||
# alert mail template
|
||||
PanelAlertTitle: "Panel Alert Notification"
|
||||
TestAlertTitle: "Test Email - Verify Email Connectivity"
|
||||
TestAlert: "This is a test email to verify that your email sending configuration is correct."
|
||||
LicenseExpirationAlert: "Your {{ .node }}{{ .ip }} Panel, the license will expire in {{ .day }} days. Please log in to the panel for details."
|
||||
CronJobFailedAlert: "Your {{ .node }}{{ .ip }} Panel, scheduled task - {{ .name }} execution failed. Please log in to the panel for details."
|
||||
ClamAlert: "Your {{ .node }}{{ .ip }} Panel, the virus scan task detected {{ .num }} infected files. Please log in to the panel for details."
|
||||
WebSiteAlert: "Your {{ .node }}{{ .ip }} Panel, {{ .num }} websites will expire in {{ .day }} days. Please log in to the panel for details."
|
||||
SSLAlert: "Your {{ .node }}{{ .ip }} Panel, {{ .num }} SSL certificates will expire in {{ .day }} days. Please log in to the panel for details."
|
||||
DiskUsedAlert: "Your {{ .node }}{{ .ip }} Panel, disk {{ .name }} usage has reached {{ .used }}. Please log in to the panel for details."
|
||||
ResourceAlert: "Your {{ .node }}{{ .ip }} Panel, the average {{ .name }} usage rate in {{ .time }} minutes is {{ .used }}. Please log in to the panel for details."
|
||||
PanelVersionAlert: "Your {{ .node }}{{ .ip }} Panel, the latest panel version is available for upgrade. Please log in to the panel for details."
|
||||
PanelPwdExpirationAlert: "Your {{ .node }}{{ .ip }} Panel, the panel password will expire in {{ .day }} days. Please log in to the panel for details."
|
||||
CommonAlert: "Your {{ .node }}{{ .ip }} Panel, {{ .msg }}. Please log in to the panel for details."
|
||||
NodeExceptionAlert: "Your {{ .node }}{{ .ip }} Panel, {{ .num }} nodes are abnormal. Please log in to the panel for details."
|
||||
LicenseExceptionAlert: "Your {{ .node }}{{ .ip }} Panel, {{ .num }} licenses are abnormal. Please log in to the panel for details."
|
||||
SSHAndPanelLoginAlert: "Your {{ .node }}{{ .ip }} Panel, {{ .name }} login from {{ .loginIp }} is abnormal. Please log in to the panel for details."
|
||||
LicenseExpirationAlert: "Panel {{ .node }}{{ .ip }}: license expires in {{ .day }} days. Log in to view details."
|
||||
CronJobFailedAlert: "Panel {{ .node }}{{ .ip }}: scheduled task {{ .name }} failed. Log in to view details."
|
||||
ClamAlert: "Panel {{ .node }}{{ .ip }}: virus scan found {{ .num }} infected files. Log in to view details."
|
||||
WebSiteAlert: "Panel {{ .node }}{{ .ip }}: {{ .num }} websites expire in {{ .day }} days. Log in to view details."
|
||||
SSLAlert: "Panel {{ .node }}{{ .ip }}: {{ .num }} SSL certificates expire in {{ .day }} days. Log in to view details."
|
||||
DiskUsedAlert: "Panel {{ .node }}{{ .ip }}: disk {{ .name }} usage reached {{ .used }}. Log in to view details."
|
||||
ResourceAlert: "Panel {{ .node }}{{ .ip }}: average {{ .name }} usage in {{ .time }} minutes is {{ .used }}. Log in to view details."
|
||||
PanelVersionAlert: "Panel {{ .node }}{{ .ip }}: a new panel version is available. Log in to view details."
|
||||
PanelPwdExpirationAlert: "Panel {{ .node }}{{ .ip }}: panel password expires in {{ .day }} days. Log in to view details."
|
||||
CommonAlert: "Panel {{ .node }}{{ .ip }}: {{ .msg }}. Log in to view details."
|
||||
NodeExceptionAlert: "Panel {{ .node }}{{ .ip }}: {{ .num }} nodes are abnormal. Log in to view details."
|
||||
LicenseExceptionAlert: "Panel {{ .node }}{{ .ip }}: {{ .num }} licenses are abnormal. Log in to view details."
|
||||
SSHAndPanelLoginAlert: "Panel {{ .node }}{{ .ip }}: abnormal {{ .name }} login from {{ .loginIp }}. Log in to view details."
|
||||
|
||||
#disk
|
||||
DeviceNotFound: "Device {{ .name }} not found"
|
||||
DeviceIsMounted: "Device {{ .name }} is mounted, please unmount first"
|
||||
PartitionDiskErr: "Failed to partition, {{ .err }}"
|
||||
FormatDiskErr: "Failed to format disk, {{ .err }}"
|
||||
MountDiskErr: "Failed to mount disk, {{ .err }}"
|
||||
UnMountDiskErr: "Failed to unmount disk, {{ .err }}"
|
||||
XfsNotFound: "xfs filesystem not detected, please install xfsprogs first"
|
||||
DeviceIsMounted: 'Device {{ .name }} is mounted; unmount first'
|
||||
PartitionDiskErr: 'Partition failed: {{ .err }}'
|
||||
FormatDiskErr: 'Format failed: {{ .err }}'
|
||||
MountDiskErr: 'Mount failed: {{ .err }}'
|
||||
XfsNotFound: 'xfs not found; install xfsprogs first'
|
||||
|
||||
# terminal
|
||||
TerminalAIBlockedRiskyCommand: 'blocked risky command: {{ .command }}'
|
||||
TerminalAIThinking: 'AI is thinking...'
|
||||
TerminalAIReadyToExecute: 'Thinking complete, press Enter to execute (duration: {{ .duration }}, tokens: {{ .tokens }})'
|
||||
TerminalAIRequestFailed: 'AI request failed: {{ .err }}'
|
||||
|
||||
+206
-230
@@ -1,30 +1,29 @@
|
||||
ErrInvalidParams: 'Error en los parámetros de la solicitud: {{ .detail }}'
|
||||
ErrTokenParse: 'Error al generar el token: {{ .detail }}'
|
||||
ErrInitialPassword: 'La contraseña original es incorrecta'
|
||||
ErrInternalServer: 'Error interno del servidor: {{ .detail }}'
|
||||
ErrRecordExist: 'El registro ya existe'
|
||||
ErrInvalidParams: 'Solicitud inválida: {{ .detail }}'
|
||||
ErrTokenParse: 'Token inválido: {{ .detail }}'
|
||||
ErrInitialPassword: 'Contraseña original incorrecta'
|
||||
ErrInternalServer: 'Error interno: {{ .detail }}'
|
||||
ErrWgetRemoteFailed: 'La descarga remota falló o la URL no es accesible: {{ .detail }}'
|
||||
ErrWgetInvalidContentType: 'La URL remota no devolvió un archivo (posiblemente una página de error), {{ .detail }}'
|
||||
ErrRecordExist: 'Registro ya existe'
|
||||
ErrRecordNotFound: 'Registro no encontrado'
|
||||
ErrStructTransform: 'Error de conversión de tipo: {{ .err }}'
|
||||
ErrNotLogin: 'Usuario no ha iniciado sesión: {{ .detail }}'
|
||||
ErrPasswordExpired: 'La contraseña actual ha expirado: {{ .detail }}'
|
||||
ErrNotSupportType: 'El sistema no admite el tipo actual: {{ .name }}'
|
||||
ErrProxy: 'Error en la solicitud, por favor revise el estado del nodo: {{ .detail }}'
|
||||
ErrApiConfigStatusInvalid: 'Se prohíbe el acceso a la interfaz API: {{ .detail }}'
|
||||
ErrApiConfigKeyInvalid: 'Error en la clave de la interfaz API: {{ .detail }}'
|
||||
ErrApiConfigIPInvalid: 'La IP usada para llamar a la API no está en la lista blanca: {{ .detail }}'
|
||||
ErrApiConfigDisable: 'Esta interfaz prohíbe el uso de llamadas a la API: {{ .detail }}'
|
||||
ErrApiConfigKeyTimeInvalid: 'Error en la marca de tiempo de la interfaz API: {{ .detail }}'
|
||||
ErrShutDown: "¡Comando terminado manualmente!"
|
||||
|
||||
ErrMinQuickJump: "¡Por favor configure al menos una entrada de acceso rápido!"
|
||||
ErrMaxQuickJump: "¡Puede configurar hasta cuatro entradas de acceso rápido!"
|
||||
|
||||
#común
|
||||
ErrUsernameIsExist: 'El nombre de usuario ya existe'
|
||||
ErrNameIsExist: 'El nombre ya existe'
|
||||
ErrDemoEnvironment: '¡Servidor de demostración, esta operación está prohibida!'
|
||||
ErrCmdTimeout: '¡Tiempo de espera excedido en la ejecución del comando!'
|
||||
ErrCmdIllegal: 'Hay caracteres ilegales en el comando, ¡modifíquelo e intente de nuevo!'
|
||||
ErrStructTransform: 'Error de conversión: {{ .err }}'
|
||||
ErrNotLogin: 'No has iniciado sesión: {{ .detail }}'
|
||||
ErrPasswordExpired: 'Contraseña expirada: {{ .detail }}'
|
||||
ErrNotSupportType: 'Tipo no soportado: {{ .name }}'
|
||||
ErrProxy: 'Solicitud fallida: {{ .detail }}'
|
||||
ErrApiConfigStatusInvalid: 'API desactivada: {{ .detail }}'
|
||||
ErrApiConfigKeyInvalid: 'Clave API inválida: {{ .detail }}'
|
||||
ErrApiConfigIPInvalid: 'IP no permitida: {{ .detail }}'
|
||||
ErrApiConfigDisable: 'API no permitida: {{ .detail }}'
|
||||
ErrApiConfigKeyTimeInvalid: 'Timestamp inválido: {{ .detail }}'
|
||||
ErrShutDown: 'Comando detenido manualmente'
|
||||
ErrMinQuickJump: 'Agrega al menos una entrada rápida'
|
||||
ErrMaxQuickJump: 'Máximo 4 entradas rápidas'
|
||||
ErrUsernameIsExist: 'Usuario ya existe'
|
||||
ErrNameIsExist: 'Nombre ya existe'
|
||||
ErrDemoEnvironment: 'No disponible en demo'
|
||||
ErrCmdTimeout: 'Comando agotó tiempo'
|
||||
ErrCmdIllegal: 'Comando contiene caracteres inválidos'
|
||||
ErrPortExist: 'El puerto {{ .port }} ya está ocupado por {{ .type }} [{{ .name }}]'
|
||||
TYPE_APP: 'Aplicación'
|
||||
TYPE_RUNTIME: 'Entorno de ejecución'
|
||||
@@ -36,45 +35,43 @@ Failed: 'Error'
|
||||
SystemRestart: 'La tarea fue interrumpida debido a un reinicio del sistema'
|
||||
ErrGroupIsDefault: 'Grupo predeterminado, no se puede eliminar'
|
||||
ErrGroupIsInWebsiteUse: 'El grupo está siendo usado por otro sitio web y no se puede eliminar.'
|
||||
Decrypt: "Descifrar"
|
||||
|
||||
#agent
|
||||
ErrAgentAccountBound: 'Esta cuenta está vinculada a un agente y no se puede eliminar. Verifique e inténtelo de nuevo!'
|
||||
ErrAgentAccountUnavailable: 'La información de conexión de la cuenta no está disponible. Error: {{ .err }}. Verifique e inténtelo de nuevo!'
|
||||
ErrAgentProviderNotSupported: 'Este proveedor de agente no es compatible. Verifique e inténtelo de nuevo!'
|
||||
ErrAgentAccountRequired: 'Seleccione una cuenta de agente y vuelva a intentarlo.'
|
||||
ErrAgentAccountNotVerified: 'La cuenta no está verificada. Verifique e inténtelo de nuevo!'
|
||||
ErrAgentProviderMismatch: 'El proveedor de la cuenta no coincide. Verifique e inténtelo de nuevo!'
|
||||
ErrAgentBaseURLRequired: 'Base URL no puede estar vacío. Verifique e inténtelo de nuevo!'
|
||||
ErrAgentApiKeyRequired: 'API Key no puede estar vacío. Verifique e inténtelo de nuevo!'
|
||||
ErrAgentComposeRequired: 'El contenido personalizado de compose no puede estar vacío. Verifique e inténtelo de nuevo!'
|
||||
ErrAgentIDRequired: 'El ID del agente no puede estar vacío. Verifique e inténtelo de nuevo!'
|
||||
ErrAgentAccountIDRequired: 'El ID de la cuenta no puede estar vacío. Verifique e inténtelo de nuevo!'
|
||||
|
||||
#backup
|
||||
ErrBackupInUsed: 'La cuenta de respaldo está siendo utilizada en una tarea programada y no se puede eliminar.'
|
||||
ErrBackupCheck: 'Error al probar la conexión de la cuenta de respaldo {{ .err }}'
|
||||
ErrBackupLocalDelete: 'No se admite aún la eliminación de la cuenta de respaldo del servidor local'
|
||||
ErrBackupLocalCreate: 'No se admite aún la creación de cuentas de respaldo del servidor local'
|
||||
Localhost: "Máquina local"
|
||||
|
||||
#app
|
||||
ErrPortInUsed: '¡El puerto {{ .detail }} ya está ocupado!'
|
||||
Decrypt: 'Descifrar'
|
||||
ErrAgentAccountBound: 'Cuenta vinculada a agente'
|
||||
ErrAgentAccountUnavailable: 'Conexión de cuenta no disponible: {{ .err }}'
|
||||
ErrAgentProviderNotSupported: 'Proveedor de agente no soportado'
|
||||
ErrAgentAccountRequired: 'Elige una cuenta de agente primero'
|
||||
ErrAgentAccountNotVerified: 'Cuenta de agente no verificada'
|
||||
ErrAgentAccountModelsRequired: 'Configure al menos un modelo'
|
||||
ErrAgentAccountSingleInitialModel: 'Solo se puede configurar un modelo inicial al crear la cuenta'
|
||||
ErrAgentModelNotInAccount: 'El modelo no está configurado en la cuenta seleccionada'
|
||||
ErrAgentModelInUse: 'Un agente vinculado todavía usa este modelo: {{ .name }}'
|
||||
ErrAgentBaseURLRequired: 'URL base requerida'
|
||||
ErrAgentApiKeyRequired: 'Clave API requerida'
|
||||
ErrAgentComposeRequired: 'Contenido compose requerido'
|
||||
ErrAgentIDRequired: 'ID de agente requerido'
|
||||
ErrAgentAccountIDRequired: 'ID de cuenta requerido'
|
||||
ErrAgentLimitReached: 'La edición Community admite hasta {{ .max }} agentes de IA. Actualice a la edición Professional para eliminar el límite'
|
||||
Localhost: 'Máquina local'
|
||||
ErrBackupInUsed: 'Cuenta de respaldo en uso por tarea programada'
|
||||
ErrBackupCheck: 'Conexión de respaldo falló: {{ .err }}'
|
||||
ErrBackupLocalDelete: 'No se puede eliminar cuentas de respaldo locales'
|
||||
ErrBackupLocalCreate: 'No se pueden crear cuentas de respaldo locales'
|
||||
ErrPortInUsed: 'El puerto {{ .detail }} ya está ocupado'
|
||||
ErrAppLimit: 'El número de aplicaciones instaladas ha superado el límite'
|
||||
ErrNotInstall: 'Aplicación no instalada'
|
||||
ErrPortInOtherApp: '¡El puerto {{ .port }} ya está ocupado por la aplicación {{ .apps }}!'
|
||||
ErrDbUserNotValid: '¡Base de datos existente, usuario y contraseña no coinciden!'
|
||||
ErrUpdateBuWebsite: 'La aplicación se actualizó correctamente, pero falló la modificación del archivo de configuración del sitio web. ¡Por favor revise la configuración!'
|
||||
Err1PanelNetworkFailed: '¡Fallo la creación de la red por defecto del contenedor! {{ .detail }}'
|
||||
ErrFileParse: '¡Fallo al analizar el archivo docker-compose de la aplicación!'
|
||||
ErrPortInOtherApp: 'El puerto {{ .port }} ya está ocupado por la aplicación {{ .apps }}'
|
||||
ErrDbUserNotValid: 'Base de datos existente, usuario y contraseña no coinciden'
|
||||
ErrUpdateBuWebsite: 'La aplicación se actualizó correctamente, pero falló la modificación del archivo de configuración del sitio web. revise la configuración'
|
||||
Err1PanelNetworkFailed: 'Fallo la creación de la red por defecto del contenedor {{ .detail }}'
|
||||
ErrFileParse: 'Fallo al analizar el archivo docker-compose de la aplicación'
|
||||
ErrInstallDirNotFound: 'El directorio de instalación no existe. Si desea desinstalar, seleccione Desinstalación forzada'
|
||||
AppStoreIsUpToDate: 'La tienda de aplicaciones ya está en la última versión'
|
||||
LocalAppVersionNull: '¡La aplicación {{ .name }} no está sincronizada a la versión! No se puede agregar a la lista de aplicaciones'
|
||||
LocalAppVersionErr: '¡La sincronización de la versión {{ .version }} de {{ .name }} falló! {{ .err }}'
|
||||
LocalAppVersionNull: 'La aplicación {{ .name }} no está sincronizada a la versión No se puede agregar a la lista de aplicaciones'
|
||||
LocalAppVersionErr: 'La sincronización de la versión {{ .version }} de {{ .name }} falló {{ .err }}'
|
||||
ErrFileNotFound: 'El archivo {{ .name }} no existe'
|
||||
ErrFileParseApp: '¡El archivo {{ .name }} falló al analizarse! {{ .err }}'
|
||||
ErrFileParseApp: 'El archivo {{ .name }} falló al analizarse {{ .err }}'
|
||||
ErrAppDirNull: 'La carpeta de la versión no existe'
|
||||
LocalAppErr: '¡La sincronización de la aplicación {{ .name }} falló! {{ .err }}'
|
||||
LocalAppErr: 'La sincronización de la aplicación {{ .name }} falló {{ .err }}'
|
||||
ErrContainerName: 'El nombre del contenedor ya existe'
|
||||
ErrCreateHttpClient: 'Fallo al crear la solicitud {{ .err }}'
|
||||
ErrHttpReqTimeOut: 'La solicitud superó el tiempo de espera {{ .err }}'
|
||||
@@ -87,46 +84,42 @@ ErrAppBackup: 'Falló el respaldo de la aplicación {{ .name }} {{ .err }}'
|
||||
ErrVersionTooLow: 'La versión actual de 1Panel es demasiado baja para actualizar la tienda de aplicaciones. Actualice primero.'
|
||||
ErrAppNameExist: 'El nombre de la aplicación ya existe'
|
||||
AppStoreIsSyncing: 'La tienda de aplicaciones está sincronizando, inténtelo más tarde'
|
||||
ErrGetCompose: '¡Fallo al obtener el archivo docker-compose.yml! {{ .detail }}'
|
||||
ErrGetCompose: 'Fallo al obtener el archivo docker-compose.yml {{ .detail }}'
|
||||
ErrAppWarn: 'Estado anómalo, revise el log'
|
||||
ErrAppParamKey: 'El campo de parámetro {{ .name }} es anómalo'
|
||||
ErrAppUpgrade: 'La actualización de la aplicación {{ .name }} falló {{ .err }}'
|
||||
AppRecover: 'Revertir la aplicación {{ .name }}'
|
||||
PullImageStart: 'Iniciar descarga de la imagen [{{ .name }}]'
|
||||
PullImageSuccess: 'Descarga de imagen exitosa'
|
||||
AppStoreIsLastVersion: 'La tienda de aplicaciones ya está en la última versión'
|
||||
AppStoreSyncSuccess: 'Sincronización de la tienda de aplicaciones exitosa'
|
||||
SyncAppDetail: 'Sincronizar configuración de la aplicación'
|
||||
AppVersionNotMatch: 'La aplicación {{ .name }} requiere una versión superior de 1Panel, se omite la sincronización'
|
||||
MoveSiteDir: 'La actualización actual requiere migrar el directorio del sitio web OpenResty'
|
||||
MoveSiteDirSuccess: 'Migración del directorio del sitio exitosa'
|
||||
DeleteRuntimePHP: 'Eliminar entorno de ejecución PHP'
|
||||
CustomAppStoreFileValid: 'Los paquetes de la tienda de aplicaciones deben estar en formato .tar.gz'
|
||||
CustomAppStoreFileValid: 'Los paquetes de la tienda de aplicaciones deben estar en formato.tar.gz'
|
||||
PullImageTimeout: 'Tiempo de espera al descargar la imagen, intente aumentar la aceleración o use otra'
|
||||
ErrAppIsDown: 'El estado de la aplicación {{ .name }} es anómalo, revise'
|
||||
ErrCustomApps: 'Hay una aplicación instalada, desinstálela primero'
|
||||
ErrCustomRuntimes: 'Hay un entorno de ejecución instalado, elimínelo primero'
|
||||
ErrAppVersionDeprecated: "La aplicación {{ .name }} no es compatible con la versión actual de 1Panel, omitida"
|
||||
ErrDockerFailed: "El estado de Docker es anómalo, revise el servicio"
|
||||
ErrDockerComposeCmdNotFound: "El comando Docker Compose no existe, por favor instálelo primero en el host"
|
||||
UseExistImage: 'Error al extraer la imagen, usando imagen existente'
|
||||
|
||||
#ssh
|
||||
ExportIP: "IP de inicio de sesión"
|
||||
ExportArea: "Ubicación"
|
||||
ExportPort: "Puerto"
|
||||
ExportAuthMode: "Método de inicio de sesión"
|
||||
ExportUser: "Usuario"
|
||||
ExportStatus: "Estado de inicio de sesión"
|
||||
ExportDate: "Fecha y hora"
|
||||
|
||||
#file
|
||||
ErrAppVersionDeprecated: 'La aplicación {{ .name }} no es compatible con la versión actual de 1Panel, omitida'
|
||||
ErrDockerFailed: 'El estado de Docker es anómalo, revise el servicio'
|
||||
ErrDockerComposeCmdNotFound: 'El comando Docker Compose no existe instálelo primero en el host'
|
||||
UseExistImage: 'La imagen ya existe, usando imagen existente'
|
||||
ErrDatabaseNotFound: 'La base de datos {{ .name }} no existe'
|
||||
ExportIP: 'IP de inicio de sesión'
|
||||
ExportArea: 'Ubicación'
|
||||
ExportPort: 'Puerto'
|
||||
ExportAuthMode: 'Método de inicio de sesión'
|
||||
ExportUser: 'Usuario'
|
||||
ExportStatus: 'Estado de inicio de sesión'
|
||||
ExportDate: 'Fecha y hora'
|
||||
ErrFileCanNotRead: 'Este archivo no soporta vista previa'
|
||||
ErrFileToLarge: 'El archivo es mayor a 10M y no puede abrirse'
|
||||
ErrPathNotFound: 'El directorio no existe'
|
||||
ErrMovePathFailed: '¡La ruta de destino no puede contener la ruta original!'
|
||||
ErrLinkPathNotFound: '¡La ruta de destino no existe!'
|
||||
ErrFileIsExist: '¡El archivo o carpeta ya existe!'
|
||||
ErrMovePathFailed: 'La ruta de destino no puede contener la ruta original'
|
||||
ErrLinkPathNotFound: 'La ruta de destino no existe'
|
||||
ErrFileIsExist: 'El archivo o carpeta ya existe'
|
||||
ErrFileUpload: '{{ .name }} falló al subir archivo {{ .detail }}'
|
||||
ErrFileDownloadDir: 'No se admite la descarga de carpetas'
|
||||
ErrCmdNotFound: 'El comando {{ .name }} no existe, instálelo primero en el host'
|
||||
@@ -134,16 +127,15 @@ ErrSourcePathNotFound: 'El directorio fuente no existe'
|
||||
ErrFavoriteExist: 'Esta ruta ya ha sido marcada como favorita'
|
||||
ErrInvalidChar: 'No se permiten caracteres ilegales'
|
||||
ErrPathNotDelete: 'No se puede eliminar el directorio seleccionado'
|
||||
FileDropFailed: "Error al limpiar el archivo {{ .name }}: {{ .err }}"
|
||||
FileDropSuccess: "Archivo {{ .name }} limpiado correctamente, {{ .count }} archivos eliminados, {{ .size }} de espacio liberado"
|
||||
FileDropSum: "Limpieza de archivos completada, total {{ .count }} archivos eliminados, {{ .size }} de espacio liberado"
|
||||
ErrBadDecrypt: "Error de contraseña de descifrado del paquete comprimido, descompresión fallida, ¡compruebe e inténtelo de nuevo!"
|
||||
|
||||
#website
|
||||
ErrLogFileToLarge: 'Log file exceeds 500 MB'
|
||||
FileDropFailed: 'Error al limpiar el archivo {{ .name }}: {{ .err }}'
|
||||
FileDropSuccess: 'Archivo {{ .name }} limpiado correctamente, {{ .count }} archivos eliminados, {{ .size }} de espacio liberado'
|
||||
FileDropSum: 'Limpieza de archivos completada, total {{ .count }} archivos eliminados, {{ .size }} de espacio liberado'
|
||||
ErrBadDecrypt: 'Error de contraseña de descifrado del paquete comprimido, descompresión fallida, compruebe e inténtelo de nuevo'
|
||||
ErrAliasIsExist: 'El alias ya existe'
|
||||
ErrBackupMatch: 'El archivo de respaldo no coincide con parte de los datos actuales del sitio web {{ .detail }}'
|
||||
ErrBackupExist: 'Parte de los datos fuente del respaldo no existe {{ .detail }}'
|
||||
ErrPHPResource: '¡El entorno local no soporta cambio!'
|
||||
ErrPHPResource: 'El entorno local no soporta cambio'
|
||||
ErrPathPermission: 'Se detectó una carpeta con permisos distintos a 1000:1000 en el directorio index, lo cual puede causar errores de acceso denegado. Haga clic en Guardar arriba'
|
||||
ErrDomainIsUsed: 'El dominio ya está en uso por el sitio web [{{ .name }}]'
|
||||
ErrDomainFormat: 'El formato del dominio {{ .name }} es incorrecto'
|
||||
@@ -151,11 +143,11 @@ ErrDefaultAlias: 'default es un código reservado, use otro'
|
||||
ErrParentWebsite: 'Primero debe eliminar el sub-sitio {{ .name }}'
|
||||
ErrBuildDirNotFound: 'El directorio de compilación no existe'
|
||||
ErrImageNotExist: 'La imagen del entorno {{ .name }} no existe, edítela de nuevo'
|
||||
ErrProxyIsUsed: "El balanceo de carga ya está usado por un proxy reverso, no se puede eliminar"
|
||||
ErrSSLValid: 'Archivo de certificado anómalo, ¡revise el estado del certificado!'
|
||||
ErrWebsiteDir: "Por favor seleccione un directorio dentro del directorio del sitio web"
|
||||
ErrComposerFileNotFound: "El archivo composer.json no existe"
|
||||
ErrRuntimeNoPort: "El entorno de ejecución no tiene configurado un puerto, edítelo primero"
|
||||
ErrProxyIsUsed: 'El balanceo de carga ya está usado por un proxy reverso, no se puede eliminar'
|
||||
ErrSSLValid: 'Archivo de certificado anómalo, revise el estado del certificado'
|
||||
ErrWebsiteDir: 'seleccione un directorio dentro del directorio del sitio web'
|
||||
ErrComposerFileNotFound: 'El archivo composer.json no existe'
|
||||
ErrRuntimeNoPort: 'El entorno de ejecución no tiene configurado un puerto, edítelo primero'
|
||||
Status: 'Estado'
|
||||
start: 'Iniciar'
|
||||
stop: 'Detener'
|
||||
@@ -165,14 +157,12 @@ pause: 'Pausar'
|
||||
unpause: 'Reanudar'
|
||||
remove: 'Eliminar'
|
||||
delete: 'Eliminar'
|
||||
ErrDefaultWebsite: 'El sitio web predeterminado ya está configurado, ¡cancélelo antes de configurar!'
|
||||
ErrDefaultWebsite: 'El sitio web predeterminado ya está configurado, cancélelo antes de configurar'
|
||||
SSL: 'Certificado'
|
||||
Set: 'Configuración'
|
||||
|
||||
#ssl
|
||||
ErrSSLCannotDelete: 'El certificado {{ .name }} está siendo utilizado por un sitio web y no puede eliminarse'
|
||||
ErrAccountCannotDelete: 'La cuenta está asociada a un certificado y no puede eliminarse'
|
||||
ErrSSLApply: 'Renovación del certificado exitosa, recarga de openresty fallida, ¡revise la configuración!'
|
||||
ErrSSLApply: 'Renovación del certificado exitosa, recarga de openresty fallida, revise la configuración'
|
||||
ErrEmailIsExist: 'El correo ya existe'
|
||||
ErrSSLKeyNotFound: 'El archivo de clave privada no existe'
|
||||
ErrSSLCertificateNotFound: 'El archivo de certificado no existe'
|
||||
@@ -185,9 +175,10 @@ dnsAccount: 'DNS Automático'
|
||||
dnsManual: 'DNS Manual'
|
||||
http: 'HTTP'
|
||||
ApplySSLFailed: 'Solicitud de certificado para [{{ .domain }}] fallida, {{ .detail }}'
|
||||
ApplySSLSuccess: '¡Solicitud de certificado para [{{ .domain }}] exitosa!'
|
||||
ApplySSLSuccess: 'Solicitud de certificado para [{{ .domain }}] exitosa'
|
||||
DNSAccountName: 'Cuenta DNS [{{ .name }}] proveedor [{{ .type }}]'
|
||||
PushDirLog: 'Certificado empujado al directorio [{{ .path }}] {{ .status }}'
|
||||
ErrDeleteCAWithSSL: 'La organización actual tiene un certificado emitido y no puede eliminarse.'
|
||||
ErrDeleteWithPanelSSL: 'La configuración SSL del panel utiliza este certificado y no puede eliminarse'
|
||||
ErrDefaultCA: 'La autoridad por defecto no puede eliminarse'
|
||||
ApplyWebSiteSSLLog: 'Iniciando renovación del certificado del sitio web {{ .name }}'
|
||||
@@ -199,98 +190,109 @@ ExecShellSuccess: 'Ejecución de script exitosa'
|
||||
StartUpdateSystemSSL: 'Iniciando actualización del certificado del sistema'
|
||||
UpdateSystemSSLSuccess: 'Certificado del sistema actualizado correctamente'
|
||||
ErrWildcardDomain: 'No se puede solicitar certificado de dominio wildcard en modo HTTP'
|
||||
ErrDeleteCAWithSSL: "La organización actual tiene un certificado emitido y no puede eliminarse."
|
||||
ErrApplySSLCanNotDelete: "El certificado {{ .name }} en proceso de solicitud no puede eliminarse, inténtelo más tarde"
|
||||
StartPushSSLToNode: "Iniciando envío de certificado al nodo"
|
||||
PushSSLToNodeFailed: "Error al enviar el certificado al nodo: {{ .err }}"
|
||||
PushSSLToNodeSuccess: "Certificado enviado correctamente al nodo"
|
||||
|
||||
#mysql
|
||||
ErrApplySSLCanNotDelete: 'El certificado {{ .name }} en proceso de solicitud no puede eliminarse, inténtelo más tarde'
|
||||
StartPushSSLToNode: 'Iniciando envío de certificado al nodo'
|
||||
PushSSLToNodeFailed: 'Error al enviar el certificado al nodo: {{ .err }}'
|
||||
PushSSLToNodeSuccess: 'Certificado enviado correctamente al nodo'
|
||||
ErrUserIsExist: 'El usuario actual ya existe, intente con otro'
|
||||
ErrDatabaseIsExist: 'La base de datos actual ya existe, intente con otro nombre'
|
||||
ErrExecTimeOut: 'Tiempo de espera en la ejecución SQL, revise la base de datos'
|
||||
ErrRemoteExist: 'La base de datos remota ya existe con ese nombre, modifíquelo e intente de nuevo'
|
||||
ErrLocalExist: 'El nombre ya existe en la base de datos local, modifíquelo e intente de nuevo'
|
||||
RemoteBackup: "Para hacer una copia de seguridad de la base de datos remota, primero debe iniciarse el servicio de base de datos del contenedor local utilizando la imagen {{ .name }}, espere por favor..."
|
||||
RemoteRecover: "Para restaurar la base de datos remota, primero debe iniciarse el servicio de base de datos del contenedor local utilizando la imagen {{ .name }}, espere por favor..."
|
||||
Arg: "Argumento"
|
||||
|
||||
#redis
|
||||
RemoteBackup: 'Para hacer una copia de seguridad de la base de datos remota, primero debe iniciarse el servicio de base de datos del contenedor local utilizando la imagen {{ .name }}, espere...'
|
||||
RemoteRecover: 'Para restaurar la base de datos remota, primero debe iniciarse el servicio de base de datos del contenedor local utilizando la imagen {{ .name }}, espere...'
|
||||
Arg: 'Argumento'
|
||||
ErrTypeOfRedis: 'El tipo de archivo de recuperación no coincide con el método de persistencia actual, modifíquelo e intente'
|
||||
|
||||
#container
|
||||
ErrInUsed: "{{ .detail }} está en uso y no se puede eliminar!"
|
||||
ErrObjectInUsed: "Este objeto está en uso y no se puede eliminar!"
|
||||
ErrObjectBeDependent: "Esta imagen es dependida por otras imágenes y no se puede eliminar!"
|
||||
ErrInUsed: '{{ .detail }} está en uso y no se puede eliminar'
|
||||
ErrObjectInUsed: 'Este objeto está en uso y no se puede eliminar'
|
||||
ErrObjectBeDependent: 'Esta imagen es dependida por otras imágenes y no se puede eliminar'
|
||||
ErrPortRules: 'El número de puerto no coincide, ingrese de nuevo'
|
||||
ErrPgImagePull: 'Tiempo de espera al descargar la imagen, configure la aceleración o descárguela manualmente ({{ .name }}) e intente de nuevo'
|
||||
PruneHelper: "Esta limpieza {{ .name }} liberó espacio en disco {{ .size }}"
|
||||
ImageRemoveHelper: "Imagen {{ .name }} eliminada, liberando {{ .size }} de espacio en disco"
|
||||
BuildCache: "Caché de compilación"
|
||||
Volume: "Volumen de almacenamiento"
|
||||
Network: "Red"
|
||||
PruneStart: "Limpiando en progreso, por favor espere..."
|
||||
|
||||
#runtime
|
||||
ErrFileNotExist: 'El archivo {{ .detail }} no existe, ¡verifique la integridad!'
|
||||
PruneHelper: 'Esta limpieza {{ .name }} liberó espacio en disco {{ .size }}'
|
||||
ImageRemoveHelper: 'Imagen {{ .name }} eliminada, liberando {{ .size }} de espacio en disco'
|
||||
BuildCache: 'Caché de compilación'
|
||||
Volume: 'Volumen de almacenamiento'
|
||||
Network: 'Red'
|
||||
PruneStart: 'Limpiando en progreso espere...'
|
||||
ContainerBackupPrepare: 'Preparar espacio de respaldo del contenedor'
|
||||
ContainerBackupStop: 'Detener contenedor antes del respaldo'
|
||||
ContainerBackupInspect: 'Respaldar inspect y red del contenedor'
|
||||
ContainerBackupMounts: 'Respaldar montajes del contenedor'
|
||||
ContainerBackupMeta: 'Escribir metadatos del respaldo del contenedor'
|
||||
ContainerBackupStart: 'Iniciar contenedor después del respaldo'
|
||||
ContainerRecoverPrepare: 'Preparar espacio de restauración del contenedor'
|
||||
ContainerRecoverExtract: 'Extraer archivo de respaldo del contenedor'
|
||||
ContainerRecoverParse: 'Cargar metadatos del respaldo del contenedor'
|
||||
ContainerRecoverCreate: 'Recrear contenedor'
|
||||
ContainerRecoverMounts: 'Restaurar montajes del contenedor'
|
||||
ContainerRecoverStart: 'Iniciar contenedor restaurado'
|
||||
ContainerRecoverCleanup: 'Limpiar espacio de restauracion del contenedor'
|
||||
ComposeBackupPrepare: 'Preparar espacio de respaldo del compose'
|
||||
ComposeBackupStop: 'Detener compose antes del respaldo'
|
||||
ComposeBackupFiles: 'Respaldar archivos de compose'
|
||||
ComposeBackupContainers: 'Respaldar contenedores del compose'
|
||||
ComposeBackupMeta: 'Escribir metadatos del respaldo de compose'
|
||||
ComposeBackupStart: 'Iniciar compose después del respaldo'
|
||||
ComposeBackupCleanup: 'Limpiar espacio de respaldo del compose'
|
||||
ComposeRecoverPrepare: 'Preparar espacio de restauración del compose'
|
||||
ComposeRecoverExtract: 'Extraer archivo de respaldo de compose'
|
||||
ComposeRecoverMeta: 'Cargar metadatos del respaldo de compose'
|
||||
ComposeRecoverMetaLogName: 'Metadatos de restauracion de compose: nombre={{ .name }}'
|
||||
ComposeRecoverMetaLogPath: 'Metadatos de restauracion de compose: rutaRespaldo={{ .backupPath }} rutaDestino={{ .targetDir }}'
|
||||
ComposeRecoverMetaLogCount: 'Metadatos de restauracion de compose: archivos={{ .files }} contenedores={{ .containers }}'
|
||||
ComposeRecoverFiles: 'Restaurar archivos de compose'
|
||||
ComposeRecoverContainers: 'Restaurar contenedores del compose'
|
||||
ComposeRecoverRecord: 'Guardar registro de compose'
|
||||
ComposeRecoverCleanup: 'Limpiar espacio de restauracion de compose'
|
||||
ErrFileNotExist: 'El archivo {{ .detail }} no existe, verifique la integridad'
|
||||
ErrImageBuildErr: 'Fallo al construir la imagen'
|
||||
ErrImageExist: "¡La imagen ya existe! Modifique el nombre de la imagen."
|
||||
ErrImageExist: 'La imagen ya existe Modifique el nombre de la imagen.'
|
||||
ErrDelWithWebsite: 'El entorno de ejecución ya está asociado a un sitio web y no puede eliminarse'
|
||||
ErrRuntimeStart: 'Fallo al iniciar'
|
||||
ErrPackageJsonNotFound: 'El archivo package.json no existe'
|
||||
ErrScriptsNotFound: 'No se encontró la configuración de scripts en package.json'
|
||||
ErrContainerNameNotFound: 'No se puede obtener el nombre del contenedor, revise el archivo .env'
|
||||
ErrContainerNameNotFound: 'No se puede obtener el nombre del contenedor, revise el archivo.env'
|
||||
ErrNodeModulesNotFound: 'La carpeta node_modules no existe, edite o espere a que inicie'
|
||||
ErrContainerNameIsNull: 'El nombre del contenedor no existe'
|
||||
ErrPHPPortIsDefault: "El puerto 9000 es el predeterminado, modifique e intente de nuevo"
|
||||
ErrPHPRuntimePortFailed: "El puerto {{ .name }} ya está en uso por el entorno actual, modifique e intente"
|
||||
|
||||
#tool
|
||||
ErrPHPPortIsDefault: 'El puerto 9000 es el predeterminado, modifique e intente de nuevo'
|
||||
ErrPHPRuntimePortFailed: 'El puerto {{ .name }} ya está en uso por el entorno actual, modifique e intente'
|
||||
ErrConfigNotFound: 'El archivo de configuración no existe'
|
||||
ErrConfigParse: 'El formato del archivo de configuración es incorrecto'
|
||||
ErrConfigIsNull: 'El archivo de configuración no puede estar vacío'
|
||||
ErrConfigDirNotFound: 'El directorio de ejecución no existe'
|
||||
ErrConfigAlreadyExist: 'Ya existe un archivo de configuración con ese nombre'
|
||||
ErrUserFindErr: 'Búsqueda del usuario {{ .name }} fallida {{ .err }}'
|
||||
|
||||
#cronjob
|
||||
CutWebsiteLogSuccess: 'Log del sitio web {{ .name }} cortado con éxito, ruta de respaldo {{ .path }}'
|
||||
HandleShell: 'Ejecutar script {{ .name }}'
|
||||
HandleCurl: "URL {{ .name }} にアクセス"
|
||||
HandleCurl: 'URL {{ .name }} にアクセス'
|
||||
HandleNtpSync: 'Sincronizar hora del sistema'
|
||||
HandleSystemClean: 'Limpiar caché del sistema'
|
||||
SystemLog: 'Log del sistema'
|
||||
CutWebsiteLog: 'Rotar log del sitio web'
|
||||
FileOrDir: 'Directorio / Archivo'
|
||||
UploadFile: 'Subiendo archivo de respaldo {{ .file }} a {{ .backup }}'
|
||||
Upload: 'Subir'
|
||||
IgnoreBackupErr: 'Fallo en el respaldo, error: {{ .detail }}, se ignora este error...'
|
||||
IgnoreUploadErr: 'Fallo en la subida, error: {{ .detail }}, se ignora este error...'
|
||||
Upload: "Subir"
|
||||
LoadBackupFailed: "Error al obtener la conexión de la cuenta de respaldo, error: {{ .detail }}"
|
||||
InExecuting: "La tarea actual se está ejecutando, por favor no la repita"
|
||||
NoSuchResource: "No se encontraron contenidos de respaldo en la base de datos, omitiendo..."
|
||||
CleanLog: "Limpiar registro"
|
||||
CleanLogByName: "Limpiar registro de {{.name}}"
|
||||
WafIpGroupNotFound: "Grupo de IP de WAF no encontrado. Por favor, vaya a [Funciones Avanzadas - WAF - Lista Blanca/Negra - Grupo de IP] para agregar un grupo de IP mediante el método de descarga remota."
|
||||
|
||||
#toolbox
|
||||
LoadBackupFailed: 'Error al obtener la conexión de la cuenta de respaldo, error: {{ .detail }}'
|
||||
InExecuting: 'La tarea actual se está ejecutando no la repita'
|
||||
NoSuchResource: 'No se encontraron contenidos de respaldo en la base de datos, omitiendo...'
|
||||
CleanLog: 'Limpiar registro'
|
||||
CleanLogByName: 'Limpiar registro de {{ .name }}'
|
||||
WafIpGroupNotFound: 'Grupo de IP de WAF no encontrado., vaya a [Funciones Avanzadas - WAF - Lista Blanca/Negra - Grupo de IP] para agregar un grupo de IP mediante el método de descarga remota.'
|
||||
ErrNotExistUser: 'El usuario actual no existe, modifíquelo e intente de nuevo'
|
||||
ErrBanAction: 'Fallo al configurar. El servicio {{ .name }} no está disponible, revise e intente de nuevo'
|
||||
ErrClamdscanNotFound: 'El comando clamdscan no fue detectado, siga la documentación para instalarlo'
|
||||
TaskSwapSet: "Configurar Swap"
|
||||
SetSwap: "Configurar Swap {{ .path }} - {{ .size }}"
|
||||
CreateSwap: "Crear Archivo Swap"
|
||||
FormatSwap: "Formatear Archivo Swap"
|
||||
EnableSwap: "Habilitar Swap"
|
||||
|
||||
#tamper
|
||||
CleanTamperSetting: "Limpiar configuraciones de protección históricas"
|
||||
SetTamperWithRules: "Iniciar protección de directorio según reglas"
|
||||
ProtectDir: "Proteger directorio {{ .name }}"
|
||||
IgnoreFile: "Proteger archivo {{ .name }}"
|
||||
TamperSetSuccessful: "Configuración de protección completada, comenzando monitoreo de los siguientes directorios:"
|
||||
|
||||
#waf
|
||||
TaskSwapSet: 'Configurar Swap'
|
||||
SetSwap: 'Configurar Swap {{ .path }} - {{ .size }}'
|
||||
CreateSwap: 'Crear Archivo Swap'
|
||||
FormatSwap: 'Formatear Archivo Swap'
|
||||
EnableSwap: 'Habilitar Swap'
|
||||
CleanTamperSetting: 'Limpiar configuraciones de protección históricas'
|
||||
SetTamperWithRules: 'Iniciar protección de directorio según reglas'
|
||||
ProtectDir: 'Proteger directorio {{ .name }}'
|
||||
IgnoreFile: 'Proteger archivo {{ .name }}'
|
||||
TamperSetSuccessful: 'Configuración de protección completada, comenzando monitoreo de los siguientes directorios:'
|
||||
ErrScope: 'No se admite la modificación de esta configuración'
|
||||
ErrStateChange: 'Fallo al cambiar de estado'
|
||||
ErrRuleExist: 'La regla ya existe'
|
||||
@@ -298,7 +300,7 @@ ErrRuleNotExist: 'La regla no existe'
|
||||
ErrParseIP: 'Formato de IP incorrecto'
|
||||
ErrDefaultIP: 'default es un nombre reservado, cámbielo'
|
||||
ErrGroupInUse: 'El grupo IP está en uso por listas negras/blancas y no se puede eliminar'
|
||||
ErrIPGroupAclUse: "El grupo IP está en uso por reglas personalizadas del sitio web {{ .name }}, no puede eliminarse"
|
||||
ErrIPGroupAclUse: 'El grupo IP está en uso por reglas personalizadas del sitio web {{ .name }}, no puede eliminarse'
|
||||
ErrGroupExist: 'El nombre del grupo IP ya existe'
|
||||
ErrIPRange: 'Rango de IP incorrecto'
|
||||
ErrIPExist: 'La IP ya existe'
|
||||
@@ -309,10 +311,10 @@ xss: 'XSS'
|
||||
phpExec: 'Ejecución de scripts PHP'
|
||||
oneWordTrojan: 'Troyano de una sola palabra'
|
||||
appFilter: 'Filtrar directorios peligrosos'
|
||||
webshell: 'Webshell'
|
||||
webShell: 'Webshell'
|
||||
args: 'Reglas de parámetros'
|
||||
protocolFilter: 'Filtrado de protocolos'
|
||||
javaFileter: 'Filtrado de archivos Java peligrosos'
|
||||
javaFilter: 'Filtrado de archivos Java peligrosos'
|
||||
scannerFilter: 'Filtrado de escáneres'
|
||||
escapeFilter: 'Filtro de escape'
|
||||
customRule: 'Regla personalizada'
|
||||
@@ -342,10 +344,9 @@ ErrDBNotExist: 'La base de datos no existe'
|
||||
allow: 'permitir'
|
||||
deny: 'denegar'
|
||||
OpenrestyNotFound: 'Openresty no está instalado'
|
||||
remoteIpIsNull: "La lista de IP está vacía"
|
||||
OpenrestyVersionErr: "La versión de Openresty es demasiado baja, actualice a 1.27.1.2-2-2-focal"
|
||||
|
||||
#task
|
||||
remoteIpIsNull: 'La lista de IP está vacía'
|
||||
OpenrestyVersionErr: 'La versión de Openresty es demasiado baja, actualice a 1.27.1.2-2-2-focal'
|
||||
ErrFileTooLarge: 'El archivo supera 1 MB y no se puede cargar'
|
||||
TaskStart: 'La tarea {{ .name }} inicia [INICIO]'
|
||||
TaskEnd: 'La tarea {{ .name }} completada [COMPLETADA]'
|
||||
TaskFailed: 'La tarea {{ .name }} falló'
|
||||
@@ -357,11 +358,11 @@ SubTaskFailed: '{{ .name }} falló: {{ .err }}'
|
||||
TaskInstall: 'Instalar'
|
||||
TaskUninstall: 'Desinstalar'
|
||||
TaskCreate: 'Crear'
|
||||
TaskProtect: "Proteger"
|
||||
TaskDelete: 'Eliminar'
|
||||
TaskUpgrade: 'Actualizar'
|
||||
TaskUpdate: 'Actualizar'
|
||||
TaskRestart: 'Reiniciar'
|
||||
TaskProtect: 'Proteger'
|
||||
TaskBackup: 'Respaldar'
|
||||
TaskRecover: 'Recuperar'
|
||||
TaskRollback: 'Revertir'
|
||||
@@ -369,9 +370,10 @@ TaskPull: 'Descargar'
|
||||
TaskCommit: 'Commit'
|
||||
TaskBuild: 'Construir'
|
||||
TaskPush: 'Subir'
|
||||
TaskClean: 'Limpieza'
|
||||
TaskHandle: 'Ejecutar'
|
||||
TaskImport: "Importar"
|
||||
TaskExport: "Exportar"
|
||||
TaskImport: 'Importar'
|
||||
TaskExport: 'Exportar'
|
||||
Website: 'Sitio web'
|
||||
App: 'Aplicación'
|
||||
Runtime: 'Entorno de ejecución'
|
||||
@@ -402,20 +404,13 @@ SubTask: 'Subtarea'
|
||||
RuntimeExtension: 'Extensión del entorno de ejecución'
|
||||
TaskIsExecuting: 'Tarea en ejecución'
|
||||
CustomAppstore: 'Almacén de aplicaciones personalizado'
|
||||
TaskClean: "Limpieza"
|
||||
TaskExec: "Ejecutar"
|
||||
TaskBatch: "Operación por Lotes"
|
||||
TaskExec: 'Ejecutar'
|
||||
TaskBatch: 'Operación por Lotes'
|
||||
FileConvert: 'Conversión de Formato de Archivo'
|
||||
|
||||
# task - ai
|
||||
Clamscan: 'Escanear {{ .name }}'
|
||||
TaskScan: 'Escaneo'
|
||||
OllamaModelPull: 'Descargar modelo Ollama {{ .name }}'
|
||||
OllamaModelSize: 'Obtener tamaño del modelo Ollama {{ .name }}'
|
||||
|
||||
# task - clam
|
||||
Clamscan: "Escanear {{ .name }}"
|
||||
TaskScan: "Escaneo"
|
||||
|
||||
# task-snapshot
|
||||
Snapshot: 'Instantánea'
|
||||
SnapDBInfo: 'Escribir información de base de datos de 1Panel'
|
||||
SnapCopy: 'Copiar archivos y directorios {{ .name }}'
|
||||
@@ -440,7 +435,7 @@ SnapCheckCompress: 'Verificar archivo de compresión de instantánea'
|
||||
SnapCompressSize: 'Tamaño del archivo de instantánea {{ .name }}'
|
||||
SnapUpload: 'Subir archivo de instantánea'
|
||||
SnapUploadTo: 'Subir archivo de instantánea a {{ .name }}'
|
||||
|
||||
SnapUploadRes: 'Subir archivo de instantánea a {{ .name }}'
|
||||
SnapshotRecover: 'Restaurar instantánea'
|
||||
RecoverDownload: 'Descargar archivo de instantánea'
|
||||
Download: 'Descargar'
|
||||
@@ -448,10 +443,10 @@ RecoverDownloadAccount: 'Obtener cuenta de respaldo para descargar instantánea
|
||||
RecoverDecompress: 'Descomprimir archivos de instantánea'
|
||||
Decompress: 'Descompresión'
|
||||
BackupBeforeRecover: 'Respaldar datos del sistema antes de restaurar instantánea'
|
||||
Readjson: 'Leer archivo Json de la instantánea'
|
||||
ReadjsonPath: 'Obtener ruta del archivo Json en la instantánea'
|
||||
ReadjsonContent: 'Leer archivo Json'
|
||||
ReadjsonMarshal: 'Procesar escape de Json'
|
||||
ReadJson: 'Leer archivo Json de la instantánea'
|
||||
ReadJsonPath: 'Obtener ruta del archivo Json en la instantánea'
|
||||
ReadJsonContent: 'Leer archivo Json'
|
||||
ReadJsonMarshal: 'Procesar escape de Json'
|
||||
RecoverApp: 'Restaurar aplicaciones instaladas'
|
||||
RecoverWebsite: 'Recuperar directorio del sitio web'
|
||||
RecoverAppImage: 'Restaurar imagen de la instantánea'
|
||||
@@ -465,9 +460,6 @@ RecoverDaemonJson: 'Restaurar archivo daemon.json de configuración de contenedo
|
||||
RecoverDBData: 'Restaurar datos de base de datos'
|
||||
RecoverBackups: 'Restaurar directorio local de respaldo'
|
||||
RecoverPanelData: 'Recuperar directorio de datos'
|
||||
|
||||
# task - container
|
||||
ContainerNewCliet: 'Inicializar cliente Docker'
|
||||
ContainerImagePull: 'Descargar imagen de contenedor {{ .name }}'
|
||||
ContainerRemoveOld: 'Eliminar el contenedor original {{ .name }}'
|
||||
ContainerImageCheck: 'Comprobar si la imagen se descargó correctamente'
|
||||
@@ -476,22 +468,13 @@ ContainerRecreate: 'Actualización del contenedor fallida, restaurando el conten
|
||||
ContainerCreate: 'Crear nuevo contenedor {{ .name }}'
|
||||
ContainerCreateFailed: 'Falló la creación del contenedor, eliminando el contenedor fallido'
|
||||
ContainerStartCheck: 'Verificar si el contenedor ha iniciado'
|
||||
|
||||
# task - image
|
||||
ImageBuild: 'Construcción de imagen'
|
||||
ImageBuildStdoutCheck: 'Analizar salida de la imagen'
|
||||
ImageBuildRes: 'Salida de construcción de imagen: {{ .name }}'
|
||||
ImagePull: 'Descargar imagen'
|
||||
ImageRepoAuthFromDB: 'Obtener información de autenticación del repositorio desde la base de datos'
|
||||
ImagePush: 'Subir imagen'
|
||||
ImageRenameTag: 'Modificar etiqueta de imagen'
|
||||
ImageNewTag: 'Nueva etiqueta de imagen {{ .name }}'
|
||||
ComposeCreate: 'Crear orquestación'
|
||||
ComposeCreateRes: 'Salida de creación de orquestación: {{ .name }}'
|
||||
ImageRepoAuthFromDB: "Obtener información de autenticación del repositorio desde la base de datos"
|
||||
ImaegPullRes: "Salida de descarga de imagen: {{ .name }}"
|
||||
ImaegPushRes: "Salida de subida de imagen: {{ .name }}"
|
||||
|
||||
# task - website
|
||||
BackupNginxConfig: 'Respaldar archivo de configuración OpenResty'
|
||||
CompressFileSuccess: 'Directorio comprimido con éxito, guardado en {{ .name }}'
|
||||
CompressDir: 'Directorio comprimido'
|
||||
@@ -502,40 +485,33 @@ websiteDir: 'Directorio del sitio web'
|
||||
RecoverFailedStartRollBack: 'Restauración fallida, iniciando reversión'
|
||||
AppBackupFileIncomplete: 'El archivo de respaldo está incompleto, falta app.json o app.tar.gz'
|
||||
AppAttributesNotMatch: 'El tipo o nombre de la aplicación no coincide'
|
||||
ErrAlertSync: 'Error en la sincronización de la alerta, revise e intente de nuevo'
|
||||
ErrInstallExtension: 'Ya hay una tarea de instalación en curso, espere a que termine'
|
||||
PanelAlertTitle: 'Notificación de alerta del panel'
|
||||
TestAlertTitle: 'Correo de prueba - Verificar conectividad de correo'
|
||||
TestAlert: 'Este es un correo de prueba para verificar que la configuración de envío de correos es correcta.'
|
||||
LicenseExpirationAlert: 'Su Panel {{ .node }}{{ .ip }}, la licencia expirará en {{ .day }} días. Inicie sesión en el panel para ver los detalles.'
|
||||
CronJobFailedAlert: 'Su Panel {{ .node }}{{ .ip }}, la ejecución de la tarea programada {{ .name }} falló. Inicie sesión en el panel para ver los detalles.'
|
||||
ClamAlert: 'Su Panel {{ .node }}{{ .ip }}, la tarea de escaneo antivirus detectó {{ .num }} archivos infectados. Inicie sesión en el panel para ver los detalles.'
|
||||
WebSiteAlert: 'Su Panel {{ .node }}{{ .ip }}, {{ .num }} sitios web expirarán en {{ .day }} días. Inicie sesión en el panel para ver los detalles.'
|
||||
SSLAlert: 'Su Panel {{ .node }}{{ .ip }}, {{ .num }} certificados SSL expirarán en {{ .day }} días. Inicie sesión en el panel para ver los detalles.'
|
||||
DiskUsedAlert: 'Su Panel {{ .node }}{{ .ip }}, el uso del disco {{ .name }} ha alcanzado {{ .used }}. Inicie sesión en el panel para ver los detalles.'
|
||||
ResourceAlert: 'Su Panel {{ .node }}{{ .ip }}, la tasa de uso promedio de {{ .name }} en {{ .time }} minutos es {{ .used }}. Inicie sesión en el panel para ver los detalles.'
|
||||
PanelVersionAlert: 'Su Panel {{ .node }}{{ .ip }}, está disponible la última versión del panel para actualizar. Inicie sesión en el panel para ver los detalles.'
|
||||
PanelPwdExpirationAlert: 'Su Panel {{ .node }}{{ .ip }}, la contraseña del panel expirará en {{ .day }} días. Inicie sesión en el panel para ver los detalles.'
|
||||
CommonAlert: 'Su Panel {{ .node }}{{ .ip }}, {{ .msg }}. Inicie sesión en el panel para ver los detalles.'
|
||||
NodeExceptionAlert: 'Su Panel {{ .node }}{{ .ip }}, {{ .num }} nodos son anómalos. Inicie sesión en el panel para ver los detalles.'
|
||||
LicenseExceptionAlert: 'Su Panel {{ .node }}{{ .ip }}, {{ .num }} licencias son anómalas. Inicie sesión en el panel para ver los detalles.'
|
||||
SSHAndPanelLoginAlert: 'Su Panel {{ .node }}{{ .ip }}, el inicio de sesión {{ .name }} desde {{ .loginIp }} es anómalo. Inicie sesión en el panel para ver los detalles.'
|
||||
DeviceNotFound: 'Dispositivo {{ .name }} no encontrado'
|
||||
DeviceIsMounted: 'El dispositivo {{ .name }} está montado, desmóntelo primero'
|
||||
PartitionDiskErr: 'Error al particionar, {{ .err }}'
|
||||
FormatDiskErr: 'Error al formatear disco, {{ .err }}'
|
||||
MountDiskErr: 'Error al montar disco, {{ .err }}'
|
||||
XfsNotFound: 'No se detectó el sistema de archivos xfs instale xfsprogs primero'
|
||||
|
||||
#alert
|
||||
ErrAlert: 'El formato del mensaje de alerta es incorrecto, ¡revise e intente de nuevo!'
|
||||
ErrAlertPush: 'Error al enviar la alerta, ¡revise e intente de nuevo!'
|
||||
ErrAlertSave: 'Error al guardar la alerta, ¡revise e intente de nuevo!'
|
||||
ErrAlertSync: 'Error en la sincronización de la alerta, ¡revise e intente de nuevo!'
|
||||
ErrAlertRemote: 'Error remoto de alerta, ¡revise e intente de nuevo!'
|
||||
|
||||
#task - runtime
|
||||
ErrInstallExtension: "Ya hay una tarea de instalación en curso, espere a que termine"
|
||||
|
||||
# alert mail template
|
||||
PanelAlertTitle: "Notificación de alerta del panel"
|
||||
TestAlertTitle: "Correo de prueba - Verificar conectividad de correo"
|
||||
TestAlert: "Este es un correo de prueba para verificar que la configuración de envío de correos es correcta."
|
||||
LicenseExpirationAlert: "Su Panel {{ .node }}{{ .ip }}, la licencia expirará en {{ .day }} días. Inicie sesión en el panel para ver los detalles."
|
||||
CronJobFailedAlert: "Su Panel {{ .node }}{{ .ip }}, la ejecución de la tarea programada {{ .name }} falló. Inicie sesión en el panel para ver los detalles."
|
||||
ClamAlert: "Su Panel {{ .node }}{{ .ip }}, la tarea de escaneo antivirus detectó {{ .num }} archivos infectados. Inicie sesión en el panel para ver los detalles."
|
||||
WebSiteAlert: "Su Panel {{ .node }}{{ .ip }}, {{ .num }} sitios web expirarán en {{ .day }} días. Inicie sesión en el panel para ver los detalles."
|
||||
SSLAlert: "Su Panel {{ .node }}{{ .ip }}, {{ .num }} certificados SSL expirarán en {{ .day }} días. Inicie sesión en el panel para ver los detalles."
|
||||
DiskUsedAlert: "Su Panel {{ .node }}{{ .ip }}, el uso del disco {{ .name }} ha alcanzado {{ .used }}. Inicie sesión en el panel para ver los detalles."
|
||||
ResourceAlert: "Su Panel {{ .node }}{{ .ip }}, la tasa de uso promedio de {{ .name }} en {{ .time }} minutos es {{ .used }}. Inicie sesión en el panel para ver los detalles."
|
||||
PanelVersionAlert: "Su Panel {{ .node }}{{ .ip }}, está disponible la última versión del panel para actualizar. Inicie sesión en el panel para ver los detalles."
|
||||
PanelPwdExpirationAlert: "Su Panel {{ .node }}{{ .ip }}, la contraseña del panel expirará en {{ .day }} días. Inicie sesión en el panel para ver los detalles."
|
||||
CommonAlert: "Su Panel {{ .node }}{{ .ip }}, {{ .msg }}. Inicie sesión en el panel para ver los detalles."
|
||||
NodeExceptionAlert: "Su Panel {{ .node }}{{ .ip }}, {{ .num }} nodos son anómalos. Inicie sesión en el panel para ver los detalles."
|
||||
LicenseExceptionAlert: "Su Panel {{ .node }}{{ .ip }}, {{ .num }} licencias son anómalas. Inicie sesión en el panel para ver los detalles."
|
||||
SSHAndPanelLoginAlert: "Su Panel {{ .node }}{{ .ip }}, el inicio de sesión {{ .name }} desde {{ .loginIp }} es anómalo. Inicie sesión en el panel para ver los detalles."
|
||||
|
||||
#disk
|
||||
DeviceNotFound: "Dispositivo {{ .name }} no encontrado"
|
||||
DeviceIsMounted: "El dispositivo {{ .name }} está montado, desmóntelo primero"
|
||||
PartitionDiskErr: "Error al particionar, {{ .err }}"
|
||||
FormatDiskErr: "Error al formatear disco, {{ .err }}"
|
||||
MountDiskErr: "Error al montar disco, {{ .err }}"
|
||||
UnMountDiskErr: "Error al desmontar disco, {{ .err }}"
|
||||
XfsNotFound: "No se detectó el sistema de archivos xfs, por favor instale xfsprogs primero"
|
||||
# terminal
|
||||
TerminalAIBlockedRiskyCommand: 'Comando de riesgo bloqueado: {{ .command }}'
|
||||
TerminalAIThinking: 'La IA está pensando...'
|
||||
TerminalAIReadyToExecute: 'Pensamiento completado, pulsa Enter para ejecutar (duración: {{ .duration }}, token: {{ .tokens }})'
|
||||
TerminalAIRequestFailed: 'La solicitud de AI ha fallado: {{ .err }}'
|
||||
|
||||
+201
-226
@@ -1,30 +1,30 @@
|
||||
ErrInvalidParams: 'リクエストパラメータエラー: {{ .detail }}'
|
||||
ErrTokenParse: 'トークン生成エラー: {{ .detail }}'
|
||||
ErrInitialPassword: '元のパスワードが正しくありません'
|
||||
ErrInternalServer: '内部サーバーエラー: {{ .detail }}'
|
||||
ErrRecordExist: 'レコードがすでに存在します'
|
||||
ErrInvalidParams: '不正なリクエスト: {{ .detail }}'
|
||||
ErrTokenParse: 'トークンエラー: {{ .detail }}'
|
||||
ErrInitialPassword: '元のパスワードが違います'
|
||||
ErrInternalServer: '内部エラー: {{ .detail }}'
|
||||
ErrWgetRemoteFailed: 'リモートのダウンロード URL が無効か、到達できません:{{ .detail }}'
|
||||
ErrWgetInvalidContentType: 'リモート URL がファイルではなくエラーページ等を返しました、{{ .detail }}'
|
||||
ErrRecordExist: 'レコードが既に存在します'
|
||||
ErrRecordNotFound: 'レコードが見つかりません'
|
||||
ErrStructTransform: '型変換に失敗しました: {{ .err }}'
|
||||
ErrNotLogin: 'ユーザーがログインしていません: {{ .detail }}'
|
||||
ErrPasswordExpired: '現在のパスワードの有効期限が切れています: {{ .detail }}'
|
||||
ErrNotSupportType: 'システムは現在のタイプ: {{ .name }} をサポートしていません'
|
||||
ErrProxy: 'リクエスト エラーです。ノードのステータスを確認してください: {{ .detail }}'
|
||||
ErrApiConfigStatusInvalid: 'API インターフェースへのアクセスは禁止されています: {{ .detail }}'
|
||||
ErrApiConfigKeyInvalid: 'API インターフェース キー エラー: {{ .detail }}'
|
||||
ErrApiConfigIPInvalid: 'API インターフェースの呼び出しに使用された IP がホワイトリストにありません: {{ .detail }}'
|
||||
ErrApiConfigDisable: 'このインターフェースは、API インターフェース呼び出しの使用を禁止しています: {{ .detail }}'
|
||||
ErrApiConfigKeyTimeInvalid: 'API インターフェースのタイムスタンプ エラー: {{ .detail }}'
|
||||
|
||||
ErrMinQuickJump: "少なくとも1つのクイックジャンプエントリを設定してください!"
|
||||
ErrMaxQuickJump: "最大4つのクイックジャンプエントリを設定できます!"
|
||||
|
||||
#common
|
||||
ErrUsernameIsExist: 'ユーザー名は既に存在します'
|
||||
ErrNameIsExist: '名前は既に存在します'
|
||||
ErrDemoEnvironment: 'デモ サーバー、この操作は禁止されています!'
|
||||
ErrCmdTimeout: 'コマンドの実行がタイムアウトしました!'
|
||||
ErrCmdIllegal: '実行コマンドに不正な文字が含まれています。修正してもう一度お試しください。'
|
||||
ErrPortExist: '{{ .port }} ポートは、{{ .type }} [{{ .name }}] によって既に使用されています'
|
||||
ErrStructTransform: '型変換エラー: {{ .err }}'
|
||||
ErrNotLogin: 'ログインしていません: {{ .detail }}'
|
||||
ErrPasswordExpired: 'パスワード期限切れ: {{ .detail }}'
|
||||
ErrNotSupportType: '未対応のタイプ: {{ .name }}'
|
||||
ErrProxy: 'リクエストに失敗しました: {{ .detail }}'
|
||||
ErrApiConfigStatusInvalid: 'API利用不可: {{ .detail }}'
|
||||
ErrApiConfigKeyInvalid: 'APIキーが無効です: {{ .detail }}'
|
||||
ErrApiConfigIPInvalid: 'IPが許可されていません: {{ .detail }}'
|
||||
ErrApiConfigDisable: 'APIは無効です: {{ .detail }}'
|
||||
ErrApiConfigKeyTimeInvalid: 'タイムスタンプが無効です: {{ .detail }}'
|
||||
ErrShutDown: 'コマンドは手動で停止されました'
|
||||
ErrMinQuickJump: 'クイックアクセスを1件以上追加'
|
||||
ErrMaxQuickJump: '最大4件のクイックアクセス'
|
||||
ErrUsernameIsExist: 'ユーザー名はすでに使われています'
|
||||
ErrNameIsExist: '名称はすでに使われています'
|
||||
ErrDemoEnvironment: 'デモ環境では利用できません'
|
||||
ErrCmdTimeout: 'コマンドがタイムアウトしました'
|
||||
ErrCmdIllegal: 'コマンドに無効な文字が含まれています'
|
||||
ErrPortExist: 'ポート {{ .port }} は {{ .type }} [{{ .name }}] が使用中です'
|
||||
TYPE_APP: 'アプリケーション'
|
||||
TYPE_RUNTIME: 'ランタイム環境'
|
||||
TYPE_DOMAIN: 'ドメイン名'
|
||||
@@ -35,45 +35,43 @@ Failed: '失敗'
|
||||
SystemRestart: 'システムの再起動によりタスクが中断されました'
|
||||
ErrGroupIsDefault: 'デフォルト グループ、削除できません'
|
||||
ErrGroupIsInWebsiteUse: 'グループは別の Web サイトで使用されているため、削除できません。'
|
||||
Decrypt: "復号化"
|
||||
|
||||
#agent
|
||||
ErrAgentAccountBound: 'このアカウントはエージェントに紐付いているため削除できません。確認して再試行してください!'
|
||||
ErrAgentAccountUnavailable: 'アカウント接続情報を利用できません。エラー: {{ .err }}。確認して再試行してください!'
|
||||
ErrAgentProviderNotSupported: 'このエージェント提供元はサポートされていません。確認して再試行してください!'
|
||||
ErrAgentAccountRequired: 'エージェントアカウントを選択して再試行してください。'
|
||||
ErrAgentAccountNotVerified: 'アカウントの検証が完了していません。確認して再試行してください!'
|
||||
ErrAgentProviderMismatch: 'アカウントの提供元が一致しません。確認して再試行してください!'
|
||||
ErrAgentBaseURLRequired: 'Base URL を空にできません。確認して再試行してください!'
|
||||
ErrAgentApiKeyRequired: 'API Key を空にできません。確認して再試行してください!'
|
||||
ErrAgentComposeRequired: 'カスタム compose 内容を空にできません。確認して再試行してください!'
|
||||
ErrAgentIDRequired: 'エージェント ID を空にできません。確認して再試行してください!'
|
||||
ErrAgentAccountIDRequired: 'アカウント ID を空にできません。確認して再試行してください!'
|
||||
|
||||
#backup
|
||||
Decrypt: '復号化'
|
||||
ErrAgentAccountBound: 'アカウントはエージェントに紐づいています'
|
||||
ErrAgentAccountUnavailable: 'アカウント接続不可: {{ .err }}'
|
||||
ErrAgentProviderNotSupported: 'エージェントプロバイダ非対応'
|
||||
ErrAgentAccountRequired: 'まずエージェントアカウントを選択'
|
||||
ErrAgentAccountNotVerified: 'アカウント未検証です'
|
||||
ErrAgentAccountModelsRequired: '少なくとも 1 つのモデルを設定してください'
|
||||
ErrAgentAccountSingleInitialModel: 'アカウント作成時に設定できる初期モデルは 1 つだけです'
|
||||
ErrAgentModelNotInAccount: '選択したモデルはこのアカウントに設定されていません'
|
||||
ErrAgentModelInUse: 'このモデルはまだエージェントで使用中です: {{ .name }}'
|
||||
ErrAgentBaseURLRequired: 'ベースURLが必要です'
|
||||
ErrAgentApiKeyRequired: 'APIキーが必要です'
|
||||
ErrAgentComposeRequired: 'Composeが必要です'
|
||||
ErrAgentIDRequired: 'エージェントIDが必要です'
|
||||
ErrAgentAccountIDRequired: 'アカウントIDが必要です'
|
||||
ErrAgentLimitReached: 'Community Edition では AI エージェントを最大 {{ .max }} 件まで作成できます。Professional Edition にアップグレードすると数の制限はありません'
|
||||
Localhost: 'ローカルマシン'
|
||||
ErrBackupInUsed: 'バックアップ アカウントはスケジュールされたタスクで使用されているため、削除できません。'
|
||||
ErrBackupCheck: 'バックアップ アカウントのテスト接続に失敗しました {{ .err }}'
|
||||
ErrBackupLocalDelete: 'ローカル サーバーのバックアップ アカウントの削除はまだサポートされていません'
|
||||
ErrBackupLocalCreate: 'ローカル サーバーのバックアップ アカウントの作成はまだサポートされていません'
|
||||
|
||||
#app
|
||||
ErrPortInUsed: '{{ .detail }} ポートはすでに使用されています!'
|
||||
ErrBackupInUsed: 'バックアップアカウントがスケジュールで使用中'
|
||||
ErrBackupCheck: '接続テストに失敗しました: {{ .err }}'
|
||||
ErrBackupLocalDelete: 'ローカルバックアップは削除できません'
|
||||
ErrBackupLocalCreate: 'ローカルバックアップは作成できません'
|
||||
ErrPortInUsed: '{{ .detail }} ポートはすでに使用されています'
|
||||
ErrAppLimit: 'インストールされているアプリケーションの数が制限を超えました'
|
||||
ErrNotInstall: 'アプリケーションがインストールされていません'
|
||||
ErrPortInOtherApp: '{{ .port }} ポートは既にアプリケーション {{ .apps }} によって使用されています!'
|
||||
ErrDbUserNotValid: '既存のデータベース、ユーザー名、およびパスワードが一致しません!'
|
||||
ErrUpdateBuWebsite: 'アプリケーションは正常に更新されましたが、Web サイト構成ファイルの変更に失敗しました。設定を確認してください! '
|
||||
ErrPortInOtherApp: '{{ .port }} ポートは既にアプリケーション {{ .apps }} によって使用されています'
|
||||
ErrDbUserNotValid: '既存のデータベース、ユーザー名、およびパスワードが一致しません'
|
||||
ErrUpdateBuWebsite: 'アプリケーションは正常に更新されましたが、Web サイト構成ファイルの変更に失敗しました。設定を確認してください'
|
||||
Err1PanelNetworkFailed: 'デフォルトのコンテナ ネットワークの作成に失敗しました。 {{ .detail }}'
|
||||
ErrFileParse: 'アプリケーションの docker-compose ファイルの解析に失敗しました!'
|
||||
ErrFileParse: 'アプリケーションの docker-compose ファイルの解析に失敗しました'
|
||||
ErrInstallDirNotFound: 'インストール ディレクトリが存在しません。アンインストールする必要がある場合は、強制アンインストールを選択してください。'
|
||||
AppStoreIsUpToDate: 'アプリストアはすでに最新バージョンです'
|
||||
LocalAppVersionNull: '{{ .name }} アプリケーションはバージョンに同期されていません。アプリリストに追加できません'
|
||||
LocalAppVersionErr: '{{ .name }} バージョン {{ .version }} の同期に失敗しました! {{ .err }}'
|
||||
LocalAppVersionErr: '{{ .name }} バージョン {{ .version }} の同期に失敗しました {{ .err }}'
|
||||
ErrFileNotFound: '{{ .name }} ファイルが存在しません'
|
||||
ErrFileParseApp: '{{ .name }} ファイルの解析に失敗しました {{ .err }}'
|
||||
ErrAppDirNull: 'バージョン フォルダーが存在しません'
|
||||
LocalAppErr: 'アプリケーション {{ .name }} の同期に失敗しました! {{ .err }}'
|
||||
LocalAppErr: 'アプリケーション {{ .name }} の同期に失敗しました {{ .err }}'
|
||||
ErrContainerName: 'コンテナ名が既に存在します'
|
||||
ErrCreateHttpClient: 'リクエスト {{ .err }} の作成に失敗しました'
|
||||
ErrHttpReqTimeOut: 'リクエストがタイムアウトしました {{ .err }}'
|
||||
@@ -86,64 +84,58 @@ ErrAppBackup: '{{ .name }} アプリケーションのバックアップに失
|
||||
ErrVersionTooLow: '現在の 1Panel のバージョンが低すぎるため、App Store を更新できません。操作する前にバージョンをアップグレードしてください。'
|
||||
ErrAppNameExist: 'アプリケーション名がすでに存在します'
|
||||
AppStoreIsSyncing: 'App Store が同期中です。しばらくしてからもう一度お試しください'
|
||||
ErrGetCompose: 'docker-compose.yml ファイルの取得に失敗しました! {{ .detail }}'
|
||||
ErrGetCompose: 'docker-compose.yml ファイルの取得に失敗しました {{ .detail }}'
|
||||
ErrAppWarn: '異常な状態です。ログを確認してください'
|
||||
ErrAppParamKey: 'パラメータ {{ .name }} フィールドが異常です'
|
||||
ErrAppUpgrade: 'アプリケーション {{ .name }} のアップグレードに失敗しました {{ .err }}'
|
||||
AppRecover: 'アプリケーション {{ .name }} をロールバックします'
|
||||
PullImageStart: "イメージ [{{ .name }}] のプルを開始します"
|
||||
PullImageStart: 'イメージ [{{ .name }}] のプルを開始します'
|
||||
PullImageSuccess: 'イメージのプルが成功しました'
|
||||
AppStoreIsLastVersion: 'App Store はすでに最新バージョンです'
|
||||
AppStoreSyncSuccess: 'App Store の同期が成功しました'
|
||||
SyncAppDetail: 'アプリケーション構成を同期する'
|
||||
AppVersionNotMatch: '{{ .name }} アプリケーションには、より高い 1Panel バージョンが必要なため、同期をスキップします'
|
||||
MoveSiteDir: "ウェブサイトディレクトリの変更を検出しました。OpenResty ウェブサイトディレクトリを {{ .name }} に移行する必要があります"
|
||||
MoveSiteDir: 'ウェブサイトディレクトリの変更を検出しました。OpenResty ウェブサイトディレクトリを {{ .name }} に移行する必要があります'
|
||||
MoveSiteDirSuccess: 'Web サイト ディレクトリの移行に成功しました'
|
||||
DeleteRuntimePHP: 'PHP ランタイムを削除する'
|
||||
CustomAppStoreFileValid: 'App Store パッケージは .tar.gz 形式である必要があります'
|
||||
CustomAppStoreFileValid: 'App Store パッケージは.tar.gz 形式である必要があります'
|
||||
PullImageTimeout: 'プル イメージのタイムアウトです。イメージのアクセラレーションを増やすか、別のイメージのアクセラレーションに変更してください'
|
||||
ErrAppIsDown: '{{ .name }} アプリケーションの状態が異常です。確認してください'
|
||||
ErrCustomApps: 'インストールされているアプリケーションがあります。まずアンインストールしてください'
|
||||
ErrCustomRuntimes: 'ランタイム環境がインストールされています。まずそれを削除してください'
|
||||
ErrAppVersionDeprecated: "{{ .name }} アプリケーションは現在の 1Panel バージョンと互換性がありません、スキップしました"
|
||||
ErrDockerFailed: "Docker の状態が異常です。サービス状態を確認してください"
|
||||
ErrDockerComposeCmdNotFound: "Docker Compose コマンドは存在しません。ホストマシンにこのコマンドを先にインストールしてください"
|
||||
UseExistImage: 'イメージのプルに失敗しました、既存のイメージを使用します'
|
||||
|
||||
#ssh
|
||||
ExportIP: "ログインIP"
|
||||
ExportArea: "地域"
|
||||
ExportPort: "ポート"
|
||||
ExportAuthMode: "ログイン方式"
|
||||
ExportUser: "ユーザー"
|
||||
ExportStatus: "ログイン状態"
|
||||
ExportDate: "時間"
|
||||
|
||||
#file
|
||||
ErrAppVersionDeprecated: '{{ .name }} アプリケーションは現在の 1Panel バージョンと互換性がありません、スキップしました'
|
||||
ErrDockerFailed: 'Docker の状態が異常です。サービス状態を確認してください'
|
||||
ErrDockerComposeCmdNotFound: 'Docker Compose コマンドは存在しません。ホストマシンにこのコマンドを先にインストールしてください'
|
||||
UseExistImage: 'イメージは既に存在します。既存のイメージを使用します'
|
||||
ErrDatabaseNotFound: 'データベース {{ .name }} は存在しません'
|
||||
ExportIP: 'ログインIP'
|
||||
ExportArea: '地域'
|
||||
ExportPort: 'ポート'
|
||||
ExportAuthMode: 'ログイン方式'
|
||||
ExportUser: 'ユーザー'
|
||||
ExportStatus: 'ログイン状態'
|
||||
ExportDate: '時間'
|
||||
ErrFileCanNotRead: 'このファイルはプレビューをサポートしていません'
|
||||
ErrFileToLarge: 'ファイルは 10M より大きいため開けません'
|
||||
ErrPathNotFound: 'ディレクトリが存在しません'
|
||||
ErrMovePathFailed: 'ターゲット パスに元のパスを含めることはできません!'
|
||||
ErrLinkPathNotFound: 'ターゲット パスが存在しません!'
|
||||
ErrFileIsExist: 'ファイルまたはフォルダーは既に存在します!'
|
||||
ErrMovePathFailed: 'ターゲット パスに元のパスを含めることはできません'
|
||||
ErrLinkPathNotFound: 'ターゲット パスが存在しません'
|
||||
ErrFileIsExist: 'ファイルまたはフォルダーは既に存在します'
|
||||
ErrFileUpload: '{{ .name }} はファイル {{ .detail }} のアップロードに失敗しました'
|
||||
ErrFileDownloadDir: 'ダウンロード フォルダーはサポートされていません'
|
||||
ErrCmdNotFound: '{{ .name}} コマンドが存在しません。まずこのコマンドをホストにインストールしてください'
|
||||
ErrCmdNotFound: '{{ .name }} コマンドが存在しません。まずこのコマンドをホストにインストールしてください'
|
||||
ErrSourcePathNotFound: 'ソース ディレクトリが存在しません'
|
||||
ErrFavoriteExist: 'このパスはすでにお気に入りに登録されています'
|
||||
ErrInvalidChar: '不正な文字は許可されません'
|
||||
ErrPathNotDelete: '選択されたディレクトリは削除できません'
|
||||
ErrLogFileToLarge: "ログファイルが500MBを超えており、開くことができません"
|
||||
FileDropFailed: "ファイル {{ .name }} のクリーンアップに失敗しました: {{ .err }}"
|
||||
FileDropSuccess: "ファイル {{ .name }} のクリーンアップに成功しました、{{ .count }} ファイルを削除、{{ .size }} のディスク領域を解放"
|
||||
FileDropSum: "ファイルのクリーンアップが完了しました、合計 {{ .count }} ファイルを削除、合計 {{ .size }} のディスク領域を解放"
|
||||
ErrBadDecrypt: "圧縮パッケージの解凍パスワードが間違っています。解凍に失敗しました。確認して再試行してください!"
|
||||
|
||||
#website
|
||||
ErrLogFileToLarge: 'ログファイルが500MBを超えており、開くことができません'
|
||||
FileDropFailed: 'ファイル {{ .name }} のクリーンアップに失敗しました: {{ .err }}'
|
||||
FileDropSuccess: 'ファイル {{ .name }} のクリーンアップに成功しました、{{ .count }} ファイルを削除、{{ .size }} のディスク領域を解放'
|
||||
FileDropSum: 'ファイルのクリーンアップが完了しました、合計 {{ .count }} ファイルを削除、合計 {{ .size }} のディスク領域を解放'
|
||||
ErrBadDecrypt: '圧縮パッケージの解凍パスワードが間違っています。解凍に失敗しました。確認後再試行!'
|
||||
ErrAliasIsExist: 'エイリアスがすでに存在します'
|
||||
ErrBackupMatch: 'バックアップ ファイルは、現在の Web サイト データ {{ .detail }} の一部と一致しません'
|
||||
ErrBackupExist: 'バックアップ ファイル内のソース データの対応する部分が存在しません {{ .detail }}'
|
||||
ErrPHPResource: 'ローカルオペレーティング環境は切り替えをサポートしていません! '
|
||||
ErrPHPResource: 'ローカルオペレーティング環境は切り替えをサポートしていません'
|
||||
ErrPathPermission: 'インデックス ディレクトリに 1000:1000 以外の権限を持つフォルダーが検出されました。これにより、Web サイトでアクセス拒否エラーが発生する可能性があります。上記の保存ボタンをクリックしてください。'
|
||||
ErrDomainIsUsed: 'ドメイン名はウェブサイト [{{ .name }}] で既に使用されています'
|
||||
ErrDomainFormat: '{{ .name }} ドメイン名の形式が正しくありません'
|
||||
@@ -151,11 +143,11 @@ ErrDefaultAlias: 'デフォルトは予約済みのコードです。別のコ
|
||||
ErrParentWebsite: 'まずサブサイト {{ .name }} を削除する必要があります'
|
||||
ErrBuildDirNotFound: 'ビルド ディレクトリが存在しません'
|
||||
ErrImageNotExist: 'オペレーティング環境 {{ .name }} イメージが存在しません。オペレーティング環境を再編集してください'
|
||||
ErrProxyIsUsed: "ロードバランシングはリバースプロキシによって使用されているため、削除できません"
|
||||
ErrProxyIsUsed: 'ロードバランシングはリバースプロキシによって使用されているため、削除できません'
|
||||
ErrSSLValid: '証明書ファイルが異常です、証明書の状態を確認してください!'
|
||||
ErrWebsiteDir: "ウェブサイトディレクトリ内のディレクトリを選択してください。"
|
||||
ErrComposerFileNotFound: "composer.json ファイルが存在しません"
|
||||
ErrRuntimeNoPort: "ランタイム環境にポートが設定されていません。先にランタイム環境を編集してください。"
|
||||
ErrWebsiteDir: 'ウェブサイトディレクトリ内のディレクトリを選択してください。'
|
||||
ErrComposerFileNotFound: 'composer.json ファイルが存在しません'
|
||||
ErrRuntimeNoPort: 'ランタイム環境にポートが設定されていません。先にランタイム環境を編集してください。'
|
||||
Status: 'ステータス'
|
||||
start: '開始'
|
||||
stop: '停止'
|
||||
@@ -168,8 +160,6 @@ delete: '削除'
|
||||
ErrDefaultWebsite: 'デフォルト Web サイトが既に設定されています。設定する前にキャンセルしてください!'
|
||||
SSL: '証明書'
|
||||
Set: '設定'
|
||||
|
||||
#ssl
|
||||
ErrSSLCannotDelete: '{{ .name }} 証明書は Web サイトで使用されているため、削除できません'
|
||||
ErrAccountCannotDelete: 'アカウントは証明書に関連付けられているため、削除できません'
|
||||
ErrSSLApply: '証明書の更新は成功しましたが、openresty の再ロードに失敗しました。設定を確認してください。'
|
||||
@@ -180,12 +170,12 @@ ErrSSLKeyFormat: '秘密鍵ファイルの検証に失敗しました'
|
||||
ErrSSLCertificateFormat: '証明書ファイルの形式が正しくありません。pem 形式を使用してください'
|
||||
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid または EabHmacKey は空白にできません'
|
||||
ErrOpenrestyNotFound: 'Http モードでは、まず Openresty をインストールする必要があります'
|
||||
ApplySSLStart: '証明書の申請を開始します。ドメイン名 [{{ .domain }}] 申請方法 [{{ .type }}] '
|
||||
ApplySSLStart: '証明書の申請を開始します。ドメイン名 [{{ .domain }}] 申請方法 [{{ .type }}]'
|
||||
dnsAccount: 'DNS 自動'
|
||||
dnsManual: 'DNS マニュアル'
|
||||
http: "HTTP"
|
||||
http: 'HTTP'
|
||||
ApplySSLFailed: '[{{ .domain }}] 証明書の申請に失敗しました、{{ .detail }}'
|
||||
ApplySSLSuccess: '[{{ .domain }}] 証明書の申請に成功しました! '
|
||||
ApplySSLSuccess: '[{{ .domain }}] 証明書の申請に成功しました。'
|
||||
DNSAccountName: 'DNS アカウント [{{ .name }}] ベンダー [{{ .type }}]'
|
||||
PushDirLog: '証明書がディレクトリ [{{ .path }}] {{ .status }} にプッシュされました'
|
||||
ErrDeleteCAWithSSL: '現在の組織には発行済みの証明書があり、削除できません。'
|
||||
@@ -200,41 +190,63 @@ ExecShellSuccess: 'スクリプトの実行が成功しました'
|
||||
StartUpdateSystemSSL: 'システム証明書の更新を開始します'
|
||||
UpdateSystemSSLSuccess: 'システム証明書を正常に更新しました'
|
||||
ErrWildcardDomain: 'HTTP モードでワイルドカード ドメイン名証明書を申請できません'
|
||||
ErrApplySSLCanNotDelete: "申請中の証明書 {{.name}} は削除できません。しばらくしてからもう一度お試しください。"
|
||||
StartPushSSLToNode: "証明書をノードにプッシュ開始"
|
||||
PushSSLToNodeFailed: "ノードに証明書をプッシュ失敗: {{ .err }}"
|
||||
PushSSLToNodeSuccess: "ノードに証明書をプッシュ成功"
|
||||
|
||||
#mysql
|
||||
ErrApplySSLCanNotDelete: '申請中の証明書 {{ .name }} は削除できません。しばらくしてからもう一度お試しください。'
|
||||
StartPushSSLToNode: '証明書をノードにプッシュ開始'
|
||||
PushSSLToNodeFailed: 'ノードに証明書をプッシュ失敗: {{ .err }}'
|
||||
PushSSLToNodeSuccess: 'ノードに証明書をプッシュ成功'
|
||||
ErrUserIsExist: '現在のユーザーは既に存在します。再入力してください'
|
||||
ErrDatabaseIsExist: '現在のデータベースは既に存在します。再入力してください'
|
||||
ErrExecTimeOut: 'SQL 実行がタイムアウトしました。データベースを確認してください'
|
||||
ErrRemoteExist: 'この名前のリモート データベースは既に存在します。変更してもう一度お試しください'
|
||||
ErrLocalExist: '名前はローカル データベースに既に存在します。変更してもう一度お試しください'
|
||||
RemoteBackup: "リモートデータベースをバックアップするには、まずイメージ {{ .name }} を使用してローカルコンテナデータベースサービスを起動する必要があります。しばらくお待ちください..."
|
||||
RemoteRecover: "リモートデータベースを復元するには、まずイメージ {{ .name }} を使用してローカルコンテナデータベースサービスを起動する必要があります。しばらくお待ちください..."
|
||||
Arg: "引数"
|
||||
|
||||
#redis
|
||||
RemoteBackup: 'リモートデータベースをバックアップするには、まずイメージ {{ .name }} を使用してローカルコンテナデータベースサービスを起動する必要があります。しばらくお待ちください...'
|
||||
RemoteRecover: 'リモートデータベースを復元するには、まずイメージ {{ .name }} を使用してローカルコンテナデータベースサービスを起動する必要があります。しばらくお待ちください...'
|
||||
Arg: '引数'
|
||||
ErrTypeOfRedis: 'リカバリ ファイルの種類が現在の永続化方法と一致しません。変更して再試行してください'
|
||||
|
||||
#container
|
||||
ErrInUsed: "{{ .detail }} は使用中であり、削除できません!"
|
||||
ErrObjectInUsed: "このオブジェクトは使用中であり、削除できません!"
|
||||
ErrObjectBeDependent: "このイメージは他のイメージに依存されているため、削除できません!"
|
||||
ErrInUsed: '{{ .detail }} は使用中であり、削除できません!'
|
||||
ErrObjectInUsed: 'このオブジェクトは使用中であり、削除できません!'
|
||||
ErrObjectBeDependent: 'このイメージは他のイメージに依存されているため、削除できません!'
|
||||
ErrPortRules: 'ポート番号が一致しません。再入力してください。'
|
||||
ErrPgImagePull: 'イメージのプルがタイムアウトしました。イメージのアクセラレーションを設定するか、{{ .name }} イメージを手動でプルして再試行してください'
|
||||
PruneHelper: "今回のクリーンアップ {{ .name }} でディスク領域 {{ .size }} を解放しました"
|
||||
ImageRemoveHelper: "イメージ{{ .name }}を削除し、{{ .size }}のディスク領域を解放しました"
|
||||
BuildCache: "ビルドキャッシュ"
|
||||
Volume: "ストレージボリューム"
|
||||
Network: "ネットワーク"
|
||||
PruneStart: "クリーンアップ中、しばらくお待ちください..."
|
||||
|
||||
#runtime
|
||||
PruneHelper: '今回のクリーンアップ {{ .name }} でディスク領域 {{ .size }} を解放しました'
|
||||
ImageRemoveHelper: 'イメージ{{ .name }}を削除し、{{ .size }}のディスク領域を解放しました'
|
||||
BuildCache: 'ビルドキャッシュ'
|
||||
Volume: 'ストレージボリューム'
|
||||
Network: 'ネットワーク'
|
||||
PruneStart: 'クリーンアップ中、しばらくお待ちください...'
|
||||
ContainerBackupPrepare: 'コンテナバックアップ作業領域を準備'
|
||||
ContainerBackupStop: 'バックアップ前にコンテナを停止'
|
||||
ContainerBackupInspect: 'コンテナ inspect とネットワーク情報をバックアップ'
|
||||
ContainerBackupMounts: 'コンテナのマウントデータをバックアップ'
|
||||
ContainerBackupMeta: 'コンテナバックアップのメタデータを書き込み'
|
||||
ContainerBackupStart: 'バックアップ後にコンテナを起動'
|
||||
ContainerRecoverPrepare: 'コンテナ復元作業領域を準備'
|
||||
ContainerRecoverExtract: 'コンテナバックアップを展開'
|
||||
ContainerRecoverParse: 'コンテナバックアップメタデータを読み込み'
|
||||
ContainerRecoverCreate: 'コンテナを再作成'
|
||||
ContainerRecoverMounts: 'コンテナマウントを復元'
|
||||
ContainerRecoverStart: '復元済みコンテナを起動'
|
||||
ContainerRecoverCleanup: 'コンテナ復元作業領域をクリーンアップ'
|
||||
ComposeBackupPrepare: 'Compose バックアップ作業領域を準備'
|
||||
ComposeBackupStop: 'バックアップ前に Compose を停止'
|
||||
ComposeBackupFiles: 'Compose ファイルをバックアップ'
|
||||
ComposeBackupContainers: 'Compose コンテナをバックアップ'
|
||||
ComposeBackupMeta: 'Compose バックアップのメタデータを書き込み'
|
||||
ComposeBackupStart: 'バックアップ後に Compose を起動'
|
||||
ComposeBackupCleanup: 'Compose バックアップ作業領域をクリーンアップ'
|
||||
ComposeRecoverPrepare: 'Compose 復元作業領域を準備'
|
||||
ComposeRecoverExtract: 'Compose バックアップを展開'
|
||||
ComposeRecoverMeta: 'Compose バックアップメタデータを読み込み'
|
||||
ComposeRecoverMetaLogName: 'Compose 復元メタ情報: 名前={{ .name }}'
|
||||
ComposeRecoverMetaLogPath: 'Compose 復元メタ情報: バックアップパス={{ .backupPath }} 復元先={{ .targetDir }}'
|
||||
ComposeRecoverMetaLogCount: 'Compose 復元メタ情報: ファイル={{ .files }} コンテナ={{ .containers }}'
|
||||
ComposeRecoverFiles: 'Compose ファイルを復元'
|
||||
ComposeRecoverContainers: 'Compose コンテナを復元'
|
||||
ComposeRecoverRecord: 'Compose 記録を保存'
|
||||
ComposeRecoverCleanup: 'Compose 復元作業領域をクリーンアップ'
|
||||
ErrFileNotExist: '{{ .detail }} ファイルが存在しません。ソース ファイルの整合性を確認してください。'
|
||||
ErrImageBuildErr: 'イメージのビルドに失敗しました'
|
||||
ErrImageExist: "イメージはすでに存在します!イメージ名を変更してください。"
|
||||
ErrImageExist: 'イメージはすでに存在します!イメージ名を変更してください。'
|
||||
ErrDelWithWebsite: 'オペレーティング環境は既に Web サイトに関連付けられているため、削除できません'
|
||||
ErrRuntimeStart: '起動に失敗しました'
|
||||
ErrPackageJsonNotFound: 'package.json ファイルが存在しません'
|
||||
@@ -242,55 +254,45 @@ ErrScriptsNotFound: 'スクリプト構成項目が package.json に見つかり
|
||||
ErrContainerNameNotFound: 'コンテナ名を取得できません。.env ファイルを確認してください'
|
||||
ErrNodeModulesNotFound: 'node_modules フォルダが存在しません。ランタイム環境を編集するか、ランタイム環境が正常に起動するまでお待ちください。'
|
||||
ErrContainerNameIsNull: 'コンテナ名が存在しません'
|
||||
ErrPHPPortIsDefault: "ポート9000はデフォルトポートです。修正してから再試行してください"
|
||||
ErrPHPRuntimePortFailed: "ポート {{ .name }} は現在のランタイム環境で使用されています。修正してから再試行してください"
|
||||
|
||||
#tool
|
||||
ErrPHPPortIsDefault: 'ポート9000はデフォルトポートです。修正してから再試行してください'
|
||||
ErrPHPRuntimePortFailed: 'ポート {{ .name }} は現在のランタイム環境で使用されています。修正してから再試行してください'
|
||||
ErrConfigNotFound: '構成ファイルが存在しません'
|
||||
ErrConfigParse: '構成ファイルの形式が正しくありません'
|
||||
ErrConfigIsNull: '構成ファイルは空にできません'
|
||||
ErrConfigDirNotFound: '実行ディレクトリが存在しません'
|
||||
ErrConfigAlreadyExist: '同じ名前の設定ファイルがすでに存在します'
|
||||
ErrUserFindErr: 'ユーザー {{ .name }} の検索に失敗しました {{ .err }}'
|
||||
|
||||
#cronjob
|
||||
CutWebsiteLogSuccess: '{{ .name }} ウェブサイトのログが正常にカットされました。バックアップ パス {{ .path }}'
|
||||
HandleShell: 'スクリプト {{ .name }} を実行します'
|
||||
HandleCurl: "URL {{ .name }} にアクセス"
|
||||
HandleCurl: 'URL {{ .name }} にアクセス'
|
||||
HandleNtpSync: 'システム時刻の同期'
|
||||
HandleSystemClean: 'システム キャッシュのクリーンアップ'
|
||||
SystemLog: 'システムログ'
|
||||
CutWebsiteLog: 'ウェブサイトログのローテーション'
|
||||
FileOrDir: 'ディレクトリ / ファイル'
|
||||
UploadFile: 'バックアップファイル {{ .file }} を {{ .backup }} にアップロード中'
|
||||
Upload: "アップロード"
|
||||
IgnoreBackupErr: "バックアップ失敗、エラー: {{ .detail }}、このエラーを無視します..."
|
||||
IgnoreUploadErr: "アップロード失敗、エラー: {{ .detail }}、このエラーを無視します..."
|
||||
LoadBackupFailed: "バックアップアカウントの接続取得に失敗、エラー: {{ .detail }}"
|
||||
InExecuting: "現在のタスクは実行中です。重複して実行しないでください!"
|
||||
NoSuchResource: "データベースにバックアップ内容が見つかりませんでした。スキップします..."
|
||||
CleanLog: "ログをクリーン"
|
||||
CleanLogByName: "{{.name}} のログをクリーン"
|
||||
WafIpGroupNotFound: "WAF IPグループが見つかりません。[高度な機能 - WAF - ブラックリスト/ホワイトリスト - IPグループ]に移動し、リモートダウンロード方式でIPグループを追加してください。"
|
||||
|
||||
#toolbox
|
||||
Upload: 'アップロード'
|
||||
IgnoreBackupErr: 'バックアップ失敗、エラー: {{ .detail }}、このエラーを無視します...'
|
||||
IgnoreUploadErr: 'アップロード失敗、エラー: {{ .detail }}、このエラーを無視します...'
|
||||
LoadBackupFailed: 'バックアップアカウントの接続取得に失敗、エラー: {{ .detail }}'
|
||||
InExecuting: '現在のタスクは実行中です。重複して実行しないでください!'
|
||||
NoSuchResource: 'データベースにバックアップ内容が見つかりませんでした。スキップします...'
|
||||
CleanLog: 'ログをクリーン'
|
||||
CleanLogByName: '{{ .name }} のログをクリーン'
|
||||
WafIpGroupNotFound: 'WAF IPグループが見つかりません。[高度な機能 - WAF - ブラックリスト/ホワイトリスト - IPグループ]に移動し、リモートダウンロード方式でIPグループを追加してください。'
|
||||
ErrNotExistUser: '現在のユーザーは存在しません。変更してもう一度お試しください。'
|
||||
ErrBanAction: '設定に失敗しました。現在の {{ .name }} サービスは利用できません。確認してもう一度お試しください。'
|
||||
ErrClamdscanNotFound: 'clamdscan コマンドが検出されませんでした。インストールするにはドキュメントを参照してください。'
|
||||
TaskSwapSet: "Swap設定"
|
||||
SetSwap: "Swap設定 {{ .path }} - {{ .size }}"
|
||||
CreateSwap: "Swapファイル作成"
|
||||
FormatSwap: "Swapファイルフォーマット"
|
||||
EnableSwap: "Swapファイル有効化"
|
||||
|
||||
#tamper
|
||||
CleanTamperSetting: "履歴保護設定をクリーンアップ"
|
||||
SetTamperWithRules: "ルールに従ってディレクトリ保護を開始"
|
||||
ProtectDir: "ディレクトリを保護 {{ .name }}"
|
||||
IgnoreFile: "ファイルを保護 {{ .name }}"
|
||||
TamperSetSuccessful: "保護設定が完了し、以下のディレクトリの監視を開始します:"
|
||||
|
||||
#waf
|
||||
TaskSwapSet: 'Swap設定'
|
||||
SetSwap: 'Swap設定 {{ .path }} - {{ .size }}'
|
||||
CreateSwap: 'Swapファイル作成'
|
||||
FormatSwap: 'Swapファイルフォーマット'
|
||||
EnableSwap: 'Swapファイル有効化'
|
||||
CleanTamperSetting: '履歴保護設定をクリーンアップ'
|
||||
SetTamperWithRules: 'ルールに従ってディレクトリ保護を開始'
|
||||
ProtectDir: 'ディレクトリを保護 {{ .name }}'
|
||||
IgnoreFile: 'ファイルを保護 {{ .name }}'
|
||||
TamperSetSuccessful: '保護設定が完了し、以下のディレクトリの監視を開始します:'
|
||||
ErrScope: 'この構成の変更はサポートされていません'
|
||||
ErrStateChange: '状態の変更に失敗しました'
|
||||
ErrRuleExist: 'ルールは既に存在します'
|
||||
@@ -298,7 +300,7 @@ ErrRuleNotExist: 'ルールが存在しません'
|
||||
ErrParseIP: 'IP 形式が間違っています'
|
||||
ErrDefaultIP: 'デフォルトは予約名です。別の名前に変更してください'
|
||||
ErrGroupInUse: 'IP グループはブラックリスト/ホワイトリストで使用されており、削除できません'
|
||||
ErrIPGroupAclUse: "IPグループはウェブサイト {{ .name }} のカスタムルールで使用されているため、削除できません"
|
||||
ErrIPGroupAclUse: 'IPグループはウェブサイト {{ .name }} のカスタムルールで使用されているため、削除できません'
|
||||
ErrGroupExist: 'IP グループ名がすでに存在します'
|
||||
ErrIPRange: 'IP 範囲が間違っています'
|
||||
ErrIPExist: 'IP がすでに存在します'
|
||||
@@ -309,10 +311,10 @@ xss: 'XSS'
|
||||
phpExec: 'PHP スクリプトの実行'
|
||||
oneWordTrojan: 'ワンワードトロイの木馬'
|
||||
appFilter: '危険なディレクトリフィルタリングを適用する'
|
||||
webshell: 'ウェブシェル'
|
||||
webShell: 'ウェブシェル'
|
||||
args: 'パラメータルール'
|
||||
protocolFilter: 'プロトコルフィルタリング'
|
||||
javaFileter: 'Java 危険ファイル フィルター'
|
||||
javaFilter: 'Java 危険ファイル フィルター'
|
||||
scannerFilter: 'スキャナ フィルタ'
|
||||
escapeFilter: 'エスケープフィルター'
|
||||
customRule: 'カスタムルール'
|
||||
@@ -342,10 +344,9 @@ ErrDBNotExist: 'データベースが存在しません'
|
||||
allow: '許可'
|
||||
deny: '拒否'
|
||||
OpenrestyNotFound: 'Openresty がインストールされていません'
|
||||
remoteIpIsNull: "IPリストが空です"
|
||||
OpenrestyVersionErr: "Openrestyのバージョンが低すぎます。Openrestyを1.27.1.2-2-2-focalにアップグレードしてください"
|
||||
|
||||
#task
|
||||
remoteIpIsNull: 'IPリストが空です'
|
||||
OpenrestyVersionErr: 'Openrestyのバージョンが低すぎます。Openrestyを1.27.1.2-2-2-focalにアップグレードしてください'
|
||||
ErrFileTooLarge: 'ファイルが 1MB を超えているためアップロードできません'
|
||||
TaskStart: '{{ .name }} タスクが開始されました [START]'
|
||||
TaskEnd: '{{ .name }} タスクが完了しました [COMPLETED]'
|
||||
TaskFailed: '{{ .name }} タスクが失敗しました'
|
||||
@@ -361,7 +362,7 @@ TaskDelete: '削除'
|
||||
TaskUpgrade: 'アップグレード'
|
||||
TaskUpdate: '更新'
|
||||
TaskRestart: '再起動'
|
||||
TaskProtect: "保護"
|
||||
TaskProtect: '保護'
|
||||
TaskBackup: 'バックアップ'
|
||||
TaskRecover: '回復'
|
||||
TaskRollback: 'ロールバック'
|
||||
@@ -369,10 +370,10 @@ TaskPull: 'プル'
|
||||
TaskCommit: 'コミット'
|
||||
TaskBuild: 'ビルド'
|
||||
TaskPush: 'プッシュ'
|
||||
TaskClean: "クリーンアップ"
|
||||
TaskClean: 'クリーンアップ'
|
||||
TaskHandle: '実行'
|
||||
TaskImport: "インポート"
|
||||
TaskExport: "エクスポート"
|
||||
TaskImport: 'インポート'
|
||||
TaskExport: 'エクスポート'
|
||||
Website: 'ウェブサイト'
|
||||
App: 'アプリケーション'
|
||||
Runtime: 'ランタイム環境'
|
||||
@@ -404,18 +405,12 @@ RuntimeExtension: 'ランタイム環境拡張'
|
||||
TaskIsExecuting: 'タスクは実行中です'
|
||||
CustomAppstore: 'カスタム アプリケーション ウェアハウス'
|
||||
TaskExec: '実行'
|
||||
TaskBatch: "一括操作"
|
||||
TaskBatch: '一括操作'
|
||||
FileConvert: 'ファイル形式の変換'
|
||||
|
||||
# task - clam
|
||||
Clamscan: "{{ .name }} をスキャン"
|
||||
TaskScan: "スキャン"
|
||||
|
||||
#task - ai
|
||||
Clamscan: '{{ .name }} をスキャン'
|
||||
TaskScan: 'スキャン'
|
||||
OllamaModelPull: 'Ollama モデル {{ .name }} をプルします'
|
||||
OllamaModelSize: "Ollama モデル {{ .name }} のサイズを取得します"
|
||||
|
||||
#task - snapshot
|
||||
OllamaModelSize: 'Ollama モデル {{ .name }} のサイズを取得します'
|
||||
Snapshot: 'スナップショット'
|
||||
SnapDBInfo: '1Panel データベース情報を書き込む'
|
||||
SnapCopy: 'ファイルとディレクトリ {{ .name }} をコピーします'
|
||||
@@ -441,7 +436,6 @@ SnapCompressSize: 'スナップショット ファイル サイズ {{ .name }}'
|
||||
SnapUpload: 'スナップショット ファイルをアップロード'
|
||||
SnapUploadTo: 'スナップショット ファイルを {{ .name }} にアップロードします'
|
||||
SnapUploadRes: 'スナップショット ファイルを {{ .name }} にアップロードします'
|
||||
|
||||
SnapshotRecover: 'スナップショットの復元'
|
||||
RecoverDownload: 'スナップショット ファイルをダウンロード'
|
||||
Download: 'ダウンロード'
|
||||
@@ -449,10 +443,10 @@ RecoverDownloadAccount: 'スナップショット ダウンロード バック
|
||||
RecoverDecompress: 'スナップショット圧縮ファイルを解凍する'
|
||||
Decompress: '減圧'
|
||||
BackupBeforeRecover: 'スナップショットの前にシステム関連データをバックアップする'
|
||||
Readjson: 'スナップショット内の Json ファイルを読み取ります'
|
||||
ReadjsonPath: 'スナップショット内の Json ファイル パスを取得します'
|
||||
ReadjsonContent: 'Json ファイルを読み取ります'
|
||||
ReadjsonMarshal: 'Json エスケープ処理'
|
||||
ReadJson: 'スナップショット内の Json ファイルを読み取ります'
|
||||
ReadJsonPath: 'スナップショット内の Json ファイル パスを取得します'
|
||||
ReadJsonContent: 'Json ファイルを読み取ります'
|
||||
ReadJsonMarshal: 'Json エスケープ処理'
|
||||
RecoverApp: 'インストールされたアプリを復元する'
|
||||
RecoverWebsite: 'ウェブサイトディレクトリを回復する'
|
||||
RecoverAppImage: 'スナップショットイメージのバックアップを復元する'
|
||||
@@ -466,9 +460,6 @@ RecoverDaemonJson: 'コンテナ構成 daemon.json ファイルを復元する'
|
||||
RecoverDBData: 'データベース データを回復する'
|
||||
RecoverBackups: 'ローカル バックアップ ディレクトリを復元する'
|
||||
RecoverPanelData: 'リカバリデータディレクトリ'
|
||||
|
||||
#task - container
|
||||
ContainerNewCliet: 'Docker クライアントを初期化する'
|
||||
ContainerImagePull: 'コンテナ イメージ {{ .name }} をプルします'
|
||||
ContainerRemoveOld: '元のコンテナ {{ .name }} を削除します'
|
||||
ContainerImageCheck: 'イメージが正常にプルされているかどうかを確認します'
|
||||
@@ -477,22 +468,13 @@ ContainerRecreate: 'コンテナの更新に失敗しました。元のコンテ
|
||||
ContainerCreate: '新しいコンテナ {{ .name }} を作成します'
|
||||
ContainerCreateFailed: 'コンテナの作成に失敗しました。失敗したコンテナを削除してください'
|
||||
ContainerStartCheck: 'コンテナが起動されているかどうかを確認します'
|
||||
|
||||
#task - image
|
||||
ImageBuild: 'イメージビルド'
|
||||
ImageBuildStdoutCheck: '画像出力コンテンツを解析する'
|
||||
ImageBuildRes: 'イメージビルド出力: {{ .name }}'
|
||||
ImagePull: '画像をプル'
|
||||
ImageRepoAuthFromDB: 'データベースからリポジトリ認証情報を取得する'
|
||||
ImaegPullRes: "画像プル出力: {{ .name }}"
|
||||
ImagePush: '画像をプッシュ'
|
||||
ImageRenameTag: '画像タグを変更する'
|
||||
ImageNewTag: '新しい画像タグ {{ .name }}'
|
||||
ImaegPushRes: '画像プッシュ出力: {{ .name }}'
|
||||
ComposeCreate: 'コンポジションを作成する'
|
||||
ComposeCreateRes: "Compose 作成出力: {{ .name }}"
|
||||
|
||||
#task - website
|
||||
BackupNginxConfig: 'ウェブサイトの OpenResty 構成ファイルをバックアップする'
|
||||
CompressFileSuccess: 'ディレクトリを正常に圧縮しました。{{ .name }} に圧縮されました'
|
||||
CompressDir: '圧縮ディレクトリ'
|
||||
@@ -503,40 +485,33 @@ websiteDir: 'ウェブサイトディレクトリ'
|
||||
RecoverFailedStartRollBack: 'リカバリに失敗しました。ロールバックを開始します'
|
||||
AppBackupFileIncomplete: 'バックアップ ファイルが不完全で、app.json または app.tar.gz ファイルが不足しています'
|
||||
AppAttributesNotMatch: 'アプリケーションの種類または名前が一致しません'
|
||||
|
||||
#alert
|
||||
ErrAlert: '警告メッセージの形式が正しくありません。確認してもう一度お試しください。'
|
||||
ErrAlertPush: 'アラート情報のプッシュ中にエラーが発生しました。確認してもう一度お試しください。'
|
||||
ErrAlertSave: 'アラーム情報の保存中にエラーが発生しました。確認してもう一度お試しください。'
|
||||
ErrAlertSync: 'アラーム情報の同期エラーです。確認してもう一度お試しください。'
|
||||
ErrAlertRemote: 'アラーム メッセージのリモート エラーです。確認してもう一度お試しください。'
|
||||
ErrInstallExtension: 'インストールタスクが進行中です、タスクが終了するのを待ってください'
|
||||
PanelAlertTitle: 'パネルアラート通知'
|
||||
TestAlertTitle: 'テストメール - メール接続の確認'
|
||||
TestAlert: 'これはテストメールです。メール送信設定が正しく構成されているかを確認します。'
|
||||
LicenseExpirationAlert: 'あなたの {{ .node }}{{ .ip }} パネル、ライセンスは {{ .day }} 日後に期限切れになります。詳細はパネルにログインして'
|
||||
CronJobFailedAlert: 'あなたの {{ .node }}{{ .ip }} パネル、スケジュールタスク {{ .name }} の実行に失敗しました。詳細はパネルにログインして'
|
||||
ClamAlert: 'あなたの {{ .node }}{{ .ip }} パネル、ウイルススキャンタスクで {{ .num }} 個の感染ファイルを検出しました。詳細はパネルにログインして'
|
||||
WebSiteAlert: 'あなたの {{ .node }}{{ .ip }} パネル、{{ .num }} 個のウェブサイトが {{ .day }} 日後に期限切れになります。詳細はパネルにログインして'
|
||||
SSLAlert: 'あなたの {{ .node }}{{ .ip }} パネル、{{ .num }} 枚のSSL証明書が {{ .day }} 日後に期限切れになります。詳細はパネルにログインして'
|
||||
DiskUsedAlert: 'あなたの {{ .node }}{{ .ip }} パネル、ディスク {{ .name }} の使用率が {{ .used }} に達しました。詳細はパネルにログインして'
|
||||
ResourceAlert: 'あなたの {{ .node }}{{ .ip }} パネル、過去 {{ .time }} 分間の {{ .name }} 平均使用率は {{ .used }} です。詳細はパネルにログインして'
|
||||
PanelVersionAlert: 'あなたの {{ .node }}{{ .ip }} パネル、最新バージョンがアップグレード可能です。詳細はパネルにログインして'
|
||||
PanelPwdExpirationAlert: 'あなたの {{ .node }}{{ .ip }} パネル、パネルパスワードは {{ .day }} 日後に期限切れになります。詳細はパネルにログインして'
|
||||
CommonAlert: 'あなたの {{ .node }}{{ .ip }} パネル、{{ .msg }}。詳細はパネルにログインして'
|
||||
NodeExceptionAlert: 'あなたの {{ .node }}{{ .ip }} パネル、{{ .num }} 個のノードに異常が発生しています。詳細はパネルにログインして'
|
||||
LicenseExceptionAlert: 'あなたの {{ .node }}{{ .ip }} パネル、{{ .num }} 個のライセンスに異常が発生しています。詳細はパネルにログインして'
|
||||
SSHAndPanelLoginAlert: 'あなたの {{ .node }}{{ .ip }} パネル、{{ .loginIp }} からの {{ .name }} ログインに異常があります。詳細はパネルにログインして'
|
||||
DeviceNotFound: 'デバイス {{ .name }} が見つかりません'
|
||||
DeviceIsMounted: 'デバイス {{ .name }} はマウントされています、まずアンマウントしてください'
|
||||
PartitionDiskErr: 'パーティションに失敗しました、{{ .err }}'
|
||||
FormatDiskErr: 'ディスクのフォーマットに失敗しました、{{ .err }}'
|
||||
MountDiskErr: 'ディスクのマウントに失敗しました、{{ .err }}'
|
||||
XfsNotFound: 'xfs ファイルシステムが検出されませんでした、最初に xfsprogs をインストールしてください'
|
||||
|
||||
#task - runtime
|
||||
ErrInstallExtension: "インストールタスクが進行中です、タスクが終了するのを待ってください"
|
||||
|
||||
# alert mail template
|
||||
PanelAlertTitle: "パネルアラート通知"
|
||||
TestAlertTitle: "テストメール - メール接続の確認"
|
||||
TestAlert: "これはテストメールです。メール送信設定が正しく構成されているかを確認します。"
|
||||
LicenseExpirationAlert: "あなたの {{ .node }}{{ .ip }} パネル、ライセンスは {{ .day }} 日後に期限切れになります。詳細はパネルにログインしてご確認ください。"
|
||||
CronJobFailedAlert: "あなたの {{ .node }}{{ .ip }} パネル、スケジュールタスク {{ .name }} の実行に失敗しました。詳細はパネルにログインしてご確認ください。"
|
||||
ClamAlert: "あなたの {{ .node }}{{ .ip }} パネル、ウイルススキャンタスクで {{ .num }} 個の感染ファイルを検出しました。詳細はパネルにログインしてご確認ください。"
|
||||
WebSiteAlert: "あなたの {{ .node }}{{ .ip }} パネル、{{ .num }} 個のウェブサイトが {{ .day }} 日後に期限切れになります。詳細はパネルにログインしてご確認ください。"
|
||||
SSLAlert: "あなたの {{ .node }}{{ .ip }} パネル、{{ .num }} 枚のSSL証明書が {{ .day }} 日後に期限切れになります。詳細はパネルにログインしてご確認ください。"
|
||||
DiskUsedAlert: "あなたの {{ .node }}{{ .ip }} パネル、ディスク {{ .name }} の使用率が {{ .used }} に達しました。詳細はパネルにログインしてご確認ください。"
|
||||
ResourceAlert: "あなたの {{ .node }}{{ .ip }} パネル、過去 {{ .time }} 分間の {{ .name }} 平均使用率は {{ .used }} です。詳細はパネルにログインしてご確認ください。"
|
||||
PanelVersionAlert: "あなたの {{ .node }}{{ .ip }} パネル、最新バージョンがアップグレード可能です。詳細はパネルにログインしてご確認ください。"
|
||||
PanelPwdExpirationAlert: "あなたの {{ .node }}{{ .ip }} パネル、パネルパスワードは {{ .day }} 日後に期限切れになります。詳細はパネルにログインしてご確認ください。"
|
||||
CommonAlert: "あなたの {{ .node }}{{ .ip }} パネル、{{ .msg }}。詳細はパネルにログインしてご確認ください。"
|
||||
NodeExceptionAlert: "あなたの {{ .node }}{{ .ip }} パネル、{{ .num }} 個のノードに異常が発生しています。詳細はパネルにログインしてご確認ください。"
|
||||
LicenseExceptionAlert: "あなたの {{ .node }}{{ .ip }} パネル、{{ .num }} 個のライセンスに異常が発生しています。詳細はパネルにログインしてご確認ください。"
|
||||
SSHAndPanelLoginAlert: "あなたの {{ .node }}{{ .ip }} パネル、{{ .loginIp }} からの {{ .name }} ログインに異常があります。詳細はパネルにログインしてご確認ください。"
|
||||
|
||||
#disk
|
||||
DeviceNotFound: "デバイス {{ .name }} が見つかりません"
|
||||
DeviceIsMounted: "デバイス {{ .name }} はマウントされています、まずアンマウントしてください"
|
||||
PartitionDiskErr: "パーティションに失敗しました、{{ .err }}"
|
||||
FormatDiskErr: "ディスクのフォーマットに失敗しました、{{ .err }}"
|
||||
MountDiskErr: "ディスクのマウントに失敗しました、{{ .err }}"
|
||||
UnMountDiskErr: "ディスクのアンマウントに失敗しました、{{ .err }}"
|
||||
XfsNotFound: "xfs ファイルシステムが検出されませんでした、最初に xfsprogs をインストールしてください"
|
||||
# terminal
|
||||
TerminalAIBlockedRiskyCommand: '危険なコマンドをブロックしました: {{ .command }}'
|
||||
TerminalAIThinking: 'AI が考えています...'
|
||||
TerminalAIReadyToExecute: '思考が完了しました。Enter で実行してください(所要時間 {{ .duration }}、token: {{ .tokens }})'
|
||||
TerminalAIRequestFailed: 'AI リクエストに失敗しました: {{ .err }}'
|
||||
|
||||
+212
-238
@@ -1,31 +1,30 @@
|
||||
ErrInvalidParams: '요청 매개변수 오류: {{ .detail }}'
|
||||
ErrTokenParse: '토큰 생성 오류: {{ .detail }}'
|
||||
ErrInitialPassword: '원래 비밀번호가 올바르지 않습니다'
|
||||
ErrInternalServer: '내부 서버 오류: {{ .detail }}'
|
||||
ErrRecordExist: '레코드가 이미 존재합니다'
|
||||
ErrInvalidParams: '잘못된 요청: {{ .detail }}'
|
||||
ErrTokenParse: '토큰 오류: {{ .detail }}'
|
||||
ErrInitialPassword: '원래 비밀번호가 틀립니다'
|
||||
ErrInternalServer: '서버 오류: {{ .detail }}'
|
||||
ErrWgetRemoteFailed: '원격 다운로드 주소가 잘못되었거나 접근할 수 없습니다: {{ .detail }}'
|
||||
ErrWgetInvalidContentType: '원격 주소가 파일이 아닌 콘텐츠를 반환했습니다 (오류 페이지일 수 있음), {{ .detail }}'
|
||||
ErrRecordExist: '레코드가 이미 있습니다'
|
||||
ErrRecordNotFound: '레코드를 찾을 수 없습니다'
|
||||
ErrStructTransform: '유형 변환에 실패했습니다: {{ .err }}'
|
||||
ErrNotLogin: '사용자가 로그인하지 않았습니다: {{ .detail }}'
|
||||
ErrPasswordExpired: '현재 비밀번호가 만료되었습니다: {{ .detail }}'
|
||||
ErrNotSupportType: '시스템은 현재 유형 {{ .name }}을 지원하지 않습니다.'
|
||||
ErrProxy: '요청 오류, 노드 상태를 확인하세요: {{ .detail }}'
|
||||
ErrApiConfigStatusInvalid: 'API 인터페이스에 대한 액세스가 금지되었습니다: {{ .detail }}'
|
||||
ErrApiConfigKeyInvalid: 'API 인터페이스 키 오류: {{ .detail }}'
|
||||
ErrApiConfigIPInvalid: 'API 인터페이스를 호출하는 데 사용된 IP가 허용 목록에 없습니다: {{ .detail }}'
|
||||
ErrApiConfigDisable: '이 인터페이스는 API 인터페이스 호출 사용을 금지합니다: {{ .detail }}'
|
||||
ErrApiConfigKeyTimeInvalid: 'API 인터페이스 타임스탬프 오류: {{ .detail }}'
|
||||
ErrShutDown: "명령이 수동으로 종료되었습니다!"
|
||||
|
||||
ErrMinQuickJump: "최소 하나의 빠른 점프 항목을 설정해 주세요!"
|
||||
ErrMaxQuickJump: "최대 네 개의 빠른 점프 항목을 설정할 수 있습니다!"
|
||||
|
||||
#흔한
|
||||
ErrUsernameIsExist: '사용자 이름이 이미 존재합니다'
|
||||
ErrStructTransform: '형 변환 실패: {{ .err }}'
|
||||
ErrNotLogin: '로그인하지 않음: {{ .detail }}'
|
||||
ErrPasswordExpired: '비밀번호 만료: {{ .detail }}'
|
||||
ErrNotSupportType: '지원하지 않는 타입: {{ .name }}'
|
||||
ErrProxy: '요청 실패: {{ .detail }}'
|
||||
ErrApiConfigStatusInvalid: 'API 사용 중지: {{ .detail }}'
|
||||
ErrApiConfigKeyInvalid: 'API 키가 잘못됨: {{ .detail }}'
|
||||
ErrApiConfigIPInvalid: 'IP 허용되지 않음: {{ .detail }}'
|
||||
ErrApiConfigDisable: 'API 접근 금지: {{ .detail }}'
|
||||
ErrApiConfigKeyTimeInvalid: '타임스탬프 오류: {{ .detail }}'
|
||||
ErrShutDown: '명령이 수동으로 중단되었습니다'
|
||||
ErrMinQuickJump: '퀵 액세스를 하나 이상 추가하세요'
|
||||
ErrMaxQuickJump: '퀵 액세스는 최대 4개'
|
||||
ErrUsernameIsExist: '아이디가 이미 존재합니다'
|
||||
ErrNameIsExist: '이름이 이미 존재합니다'
|
||||
ErrDemoEnvironment: '데모 서버, 이 작업은 금지되어 있습니다!'
|
||||
ErrCmdTimeout: '명령 실행 시간이 초과되었습니다!'
|
||||
ErrCmdIllegal: '실행 명령에 잘못된 문자가 있습니다. 수정하여 다시 시도하세요!'
|
||||
ErrPortExist: '{{ .port }} 포트는 이미 {{ .type }} [{{ .name }}]에 의해 사용되고 있습니다.'
|
||||
ErrDemoEnvironment: '데모 환경에서는 불가'
|
||||
ErrCmdTimeout: '명령이 시간 초과했습니다'
|
||||
ErrCmdIllegal: '명령어에 허용되지 않은 문자가 포함됨'
|
||||
ErrPortExist: '포트 {{ .port }}는 {{ .type }} [{{ .name }}]에서 사용 중입니다'
|
||||
TYPE_APP: '응용 프로그램'
|
||||
TYPE_RUNTIME: '런타임 환경'
|
||||
TYPE_DOMAIN: '도메인 이름'
|
||||
@@ -36,45 +35,43 @@ Failed: '실패했습니다'
|
||||
SystemRestart: '시스템 재시작으로 인해 작업이 중단되었습니다'
|
||||
ErrGroupIsDefault: '기본 그룹, 삭제할 수 없습니다'
|
||||
ErrGroupIsInWebsiteUse: '그룹이 다른 웹사이트에서 사용 중이므로 삭제할 수 없습니다.'
|
||||
Decrypt: "복호화"
|
||||
|
||||
#agent
|
||||
ErrAgentAccountBound: '이 계정은 에이전트에 바인딩되어 있어 삭제할 수 없습니다. 확인 후 다시 시도하세요!'
|
||||
ErrAgentAccountUnavailable: '계정 연결 정보를 사용할 수 없습니다. 오류: {{ .err }}. 확인 후 다시 시도하세요!'
|
||||
ErrAgentProviderNotSupported: '이 에이전트 제공자는 지원되지 않습니다. 확인 후 다시 시도하세요!'
|
||||
ErrAgentAccountRequired: '에이전트 계정을 선택한 후 다시 시도하세요.'
|
||||
ErrAgentAccountNotVerified: '계정이 검증되지 않았습니다. 확인 후 다시 시도하세요!'
|
||||
ErrAgentProviderMismatch: '계정 제공자가 일치하지 않습니다. 확인 후 다시 시도하세요!'
|
||||
ErrAgentBaseURLRequired: 'Base URL은 비워 둘 수 없습니다. 확인 후 다시 시도하세요!'
|
||||
ErrAgentApiKeyRequired: 'API Key는 비워 둘 수 없습니다. 확인 후 다시 시도하세요!'
|
||||
ErrAgentComposeRequired: '사용자 정의 compose 내용은 비워 둘 수 없습니다. 확인 후 다시 시도하세요!'
|
||||
ErrAgentIDRequired: '에이전트 ID는 비워 둘 수 없습니다. 확인 후 다시 시도하세요!'
|
||||
ErrAgentAccountIDRequired: '계정 ID는 비워 둘 수 없습니다. 확인 후 다시 시도하세요!'
|
||||
|
||||
#지원
|
||||
Decrypt: '복호화'
|
||||
ErrAgentAccountBound: '계정이 에이전트에 묶여 있습니다'
|
||||
ErrAgentAccountUnavailable: '계정 연결 불가: {{ .err }}'
|
||||
ErrAgentProviderNotSupported: '지원되지 않는 에이전트 공급자'
|
||||
ErrAgentAccountRequired: '먼저 에이전트 계정을 선택하세요'
|
||||
ErrAgentAccountNotVerified: '에이전트 계정 미확인'
|
||||
ErrAgentAccountModelsRequired: '최소 한 개의 모델을 구성해 주세요'
|
||||
ErrAgentAccountSingleInitialModel: '계정 생성 시 초기 모델은 하나만 구성할 수 있습니다'
|
||||
ErrAgentModelNotInAccount: '선택한 모델이 현재 계정에 구성되어 있지 않습니다'
|
||||
ErrAgentModelInUse: '이 모델을 아직 사용 중인 에이전트가 있습니다: {{ .name }}'
|
||||
ErrAgentBaseURLRequired: '기본 URL 필요'
|
||||
ErrAgentApiKeyRequired: 'API 키 필요'
|
||||
ErrAgentComposeRequired: 'Compose 내용 필요'
|
||||
ErrAgentIDRequired: '에이전트 ID 필요'
|
||||
ErrAgentAccountIDRequired: '계정 ID 필요'
|
||||
ErrAgentLimitReached: '커뮤니티 에디션에서는 AI 에이전트를 최대 {{ .max }}개까지 생성할 수 있으며, Professional 에디션으로 업그레이드하면 수량 제한이 없습니다'
|
||||
Localhost: '로컬 머신'
|
||||
ErrBackupInUsed: '백업 계정이 예약된 작업에 사용되었으므로 삭제할 수 없습니다.'
|
||||
ErrBackupCheck: '백업 계정 테스트 연결에 실패했습니다 {{ .err }}'
|
||||
ErrBackupLocalDelete: '로컬 서버 백업 계정 삭제는 아직 지원되지 않습니다.'
|
||||
ErrBackupLocalCreate: '로컬 서버 백업 계정 생성은 아직 지원되지 않습니다.'
|
||||
|
||||
#앱
|
||||
ErrPortInUsed: '{{ .detail }} 포트가 이미 사용 중입니다!'
|
||||
ErrBackupInUsed: '백업 계정이 예약에 사용 중'
|
||||
ErrBackupCheck: '연결 테스트 실패: {{ .err }}'
|
||||
ErrBackupLocalDelete: '로컬 백업은 삭제할 수 없습니다'
|
||||
ErrBackupLocalCreate: '로컬 백업은 만들 수 없습니다'
|
||||
ErrPortInUsed: '{{ .detail }} 포트가 이미 사용 중입니다'
|
||||
ErrAppLimit: '설치된 애플리케이션 수가 한도를 초과했습니다'
|
||||
ErrNotInstall: '응용 프로그램이 설치되지 않았습니다'
|
||||
ErrPortInOtherApp: '{{ .port }} 포트는 이미 {{ .apps }} 애플리케이션에 의해 사용되고 있습니다!'
|
||||
ErrDbUserNotValid: '기존 데이터베이스, 사용자 이름 및 비밀번호가 일치하지 않습니다!'
|
||||
ErrUpdateBuWebsite: '응용 프로그램이 성공적으로 업데이트되었지만, 웹사이트 구성 파일 수정에 실패했습니다. 구성을 확인하세요! '
|
||||
Err1PanelNetworkFailed: '기본 컨테이너 네트워크 생성에 실패했습니다! {{ .세부 사항 }}'
|
||||
ErrFileParse: '응용 프로그램 docker-compose 파일 구문 분석에 실패했습니다!'
|
||||
ErrPortInOtherApp: '{{ .port }} 포트는 이미 {{ .apps }} 애플리케이션에 의해 사용되고 있습니다'
|
||||
ErrDbUserNotValid: '기존 데이터베이스, 사용자 이름 및 비밀번호가 일치하지 않습니다'
|
||||
ErrUpdateBuWebsite: '응용 프로그램이 성공적으로 업데이트되었지만, 웹사이트 구성 파일 수정에 실패했습니다. 구성을 확인하세요'
|
||||
Err1PanelNetworkFailed: '기본 컨테이너 네트워크 생성에 실패했습니다 {{ .세부 사항 }}'
|
||||
ErrFileParse: '응용 프로그램 docker-compose 파일 구문 분석에 실패했습니다'
|
||||
ErrInstallDirNotFound: '설치 디렉토리가 존재하지 않습니다. 제거를 원하실 경우 강제 제거 를 선택해주세요.'
|
||||
AppStoreIsUpToDate: '앱 스토어가 이미 최신 버전입니다'
|
||||
LocalAppVersionNull: '{{ .name }} 애플리케이션이 버전과 동기화되지 않았습니다! 앱 목록에 추가할 수 없습니다.'
|
||||
LocalAppVersionErr: '{{ .name }} 동기화 버전 {{ .version }}이 실패했습니다! {{ .err }}'
|
||||
LocalAppVersionNull: '{{ .name }} 애플리케이션이 버전과 동기화되지 않았습니다 앱 목록에 추가할 수 없습니다.'
|
||||
LocalAppVersionErr: '{{ .name }} 동기화 버전 {{ .version }}이 실패했습니다 {{ .err }}'
|
||||
ErrFileNotFound: '{{ .name }} 파일이 존재하지 않습니다'
|
||||
ErrFileParseApp: '{{ .name }} 파일 구문 분석에 실패했습니다 {{ .err }}'
|
||||
ErrAppDirNull: '버전 폴더가 존재하지 않습니다'
|
||||
LocalAppErr: '애플리케이션 {{ .name }} 동기화에 실패했습니다! {{ .err }}'
|
||||
LocalAppErr: '애플리케이션 {{ .name }} 동기화에 실패했습니다 {{ .err }}'
|
||||
ErrContainerName: '컨테이너 이름이 이미 존재합니다'
|
||||
ErrCreateHttpClient: '요청 {{ .err }}을(를) 생성하지 못했습니다.'
|
||||
ErrHttpReqTimeOut: '요청 시간이 초과되었습니다 {{ .err }}'
|
||||
@@ -87,64 +84,58 @@ ErrAppBackup: '{{ .name }} 애플리케이션 백업에 실패했습니다 {{ .e
|
||||
ErrVersionTooLow: '현재 1Panel 버전이 너무 낮아 App Store를 업데이트할 수 없습니다. 작동하시기 전에 버전을 업그레이드하세요.'
|
||||
ErrAppNameExist: '응용 프로그램 이름이 이미 존재합니다'
|
||||
AppStoreIsSyncing: 'App Store가 동기화 중입니다. 나중에 다시 시도하세요.'
|
||||
ErrGetCompose: 'docker-compose.yml 파일을 가져오지 못했습니다! {{ .detail }}'
|
||||
ErrGetCompose: 'docker-compose.yml 파일을 가져오지 못했습니다 {{ .detail }}'
|
||||
ErrAppWarn: '비정상적인 상태입니다. 로그를 확인해 주세요.'
|
||||
ErrAppParamKey: '매개변수 {{ .name }} 필드가 비정상입니다.'
|
||||
ErrAppUpgrade: '애플리케이션 {{ .name }} 업그레이드에 실패했습니다 {{ .err }}'
|
||||
AppRecover: '롤백 애플리케이션 {{ .name }}'
|
||||
PullImageStart: '이미지 [{{ .name }}] 가져오기 시작'
|
||||
PullImageSuccess: '이미지 가져오기 성공'
|
||||
AppStoreIsLastVersion: '앱스토어가 이미 최신 버전입니다'
|
||||
AppStoreSyncSuccess: '앱스토어 동기화 성공'
|
||||
SyncAppDetail: '애플리케이션 구성 동기화'
|
||||
AppVersionNotMatch: '{{ .name }} 애플리케이션에 더 높은 1Panel 버전이 필요하므로 동기화를 건너뜁니다.'
|
||||
MoveSiteDir: "웹사이트 디렉토리 변경이 감지되었습니다. OpenResty 웹사이트 디렉토리를 {{ .name }} 로 마이그레이션해야 합니다"
|
||||
MoveSiteDir: '웹사이트 디렉토리 변경이 감지되었습니다. OpenResty 웹사이트 디렉토리를 {{ .name }} 로 마이그레이션해야 합니다'
|
||||
MoveSiteDirSuccess: '웹사이트 디렉토리 마이그레이션 성공'
|
||||
DeleteRuntimePHP: 'PHP 런타임 삭제'
|
||||
CustomAppStoreFileValid: '앱 스토어 패키지는 .tar.gz 형식이어야 합니다.'
|
||||
CustomAppStoreFileValid: '앱 스토어 패키지는.tar.gz 형식이어야 합니다.'
|
||||
PullImageTimeout: '이미지 가져오기 시간 초과, 이미지 가속을 높이거나 다른 이미지 가속으로 변경해 보세요.'
|
||||
ErrAppIsDown: '{{ .name }} 애플리케이션 상태가 비정상적입니다. 확인해 주세요'
|
||||
ErrCustomApps: '설치된 애플리케이션이 있습니다. 먼저 제거해 주세요'
|
||||
ErrCustomRuntimes: '설치된 런타임 환경이 있습니다. 먼저 삭제해 주세요'
|
||||
ErrAppVersionDeprecated: "{{ .name }} 응용 프로그램은 현재 1Panel 버전과 호환되지 않아 건너뛰었습니다"
|
||||
ErrDockerFailed: "Docker의 상태가 비정상입니다. 서비스 상태를 확인하세요"
|
||||
ErrDockerComposeCmdNotFound: "Docker Compose 명령이 없습니다. 호스트 머신에 먼저 이 명령을 설치하세요"
|
||||
UseExistImage: '이미지 가져오기 실패, 기존 이미지 사용'
|
||||
|
||||
#ssh
|
||||
ExportIP: "로그인 IP"
|
||||
ExportArea: "위치"
|
||||
ExportPort: "포트"
|
||||
ExportAuthMode: "로그인 방식"
|
||||
ExportUser: "사용자"
|
||||
ExportStatus: "로그인 상태"
|
||||
ExportDate: "시간"
|
||||
|
||||
#파일
|
||||
ErrAppVersionDeprecated: '{{ .name }} 응용 프로그램은 현재 1Panel 버전과 호환되지 않아 건너뛰었습니다'
|
||||
ErrDockerFailed: 'Docker의 상태가 비정상입니다. 서비스 상태를 확인하세요'
|
||||
ErrDockerComposeCmdNotFound: 'Docker Compose 명령이 없습니다. 호스트 머신에 먼저 이 명령을 설치하세요'
|
||||
UseExistImage: '이미지가 이미 존재하여 기존 이미지를 사용합니다'
|
||||
ErrDatabaseNotFound: '데이터베이스 {{ .name }} 이(가) 존재하지 않습니다'
|
||||
ExportIP: '로그인 IP'
|
||||
ExportArea: '위치'
|
||||
ExportPort: '포트'
|
||||
ExportAuthMode: '로그인 방식'
|
||||
ExportUser: '사용자'
|
||||
ExportStatus: '로그인 상태'
|
||||
ExportDate: '시간'
|
||||
ErrFileCanNotRead: '이 파일은 미리보기를 지원하지 않습니다'
|
||||
ErrFileToLarge: '파일이 10M보다 커서 열 수 없습니다'
|
||||
ErrPathNotFound: '디렉토리가 존재하지 않습니다'
|
||||
ErrMovePathFailed: '대상 경로에는 원래 경로가 포함될 수 없습니다!'
|
||||
ErrLinkPathNotFound: '대상 경로가 존재하지 않습니다!'
|
||||
ErrFileIsExist: '파일이나 폴더가 이미 존재합니다!'
|
||||
ErrMovePathFailed: '대상 경로에는 원래 경로가 포함될 수 없습니다'
|
||||
ErrLinkPathNotFound: '대상 경로가 존재하지 않습니다'
|
||||
ErrFileIsExist: '파일이나 폴더가 이미 존재합니다'
|
||||
ErrFileUpload: '{{ .name }}이 파일 {{ .detail }}을 업로드하지 못했습니다.'
|
||||
ErrFileDownloadDir: '다운로드 폴더가 지원되지 않습니다'
|
||||
ErrCmdNotFound: '{{ .name}} 명령이 존재하지 않습니다. 먼저 호스트에 이 명령을 설치하세요'
|
||||
ErrCmdNotFound: '{{ .name }} 명령이 존재하지 않습니다. 먼저 호스트에 이 명령을 설치하세요'
|
||||
ErrSourcePathNotFound: '소스 디렉토리가 존재하지 않습니다'
|
||||
ErrFavoriteExist: '이 경로는 이미 즐겨찾기되었습니다'
|
||||
ErrInvalidChar: '불법 문자는 허용되지 않습니다'
|
||||
ErrPathNotDelete: '선택한 디렉토리를 삭제할 수 없습니다'
|
||||
ErrLogFileToLarge: "로그 파일이 500MB를 초과하여 열 수 없습니다"
|
||||
FileDropFailed: "파일 {{ .name }} 정리 실패: {{ .err }}"
|
||||
FileDropSuccess: "파일 {{ .name }} 정리 성공, {{ .count }}개 파일 삭제, {{ .size }} 디스크 공간 확보"
|
||||
FileDropSum: "파일 정리 완료, 총 {{ .count }}개 파일 삭제, 총 {{ .size }} 디스크 공간 확보"
|
||||
ErrBadDecrypt: "압축 패키지 암호 해제 비밀번호 오류, 압축 해제 실패, 확인 후 다시 시도하세요!"
|
||||
|
||||
#웹사이트
|
||||
ErrLogFileToLarge: '로그 파일이 500MB를 초과하여 열 수 없습니다'
|
||||
FileDropFailed: '파일 {{ .name }} 정리 실패: {{ .err }}'
|
||||
FileDropSuccess: '파일 {{ .name }} 정리 성공, {{ .count }}개 파일 삭제, {{ .size }} 디스크 공간 확보'
|
||||
FileDropSum: '파일 정리 완료, 총 {{ .count }}개 파일 삭제, 총 {{ .size }} 디스크 공간 확보'
|
||||
ErrBadDecrypt: '압축 패키지 암호 해제 비밀번호 오류, 압축 해제 실패, 확인 후 다시 시도하세요'
|
||||
ErrAliasIsExist: '별칭이 이미 존재합니다'
|
||||
ErrBackupMatch: '백업 파일이 현재 웹사이트 데이터 중 일부 {{ .detail }}와 일치하지 않습니다.'
|
||||
ErrBackupExist: '백업 파일에 있는 소스 데이터의 해당 부분이 존재하지 않습니다 {{ .detail }}'
|
||||
ErrPHPResource: '로컬 운영 환경이 전환을 지원하지 않습니다! '
|
||||
ErrPHPResource: '로컬 운영 환경이 전환을 지원하지 않습니다'
|
||||
ErrPathPermission: '인덱스 디렉토리에서 1000:1000이 아닌 권한을 가진 폴더가 감지되었습니다. 이로 인해 웹사이트에서 액세스 거부 오류가 발생할 수 있습니다. 위의 저장 버튼을 클릭해주세요.'
|
||||
ErrDomainIsUsed: '[{{ .name }}] 웹사이트에서 이미 도메인 이름을 사용하고 있습니다.'
|
||||
ErrDomainFormat: '{{ .name }} 도메인 이름 형식이 올바르지 않습니다'
|
||||
@@ -152,11 +143,11 @@ ErrDefaultAlias: '기본값은 예약된 코드입니다. 다른 코드를 사
|
||||
ErrParentWebsite: '먼저 하위 사이트 {{ .name }}을 삭제해야 합니다.'
|
||||
ErrBuildDirNotFound: '빌드 디렉토리가 존재하지 않습니다'
|
||||
ErrImageNotExist: '운영 환경 {{ .name }} 이미지가 존재하지 않습니다. 운영 환경을 다시 편집하세요.'
|
||||
ErrProxyIsUsed: "로드 밸런싱이 역방향 프록시에 의해 사용되었으므로 삭제할 수 없습니다"
|
||||
ErrSSLValid: '인증서 파일에 문제가 있습니다. 인증서 상태를 확인하세요!'
|
||||
ErrWebsiteDir: "웹사이트 디렉토리 내의 디렉토리를 선택하세요."
|
||||
ErrComposerFileNotFound: "composer.json 파일이 존재하지 않습니다"
|
||||
ErrRuntimeNoPort: "런타임 환경에 포트가 설정되지 않았습니다. 먼저 런타임 환경을 편집하세요."
|
||||
ErrProxyIsUsed: '로드 밸런싱이 역방향 프록시에 의해 사용되었으므로 삭제할 수 없습니다'
|
||||
ErrSSLValid: '인증서 파일에 문제가 있습니다. 인증서 상태를 확인하세요'
|
||||
ErrWebsiteDir: '웹사이트 디렉토리 내의 디렉토리를 선택하세요.'
|
||||
ErrComposerFileNotFound: 'composer.json 파일이 존재하지 않습니다'
|
||||
ErrRuntimeNoPort: '런타임 환경에 포트가 설정되지 않았습니다. 먼저 런타임 환경을 편집하세요.'
|
||||
Status: '상태'
|
||||
start: '시작'
|
||||
stop: '중지'
|
||||
@@ -166,14 +157,12 @@ pause: '일시 정지'
|
||||
unpause: '재개'
|
||||
remove: '삭제'
|
||||
delete: '삭제'
|
||||
ErrDefaultWebsite: '기본 웹사이트가 이미 설정되었습니다. 설정하기 전에 취소하세요!'
|
||||
ErrDefaultWebsite: '기본 웹사이트가 이미 설정되었습니다. 설정하기 전에 취소하세요'
|
||||
SSL: '인증서'
|
||||
Set: '설정'
|
||||
|
||||
#SSL인증
|
||||
ErrSSLCannotDelete: '{{ .name }} 인증서는 웹사이트에서 사용 중이므로 삭제할 수 없습니다.'
|
||||
ErrAccountCannotDelete: '계정이 인증서와 연결되어 있어 삭제할 수 없습니다.'
|
||||
ErrSSLApply: '인증서 갱신이 성공했으나, openresty 재로드에 실패했습니다. 구성을 확인해 주세요!'
|
||||
ErrSSLApply: '인증서 갱신이 성공했으나, openresty 재로드에 실패했습니다. 구성을 확인해 주세요'
|
||||
ErrEmailIsExist: '사서함이 이미 존재합니다'
|
||||
ErrSSLKeyNotFound: '개인 키 파일이 존재하지 않습니다'
|
||||
ErrSSLCertificateNotFound: '인증서 파일이 존재하지 않습니다'
|
||||
@@ -181,12 +170,12 @@ ErrSSLKeyFormat: '개인 키 파일 검증에 실패했습니다'
|
||||
ErrSSLCertificateFormat: '인증서 파일 형식이 올바르지 않습니다. pem 형식을 사용하세요.'
|
||||
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid 또는 EabHmacKey는 비워둘 수 없습니다'
|
||||
ErrOpenrestyNotFound: 'Http 모드를 사용하려면 먼저 Openresty를 설치해야 합니다.'
|
||||
ApplySSLStart: '인증서 신청 시작, 도메인 이름 [{{ .domain }}] 신청 방법 [{{ .type }}] '
|
||||
ApplySSLStart: '인증서 신청 시작, 도메인 이름 [{{ .domain }}] 신청 방법 [{{ .type }}]'
|
||||
dnsAccount: 'DNS 자동'
|
||||
dnsManual: 'DNS 매뉴얼'
|
||||
http: 'HTTP'
|
||||
ApplySSLFailed: '[{{ .domain }}] 인증서 신청이 실패했습니다. {{ .detail }} '
|
||||
ApplySSLSuccess: '[{{ .domain }}] 인증서 신청이 성공적으로 완료되었습니다! ! '
|
||||
ApplySSLFailed: '[{{ .domain }}] 인증서 신청이 실패했습니다. {{ .detail }}'
|
||||
ApplySSLSuccess: '[{{ .domain }}] 인증서 신청이 성공적으로 완료되었습니다'
|
||||
DNSAccountName: 'DNS 계정 [{{ .name }}] 공급업체 [{{ .type }}]'
|
||||
PushDirLog: '인증서가 [{{ .path }}] {{ .status }} 디렉토리로 푸시되었습니다.'
|
||||
ErrDeleteCAWithSSL: '현재 조직에는 발급된 인증서가 있어 삭제할 수 없습니다.'
|
||||
@@ -201,97 +190,109 @@ ExecShellSuccess: '스크립트 실행 성공'
|
||||
StartUpdateSystemSSL: '시스템 인증서 업데이트 시작'
|
||||
UpdateSystemSSLSuccess: '시스템 인증서 업데이트가 성공적으로 완료되었습니다.'
|
||||
ErrWildcardDomain: 'HTTP 모드에서 와일드카드 도메인 이름 인증서를 신청할 수 없습니다'
|
||||
ErrApplySSLCanNotDelete: "신청 중인 인증서 {{.name}}는 삭제할 수 없습니다. 나중에 다시 시도해 주세요."
|
||||
StartPushSSLToNode: "인증서를 노드로 푸시 시작"
|
||||
PushSSLToNodeFailed: "노드로 인증서 푸시 실패: {{ .err }}"
|
||||
PushSSLToNodeSuccess: "노드로 인증서 푸시 성공"
|
||||
|
||||
#마이SQL
|
||||
ErrApplySSLCanNotDelete: '신청 중인 인증서 {{ .name }}는 삭제할 수 없습니다. 나중에 재시도'
|
||||
StartPushSSLToNode: '인증서를 노드로 푸시 시작'
|
||||
PushSSLToNodeFailed: '노드로 인증서 푸시 실패: {{ .err }}'
|
||||
PushSSLToNodeSuccess: '노드로 인증서 푸시 성공'
|
||||
ErrUserIsExist: '현재 사용자가 이미 존재합니다. 다시 입력하세요'
|
||||
ErrDatabaseIsExist: '현재 데이터베이스가 이미 존재합니다. 다시 입력하세요'
|
||||
ErrExecTimeOut: 'SQL 실행 시간이 초과되었습니다. 데이터베이스를 확인하십시오.'
|
||||
ErrRemoteExist: '이 이름을 가진 원격 데이터베이스가 이미 존재합니다. 수정하고 다시 시도하세요'
|
||||
ErrLocalExist: '이름이 로컬 데이터베이스에 이미 존재합니다. 이름을 수정하고 다시 시도하세요'
|
||||
RemoteBackup: "원격 데이터베이스를 백업하려면 먼저 이미지 {{ .name }}을(를) 사용하여 로컬 컨테이너 데이터베이스 서비스를 시작해야 합니다. 잠시만 기다려 주세요..."
|
||||
RemoteRecover: "원격 데이터베이스를 복원하려면 먼저 이미지 {{ .name }}을(를) 사용하여 로컬 컨테이너 데이터베이스 서비스를 시작해야 합니다. 잠시만 기다려 주세요..."
|
||||
Arg: "인수"
|
||||
|
||||
#레디스
|
||||
RemoteBackup: '원격 데이터베이스를 백업하려면 먼저 이미지 {{ .name }}을(를) 사용하여 로컬 컨테이너 데이터베이스 서비스를 시작해야 합니다. 잠시만 기다려 주세요...'
|
||||
RemoteRecover: '원격 데이터베이스를 복원하려면 먼저 이미지 {{ .name }}을(를) 사용하여 로컬 컨테이너 데이터베이스 서비스를 시작해야 합니다. 잠시만 기다려 주세요...'
|
||||
Arg: '인수'
|
||||
ErrTypeOfRedis: '복구 파일 유형이 현재 지속성 방법과 일치하지 않습니다. 수정하고 다시 시도하세요'
|
||||
|
||||
#컨테이너
|
||||
ErrInUsed: "{{ .detail }}(이)가 사용 중이어서 삭제할 수 없습니다!"
|
||||
ErrObjectInUsed: "이 객체는 사용 중이어서 삭제할 수 없습니다!"
|
||||
ErrObjectBeDependent: "이 이미지는 다른 이미지에 의존되어 있어 삭제할 수 없습니다!"
|
||||
ErrPortRules: '포트 번호가 일치하지 않습니다. 다시 입력하세요!'
|
||||
ErrInUsed: '{{ .detail }}(이)가 사용 중이어서 삭제할 수 없습니다'
|
||||
ErrObjectInUsed: '이 객체는 사용 중이어서 삭제할 수 없습니다'
|
||||
ErrObjectBeDependent: '이 이미지는 다른 이미지에 의존되어 있어 삭제할 수 없습니다'
|
||||
ErrPortRules: '포트 번호가 일치하지 않습니다. 다시 입력하세요'
|
||||
ErrPgImagePull: '이미지 풀링 시간이 초과되었습니다. 이미지 가속을 구성하거나 {{ .name }} 이미지를 수동으로 풀링한 다음 다시 시도하세요.'
|
||||
PruneHelper: "이번 정리 {{ .name }}가 디스크 공간 {{ .size }}을(를) 확보했습니다"
|
||||
ImageRemoveHelper: "이미지 {{ .name }} 삭제, {{ .size }} 디스크 공간 확보"
|
||||
BuildCache: "빌드 캐시"
|
||||
Volume: "스토리지 볼륨"
|
||||
Network: "네트워크"
|
||||
PruneStart: "정리 중입니다. 잠시만 기다려주세요..."
|
||||
|
||||
#실행 시간
|
||||
ErrFileNotExist: '{{ .detail }} 파일이 존재하지 않습니다! 소스 파일의 무결성을 확인하세요!'
|
||||
PruneHelper: '이번 정리 {{ .name }}가 디스크 공간 {{ .size }}을(를) 확보했습니다'
|
||||
ImageRemoveHelper: '이미지 {{ .name }} 삭제, {{ .size }} 디스크 공간 확보'
|
||||
BuildCache: '빌드 캐시'
|
||||
Volume: '스토리지 볼륨'
|
||||
Network: '네트워크'
|
||||
PruneStart: '정리 중입니다. 잠시만 기다려주세요...'
|
||||
ContainerBackupPrepare: '컨테이너 백업 작업 공간 준비'
|
||||
ContainerBackupStop: '백업 전에 컨테이너 중지'
|
||||
ContainerBackupInspect: '컨테이너 inspect 및 네트워크 정보 백업'
|
||||
ContainerBackupMounts: '컨테이너 마운트 데이터 백업'
|
||||
ContainerBackupMeta: '컨테이너 백업 메타데이터 작성'
|
||||
ContainerBackupStart: '백업 후 컨테이너 시작'
|
||||
ContainerRecoverPrepare: '컨테이너 복원 작업 공간 준비'
|
||||
ContainerRecoverExtract: '컨테이너 백업 파일 압축 해제'
|
||||
ContainerRecoverParse: '컨테이너 백업 메타데이터 로드'
|
||||
ContainerRecoverCreate: '컨테이너 재생성'
|
||||
ContainerRecoverMounts: '컨테이너 마운트 데이터 복원'
|
||||
ContainerRecoverStart: '복원된 컨테이너 시작'
|
||||
ContainerRecoverCleanup: '컨테이너 복원 작업 공간 정리'
|
||||
ComposeBackupPrepare: 'Compose 백업 작업 공간 준비'
|
||||
ComposeBackupStop: '백업 전에 Compose 중지'
|
||||
ComposeBackupFiles: 'Compose 파일 백업'
|
||||
ComposeBackupContainers: 'Compose 컨테이너 백업'
|
||||
ComposeBackupMeta: 'Compose 백업 메타데이터 작성'
|
||||
ComposeBackupStart: '백업 후 Compose 시작'
|
||||
ComposeBackupCleanup: 'Compose 백업 작업 공간 정리'
|
||||
ComposeRecoverPrepare: 'Compose 복원 작업 공간 준비'
|
||||
ComposeRecoverExtract: 'Compose 백업 파일 압축 해제'
|
||||
ComposeRecoverMeta: 'Compose 백업 메타데이터 로드'
|
||||
ComposeRecoverMetaLogName: 'Compose 복원 메타: 이름={{ .name }}'
|
||||
ComposeRecoverMetaLogPath: 'Compose 복원 메타: 백업경로={{ .backupPath }} 대상경로={{ .targetDir }}'
|
||||
ComposeRecoverMetaLogCount: 'Compose 복원 메타: 파일={{ .files }} 컨테이너={{ .containers }}'
|
||||
ComposeRecoverFiles: 'Compose 파일 복원'
|
||||
ComposeRecoverContainers: 'Compose 컨테이너 복원'
|
||||
ComposeRecoverRecord: 'Compose 기록 저장'
|
||||
ComposeRecoverCleanup: 'Compose 복원 작업 공간 정리'
|
||||
ErrFileNotExist: '{{ .detail }} 파일이 존재하지 않습니다 소스 파일의 무결성을 확인하세요'
|
||||
ErrImageBuildErr: '이미지 빌드 실패'
|
||||
ErrImageExist: "이미지가 이미 존재합니다! 이미지 이름을 수정하세요."
|
||||
ErrImageExist: '이미지가 이미 존재합니다 이미지 이름을 수정하세요.'
|
||||
ErrDelWithWebsite: '운영 환경이 이미 웹사이트와 연결되어 있어 삭제할 수 없습니다'
|
||||
ErrRuntimeStart: '시작 실패'
|
||||
ErrPackageJsonNotFound: 'package.json 파일이 존재하지 않습니다'
|
||||
ErrScriptsNotFound: 'package.json에서 스크립트 구성 항목을 찾을 수 없습니다.'
|
||||
ErrContainerNameNotFound: '컨테이너 이름을 가져올 수 없습니다. .env 파일을 확인하세요.'
|
||||
ErrNodeModulesNotFound: 'node_modules 폴더가 없습니다! 런타임 환경을 편집하거나 런타임 환경이 성공적으로 시작될 때까지 기다리십시오'
|
||||
ErrContainerNameNotFound: '컨테이너 이름을 가져올 수 없습니다..env 파일을 확인하세요.'
|
||||
ErrNodeModulesNotFound: 'node_modules 폴더가 없습니다 런타임 환경을 편집하거나 런타임 환경이 성공적으로 시작될 때까지 기다리십시오'
|
||||
ErrContainerNameIsNull: '컨테이너 이름이 존재하지 않습니다'
|
||||
ErrPHPPortIsDefault: "9000 포트는 기본 포트입니다. 수정 후 다시 시도하세요"
|
||||
ErrPHPRuntimePortFailed: "포트 {{ .name }} 는 현재 런타임 환경에서 이미 사용 중입니다. 수정 후 다시 시도하세요"
|
||||
|
||||
#도구
|
||||
ErrPHPPortIsDefault: '9000 포트는 기본 포트입니다. 수정 후 다시 시도하세요'
|
||||
ErrPHPRuntimePortFailed: '포트 {{ .name }} 는 현재 런타임 환경에서 이미 사용 중입니다. 수정 후 다시 시도하세요'
|
||||
ErrConfigNotFound: '구성 파일이 존재하지 않습니다'
|
||||
ErrConfigParse: '구성 파일 형식이 올바르지 않습니다'
|
||||
ErrConfigIsNull: '구성 파일은 비어 있을 수 없습니다'
|
||||
ErrConfigDirNotFound: '실행 디렉토리가 존재하지 않습니다'
|
||||
ErrConfigAlreadyExist: '같은 이름의 구성 파일이 이미 존재합니다'
|
||||
ErrUserFindErr: '사용자 {{ .name }} 검색에 실패했습니다 {{ .err }}'
|
||||
|
||||
#크론잡
|
||||
CutWebsiteLogSuccess: '{{ .name }} 웹사이트 로그가 성공적으로 잘렸습니다. 백업 경로 {{ .path }}'
|
||||
HandleShell: '스크립트 {{ .name }} 실행'
|
||||
HandleCurl: "URL {{ .name }} 접근"
|
||||
HandleCurl: 'URL {{ .name }} 접근'
|
||||
HandleNtpSync: '시스템 시간 동기화'
|
||||
HandleSystemClean: '시스템 캐시 정리'
|
||||
SystemLog: '시스템 로그'
|
||||
CutWebsiteLog: '웹사이트 로그 회전'
|
||||
FileOrDir: '디렉터리 / 파일'
|
||||
UploadFile: '백업 파일 {{ .file }} 을(를) {{ .backup }}(으)로 업로드 중'
|
||||
Upload: "업로드"
|
||||
IgnoreBackupErr: "백업 실패, 오류: {{ .detail }}, 이 오류를 무시합니다..."
|
||||
IgnoreUploadErr: "업로드 실패, 오류: {{ .detail }}, 이 오류를 무시합니다..."
|
||||
LoadBackupFailed: "백업 계정 연결 획득 실패, 오류: {{ .detail }}"
|
||||
InExecuting: "현재 작업이 실행 중입니다. 중복 실행하지 마세요!"
|
||||
NoSuchResource: "데이터베이스에서 백업 내용을 찾을 수 없어 건너뜁니다..."
|
||||
CleanLog: "로그 정리"
|
||||
CleanLogByName: "{{.name}} 로그 정리"
|
||||
WafIpGroupNotFound: "WAF IP 그룹을 찾을 수 없습니다. [고급 기능 - WAF - 블랙/화이트 목록 - IP 그룹]으로 이동하여 원격 다운로드 방식으로 IP 그룹을 추가하십시오."
|
||||
|
||||
#도구상자
|
||||
ErrNotExistUser: '현재 사용자가 존재하지 않습니다. 수정한 후 다시 시도하세요!'
|
||||
ErrBanAction: '설정에 실패했습니다. 현재 {{ .name }} 서비스를 사용할 수 없습니다. 확인하고 다시 시도하세요!'
|
||||
ErrClamdscanNotFound: 'clamdscan 명령이 감지되지 않았습니다. 설명서를 참조하여 설치하세요!'
|
||||
TaskSwapSet: "Swap 설정"
|
||||
SetSwap: "Swap 설정 {{ .path }} - {{ .size }}"
|
||||
CreateSwap: "Swap 파일 생성"
|
||||
FormatSwap: "Swap 파일 포맷"
|
||||
EnableSwap: "Swap 활성화"
|
||||
|
||||
#tamper
|
||||
CleanTamperSetting: "히스토리 보호 설정 정리"
|
||||
SetTamperWithRules: "규칙에 따라 디렉토리 보호 시작"
|
||||
ProtectDir: "디렉토리 보호 {{ .name }}"
|
||||
IgnoreFile: "파일 보호 {{ .name }}"
|
||||
TamperSetSuccessful: "보호 설정 완료, 다음 디렉토리 모니터링 시작:"
|
||||
|
||||
#와프
|
||||
Upload: '업로드'
|
||||
IgnoreBackupErr: '백업 실패, 오류: {{ .detail }}, 이 오류를 무시합니다...'
|
||||
IgnoreUploadErr: '업로드 실패, 오류: {{ .detail }}, 이 오류를 무시합니다...'
|
||||
LoadBackupFailed: '백업 계정 연결 획득 실패, 오류: {{ .detail }}'
|
||||
InExecuting: '현재 작업이 실행 중입니다. 중복 실행하지 마세요'
|
||||
NoSuchResource: '데이터베이스에서 백업 내용을 찾을 수 없어 건너뜁니다...'
|
||||
CleanLog: '로그 정리'
|
||||
CleanLogByName: '{{ .name }} 로그 정리'
|
||||
WafIpGroupNotFound: 'WAF IP 그룹을 찾을 수 없습니다. [고급 기능 - WAF - 블랙/화이트 목록 - IP 그룹]으로 이동하여 원격 다운로드 방식으로 IP 그룹을 추가하십시오.'
|
||||
ErrNotExistUser: '현재 사용자가 존재하지 않습니다. 수정한 후 다시 시도하세요'
|
||||
ErrBanAction: '설정에 실패했습니다. 현재 {{ .name }} 서비스를 사용할 수 없습니다. 확인하고 다시 시도하세요'
|
||||
ErrClamdscanNotFound: 'clamdscan 명령이 감지되지 않았습니다. 설명서를 참조하여 설치하세요'
|
||||
TaskSwapSet: 'Swap 설정'
|
||||
SetSwap: 'Swap 설정 {{ .path }} - {{ .size }}'
|
||||
CreateSwap: 'Swap 파일 생성'
|
||||
FormatSwap: 'Swap 파일 포맷'
|
||||
EnableSwap: 'Swap 활성화'
|
||||
CleanTamperSetting: '히스토리 보호 설정 정리'
|
||||
SetTamperWithRules: '규칙에 따라 디렉토리 보호 시작'
|
||||
ProtectDir: '디렉토리 보호 {{ .name }}'
|
||||
IgnoreFile: '파일 보호 {{ .name }}'
|
||||
TamperSetSuccessful: '보호 설정 완료, 다음 디렉토리 모니터링 시작:'
|
||||
ErrScope: '이 구성을 수정하는 것은 지원되지 않습니다'
|
||||
ErrStateChange: '상태 변경에 실패했습니다'
|
||||
ErrRuleExist: '규칙이 이미 존재합니다'
|
||||
@@ -299,7 +300,7 @@ ErrRuleNotExist: '규칙이 존재하지 않습니다'
|
||||
ErrParseIP: '잘못된 IP 형식'
|
||||
ErrDefaultIP: '기본값은 예약된 이름입니다. 다른 이름으로 변경해 주세요'
|
||||
ErrGroupInUse: 'IP 그룹이 블랙리스트/화이트리스트에 사용 중이므로 삭제할 수 없습니다.'
|
||||
ErrIPGroupAclUse: "IP 그룹은 웹사이트 {{ .name }} 의 사용자 정의 규칙에 사용되므로 삭제할 수 없습니다"
|
||||
ErrIPGroupAclUse: 'IP 그룹은 웹사이트 {{ .name }} 의 사용자 정의 규칙에 사용되므로 삭제할 수 없습니다'
|
||||
ErrGroupExist: 'IP 그룹 이름이 이미 존재합니다'
|
||||
ErrIPRange: '잘못된 IP 범위'
|
||||
ErrIPExist: 'IP가 이미 존재합니다'
|
||||
@@ -310,10 +311,10 @@ xss: 'XSS'
|
||||
phpExec: 'PHP 스크립트 실행'
|
||||
oneWordTrojan: '한 단어 트로이'
|
||||
appFilter: '위험한 디렉토리 필터링 적용'
|
||||
webshell: '웹쉘'
|
||||
webShell: '웹쉘'
|
||||
args: '매개변수 규칙'
|
||||
protocolFilter: '프로토콜 필터링'
|
||||
javaFileter: 'Java 위험 파일 필터'
|
||||
javaFilter: 'Java 위험 파일 필터'
|
||||
scannerFilter: '스캐너 필터'
|
||||
escapeFilter: '탈출 필터'
|
||||
customRule: '사용자 정의 규칙'
|
||||
@@ -343,10 +344,9 @@ ErrDBNotExist: '데이터베이스가 존재하지 않습니다'
|
||||
allow: '허용하다'
|
||||
deny: '거부하다'
|
||||
OpenrestyNotFound: 'Openresty가 설치되지 않았습니다'
|
||||
remoteIpIsNull: "IP 목록이 비어 있습니다"
|
||||
OpenrestyVersionErr: "Openresty 버전이 너무 낮습니다. Openresty를 1.27.1.2-2-2-focal로 업그레이드하세요"
|
||||
|
||||
#일
|
||||
remoteIpIsNull: 'IP 목록이 비어 있습니다'
|
||||
OpenrestyVersionErr: 'Openresty 버전이 너무 낮습니다. Openresty를 1.27.1.2-2-2-focal로 업그레이드하세요'
|
||||
ErrFileTooLarge: '파일이 1MB를 초과하여 업로드할 수 없습니다'
|
||||
TaskStart: '[START] {{ .name }} 작업이 시작됩니다.'
|
||||
TaskEnd: '{{ .name }} 작업이 완료되었습니다 [완료]'
|
||||
TaskFailed: '{{ .name }} 작업이 실패했습니다'
|
||||
@@ -362,7 +362,7 @@ TaskDelete: '삭제'
|
||||
TaskUpgrade: '업그레이드'
|
||||
TaskUpdate: '업데이트'
|
||||
TaskRestart: '다시 시작'
|
||||
TaskProtect: "보호"
|
||||
TaskProtect: '보호'
|
||||
TaskBackup: '백업'
|
||||
TaskRecover: '복구'
|
||||
TaskRollback: '롤백'
|
||||
@@ -370,10 +370,10 @@ TaskPull: '당기기'
|
||||
TaskCommit: '커밋'
|
||||
TaskBuild: '빌드'
|
||||
TaskPush: '푸시'
|
||||
TaskClean: "정리"
|
||||
TaskClean: '정리'
|
||||
TaskHandle: '실행'
|
||||
TaskImport: "가져오기"
|
||||
TaskExport: "내보내기"
|
||||
TaskImport: '가져오기'
|
||||
TaskExport: '내보내기'
|
||||
Website: '웹사이트'
|
||||
App: '애플리케이션'
|
||||
Runtime: '런타임 환경'
|
||||
@@ -393,7 +393,7 @@ Start: '시작'
|
||||
Run: '시작'
|
||||
Stop: '정지'
|
||||
Image: '거울'
|
||||
Compose : '오케스트레이션'
|
||||
Compose: '오케스트레이션'
|
||||
Container: '컨테이너'
|
||||
AppLink: '연결된 애플리케이션'
|
||||
EnableSSL: 'HTTPS 활성화'
|
||||
@@ -405,18 +405,12 @@ RuntimeExtension: '런타임 환경 확장'
|
||||
TaskIsExecuting: '작업이 실행 중입니다'
|
||||
CustomAppstore: '사용자 정의 애플리케이션 웨어하우스'
|
||||
TaskExec: '실행'
|
||||
TaskBatch: "일괄 작업"
|
||||
TaskBatch: '일괄 작업'
|
||||
FileConvert: '파일 형식 변환'
|
||||
|
||||
# task - clam
|
||||
Clamscan: "{{ .name }} 스캔"
|
||||
TaskScan: "스캔"
|
||||
|
||||
# 작업 - ai
|
||||
Clamscan: '{{ .name }} 스캔'
|
||||
TaskScan: '스캔'
|
||||
OllamaModelPull: 'Ollama 모델 {{ .name }}을(를) 끌어오세요'
|
||||
OllamaModelSize: 'Ollama 모델 {{ .name }}의 크기를 가져옵니다'
|
||||
|
||||
# 작업-스냅샷
|
||||
Snapshot: '스냅샷'
|
||||
SnapDBInfo: '1Panel 데이터베이스 정보 쓰기'
|
||||
SnapCopy: '파일 및 디렉토리 {{ .name }} 복사'
|
||||
@@ -442,7 +436,6 @@ SnapCompressSize: '스냅샷 파일 크기 {{ .name }}'
|
||||
SnapUpload: '스냅샷 파일 업로드'
|
||||
SnapUploadTo: '스냅샷 파일을 {{ .name }}에 업로드'
|
||||
SnapUploadRes: '스냅샷 파일을 {{ .name }}에 업로드'
|
||||
|
||||
SnapshotRecover: '스냅샷 복원'
|
||||
RecoverDownload: '스냅샷 파일 다운로드'
|
||||
Download: '다운로드'
|
||||
@@ -450,10 +443,10 @@ RecoverDownloadAccount: '스냅샷 다운로드 백업 계정 {{ .name }} 가져
|
||||
RecoverDecompress: '스냅샷 압축 파일 압축 해제'
|
||||
Decompress: '감압'
|
||||
BackupBeforeRecover: '스냅샷 전에 시스템 관련 데이터 백업'
|
||||
Readjson: '스냅샷에서 Json 파일을 읽습니다'
|
||||
ReadjsonPath: '스냅샷의 Json 파일 경로 가져오기'
|
||||
ReadjsonContent: 'Json 파일 읽기'
|
||||
ReadjsonMarshal: 'Json 이스케이프 처리'
|
||||
ReadJson: '스냅샷에서 Json 파일을 읽습니다'
|
||||
ReadJsonPath: '스냅샷의 Json 파일 경로 가져오기'
|
||||
ReadJsonContent: 'Json 파일 읽기'
|
||||
ReadJsonMarshal: 'Json 이스케이프 처리'
|
||||
RecoverApp: '설치된 앱 복원'
|
||||
RecoverWebsite: '웹사이트 디렉토리 복구'
|
||||
RecoverAppImage: '스냅샷 이미지 백업 복원'
|
||||
@@ -467,9 +460,6 @@ RecoverDaemonJson: '컨테이너 구성 daemon.json 파일 복원'
|
||||
RecoverDBData: '데이터베이스 데이터 복구'
|
||||
RecoverBackups: '로컬 백업 디렉토리 복원'
|
||||
RecoverPanelData: '복구 데이터 디렉토리'
|
||||
|
||||
# 작업 - 컨테이너
|
||||
ContainerNewCliet: 'Docker 클라이언트 초기화'
|
||||
ContainerImagePull: '컨테이너 이미지 {{ .name }} 가져오기'
|
||||
ContainerRemoveOld: '원래 컨테이너 {{ .name }} 제거'
|
||||
ContainerImageCheck: '이미지가 정상적으로 당겨졌는지 확인'
|
||||
@@ -478,22 +468,13 @@ ContainerRecreate: '컨테이너 업데이트에 실패했습니다. 이제 원
|
||||
ContainerCreate: '새로운 컨테이너 {{ .name }}를 만듭니다'
|
||||
ContainerCreateFailed: '컨테이너 생성에 실패했습니다. 실패한 컨테이너를 삭제하세요'
|
||||
ContainerStartCheck: '컨테이너가 시작되었는지 확인'
|
||||
|
||||
# 작업 - 이미지
|
||||
ImageBuild: '이미지 빌드'
|
||||
ImageBuildStdoutCheck: '이미지 출력 콘텐츠 구문 분석'
|
||||
ImageBuildRes: '이미지 빌드 출력: {{ .name }}'
|
||||
ImagePull: '이미지 가져오기'
|
||||
ImageRepoAuthFromDB: '데이터베이스에서 저장소 인증 정보 가져오기'
|
||||
ImaegPullRes: '이미지 풀 출력: {{ .name }}'
|
||||
ImagePush: '이미지 푸시'
|
||||
ImageRenameTag: '이미지 태그 수정'
|
||||
ImageNewTag: '새로운 이미지 태그 {{ .name }}'
|
||||
ImaegPushRes: '이미지 푸시 출력: {{ .name }}'
|
||||
ComposeCreate: '작곡 만들기'
|
||||
ComposeCreateRes: 'Compose 생성 출력: {{ .name }}'
|
||||
|
||||
# 작업 - 웹사이트
|
||||
BackupNginxConfig: '웹사이트 OpenResty 구성 파일 백업'
|
||||
CompressFileSuccess: '디렉토리 압축이 성공했습니다. {{ .name }}으로 압축되었습니다.'
|
||||
CompressDir: '압축 디렉토리'
|
||||
@@ -504,40 +485,33 @@ websiteDir: '웹사이트 디렉토리'
|
||||
RecoverFailedStartRollBack: '복구 실패, 롤백 시작'
|
||||
AppBackupFileIncomplete: '백업 파일이 불완전하며 app.json 또는 app.tar.gz 파일이 없습니다.'
|
||||
AppAttributesNotMatch: '애플리케이션 유형 또는 이름이 일치하지 않습니다'
|
||||
ErrAlertSync: '알람 정보 동기화 오류입니다. 확인하고 다시 시도하세요'
|
||||
ErrInstallExtension: '이미 설치 작업이 진행 중입니다. 작업이 완료될 때까지 기다려 주세요.'
|
||||
PanelAlertTitle: '패널 알림 통지'
|
||||
TestAlertTitle: '테스트 이메일 - 이메일 연결 확인'
|
||||
TestAlert: '이것은 테스트 이메일이며 이메일 발송 설정이 올바른지 확인합니다.'
|
||||
LicenseExpirationAlert: '귀하의 {{ .node }}{{ .ip }} 패널, 라이센스가 {{ .day }}일 후에 만료됩니다. 자세한 내용은 패널에 로그인하십시오.'
|
||||
CronJobFailedAlert: '귀하의 {{ .node }}{{ .ip }} 패널, 예약 작업 {{ .name }} 실행에 실패했습니다. 자세한 내용은 패널에 로그인하십시오.'
|
||||
ClamAlert: '귀하의 {{ .node }}{{ .ip }} 패널, 바이러스 스캔 작업에서 {{ .num }}개의 감염 파일이 감지되었습니다. 자세한 내용은 패널에 로그인하십시오.'
|
||||
WebSiteAlert: '귀하의 {{ .node }}{{ .ip }} 패널, {{ .num }}개의 웹사이트가 {{ .day }}일 후에 만료됩니다. 자세한 내용은 패널에 로그인하십시오.'
|
||||
SSLAlert: '귀하의 {{ .node }}{{ .ip }} 패널, {{ .num }}개의 SSL 인증서가 {{ .day }}일 후에 만료됩니다. 자세한 내용은 패널에 로그인하십시오.'
|
||||
DiskUsedAlert: '귀하의 {{ .node }}{{ .ip }} 패널, 디스크 {{ .name }} 사용률이 {{ .used }}에 도달했습니다. 자세한 내용은 패널에 로그인하십시오.'
|
||||
ResourceAlert: '귀하의 {{ .node }}{{ .ip }} 패널, 최근 {{ .time }}분간 {{ .name }} 평균 사용률은 {{ .used }}입니다. 자세한 내용은 패널에 로그인하십시오.'
|
||||
PanelVersionAlert: '귀하의 {{ .node }}{{ .ip }} 패널, 최신 버전으로 업그레이드할 수 있습니다. 자세한 내용은 패널에 로그인하십시오.'
|
||||
PanelPwdExpirationAlert: '귀하의 {{ .node }}{{ .ip }} 패널, 패널 비밀번호가 {{ .day }}일 후에 만료됩니다. 자세한 내용은 패널에 로그인하십시오.'
|
||||
CommonAlert: '귀하의 {{ .node }}{{ .ip }} 패널, {{ .msg }}。자세한 내용은 패널에 로그인하십시오.'
|
||||
NodeExceptionAlert: '귀하의 {{ .node }}{{ .ip }} 패널, {{ .num }}개의 노드에 이상이 있습니다. 자세한 내용은 패널에 로그인하십시오.'
|
||||
LicenseExceptionAlert: '귀하의 {{ .node }}{{ .ip }} 패널, {{ .num }}개의 라이센스에 이상이 있습니다. 자세한 내용은 패널에 로그인하십시오.'
|
||||
SSHAndPanelLoginAlert: '귀하의 {{ .node }}{{ .ip }} 패널, {{ .loginIp }}에서의 {{ .name }} 로그인에 이상이 있습니다. 자세한 내용은 패널에 로그인하십시오.'
|
||||
DeviceNotFound: '장치 {{ .name }} 을(를) 찾을 수 없습니다'
|
||||
DeviceIsMounted: '장치 {{ .name }} 이(가) 마운트되었습니다, 먼저 마운트 해제하세요'
|
||||
PartitionDiskErr: '파티션 분할에 실패했습니다, {{ .err }}'
|
||||
FormatDiskErr: '디스크 포맷에 실패했습니다, {{ .err }}'
|
||||
MountDiskErr: '디스크 마운트에 실패했습니다, {{ .err }}'
|
||||
XfsNotFound: 'xfs 파일 시스템이 감지되지 않았습니다, 먼저 xfsprogs 를 설치하세요'
|
||||
|
||||
#알리다
|
||||
ErrAlert: '경고 메시지의 형식이 올바르지 않습니다. 확인하고 다시 시도하세요!'
|
||||
ErrAlertPush: '알림 정보를 푸시하는 중 오류가 발생했습니다. 확인하고 다시 시도하세요!'
|
||||
ErrAlertSave: '알람 정보를 저장하는 중 오류가 발생했습니다. 확인하고 다시 시도하세요!'
|
||||
ErrAlertSync: '알람 정보 동기화 오류입니다. 확인하고 다시 시도하세요!'
|
||||
ErrAlertRemote: '알람 메시지 원격 오류, 확인하고 다시 시도하세요!'
|
||||
|
||||
#task - runtime
|
||||
ErrInstallExtension: "이미 설치 작업이 진행 중입니다. 작업이 완료될 때까지 기다려 주세요."
|
||||
|
||||
# alert mail template
|
||||
PanelAlertTitle: "패널 알림 통지"
|
||||
TestAlertTitle: "테스트 이메일 - 이메일 연결 확인"
|
||||
TestAlert: "이것은 테스트 이메일이며 이메일 발송 설정이 올바른지 확인합니다."
|
||||
LicenseExpirationAlert: "귀하의 {{ .node }}{{ .ip }} 패널, 라이센스가 {{ .day }}일 후에 만료됩니다. 자세한 내용은 패널에 로그인하십시오."
|
||||
CronJobFailedAlert: "귀하의 {{ .node }}{{ .ip }} 패널, 예약 작업 {{ .name }} 실행에 실패했습니다. 자세한 내용은 패널에 로그인하십시오."
|
||||
ClamAlert: "귀하의 {{ .node }}{{ .ip }} 패널, 바이러스 스캔 작업에서 {{ .num }}개의 감염 파일이 감지되었습니다. 자세한 내용은 패널에 로그인하십시오."
|
||||
WebSiteAlert: "귀하의 {{ .node }}{{ .ip }} 패널, {{ .num }}개의 웹사이트가 {{ .day }}일 후에 만료됩니다. 자세한 내용은 패널에 로그인하십시오."
|
||||
SSLAlert: "귀하의 {{ .node }}{{ .ip }} 패널, {{ .num }}개의 SSL 인증서가 {{ .day }}일 후에 만료됩니다. 자세한 내용은 패널에 로그인하십시오."
|
||||
DiskUsedAlert: "귀하의 {{ .node }}{{ .ip }} 패널, 디스크 {{ .name }} 사용률이 {{ .used }}에 도달했습니다. 자세한 내용은 패널에 로그인하십시오."
|
||||
ResourceAlert: "귀하의 {{ .node }}{{ .ip }} 패널, 최근 {{ .time }}분간 {{ .name }} 평균 사용률은 {{ .used }}입니다. 자세한 내용은 패널에 로그인하십시오."
|
||||
PanelVersionAlert: "귀하의 {{ .node }}{{ .ip }} 패널, 최신 버전으로 업그레이드할 수 있습니다. 자세한 내용은 패널에 로그인하십시오."
|
||||
PanelPwdExpirationAlert: "귀하의 {{ .node }}{{ .ip }} 패널, 패널 비밀번호가 {{ .day }}일 후에 만료됩니다. 자세한 내용은 패널에 로그인하십시오."
|
||||
CommonAlert: "귀하의 {{ .node }}{{ .ip }} 패널, {{ .msg }}。자세한 내용은 패널에 로그인하십시오."
|
||||
NodeExceptionAlert: "귀하의 {{ .node }}{{ .ip }} 패널, {{ .num }}개의 노드에 이상이 있습니다. 자세한 내용은 패널에 로그인하십시오."
|
||||
LicenseExceptionAlert: "귀하의 {{ .node }}{{ .ip }} 패널, {{ .num }}개의 라이센스에 이상이 있습니다. 자세한 내용은 패널에 로그인하십시오."
|
||||
SSHAndPanelLoginAlert: "귀하의 {{ .node }}{{ .ip }} 패널, {{ .loginIp }}에서의 {{ .name }} 로그인에 이상이 있습니다. 자세한 내용은 패널에 로그인하십시오."
|
||||
|
||||
#disk
|
||||
DeviceNotFound: "장치 {{ .name }} 을(를) 찾을 수 없습니다"
|
||||
DeviceIsMounted: "장치 {{ .name }} 이(가) 마운트되었습니다, 먼저 마운트 해제하세요"
|
||||
PartitionDiskErr: "파티션 분할에 실패했습니다, {{ .err }}"
|
||||
FormatDiskErr: "디스크 포맷에 실패했습니다, {{ .err }}"
|
||||
MountDiskErr: "디스크 마운트에 실패했습니다, {{ .err }}"
|
||||
UnMountDiskErr: "디스크 마운트 해제에 실패했습니다, {{ .err }}"
|
||||
XfsNotFound: "xfs 파일 시스템이 감지되지 않았습니다, 먼저 xfsprogs 를 설치하세요"
|
||||
# terminal
|
||||
TerminalAIBlockedRiskyCommand: '위험한 명령이 차단되었습니다: {{ .command }}'
|
||||
TerminalAIThinking: 'AI가 생각 중입니다...'
|
||||
TerminalAIReadyToExecute: '생각이 완료되었습니다. Enter를 눌러 실행하세요 (소요 시간 {{ .duration }}, token: {{ .tokens }})'
|
||||
TerminalAIRequestFailed: 'AI 요청 실패: {{ .err }}'
|
||||
|
||||
+213
-239
@@ -1,83 +1,77 @@
|
||||
ErrInvalidParams: 'Permintaan ralat parameter: {{ .detail }}'
|
||||
ErrTokenParse: 'Ralat penjanaan token: {{ .detail }}'
|
||||
ErrInitialPassword: 'Kata laluan asal tidak betul'
|
||||
ErrInternalServer: 'Ralat pelayan dalaman: {{ .detail }}'
|
||||
ErrInvalidParams: 'Permintaan tidak sah: {{ .detail }}'
|
||||
ErrTokenParse: 'Token tidak sah: {{ .detail }}'
|
||||
ErrInitialPassword: 'Kata laluan asal salah'
|
||||
ErrInternalServer: 'Ralat pelayan: {{ .detail }}'
|
||||
ErrWgetRemoteFailed: 'Muat turun jauh gagal atau URL tidak boleh diakses: {{ .detail }}'
|
||||
ErrWgetInvalidContentType: 'URL jauh tidak mengembalikan fail (mungkin halaman ralat), {{ .detail }}'
|
||||
ErrRecordExist: 'Rekod sudah wujud'
|
||||
ErrRecordNotFound: 'Rekod tidak ditemui'
|
||||
ErrStructTransform: 'Penukaran jenis gagal: {{ .err }}'
|
||||
ErrNotLogin: 'Pengguna tidak log masuk: {{ .detail }}'
|
||||
ErrPasswordExpired: 'Kata laluan semasa telah tamat tempoh: {{ .detail }}'
|
||||
ErrNotSupportType: 'Sistem tidak menyokong jenis semasa: {{ .name }}'
|
||||
ErrProxy: 'Ralat permintaan, sila semak status nod: {{ .detail }}'
|
||||
ErrApiConfigStatusInvalid: 'Akses kepada antara muka API adalah dilarang: {{ .detail }}'
|
||||
ErrApiConfigKeyInvalid: 'Ralat kunci antara muka API: {{ .detail }}'
|
||||
ErrApiConfigIPInvalid: 'IP yang digunakan untuk memanggil antara muka API tiada dalam senarai putih: {{ .detail }}'
|
||||
ErrApiConfigDisable: 'Antara muka ini melarang penggunaan panggilan antara muka API: {{ .detail }}'
|
||||
ErrApiConfigKeyTimeInvalid: 'Ralat cap masa antara muka API: {{ .detail }}'
|
||||
StartPushSSLToNode: "Mula menolak sijil ke nod"
|
||||
PushSSLToNodeFailed: "Gagal menolak sijil ke nod: {{ .err }}"
|
||||
PushSSLToNodeSuccess: "Berjaya menolak sijil ke nod"
|
||||
ErrShutDown: "Arahan dihentikan secara manual!"
|
||||
|
||||
ErrMinQuickJump: "Sila tetapkan sekurang-kurangnya satu entri lompat pantas!"
|
||||
ErrMaxQuickJump: "Anda boleh menetapkan sehingga empat entri lompat pantas!"
|
||||
|
||||
#biasa
|
||||
ErrStructTransform: 'Ralat penukaran: {{ .err }}'
|
||||
ErrNotLogin: 'Belum log masuk: {{ .detail }}'
|
||||
ErrPasswordExpired: 'Kata laluan tamat: {{ .detail }}'
|
||||
ErrNotSupportType: 'Jenis tidak disokong: {{ .name }}'
|
||||
ErrProxy: 'Permintaan gagal: {{ .detail }}'
|
||||
ErrApiConfigStatusInvalid: 'API dilumpuhkan: {{ .detail }}'
|
||||
ErrApiConfigKeyInvalid: 'Kunci API tidak sah: {{ .detail }}'
|
||||
ErrApiConfigIPInvalid: 'IP tidak dibenarkan: {{ .detail }}'
|
||||
ErrApiConfigDisable: 'API tidak dibenarkan: {{ .detail }}'
|
||||
ErrApiConfigKeyTimeInvalid: 'Cap masa tidak sah: {{ .detail }}'
|
||||
ErrShutDown: 'Arahan diberhentikan manual'
|
||||
ErrMinQuickJump: 'Tambah sekurang-kurangnya satu pintasan pantas'
|
||||
ErrMaxQuickJump: 'Maksimum 4 pintasan pantas'
|
||||
ErrUsernameIsExist: 'Nama pengguna sudah wujud'
|
||||
ErrNameIsExist: 'Nama sudah wujud'
|
||||
ErrDemoEnvironment: 'Pelayan demo, operasi ini dilarang!'
|
||||
ErrCmdTimeout: 'Pelaksanaan arahan tamat masa!'
|
||||
ErrCmdIllegal: 'Terdapat aksara yang menyalahi undang-undang dalam perintah pelaksanaan, sila ubah suainya dan cuba lagi!'
|
||||
ErrPortExist: '{{ .port }} port sudah diduduki oleh {{ .type }} [{{ .name }}]'
|
||||
ErrDemoEnvironment: 'Tidak tersedia dalam demo'
|
||||
ErrCmdTimeout: 'Arahan tamat masa'
|
||||
ErrCmdIllegal: 'Arahan ada aksara tidak sah'
|
||||
ErrPortExist: 'Port {{ .port }} sudah digunakan oleh {{ .type }} [{{ .name }}]'
|
||||
TYPE_APP: 'Aplikasi'
|
||||
TYPE_RUNTIME: 'Persekitaran masa jalan'
|
||||
TYPE_DOMAIN: 'Nama Domain'
|
||||
ErrTypePort: Format 'Port {{ .name }} tidak betul'
|
||||
ErrTypePort: 'Format ''Port {{ .name }} tidak betul'''
|
||||
ErrTypePortRange: 'Julat port perlu antara 1-65535'
|
||||
Success: 'Kejayaan'
|
||||
Failed: 'Gagal'
|
||||
SystemRestart: 'Tugas terganggu kerana sistem mula semula'
|
||||
ErrGroupIsDefault: 'Kumpulan lalai, tidak boleh dipadamkan'
|
||||
ErrGroupIsInWebsiteUse: 'Kumpulan sedang digunakan oleh tapak web lain dan tidak boleh dipadamkan.'
|
||||
Decrypt: "Dekripsi"
|
||||
|
||||
#agent
|
||||
ErrAgentAccountBound: 'Akaun ini telah dipautkan kepada ejen dan tidak boleh dipadam. Sila semak dan cuba lagi!'
|
||||
ErrAgentAccountUnavailable: 'Maklumat sambungan akaun tidak tersedia. Ralat: {{ .err }}. Sila semak dan cuba lagi!'
|
||||
ErrAgentProviderNotSupported: 'Penyedia ejen ini tidak disokong. Sila semak dan cuba lagi!'
|
||||
ErrAgentAccountRequired: 'Sila pilih akaun ejen dan cuba lagi.'
|
||||
ErrAgentAccountNotVerified: 'Akaun belum disahkan. Sila semak dan cuba lagi!'
|
||||
ErrAgentProviderMismatch: 'Penyedia akaun tidak sepadan. Sila semak dan cuba lagi!'
|
||||
ErrAgentBaseURLRequired: 'Base URL tidak boleh kosong. Sila semak dan cuba lagi!'
|
||||
ErrAgentApiKeyRequired: 'API Key tidak boleh kosong. Sila semak dan cuba lagi!'
|
||||
ErrAgentComposeRequired: 'Kandungan compose tersuai tidak boleh kosong. Sila semak dan cuba lagi!'
|
||||
ErrAgentIDRequired: 'ID ejen tidak boleh kosong. Sila semak dan cuba lagi!'
|
||||
ErrAgentAccountIDRequired: 'ID akaun tidak boleh kosong. Sila semak dan cuba lagi!'
|
||||
|
||||
#sandaran
|
||||
Decrypt: 'Dekripsi'
|
||||
ErrAgentAccountBound: 'Akaun terikat kepada ejen'
|
||||
ErrAgentAccountUnavailable: 'Sambungan akaun tidak tersedia: {{ .err }}'
|
||||
ErrAgentProviderNotSupported: 'Penyedia ejen tidak disokong'
|
||||
ErrAgentAccountRequired: 'Pilih akaun ejen terlebih dahulu'
|
||||
ErrAgentAccountNotVerified: 'Akaun ejen belum disahkan'
|
||||
ErrAgentAccountModelsRequired: 'Sila konfigur sekurang-kurangnya satu model'
|
||||
ErrAgentAccountSingleInitialModel: 'Hanya satu model awal boleh dikonfigurasi semasa mencipta akaun'
|
||||
ErrAgentModelNotInAccount: 'Model tidak dikonfigurasikan dalam akaun yang dipilih'
|
||||
ErrAgentModelInUse: 'Masih ada ejen terikat yang menggunakan model ini: {{ .name }}'
|
||||
ErrAgentBaseURLRequired: 'URL asas diperlukan'
|
||||
ErrAgentApiKeyRequired: 'Kunci API diperlukan'
|
||||
ErrAgentComposeRequired: 'Kandungan compose diperlukan'
|
||||
ErrAgentIDRequired: 'ID ejen diperlukan'
|
||||
ErrAgentAccountIDRequired: 'ID akaun diperlukan'
|
||||
ErrAgentLimitReached: 'Edisi Community menyokong sehingga {{ .max }} ejen AI. Naik taraf ke edisi Professional untuk tiada had bilangan'
|
||||
Localhost: 'Mesin Tempatan'
|
||||
ErrBackupInUsed: 'Akaun sandaran telah digunakan dalam tugas yang dijadualkan dan tidak boleh dipadamkan.'
|
||||
ErrBackupCheck: 'Sambungan ujian akaun sandaran gagal {{ .err }}'
|
||||
ErrBackupLocalDelete: 'Memadam akaun sandaran pelayan tempatan belum disokong lagi'
|
||||
ErrBackupLocalCreate: 'Membuat akaun sandaran pelayan tempatan belum disokong lagi'
|
||||
|
||||
#app
|
||||
ErrPortInUsed: 'Port {{ .detail }} sudah diduduki!'
|
||||
ErrBackupInUsed: 'Akaun sandaran sedang digunakan oleh tugas'
|
||||
ErrBackupCheck: 'Ujian sambungan gagal: {{ .err }}'
|
||||
ErrBackupLocalDelete: 'Tidak boleh padam sandaran tempatan'
|
||||
ErrBackupLocalCreate: 'Tidak boleh buat sandaran tempatan'
|
||||
ErrPortInUsed: 'Port {{ .detail }} sudah diduduki'
|
||||
ErrAppLimit: 'Bilangan aplikasi yang dipasang telah melebihi had'
|
||||
ErrNotInstall: 'Aplikasi tidak dipasang'
|
||||
ErrPortInOtherApp: 'Port {{ .port }} sudah diduduki oleh aplikasi {{ .apps }}!'
|
||||
ErrDbUserNotValid: 'Pangkalan data sedia ada, nama pengguna dan kata laluan tidak sepadan!'
|
||||
ErrUpdateBuWebsite: 'Aplikasi telah berjaya dikemas kini, tetapi pengubahsuaian fail konfigurasi tapak web gagal. Sila semak konfigurasi! '
|
||||
Err1PanelNetworkFailed: 'Pembuatan rangkaian kontena lalai gagal! {{ .detail }}'
|
||||
ErrFileParse: 'Penghuraian fail karang docker aplikasi gagal!'
|
||||
ErrPortInOtherApp: 'Port {{ .port }} sudah diduduki oleh aplikasi {{ .apps }}'
|
||||
ErrDbUserNotValid: 'Pangkalan data sedia ada, nama pengguna dan kata laluan tidak sepadan'
|
||||
ErrUpdateBuWebsite: 'Aplikasi telah berjaya dikemas kini, tetapi pengubahsuaian fail konfigurasi tapak web gagal. Sila semak konfigurasi'
|
||||
Err1PanelNetworkFailed: 'Pembuatan rangkaian kontena lalai gagal {{ .detail }}'
|
||||
ErrFileParse: 'Penghuraian fail karang docker aplikasi gagal'
|
||||
ErrInstallDirNotFound: 'Direktori pemasangan tidak wujud. Jika anda perlu menyahpasang, sila pilih Paksa Nyahpasang'
|
||||
AppStoreIsUpToDate: 'Gedung aplikasi sudah pun versi terkini'
|
||||
LocalAppVersionNull: Aplikasi '{{ .name }} tidak disegerakkan ke versi! Tidak dapat menambah pada senarai apl'
|
||||
LocalAppVersionErr: '{{ .name }} versi penyegerakan {{ .version }} gagal! {{ .err }}'
|
||||
LocalAppVersionNull: 'Aplikasi ''{{ .name }} tidak disegerakkan ke versi Tidak dapat menambah pada senarai apl'''
|
||||
LocalAppVersionErr: '{{ .name }} versi penyegerakan {{ .version }} gagal {{ .err }}'
|
||||
ErrFileNotFound: 'Fail {{ .name }} tidak wujud'
|
||||
ErrFileParseApp: '{{ .name }} penghuraian fail gagal {{ .err }}'
|
||||
ErrAppDirNull: 'Folder versi tidak wujud'
|
||||
LocalAppErr: 'Penyegerakan {{ .name }} aplikasi gagal! {{ .err }}'
|
||||
LocalAppErr: 'Penyegerakan {{ .name }} aplikasi gagal {{ .err }}'
|
||||
ErrContainerName: 'Nama kontena sudah wujud'
|
||||
ErrCreateHttpClient: 'Gagal membuat permintaan {{ .err }}'
|
||||
ErrHttpReqTimeOut: 'Permintaan tamat masa {{ .err }}'
|
||||
@@ -85,69 +79,63 @@ ErrHttpReqFailed: 'Permintaan gagal {{ .err }}'
|
||||
ErrNoSuchHost: 'Tidak dapat mencari pelayan yang diminta {{ .err }}'
|
||||
ErrHttpReqNotFound: 'Sumber yang diminta {{ .err }} tidak dapat ditemui'
|
||||
ErrContainerNotFound: 'Bekas {{ .name }} tidak wujud'
|
||||
ErrContainerMsg: Bekas '{{ .name }} adalah tidak normal. Sila semak log pada halaman kontena untuk butiran'
|
||||
ErrContainerMsg: 'Bekas ''{{ .name }} adalah tidak normal. Sila semak log pada halaman kontena untuk butiran'''
|
||||
ErrAppBackup: '{{ .name }} sandaran aplikasi gagal {{ .err }}'
|
||||
ErrVersionTooLow: 'Versi 1Panel semasa terlalu rendah untuk mengemas kini App Store. Sila tingkatkan versi sebelum beroperasi.'
|
||||
ErrAppNameExist: 'Nama aplikasi sudah wujud'
|
||||
AppStoreIsSyncing: 'App Store sedang menyegerak, sila cuba sebentar lagi'
|
||||
ErrGetCompose: 'Gagal mendapatkan fail docker-compose.yml! {{ .detail }}'
|
||||
ErrGetCompose: 'Gagal mendapatkan fail docker-compose.yml {{ .detail }}'
|
||||
ErrAppWarn: 'Status tidak normal, sila semak log'
|
||||
ErrAppParamKey: 'Parameter {{ .name }} medan tidak normal'
|
||||
ErrAppUpgrade: 'Peningkatan {{ .name }} aplikasi gagal {{ .err }}'
|
||||
AppRecover: 'Aplikasi tarik balik {{ .name }}'
|
||||
PullImageStart: 'Mula tarik imej [{{ .name }}]'
|
||||
PullImageSuccess: 'Tarik imej berjaya'
|
||||
AppStoreIsLastVersion: 'App Store sudah pun versi terkini'
|
||||
AppStoreSyncSuccess: 'Penyegerakan App Store berjaya'
|
||||
SyncAppDetail: 'Segerakkan konfigurasi aplikasi'
|
||||
AppVersionNotMatch: 'Aplikasi {{ .name }} memerlukan versi 1Panel yang lebih tinggi, melangkau penyegerakan'
|
||||
MoveSiteDir: "Perubahan direktori laman web didapati. Perlu memindahkan direktori laman web OpenResty ke {{ .name }}"
|
||||
MoveSiteDir: 'Perubahan direktori laman web didapati. Perlu memindahkan direktori laman web OpenResty ke {{ .name }}'
|
||||
MoveSiteDirSuccess: 'Penghijrahan direktori tapak web yang berjaya'
|
||||
DeleteRuntimePHP: 'Padam masa jalan PHP'
|
||||
CustomAppStoreFileValid: 'Pakej gedung apl perlu dalam format .tar.gz'
|
||||
CustomAppStoreFileValid: 'Pakej gedung apl perlu dalam format.tar.gz'
|
||||
PullImageTimeout: 'Tarik tamat masa imej, sila cuba tingkatkan pecutan imej atau tukar kepada pecutan imej lain'
|
||||
ErrAppIsDown: 'Status permohonan {{ .name }} tidak normal, sila semak'
|
||||
ErrCustomApps: 'Ada aplikasi yang dipasang, sila nyahpasangnya dahulu'
|
||||
ErrCustomRuntimes: 'Terdapat persekitaran masa jalan yang dipasang, sila padamkannya dahulu'
|
||||
ErrAppVersionDeprecated: "Aplikasi {{ .name }} tidak sesuai dengan versi 1Panel saat ini, dilewati"
|
||||
ErrDockerFailed: "Keadaan Docker tidak normal, sila periksa status perkhidmatan"
|
||||
ErrDockerComposeCmdNotFound: "Perintah Docker Compose tidak wujud, sila pasang perintah ini di mesin tuan terlebih dahulu"
|
||||
UseExistImage: 'Gagal menarik imej, menggunakan imej sedia ada'
|
||||
|
||||
#ssh
|
||||
ExportIP: "IP Log Masuk"
|
||||
ExportArea: "Lokasi"
|
||||
ExportPort: "Port"
|
||||
ExportAuthMode: "Kaedah Log Masuk"
|
||||
ExportUser: "Pengguna"
|
||||
ExportStatus: "Status Log Masuk"
|
||||
ExportDate: "Masa"
|
||||
|
||||
#fail
|
||||
ErrAppVersionDeprecated: 'Aplikasi {{ .name }} tidak sesuai dengan versi 1Panel saat ini, dilewati'
|
||||
ErrDockerFailed: 'Keadaan Docker tidak normal, sila periksa status perkhidmatan'
|
||||
ErrDockerComposeCmdNotFound: 'Perintah Docker Compose tidak wujud, sila pasang perintah ini di mesin tuan terlebih dahulu'
|
||||
UseExistImage: 'Imej sudah wujud, menggunakan imej sedia ada'
|
||||
ErrDatabaseNotFound: 'Pangkalan data {{ .name }} tidak wujud'
|
||||
ExportIP: 'IP Log Masuk'
|
||||
ExportArea: 'Lokasi'
|
||||
ExportPort: 'Port'
|
||||
ExportAuthMode: 'Kaedah Log Masuk'
|
||||
ExportUser: 'Pengguna'
|
||||
ExportStatus: 'Status Log Masuk'
|
||||
ExportDate: 'Masa'
|
||||
ErrFileCanNotRead: 'Fail ini tidak menyokong pratonton'
|
||||
ErrFileToLarge: 'Fail lebih besar daripada 10M dan tidak boleh dibuka'
|
||||
ErrPathNotFound: 'Direktori tidak wujud'
|
||||
ErrMovePathFailed: 'Laluan sasaran tidak boleh mengandungi laluan asal!'
|
||||
ErrLinkPathNotFound: 'Laluan sasaran tidak wujud!'
|
||||
ErrFileIsExist: 'Fail atau folder sudah wujud!'
|
||||
ErrMovePathFailed: 'Laluan sasaran tidak boleh mengandungi laluan asal'
|
||||
ErrLinkPathNotFound: 'Laluan sasaran tidak wujud'
|
||||
ErrFileIsExist: 'Fail atau folder sudah wujud'
|
||||
ErrFileUpload: '{{ .name }} gagal memuat naik fail {{ .detail }}'
|
||||
ErrFileDownloadDir: 'Folder muat turun tidak disokong'
|
||||
ErrCmdNotFound: 'Arahan {{ .name}} tidak wujud, sila pasang arahan ini pada hos dahulu'
|
||||
ErrCmdNotFound: 'Arahan {{ .name }} tidak wujud, sila pasang arahan ini pada hos dahulu'
|
||||
ErrSourcePathNotFound: 'Direktori sumber tidak wujud'
|
||||
ErrFavoriteExist: 'Laluan ini telah digemari'
|
||||
ErrInvalidChar: 'Aksara haram tidak dibenarkan'
|
||||
ErrPathNotDelete: 'Direktori yang dipilih tidak boleh dipadamkan'
|
||||
ErrLogFileTooLarge: "Fail log melebihi 500MB dan tidak boleh dibuka"
|
||||
FileDropFailed: "Gagal membersihkan fail {{ .name }}: {{ .err }}"
|
||||
FileDropSuccess: "Berjaya membersihkan fail {{ .name }}, {{ .count }} fail dibersihkan, {{ .size }} ruang cakera dibebaskan"
|
||||
FileDropSum: "Pembersihan fail selesai, sejumlah {{ .count }} fail dibersihkan, sejumlah {{ .size }} ruang cakera dibebaskan"
|
||||
ErrBadDecrypt: "Kata laluan penyahsulitan pakej termampat salah, penyahmampatan gagal, sila periksa dan cuba lagi!"
|
||||
|
||||
#laman web
|
||||
ErrLogFileToLarge: 'Fail log melebihi 500MB dan tidak boleh dibuka'
|
||||
FileDropFailed: 'Gagal membersihkan fail {{ .name }}: {{ .err }}'
|
||||
FileDropSuccess: 'Berjaya membersihkan fail {{ .name }}, {{ .count }} fail dibersihkan, {{ .size }} ruang cakera dibebaskan'
|
||||
FileDropSum: 'Pembersihan fail selesai, sejumlah {{ .count }} fail dibersihkan, sejumlah {{ .size }} ruang cakera dibebaskan'
|
||||
ErrBadDecrypt: 'Kata laluan penyahsulitan pakej termampat salah, penyahmampatan gagal, sila periksa dan cuba lagi'
|
||||
ErrAliasIsExist: 'Alias sudah wujud'
|
||||
ErrBackupMatch: 'Fail sandaran tidak sepadan dengan beberapa data tapak web semasa {{ .detail }}'
|
||||
ErrBackupExist: 'Bahagian sepadan data sumber dalam fail sandaran tidak wujud {{ .detail }}'
|
||||
ErrPHPResource: 'Persekitaran operasi tempatan tidak menyokong penukaran! '
|
||||
ErrPHPResource: 'Persekitaran operasi tempatan tidak menyokong penukaran'
|
||||
ErrPathPermission: 'Folder dengan keizinan bukan 1000:1000 telah dikesan dalam direktori indeks, yang mungkin menyebabkan ralat dinafikan Akses pada tapak web. Sila klik butang Simpan di atas'
|
||||
ErrDomainIsUsed: 'Nama domain telah digunakan oleh tapak web [{{ .name }}]'
|
||||
ErrDomainFormat: 'Format nama domain {{ .name }} tidak betul'
|
||||
@@ -155,11 +143,11 @@ ErrDefaultAlias: 'lalai ialah kod simpanan, sila gunakan kod lain'
|
||||
ErrParentWebsite: 'Anda perlu memadamkan subtapak {{ .name }} dahulu'
|
||||
ErrBuildDirNotFound: 'Direktori binaan tidak wujud'
|
||||
ErrImageNotExist: 'Imej persekitaran operasi {{ .name }} tidak wujud, sila edit semula persekitaran pengendalian'
|
||||
ErrProxyIsUsed: "Pengimbang beban telah digunakan oleh pengganti terbalik, tidak boleh dipadamkan"
|
||||
ErrSSLValid: 'Fail sijil bermasalah, sila periksa status sijil!'
|
||||
ErrWebsiteDir: "Sila pilih direktori dalam direktori laman web."
|
||||
ErrComposerFileNotFound: "Fail composer.json tidak wujud"
|
||||
ErrRuntimeNoPort: "Persekitaran runtime tidak diset dengan port, sila edit persekitaran runtime terlebih dahulu."
|
||||
ErrProxyIsUsed: 'Pengimbang beban telah digunakan oleh pengganti terbalik, tidak boleh dipadamkan'
|
||||
ErrSSLValid: 'Fail sijil bermasalah, sila periksa status sijil'
|
||||
ErrWebsiteDir: 'Sila pilih direktori dalam direktori laman web.'
|
||||
ErrComposerFileNotFound: 'Fail composer.json tidak wujud'
|
||||
ErrRuntimeNoPort: 'Persekitaran runtime tidak diset dengan port, sila edit persekitaran runtime terlebih dahulu.'
|
||||
Status: 'Status'
|
||||
start: 'Mulakan'
|
||||
stop: 'Berhenti'
|
||||
@@ -169,27 +157,25 @@ pause: 'Jeda'
|
||||
unpause: 'Sambung Semula'
|
||||
remove: 'Padam'
|
||||
delete: 'Padam'
|
||||
ErrDefaultWebsite: 'Laman web lalai telah ditetapkan, sila batalkan sebelum menetapkan!'
|
||||
ErrDefaultWebsite: 'Laman web lalai telah ditetapkan, sila batalkan sebelum menetapkan'
|
||||
SSL: 'Sijil'
|
||||
Set: 'Tetapan'
|
||||
|
||||
#ssl
|
||||
ErrSSLCannotDelete: 'Sijil {{ .name }} sedang digunakan oleh tapak web dan tidak boleh dipadamkan'
|
||||
ErrAccountCannotDelete: 'Akaun dikaitkan dengan sijil dan tidak boleh dipadamkan'
|
||||
ErrSSSLApply: 'Pembaharuan sijil berjaya, muat semula openresty gagal, sila semak konfigurasi!'
|
||||
ErrSSLApply: 'Pembaharuan sijil berjaya, muat semula openresty gagal, sila semak konfigurasi'
|
||||
ErrEmailIsExist: 'Peti mel sudah wujud'
|
||||
ErrSSLKeyNotFound: 'Fail kunci peribadi tidak wujud'
|
||||
ErrSSLCertificateNotFound: 'Fail sijil tidak wujud'
|
||||
ErrSSLKeyFormat: 'Pengesahan fail kunci peribadi gagal'
|
||||
ErrSSLCertificateFormat: 'Format fail sijil tidak betul, sila gunakan format pem'
|
||||
ErrEabKidOrEabHmacKeyCannot Blank: 'EabKid atau EabHmacKey tidak boleh kosong'
|
||||
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid atau EabHmacKey tidak boleh kosong'
|
||||
ErrOpenrestyNotFound: 'Mod Http memerlukan Openresty dipasang dahulu'
|
||||
ApplySSLStart: 'Mula memohon sijil, nama domain [{{ .domain }}] kaedah permohonan [{{ .type }}] '
|
||||
ApplySSLStart: 'Mula memohon sijil, nama domain [{{ .domain }}] kaedah permohonan [{{ .type }}]'
|
||||
dnsAccount: 'DNS Auto'
|
||||
dnsManual: 'Manual DNS'
|
||||
http: 'HTTP'
|
||||
ApplySSLFailed: 'Permohonan untuk sijil [{{ .domain }}] gagal, {{ .detail }} '
|
||||
ApplySSLSuccess: 'Berjaya memohon sijil [{{ .domain }}]! ! '
|
||||
ApplySSLFailed: 'Permohonan untuk sijil [{{ .domain }}] gagal, {{ .detail }}'
|
||||
ApplySSLSuccess: 'Berjaya memohon sijil [{{ .domain }}]'
|
||||
DNSAccountName: 'Akaun DNS [{{ .name }}] vendor [{{ .type }}]'
|
||||
PushDirLog: 'Sijil ditolak ke direktori [{{ .path }}] {{ .status }}'
|
||||
ErrDeleteCAWithSSL: 'Organisasi semasa mempunyai sijil yang telah dikeluarkan dan tidak boleh dipadamkan.'
|
||||
@@ -204,94 +190,109 @@ ExecShellSuccess: 'Pelaksanaan skrip berjaya'
|
||||
StartUpdateSystemSSL: 'Mula mengemas kini sijil sistem'
|
||||
UpdateSystemSSLSuccess: 'Berjaya kemas kini sijil sistem'
|
||||
ErrWildcardDomain: 'Tidak dapat memohon sijil nama domain kad bebas dalam mod HTTP'
|
||||
ErrApplySSLCanNotDelete: "Sertifikat {{.name}} yang sedang diajukan tidak dapat dihapus, sila cuba lagi kemudian."
|
||||
|
||||
#mysql
|
||||
ErrApplySSLCanNotDelete: 'Sertifikat {{ .name }} yang sedang diajukan tidak dapat dihapus, sila cuba lagi kemudian.'
|
||||
StartPushSSLToNode: 'Mula menolak sijil ke nod'
|
||||
PushSSLToNodeFailed: 'Gagal menolak sijil ke nod: {{ .err }}'
|
||||
PushSSLToNodeSuccess: 'Berjaya menolak sijil ke nod'
|
||||
ErrUserIsExist: 'Pengguna semasa sudah wujud, sila masukkan semula'
|
||||
ErrDatabaseIsExist: 'Pangkalan data semasa sudah wujud, sila masukkan semula'
|
||||
ErrExecTimeOut: 'Pelaksanaan SQL tamat masa, sila semak pangkalan data'
|
||||
ErrRemoteExist: 'Pangkalan data jauh sudah wujud dengan nama ini, sila ubah suainya dan cuba lagi'
|
||||
ErrLocalExist: 'Nama sudah wujud dalam pangkalan data tempatan, sila ubah suai dan cuba lagi'
|
||||
RemoteBackup: "Untuk menyandarkan pangkalan data jauh, perkhidmatan pangkalan data bekas tempatan perlu dimulakan terlebih dahulu menggunakan imej {{ .name }}, sila tunggu..."
|
||||
RemoteRecover: "Untuk memulihkan pangkalan data jauh, perkhidmatan pangkalan data bekas tempatan perlu dimulakan terlebih dahulu menggunakan imej {{ .name }}, sila tunggu..."
|
||||
Arg: "Hujah"
|
||||
|
||||
#redis
|
||||
RemoteBackup: 'Untuk menyandarkan pangkalan data jauh, perkhidmatan pangkalan data bekas tempatan perlu dimulakan terlebih dahulu menggunakan imej {{ .name }}, sila tunggu...'
|
||||
RemoteRecover: 'Untuk memulihkan pangkalan data jauh, perkhidmatan pangkalan data bekas tempatan perlu dimulakan terlebih dahulu menggunakan imej {{ .name }}, sila tunggu...'
|
||||
Arg: 'Hujah'
|
||||
ErrTypeOfRedis: 'Jenis fail pemulihan tidak sepadan dengan kaedah kegigihan semasa, sila ubah suai dan cuba lagi'
|
||||
|
||||
#bekas
|
||||
ErrInUsed: "{{ .detail }} sedang digunakan dan tidak boleh dipadam!"
|
||||
ErrObjectInUsed: "Objek ini sedang digunakan dan tidak boleh dipadam!"
|
||||
ErrObjectBeDependent: "Imej ini digantungkan oleh imej lain dan tidak boleh dipadam!"
|
||||
ErrPortRules: 'Nombor port tidak sepadan, sila masukkan semula!'
|
||||
ErrInUsed: '{{ .detail }} sedang digunakan dan tidak boleh dipadam'
|
||||
ErrObjectInUsed: 'Objek ini sedang digunakan dan tidak boleh dipadam'
|
||||
ErrObjectBeDependent: 'Imej ini digantungkan oleh imej lain dan tidak boleh dipadam'
|
||||
ErrPortRules: 'Nombor port tidak sepadan, sila masukkan semula'
|
||||
ErrPgImagePull: 'Tarikh imej tamat masa, sila konfigurasikan pecutan imej atau tarik imej {{ .name }} secara manual dan cuba lagi'
|
||||
PruneHelper: "Pembersihan ini {{ .name }} membebaskan ruang cakera {{ .size }}"
|
||||
ImageRemoveHelper: "Padam imej {{ .name }}, membebaskan {{ .size }} ruang cakera"
|
||||
BuildCache: "Cache binaan"
|
||||
Volume: "Jilid storan"
|
||||
Network: "Rangkaian"
|
||||
PruneStart: "Pembersihan sedang dijalankan, sila tunggu..."
|
||||
|
||||
#masa berjalan
|
||||
ErrFileNotExist: 'Fail {{ .detail }} tidak wujud! Sila semak integriti fail sumber!'
|
||||
PruneHelper: 'Pembersihan ini {{ .name }} membebaskan ruang cakera {{ .size }}'
|
||||
ImageRemoveHelper: 'Padam imej {{ .name }}, membebaskan {{ .size }} ruang cakera'
|
||||
BuildCache: 'Cache binaan'
|
||||
Volume: 'Jilid storan'
|
||||
Network: 'Rangkaian'
|
||||
PruneStart: 'Pembersihan sedang dijalankan, sila tunggu...'
|
||||
ContainerBackupPrepare: 'Sediakan ruang kerja sandaran kontena'
|
||||
ContainerBackupStop: 'Hentikan kontena sebelum sandaran'
|
||||
ContainerBackupInspect: 'Sandarkan inspect dan rangkaian kontena'
|
||||
ContainerBackupMounts: 'Sandarkan mount kontena'
|
||||
ContainerBackupMeta: 'Tulis metadata sandaran kontena'
|
||||
ContainerBackupStart: 'Mulakan kontena selepas sandaran'
|
||||
ContainerRecoverPrepare: 'Sediakan ruang kerja pemulihan kontena'
|
||||
ContainerRecoverExtract: 'Ekstrak arkib sandaran kontena'
|
||||
ContainerRecoverParse: 'Muat metadata sandaran kontena'
|
||||
ContainerRecoverCreate: 'Cipta semula kontena'
|
||||
ContainerRecoverMounts: 'Pulihkan mount kontena'
|
||||
ContainerRecoverStart: 'Mulakan kontena dipulihkan'
|
||||
ContainerRecoverCleanup: 'Bersihkan ruang kerja pemulihan kontena'
|
||||
ComposeBackupPrepare: 'Sediakan ruang kerja sandaran compose'
|
||||
ComposeBackupStop: 'Hentikan compose sebelum sandaran'
|
||||
ComposeBackupFiles: 'Sandarkan fail compose'
|
||||
ComposeBackupContainers: 'Sandarkan kontena compose'
|
||||
ComposeBackupMeta: 'Tulis metadata sandaran compose'
|
||||
ComposeBackupStart: 'Mulakan compose selepas sandaran'
|
||||
ComposeBackupCleanup: 'Bersihkan ruang kerja sandaran compose'
|
||||
ComposeRecoverPrepare: 'Sediakan ruang kerja pemulihan compose'
|
||||
ComposeRecoverExtract: 'Ekstrak arkib sandaran compose'
|
||||
ComposeRecoverMeta: 'Muat metadata sandaran compose'
|
||||
ComposeRecoverMetaLogName: 'Metadata pemulihan compose: nama={{ .name }}'
|
||||
ComposeRecoverMetaLogPath: 'Metadata pemulihan compose: laluanSandaran={{ .backupPath }} laluanSasaran={{ .targetDir }}'
|
||||
ComposeRecoverMetaLogCount: 'Metadata pemulihan compose: fail={{ .files }} kontena={{ .containers }}'
|
||||
ComposeRecoverFiles: 'Pulihkan fail compose'
|
||||
ComposeRecoverContainers: 'Pulihkan kontena compose'
|
||||
ComposeRecoverRecord: 'Simpan rekod compose'
|
||||
ComposeRecoverCleanup: 'Bersihkan ruang kerja pemulihan compose'
|
||||
ErrFileNotExist: 'Fail {{ .detail }} tidak wujud Sila semak integriti fail sumber'
|
||||
ErrImageBuildErr: 'Pembinaan imej gagal'
|
||||
ErrImageExist: "Imej sudah wujud! Sila ubah nama imej."
|
||||
ErrImageExist: 'Imej sudah wujud Sila ubah nama imej.'
|
||||
ErrDelWithWebsite: 'Persekitaran pengendalian sudah dikaitkan dengan tapak web dan tidak boleh dipadamkan'
|
||||
ErrRuntimeStart: 'Permulaan gagal'
|
||||
ErrPackageJsonNotFound: 'fail package.json tidak wujud'
|
||||
ErrScriptsNotFound: 'Item konfigurasi skrip tidak ditemui dalam package.json'
|
||||
ErrContainerNameNotFound: 'Tidak dapat mendapatkan nama kontena, sila semak fail .env'
|
||||
ErrNodeModulesNotFound: 'Folder node_modules tidak wujud! Sila edit persekitaran masa jalan atau tunggu persekitaran masa jalan berjaya dimulakan'
|
||||
ErrContainerNameNotFound: 'Tidak dapat mendapatkan nama kontena, sila semak fail.env'
|
||||
ErrNodeModulesNotFound: 'Folder node_modules tidak wujud Sila edit persekitaran masa jalan atau tunggu persekitaran masa jalan berjaya dimulakan'
|
||||
ErrContainerNameIsNull: 'Nama kontena tidak wujud'
|
||||
ErrPHPPortIsDefault: "Порт 9000 является портом по умолчанию, пожалуйста, измените и попробуйте снова"
|
||||
ErrPHPRuntimePortFailed: "Порт {{ .name }} уже используется текущей средой выполнения, пожалуйста, измените и попробуйте снова"
|
||||
|
||||
#alat
|
||||
ErrPHPPortIsDefault: 'Порт 9000 является портом по умолчанию, пожалуйста, измените и попробуйте снова'
|
||||
ErrPHPRuntimePortFailed: 'Порт {{ .name }} уже используется текущей средой выполнения, пожалуйста, измените и попробуйте снова'
|
||||
ErrConfigNotFound: 'Fail konfigurasi tidak wujud'
|
||||
ErrConfigParse: 'Format fail konfigurasi tidak betul'
|
||||
ErrConfigIsNull: 'Fail konfigurasi tidak boleh kosong'
|
||||
ErrConfigDirNotFound: 'Direktori berjalan tidak wujud'
|
||||
ErrConfigAlreadyExist: 'Fail konfigurasi dengan nama yang sama sudah wujud'
|
||||
ErrUserFindErr: 'Pengguna {{ .name }} carian gagal {{ .err }}'
|
||||
|
||||
#cronjob
|
||||
CutWebsiteLogSuccess: '{{ .name }} log tapak web berjaya dipotong, laluan sandaran {{ .path }}'
|
||||
HandleShell: 'Laksanakan skrip {{ .name }}'
|
||||
HandleCurl: "Akses URL {{ .name }}"
|
||||
HandleCurl: 'Akses URL {{ .name }}'
|
||||
HandleNtpSync: 'Penyegerakan masa sistem'
|
||||
HandleSystemClean: 'Pembersihan cache sistem'
|
||||
SystemLog: 'Log Sistem'
|
||||
CutWebsiteLog: 'Putar Log Laman Web'
|
||||
FileOrDir: 'Direktori / Fail'
|
||||
UploadFile: 'Muat naik fail sandaran {{ .file }} ke {{ .backup }}'
|
||||
Upload: "Muat Naik"
|
||||
IgnoreBackupErr: "Sandaran gagal, ralat: {{ .detail }}, abaikan ralat ini..."
|
||||
IgnoreUploadErr: "Muat naik gagal, ralat: {{ .detail }}, abaikan ralat ini..."
|
||||
LoadBackupFailed: "Gagal mendapatkan sambungan akaun sandaran, ralat: {{ .detail }}"
|
||||
InExecuting: "Tugas semasa sedang dilaksanakan, jangan ulangi pelaksanaan!"
|
||||
NoSuchResource: "Tiada kandungan sandaran ditemui dalam pangkalan data, dilangkau..."
|
||||
CleanLog: "Bersihkan log"
|
||||
CleanLogByName: "Bersihkan log {{.name}}"
|
||||
WafIpGroupNotFound: "Kumpulan IP WAF tidak ditemui. Sila pergi ke [Ciri Lanjutan - WAF - Senarai Hitam/Putih - Kumpulan IP] untuk menambah kumpulan IP melalui kaedah muat turun jauh."
|
||||
|
||||
#kotak alat
|
||||
ErrNotExistUser: 'Pengguna semasa tidak wujud, sila ubah suai dan cuba lagi!'
|
||||
ErrBanAction: 'Tetapan gagal. Perkhidmatan {{ .name }} semasa tidak tersedia. Sila semak dan cuba lagi!'
|
||||
ErrClamdscanNotFound: 'Arahan clamdscan tidak dikesan, sila rujuk dokumentasi untuk memasangnya!'
|
||||
TaskSwapSet: "Tetapkan Swap"
|
||||
SetSwap: "Tetapkan Swap {{ .path }} - {{ .size }}"
|
||||
CreateSwap: "Buat Fail Swap"
|
||||
FormatSwap: "Format Fail Swap"
|
||||
EnableSwap: "Dayakan Swap"
|
||||
|
||||
#tamper
|
||||
CleanTamperSetting: "Bersihkan tetapan perlindungan sejarah"
|
||||
SetTamperWithRules: "Mulakan perlindungan direktori mengikut peraturan"
|
||||
ProtectDir: "Lindungi direktori {{ .name }}"
|
||||
IgnoreFile: "Lindungi fail {{ .name }}"
|
||||
TamperSetSuccessful: "Tetapan perlindungan selesai, mula memantau direktori berikut:"
|
||||
|
||||
#waf
|
||||
Upload: 'Muat Naik'
|
||||
IgnoreBackupErr: 'Sandaran gagal, ralat: {{ .detail }}, abaikan ralat ini...'
|
||||
IgnoreUploadErr: 'Muat naik gagal, ralat: {{ .detail }}, abaikan ralat ini...'
|
||||
LoadBackupFailed: 'Gagal mendapatkan sambungan akaun sandaran, ralat: {{ .detail }}'
|
||||
InExecuting: 'Tugas semasa sedang dilaksanakan, jangan ulangi pelaksanaan'
|
||||
NoSuchResource: 'Tiada kandungan sandaran ditemui dalam pangkalan data, dilangkau...'
|
||||
CleanLog: 'Bersihkan log'
|
||||
CleanLogByName: 'Bersihkan log {{ .name }}'
|
||||
WafIpGroupNotFound: 'Kumpulan IP WAF tidak ditemui. Sila pergi ke [Ciri Lanjutan - WAF - Senarai Hitam/Putih - Kumpulan IP] untuk menambah kumpulan IP melalui kaedah muat turun jauh.'
|
||||
ErrNotExistUser: 'Pengguna semasa tidak wujud, sila ubah suai dan cuba lagi'
|
||||
ErrBanAction: 'Tetapan gagal. Perkhidmatan {{ .name }} semasa tidak tersedia. Sila semak dan cuba lagi'
|
||||
ErrClamdscanNotFound: 'Arahan clamdscan tidak dikesan, sila rujuk dokumentasi untuk memasangnya'
|
||||
TaskSwapSet: 'Tetapkan Swap'
|
||||
SetSwap: 'Tetapkan Swap {{ .path }} - {{ .size }}'
|
||||
CreateSwap: 'Buat Fail Swap'
|
||||
FormatSwap: 'Format Fail Swap'
|
||||
EnableSwap: 'Dayakan Swap'
|
||||
CleanTamperSetting: 'Bersihkan tetapan perlindungan sejarah'
|
||||
SetTamperWithRules: 'Mulakan perlindungan direktori mengikut peraturan'
|
||||
ProtectDir: 'Lindungi direktori {{ .name }}'
|
||||
IgnoreFile: 'Lindungi fail {{ .name }}'
|
||||
TamperSetSuccessful: 'Tetapan perlindungan selesai, mula memantau direktori berikut:'
|
||||
ErrScope: 'Mengubah suai konfigurasi ini tidak disokong'
|
||||
ErrStateChange: 'Perubahan keadaan gagal'
|
||||
ErrRuleExist: 'Peraturan sudah wujud'
|
||||
@@ -299,7 +300,7 @@ ErrRuleNotExist: 'Peraturan tidak wujud'
|
||||
ErrParseIP: 'Format IP salah'
|
||||
ErrDefaultIP: 'default ialah nama simpanan, sila tukar kepada nama lain'
|
||||
ErrGroupInUse: 'Kumpulan IP digunakan oleh senarai hitam/senarai putih dan tidak boleh dipadamkan'
|
||||
ErrIPGroupAclUse: "Kumpulan IP digunakan oleh peraturan tersuai tapak web {{ .name }}, tidak boleh dipadamkan"
|
||||
ErrIPGroupAclUse: 'Kumpulan IP digunakan oleh peraturan tersuai tapak web {{ .name }}, tidak boleh dipadamkan'
|
||||
ErrGroupExist: 'Nama kumpulan IP sudah wujud'
|
||||
ErrIPRange: 'Julat IP salah'
|
||||
ErrIPExist: 'IP sudah wujud'
|
||||
@@ -310,11 +311,11 @@ xss: 'XSS'
|
||||
phpExec: 'Pelaksanaan skrip PHP'
|
||||
oneWordTrojan: 'One Word Trojan'
|
||||
appFilter: 'Gunakan penapisan direktori berbahaya'
|
||||
webshell: 'Webshell'
|
||||
webShell: 'Webshell'
|
||||
args: 'Peraturan parameter'
|
||||
protocolFilter: 'Penapisan protokol'
|
||||
javaFileter: 'Penapis fail berbahaya Java'
|
||||
Penapis pengimbas: 'Penapis pengimbas'
|
||||
javaFilter: 'Penapis fail berbahaya Java'
|
||||
scannerFilter: 'Penapis pengimbas'
|
||||
escapeFilter: 'penapis melarikan diri'
|
||||
customRule: 'Peraturan tersuai'
|
||||
httpMethod: 'Penapisan kaedah HTTP'
|
||||
@@ -343,10 +344,9 @@ ErrDBNotExist: 'Pangkalan data tidak wujud'
|
||||
allow: 'membenarkan'
|
||||
deny: 'menafikan'
|
||||
OpenrestyNotFound: 'Openresty tidak dipasang'
|
||||
remoteIpIsNull: "Senarai IP kosong"
|
||||
OpenrestyVersionErr: "Versi Openresty terlalu rendah, sila naik taraf Openresty ke 1.27.1.2-2-2-focal"
|
||||
|
||||
#tugas
|
||||
remoteIpIsNull: 'Senarai IP kosong'
|
||||
OpenrestyVersionErr: 'Versi Openresty terlalu rendah, sila naik taraf Openresty ke 1.27.1.2-2-2-focal'
|
||||
ErrFileTooLarge: 'File is larger than 1 MB and cannot be uploaded'
|
||||
TaskStart: '{{ .name }} Task mula [MULA]'
|
||||
TaskEnd: '{{ .name }} Task selesai [SELESAI]'
|
||||
TaskFailed: 'tugas {{ .name }} gagal'
|
||||
@@ -362,7 +362,7 @@ TaskDelete: 'Padam'
|
||||
TaskUpgrade: 'Naik taraf'
|
||||
TaskUpdate: 'Kemas kini'
|
||||
TaskRestart: 'Mulakan semula'
|
||||
TaskProtect: "Lindungi"
|
||||
TaskProtect: 'Lindungi'
|
||||
TaskBackup: 'Sandaran'
|
||||
TaskRecover: 'Pulihkan'
|
||||
TaskRollback: 'Rollback'
|
||||
@@ -370,10 +370,10 @@ TaskPull: 'Tarik'
|
||||
TaskCommit: 'Komit'
|
||||
TaskBuild: 'Bina'
|
||||
TaskPush: 'Tolak'
|
||||
TaskClean: "Pembersihan"
|
||||
TaskClean: 'Pembersihan'
|
||||
TaskHandle: 'Laksanakan'
|
||||
TaskImport: "Import"
|
||||
TaskExport: "Eksport"
|
||||
TaskImport: 'Import'
|
||||
TaskExport: 'Eksport'
|
||||
Website: 'Laman web'
|
||||
App: 'Aplikasi'
|
||||
Runtime: 'Persekitaran masa jalan'
|
||||
@@ -405,18 +405,12 @@ RuntimeExtension: 'Sambungan Persekitaran Runtime'
|
||||
TaskIsExecuting: 'Tugas sedang berjalan'
|
||||
CustomAppstore: 'Gudang aplikasi tersuai'
|
||||
TaskExec: 'Laksanakan'
|
||||
TaskBatch: "Operasi Batch"
|
||||
TaskBatch: 'Operasi Batch'
|
||||
FileConvert: 'Penukaran Format Fail'
|
||||
|
||||
# task - clam
|
||||
Clamscan: "Imbas {{ .name }}"
|
||||
TaskScan: "Imbas"
|
||||
|
||||
# tugasan - ai
|
||||
Clamscan: 'Imbas {{ .name }}'
|
||||
TaskScan: 'Imbas'
|
||||
OllamaModelPull: 'Tarik model Ollama {{ .name }}'
|
||||
OllamaModelSize: 'Dapatkan saiz model Ollama {{ .name }}'
|
||||
|
||||
# gambar tugasan
|
||||
Snapshot: 'Snapshot'
|
||||
SnapDBInfo: 'Tulis maklumat pangkalan data 1Panel'
|
||||
SnapCopy: 'Salin fail & direktori {{ .name }}'
|
||||
@@ -442,7 +436,6 @@ SnapCompressSize: 'Saiz fail syot kilat {{ .name }}'
|
||||
SnapUpload: 'Muat naik fail syot kilat'
|
||||
SnapUploadTo: 'Muat naik fail syot kilat ke {{ .name }}'
|
||||
SnapUploadRes: 'Muat naik fail syot kilat ke {{ .name }}'
|
||||
|
||||
SnapshotRecover: 'Snapshot Restore'
|
||||
RecoverDownload: 'Muat turun fail syot kilat'
|
||||
Download: 'Muat turun'
|
||||
@@ -450,10 +443,10 @@ RecoverDownloadAccount: 'Dapatkan syot kilat muat turun akaun sandaran {{ .name
|
||||
RecoverDecompress: 'Nyahmampatkan fail termampat syot kilat'
|
||||
Decompress: 'Penyahmampatan'
|
||||
BackupBeforeRecover: 'Sandarkan data berkaitan sistem sebelum syot kilat'
|
||||
Readjson: 'Baca fail Json dalam petikan'
|
||||
ReadjsonPath: 'Dapatkan laluan fail Json dalam petikan'
|
||||
ReadjsonContent: 'Baca fail Json'
|
||||
ReadjsonMarshal: 'Pemprosesan melarikan diri Json'
|
||||
ReadJson: 'Baca fail Json dalam petikan'
|
||||
ReadJsonPath: 'Dapatkan laluan fail Json dalam petikan'
|
||||
ReadJsonContent: 'Baca fail Json'
|
||||
ReadJsonMarshal: 'Pemprosesan melarikan diri Json'
|
||||
RecoverApp: 'Pulihkan apl yang dipasang'
|
||||
RecoverWebsite: 'Pulihkan direktori tapak web'
|
||||
RecoverAppImage: 'Pulihkan sandaran imej syot kilat'
|
||||
@@ -467,9 +460,6 @@ RecoverDaemonJson: 'Pulihkan fail daemon.json konfigurasi bekas'
|
||||
RecoverDBData: 'Pulihkan data pangkalan data'
|
||||
RecoverBackups: 'Pulihkan direktori sandaran tempatan'
|
||||
RecoverPanelData: 'Direktori data pemulihan'
|
||||
|
||||
# tugas - bekas
|
||||
ContainerNewCliet: 'Memulakan Klien Docker'
|
||||
ContainerImagePull: 'Tarik imej bekas {{ .name }}'
|
||||
ContainerRemoveOld: 'Alih keluar bekas asal {{ .name }}'
|
||||
ContainerImageCheck: 'Periksa sama ada imej ditarik seperti biasa'
|
||||
@@ -478,22 +468,13 @@ ContainerRecreate: 'Kemas kini kontena gagal, kini mula memulihkan bekas asal'
|
||||
ContainerCreate: 'Buat bekas baharu {{ .name }}'
|
||||
ContainerCreateFailed: 'Penciptaan bekas gagal, padamkan bekas yang gagal'
|
||||
ContainerStartCheck: 'Periksa sama ada bekas telah dimulakan'
|
||||
|
||||
# tugas - imej
|
||||
ImageBuild: 'Membina Imej'
|
||||
ImageBuildStdoutCheck: 'Menghuraikan kandungan output imej'
|
||||
ImageBuildRes: 'Output binaan imej: {{ .name }}'
|
||||
ImagePull: 'Tarik imej'
|
||||
ImageRepoAuthFromDB: 'Dapatkan maklumat pengesahan repositori daripada pangkalan data'
|
||||
ImaegPullRes: 'Output tarik imej: {{ .name }}'
|
||||
ImagePush: 'Tolak imej'
|
||||
ImageRenameTag: 'Ubah suai teg imej'
|
||||
ImageNewTag: 'Teg imej baharu {{ .name }}'
|
||||
ImaegPushRes: 'Output tolak imej: {{ .name }}'
|
||||
ComposeCreate: 'Buat gubahan'
|
||||
ComposeCreateRes: 'Karang cipta output: {{ .name }}'
|
||||
|
||||
# tugas - laman web
|
||||
BackupNginxConfig: 'Fail konfigurasi OpenResty tapak web sandaran'
|
||||
CompressFileSuccess: 'Mampatkan direktori berjaya, dimampatkan ke {{ .name }}'
|
||||
CompressDir: 'Direktori mampatan'
|
||||
@@ -504,40 +485,33 @@ websiteDir: 'Direktori Laman Web'
|
||||
RecoverFailedStartRollBack: 'Pemulihan gagal, mulakan rollback'
|
||||
AppBackupFileIncomplete: 'Fail sandaran tidak lengkap dan tidak mempunyai fail app.json atau app.tar.gz'
|
||||
AppAttributesNotMatch: 'Jenis atau nama aplikasi tidak sepadan'
|
||||
ErrAlertSync: 'Ralat penyegerakan maklumat penggera, sila semak dan cuba lagi'
|
||||
ErrInstallExtension: 'Tugas pemasangan sudah sedang berjalan, silakan tunggu tugas selesai'
|
||||
PanelAlertTitle: 'Notifikasi Amaran Panel'
|
||||
TestAlertTitle: 'E-mel Ujian - Sahkan Sambungan E-mel'
|
||||
TestAlert: 'Ini adalah e-mel ujian untuk mengesahkan konfigurasi penghantaran e-mel anda betul.'
|
||||
LicenseExpirationAlert: 'Panel {{ .node }}{{ .ip }} Anda, lesen akan tamat tempoh dalam {{ .day }} hari. Sila log masuk ke panel untuk butiran lanjut.'
|
||||
CronJobFailedAlert: 'Panel {{ .node }}{{ .ip }} Anda, tugas jadual {{ .name }} gagal dilaksanakan. Sila log masuk ke panel untuk butiran lanjut.'
|
||||
ClamAlert: 'Panel {{ .node }}{{ .ip }} Anda, tugas imbasan virus mengesan {{ .num }} fail yang dijangkiti. Sila log masuk ke panel untuk butiran lanjut.'
|
||||
WebSiteAlert: 'Panel {{ .node }}{{ .ip }} Anda, {{ .num }} laman web akan tamat tempoh dalam {{ .day }} hari. Sila log masuk ke panel untuk butiran lanjut.'
|
||||
SSLAlert: 'Panel {{ .node }}{{ .ip }} Anda, {{ .num }} sijil SSL akan tamat tempoh dalam {{ .day }} hari. Sila log masuk ke panel untuk butiran lanjut.'
|
||||
DiskUsedAlert: 'Panel {{ .node }}{{ .ip }} Anda, penggunaan cakera {{ .name }} telah mencapai {{ .used }}. Sila log masuk ke panel untuk butiran lanjut.'
|
||||
ResourceAlert: 'Panel {{ .node }}{{ .ip }} Anda, kadar penggunaan purata {{ .name }} dalam tempoh {{ .time }} minit ialah {{ .used }}. Sila log masuk ke panel untuk butiran lanjut.'
|
||||
PanelVersionAlert: 'Panel {{ .node }}{{ .ip }} Anda, versi panel terkini tersedia untuk dinaik taraf. Sila log masuk ke panel untuk butiran lanjut.'
|
||||
PanelPwdExpirationAlert: 'Panel {{ .node }}{{ .ip }} Anda, kata laluan panel akan tamat tempoh dalam {{ .day }} hari. Sila log masuk ke panel untuk butiran lanjut.'
|
||||
CommonAlert: 'Panel {{ .node }}{{ .ip }} Anda, {{ .msg }}. Sila log masuk ke panel untuk butiran lanjut.'
|
||||
NodeExceptionAlert: 'Panel {{ .node }}{{ .ip }} Anda, {{ .num }} nod tidak normal. Sila log masuk ke panel untuk butiran lanjut.'
|
||||
LicenseExceptionAlert: 'Panel {{ .node }}{{ .ip }} Anda, {{ .num }} lesen tidak normal. Sila log masuk ke panel untuk butiran lanjut.'
|
||||
SSHAndPanelLoginAlert: 'Panel {{ .node }}{{ .ip }} Anda, log masuk {{ .name }} dari {{ .loginIp }} tidak normal. Sila log masuk ke panel untuk butiran lanjut.'
|
||||
DeviceNotFound: 'Peranti {{ .name }} tidak ditemui'
|
||||
DeviceIsMounted: 'Peranti {{ .name }} telah dikaitkan, sila nyahkaitkan terlebih dahulu'
|
||||
PartitionDiskErr: 'Gagal membahagikan, {{ .err }}'
|
||||
FormatDiskErr: 'Gagal memformat cakera, {{ .err }}'
|
||||
MountDiskErr: 'Gagal mengkaitkan cakera, {{ .err }}'
|
||||
XfsNotFound: 'Sistem fail xfs tidak dikesan, sila pasang xfsprogs terlebih dahulu'
|
||||
|
||||
#alert
|
||||
ErrAlert: 'Format mesej amaran tidak betul, sila semak dan cuba lagi!'
|
||||
ErrAlertPush: 'Ralat dalam menolak maklumat amaran, sila semak dan cuba lagi!'
|
||||
ErrAlertSave: 'Ralat menyimpan maklumat penggera, sila semak dan cuba lagi!'
|
||||
ErrAlertSync: 'Ralat penyegerakan maklumat penggera, sila semak dan cuba lagi!'
|
||||
ErrAlertRemote: 'Ralat jauh mesej penggera, sila semak dan cuba lagi!'
|
||||
|
||||
#task - runtime
|
||||
ErrInstallExtension: "Tugas pemasangan sudah sedang berjalan, silakan tunggu tugas selesai"
|
||||
|
||||
# alert mail template
|
||||
PanelAlertTitle: "Notifikasi Amaran Panel"
|
||||
TestAlertTitle: "E-mel Ujian - Sahkan Sambungan E-mel"
|
||||
TestAlert: "Ini adalah e-mel ujian untuk mengesahkan konfigurasi penghantaran e-mel anda betul."
|
||||
LicenseExpirationAlert: "Panel {{ .node }}{{ .ip }} Anda, lesen akan tamat tempoh dalam {{ .day }} hari. Sila log masuk ke panel untuk butiran lanjut."
|
||||
CronJobFailedAlert: "Panel {{ .node }}{{ .ip }} Anda, tugas jadual {{ .name }} gagal dilaksanakan. Sila log masuk ke panel untuk butiran lanjut."
|
||||
ClamAlert: "Panel {{ .node }}{{ .ip }} Anda, tugas imbasan virus mengesan {{ .num }} fail yang dijangkiti. Sila log masuk ke panel untuk butiran lanjut."
|
||||
WebSiteAlert: "Panel {{ .node }}{{ .ip }} Anda, {{ .num }} laman web akan tamat tempoh dalam {{ .day }} hari. Sila log masuk ke panel untuk butiran lanjut."
|
||||
SSLAlert: "Panel {{ .node }}{{ .ip }} Anda, {{ .num }} sijil SSL akan tamat tempoh dalam {{ .day }} hari. Sila log masuk ke panel untuk butiran lanjut."
|
||||
DiskUsedAlert: "Panel {{ .node }}{{ .ip }} Anda, penggunaan cakera {{ .name }} telah mencapai {{ .used }}. Sila log masuk ke panel untuk butiran lanjut."
|
||||
ResourceAlert: "Panel {{ .node }}{{ .ip }} Anda, kadar penggunaan purata {{ .name }} dalam tempoh {{ .time }} minit ialah {{ .used }}. Sila log masuk ke panel untuk butiran lanjut."
|
||||
PanelVersionAlert: "Panel {{ .node }}{{ .ip }} Anda, versi panel terkini tersedia untuk dinaik taraf. Sila log masuk ke panel untuk butiran lanjut."
|
||||
PanelPwdExpirationAlert: "Panel {{ .node }}{{ .ip }} Anda, kata laluan panel akan tamat tempoh dalam {{ .day }} hari. Sila log masuk ke panel untuk butiran lanjut."
|
||||
CommonAlert: "Panel {{ .node }}{{ .ip }} Anda, {{ .msg }}. Sila log masuk ke panel untuk butiran lanjut."
|
||||
NodeExceptionAlert: "Panel {{ .node }}{{ .ip }} Anda, {{ .num }} nod tidak normal. Sila log masuk ke panel untuk butiran lanjut."
|
||||
LicenseExceptionAlert: "Panel {{ .node }}{{ .ip }} Anda, {{ .num }} lesen tidak normal. Sila log masuk ke panel untuk butiran lanjut."
|
||||
SSHAndPanelLoginAlert: "Panel {{ .node }}{{ .ip }} Anda, log masuk {{ .name }} dari {{ .loginIp }} tidak normal. Sila log masuk ke panel untuk butiran lanjut."
|
||||
|
||||
#disk
|
||||
DeviceNotFound: "Peranti {{ .name }} tidak ditemui"
|
||||
DeviceIsMounted: "Peranti {{ .name }} telah dikaitkan, sila nyahkaitkan terlebih dahulu"
|
||||
PartitionDiskErr: "Gagal membahagikan, {{ .err }}"
|
||||
FormatDiskErr: "Gagal memformat cakera, {{ .err }}"
|
||||
MountDiskErr: "Gagal mengkaitkan cakera, {{ .err }}"
|
||||
UnMountDiskErr: "Gagal nyahkaitkan cakera, {{ .err }}"
|
||||
XfsNotFound: "Sistem fail xfs tidak dikesan, sila pasang xfsprogs terlebih dahulu"
|
||||
# terminal
|
||||
TerminalAIBlockedRiskyCommand: 'Perintah berisiko telah disekat: {{ .command }}'
|
||||
TerminalAIThinking: 'AI sedang berfikir...'
|
||||
TerminalAIReadyToExecute: 'Pemikiran selesai, tekan Enter untuk jalankan (tempoh: {{ .duration }}, token: {{ .tokens }})'
|
||||
TerminalAIRequestFailed: 'Permintaan AI gagal: {{ .err }}'
|
||||
|
||||
+218
-244
@@ -1,34 +1,30 @@
|
||||
ErrInvalidParams: 'Erro de parâmetro de solicitação: {{ .detail }}'
|
||||
ErrTokenParse: 'Erro de geração de token: {{ .detail }}'
|
||||
ErrInitialPassword: 'A senha original está incorreta'
|
||||
ErrInternalServer: 'Erro interno do servidor: {{ .detail }}'
|
||||
ErrRecordExist: 'O registro já existe'
|
||||
ErrInvalidParams: 'Requisição inválida: {{ .detail }}'
|
||||
ErrTokenParse: 'Erro de token: {{ .detail }}'
|
||||
ErrInitialPassword: 'Senha original incorreta'
|
||||
ErrInternalServer: 'Erro interno: {{ .detail }}'
|
||||
ErrWgetRemoteFailed: 'Falha no download remoto ou URL inacessível: {{ .detail }}'
|
||||
ErrWgetInvalidContentType: 'A URL remota não retornou um arquivo (possivelmente uma página de erro), {{ .detail }}'
|
||||
ErrRecordExist: 'Registro já existe'
|
||||
ErrRecordNotFound: 'Registro não encontrado'
|
||||
ErrStructTransform: 'Falha na conversão de tipo: {{ .err }}'
|
||||
ErrNotLogin: 'Usuário não logado: {{ .detail }}'
|
||||
ErrPasswordExpired: 'A senha atual expirou: {{ .detail }}'
|
||||
ErrNotSupportType: 'O sistema não suporta o tipo atual: {{ .name }}'
|
||||
ErrProxy: 'Erro de solicitação, verifique o status do nó: {{ .detail }}'
|
||||
ErrApiConfigStatusInvalid: 'O acesso à interface da API é proibido: {{ .detail }}'
|
||||
ErrApiConfigKeyInvalid: 'Erro de chave da interface da API: {{ .detail }}'
|
||||
ErrApiConfigIPInvalid: 'O IP usado para chamar a interface da API não está na lista de permissões: {{ .detail }}'
|
||||
ErrApiConfigDisable: 'Esta interface proíbe o uso de chamadas de interface de API: {{ .detail }}'
|
||||
ErrApiConfigKeyTimeInvalid: 'Erro de registro de data e hora da interface da API: {{ .detail }}'
|
||||
StartPushSSLToNode: "Iniciando o envio do certificado para o nó"
|
||||
PushSSLToNodeFailed: "Falha ao enviar o certificado para o nó: {{ .err }}"
|
||||
PushSSLToNodeSuccess: "Certificado enviado com sucesso para o nó"
|
||||
ErrShutDown: "Comando terminado manualmente!"
|
||||
|
||||
ErrMinQuickJump: "Defina pelo menos uma entrada de salto rápido!"
|
||||
ErrMaxQuickJump: "Você pode definir até quatro entradas de salto rápido!"
|
||||
|
||||
#comum
|
||||
ErrUsernameIsExist: 'Nome de usuário já existe'
|
||||
ErrStructTransform: 'Erro de conversão: {{ .err }}'
|
||||
ErrNotLogin: 'Sem sessão: {{ .detail }}'
|
||||
ErrPasswordExpired: 'Senha expirada: {{ .detail }}'
|
||||
ErrNotSupportType: 'Tipo não suportado: {{ .name }}'
|
||||
ErrProxy: 'Requisição falhou: {{ .detail }}'
|
||||
ErrApiConfigStatusInvalid: 'API desativada: {{ .detail }}'
|
||||
ErrApiConfigKeyInvalid: 'Chave API inválida: {{ .detail }}'
|
||||
ErrApiConfigIPInvalid: 'IP sem permissão: {{ .detail }}'
|
||||
ErrApiConfigDisable: 'API bloqueada: {{ .detail }}'
|
||||
ErrApiConfigKeyTimeInvalid: 'Timestamp inválido: {{ .detail }}'
|
||||
ErrShutDown: 'Comando parado manualmente'
|
||||
ErrMinQuickJump: 'Adicione pelo menos um atalho rápido'
|
||||
ErrMaxQuickJump: 'Máximo 4 atalhos rápidos'
|
||||
ErrUsernameIsExist: 'Usuário já existe'
|
||||
ErrNameIsExist: 'Nome já existe'
|
||||
ErrDemoEnvironment: 'Servidor de demonstração, esta operação é proibida!'
|
||||
ErrCmdTimeout: 'Tempo limite de execução do comando expirou!'
|
||||
ErrCmdIllegal: 'Há caracteres ilegais no comando de execução, modifique-o e tente novamente!'
|
||||
ErrPortExist: 'A porta {{ .port }} já está ocupada por {{ .type }} [{{ .name }}]'
|
||||
ErrDemoEnvironment: 'Indisponível em demo'
|
||||
ErrCmdTimeout: 'Comando expirou'
|
||||
ErrCmdIllegal: 'Comando tem caracteres inválidos'
|
||||
ErrPortExist: 'Porta {{ .port }} já ocupada por {{ .type }} [{{ .name }}]'
|
||||
TYPE_APP: 'Aplicativo'
|
||||
TYPE_RUNTIME: 'Ambiente de tempo de execução'
|
||||
TYPE_DOMAIN: 'Nome de domínio'
|
||||
@@ -39,45 +35,43 @@ Failed: 'Falhou'
|
||||
SystemRestart: 'Tarefa interrompida devido à reinicialização do sistema'
|
||||
ErrGroupIsDefault: 'Grupo padrão, não pode ser excluído'
|
||||
ErrGroupIsInWebsiteUse: 'O grupo está sendo usado por outro site e não pode ser excluído.'
|
||||
Decrypt: "Descriptografar"
|
||||
|
||||
#agent
|
||||
ErrAgentAccountBound: 'Esta conta está vinculada a um agente e não pode ser excluída. Verifique e tente novamente!'
|
||||
ErrAgentAccountUnavailable: 'As informações de conexão da conta não estão disponíveis. Erro: {{ .err }}. Verifique e tente novamente!'
|
||||
ErrAgentProviderNotSupported: 'Este provedor de agente não é suportado. Verifique e tente novamente!'
|
||||
ErrAgentAccountRequired: 'Selecione uma conta de agente e tente novamente.'
|
||||
ErrAgentAccountNotVerified: 'A conta não foi verificada. Verifique e tente novamente!'
|
||||
ErrAgentProviderMismatch: 'O provedor da conta não corresponde. Verifique e tente novamente!'
|
||||
ErrAgentBaseURLRequired: 'Base URL não pode estar vazio. Verifique e tente novamente!'
|
||||
ErrAgentApiKeyRequired: 'API Key não pode estar vazio. Verifique e tente novamente!'
|
||||
ErrAgentComposeRequired: 'O conteúdo personalizado do compose não pode estar vazio. Verifique e tente novamente!'
|
||||
ErrAgentIDRequired: 'ID do agente não pode estar vazio. Verifique e tente novamente!'
|
||||
ErrAgentAccountIDRequired: 'ID da conta não pode estar vazio. Verifique e tente novamente!'
|
||||
|
||||
#backup
|
||||
Decrypt: 'Descriptografar'
|
||||
ErrAgentAccountBound: 'Conta vinculada a agente'
|
||||
ErrAgentAccountUnavailable: 'Conexão da conta indisponível: {{ .err }}'
|
||||
ErrAgentProviderNotSupported: 'Provedor de agente não suportado'
|
||||
ErrAgentAccountRequired: 'Selecione uma conta de agente primeiro'
|
||||
ErrAgentAccountNotVerified: 'Conta de agente não verificada'
|
||||
ErrAgentAccountModelsRequired: 'Configure pelo menos um modelo'
|
||||
ErrAgentAccountSingleInitialModel: 'Apenas um modelo inicial pode ser configurado ao criar a conta'
|
||||
ErrAgentModelNotInAccount: 'O modelo não está configurado na conta selecionada'
|
||||
ErrAgentModelInUse: 'Ainda há um agente vinculado usando este modelo: {{ .name }}'
|
||||
ErrAgentBaseURLRequired: 'URL base obrigatória'
|
||||
ErrAgentApiKeyRequired: 'Chave API obrigatória'
|
||||
ErrAgentComposeRequired: 'Conteúdo de compose obrigatório'
|
||||
ErrAgentIDRequired: 'ID do agente obrigatório'
|
||||
ErrAgentAccountIDRequired: 'ID da conta obrigatório'
|
||||
ErrAgentLimitReached: 'A edição Community suporta até {{ .max }} agentes de IA. Atualize para a edição Professional para remover o limite'
|
||||
Localhost: 'Máquina Local'
|
||||
ErrBackupInUsed: 'A conta de backup foi usada na tarefa agendada e não pode ser excluída.'
|
||||
ErrBackupCheck: 'Falha na conexão de teste da conta de backup {{ .err }}'
|
||||
ErrBackupLocalDelete: 'A exclusão da conta de backup do servidor local ainda não é suportada'
|
||||
ErrBackupLocalCreate: 'A criação de contas de backup do servidor local ainda não é suportada'
|
||||
|
||||
#aplicativo
|
||||
ErrPortInUsed: 'A porta {{ .detail }} já está ocupada!'
|
||||
ErrBackupInUsed: 'Conta de backup em uso por tarefa'
|
||||
ErrBackupCheck: 'Teste de conexão falhou: {{ .err }}'
|
||||
ErrBackupLocalDelete: 'Não é permitido excluir contas locais'
|
||||
ErrBackupLocalCreate: 'Não é permitido criar contas locais'
|
||||
ErrPortInUsed: 'A porta {{ .detail }} já está ocupada'
|
||||
ErrAppLimit: 'O número de aplicativos instalados excedeu o limite'
|
||||
ErrNotInstall: 'Aplicativo não instalado'
|
||||
ErrPortInOtherApp: 'A porta {{ .port }} já está ocupada pelo aplicativo {{ .apps }}!'
|
||||
ErrDbUserNotValid: 'Banco de dados existente, nome de usuário e senha não correspondem!'
|
||||
ErrUpdateBuWebsite: 'O aplicativo foi atualizado com sucesso, mas a modificação do arquivo de configuração do site falhou. Por favor, verifique a configuração! '
|
||||
Err1PanelNetworkFailed: 'Falha na criação da rede de contêiner padrão! {{ .detalhe }}'
|
||||
ErrFileParse: 'Falha na análise do arquivo docker-compose do aplicativo!'
|
||||
ErrPortInOtherApp: 'A porta {{ .port }} já está ocupada pelo aplicativo {{ .apps }}'
|
||||
ErrDbUserNotValid: 'Banco de dados existente, nome de usuário e senha não correspondem'
|
||||
ErrUpdateBuWebsite: 'O aplicativo foi atualizado com sucesso, mas a modificação do arquivo de configuração do site falhou., verifique a configuração'
|
||||
Err1PanelNetworkFailed: 'Falha na criação da rede de contêiner padrão {{ .detalhe }}'
|
||||
ErrFileParse: 'Falha na análise do arquivo docker-compose do aplicativo'
|
||||
ErrInstallDirNotFound: 'O diretório de instalação não existe. Se precisar desinstalar, selecione Forçar desinstalação'
|
||||
AppStoreIsUpToDate: 'A loja de aplicativos já é a versão mais recente'
|
||||
LocalAppVersionNull: 'O aplicativo {{ .name }} não está sincronizado com a versão! Não é possível adicionar à lista de aplicativos'
|
||||
LocalAppVersionErr: '{{ .name }} sincronização da versão {{ .version }} falhou! {{ .err }}'
|
||||
LocalAppVersionNull: 'O aplicativo {{ .name }} não está sincronizado com a versão Não é possível adicionar à lista de aplicativos'
|
||||
LocalAppVersionErr: '{{ .name }} sincronização da versão {{ .version }} falhou {{ .err }}'
|
||||
ErrFileNotFound: 'arquivo {{ .name }} não existe'
|
||||
ErrFileParseApp: 'Falha na análise do arquivo {{ .name }} {{ .err }}'
|
||||
ErrAppDirNull: 'A pasta da versão não existe'
|
||||
LocalAppErr: 'Falha na sincronização do aplicativo {{ .name }}! {{ .err }}'
|
||||
LocalAppErr: 'Falha na sincronização do aplicativo {{ .name }} {{ .err }}'
|
||||
ErrContainerName: 'O nome do contêiner já existe'
|
||||
ErrCreateHttpClient: 'Falha ao criar solicitação {{ .err }}'
|
||||
ErrHttpReqTimeOut: 'Tempo limite da solicitação esgotado {{ .err }}'
|
||||
@@ -85,69 +79,63 @@ ErrHttpReqFailed: 'Falha na solicitação {{ .err }}'
|
||||
ErrNoSuchHost: 'Não foi possível encontrar o servidor solicitado {{ .err }}'
|
||||
ErrHttpReqNotFound: 'O recurso solicitado {{ .err }} não pôde ser encontrado'
|
||||
ErrContainerNotFound: '{{ .name }} container não existe'
|
||||
ErrContainerMsg: 'O contêiner {{ .name }} está anormal. Por favor, verifique o log na página do contêiner para obter detalhes.'
|
||||
ErrContainerMsg: 'O contêiner {{ .name }} está anormal., verifique o log na página do contêiner para obter detalhes.'
|
||||
ErrAppBackup: '{{ .name }} falha no backup do aplicativo {{ .err }}'
|
||||
ErrVersionTooLow: 'A versão atual do 1Panel é muito antiga para atualizar a App Store. Por favor, atualize a versão antes de operar.'
|
||||
ErrVersionTooLow: 'A versão atual do 1Panel é muito antiga para atualizar a App Store., atualize a versão antes de operar.'
|
||||
ErrAppNameExist: 'O nome do aplicativo já existe'
|
||||
AppStoreIsSyncing: 'A App Store está sincronizando, tente novamente mais tarde'
|
||||
ErrGetCompose: 'Falha ao obter o arquivo docker-compose.yml! {{ .detail }}'
|
||||
AppStoreIsSyncing: 'A App Store está sincronizando mais tarde'
|
||||
ErrGetCompose: 'Falha ao obter o arquivo docker-compose.yml {{ .detail }}'
|
||||
ErrAppWarn: 'Status anormal, verifique o log'
|
||||
ErrAppParamKey: 'O campo de parâmetro {{ .name }} está anormal'
|
||||
ErrAppUpgrade: 'Falha na atualização do aplicativo {{ .name }} {{ .err }}'
|
||||
AppRecover: 'Reverter aplicativo {{ .name }}'
|
||||
PullImageStart: 'Comece a extrair a imagem [{{ .name }}]'
|
||||
PullImageSuccess: 'Imagem retirada com sucesso'
|
||||
AppStoreIsLastVersion: 'A App Store já é a versão mais recente'
|
||||
AppStoreSyncSuccess: 'Sincronização da App Store bem-sucedida'
|
||||
SyncAppDetail: 'Sincronizar configuração do aplicativo'
|
||||
AppVersionNotMatch: 'o aplicativo {{ .name }} requer uma versão superior do 1Panel, ignorando a sincronização'
|
||||
MoveSiteDir: "Detecção de alteração no diretório do site. É necessário migrar o diretório do site OpenResty para {{ .name }}"
|
||||
MoveSiteDir: 'Detecção de alteração no diretório do site. É necessário migrar o diretório do site OpenResty para {{ .name }}'
|
||||
MoveSiteDirSuccess: 'Migração bem-sucedida do diretório do site'
|
||||
DeleteRuntimePHP: 'Excluir tempo de execução do PHP'
|
||||
CustomAppStoreFileValid: 'Os pacotes da App Store precisam estar no formato .tar.gz'
|
||||
CustomAppStoreFileValid: 'Os pacotes da App Store precisam estar no formato.tar.gz'
|
||||
PullImageTimeout: 'Tempo limite para puxar imagem, tente aumentar a aceleração da imagem ou altere para outra aceleração de imagem'
|
||||
ErrAppIsDown: 'O status do aplicativo {{ .name }} é anormal, verifique'
|
||||
ErrCustomApps: 'Há um aplicativo instalado, desinstale-o primeiro'
|
||||
ErrCustomRuntimes: 'Há um ambiente de execução instalado, exclua-o primeiro'
|
||||
ErrAppVersionDeprecated: "O aplicativo {{ .name }} não é compatível com a versão atual do 1Panel, ignorado"
|
||||
ErrDockerFailed: "O estado do Docker está anormal, verifique o status do serviço"
|
||||
ErrDockerComposeCmdNotFound: "O comando Docker Compose não existe, por favor, instale este comando na máquina host primeiro"
|
||||
UseExistImage: 'Falha ao puxar a imagem, usando imagem existente'
|
||||
|
||||
#ssh
|
||||
ExportIP: "IP de Login"
|
||||
ExportArea: "Localização"
|
||||
ExportPort: "Porta"
|
||||
ExportAuthMode: "Método de Login"
|
||||
ExportUser: "Usuário"
|
||||
ExportStatus: "Status de Login"
|
||||
ExportDate: "Hora"
|
||||
|
||||
#arquivo
|
||||
ErrAppVersionDeprecated: 'O aplicativo {{ .name }} não é compatível com a versão atual do 1Panel, ignorado'
|
||||
ErrDockerFailed: 'O estado do Docker está anormal, verifique o status do serviço'
|
||||
ErrDockerComposeCmdNotFound: 'O comando Docker Compose não existe, instale este comando na máquina host primeiro'
|
||||
UseExistImage: 'A imagem já existe, usando imagem existente'
|
||||
ErrDatabaseNotFound: 'O banco de dados {{ .name }} não existe'
|
||||
ExportIP: 'IP de Login'
|
||||
ExportArea: 'Localização'
|
||||
ExportPort: 'Porta'
|
||||
ExportAuthMode: 'Método de Login'
|
||||
ExportUser: 'Usuário'
|
||||
ExportStatus: 'Status de Login'
|
||||
ExportDate: 'Hora'
|
||||
ErrFileCanNotRead: 'Este arquivo não suporta visualização'
|
||||
ErrFileToLarge: 'O arquivo é maior que 10M e não pode ser aberto'
|
||||
ErrPathNotFound: 'O diretório não existe'
|
||||
ErrMovePathFailed: 'O caminho de destino não pode conter o caminho original!'
|
||||
ErrLinkPathNotFound: 'O caminho de destino não existe!'
|
||||
ErrFileIsExist: 'O arquivo ou pasta já existe!'
|
||||
ErrMovePathFailed: 'O caminho de destino não pode conter o caminho original'
|
||||
ErrLinkPathNotFound: 'O caminho de destino não existe'
|
||||
ErrFileIsExist: 'O arquivo ou pasta já existe'
|
||||
ErrFileUpload: '{{ .name }} falhou ao carregar o arquivo {{ .detail }}'
|
||||
ErrFileDownloadDir: 'A pasta de download não é suportada'
|
||||
ErrCmdNotFound: 'O comando {{ .name}} não existe, instale este comando no host primeiro'
|
||||
ErrCmdNotFound: 'O comando {{ .name }} não existe, instale este comando no host primeiro'
|
||||
ErrSourcePathNotFound: 'O diretório de origem não existe'
|
||||
ErrFavoriteExist: 'Este caminho já foi favorito'
|
||||
ErrInvalidChar: 'Caracteres ilegais não são permitidos'
|
||||
ErrPathNotDelete: 'O diretório selecionado não pode ser excluído'
|
||||
ErrLogFileToLarge: "Fail log melebihi 500MB, tidak boleh dibuka"
|
||||
FileDropFailed: "Falha ao limpar arquivo {{ .name }}: {{ .err }}"
|
||||
FileDropSuccess: "Arquivo {{ .name }} limpo com sucesso, {{ .count }} arquivos removidos, {{ .size }} de espaço em disco liberado"
|
||||
FileDropSum: "Limpeza de arquivos concluída com sucesso, total de {{ .count }} arquivos removidos, total de {{ .size }} de espaço em disco liberado"
|
||||
ErrBadDecrypt: "Erro de senha de descriptografia do pacote compactado, descompactação falhou, verifique e tente novamente!"
|
||||
|
||||
#site
|
||||
ErrLogFileToLarge: 'Fail log melebihi 500MB, tidak boleh dibuka'
|
||||
FileDropFailed: 'Falha ao limpar arquivo {{ .name }}: {{ .err }}'
|
||||
FileDropSuccess: 'Arquivo {{ .name }} limpo com sucesso, {{ .count }} arquivos removidos, {{ .size }} de espaço em disco liberado'
|
||||
FileDropSum: 'Limpeza de arquivos concluída com sucesso, total de {{ .count }} arquivos removidos, total de {{ .size }} de espaço em disco liberado'
|
||||
ErrBadDecrypt: 'Erro de senha de descriptografia do pacote compactado, descompactação falhou, verifique e'
|
||||
ErrAliasIsExist: 'Alias já existe'
|
||||
ErrBackupMatch: 'O arquivo de backup não corresponde a alguns dados atuais do site {{ .detail }}'
|
||||
ErrBackupExist: 'A parte correspondente dos dados de origem no arquivo de backup não existe {{ .detail }}'
|
||||
ErrPHPResource: 'O ambiente operacional local não suporta comutação! '
|
||||
ErrPHPResource: 'O ambiente operacional local não suporta comutação'
|
||||
ErrPathPermission: 'Uma pasta com permissões diferentes de 1000:1000 foi detectada no diretório de índice, o que pode causar um erro de acesso negado no site. Clique no botão Salvar acima'
|
||||
ErrDomainIsUsed: 'O nome de domínio já está sendo usado pelo site [{{ .name }}]'
|
||||
ErrDomainFormat: 'o formato do nome de domínio {{ .name }} está incorreto'
|
||||
@@ -155,11 +143,11 @@ ErrDefaultAlias: 'padrão é um código reservado, use outro código'
|
||||
ErrParentWebsite: 'Você precisa excluir o subsite {{ .name }} primeiro'
|
||||
ErrBuildDirNotFound: 'O diretório de compilação não existe'
|
||||
ErrImageNotExist: 'A imagem do ambiente operacional {{ .name }} não existe, edite novamente o ambiente operacional'
|
||||
ErrProxyIsUsed: "Balanceamento de carga foi usado por proxy reverso, não pode ser excluído"
|
||||
ErrSSLValid: 'O arquivo do certificado está anormal, verifique o status do certificado!'
|
||||
ErrWebsiteDir: "Por favor, selecione um diretório dentro do diretório do site."
|
||||
ErrComposerFileNotFound: "O arquivo composer.json não existe"
|
||||
ErrRuntimeNoPort: "O ambiente de tempo de execução não está configurado com uma porta, edite o ambiente de tempo de execução primeiro."
|
||||
ErrProxyIsUsed: 'Balanceamento de carga foi usado por proxy reverso, não pode ser excluído'
|
||||
ErrSSLValid: 'O arquivo do certificado está anormal, verifique o status do certificado'
|
||||
ErrWebsiteDir: ', selecione um diretório dentro do diretório do site.'
|
||||
ErrComposerFileNotFound: 'O arquivo composer.json não existe'
|
||||
ErrRuntimeNoPort: 'O ambiente de tempo de execução não está configurado com uma porta, edite o ambiente de tempo de execução primeiro.'
|
||||
Status: 'Status'
|
||||
start: 'Iniciar'
|
||||
stop: 'Parar'
|
||||
@@ -169,14 +157,12 @@ pause: 'Pausar'
|
||||
unpause: 'Retomar'
|
||||
remove: 'Excluir'
|
||||
delete: 'Excluir'
|
||||
ErrDefaultWebsite: 'O site padrão já foi definido, cancele-o antes de definir!'
|
||||
ErrDefaultWebsite: 'O site padrão já foi definido, cancele-o antes de definir'
|
||||
SSL: 'Certificado'
|
||||
Set: 'Configurações'
|
||||
|
||||
#ssl
|
||||
ErrSSLCannotDelete: 'O certificado {{ .name }} está sendo usado por um site e não pode ser excluído'
|
||||
ErrAccountCannotDelete: 'A conta está associada a um certificado e não pode ser excluída'
|
||||
ErrSSLApply: 'Renovação do certificado bem-sucedida, falha na recarga do Openresty, verifique a configuração!'
|
||||
ErrSSLApply: 'Renovação do certificado bem-sucedida, falha na recarga do Openresty, verifique a configuração'
|
||||
ErrEmailIsExist: 'A caixa de correio já existe'
|
||||
ErrSSLKeyNotFound: 'O arquivo de chave privada não existe'
|
||||
ErrSSLCertificateNotFound: 'O arquivo de certificado não existe'
|
||||
@@ -188,8 +174,8 @@ ApplySSLStart: 'Comece a solicitar um certificado, nome de domínio [{{ .domain
|
||||
dnsAccount: 'DNS Automático'
|
||||
dnsManual: 'Manual DNS'
|
||||
http: 'HTTP'
|
||||
ApplySSLFailed: 'Falha na solicitação de certificado [{{ .domain }}], {{ .detail }} '
|
||||
ApplySSLSuccess: 'Solicitação de certificado [{{ .domain }}] realizada com sucesso! ! '
|
||||
ApplySSLFailed: 'Falha na solicitação de certificado [{{ .domain }}], {{ .detail }}'
|
||||
ApplySSLSuccess: 'Solicitação de certificado [{{ .domain }}] realizada com sucesso'
|
||||
DNSAccountName: 'Conta DNS [{{ .name }}] fornecedor [{{ .type }}]'
|
||||
PushDirLog: 'Certificado enviado para o diretório [{{ .path }}] {{ .status }}'
|
||||
ErrDeleteCAWithSSL: 'A organização atual tem um certificado que foi emitido e não pode ser excluído.'
|
||||
@@ -204,94 +190,109 @@ ExecShellSuccess: 'Execução do script bem-sucedida'
|
||||
StartUpdateSystemSSL: 'Iniciar atualização do certificado do sistema'
|
||||
UpdateSystemSSLSuccess: 'Atualizar certificado do sistema com sucesso'
|
||||
ErrWildcardDomain: 'Não é possível solicitar certificado de nome de domínio curinga no modo HTTP'
|
||||
ErrApplySSLCanNotDelete: "O certificado {{.name}} que está sendo solicitado não pode ser excluído, tente novamente mais tarde."
|
||||
|
||||
#mysql
|
||||
ErrApplySSLCanNotDelete: 'O certificado {{ .name }} que está sendo solicitado não pode ser excluído mais tarde.'
|
||||
StartPushSSLToNode: 'Iniciando o envio do certificado para o nó'
|
||||
PushSSLToNodeFailed: 'Falha ao enviar o certificado para o nó: {{ .err }}'
|
||||
PushSSLToNodeSuccess: 'Certificado enviado com sucesso para o nó'
|
||||
ErrUserIsExist: 'O usuário atual já existe, digite novamente'
|
||||
ErrDatabaseIsExist: 'O banco de dados atual já existe, digite novamente'
|
||||
ErrExecTimeOut: 'Tempo limite de execução do SQL expirou, verifique o banco de dados'
|
||||
ErrRemoteExist: 'O banco de dados remoto já existe com este nome, modifique-o e tente novamente'
|
||||
ErrLocalExist: 'O nome já existe no banco de dados local, modifique-o e tente novamente'
|
||||
RemoteBackup: "Para fazer backup do banco de dados remoto, o serviço de banco de dados do contêiner local precisa ser iniciado primeiro usando a imagem {{ .name }}, por favor aguarde..."
|
||||
RemoteRecover: "Para restaurar o banco de dados remoto, o serviço de banco de dados do contêiner local precisa ser iniciado primeiro usando a imagem {{ .name }}, por favor aguarde..."
|
||||
Arg: "Argumento"
|
||||
|
||||
#redis
|
||||
ErrTypeOfRedis: 'O tipo de arquivo de recuperação não corresponde ao método de persistência atual, modifique-o e tente novamente'
|
||||
|
||||
#recipiente
|
||||
ErrInUsed: "{{ .detail }} está em uso e não pode ser excluído!"
|
||||
ErrObjectInUsed: "Este objeto está em uso e não pode ser excluído!"
|
||||
ErrObjectBeDependent: "Esta imagem é dependida por outras imagens e não pode ser excluída!"
|
||||
ErrPortRules: 'O número da porta não corresponde, digite novamente!'
|
||||
ErrPgImagePull: 'Tempo limite para extração de imagem. Configure a aceleração de imagem ou extraia manualmente a imagem {{ .name }} e tente novamente'
|
||||
PruneHelper: "Esta limpeza {{ .name }} liberou espaço em disco {{ .size }}"
|
||||
ImageRemoveHelper: "Excluída a imagem {{ .name }}, liberando {{ .size }} de espaço em disco"
|
||||
BuildCache: "Cache de construção"
|
||||
Volume: "Volume de armazenamento"
|
||||
Network: "Rede"
|
||||
PruneStart: "Limpando em andamento, por favor aguarde..."
|
||||
|
||||
#tempo de execução
|
||||
ErrFileNotExist: 'O arquivo {{ .detail }} não existe! Verifique a integridade do arquivo de origem!'
|
||||
ErrRemoteExist: 'O banco de dados remoto já existe com este nome, modifique-o e'
|
||||
ErrLocalExist: 'O nome já existe no banco de dados local, modifique-o e'
|
||||
RemoteBackup: 'Para fazer backup do banco de dados remoto, o serviço de banco de dados do contêiner local precisa ser iniciado primeiro usando a imagem {{ .name }} aguarde...'
|
||||
RemoteRecover: 'Para restaurar o banco de dados remoto, o serviço de banco de dados do contêiner local precisa ser iniciado primeiro usando a imagem {{ .name }} aguarde...'
|
||||
Arg: 'Argumento'
|
||||
ErrTypeOfRedis: 'O tipo de arquivo de recuperação não corresponde ao método de persistência atual, modifique-o e'
|
||||
ErrInUsed: '{{ .detail }} está em uso e não pode ser excluído'
|
||||
ErrObjectInUsed: 'Este objeto está em uso e não pode ser excluído'
|
||||
ErrObjectBeDependent: 'Esta imagem é dependida por outras imagens e não pode ser excluída'
|
||||
ErrPortRules: 'O número da porta não corresponde, digite novamente'
|
||||
ErrPgImagePull: 'Tempo limite para extração de imagem. Configure a aceleração de imagem ou extraia manualmente a imagem {{ .name }} e'
|
||||
PruneHelper: 'Esta limpeza {{ .name }} liberou espaço em disco {{ .size }}'
|
||||
ImageRemoveHelper: 'Excluída a imagem {{ .name }}, liberando {{ .size }} de espaço em disco'
|
||||
BuildCache: 'Cache de construção'
|
||||
Volume: 'Volume de armazenamento'
|
||||
Network: 'Rede'
|
||||
PruneStart: 'Limpando em andamento aguarde...'
|
||||
ContainerBackupPrepare: 'Preparar área de backup do contêiner'
|
||||
ContainerBackupStop: 'Parar contêiner antes do backup'
|
||||
ContainerBackupInspect: 'Fazer backup do inspect e rede do contêiner'
|
||||
ContainerBackupMounts: 'Fazer backup dos mounts do contêiner'
|
||||
ContainerBackupMeta: 'Escrever metadados do backup do contêiner'
|
||||
ContainerBackupStart: 'Iniciar contêiner após o backup'
|
||||
ContainerRecoverPrepare: 'Preparar área de restauração do contêiner'
|
||||
ContainerRecoverExtract: 'Extrair arquivo de backup do contêiner'
|
||||
ContainerRecoverParse: 'Carregar metadados do backup do contêiner'
|
||||
ContainerRecoverCreate: 'Recriar contêiner'
|
||||
ContainerRecoverMounts: 'Restaurar mounts do contêiner'
|
||||
ContainerRecoverStart: 'Iniciar contêiner restaurado'
|
||||
ContainerRecoverCleanup: 'Limpar área de restauração do contêiner'
|
||||
ComposeBackupPrepare: 'Preparar área de backup do compose'
|
||||
ComposeBackupStop: 'Parar compose antes do backup'
|
||||
ComposeBackupFiles: 'Fazer backup dos arquivos do compose'
|
||||
ComposeBackupContainers: 'Fazer backup dos contêineres do compose'
|
||||
ComposeBackupMeta: 'Escrever metadados do backup do compose'
|
||||
ComposeBackupStart: 'Iniciar compose após o backup'
|
||||
ComposeBackupCleanup: 'Limpar área de backup do compose'
|
||||
ComposeRecoverPrepare: 'Preparar área de restauração do compose'
|
||||
ComposeRecoverExtract: 'Extrair arquivo de backup do compose'
|
||||
ComposeRecoverMeta: 'Carregar metadados do backup do compose'
|
||||
ComposeRecoverMetaLogName: 'Metadados da restauracao do compose: nome={{ .name }}'
|
||||
ComposeRecoverMetaLogPath: 'Metadados da restauracao do compose: caminhoBackup={{ .backupPath }} caminhoDestino={{ .targetDir }}'
|
||||
ComposeRecoverMetaLogCount: 'Metadados da restauracao do compose: arquivos={{ .files }} containers={{ .containers }}'
|
||||
ComposeRecoverFiles: 'Restaurar arquivos do compose'
|
||||
ComposeRecoverContainers: 'Restaurar contêineres do compose'
|
||||
ComposeRecoverRecord: 'Salvar registro do compose'
|
||||
ComposeRecoverCleanup: 'Limpar área de restauração do compose'
|
||||
ErrFileNotExist: 'O arquivo {{ .detail }} não existe Verifique a integridade do arquivo de origem'
|
||||
ErrImageBuildErr: 'Falha na criação da imagem'
|
||||
ErrImageExist: "A imagem já existe! Por favor, modifique o nome da imagem."
|
||||
ErrImageExist: 'A imagem já existe, modifique o nome da imagem.'
|
||||
ErrDelWithWebsite: 'O ambiente operacional já está associado a um site e não pode ser excluído'
|
||||
ErrRuntimeStart: 'Falha na inicialização'
|
||||
ErrPackageJsonNotFound: 'o arquivo package.json não existe'
|
||||
ErrScriptsNotFound: 'O item de configuração de scripts não foi encontrado em package.json'
|
||||
ErrContainerNameNotFound: 'Não foi possível obter o nome do contêiner, verifique o arquivo .env'
|
||||
ErrNodeModulesNotFound: 'A pasta node_modules não existe! Edite o ambiente de execução ou aguarde até que o ambiente de execução inicie com sucesso'
|
||||
ErrContainerNameNotFound: 'Não foi possível obter o nome do contêiner, verifique o arquivo.env'
|
||||
ErrNodeModulesNotFound: 'A pasta node_modules não existe Edite o ambiente de execução ou aguarde até que o ambiente de execução inicie com sucesso'
|
||||
ErrContainerNameIsNull: 'O nome do contêiner não existe'
|
||||
ErrPHPPortIsDefault: "Port 9000 adalah port lalai, sila ubah dan cuba lagi"
|
||||
ErrPHPRuntimePortFailed: "Port {{ .name }} telah digunakan oleh persekitaran runtime semasa, sila ubah dan cuba lagi"
|
||||
|
||||
#ferramenta
|
||||
ErrPHPPortIsDefault: 'Port 9000 adalah port lalai, sila ubah dan cuba lagi'
|
||||
ErrPHPRuntimePortFailed: 'Port {{ .name }} telah digunakan oleh persekitaran runtime semasa, sila ubah dan cuba lagi'
|
||||
ErrConfigNotFound: 'O arquivo de configuração não existe'
|
||||
ErrConfigParse: 'O formato do arquivo de configuração está incorreto'
|
||||
ErrConfigIsNull: 'O arquivo de configuração não pode estar vazio'
|
||||
ErrConfigDirNotFound: 'O diretório de execução não existe'
|
||||
ErrConfigAlreadyExist: 'Um arquivo de configuração com o mesmo nome já existe'
|
||||
ErrUserFindErr: 'Falha na pesquisa do usuário {{ .name }} {{ .err }}'
|
||||
|
||||
#cronjob
|
||||
CutWebsiteLogSuccess: '{{ .name }} registro do site cortado com sucesso, caminho de backup {{ .path }}'
|
||||
HandleShell: 'Executar script {{ .name }}'
|
||||
HandleCurl: "Acessar URL {{ .name }}"
|
||||
HandleCurl: 'Acessar URL {{ .name }}'
|
||||
HandleNtpSync: 'Sincronização de hora do sistema'
|
||||
HandleSystemClean: 'Limpeza de cache do sistema'
|
||||
SystemLog: 'Log do Sistema'
|
||||
CutWebsiteLog: 'Rotacionar Log do Website'
|
||||
FileOrDir: 'Diretório / Arquivo'
|
||||
UploadFile: 'Enviando arquivo de backup {{ .file }} para {{ .backup }}'
|
||||
Upload: "Enviar"
|
||||
IgnoreBackupErr: "Backup falhou, erro: {{ .detail }}, ignorando este erro..."
|
||||
IgnoreUploadErr: "Upload falhou, erro: {{ .detail }}, ignorando este erro..."
|
||||
LoadBackupFailed: "Falha ao obter conexão da conta de backup, erro: {{ .detail }}"
|
||||
InExecuting: "A tarefa atual está sendo executada, não repita a execução!"
|
||||
NoSuchResource: "Nenhum conteúdo de backup encontrado no banco de dados, ignorando..."
|
||||
CleanLog: "Limpar log"
|
||||
CleanLogByName: "Limpar log de {{.name}}"
|
||||
WafIpGroupNotFound: "Grupo de IP do WAF não encontrado. Por favor, vá para [Recursos Avançados - WAF - Lista Branca/Preta - Grupo de IP] para adicionar um grupo de IP via método de download remoto."
|
||||
|
||||
#caixa de ferramentas
|
||||
ErrNotExistUser: 'O usuário atual não existe, modifique e tente novamente!'
|
||||
ErrBanAction: 'Falha na configuração. O serviço atual {{ .name }} não está disponível. Verifique e tente novamente!'
|
||||
ErrClamdscanNotFound: 'O comando clamdscan não foi detectado, consulte a documentação para instalá-lo!'
|
||||
TaskSwapSet: "Configurar Swap"
|
||||
SetSwap: "Configurar Swap {{ .path }} - {{ .size }}"
|
||||
CreateSwap: "Criar Arquivo Swap"
|
||||
FormatSwap: "Formatar Arquivo Swap"
|
||||
EnableSwap: "Ativar Swap"
|
||||
|
||||
#tamper
|
||||
CleanTamperSetting: "Limpar configurações de proteção históricas"
|
||||
SetTamperWithRules: "Iniciar proteção de diretório de acordo com as regras"
|
||||
ProtectDir: "Proteger diretório {{ .name }}"
|
||||
IgnoreFile: "Proteger arquivo {{ .name }}"
|
||||
TamperSetSuccessful: "Configurações de proteção concluídas, iniciando monitoramento dos seguintes diretórios:"
|
||||
|
||||
#waf
|
||||
Upload: 'Enviar'
|
||||
IgnoreBackupErr: 'Backup falhou, erro: {{ .detail }}, ignorando este erro...'
|
||||
IgnoreUploadErr: 'Upload falhou, erro: {{ .detail }}, ignorando este erro...'
|
||||
LoadBackupFailed: 'Falha ao obter conexão da conta de backup, erro: {{ .detail }}'
|
||||
InExecuting: 'A tarefa atual está sendo executada, não repita a execução'
|
||||
NoSuchResource: 'Nenhum conteúdo de backup encontrado no banco de dados, ignorando...'
|
||||
CleanLog: 'Limpar log'
|
||||
CleanLogByName: 'Limpar log de {{ .name }}'
|
||||
WafIpGroupNotFound: 'Grupo de IP do WAF não encontrado., vá para [Recursos Avançados - WAF - Lista Branca/Preta - Grupo de IP] para adicionar um grupo de IP via método de download remoto.'
|
||||
ErrNotExistUser: 'O usuário atual não existe, modifique e'
|
||||
ErrBanAction: 'Falha na configuração. O serviço atual {{ .name }} não está disponível. Verifique e'
|
||||
ErrClamdscanNotFound: 'O comando clamdscan não foi detectado, consulte a documentação para instalá-lo'
|
||||
TaskSwapSet: 'Configurar Swap'
|
||||
SetSwap: 'Configurar Swap {{ .path }} - {{ .size }}'
|
||||
CreateSwap: 'Criar Arquivo Swap'
|
||||
FormatSwap: 'Formatar Arquivo Swap'
|
||||
EnableSwap: 'Ativar Swap'
|
||||
CleanTamperSetting: 'Limpar configurações de proteção históricas'
|
||||
SetTamperWithRules: 'Iniciar proteção de diretório de acordo com as regras'
|
||||
ProtectDir: 'Proteger diretório {{ .name }}'
|
||||
IgnoreFile: 'Proteger arquivo {{ .name }}'
|
||||
TamperSetSuccessful: 'Configurações de proteção concluídas, iniciando monitoramento dos seguintes diretórios:'
|
||||
ErrScope: 'A modificação desta configuração não é suportada'
|
||||
ErrStateChange: 'Falha na alteração de estado'
|
||||
ErrRuleExist: 'A regra já existe'
|
||||
@@ -299,7 +300,7 @@ ErrRuleNotExist: 'A regra não existe'
|
||||
ErrParseIP: 'Formato de IP errado'
|
||||
ErrDefaultIP: 'padrão é um nome reservado, altere-o para outro nome'
|
||||
ErrGroupInUse: 'O grupo de IP é usado pela lista negra/lista branca e não pode ser excluído'
|
||||
ErrIPGroupAclUse: "O grupo de IP está sendo usado por regras personalizadas do site {{ .name }}, não pode ser excluído"
|
||||
ErrIPGroupAclUse: 'O grupo de IP está sendo usado por regras personalizadas do site {{ .name }}, não pode ser excluído'
|
||||
ErrGroupExist: 'O nome do grupo IP já existe'
|
||||
ErrIPRange: 'Intervalo de IP errado'
|
||||
ErrIPExist: 'IP já existe'
|
||||
@@ -310,10 +311,10 @@ xss: 'XSS'
|
||||
phpExec: 'Execução de script PHP'
|
||||
oneWordTrojan: 'Cavalo de Tróia de Uma Palavra'
|
||||
appFilter: 'Aplicar filtragem de diretório perigosa'
|
||||
webshell: 'Webshell'
|
||||
webShell: 'Webshell'
|
||||
args: 'Regras de parâmetros'
|
||||
protocolFilter: 'Filtragem de protocolo'
|
||||
javaFileter: 'Filtro de arquivo perigoso Java'
|
||||
javaFilter: 'Filtro de arquivo perigoso Java'
|
||||
scannerFilter: 'Filtro do scanner'
|
||||
escapeFilter: 'filtro de escape'
|
||||
customRule: 'Regra personalizada'
|
||||
@@ -343,10 +344,9 @@ ErrDBNotExist: 'O banco de dados não existe'
|
||||
allow: 'permitir'
|
||||
deny: 'negar'
|
||||
OpenrestyNotFound: 'Openresty não está instalado'
|
||||
remoteIpIsNull: "A lista de IP está vazia"
|
||||
OpenrestyVersionErr: "A versão do Openresty é muito baixa, por favor atualize o Openresty para 1.27.1.2-2-2-focal"
|
||||
|
||||
#tarefa
|
||||
remoteIpIsNull: 'A lista de IP está vazia'
|
||||
OpenrestyVersionErr: 'A versão do Openresty é muito baixa atualize o Openresty para 1.27.1.2-2-2-focal'
|
||||
ErrFileTooLarge: 'O arquivo excede 1 MB e não pode ser enviado'
|
||||
TaskStart: '{{ .name }} A tarefa inicia [START]'
|
||||
TaskEnd: '{{ .name }} Tarefa concluída [CONCLUÍDA]'
|
||||
TaskFailed: '{{ .name }} tarefa falhou'
|
||||
@@ -362,18 +362,18 @@ TaskDelete: 'Excluir'
|
||||
TaskUpgrade: 'Atualizar'
|
||||
TaskUpdate: 'Atualizar'
|
||||
TaskRestart: 'Reiniciar'
|
||||
TaskProtect: "Proteger"
|
||||
Backup de Tarefa: 'Backup'
|
||||
TaskProtect: 'Proteger'
|
||||
TaskBackup: 'Backup'
|
||||
TaskRecover: 'Recuperar'
|
||||
TaskRollback: 'Reverter'
|
||||
TaskPull: 'Puxar'
|
||||
TaskCommit: 'Commit'
|
||||
TaskBuild: 'Construir'
|
||||
TaskPush: 'Empurrar'
|
||||
TaskClean: "Limpeza"
|
||||
TaskClean: 'Limpeza'
|
||||
TaskHandle: 'Executar'
|
||||
TaskImport: "Importar"
|
||||
TaskExport: "Exportar"
|
||||
TaskImport: 'Importar'
|
||||
TaskExport: 'Exportar'
|
||||
Website: 'Site'
|
||||
App: 'Aplicativo'
|
||||
Runtime: 'Ambiente de tempo de execução'
|
||||
@@ -405,18 +405,12 @@ RuntimeExtension: 'Extensão do ambiente de tempo de execução'
|
||||
TaskIsExecuting: 'A tarefa está em execução'
|
||||
CustomAppstore: 'Armazém de aplicativos personalizados'
|
||||
TaskExec: 'Executar'
|
||||
TaskBatch: "Operação em Lote"
|
||||
TaskBatch: 'Operação em Lote'
|
||||
FileConvert: 'Conversão de Formato de Arquivo'
|
||||
|
||||
# task - clam
|
||||
Clamscan: "Escanear {{ .name }}"
|
||||
TaskScan: "Escanear"
|
||||
|
||||
# tarefa - ai
|
||||
Clamscan: 'Escanear {{ .name }}'
|
||||
TaskScan: 'Escanear'
|
||||
OllamaModelPull: 'Puxar modelo Ollama {{ .name }}'
|
||||
OllamaModelSize: 'Obtenha o tamanho do modelo Ollama {{ .name }}'
|
||||
|
||||
# tarefa-instantânea
|
||||
Snapshot: 'Instantâneo'
|
||||
SnapDBInfo: 'Escrever informações do banco de dados 1Panel'
|
||||
SnapCopy: 'Copiar arquivos e diretórios {{ .name }}'
|
||||
@@ -442,7 +436,6 @@ SnapCompressSize: 'Tamanho do arquivo de instantâneo {{ .name }}'
|
||||
SnapUpload: 'Carregar arquivo de instantâneo'
|
||||
SnapUploadTo: 'Carregar arquivo de instantâneo para {{ .name }}'
|
||||
SnapUploadRes: 'Carregar arquivo de instantâneo para {{ .name }}'
|
||||
|
||||
SnapshotRecover: 'Restauração de instantâneo'
|
||||
RecoverDownload: 'Baixar arquivo de instantâneo'
|
||||
Download: 'Baixar'
|
||||
@@ -450,10 +443,10 @@ RecoverDownloadAccount: 'Obter conta de backup de download de instantâneo {{ .n
|
||||
RecoverDecompress: 'Descompacte arquivos compactados de instantâneos'
|
||||
Decompress: 'Descompressão'
|
||||
BackupBeforeRecover: 'Fazer backup de dados relacionados ao sistema antes do snapshot'
|
||||
Readjson: 'Ler o arquivo Json no snapshot'
|
||||
ReadjsonPath: 'Obtenha o caminho do arquivo Json no snapshot'
|
||||
ReadjsonContent: 'Ler arquivo Json'
|
||||
ReadjsonMarshal: 'Processamento de escape JSON'
|
||||
ReadJson: 'Ler o arquivo Json no snapshot'
|
||||
ReadJsonPath: 'Obtenha o caminho do arquivo Json no snapshot'
|
||||
ReadJsonContent: 'Ler arquivo Json'
|
||||
ReadJsonMarshal: 'Processamento de escape JSON'
|
||||
RecoverApp: 'Restaurar aplicativos instalados'
|
||||
RecoverWebsite: 'Recuperar diretório de sites'
|
||||
RecoverAppImage: 'Restaurar backup de imagem instantânea'
|
||||
@@ -467,9 +460,6 @@ RecoverDaemonJson: 'Restaurar arquivo daemon.json de configuração do contêine
|
||||
RecoverDBData: 'Recuperar dados do banco de dados'
|
||||
RecoverBackups: 'Restaurar diretório de backup local'
|
||||
RecoverPanelData: 'Diretório de dados de recuperação'
|
||||
|
||||
# tarefa - contêiner
|
||||
ContainerNewCliet: 'Inicializar cliente Docker'
|
||||
ContainerImagePull: 'Puxar imagem do contêiner {{ .name }}'
|
||||
ContainerRemoveOld: 'Remover o contêiner original {{ .name }}'
|
||||
ContainerImageCheck: 'Verifique se a imagem é extraída normalmente'
|
||||
@@ -478,22 +468,13 @@ ContainerRecreate: 'A atualização do contêiner falhou, agora iniciando a rest
|
||||
ContainerCreate: 'Criar um novo contêiner {{ .name }}'
|
||||
ContainerCreateFailed: 'Falha na criação do contêiner, exclua o contêiner com falha'
|
||||
ContainerStartCheck: 'Verifique se o contêiner foi iniciado'
|
||||
|
||||
# tarefa - imagem
|
||||
ImageBuild: 'Construção de imagem'
|
||||
ImageBuildStdoutCheck: 'Analisar conteúdo de saída da imagem'
|
||||
ImageBuildRes: 'Saída da criação da imagem: {{ .name }}'
|
||||
ImagePull: 'Puxar imagem'
|
||||
ImageRepoAuthFromDB: 'Obter informações de autenticação do repositório do banco de dados'
|
||||
ImaegPullRes: 'Saída de extração de imagem: {{ .name }}'
|
||||
ImagePush: 'Enviar imagem'
|
||||
ImageRenameTag: 'Modificar tag de imagem'
|
||||
ImageNewTag: 'Nova tag de imagem {{ .name }}'
|
||||
ImaegPushRes: 'Saída de envio de imagem: {{ .name }}'
|
||||
ComposeCreate: 'Criar uma composição'
|
||||
ComposeCreateRes: 'Compose cria saída: {{ .name }}'
|
||||
|
||||
# tarefa - site
|
||||
BackupNginxConfig: 'Fazer backup do arquivo de configuração do OpenResty do site'
|
||||
CompressFileSuccess: 'Diretório compactado com sucesso, compactado para {{ .name }}'
|
||||
CompressDir: 'Diretório de compressão'
|
||||
@@ -504,40 +485,33 @@ websiteDir: 'Diretório de sites'
|
||||
RecoverFailedStartRollBack: 'Falha na recuperação, iniciar reversão'
|
||||
AppBackupFileIncomplete: 'O arquivo de backup está incompleto e não contém os arquivos app.json ou app.tar.gz'
|
||||
AppAttributesNotMatch: 'O tipo ou nome do aplicativo não corresponde'
|
||||
ErrAlertSync: 'Erro de sincronização de informações de alarme, verifique e'
|
||||
ErrInstallExtension: 'Já existe uma tarefa de instalação em andamento, aguarde a conclusão da tarefa'
|
||||
PanelAlertTitle: 'Notificação de Alerta do Painel'
|
||||
TestAlertTitle: 'E-mail de Teste - Verificar Conectividade de E-mail'
|
||||
TestAlert: 'Este é um e-mail de teste para verificar se sua configuração de envio de e-mails está correta.'
|
||||
LicenseExpirationAlert: 'Seu Painel {{ .node }}{{ .ip }}, a licença expirará em {{ .day }} dias. Faça login no painel para obter detalhes.'
|
||||
CronJobFailedAlert: 'Seu Painel {{ .node }}{{ .ip }}, a execução da tarefa agendada {{ .name }} falhou. Faça login no painel para obter detalhes.'
|
||||
ClamAlert: 'Seu Painel {{ .node }}{{ .ip }}, a tarefa de verificação de vírus detectou {{ .num }} arquivos infectados. Faça login no painel para obter detalhes.'
|
||||
WebSiteAlert: 'Seu Painel {{ .node }}{{ .ip }}, {{ .num }} sites expirarão em {{ .day }} dias. Faça login no painel para obter detalhes.'
|
||||
SSLAlert: 'Seu Painel {{ .node }}{{ .ip }}, {{ .num }} certificados SSL expirarão em {{ .day }} dias. Faça login no painel para obter detalhes.'
|
||||
DiskUsedAlert: 'Seu Painel {{ .node }}{{ .ip }}, o uso do disco {{ .name }} atingiu {{ .used }}. Faça login no painel para obter detalhes.'
|
||||
ResourceAlert: 'Seu Painel {{ .node }}{{ .ip }}, a taxa de uso médio do {{ .name }} nos últimos {{ .time }} minutos é {{ .used }}. Faça login no painel para obter detalhes.'
|
||||
PanelVersionAlert: 'Seu Painel {{ .node }}{{ .ip }}, a versão mais recente do painel está disponível para atualização. Faça login no painel para obter detalhes.'
|
||||
PanelPwdExpirationAlert: 'Seu Painel {{ .node }}{{ .ip }}, a senha do painel expirará em {{ .day }} dias. Faça login no painel para obter detalhes.'
|
||||
CommonAlert: 'Seu Painel {{ .node }}{{ .ip }}, {{ .msg }}. Faça login no painel para obter detalhes.'
|
||||
NodeExceptionAlert: 'Seu Painel {{ .node }}{{ .ip }}, {{ .num }} nós estão anormais. Faça login no painel para obter detalhes.'
|
||||
LicenseExceptionAlert: 'Seu Painel {{ .node }}{{ .ip }}, {{ .num }} licenças estão anormais. Faça login no painel para obter detalhes.'
|
||||
SSHAndPanelLoginAlert: 'Seu Painel {{ .node }}{{ .ip }}, o login {{ .name }} a partir de {{ .loginIp }} é anormal. Faça login no painel para obter detalhes.'
|
||||
DeviceNotFound: 'Dispositivo {{ .name }} não encontrado'
|
||||
DeviceIsMounted: 'Dispositivo {{ .name }} está montado desmonte primeiro'
|
||||
PartitionDiskErr: 'Falha ao particionar, {{ .err }}'
|
||||
FormatDiskErr: 'Falha ao formatar disco, {{ .err }}'
|
||||
MountDiskErr: 'Falha ao montar disco, {{ .err }}'
|
||||
XfsNotFound: 'Sistema de arquivos xfs não detectado instale xfsprogs primeiro'
|
||||
|
||||
#alerta
|
||||
ErrAlert: 'O formato da mensagem de aviso está incorreto, verifique e tente novamente!'
|
||||
ErrAlertPush: 'Erro ao enviar informações de alerta, verifique e tente novamente!'
|
||||
ErrAlertSave: 'Erro ao salvar as informações do alarme. Verifique e tente novamente!'
|
||||
ErrAlertSync: 'Erro de sincronização de informações de alarme, verifique e tente novamente!'
|
||||
ErrAlertRemote: 'Erro remoto na mensagem de alarme, verifique e tente novamente!'
|
||||
|
||||
#task - runtime
|
||||
ErrInstallExtension: "Já existe uma tarefa de instalação em andamento, aguarde a conclusão da tarefa"
|
||||
|
||||
# alert mail template
|
||||
PanelAlertTitle: "Notificação de Alerta do Painel"
|
||||
TestAlertTitle: "E-mail de Teste - Verificar Conectividade de E-mail"
|
||||
TestAlert: "Este é um e-mail de teste para verificar se sua configuração de envio de e-mails está correta."
|
||||
LicenseExpirationAlert: "Seu Painel {{ .node }}{{ .ip }}, a licença expirará em {{ .day }} dias. Faça login no painel para obter detalhes."
|
||||
CronJobFailedAlert: "Seu Painel {{ .node }}{{ .ip }}, a execução da tarefa agendada {{ .name }} falhou. Faça login no painel para obter detalhes."
|
||||
ClamAlert: "Seu Painel {{ .node }}{{ .ip }}, a tarefa de verificação de vírus detectou {{ .num }} arquivos infectados. Faça login no painel para obter detalhes."
|
||||
WebSiteAlert: "Seu Painel {{ .node }}{{ .ip }}, {{ .num }} sites expirarão em {{ .day }} dias. Faça login no painel para obter detalhes."
|
||||
SSLAlert: "Seu Painel {{ .node }}{{ .ip }}, {{ .num }} certificados SSL expirarão em {{ .day }} dias. Faça login no painel para obter detalhes."
|
||||
DiskUsedAlert: "Seu Painel {{ .node }}{{ .ip }}, o uso do disco {{ .name }} atingiu {{ .used }}. Faça login no painel para obter detalhes."
|
||||
ResourceAlert: "Seu Painel {{ .node }}{{ .ip }}, a taxa de uso médio do {{ .name }} nos últimos {{ .time }} minutos é {{ .used }}. Faça login no painel para obter detalhes."
|
||||
PanelVersionAlert: "Seu Painel {{ .node }}{{ .ip }}, a versão mais recente do painel está disponível para atualização. Faça login no painel para obter detalhes."
|
||||
PanelPwdExpirationAlert: "Seu Painel {{ .node }}{{ .ip }}, a senha do painel expirará em {{ .day }} dias. Faça login no painel para obter detalhes."
|
||||
CommonAlert: "Seu Painel {{ .node }}{{ .ip }}, {{ .msg }}. Faça login no painel para obter detalhes."
|
||||
NodeExceptionAlert: "Seu Painel {{ .node }}{{ .ip }}, {{ .num }} nós estão anormais. Faça login no painel para obter detalhes."
|
||||
LicenseExceptionAlert: "Seu Painel {{ .node }}{{ .ip }}, {{ .num }} licenças estão anormais. Faça login no painel para obter detalhes."
|
||||
SSHAndPanelLoginAlert: "Seu Painel {{ .node }}{{ .ip }}, o login {{ .name }} a partir de {{ .loginIp }} é anormal. Faça login no painel para obter detalhes."
|
||||
|
||||
#disk
|
||||
DeviceNotFound: "Dispositivo {{ .name }} não encontrado"
|
||||
DeviceIsMounted: "Dispositivo {{ .name }} está montado, por favor desmonte primeiro"
|
||||
PartitionDiskErr: "Falha ao particionar, {{ .err }}"
|
||||
FormatDiskErr: "Falha ao formatar disco, {{ .err }}"
|
||||
MountDiskErr: "Falha ao montar disco, {{ .err }}"
|
||||
UnMountDiskErr: "Falha ao desmontar disco, {{ .err }}"
|
||||
XfsNotFound: "Sistema de arquivos xfs não detectado, por favor instale xfsprogs primeiro"
|
||||
# terminal
|
||||
TerminalAIBlockedRiskyCommand: 'Comando de risco bloqueado: {{ .command }}'
|
||||
TerminalAIThinking: 'A IA está pensando...'
|
||||
TerminalAIReadyToExecute: 'Pensamento concluído, pressione Enter para executar (duração: {{ .duration }}, token: {{ .tokens }})'
|
||||
TerminalAIRequestFailed: 'Falha na solicitação de AI: {{ .err }}'
|
||||
|
||||
+209
-235
@@ -1,34 +1,30 @@
|
||||
ErrInvalidParams: 'Ошибка параметра запроса: {{ .detail }}'
|
||||
ErrTokenParse: 'Ошибка генерации токена: {{ .detail }}'
|
||||
ErrInitialPassword: 'Исходный пароль неверный'
|
||||
ErrInternalServer: 'Внутренняя ошибка сервера: {{ .detail }}'
|
||||
ErrInvalidParams: 'Неверный запрос: {{ .detail }}'
|
||||
ErrTokenParse: 'Ошибка токена: {{ .detail }}'
|
||||
ErrInitialPassword: 'Исходный пароль неверен'
|
||||
ErrInternalServer: 'Внутренняя ошибка: {{ .detail }}'
|
||||
ErrWgetRemoteFailed: 'Неверный или недоступный URL удалённой загрузки: {{ .detail }}'
|
||||
ErrWgetInvalidContentType: 'Удалённый сервер вернул не файл (возможно, страницу ошибки), {{ .detail }}'
|
||||
ErrRecordExist: 'Запись уже существует'
|
||||
ErrRecordNotFound: 'Запись не найдена'
|
||||
ErrStructTransform: 'Не удалось преобразовать тип: {{ .err }}'
|
||||
ErrNotLogin: 'Пользователь не вошел в систему: {{ .detail }}'
|
||||
ErrPasswordExpired: 'Срок действия текущего пароля истек: {{ .detail }}'
|
||||
ErrNotSupportType: 'Система не поддерживает текущий тип: {{ .name }}'
|
||||
ErrProxy: 'Ошибка запроса, проверьте статус узла: {{ .detail }}'
|
||||
ErrApiConfigStatusInvalid: 'Доступ к интерфейсу API запрещен: {{ .detail }}'
|
||||
ErrApiConfigKeyInvalid: 'Ошибка ключа интерфейса API: {{ .detail }}'
|
||||
ErrApiConfigIPInvalid: 'IP-адрес, используемый для вызова интерфейса API, отсутствует в белом списке: {{ .detail }}'
|
||||
ErrApiConfigDisable: 'Этот интерфейс запрещает использование вызовов интерфейса API: {{ .detail }}'
|
||||
ErrApiConfigKeyTimeInvalid: 'Ошибка временной метки интерфейса API: {{ .detail }}'
|
||||
StartPushSSLToNode: "Начало отправки сертификата на узел"
|
||||
PushSSLToNodeFailed: "Не удалось отправить сертификат на узел: {{ .err }}"
|
||||
PushSSLToNodeSuccess: "Сертификат успешно отправлен на узел"
|
||||
ErrShutDown: "Команда завершена вручную!"
|
||||
|
||||
ErrMinQuickJump: "Пожалуйста, установите хотя бы одну запись быстрого перехода!"
|
||||
ErrMaxQuickJump: "Можно установить до четырех записей быстрого перехода!"
|
||||
|
||||
#общий
|
||||
ErrUsernameIsExist: 'Имя пользователя уже существует'
|
||||
ErrNameIsExist: 'Имя уже существует'
|
||||
ErrDemoEnvironment: 'Демо-сервер, эта операция запрещена!'
|
||||
ErrCmdTimeout: 'Время выполнения команды истекло!'
|
||||
ErrCmdIllegal: 'В команде выполнения есть недопустимые символы. Измените ее и повторите попытку!'
|
||||
ErrPortExist: 'Порт {{ .port }} уже занят {{ .type }} [{{ .name }}]'
|
||||
ErrStructTransform: 'Ошибка преобразования: {{ .err }}'
|
||||
ErrNotLogin: 'Не авторизован: {{ .detail }}'
|
||||
ErrPasswordExpired: 'Пароль истёк: {{ .detail }}'
|
||||
ErrNotSupportType: 'Тип не поддерживается: {{ .name }}'
|
||||
ErrProxy: 'Запрос не удался: {{ .detail }}'
|
||||
ErrApiConfigStatusInvalid: 'API отключено: {{ .detail }}'
|
||||
ErrApiConfigKeyInvalid: 'Неверный API-ключ: {{ .detail }}'
|
||||
ErrApiConfigIPInvalid: 'IP не в списке: {{ .detail }}'
|
||||
ErrApiConfigDisable: 'Доступ по API запрещён: {{ .detail }}'
|
||||
ErrApiConfigKeyTimeInvalid: 'Неверная метка времени: {{ .detail }}'
|
||||
ErrShutDown: 'Команда остановлена вручную'
|
||||
ErrMinQuickJump: 'Добавьте хотя бы одну быструю ссылку'
|
||||
ErrMaxQuickJump: 'Не более 4 быстрых ссылок'
|
||||
ErrUsernameIsExist: 'Имя пользователя занято'
|
||||
ErrNameIsExist: 'Имя уже занято'
|
||||
ErrDemoEnvironment: 'Недоступно в демо'
|
||||
ErrCmdTimeout: 'Команда завершилась по тайм-ауту'
|
||||
ErrCmdIllegal: 'Команда содержит недопустимые символы'
|
||||
ErrPortExist: 'Порт {{ .port }} занят {{ .type }} [{{ .name }}]'
|
||||
TYPE_APP: 'Приложение'
|
||||
TYPE_RUNTIME: 'Среда выполнения'
|
||||
TYPE_DOMAIN: 'Доменное имя'
|
||||
@@ -39,45 +35,43 @@ Failed: 'Не удалось'
|
||||
SystemRestart: 'Задача прервана из-за перезапуска системы'
|
||||
ErrGroupIsDefault: 'Группа по умолчанию, не может быть удалена'
|
||||
ErrGroupIsInWebsiteUse: 'Группа используется другим веб-сайтом и не может быть удалена.'
|
||||
Decrypt: "Расшифровать"
|
||||
|
||||
#agent
|
||||
ErrAgentAccountBound: 'Эта учетная запись привязана к агенту и не может быть удалена. Проверьте и повторите попытку!'
|
||||
ErrAgentAccountUnavailable: 'Информация о подключении учетной записи недоступна. Ошибка: {{ .err }}. Проверьте и повторите попытку!'
|
||||
ErrAgentProviderNotSupported: 'Этот провайдер агента не поддерживается. Проверьте и повторите попытку!'
|
||||
ErrAgentAccountRequired: 'Выберите учетную запись агента и повторите попытку.'
|
||||
ErrAgentAccountNotVerified: 'Учетная запись не прошла проверку. Проверьте и повторите попытку!'
|
||||
ErrAgentProviderMismatch: 'Провайдер учетной записи не совпадает. Проверьте и повторите попытку!'
|
||||
ErrAgentBaseURLRequired: 'Base URL не может быть пустым. Проверьте и повторите попытку!'
|
||||
ErrAgentApiKeyRequired: 'API Key не может быть пустым. Проверьте и повторите попытку!'
|
||||
ErrAgentComposeRequired: 'Пользовательское содержимое compose не может быть пустым. Проверьте и повторите попытку!'
|
||||
ErrAgentIDRequired: 'ID агента не может быть пустым. Проверьте и повторите попытку!'
|
||||
ErrAgentAccountIDRequired: 'ID учетной записи не может быть пустым. Проверьте и повторите попытку!'
|
||||
|
||||
#резервное копирование
|
||||
Decrypt: 'Расшифровать'
|
||||
ErrAgentAccountBound: 'Акаунт привязан к агенту'
|
||||
ErrAgentAccountUnavailable: 'Связь с аккаунтом недоступна: {{ .err }}'
|
||||
ErrAgentProviderNotSupported: 'Провайдер агента не поддерживается'
|
||||
ErrAgentAccountRequired: 'Выберите аккаунт агента'
|
||||
ErrAgentAccountNotVerified: 'Аккаунт не подтверждён'
|
||||
ErrAgentAccountModelsRequired: 'Настройте хотя бы одну модель'
|
||||
ErrAgentAccountSingleInitialModel: 'При создании аккаунта можно указать только одну начальную модель'
|
||||
ErrAgentModelNotInAccount: 'Модель не настроена в выбранном аккаунте'
|
||||
ErrAgentModelInUse: 'Эта модель всё ещё используется агентом: {{ .name }}'
|
||||
ErrAgentBaseURLRequired: 'Нужен базовый URL'
|
||||
ErrAgentApiKeyRequired: 'Нужен API-ключ'
|
||||
ErrAgentComposeRequired: 'Нужен compose'
|
||||
ErrAgentIDRequired: 'Нужен ID агента'
|
||||
ErrAgentAccountIDRequired: 'Нужен ID аккаунта'
|
||||
ErrAgentLimitReached: 'Community Edition поддерживает до {{ .max }} AI-агентов. Перейдите на Professional Edition, чтобы снять ограничение'
|
||||
Localhost: 'Локальная машина'
|
||||
ErrBackupInUsed: 'Учетная запись резервного копирования использовалась в запланированной задаче и не может быть удалена.'
|
||||
ErrBackupCheck: 'Проверка подключения к резервной копии учетной записи не удалась {{ .err }}'
|
||||
ErrBackupLocalDelete: 'Удаление учетной записи резервного копирования локального сервера пока не поддерживается'
|
||||
ErrBackupLocalCreate: 'Создание учетных записей резервного копирования локального сервера пока не поддерживается'
|
||||
|
||||
#приложение
|
||||
ErrPortInUsed: '{{ .detail }} порт уже занят!'
|
||||
ErrBackupInUsed: 'Аккаунт бэкапа занят задачей'
|
||||
ErrBackupCheck: 'Проверка подключения не удалась: {{ .err }}'
|
||||
ErrBackupLocalDelete: 'Нельзя удалить локальные бэкапы'
|
||||
ErrBackupLocalCreate: 'Нельзя создать локальные бэкапы'
|
||||
ErrPortInUsed: '{{ .detail }} порт уже занят'
|
||||
ErrAppLimit: 'Количество установленных приложений превысило лимит'
|
||||
ErrNotInstall: 'Приложение не установлено'
|
||||
ErrPortInOtherApp: 'Порт {{ .port }} уже занят приложением {{ .apps }}!'
|
||||
ErrDbUserNotValid: 'Существующая база данных, имя пользователя и пароль не совпадают!'
|
||||
ErrUpdateBuWebsite: 'Приложение успешно обновлено, но изменение файла конфигурации веб-сайта не удалось. Пожалуйста, проверьте конфигурацию! '
|
||||
Err1PanelNetworkFailed: 'Создание сети контейнеров по умолчанию не удалось! {{ .detail }}'
|
||||
ErrFileParse: 'Ошибка анализа файла docker-compose приложения!'
|
||||
ErrPortInOtherApp: 'Порт {{ .port }} уже занят приложением {{ .apps }}'
|
||||
ErrDbUserNotValid: 'Существующая база данных, имя пользователя и пароль не совпадают'
|
||||
ErrUpdateBuWebsite: 'Приложение успешно обновлено, но изменение файла конфигурации веб-сайта не удалось. Пожалуйста, проверьте конфигурацию'
|
||||
Err1PanelNetworkFailed: 'Создание сети контейнеров по умолчанию не удалось {{ .detail }}'
|
||||
ErrFileParse: 'Ошибка анализа файла docker-compose приложения'
|
||||
ErrInstallDirNotFound: 'Каталог установки не существует. Если вам необходимо удалить, выберите Принудительное удаление.'
|
||||
AppStoreIsUpToDate: 'В магазине приложений уже установлена последняя версия'
|
||||
LocalAppVersionNull: 'Приложение {{ .name }} не синхронизировано с версией! Невозможно добавить в список приложений.'
|
||||
LocalAppVersionErr: 'Синхронизация версии {{ .version }} {{ .name }} не удалась! {{ .err }}'
|
||||
LocalAppVersionNull: 'Приложение {{ .name }} не синхронизировано с версией Невозможно добавить в список приложений.'
|
||||
LocalAppVersionErr: 'Синхронизация версии {{ .version }} {{ .name }} не удалась {{ .err }}'
|
||||
ErrFileNotFound: 'Файл {{ .name }} не существует'
|
||||
ErrFileParseApp: '{{ .name }} анализ файла не удался {{ .err }}'
|
||||
ErrAppDirNull: 'Папка версии не существует'
|
||||
LocalAppErr: 'Синхронизация приложения {{ .name }} не удалась! {{ .err }}'
|
||||
LocalAppErr: 'Синхронизация приложения {{ .name }} не удалась {{ .err }}'
|
||||
ErrContainerName: 'Имя контейнера уже существует'
|
||||
ErrCreateHttpClient: 'Не удалось создать запрос {{ .err }}'
|
||||
ErrHttpReqTimeOut: 'Истекло время ожидания запроса {{ .err }}'
|
||||
@@ -90,46 +84,42 @@ ErrAppBackup: '{{ .name }} резервное копирование прило
|
||||
ErrVersionTooLow: 'Текущая версия 1Panel слишком низкая для обновления App Store. Пожалуйста, обновите версию перед началом работы.'
|
||||
ErrAppNameExist: 'Имя приложения уже существует'
|
||||
AppStoreIsSyncing: 'App Store синхронизируется, повторите попытку позже'
|
||||
ErrGetCompose: 'Не удалось получить файл docker-compose.yml! {{ .detail }}'
|
||||
ErrGetCompose: 'Не удалось получить файл docker-compose.yml {{ .detail }}'
|
||||
ErrAppWarn: 'Ненормальное состояние, проверьте журнал'
|
||||
ErrAppParamKey: 'Поле параметра {{ .name }} ненормально'
|
||||
ErrAppUpgrade: 'Обновление приложения {{ .name }} не удалось {{ .err }}'
|
||||
AppRecover: 'Откатить приложение {{ .name }}'
|
||||
PullImageStart: 'Начать извлечение изображения [{{ .name }}]'
|
||||
PullImageSuccess: 'Изображение извлечено успешно'
|
||||
AppStoreIsLastVersion: 'В App Store уже установлена последняя версия'
|
||||
AppStoreSyncSuccess: 'Синхронизация с App Store прошла успешно'
|
||||
SyncAppDetail: 'Синхронизировать конфигурацию приложения'
|
||||
AppVersionNotMatch: 'Приложению {{ .name }} требуется более высокая версия 1Panel, синхронизация пропускается'
|
||||
MoveSiteDir: "Обнаружено изменение каталога сайта. Необходимо перенести каталог сайта OpenResty в {{ .name }}"
|
||||
MoveSiteDir: 'Обнаружено изменение каталога сайта. Необходимо перенести каталог сайта OpenResty в {{ .name }}'
|
||||
MoveSiteDirSuccess: 'Успешная миграция каталога веб-сайта'
|
||||
DeleteRuntimePHP: 'Удалить среду выполнения PHP'
|
||||
CustomAppStoreFileValid: 'Пакеты магазина приложений должны быть в формате .tar.gz'
|
||||
CustomAppStoreFileValid: 'Пакеты магазина приложений должны быть в формате.tar.gz'
|
||||
PullImageTimeout: 'Истекло время ожидания извлечения изображения. Попробуйте увеличить ускорение изображения или выбрать другое ускорение изображения'
|
||||
ErrAppIsDown: '{{ .name }} статус приложения ненормальный, проверьте'
|
||||
ErrCustomApps: 'Установлено приложение. Сначала удалите его'
|
||||
ErrCustomRuntimes: 'Установлена среда выполнения. Сначала удалите ее'
|
||||
ErrAppVersionDeprecated: "Приложение {{ .name }} несовместимо с текущей версией 1Panel, пропущено"
|
||||
ErrDockerFailed: "Состояние Docker аномально, проверьте состояние сервиса"
|
||||
ErrDockerComposeCmdNotFound: "Команда Docker Compose отсутствует, пожалуйста, установите эту команду на хост-машине сначала"
|
||||
UseExistImage: 'Не удалось загрузить образ, используется существующий образ'
|
||||
|
||||
#ssh
|
||||
ExportIP: "IP входа"
|
||||
ExportArea: "Местоположение"
|
||||
ExportPort: "Порт"
|
||||
ExportAuthMode: "Способ входа"
|
||||
ExportUser: "Пользователь"
|
||||
ExportStatus: "Статус входа"
|
||||
ExportDate: "Время"
|
||||
|
||||
#файл
|
||||
ErrAppVersionDeprecated: 'Приложение {{ .name }} несовместимо с текущей версией 1Panel, пропущено'
|
||||
ErrDockerFailed: 'Состояние Docker аномально, проверьте состояние сервиса'
|
||||
ErrDockerComposeCmdNotFound: 'Команда Docker Compose отсутствует, пожалуйста, установите эту команду на хост-машине сначала'
|
||||
UseExistImage: 'Образ уже существует, используется существующий образ'
|
||||
ErrDatabaseNotFound: 'База данных {{ .name }} не существует'
|
||||
ExportIP: 'IP входа'
|
||||
ExportArea: 'Местоположение'
|
||||
ExportPort: 'Порт'
|
||||
ExportAuthMode: 'Способ входа'
|
||||
ExportUser: 'Пользователь'
|
||||
ExportStatus: 'Статус входа'
|
||||
ExportDate: 'Время'
|
||||
ErrFileCanNotRead: 'Этот файл не поддерживает предварительный просмотр'
|
||||
ErrFileToLarge: 'Файл больше 10 МБ и не может быть открыт'
|
||||
ErrPathNotFound: 'Каталог не существует'
|
||||
ErrMovePathFailed: 'Целевой путь не может содержать исходный путь!'
|
||||
ErrLinkPathNotFound: 'Целевой путь не существует!'
|
||||
ErrFileIsExist: 'Файл или папка уже существует!'
|
||||
ErrMovePathFailed: 'Целевой путь не может содержать исходный путь'
|
||||
ErrLinkPathNotFound: 'Целевой путь не существует'
|
||||
ErrFileIsExist: 'Файл или папка уже существует'
|
||||
ErrFileUpload: '{{ .name }} не удалось загрузить файл {{ .detail }}'
|
||||
ErrFileDownloadDir: 'Папка загрузки не поддерживается'
|
||||
ErrCmdNotFound: 'Команда {{ .name }} не существует, сначала установите эту команду на хосте'
|
||||
@@ -137,17 +127,15 @@ ErrSourcePathNotFound: 'Исходный каталог не существуе
|
||||
ErrFavoriteExist: 'Этот путь уже добавлен в избранное'
|
||||
ErrInvalidChar: 'Недопустимые символы не допускаются'
|
||||
ErrPathNotDelete: 'Выбранный каталог не может быть удален'
|
||||
ErrLogFileToLarge: "Файл журнала превышает 500MB и не может быть открыт"
|
||||
FileDropFailed: "Не удалось очистить файл {{ .name }}: {{ .err }}"
|
||||
FileDropSuccess: "Файл {{ .name }} успешно очищен, удалено {{ .count }} файлов, освобождено {{ .size }} дискового пространства"
|
||||
FileDropSum: "Очистка файлов завершена успешно, всего удалено {{ .count }} файлов, освобождено {{ .size }} дискового пространства"
|
||||
ErrBadDecrypt: "Ошибка пароля расшифровки сжатого пакета, распаковка не удалась, проверьте и повторите попытку!"
|
||||
|
||||
#веб-сайт
|
||||
ErrLogFileToLarge: 'Файл журнала превышает 500MB и не может быть открыт'
|
||||
FileDropFailed: 'Не удалось очистить файл {{ .name }}: {{ .err }}'
|
||||
FileDropSuccess: 'Файл {{ .name }} успешно очищен, удалено {{ .count }} файлов, освобождено {{ .size }} дискового пространства'
|
||||
FileDropSum: 'Очистка файлов завершена успешно, всего удалено {{ .count }} файлов, освобождено {{ .size }} дискового пространства'
|
||||
ErrBadDecrypt: 'Ошибка пароля расшифровки сжатого пакета, распаковка не удалась, проверьте и повторите попытку'
|
||||
ErrAliasIsExist: 'Псевдоним уже существует'
|
||||
ErrBackupMatch: 'Файл резервной копии не соответствует некоторым текущим данным веб-сайта {{ .detail }}'
|
||||
ErrBackupExist: 'Соответствующая часть исходных данных в файле резервной копии не существует {{ .detail }}'
|
||||
ErrPHPResource: 'Локальная операционная среда не поддерживает переключение!'
|
||||
ErrPHPResource: 'Локальная операционная среда не поддерживает переключение'
|
||||
ErrPathPermission: 'В каталоге index обнаружена папка с разрешениями, отличными от 1000:1000, что может привести к ошибке Отказано в доступе на веб-сайте. Пожалуйста, нажмите кнопку Сохранить выше.'
|
||||
ErrDomainIsUsed: 'Доменное имя уже используется веб-сайтом [{{ .name }}]'
|
||||
ErrDomainFormat: 'Неверный формат доменного имени {{ .name }}'
|
||||
@@ -155,11 +143,11 @@ ErrDefaultAlias: 'по умолчанию зарезервирован код,
|
||||
ErrParentWebsite: 'Сначала вам необходимо удалить дочерний сайт {{ .name }}'
|
||||
ErrBuildDirNotFound: 'Каталог сборки не существует'
|
||||
ErrImageNotExist: 'Образ операционной среды {{ .name }} не существует, пожалуйста, отредактируйте операционную среду заново'
|
||||
ErrProxyIsUsed: "Балансировка нагрузки используется обратным прокси, невозможно удалить"
|
||||
ErrSSLValid: 'Файл сертификата аномален, проверьте статус сертификата!'
|
||||
ErrWebsiteDir: "Пожалуйста, выберите директорию внутри директории сайта."
|
||||
ErrComposerFileNotFound: "Файл composer.json не существует"
|
||||
ErrRuntimeNoPort: "Среда выполнения не настроена с портом, сначала отредактируйте среду выполнения."
|
||||
ErrProxyIsUsed: 'Балансировка нагрузки используется обратным прокси, невозможно удалить'
|
||||
ErrSSLValid: 'Файл сертификата аномален, проверьте статус сертификата'
|
||||
ErrWebsiteDir: 'Пожалуйста, выберите директорию внутри директории сайта.'
|
||||
ErrComposerFileNotFound: 'Файл composer.json не существует'
|
||||
ErrRuntimeNoPort: 'Среда выполнения не настроена с портом, сначала отредактируйте среду выполнения.'
|
||||
Status: 'Статус'
|
||||
start: 'Запустить'
|
||||
stop: 'Остановить'
|
||||
@@ -169,14 +157,12 @@ pause: 'Приостановить'
|
||||
unpause: 'Возобновить'
|
||||
remove: 'Удалить'
|
||||
delete: 'Удалить'
|
||||
ErrDefaultWebsite: 'Веб-сайт по умолчанию уже установлен, отмените его перед настройкой!'
|
||||
ErrDefaultWebsite: 'Веб-сайт по умолчанию уже установлен, отмените его перед настройкой'
|
||||
SSL: 'Сертификат'
|
||||
Set: 'Настройки'
|
||||
|
||||
#ssl
|
||||
ErrSSLCannotDelete: 'Сертификат {{ .name }} используется веб-сайтом и не может быть удален'
|
||||
ErrAccountCannotDelete: 'Учетная запись связана с сертификатом и не может быть удалена'
|
||||
ErrSSLApply: 'Обновление сертификата прошло успешно, перезагрузка OpenResty не удалась, проверьте конфигурацию!'
|
||||
ErrSSLApply: 'Обновление сертификата прошло успешно, перезагрузка OpenResty не удалась, проверьте конфигурацию'
|
||||
ErrEmailIsExist: 'Почтовый ящик уже существует'
|
||||
ErrSSLKeyNotFound: 'Файл закрытого ключа не существует'
|
||||
ErrSSLCertificateNotFound: 'Файл сертификата не существует'
|
||||
@@ -189,7 +175,7 @@ dnsAccount: 'DNS Авто'
|
||||
dnsManual: 'Руководство по DNS'
|
||||
http: 'HTTP'
|
||||
ApplySSLFailed: 'Заявка на сертификат [{{ .domain }}] не выполнена, {{ .detail }}'
|
||||
ApplySSLSuccess: 'Заявка на сертификат [{{ .domain }}] успешно подана! '
|
||||
ApplySSLSuccess: 'Заявка на сертификат [{{ .domain }}] успешно подана'
|
||||
DNSAccountName: 'DNS-аккаунт [{{ .name }}] поставщик [{{ .type }}]'
|
||||
PushDirLog: 'Сертификат перемещен в каталог [{{ .path }}] {{ .status }}'
|
||||
ErrDeleteCAWithSSL: 'Текущая организация имеет сертификат, который был выдан и не может быть удален.'
|
||||
@@ -204,94 +190,109 @@ ExecShellSuccess: 'Скрипт выполнен успешно'
|
||||
StartUpdateSystemSSL: 'Начать обновление системного сертификата'
|
||||
UpdateSystemSSLSuccess: 'Сертификат системы обновлен успешно'
|
||||
ErrWildcardDomain: 'Невозможно подать заявку на получение подстановочного сертификата доменного имени в режиме HTTP'
|
||||
ErrApplySSLCanNotDelete: "Нельзя удалить сертификат {{.name}}, который находится в процессе выпуска. Пожалуйста, попробуйте позже."
|
||||
|
||||
#mysql
|
||||
ErrApplySSLCanNotDelete: 'Нельзя удалить сертификат {{ .name }}, который находится в процессе выпуска. Пожалуйста, попробуйте позже.'
|
||||
StartPushSSLToNode: 'Начало отправки сертификата на узел'
|
||||
PushSSLToNodeFailed: 'Не удалось отправить сертификат на узел: {{ .err }}'
|
||||
PushSSLToNodeSuccess: 'Сертификат успешно отправлен на узел'
|
||||
ErrUserIsExist: 'Текущий пользователь уже существует, пожалуйста, введите еще раз'
|
||||
ErrDatabaseIsExist: 'Текущая база данных уже существует, пожалуйста, введите ее повторно'
|
||||
ErrExecTimeOut: 'Время выполнения SQL истекло, проверьте базу данных'
|
||||
ErrRemoteExist: 'Удаленная база данных с таким именем уже существует. Измените его и повторите попытку'
|
||||
ErrLocalExist: 'Имя уже существует в локальной базе данных, измените его и повторите попытку'
|
||||
RemoteBackup: "Для резервного копирования удаленной базы данных необходимо сначала запустить службу базы данных локального контейнера с помощью образа {{ .name }}, пожалуйста, подождите..."
|
||||
RemoteRecover: "Для восстановления удаленной базы данных необходимо сначала запустить службу базы данных локального контейнера с помощью образа {{ .name }}, пожалуйста, подождите..."
|
||||
Arg: "Аргумент"
|
||||
|
||||
#редис
|
||||
RemoteBackup: 'Для резервного копирования удаленной базы данных необходимо сначала запустить службу базы данных локального контейнера с помощью образа {{ .name }}, пожалуйста, подождите...'
|
||||
RemoteRecover: 'Для восстановления удаленной базы данных необходимо сначала запустить службу базы данных локального контейнера с помощью образа {{ .name }}, пожалуйста, подождите...'
|
||||
Arg: 'Аргумент'
|
||||
ErrTypeOfRedis: 'Тип файла восстановления не соответствует текущему методу сохранения. Измените его и повторите попытку'
|
||||
|
||||
#контейнер
|
||||
ErrInUsed: "{{ .detail }} используется и не может быть удалено!"
|
||||
ErrObjectInUsed: "Этот объект используется и не может быть удален!"
|
||||
ErrObjectBeDependent: "Этот образ зависит от других образов и не может быть удален!"
|
||||
ErrPortRules: 'Номер порта не совпадает, введите заново!'
|
||||
ErrInUsed: '{{ .detail }} используется и не может быть удалено'
|
||||
ErrObjectInUsed: 'Этот объект используется и не может быть удален'
|
||||
ErrObjectBeDependent: 'Этот образ зависит от других образов и не может быть удален'
|
||||
ErrPortRules: 'Номер порта не совпадает, введите заново'
|
||||
ErrPgImagePull: 'Время извлечения изображения истекло. Настройте ускорение изображения или вручную извлеките изображение {{ .name }} и повторите попытку'
|
||||
PruneHelper: "Эта очистка {{ .name }} освободила дисковое пространство {{ .size }}"
|
||||
ImageRemoveHelper: "Удалён образ {{ .name }}, освобождено {{ .size }} дискового пространства"
|
||||
BuildCache: "Кэш сборки"
|
||||
Volume: "Том хранилища"
|
||||
Network: "Сеть"
|
||||
PruneStart: "Очистка выполняется, пожалуйста, подождите..."
|
||||
|
||||
#время выполнения
|
||||
ErrFileNotExist: 'Файл {{ .detail }} не существует! Проверьте целостность исходного файла!'
|
||||
PruneHelper: 'Эта очистка {{ .name }} освободила дисковое пространство {{ .size }}'
|
||||
ImageRemoveHelper: 'Удалён образ {{ .name }}, освобождено {{ .size }} дискового пространства'
|
||||
BuildCache: 'Кэш сборки'
|
||||
Volume: 'Том хранилища'
|
||||
Network: 'Сеть'
|
||||
PruneStart: 'Очистка выполняется, пожалуйста, подождите...'
|
||||
ContainerBackupPrepare: 'Подготовить рабочую область резервной копии контейнера'
|
||||
ContainerBackupStop: 'Остановить контейнер перед резервным копированием'
|
||||
ContainerBackupInspect: 'Сделать резервную копию inspect и сети контейнера'
|
||||
ContainerBackupMounts: 'Сделать резервную копию точек монтирования контейнера'
|
||||
ContainerBackupMeta: 'Записать метаданные резервной копии контейнера'
|
||||
ContainerBackupStart: 'Запустить контейнер после резервного копирования'
|
||||
ContainerRecoverPrepare: 'Подготовить рабочую область восстановления контейнера'
|
||||
ContainerRecoverExtract: 'Распаковать архив резервной копии контейнера'
|
||||
ContainerRecoverParse: 'Загрузить метаданные резервной копии контейнера'
|
||||
ContainerRecoverCreate: 'Пересоздать контейнер'
|
||||
ContainerRecoverMounts: 'Восстановить точки монтирования контейнера'
|
||||
ContainerRecoverStart: 'Запустить восстановленный контейнер'
|
||||
ContainerRecoverCleanup: 'Очистить рабочую область восстановления контейнера'
|
||||
ComposeBackupPrepare: 'Подготовить рабочую область резервной копии compose'
|
||||
ComposeBackupStop: 'Остановить compose перед резервным копированием'
|
||||
ComposeBackupFiles: 'Сделать резервную копию файлов compose'
|
||||
ComposeBackupContainers: 'Сделать резервную копию контейнеров compose'
|
||||
ComposeBackupMeta: 'Записать метаданные резервной копии compose'
|
||||
ComposeBackupStart: 'Запустить compose после резервного копирования'
|
||||
ComposeBackupCleanup: 'Очистить рабочую область резервного копирования compose'
|
||||
ComposeRecoverPrepare: 'Подготовить рабочую область восстановления compose'
|
||||
ComposeRecoverExtract: 'Распаковать архив резервной копии compose'
|
||||
ComposeRecoverMeta: 'Загрузить метаданные резервной копии compose'
|
||||
ComposeRecoverMetaLogName: 'Метаданные восстановления compose: имя={{ .name }}'
|
||||
ComposeRecoverMetaLogPath: 'Метаданные восстановления compose: путьБэкапа={{ .backupPath }} путьНазначения={{ .targetDir }}'
|
||||
ComposeRecoverMetaLogCount: 'Метаданные восстановления compose: файлов={{ .files }} контейнеров={{ .containers }}'
|
||||
ComposeRecoverFiles: 'Восстановить файлы compose'
|
||||
ComposeRecoverContainers: 'Восстановить контейнеры compose'
|
||||
ComposeRecoverRecord: 'Сохранить запись compose'
|
||||
ComposeRecoverCleanup: 'Очистить рабочую область восстановления compose'
|
||||
ErrFileNotExist: 'Файл {{ .detail }} не существует Проверьте целостность исходного файла'
|
||||
ErrImageBuildErr: 'Сборка образа не удалась'
|
||||
ErrImageExist: "Изображение уже существует! Пожалуйста, измените имя изображения."
|
||||
ErrImageExist: 'Изображение уже существует Пожалуйста, измените имя изображения.'
|
||||
ErrDelWithWebsite: 'Операционная среда уже связана с веб-сайтом и не может быть удалена'
|
||||
ErrRuntimeStart: 'Запуск не удался'
|
||||
ErrPackageJsonNotFound: 'файл package.json не существует'
|
||||
ErrScriptsNotFound: 'Элемент конфигурации скриптов не найден в package.json'
|
||||
ErrContainerNameNotFound: 'Не удалось получить имя контейнера, проверьте файл .env'
|
||||
ErrNodeModulesNotFound: 'Папка node_modules не существует! Измените среду выполнения или дождитесь ее успешного запуска'
|
||||
ErrContainerNameNotFound: 'Не удалось получить имя контейнера, проверьте файл.env'
|
||||
ErrNodeModulesNotFound: 'Папка node_modules не существует Измените среду выполнения или дождитесь ее успешного запуска'
|
||||
ErrContainerNameIsNull: 'Имя контейнера не существует'
|
||||
ErrPHPPortIsDefault: "A porta 9000 é a porta padrão, por favor, modifique e tente novamente"
|
||||
ErrPHPRuntimePortFailed: "A porta {{ .name }} já está sendo usada pelo ambiente de tempo de execução atual, por favor, modifique e tente novamente"
|
||||
|
||||
#инструмент
|
||||
ErrPHPPortIsDefault: 'A porta 9000 é a porta padrão, por favor, modifique e tente novamente'
|
||||
ErrPHPRuntimePortFailed: 'A porta {{ .name }} já está sendo usada pelo ambiente de tempo de execução atual, por favor, modifique e tente novamente'
|
||||
ErrConfigNotFound: 'Файл конфигурации не существует'
|
||||
ErrConfigParse: 'Неверный формат файла конфигурации'
|
||||
ErrConfigIsNull: 'Файл конфигурации не может быть пустым'
|
||||
ErrConfigDirNotFound: 'Рабочий каталог не существует'
|
||||
ErrConfigAlreadyExist: 'Файл конфигурации с таким именем уже существует'
|
||||
ErrUserFindErr: 'Поиск пользователя {{ .name }} не удался {{ .err }}'
|
||||
|
||||
#cronjob
|
||||
CutWebsiteLogSuccess: 'Журнал веб-сайта {{ .name }} успешно вырезан, путь к резервной копии {{ .path }}'
|
||||
HandleShell: 'Выполнить скрипт {{ .name }}'
|
||||
HandleCurl: "Доступ к URL {{ .name }}"
|
||||
HandleCurl: 'Доступ к URL {{ .name }}'
|
||||
HandleNtpSync: 'Синхронизация системного времени'
|
||||
HandleSystemClean: 'Очистка системного кэша'
|
||||
SystemLog: 'Системный лог'
|
||||
CutWebsiteLog: 'Ротация логов сайта'
|
||||
FileOrDir: 'Каталог / Файл'
|
||||
UploadFile: 'Загрузка файла резервной копии {{ .file }} в {{ .backup }}'
|
||||
Upload: "Загрузить"
|
||||
IgnoreBackupErr: "Ошибка резервного копирования: {{ .detail }}, игнорируем эту ошибку..."
|
||||
IgnoreUploadErr: "Ошибка загрузки: {{ .detail }}, игнорируем эту ошибку..."
|
||||
LoadBackupFailed: "Не удалось получить подключение к учетной записи резервной копии, ошибка: {{ .detail }}"
|
||||
InExecuting: "Текущая задача выполняется, не повторяйте выполнение!"
|
||||
NoSuchResource: "В базе данных не найдено содержимое резервной копии, пропускаем..."
|
||||
CleanLog: "Очистить журнал"
|
||||
CleanLogByName: "Очистить журнал {{.name}}"
|
||||
WafIpGroupNotFound: "Группа IP WAF не найдена. Пожалуйста, перейдите в [Расширенные функции - WAF - Черный/Белый список - Группа IP], чтобы добавить группу IP с помощью метода удаленной загрузки."
|
||||
|
||||
#ящик для инструментов
|
||||
ErrNotExistUser: 'Текущий пользователь не существует, измените его и повторите попытку!'
|
||||
ErrBanAction: 'Настройка не удалась. Текущая служба {{ .name }} недоступна. Проверьте и повторите попытку!'
|
||||
ErrClamdscanNotFound: 'Команда clamdscan не обнаружена, обратитесь к документации, чтобы установить ее!'
|
||||
TaskSwapSet: "Настройка Swap"
|
||||
SetSwap: "Настроить Swap {{ .path }} - {{ .size }}"
|
||||
CreateSwap: "Создать Файл Swap"
|
||||
FormatSwap: "Форматировать Файл Swap"
|
||||
EnableSwap: "Включить Swap"
|
||||
|
||||
#tamper
|
||||
CleanTamperSetting: "Очистить исторические настройки защиты"
|
||||
SetTamperWithRules: "Запустить защиту каталога согласно правилам"
|
||||
ProtectDir: "Защитить каталог {{ .name }}"
|
||||
IgnoreFile: "Защитить файл {{ .name }}"
|
||||
TamperSetSuccessful: "Настройки защиты завершены, начинаю мониторинг следующих каталогов:"
|
||||
|
||||
#ваф
|
||||
Upload: 'Загрузить'
|
||||
IgnoreBackupErr: 'Ошибка резервного копирования: {{ .detail }}, игнорируем эту ошибку...'
|
||||
IgnoreUploadErr: 'Ошибка загрузки: {{ .detail }}, игнорируем эту ошибку...'
|
||||
LoadBackupFailed: 'Не удалось получить подключение к учетной записи резервной копии, ошибка: {{ .detail }}'
|
||||
InExecuting: 'Текущая задача выполняется, не повторяйте выполнение'
|
||||
NoSuchResource: 'В базе данных не найдено содержимое резервной копии, пропускаем...'
|
||||
CleanLog: 'Очистить журнал'
|
||||
CleanLogByName: 'Очистить журнал {{ .name }}'
|
||||
WafIpGroupNotFound: 'Группа IP WAF не найдена. Пожалуйста, перейдите в [Расширенные функции - WAF - Черный/Белый список - Группа IP], чтобы добавить группу IP с помощью метода удаленной загрузки.'
|
||||
ErrNotExistUser: 'Текущий пользователь не существует, измените его и повторите попытку'
|
||||
ErrBanAction: 'Настройка не удалась. Текущая служба {{ .name }} недоступна. Проверьте и повторите попытку'
|
||||
ErrClamdscanNotFound: 'Команда clamdscan не обнаружена, обратитесь к документации, чтобы установить ее'
|
||||
TaskSwapSet: 'Настройка Swap'
|
||||
SetSwap: 'Настроить Swap {{ .path }} - {{ .size }}'
|
||||
CreateSwap: 'Создать Файл Swap'
|
||||
FormatSwap: 'Форматировать Файл Swap'
|
||||
EnableSwap: 'Включить Swap'
|
||||
CleanTamperSetting: 'Очистить исторические настройки защиты'
|
||||
SetTamperWithRules: 'Запустить защиту каталога согласно правилам'
|
||||
ProtectDir: 'Защитить каталог {{ .name }}'
|
||||
IgnoreFile: 'Защитить файл {{ .name }}'
|
||||
TamperSetSuccessful: 'Настройки защиты завершены, начинаю мониторинг следующих каталогов:'
|
||||
ErrScope: 'Изменение этой конфигурации не поддерживается'
|
||||
ErrStateChange: 'He удалось изменить состояние'
|
||||
ErrRuleExist: 'Правило уже существует'
|
||||
@@ -299,7 +300,7 @@ ErrRuleNotExist: 'Правило не существует'
|
||||
ErrParseIP: 'Неправильный формат IP'
|
||||
ErrDefaultIP: 'по умолчанию это зарезервированное имя, пожалуйста, измените его на другое имя'
|
||||
ErrGroupInUse: 'IP-группа используется черным/белым списком и не может быть удалена'
|
||||
ErrIPGroupAclUse: "Группа IP используется пользовательскими правилами сайта {{ .name }}, невозможно удалить"
|
||||
ErrIPGroupAclUse: 'Группа IP используется пользовательскими правилами сайта {{ .name }}, невозможно удалить'
|
||||
ErrGroupExist: 'Имя группы IP уже существует'
|
||||
ErrIPRange: 'Неверный диапазон IP-адресов'
|
||||
ErrIPExist: 'IP-адрес уже существует'
|
||||
@@ -310,10 +311,10 @@ xss: 'XSS'
|
||||
phpExec: 'Выполнение PHP-скрипта'
|
||||
oneWordTrojan: 'Троян из одного слова'
|
||||
appFilter: 'Применить фильтрацию опасных каталогов'
|
||||
webshell: 'Be6-оболочка'
|
||||
webShell: 'Be6-оболочка'
|
||||
args: 'Правила параметров'
|
||||
protocolFilter: 'Фильтрация протоколов'
|
||||
javaFileter: 'Фильтр опасных файлов Java'
|
||||
javaFilter: 'Фильтр опасных файлов Java'
|
||||
scannerFilter: 'Фильтр сканера'
|
||||
escapeFilter: 'фильтр побега'
|
||||
customRule: 'Пользовательское правило'
|
||||
@@ -343,10 +344,9 @@ ErrDBNotExist: 'База данных не существует'
|
||||
allow: 'разрешить'
|
||||
deny: 'отрицать'
|
||||
OpenrestyNotFound: 'Openresty не установлен'
|
||||
remoteIpIsNull: "Список IP пуст"
|
||||
OpenrestyVersionErr: "Версия Openresty слишком низкая, пожалуйста, обновите Openresty до 1.27.1.2-2-2-focal"
|
||||
|
||||
#задача
|
||||
remoteIpIsNull: 'Список IP пуст'
|
||||
OpenrestyVersionErr: 'Версия Openresty слишком низкая, пожалуйста, обновите Openresty до 1.27.1.2-2-2-focal'
|
||||
ErrFileTooLarge: 'File is larger than 1 MB and cannot be uploaded'
|
||||
TaskStart: '{{ .name }} Задача начинается [START]'
|
||||
TaskEnd: '{{ .name }} Задача выполнена [ЗАВЕРШЕНО]'
|
||||
TaskFailed: 'Задача {{ .name }} не выполнена'
|
||||
@@ -362,18 +362,18 @@ TaskDelete: 'Удалить'
|
||||
TaskUpgrade: 'Обновить'
|
||||
TaskUpdate: 'Обновить'
|
||||
TaskRestart: 'Перезапуск'
|
||||
TaskProtect: "Защита"
|
||||
TaskProtect: 'Защита'
|
||||
TaskBackup: 'Резервное копирование'
|
||||
TaskRecover: 'Восстановить'
|
||||
TaskRollback: 'Откат'
|
||||
TaskPull: 'Вытянуть'
|
||||
TaskCommit: 'Kоммит'
|
||||
ЗадачаСборка: 'Сборка'
|
||||
TaskBuild: 'Сборка'
|
||||
TaskPush: 'Push'
|
||||
TaskClean: "Очистка"
|
||||
TaskClean: 'Очистка'
|
||||
TaskHandle: 'Выполнить'
|
||||
TaskImport: "Импорт"
|
||||
TaskExport: "Экспорт"
|
||||
TaskImport: 'Импорт'
|
||||
TaskExport: 'Экспорт'
|
||||
Website: 'Be6-сайт'
|
||||
App: 'Приложение'
|
||||
Runtime: 'Среда выполнения'
|
||||
@@ -405,18 +405,12 @@ RuntimeExtension: 'Расширение среды выполнения'
|
||||
TaskIsExecuting: 'Задача выполняется'
|
||||
CustomAppstore: 'Хранилище пользовательских приложений'
|
||||
TaskExec: 'Выполнить'
|
||||
TaskBatch: "Пакетная операция"
|
||||
TaskBatch: 'Пакетная операция'
|
||||
FileConvert: 'Преобразование формата файла'
|
||||
|
||||
# task - clam
|
||||
Clamscan: "Сканировать {{ .name }}"
|
||||
TaskScan: "Сканировать"
|
||||
|
||||
# задача - ай
|
||||
Clamscan: 'Сканировать {{ .name }}'
|
||||
TaskScan: 'Сканировать'
|
||||
OllamaModelPull: 'Вытянуть модель Ollama {{ .name }}'
|
||||
OllamaModelSize: 'Получить размер модели Ollama {{ .name }}'
|
||||
|
||||
# снимок задачи
|
||||
Snapshot: 'Снимок'
|
||||
SnapDBInfo: 'Запись информации о базе данных 1Panel'
|
||||
SnapCopy: 'Копировать файлы и каталоги {{ .name }}'
|
||||
@@ -442,7 +436,6 @@ SnapCompressSize: 'Размер файла снимка {{ .name }}'
|
||||
SnapUpload: 'Загрузить файл снимка'
|
||||
SnapUploadTo: 'Загрузить файл снимка в {{ .name }}'
|
||||
SnapUploadRes: 'Загрузить файл снимка в {{ .name }}'
|
||||
|
||||
SnapshotRecover: 'Восстановление моментального снимка'
|
||||
RecoverDownload: 'Загрузить файл снимка'
|
||||
Download: 'Скачать'
|
||||
@@ -450,10 +443,10 @@ RecoverDownloadAccount: 'Получить резервную копию учет
|
||||
RecoverDecompress: 'Распаковка сжатых файлов моментальных снимков'
|
||||
Decompress: 'Декомпрессия'
|
||||
BackupBeforeRecover: 'Резервное копирование системных данных перед созданием снимка'
|
||||
Readjson: 'Прочитать файл JSON в снимке'
|
||||
ReadjsonPath: 'Получить путь к файлу JSON в снимке'
|
||||
ReadjsonContent: 'Прочитать файл Json'
|
||||
ReadjsonMarshal: 'Обработка Json escape'
|
||||
ReadJson: 'Прочитать файл JSON в снимке'
|
||||
ReadJsonPath: 'Получить путь к файлу JSON в снимке'
|
||||
ReadJsonContent: 'Прочитать файл Json'
|
||||
ReadJsonMarshal: 'Обработка Json escape'
|
||||
RecoverApp: 'Восстановить установленные приложения'
|
||||
RecoverWebsite: 'Восстановить каталог веб-сайта'
|
||||
RecoverAppImage: 'Восстановить резервную копию образа снимка'
|
||||
@@ -467,9 +460,6 @@ RecoverDaemonJson: 'Восстановить файл конфигурации
|
||||
RecoverDBData: 'Восстановить данные базы данных'
|
||||
RecoverBackups: 'Восстановить локальный каталог резервных копий'
|
||||
RecoverPanelData: 'Каталог данных восстановления'
|
||||
|
||||
# задача - контейнер
|
||||
ContainerNewCliet: 'Инициализировать Docker-клиент'
|
||||
ContainerImagePull: 'Извлечь изображение контейнера {{ .name }}'
|
||||
ContainerRemoveOld: 'Удалить исходный контейнер {{ .name }}'
|
||||
ContainerImageCheck: 'Проверьте, нормально ли извлекается изображение'
|
||||
@@ -478,22 +468,13 @@ ContainerRecreate: 'Обновление контейнера не удалос
|
||||
ContainerCreate: 'Создать новый контейнер {{ .name }}'
|
||||
ContainerCreateFailed: 'Создание контейнера не удалось, удалите неудавшийся контейнер'
|
||||
ContainerStartCheck: 'Проверить, запущен ли контейнер'
|
||||
|
||||
# задача - изображение
|
||||
ImageBuild: 'Создание изображения'
|
||||
ImageBuildStdoutCheck: 'Анализ содержимого выходного изображения'
|
||||
ImageBuildRes: 'Выходные данные сборки образа: {{ .name }}'
|
||||
ImagePull: 'Вытащить изображение'
|
||||
ImageRepoAuthFromDB: 'Получить информацию об аутентификации репозитория из базы данных'
|
||||
ImaegPullRes: 'Выход извлечения изображения: {{ .name }}'
|
||||
ImagePush: 'Отправить изображение'
|
||||
ImageRenameTag: 'Изменить тег изображения'
|
||||
ImageNewTag: 'Новый тег изображения {{ .name }}'
|
||||
ImaegPushRes: 'Вывод push-изображения: {{ .name }}'
|
||||
ComposeCreate: 'Создать композицию'
|
||||
ComposeCreateRes: 'Составить вывод создания: {{ .name }}'
|
||||
|
||||
# задача - веб-сайт
|
||||
BackupNginxConfig: 'Резервное копирование файла конфигурации OpenResty веб-сайта'
|
||||
CompressFileSuccess: 'Каталог успешно сжат, сжат в {{ .name }}'
|
||||
CompressDir: 'Каталог сжатия'
|
||||
@@ -504,40 +485,33 @@ websiteDir: 'Каталог веб-сайтов'
|
||||
RecoverFailedStartRollBack: 'Восстановление не удалось, начать откат'
|
||||
AppBackupFileIncomplete: 'Файл резервной копии неполный и в нем отсутствуют файлы app.json или app.tar.gz'
|
||||
AppAttributesNotMatch: 'Тип или имя приложения не совпадают'
|
||||
ErrAlertSync: 'Ошибка синхронизации информации о тревоге, проверьте и повторите попытку'
|
||||
ErrInstallExtension: 'Уже выполняется задача установки, подождите, пока задача завершится'
|
||||
PanelAlertTitle: 'Уведомление панели'
|
||||
TestAlertTitle: 'Тестовое письмо - проверка подключения'
|
||||
TestAlert: 'Это тестовое письмо для проверки правильности настройки отправки писем.'
|
||||
LicenseExpirationAlert: 'Ваш панель {{ .node }}{{ .ip }}, лицензия истечет через {{ .day }} дней. Войдите в панель для получения деталей.'
|
||||
CronJobFailedAlert: 'Ваш панель {{ .node }}{{ .ip }}, выполнение запланированной задачи {{ .name }} не удалось. Войдите в панель для получения деталей.'
|
||||
ClamAlert: 'Ваш панель {{ .node }}{{ .ip }}, задача сканирования на вирусы обнаружила {{ .num }} зараженных файлов. Войдите в панель для получения деталей.'
|
||||
WebSiteAlert: 'Ваш панель {{ .node }}{{ .ip }}, {{ .num }} веб-сайтов истечут через {{ .day }} дней. Войдите в панель для получения деталей.'
|
||||
SSLAlert: 'Ваш панель {{ .node }}{{ .ip }}, {{ .num }} сертификатов SSL истечут через {{ .day }} дней. Войдите в панель для получения деталей.'
|
||||
DiskUsedAlert: 'Ваш панель {{ .node }}{{ .ip }}, использование диска {{ .name }} достигло {{ .used }}. Войдите в панель для получения деталей.'
|
||||
ResourceAlert: 'Ваш панель {{ .node }}{{ .ip }}, средний уровень использования {{ .name }} за {{ .time }} минут составляет {{ .used }}. Войдите в панель для получения деталей.'
|
||||
PanelVersionAlert: 'Ваш панель {{ .node }}{{ .ip }}, доступна последняя версия панели для обновления. Войдите в панель для получения деталей.'
|
||||
PanelPwdExpirationAlert: 'Ваш панель {{ .node }}{{ .ip }}, пароль панели истечет через {{ .day }} дней. Войдите в панель для получения деталей.'
|
||||
CommonAlert: 'Ваш панель {{ .node }}{{ .ip }}, {{ .msg }}. Войдите в панель для получения деталей.'
|
||||
NodeExceptionAlert: 'Ваш панель {{ .node }}{{ .ip }}, {{ .num }} узлов работают с ошибками. Войдите в панель для получения деталей.'
|
||||
LicenseExceptionAlert: 'Ваш панель {{ .node }}{{ .ip }}, {{ .num }} лицензий имеют ошибки. Войдите в панель для получения деталей.'
|
||||
SSHAndPanelLoginAlert: 'Ваш панель {{ .node }}{{ .ip }}, вход {{ .name }} с адреса {{ .loginIp }} является аномальным. Войдите в панель для получения деталей.'
|
||||
DeviceNotFound: 'Устройство {{ .name }} не найдено'
|
||||
DeviceIsMounted: 'Устройство {{ .name }} подключено, сначала отключите'
|
||||
PartitionDiskErr: 'Не удалось разделить, {{ .err }}'
|
||||
FormatDiskErr: 'Не удалось отформатировать диск, {{ .err }}'
|
||||
MountDiskErr: 'Не удалось подключить диск, {{ .err }}'
|
||||
XfsNotFound: 'Файловая система xfs не обнаружена, сначала установите xfsprogs'
|
||||
|
||||
#тревога
|
||||
ErrAlert: 'Формат предупреждающего сообщения неверен, проверьте и повторите попытку!'
|
||||
ErrAlertPush: 'Ошибка при отправке оповещения. Проверьте и повторите попытку!'
|
||||
ErrAlertSave: 'Ошибка сохранения информации о тревоге, проверьте и повторите попытку!'
|
||||
ErrAlertSync: 'Ошибка синхронизации информации о тревоге, проверьте и повторите попытку!'
|
||||
ErrAlertRemote: 'Ошибка удаленного сообщения об ошибке, проверьте и повторите попытку!'
|
||||
|
||||
#task - runtime
|
||||
ErrInstallExtension: "Уже выполняется задача установки, подождите, пока задача завершится"
|
||||
|
||||
# alert mail template
|
||||
PanelAlertTitle: "Уведомление панели"
|
||||
TestAlertTitle: "Тестовое письмо - проверка подключения"
|
||||
TestAlert: "Это тестовое письмо для проверки правильности настройки отправки писем."
|
||||
LicenseExpirationAlert: "Ваш панель {{ .node }}{{ .ip }}, лицензия истечет через {{ .day }} дней. Войдите в панель для получения деталей."
|
||||
CronJobFailedAlert: "Ваш панель {{ .node }}{{ .ip }}, выполнение запланированной задачи {{ .name }} не удалось. Войдите в панель для получения деталей."
|
||||
ClamAlert: "Ваш панель {{ .node }}{{ .ip }}, задача сканирования на вирусы обнаружила {{ .num }} зараженных файлов. Войдите в панель для получения деталей."
|
||||
WebSiteAlert: "Ваш панель {{ .node }}{{ .ip }}, {{ .num }} веб-сайтов истечут через {{ .day }} дней. Войдите в панель для получения деталей."
|
||||
SSLAlert: "Ваш панель {{ .node }}{{ .ip }}, {{ .num }} сертификатов SSL истечут через {{ .day }} дней. Войдите в панель для получения деталей."
|
||||
DiskUsedAlert: "Ваш панель {{ .node }}{{ .ip }}, использование диска {{ .name }} достигло {{ .used }}. Войдите в панель для получения деталей."
|
||||
ResourceAlert: "Ваш панель {{ .node }}{{ .ip }}, средний уровень использования {{ .name }} за {{ .time }} минут составляет {{ .used }}. Войдите в панель для получения деталей."
|
||||
PanelVersionAlert: "Ваш панель {{ .node }}{{ .ip }}, доступна последняя версия панели для обновления. Войдите в панель для получения деталей."
|
||||
PanelPwdExpirationAlert: "Ваш панель {{ .node }}{{ .ip }}, пароль панели истечет через {{ .day }} дней. Войдите в панель для получения деталей."
|
||||
CommonAlert: "Ваш панель {{ .node }}{{ .ip }}, {{ .msg }}. Войдите в панель для получения деталей."
|
||||
NodeExceptionAlert: "Ваш панель {{ .node }}{{ .ip }}, {{ .num }} узлов работают с ошибками. Войдите в панель для получения деталей."
|
||||
LicenseExceptionAlert: "Ваш панель {{ .node }}{{ .ip }}, {{ .num }} лицензий имеют ошибки. Войдите в панель для получения деталей."
|
||||
SSHAndPanelLoginAlert: "Ваш панель {{ .node }}{{ .ip }}, вход {{ .name }} с адреса {{ .loginIp }} является аномальным. Войдите в панель для получения деталей."
|
||||
|
||||
#disk
|
||||
DeviceNotFound: "Устройство {{ .name }} не найдено"
|
||||
DeviceIsMounted: "Устройство {{ .name }} подключено, сначала отключите"
|
||||
PartitionDiskErr: "Не удалось разделить, {{ .err }}"
|
||||
FormatDiskErr: "Не удалось отформатировать диск, {{ .err }}"
|
||||
MountDiskErr: "Не удалось подключить диск, {{ .err }}"
|
||||
UnMountDiskErr: "Не удалось отключить диск, {{ .err }}"
|
||||
XfsNotFound: "Файловая система xfs не обнаружена, сначала установите xfsprogs"
|
||||
# terminal
|
||||
TerminalAIBlockedRiskyCommand: 'Опасная команда заблокирована: {{ .command }}'
|
||||
TerminalAIThinking: 'AI думает...'
|
||||
TerminalAIReadyToExecute: 'Обдумывание завершено, нажмите Enter для выполнения (время: {{ .duration }}, token: {{ .tokens }})'
|
||||
TerminalAIRequestFailed: 'Ошибка запроса AI: {{ .err }}'
|
||||
|
||||
+211
-238
@@ -1,34 +1,30 @@
|
||||
ErrInvalidParams: 'İstek parametre hatası: {{ .detail }}'
|
||||
ErrTokenParse: 'Token oluşturma hatası: {{ .detail }}'
|
||||
ErrInvalidParams: 'İstek geçersiz: {{ .detail }}'
|
||||
ErrTokenParse: 'Token hatası: {{ .detail }}'
|
||||
ErrInitialPassword: 'Orijinal şifre yanlış'
|
||||
ErrInternalServer: 'Dahili sunucu hatası: {{ .detail }}'
|
||||
ErrRecordExist: 'Kayıt zaten mevcut'
|
||||
ErrInternalServer: 'Sunucu hatası: {{ .detail }}'
|
||||
ErrWgetRemoteFailed: 'Uzak indirme başarısız veya URL erişilemiyor: {{ .detail }}'
|
||||
ErrWgetInvalidContentType: 'Uzak URL bir dosya değil, muhtemelen bir hata sayfası döndürdü, {{ .detail }}'
|
||||
ErrRecordExist: 'Kayıt zaten var'
|
||||
ErrRecordNotFound: 'Kayıt bulunamadı'
|
||||
ErrStructTransform: 'Tür dönüştürme başarısız: {{ .err }}'
|
||||
ErrNotLogin: 'Kullanıcı giriş yapmamış: {{ .detail }}'
|
||||
ErrPasswordExpired: 'Mevcut şifrenin süresi dolmuş: {{ .detail }}'
|
||||
ErrNotSupportType: 'Sistem mevcut türü desteklemiyor: {{ .name }}'
|
||||
ErrProxy: 'İstek hatası, lütfen düğüm durumunu kontrol edin: {{ .detail }}'
|
||||
ErrApiConfigStatusInvalid: 'API arayüzüne erişim yasak: {{ .detail }}'
|
||||
ErrApiConfigKeyInvalid: 'API arayüz anahtarı hatası: {{ .detail }}'
|
||||
ErrApiConfigIPInvalid: 'API arayüzünü çağırmak için kullanılan IP beyaz listede değil: {{ .detail }}'
|
||||
ErrApiConfigDisable: 'Bu arayüz API arayüz çağrılarının kullanımını yasaklıyor: {{ .detail }}'
|
||||
ErrApiConfigKeyTimeInvalid: 'API arayüz zaman damgası hatası: {{ .detail }}'
|
||||
StartPushSSLToNode: "Sertifika düğüme gönderilmeye başlandı"
|
||||
PushSSLToNodeFailed: "Sertifika düğüme gönderilemedi: {{ .err }}"
|
||||
PushSSLToNodeSuccess: "Sertifika düğüme başarıyla gönderildi"
|
||||
ErrShutDown: "Komut manuel olarak sonlandırıldı!"
|
||||
|
||||
ErrMinQuickJump: "Lütfen en az bir hızlı atlama girişi ayarlayın!"
|
||||
ErrMaxQuickJump: "En fazla dört hızlı atlama girişi ayarlayabilirsiniz!"
|
||||
|
||||
#common
|
||||
ErrUsernameIsExist: 'Kullanıcı adı zaten mevcut'
|
||||
ErrNameIsExist: 'İsim zaten mevcut'
|
||||
ErrDemoEnvironment: 'Demo sunucu, bu işlem yasak!'
|
||||
ErrCmdTimeout: 'Komut yürütme zaman aşımına uğradı!'
|
||||
ErrCmdIllegal: 'Yürütme komutunda yasak karakterler var, lütfen değiştirin ve tekrar deneyin!'
|
||||
ErrPortExist: '{{ .port }} portu zaten {{ .type }} [{{ .name }}] tarafından kullanılıyor'
|
||||
ErrStructTransform: 'Dönüşüm hatası: {{ .err }}'
|
||||
ErrNotLogin: 'Oturum açılmamış: {{ .detail }}'
|
||||
ErrPasswordExpired: 'Şifre süresi doldu: {{ .detail }}'
|
||||
ErrNotSupportType: 'Bu tür desteklenmiyor: {{ .name }}'
|
||||
ErrProxy: 'İstek başarısız: {{ .detail }}'
|
||||
ErrApiConfigStatusInvalid: 'API kapalı: {{ .detail }}'
|
||||
ErrApiConfigKeyInvalid: 'Geçersiz API anahtarı: {{ .detail }}'
|
||||
ErrApiConfigIPInvalid: 'IP izinli değil: {{ .detail }}'
|
||||
ErrApiConfigDisable: 'API erişimi kapalı: {{ .detail }}'
|
||||
ErrApiConfigKeyTimeInvalid: 'Zaman damgası hatalı: {{ .detail }}'
|
||||
ErrShutDown: 'Komut manuel durduruldu'
|
||||
ErrMinQuickJump: 'En az bir hızlı erişim ekle'
|
||||
ErrMaxQuickJump: '4 hızlı erişim hakkı var'
|
||||
ErrUsernameIsExist: 'Kullanıcı adı mevcut'
|
||||
ErrNameIsExist: 'İsim mevcut'
|
||||
ErrDemoEnvironment: 'Demo modunda yok'
|
||||
ErrCmdTimeout: 'Komut zaman aşımına uğradı'
|
||||
ErrCmdIllegal: 'Komut geçersiz karakter içeriyor'
|
||||
ErrPortExist: '{{ .port }} portu {{ .type }} [{{ .name }}] tarafından kullanılıyor'
|
||||
TYPE_APP: 'Uygulama'
|
||||
TYPE_RUNTIME: 'Çalışma ortamı'
|
||||
TYPE_DOMAIN: 'Alan Adı'
|
||||
@@ -39,45 +35,43 @@ Failed: 'Başarısız'
|
||||
SystemRestart: 'Sistem yeniden başlatması nedeniyle görev kesildi'
|
||||
ErrGroupIsDefault: 'Varsayılan grup, silinemez'
|
||||
ErrGroupIsInWebsiteUse: 'Grup başka bir web sitesi tarafından kullanılıyor ve silinemez.'
|
||||
Decrypt: "Şifre Çöz"
|
||||
|
||||
#agent
|
||||
ErrAgentAccountBound: 'Bu hesap bir ajana bağlı ve silinemez. Kontrol edip tekrar deneyin!'
|
||||
ErrAgentAccountUnavailable: 'Hesap bağlantı bilgileri kullanılamıyor. Hata: {{ .err }}. Kontrol edip tekrar deneyin!'
|
||||
ErrAgentProviderNotSupported: 'Bu ajan sağlayıcısı desteklenmiyor. Kontrol edip tekrar deneyin!'
|
||||
ErrAgentAccountRequired: 'Lütfen bir ajan hesabı seçip tekrar deneyin.'
|
||||
ErrAgentAccountNotVerified: 'Hesap doğrulanmadı. Kontrol edip tekrar deneyin!'
|
||||
ErrAgentProviderMismatch: 'Hesap sağlayıcısı eşleşmiyor. Kontrol edip tekrar deneyin!'
|
||||
ErrAgentBaseURLRequired: 'Base URL boş olamaz. Kontrol edip tekrar deneyin!'
|
||||
ErrAgentApiKeyRequired: 'API Key boş olamaz. Kontrol edip tekrar deneyin!'
|
||||
ErrAgentComposeRequired: 'Özel compose içeriği boş olamaz. Kontrol edip tekrar deneyin!'
|
||||
ErrAgentIDRequired: 'Ajan ID boş olamaz. Kontrol edip tekrar deneyin!'
|
||||
ErrAgentAccountIDRequired: 'Hesap ID boş olamaz. Kontrol edip tekrar deneyin!'
|
||||
|
||||
#backup
|
||||
Decrypt: 'Şifre Çöz'
|
||||
ErrAgentAccountBound: 'Hesap bir ajana bağlı'
|
||||
ErrAgentAccountUnavailable: 'Hesap bağlantısı yok: {{ .err }}'
|
||||
ErrAgentProviderNotSupported: 'Ajans sağlayıcısı desteklenmiyor'
|
||||
ErrAgentAccountRequired: 'Önce bir ajans hesabı seçin'
|
||||
ErrAgentAccountNotVerified: 'Ajans hesabı doğrulanmadı'
|
||||
ErrAgentAccountModelsRequired: 'Lütfen en az bir model yapılandırın'
|
||||
ErrAgentAccountSingleInitialModel: 'Hesap oluştururken yalnızca bir başlangıç modeli yapılandırılabilir'
|
||||
ErrAgentModelNotInAccount: 'Model seçilen hesapta yapılandırılmadı'
|
||||
ErrAgentModelInUse: 'Bu modeli hâlâ kullanan bağlı bir ajan var: {{ .name }}'
|
||||
ErrAgentBaseURLRequired: 'Temel URL gerekli'
|
||||
ErrAgentApiKeyRequired: 'API anahtarı gerekli'
|
||||
ErrAgentComposeRequired: 'Compose içeriği gerekli'
|
||||
ErrAgentIDRequired: 'Ajans ID gerekli'
|
||||
ErrAgentAccountIDRequired: 'Hesap ID gerekli'
|
||||
ErrAgentLimitReached: 'Community sürümü en fazla {{ .max }} AI ajanını destekler. Sınırı kaldırmak için Professional sürüme yükseltin'
|
||||
Localhost: 'Yerel Makine'
|
||||
ErrBackupInUsed: 'Yedekleme hesabı zamanlanmış görevde kullanıldı ve silinemez.'
|
||||
ErrBackupCheck: 'Yedekleme hesabı test bağlantısı başarısız {{ .err }}'
|
||||
ErrBackupLocalDelete: 'Yerel sunucu yedekleme hesabını silme henüz desteklenmiyor'
|
||||
ErrBackupLocalCreate: 'Yerel sunucu yedekleme hesapları oluşturma henüz desteklenmiyor'
|
||||
|
||||
#app
|
||||
ErrPortInUsed: '{{ .detail }} portu zaten kullanılıyor!'
|
||||
ErrBackupInUsed: 'Yedek hesabı görevde kullanılıyor'
|
||||
ErrBackupCheck: 'Bağlantı testi başarısız: {{ .err }}'
|
||||
ErrBackupLocalDelete: 'Yerel yedek silme yok'
|
||||
ErrBackupLocalCreate: 'Yerel yedek oluşturma yok'
|
||||
ErrPortInUsed: '{{ .detail }} portu zaten kullanılıyor'
|
||||
ErrAppLimit: 'Yüklenen uygulama sayısı sınırı aştı'
|
||||
ErrNotInstall: 'Uygulama yüklenmedi'
|
||||
ErrPortInOtherApp: '{{ .port }} portu zaten {{ .apps }} uygulaması tarafından kullanılıyor!'
|
||||
ErrDbUserNotValid: 'Mevcut veritabanı, kullanıcı adı ve şifre eşleşmiyor!'
|
||||
ErrUpdateBuWebsite: 'Uygulama başarıyla güncellendi, ancak web sitesi yapılandırma dosyası değiştirme başarısız. Lütfen yapılandırmayı kontrol edin! '
|
||||
Err1PanelNetworkFailed: 'Varsayılan konteyner ağ oluşturma başarısız! {{ .detail }}'
|
||||
ErrFileParse: 'Uygulama docker-compose dosya ayrıştırma başarısız!'
|
||||
ErrPortInOtherApp: '{{ .port }} portu zaten {{ .apps }} uygulaması tarafından kullanılıyor'
|
||||
ErrDbUserNotValid: 'Mevcut veritabanı, kullanıcı adı ve şifre eşleşmiyor'
|
||||
ErrUpdateBuWebsite: 'Uygulama başarıyla güncellendi, ancak web sitesi yapılandırma dosyası değiştirme başarısız. Lütfen yapılandırmayı kontrol edin'
|
||||
Err1PanelNetworkFailed: 'Varsayılan konteyner ağ oluşturma başarısız {{ .detail }}'
|
||||
ErrFileParse: 'Uygulama docker-compose dosya ayrıştırma başarısız'
|
||||
ErrInstallDirNotFound: 'Kurulum dizini mevcut değil. Kaldırmanız gerekiyorsa, lütfen Zorla Kaldır seçeneğini seçin'
|
||||
AppStoreIsUpToDate: 'Uygulama mağazası zaten en son sürüm'
|
||||
LocalAppVersionNull: '{{ .name }} uygulaması sürüme senkronize edilmedi! Uygulama listesine eklenemez'
|
||||
LocalAppVersionErr: '{{ .name }} senkronizasyon sürümü {{ .version }} başarısız! {{ .err }}'
|
||||
LocalAppVersionNull: '{{ .name }} uygulaması sürüme senkronize edilmedi Uygulama listesine eklenemez'
|
||||
LocalAppVersionErr: '{{ .name }} senkronizasyon sürümü {{ .version }} başarısız {{ .err }}'
|
||||
ErrFileNotFound: '{{ .name }} dosyası mevcut değil'
|
||||
ErrFileParseApp: '{{ .name }} dosya ayrıştırma başarısız {{ .err }}'
|
||||
ErrAppDirNull: 'Sürüm klasörü mevcut değil'
|
||||
LocalAppErr: 'Uygulama {{ .name }} senkronizasyonu başarısız! {{ .err }}'
|
||||
LocalAppErr: 'Uygulama {{ .name }} senkronizasyonu başarısız {{ .err }}'
|
||||
ErrContainerName: 'Konteyner adı zaten mevcut'
|
||||
ErrCreateHttpClient: 'İstek oluşturma başarısız {{ .err }}'
|
||||
ErrHttpReqTimeOut: 'İstek zaman aşımı {{ .err }}'
|
||||
@@ -90,65 +84,58 @@ ErrAppBackup: '{{ .name }} uygulama yedeklemesi başarısız {{ .err }}'
|
||||
ErrVersionTooLow: 'Mevcut 1Panel sürümü Uygulama Mağazasını güncellemek için çok düşük. Lütfen işlemden önce sürümü yükseltin.'
|
||||
ErrAppNameExist: 'Uygulama adı zaten mevcut'
|
||||
AppStoreIsSyncing: 'Uygulama Mağazası senkronize ediliyor, lütfen daha sonra tekrar deneyin'
|
||||
ErrGetCompose: 'docker-compose.yml dosyası alınamadı! {{ .detail }}'
|
||||
ErrGetCompose: 'docker-compose.yml dosyası alınamadı {{ .detail }}'
|
||||
ErrAppWarn: 'Anormal durum, lütfen günlüğü kontrol edin'
|
||||
ErrAppParamKey: 'Parametre {{ .name }} alanı anormal'
|
||||
ErrAppUpgrade: 'Uygulama {{ .name }} yükseltmesi başarısız {{ .err }}'
|
||||
AppRecover: 'Uygulama {{ .name }} geri alma'
|
||||
PullImageStart: 'Image [{{ .name }}] çekmeye başla'
|
||||
PullImageSuccess: 'Image çekme başarılı'
|
||||
AppStoreIsLastVersion: 'Uygulama Mağazası zaten en son sürüm'
|
||||
AppStoreSyncSuccess: 'Uygulama Mağazası senkronizasyonu başarılı'
|
||||
SyncAppDetail: 'Uygulama yapılandırmasını senkronize et'
|
||||
AppVersionNotMatch: '{{ .name }} uygulaması daha yüksek 1Panel sürümü gerektiriyor, senkronizasyon atlanıyor'
|
||||
MoveSiteDir: "Web sitesi dizini değişikliği tespit edildi. OpenResty web sitesi dizinini {{ .name }} adresine taşımak gerekiyor"
|
||||
MoveSiteDir: 'Web sitesi dizini değişikliği tespit edildi. OpenResty web sitesi dizinini {{ .name }} adresine taşımak gerekiyor'
|
||||
MoveSiteDirSuccess: 'Web sitesi dizini taşıma başarılı'
|
||||
DeleteRuntimePHP: 'PHP çalışma zamanını sil'
|
||||
CustomAppStoreFileValid: 'Uygulama mağazası paketlerinin .tar.gz formatında olması gerekiyor'
|
||||
CustomAppStoreFileValid: 'Uygulama mağazası paketlerinin.tar.gz formatında olması gerekiyor'
|
||||
PullImageTimeout: 'Image çekme zaman aşımı, lütfen image hızlandırmayı artırmayı deneyin veya başka bir image hızlandırmaya geçin'
|
||||
ErrAppIsDown: '{{ .name }} uygulama durumu anormal, lütfen kontrol edin'
|
||||
ErrCustomApps: 'Yüklü bir uygulama var, lütfen önce kaldırın'
|
||||
ErrCustomRuntimes: 'Yüklü bir çalışma ortamı var, lütfen önce silin'
|
||||
ErrAppVersionDeprecated: "{{ .name }} uygulaması mevcut 1Panel sürümü ile uyumlu değil, atlandı"
|
||||
ErrDockerFailed: "Docker durumu anormal, lütfen servis durumunu kontrol edin"
|
||||
ErrDockerComposeCmdNotFound: "Docker Compose komutu mevcut değil, lütfen önce bu komutu host makinesine yükleyin"
|
||||
UseExistImage: 'Görüntü çekme başarısız, mevcut görüntü kullanılıyor'
|
||||
|
||||
#ssh
|
||||
ExportIP: "Giriş IP"
|
||||
ExportArea: "Konum"
|
||||
ExportPort: "Port"
|
||||
ExportAuthMode: "Giriş Yöntemi"
|
||||
ExportUser: "Kullanıcı"
|
||||
ExportStatus: "Giriş Durumu"
|
||||
ExportDate: "Zaman"
|
||||
|
||||
#file
|
||||
ErrAppVersionDeprecated: '{{ .name }} uygulaması mevcut 1Panel sürümü ile uyumlu değil, atlandı'
|
||||
ErrDockerFailed: 'Docker durumu anormal, lütfen servis durumunu kontrol edin'
|
||||
ErrDockerComposeCmdNotFound: 'Docker Compose komutu mevcut değil, lütfen önce bu komutu host makinesine yükleyin'
|
||||
UseExistImage: 'Görüntü zaten mevcut, mevcut görüntü kullanılıyor'
|
||||
ErrDatabaseNotFound: '{{ .name }} veritabanı mevcut değil'
|
||||
ExportIP: 'Giriş IP'
|
||||
ExportArea: 'Konum'
|
||||
ExportPort: 'Port'
|
||||
ExportAuthMode: 'Giriş Yöntemi'
|
||||
ExportUser: 'Kullanıcı'
|
||||
ExportStatus: 'Giriş Durumu'
|
||||
ExportDate: 'Zaman'
|
||||
ErrFileCanNotRead: 'Bu dosya önizlemeyi desteklemiyor'
|
||||
ErrFileToLarge: 'Dosya 10M dan büyük ve açılamıyor'
|
||||
ErrPathNotFound: 'Dizin mevcut değil'
|
||||
ErrMovePathFailed: 'Hedef yol orijinal yolu içeremez!'
|
||||
ErrLinkPathNotFound: 'Hedef yol mevcut değil!'
|
||||
ErrFileIsExist: 'Dosya veya klasör zaten mevcut!'
|
||||
ErrMovePathFailed: 'Hedef yol orijinal yolu içeremez'
|
||||
ErrLinkPathNotFound: 'Hedef yol mevcut değil'
|
||||
ErrFileIsExist: 'Dosya veya klasör zaten mevcut'
|
||||
ErrFileUpload: '{{ .name }} dosya yükleme başarısız {{ .detail }}'
|
||||
ErrFileDownloadDir: 'Klasör indirme desteklenmiyor'
|
||||
ErrCmdNotFound: '{{ .name}} komutu mevcut değil, lütfen önce bu komutu host a yükleyin'
|
||||
ErrCmdNotFound: '{{ .name }} komutu mevcut değil, lütfen önce bu komutu host a yükleyin'
|
||||
ErrSourcePathNotFound: 'Kaynak dizin mevcut değil'
|
||||
ErrFavoriteExist: 'Bu yol zaten favorilere eklendi'
|
||||
ErrInvalidChar: 'Yasak karakterlere izin verilmiyor'
|
||||
ErrPathNotDelete: 'Seçilen dizin silinemez'
|
||||
ErrLogFileToLarge: "Günlük dosyası 500MB'ı aşıyor ve açılamıyor"
|
||||
FileDropFailed: "{{ .name }} dosyası temizlenemedi: {{ .err }}"
|
||||
FileDropSuccess: "{{ .name }} dosyası başarıyla temizlendi, {{ .count }} dosya silindi, {{ .size }} disk alanı boşaltıldı"
|
||||
FileDropSum: "Dosya temizleme tamamlandı, toplam {{ .count }} dosya silindi, toplam {{ .size }} disk alanı boşaltıldı"
|
||||
ErrBadDecrypt: "Sıkıştırılmış paket şifre çözme parolası hatalı, açma başarısız, lütfen kontrol edip tekrar deneyin!"
|
||||
|
||||
|
||||
#website
|
||||
ErrLogFileToLarge: 'Günlük dosyası 500MB''ı aşıyor ve açılamıyor'
|
||||
FileDropFailed: '{{ .name }} dosyası temizlenemedi: {{ .err }}'
|
||||
FileDropSuccess: '{{ .name }} dosyası başarıyla temizlendi, {{ .count }} dosya silindi, {{ .size }} disk alanı boşaltıldı'
|
||||
FileDropSum: 'Dosya temizleme tamamlandı, toplam {{ .count }} dosya silindi, toplam {{ .size }} disk alanı boşaltıldı'
|
||||
ErrBadDecrypt: 'Sıkıştırılmış paket şifre çözme parolası hatalı, açma başarısız, lütfen kontrol edip tekrar deneyin'
|
||||
ErrAliasIsExist: 'Takma ad zaten mevcut'
|
||||
ErrBackupMatch: 'Yedekleme dosyası mevcut web sitesi verilerinin bir kısmıyla eşleşmiyor {{ .detail }}'
|
||||
ErrBackupExist: 'Yedekleme dosyasındaki kaynak verilerin karşılık gelen kısmı mevcut değil {{ .detail }}'
|
||||
ErrPHPResource: 'Yerel işletim ortamı değiştirmeyi desteklemiyor! '
|
||||
ErrPHPResource: 'Yerel işletim ortamı değiştirmeyi desteklemiyor'
|
||||
ErrPathPermission: 'Dizin dizininde 1000:1000 olmayan izinlere sahip bir klasör tespit edildi, bu web sitesinde Erişim reddedildi hatasına neden olabilir. Lütfen yukarıdaki Kaydet düğmesine tıklayın'
|
||||
ErrDomainIsUsed: 'Alan adı zaten [{{ .name }}] web sitesi tarafından kullanılıyor'
|
||||
ErrDomainFormat: '{{ .name }} alan adı formatı yanlış'
|
||||
@@ -156,11 +143,11 @@ ErrDefaultAlias: 'default ayrılmış bir kod, lütfen başka bir kod kullanın'
|
||||
ErrParentWebsite: 'Önce {{ .name }} alt sitesini silmeniz gerekiyor'
|
||||
ErrBuildDirNotFound: 'Yapı dizini mevcut değil'
|
||||
ErrImageNotExist: 'İşletim ortamı {{ .name }} image mevcut değil, lütfen işletim ortamını yeniden düzenleyin'
|
||||
ErrProxyIsUsed: "Yük dengeleme ters proxy tarafından kullanıldı, silinemez"
|
||||
ErrSSLValid: 'Sertifika dosyası anormal, lütfen sertifika durumunu kontrol edin!'
|
||||
ErrWebsiteDir: "Lütfen web sitesi dizini içindeki bir dizin seçin."
|
||||
ErrComposerFileNotFound: "composer.json dosyası mevcut değil"
|
||||
ErrRuntimeNoPort: "Çalışma zamanı ortamı bir porta sahip değil, lütfen önce çalışma zamanı ortamını düzenleyin."
|
||||
ErrProxyIsUsed: 'Yük dengeleme ters proxy tarafından kullanıldı, silinemez'
|
||||
ErrSSLValid: 'Sertifika dosyası anormal, lütfen sertifika durumunu kontrol edin'
|
||||
ErrWebsiteDir: 'Lütfen web sitesi dizini içindeki bir dizin seçin.'
|
||||
ErrComposerFileNotFound: 'composer.json dosyası mevcut değil'
|
||||
ErrRuntimeNoPort: 'Çalışma zamanı ortamı bir porta sahip değil, lütfen önce çalışma zamanı ortamını düzenleyin.'
|
||||
Status: 'Durum'
|
||||
start: 'Başlat'
|
||||
stop: 'Durdur'
|
||||
@@ -170,14 +157,12 @@ pause: 'Duraklat'
|
||||
unpause: 'Devam Et'
|
||||
remove: 'Sil'
|
||||
delete: 'Sil'
|
||||
ErrDefaultWebsite: "Varsayılan bir web sitesi zaten ayarlanmış. Yeni bir tane ayarlamadan önce lütfen iptal edin!"
|
||||
ErrDefaultWebsite: 'Varsayılan bir web sitesi zaten ayarlanmış. Yeni bir tane ayarlamadan önce lütfen iptal edin'
|
||||
SSL: 'Sertifika'
|
||||
Set: 'Ayarlar'
|
||||
|
||||
#ssl
|
||||
ErrSSLCannotDelete: '{{ .name }} sertifikası bir web sitesi tarafından kullanılıyor ve silinemez'
|
||||
ErrAccountCannotDelete: 'Hesap bir sertifikayla ilişkili ve silinemez'
|
||||
ErrSSLApply: 'Sertifika yenileme başarılı, openresty yeniden yükleme başarısız, lütfen yapılandırmayı kontrol edin!'
|
||||
ErrSSLApply: 'Sertifika yenileme başarılı, openresty yeniden yükleme başarısız, lütfen yapılandırmayı kontrol edin'
|
||||
ErrEmailIsExist: 'Posta kutusu zaten mevcut'
|
||||
ErrSSLKeyNotFound: 'Özel anahtar dosyası mevcut değil'
|
||||
ErrSSLCertificateNotFound: 'Sertifika dosyası mevcut değil'
|
||||
@@ -185,12 +170,12 @@ ErrSSLKeyFormat: 'Özel anahtar dosyası doğrulama başarısız'
|
||||
ErrSSLCertificateFormat: 'Sertifika dosyası formatı yanlış, lütfen pem formatını kullanın'
|
||||
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid veya EabHmacKey boş olamaz'
|
||||
ErrOpenrestyNotFound: 'Http modu önce Openresty yüklenmesini gerektiriyor'
|
||||
ApplySSLStart: 'Sertifika başvurusu başlatılıyor, alan adı [{{ .domain }}] başvuru yöntemi [{{ .type }}] '
|
||||
ApplySSLStart: 'Sertifika başvurusu başlatılıyor, alan adı [{{ .domain }}] başvuru yöntemi [{{ .type }}]'
|
||||
dnsAccount: 'DNS Otomatik'
|
||||
dnsManual: 'DNS Manuel'
|
||||
http: 'HTTP'
|
||||
ApplySSLFailed: '[{{ .domain }}] sertifika başvurusu başarısız, {{ .detail }} '
|
||||
ApplySSLSuccess: '[{{ .domain }}] sertifika başvurusu başarılı! !'
|
||||
ApplySSLFailed: '[{{ .domain }}] sertifika başvurusu başarısız, {{ .detail }}'
|
||||
ApplySSLSuccess: '[{{ .domain }}] sertifika başvurusu başarılı'
|
||||
DNSAccountName: 'DNS hesabı [{{ .name }}] sağlayıcı [{{ .type }}]'
|
||||
PushDirLog: 'Sertifika [{{ .path }}] dizinine gönderildi {{ .status }}'
|
||||
ErrDeleteCAWithSSL: 'Mevcut kuruluşun verilmiş bir sertifikası var ve silinemez.'
|
||||
@@ -205,94 +190,109 @@ ExecShellSuccess: 'Betik yürütme başarılı'
|
||||
StartUpdateSystemSSL: 'Sistem sertifikası güncellemeyi başlat'
|
||||
UpdateSystemSSLSuccess: 'Sistem sertifikası güncelleme başarılı'
|
||||
ErrWildcardDomain: 'HTTP modunda joker alan adı sertifikası başvurusu yapılamıyor'
|
||||
ErrApplySSLCanNotDelete: "İşlemde olan sertifika {{.name}} silinemez, lütfen daha sonra tekrar deneyin."
|
||||
|
||||
#mysql
|
||||
ErrApplySSLCanNotDelete: 'İşlemde olan sertifika {{ .name }} silinemez, lütfen daha sonra tekrar deneyin.'
|
||||
StartPushSSLToNode: 'Sertifika düğüme gönderilmeye başlandı'
|
||||
PushSSLToNodeFailed: 'Sertifika düğüme gönderilemedi: {{ .err }}'
|
||||
PushSSLToNodeSuccess: 'Sertifika düğüme başarıyla gönderildi'
|
||||
ErrUserIsExist: 'Mevcut kullanıcı zaten mevcut, lütfen yeniden girin'
|
||||
ErrDatabaseIsExist: 'Mevcut veritabanı zaten mevcut, lütfen yeniden girin'
|
||||
ErrExecTimeOut: 'SQL yürütme zaman aşımı, lütfen veritabanını kontrol edin'
|
||||
ErrRemoteExist: 'Uzak veritabanında bu adla zaten mevcut, lütfen değiştirin ve tekrar deneyin'
|
||||
ErrLocalExist: 'Yerel veritabanında bu ad zaten mevcut, lütfen değiştirin ve tekrar deneyin'
|
||||
RemoteBackup: "Uzak veritabanını yedeklemek için önce {{ .name }} görüntüsü kullanılarak yerel konteyner veritabanı hizmetinin başlatılması gerekiyor, lütfen bekleyin..."
|
||||
RemoteRecover: "Uzak veritabanını geri yüklemek için önce {{ .name }} görüntüsü kullanılarak yerel konteyner veritabanı hizmetinin başlatılması gerekiyor, lütfen bekleyin..."
|
||||
Arg: "Argüman"
|
||||
|
||||
#redis
|
||||
RemoteBackup: 'Uzak veritabanını yedeklemek için önce {{ .name }} görüntüsü kullanılarak yerel konteyner veritabanı hizmetinin başlatılması gerekiyor, lütfen bekleyin...'
|
||||
RemoteRecover: 'Uzak veritabanını geri yüklemek için önce {{ .name }} görüntüsü kullanılarak yerel konteyner veritabanı hizmetinin başlatılması gerekiyor, lütfen bekleyin...'
|
||||
Arg: 'Argüman'
|
||||
ErrTypeOfRedis: 'Kurtarma dosyası türü mevcut kalıcılık yöntemiyle eşleşmiyor, lütfen değiştirin ve tekrar deneyin'
|
||||
|
||||
#container
|
||||
ErrInUsed: "{{ .detail }} kullanımda ve silinemez!"
|
||||
ErrObjectInUsed: "Bu nesne kullanımda ve silinemez!"
|
||||
ErrObjectBeDependent: "Bu görüntü diğer görüntüler tarafından bağımlıdır ve silinemez!"
|
||||
ErrPortRules: 'Port numarası eşleşmiyor, lütfen yeniden girin!'
|
||||
ErrInUsed: '{{ .detail }} kullanımda ve silinemez'
|
||||
ErrObjectInUsed: 'Bu nesne kullanımda ve silinemez'
|
||||
ErrObjectBeDependent: 'Bu görüntü diğer görüntüler tarafından bağımlıdır ve silinemez'
|
||||
ErrPortRules: 'Port numarası eşleşmiyor, lütfen yeniden girin'
|
||||
ErrPgImagePull: 'Image çekme zaman aşımı, lütfen image hızlandırma yapılandırın veya manuel olarak {{ .name }} imageını çekin ve tekrar deneyin'
|
||||
PruneHelper: "Bu temizlik {{ .name }} disk alanı {{ .size }} boşalttı"
|
||||
ImageRemoveHelper: "{{ .name }} imajı silindi, {{ .size }} disk alanı boşalttı"
|
||||
BuildCache: "Derleme önbelleği"
|
||||
Volume: "Depolama hacmi"
|
||||
Network: "Ağ"
|
||||
PruneStart: "Temizlik devam ediyor, lütfen bekleyin..."
|
||||
|
||||
#runtime
|
||||
ErrFileNotExist: '{{ .detail }} dosyası mevcut değil! Lütfen kaynak dosyanın bütünlüğünü kontrol edin!'
|
||||
PruneHelper: 'Bu temizlik {{ .name }} disk alanı {{ .size }} boşalttı'
|
||||
ImageRemoveHelper: '{{ .name }} imajı silindi, {{ .size }} disk alanı boşalttı'
|
||||
BuildCache: 'Derleme önbelleği'
|
||||
Volume: 'Depolama hacmi'
|
||||
Network: 'Ağ'
|
||||
PruneStart: 'Temizlik devam ediyor, lütfen bekleyin...'
|
||||
ContainerBackupPrepare: 'Konteyner yedekleme çalışma alanını hazırla'
|
||||
ContainerBackupStop: 'Yedeklemeden önce konteyneri durdur'
|
||||
ContainerBackupInspect: 'Konteyner inspect ve ağ bilgisini yedekle'
|
||||
ContainerBackupMounts: 'Konteyner mount verilerini yedekle'
|
||||
ContainerBackupMeta: 'Konteyner yedekleme meta verisini yaz'
|
||||
ContainerBackupStart: 'Yedeklemeden sonra konteyneri başlat'
|
||||
ContainerRecoverPrepare: 'Konteyner geri yükleme çalışma alanını hazırla'
|
||||
ContainerRecoverExtract: 'Konteyner yedek arşivini çıkar'
|
||||
ContainerRecoverParse: 'Konteyner yedek meta verisini yükle'
|
||||
ContainerRecoverCreate: 'Konteyneri yeniden oluştur'
|
||||
ContainerRecoverMounts: 'Konteyner mount verilerini geri yükle'
|
||||
ContainerRecoverStart: 'Geri yüklenen konteyneri başlat'
|
||||
ContainerRecoverCleanup: 'Konteyner geri yükleme çalışma alanını temizle'
|
||||
ComposeBackupPrepare: 'Compose yedekleme çalışma alanını hazırla'
|
||||
ComposeBackupStop: 'Yedeklemeden önce compose’u durdur'
|
||||
ComposeBackupFiles: 'Compose dosyalarını yedekle'
|
||||
ComposeBackupContainers: 'Compose konteynerlerini yedekle'
|
||||
ComposeBackupMeta: 'Compose yedek meta verisini yaz'
|
||||
ComposeBackupStart: 'Yedeklemeden sonra compose’u başlat'
|
||||
ComposeBackupCleanup: 'Compose yedekleme çalışma alanını temizle'
|
||||
ComposeRecoverPrepare: 'Compose geri yükleme çalışma alanını hazırla'
|
||||
ComposeRecoverExtract: 'Compose yedek arşivini çıkar'
|
||||
ComposeRecoverMeta: 'Compose yedek meta verisini yükle'
|
||||
ComposeRecoverMetaLogName: 'Compose geri yükleme meta: ad={{ .name }}'
|
||||
ComposeRecoverMetaLogPath: 'Compose geri yükleme meta: yedekYolu={{ .backupPath }} hedefYol={{ .targetDir }}'
|
||||
ComposeRecoverMetaLogCount: 'Compose geri yükleme meta: dosya={{ .files }} konteyner={{ .containers }}'
|
||||
ComposeRecoverFiles: 'Compose dosyalarını geri yükle'
|
||||
ComposeRecoverContainers: 'Compose konteynerlerini geri yükle'
|
||||
ComposeRecoverRecord: 'Compose kaydını sakla'
|
||||
ComposeRecoverCleanup: 'Compose geri yükleme çalışma alanını temizle'
|
||||
ErrFileNotExist: '{{ .detail }} dosyası mevcut değil Lütfen kaynak dosyanın bütünlüğünü kontrol edin'
|
||||
ErrImageBuildErr: 'Image yapı başarısız'
|
||||
ErrImageExist: "Görüntü zaten var! Lütfen görüntü adını değiştirin."
|
||||
ErrImageExist: 'Görüntü zaten var Lütfen görüntü adını değiştirin.'
|
||||
ErrDelWithWebsite: 'İşletim ortamı zaten bir web sitesiyle ilişkili ve silinemez'
|
||||
ErrRuntimeStart: 'Başlatma başarısız'
|
||||
ErrPackageJsonNotFound: 'package.json dosyası mevcut değil'
|
||||
ErrScriptsNotFound: 'package.json içinde scripts yapılandırma öğesi bulunamadı'
|
||||
ErrContainerNameNotFound: 'Konteyner adı alınamıyor, lütfen .env dosyasını kontrol edin'
|
||||
ErrNodeModulesNotFound: 'node_modules klasörü mevcut değil! Lütfen çalışma ortamını düzenleyin veya çalışma ortamının başarıyla başlatılmasını bekleyin'
|
||||
ErrContainerNameNotFound: 'Konteyner adı alınamıyor, lütfen.env dosyasını kontrol edin'
|
||||
ErrNodeModulesNotFound: 'node_modules klasörü mevcut değil Lütfen çalışma ortamını düzenleyin veya çalışma ortamının başarıyla başlatılmasını bekleyin'
|
||||
ErrContainerNameIsNull: 'Konteyner adı mevcut değil'
|
||||
ErrPHPPortIsDefault: "9000 portu varsayılan port, lütfen değiştirin ve tekrar deneyin"
|
||||
ErrPHPRuntimePortFailed: "{{ .name }} portu mevcut çalışma ortamı tarafından zaten kullanılıyor, lütfen değiştirin ve tekrar deneyin"
|
||||
|
||||
#tool
|
||||
ErrPHPPortIsDefault: '9000 portu varsayılan port, lütfen değiştirin ve tekrar deneyin'
|
||||
ErrPHPRuntimePortFailed: '{{ .name }} portu mevcut çalışma ortamı tarafından zaten kullanılıyor, lütfen değiştirin ve tekrar deneyin'
|
||||
ErrConfigNotFound: 'Yapılandırma dosyası mevcut değil'
|
||||
ErrConfigParse: 'Yapılandırma dosyası formatı yanlış'
|
||||
ErrConfigIsNull: 'Yapılandırma dosyası boş olamaz'
|
||||
ErrConfigDirNotFound: 'Çalışma dizini mevcut değil'
|
||||
ErrConfigAlreadyExist: 'Aynı ada sahip bir yapılandırma dosyası zaten mevcut'
|
||||
ErrUserFindErr: 'Kullanıcı {{ .name }} arama başarısız {{ .err }}'
|
||||
|
||||
#cronjob
|
||||
CutWebsiteLogSuccess: '{{ .name }} web sitesi günlüğü başarıyla kesildi, yedekleme yolu {{ .path }}'
|
||||
HandleShell: 'Betik {{ .name }} yürüt'
|
||||
HandleCurl: "URL'ye Eriş {{ .name }}"
|
||||
HandleCurl: 'URL''ye Eriş {{ .name }}'
|
||||
HandleNtpSync: 'Sistem zaman senkronizasyonu'
|
||||
HandleSystemClean: 'Sistem önbellek temizliği'
|
||||
SystemLog: 'Sistem Günlüğü'
|
||||
CutWebsiteLog: 'Web Sitesi Günlüğünü Döndür'
|
||||
FileOrDir: 'Dizin / Dosya'
|
||||
UploadFile: 'Yedekleme dosyası {{ .file }} {{ .backup }} konumuna yükleniyor'
|
||||
Upload: "Yükle"
|
||||
IgnoreBackupErr: "Yedekleme başarısız, hata: {{ .detail }}, bu hata yoksayılıyor..."
|
||||
IgnoreUploadErr: "Yükleme başarısız, hata: {{ .detail }}, bu hata yoksayılıyor..."
|
||||
LoadBackupFailed: "Yedek hesap bağlantısı alınamadı, hata: {{ .detail }}"
|
||||
InExecuting: "Mevcut görev yürütülüyor, lütfen tekrar yürütmeyin!"
|
||||
NoSuchResource: "Veritabanında yedek içeriği bulunamadı, atlanıyor..."
|
||||
CleanLog: "Günlüğü temizle"
|
||||
CleanLogByName: "{{.name}} günlüğünü temizle"
|
||||
WafIpGroupNotFound: "WAF IP grubu bulunamadı. Lütfen [Gelişmiş Özellikler - WAF - Kara/Beyaz Liste - IP Grubu] bölümüne giderek uzaktan indirme yöntemiyle bir IP grubu ekleyin."
|
||||
|
||||
#toolbox
|
||||
ErrNotExistUser: 'Mevcut kullanıcı mevcut değil, lütfen değiştirin ve tekrar deneyin!'
|
||||
ErrBanAction: 'Ayarlama başarısız. Mevcut {{ .name }} servisi kullanılamıyor. Lütfen kontrol edin ve tekrar deneyin!'
|
||||
ErrClamdscanNotFound: 'clamdscan komutu tespit edilmedi, lütfen yüklemek için belgeleri inceleyin!'
|
||||
TaskSwapSet: "Swap Ayarla"
|
||||
SetSwap: "Swap Ayarla {{ .path }} - {{ .size }}"
|
||||
CreateSwap: "Swap Dosyası Oluştur"
|
||||
FormatSwap: "Swap Dosyasını Biçimlendir"
|
||||
EnableSwap: "Swap Etkinleştir"
|
||||
|
||||
#tamper
|
||||
CleanTamperSetting: "Geçmiş koruma ayarlarını temizle"
|
||||
SetTamperWithRules: "Kurallara göre dizin korumasını başlat"
|
||||
ProtectDir: "Dizini koru {{ .name }}"
|
||||
IgnoreFile: "Dosyayı koru {{ .name }}"
|
||||
TamperSetSuccessful: "Koruma ayarları tamamlandı, aşağıdaki dizinler izlenmeye başlanıyor:"
|
||||
|
||||
#waf
|
||||
Upload: 'Yükle'
|
||||
IgnoreBackupErr: 'Yedekleme başarısız, hata: {{ .detail }}, bu hata yoksayılıyor...'
|
||||
IgnoreUploadErr: 'Yükleme başarısız, hata: {{ .detail }}, bu hata yoksayılıyor...'
|
||||
LoadBackupFailed: 'Yedek hesap bağlantısı alınamadı, hata: {{ .detail }}'
|
||||
InExecuting: 'Mevcut görev yürütülüyor, lütfen tekrar yürütmeyin'
|
||||
NoSuchResource: 'Veritabanında yedek içeriği bulunamadı, atlanıyor...'
|
||||
CleanLog: 'Günlüğü temizle'
|
||||
CleanLogByName: '{{ .name }} günlüğünü temizle'
|
||||
WafIpGroupNotFound: 'WAF IP grubu bulunamadı. Lütfen [Gelişmiş Özellikler - WAF - Kara/Beyaz Liste - IP Grubu] bölümüne giderek uzaktan indirme yöntemiyle bir IP grubu ekleyin.'
|
||||
ErrNotExistUser: 'Mevcut kullanıcı mevcut değil, lütfen değiştirin ve tekrar deneyin'
|
||||
ErrBanAction: 'Ayarlama başarısız. Mevcut {{ .name }} servisi kullanılamıyor. Lütfen kontrol edin ve tekrar deneyin'
|
||||
ErrClamdscanNotFound: 'clamdscan komutu tespit edilmedi, lütfen yüklemek için belgeleri inceleyin'
|
||||
TaskSwapSet: 'Swap Ayarla'
|
||||
SetSwap: 'Swap Ayarla {{ .path }} - {{ .size }}'
|
||||
CreateSwap: 'Swap Dosyası Oluştur'
|
||||
FormatSwap: 'Swap Dosyasını Biçimlendir'
|
||||
EnableSwap: 'Swap Etkinleştir'
|
||||
CleanTamperSetting: 'Geçmiş koruma ayarlarını temizle'
|
||||
SetTamperWithRules: 'Kurallara göre dizin korumasını başlat'
|
||||
ProtectDir: 'Dizini koru {{ .name }}'
|
||||
IgnoreFile: 'Dosyayı koru {{ .name }}'
|
||||
TamperSetSuccessful: 'Koruma ayarları tamamlandı, aşağıdaki dizinler izlenmeye başlanıyor:'
|
||||
ErrScope: 'Bu yapılandırmayı değiştirme desteklenmiyor'
|
||||
ErrStateChange: 'Durum değiştirme başarısız'
|
||||
ErrRuleExist: 'Kural zaten mevcut'
|
||||
@@ -300,7 +300,7 @@ ErrRuleNotExist: 'Kural mevcut değil'
|
||||
ErrParseIP: 'Yanlış IP formatı'
|
||||
ErrDefaultIP: 'default ayrılmış bir isim, lütfen başka bir isme değiştirin'
|
||||
ErrGroupInUse: 'IP grubu kara liste/beyaz liste tarafından kullanılıyor ve silinemez'
|
||||
ErrIPGroupAclUse: "IP grubu {{ .name }} web sitesinin özel kuralları tarafından kullanılıyor, silinemez"
|
||||
ErrIPGroupAclUse: 'IP grubu {{ .name }} web sitesinin özel kuralları tarafından kullanılıyor, silinemez'
|
||||
ErrGroupExist: 'IP grup adı zaten mevcut'
|
||||
ErrIPRange: 'Yanlış IP aralığı'
|
||||
ErrIPExist: 'IP zaten mevcut'
|
||||
@@ -311,10 +311,10 @@ xss: 'XSS'
|
||||
phpExec: 'PHP betik yürütme'
|
||||
oneWordTrojan: 'Tek Kelime Truva Atı'
|
||||
appFilter: 'Tehlikeli dizin filtreleme uygula'
|
||||
webshell: 'Webshell'
|
||||
webShell: 'Webshell'
|
||||
args: 'Parametre kuralları'
|
||||
protocolFilter: 'Protokol filtreleme'
|
||||
javaFileter: 'Java tehlikeli dosya filtresi'
|
||||
javaFilter: 'Java tehlikeli dosya filtresi'
|
||||
scannerFilter: 'Tarayıcı filtresi'
|
||||
escapeFilter: 'kaçış filtresi'
|
||||
customRule: 'Özel kural'
|
||||
@@ -344,10 +344,9 @@ ErrDBNotExist: 'Veritabanı mevcut değil'
|
||||
allow: 'izin ver'
|
||||
deny: 'reddet'
|
||||
OpenrestyNotFound: 'Openresty yüklü değil'
|
||||
remoteIpIsNull: "IP listesi boş"
|
||||
OpenrestyVersionErr: "Openresty sürümü çok düşük, lütfen Openresty'i 1.27.1.2-2-2-focal olarak güncelleyin"
|
||||
|
||||
#task
|
||||
remoteIpIsNull: 'IP listesi boş'
|
||||
OpenrestyVersionErr: 'Openresty sürümü çok düşük, lütfen Openresty''i 1.27.1.2-2-2-focal olarak güncelleyin'
|
||||
ErrFileTooLarge: 'File is larger than 1 MB and cannot be uploaded'
|
||||
TaskStart: '{{ .name }} görevi başlıyor [BAŞLAT]'
|
||||
TaskEnd: '{{ .name }} Görev tamamlandı [TAMAMLANDI]'
|
||||
TaskFailed: '{{ .name }} görev başarısız'
|
||||
@@ -363,7 +362,7 @@ TaskDelete: 'Sil'
|
||||
TaskUpgrade: 'Yükselt'
|
||||
TaskUpdate: 'Güncelle'
|
||||
TaskRestart: 'Yeniden Başlat'
|
||||
TaskProtect: "Koru"
|
||||
TaskProtect: 'Koru'
|
||||
TaskBackup: 'Yedekle'
|
||||
TaskRecover: 'Kurtar'
|
||||
TaskRollback: 'Geri Al'
|
||||
@@ -371,10 +370,10 @@ TaskPull: 'Çek'
|
||||
TaskCommit: 'işleme'
|
||||
TaskBuild: 'Yapı'
|
||||
TaskPush: 'Gönder'
|
||||
TaskClean: "Temizleme"
|
||||
TaskClean: 'Temizleme'
|
||||
TaskHandle: 'Yürüt'
|
||||
TaskImport: "İçe Aktar"
|
||||
TaskExport: "Dışa Aktar"
|
||||
TaskImport: 'İçe Aktar'
|
||||
TaskExport: 'Dışa Aktar'
|
||||
Website: 'Web Sitesi'
|
||||
App: 'Uygulama'
|
||||
Runtime: 'Çalışma ortamı'
|
||||
@@ -406,18 +405,12 @@ RuntimeExtension: 'Çalışma Ortamı Uzantısı'
|
||||
TaskIsExecuting: 'Görev çalışıyor'
|
||||
CustomAppstore: 'Özel uygulama deposu'
|
||||
TaskExec: 'Çalıştır'
|
||||
TaskBatch: "Toplu İşlem"
|
||||
TaskBatch: 'Toplu İşlem'
|
||||
FileConvert: 'Dosya Formatı Dönüştürme'
|
||||
|
||||
# task - clam
|
||||
Clamscan: "{{ .name }} Tara"
|
||||
TaskScan: "Tara"
|
||||
|
||||
# task - ai
|
||||
Clamscan: '{{ .name }} Tara'
|
||||
TaskScan: 'Tara'
|
||||
OllamaModelPull: 'Ollama modeli {{ .name }} çek'
|
||||
OllamaModelSize: 'Ollama modeli {{ .name }} boyutunu al'
|
||||
|
||||
# task-snapshot
|
||||
Snapshot: 'Anlık Görüntü'
|
||||
SnapDBInfo: '1Panel veritabanı bilgilerini yaz'
|
||||
SnapCopy: 'Dosya ve dizinleri {{ .name }} kopyala'
|
||||
@@ -443,7 +436,6 @@ SnapCompressSize: 'Anlık görüntü dosya boyutu {{ .name }}'
|
||||
SnapUpload: 'Anlık görüntü dosyasını yükle'
|
||||
SnapUploadTo: 'Anlık görüntü dosyasını {{ .name }} konumuna yükle'
|
||||
SnapUploadRes: 'Anlık görüntü dosyasını {{ .name }} konumuna yükle'
|
||||
|
||||
SnapshotRecover: 'Anlık Görüntü Geri Yükleme'
|
||||
RecoverDownload: 'Anlık görüntü dosyasını indir'
|
||||
Download: 'İndir'
|
||||
@@ -451,10 +443,10 @@ RecoverDownloadAccount: 'Anlık görüntü indirme yedek hesabı al {{ .name }}'
|
||||
RecoverDecompress: 'Anlık görüntü sıkıştırılmış dosyalarını açma'
|
||||
Decompress: 'Açma'
|
||||
BackupBeforeRecover: 'Anlık görüntü öncesi sistem ilgili verileri yedekleme'
|
||||
Readjson: 'Anlık görüntüdeki Json dosyasını oku'
|
||||
ReadjsonPath: 'Anlık görüntüdeki Json dosya yolunu al'
|
||||
ReadjsonContent: 'Json dosyasını oku'
|
||||
ReadjsonMarshal: 'Json kaçış işlemesi'
|
||||
ReadJson: 'Anlık görüntüdeki Json dosyasını oku'
|
||||
ReadJsonPath: 'Anlık görüntüdeki Json dosya yolunu al'
|
||||
ReadJsonContent: 'Json dosyasını oku'
|
||||
ReadJsonMarshal: 'Json kaçış işlemesi'
|
||||
RecoverApp: 'Yüklü uygulamaları geri yükle'
|
||||
RecoverWebsite: 'Web sitesi dizinini kurtar'
|
||||
RecoverAppImage: 'Anlık görüntü imaj yedeklemesini geri yükle'
|
||||
@@ -468,9 +460,6 @@ RecoverDaemonJson: 'Konteyner yapılandırma daemon.json dosyasını geri yükle
|
||||
RecoverDBData: 'Veritabanı verilerini kurtar'
|
||||
RecoverBackups: 'Yerel yedekleme dizinini geri yükle'
|
||||
RecoverPanelData: 'Veri dizinini kurtar'
|
||||
|
||||
# task - container
|
||||
ContainerNewCliet: 'Docker İstemcisini Başlat'
|
||||
ContainerImagePull: 'Konteyner imajını çek {{ .name }}'
|
||||
ContainerRemoveOld: 'Orijinal konteyneri kaldır {{ .name }}'
|
||||
ContainerImageCheck: 'İmajın normal çekilip çekilmediğini kontrol et'
|
||||
@@ -479,22 +468,13 @@ ContainerRecreate: 'Konteyner güncellemesi başarısız, şimdi orijinal kontey
|
||||
ContainerCreate: 'Yeni konteyner oluştur {{ .name }}'
|
||||
ContainerCreateFailed: 'Konteyner oluşturma başarısız, başarısız konteyneri sil'
|
||||
ContainerStartCheck: 'Konteynerin başlatılıp başlatılmadığını kontrol et'
|
||||
|
||||
# task - image
|
||||
ImageBuild: 'İmaj Oluşturma'
|
||||
ImageBuildStdoutCheck: 'İmaj çıktı içeriğini ayrıştır'
|
||||
ImageBuildRes: 'İmaj oluşturma çıktısı: {{ .name }}'
|
||||
ImagePull: 'İmaj çek'
|
||||
ImageRepoAuthFromDB: 'Veritabanından depo kimlik doğrulama bilgilerini al'
|
||||
ImaegPullRes: 'İmaj çekme çıktısı: {{ .name }}'
|
||||
ImagePush: 'İmaj gönder'
|
||||
ImageRenameTag: 'İmaj etiketini değiştir'
|
||||
ImageNewTag: 'Yeni imaj etiketi {{ .name }}'
|
||||
ImaegPushRes: 'İmaj gönderme çıktısı: {{ .name }}'
|
||||
ComposeCreate: 'Kompozisyon oluştur'
|
||||
ComposeCreateRes: 'Compose oluşturma çıktısı: {{ .name }}'
|
||||
|
||||
# task - website
|
||||
BackupNginxConfig: 'Web sitesi OpenResty yapılandırma dosyasını yedekle'
|
||||
CompressFileSuccess: 'Dizin başarıyla sıkıştırıldı, şuraya sıkıştırıldı {{ .name }}'
|
||||
CompressDir: 'Sıkıştırma dizini'
|
||||
@@ -505,40 +485,33 @@ websiteDir: 'Web Sitesi Dizini'
|
||||
RecoverFailedStartRollBack: 'Kurtarma başarısız, geri almayı başlat'
|
||||
AppBackupFileIncomplete: 'Yedekleme dosyası eksik ve app.json veya app.tar.gz dosyalarından yoksun'
|
||||
AppAttributesNotMatch: 'Uygulama türü veya adı eşleşmiyor'
|
||||
ErrAlertSync: 'Alarm bilgisi senkronizasyon hatası, lütfen kontrol edin ve tekrar deneyin'
|
||||
ErrInstallExtension: 'Zaten devam eden bir kurulum görevi var, görevin bitmesini bekleyin'
|
||||
PanelAlertTitle: 'Panel Uyarı Bildirimi'
|
||||
TestAlertTitle: 'Test E-postası - E-posta Bağlantısını Doğrula'
|
||||
TestAlert: 'Bu, e-posta gönderim ayarlarınızın doğru yapılandırıldığını doğrulamak için gönderilen bir test e-postasıdır.'
|
||||
LicenseExpirationAlert: 'Paneliniz {{ .node }}{{ .ip }}, lisans {{ .day }} gün içinde süresi dolacak. Detaylar için paneline giriş yapın.'
|
||||
CronJobFailedAlert: 'Paneliniz {{ .node }}{{ .ip }}, zamanlanmış görev {{ .name }} çalıştırma başarısız oldu. Detaylar için paneline giriş yapın.'
|
||||
ClamAlert: 'Paneliniz {{ .node }}{{ .ip }}, virüs tarama görevi {{ .num }} enfekte dosya tespit etti. Detaylar için paneline giriş yapın.'
|
||||
WebSiteAlert: 'Paneliniz {{ .node }}{{ .ip }}, {{ .num }} web sitesi {{ .day }} gün içinde süresi dolacak. Detaylar için paneline giriş yapın.'
|
||||
SSLAlert: 'Paneliniz {{ .node }}{{ .ip }}, {{ .num }} SSL sertifikası {{ .day }} gün içinde süresi dolacak. Detaylar için paneline giriş yapın.'
|
||||
DiskUsedAlert: 'Paneliniz {{ .node }}{{ .ip }}, {{ .name }} diski kullanımı {{ .used }}''a ulaştı. Detaylar için paneline giriş yapın.'
|
||||
ResourceAlert: 'Paneliniz {{ .node }}{{ .ip }}, son {{ .time }} dakika içindeki ortalama {{ .name }} kullanım oranı {{ .used }}''dır. Detaylar için paneline giriş yapın.'
|
||||
PanelVersionAlert: 'Paneliniz {{ .node }}{{ .ip }}, en son panel sürümü yükseltme için mevcut. Detaylar için paneline giriş yapın.'
|
||||
PanelPwdExpirationAlert: 'Paneliniz {{ .node }}{{ .ip }}, panel şifresi {{ .day }} gün içinde süresi dolacak. Detaylar için paneline giriş yapın.'
|
||||
CommonAlert: 'Paneliniz {{ .node }}{{ .ip }}, {{ .msg }}. Detaylar için paneline giriş yapın.'
|
||||
NodeExceptionAlert: 'Paneliniz {{ .node }}{{ .ip }}, {{ .num }} düğüm anormal durumda. Detaylar için paneline giriş yapın.'
|
||||
LicenseExceptionAlert: 'Paneliniz {{ .node }}{{ .ip }}, {{ .num }} lisans anormal durumda. Detaylar için paneline giriş yapın.'
|
||||
SSHAndPanelLoginAlert: 'Paneliniz {{ .node }}{{ .ip }}, {{ .loginIp }} adresinden {{ .name }} girişi anormal. Detaylar için paneline giriş yapın.'
|
||||
DeviceNotFound: 'Cihaz {{ .name }} bulunamadı'
|
||||
DeviceIsMounted: 'Cihaz {{ .name }} bağlandı, önce çıkarın'
|
||||
PartitionDiskErr: 'Bölümleme başarısız, {{ .err }}'
|
||||
FormatDiskErr: 'Disk biçimlendirme başarısız, {{ .err }}'
|
||||
MountDiskErr: 'Disk bağlama başarısız, {{ .err }}'
|
||||
XfsNotFound: 'XFS dosya sistemi algılanmadı, lütfen önce xfsprogs''i yükleyin'
|
||||
|
||||
#alert
|
||||
ErrAlert: 'Uyarı mesajının formatı yanlış, lütfen kontrol edin ve tekrar deneyin!'
|
||||
ErrAlertPush: 'Uyarı bilgisi gönderiminde hata, lütfen kontrol edin ve tekrar deneyin!'
|
||||
ErrAlertSave: 'Alarm bilgisini kaydetmede hata, lütfen kontrol edin ve tekrar deneyin!'
|
||||
ErrAlertSync: 'Alarm bilgisi senkronizasyon hatası, lütfen kontrol edin ve tekrar deneyin!'
|
||||
ErrAlertRemote: 'Alarm mesajı uzak hata, lütfen kontrol edin ve tekrar deneyin!'
|
||||
|
||||
#task - runtime
|
||||
ErrInstallExtension: "Zaten devam eden bir kurulum görevi var, görevin bitmesini bekleyin"
|
||||
|
||||
# alert mail template
|
||||
PanelAlertTitle: "Panel Uyarı Bildirimi"
|
||||
TestAlertTitle: "Test E-postası - E-posta Bağlantısını Doğrula"
|
||||
TestAlert: "Bu, e-posta gönderim ayarlarınızın doğru yapılandırıldığını doğrulamak için gönderilen bir test e-postasıdır."
|
||||
LicenseExpirationAlert: "Paneliniz {{ .node }}{{ .ip }}, lisans {{ .day }} gün içinde süresi dolacak. Detaylar için paneline giriş yapın."
|
||||
CronJobFailedAlert: "Paneliniz {{ .node }}{{ .ip }}, zamanlanmış görev {{ .name }} çalıştırma başarısız oldu. Detaylar için paneline giriş yapın."
|
||||
ClamAlert: "Paneliniz {{ .node }}{{ .ip }}, virüs tarama görevi {{ .num }} enfekte dosya tespit etti. Detaylar için paneline giriş yapın."
|
||||
WebSiteAlert: "Paneliniz {{ .node }}{{ .ip }}, {{ .num }} web sitesi {{ .day }} gün içinde süresi dolacak. Detaylar için paneline giriş yapın."
|
||||
SSLAlert: "Paneliniz {{ .node }}{{ .ip }}, {{ .num }} SSL sertifikası {{ .day }} gün içinde süresi dolacak. Detaylar için paneline giriş yapın."
|
||||
DiskUsedAlert: "Paneliniz {{ .node }}{{ .ip }}, {{ .name }} diski kullanımı {{ .used }}'a ulaştı. Detaylar için paneline giriş yapın."
|
||||
ResourceAlert: "Paneliniz {{ .node }}{{ .ip }}, son {{ .time }} dakika içindeki ortalama {{ .name }} kullanım oranı {{ .used }}'dır. Detaylar için paneline giriş yapın."
|
||||
PanelVersionAlert: "Paneliniz {{ .node }}{{ .ip }}, en son panel sürümü yükseltme için mevcut. Detaylar için paneline giriş yapın."
|
||||
PanelPwdExpirationAlert: "Paneliniz {{ .node }}{{ .ip }}, panel şifresi {{ .day }} gün içinde süresi dolacak. Detaylar için paneline giriş yapın."
|
||||
CommonAlert: "Paneliniz {{ .node }}{{ .ip }}, {{ .msg }}. Detaylar için paneline giriş yapın."
|
||||
NodeExceptionAlert: "Paneliniz {{ .node }}{{ .ip }}, {{ .num }} düğüm anormal durumda. Detaylar için paneline giriş yapın."
|
||||
LicenseExceptionAlert: "Paneliniz {{ .node }}{{ .ip }}, {{ .num }} lisans anormal durumda. Detaylar için paneline giriş yapın."
|
||||
SSHAndPanelLoginAlert: "Paneliniz {{ .node }}{{ .ip }}, {{ .loginIp }} adresinden {{ .name }} girişi anormal. Detaylar için paneline giriş yapın."
|
||||
|
||||
#disk
|
||||
DeviceNotFound: "Cihaz {{ .name }} bulunamadı"
|
||||
DeviceIsMounted: "Cihaz {{ .name }} bağlandı, önce çıkarın"
|
||||
PartitionDiskErr: "Bölümleme başarısız, {{ .err }}"
|
||||
FormatDiskErr: "Disk biçimlendirme başarısız, {{ .err }}"
|
||||
MountDiskErr: "Disk bağlama başarısız, {{ .err }}"
|
||||
UnMountDiskErr: "Disk bağının kaldırılması başarısız, {{ .err }}"
|
||||
XfsNotFound: "XFS dosya sistemi algılanmadı, lütfen önce xfsprogs'i yükleyin"
|
||||
# terminal
|
||||
TerminalAIBlockedRiskyCommand: 'Riskli komut engellendi: {{ .command }}'
|
||||
TerminalAIThinking: 'AI dusunuyor...'
|
||||
TerminalAIReadyToExecute: 'Dusunme tamamlandi, calistirmak icin Enter tusuna basin (sure: {{ .duration }}, token: {{ .tokens }})'
|
||||
TerminalAIRequestFailed: 'AI istegi basarisiz oldu: {{ .err }}'
|
||||
|
||||
+211
-236
@@ -2,6 +2,8 @@ ErrInvalidParams: '請求參數錯誤: {{ .detail }}'
|
||||
ErrTokenParse: 'Token 產生錯誤: {{ .detail }}'
|
||||
ErrInitialPassword: '原密碼錯誤'
|
||||
ErrInternalServer: '服務內部錯誤: {{ .detail }}'
|
||||
ErrWgetRemoteFailed: '遠端下載網址無效或無法存取:{{ .detail }}'
|
||||
ErrWgetInvalidContentType: '遠端網址未回傳檔案內容(可能是錯誤頁),{{ .detail }}'
|
||||
ErrRecordExist: '記錄已存在'
|
||||
ErrRecordNotFound: '記錄未能找到'
|
||||
ErrStructTransform: '型別轉換失敗: {{ .err }}'
|
||||
@@ -14,12 +16,9 @@ ErrApiConfigKeyInvalid: 'API 介面金鑰錯誤: {{ .detail }}'
|
||||
ErrApiConfigIPInvalid: '呼叫 API 介面 IP 不在白名單: {{ .detail }}'
|
||||
ErrApiConfigDisable: '此介面禁止使用 API 介面呼叫: {{ .detail }}'
|
||||
ErrApiConfigKeyTimeInvalid: 'API 介面時間戳記錯誤: {{ .detail }}'
|
||||
ErrShutDown: "指令被手動結束!"
|
||||
|
||||
ErrMinQuickJump: "請至少設定一個快速跳轉入口!"
|
||||
ErrMaxQuickJump: "最多可設定四個快速跳轉入口!"
|
||||
|
||||
#common
|
||||
ErrShutDown: '指令被手動結束!'
|
||||
ErrMinQuickJump: '請至少設定一個快速跳轉入口!'
|
||||
ErrMaxQuickJump: '最多可設定四個快速跳轉入口!'
|
||||
ErrUsernameIsExist: '使用者名稱已存在'
|
||||
ErrNameIsExist: '名稱已存在'
|
||||
ErrDemoEnvironment: '示範伺服器,禁止此操作!'
|
||||
@@ -36,37 +35,35 @@ Failed: '失敗'
|
||||
SystemRestart: '系統重新啟動導致任務中斷'
|
||||
ErrGroupIsDefault: '預設分組,無法刪除'
|
||||
ErrGroupIsInWebsiteUse: '分組正在被其他網站使用,無法刪除'
|
||||
Decrypt: "解密"
|
||||
|
||||
#agent
|
||||
ErrAgentAccountBound: '該帳號已綁定到智能體,無法刪除,請檢查後重試!!'
|
||||
ErrAgentAccountUnavailable: '帳號連線資訊不可用,錯誤:{{ .err }},請檢查後重試!'
|
||||
ErrAgentProviderNotSupported: '暫不支援該智能體提供商,請檢查後重試!'
|
||||
Decrypt: '解密'
|
||||
ErrAgentAccountBound: '該帳號已綁定到智能體,無法刪除,請重試。'
|
||||
ErrAgentAccountUnavailable: '帳號連線資訊不可用,錯誤:{{ .err }},請重試'
|
||||
ErrAgentProviderNotSupported: '暫不支援該智能體提供商,請重試'
|
||||
ErrAgentAccountRequired: '請選擇智能體帳號後重試'
|
||||
ErrAgentAccountNotVerified: '帳號未驗證通過,請檢查後重試!'
|
||||
ErrAgentProviderMismatch: '帳號提供商不匹配,請檢查後重試!'
|
||||
ErrAgentBaseURLRequired: 'Base URL 不能為空,請檢查後重試!'
|
||||
ErrAgentApiKeyRequired: 'API Key 不能為空,請檢查後重試!'
|
||||
ErrAgentComposeRequired: '自訂編排內容不能為空,請檢查後重試!'
|
||||
ErrAgentIDRequired: '智能體 ID 不能為空,請檢查後重試!'
|
||||
ErrAgentAccountIDRequired: '帳號 ID 不能為空,請檢查後重試!'
|
||||
|
||||
#backup
|
||||
ErrAgentAccountNotVerified: '帳號未驗證通過,請重試'
|
||||
ErrAgentAccountModelsRequired: '請至少配置一個模型'
|
||||
ErrAgentAccountSingleInitialModel: '建立帳號時只能配置一個初始模型'
|
||||
ErrAgentModelNotInAccount: '所選模型未配置在當前帳號中,請重試'
|
||||
ErrAgentModelInUse: '仍有智能體正在使用該模型:{{ .name }}'
|
||||
ErrAgentBaseURLRequired: 'Base URL 不可為空,請重試'
|
||||
ErrAgentApiKeyRequired: 'API Key 不可為空,請重試'
|
||||
ErrAgentComposeRequired: '自訂編排內容不可為空,請重試'
|
||||
ErrAgentIDRequired: '智能體 ID 不可為空,請重試'
|
||||
ErrAgentAccountIDRequired: '帳號 ID 不可為空,請重試'
|
||||
ErrAgentLimitReached: '社群版最多支援建立 {{ .max }} 個智能體,升級至專業版則無數量限制'
|
||||
Localhost: '本機'
|
||||
ErrBackupInUsed: '此備份帳號已在排程任務中使用,無法刪除'
|
||||
ErrBackupCheck: '備份帳號測試連線失敗{{ .err }}'
|
||||
ErrBackupLocalDelete: '暫時不支援刪除本機伺服器備份帳號'
|
||||
ErrBackupLocalCreate: '暫時不支援建立本機伺服器備份帳號'
|
||||
|
||||
#app
|
||||
ErrPortInUsed: '{{ .detail }} 連接埠已被佔用!'
|
||||
ErrAppLimit: '應用程式超出安裝數量限制'
|
||||
ErrNotInstall: '應用程式未安裝'
|
||||
ErrPortInOtherApp: '{{ .port }} 連接埠已被應用程式{{ .apps }} 佔用!'
|
||||
ErrDbUserNotValid: '存量資料庫,使用者名稱密碼不符!'
|
||||
ErrUpdateBuWebsite: '應用程式更新成功,但網站設定檔修改失敗,請檢查設定! '
|
||||
ErrUpdateBuWebsite: '應用程式更新成功,但網站設定檔修改失敗,請檢查設定!'
|
||||
Err1PanelNetworkFailed: '預設容器網路建立失敗! {{ .detail }}'
|
||||
ErrFileParse: '應用docker-compose 檔案解析失敗!'
|
||||
ErrFileParse: '應用docker-compose 檔解析失敗!'
|
||||
ErrInstallDirNotFound: '安裝目錄不存在,如需移除,請選擇強制移除'
|
||||
AppStoreIsUpToDate: '應用程式商店已經是最新版本'
|
||||
LocalAppVersionNull: '{{ .name }} 應用程式未同步到版本!無法加入到應用程式列表'
|
||||
@@ -82,115 +79,108 @@ ErrHttpReqFailed: '請求失敗{{ .err }}'
|
||||
ErrNoSuchHost: '無法找到請求的伺服器{{ .err }}'
|
||||
ErrHttpReqNotFound: '無法找到要求的資源{{ .err }}'
|
||||
ErrContainerNotFound: '{{ .name }} 容器不存在'
|
||||
ErrContainerMsg: '{{ .name }} 容器異常,請在容器頁面上查看日誌'
|
||||
ErrContainerMsg: '{{ .name }} 容器異常,請在容器頁面上檢視日誌'
|
||||
ErrAppBackup: '{{ .name }} 應用備份失敗 {{ .err }}'
|
||||
ErrVersionTooLow: '目前1Panel 版本過低,無法更新應用程式商店,請升級版本之後操作'
|
||||
ErrVersionTooLow: '目前 1Panel 版本過低,無法更新應用程式商店,請升級版本之後操作'
|
||||
ErrAppNameExist: '應用程式名稱已存在'
|
||||
AppStoreIsSyncing: '應用程式商店正在同步中,請稍後再試'
|
||||
AppStoreIsSyncing: '應用程式商店正在同步中,請稍後重試'
|
||||
ErrGetCompose: 'docker-compose.yml 檔案取得失敗!{{ .detail }}'
|
||||
ErrAppWarn: '狀態異常,請檢視日誌'
|
||||
ErrAppParamKey: '參數{{ .name }} 欄位異常'
|
||||
ErrAppUpgrade: '應用程式{{ .name }} 升級失敗{{ .err }}'
|
||||
AppRecover: '應用程式{{ .name }} 回滾'
|
||||
PullImageStart: '開始拉取鏡像 [{{ .name }}]'
|
||||
PullImageSuccess: '鏡像拉取成功'
|
||||
PullImageStart: '開始拉取映像 [{{ .name }}]'
|
||||
PullImageSuccess: '映像拉取成功'
|
||||
AppStoreSyncSuccess: '應用程式商店同步成功'
|
||||
SyncAppDetail: '同步應用程式設定'
|
||||
AppVersionNotMatch: '{{ .name }} 應用程式需要更高的 1Panel 版本,跳過同步'
|
||||
MoveSiteDir: "檢測到網站目錄變更 需要遷移 OpenResty 網站目錄到 {{ .name }}"
|
||||
MoveSiteDir: '偵測到網站目錄變更 需要遷移 OpenResty 網站目錄到 {{ .name }}'
|
||||
MoveSiteDirSuccess: '遷移網站目錄成功'
|
||||
DeleteRuntimePHP: '刪除PHP 執行環境'
|
||||
CustomAppStoreFileValid: '應用程式商店包需要 .tar.gz 格式'
|
||||
PullImageTimeout: '拉取鏡像逾時,請嘗試增加鏡像加速或更換其他鏡像加速'
|
||||
CustomAppStoreFileValid: '應用程式商店包需要.tar.gz 格式'
|
||||
PullImageTimeout: '拉取映像逾時,請嘗試增加映像加速或更換其他映像加速'
|
||||
ErrAppIsDown: '{{ .name }} 應用程式狀態異常,請檢查'
|
||||
ErrCustomApps: '存在已安裝的應用程式,請先解除安裝'
|
||||
ErrCustomRuntimes: '存在已安裝的運作環境,請先刪除'
|
||||
ErrAppVersionDeprecated: "{{ .name }} 應用不適配目前 1Panel 版本,跳過"
|
||||
ErrDockerFailed: "Docker 狀態異常,請檢查服務狀態"
|
||||
ErrDockerComposeCmdNotFound: "Docker Compose 指令不存在,請先在宿主機安裝此指令"
|
||||
UseExistImage: '拉取映像失敗,使用存量映像'
|
||||
|
||||
#ssh
|
||||
ExportIP: "登入IP"
|
||||
ExportArea: "歸屬地"
|
||||
ExportPort: "埠"
|
||||
ExportAuthMode: "登入方式"
|
||||
ExportUser: "使用者"
|
||||
ExportStatus: "登入狀態"
|
||||
ExportDate: "時間"
|
||||
|
||||
#file
|
||||
ErrAppVersionDeprecated: '{{ .name }} 應用不適配目前 1Panel 版本,跳過'
|
||||
ErrDockerFailed: 'Docker 狀態異常,請檢查服務狀態'
|
||||
ErrDockerComposeCmdNotFound: 'Docker Compose 指令不存在,請先在宿主機安裝此指令'
|
||||
UseExistImage: '映像已存在,使用現有映像'
|
||||
ErrDatabaseNotFound: '資料庫 {{ .name }} 不存在'
|
||||
ExportIP: '登入IP'
|
||||
ExportArea: '歸屬地'
|
||||
ExportPort: '埠'
|
||||
ExportAuthMode: '登入方式'
|
||||
ExportUser: '使用者'
|
||||
ExportStatus: '登入狀態'
|
||||
ExportDate: '時間'
|
||||
ErrFileCanNotRead: '此檔案不支援預覽'
|
||||
ErrFileToLarge: '檔案超過10M,無法開啟'
|
||||
ErrPathNotFound: '目錄不存在'
|
||||
ErrMovePathFailed: '目標路徑不能包含原路徑!'
|
||||
ErrMovePathFailed: '目標路徑不可包含原路徑!'
|
||||
ErrLinkPathNotFound: '目標路徑不存在!'
|
||||
ErrFileIsExist: '檔案或資料夾已存在!'
|
||||
ErrFileUpload: '{{ .name }} 上傳檔案失敗{{ .detail }}'
|
||||
ErrFileDownloadDir: '不支援下載資料夾'
|
||||
ErrCmdNotFound: '{{ .name}} 指令不存在,請先在宿主機安裝此指令'
|
||||
ErrCmdNotFound: '{{ .name }} 指令不存在,請先在宿主機安裝此指令'
|
||||
ErrSourcePathNotFound: '來源目錄不存在'
|
||||
ErrFavoriteExist: '已收藏此路徑'
|
||||
ErrInvalidChar: '禁止使用無效字元'
|
||||
ErrPathNotDelete: '所選目錄不可刪除'
|
||||
ErrLogFileToLarge: "日誌檔案超過 500M,無法打開"
|
||||
FileDropFailed: "清理文件 {{ .name }} 失敗: {{ .err }}"
|
||||
FileDropSuccess: "清理文件 {{ .name }} 成功,清理 {{ .count }} 個文件,釋放 {{ .size }} 磁碟空間"
|
||||
FileDropSum: "文件清理完成,共清理 {{ .count }} 個文件,共釋放 {{ .size }} 磁碟空間"
|
||||
ErrBadDecrypt: "壓縮檔解壓密碼錯誤,解壓失敗,請檢查後重試!!"
|
||||
|
||||
#website
|
||||
ErrLogFileToLarge: '日誌檔案超過 500M,無法開啟'
|
||||
FileDropFailed: '清理檔案 {{ .name }} 失敗: {{ .err }}'
|
||||
FileDropSuccess: '清理檔案 {{ .name }} 成功,清理 {{ .count }} 個檔案,釋放 {{ .size }} 磁碟空間'
|
||||
FileDropSum: '檔案清理完成,共清理 {{ .count }} 個檔案,共釋放 {{ .size }} 磁碟空間'
|
||||
ErrBadDecrypt: '壓縮檔解壓密碼錯誤,解壓失敗,請重試。'
|
||||
ErrAliasIsExist: '代號已存在'
|
||||
ErrBackupMatch: '該備份檔案與目前網站部分資料不符{{ .detail }}'
|
||||
ErrBackupExist: '該備份檔案對應部分來源資料不存在{{ .detail }}'
|
||||
ErrPHPResource: '本機執行環境不支援切換! '
|
||||
ErrPathPermission: 'index 目錄下偵測到非 1000:1000 權限資料夾,可能導致網站存取 Access denied 錯誤,請點擊上方儲存按鈕'
|
||||
ErrPHPResource: '本機執行環境不支援切換!'
|
||||
ErrPathPermission: 'index 目錄下偵測到非 1000:1000 權限資料夾,可能導致網站存取 Access denied 錯誤,請點選上方儲存按鈕'
|
||||
ErrDomainIsUsed: '網域名稱已被網站【{{ .name }}】使用'
|
||||
ErrDomainFormat: '{{ .name }} 網域格式不正確'
|
||||
ErrDefaultAlias: 'default 為保留代號,請使用其他代號'
|
||||
ErrParentWebsite: '需要先移除子網站{{ .name }}'
|
||||
ErrBuildDirNotFound: '建置目錄不存在'
|
||||
ErrImageNotExist: '執行環境{{ .name }} 映像不存在,請重新編輯執行環境'
|
||||
ErrProxyIsUsed: "負載均衡已被反向代理使用,無法刪除"
|
||||
ErrSSLValid: '證書文件異常,請檢查證書狀態!'
|
||||
ErrWebsiteDir: "請選擇網站目錄下的目錄"
|
||||
ErrComposerFileNotFound: "composer.json 文件不存在"
|
||||
ErrRuntimeNoPort: "執行環境未設定埠,請先編輯執行環境"
|
||||
ErrProxyIsUsed: '負載均衡已被反向代理使用,無法刪除'
|
||||
ErrSSLValid: '憑證檔案異常,請檢查憑證狀態!'
|
||||
ErrWebsiteDir: '請選擇網站目錄下的目錄'
|
||||
ErrComposerFileNotFound: 'composer.json 檔案不存在'
|
||||
ErrRuntimeNoPort: '執行環境未設定埠,請先編輯執行環境'
|
||||
Status: '狀態'
|
||||
start: '開啟'
|
||||
stop: '關閉'
|
||||
restart: '重啟'
|
||||
restart: '重新啟動'
|
||||
kill: '強制停止'
|
||||
pause: '暫停'
|
||||
unpause: '恢復'
|
||||
remove: '刪除'
|
||||
delete: '刪除'
|
||||
ErrDefaultWebsite: '已經設置默認網站,請取消後再設置!'
|
||||
ErrDefaultWebsite: '已經設定預設網站,請取消後再設定!'
|
||||
SSL: '憑證'
|
||||
Set: '設定'
|
||||
|
||||
#ssl
|
||||
ErrSSLCannotDelete: '{{ .name }} 憑證正在被網站使用,無法刪除'
|
||||
ErrAccountCannotDelete: '帳號關聯證書,無法刪除'
|
||||
ErrAccountCannotDelete: '帳號關聯憑證,無法刪除'
|
||||
ErrSSLApply: '憑證續簽成功,openresty reload 失敗,請檢查設定!'
|
||||
ErrEmailIsExist: '信箱已存在'
|
||||
ErrSSLKeyNotFound: '私鑰檔案不存在'
|
||||
ErrSSLCertificateNotFound: '憑證檔案不存在'
|
||||
ErrSSLKeyFormat: '私鑰檔案校驗失敗'
|
||||
ErrSSLKeyFormat: '私鑰檔案驗證失敗'
|
||||
ErrSSLCertificateFormat: '憑證檔案格式錯誤,請使用 pem 格式'
|
||||
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid 或 EabHmacKey 不能為空'
|
||||
ErrOpenrestyNotFound: 'Http 模式需要先安裝Openresty'
|
||||
ApplySSLStart: '開始申請證書,網域名稱[{{ .domain }}] 申請方式[{{ .type }}] '
|
||||
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid 或 EabHmacKey 不可為空'
|
||||
ErrOpenrestyNotFound: 'Http 模式需要先安裝OpenResty'
|
||||
ApplySSLStart: '開始申請憑證,網域名稱[{{ .domain }}] 申請方式[{{ .type }}]'
|
||||
dnsAccount: 'DNS 自動'
|
||||
dnsManual: 'DNS 手排'
|
||||
http: 'HTTP'
|
||||
ApplySSLFailed: '申請[{{ .domain }}] 憑證失敗, {{ .detail }} '
|
||||
ApplySSLSuccess: '申請[{{ .domain }}] 憑證成功! ! '
|
||||
ApplySSLFailed: '申請[{{ .domain }}] 憑證失敗, {{ .detail }}'
|
||||
ApplySSLSuccess: '申請[{{ .domain }}] 憑證成功!'
|
||||
DNSAccountName: 'DNS 帳號[{{ .name }}] 廠商[{{ .type }}]'
|
||||
PushDirLog: '憑證推送到目錄[{{ .path }}] {{ .status }}'
|
||||
ErrDeleteCAWithSSL: '目前機構下存在已簽發證書,無法刪除'
|
||||
ErrDeleteWithPanelSSL: '面板SSL 配置使用此證書,無法刪除'
|
||||
ErrDefaultCA: '預設機構不能刪除'
|
||||
ErrDeleteCAWithSSL: '目前機構下存在已簽發憑證,無法刪除'
|
||||
ErrDeleteWithPanelSSL: '面板SSL 設定使用此憑證,無法刪除'
|
||||
ErrDefaultCA: '預設機構不可刪除'
|
||||
ApplyWebSiteSSLLog: '開始更新{{ .name }} 網站憑證'
|
||||
ErrUpdateWebsiteSSL: '{{ .name }} 網站更新憑證失敗: {{ .err }}'
|
||||
ApplyWebSiteSSLSuccess: '更新網站憑證成功'
|
||||
@@ -200,105 +190,117 @@ ExecShellSuccess: '腳本執行成功'
|
||||
StartUpdateSystemSSL: '開始更新系統憑證'
|
||||
UpdateSystemSSLSuccess: '更新系統憑證成功'
|
||||
ErrWildcardDomain: 'HTTP 模式無法申請泛網域憑證'
|
||||
ErrApplySSLCanNotDelete: "正在申請的證書 {{.name}} 無法刪除,請稍後再試"
|
||||
StartPushSSLToNode: "開始推送證書到節點"
|
||||
PushSSLToNodeFailed: "推送證書到節點失敗: {{ .err }}"
|
||||
PushSSLToNodeSuccess: "推送證書到節點成功"
|
||||
|
||||
#mysql
|
||||
ErrApplySSLCanNotDelete: '正在申請的憑證 {{ .name }} 無法刪除,請稍後重試'
|
||||
StartPushSSLToNode: '開始推送憑證到節點'
|
||||
PushSSLToNodeFailed: '推送憑證到節點失敗: {{ .err }}'
|
||||
PushSSLToNodeSuccess: '推送憑證到節點成功'
|
||||
ErrUserIsExist: '目前使用者已存在,請重新輸入'
|
||||
ErrDatabaseIsExist: '目前資料庫已存在,請重新輸入'
|
||||
ErrExecTimeOut: 'SQL 執行逾時,請檢查資料庫'
|
||||
ErrRemoteExist: '遠端資料庫已存在該名稱,請修改後重試'
|
||||
ErrLocalExist: '本機資料庫已存在該名稱,請修改後重試'
|
||||
RemoteBackup: "備份遠端資料庫需要先使用映像 {{ .name }} 啟動本機容器資料庫服務,請稍候..."
|
||||
RemoteRecover: "恢復遠端資料庫需要先使用映像 {{ .name }} 啟動本機容器資料庫服務,請稍候..."
|
||||
Arg: "參數"
|
||||
|
||||
#redis
|
||||
RemoteBackup: '備份遠端資料庫需要先使用映像 {{ .name }} 啟動本機容器資料庫服務,請稍候...'
|
||||
RemoteRecover: '復原遠端資料庫需要先使用映像 {{ .name }} 啟動本機容器資料庫服務,請稍候...'
|
||||
Arg: '參數'
|
||||
ErrTypeOfRedis: '復原檔案類型與目前持久化方式不符,請修改後重試'
|
||||
|
||||
#container
|
||||
ErrInUsed: "{{ .detail }} 正被使用,無法刪除!"
|
||||
ErrObjectInUsed: "該對象正被使用,無法刪除!"
|
||||
ErrObjectBeDependent: "該映像正被其他映像所依賴,無法刪除!"
|
||||
ErrPortRules: '連接埠數目不匹配,請重新輸入!'
|
||||
ErrPgImagePull: '鏡像拉取逾時,請配置鏡像加速或手動拉取{{ .name }} 鏡像後重試'
|
||||
PruneHelper: "本次清理 {{ .name }} 釋放磁碟空間 {{ .size }}"
|
||||
ImageRemoveHelper: "刪除鏡像 {{ .name }} ,釋放磁碟空間 {{ .size }}"
|
||||
BuildCache: "構建快取"
|
||||
Volume: "磁碟區"
|
||||
Network: "網路"
|
||||
PruneStart: "清理中,請稍候..."
|
||||
|
||||
#runtime
|
||||
ErrInUsed: '{{ .detail }} 正被使用,無法刪除!'
|
||||
ErrObjectInUsed: '該對象正被使用,無法刪除!'
|
||||
ErrObjectBeDependent: '該映像正被其他映像所依賴,無法刪除!'
|
||||
ErrPortRules: '連接埠數目不符,請重新輸入!'
|
||||
ErrPgImagePull: '映像拉取逾時,請設定映像加速或手動拉取{{ .name }} 映像後重試'
|
||||
PruneHelper: '本次清理 {{ .name }} 釋放磁碟空間 {{ .size }}'
|
||||
ImageRemoveHelper: '刪除映像 {{ .name }} ,釋放磁碟空間 {{ .size }}'
|
||||
BuildCache: '構建快取'
|
||||
Volume: '磁碟區'
|
||||
Network: '網路'
|
||||
PruneStart: '清理中,請稍候...'
|
||||
ContainerBackupPrepare: '準備容器備份工作目錄'
|
||||
ContainerBackupStop: '備份前停止容器'
|
||||
ContainerBackupInspect: '備份容器 Inspect 與網路資訊'
|
||||
ContainerBackupMounts: '備份容器掛載資料'
|
||||
ContainerBackupMeta: '寫入容器備份中繼資訊'
|
||||
ContainerBackupStart: '備份後啟動容器'
|
||||
ContainerRecoverPrepare: '準備容器還原工作目錄'
|
||||
ContainerRecoverExtract: '解壓容器備份檔案'
|
||||
ContainerRecoverParse: '載入容器備份中繼資訊'
|
||||
ContainerRecoverCreate: '重建容器'
|
||||
ContainerRecoverMounts: '還原容器掛載資料'
|
||||
ContainerRecoverStart: '啟動已還原容器'
|
||||
ContainerRecoverCleanup: '清理容器還原工作目錄'
|
||||
ComposeBackupPrepare: '準備編排備份工作目錄'
|
||||
ComposeBackupStop: '備份前停止編排'
|
||||
ComposeBackupFiles: '備份編排檔案'
|
||||
ComposeBackupContainers: '備份編排容器'
|
||||
ComposeBackupMeta: '寫入編排備份中繼資訊'
|
||||
ComposeBackupStart: '備份後啟動編排'
|
||||
ComposeBackupCleanup: '清理編排備份工作目錄'
|
||||
ComposeRecoverPrepare: '準備編排還原工作目錄'
|
||||
ComposeRecoverExtract: '解壓編排備份檔案'
|
||||
ComposeRecoverMeta: '載入編排備份中繼資訊'
|
||||
ComposeRecoverMetaLogName: '編排還原中繼資訊:名稱={{ .name }}'
|
||||
ComposeRecoverMetaLogPath: '編排還原中繼資訊:備份路徑={{ .backupPath }} 目標路徑={{ .targetDir }}'
|
||||
ComposeRecoverMetaLogCount: '編排還原中繼資訊:檔案數={{ .files }} 容器數={{ .containers }}'
|
||||
ComposeRecoverFiles: '還原編排檔案'
|
||||
ComposeRecoverContainers: '還原編排容器'
|
||||
ComposeRecoverRecord: '保存編排記錄'
|
||||
ComposeRecoverCleanup: '清理編排還原工作目錄'
|
||||
ErrFileNotExist: '{{ .detail }} 檔案不存在!請檢查來源檔案完整性!'
|
||||
ErrImageBuildErr: '鏡像build 失敗'
|
||||
ErrImageExist: "鏡像已存在!請修改鏡像名稱。"
|
||||
ErrImageBuildErr: '映像build 失敗'
|
||||
ErrImageExist: '映像已存在!請修改映像名稱。'
|
||||
ErrDelWithWebsite: '執行環境已經關聯網站,無法刪除'
|
||||
ErrRuntimeStart: '啟動失敗'
|
||||
ErrPackageJsonNotFound: 'package.json 檔案不存在'
|
||||
ErrScriptsNotFound: '沒有在 package.json 中找到 scripts 設定項'
|
||||
ErrContainerNameNotFound: '無法取得容器名稱,請檢查 .env 檔案'
|
||||
ErrContainerNameNotFound: '無法取得容器名稱,請檢查.env 檔案'
|
||||
ErrNodeModulesNotFound: 'node_modules 資料夾不存在!請編輯執行環境或等待執行環境啟動成功'
|
||||
ErrContainerNameIsNull: '容器名稱不存在'
|
||||
ErrPHPPortIsDefault: "9000 埠為預設埠,請修改後重試"
|
||||
ErrPHPRuntimePortFailed: "{{ .name }} 埠已被目前執行環境使用,請修改後重試"
|
||||
|
||||
#tool
|
||||
ErrPHPPortIsDefault: '9000 埠為預設埠,請修改後重試'
|
||||
ErrPHPRuntimePortFailed: '{{ .name }} 埠已被目前執行環境使用,請修改後重試'
|
||||
ErrConfigNotFound: '設定檔不存在'
|
||||
ErrConfigParse: '設定檔格式有誤'
|
||||
ErrConfigIsNull: '設定檔不允許為空'
|
||||
ErrConfigDirNotFound: '執行目錄不存在'
|
||||
ErrConfigAlreadyExist: '已存在同名設定檔'
|
||||
ErrUserFindErr: '使用者{{ .name }} 尋找失敗{{ .err }}'
|
||||
|
||||
#cronjob
|
||||
CutWebsiteLogSuccess: '{{ .name }} 網站日誌切割成功,備份路徑{{ .path }}'
|
||||
HandleShell: '執行腳本{{ .name }}'
|
||||
HandleCurl: "存取 URL {{ .name }}"
|
||||
HandleCurl: '存取 URL {{ .name }}'
|
||||
HandleNtpSync: '系統時間同步'
|
||||
HandleSystemClean: '系統快取清理'
|
||||
SystemLog: '系統日誌'
|
||||
CutWebsiteLog: '切割網站日誌'
|
||||
FileOrDir: '目錄 / 檔案'
|
||||
UploadFile: '上傳備份文件 {{ .file }} 到 {{ .backup }}'
|
||||
Upload: "上傳"
|
||||
IgnoreBackupErr: "備份失敗,錯誤:{{ .detail }},忽略本次錯誤..."
|
||||
IgnoreUploadErr: "上傳失敗,錯誤:{{ .detail }},忽略本次錯誤..."
|
||||
LoadBackupFailed: "取得備份帳號連線失敗,錯誤:{{ .detail }}"
|
||||
InExecuting: "目前任務正在執行中,請勿重複執行!"
|
||||
NoSuchResource: "資料庫中未能查詢到備份內容,跳過..."
|
||||
CleanLog: "清理日誌"
|
||||
CleanLogByName: "清理 {{.name}} 日誌"
|
||||
WafIpGroupNotFound: "WAF IP 群組未找到,請先到【進階功能-WAF-黑白名單-IP群組】添加遠端下載方式的IP群組"
|
||||
|
||||
#toolbox
|
||||
UploadFile: '上傳備份檔案 {{ .file }} 到 {{ .backup }}'
|
||||
Upload: '上傳'
|
||||
IgnoreBackupErr: '備份失敗,錯誤:{{ .detail }},忽略本次錯誤...'
|
||||
IgnoreUploadErr: '上傳失敗,錯誤:{{ .detail }},忽略本次錯誤...'
|
||||
LoadBackupFailed: '取得備份帳號連線失敗,錯誤:{{ .detail }}'
|
||||
InExecuting: '目前任務正在執行中,請勿重複執行!'
|
||||
NoSuchResource: '資料庫中未能查詢到備份內容,跳過...'
|
||||
CleanLog: '清理日誌'
|
||||
CleanLogByName: '清理 {{ .name }} 日誌'
|
||||
WafIpGroupNotFound: 'WAF IP 群組未找到,請先到【進階功能-WAF-黑白名單-IP群組】添加遠端下載方式的IP群組'
|
||||
ErrNotExistUser: '目前使用者不存在,請修改後重試!'
|
||||
ErrBanAction: '設定失敗,目前{{ .name }} 服務不可用,請檢查後再試一次!'
|
||||
ErrClamdscanNotFound: '未偵測到clamdscan 指令,請參考文件安裝!'
|
||||
TaskSwapSet: "設定 Swap"
|
||||
SetSwap: "設定 Swap {{ .path }} - {{ .size }}"
|
||||
CreateSwap: "建立 Swap 檔案"
|
||||
FormatSwap: "格式化 Swap 檔案"
|
||||
EnableSwap: "啟用 Swap"
|
||||
|
||||
#tamper
|
||||
CleanTamperSetting: "清理歷史防護設定"
|
||||
SetTamperWithRules: "根據規則啟動目錄防護"
|
||||
ProtectDir: "防護目錄 {{ .name }}"
|
||||
IgnoreFile: "保護文件 {{ .name }}"
|
||||
TamperSetSuccessful: "防護設定完成,開始監聽以下目錄:"
|
||||
|
||||
#waf
|
||||
ErrScope: '不支援修改此配置'
|
||||
ErrBanAction: '設定失敗,目前{{ .name }} 服務不可用,請重試'
|
||||
ErrClamdscanNotFound: '未偵測到clamdscan 指令,請參考檔案安裝!'
|
||||
TaskSwapSet: '設定 Swap'
|
||||
SetSwap: '設定 Swap {{ .path }} - {{ .size }}'
|
||||
CreateSwap: '建立 Swap 檔案'
|
||||
FormatSwap: '格式化 Swap 檔案'
|
||||
EnableSwap: '啟用 Swap'
|
||||
CleanTamperSetting: '清理歷史防護設定'
|
||||
SetTamperWithRules: '根據規則啟動目錄防護'
|
||||
ProtectDir: '防護目錄 {{ .name }}'
|
||||
IgnoreFile: '保護檔案 {{ .name }}'
|
||||
TamperSetSuccessful: '防護設定完成,開始監聽以下目錄:'
|
||||
ErrScope: '不支援修改此設定'
|
||||
ErrStateChange: '狀態修改失敗'
|
||||
ErrRuleExist: '規則已存在'
|
||||
ErrRuleNotExist: '規則不存在'
|
||||
ErrParseIP: 'IP 格式錯誤'
|
||||
ErrDefaultIP: 'default 為保留名稱,請更換其他名稱'
|
||||
ErrGroupInUse: 'IP 群組被黑/白名單使用,無法刪除'
|
||||
ErrIPGroupAclUse: "IP 群組被網站 {{ .name }} 自訂規則使用,無法刪除"
|
||||
ErrIPGroupAclUse: 'IP 群組被網站 {{ .name }} 自訂規則使用,無法刪除'
|
||||
ErrGroupExist: 'IP 群組名稱已存在'
|
||||
ErrIPRange: 'IP 範圍錯誤'
|
||||
ErrIPExist: 'IP 已存在'
|
||||
@@ -309,10 +311,10 @@ xss: 'XSS'
|
||||
phpExec: 'PHP 腳本執行'
|
||||
oneWordTrojan: '一句話木馬'
|
||||
appFilter: '套用危險目錄過濾'
|
||||
webshell: 'Webshell'
|
||||
webShell: 'WebShell'
|
||||
args: '參數規則'
|
||||
protocolFilter: '協定過濾'
|
||||
javaFileter: 'Java 危險檔案過濾'
|
||||
javaFilter: 'Java 危險檔案過濾'
|
||||
scannerFilter: '掃描器過濾'
|
||||
escapeFilter: '轉義過濾'
|
||||
customRule: '自訂規則'
|
||||
@@ -341,11 +343,10 @@ sqlInject: 'SQL 注入'
|
||||
ErrDBNotExist: '資料庫不存在'
|
||||
allow: '允許'
|
||||
deny: '禁止'
|
||||
OpenrestyNotFound: 'Openresty 未安裝'
|
||||
remoteIpIsNull: "IP 列表為空"
|
||||
OpenrestyVersionErr: "Openresty 版本過低,請升級 Openresty 至 1.27.1.2-2-2-focal"
|
||||
|
||||
#task
|
||||
OpenrestyNotFound: 'OpenResty 未安裝'
|
||||
remoteIpIsNull: 'IP 列表為空'
|
||||
OpenrestyVersionErr: 'OpenResty 版本過低,請升級 OpenResty 至 1.27.1.2-2-2-focal'
|
||||
ErrFileTooLarge: '檔案超過 1MB,無法上傳'
|
||||
TaskStart: '{{ .name }} 任務開始[START]'
|
||||
TaskEnd: '{{ .name }} 任務結束[COMPLETED]'
|
||||
TaskFailed: '{{ .name }} 任務失敗'
|
||||
@@ -360,8 +361,8 @@ TaskCreate: '建立'
|
||||
TaskDelete: '刪除'
|
||||
TaskUpgrade: '升級'
|
||||
TaskUpdate: '更新'
|
||||
TaskRestart: '重啟'
|
||||
TaskProtect: "防護"
|
||||
TaskRestart: '重新啟動'
|
||||
TaskProtect: '防護'
|
||||
TaskBackup: '備份'
|
||||
TaskRecover: '復原'
|
||||
TaskRollback: '回滾'
|
||||
@@ -369,16 +370,16 @@ TaskPull: '拉取'
|
||||
TaskCommit: '製作'
|
||||
TaskBuild: '建置'
|
||||
TaskPush: '推送'
|
||||
TaskClean: "清理"
|
||||
TaskClean: '清理'
|
||||
TaskHandle: '執行'
|
||||
TaskImport: "導入"
|
||||
TaskExport: "導出"
|
||||
TaskImport: '匯入'
|
||||
TaskExport: '匯出'
|
||||
Website: '網站'
|
||||
App: '應用程式'
|
||||
Runtime: '運作環境'
|
||||
Database: '資料庫'
|
||||
ConfigFTP: '建立FTP 使用者{{ .name }}'
|
||||
ConfigOpenresty: '建立Openresty 設定檔'
|
||||
ConfigOpenresty: '建立OpenResty 設定檔'
|
||||
InstallAppSuccess: '應用程式{{ .name }} 安裝成功'
|
||||
ConfigRuntime: '設定執行環境'
|
||||
ConfigApp: '設定應用程式'
|
||||
@@ -387,11 +388,11 @@ FailedStatus: '{{ .name }} 失敗{{ .err }}'
|
||||
HandleLink: '處理應用程式關聯'
|
||||
HandleDatabaseApp: '處理應用程式參數'
|
||||
ExecShell: '執行{{ .name }} 腳本'
|
||||
PullImage: '拉取鏡像'
|
||||
PullImage: '拉取映像'
|
||||
Start: '開始'
|
||||
Run: '啟動'
|
||||
Stop: '停止'
|
||||
Image: '鏡像'
|
||||
Image: '映像'
|
||||
Compose: '編排'
|
||||
Container: '容器'
|
||||
AppLink: '關聯應用程式'
|
||||
@@ -404,28 +405,22 @@ RuntimeExtension: '執行環境擴充'
|
||||
TaskIsExecuting: '任務正在運作'
|
||||
CustomAppstore: '自訂應用程式倉庫'
|
||||
TaskExec: '執行'
|
||||
TaskBatch: "批次操作"
|
||||
TaskBatch: '批次操作'
|
||||
FileConvert: '檔案格式轉換'
|
||||
|
||||
# task - clam
|
||||
Clamscan: "掃描 {{ .name }}"
|
||||
TaskScan: "掃描"
|
||||
|
||||
# task - ai
|
||||
OllamaModelPull: '拉取 Ollama 模型{{ .name }} '
|
||||
Clamscan: '掃描 {{ .name }}'
|
||||
TaskScan: '掃描'
|
||||
OllamaModelPull: '拉取 Ollama 模型{{ .name }}'
|
||||
OllamaModelSize: '取得 Ollama 模型{{ .name }} 大小'
|
||||
|
||||
# task - snapshot
|
||||
Snapshot: '快照'
|
||||
SnapDBInfo: '寫入1Panel 資料庫資訊'
|
||||
SnapCopy: '複製檔案&目錄{{ .name }} '
|
||||
SnapCopy: '複製檔案&目錄{{ .name }}'
|
||||
SnapNewDB: '初始化資料庫{{ .name }} 連線'
|
||||
SnapDeleteOperationLog: '刪除操作日誌'
|
||||
SnapDeleteLoginLog: '刪除存取日誌'
|
||||
SnapDeleteMonitor: '刪除監控資料'
|
||||
SnapRemoveSystemIP: '移除系統IP'
|
||||
SnapBaseInfo: '寫入1Panel 基本資料'
|
||||
SnapInstallAppImageEmpty: '目前未勾選應用鏡像,跳過...'
|
||||
SnapInstallAppImageEmpty: '目前未勾選應用映像,跳過...'
|
||||
SnapInstallApp: '備份 1Panel 已安裝應用'
|
||||
SnapDockerSave: '壓縮已安裝應用'
|
||||
SnapLocalBackup: '備份1Panel 本機備份目錄'
|
||||
@@ -441,7 +436,6 @@ SnapCompressSize: '快照檔案大小{{ .name }}'
|
||||
SnapUpload: '上傳快照檔案'
|
||||
SnapUploadTo: '上傳快照檔案到{{ .name }}'
|
||||
SnapUploadRes: '上傳快照檔案到{{ .name }}'
|
||||
|
||||
SnapshotRecover: '快照復原'
|
||||
RecoverDownload: '下載快照檔案'
|
||||
Download: '下載'
|
||||
@@ -449,94 +443,75 @@ RecoverDownloadAccount: '取得快照下載備份帳號{{ .name }}'
|
||||
RecoverDecompress: '解壓縮快照壓縮檔'
|
||||
Decompress: '解壓縮'
|
||||
BackupBeforeRecover: '快照前備份系統相關資料'
|
||||
Readjson: '讀取快照內Json 檔案'
|
||||
ReadjsonPath: '取得快照內Json 檔案路徑'
|
||||
ReadjsonContent: '讀取Json 檔案'
|
||||
ReadjsonMarshal: 'Json 轉義處理'
|
||||
ReadJson: '讀取快照內JSON 檔案'
|
||||
ReadJsonPath: '取得快照內JSON 檔案路徑'
|
||||
ReadJsonContent: '讀取JSON 檔案'
|
||||
ReadJsonMarshal: 'JSON 轉義處理'
|
||||
RecoverApp: '復原已安裝應用程式'
|
||||
RecoverWebsite: '復原網站目錄'
|
||||
RecoverAppImage: '復原快照鏡像備份'
|
||||
RecoverAppImage: '復原快照映像備份'
|
||||
RecoverCompose: '復原其他編排內容'
|
||||
RecoverComposeList: '取得所有待復原編排'
|
||||
RecoverComposeItem: '復原編排{{ .name }}'
|
||||
RecoverAppEmpty: '快照檔案中未發現應用程式鏡像備份'
|
||||
RecoverAppEmpty: '快照檔案中未發現應用程式映像備份'
|
||||
RecoverBaseData: '復原基礎資料及檔案'
|
||||
RecoverDaemonJsonEmpty: '快照檔案及目前機器都不存在容器配置daemon.json 檔案'
|
||||
RecoverDaemonJson: '復原容器配置daemon.json 檔案'
|
||||
RecoverDaemonJsonEmpty: '快照檔案及目前機器都不存在容器設定daemon.json 檔'
|
||||
RecoverDaemonJson: '復原容器設定daemon.json 檔'
|
||||
RecoverDBData: '復原資料庫資料'
|
||||
RecoverBackups: '還原本機備份目錄'
|
||||
RecoverPanelData: '復原資料目錄'
|
||||
|
||||
# task - container
|
||||
ContainerNewCliet: '初始化Docker Client'
|
||||
ContainerImagePull: '拉取容器鏡像{{ .name }}'
|
||||
ContainerImagePull: '拉取容器映像{{ .name }}'
|
||||
ContainerRemoveOld: '刪除原容器{{ .name }}'
|
||||
ContainerImageCheck: '檢查鏡像是否正常拉取'
|
||||
ContainerImageCheck: '檢查映像是否正常拉取'
|
||||
ContainerLoadInfo: '取得容器基本資訊'
|
||||
ContainerRecreate: '容器更新失敗,現在開始復原原容器'
|
||||
ContainerCreate: '建立新容器{{ .name }}'
|
||||
ContainerCreateFailed: '容器建立失敗,刪除失敗容器'
|
||||
ContainerStartCheck: '檢查容器是否已啟動'
|
||||
|
||||
# task - image
|
||||
ImageBuild: '鏡像建置'
|
||||
ImageBuildStdoutCheck: '解析鏡像輸出內容'
|
||||
ImageBuildRes: '鏡像建置輸出:{{ .name }}'
|
||||
ImagePull: '拉取鏡像'
|
||||
ImageBuild: '映像建置'
|
||||
ImagePull: '拉取映像'
|
||||
ImageRepoAuthFromDB: '從資料庫取得倉庫認證資訊'
|
||||
ImaegPullRes: '鏡像拉取輸出:{{ .name }}'
|
||||
ImagePush: '推播鏡像'
|
||||
ImageRenameTag: '修改鏡像Tag'
|
||||
ImageNewTag: '新鏡像Tag {{ .name }}'
|
||||
ImaegPushRes: '鏡像推播輸出:{{ .name }}'
|
||||
ImagePush: '推播映像'
|
||||
ImageRenameTag: '修改映像Tag'
|
||||
ImageNewTag: '新映像Tag {{ .name }}'
|
||||
ComposeCreate: '建立編排'
|
||||
ComposeCreateRes: '編排建立輸出:{{ .name }}'
|
||||
|
||||
# task - website
|
||||
BackupNginxConfig: '備份網站OpenResty 設定檔'
|
||||
CompressFileSuccess: '壓縮目錄成功,壓縮為{{ .name }}'
|
||||
CompressDir: '壓縮目錄'
|
||||
DeCompressFile: '解壓縮檔案{{ .name }}'
|
||||
ErrCheckValid: '校驗備份檔失敗,{{ .name }}'
|
||||
ErrCheckValid: '驗證備份檔失敗,{{ .name }}'
|
||||
Rollback: '回滾'
|
||||
websiteDir: '網站目錄'
|
||||
RecoverFailedStartRollBack: '復原失敗,開始回溯'
|
||||
AppBackupFileIncomplete: '備份檔案不完整缺少 app.json 或 app.tar.gz 檔案'
|
||||
AppAttributesNotMatch: '應用程式類型或名稱不一致'
|
||||
ErrAlertSync: '警告訊息同步錯誤,請重試。'
|
||||
ErrInstallExtension: '已有安裝任務正在進行,請等待任務結束'
|
||||
PanelAlertTitle: '面板警示通知'
|
||||
TestAlertTitle: '測試郵件 - 驗證信箱連通性'
|
||||
TestAlert: '這是一封測試郵件,用於確認您的郵件發送設定是否正確。'
|
||||
LicenseExpirationAlert: '您的 {{ .node }}{{ .ip }} 面板,授權將於 {{ .day }} 天後到期,詳情請登入面板檢視。'
|
||||
CronJobFailedAlert: '您的 {{ .node }}{{ .ip }} 面板,排程任務{{ .name }}執行失敗,詳情請登入面板檢視。'
|
||||
ClamAlert: '您的 {{ .node }}{{ .ip }} 面板,病毒掃描任務偵測到 {{ .num }} 個感染檔案,詳情請登入面板檢視。'
|
||||
WebSiteAlert: '您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 個網站將於 {{ .day }} 天後到期,詳情請登入面板檢視。'
|
||||
SSLAlert: '您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 張SSL憑證將於 {{ .day }} 天後到期,詳情請登入面板檢視。'
|
||||
DiskUsedAlert: '您的 {{ .node }}{{ .ip }} 面板,磁碟 {{ .name }} 使用率已達 {{ .used }},詳情請登入面板檢視。'
|
||||
ResourceAlert: '您的 {{ .node }}{{ .ip }} 面板,過去 {{ .time }} 分鐘內 {{ .name }} 平均使用率為 {{ .used }},詳情請登入面板檢視。'
|
||||
PanelVersionAlert: '您的 {{ .node }}{{ .ip }} 面板,已有最新版本可供升級,詳情請登入面板檢視。'
|
||||
PanelPwdExpirationAlert: '您的 {{ .node }}{{ .ip }} 面板,面板密碼將於 {{ .day }} 天後到期,詳情請登入面板檢視。'
|
||||
CommonAlert: '您的 {{ .node }}{{ .ip }} 面板,{{ .msg }},詳情請登入面板檢視。'
|
||||
NodeExceptionAlert: '您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 個節點出現異常,詳情請登入面板檢視。'
|
||||
LicenseExceptionAlert: '您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 個授權出現異常,詳情請登入面板檢視。'
|
||||
SSHAndPanelLoginAlert: '您的 {{ .node }}{{ .ip }} 面板,來自 {{ .loginIp }} 的 {{ .name }} 登入出現異常,詳情請登入面板檢視。'
|
||||
DeviceNotFound: '裝置 {{ .name }} 未找到'
|
||||
DeviceIsMounted: '裝置 {{ .name }} 已掛載,請先移除'
|
||||
PartitionDiskErr: '分區失敗,{{ .err }}'
|
||||
FormatDiskErr: '格式化磁碟失敗,{{ .err }}'
|
||||
MountDiskErr: '掛載磁碟失敗,{{ .err }}'
|
||||
XfsNotFound: '未偵測到 xfs 檔案系統,請先安裝 xfsprogs'
|
||||
|
||||
#alert
|
||||
ErrAlert: '警告訊息格式錯誤,請檢查後重試!!'
|
||||
ErrAlertPush: '警告訊息推送錯誤,請檢查後重試!!'
|
||||
ErrAlertSave: '警告訊息儲存錯誤,請檢查後重試!!'
|
||||
ErrAlertSync: '警告訊息同步錯誤,請檢查後重試!!'
|
||||
ErrAlertRemote: '警告訊息遠端錯誤,請檢查後重試!!'
|
||||
|
||||
#task - runtime
|
||||
ErrInstallExtension: "已有安裝任務正在進行,請等待任務結束"
|
||||
|
||||
# alert mail template
|
||||
PanelAlertTitle: "面板警示通知"
|
||||
TestAlertTitle: "測試郵件 - 驗證信箱連通性"
|
||||
TestAlert: "這是一封測試郵件,用於確認您的郵件發送設定是否正確。"
|
||||
LicenseExpirationAlert: "您的 {{ .node }}{{ .ip }} 面板,授權將於 {{ .day }} 天後到期,詳情請登入面板查看。"
|
||||
CronJobFailedAlert: "您的 {{ .node }}{{ .ip }} 面板,排程任務{{ .name }}執行失敗,詳情請登入面板查看。"
|
||||
ClamAlert: "您的 {{ .node }}{{ .ip }} 面板,病毒掃描任務偵測到 {{ .num }} 個感染檔案,詳情請登入面板查看。"
|
||||
WebSiteAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 個網站將於 {{ .day }} 天後到期,詳情請登入面板查看。"
|
||||
SSLAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 張SSL憑證將於 {{ .day }} 天後到期,詳情請登入面板查看。"
|
||||
DiskUsedAlert: "您的 {{ .node }}{{ .ip }} 面板,磁碟 {{ .name }} 使用率已達 {{ .used }},詳情請登入面板查看。"
|
||||
ResourceAlert: "您的 {{ .node }}{{ .ip }} 面板,過去 {{ .time }} 分鐘內 {{ .name }} 平均使用率為 {{ .used }},詳情請登入面板查看。"
|
||||
PanelVersionAlert: "您的 {{ .node }}{{ .ip }} 面板,已有最新版本可供升級,詳情請登入面板查看。"
|
||||
PanelPwdExpirationAlert: "您的 {{ .node }}{{ .ip }} 面板,面板密碼將於 {{ .day }} 天後到期,詳情請登入面板查看。"
|
||||
CommonAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .msg }},詳情請登入面板查看。"
|
||||
NodeExceptionAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 個節點出現異常,詳情請登入面板查看。"
|
||||
LicenseExceptionAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 個授權出現異常,詳情請登入面板查看。"
|
||||
SSHAndPanelLoginAlert: "您的 {{ .node }}{{ .ip }} 面板,來自 {{ .loginIp }} 的 {{ .name }} 登入出現異常,詳情請登入面板查看。"
|
||||
|
||||
#disk
|
||||
DeviceNotFound: "裝置 {{ .name }} 未找到"
|
||||
DeviceIsMounted: "裝置 {{ .name }} 已掛載,請先移除"
|
||||
PartitionDiskErr: "分區失敗,{{ .err }}"
|
||||
FormatDiskErr: "格式化磁碟失敗,{{ .err }}"
|
||||
MountDiskErr: "掛載磁碟失敗,{{ .err }}"
|
||||
UnMountDiskErr: "移除磁碟失敗,{{ .err }}"
|
||||
XfsNotFound: "未檢測到 xfs 文件系統,請先安裝 xfsprogs"
|
||||
# terminal
|
||||
TerminalAIBlockedRiskyCommand: '已攔截風險命令:{{ .command }}'
|
||||
TerminalAIThinking: 'AI 正在思考...'
|
||||
TerminalAIReadyToExecute: '思考完成,請按 Enter 執行(耗時 {{ .duration }},token:{{ .tokens }})'
|
||||
TerminalAIRequestFailed: 'AI 請求失敗:{{ .err }}'
|
||||
|
||||
+141
-110
@@ -1,30 +1,32 @@
|
||||
ErrInvalidParams: "请求参数错误: {{ .detail }}"
|
||||
ErrTokenParse: "Token 生成错误: {{ .detail }}"
|
||||
ErrInvalidParams: "参数错误: {{ .detail }}"
|
||||
ErrTokenParse: "Token 错误: {{ .detail }}"
|
||||
ErrInitialPassword: "原密码错误"
|
||||
ErrInternalServer: "服务内部错误: {{ .detail }}"
|
||||
ErrInternalServer: "服务错误: {{ .detail }}"
|
||||
ErrWgetRemoteFailed: "远程下载地址无效或无法访问:{{ .detail }}"
|
||||
ErrWgetInvalidContentType: "远程地址返回的不是文件内容(可能是错误页),{{ .detail }}"
|
||||
ErrRecordExist: "记录已存在"
|
||||
ErrRecordNotFound: "记录未能找到"
|
||||
ErrRecordNotFound: "记录不存在"
|
||||
ErrStructTransform: "类型转换失败: {{ .err }}"
|
||||
ErrNotLogin: "用户未登录: {{ .detail }}"
|
||||
ErrPasswordExpired: "当前密码已过期: {{ .detail }}"
|
||||
ErrNotSupportType: "系统暂不支持当前类型: {{ .name }}"
|
||||
ErrProxy: "请求错误,请检查该节点状态: {{ .detail }}"
|
||||
ErrApiConfigStatusInvalid: "API 接口禁止访问: {{ .detail }}"
|
||||
ErrNotSupportType: "不支持当前类型: {{ .name }}"
|
||||
ErrProxy: "请求失败,请检查节点状态: {{ .detail }}"
|
||||
ErrApiConfigStatusInvalid: "API 禁止访问: {{ .detail }}"
|
||||
ErrApiConfigKeyInvalid: "API 接口密钥错误: {{ .detail }}"
|
||||
ErrApiConfigIPInvalid: "调用 API 接口 IP 不在白名单: {{ .detail }}"
|
||||
ErrApiConfigDisable: "此接口禁止使用 API 接口调用: {{ .detail }}"
|
||||
ErrApiConfigKeyTimeInvalid: "API 接口时间戳错误: {{ .detail }}"
|
||||
ErrShutDown: "命令被手动结束!"
|
||||
ErrApiConfigDisable: "此接口禁止 API 调用: {{ .detail }}"
|
||||
ErrApiConfigKeyTimeInvalid: "API 时间戳错误: {{ .detail }}"
|
||||
ErrShutDown: "命令已手动终止"
|
||||
|
||||
ErrMinQuickJump: "请至少设置一个快速跳转入口!"
|
||||
ErrMaxQuickJump: "最多可设置四个快速跳转入口!"
|
||||
ErrMinQuickJump: "至少设置 1 个快速跳转入口"
|
||||
ErrMaxQuickJump: "最多设置 4 个快速跳转入口"
|
||||
|
||||
#common
|
||||
ErrUsernameIsExist: "用户名已存在"
|
||||
ErrNameIsExist: "名称已存在"
|
||||
ErrDemoEnvironment: "演示服务器,禁止此操作!"
|
||||
ErrCmdTimeout: "命令执行超时!"
|
||||
ErrCmdIllegal: "执行命令中存在不合法字符,请修改后重试!"
|
||||
ErrDemoEnvironment: "演示环境,禁止操作"
|
||||
ErrCmdTimeout: "命令超时"
|
||||
ErrCmdIllegal: "命令含非法字符,请修改后重试"
|
||||
ErrPortExist: '{{ .port }} 端口已被 {{ .type }} [{{ .name }}] 占用'
|
||||
TYPE_APP: "应用"
|
||||
TYPE_RUNTIME: "运行环境"
|
||||
@@ -33,23 +35,27 @@ ErrTypePort: '端口 {{ .name }} 格式错误'
|
||||
ErrTypePortRange: '端口范围需要在 1-65535 之间'
|
||||
Success: "成功"
|
||||
Failed: "失败"
|
||||
SystemRestart: "系统重启导致任务中断"
|
||||
ErrGroupIsDefault: "默认分组,无法删除"
|
||||
SystemRestart: "系统重启,任务中断"
|
||||
ErrGroupIsDefault: "默认分组不可删除"
|
||||
ErrGroupIsInWebsiteUse: "分组正在被其他网站使用,无法删除"
|
||||
Decrypt: "解密"
|
||||
|
||||
#agent
|
||||
ErrAgentAccountBound: "该账号已经绑定到智能体,无法删除,请检查后重试!!"
|
||||
ErrAgentAccountUnavailable: "账号连接信息不可用,错误:{{ .err }},请检查后重试!"
|
||||
ErrAgentProviderNotSupported: "暂不支持该智能体提供商,请检查后重试!"
|
||||
ErrAgentAccountBound: "该账号已绑定智能体,无法删除"
|
||||
ErrAgentAccountUnavailable: "账号连接信息不可用: {{ .err }}"
|
||||
ErrAgentProviderNotSupported: "不支持该智能体提供商"
|
||||
ErrAgentAccountRequired: "请选择智能体账号后重试"
|
||||
ErrAgentAccountNotVerified: "账号未验证通过,请检查后重试!"
|
||||
ErrAgentProviderMismatch: "账号提供商不匹配,请检查后重试!"
|
||||
ErrAgentBaseURLRequired: "Base URL 不能为空,请检查后重试!"
|
||||
ErrAgentApiKeyRequired: "API Key 不能为空,请检查后重试!"
|
||||
ErrAgentComposeRequired: "自定义编排内容不能为空,请检查后重试!"
|
||||
ErrAgentIDRequired: "智能体 ID 不能为空,请检查后重试!"
|
||||
ErrAgentAccountIDRequired: "账号 ID 不能为空,请检查后重试!"
|
||||
ErrAgentAccountNotVerified: "账号未通过验证"
|
||||
ErrAgentAccountModelsRequired: "请至少配置一个模型"
|
||||
ErrAgentAccountSingleInitialModel: "创建账号时只能配置一个初始模型"
|
||||
ErrAgentModelNotInAccount: "所选模型未配置在当前账号中"
|
||||
ErrAgentModelInUse: "仍有智能体正在使用该模型: {{ .name }}"
|
||||
ErrAgentBaseURLRequired: "Base URL 不能为空"
|
||||
ErrAgentApiKeyRequired: "API Key 不能为空"
|
||||
ErrAgentComposeRequired: "自定义编排内容不能为空"
|
||||
ErrAgentIDRequired: "智能体 ID 不能为空"
|
||||
ErrAgentAccountIDRequired: "账号 ID 不能为空"
|
||||
ErrAgentLimitReached: "社区版最多支持创建 {{ .max }} 个智能体,升级至专业版无数量限制"
|
||||
|
||||
#backup
|
||||
Localhost: '本机'
|
||||
@@ -60,15 +66,15 @@ ErrBackupLocalCreate: "暂不支持创建本地服务器备份账号"
|
||||
|
||||
#app
|
||||
ErrPortInUsed: "{{ .detail }} 端口已被占用!"
|
||||
ErrAppLimit: "应用超出安装数量限制"
|
||||
ErrAppLimit: "应用安装数量超限"
|
||||
ErrNotInstall: "应用未安装"
|
||||
ErrPortInOtherApp: "{{ .port }} 端口已被应用 {{ .apps }} 占用!"
|
||||
ErrDbUserNotValid: "存量数据库,用户名密码不匹配!"
|
||||
ErrUpdateBuWebsite: '应用更新成功,但是网站配置文件修改失败,请检查配置!'
|
||||
ErrDbUserNotValid: "存量数据库用户名或密码不匹配"
|
||||
ErrUpdateBuWebsite: '应用更新成功,但网站配置文件修改失败,请检查配置'
|
||||
Err1PanelNetworkFailed: '默认容器网络创建失败!{{ .detail }}'
|
||||
ErrFileParse: '应用 docker-compose 文件解析失败!'
|
||||
ErrInstallDirNotFound: '安装目录不存在,如需卸载,请选择强制卸载'
|
||||
AppStoreIsUpToDate: '应用商店已经是最新版本'
|
||||
ErrFileParse: '应用 docker-compose 解析失败'
|
||||
ErrInstallDirNotFound: '安装目录不存在,如需卸载请选强制卸载'
|
||||
AppStoreIsUpToDate: '应用商店已是最新版本'
|
||||
LocalAppVersionNull: '{{ .name }} 应用未同步到版本!无法添加到应用列表'
|
||||
LocalAppVersionErr: '{{ .name }} 同步版本 {{ .version }} 失败!{{ .err }}'
|
||||
ErrFileNotFound: '{{ .name }} 文件不存在'
|
||||
@@ -86,29 +92,29 @@ ErrContainerMsg: '{{ .name }} 容器异常,具体请在容器页面查看日
|
||||
ErrAppBackup: '{{ .name }} 应用备份失败 {{ .err }}'
|
||||
ErrVersionTooLow: '当前 1Panel 版本过低,无法更新应用商店,请升级版本之后操作'
|
||||
ErrAppNameExist: '应用名称已存在'
|
||||
AppStoreIsSyncing: '应用商店正在同步中,请稍后再试'
|
||||
AppStoreIsSyncing: '应用商店同步中,请稍后重试'
|
||||
ErrGetCompose: "docker-compose.yml 文件获取失败!{{ .detail }}"
|
||||
ErrAppWarn: "状态异常,请查看日志"
|
||||
ErrAppParamKey: "参数 {{ .name }} 字段异常"
|
||||
ErrAppUpgrade: "应用 {{ .name }} 升级失败 {{ .err }}"
|
||||
AppRecover: "应用 {{ .name }} 回滚 "
|
||||
AppRecover: "应用 {{ .name }} 回滚"
|
||||
PullImageStart: "开始拉取镜像 [{{ .name }}]"
|
||||
PullImageSuccess: "镜像拉取成功"
|
||||
AppStoreSyncSuccess: "应用商店同步成功"
|
||||
SyncAppDetail: "同步应用配置"
|
||||
AppVersionNotMatch: "{{ .name }} 应用需要更高的 1Panel 版本,跳过"
|
||||
MoveSiteDir: "检测到网站目录变更 需要迁移 OpenResty 网站目录到 {{ .name }}"
|
||||
MoveSiteDir: "检测到网站目录变更,需迁移 OpenResty 网站目录到 {{ .name }}"
|
||||
MoveSiteDirSuccess: "迁移网站目录成功"
|
||||
DeleteRuntimePHP: "删除 PHP 运行环境"
|
||||
CustomAppStoreFileValid: "应用商店包需要 .tar.gz 格式"
|
||||
PullImageTimeout: "拉取镜像超时,请尝试增加镜像加速或者更换其他镜像加速"
|
||||
PullImageTimeout: "拉取镜像超时,请调整镜像加速配置"
|
||||
ErrAppIsDown: "{{ .name }} 应用状态异常,请检查"
|
||||
ErrCustomApps: "存在已经安装的应用,请先卸载"
|
||||
ErrCustomRuntimes: "存在已经安装的运行环境,请先删除"
|
||||
ErrAppVersionDeprecated: " {{ .name }} 应用不适配当前 1Panel 版本,跳过"
|
||||
ErrCustomApps: "存在已安装应用,请先卸载"
|
||||
ErrCustomRuntimes: "存在已安装运行环境,请先删除"
|
||||
ErrAppVersionDeprecated: "{{ .name }} 应用不适配当前 1Panel 版本,跳过"
|
||||
ErrDockerFailed: "Docker 状态异常,请检查服务状态"
|
||||
ErrDockerComposeCmdNotFound: "Docker Compose 命令不存在,请先在宿主机安装此命令"
|
||||
UseExistImage: "拉取镜像失败,使用存量镜像"
|
||||
ErrDockerComposeCmdNotFound: "Docker Compose 命令不存在,请先在宿主机安装"
|
||||
UseExistImage: "镜像已存在,使用存量镜像"
|
||||
ErrDatabaseNotFound: "数据库 {{ .name }} 不存在"
|
||||
|
||||
#ssh
|
||||
@@ -122,30 +128,30 @@ ExportDate: "时间"
|
||||
|
||||
#file
|
||||
ErrFileCanNotRead: "此文件不支持预览"
|
||||
ErrFileToLarge: "文件超过10M,无法打开"
|
||||
ErrFileToLarge: "文件超过 10M,无法打开"
|
||||
ErrPathNotFound: "目录不存在"
|
||||
ErrMovePathFailed: "目标路径不能包含原路径!"
|
||||
ErrLinkPathNotFound: "目标路径不存在!"
|
||||
ErrLinkPathNotFound: "目标路径不存在!"
|
||||
ErrFileIsExist: "文件或文件夹已存在!"
|
||||
ErrFileUpload: "{{ .name }} 上传文件失败 {{ .detail }}"
|
||||
ErrFileDownloadDir: "不支持下载文件夹"
|
||||
ErrCmdNotFound: "{{ .name}} 命令不存在,请先在宿主机安装此命令"
|
||||
ErrFileDownloadDir: "不支持下载目录"
|
||||
ErrCmdNotFound: "{{ .name }} 命令不存在,请先在宿主机安装此命令"
|
||||
ErrSourcePathNotFound: "源目录不存在"
|
||||
ErrFavoriteExist: "已收藏此路径"
|
||||
ErrInvalidChar: "禁止使用非法字符"
|
||||
ErrPathNotDelete: "所选目录不可删除"
|
||||
ErrLogFileToLarge: "日志文件超过 500M,无法打开"
|
||||
FileDropFailed: "清理文件 {{ .name }} 失败 {{ .err }}"
|
||||
FileDropSuccess: "清理文件 {{ .name }} 成功,清理文件 {{ .count }},释放磁盘空间 {{ .size }}"
|
||||
FileDropSum: "文件清理成功,清理文件共计 {{ .count }},释放磁盘空间共计 {{ .size }}"
|
||||
ErrBadDecrypt: "压缩包解压密码错误,解压失败,请检查后重试!!"
|
||||
FileDropSuccess: "清理 {{ .name }} 成功,文件 {{ .count }},释放 {{ .size }}"
|
||||
FileDropSum: "文件清理完成,共 {{ .count }},释放 {{ .size }}"
|
||||
ErrBadDecrypt: "压缩包密码错误,解压失败,请检查后重试"
|
||||
|
||||
#website
|
||||
ErrAliasIsExist: "代号已存在"
|
||||
ErrAliasIsExist: "标识已存在"
|
||||
ErrBackupMatch: '该备份文件与当前网站部分数据不匹配 {{ .detail }}'
|
||||
ErrBackupExist: '该备份文件对应部分源数据不存在 {{ .detail }}'
|
||||
ErrPHPResource: '本地运行环境不支持切换!'
|
||||
ErrPathPermission: 'index 目录下检测到非 1000:1000 权限文件夹,可能导致网站访问 Access denied 错误,请点击上方保存按钮'
|
||||
ErrPathPermission: 'index 目录存在非 1000:1000 权限目录,可能导致 Access denied,请点击上方保存'
|
||||
ErrDomainIsUsed: "域名已被网站【{{ .name }}】使用"
|
||||
ErrDomainFormat: "{{ .name }} 域名格式不正确"
|
||||
ErrDefaultAlias: "default 为保留代号,请使用其他代号"
|
||||
@@ -166,14 +172,14 @@ pause: '暂停'
|
||||
unpause: '恢复'
|
||||
remove: '删除'
|
||||
delete: '删除'
|
||||
ErrDefaultWebsite: "已经设置默认网站,请取消后再设置!"
|
||||
ErrDefaultWebsite: "已设置默认网站,请先取消再设置"
|
||||
SSL: '证书'
|
||||
Set: '设置'
|
||||
|
||||
#ssl
|
||||
ErrSSLCannotDelete: "{{ .name }} 证书正在被网站使用,无法删除"
|
||||
ErrAccountCannotDelete: "账号关联证书,无法删除"
|
||||
ErrSSLApply: "证书续签成功,openresty reload失败,请检查配置!"
|
||||
ErrSSLApply: "证书续签成功,但 OpenResty reload 失败,请检查配置"
|
||||
ErrEmailIsExist: '邮箱已存在'
|
||||
ErrSSLKeyNotFound: '私钥文件不存在'
|
||||
ErrSSLCertificateNotFound: '证书文件不存在'
|
||||
@@ -181,18 +187,18 @@ ErrSSLKeyFormat: '私钥文件校验失败'
|
||||
ErrSSLCertificateFormat: '证书文件格式错误,请使用 pem 格式'
|
||||
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid 或 EabHmacKey 不能为空'
|
||||
ErrOpenrestyNotFound: 'Http 模式需要首先安装 Openresty'
|
||||
ApplySSLStart: '开始申请证书,域名 [{{ .domain }}] 申请方式 [{{ .type }}] '
|
||||
ApplySSLStart: '开始申请证书,域名 [{{ .domain }}],方式 [{{ .type }}]'
|
||||
dnsAccount: "DNS 自动"
|
||||
dnsManual: "DNS 手动"
|
||||
http: "HTTP"
|
||||
ApplySSLFailed: '申请 [{{ .domain }}] 证书失败, {{ .detail }} '
|
||||
ApplySSLSuccess: '申请 [{{ .domain }}] 证书成功!!'
|
||||
ApplySSLFailed: '申请 [{{ .domain }}] 证书失败,{{ .detail }}'
|
||||
ApplySSLSuccess: '申请 [{{ .domain }}] 证书成功'
|
||||
DNSAccountName: 'DNS 账号 [{{ .name }}] 厂商 [{{ .type }}]'
|
||||
PushDirLog: '证书推送到目录 [{{ .path }}] {{ .status }}'
|
||||
ErrDeleteCAWithSSL: "当前机构下存在已签发证书,无法删除"
|
||||
ErrDeleteWithPanelSSL: "面板 SSL 配置使用此证书,无法删除"
|
||||
ErrDefaultCA: "默认机构不能删除"
|
||||
ApplyWebSiteSSLLog: "开始更新 {{ .name }} 网站证书"
|
||||
ApplyWebSiteSSLLog: "开始更新网站证书 {{ .name }}"
|
||||
ErrUpdateWebsiteSSL: "{{ .name }} 网站更新证书失败: {{ .err }}"
|
||||
ApplyWebSiteSSLSuccess: "更新网站证书成功"
|
||||
ErrExecShell: "执行脚本失败 {{ .err }}"
|
||||
@@ -201,19 +207,19 @@ ExecShellSuccess: "脚本执行成功"
|
||||
StartUpdateSystemSSL: "开始更新系统证书"
|
||||
UpdateSystemSSLSuccess: "更新系统证书成功"
|
||||
ErrWildcardDomain: "HTTP 模式无法申请泛域名证书"
|
||||
ErrApplySSLCanNotDelete: "正在申请的证书{{.name}}无法删除,请稍后再试"
|
||||
ErrApplySSLCanNotDelete: "正在申请的证书{{ .name }}无法删除,请稍后重试"
|
||||
StartPushSSLToNode: "开始推送证书到节点"
|
||||
PushSSLToNodeFailed: "推送证书到节点失败: {{ .err }}"
|
||||
PushSSLToNodeSuccess: "推送证书到节点成功"
|
||||
|
||||
#mysql
|
||||
ErrUserIsExist: "当前用户已存在,请重新输入"
|
||||
ErrDatabaseIsExist: "当前数据库已存在,请重新输入"
|
||||
ErrUserIsExist: "用户已存在,请重新输入"
|
||||
ErrDatabaseIsExist: "数据库已存在,请重新输入"
|
||||
ErrExecTimeOut: "SQL 执行超时,请检查数据库"
|
||||
ErrRemoteExist: "远程数据库已存在该名称,请修改后重试"
|
||||
ErrLocalExist: "本地数据库已存在该名称,请修改后重试"
|
||||
RemoteBackup: "备份远程数据库需要先使用镜像 {{ .name }} 启动本地容器数据库服务,请稍候..."
|
||||
RemoteRecover: "恢复远程数据库需要先使用镜像 {{ .name }} 启动本地容器数据库服务,请稍候..."
|
||||
RemoteBackup: "备份远程数据库前需先用镜像 {{ .name }} 启动本地容器数据库服务,请稍候"
|
||||
RemoteRecover: "恢复远程数据库前需先用镜像 {{ .name }} 启动本地容器数据库服务,请稍候"
|
||||
Arg: "参数"
|
||||
|
||||
#redis
|
||||
@@ -224,13 +230,43 @@ ErrInUsed: "{{ .detail }} 正被使用,无法删除!"
|
||||
ErrObjectInUsed: "该对象正被使用,无法删除!"
|
||||
ErrObjectBeDependent: "该镜像正被其他镜像所依赖,无法删除!"
|
||||
ErrPortRules: "端口数目不匹配,请重新输入!"
|
||||
ErrPgImagePull: "镜像拉取超时,请配置镜像加速或手动拉取 {{ .name }} 镜像后重试"
|
||||
ErrPgImagePull: "镜像拉取超时,请配置镜像加速或手动拉取 {{ .name }} 后重试"
|
||||
PruneHelper: "本次清理 {{ .name }} 释放磁盘空间 {{ .size }}"
|
||||
ImageRemoveHelper: "删除镜像 {{ .name }} ,释放磁盘空间 {{ .size }}"
|
||||
BuildCache: "构建缓存"
|
||||
Volume: "存储卷"
|
||||
Network: "网络"
|
||||
PruneStart: "清理中,请稍候..."
|
||||
ContainerBackupPrepare: "准备容器备份工作目录"
|
||||
ContainerBackupStop: "备份前停止容器"
|
||||
ContainerBackupInspect: "备份容器 Inspect 与网络信息"
|
||||
ContainerBackupMounts: "备份容器挂载数据"
|
||||
ContainerBackupMeta: "写入容器备份元信息"
|
||||
ContainerBackupStart: "备份后启动容器"
|
||||
ContainerRecoverPrepare: "准备容器恢复工作目录"
|
||||
ContainerRecoverExtract: "解压容器备份文件"
|
||||
ContainerRecoverParse: "加载容器备份元信息"
|
||||
ContainerRecoverCreate: "重建容器"
|
||||
ContainerRecoverMounts: "恢复容器挂载数据"
|
||||
ContainerRecoverStart: "启动已恢复容器"
|
||||
ContainerRecoverCleanup: "清理容器恢复工作目录"
|
||||
ComposeBackupPrepare: "准备编排备份工作目录"
|
||||
ComposeBackupStop: "备份前停止编排"
|
||||
ComposeBackupFiles: "备份编排文件"
|
||||
ComposeBackupContainers: "备份编排容器"
|
||||
ComposeBackupMeta: "写入编排备份元信息"
|
||||
ComposeBackupStart: "备份后启动编排"
|
||||
ComposeBackupCleanup: "清理编排备份工作目录"
|
||||
ComposeRecoverPrepare: "准备编排恢复工作目录"
|
||||
ComposeRecoverExtract: "解压编排备份文件"
|
||||
ComposeRecoverMeta: "加载编排备份元信息"
|
||||
ComposeRecoverMetaLogName: "编排恢复元信息:名称={{ .name }}"
|
||||
ComposeRecoverMetaLogPath: "编排恢复元信息:备份路径={{ .backupPath }} 目标路径={{ .targetDir }}"
|
||||
ComposeRecoverMetaLogCount: "编排恢复元信息:文件数={{ .files }} 容器数={{ .containers }}"
|
||||
ComposeRecoverFiles: "恢复编排文件"
|
||||
ComposeRecoverContainers: "恢复编排容器"
|
||||
ComposeRecoverRecord: "保存编排记录"
|
||||
ComposeRecoverCleanup: "清理编排恢复工作目录"
|
||||
|
||||
#runtime
|
||||
ErrFileNotExist: "{{ .detail }} 文件不存在!请检查源文件完整性!"
|
||||
@@ -241,7 +277,7 @@ ErrRuntimeStart: "启动失败"
|
||||
ErrPackageJsonNotFound: "package.json 文件不存在"
|
||||
ErrScriptsNotFound: "没有在 package.json 中找到 scripts 配置项"
|
||||
ErrContainerNameNotFound: "无法获取容器名称,请检查 .env 文件"
|
||||
ErrNodeModulesNotFound: "node_modules 文件夹不存在!请编辑运行环境或者等待运行环境启动成功"
|
||||
ErrNodeModulesNotFound: "node_modules 文件夹不存在!请编辑运行环境或等待运行环境启动成功"
|
||||
ErrContainerNameIsNull: "容器名称不存在"
|
||||
ErrPHPPortIsDefault: "9000 端口为默认端口,请修改后重试"
|
||||
ErrPHPRuntimePortFailed: "{{ .name }} 端口已被当前运行环境使用,请修改后重试"
|
||||
@@ -268,16 +304,16 @@ Upload: "上传"
|
||||
IgnoreBackupErr: "备份失败,错误:{{ .detail }},忽略本次错误..."
|
||||
IgnoreUploadErr: "上传失败,错误:{{ .detail }},忽略本次错误..."
|
||||
LoadBackupFailed: "获取备份账号连接失败,错误:{{ .detail }}"
|
||||
InExecuting: "当前任务正在执行中,请勿重复执行!"
|
||||
NoSuchResource: "数据库中未能查询到备份内容,跳过..."
|
||||
InExecuting: "任务执行中,请勿重复执行"
|
||||
NoSuchResource: "数据库未查询到备份内容,跳过"
|
||||
CleanLog: "清理日志"
|
||||
CleanLogByName: "清理 {{.name}} 日志"
|
||||
CleanLogByName: "清理 {{ .name }} 日志"
|
||||
WafIpGroupNotFound: "WAF IP组未找到,请先到【高级功能-WAF-黑白名单-IP组】添加远程下载方式的IP组"
|
||||
|
||||
#toolbox
|
||||
ErrNotExistUser: "当前用户不存在,请修改后重试!"
|
||||
ErrBanAction: "设置失败,当前 {{ .name }} 服务不可用,请检查后重试!!"
|
||||
ErrClamdscanNotFound: "未检测到 clamdscan 命令,请参考文档安装!"
|
||||
ErrBanAction: "设置失败,{{ .name }} 服务不可用,请检查后重试"
|
||||
ErrClamdscanNotFound: "未检测到 clamdscan 命令,请按文档安装"
|
||||
TaskSwapSet: "设置 Swap"
|
||||
SetSwap: "设置 Swap {{ .path }} - {{ .size }}"
|
||||
CreateSwap: "创建 Swap 文件"
|
||||
@@ -310,10 +346,10 @@ xss: 'XSS'
|
||||
phpExec: 'PHP 脚本执行'
|
||||
oneWordTrojan: '一句话木马'
|
||||
appFilter: '应用危险目录过滤'
|
||||
webshell: 'Webshell'
|
||||
webShell: 'WebShell'
|
||||
args: '参数规则'
|
||||
protocolFilter: '协议过滤'
|
||||
javaFileter: 'Java 危险文件过滤'
|
||||
javaFilter: 'Java 危险文件过滤'
|
||||
scannerFilter: '扫描器过滤'
|
||||
escapeFilter: '转义过滤'
|
||||
customRule: '自定义规则'
|
||||
@@ -344,8 +380,8 @@ allow: "允许"
|
||||
deny: "禁止"
|
||||
OpenrestyNotFound: "Openresty 未安装"
|
||||
remoteIpIsNull: "IP 列表为空"
|
||||
OpenrestyVersionErr: "Openresty 版本过低,请升级 Openresty 至 1.27.1.2-2-2-focal"
|
||||
ErrFileTooLarge: "文件超过 1MB 无法上传"
|
||||
OpenrestyVersionErr: "OpenResty 版本过低,请升级到 1.27.1.2-2-2-focal"
|
||||
ErrFileTooLarge: "文件超过 1MB,无法上传"
|
||||
|
||||
#task
|
||||
TaskStart: "{{ .name }} 任务开始 [START]"
|
||||
@@ -403,7 +439,7 @@ TaskSync: "同步"
|
||||
LocalApp: "本地应用"
|
||||
SubTask: "子任务"
|
||||
RuntimeExtension: "运行环境扩展"
|
||||
TaskIsExecuting: "任务正在运行"
|
||||
TaskIsExecuting: "任务运行中"
|
||||
CustomAppstore: "自定义应用仓库"
|
||||
TaskExec: "执行"
|
||||
TaskBatch: "批量操作"
|
||||
@@ -414,14 +450,14 @@ Clamscan: "扫描 {{ .name }}"
|
||||
TaskScan: "扫描"
|
||||
|
||||
# task - ai
|
||||
OllamaModelPull: "拉取 Ollama 模型 {{ .name }} "
|
||||
OllamaModelSize: "获取 Ollama 模型 {{ .name }} 大小 "
|
||||
OllamaModelPull: "拉取 Ollama 模型 {{ .name }}"
|
||||
OllamaModelSize: "获取 Ollama 模型 {{ .name }} 大小"
|
||||
|
||||
# task - snapshot
|
||||
Snapshot: "快照"
|
||||
SnapDBInfo: "写入 1Panel 数据库信息"
|
||||
SnapCopy: "复制文件&目录 {{ .name }} "
|
||||
SnapNewDB: "初始化数据库 {{ .name }} 连接 "
|
||||
SnapCopy: "复制文件/目录 {{ .name }}"
|
||||
SnapNewDB: "初始化数据库 {{ .name }} 连接"
|
||||
SnapDeleteOperationLog: "删除操作日志"
|
||||
SnapDeleteLoginLog: "删除访问日志"
|
||||
SnapDeleteMonitor: "删除监控数据"
|
||||
@@ -451,10 +487,10 @@ RecoverDownloadAccount: "获取快照下载备份账号 {{ .name }}"
|
||||
RecoverDecompress: "解压快照压缩文件"
|
||||
Decompress: "解压"
|
||||
BackupBeforeRecover: "快照前备份系统相关数据"
|
||||
Readjson: "读取快照内 Json 文件"
|
||||
ReadjsonPath: "获取快照内 Json 文件路径"
|
||||
ReadjsonContent: "读取 Json 文件"
|
||||
ReadjsonMarshal: "Json 转义处理"
|
||||
ReadJson: "读取快照内 Json 文件"
|
||||
ReadJsonPath: "获取快照内 Json 文件路径"
|
||||
ReadJsonContent: "读取 Json 文件"
|
||||
ReadJsonMarshal: "Json 转义处理"
|
||||
RecoverApp: "恢复已安装应用"
|
||||
RecoverWebsite: "恢复网站目录"
|
||||
RecoverAppImage: "恢复快照镜像备份"
|
||||
@@ -470,7 +506,6 @@ RecoverBackups: "恢复本地备份目录"
|
||||
RecoverPanelData: "恢复数据目录"
|
||||
|
||||
# task - container
|
||||
ContainerNewCliet: "初始化 Docker Client"
|
||||
ContainerImagePull: "拉取容器镜像 {{ .name }}"
|
||||
ContainerRemoveOld: "删除原容器 {{ .name }}"
|
||||
ContainerImageCheck: "检查镜像是否正常拉取"
|
||||
@@ -482,17 +517,12 @@ ContainerStartCheck: "检查容器是否已启动"
|
||||
|
||||
# task - image
|
||||
ImageBuild: "镜像构建"
|
||||
ImageBuildStdoutCheck: "解析镜像输出内容"
|
||||
ImageBuildRes: "镜像构建输出:{{ .name }}"
|
||||
ImagePull: "拉取镜像"
|
||||
ImageRepoAuthFromDB: "从数据库获取仓库认证信息"
|
||||
ImaegPullRes: "镜像拉取输出:{{ .name }}"
|
||||
ImagePush: "推送镜像"
|
||||
ImageRenameTag: "修改镜像 Tag"
|
||||
ImageNewTag: "新镜像 Tag {{ .name }}"
|
||||
ImaegPushRes: "镜像推送输出:{{ .name }}"
|
||||
ComposeCreate: "创建编排"
|
||||
ComposeCreateRes: "编排创建输出:{{ .name }}"
|
||||
|
||||
# task - website
|
||||
BackupNginxConfig: "备份网站 OpenResty 配置文件"
|
||||
@@ -503,15 +533,11 @@ ErrCheckValid: "校验备份文件失败,{{ .name }}"
|
||||
Rollback: "回滚"
|
||||
websiteDir: "网站目录"
|
||||
RecoverFailedStartRollBack: "恢复失败,开始回滚"
|
||||
AppBackupFileIncomplete: "备份文件不完整 缺少 app.json 或者 app.tar.gz 文件"
|
||||
AppAttributesNotMatch: "应用类型或者名称不一致"
|
||||
AppBackupFileIncomplete: "备份文件不完整,缺少 app.json 或 app.tar.gz 文件"
|
||||
AppAttributesNotMatch: "应用类型或名称不一致"
|
||||
|
||||
#alert
|
||||
ErrAlert: "告警信息格式错误,请检查后重试!!"
|
||||
ErrAlertPush: "告警信息推送错误,请检查后重试!!"
|
||||
ErrAlertSave: "告警信息保存错误,请检查后重试!!"
|
||||
ErrAlertSync: "告警信息同步错误,请检查后重试!!"
|
||||
ErrAlertRemote: "告警信息远端错误,请检查后重试!!"
|
||||
ErrAlertSync: "告警信息同步错误,请检查后重试"
|
||||
|
||||
#task - runtime
|
||||
ErrInstallExtension: "已有安装任务正在进行,请等待任务结束"
|
||||
@@ -519,20 +545,20 @@ ErrInstallExtension: "已有安装任务正在进行,请等待任务结束"
|
||||
# alert mail template
|
||||
PanelAlertTitle: "面板告警通知"
|
||||
TestAlertTitle: "测试邮件 - 验证邮箱连通性"
|
||||
TestAlert: "这是一封测试邮件,旨在验证您的邮箱发件配置是否正确。"
|
||||
LicenseExpirationAlert: "您的 {{ .node }}{{ .ip }} 面板,许可证将在 {{ .day }} 天后到期,详情请登录面板查看。"
|
||||
CronJobFailedAlert: "您的 {{ .node }}{{ .ip }} 面板,计划任务-{{ .name }}执行失败,详情请登录面板查看。"
|
||||
ClamAlert: "您的 {{ .node }}{{ .ip }} 面板,病毒扫描任务发现 {{ .num }} 个感染文件,详情请登录面板查看。"
|
||||
WebSiteAlert: "您的 {{ .node }}{{ .ip }} 面板,有 {{ .num }} 个网站将在 {{ .day }} 天后到期,详情请登录面板查看。"
|
||||
SSLAlert: "您的 {{ .node }}{{ .ip }} 面板,有 {{ .num }} 张SSL证书将在 {{ .day }} 天后到期,详情请登录面板查看。"
|
||||
DiskUsedAlert: "您的 {{ .node }}{{ .ip }} 面板,磁盘 {{ .name }} 已使用 {{ .used }},详情请登录面板查看。"
|
||||
ResourceAlert: "您的 {{ .node }}{{ .ip }} 面板,平均 {{ .time }} 分钟内的 {{ .name }} 使用率为 {{ .used }},详情请登录面板查看。"
|
||||
PanelVersionAlert: "您的 {{ .node }}{{ .ip }} 面板,有最新面板版本可供升级,详情请登录面板查看。"
|
||||
PanelPwdExpirationAlert: "您的 {{ .node }}{{ .ip }} 面板,面板密码将在 {{ .day }} 天后到期,详情请登录面板查看。"
|
||||
CommonAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .msg }},详情请登录面板查看。"
|
||||
NodeExceptionAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 个节点存在异常,详情请登录面板查看。"
|
||||
LicenseExceptionAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 个许可证存在异常,详情请登录面板查看。"
|
||||
SSHAndPanelLoginAlert: "您的 {{ .node }}{{ .ip }} 面板,面板{{ .name }}登录{{ .loginIp }}异常,详情请登录面板查看。"
|
||||
TestAlert: "这是一封测试邮件,用于验证邮箱发件配置。"
|
||||
LicenseExpirationAlert: "您的 {{ .node }}{{ .ip }} 面板,许可证将在 {{ .day }} 天后到期,请登录面板查看详情。"
|
||||
CronJobFailedAlert: "您的 {{ .node }}{{ .ip }} 面板,计划任务-{{ .name }}执行失败,请登录面板查看详情。"
|
||||
ClamAlert: "您的 {{ .node }}{{ .ip }} 面板,病毒扫描任务发现 {{ .num }} 个感染文件,请登录面板查看详情。"
|
||||
WebSiteAlert: "您的 {{ .node }}{{ .ip }} 面板,有 {{ .num }} 个网站将在 {{ .day }} 天后到期,请登录面板查看详情。"
|
||||
SSLAlert: "您的 {{ .node }}{{ .ip }} 面板,有 {{ .num }} 张SSL证书将在 {{ .day }} 天后到期,请登录面板查看详情。"
|
||||
DiskUsedAlert: "您的 {{ .node }}{{ .ip }} 面板,磁盘 {{ .name }} 已使用 {{ .used }},请登录面板查看详情。"
|
||||
ResourceAlert: "您的 {{ .node }}{{ .ip }} 面板,平均 {{ .time }} 分钟内的 {{ .name }} 使用率为 {{ .used }},请登录面板查看详情。"
|
||||
PanelVersionAlert: "您的 {{ .node }}{{ .ip }} 面板,有最新面板版本可供升级,请登录面板查看详情。"
|
||||
PanelPwdExpirationAlert: "您的 {{ .node }}{{ .ip }} 面板,面板密码将在 {{ .day }} 天后到期,请登录面板查看详情。"
|
||||
CommonAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .msg }},请登录面板查看详情。"
|
||||
NodeExceptionAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 个节点存在异常,请登录面板查看详情。"
|
||||
LicenseExceptionAlert: "您的 {{ .node }}{{ .ip }} 面板,{{ .num }} 个许可证存在异常,请登录面板查看详情。"
|
||||
SSHAndPanelLoginAlert: "您的 {{ .node }}{{ .ip }} 面板,面板 {{ .name }} 登录 {{ .loginIp }} 异常,请登录面板查看详情。"
|
||||
|
||||
#disk
|
||||
DeviceNotFound: "设备 {{ .name }} 未找到"
|
||||
@@ -540,5 +566,10 @@ DeviceIsMounted: "设备 {{ .name }} 已挂载,请先卸载"
|
||||
PartitionDiskErr: "分区失败,{{ .err }}"
|
||||
FormatDiskErr: "格式化磁盘失败,{{ .err }}"
|
||||
MountDiskErr: "挂载磁盘失败,{{ .err }}"
|
||||
UnMountDiskErr: "卸载磁盘失败,{{ .err }}"
|
||||
XfsNotFound: "未检测到 xfs 文件系统,请先安装 xfsprogs"
|
||||
|
||||
# terminal
|
||||
TerminalAIBlockedRiskyCommand: "已拦截风险命令:{{ .command }}"
|
||||
TerminalAIThinking: "AI 正在思考..."
|
||||
TerminalAIReadyToExecute: "思考完成,请回车执行(耗时 {{ .duration }},token:{{ .tokens }})"
|
||||
TerminalAIRequestFailed: "AI 请求失败:{{ .err }}"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user