Compare commits

..
123 Commits
Author SHA1 Message Date
CityFun d114933fe9 feat: agent account add openRouter (#12143) 2026-03-11 04:00:55 +00:00
蘭 432412e596 ref: Adjust the column widths of the file system (#12141) 2026-03-11 10:38:15 +08:00
ssongliu 1b577e3d41 fix(core): resolve operation data by agent for non-local nodes (#12136) 2026-03-10 17:23:06 +08:00
CityFun eda9641d35 feat: change some prompt (#12135) 2026-03-10 16:11:31 +08:00
CityFun 6808e4892c fix: Fixed ai menu router error (#12134) 2026-03-10 14:55:00 +08:00
CityFun 0d71cd4db2 feat: change custom model name logic (#12132) 2026-03-10 03:25:58 +00:00
CityFun b6b3304a71 style: change some prompt (#12131) 2026-03-10 11:09:20 +08:00
CityFun 59a0a573ed feat: 增加 vLLM 管理 (#12129) 2026-03-09 20:01:39 +08:00
CityFun e82d686940 feat: add vLLm management (#12128) 2026-03-09 19:55:20 +08:00
ssongliu b48bb88aa1 fix(postgresql): check active sessions before dropping db (#12127) 2026-03-09 16:04:58 +08:00
ssongliu 5ce9ec5b64 feat(container): refine compose and setting i18n (#12125)
Refs #12109
2026-03-09 08:03:52 +00:00
ssongliu 740f3549ad fix: optimize operation log resolve flow and i18n replacements (#12123) 2026-03-09 16:02:12 +08:00
zhengkunwang223 aa8a9e8b1e feat: add QQbot channel for ai Agent 2026-03-09 15:11:04 +08:00
ssongliu 14218e3495 fix(compose): load directory when compose path changes (#12122)
Refs #12106
2026-03-09 06:57:50 +00:00
蘭 c526eb2962 fix: Resolve the issue of not receiving emails when the sender's name is in Chinese (#12121)
#12006
2026-03-09 06:55:50 +00:00
CityFun 1ff8292be4 feat: add ai agent create limit (#12124) 2026-03-09 13:54:06 +08:00
A_Words d4394616af feat: support toggling file list columns (#12099)
* feat: support toggling file list columns

* feat: use fu table column select for file list

* chore: remove unused file column i18n keys

* feat: simplify file column visibility logic and remove unused configurations
2026-03-09 09:51:53 +08:00
王贺 462ca6b373 feat: rename AI model menu to local model (#12108) 2026-03-09 09:51:24 +08:00
ssongliu 657c3ad37a chore: localize newly added i18n keys (#12104) 2026-03-06 14:05:40 +00:00
王贺 90d11a3b74 remove ssl helper (#12102) 2026-03-06 15:31:28 +08:00
CityFun 883f3db725 feat: add openclaw default tool config (#12096) 2026-03-06 13:40:03 +08:00
ssongliu 75da72c265 fix: avoid pulling build-only compose images (#12095)
Refs  #12092
2026-03-06 02:29:39 +00:00
ssongliu 78eef15f5b feat(ai): hide coding plan providers in intl mode (#12090) 2026-03-05 10:47:38 +00:00
ssongliu 735cf3cc71 chore(agent): downgrade docker/cli to v28.5.1 (#12091) 2026-03-05 18:38:42 +08:00
Rowan Chen 99c5d9a8b2 feat: show SSL certificate issuer and expiry date in certificate selectors (#12071) 2026-03-05 15:44:12 +08:00
Rowan Chen 3a0d2039d0 fix: set sort_order for newly installed apps to avoid random positioning (#12069) 2026-03-05 15:43:18 +08:00
dependabot[bot] 98e2d6390a chore(deps): bump github.com/docker/cli in /agent (#12079)
Bumps [github.com/docker/cli](https://github.com/docker/cli) from 28.5.1+incompatible to 29.2.0+incompatible.
- [Commits](https://github.com/docker/cli/compare/v28.5.1...v29.2.0)

---
updated-dependencies:
- dependency-name: github.com/docker/cli
  dependency-version: 29.2.0+incompatible
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-05 12:02:34 +08:00
ssongliu 975e7f9f58 revert: drop nginx safe string validation changes (#12076) 2026-03-05 11:55:23 +08:00
王贺 77c90a26f8 chore: change screenshot (#12075)
Updated UI display screenshot in README.
2026-03-05 11:07:33 +08:00
ssongliu 17cb12df8b fix: avoid sidebar menu flicker when loading and sorting (#12066) 2026-03-05 02:37:04 +00:00
王贺 c986b04667 Update installation script URL in README (#12065) 2026-03-04 18:11:06 +08:00
CityFun 754e016f20 feat: Optimize the Ollama configuration workflow (#12063) 2026-03-04 08:39:03 +00:00
ssongliu f67ce75492 fix: update hook initialization handling (#12062) 2026-03-04 16:38:59 +08:00
ssongliu e236ca5607 fix(swagger): correct api annotation types and methods (#12060) 2026-03-04 07:13:02 +00:00
CityFun 10e74c4fe4 feat: add agent analytics req (#12059) 2026-03-04 07:01:03 +00:00
MoeShin 473c0a5ff2 chore: optimize website proxy api and log (#12052) 2026-03-04 14:35:05 +08:00
ssongliu 7fbe6e4091 fix(auth): update 1panel token hash validation (#12056) 2026-03-04 14:30:17 +08:00
ssongliu 65dbb53a07 fix(terminal): remove command completion protocol (#12055) 2026-03-04 06:25:05 +00:00
KOMATA c21910c203 feat: enhance container compose view with additional status indicators and environment configuration options (#12042)
- Added visual indicators for container status and creation date.
- Introduced a new radio button for environment configuration alongside existing options.
- Improved layout and styling for better user experience.
- Adjusted table column widths and added a class for styling.
2026-03-03 18:31:47 +08:00
CityFun 1728511bad feat: update kimi-coding logic (#12049) 2026-03-03 09:32:03 +00:00
ssongliu e63f6672f8 fix: persist terminal font settings (#12047) 2026-03-03 09:30:03 +00:00
ssongliu 7fa9754ce7 refine passkey prerequisite checks and i18n guidance 2026-03-03 17:08:06 +08:00
王贺 475e4ca629 ci: configure Go dependency cache (#12046)
Add cache dependency path for Go setup
2026-03-03 16:04:26 +08:00
ssongliu 32dee74edf fix: update intl domain links to 1panel.pro (#12045) 2026-03-03 07:20:02 +00:00
CityFun 6715559925 feat: support z.ai agent account (#12044) 2026-03-03 15:18:24 +08:00
KOMATA 2f380c9f2f feat: enhance app synchronization with concurrent processing and detailed result handling (#12041)
- Introduced concurrent processing for app synchronization tasks to improve performance.
- Added structures for managing app work items and results.
- Implemented detailed logging of synchronization progress and HTTP request outcomes.
- Enhanced error handling for icon downloads and app details updates.
2026-03-03 13:46:22 +08:00
CityFun 0aebb0d2db [Feature] Add App Store status check on the Agent page (#12039) 2026-03-03 12:02:42 +08:00
CityFun b7cf9aab2b feat(ai-agents): support ollama apiType selection (#12037) 2026-03-03 11:57:17 +08:00
Rowan Chen ea2f1875bc fix: disable favorite toggle during sort mode and fix drag rebound issue (#12026)
* Optimize sorting-related issues, disable the "favorite stars" function during sorting, and optimize sorting behavior caused by DOM conflicts.

* fix: prevent app icon from being squished in detail drawer
2026-03-03 11:06:53 +08:00
KOMATA 83524940ed refactor: remove unused pprof import from main.go (#12031) 2026-03-03 11:05:26 +08:00
MoeShin 8c33eb8409 feat: Operate website proxy config (#12005)
* feat: Operate website proxy config

* chore: Update api comment

* chore: update swagger

* chore: update x-log

* feat: split api
2026-03-03 11:05:00 +08:00
ssongliu 644655af86 feat: add extra_hosts support for container config (#12035)
Related: https://github.com/1Panel-dev/1Panel/issues/11861
2026-03-03 03:02:02 +00:00
CityFun 8082f4d2ca fix(ai-agents): mark skipped-verification accounts as unverified and normalize legacy status (#12034) 2026-03-03 11:00:53 +08:00
ssongliu 3c896bc5f8 fix: improve MFA interval compatibility i18n hints (#12033)
Related: https://github.com/1Panel-dev/1Panel/issues/11915
2026-03-03 03:00:02 +00:00
ssongliu 5c7ce6d8d4 fix: add PostgreSQL recreation and DB host env sync in app restore (#12029) 2026-03-03 02:58:01 +00:00
CityFun 222702f66b feat(ai-agents): Add ark coding plan for agent account (#12027) 2026-03-02 22:04:57 +08:00
ssongliu b3b3feb1b9 feat: improve container log viewer and download options (#12025)
#11898
2026-03-02 14:04:02 +00:00
ssongliu 133140040b fix: store loadingText as key and localize on render (#12022) 2026-03-02 10:32:01 +00:00
ssongliu d70f903432 fix: hide upage for intl and reload after edition switch (#12021) 2026-03-02 10:30:00 +00:00
CityFun f77f305316 feat(ai-agents): add time zone config for openclaw (#12020) 2026-03-02 10:28:01 +00:00
ssongliu 13b5908442 fix: handle sqlite wal/shm mismatch during restore (#12024) 2026-03-02 18:27:38 +08:00
CityFun d1335db986 fix(ai-agents): fixed issue with minimax-m2.5 is not work for bailian-coding-plan (#12023) 2026-03-02 18:27:25 +08:00
KOMATA 1e8836ad95 feat: add passkey prerequisites messages and update passkey settings UI (#12018)
* feat: add passkey prerequisites messages and update passkey settings UI

* refactor: enhance passkey settings UI by restructuring alert component and adding prerequisite checks
2026-03-02 16:05:55 +08:00
CityFun d155217ce3 feat(ai-agents): Add copaw type for agent (#12017) 2026-03-02 06:53:58 +00:00
ssongliu 9ddb996a44 fix: fix menu sort mismatch between load and migration (#12016) 2026-03-02 06:25:58 +00:00
ssongliu 226eccf5f1 fix: fix terminal extra line after settings change (#12015) 2026-03-02 06:24:00 +00:00
CityFun 4b2d6f0ef2 feat(ai-agents): add bailian-coding-plan account support (#12014) 2026-03-02 14:22:09 +08:00
蘭 f1c9ad45a7 feat: Improve file decompression processing (#12012) 2026-03-02 05:53:58 +00:00
KOMATA 93489c85fa feat: Enhance compose item interaction and styling (#12008)
- Updated the compose item to include a new class for active state, improving visual feedback.
- Refactored action buttons into a dedicated container for better organization and hover effects.
- Added CSS transitions for smoother interactions on hover and active states.
2026-03-02 13:43:34 +08:00
CityFun 8cead06d7b feat(ai-agents): add anthropic-messages support for custom account apiType (#12013) 2026-03-02 13:43:06 +08:00
ssongliu 625cd9f32f chore: update installation package path (#12009) 2026-03-02 04:05:59 +00:00
dependabot[bot] 9aab3b42af chore(deps): bump go.opentelemetry.io/otel/sdk in /agent (#12010)
Bumps [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go) from 1.38.0 to 1.40.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.38.0...v1.40.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/sdk
  dependency-version: 1.40.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-02 12:04:30 +08:00
A_Words 6684cd57d7 fix: open compressed files in decompress drawer and improve archive detection (#11997) 2026-03-02 11:29:19 +08:00
A_Words 00d95d6e19 fix(mkdown-editor): avoid pre-sanitizing markdown source (#11998) (#12004) 2026-03-02 10:54:25 +08:00
ssongliu 36f03e5daf chore: refine partial i18n copy (#11992) 2026-02-28 15:01:54 +00:00
ssongliu 7c395a6bf8 fix: resolve GPU menu selected state style issue 2026-02-28 18:58:18 +08:00
zhengkunwang223 e9a7cac5fd fix(frontend): change table style 2026-02-28 18:58:06 +08:00
zhengkunwang223 9f00b431d4 fix(frontend): stabilize installed app card name truncation 2026-02-28 18:35:34 +08:00
maninhill 1705d44378 Update README.md 2026-02-28 17:54:03 +08:00
CityFun 537e2c730c feat: preserve Feishu account map on config updates (#11990) 2026-02-28 09:19:52 +00:00
ssongliu b4dc6b1ab0 chore: optimize runtime region description (#11989) 2026-02-28 09:17:53 +00:00
CityFun 1f22285cf4 chore: refine other language i18n wording (#11988) 2026-02-28 16:41:28 +08:00
A_Words 1d546236e4 fix: restore subsite nginx config on start to avoid stale stop page (#11978) 2026-02-28 15:25:18 +08:00
Kadxy 982e0bbd3e feat: add copy connection URL button to database connection info drawers (#11981)
Add a copy connection URL button to the MySQL, PostgreSQL, and Redis connection info drawers, allowing users to one-click copy the full connection URI for both container and remote connections.
2026-02-28 15:13:58 +08:00
CityFun 5a67a62733 chore: refine en and zh-Hant i18n wording (#11987) 2026-02-28 07:11:52 +00:00
ssongliu 961ebd30a1 fix: resolve overseas script repository sync failure (#11985) 2026-02-28 10:38:35 +08:00
ssongliu 9819546694 chore: optimize internationalization content (#11980) 2026-02-27 14:57:50 +00:00
ssongliu 5c8d391a73 fix: resolve partial application restore failure (#11979)
Refs #11716
2026-02-27 14:43:50 +00:00
CityFun 0e84b9a2a7 feat: fix error after deleting website deployed via deployment for "openclaw" (#11975) 2026-02-27 17:54:19 +08:00
王贺 1f58da501e ci: update build workflow (#11976) 2026-02-27 17:53:52 +08:00
ssongliu 0041184a3e fix: resolve version synchronization issue (#11971) 2026-02-27 07:17:51 +00:00
ssongliu 112468949d feat: Update sqlite connection settings (#11969) 2026-02-27 15:16:59 +08:00
KOMATA 7e03e94460 chore: remove stylelint configuration and ignore files, update lint-staged for stylelint removal (#11968) 2026-02-27 15:08:37 +08:00
ssongliu fd9b299b14 chore: update Edition key (#11962) 2026-02-26 10:05:46 +00:00
ssongliu 37932d7fb2 chore: Complete system security upgrade (#11961) 2026-02-26 10:01:47 +00:00
CityFun f0f2087f54 feat: add browser config for Agent (#11958) 2026-02-26 09:59:48 +00:00
CityFun f3de09e5d7 feat: Add discord channel for Agent (#11950) 2026-02-25 13:59:46 +00:00
maninhill 1a8c1613a4 Update README to eliminate redundant text
Removed duplicate description of 1Panel.
2026-02-25 16:56:40 +08:00
KOMATA 5209275f28 feat: Implement caching for settings retrieval and update logic (#11886)
* feat: Implement caching for settings retrieval and update logic

- Introduced a caching mechanism for settings using go-cache to improve performance.
- Updated the Create, Update, and UpdateOrCreate methods to cache values after database operations.
- Modified the Get and GetValueByKey methods to utilize the cache for faster access.
- Adjusted middleware to use the new GetValueByKey method for retrieving settings, enhancing code consistency.

* feat: Enhance setting cache management with dynamic TTL

- Introduced a function to determine cache TTL based on setting keys, allowing critical settings to have shorter cache durations.
- Updated cache logic in Create, Update, Get, and GetValueByKey methods to utilize the new TTL management, improving performance and consistency in settings retrieval.

* refactor: Simplify setting cache TTL management

- Removed the dynamic TTL function and standardized the cache TTL to a fixed duration for all settings.
- Updated cache logic in Create, Update, Get, and GetValueByKey methods to use the new fixed TTL, enhancing code clarity and maintainability.
- Added a new function to restart the core service after resetting settings, improving the reset command's functionality.

* refactor: Remove core restart functionality from reset commands

- Eliminated the restartCoreAfterReset function to simplify the reset command logic.
- Updated reset commands to return nil after setting changes, enhancing clarity and reducing unnecessary complexity.
2026-02-25 16:40:20 +08:00
ssongliu 924e59d948 fix: Fix login log recording abnormality in some scenarios (#11948) 2026-02-25 08:39:46 +00:00
CityFun 87a39cab3a feat: Add telegram channel for Agent (#11947) 2026-02-25 16:38:34 +08:00
CityFun f9a70757a1 feat: add rememberApiKey for AgentAccount (#11945) 2026-02-25 05:53:45 +00:00
ssongliu 741acee393 fix: Modify command invocation method (#11944) 2026-02-25 05:51:44 +00:00
dependabot[bot] c964a02088 chore(deps): bump filippo.io/edwards25519 from 1.1.0 to 1.1.1 in /agent (#11946)
Bumps [filippo.io/edwards25519](https://github.com/FiloSottile/edwards25519) from 1.1.0 to 1.1.1.
- [Commits](https://github.com/FiloSottile/edwards25519/compare/v1.1.0...v1.1.1)

---
updated-dependencies:
- dependency-name: filippo.io/edwards25519
  dependency-version: 1.1.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-25 11:58:33 +08:00
ssongliu ba71613ae6 fix: Fix docker compose parsing failure issue (#11942)
Refs #11934
2026-02-25 03:51:44 +00:00
ssongliu 5493b41381 fix: Modify monitoring interval prompt information (#11940)
Refs #11918
2026-02-25 02:57:47 +00:00
ssongliu c3f45d4787 feat: v2 supports international version (#11939) 2026-02-25 10:56:06 +08:00
rowanchen-com a8a92da483 Added the ability to sort installed applications. (#11936) 2026-02-25 10:35:29 +08:00
CityFun a77e61a861 feat: Agent Model Account support custom config (#11933)
* feat: Agent Model Account support custom config

* feat: Add agent gatewayControlUi config
2026-02-24 18:26:02 +08:00
kgbow e589728987 refactor(website-ssl): improve API flexibility for WebsiteSSLSearch validation (#11926) 2026-02-24 17:30:37 +08:00
maninhill cc3cdef5f3 Revise 1Panel description and add feature highlights
Updated the description of 1Panel to clarify its purpose and features.
2026-02-24 17:28:14 +08:00
A_Words 7101af0594 fix: Allow wildcard domains in domainOrIP validator for SSL form (#11923) 2026-02-24 17:22:30 +08:00
KOMATA df6cda7143 chore: change esbuild to native version (#11904)
* chore: change esbuild to native version

* chore: add @parcel/watcher and esbuild to trustedDependencies
2026-02-24 17:21:20 +08:00
KOMATA 62beb534d2 feat: enhance passkey function 2026-02-24 17:19:13 +08:00
adriandadri 9a70bf1c2d feat: Add support for Technitium DNS (#11896)
Co-authored-by: adri <adri@digitalunited.net>
2026-02-24 17:13:59 +08:00
KOMATA 6f4559c7cd style: Update Prettier configuration and adjust SCSS formatting (#11885)
- Added iconfont directory to .prettierignore
- Removed jsxBracketSameLine option from .prettierrc.js
- Fixed indentation in common.scss and reset.scss files
2026-02-24 17:03:20 +08:00
KOMATA 8b2bcdc814 feat: Enhance domain handling in website creation (#11884)
* feat: Enhance domain handling in website creation

* fix: Improve domain trimming and validation in website alias assignment
2026-02-24 16:58:30 +08:00
maninhill 0f6ddf26fd Refine README.md content for conciseness
Removed redundant phrases for clarity.
2026-02-24 14:11:31 +08:00
王贺 ebe472a9db feat(i18n): refine translations (#11931) 2026-02-24 13:42:10 +08:00
ssongliu c1a87baad7 feat: Terminal supports foreground and background colors (#11929) 2026-02-24 13:39:02 +08:00
Scars c7f185a184 feat: Terminal supports custom fonts (#11889) 2026-02-13 18:15:12 +08:00
蘭 4ca2ba5482 feat: File editing supports last opened file (#11893)
#11572
2026-02-13 10:01:14 +00:00
蘭 727b74895d ref: Adjust the column widths of the file system (#11887)
#11699
2026-02-13 09:59:13 +00:00
284 changed files with 20764 additions and 7423 deletions
@@ -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
+10 -20
View File
@@ -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
![UI Display](https://resource.1panel.pro/img/1panel.png)
![UI Display](https://resource.1panel.pro/img/overview_en_v2.png)
## Star History
+267 -1
View File
@@ -113,7 +113,7 @@ func (b *BaseApi) UpdateAgentModelConfig(c *gin.Context) {
// @Tags AI
// @Summary Get Providers
// @Success 200 {object} []dto.ProviderInfo
// @Success 200 {array} dto.ProviderInfo
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/agents/providers [get]
@@ -271,6 +271,252 @@ func (b *BaseApi) UpdateAgentFeishuConfig(c *gin.Context) {
helper.Success(c)
}
// @Tags AI
// @Summary Get Agent Telegram channel config
// @Accept json
// @Param request body dto.AgentTelegramConfigReq true "request"
// @Success 200 {object} dto.AgentTelegramConfig
// @Security ApiKeyAuth
// @Security Timestamp
// @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
}
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.AgentDiscordConfigReq 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.AgentDiscordConfigReq
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.AgentQQBotConfigReq 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.AgentQQBotConfigReq
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 Browser config
// @Accept json
// @Param request body dto.AgentBrowserConfigReq true "request"
// @Success 200 {object} dto.AgentBrowserConfig
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/agents/browser/get [post]
func (b *BaseApi) GetAgentBrowserConfig(c *gin.Context) {
var req dto.AgentBrowserConfigReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := agentService.GetBrowserConfig(req)
if err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags AI
// @Summary Update Agent Browser config
// @Accept json
// @Param request body dto.AgentBrowserConfigUpdateReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/agents/browser/update [post]
func (b *BaseApi) UpdateAgentBrowserConfig(c *gin.Context) {
var req dto.AgentBrowserConfigUpdateReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := agentService.UpdateBrowserConfig(req); err != nil {
helper.BadRequest(c, err)
return
}
helper.Success(c)
}
// @Tags AI
// @Summary Get Agent Other config
// @Accept json
// @Param request body dto.AgentOtherConfigReq 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.AgentOtherConfigReq
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 Approve Agent Feishu pairing code
// @Accept json
@@ -290,3 +536,23 @@ func (b *BaseApi) ApproveAgentFeishuPairing(c *gin.Context) {
}
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
}
helper.Success(c)
}
+13 -1
View File
@@ -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)
}
-21
View File
@@ -306,27 +306,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
+2 -2
View File
@@ -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]
+1 -1
View File
@@ -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]
+46 -2
View File
@@ -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 {
+152 -35
View File
@@ -6,9 +6,10 @@ 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"`
BridgePort int `json:"bridgePort"`
AgentType string `json:"agentType"`
Provider string `json:"provider"`
Model string `json:"model"`
APIType string `json:"apiType"`
MaxTokens int `json:"maxTokens"`
ContextWindow int `json:"contextWindow"`
@@ -33,6 +34,7 @@ type AgentCreateReq struct {
type AgentItem struct {
ID uint `json:"id"`
Name string `json:"name"`
AgentType string `json:"agentType"`
Provider string `json:"provider"`
ProviderName string `json:"providerName"`
Model string `json:"model"`
@@ -73,28 +75,30 @@ type AgentModelConfigUpdateReq struct {
}
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"`
Model string `json:"model"`
APIType string `json:"apiType"`
MaxTokens int `json:"maxTokens"`
ContextWindow int `json:"contextWindow"`
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"`
Model string `json:"model"`
APIType string `json:"apiType"`
MaxTokens int `json:"maxTokens"`
ContextWindow int `json:"contextWindow"`
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"`
Model string `json:"model"`
APIType string `json:"apiType"`
MaxTokens int `json:"maxTokens"`
ContextWindow int `json:"contextWindow"`
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"`
Model string `json:"model"`
APIType string `json:"apiType"`
MaxTokens int `json:"maxTokens"`
ContextWindow int `json:"contextWindow"`
Remark string `json:"remark"`
SyncAgents bool `json:"syncAgents"`
}
type AgentAccountVerifyReq struct {
@@ -114,19 +118,20 @@ 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"`
Model string `json:"model"`
APIType string `json:"apiType"`
MaxTokens int `json:"maxTokens"`
ContextWindow int `json:"contextWindow"`
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"`
Model string `json:"model"`
APIType string `json:"apiType"`
MaxTokens int `json:"maxTokens"`
ContextWindow int `json:"contextWindow"`
Verified bool `json:"verified"`
Remark string `json:"remark"`
CreatedAt time.Time `json:"createdAt"`
}
type ProviderModelInfo struct {
@@ -167,3 +172,115 @@ 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"`
PairingCode string `json:"pairingCode" validate:"required"`
}
type AgentQQBotConfigReq struct {
AgentID uint `json:"agentId" 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"`
TaskID string `json:"taskID" validate:"required"`
}
type AgentPluginCheckReq struct {
AgentID uint `json:"agentId" validate:"required"`
Type string `json:"type" validate:"required,oneof=qqbot"`
}
type AgentPluginStatus struct {
Installed bool `json:"installed"`
}
type AgentDiscordConfigReq struct {
AgentID uint `json:"agentId" validate:"required"`
}
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 AgentBrowserConfigReq struct {
AgentID uint `json:"agentId" validate:"required"`
}
type AgentBrowserConfigUpdateReq struct {
AgentID uint `json:"agentId" validate:"required"`
Enabled bool `json:"enabled"`
Headless bool `json:"headless"`
NoSandbox bool `json:"noSandbox"`
DefaultProfile string `json:"defaultProfile" validate:"required"`
}
type AgentBrowserConfig struct {
Enabled bool `json:"enabled"`
ExecutablePath string `json:"executablePath"`
Headless bool `json:"headless"`
NoSandbox bool `json:"noSandbox"`
DefaultProfile string `json:"defaultProfile"`
}
type AgentOtherConfigReq struct {
AgentID uint `json:"agentId" validate:"required"`
}
type AgentOtherConfigUpdateReq struct {
AgentID uint `json:"agentId" validate:"required"`
UserTimezone string `json:"userTimezone" validate:"required"`
}
type AgentOtherConfig struct {
UserTimezone string `json:"userTimezone"`
}
+6
View File
@@ -95,10 +95,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"`
+9
View File
@@ -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"`
}
+11
View File
@@ -272,6 +272,17 @@ type WebsiteProxyConfig struct {
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"`
+2 -2
View File
@@ -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 {
+1
View File
@@ -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"`
+1
View File
@@ -3,6 +3,7 @@ package model
type Agent struct {
BaseModel
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"`
+11 -10
View File
@@ -2,16 +2,17 @@ package model
type AgentAccount struct {
BaseModel
Provider string `json:"provider"`
Name string `json:"name"`
APIKey string `json:"apiKey"`
BaseURL string `json:"baseUrl"`
Model string `json:"model"`
APIType string `json:"apiType"`
MaxTokens int `json:"maxTokens"`
ContextWindow int `json:"contextWindow"`
Verified bool `json:"verified"`
Remark string `json:"remark"`
Provider string `json:"provider"`
Name string `json:"name"`
APIKey string `json:"apiKey"`
BaseURL string `json:"baseUrl"`
Model string `json:"model"`
APIType string `json:"apiType"`
MaxTokens int `json:"maxTokens"`
ContextWindow int `json:"contextWindow"`
RememberAPIKey bool `json:"rememberApiKey"`
Verified bool `json:"verified"`
Remark string `json:"remark"`
}
func (AgentAccount) TableName() string {
+1
View File
@@ -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"`
}
+117 -56
View File
@@ -20,16 +20,34 @@ type Meta struct {
}
var catalog = map[string]Meta{
"custom": {
Key: "custom",
DisplayName: "Custom",
Sort: 10,
DefaultBaseURL: "",
EnvKey: "CUSTOM_API_KEY",
Enabled: true,
Models: []Model{},
},
"ollama": {
Key: "ollama",
DisplayName: "Ollama",
Sort: 10,
Sort: 15,
Enabled: true,
},
"vllm": {
Key: "vllm",
DisplayName: "vLLM",
Sort: 20,
DefaultBaseURL: "",
EnvKey: "VLLM_API_KEY",
Enabled: true,
Models: []Model{},
},
"deepseek": {
Key: "deepseek",
DisplayName: "DeepSeek",
Sort: 20,
Sort: 25,
DefaultBaseURL: "https://api.deepseek.com/v1",
EnvKey: "DEEPSEEK_API_KEY",
Enabled: true,
@@ -39,19 +57,94 @@ var catalog = map[string]Meta{
{ID: "deepseek/deepseek-r1:1.5b", Name: "DeepSeek R1 1.5B"},
},
},
"custom": {
Key: "custom",
DisplayName: "Custom",
"bailian-coding-plan": {
Key: "bailian-coding-plan",
DisplayName: "阿里云百炼 Coding Plan",
Sort: 30,
DefaultBaseURL: "",
EnvKey: "CUSTOM_API_KEY",
DefaultBaseURL: "https://coding.dashscope.aliyuncs.com/v1",
EnvKey: "QWEN_API_KEY",
Enabled: true,
Models: []Model{},
Models: []Model{
{ID: "bailian-coding-plan/qwen3.5-plus", Name: "Qwen3.5-Plus"},
{ID: "bailian-coding-plan/qwen3-max", Name: "Qwen3-Max"},
{ID: "bailian-coding-plan/qwen3-coder-next", Name: "Qwen3-Coder-Next"},
{ID: "bailian-coding-plan/qwen3-coder-plus", Name: "Qwen3-Coder-Plus"},
{ID: "bailian-coding-plan/minimax-m2.5", Name: "MiniMax M2.5"},
{ID: "bailian-coding-plan/glm-5", Name: "GLM-5"},
{ID: "bailian-coding-plan/kimi-k2.5", Name: "Kimi-k2.5"},
{ID: "bailian-coding-plan/glm-4.7", Name: "GLM-4.7"},
},
},
"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",
Enabled: true,
Models: []Model{
{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"},
{ID: "ark-coding-plan/glm-4.7", Name: "GLM-4.7"},
{ID: "ark-coding-plan/deepseek-v3.2", Name: "DeepSeek-V3.2"},
{ID: "ark-coding-plan/kimi-k2-thinking", Name: "Kimi-K2-thinking"},
},
},
"zai": {
Key: "zai",
DisplayName: "Z.ai",
Sort: 40,
DefaultBaseURL: "https://open.bigmodel.cn/api/paas/v4",
EnvKey: "ZAI_API_KEY",
Enabled: true,
Models: []Model{
{ID: "zai/glm-5", Name: "GLM-5"},
{ID: "zai/glm-4.7", Name: "GLM-4.7"},
{ID: "zai/glm-4.7-flash", Name: "GLM-4.7-Flash"},
{ID: "zai/glm-4.7-flashx", Name: "GLM-4.7-FlashX"},
},
},
"minimax": {
Key: "minimax",
DisplayName: "MiniMax (CN)",
Sort: 45,
DefaultBaseURL: "https://api.minimaxi.com/anthropic",
EnvKey: "MINIMAX_API_KEY",
Enabled: true,
Models: []Model{
{ID: "minimax/MiniMax-M2.1", Name: "MiniMax M2.1"},
{ID: "minimax/MiniMax-M2.1-lightning", Name: "MiniMax M2.1 Lightning"},
},
},
"kimi": {
Key: "kimi",
DisplayName: "Kimi (CN)",
Sort: 50,
DefaultBaseURL: "https://api.moonshot.cn/v1",
EnvKey: "KIMI_API_KEY",
Enabled: true,
Models: []Model{
{ID: "kimi/kimi-k2.5", Name: "Kimi K2.5"},
{ID: "kimi/kimi-k2-0905-preview", Name: "Kimi K2 0905 Preview"},
{ID: "kimi/kimi-k2-thinking", Name: "Kimi K2 Thinking"},
},
},
"kimi-coding": {
Key: "kimi-coding",
DisplayName: "Kimi Coding",
Sort: 51,
DefaultBaseURL: "https://api.kimi.com/coding/",
EnvKey: "KIMI_API_KEY",
Enabled: true,
Models: []Model{
{ID: "kimi-coding/k2p5", Name: "Kimi K2.5"},
},
},
"openai": {
Key: "openai",
DisplayName: "OpenAI",
Sort: 40,
Sort: 55,
DefaultBaseURL: "https://api.openai.com/v1",
EnvKey: "OPENAI_API_KEY",
Enabled: true,
@@ -64,10 +157,22 @@ var catalog = map[string]Meta{
{ID: "openai/gpt-5-mini", Name: "GPT-5 Mini"},
},
},
"openrouter": {
Key: "openrouter",
DisplayName: "OpenRouter",
Sort: 56,
DefaultBaseURL: "https://openrouter.ai/api/v1",
EnvKey: "OPENROUTER_API_KEY",
Enabled: true,
Models: []Model{
{ID: "openrouter/free", Name: "openrouter/free"},
{ID: "openrouter/auto", Name: "openrouter/auto"},
},
},
"anthropic": {
Key: "anthropic",
DisplayName: "Anthropic",
Sort: 50,
Sort: 60,
DefaultBaseURL: "https://api.anthropic.com",
EnvKey: "ANTHROPIC_API_KEY",
Enabled: true,
@@ -82,7 +187,7 @@ var catalog = map[string]Meta{
"gemini": {
Key: "gemini",
DisplayName: "Gemini",
Sort: 60,
Sort: 65,
DefaultBaseURL: "https://generativelanguage.googleapis.com",
EnvKey: "GEMINI_API_KEY",
Enabled: true,
@@ -95,22 +200,10 @@ var catalog = map[string]Meta{
{ID: "google/gemini-3-flash-preview", Name: "Gemini 3 Flash Preview"},
},
},
"minimax": {
Key: "minimax",
DisplayName: "MiniMax (CN)",
Sort: 70,
DefaultBaseURL: "https://api.minimaxi.com/anthropic",
EnvKey: "MINIMAX_API_KEY",
Enabled: true,
Models: []Model{
{ID: "minimax/MiniMax-M2.1", Name: "MiniMax M2.1"},
{ID: "minimax/MiniMax-M2.1-lightning", Name: "MiniMax M2.1 Lightning"},
},
},
"moonshot": {
Key: "moonshot",
DisplayName: "Moonshot (Global)",
Sort: 80,
Sort: 70,
DefaultBaseURL: "https://api.moonshot.ai/v1",
EnvKey: "MOONSHOT_API_KEY",
Enabled: true,
@@ -120,38 +213,6 @@ var catalog = map[string]Meta{
{ID: "moonshot/kimi-k2-thinking", Name: "Kimi K2 Thinking"},
},
},
"kimi": {
Key: "kimi",
DisplayName: "Kimi (CN)",
Sort: 90,
DefaultBaseURL: "https://api.moonshot.cn/v1",
EnvKey: "KIMI_API_KEY",
Enabled: true,
Models: []Model{
{ID: "kimi/kimi-k2.5", Name: "Kimi K2.5"},
{ID: "kimi/kimi-k2-0905-preview", Name: "Kimi K2 0905 Preview"},
{ID: "kimi/kimi-k2-thinking", Name: "Kimi K2 Thinking"},
},
},
"kimi-coding": {
Key: "kimi-coding",
DisplayName: "Kimi Coding",
Sort: 100,
DefaultBaseURL: "https://api.moonshot.cn/anthropic/v1",
EnvKey: "KIMI_API_KEY",
Enabled: true,
Models: []Model{
{ID: "kimi-coding/k2p5", Name: "Kimi K2.5"},
},
},
"qwen": {
Key: "qwen",
DisplayName: "Qwen",
Sort: 110,
DefaultBaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
EnvKey: "QWEN_API_KEY",
Enabled: false,
},
}
func Get(key string) (Meta, bool) {
+1102 -134
View File
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -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,
}
+7 -3
View File
@@ -416,7 +416,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 +516,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
}
@@ -817,7 +821,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 +893,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")
+14
View File
@@ -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,6 +323,15 @@ 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 {
+175 -66
View File
@@ -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",
+2 -1
View File
@@ -393,7 +393,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)
}
@@ -1613,6 +1613,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),
}
+153 -39
View File
@@ -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
}
+19 -40
View File
@@ -43,7 +43,6 @@ import (
"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 +68,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)
@@ -104,7 +102,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 +300,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 +583,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 +1153,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
}
@@ -1488,6 +1460,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{
+5
View File
@@ -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))
+8 -8
View File
@@ -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)
+10 -9
View File
@@ -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 {
+10 -5
View File
@@ -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)
+4
View File
@@ -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")
+4 -3
View File
@@ -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{}
+3 -3
View File
@@ -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
}
+85 -30
View File
@@ -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
View File
@@ -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
+28 -8
View File
@@ -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 {
@@ -1325,7 +1325,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
@@ -1906,6 +1906,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)
@@ -2333,9 +2337,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
+9 -4
View File
@@ -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
}
+20 -3
View File
@@ -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)
+47 -17
View File
@@ -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
@@ -391,24 +398,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")
}
+12 -3
View File
@@ -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":
+25
View File
@@ -1048,6 +1048,31 @@ func opWebsite(website *model.Website, operate string) error {
proxy := fmt.Sprintf("http://%s", 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()
-5
View File
@@ -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
+6
View File
@@ -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
+1
View File
@@ -8,6 +8,7 @@ const (
AppOpenresty = "openresty"
AppOpenclaw = "openclaw"
AppCopaw = "copaw"
AppMysql = "mysql"
AppMariaDB = "mariadb"
AppPostgresql = "postgresql"
+13 -2
View File
@@ -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 {
+1 -7
View File
@@ -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
+19
View File
@@ -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"
}
+8 -7
View File
@@ -50,7 +50,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 +59,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 +94,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
@@ -236,14 +237,14 @@ require (
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 v1.40.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/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.5.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
+14 -15
View File
@@ -31,8 +31,8 @@ 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=
@@ -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=
@@ -1025,8 +1024,8 @@ go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.
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/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/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/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
@@ -1035,14 +1034,14 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+
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/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/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=
@@ -1308,8 +1307,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=
+286 -295
View File
@@ -1,115 +1,116 @@
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 }}'
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'
ErrAgentProviderMismatch: 'Agent provider mismatch'
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 +122,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 +167,157 @@ 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'
#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 +332,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 +341,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 +378,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 +416,119 @@ 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'
+166 -231
View File
@@ -1,30 +1,27 @@
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 }}'
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 +33,40 @@ 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'
ErrAgentProviderMismatch: 'Proveedor de agente no coincide'
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 +79,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 +122,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 +138,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 +152,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 +170,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 +185,79 @@ 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...'
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 +265,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 +276,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 +309,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 +323,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 +335,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 +369,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 +400,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 +408,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 +425,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 +433,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 +450,27 @@ 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'
#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"
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'
+161 -227
View File
@@ -1,30 +1,28 @@
ErrInvalidParams: 'リクエストパラメータエラー: {{ .detail }}'
ErrTokenParse: 'トークン生成エラー: {{ .detail }}'
ErrInitialPassword: '元のパスワードが正しくありません'
ErrInternalServer: '内部サーバーエラー: {{ .detail }}'
ErrRecordExist: 'レコードがすでに存在します'
ErrInvalidParams: '不正なリクエスト: {{ .detail }}'
ErrTokenParse: 'トークンエラー: {{ .detail }}'
ErrInitialPassword: '元のパスワードが違います'
ErrInternalServer: '内部エラー: {{ .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 +33,40 @@ 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: 'アカウント未検証です'
ErrAgentProviderMismatch: 'エージェントプロバイダが一致しません'
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 +79,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 +138,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 +155,6 @@ delete: '削除'
ErrDefaultWebsite: 'デフォルト Web サイトが既に設定されています。設定する前にキャンセルしてください!'
SSL: '証明書'
Set: '設定'
#ssl
ErrSSLCannotDelete: '{{ .name }} 証明書は Web サイトで使用されているため、削除できません'
ErrAccountCannotDelete: 'アカウントは証明書に関連付けられているため、削除できません'
ErrSSLApply: '証明書の更新は成功しましたが、openresty の再ロードに失敗しました。設定を確認してください。'
@@ -180,12 +165,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 +185,33 @@ 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: 'クリーンアップ中、しばらくお待ちください...'
ErrFileNotExist: '{{ .detail }} ファイルが存在しません。ソース ファイルの整合性を確認してください。'
ErrImageBuildErr: 'イメージのビルドに失敗しました'
ErrImageExist: "イメージはすでに存在します!イメージ名を変更してください。"
ErrImageExist: 'イメージはすでに存在します!イメージ名を変更してください。'
ErrDelWithWebsite: 'オペレーティング環境は既に Web サイトに関連付けられているため、削除できません'
ErrRuntimeStart: '起動に失敗しました'
ErrPackageJsonNotFound: 'package.json ファイルが存在しません'
@@ -242,55 +219,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 +265,7 @@ ErrRuleNotExist: 'ルールが存在しません'
ErrParseIP: 'IP 形式が間違っています'
ErrDefaultIP: 'デフォルトは予約名です。別の名前に変更してください'
ErrGroupInUse: 'IP グループはブラックリスト/ホワイトリストで使用されており、削除できません'
ErrIPGroupAclUse: "IPグループはウェブサイト {{ .name }} のカスタムルールで使用されているため、削除できません"
ErrIPGroupAclUse: 'IPグループはウェブサイト {{ .name }} のカスタムルールで使用されているため、削除できません'
ErrGroupExist: 'IP グループ名がすでに存在します'
ErrIPRange: 'IP 範囲が間違っています'
ErrIPExist: 'IP がすでに存在します'
@@ -309,10 +276,10 @@ xss: 'XSS'
phpExec: 'PHP スクリプトの実行'
oneWordTrojan: 'ワンワードトロイの木馬'
appFilter: '危険なディレクトリフィルタリングを適用する'
webshell: 'ウェブシェル'
webShell: 'ウェブシェル'
args: 'パラメータルール'
protocolFilter: 'プロトコルフィルタリング'
javaFileter: 'Java 危険ファイル フィルター'
javaFilter: 'Java 危険ファイル フィルター'
scannerFilter: 'スキャナ フィルタ'
escapeFilter: 'エスケープフィルター'
customRule: 'カスタムルール'
@@ -342,10 +309,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 +327,7 @@ TaskDelete: '削除'
TaskUpgrade: 'アップグレード'
TaskUpdate: '更新'
TaskRestart: '再起動'
TaskProtect: "保護"
TaskProtect: '保護'
TaskBackup: 'バックアップ'
TaskRecover: '回復'
TaskRollback: 'ロールバック'
@@ -369,10 +335,10 @@ TaskPull: 'プル'
TaskCommit: 'コミット'
TaskBuild: 'ビルド'
TaskPush: 'プッシュ'
TaskClean: "クリーンアップ"
TaskClean: 'クリーンアップ'
TaskHandle: '実行'
TaskImport: "インポート"
TaskExport: "エクスポート"
TaskImport: 'インポート'
TaskExport: 'エクスポート'
Website: 'ウェブサイト'
App: 'アプリケーション'
Runtime: 'ランタイム環境'
@@ -404,18 +370,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 +401,6 @@ SnapCompressSize: 'スナップショット ファイル サイズ {{ .name }}'
SnapUpload: 'スナップショット ファイルをアップロード'
SnapUploadTo: 'スナップショット ファイルを {{ .name }} にアップロードします'
SnapUploadRes: 'スナップショット ファイルを {{ .name }} にアップロードします'
SnapshotRecover: 'スナップショットの復元'
RecoverDownload: 'スナップショット ファイルをダウンロード'
Download: 'ダウンロード'
@@ -449,10 +408,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 +425,6 @@ RecoverDaemonJson: 'コンテナ構成 daemon.json ファイルを復元する'
RecoverDBData: 'データベース データを回復する'
RecoverBackups: 'ローカル バックアップ ディレクトリを復元する'
RecoverPanelData: 'リカバリデータディレクトリ'
#task - container
ContainerNewCliet: 'Docker クライアントを初期化する'
ContainerImagePull: 'コンテナ イメージ {{ .name }} をプルします'
ContainerRemoveOld: '元のコンテナ {{ .name }} を削除します'
ContainerImageCheck: 'イメージが正常にプルされているかどうかを確認します'
@@ -477,22 +433,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 +450,27 @@ websiteDir: 'ウェブサイトディレクトリ'
RecoverFailedStartRollBack: 'リカバリに失敗しました。ロールバックを開始します'
AppBackupFileIncomplete: 'バックアップ ファイルが不完全で、app.json または app.tar.gz ファイルが不足しています'
AppAttributesNotMatch: 'アプリケーションの種類または名前が一致しません'
#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 をインストールしてください"
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 をインストールしてください'
+172 -239
View File
@@ -1,31 +1,28 @@
ErrInvalidParams: '요청 매개변수 오류: {{ .detail }}'
ErrTokenParse: '토큰 생성 오류: {{ .detail }}'
ErrInitialPassword: '원래 비밀번호가 올바르지 않습니다'
ErrInternalServer: '내부 서버 오류: {{ .detail }}'
ErrRecordExist: '레코드가 이미 존재합니다'
ErrInvalidParams: '잘못된 요청: {{ .detail }}'
ErrTokenParse: '토큰 오류: {{ .detail }}'
ErrInitialPassword: '원래 비밀번호가 틀립니다'
ErrInternalServer: '서버 오류: {{ .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 +33,40 @@ 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: '에이전트 계정 미확인'
ErrAgentProviderMismatch: '에이전트 공급자 불일치'
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 +79,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 +138,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 +152,12 @@ pause: '일시 정지'
unpause: '재개'
remove: '삭제'
delete: '삭제'
ErrDefaultWebsite: '기본 웹사이트가 이미 설정되었습니다. 설정하기 전에 취소하세요!'
ErrDefaultWebsite: '기본 웹사이트가 이미 설정되었습니다. 설정하기 전에 취소하세요'
SSL: '인증서'
Set: '설정'
#SSL인증
ErrSSLCannotDelete: '{{ .name }} 인증서는 웹사이트에서 사용 중이므로 삭제할 수 없습니다.'
ErrAccountCannotDelete: '계정이 인증서와 연결되어 있어 삭제할 수 없습니다.'
ErrSSLApply: '인증서 갱신이 성공했으나, openresty 재로드에 실패했습니다. 구성을 확인해 주세요!'
ErrSSLApply: '인증서 갱신이 성공했으나, openresty 재로드에 실패했습니다. 구성을 확인해 주세요'
ErrEmailIsExist: '사서함이 이미 존재합니다'
ErrSSLKeyNotFound: '개인 키 파일이 존재하지 않습니다'
ErrSSLCertificateNotFound: '인증서 파일이 존재하지 않습니다'
@@ -181,12 +165,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 +185,79 @@ 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: '정리 중입니다. 잠시만 기다려주세요...'
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 +265,7 @@ ErrRuleNotExist: '규칙이 존재하지 않습니다'
ErrParseIP: '잘못된 IP 형식'
ErrDefaultIP: '기본값은 예약된 이름입니다. 다른 이름으로 변경해 주세요'
ErrGroupInUse: 'IP 그룹이 블랙리스트/화이트리스트에 사용 중이므로 삭제할 수 없습니다.'
ErrIPGroupAclUse: "IP 그룹은 웹사이트 {{ .name }} 의 사용자 정의 규칙에 사용되므로 삭제할 수 없습니다"
ErrIPGroupAclUse: 'IP 그룹은 웹사이트 {{ .name }} 의 사용자 정의 규칙에 사용되므로 삭제할 수 없습니다'
ErrGroupExist: 'IP 그룹 이름이 이미 존재합니다'
ErrIPRange: '잘못된 IP 범위'
ErrIPExist: 'IP가 이미 존재합니다'
@@ -310,10 +276,10 @@ xss: 'XSS'
phpExec: 'PHP 스크립트 실행'
oneWordTrojan: '한 단어 트로이'
appFilter: '위험한 디렉토리 필터링 적용'
webshell: '웹쉘'
webShell: '웹쉘'
args: '매개변수 규칙'
protocolFilter: '프로토콜 필터링'
javaFileter: 'Java 위험 파일 필터'
javaFilter: 'Java 위험 파일 필터'
scannerFilter: '스캐너 필터'
escapeFilter: '탈출 필터'
customRule: '사용자 정의 규칙'
@@ -343,10 +309,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 +327,7 @@ TaskDelete: '삭제'
TaskUpgrade: '업그레이드'
TaskUpdate: '업데이트'
TaskRestart: '다시 시작'
TaskProtect: "보호"
TaskProtect: '보호'
TaskBackup: '백업'
TaskRecover: '복구'
TaskRollback: '롤백'
@@ -370,10 +335,10 @@ TaskPull: '당기기'
TaskCommit: '커밋'
TaskBuild: '빌드'
TaskPush: '푸시'
TaskClean: "정리"
TaskClean: '정리'
TaskHandle: '실행'
TaskImport: "가져오기"
TaskExport: "내보내기"
TaskImport: '가져오기'
TaskExport: '내보내기'
Website: '웹사이트'
App: '애플리케이션'
Runtime: '런타임 환경'
@@ -393,7 +358,7 @@ Start: '시작'
Run: '시작'
Stop: '정지'
Image: '거울'
Compose : '오케스트레이션'
Compose: '오케스트레이션'
Container: '컨테이너'
AppLink: '연결된 애플리케이션'
EnableSSL: 'HTTPS 활성화'
@@ -405,18 +370,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 +401,6 @@ SnapCompressSize: '스냅샷 파일 크기 {{ .name }}'
SnapUpload: '스냅샷 파일 업로드'
SnapUploadTo: '스냅샷 파일을 {{ .name }}에 업로드'
SnapUploadRes: '스냅샷 파일을 {{ .name }}에 업로드'
SnapshotRecover: '스냅샷 복원'
RecoverDownload: '스냅샷 파일 다운로드'
Download: '다운로드'
@@ -450,10 +408,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 +425,6 @@ RecoverDaemonJson: '컨테이너 구성 daemon.json 파일 복원'
RecoverDBData: '데이터베이스 데이터 복구'
RecoverBackups: '로컬 백업 디렉토리 복원'
RecoverPanelData: '복구 데이터 디렉토리'
# 작업 - 컨테이너
ContainerNewCliet: 'Docker 클라이언트 초기화'
ContainerImagePull: '컨테이너 이미지 {{ .name }} 가져오기'
ContainerRemoveOld: '원래 컨테이너 {{ .name }} 제거'
ContainerImageCheck: '이미지가 정상적으로 당겨졌는지 확인'
@@ -478,22 +433,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 +450,27 @@ websiteDir: '웹사이트 디렉토리'
RecoverFailedStartRollBack: '복구 실패, 롤백 시작'
AppBackupFileIncomplete: '백업 파일이 불완전하며 app.json 또는 app.tar.gz 파일이 없습니다.'
AppAttributesNotMatch: '애플리케이션 유형 또는 이름이 일치하지 않습니다'
#알리다
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 를 설치하세요"
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 를 설치하세요'
+173 -240
View File
@@ -1,83 +1,72 @@
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 }}'
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'
ErrAgentProviderMismatch: 'Penyedia ejen tidak sepadan'
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 +74,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 +138,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 +152,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 +185,79 @@ 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...'
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 +265,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 +276,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 +309,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 +327,7 @@ TaskDelete: 'Padam'
TaskUpgrade: 'Naik taraf'
TaskUpdate: 'Kemas kini'
TaskRestart: 'Mulakan semula'
TaskProtect: "Lindungi"
TaskProtect: 'Lindungi'
TaskBackup: 'Sandaran'
TaskRecover: 'Pulihkan'
TaskRollback: 'Rollback'
@@ -370,10 +335,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 +370,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 +401,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 +408,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 +425,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 +433,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 +450,27 @@ 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'
#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"
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'
+178 -245
View File
@@ -1,34 +1,28 @@
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 }}'
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 +33,40 @@ 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'
ErrAgentProviderMismatch: 'Provedor de agente diferente'
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 +74,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 +138,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 +152,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 +169,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 +185,79 @@ 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...'
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 +265,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 +276,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 +309,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 +327,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 +370,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 +401,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 +408,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 +425,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 +433,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 +450,27 @@ 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'
#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"
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'
+169 -236
View File
@@ -1,34 +1,28 @@
ErrInvalidParams: 'Ошибка параметра запроса: {{ .detail }}'
ErrTokenParse: 'Ошибка генерации токена: {{ .detail }}'
ErrInitialPassword: 'Исходный пароль неверный'
ErrInternalServer: 'Внутренняя ошибка сервера: {{ .detail }}'
ErrInvalidParams: 'Неверный запрос: {{ .detail }}'
ErrTokenParse: 'Ошибка токена: {{ .detail }}'
ErrInitialPassword: 'Исходный пароль неверен'
ErrInternalServer: 'Внутренняя ошибка: {{ .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 +33,40 @@ 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: 'Аккаунт не подтверждён'
ErrAgentProviderMismatch: 'Провайдер не совпадает'
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 +79,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 +122,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 +138,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 +152,12 @@ pause: 'Приостановить'
unpause: 'Возобновить'
remove: 'Удалить'
delete: 'Удалить'
ErrDefaultWebsite: 'Веб-сайт по умолчанию уже установлен, отмените его перед настройкой!'
ErrDefaultWebsite: 'Веб-сайт по умолчанию уже установлен, отмените его перед настройкой'
SSL: 'Сертификат'
Set: 'Настройки'
#ssl
ErrSSLCannotDelete: 'Сертификат {{ .name }} используется веб-сайтом и не может быть удален'
ErrAccountCannotDelete: 'Учетная запись связана с сертификатом и не может быть удалена'
ErrSSLApply: 'Обновление сертификата прошло успешно, перезагрузка OpenResty не удалась, проверьте конфигурацию!'
ErrSSLApply: 'Обновление сертификата прошло успешно, перезагрузка OpenResty не удалась, проверьте конфигурацию'
ErrEmailIsExist: 'Почтовый ящик уже существует'
ErrSSLKeyNotFound: 'Файл закрытого ключа не существует'
ErrSSLCertificateNotFound: 'Файл сертификата не существует'
@@ -189,7 +170,7 @@ dnsAccount: 'DNS Авто'
dnsManual: 'Руководство по DNS'
http: 'HTTP'
ApplySSLFailed: 'Заявка на сертификат [{{ .domain }}] не выполнена, {{ .detail }}'
ApplySSLSuccess: 'Заявка на сертификат [{{ .domain }}] успешно подана! '
ApplySSLSuccess: 'Заявка на сертификат [{{ .domain }}] успешно подана'
DNSAccountName: 'DNS-аккаунт [{{ .name }}] поставщик [{{ .type }}]'
PushDirLog: 'Сертификат перемещен в каталог [{{ .path }}] {{ .status }}'
ErrDeleteCAWithSSL: 'Текущая организация имеет сертификат, который был выдан и не может быть удален.'
@@ -204,94 +185,79 @@ 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: 'Очистка выполняется, пожалуйста, подождите...'
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 +265,7 @@ ErrRuleNotExist: 'Правило не существует'
ErrParseIP: 'Неправильный формат IP'
ErrDefaultIP: 'по умолчанию это зарезервированное имя, пожалуйста, измените его на другое имя'
ErrGroupInUse: 'IP-группа используется черным/белым списком и не может быть удалена'
ErrIPGroupAclUse: "Группа IP используется пользовательскими правилами сайта {{ .name }}, невозможно удалить"
ErrIPGroupAclUse: 'Группа IP используется пользовательскими правилами сайта {{ .name }}, невозможно удалить'
ErrGroupExist: 'Имя группы IP уже существует'
ErrIPRange: 'Неверный диапазон IP-адресов'
ErrIPExist: 'IP-адрес уже существует'
@@ -310,10 +276,10 @@ xss: 'XSS'
phpExec: 'Выполнение PHP-скрипта'
oneWordTrojan: 'Троян из одного слова'
appFilter: 'Применить фильтрацию опасных каталогов'
webshell: 'Be6-оболочка'
webShell: 'Be6-оболочка'
args: 'Правила параметров'
protocolFilter: 'Фильтрация протоколов'
javaFileter: 'Фильтр опасных файлов Java'
javaFilter: 'Фильтр опасных файлов Java'
scannerFilter: 'Фильтр сканера'
escapeFilter: 'фильтр побега'
customRule: 'Пользовательское правило'
@@ -343,10 +309,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 +327,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 +370,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 +401,6 @@ SnapCompressSize: 'Размер файла снимка {{ .name }}'
SnapUpload: 'Загрузить файл снимка'
SnapUploadTo: 'Загрузить файл снимка в {{ .name }}'
SnapUploadRes: 'Загрузить файл снимка в {{ .name }}'
SnapshotRecover: 'Восстановление моментального снимка'
RecoverDownload: 'Загрузить файл снимка'
Download: 'Скачать'
@@ -450,10 +408,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 +425,6 @@ RecoverDaemonJson: 'Восстановить файл конфигурации
RecoverDBData: 'Восстановить данные базы данных'
RecoverBackups: 'Восстановить локальный каталог резервных копий'
RecoverPanelData: 'Каталог данных восстановления'
# задача - контейнер
ContainerNewCliet: 'Инициализировать Docker-клиент'
ContainerImagePull: 'Извлечь изображение контейнера {{ .name }}'
ContainerRemoveOld: 'Удалить исходный контейнер {{ .name }}'
ContainerImageCheck: 'Проверьте, нормально ли извлекается изображение'
@@ -478,22 +433,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 +450,27 @@ websiteDir: 'Каталог веб-сайтов'
RecoverFailedStartRollBack: 'Восстановление не удалось, начать откат'
AppBackupFileIncomplete: 'Файл резервной копии неполный и в нем отсутствуют файлы app.json или app.tar.gz'
AppAttributesNotMatch: 'Тип или имя приложения не совпадают'
#тревога
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"
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'
+171 -239
View File
@@ -1,34 +1,28 @@
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 }}'
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 +33,40 @@ 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ı'
ErrAgentProviderMismatch: 'Ajans sağlayıcısı eşleşmiyor'
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 +79,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 +138,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 +152,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 +165,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 +185,79 @@ 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...'
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 +265,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 +276,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 +309,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 +327,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 +335,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 +370,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 +401,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 +408,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 +425,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 +433,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 +450,27 @@ 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'
#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"
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'
+171 -237
View File
@@ -14,12 +14,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 +33,32 @@ 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: '帳號未驗證通過,請重試'
ErrAgentProviderMismatch: '帳號提供商不符,請重試'
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 +74,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 +185,87 @@ 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: '清理中,請稍候...'
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 +276,10 @@ xss: 'XSS'
phpExec: 'PHP 腳本執行'
oneWordTrojan: '一句話木馬'
appFilter: '套用危險目錄過濾'
webshell: 'Webshell'
webShell: 'WebShell'
args: '參數規則'
protocolFilter: '協定過濾'
javaFileter: 'Java 危險檔案過濾'
javaFilter: 'Java 危險檔案過濾'
scannerFilter: '掃描器過濾'
escapeFilter: '轉義過濾'
customRule: '自訂規則'
@@ -341,11 +308,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 +326,8 @@ TaskCreate: '建立'
TaskDelete: '刪除'
TaskUpgrade: '升級'
TaskUpdate: '更新'
TaskRestart: '重啟'
TaskProtect: "防護"
TaskRestart: '重新啟動'
TaskProtect: '防護'
TaskBackup: '備份'
TaskRecover: '復原'
TaskRollback: '回滾'
@@ -369,16 +335,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 +353,11 @@ FailedStatus: '{{ .name }} 失敗{{ .err }}'
HandleLink: '處理應用程式關聯'
HandleDatabaseApp: '處理應用程式參數'
ExecShell: '執行{{ .name }} 腳本'
PullImage: '拉取鏡像'
PullImage: '拉取映像'
Start: '開始'
Run: '啟動'
Stop: '停止'
Image: '鏡像'
Image: '映像'
Compose: '編排'
Container: '容器'
AppLink: '關聯應用程式'
@@ -404,28 +370,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 +401,6 @@ SnapCompressSize: '快照檔案大小{{ .name }}'
SnapUpload: '上傳快照檔案'
SnapUploadTo: '上傳快照檔案到{{ .name }}'
SnapUploadRes: '上傳快照檔案到{{ .name }}'
SnapshotRecover: '快照復原'
RecoverDownload: '下載快照檔案'
Download: '下載'
@@ -449,94 +408,69 @@ 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: '應用程式類型或名稱不一致'
#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"
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'
+100 -110
View File
@@ -1,30 +1,30 @@
ErrInvalidParams: "请求参数错误: {{ .detail }}"
ErrTokenParse: "Token 生成错误: {{ .detail }}"
ErrInvalidParams: "参数错误: {{ .detail }}"
ErrTokenParse: "Token 错误: {{ .detail }}"
ErrInitialPassword: "原密码错误"
ErrInternalServer: "服务内部错误: {{ .detail }}"
ErrInternalServer: "服务错误: {{ .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 +33,24 @@ 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: "账号未通过验证"
ErrAgentProviderMismatch: "账号提供商不匹配"
ErrAgentBaseURLRequired: "Base URL 不能为空"
ErrAgentApiKeyRequired: "API Key 不能为空"
ErrAgentComposeRequired: "自定义编排内容不能为空"
ErrAgentIDRequired: "智能体 ID 不能为空"
ErrAgentAccountIDRequired: "账号 ID 不能为空"
ErrAgentLimitReached: "社区版最多支持创建 {{ .max }} 个智能体,升级至专业版无数量限制"
#backup
Localhost: '本机'
@@ -60,15 +61,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 +87,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 +123,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 +167,14 @@ pause: '暂停'
unpause: '恢复'
remove: '删除'
delete: '删除'
ErrDefaultWebsite: "已经设置默认网站,请取消后再设置!"
ErrDefaultWebsite: "已设置默认网站,请先取消再设置"
SSL: '证书'
Set: '设置'
#ssl
ErrSSLCannotDelete: "{{ .name }} 证书正在被网站使用,无法删除"
ErrAccountCannotDelete: "账号关联证书,无法删除"
ErrSSLApply: "证书续签成功,openresty reload失败,请检查配置!"
ErrSSLApply: "证书续签成功,但 OpenResty reload 失败,请检查配置"
ErrEmailIsExist: '邮箱已存在'
ErrSSLKeyNotFound: '私钥文件不存在'
ErrSSLCertificateNotFound: '证书文件不存在'
@@ -181,18 +182,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 +202,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,7 +225,7 @@ ErrInUsed: "{{ .detail }} 正被使用,无法删除!"
ErrObjectInUsed: "该对象正被使用,无法删除!"
ErrObjectBeDependent: "该镜像正被其他镜像所依赖,无法删除!"
ErrPortRules: "端口数目不匹配,请重新输入!"
ErrPgImagePull: "镜像拉取超时,请配置镜像加速或手动拉取 {{ .name }} 镜像后重试"
ErrPgImagePull: "镜像拉取超时,请配置镜像加速或手动拉取 {{ .name }} 后重试"
PruneHelper: "本次清理 {{ .name }} 释放磁盘空间 {{ .size }}"
ImageRemoveHelper: "删除镜像 {{ .name }} ,释放磁盘空间 {{ .size }}"
BuildCache: "构建缓存"
@@ -241,7 +242,7 @@ ErrRuntimeStart: "启动失败"
ErrPackageJsonNotFound: "package.json 文件不存在"
ErrScriptsNotFound: "没有在 package.json 中找到 scripts 配置项"
ErrContainerNameNotFound: "无法获取容器名称,请检查 .env 文件"
ErrNodeModulesNotFound: "node_modules 文件夹不存在!请编辑运行环境或者等待运行环境启动成功"
ErrNodeModulesNotFound: "node_modules 文件夹不存在!请编辑运行环境或等待运行环境启动成功"
ErrContainerNameIsNull: "容器名称不存在"
ErrPHPPortIsDefault: "9000 端口为默认端口,请修改后重试"
ErrPHPRuntimePortFailed: "{{ .name }} 端口已被当前运行环境使用,请修改后重试"
@@ -268,16 +269,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 +311,10 @@ xss: 'XSS'
phpExec: 'PHP 脚本执行'
oneWordTrojan: '一句话木马'
appFilter: '应用危险目录过滤'
webshell: 'Webshell'
webShell: 'WebShell'
args: '参数规则'
protocolFilter: '协议过滤'
javaFileter: 'Java 危险文件过滤'
javaFilter: 'Java 危险文件过滤'
scannerFilter: '扫描器过滤'
escapeFilter: '转义过滤'
customRule: '自定义规则'
@@ -344,8 +345,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 +404,7 @@ TaskSync: "同步"
LocalApp: "本地应用"
SubTask: "子任务"
RuntimeExtension: "运行环境扩展"
TaskIsExecuting: "任务正在运行"
TaskIsExecuting: "任务运行中"
CustomAppstore: "自定义应用仓库"
TaskExec: "执行"
TaskBatch: "批量操作"
@@ -414,14 +415,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 +452,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 +471,6 @@ RecoverBackups: "恢复本地备份目录"
RecoverPanelData: "恢复数据目录"
# task - container
ContainerNewCliet: "初始化 Docker Client"
ContainerImagePull: "拉取容器镜像 {{ .name }}"
ContainerRemoveOld: "删除原容器 {{ .name }}"
ContainerImageCheck: "检查镜像是否正常拉取"
@@ -482,17 +482,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 +498,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 +510,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 +531,4 @@ DeviceIsMounted: "设备 {{ .name }} 已挂载,请先卸载"
PartitionDiskErr: "分区失败,{{ .err }}"
FormatDiskErr: "格式化磁盘失败,{{ .err }}"
MountDiskErr: "挂载磁盘失败,{{ .err }}"
UnMountDiskErr: "卸载磁盘失败,{{ .err }}"
XfsNotFound: "未检测到 xfs 文件系统,请先安装 xfsprogs"
+8 -1
View File
@@ -27,11 +27,12 @@ func Init() {
initDockerConf()
initAlertTask()
initMonitorDB()
}
func initGlobalData() {
settingRepo := repo.NewISettingRepo()
if _, err := settingRepo.Get(settingRepo.WithByKey("SystemStatus")); err != nil {
if _, err := settingRepo.GetValueByKey("SystemStatus"); err != nil {
_ = settingRepo.Create("SystemStatus", "Free")
}
if err := settingRepo.Update("SystemStatus", "Free"); err != nil {
@@ -42,6 +43,7 @@ func initGlobalData() {
_ = settingRepo.Update("SystemVersion", node.Version)
}
global.CONF.Base.Version = node.Version
global.CONF.Base.Edition, _ = settingRepo.GetValueByKey("Edition")
global.CONF.Base.EncryptKey, _ = settingRepo.GetValueByKey("EncryptKey")
}
@@ -151,3 +153,8 @@ func initDockerConf() {
func initAlertTask() {
service.NewIAlertTaskHelper().ResetTask()
}
func initMonitorDB() {
_ = global.MonitorDB.AutoMigrate(&model.MonitorBase{}, &model.MonitorNetwork{}, &model.MonitorGPU{}, &model.MonitorIO{})
_ = global.TaskDB.AutoMigrate(&model.Task{})
}
+2 -2
View File
@@ -105,7 +105,7 @@ func loadRestorePath(upgradeDir string) (string, error) {
}
func downloadLangFromRemote(fileOp files.FileOp) {
path := fmt.Sprintf("%s/language/lang.tar.gz", global.CONF.RemoteURL.RepoUrl)
path := fmt.Sprintf("%s/language/lang.tar.gz", global.RepoURL())
if err := fileOp.DownloadFile(path, "/usr/local/bin/lang.tar.gz"); err != nil {
global.LOG.Errorf("download lang.tar.gz failed, err: %v", err)
return
@@ -123,7 +123,7 @@ func downloadLangFromRemote(fileOp files.FileOp) {
}
func downloadGeoFromRemote(fileOp files.FileOp, targetPath string) {
_ = os.MkdirAll(path.Dir(targetPath), os.ModePerm)
pathItem := fmt.Sprintf("%s/geo/GeoIP.mmdb", global.CONF.RemoteURL.RepoUrl)
pathItem := fmt.Sprintf("%s/geo/GeoIP.mmdb", global.RepoURL())
if err := fileOp.DownloadFile(pathItem, targetPath); err != nil {
global.LOG.Errorf("download geo ip failed, err: %v", err)
return
+6
View File
@@ -66,6 +66,12 @@ func InitAgentDB() {
migrations.AddAgentTables,
migrations.MigrateOpenclawAgents,
migrations.AddAgentCustomModelFields,
migrations.AddAppInstallSortOrder,
migrations.AddAgentAccountRememberAPIKey,
migrations.AddEditionSetting,
migrations.AddAgentTypeForAgents,
migrations.NormalizeAgentAccountVerifiedStatus,
migrations.NormalizeOllamaAccountAPIType,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)
+78 -1
View File
@@ -128,7 +128,8 @@ var InitSetting = &gormigrate.Migration{
if err := tx.Create(&model.Setting{Key: "SystemStatus", Value: "Free"}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "Language", Value: "zh"}).Error; err != nil {
lang := common.LoadParamsWithoutPanic("LANGUAGE")
if err := tx.Create(&model.Setting{Key: "Language", Value: lang}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "SystemIP", Value: ""}).Error; err != nil {
@@ -957,3 +958,79 @@ var AddAgentCustomModelFields = &gormigrate.Migration{
return nil
},
}
var AddAppInstallSortOrder = &gormigrate.Migration{
ID: "20260222-add-app-install-sort-order",
Migrate: func(tx *gorm.DB) error {
return tx.AutoMigrate(&model.AppInstall{})
},
}
var AddAgentAccountRememberAPIKey = &gormigrate.Migration{
ID: "20260225-add-agent-account-remember-api-key",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.AgentAccount{}); err != nil {
return err
}
return tx.Model(&model.AgentAccount{}).Where("remember_api_key IS NULL").Update("remember_api_key", true).Error
},
}
var AddEditionSetting = &gormigrate.Migration{
ID: "20260224-add-edition-setting",
Migrate: func(tx *gorm.DB) error {
var setting model.Setting
edition := common.LoadParamsWithoutPanic("PANEL_EDITION")
if err := tx.Where("key = ?", "Edition").First(&setting).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return tx.Create(&model.Setting{Key: "Edition", Value: edition}).Error
}
return err
}
if setting.Value == "" {
return tx.Model(&model.Setting{}).Where("key = ?", "Edition").Update("value", edition).Error
}
return nil
},
}
var AddAgentTypeForAgents = &gormigrate.Migration{
ID: "20260302-add-agent-type-for-agents",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.Agent{}); err != nil {
return err
}
if err := tx.Model(&model.Agent{}).Where("agent_type = '' OR agent_type IS NULL").Update("agent_type", constant.AppOpenclaw).Error; err != nil {
return err
}
return tx.Exec(
"UPDATE agents SET agent_type = ? WHERE app_install_id IN (SELECT ai.id FROM app_installs ai JOIN apps a ON ai.app_id = a.id WHERE a.key = ?)",
constant.AppCopaw,
constant.AppCopaw,
).Error
},
}
var NormalizeAgentAccountVerifiedStatus = &gormigrate.Migration{
ID: "20260303-normalize-agent-account-verified-status",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.AgentAccount{}); err != nil {
return err
}
return tx.Model(&model.AgentAccount{}).
Where("provider IN ?", []string{"custom", "ollama", "kimi-coding"}).
Update("verified", false).Error
},
}
var NormalizeOllamaAccountAPIType = &gormigrate.Migration{
ID: "20260304-normalize-ollama-account-api-type",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.AgentAccount{}); err != nil {
return err
}
return tx.Model(&model.AgentAccount{}).
Where("provider = ?", "ollama").
Update("api_type", "openai-responses").Error
},
}
+1
View File
@@ -21,6 +21,7 @@ func Routers() *gin.Engine {
if !global.IsMaster {
PrivateGroup.Use(middleware.Certificate())
}
PrivateGroup.Use(middleware.OperationResolveMeta())
for _, router := range rou.RouterGroupApp {
router.InitRouter(PrivateGroup)
}
+206
View File
@@ -0,0 +1,206 @@
package middleware
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"mime"
"mime/multipart"
"net/http"
"path"
"strings"
"sync"
"time"
"github.com/1Panel-dev/1Panel/agent/cmd/server/docs"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/re"
"github.com/gin-gonic/gin"
"github.com/glebarez/sqlite"
"gorm.io/gorm"
)
const (
headerNeedOperationResolve = "X-Need-Op-Resolve"
headerOperationResolved = "X-Op-Resolved"
)
var (
logMetaOnce sync.Once
logMetaData map[string]operationMeta
logMetaLoadErr error
)
type operationMeta struct {
BodyKeys []string `json:"bodyKeys"`
BeforeFunctions []functionInfo `json:"beforeFunctions"`
}
type functionInfo struct {
InputColumn string `json:"input_column"`
InputValue string `json:"input_value"`
IsList bool `json:"isList"`
DB string `json:"db"`
OutputColumn string `json:"output_column"`
OutputValue string `json:"output_value"`
}
func OperationResolveMeta() gin.HandlerFunc {
return func(c *gin.Context) {
if c.GetHeader(headerNeedOperationResolve) != "1" {
c.Next()
return
}
metaMap, err := loadOperationMeta()
if err != nil {
c.Next()
return
}
reqPath := strings.TrimPrefix(c.Request.URL.Path, "/api/v2")
meta, ok := metaMap[reqPath]
if !ok || len(meta.BeforeFunctions) == 0 {
c.Next()
return
}
values := make(map[string]interface{})
if len(meta.BodyKeys) > 0 {
body, err := io.ReadAll(c.Request.Body)
if err == nil {
c.Request.Body = io.NopCloser(bytes.NewBuffer(body))
bodyMap := make(map[string]interface{})
if strings.Contains(c.Request.Header.Get("Content-Type"), "multipart/form-data") {
bodyMap, _ = parseMultipart(body, c.Request.Header.Get("Content-Type"))
} else {
_ = json.Unmarshal(body, &bodyMap)
}
for _, key := range meta.BodyKeys {
if value, ok := bodyMap[key]; ok {
values[key] = value
}
}
}
}
resolved, err := resolveOperationValues(reqPath, values, meta.BeforeFunctions)
if err == nil && len(resolved) > 0 {
if data, err := json.Marshal(resolved); err == nil {
c.Header(headerOperationResolved, base64.RawURLEncoding.EncodeToString(data))
}
}
c.Next()
}
}
func loadOperationMeta() (map[string]operationMeta, error) {
logMetaOnce.Do(func() {
logMetaData = make(map[string]operationMeta)
logMetaLoadErr = json.Unmarshal(docs.XLogJson, &logMetaData)
})
return logMetaData, logMetaLoadErr
}
func parseMultipart(formData []byte, contentType string) (map[string]interface{}, error) {
d, params, err := mime.ParseMediaType(contentType)
if err != nil || d != "multipart/form-data" {
return nil, http.ErrNotMultipart
}
boundary, ok := params["boundary"]
if !ok {
return nil, http.ErrMissingBoundary
}
reader := multipart.NewReader(bytes.NewReader(formData), boundary)
ret := make(map[string]interface{})
f, err := reader.ReadForm(32 << 20)
if err != nil {
return nil, err
}
for k, v := range f.Value {
if len(v) > 0 {
ret[k] = v[0]
}
}
for k, v := range f.File {
if len(v) > 0 {
ret[k] = v[0].Filename
}
}
return ret, nil
}
func resolveOperationValues(pathItem string, values map[string]interface{}, beforeFunctions []functionInfo) (map[string]string, error) {
dbItem, err := newResolveDB(pathItem)
if err != nil {
return nil, err
}
defer closeResolveDB(dbItem)
resolved := make(map[string]string)
for _, funcs := range beforeFunctions {
if !isSafeIdentifier(funcs.DB) || !isSafeIdentifier(funcs.InputColumn) || !isSafeIdentifier(funcs.OutputColumn) {
continue
}
for key, value := range values {
if funcs.InputValue != key {
continue
}
var names []string
if funcs.IsList {
sql := fmt.Sprintf("SELECT %s FROM %s where %s in (?);", funcs.OutputColumn, funcs.DB, funcs.InputColumn)
_ = dbItem.Raw(sql, value).Scan(&names)
} else {
sql := fmt.Sprintf("SELECT %s FROM %s where %s = ?;", funcs.OutputColumn, funcs.DB, funcs.InputColumn)
_ = dbItem.Raw(sql, value).Scan(&names)
}
outputValue := strings.Join(names, ",")
resolved[funcs.OutputValue] = outputValue
values[funcs.OutputValue] = outputValue
break
}
}
return resolved, nil
}
func newResolveDB(pathItem string) (*gorm.DB, error) {
dbFile := ""
switch {
case strings.HasPrefix(pathItem, "/core"):
dbFile = path.Join(global.CONF.Base.InstallDir, "1panel/db/core.db")
case strings.HasPrefix(pathItem, "/xpack"):
dbFile = path.Join(global.CONF.Base.InstallDir, "1panel/db/xpack.db")
default:
dbFile = path.Join(global.CONF.Base.InstallDir, "1panel/db/agent.db")
}
db, err := gorm.Open(sqlite.Open(dbFile), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
})
if err != nil {
return nil, err
}
sqlDB, err := db.DB()
if err != nil {
return nil, err
}
sqlDB.SetMaxOpenConns(4)
sqlDB.SetMaxIdleConns(1)
sqlDB.SetConnMaxIdleTime(15 * time.Minute)
sqlDB.SetConnMaxLifetime(time.Hour)
return db, nil
}
func closeResolveDB(db *gorm.DB) {
sqlDB, err := db.DB()
if err != nil {
return
}
_ = sqlDB.Close()
}
func isSafeIdentifier(val string) bool {
return re.GetRegex(re.SQLIdentifierPattern).MatchString(val)
}
+13
View File
@@ -54,5 +54,18 @@ func (a *AIToolsRouter) InitRouter(Router *gin.RouterGroup) {
aiToolsRouter.POST("/agents/channel/feishu/get", baseApi.GetAgentFeishuConfig)
aiToolsRouter.POST("/agents/channel/feishu/update", baseApi.UpdateAgentFeishuConfig)
aiToolsRouter.POST("/agents/channel/feishu/approve", baseApi.ApproveAgentFeishuPairing)
aiToolsRouter.POST("/agents/channel/telegram/get", baseApi.GetAgentTelegramConfig)
aiToolsRouter.POST("/agents/channel/telegram/update", baseApi.UpdateAgentTelegramConfig)
aiToolsRouter.POST("/agents/channel/discord/get", baseApi.GetAgentDiscordConfig)
aiToolsRouter.POST("/agents/channel/discord/update", baseApi.UpdateAgentDiscordConfig)
aiToolsRouter.POST("/agents/channel/qqbot/get", baseApi.GetAgentQQBotConfig)
aiToolsRouter.POST("/agents/channel/qqbot/update", baseApi.UpdateAgentQQBotConfig)
aiToolsRouter.POST("/agents/plugin/install", baseApi.InstallAgentPlugin)
aiToolsRouter.POST("/agents/plugin/check", baseApi.CheckAgentPlugin)
aiToolsRouter.POST("/agents/browser/get", baseApi.GetAgentBrowserConfig)
aiToolsRouter.POST("/agents/browser/update", baseApi.UpdateAgentBrowserConfig)
aiToolsRouter.POST("/agents/other/get", baseApi.GetAgentOtherConfig)
aiToolsRouter.POST("/agents/other/update", baseApi.UpdateAgentOtherConfig)
aiToolsRouter.POST("/agents/channel/pairing/approve", baseApi.ApproveAgentChannelPairing)
}
}
+1
View File
@@ -40,6 +40,7 @@ func (a *AppRouter) InitRouter(Router *gin.RouterGroup) {
appRouter.POST("/installed/params/update", baseApi.UpdateInstalled)
appRouter.POST("/installed/update/versions", baseApi.GetUpdateVersions)
appRouter.POST("/installed/config/update", baseApi.UpdateAppConfig)
appRouter.POST("/installed/sort/update", baseApi.UpdateAppInstallSort)
appRouter.GET("/installed/info/:appInstallId", baseApi.GetAppInstallInfo)
appRouter.POST("/installed/ignore", baseApi.IgnoreAppUpgrade)
-1
View File
@@ -15,7 +15,6 @@ func (s *ContainerRouter) InitRouter(Router *gin.RouterGroup) {
baRouter.GET("/stats/:id", baseApi.ContainerStats)
baRouter.POST("", baseApi.ContainerCreate)
baRouter.POST("command", baseApi.ContainerCreateByCommand)
baRouter.POST("/update", baseApi.ContainerUpdate)
baRouter.POST("/upgrade", baseApi.ContainerUpgrade)
baRouter.POST("/info", baseApi.ContainerInfo)
+2
View File
@@ -54,6 +54,8 @@ func (a *WebsiteRouter) InitRouter(Router *gin.RouterGroup) {
websiteRouter.POST("/proxies", baseApi.GetProxyConfig)
websiteRouter.POST("/proxies/update", baseApi.UpdateProxyConfig)
websiteRouter.POST("/proxies/delete", baseApi.DeleteProxyConfig)
websiteRouter.POST("/proxies/status", baseApi.UpdateProxyConfigStatus)
websiteRouter.POST("/proxies/file", baseApi.UpdateProxyConfigFile)
websiteRouter.POST("/proxy/config", baseApi.UpdateProxyCache)
websiteRouter.GET("/proxy/config/:id", baseApi.GetProxyCache)
+3 -1
View File
@@ -15,6 +15,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/psutil"
"github.com/1Panel-dev/1Panel/agent/utils/re"
"github.com/jinzhu/copier"
"mime"
network "net"
"net/http"
"os"
@@ -71,13 +72,14 @@ func CreateEmailAlertLog(create dto.AlertLogCreate, alert dto.AlertDTO, params [
if username == "" {
username = emailInfo.Sender
}
encodedDisplayName := mime.BEncoding.Encode("UTF-8", emailInfo.DisplayName)
smtpConfig := email.SMTPConfig{
Host: emailInfo.Host,
Port: emailInfo.Port,
Sender: emailInfo.Sender,
Username: username,
Password: emailInfo.Password,
From: fmt.Sprintf(`"%s" <%s>`, emailInfo.DisplayName, emailInfo.Sender),
From: fmt.Sprintf(`"%s" <%s>`, encodedDisplayName, emailInfo.Sender),
Encryption: emailInfo.Encryption,
Recipient: emailInfo.Recipient,
}
+36 -16
View File
@@ -4,7 +4,7 @@ import (
"crypto/rand"
"fmt"
"io"
mathRand "math/rand"
"math/big"
"net"
"os/exec"
"reflect"
@@ -135,13 +135,6 @@ func isDigit(r rune) bool {
return r >= '0' && r <= '9'
}
func max(x, y int) int {
if x > y {
return x
}
return y
}
func GetSortedVersions(versions []string) []string {
sort.Slice(versions, func(i, j int) bool {
return CompareVersion(versions[i], versions[j])
@@ -169,21 +162,29 @@ var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456
func RandStr(n int) string {
b := make([]rune, n)
max := big.NewInt(int64(len(letters)))
for i := range b {
b[i] = letters[mathRand.Intn(len(letters))]
num, err := rand.Int(rand.Reader, max)
if err != nil {
return ""
}
b[i] = letters[num.Int64()]
}
return string(b)
}
func RandStrAndNum(n int) string {
source := mathRand.NewSource(time.Now().UnixNano())
randGen := mathRand.New(source)
const charset = "abcdefghijklmnopqrstuvwxyz0123456789"
b := make([]byte, n)
max := big.NewInt(int64(len(charset)))
for i := range b {
b[i] = charset[randGen.Intn(len(charset)-1)]
num, err := rand.Int(rand.Reader, max)
if err != nil {
return ""
}
b[i] = charset[num.Int64()]
}
return (string(b))
return string(b)
}
func ScanPort(port int) bool {
@@ -191,7 +192,7 @@ func ScanPort(port int) bool {
if err != nil {
return true
}
defer ln.Close()
defer func() { _ = ln.Close() }()
return false
}
@@ -200,7 +201,7 @@ func ScanUDPPort(port int) bool {
if err != nil {
return true
}
defer ln.Close()
defer func() { _ = ln.Close() }()
return false
}
@@ -225,7 +226,7 @@ func ScanPortWithIP(ip string, port int) bool {
}
return false
}
defer conn.Close()
defer func() { _ = conn.Close() }()
return true
}
@@ -419,3 +420,22 @@ func GetDockerComposeCommand() string {
}
return ""
}
func LoadParams(param string) string {
stdout, err := cmd.RunDefaultWithStdoutBashCf("grep '^%s=' /usr/local/bin/1pctl | cut -d'=' -f2", param)
if err != nil {
panic(err)
}
info := strings.ReplaceAll(stdout, "\n", "")
if len(info) == 0 || info == `""` {
panic(fmt.Sprintf("error `%s` find in /usr/local/bin/1pctl", param))
}
return info
}
func LoadParamsWithoutPanic(param string) string {
stdout, err := cmd.RunDefaultWithStdoutBashCf("grep '^%s=' /usr/local/bin/1pctl | cut -d'=' -f2", param)
if err != nil {
return ""
}
return strings.ReplaceAll(stdout, "\n", "")
}
+20 -5
View File
@@ -67,14 +67,29 @@ func GetDBWithPath(dbPath string) (*gorm.DB, error) {
if err != nil {
return nil, err
}
sqlDB, dbError := db.DB()
if dbError != nil {
return nil, dbError
sqlDB, err := db.DB()
if err != nil {
return nil, err
}
sqlDB.SetMaxOpenConns(4)
sqlDB.SetMaxIdleConns(1)
sqlDB.SetConnMaxIdleTime(15 * time.Minute)
sqlDB.SetConnMaxLifetime(time.Hour)
sqlDB.SetConnMaxLifetime(0)
sqlDB.SetConnMaxIdleTime(0)
if err := db.Exec("PRAGMA journal_mode = WAL;").Error; err != nil {
return nil, err
}
if err := db.Exec("PRAGMA synchronous = NORMAL;").Error; err != nil {
return nil, err
}
if err := db.Exec("PRAGMA busy_timeout = 5000;").Error; err != nil {
return nil, err
}
if err := db.Exec("PRAGMA temp_store = MEMORY;").Error; err != nil {
return nil, err
}
return db, nil
}
+101 -19
View File
@@ -1,6 +1,7 @@
package compose
import (
"encoding/json"
"errors"
"fmt"
"os"
@@ -15,7 +16,6 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/docker"
"gopkg.in/yaml.v3"
)
func checkCmd() error {
@@ -29,31 +29,37 @@ func checkCmd() error {
return nil
}
func getComposeBaseCmd() (string, []string) {
cmdStr := strings.TrimSpace(global.CONF.DockerConfig.Command)
parts := strings.Fields(cmdStr)
if len(parts) == 0 {
return "", nil
}
return parts[0], parts[1:]
}
func Up(filePath string) (string, error) {
if err := checkCmd(); err != nil {
return "", err
}
return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdoutBashCf("%s %s up -d", global.CONF.DockerConfig.Command, loadFiles(filePath))
base, extra := getComposeBaseCmd()
args := append(extra, strings.Fields(loadFiles(filePath))...)
args = append(args, "up", "-d")
return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdout(base, args...)
}
func UpWithTask(filePath string, task *task.Task, forcePull bool) error {
if err := pullComposeImages(filePath, forcePull, task); err != nil {
return err
}
return cmd.NewCommandMgr(cmd.WithTask(*task)).RunBashCf("%s %s up -d", global.CONF.DockerConfig.Command, loadFiles(filePath))
base, extra := getComposeBaseCmd()
args := append(extra, strings.Fields(loadFiles(filePath))...)
args = append(args, "up", "-d")
return cmd.NewCommandMgr(cmd.WithTask(*task)).Run(base, args...)
}
func pullComposeImages(filePath string, forcePull bool, task *task.Task) error {
content, err := os.ReadFile(filePath)
if err != nil {
return err
}
env, _ := os.ReadFile(path.Join(path.Dir(filePath), ".env"))
var compose docker.ComposeProject
if err := yaml.Unmarshal(content, &compose); err != nil {
return fmt.Errorf("parse docker-compose file failed: %v", err)
}
images, err := docker.GetImagesFromDockerCompose(env, content)
images, err := GetComposeImages(filePath)
if err != nil {
return err
}
@@ -110,32 +116,103 @@ func pullComposeImages(filePath string, forcePull bool, task *task.Task) error {
return nil
}
func GetComposeImages(filePath string) ([]string, error) {
images, err := getComposeImagesByCommand(filePath)
if err == nil {
return images, nil
}
content, readErr := os.ReadFile(filePath)
if readErr != nil {
return nil, readErr
}
env, _ := os.ReadFile(path.Join(path.Dir(filePath), ".env"))
images, parseErr := docker.GetImagesFromDockerCompose(env, content)
if parseErr != nil {
return nil, fmt.Errorf("get compose images failed, cmd err: %v, parse err: %v", err, parseErr)
}
return images, nil
}
func getComposeImagesByCommand(filePath string) ([]string, error) {
if err := checkCmd(); err != nil {
return nil, err
}
base, extra := getComposeBaseCmd()
args := append(extra, strings.Fields(loadFiles(filePath))...)
args = append(args, "config", "--format", "json", "--no-normalize")
stdout, err := cmd.NewCommandMgr(cmd.WithTimeout(5*time.Minute)).
RunWithStdout(base, args...)
if err != nil {
return nil, fmt.Errorf("run compose config --format json --no-normalize failed, std: %s, err: %v", stdout, err)
}
var composeConfig struct {
Services map[string]struct {
Image string `json:"image"`
} `json:"services"`
}
if err = json.Unmarshal([]byte(stdout), &composeConfig); err != nil {
return nil, fmt.Errorf("parse compose config json failed, std: %s, err: %v", stdout, err)
}
var images []string
seen := make(map[string]struct{})
for _, service := range composeConfig.Services {
image := strings.TrimSpace(service.Image)
if image == "" {
continue
}
if _, ok := seen[image]; ok {
continue
}
seen[image] = struct{}{}
images = append(images, image)
}
if len(images) == 0 {
return nil, errors.New("no images found from compose config json")
}
return images, nil
}
func Down(filePath string) (string, error) {
if err := checkCmd(); err != nil {
return "", err
}
return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdoutBashCf("%s %s down --remove-orphans", global.CONF.DockerConfig.Command, loadFiles(filePath))
base, extra := getComposeBaseCmd()
args := append(extra, strings.Fields(loadFiles(filePath))...)
args = append(args, "down", "--remove-orphans")
return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdout(base, args...)
}
func Stop(filePath string) (string, error) {
if err := checkCmd(); err != nil {
return "", err
}
return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdoutBashCf("%s %s stop", global.CONF.DockerConfig.Command, loadFiles(filePath))
base, extra := getComposeBaseCmd()
args := append(extra, strings.Fields(loadFiles(filePath))...)
args = append(args, "stop")
return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdout(base, args...)
}
func Restart(filePath string) (string, error) {
if err := checkCmd(); err != nil {
return "", err
}
return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdoutBashCf("%s %s restart", global.CONF.DockerConfig.Command, loadFiles(filePath))
base, extra := getComposeBaseCmd()
args := append(extra, strings.Fields(loadFiles(filePath))...)
args = append(args, "restart")
return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdout(base, args...)
}
func Operate(filePath, operation string) (string, error) {
if err := checkCmd(); err != nil {
return "", err
}
return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdoutBashCf("%s %s %s", global.CONF.DockerConfig.Command, loadFiles(filePath), operation)
base, extra := getComposeBaseCmd()
args := append(extra, strings.Fields(loadFiles(filePath))...)
args = append(args, operation)
return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdout(base, args...)
}
func DownAndUp(filePath string) (string, error) {
@@ -143,11 +220,16 @@ func DownAndUp(filePath string) (string, error) {
return "", err
}
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(20 * time.Minute))
stdout, err := cmdMgr.RunWithStdoutBashCf("%s %s down", global.CONF.DockerConfig.Command, loadFiles(filePath))
base, extra := getComposeBaseCmd()
argsDown := append(extra, strings.Fields(loadFiles(filePath))...)
argsDown = append(argsDown, "down")
stdout, err := cmdMgr.RunWithStdout(base, argsDown...)
if err != nil {
return stdout, err
}
stdout, err = cmdMgr.RunWithStdoutBashCf("%s %s up -d", global.CONF.DockerConfig.Command, loadFiles(filePath))
argsUp := append(extra, strings.Fields(loadFiles(filePath))...)
argsUp = append(argsUp, "up", "-d")
stdout, err = cmdMgr.RunWithStdout(base, argsUp...)
return stdout, err
}
+1 -2
View File
@@ -120,8 +120,7 @@ func parseRecipients(recipient string) []string {
func buildMessage(config SMTPConfig, message EmailMessage, toList []string) (string, error) {
headers := make(map[string]string)
encodedFrom := mime.BEncoding.Encode("UTF-8", config.From)
headers["From"] = encodedFrom
headers["From"] = config.From
encodedSubject := mime.BEncoding.Encode("UTF-8", message.Subject)
headers["Subject"] = encodedSubject
headers["To"] = strings.Join(toList, ",")
+22 -4
View File
@@ -118,10 +118,24 @@ func padding(plaintext []byte, blockSize int) []byte {
return append(plaintext, padtext...)
}
func unPadding(origData []byte) []byte {
func unPadding(origData []byte) ([]byte, error) {
length := len(origData)
if length == 0 {
return nil, fmt.Errorf("invalid padding size")
}
unpadding := int(origData[length-1])
return origData[:(length - unpadding)]
if unpadding == 0 || unpadding > length {
return nil, fmt.Errorf("invalid padding")
}
for i := 0; i < unpadding; i++ {
if origData[length-1-i] != byte(unpadding) {
return nil, fmt.Errorf("invalid padding")
}
}
return origData[:(length - unpadding)], nil
}
func aesEncryptWithSalt(key, plaintext []byte) ([]byte, error) {
@@ -152,6 +166,10 @@ func aesDecryptWithSalt(key, ciphertext []byte) ([]byte, error) {
ciphertext = ciphertext[aes.BlockSize:]
cbc := cipher.NewCBCDecrypter(block, iv)
cbc.CryptBlocks(ciphertext, ciphertext)
ciphertext = unPadding(ciphertext)
return ciphertext, nil
unpadded, err := unPadding(ciphertext)
if err != nil {
return nil, err
}
return unpadded, nil
}
+17 -4
View File
@@ -31,10 +31,10 @@ func Marshal(envMap map[string]string) (string, error) {
for k, v := range envMap {
if d, err := strconv.Atoi(v); err == nil && !isStartWithZero(v) {
lines = append(lines, fmt.Sprintf(`%s=%d`, k, d))
} else if hasEvenDoubleQuotes(v) {
} else if shouldUseSingleQuotes(v) {
lines = append(lines, fmt.Sprintf(`%s='%s'`, k, v))
} else {
lines = append(lines, fmt.Sprintf(`%s="%s"`, k, v))
lines = append(lines, fmt.Sprintf(`%s="%s"`, k, escapeDoubleQuotedEnvValue(v)))
}
}
return strings.Join(lines, "\n"), nil
@@ -88,10 +88,10 @@ func MarshalWithOrder(envMap map[string]string, orders []string) (string, error)
func formatEnvLine(k, v string) string {
if d, err := strconv.Atoi(v); err == nil && !isStartWithZero(v) {
return fmt.Sprintf(`%s=%d`, k, d)
} else if hasEvenDoubleQuotes(v) {
} else if shouldUseSingleQuotes(v) {
return fmt.Sprintf(`%s='%s'`, k, v)
} else {
return fmt.Sprintf(`%s="%s"`, k, v)
return fmt.Sprintf(`%s="%s"`, k, escapeDoubleQuotedEnvValue(v))
}
}
@@ -123,3 +123,16 @@ func hasEvenDoubleQuotes(s string) bool {
}
return count%2 == 0
}
func shouldUseSingleQuotes(s string) bool {
return hasEvenDoubleQuotes(s) && !strings.Contains(s, `\`) && !strings.Contains(s, `'`)
}
func escapeDoubleQuotedEnvValue(s string) string {
replacer := strings.NewReplacer(
`\`, `\\`,
`"`, `\"`,
"\n", `\n`,
)
return replacer.Replace(s)
}
+4 -4
View File
@@ -233,7 +233,7 @@ func (r *Local) Backup(info BackupInfo) error {
if err != nil {
return fmt.Errorf("open file %s failed, err: %v", path.Join(info.TargetDir, info.FileName), err)
}
defer outfile.Close()
defer func() { _ = outfile.Close() }()
dumpCmd := "mysqldump"
if r.Type == constant.AppMariaDB {
dumpCmd = "mariadb-dump"
@@ -268,7 +268,7 @@ func (r *Local) Backup(info BackupInfo) error {
func (r *Local) Recover(info RecoverInfo) error {
fi, _ := os.Open(info.SourceFile)
defer fi.Close()
defer func() { _ = fi.Close() }()
mysqlCli := r.Type
if mysqlCli == "mysql-cluster" {
mysqlCli = "mysql"
@@ -280,12 +280,12 @@ func (r *Local) Recover(info RecoverInfo) error {
if err != nil {
return err
}
defer gzipFile.Close()
defer func() { _ = gzipFile.Close() }()
gzipReader, err := gzip.NewReader(gzipFile)
if err != nil {
return err
}
defer gzipReader.Close()
defer func() { _ = gzipReader.Close() }()
cmd.Stdin = gzipReader
} else {
cmd.Stdin = fi
+7 -6
View File
@@ -248,7 +248,7 @@ func (r *Remote) Backup(info BackupInfo) error {
if err != nil {
return fmt.Errorf("open file %s failed, err: %v", path.Join(info.TargetDir, info.FileName), err)
}
defer outfile.Close()
defer func() { _ = outfile.Close() }()
dumpCmd := "mysqldump"
if r.Type == constant.AppMariaDB {
dumpCmd = "mariadb-dump"
@@ -293,7 +293,7 @@ func (r *Remote) Recover(info RecoverInfo) error {
return buserr.New("ErrCmdIllegal")
}
fi, _ := os.Open(info.SourceFile)
defer fi.Close()
defer func() { _ = fi.Close() }()
image, err := loadImage(info.Type, info.Version)
if err != nil {
@@ -311,12 +311,12 @@ func (r *Remote) Recover(info RecoverInfo) error {
if err != nil {
return err
}
defer gzipFile.Close()
defer func() { _ = gzipFile.Close() }()
gzipReader, err := gzip.NewReader(gzipFile)
if err != nil {
return err
}
defer gzipReader.Close()
defer func() { _ = gzipReader.Close() }()
cmd.Stdin = gzipReader
} else {
cmd.Stdin = fi
@@ -336,7 +336,7 @@ func (r *Remote) SyncDB(version string) ([]SyncDBInfo, error) {
if err != nil {
return datas, err
}
defer rows.Close()
defer func() { _ = rows.Close() }()
for rows.Next() {
var dbName, charsetName, collation string
@@ -431,7 +431,7 @@ func (r *Remote) LoadFormatCollation(timeout uint) ([]dto.MysqlFormatCollationOp
if err != nil {
return nil, err
}
defer rows.Close()
defer func() { _ = rows.Close() }()
formatMap := make(map[string][]string)
for rows.Next() {
@@ -471,6 +471,7 @@ func (r *Remote) ExecSQLForHosts(timeout uint) ([]string, error) {
return nil, buserr.New("ErrExecTimeOut")
}
var rows []string
defer func() { _ = results.Close() }()
for results.Next() {
var host string
if err := results.Scan(&host); err != nil {
+32
View File
@@ -9,6 +9,7 @@ import (
"os"
"os/exec"
"path"
"strconv"
"strings"
"time"
@@ -99,6 +100,13 @@ func (r *Local) CreateUser(info CreateInfo, withDeleteDB bool) error {
func (r *Local) Delete(info DeleteInfo) error {
if len(info.Name) != 0 {
inUse, err := r.isDatabaseInUse(info.Name, info.Timeout)
if err != nil && !info.ForceDelete {
return fmt.Errorf("check database connections failed, err: %v", err)
}
if inUse && !info.ForceDelete {
return buserr.WithDetail("ErrInUsed", info.Name, nil)
}
dropSql := fmt.Sprintf("DROP DATABASE \"%s\"", info.Name)
if err := r.ExecSQL(dropSql, info.Timeout); err != nil && !info.ForceDelete {
return fmt.Errorf("drop database failed, err: %v", err)
@@ -203,6 +211,30 @@ func (r *Local) SyncDB() ([]SyncDBInfo, error) {
func (r *Local) Close() {}
func (r *Local) isDatabaseInUse(name string, timeout uint) (bool, error) {
escapedName := strings.ReplaceAll(name, "'", "''")
checkSQL := fmt.Sprintf(
"SELECT COUNT(*) FROM pg_stat_activity WHERE datname='%s' AND pid <> pg_backend_pid()",
escapedName,
)
lines, err := r.ExecSQLForRows(checkSQL, timeout)
if err != nil {
return false, err
}
for _, line := range lines {
countStr := strings.TrimSpace(line)
if len(countStr) == 0 {
continue
}
count, parseErr := strconv.Atoi(countStr)
if parseErr != nil {
return false, parseErr
}
return count > 0, nil
}
return false, nil
}
func (r *Local) ExecSQL(command string, timeout uint) error {
itemCommand := r.PrefixCommand[:]
itemCommand = append(itemCommand, command)
+25
View File
@@ -95,6 +95,13 @@ func (r *Remote) CreateUser(info CreateInfo, withDeleteDB bool) error {
func (r *Remote) Delete(info DeleteInfo) error {
if len(info.Name) != 0 {
inUse, err := r.isDatabaseInUse(info.Name, info.Timeout)
if err != nil && !info.ForceDelete {
return fmt.Errorf("check database connections failed, err: %v", err)
}
if inUse && !info.ForceDelete {
return buserr.WithDetail("ErrInUsed", info.Name, nil)
}
dropSql := fmt.Sprintf("DROP DATABASE \"%s\"", info.Name)
if err := r.ExecSQL(dropSql, info.Timeout); err != nil && !info.ForceDelete {
return fmt.Errorf("drop database failed, err: %v", err)
@@ -107,6 +114,24 @@ func (r *Remote) Delete(info DeleteInfo) error {
return nil
}
func (r *Remote) isDatabaseInUse(name string, timeout uint) (bool, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
defer cancel()
var count int
if err := r.Client.QueryRowContext(
ctx,
"SELECT COUNT(*) FROM pg_stat_activity WHERE datname = $1 AND pid <> pg_backend_pid()",
name,
).Scan(&count); err != nil {
return false, err
}
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
return false, buserr.New("ErrExecTimeOut")
}
return count > 0, nil
}
func (r *Remote) ChangePrivileges(info Privileges) error {
super := "SUPERUSER"
if !info.SuperUser {
+6
View File
@@ -29,6 +29,9 @@ const (
AnsiEscapePattern = "\x1b\\[[0-9;?]*[A-Za-z]|\x1b=|\x1b>"
RecycleBinFilePattern = `_1p_file_1p_(.+)_p_(\d+)_(\d+)`
OrderByValidationPattern = `^[a-zA-Z_][a-zA-Z0-9_]*$`
SQLIdentifierPattern = `^[A-Za-z_][A-Za-z0-9_]*$`
NginxHostPattern = `^[a-zA-Z0-9.-]+(:[0-9]+)?$`
NginxPathPattern = `^/[a-zA-Z0-9._/\-]*$`
)
var regexMap = make(map[string]*regexp.Regexp)
@@ -58,6 +61,9 @@ func Init() {
AnsiEscapePattern,
RecycleBinFilePattern,
OrderByValidationPattern,
SQLIdentifierPattern,
NginxHostPattern,
NginxPathPattern,
}
for _, pattern := range patterns {
+10
View File
@@ -24,6 +24,7 @@ import (
"github.com/go-acme/lego/v4/providers/dns/regru"
"github.com/go-acme/lego/v4/providers/dns/route53"
"github.com/go-acme/lego/v4/providers/dns/spaceship"
"github.com/go-acme/lego/v4/providers/dns/technitium"
"github.com/go-acme/lego/v4/providers/dns/tencentcloud"
"github.com/go-acme/lego/v4/providers/dns/vercel"
"github.com/go-acme/lego/v4/providers/dns/volcengine"
@@ -59,6 +60,7 @@ const (
Ovh DnsType = "Ovh"
AcmeDNS DnsType = "AcmeDNS"
PorkBun DnsType = "PorkBun"
Technitium DnsType = "Technitium"
)
type DNSParam struct {
@@ -301,6 +303,14 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
config.PollingInterval = pollingInterval
config.TTL = ttl
p, err = porkbun.NewDNSProviderConfig(config)
case Technitium:
config := technitium.NewDefaultConfig()
config.BaseURL = param.BaseURL
config.APIToken = param.Token
config.PropagationTimeout = propagationTimeout
config.PollingInterval = pollingInterval
config.TTL = ttl
p, err = technitium.NewDNSProviderConfig(config)
}
if err != nil {
+3 -3
View File
@@ -74,15 +74,15 @@ func (f *Fail2ban) Operate(operate string) error {
func (f *Fail2ban) ReBanIPs(ips []string) error {
ipItems, _ := f.ListBanned()
stdout, err := cmd.RunDefaultWithStdoutBashCfAndTimeOut("fail2ban-client unban --all", 10*time.Minute)
stdout, err := cmd.NewCommandMgr(cmd.WithTimeout(10*time.Minute)).RunWithStdout("fail2ban-client", "unban", "--all")
if err != nil {
stdout1, err := cmd.RunDefaultWithStdoutBashCfAndTimeOut("fail2ban-client set sshd banip %s", 10*time.Minute, strings.Join(ipItems, " "))
stdout1, err := cmd.NewCommandMgr(cmd.WithTimeout(10*time.Minute)).RunWithStdout("fail2ban-client", "set", "sshd", "banip", strings.Join(ipItems, " "))
if err != nil {
global.LOG.Errorf("rebanip after fail2ban-client unban --all failed, err: %s", stdout1)
}
return fmt.Errorf("fail2ban-client unban --all failed, err: %s", stdout)
}
stdout, err = cmd.RunDefaultWithStdoutBashCfAndTimeOut("fail2ban-client set sshd banip %s", 10*time.Minute, strings.Join(ips, " "))
stdout, err = cmd.NewCommandMgr(cmd.WithTimeout(10*time.Minute)).RunWithStdout("fail2ban-client", "set", "sshd", "banip", strings.Join(ips, " "))
if err != nil {
return fmt.Errorf("handle `fail2ban-client set sshd banip %s` failed, err: %s", strings.Join(ips, " "), stdout)
}
+3 -3
View File
@@ -51,7 +51,7 @@ func GetUpgradeVersionInfo() (*dto.UpgradeInfo, error) {
if strings.Contains(itemVersion, "beta") {
mode = "beta"
}
notes, err := loadReleaseNotes(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.CONF.RemoteURL.RepoUrl, mode, itemVersion, itemVersion))
notes, err := loadReleaseNotes(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.RepoURL(), mode, itemVersion, itemVersion))
if err != nil {
return nil, fmt.Errorf("load releases-notes of version %s failed, err: %v", itemVersion, err)
}
@@ -101,9 +101,9 @@ func loadVersionByMode(developer, currentVersion string) (string, string, string
}
func loadVersion(isLatest bool, currentVersion, mode string) string {
path := fmt.Sprintf("%s/%s/latest", global.CONF.RemoteURL.RepoUrl, mode)
path := fmt.Sprintf("%s/%s/latest", global.RepoURL(), mode)
if !isLatest {
path = fmt.Sprintf("%s/%s/latest.current", global.CONF.RemoteURL.RepoUrl, mode)
path = fmt.Sprintf("%s/%s/latest.current", global.RepoURL(), mode)
}
_, latestVersionRes, err := HandleRequest(path, http.MethodGet, constant.TimeOut20s)
if err != nil {
+3 -17
View File
@@ -4,17 +4,15 @@ package xpack
import (
"crypto/tls"
"fmt"
"net"
"net/http"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/gin-gonic/gin"
)
@@ -26,25 +24,13 @@ func StartClam(startClam *model.Clam, isUpdate bool) (int, error) {
func LoadNodeInfo(isBase bool) (model.NodeInfo, error) {
var info model.NodeInfo
info.BaseDir = loadParams("BASE_DIR")
info.Version = loadParams("ORIGINAL_VERSION")
info.BaseDir = common.LoadParams("BASE_DIR")
info.Version = common.LoadParams("ORIGINAL_VERSION")
info.Scope = "master"
global.IsMaster = true
return info, nil
}
func loadParams(param string) string {
stdout, err := cmd.RunDefaultWithStdoutBashCf("grep '^%s=' /usr/local/bin/1pctl | cut -d'=' -f2", param)
if err != nil {
panic(err)
}
info := strings.ReplaceAll(stdout, "\n", "")
if len(info) == 0 || info == `""` {
panic(fmt.Sprintf("error `%s` find in /usr/local/bin/1pctl", param))
}
return info
}
func GetImagePrefix() string {
return ""
}
+17 -3
View File
@@ -9,6 +9,7 @@ import (
"github.com/1Panel-dev/1Panel/core/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/core/app/dto"
"github.com/1Panel-dev/1Panel/core/app/model"
"github.com/1Panel-dev/1Panel/core/buserr"
"github.com/1Panel-dev/1Panel/core/constant"
"github.com/1Panel-dev/1Panel/core/global"
"github.com/1Panel-dev/1Panel/core/utils/captcha"
@@ -53,7 +54,9 @@ func (b *BaseApi) Login(c *gin.Context) {
}
user, msgKey, err := authService.Login(c, req, string(entrance))
go saveLoginLogs(c, err)
if user == nil || user.MfaStatus != constant.StatusEnable {
go saveLoginLogs(c, wrapLoginErr(msgKey, err))
}
if msgKey == "ErrAuth" || msgKey == "ErrEntrance" {
if msgKey == "ErrAuth" {
global.IPTracker.SetNeedCaptcha(ip)
@@ -90,6 +93,7 @@ func (b *BaseApi) MFALogin(c *gin.Context) {
}
user, msgKey, err := authService.MFALogin(c, req, string(entrance))
go saveLoginLogs(c, wrapLoginErr(msgKey, err))
if msgKey == "ErrAuth" {
helper.BadAuth(c, msgKey, err)
return
@@ -135,7 +139,7 @@ func (b *BaseApi) PasskeyFinishLogin(c *gin.Context) {
sessionID := c.GetHeader("Passkey-Session")
entrance := loadEntranceFromRequest(c)
user, msgKey, err := authService.PasskeyFinishLogin(c, sessionID, entrance)
go saveLoginLogs(c, err)
go saveLoginLogs(c, wrapLoginErr(msgKey, err))
if msgKey == "ErrAuth" || msgKey == "ErrEntrance" {
if msgKey == "ErrAuth" {
global.IPTracker.SetNeedCaptcha(common.GetRealClientIP(c))
@@ -211,7 +215,7 @@ func (b *BaseApi) GetLoginSetting(c *gin.Context) {
needCaptcha := global.IPTracker.NeedCaptcha(ip)
res := &dto.LoginSetting{
IsDemo: global.CONF.Base.IsDemo,
IsIntl: global.CONF.Base.IsIntl,
IsIntl: global.CONF.Base.Edition == "intl",
IsFxplay: global.CONF.Base.IsFxplay,
IsOffLine: global.CONF.Base.IsOffLine,
Language: settingInfo.Language,
@@ -237,6 +241,16 @@ func saveLoginLogs(c *gin.Context, err error) {
_ = logService.CreateLoginLog(logs)
}
func wrapLoginErr(msgKey string, err error) error {
if err != nil {
return err
}
if msgKey == "ErrAuth" || msgKey == "ErrEntrance" {
return buserr.New(msgKey)
}
return nil
}
func loadEntranceFromRequest(c *gin.Context) string {
entranceItem := c.Request.Header.Get("EntranceCode")
var entrance []byte
+32
View File
@@ -15,6 +15,7 @@ import (
"github.com/1Panel-dev/1Panel/core/buserr"
"github.com/1Panel-dev/1Panel/core/constant"
"github.com/1Panel-dev/1Panel/core/global"
"github.com/1Panel-dev/1Panel/core/utils/common"
"github.com/1Panel-dev/1Panel/core/utils/mfa"
"github.com/gin-gonic/gin"
)
@@ -98,6 +99,14 @@ func (b *BaseApi) UpdateSetting(c *gin.Context) {
return
}
}
if req.Key == "PasskeyTrustedProxies" {
value, err := normalizePasskeyTrustedProxies(req.Value)
if err != nil {
helper.BadRequest(c, err)
return
}
req.Value = value
}
if err := settingService.Update(req.Key, req.Value); err != nil {
helper.InternalServer(c, err)
@@ -666,3 +675,26 @@ func checkEntrancePattern(val string) bool {
}
return true
}
func normalizePasskeyTrustedProxies(value string) (string, error) {
if strings.TrimSpace(value) == "" {
return "", nil
}
lines := strings.Split(value, "\n")
validLines := make([]string, 0, len(lines))
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" {
continue
}
validLines = append(validLines, line)
}
if len(validLines) == 0 {
return "", nil
}
normalized := strings.Join(validLines, "\n")
if _, err := common.HandleIPList(normalized); err != nil {
return "", err
}
return normalized, nil
}
+5 -1
View File
@@ -15,6 +15,7 @@ type SettingInfo struct {
Ipv6 string `json:"ipv6"`
BindAddress string `json:"bindAddress"`
PanelName string `json:"panelName"`
Edition string `json:"edition"`
Theme string `json:"theme"`
MenuTabs string `json:"menuTabs"`
Language string `json:"language"`
@@ -23,6 +24,7 @@ type SettingInfo struct {
SSL string `json:"ssl"`
SSLType string `json:"sslType"`
BindDomain string `json:"bindDomain"`
PasskeyTrustedProxies string `json:"passkeyTrustedProxies"`
AllowIPs string `json:"allowIPs"`
SecurityEntrance string `json:"securityEntrance"`
DashboardMemoVisible string `json:"dashboardMemoVisible"`
@@ -31,7 +33,6 @@ type SettingInfo struct {
ExpirationTime string `json:"expirationTime"`
ComplexityVerification string `json:"complexityVerification"`
MFAStatus string `json:"mfaStatus"`
MFASecret string `json:"mfaSecret"`
MFAInterval string `json:"mfaInterval"`
AppStoreVersion string `json:"appStoreVersion"`
@@ -226,6 +227,9 @@ type TerminalInfo struct {
LineHeight string `json:"lineHeight"`
LetterSpacing string `json:"letterSpacing"`
FontSize string `json:"fontSize"`
FontFamily string `json:"fontFamily"`
BackgroundColor string `json:"backgroundColor"`
ForegroundColor string `json:"foregroundColor"`
CursorBlink string `json:"cursorBlink"`
CursorStyle string `json:"cursorStyle"`
Scrollback string `json:"scrollback"`
+44 -6
View File
@@ -2,14 +2,22 @@ package repo
import (
"errors"
"time"
"github.com/1Panel-dev/1Panel/core/app/model"
"github.com/1Panel-dev/1Panel/core/global"
"github.com/1Panel-dev/1Panel/core/init/migration/helper"
"github.com/patrickmn/go-cache"
"gorm.io/gorm"
)
type SettingRepo struct{}
var (
settingCache = cache.New(5*time.Minute, 10*time.Minute)
settingTTL = 5 * time.Minute
)
type ISettingRepo interface {
List(opts ...global.DBOption) ([]model.Setting, error)
Get(opts ...global.DBOption) (model.Setting, error)
@@ -39,7 +47,11 @@ func (u *SettingRepo) Create(key, value string) error {
Key: key,
Value: value,
}
return global.DB.Create(setting).Error
if err := global.DB.Create(setting).Error; err != nil {
return err
}
settingCache.Set(key, value, settingTTL)
return nil
}
func (u *SettingRepo) Get(opts ...global.DBOption) (model.Setting, error) {
@@ -48,20 +60,33 @@ func (u *SettingRepo) Get(opts ...global.DBOption) (model.Setting, error) {
for _, opt := range opts {
db = opt(db)
}
err := db.First(&settings).Error
if err == nil && settings.Key != "" {
settingCache.Set(settings.Key, settings.Value, settingTTL)
}
return settings, err
}
func (u *SettingRepo) GetValueByKey(key string) (string, error) {
if val, found := settingCache.Get(key); found {
return val.(string), nil
}
var setting model.Setting
if err := global.DB.Model(&model.Setting{}).Where("key = ?", key).First(&setting).Error; err != nil {
return "", err
}
settingCache.Set(key, setting.Value, settingTTL)
return setting.Value, nil
}
func (u *SettingRepo) Update(key, value string) error {
return global.DB.Model(&model.Setting{}).Where("key = ?", key).Updates(map[string]interface{}{"value": value}).Error
if err := global.DB.Model(&model.Setting{}).Where("key = ?", key).Updates(map[string]interface{}{"value": value}).Error; err != nil {
return err
}
settingCache.Set(key, value, settingTTL)
return nil
}
func (u *SettingRepo) UpdateOrCreate(key, value string) error {
@@ -69,15 +94,28 @@ func (u *SettingRepo) UpdateOrCreate(key, value string) error {
result := global.DB.Where("key = ?", key).First(&setting)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return global.DB.Create(&model.Setting{Key: key, Value: value}).Error
if err := global.DB.Create(&model.Setting{Key: key, Value: value}).Error; err != nil {
return err
}
settingCache.Set(key, value, settingTTL)
return nil
}
return result.Error
}
return global.DB.Model(&setting).UpdateColumn("value", value).Error
if err := global.DB.Model(&setting).UpdateColumn("value", value).Error; err != nil {
return err
}
settingCache.Set(key, value, settingTTL)
return nil
}
func (u *SettingRepo) DefaultMenu() error {
return global.DB.Model(&model.Setting{}).
menus := helper.LoadMenus()
if err := global.DB.Model(&model.Setting{}).
Where("key = ?", "HideMenu").
Update("value", helper.LoadMenus()).Error
Update("value", menus).Error; err != nil {
return err
}
settingCache.Set("HideMenu", menus, settingTTL)
return nil
}
+108 -8
View File
@@ -5,6 +5,7 @@ import (
"crypto/rand"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"net"
"strconv"
@@ -22,6 +23,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/go-webauthn/webauthn/protocol"
"github.com/go-webauthn/webauthn/webauthn"
"gorm.io/gorm"
)
type AuthService struct{}
@@ -114,7 +116,7 @@ func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin, entrance strin
}
success := mfa.ValidCode(info.Code, mfaInterval.Value, mfaSecret.Value)
if !success {
return nil, "ErrAuth", nil
return nil, "ErrMFA", nil
}
res, err := u.generateSession(c, info.Name)
if err != nil {
@@ -454,13 +456,6 @@ func (u *AuthService) PasskeyDelete(id string) error {
}
func (u *AuthService) passkeyEnabled(c *gin.Context) (bool, error) {
sslSetting, err := settingRepo.Get(repo.WithByKey("SSL"))
if err != nil {
return false, err
}
if sslSetting.Value == constant.StatusDisable {
return false, nil
}
return strings.EqualFold(passkeyRequestScheme(c), "https"), nil
}
@@ -679,9 +674,114 @@ func passkeyRequestScheme(c *gin.Context) string {
if c.Request.TLS != nil {
return "https"
}
if !passkeyIsFromTrustedProxy(c) {
return "http"
}
if proto := passkeyForwardedProto(c.GetHeader("Forwarded")); proto != "" {
return proto
}
if proto := passkeyXForwardedProto(c.GetHeader("X-Forwarded-Proto")); proto != "" {
return proto
}
return "http"
}
func passkeyIsFromTrustedProxy(c *gin.Context) bool {
remoteIP := passkeyRemoteIP(c.Request.RemoteAddr)
if remoteIP == nil {
return false
}
proxies, err := loadPasskeyTrustedProxies()
if err != nil {
global.LOG.Errorf("load passkey trusted proxies failed, err: %v", err)
return false
}
for _, cidr := range proxies {
if cidr.Contains(remoteIP) {
return true
}
}
return false
}
func passkeyRemoteIP(remoteAddr string) net.IP {
if host, _, err := net.SplitHostPort(strings.TrimSpace(remoteAddr)); err == nil {
return net.ParseIP(host)
}
return net.ParseIP(strings.TrimSpace(remoteAddr))
}
func loadPasskeyTrustedProxies() ([]*net.IPNet, error) {
setting, err := settingRepo.Get(repo.WithByKey("PasskeyTrustedProxies"))
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return parsePasskeyTrustedProxies("127.0.0.1\n::1")
}
return nil, err
}
return parsePasskeyTrustedProxies(setting.Value)
}
func parsePasskeyTrustedProxies(value string) ([]*net.IPNet, error) {
lines := strings.Split(strings.TrimSpace(value), "\n")
if len(lines) == 0 {
return []*net.IPNet{}, nil
}
cidrs := make([]*net.IPNet, 0, len(lines))
for _, line := range lines {
item := strings.TrimSpace(line)
if item == "" {
continue
}
if ip := net.ParseIP(item); ip != nil {
if ip.To4() != nil {
item += "/32"
} else {
item += "/128"
}
}
_, ipNet, err := net.ParseCIDR(item)
if err != nil {
return nil, err
}
cidrs = append(cidrs, ipNet)
}
return cidrs, nil
}
func passkeyForwardedProto(value string) string {
if value == "" {
return ""
}
for _, item := range strings.Split(value, ",") {
for _, part := range strings.Split(item, ";") {
kv := strings.SplitN(strings.TrimSpace(part), "=", 2)
if len(kv) != 2 {
continue
}
if !strings.EqualFold(strings.TrimSpace(kv[0]), "proto") {
continue
}
proto := strings.ToLower(strings.Trim(strings.TrimSpace(kv[1]), `"`))
if proto == "http" || proto == "https" {
return proto
}
}
}
return ""
}
func passkeyXForwardedProto(value string) string {
if value == "" {
return ""
}
proto := strings.ToLower(strings.TrimSpace(strings.Split(value, ",")[0]))
if proto == "http" || proto == "https" {
return proto
}
return ""
}
func stripHostPort(hostport string) string {
if hostport == "" {
return hostport
+16 -5
View File
@@ -1,9 +1,10 @@
package service
import (
"crypto/rand"
"encoding/json"
"fmt"
mathRand "math/rand"
"math/big"
"net/http"
"os"
"path"
@@ -172,7 +173,17 @@ func StartSync() {
service := NewIScriptService()
scriptSync, _ := repo.NewISettingRepo().GetValueByKey("ScriptSync")
if !global.CONF.Base.IsOffLine && scriptSync == constant.StatusEnable {
id, err := global.Cron.AddJob(fmt.Sprintf("%v %v * * *", mathRand.Intn(60), mathRand.Intn(3)), service)
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)
}
id, err := global.Cron.AddJob(fmt.Sprintf("%v %v * * *", minuteRand.Int64(), hourRand.Int64()), service)
if err != nil {
global.LOG.Errorf("[core] can not add script sync corn job: %s", err.Error())
}
@@ -201,7 +212,7 @@ func (u *ScriptService) Sync(req dto.OperateByTaskID) error {
}
syncTask.AddSubTask(task.GetTaskName(i18n.GetMsgByKey("RemoteScriptLibrary"), task.TaskSync, task.TaskScopeScript), func(t *task.Task) (err error) {
versionUrl := fmt.Sprintf("%s/scripts/version.txt", global.CONF.RemoteURL.ResourceURL)
versionUrl := fmt.Sprintf("%s/scripts/version.txt", global.ResourceURL())
_, versionRes, err := req_helper.HandleRequestWithProxy(versionUrl, http.MethodGet, constant.TimeOut20s)
if err != nil {
return fmt.Errorf("load scripts version from remote failed, err: %v", err)
@@ -216,7 +227,7 @@ func (u *ScriptService) Sync(req dto.OperateByTaskID) error {
return nil
}
dataUrl := fmt.Sprintf("%s/scripts/data.yaml", global.CONF.RemoteURL.ResourceURL)
dataUrl := fmt.Sprintf("%s/scripts/data.yaml", global.ResourceURL())
_, dataRes, err := req_helper.HandleRequestWithProxy(dataUrl, http.MethodGet, constant.TimeOut20s)
syncTask.LogWithStatus(i18n.GetMsgByKey("DownloadData"), err)
if err != nil {
@@ -232,7 +243,7 @@ func (u *ScriptService) Sync(req dto.OperateByTaskID) error {
if _, err := os.Stat(tmpDir); err != nil {
_ = os.MkdirAll(tmpDir, 0755)
}
scriptsUrl := fmt.Sprintf("%s/scripts/scripts.tar.gz", global.CONF.RemoteURL.ResourceURL)
scriptsUrl := fmt.Sprintf("%s/scripts/scripts.tar.gz", global.ResourceURL())
err = files.DownloadFileWithProxyStream(scriptsUrl, tmpDir+"/scripts.tar.gz")
syncTask.LogWithStatus(i18n.GetMsgByKey("DownloadPackage"), err)
if err != nil {
+52 -28
View File
@@ -16,6 +16,7 @@ import (
"net/url"
"os"
"path"
"sort"
"strconv"
"strings"
"time"
@@ -61,8 +62,6 @@ type ISettingService interface {
UpdateSystemSSL() error
GenerateRSAKey() error
GetLoginSetting() (*dto.SystemSetting, error)
UpdateAppstoreConfig(req dto.AppstoreUpdate) error
GetAppstoreConfig() (*dto.AppstoreConfig, error)
DefaultMenu() error
@@ -84,6 +83,15 @@ func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) {
for _, set := range setting {
settingMap[set.Key] = set.Value
}
if hideMenu, ok := settingMap["HideMenu"]; ok && len(hideMenu) > 0 {
var menus []dto.ShowMenu
if err := json.Unmarshal([]byte(hideMenu), &menus); err == nil {
sortShowMenus(menus)
if sortedBytes, err := json.Marshal(menus); err == nil {
settingMap["HideMenu"] = string(sortedBytes)
}
}
}
var info dto.SettingInfo
arr, err := json.Marshal(settingMap)
if err != nil {
@@ -92,6 +100,10 @@ func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) {
if err := json.Unmarshal(arr, &info); err != nil {
return nil, err
}
if info.Edition == "" {
info.Edition = "cn"
_ = settingRepo.UpdateOrCreate("Edition", info.Edition)
}
if info.ProxyPasswdKeep != constant.StatusEnable {
info.ProxyPasswd = ""
} else {
@@ -101,6 +113,20 @@ func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) {
return &info, err
}
func sortShowMenus(menus []dto.ShowMenu) {
for i := range menus {
if len(menus[i].Children) > 0 {
sortShowMenus(menus[i].Children)
}
}
sort.SliceStable(menus, func(i, j int) bool {
if menus[i].Sort == menus[j].Sort {
return menus[i].ID < menus[j].ID
}
return menus[i].Sort < menus[j].Sort
})
}
func (u *SettingService) Update(key, value string) error {
oldVal, err := settingRepo.Get(repo.WithByKey(key))
if err != nil {
@@ -168,6 +194,11 @@ func (u *SettingService) Update(key, value string) error {
} else {
global.Cron.Remove(global.ScriptSyncJobID)
}
case "Edition":
global.CONF.Base.Edition = value
if err := xpack.Sync(constant.SyncEdition); err != nil {
global.LOG.Errorf("sync edition to node failed, err: %v", err)
}
}
return nil
@@ -283,9 +314,6 @@ func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error {
if err := settingRepo.Update("SSLType", "self"); err != nil {
return err
}
if err := u.clearPasskeySettings(); err != nil {
return err
}
_ = os.Remove(path.Join(secretDir, "server.crt"))
_ = os.Remove(path.Join(secretDir, "server.key"))
go func() {
@@ -391,9 +419,6 @@ func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error {
if err := settingRepo.Update("SSL", req.SSL); err != nil {
return err
}
if err := u.clearPasskeySettings(); err != nil {
return err
}
return u.UpdateSystemSSL()
}
@@ -500,25 +525,37 @@ func (u *SettingService) GetTerminalInfo() (*dto.TerminalInfo, error) {
return &info, err
}
func (u *SettingService) UpdateTerminal(req dto.TerminalInfo) error {
if err := settingRepo.Update("LineHeight", req.LineHeight); err != nil {
if err := settingRepo.UpdateOrCreate("LineHeight", req.LineHeight); err != nil {
return err
}
if err := settingRepo.Update("LetterSpacing", req.LetterSpacing); err != nil {
if err := settingRepo.UpdateOrCreate("LetterSpacing", req.LetterSpacing); err != nil {
return err
}
if err := settingRepo.Update("FontSize", req.FontSize); err != nil {
if err := settingRepo.UpdateOrCreate("FontSize", req.FontSize); err != nil {
return err
}
if err := settingRepo.Update("CursorBlink", req.CursorBlink); err != nil {
if err := settingRepo.UpdateOrCreate("FontFamily", req.FontFamily); err != nil {
return err
}
if err := settingRepo.Update("CursorStyle", req.CursorStyle); err != nil {
if err := settingRepo.UpdateOrCreate("CursorBlink", req.CursorBlink); err != nil {
return err
}
if err := settingRepo.Update("Scrollback", req.Scrollback); err != nil {
if err := settingRepo.UpdateOrCreate("BackgroundColor", req.BackgroundColor); err != nil {
return err
}
if err := settingRepo.Update("ScrollSensitivity", req.ScrollSensitivity); err != nil {
if err := settingRepo.UpdateOrCreate("ForegroundColor", req.ForegroundColor); err != nil {
return err
}
if err := settingRepo.UpdateOrCreate("CursorBlink", req.CursorBlink); err != nil {
return err
}
if err := settingRepo.UpdateOrCreate("CursorStyle", req.CursorStyle); err != nil {
return err
}
if err := settingRepo.UpdateOrCreate("Scrollback", req.Scrollback); err != nil {
return err
}
if err := settingRepo.UpdateOrCreate("ScrollSensitivity", req.ScrollSensitivity); err != nil {
return err
}
return nil
@@ -673,19 +710,6 @@ func (u *SettingService) GenerateRSAKey() error {
return nil
}
func (u *SettingService) GetLoginSetting() (*dto.SystemSetting, error) {
settingInfo, err := u.GetSettingInfo()
if err != nil {
return nil, err
}
res := &dto.SystemSetting{
Language: settingInfo.Language,
IsDemo: global.CONF.Base.IsDemo,
IsIntl: global.CONF.Base.IsIntl,
}
return res, nil
}
func (u *SettingService) UpdateAppstoreConfig(req dto.AppstoreUpdate) error {
return settingRepo.UpdateOrCreate(req.Scope, req.Status)
}
+13 -9
View File
@@ -117,7 +117,7 @@ func (u *UpgradeService) SearchUpgrade() (*dto.UpgradeInfo, error) {
if strings.HasPrefix(upgrade.TestVersion, upgrade.LatestVersion+"-beta") {
upgrade.TestVersion = ""
}
notes, err := u.loadReleaseNotes(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.CONF.RemoteURL.RepoUrl, mode, itemVersion, itemVersion))
notes, err := u.loadReleaseNotes(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.RepoURL(), mode, itemVersion, itemVersion))
if err != nil {
return nil, fmt.Errorf("load releases-notes of version %s failed, err: %v", itemVersion, err)
}
@@ -130,7 +130,7 @@ func (u *UpgradeService) LoadNotes(req dto.Upgrade) (string, error) {
if strings.Contains(req.Version, "beta") {
mode = "beta"
}
notes, err := u.loadReleaseNotes(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.CONF.RemoteURL.RepoUrl, mode, req.Version, req.Version))
notes, err := u.loadReleaseNotes(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.RepoURL(), mode, req.Version, req.Version))
if err != nil {
return "", fmt.Errorf("load releases-notes of version %s failed, err: %v", req.Version, err)
}
@@ -162,7 +162,7 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
if strings.Contains(req.Version, "beta") {
mode = "beta"
}
downloadPath := fmt.Sprintf("%s/%s/%s/release", global.CONF.RemoteURL.RepoUrl, mode, req.Version)
downloadPath := fmt.Sprintf("%s/%s/%s/release", global.RepoURL(), mode, req.Version)
fileName := fmt.Sprintf("1panel-%s-%s-%s.tar.gz", req.Version, "linux", itemArch)
_ = settingRepo.Update("SystemStatus", "Upgrading")
go func() {
@@ -287,7 +287,11 @@ type noteDetailHelper struct {
func (u *UpgradeService) LoadRelease() ([]dto.ReleasesNotes, error) {
var notes []dto.ReleasesNotes
resp, err := req_helper.HandleGet("https://1panel.cn/docs/v2/search/search_index.json")
url := "https://docs.1panel.pro/v2/search/search_index.json"
if global.CONF.Base.Edition != "intl" {
url = "https://1panel.cn/docs/v2/search/search_index.json"
}
resp, err := req_helper.HandleGet(url)
if err != nil {
return notes, err
}
@@ -321,13 +325,13 @@ func analyzeDoc(version, content string) dto.ReleasesNotes {
}
item.CreatedAt = strings.ReplaceAll(strings.TrimSpace(parts[1]), "</p>", "")
for i := 1; i < len(parts); i++ {
if strings.Contains(parts[i], "问题修复") {
if strings.Contains(parts[i], "问题修复") || strings.Contains(parts[i], "Bug Fixes") {
item.FixCount = strings.Count(parts[i], "<li>")
}
if strings.Contains(parts[i], "新增功能") {
if strings.Contains(parts[i], "新增功能") || strings.Contains(parts[i], "New Features") {
item.NewCount = strings.Count(parts[i], "<li>")
}
if strings.Contains(parts[i], "功能优化") {
if strings.Contains(parts[i], "功能优化") || strings.Contains(parts[i], "Improvements") {
item.OptimizationCount = strings.Count(parts[i], "<li>")
}
}
@@ -442,9 +446,9 @@ func (u *UpgradeService) loadVersionByMode(developer, currentVersion string) (st
}
func (u *UpgradeService) loadVersion(isLatest bool, currentVersion, mode string) string {
path := fmt.Sprintf("%s/%s/latest", global.CONF.RemoteURL.RepoUrl, mode)
path := fmt.Sprintf("%s/%s/latest", global.RepoURL(), mode)
if !isLatest {
path = fmt.Sprintf("%s/%s/latest.current", global.CONF.RemoteURL.RepoUrl, mode)
path = fmt.Sprintf("%s/%s/latest.current", global.RepoURL(), mode)
}
_, latestVersionRes, err := req_helper.HandleRequestWithProxy(path, http.MethodGet, constant.TimeOut20s)
if err != nil {
+12 -3
View File
@@ -62,7 +62,10 @@ var resetSSLCmd = &cobra.Command{
return err
}
return setSettingByKey(db, "SSL", constant.StatusDisable)
if err := setSettingByKey(db, "SSL", constant.StatusDisable); err != nil {
return err
}
return nil
},
}
var resetEntranceCmd = &cobra.Command{
@@ -94,7 +97,10 @@ var resetBindIpsCmd = &cobra.Command{
return err
}
return setSettingByKey(db, "AllowIPs", "")
if err := setSettingByKey(db, "AllowIPs", ""); err != nil {
return err
}
return nil
},
}
var resetDomainCmd = &cobra.Command{
@@ -110,7 +116,10 @@ var resetDomainCmd = &cobra.Command{
return err
}
return setSettingByKey(db, "BindDomain", "")
if err := setSettingByKey(db, "BindDomain", ""); err != nil {
return err
}
return nil
},
}
+45
View File
@@ -92,9 +92,16 @@ var restoreCmd = &cobra.Command{
fmt.Println(i18n.GetMsgByKeyForCmd("RestoreStep4"))
if _, err := os.Stat(path.Join(tmpPath, "db")); err == nil {
dbPath := path.Join(baseDir, "1panel")
targetDBDir := path.Join(dbPath, "db")
if err := os.RemoveAll(targetDBDir); err != nil {
global.LOG.Errorf("rollback 1panel db cleanup failed, err: %v", err)
}
if err := files.CopyItem(true, true, path.Join(tmpPath, "db"), dbPath); err != nil {
global.LOG.Errorf("rollback 1panel db failed, err: %v", err)
}
if err := cleanOrphanSQLiteSidecars(targetDBDir); err != nil {
global.LOG.Errorf("rollback 1panel db sidecar cleanup failed, err: %v", err)
}
}
fmt.Println(i18n.GetMsgByKeyForCmd("RestoreStep5"))
@@ -140,3 +147,41 @@ func loadRollbackVersion(upgradeDir string) string {
}
return info
}
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
}
+1 -1
View File
@@ -258,7 +258,7 @@ func isValidPassword(password string) bool {
}
}
if len(password) < 8 && len(password) > 30 {
if len(password) < 8 || len(password) > 30 {
return false
}
if (numCount == 0 && alphaCount == 0) || (alphaCount == 0 && specialCount == 0) || (numCount == 0 && specialCount == 0) {
-6
View File
@@ -2,7 +2,6 @@ base:
install_dir: /opt
mode: dev
is_demo: false
is_intl: false
is_offline: false
is_fxplay: false
port: 9999
@@ -10,11 +9,6 @@ base:
password: admin123
version: v2.0.0
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
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More