Compare commits

...
Author SHA1 Message Date
CityFun 6ea07421ac feat: change feishu channel logic (#12465) 2026-04-10 13:36:25 +08:00
蘭 326d52e311 fix: Fix some issues with sharing files (#12466) 2026-04-10 13:36:10 +08:00
CityFun 6e3f751dba feat: add model name required check for agent (#12463) 2026-04-10 03:30:40 +00:00
ssongliu 6982fd22d4 fix: extend agent role command timeout (#12464) 2026-04-10 03:28:39 +00:00
ssongliu 04fd456b63 fix: silence csrf forbidden toast (#12461) 2026-04-09 22:23:47 +08:00
ssongliu bea4bfeee8 feat: update ai agent config texts 2026-04-09 21:33:16 +08:00
蘭 fa6157b610 feat: enhance AI search functionality with response language support (#12460) 2026-04-09 19:00:26 +08:00
CityFun 30af26779c style: change openclaw channel style (#12459) 2026-04-09 10:48:38 +00:00
CityFun 16d5c00b9a fix: fix issue with openclaw discord config error (#12456) 2026-04-09 17:39:01 +08:00
蘭 3f14f8f30e feat: File management supports link share file (#12453) 2026-04-09 17:38:38 +08:00
ssongliu 218f92a960 fix: fallback to password login when mfa needs captcha (#12438) 2026-04-09 10:28:39 +08:00
ssongliu 75be8d3a23 fix: extend ai agent api timeouts (#12443) 2026-04-09 10:27:28 +08:00
ssongliu e2d8125b3d chore: shorten tamper translations (#12442) 2026-04-09 10:26:41 +08:00
CityFun 7ec3c517c2 feat: Modify the configuration of the OpenClaw DingTalk channel. (#12440) 2026-04-09 10:26:22 +08:00
CityFun e186e30d8c feat: remove deployment type for agent website binding (#12435) 2026-04-09 10:26:00 +08:00
CityFun 67a8ba2421 feat: Add app status sync logic for agent (#12436) 2026-04-08 08:36:40 +00:00
dependabot[bot] 24fc15beaf chore(deps): bump github.com/go-jose/go-jose/v4 in /agent (#12432)
Bumps [github.com/go-jose/go-jose/v4](https://github.com/go-jose/go-jose) from 4.1.3 to 4.1.4.
- [Release notes](https://github.com/go-jose/go-jose/releases)
- [Commits](https://github.com/go-jose/go-jose/compare/v4.1.3...v4.1.4)

---
updated-dependencies:
- dependency-name: github.com/go-jose/go-jose/v4
  dependency-version: 4.1.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-07 18:12:42 +08:00
王贺 c1fe70b53e chore: align build workflow runtime versions (#12434) 2026-04-07 18:11:20 +08:00
CityFun 30bf4a8d46 feat: add agent website check for delete (#12433) 2026-04-07 09:56:39 +00:00
CityFun 9e501ce02b feat: Modify the certificate auto-renewal logic. (#12430)
Refs https://github.com/1Panel-dev/1Panel/issues/12389
2026-04-07 09:54:38 +00:00
王贺 d74692a68b fix: optimize runtime script dropdown (#12431) 2026-04-07 17:45:09 +08:00
王贺 57fd80fb3d fix: optimize openresty other page layout (#12429) 2026-04-07 17:44:28 +08:00
KOMATA 39d2c34682 fix: ensure proper resource management in file operations (#12358)
* fix: Ensure proper resource management in file operations by closing files after creation and compression

* fix: Improve error handling for log file operations in MediaFile function
2026-04-07 17:35:31 +08:00
CityFun 3e4a48c892 feat: Modify the OpenClaw plugin installation parameters. (#12427) 2026-04-07 07:33:32 +00:00
CityFun 2c8f8f32fd feat: Add default external port access to application settings. (#12426) 2026-04-07 07:31:32 +00:00
maninhill 3cf6d9ac51 Update user count in README 2026-04-07 15:26:42 +08:00
BugKing 64d8fe8bd0 docs: Update README.md 2026-04-07 15:26:13 +08:00
蘭 8129260508 feat: File management supports AI search (#12415) 2026-04-03 22:11:01 +08:00
ssongliu a40786a7fa refactor: clean up ollama pull log parsing (#12414) 2026-04-03 22:10:47 +08:00
CityFun 368193a777 feat: Add website binding support for the agent (#12413) 2026-04-03 09:15:54 +00:00
ssongliu 61ef725892 feat: improve terminal ai runtime updates (#12409) 2026-04-03 07:58:13 +00:00
CityFun 0c49c41085 feat: change ai menu (#12407) 2026-04-03 07:56:12 +00:00
ssongliu 2a498de96e fix: tighten clean tree delete semantics (#12406)
Refs #12399
2026-04-03 07:54:12 +00:00
ssongliu d055cbb053 feat: improve terminal ai notice interaction 2026-04-03 14:28:09 +08:00
ssongliu 4b3f8a9fd0 style: refine app launcher card layout (#12404) 2026-04-03 11:23:27 +08:00
CityFun 4c26945910 feat: update default model config (#12403) 2026-04-02 10:30:07 +00:00
ssongliu 55b6104b56 fix: reset dashboard delta baseline after resume (#12402)
Refs #12067
2026-04-02 10:28:08 +00:00
CityFun 12dc8aae81 feat: Optimize Monaco editor loading (#12401) 2026-04-02 10:26:08 +00:00
ssongliu 37e2b93693 refactor: replace el-dialog with DialogPro (#12400) 2026-04-02 10:24:09 +00:00
ssongliu 62daf1e019 fix: improve ssh config file editing and include handling (#12398)
Refs #11899
2026-04-02 10:22:09 +00:00
CityFun f5e75d4bfa feat: The skill marketplace supports ClawHub China acceleration. (#12396) 2026-04-02 06:02:06 +00:00
ssongliu 32fed45c1e fix: skip proxy check when disabled (#12388)
Refs #12379
2026-04-01 11:22:08 +00:00
ssongliu 8b7597abef chore: upgrade grpc buildkit and x/image (#12387) 2026-04-01 11:20:08 +00:00
CityFun 01fb9a461b feat: OpenClaw supports configuring backup models. (#12386) 2026-04-01 11:18:09 +00:00
ssongliu bc022a4d21 refactor: simplify agent role binding inputs (#12385) 2026-04-01 11:16:09 +00:00
蘭 973cab2837 fix: Update ESLint plugin and solve the issue of code filter overflow in the development environment (#12383) 2026-04-01 09:14:09 +00:00
ssongliu 5ce155eee6 feat: add agent role binding management (#12382) 2026-04-01 09:12:07 +00:00
CityFun d83b49cc70 feat: Add more configuration options to channels. (#12380) 2026-04-01 08:24:08 +00:00
ssongliu ec981f8153 fix: harden login IP checks and entrance cookie (#12378) 2026-04-01 08:22:09 +00:00
ssongliu b2853277e7 fix: improve auth lock handling and login feedback (#12377) 2026-03-31 11:00:06 +00:00
ssongliu f573a0fa0e fix: improve request validation and session lifecycle (#12375) 2026-03-31 10:58:05 +00:00
CityFun f787734feb feat: Support uninstallation and upgrades of OpenClaw plugins. (#12373) 2026-03-31 10:08:05 +00:00
CityFun 52778f520a feat: QQ and Feishu use official plugins.QQ, Feishu, DingTalk, Telegram, and Discord support multiple accounts. (#12369)
feat: QQ and Feishu use official plugins.QQ, Feishu, DingTalk, Telegram, and Discord support multiple accounts.
2026-03-31 05:48:04 +00:00
ssongliu b5aba18248 fix: harden core memory session handling (#12366) 2026-03-30 14:30:06 +00:00
ssongliu bc9882da50 fix: guard mfa login with ip tracker (#12365) 2026-03-30 22:28:32 +08:00
ssongliu 0ba7e98e8d feat: add agent config role management (#12364) 2026-03-30 08:59:06 +00:00
王贺 e6dc44d135 chore: refine i18n (#12350)
* chore: refine i18n

* fix overview i18n

* feat: remove joinLabel funciton
2026-03-30 16:54:19 +08:00
蘭 a9f30e9f5f feat: Enhance Bark alert configuration options and visibility (#12362) 2026-03-30 06:55:05 +00:00
CityFun f3bf9d0905 feat: support remark for openclaw (#12361)
Refs https://github.com/1Panel-dev/1Panel/issues/12191
2026-03-30 06:53:05 +00:00
Alan 05ea384a00 feat: Add Bark as a new alert notification channel. (#12338) 2026-03-30 10:58:52 +08:00
maninhill 62c50aafe1 Add video introduction link to README
Added a link to a video introduction of 1Panel.
2026-03-27 09:59:17 +08:00
CityFun 6e052b8ef2 fix: Fixed issue with openclaw skill page return empty (#12347) 2026-03-26 17:57:07 +08:00
ssongliu 1d40bdc11b fix: resolve menu item wrapping issue (#12346) 2026-03-26 09:16:24 +00:00
CityFun 3b62197f29 fix: Fixed issue with openclaw skill page return empty (#12345) 2026-03-26 09:10:24 +00:00
222 changed files with 21040 additions and 5109 deletions
+2 -2
View File
@@ -15,7 +15,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '22.19.0'
node-version: '24.11.1'
- name: Build Web
run: |
@@ -26,7 +26,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.24'
go-version: '1.25'
cache-dependency-path: |
core/go.sum
agent/go.sum
+68 -29
View File
@@ -1,17 +1,23 @@
<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>
<h3 align="center">The open-source VPS control panel with native AI agent support</h3>
<p align="center">
Trusted by <strong>2,000,000+</strong> self-hosters worldwide
</p>
<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>
<p align="center">
<a href="https://www.gnu.org/licenses/gpl-3.0.html"><img src="https://shields.io/github/license/1Panel-dev/1Panel?color=%231890FF" alt="License: GPL v3"></a>
<a href="https://app.codacy.com/gh/1Panel-dev/1Panel?utm_source=github.com&utm_medium=referral&utm_content=1Panel-dev/1Panel&utm_campaign=Badge_Grade_Dashboard"><img src="https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef" alt="Codacy"></a>
<a href="https://discord.gg/bUpUqWqdRr" target="_blank">
<img src="https://img.shields.io/discord/1318846410149335080?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb"
alt="chat on Discord"></a>
<a href="https://app.codacy.com/gh/1Panel-dev/1Panel"><img src="https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef" alt="Codacy"></a>
<a href="https://discord.gg/bUpUqWqdRr"><img src="https://img.shields.io/discord/1318846410149335080?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb" alt="Discord"></a>
<a href="https://github.com/1Panel-dev/1Panel/releases"><img src="https://img.shields.io/github/v/release/1Panel-dev/1Panel" alt="GitHub release"></a>
<a href="https://github.com/1Panel-dev/1Panel"><img src="https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square" alt="Stars"></a><br>
<a href="https://github.com/1Panel-dev/1Panel"><img src="https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square" alt="Stars"></a>
</p>
<p align="center">
<a href="/README.md"><img alt="English" src="https://img.shields.io/badge/English-d9d9d9"></a>
<a href="/docs/README.zh-Hans.md"><img alt="中文(简体)" src="https://img.shields.io/badge/中文(简体)-d9d9d9"></a>
@@ -19,58 +25,91 @@
<a href="/docs/README.pt-br.md"><img alt="Português (Brasil)" src="https://img.shields.io/badge/Português (Brasil)-d9d9d9"></a>
<a href="/docs/README.ar.md"><img alt="العربية" src="https://img.shields.io/badge/العربية-d9d9d9"></a>
<a href="/docs/README.de.md"><img alt="Deutsch" src="https://img.shields.io/badge/Deutsch-d9d9d9"></a>
<a href="/docs/README.es.md"><img alt="Español" src="https://img.shields.io/badge/Español-d9d9d9"></a><br>
<a href="/docs/README.es.md"><img alt="Español" src="https://img.shields.io/badge/Español-d9d9d9"></a>
<a href="/docs/README.fr.md"><img alt="français" src="https://img.shields.io/badge/français-d9d9d9"></a>
<a href="/docs/README.ko.md"><img alt="한국어" src="https://img.shields.io/badge/한국어-d9d9d9"></a>
<a href="/docs/README.id.md"><img alt="Bahasa Indonesia" src="https://img.shields.io/badge/Bahasa Indonesia-d9d9d9"></a>
<a href="/docs/README.zh-Hant.md"><img alt="中文(繁體)" src="https://img.shields.io/badge/中文(繁體)-d9d9d9"></a>
<a href="/docs/README.tr.md"><img alt="Türkçe" src="https://img.shields.io/badge/Türkçe-d9d9d9"></a>
<a href="/docs/README.ru.md"><img alt="Русский" src="https://img.shields.io/badge/%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9-d9d9d9"></a>
<a href="/docs/README.ru.md"><img alt="Русский" src="https://img.shields.io/badge/Русский-d9d9d9"></a>
<a href="/docs/README.ms.md"><img alt="Bahasa Melayu" src="https://img.shields.io/badge/Bahasa Melayu-d9d9d9"></a>
</p>
------------------------------
## What is 1Panel?
---
1Panel is a modern, open-source VPS control panel that streamlines administration through an intuitive, clean web interface.
## What is 1Panel?
## Key Features
1Panel is a modern, open-source VPS control panel — and the only one with **native AI agent support**. Run Ollama models, deploy OpenClaw agents, and manage your entire server stack from one clean web interface. No CLI memorization required.
- **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.  
👉 Watch the [2-minute introduction](https://www.youtube.com/watch?v=Jl_wqp-XA08)
## Why 1Panel?
| | 1Panel | cPanel / Plesk | aaPanel | Webmin |
|--|--------|----------------|---------|--------|
| Free & open source | ✅ | ❌ | Partial | ✅ |
| Native AI agent runtime | ✅ | ❌ | ❌ | ❌ |
| One-click app marketplace | ✅ 165+ apps | ❌ | ✅ | ❌ |
| Modern UI (post-2020) | ✅ | ❌ | Partial | ❌ |
| Docker / container management | ✅ | ❌ | ❌ | ❌ |
| Active development | ✅ | ✅ | ✅ | Slow |
## Key Features
- **AI Agent Runtime**: Deploy Ollama LLMs, spin up OpenClaw personal agents, and monitor GPU utilization — all from the dashboard. No separate AI stack to manage.
- **One-Click Website Deployment**: Launch production-ready websites with automatic domain binding, SSL provisioning, and Nginx config — zero manual setup.
- **App Marketplace**: 165+ trusted open-source apps (Nextcloud, Bitwarden, Umami, NocoBase, and more) installed and updated with a single click.
- **Docker & Container Management**: Create, start, stop, and inspect containers, images, networks, and volumes through a visual UI — no CLI juggling.
- **Security Out of the Box**: Firewall rules, fail2ban, container isolation, WAF, and audit logs — configured and running from day one.
- **Backup & Restore**: Schedule automated backups to AWS S3, Cloudflare R2, or local storage. Restore any snapshot in one click.
## Quick Start
Execute the script below and follow the prompts to install 1Panel:
> **Requirements:** Linux VPS (Debian / Ubuntu / CentOS / Rocky), 1 GB RAM, internet access.
> Takes ~60 seconds.
```bash
bash -c "$(curl -sSL https://resource.1panel.pro/v2/quick_start.sh)"
```
After installation, open `http://<your-server-ip>:<port>/<security-path>` in your browser.
Run `1pctl user-info` via SSH if you need to retrieve your access credentials.
## Screenshot
![UI Display](https://resource.1panel.pro/img/overview_en_v2.png)
![1Panel UI](https://resource.1panel.pro/img/overview_en_v2.png)
## Pro Edition
1Panel OSS is free forever. Pro adds features built for teams and production workloads:
| Feature | OSS | Pro |
|---------|:---:|:---:|
| One-click app installs | ✅ | ✅ |
| AI agents (OpenClaw) | 1 agent | Unlimited |
| WAF & advanced security | Basic | ✅ |
| Website tamper protection | ❌ | ✅ |
| Website uptime monitoring | ❌ | ✅ |
| Multi-node management | ❌ | ✅ |
| Custom logo & theme | ❌ | ✅ |
| Priority support | ❌ | ✅ |
**From $80/year.** [Compare plans & start 30-day free trial →](https://1panel.pro/pricing)
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=1Panel-dev/1Panel&type=Date)](https://star-history.com/#1Panel-dev/1Panel&Date)
## Pro Edition
## Community & Support
Compared to the OSS Edition, 1Panel Pro Edition provides users with a wealth of enhanced features and technical support services. Enhanced features include WAF enhancement, website tamper protection, website monitoring, GPU monitoring, custom logo and theme color, etc. [Click to view the detailed introduction of the Pro Edition](https://1panel.pro/pricing).
- **Discord** — [Join the community](https://discord.gg/bUpUqWqdRr) for help, feature requests, and show-and-tell
- **Docs** — [1panel.pro/docs](https://1panel.pro/docs)
- **Issues** — [GitHub Issues](https://github.com/1Panel-dev/1Panel/issues) for bug reports
## Security Information
## Security
If you discover any security issues, please refer to [SECURITY.md](/SECURITY.md).
Found a vulnerability? Please read [SECURITY.md](/SECURITY.md) before disclosing.
## License
Licensed under The GNU General Public License version 3 (GPLv3) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
<https://www.gnu.org/licenses/gpl-3.0.html>
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Licensed under the [GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.html).
+286
View File
@@ -51,6 +51,27 @@ func (b *BaseApi) PageAgents(c *gin.Context) {
})
}
// @Tags AI
// @Summary Delete check Agent
// @Accept json
// @Param request body dto.AgentIDReq true "request"
// @Success 200 {array} dto.AppResource
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/agents/delete/check [post]
func (b *BaseApi) DeleteCheckAgent(c *gin.Context) {
var req dto.AgentIDReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
checkData, err := agentService.DeleteCheck(req)
if err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithData(c, checkData)
}
// @Tags AI
// @Summary Delete Agent
// @Accept json
@@ -91,6 +112,67 @@ func (b *BaseApi) ResetAgentToken(c *gin.Context) {
helper.Success(c)
}
// @Tags AI
// @Summary Update Agent remark
// @Accept json
// @Param request body dto.AgentRemarkUpdateReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/agents/remark [post]
func (b *BaseApi) UpdateAgentRemark(c *gin.Context) {
var req dto.AgentRemarkUpdateReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := agentService.UpdateRemark(req); err != nil {
helper.BadRequest(c, err)
return
}
helper.Success(c)
}
// @Tags AI
// @Summary Bind Agent website
// @Accept json
// @Param request body dto.AgentWebsiteBindReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/agents/website/bind [post]
func (b *BaseApi) BindAgentWebsite(c *gin.Context) {
var req dto.AgentWebsiteBindReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := agentService.BindWebsite(req); err != nil {
helper.BadRequest(c, err)
return
}
helper.Success(c)
}
// @Tags AI
// @Summary Get Agent model config
// @Accept json
// @Param request body dto.AgentIDReq true "request"
// @Success 200 {object} dto.AgentModelConfig
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/agents/model/get [post]
func (b *BaseApi) GetAgentModelConfig(c *gin.Context) {
var req dto.AgentIDReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
res, err := agentService.GetModelConfig(req)
if err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithData(c, res)
}
// @Tags AI
// @Summary Update Agent model config
// @Accept json
@@ -332,6 +414,170 @@ func (b *BaseApi) DeleteAgentAccount(c *gin.Context) {
helper.Success(c)
}
// @Tags AI
// @Summary Create Agent role
// @Accept json
// @Param request body dto.AgentRoleCreateReq true "request"
// @Success 200 {object} dto.AgentRoleCreateResp
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/agents/agent/create [post]
func (b *BaseApi) CreateAgentRole(c *gin.Context) {
var req dto.AgentRoleCreateReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := agentService.CreateRole(req)
if err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags AI
// @Summary Delete Agent role
// @Accept json
// @Param request body dto.AgentRoleDeleteReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/agents/agent/delete [post]
func (b *BaseApi) DeleteAgentRole(c *gin.Context) {
var req dto.AgentRoleDeleteReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := agentService.DeleteRole(req); err != nil {
helper.BadRequest(c, err)
return
}
helper.Success(c)
}
// @Tags AI
// @Summary Bind Agent role channel
// @Accept json
// @Param request body dto.AgentRoleBindReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/agents/agent/bind [post]
func (b *BaseApi) BindAgentRole(c *gin.Context) {
var req dto.AgentRoleBindReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := agentService.BindRole(req); err != nil {
helper.BadRequest(c, err)
return
}
helper.Success(c)
}
// @Tags AI
// @Summary Unbind Agent role channel
// @Accept json
// @Param request body dto.AgentRoleBindReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/agents/agent/unbind [post]
func (b *BaseApi) UnbindAgentRole(c *gin.Context) {
var req dto.AgentRoleBindReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := agentService.UnbindRole(req); err != nil {
helper.BadRequest(c, err)
return
}
helper.Success(c)
}
// @Tags AI
// @Summary Get configured Agent roles from config file
// @Accept json
// @Param request body dto.AgentConfiguredAgentsReq true "request"
// @Success 200 {array} dto.AgentConfiguredAgentItem
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/agents/agent/list [post]
func (b *BaseApi) GetConfiguredAgentRoles(c *gin.Context) {
var req dto.AgentConfiguredAgentsReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := agentService.GetConfiguredAgents(req)
if err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags AI
// @Summary Get Agent role channels from config file
// @Accept json
// @Param request body dto.AgentRoleChannelsReq true "request"
// @Success 200 {array} dto.AgentRoleChannelItem
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/agents/agent/channels [post]
func (b *BaseApi) GetAgentRoleChannels(c *gin.Context) {
var req dto.AgentRoleChannelsReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := agentService.GetRoleChannels(req)
if err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags AI
// @Summary Get Agent role markdown files
// @Accept json
// @Param request body dto.AgentRoleMarkdownFilesReq true "request"
// @Success 200 {array} dto.AgentRoleMarkdownFileItem
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/agents/agent/md/list [post]
func (b *BaseApi) GetAgentRoleMarkdownFiles(c *gin.Context) {
var req dto.AgentRoleMarkdownFilesReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := agentService.GetRoleMarkdownFiles(req)
if err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags AI
// @Summary Update Agent role markdown file
// @Accept json
// @Param request body dto.AgentRoleMarkdownFilesUpdateReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/agents/agent/md/update [post]
func (b *BaseApi) UpdateAgentRoleMarkdownFile(c *gin.Context) {
var req dto.AgentRoleMarkdownFilesUpdateReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := agentService.UpdateRoleMarkdownFiles(req); err != nil {
helper.BadRequest(c, err)
return
}
helper.Success(c)
}
// @Tags AI
// @Summary Get Agent Feishu channel config
// @Accept json
@@ -598,6 +844,46 @@ func (b *BaseApi) InstallAgentPlugin(c *gin.Context) {
helper.Success(c)
}
// @Tags AI
// @Summary Upgrade Agent plugin
// @Accept json
// @Param request body dto.AgentPluginUpgradeReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/agents/plugin/upgrade [post]
func (b *BaseApi) UpgradeAgentPlugin(c *gin.Context) {
var req dto.AgentPluginUpgradeReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := agentService.UpgradePlugin(req); err != nil {
helper.BadRequest(c, err)
return
}
helper.Success(c)
}
// @Tags AI
// @Summary Uninstall Agent plugin
// @Accept json
// @Param request body dto.AgentPluginUninstallReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/agents/plugin/uninstall [post]
func (b *BaseApi) UninstallAgentPlugin(c *gin.Context) {
var req dto.AgentPluginUninstallReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := agentService.UninstallPlugin(req); err != nil {
helper.BadRequest(c, err)
return
}
helper.Success(c)
}
// @Tags AI
// @Summary Check Agent plugin installation status
// @Accept json
+7 -6
View File
@@ -36,12 +36,13 @@ var (
cronjobService = service.NewICronjobService()
fileService = service.NewIFileService()
sshService = service.NewISSHService()
firewallService = service.NewIFirewallService()
iptablesService = service.NewIIptablesService()
monitorService = service.NewIMonitorService()
systemService = service.NewISystemService()
fileService = service.NewIFileService()
fileShareService = service.NewIFileShareService()
sshService = service.NewISSHService()
firewallService = service.NewIFirewallService()
iptablesService = service.NewIIptablesService()
monitorService = service.NewIMonitorService()
systemService = service.NewISystemService()
deviceService = service.NewIDeviceService()
fail2banService = service.NewIFail2BanService()
+267
View File
@@ -25,6 +25,7 @@ import (
websocket2 "github.com/1Panel-dev/1Panel/agent/utils/websocket"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
qrcode "github.com/skip2/go-qrcode"
)
// @Tags File
@@ -48,6 +49,31 @@ func (b *BaseApi) ListFiles(c *gin.Context) {
helper.SuccessWithData(c, fileList)
}
// @Tags File
// @Summary File search: content grep + optional AI summary
// @Description When file-management AI is enabled, returns mode=ai with summary and hits. When disabled, returns mode=grep with hits only. Scans file contents only. Supports match options, extension/size/time filters, and scan limits.
// @Accept json
// @Param request body request.FileAISearch true "request"
// @Success 200 {object} response.FileAISearchResult
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /files/ai-search [post]
func (b *BaseApi) FileAISearch(c *gin.Context) {
var req request.FileAISearch
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if strings.TrimSpace(req.ResponseLanguage) == "" {
req.ResponseLanguage = strings.TrimSpace(c.GetHeader("Accept-Language"))
}
res, err := fileService.AISearch(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, res)
}
// @Tags File
// @Summary Page file
// @Accept json
@@ -1038,3 +1064,244 @@ func (b *BaseApi) SetFileRemark(c *gin.Context) {
}
helper.Success(c)
}
// @Tags File
// @Summary List file shares
// @Accept json
// @Param request body dto.PageInfo true "request"
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /files/share/search [post]
func (b *BaseApi) SearchFileShare(c *gin.Context) {
var req dto.PageInfo
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
total, list, err := fileShareService.Page(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Total: total,
Items: list,
})
}
// @Tags File
// @Summary Get file share detail by path
// @Accept json
// @Param request body dto.FilePath true "request"
// @Success 200 {object} response.FileShareInfo
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /files/share/detail [post]
func (b *BaseApi) GetFileShareDetail(c *gin.Context) {
var req dto.FilePath
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
info, err := fileShareService.GetByPath(req.Path)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, info)
}
// @Tags File
// @Summary Get file share detail by code (no login)
// @Param code query string true "share code"
// @Success 200 {object} response.FileSharePublicInfo
// @Router /files/share/info [get]
func (b *BaseApi) GetPublicFileShareInfo(c *gin.Context) {
code := strings.TrimSpace(c.Query("code"))
if code == "" {
helper.BadRequest(c, errors.New("code is required"))
return
}
info, err := fileShareService.GetPublicByCode(code)
if err != nil {
if be, ok := err.(buserr.BusinessError); ok {
helper.ErrorWithDetail(c, http.StatusBadRequest, be.Msg, be)
return
}
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, info)
}
func buildSharePublicURL(c *gin.Context, code, operateNode string) string {
scheme := strings.TrimSpace(c.GetHeader("X-Forwarded-Proto"))
if scheme == "" {
if c.Request.TLS != nil {
scheme = "https"
} else {
scheme = "http"
}
}
host := strings.TrimSpace(c.GetHeader("X-Forwarded-Host"))
if host == "" {
host = c.Request.Host
}
shareURL := url.URL{
Scheme: scheme,
Host: host,
Path: "/s/" + url.PathEscape(code),
}
query := shareURL.Query()
if strings.TrimSpace(operateNode) != "" {
query.Set("operateNode", operateNode)
}
shareURL.RawQuery = query.Encode()
return shareURL.String()
}
// @Tags File
// @Summary Get file share QR code image
// @Produce png
// @Param code query string true "share code"
// @Param operateNode query string false "operate node"
// @Success 200 {file} file
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /files/share/qrcode [get]
func (b *BaseApi) GetFileShareQRCode(c *gin.Context) {
code := strings.TrimSpace(c.Query("code"))
if code == "" {
helper.BadRequest(c, errors.New("code is required"))
return
}
if _, err := fileShareService.GetByCode(code); err != nil {
if be, ok := err.(buserr.BusinessError); ok {
helper.ErrorWithDetail(c, http.StatusBadRequest, be.Msg, be)
return
}
helper.InternalServer(c, err)
return
}
png, err := qrcode.Encode(buildSharePublicURL(c, code, c.Query("operateNode")), qrcode.Medium, 256)
if err != nil {
helper.InternalServer(c, err)
return
}
c.Header("Cache-Control", "private, max-age=300")
c.Data(http.StatusOK, "image/png", png)
}
// @Tags File
// @Summary Create temporary file share link
// @Accept json
// @Param request body request.FileShareCreate true "request"
// @Success 200 {object} response.FileShareInfo
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /files/share/create [post]
// @x-panel-log {"bodyKeys":["path","expireMinutes"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建文件分享 [path]","formatEN":"Create file share [path]"}
func (b *BaseApi) CreateFileShare(c *gin.Context) {
var req request.FileShareCreate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
res, err := fileShareService.Create(req)
if err != nil {
if be, ok := err.(buserr.BusinessError); ok {
helper.ErrorWithDetail(c, http.StatusInternalServerError, be.Msg, be.Err)
return
}
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, res)
}
// @Tags File
// @Summary Delete file share by path
// @Accept json
// @Param request body dto.FilePath true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /files/share/del [post]
// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"关闭文件分享 [path]","formatEN":"Close file share [path]"}
func (b *BaseApi) DeleteFileShare(c *gin.Context) {
var req dto.FilePath
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := fileShareService.DeleteByPath(req.Path); err != nil {
if be, ok := err.(buserr.BusinessError); ok {
helper.ErrorWithDetail(c, http.StatusInternalServerError, be.Msg, be.Err)
return
}
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags File
// @Summary Check file share code (no login)
// @Param code query string true "share code"
// @Param password query string false "optional password"
// @Success 200 {object} dto.Response
// @Router /files/share/check [get]
func (b *BaseApi) CheckFileShare(c *gin.Context) {
code := strings.TrimSpace(c.Query("code"))
password := c.Query("password")
if code == "" {
helper.BadRequest(c, errors.New("code is required"))
return
}
if err := fileShareService.Check(code, password); err != nil {
if be, ok := err.(buserr.BusinessError); ok {
helper.ErrorWithDetail(c, http.StatusBadRequest, be.Msg, be)
return
}
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags File
// @Summary Download file by share code (no login)
// @Produce octet-stream
// @Param code query string true "share code"
// @Param password query string false "optional password"
// @Success 200 {file} file
// @Router /files/share/download [get]
func (b *BaseApi) DownloadFileShare(c *gin.Context) {
code := strings.TrimSpace(c.Query("code"))
password := c.Query("password")
if code == "" {
helper.BadRequest(c, errors.New("code is required"))
return
}
filePath, displayName, err := fileShareService.PrepareDownload(code, password)
if err != nil {
if be, ok := err.(buserr.BusinessError); ok {
helper.ErrorWithDetail(c, http.StatusBadRequest, be.Msg, be)
return
}
helper.InternalServer(c, err)
return
}
file, err := os.Open(filePath)
if err != nil {
helper.InternalServer(c, err)
return
}
defer file.Close()
info, err := file.Stat()
if err != nil {
helper.InternalServer(c, err)
return
}
c.Header("Content-Length", strconv.FormatInt(info.Size(), 10))
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(displayName))
http.ServeContent(c.Writer, c.Request, displayName, info.ModTime(), file)
}
+21
View File
@@ -81,6 +81,27 @@ func (b *BaseApi) UpdateTerminalAISetting(c *gin.Context) {
helper.Success(c)
}
func (b *BaseApi) GetFileManageAISettingInfo(c *gin.Context) {
setting, err := settingService.GetFileManageAIInfo()
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, setting)
}
func (b *BaseApi) UpdateFileManageAISetting(c *gin.Context) {
var req dto.FileManageAIInfo
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := settingService.UpdateFileManageAI(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags System Setting
// @Summary Load local backup dir
// @Success 200 {string} path
+2 -2
View File
@@ -249,14 +249,14 @@ func (b *BaseApi) LoadSSHFile(c *gin.Context) {
// @Tags SSH
// @Summary Update host SSH setting by file
// @Accept json
// @Param request body dto.SSHConf true "request"
// @Param request body dto.SSHConfUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/ssh/file/update [post]
// @x-panel-log {"bodyKeys":["key"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改 SSH 配置文件 [key]","formatEN":"update SSH conf [key]"}
func (b *BaseApi) UpdateSSHByFile(c *gin.Context) {
var req dto.SettingUpdate
var req dto.SSHConfUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
+282 -93
View File
@@ -4,6 +4,7 @@ import "time"
type AgentCreateReq struct {
Name string `json:"name" validate:"required"`
Remark string `json:"remark"`
AppVersion string `json:"appVersion" validate:"required"`
WebUIPort int `json:"webUIPort" validate:"required"`
BridgePort int `json:"bridgePort"`
@@ -27,30 +28,34 @@ 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"`
APIType string `json:"apiType"`
MaxTokens int `json:"maxTokens"`
ContextWindow int `json:"contextWindow"`
BaseURL string `json:"baseUrl"`
APIKey string `json:"apiKey"`
Token string `json:"token"`
Status string `json:"status"`
Message string `json:"message"`
AppInstallID uint `json:"appInstallId"`
AccountID uint `json:"accountId"`
AppVersion string `json:"appVersion"`
Container string `json:"containerName"`
WebUIPort int `json:"webUIPort"`
BridgePort int `json:"bridgePort"`
Path string `json:"path"`
ConfigPath string `json:"configPath"`
Upgradable bool `json:"upgradable"`
CreatedAt time.Time `json:"createdAt"`
ID uint `json:"id"`
Name string `json:"name"`
Remark string `json:"remark"`
AgentType string `json:"agentType"`
Provider string `json:"provider"`
ProviderName string `json:"providerName"`
Model string `json:"model"`
APIType string `json:"apiType"`
MaxTokens int `json:"maxTokens"`
ContextWindow int `json:"contextWindow"`
BaseURL string `json:"baseUrl"`
APIKey string `json:"apiKey"`
Token string `json:"token"`
Status string `json:"status"`
Message string `json:"message"`
AppInstallID uint `json:"appInstallId"`
WebsiteID uint `json:"websiteId"`
WebsitePrimaryDomain string `json:"websitePrimaryDomain"`
WebsiteProtocol string `json:"websiteProtocol"`
AccountID uint `json:"accountId"`
AppVersion string `json:"appVersion"`
Container string `json:"containerName"`
WebUIPort int `json:"webUIPort"`
BridgePort int `json:"bridgePort"`
Path string `json:"path"`
ConfigPath string `json:"configPath"`
Upgradable bool `json:"upgradable"`
CreatedAt time.Time `json:"createdAt"`
}
type AgentDeleteReq struct {
@@ -63,10 +68,27 @@ type AgentTokenResetReq struct {
ID uint `json:"id" validate:"required"`
}
type AgentRemarkUpdateReq struct {
ID uint `json:"id" validate:"required"`
Remark string `json:"remark"`
}
type AgentWebsiteBindReq struct {
AgentID uint `json:"agentId" validate:"required"`
WebsiteID uint `json:"websiteId" validate:"required"`
}
type AgentModelConfigUpdateReq struct {
AgentID uint `json:"agentId" validate:"required"`
AccountID uint `json:"accountId" validate:"required"`
Model string `json:"model" validate:"required"`
AgentID uint `json:"agentId" validate:"required"`
AccountID uint `json:"accountId" validate:"required"`
Model string `json:"model" validate:"required"`
Fallbacks []string `json:"fallbacks"`
}
type AgentModelConfig struct {
AccountID uint `json:"accountId"`
Model string `json:"model"`
Fallbacks []string `json:"fallbacks"`
}
type AgentOverviewReq struct {
@@ -81,6 +103,79 @@ type AgentOverview struct {
Snapshot AgentOverviewSnapshot `json:"snapshot"`
}
type AgentRoleBinding struct {
Channel string `json:"channel" validate:"required"`
AccountID string `json:"accountId"`
}
type AgentRoleCreateReq struct {
AgentID uint `json:"agentId" validate:"required"`
Name string `json:"name" validate:"required"`
Model string `json:"model"`
Bindings []AgentRoleBinding `json:"bindings"`
}
type AgentRoleCreateResp struct {
Output string `json:"output"`
}
type AgentRoleDeleteReq struct {
AgentID uint `json:"agentId" validate:"required"`
ID string `json:"id" validate:"required"`
}
type AgentRoleBindReq struct {
AgentID uint `json:"agentId" validate:"required"`
ID string `json:"id" validate:"required"`
Channel string `json:"channel" validate:"required"`
AccountID string `json:"accountId"`
}
type AgentConfiguredAgentsReq struct {
AgentID uint `json:"agentId" validate:"required"`
}
type AgentRoleChannelsReq struct {
AgentID uint `json:"agentId" validate:"required"`
}
type AgentRoleChannelItem struct {
Name string `json:"name"`
Bound bool `json:"bound"`
AccountIDs []string `json:"accountIds"`
}
type AgentRoleMarkdownFilesReq struct {
AgentID uint `json:"agentId" validate:"required"`
Workspace string `json:"workspace" validate:"required"`
}
type AgentConfiguredAgentItem struct {
ID string `json:"id"`
Name string `json:"name"`
Workspace string `json:"workspace"`
Model string `json:"model"`
AgentDir string `json:"agentDir"`
Bindings []AgentRoleBinding `json:"bindings"`
}
type AgentRoleMarkdownFileItem struct {
Name string `json:"name"`
Content string `json:"content"`
}
type AgentRoleMarkdownFileUpdateItem struct {
Name string `json:"name" validate:"required,oneof=AGENTS.md SOUL.md USER.md IDENTITY.md TOOLS.md HEARTBEAT.md BOOT.md BOOTSTRAP.md"`
Content string `json:"content"`
}
type AgentRoleMarkdownFilesUpdateReq struct {
AgentID uint `json:"agentId" validate:"required"`
Workspace string `json:"workspace" validate:"required"`
Restart bool `json:"restart"`
Files []AgentRoleMarkdownFileUpdateItem `json:"files" validate:"required"`
}
type AgentOverviewSnapshot struct {
ContainerStatus string `json:"containerStatus"`
AppVersion string `json:"appVersion"`
@@ -195,12 +290,15 @@ type AgentFeishuConfigReq struct {
}
type AgentFeishuConfigUpdateReq struct {
AgentID uint `json:"agentId" validate:"required"`
BotName string `json:"botName" validate:"required"`
AppID string `json:"appId" validate:"required"`
AppSecret string `json:"appSecret" validate:"required"`
Enabled bool `json:"enabled"`
DmPolicy string `json:"dmPolicy" validate:"required"`
AgentID uint `json:"agentId" validate:"required"`
Enabled bool `json:"enabled"`
ThreadSession bool `json:"threadSession"`
ReplyMode string `json:"replyMode" validate:"required"`
Streaming bool `json:"streaming"`
RequireMention string `json:"requireMention" validate:"required,oneof=true false open"`
GroupPolicy string `json:"groupPolicy" validate:"required,oneof=open allowlist disabled"`
GroupAllowFrom []string `json:"groupAllowFrom"`
Bots []AgentFeishuBot `json:"bots" validate:"required,min=1"`
}
type AgentFeishuPairingApproveReq struct {
@@ -209,11 +307,15 @@ type AgentFeishuPairingApproveReq struct {
}
type AgentFeishuConfig struct {
Enabled bool `json:"enabled"`
DmPolicy string `json:"dmPolicy"`
BotName string `json:"botName"`
AppID string `json:"appId"`
AppSecret string `json:"appSecret"`
Enabled bool `json:"enabled"`
ThreadSession bool `json:"threadSession"`
ReplyMode string `json:"replyMode"`
Streaming bool `json:"streaming"`
RequireMention string `json:"requireMention"`
GroupPolicy string `json:"groupPolicy"`
GroupAllowFrom []string `json:"groupAllowFrom"`
Bots []AgentFeishuBot `json:"bots"`
Installed bool `json:"installed"`
}
type AgentTelegramConfigReq struct {
@@ -221,113 +323,200 @@ type AgentTelegramConfigReq struct {
}
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"`
AgentID uint `json:"agentId" validate:"required"`
Enabled bool `json:"enabled"`
DmPolicy string `json:"dmPolicy" validate:"required,oneof=pairing open allowlist disabled"`
AllowFrom []string `json:"allowFrom"`
GroupPolicy string `json:"groupPolicy" validate:"required,oneof=open allowlist disabled"`
GroupAllowFrom []string `json:"groupAllowFrom"`
Proxy string `json:"proxy"`
Streaming string `json:"streaming" validate:"required,oneof=off partial block progress"`
DefaultAccount string `json:"defaultAccount" validate:"required"`
Bots []AgentTelegramBot `json:"bots" validate:"required,min=1"`
}
type AgentTelegramConfig struct {
Enabled bool `json:"enabled"`
DmPolicy string `json:"dmPolicy"`
BotToken string `json:"botToken"`
Proxy string `json:"proxy"`
Enabled bool `json:"enabled"`
DmPolicy string `json:"dmPolicy"`
AllowFrom []string `json:"allowFrom"`
GroupPolicy string `json:"groupPolicy"`
GroupAllowFrom []string `json:"groupAllowFrom"`
Proxy string `json:"proxy"`
Streaming string `json:"streaming"`
DefaultAccount string `json:"defaultAccount"`
Bots []AgentTelegramBot `json:"bots"`
}
type AgentChannelPairingApproveReq struct {
AgentID uint `json:"agentId" validate:"required"`
Type string `json:"type" validate:"required,oneof=feishu telegram discord wecom dingtalk-connector"`
Type string `json:"type" validate:"required,oneof=feishu telegram discord wecom"`
PairingCode string `json:"pairingCode" validate:"required"`
AccountID string `json:"accountId"`
}
type AgentWecomConfigUpdateReq struct {
AgentID uint `json:"agentId" validate:"required"`
Enabled bool `json:"enabled"`
DmPolicy string `json:"dmPolicy" validate:"required,oneof=pairing open"`
BotID string `json:"botId" validate:"required"`
Secret string `json:"secret" validate:"required"`
}
type AgentWecomConfig struct {
Enabled bool `json:"enabled"`
DmPolicy string `json:"dmPolicy"`
BotID string `json:"botId"`
Secret string `json:"secret"`
Installed bool `json:"installed"`
}
type AgentDingTalkConfigUpdateReq struct {
AgentID uint `json:"agentId" validate:"required"`
Enabled bool `json:"enabled"`
ClientID string `json:"clientId" validate:"required"`
ClientSecret string `json:"clientSecret" validate:"required"`
DmPolicy string `json:"dmPolicy" validate:"required,oneof=pairing allowlist open disabled"`
DmPolicy string `json:"dmPolicy" validate:"required,oneof=pairing open allowlist disabled"`
AllowFrom []string `json:"allowFrom"`
GroupPolicy string `json:"groupPolicy" validate:"required,oneof=open allowlist disabled"`
GroupAllowFrom []string `json:"groupAllowFrom"`
BotID string `json:"botId" validate:"required"`
Secret string `json:"secret" validate:"required"`
}
type AgentDingTalkConfig struct {
type AgentWecomConfig struct {
Enabled bool `json:"enabled"`
ClientID string `json:"clientId"`
ClientSecret string `json:"clientSecret"`
DmPolicy string `json:"dmPolicy"`
AllowFrom []string `json:"allowFrom"`
GroupPolicy string `json:"groupPolicy"`
GroupAllowFrom []string `json:"groupAllowFrom"`
BotID string `json:"botId"`
Secret string `json:"secret"`
Installed bool `json:"installed"`
}
type AgentDingTalkConfigUpdateReq struct {
AgentID uint `json:"agentId" validate:"required"`
Enabled bool `json:"enabled"`
DmPolicy string `json:"dmPolicy" validate:"required,oneof=allowlist open disabled"`
AllowFrom []string `json:"allowFrom"`
GroupPolicy string `json:"groupPolicy" validate:"required,oneof=open allowlist disabled"`
GroupAllowFrom []string `json:"groupAllowFrom"`
SeparateSessionByConversation bool `json:"separateSessionByConversation"`
GroupSessionScope string `json:"groupSessionScope" validate:"required,oneof=group group_sender"`
SharedMemoryAcrossConversations bool `json:"sharedMemoryAcrossConversations"`
AsyncMode bool `json:"asyncMode"`
AckText string `json:"ackText"`
Bots []AgentDingTalkBot `json:"bots" validate:"required,min=1"`
}
type AgentDingTalkConfig struct {
Enabled bool `json:"enabled"`
DmPolicy string `json:"dmPolicy"`
AllowFrom []string `json:"allowFrom"`
GroupPolicy string `json:"groupPolicy"`
GroupAllowFrom []string `json:"groupAllowFrom"`
SeparateSessionByConversation bool `json:"separateSessionByConversation"`
GroupSessionScope string `json:"groupSessionScope"`
SharedMemoryAcrossConversations bool `json:"sharedMemoryAcrossConversations"`
AsyncMode bool `json:"asyncMode"`
AckText string `json:"ackText"`
Bots []AgentDingTalkBot `json:"bots"`
Installed bool `json:"installed"`
}
type AgentWeixinLoginReq struct {
AgentID uint `json:"agentId" validate:"required"`
TaskID string `json:"taskID" validate:"required"`
}
type AgentQQBotConfigUpdateReq struct {
AgentID uint `json:"agentId" validate:"required"`
Enabled bool `json:"enabled"`
AppID string `json:"appId" validate:"required"`
ClientSecret string `json:"clientSecret" validate:"required"`
AgentID uint `json:"agentId" validate:"required"`
Enabled bool `json:"enabled"`
Bots []AgentQQBotBot `json:"bots" validate:"required,min=1"`
}
type AgentQQBotConfig struct {
Enabled bool `json:"enabled"`
AppID string `json:"appId"`
ClientSecret string `json:"clientSecret"`
Installed bool `json:"installed"`
Enabled bool `json:"enabled"`
Bots []AgentQQBotBot `json:"bots"`
Installed bool `json:"installed"`
}
type AgentPluginInstallReq struct {
AgentID uint `json:"agentId" validate:"required"`
Type string `json:"type" validate:"required,oneof=qqbot wecom dingtalk weixin"`
Type string `json:"type" validate:"required,oneof=feishu qqbot wecom dingtalk weixin"`
TaskID string `json:"taskID" validate:"required"`
}
type AgentPluginUpgradeReq struct {
AgentID uint `json:"agentId" validate:"required"`
Type string `json:"type" validate:"required,oneof=feishu qqbot wecom dingtalk weixin"`
TaskID string `json:"taskID" validate:"required"`
}
type AgentPluginUninstallReq struct {
AgentID uint `json:"agentId" validate:"required"`
Type string `json:"type" validate:"required,oneof=feishu qqbot wecom dingtalk weixin"`
TaskID string `json:"taskID" validate:"required"`
}
type AgentPluginCheckReq struct {
AgentID uint `json:"agentId" validate:"required"`
Type string `json:"type" validate:"required,oneof=qqbot wecom dingtalk weixin"`
AgentID uint `json:"agentId" validate:"required"`
Type string `json:"type" validate:"required,oneof=feishu qqbot wecom dingtalk weixin"`
CheckLatest bool `json:"checkLatest"`
}
type AgentPluginStatus struct {
Installed bool `json:"installed"`
Installed bool `json:"installed"`
CurrentVersion string `json:"currentVersion"`
LatestVersion string `json:"latestVersion"`
Upgradable bool `json:"upgradable"`
}
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"`
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"`
Proxy string `json:"proxy"`
DefaultAccount string `json:"defaultAccount" validate:"required"`
Bots []AgentDiscordBot `json:"bots" validate:"required,min=1"`
}
type AgentDiscordConfig struct {
Enabled bool `json:"enabled"`
Enabled bool `json:"enabled"`
DmPolicy string `json:"dmPolicy"`
GroupPolicy string `json:"groupPolicy"`
Proxy string `json:"proxy"`
DefaultAccount string `json:"defaultAccount"`
Bots []AgentDiscordBot `json:"bots"`
}
type AgentChannelBotBase struct {
AccountID string `json:"accountId"`
Name string `json:"name"`
Enabled bool `json:"enabled"`
IsDefault bool `json:"isDefault"`
}
func (b AgentChannelBotBase) IsEnabled() bool {
return b.Enabled
}
type AgentFeishuBot struct {
AgentChannelBotBase
AppID string `json:"appId"`
AppSecret string `json:"appSecret"`
DmPolicy string `json:"dmPolicy"`
AllowFrom []string `json:"allowFrom"`
}
type AgentTelegramBot struct {
AgentChannelBotBase
BotToken string `json:"botToken"`
DmPolicy string `json:"dmPolicy"`
GroupPolicy string `json:"groupPolicy"`
Token string `json:"token"`
Proxy string `json:"proxy"`
Streaming string `json:"streaming"`
}
type AgentDiscordBot struct {
AgentChannelBotBase
Token string `json:"token"`
}
type AgentQQBotBot struct {
AgentChannelBotBase
AppID string `json:"appId"`
ClientSecret string `json:"clientSecret"`
AllowFrom []string `json:"allowFrom"`
SystemPrompt string `json:"systemPrompt"`
}
type AgentDingTalkBot struct {
AgentChannelBotBase
ClientID string `json:"clientId"`
ClientSecret string `json:"clientSecret"`
}
type AgentSecurityConfigUpdateReq struct {
@@ -367,7 +556,7 @@ type AgentConfigFile struct {
type AgentSkillSearchReq struct {
AgentID uint `json:"agentId" validate:"required"`
Source string `json:"source" validate:"required,oneof=clawhub skillhub"`
Source string `json:"source" validate:"required,oneof=clawhub-global clawhub-cn skillhub"`
Keyword string `json:"keyword" validate:"required"`
}
@@ -397,7 +586,7 @@ type AgentSkillUpdateReq struct {
type AgentSkillInstallReq struct {
AgentID uint `json:"agentId" validate:"required"`
Source string `json:"source" validate:"required,oneof=clawhub skillhub"`
Source string `json:"source" validate:"required,oneof=clawhub-global clawhub-cn skillhub"`
Slug string `json:"slug" validate:"required"`
TaskID string `json:"taskID" validate:"required"`
}
+29
View File
@@ -9,6 +9,29 @@ type FileOption struct {
files.FileOption
}
type FileAISearch struct {
Path string `json:"path" validate:"required"`
Query string `json:"query" validate:"required"`
ResponseLanguage string `json:"responseLanguage,omitempty"`
ContainSub *bool `json:"containSub,omitempty"`
MaxItems int `json:"maxItems" validate:"omitempty,min=1,max=2000"`
MatchCase bool `json:"matchCase"`
WholeWord bool `json:"wholeWord"`
UseRegex bool `json:"useRegex"`
Extensions []string `json:"extensions,omitempty"`
MinSize int64 `json:"minSize"`
MaxSize int64 `json:"maxSize"`
ModifiedAfter string `json:"modifiedAfter,omitempty"`
ModifiedBefore string `json:"modifiedBefore,omitempty"`
MaxScanFiles int `json:"maxScanFiles"`
MaxFileBytes int64 `json:"maxFileBytes"`
MaxHitsPerFile int `json:"maxHitsPerFile"`
MaxTotalHits int `json:"maxTotalHits"`
ContentHitsPromptMaxBytes int `json:"contentHitsPromptMaxBytes"`
LlmMaxOutputTokens int `json:"llmMaxOutputTokens"`
}
type FileContentReq struct {
Path string `json:"path" validate:"required"`
IsDetail bool `json:"isDetail"`
@@ -172,3 +195,9 @@ type FileRemarkUpdate struct {
Path string `json:"path" validate:"required"`
Remark string `json:"remark"`
}
type FileShareCreate struct {
Path string `json:"path" validate:"required"`
ExpireMinutes int `json:"expireMinutes" validate:"min=0,max=10080"`
Password *string `json:"password"`
}
-1
View File
@@ -51,7 +51,6 @@ type WebsiteSSLApply struct {
ID uint `json:"ID" validate:"required"`
SkipDNSCheck bool `json:"skipDNSCheck"`
Nameservers []string `json:"nameservers"`
DisableLog bool `json:"disableLog"`
}
type WebsiteSSLObtain struct {
+38
View File
@@ -85,3 +85,41 @@ type FileConvertLog struct {
type FileRemarksRes struct {
Remarks map[string]string `json:"remarks"`
}
type FileShareInfo struct {
Code string `json:"code"`
Path string `json:"path"`
FileName string `json:"fileName"`
ExpiresAt int64 `json:"expiresAt"`
Permanent bool `json:"permanent"`
HasPassword bool `json:"hasPassword"`
Password string `json:"password,omitempty"`
}
type FileSharePublicInfo struct {
FileName string `json:"fileName"`
ExpiresAt int64 `json:"expiresAt"`
Permanent bool `json:"permanent"`
HasPassword bool `json:"hasPassword"`
}
type FileAIContentHit struct {
Path string `json:"path"`
Line int `json:"line"`
Text string `json:"text"`
}
type FileAISearchResult struct {
Mode string `json:"mode"`
Summary string `json:"summary"`
Hits []FileAIContentHit `json:"hits"`
ContentScannedFiles int `json:"contentScannedFiles"`
ContentHitsTruncated bool `json:"contentHitsTruncated"`
Truncated bool `json:"truncated"`
PreFiltered bool `json:"preFiltered"`
ItemCount int `json:"itemCount"`
PromptTokens int `json:"promptTokens"`
CompletionTokens int `json:"completionTokens"`
TotalTokens int `json:"totalTokens"`
Duration string `json:"duration"`
}
+6
View File
@@ -55,6 +55,7 @@ type CleanTree struct {
Size uint64 `json:"size"`
IsCheck bool `json:"isCheck"`
IsRecommend bool `json:"isRecommend"`
CanDelete bool `json:"canDelete"`
}
type Clean struct {
@@ -95,6 +96,11 @@ type TerminalAIInfo struct {
AIRiskCommandsDefault string `json:"aiRiskCommandsDefault"`
}
type FileManageAIInfo struct {
AIStatus string `json:"aiStatus"`
AIAccountID string `json:"aiAccountId"`
}
type CommonDescription struct {
ID string `json:"id" validate:"required"`
Type string `json:"type" validate:"required"`
+6 -1
View File
@@ -4,7 +4,6 @@ import "time"
type SSHUpdate struct {
Key string `json:"key" validate:"required"`
OldValue string `json:"oldValue"`
NewValue string `json:"newValue"`
}
@@ -50,6 +49,12 @@ type GenerateLoad struct {
type SSHConf struct {
File string `json:"file"`
}
type SSHConfUpdate struct {
Key string `json:"key" validate:"required"`
Path string `json:"path"`
Value string `json:"value"`
}
type SearchSSHLog struct {
PageInfo
Info string `json:"info"`
+2
View File
@@ -3,6 +3,7 @@ package model
type Agent struct {
BaseModel
Name string `json:"name" gorm:"not null;unique"`
Remark string `json:"remark"`
AgentType string `json:"agentType" gorm:"default:openclaw"`
Provider string `json:"provider"`
Model string `json:"model"`
@@ -15,6 +16,7 @@ type Agent struct {
Status string `json:"status"`
Message string `json:"message"`
AppInstallID uint `json:"appInstallId"`
WebsiteID uint `json:"websiteId"`
AccountID uint `json:"accountId"`
ConfigPath string `json:"configPath"`
}
+14
View File
@@ -0,0 +1,14 @@
package model
type FileShare struct {
BaseModel
Path string `gorm:"not null;uniqueIndex" json:"path"`
Token string `gorm:"not null;uniqueIndex" json:"token"`
FileName string `gorm:"not null" json:"fileName"`
ExpiresUnix int64 `json:"expiresUnix"`
PasswordEnc string `json:"-"`
PasswordSalt string `json:"passwordSalt"`
PasswordHash string `json:"passwordHash"`
MaxDownloads int `json:"maxDownloads"`
DownloadCount int `json:"downloadCount"`
}
+34 -36
View File
@@ -78,14 +78,13 @@ var catalog = map[string]Meta{
EnvKey: "DEEPSEEK_API_KEY",
Default: RuntimeDefault{
APIType: "openai-completions",
ContextWindow: 128000,
ContextWindow: 131072,
MaxTokens: 8192,
Input: []string{"text"},
},
Models: []Model{
{ID: "deepseek/deepseek-chat", Name: "DeepSeek Chat"},
{ID: "deepseek/deepseek-reasoner", Name: "DeepSeek Reasoner", Reasoning: true},
{ID: "deepseek/deepseek-r1:1.5b", Name: "DeepSeek R1 1.5B", Reasoning: true},
{ID: "deepseek/deepseek-reasoner", Name: "DeepSeek Reasoner", MaxTokens: 65536, ContextWindow: 131072, Reasoning: true},
},
},
"bailian-coding-plan": {
@@ -120,16 +119,16 @@ var catalog = map[string]Meta{
Default: RuntimeDefault{
APIType: "openai-completions",
ContextWindow: 256000,
MaxTokens: 8192,
MaxTokens: 4096,
Input: []string{"text"},
},
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", Reasoning: true},
{ID: "ark-coding-plan/glm-4.7", Name: "GLM-4.7", Reasoning: true},
{ID: "ark-coding-plan/deepseek-v3.2", Name: "DeepSeek-V3.2", Reasoning: true},
{ID: "ark-coding-plan/kimi-k2-thinking", Name: "Kimi-K2-thinking", Reasoning: true},
{ID: "ark-coding-plan/ark-code-latest", Name: "Ark Coding Plan", ContextWindow: 256000, MaxTokens: 4096},
{ID: "ark-coding-plan/doubao-seed-code", Name: "Doubao Seed Code", ContextWindow: 256000, MaxTokens: 4096},
{ID: "ark-coding-plan/glm-4.7", Name: "GLM 4.7 Coding", ContextWindow: 200000, MaxTokens: 4096},
{ID: "ark-coding-plan/kimi-k2-thinking", Name: "Kimi K2 Thinking", ContextWindow: 256000, MaxTokens: 4096},
{ID: "ark-coding-plan/kimi-k2.5", Name: "Kimi K2.5 Coding", ContextWindow: 256000, MaxTokens: 4096},
{ID: "ark-coding-plan/doubao-seed-code-preview-251028", Name: "Doubao Seed Code Preview", ContextWindow: 256000, MaxTokens: 4096},
},
},
"zai": {
@@ -174,19 +173,18 @@ var catalog = map[string]Meta{
Key: "xiaomi",
DisplayName: "Xiaomi",
Sort: 46,
DefaultBaseURL: "https://api.xiaomimimo.com/anthropic",
DefaultBaseURL: "https://api.xiaomimimo.com/v1",
EnvKey: "XIAOMI_API_KEY",
Default: RuntimeDefault{
APIType: "anthropic-messages",
APIType: "openai-completions",
ContextWindow: 262144,
MaxTokens: 8192,
Input: []string{"text"},
},
Models: []Model{
{ID: "xiaomi/mimo-v2-pro", Name: "Xiaomi MiMo V2 Pro"},
{ID: "xiaomi/mimo-v2-omni", Name: "Xiaomi MiMo V2 Omni"},
{ID: "xiaomi/mimo-v2-tts", Name: "Xiaomi MiMo V2 TTS"},
{ID: "xiaomi/mimo-v2-flash", Name: "Xiaomi MiMo V2 Flash"},
{ID: "xiaomi/mimo-v2-flash", Name: "Xiaomi MiMo V2 Flash", ContextWindow: 262144, MaxTokens: 8192, Input: []string{"text"}},
{ID: "xiaomi/mimo-v2-pro", Name: "Xiaomi MiMo V2 Pro", ContextWindow: 1048576, MaxTokens: 32000, Reasoning: true, Input: []string{"text"}},
{ID: "xiaomi/mimo-v2-omni", Name: "Xiaomi MiMo V2 Omni", ContextWindow: 262144, MaxTokens: 32000, Reasoning: true, Input: []string{"text", "image"}},
},
},
"kimi": {
@@ -214,13 +212,14 @@ var catalog = map[string]Meta{
DefaultBaseURL: "https://api.kimi.com/coding/",
EnvKey: "KIMI_API_KEY",
Default: RuntimeDefault{
APIType: "openai-completions",
APIType: "anthropic-messages",
ContextWindow: 262144,
MaxTokens: 32768,
Input: []string{"text", "image"},
},
Models: []Model{
{ID: "kimi-coding/k2p5", Name: "Kimi K2.5", Reasoning: true},
{ID: "kimi-coding/kimi-code", Name: "Kimi Code", ContextWindow: 262144, MaxTokens: 32768, Reasoning: true, Input: []string{"text", "image"}},
{ID: "kimi-coding/k2p5", Name: "Kimi K2.5", ContextWindow: 262144, MaxTokens: 32768, Reasoning: true, Input: []string{"text", "image"}},
},
},
"openai": {
@@ -230,17 +229,16 @@ var catalog = map[string]Meta{
DefaultBaseURL: "https://api.openai.com/v1",
EnvKey: "OPENAI_API_KEY",
Default: RuntimeDefault{
APIType: "openai-completions",
ContextWindow: 256000,
MaxTokens: 8192,
APIType: "openai-responses",
ContextWindow: 272000,
MaxTokens: 128000,
Input: []string{"text", "image"},
},
Models: []Model{
{ID: "openai/codex-mini-latest", Name: "Codex Mini", Reasoning: true},
{ID: "openai/gpt-5", Name: "GPT-5", Reasoning: true},
{ID: "openai/gpt-5-mini", Name: "GPT-5 Mini", Reasoning: true},
{ID: "openai/gpt-5.4", Name: "GPT-5.4", Reasoning: true},
{ID: "openai/gpt-5.3-codex", Name: "GPT-5.3-Codex", Reasoning: true},
{ID: "openai/gpt-5.4", Name: "gpt-5.4", ContextWindow: 272000, MaxTokens: 128000, Reasoning: true, Input: []string{"text", "image"}},
{ID: "openai/gpt-5.4-pro", Name: "gpt-5.4-pro", ContextWindow: 1050000, MaxTokens: 128000, Reasoning: true, Input: []string{"text", "image"}},
{ID: "openai/gpt-5.4-mini", Name: "gpt-5.4-mini", ContextWindow: 400000, MaxTokens: 128000, Reasoning: true, Input: []string{"text", "image"}},
{ID: "openai/gpt-5.4-nano", Name: "gpt-5.4-nano", ContextWindow: 400000, MaxTokens: 128000, Reasoning: true, Input: []string{"text", "image"}},
},
},
"openrouter": {
@@ -267,17 +265,17 @@ var catalog = map[string]Meta{
DefaultBaseURL: "https://api.anthropic.com",
EnvKey: "ANTHROPIC_API_KEY",
Default: RuntimeDefault{
APIType: "openai-completions",
APIType: "anthropic-messages",
ContextWindow: 256000,
MaxTokens: 8192,
Input: []string{"text", "image"},
},
Models: []Model{
{ID: "anthropic/claude-3-haiku-20240307", Name: "Claude 3 Haiku"},
{ID: "anthropic/claude-3-5-haiku-latest", Name: "Claude 3.5 Haiku"},
{ID: "anthropic/claude-3-5-sonnet-20241022", Name: "Claude 3.5 Sonnet"},
{ID: "anthropic/claude-3-7-sonnet-20250219", Name: "Claude 3.7 Sonnet", Reasoning: true},
{ID: "anthropic/claude-opus-4-1", Name: "Claude Opus 4.1", Reasoning: true},
{ID: "anthropic/claude-sonnet-4-6", Name: "Claude Sonnet 4.6", Reasoning: true},
{ID: "anthropic/claude-opus-4-6", Name: "Claude Opus 4.6", Reasoning: true},
{ID: "anthropic/claude-opus-4-5", Name: "Claude Opus 4.5"},
{ID: "anthropic/claude-sonnet-4-5", Name: "Claude Sonnet 4.5"},
{ID: "anthropic/claude-haiku-4-5", Name: "Claude Haiku 4.5"},
},
},
"gemini": {
@@ -319,7 +317,7 @@ var catalog = map[string]Meta{
}
func Get(key string) (Meta, bool) {
meta, ok := catalog[strings.ToLower(strings.TrimSpace(key))]
meta, ok := catalog[key]
if !ok {
return Meta{}, false
}
@@ -335,7 +333,7 @@ func All() map[string]Meta {
}
func DefaultBaseURL(key string) (string, bool) {
meta, ok := catalog[strings.ToLower(strings.TrimSpace(key))]
meta, ok := catalog[key]
if !ok || strings.TrimSpace(meta.DefaultBaseURL) == "" {
return "", false
}
@@ -343,7 +341,7 @@ func DefaultBaseURL(key string) (string, bool) {
}
func EnvKey(key string) string {
meta, ok := catalog[strings.ToLower(strings.TrimSpace(key))]
meta, ok := catalog[key]
if !ok {
return ""
}
@@ -351,7 +349,7 @@ func EnvKey(key string) string {
}
func DisplayName(key string) string {
meta, ok := catalog[strings.ToLower(strings.TrimSpace(key))]
meta, ok := catalog[key]
if !ok {
return key
}
+109 -271
View File
@@ -10,324 +10,162 @@ type OpenClawPatch struct {
Models map[string]interface{}
}
type openClawModelSpec struct {
ID string
Name string
Reasoning bool
Input []string
ContextWindow int
MaxTokens int
}
type openClawPatchSpec struct {
type OpenClawProviderPatch struct {
PrimaryModel string
Provider string
ProviderKey string
ModelID string
APIKey string
BaseURL string
APIType string
AuthHeader bool
Model openClawModelSpec
}
func BuildOpenClawPatch(provider, modelName, apiType string, reasoning bool, maxTokens, contextWindow int, baseURL, apiKey string) (*OpenClawPatch, error) {
provider = strings.ToLower(strings.TrimSpace(provider))
providerPatch, err := BuildOpenClawProviderPatch(provider, modelName, apiType, baseURL, apiKey)
if err != nil {
return nil, err
}
_, maxTokens, contextWindow = ResolveRuntimeParams(provider, apiType, maxTokens, contextWindow)
return newOpenClawPatch(
providerPatch,
resolveOpenClawModelName(provider, providerPatch.ModelID),
resolveOpenClawModelInput(provider, providerPatch.ModelID),
reasoning,
contextWindow,
maxTokens,
), nil
}
func BuildOpenClawProviderPatch(provider, modelName, apiType, baseURL, apiKey string) (*OpenClawProviderPatch, error) {
modelName = strings.TrimSpace(modelName)
if modelName == "" {
return nil, fmt.Errorf("model is required")
}
apiType, maxTokens, contextWindow = ResolveRuntimeParams(provider, apiType, maxTokens, contextWindow)
modelID := modelName
if parts := strings.SplitN(modelName, "/", 2); len(parts) == 2 {
modelID = parts[1]
}
var spec openClawPatchSpec
resolvedAPIType, _, _ := ResolveRuntimeParams(provider, apiType, 0, 0)
modelID := resolveOpenClawModelID(provider, modelName)
switch provider {
case "deepseek":
spec = buildDeepseekPatchSpec(modelName, reasoning, maxTokens, contextWindow, baseURL, apiKey)
return newOpenClawProviderPatch(modelName, "deepseek", modelID, strings.TrimSpace(apiKey), baseURL, "openai-completions", false), nil
case "gemini":
spec = buildGenericPatchSpec(provider, modelName, modelID, apiType, reasoning, maxTokens, contextWindow, baseURL, apiKey)
return newOpenClawProviderPatch("google/"+modelID, "google", modelID, strings.TrimSpace(apiKey), baseURL, resolvedAPIType, false), nil
case "moonshot", "kimi":
spec = buildMoonshotPatchSpec(provider, modelName, modelID, reasoning, maxTokens, contextWindow, baseURL, apiKey)
return buildMoonshotProviderPatch(provider, modelName, modelID, baseURL, apiKey), nil
case "bailian-coding-plan":
spec = buildBailianPatchSpec(modelID, reasoning, maxTokens, contextWindow, baseURL, apiKey)
return newOpenClawProviderPatch("bailian-coding-plan/"+modelID, "bailian-coding-plan", modelID, strings.TrimSpace(apiKey), baseURL, "openai-completions", false), nil
case "ark-coding-plan":
spec = buildArkPatchSpec(modelID, reasoning, maxTokens, contextWindow, baseURL, apiKey)
return newOpenClawProviderPatch("ark-coding-plan/"+modelID, "ark-coding-plan", modelID, strings.TrimSpace(apiKey), baseURL, "openai-completions", false), nil
case "minimax":
spec = buildMiniMaxPatchSpec(modelID, reasoning, maxTokens, contextWindow, baseURL, apiKey)
return newOpenClawProviderPatch("minimax/"+modelID, "minimax", modelID, strings.TrimSpace(apiKey), baseURL, "anthropic-messages", true), nil
case "xiaomi":
spec = buildXiaomiPatchSpec(modelID, reasoning, maxTokens, contextWindow, baseURL, apiKey)
return newOpenClawProviderPatch("xiaomi/"+modelID, "xiaomi", modelID, strings.TrimSpace(apiKey), baseURL, "openai-completions", false), nil
case "custom", "vllm":
spec = buildCustomPatchSpec(provider, modelName, apiType, reasoning, maxTokens, contextWindow, baseURL, apiKey)
return newOpenClawProviderPatch(provider+"/"+modelID, provider, modelID, strings.TrimSpace(apiKey), strings.TrimSpace(baseURL), resolvedAPIType, false), nil
case "ollama":
spec = buildOllamaPatchSpec(modelName, modelID, apiType, reasoning, maxTokens, contextWindow, baseURL)
return newOpenClawProviderPatch(modelName, "ollama", modelID, "ollama", strings.TrimSpace(baseURL), resolvedAPIType, false), nil
case "kimi-coding":
spec = buildKimiCodingPatchSpec(modelName, modelID, reasoning, maxTokens, contextWindow, baseURL, apiKey)
return newOpenClawProviderPatch(modelName, "kimi-coding", modelID, strings.TrimSpace(apiKey), baseURL, "anthropic-messages", false), nil
case "zai":
spec = buildZaiPatchSpec(modelID, reasoning, maxTokens, contextWindow, baseURL, apiKey)
return newOpenClawProviderPatch("zai/"+modelID, "zai", modelID, strings.TrimSpace(apiKey), baseURL, "openai-completions", false), nil
default:
spec = buildGenericPatchSpec(provider, modelName, modelID, apiType, reasoning, maxTokens, contextWindow, baseURL, apiKey)
}
return buildOpenClawPatch(spec), nil
}
func buildOpenClawPatch(spec openClawPatchSpec) *OpenClawPatch {
return &OpenClawPatch{
PrimaryModel: spec.PrimaryModel,
Models: providerModels(
spec.Provider,
spec.APIKey,
spec.BaseURL,
spec.APIType,
spec.AuthHeader,
buildOpenClawModel(spec.Model),
),
return newOpenClawProviderPatch(modelName, provider, modelID, strings.TrimSpace(apiKey), baseURL, resolvedAPIType, false), nil
}
}
func buildOpenClawModel(spec openClawModelSpec) map[string]interface{} {
return map[string]interface{}{
"id": spec.ID,
"name": spec.Name,
"reasoning": spec.Reasoning,
"input": spec.Input,
"contextWindow": spec.ContextWindow,
"maxTokens": spec.MaxTokens,
"cost": map[string]interface{}{},
}
}
func buildDeepseekPatchSpec(modelName string, reasoning bool, maxTokens, contextWindow int, baseURL, apiKey string) openClawPatchSpec {
return openClawPatchSpec{
PrimaryModel: modelName,
Provider: "deepseek",
APIKey: strings.TrimSpace(apiKey),
BaseURL: baseURL,
APIType: "openai-completions",
Model: openClawModelSpec{
ID: "deepseek-chat",
Name: "DeepSeek Chat",
Reasoning: reasoning,
Input: []string{"text"},
ContextWindow: contextWindow,
MaxTokens: maxTokens,
},
}
}
func buildMoonshotPatchSpec(provider, modelName, modelID string, reasoning bool, maxTokens, contextWindow int, baseURL, apiKey string) openClawPatchSpec {
configProvider := provider
func buildMoonshotProviderPatch(provider, modelName, modelID, baseURL, apiKey string) *OpenClawProviderPatch {
providerKey := provider
primaryModel := modelName
if provider == "kimi" {
configProvider = "moonshot"
providerKey = "moonshot"
primaryModel = "moonshot/" + modelID
}
return openClawPatchSpec{
return newOpenClawProviderPatch(primaryModel, providerKey, modelID, strings.TrimSpace(apiKey), baseURL, "openai-completions", false)
}
func newOpenClawProviderPatch(primaryModel, providerKey, modelID, apiKey, baseURL, apiType string, authHeader bool) *OpenClawProviderPatch {
return &OpenClawProviderPatch{
PrimaryModel: primaryModel,
Provider: configProvider,
APIKey: strings.TrimSpace(apiKey),
BaseURL: baseURL,
APIType: "openai-completions",
Model: openClawModelSpec{
ID: modelID,
Name: modelID,
Reasoning: reasoning,
Input: []string{"text"},
ContextWindow: contextWindow,
MaxTokens: maxTokens,
},
}
}
func buildBailianPatchSpec(modelID string, reasoning bool, maxTokens, contextWindow int, baseURL, apiKey string) openClawPatchSpec {
return openClawPatchSpec{
PrimaryModel: "bailian-coding-plan/" + modelID,
Provider: "bailian-coding-plan",
APIKey: strings.TrimSpace(apiKey),
BaseURL: baseURL,
APIType: "openai-completions",
Model: openClawModelSpec{
ID: modelID,
Name: modelID,
Reasoning: reasoning,
Input: []string{"text"},
ContextWindow: contextWindow,
MaxTokens: maxTokens,
},
}
}
func buildArkPatchSpec(modelID string, reasoning bool, maxTokens, contextWindow int, baseURL, apiKey string) openClawPatchSpec {
return openClawPatchSpec{
PrimaryModel: "ark-coding-plan/" + modelID,
Provider: "ark-coding-plan",
APIKey: strings.TrimSpace(apiKey),
BaseURL: baseURL,
APIType: "openai-completions",
Model: openClawModelSpec{
ID: modelID,
Name: modelID,
Reasoning: reasoning,
Input: []string{"text"},
ContextWindow: contextWindow,
MaxTokens: maxTokens,
},
}
}
func buildMiniMaxPatchSpec(modelID string, reasoning bool, maxTokens, contextWindow int, baseURL, apiKey string) openClawPatchSpec {
return openClawPatchSpec{
PrimaryModel: "minimax/" + modelID,
Provider: "minimax",
APIKey: strings.TrimSpace(apiKey),
BaseURL: baseURL,
APIType: "anthropic-messages",
AuthHeader: true,
Model: openClawModelSpec{
ID: modelID,
Name: modelID,
Reasoning: reasoning,
Input: []string{"text"},
ContextWindow: contextWindow,
MaxTokens: maxTokens,
},
}
}
func buildXiaomiPatchSpec(modelID string, reasoning bool, maxTokens, contextWindow int, baseURL, apiKey string) openClawPatchSpec {
normalizedID := strings.TrimSpace(modelID)
return openClawPatchSpec{
PrimaryModel: "xiaomi/" + normalizedID,
Provider: "xiaomi",
APIKey: strings.TrimSpace(apiKey),
BaseURL: baseURL,
APIType: "anthropic-messages",
Model: openClawModelSpec{
ID: normalizedID,
Name: normalizedID,
Reasoning: reasoning,
Input: []string{"text"},
ContextWindow: contextWindow,
MaxTokens: maxTokens,
},
}
}
func buildCustomPatchSpec(provider, modelName, apiType string, reasoning bool, maxTokens, contextWindow int, baseURL, apiKey string) openClawPatchSpec {
customModelID := normalizeCustomModel(modelName)
return openClawPatchSpec{
PrimaryModel: provider + "/" + customModelID,
Provider: provider,
APIKey: strings.TrimSpace(apiKey),
BaseURL: strings.TrimSpace(baseURL),
APIType: apiType,
Model: openClawModelSpec{
ID: customModelID,
Name: customModelID,
Reasoning: reasoning,
Input: []string{"text"},
ContextWindow: contextWindow,
MaxTokens: maxTokens,
},
}
}
func buildOllamaPatchSpec(modelName, modelID, apiType string, reasoning bool, maxTokens, contextWindow int, baseURL string) openClawPatchSpec {
return openClawPatchSpec{
PrimaryModel: modelName,
Provider: "ollama",
APIKey: "ollama",
BaseURL: strings.TrimSpace(baseURL),
APIType: apiType,
Model: openClawModelSpec{
ID: modelID,
Name: modelID,
Reasoning: reasoning,
Input: []string{"text"},
ContextWindow: contextWindow,
MaxTokens: maxTokens,
},
}
}
func buildKimiCodingPatchSpec(modelName, modelID string, reasoning bool, maxTokens, contextWindow int, baseURL, apiKey string) openClawPatchSpec {
return openClawPatchSpec{
PrimaryModel: modelName,
Provider: "kimi-coding",
APIKey: strings.TrimSpace(apiKey),
BaseURL: baseURL,
APIType: "anthropic-messages",
Model: openClawModelSpec{
ID: modelID,
Name: "Kimi for Coding",
Reasoning: reasoning,
Input: []string{"text", "image"},
ContextWindow: contextWindow,
MaxTokens: maxTokens,
},
}
}
func buildZaiPatchSpec(modelID string, reasoning bool, maxTokens, contextWindow int, baseURL, apiKey string) openClawPatchSpec {
return openClawPatchSpec{
PrimaryModel: "zai/" + modelID,
Provider: "zai",
APIKey: strings.TrimSpace(apiKey),
BaseURL: baseURL,
APIType: "openai-completions",
Model: openClawModelSpec{
ID: modelID,
Name: modelID,
Reasoning: reasoning,
Input: []string{"text"},
ContextWindow: contextWindow,
MaxTokens: maxTokens,
},
}
}
func buildGenericPatchSpec(provider, modelName, modelID, apiType string, reasoning bool, maxTokens, contextWindow int, baseURL, apiKey string) openClawPatchSpec {
providerName := provider
primaryModel := modelName
if provider == "gemini" {
providerName = "google"
primaryModel = "google/" + modelID
}
return openClawPatchSpec{
PrimaryModel: primaryModel,
Provider: providerName,
APIKey: strings.TrimSpace(apiKey),
ProviderKey: providerKey,
ModelID: modelID,
APIKey: apiKey,
BaseURL: baseURL,
APIType: apiType,
Model: openClawModelSpec{
ID: modelID,
Name: modelID,
Reasoning: reasoning,
Input: []string{"text"},
ContextWindow: contextWindow,
MaxTokens: maxTokens,
},
AuthHeader: authHeader,
}
}
func providerModels(provider, apiKey, baseURL, api string, authHeader bool, model map[string]interface{}) map[string]interface{} {
func newOpenClawPatch(providerPatch *OpenClawProviderPatch, modelName string, input []string, reasoning bool, contextWindow, maxTokens int) *OpenClawPatch {
model := map[string]interface{}{
"id": providerPatch.ModelID,
"name": modelName,
"reasoning": reasoning,
"input": input,
"contextWindow": contextWindow,
"maxTokens": maxTokens,
"cost": map[string]interface{}{},
}
providerConfig := map[string]interface{}{
"apiKey": apiKey,
"baseUrl": baseURL,
"api": api,
"apiKey": providerPatch.APIKey,
"baseUrl": providerPatch.BaseURL,
"api": providerPatch.APIType,
"models": []map[string]interface{}{model},
}
if authHeader {
if providerPatch.AuthHeader {
providerConfig["authHeader"] = true
}
return map[string]interface{}{
"mode": "merge",
"providers": map[string]interface{}{
provider: providerConfig,
return &OpenClawPatch{
PrimaryModel: providerPatch.PrimaryModel,
Models: map[string]interface{}{
"mode": "merge",
"providers": map[string]interface{}{
providerPatch.ProviderKey: providerConfig,
},
},
}
}
func resolveOpenClawModelID(provider, modelName string) string {
if provider == "custom" || provider == "vllm" {
return normalizeCustomModel(modelName)
}
if parts := strings.SplitN(modelName, "/", 2); len(parts) == 2 {
return parts[1]
}
return modelName
}
func resolveOpenClawModelName(provider, modelID string) string {
if item, ok := resolveOpenClawCatalogModel(provider, modelID); ok {
return item.Name
}
if provider == "kimi-coding" {
return "Kimi for Coding"
}
return modelID
}
func resolveOpenClawModelInput(provider, modelID string) []string {
if item, ok := resolveOpenClawCatalogModel(provider, modelID); ok && len(item.Input) > 0 {
return item.Input
}
if provider == "kimi-coding" {
return []string{"text", "image"}
}
return []string{"text"}
}
func resolveOpenClawCatalogModel(provider, modelID string) (Model, bool) {
candidates := []string{provider + "/" + modelID}
if provider == "gemini" {
candidates = append(candidates, "google/"+modelID)
}
for _, candidate := range candidates {
if item, ok := FindModel(provider, candidate); ok {
return item, true
}
}
return Model{}, false
}
func normalizeCustomModel(modelName string) string {
trim := strings.TrimSpace(modelName)
trim = strings.TrimLeft(trim, "/")
+7 -16
View File
@@ -23,7 +23,7 @@ const (
)
func SkipVerification(key string) bool {
switch strings.ToLower(strings.TrimSpace(key)) {
switch key {
case "custom", "vllm", "ollama", "kimi-coding":
return true
default:
@@ -62,7 +62,6 @@ func verifyTimeout() time.Duration {
}
func BuildVerifyRequest(provider, baseURL, apiKey string) VerifyRequest {
provider = strings.ToLower(strings.TrimSpace(provider))
base := strings.TrimRight(strings.TrimSpace(baseURL), "/")
headers := map[string]string{}
request := VerifyRequest{Method: http.MethodGet, Headers: headers}
@@ -117,7 +116,7 @@ func BuildVerifyRequest(provider, baseURL, apiKey string) VerifyRequest {
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
headers["Content-Type"] = "application/json"
request.Body = mustJSON(map[string]interface{}{
"model": "doubao-seed-2.0-code",
"model": "ark-code-latest",
"messages": []map[string]string{{"role": "user", "content": "test"}},
"max_tokens": 1,
})
@@ -144,24 +143,16 @@ func BuildVerifyRequest(provider, baseURL, apiKey string) VerifyRequest {
})
case "xiaomi":
request.Method = http.MethodPost
headers["x-api-key"] = apiKey
headers["anthropic-version"] = "2023-06-01"
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
headers["Content-Type"] = "application/json"
if strings.Contains(base, "/v1") {
request.URL = base + "/messages"
} else {
request.URL = base + "/v1/messages"
if !strings.Contains(base, "/v1") {
base = base + "/v1"
}
request.URL = base + "/chat/completions"
request.Body = mustJSON(map[string]interface{}{
"model": "mimo-v2-flash",
"max_tokens": 1,
"messages": []map[string]interface{}{{
"role": "user",
"content": []map[string]string{{
"type": "text",
"text": "test",
}},
}},
"messages": []map[string]string{{"role": "user", "content": "test"}},
})
case "openrouter":
headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey)
+8
View File
@@ -16,6 +16,7 @@ type IAgentRepo interface {
DeleteByID(id uint) error
DeleteByAppInstallID(appInstallID uint) error
DeleteByAppInstallIDWithCtx(ctx context.Context, appInstallID uint) error
ClearWebsiteIDByWebsiteIDWithCtx(ctx context.Context, websiteID uint) error
List(opts ...DBOption) ([]model.Agent, error)
}
@@ -66,6 +67,13 @@ func (a AgentRepo) DeleteByAppInstallIDWithCtx(ctx context.Context, appInstallID
return getTx(ctx).Where("app_install_id = ?", appInstallID).Delete(&model.Agent{}).Error
}
func (a AgentRepo) ClearWebsiteIDByWebsiteIDWithCtx(ctx context.Context, websiteID uint) error {
if websiteID == 0 {
return nil
}
return getTx(ctx).Model(&model.Agent{}).Where("website_id = ?", websiteID).Update("website_id", 0).Error
}
func (a AgentRepo) List(opts ...DBOption) ([]model.Agent, error) {
var agents []model.Agent
if err := getDb(opts...).Find(&agents).Error; err != nil {
+18
View File
@@ -112,6 +112,24 @@ func WithByAccountID(accountID uint) DBOption {
}
}
func WithByWebsiteID(websiteID uint) DBOption {
return func(g *gorm.DB) *gorm.DB {
if websiteID == 0 {
return g
}
return g.Where("website_id = ?", websiteID)
}
}
func WithByAppInstallID(appInstallID uint) DBOption {
return func(g *gorm.DB) *gorm.DB {
if appInstallID == 0 {
return g
}
return g.Where("app_install_id = ?", appInstallID)
}
}
func WithByType(tp string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("`type` = ?", tp)
+77
View File
@@ -0,0 +1,77 @@
package repo
import (
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/global"
"gorm.io/gorm"
)
type FileShareRepo struct{}
type IFileShareRepo interface {
Page(page, size int, opts ...DBOption) (int64, []model.FileShare, error)
Create(fileShare *model.FileShare) error
Save(fileShare *model.FileShare) error
Delete(opts ...DBOption) error
GetFirst(opts ...DBOption) (model.FileShare, error)
All() ([]model.FileShare, error)
WithByPath(path string) DBOption
WithByCode(code string) DBOption
}
func NewIFileShareRepo() IFileShareRepo {
return &FileShareRepo{}
}
func (r *FileShareRepo) WithByPath(path string) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("path = ?", path)
}
}
func (r *FileShareRepo) WithByCode(code string) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("token = ?", code)
}
}
func (r *FileShareRepo) Page(page, size int, opts ...DBOption) (int64, []model.FileShare, error) {
var (
items []model.FileShare
count int64
)
db := getDb(opts...).Model(&model.FileShare{})
db = db.Count(&count)
err := db.Order("path asc").Limit(size).Offset(size * (page - 1)).Find(&items).Error
return count, items, err
}
func (r *FileShareRepo) Create(fileShare *model.FileShare) error {
return global.DB.Create(fileShare).Error
}
func (r *FileShareRepo) Save(fileShare *model.FileShare) error {
return global.DB.Save(fileShare).Error
}
func (r *FileShareRepo) GetFirst(opts ...DBOption) (model.FileShare, error) {
var item model.FileShare
db := getDb(opts...).Model(&model.FileShare{})
if err := db.First(&item).Error; err != nil {
return item, err
}
return item, nil
}
func (r *FileShareRepo) Delete(opts ...DBOption) error {
db := getDb(opts...).Model(&model.FileShare{})
return db.Delete(&model.FileShare{}).Error
}
func (r *FileShareRepo) All() ([]model.FileShare, error) {
var items []model.FileShare
if err := getDb().Order("path asc").Find(&items).Error; err != nil {
return nil, err
}
return items, nil
}
+10
View File
@@ -10,6 +10,7 @@ import (
type IWebsiteRepo interface {
WithAppInstallId(appInstallId uint) DBOption
WithAppInstallIds(appInstallIds []uint) DBOption
WithDomain(domain string) DBOption
WithAlias(alias string) DBOption
WithWebsiteSSLID(sslId uint) DBOption
@@ -48,6 +49,15 @@ func (w *WebsiteRepo) WithAppInstallId(appInstallID uint) DBOption {
}
}
func (w *WebsiteRepo) WithAppInstallIds(appInstallIDs []uint) DBOption {
return func(db *gorm.DB) *gorm.DB {
if len(appInstallIDs) == 0 {
return db
}
return db.Where("app_install_id in (?)", appInstallIDs)
}
}
func (w *WebsiteRepo) WithRuntimeID(runtimeID uint) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("runtime_id = ?", runtimeID)
+10
View File
@@ -13,6 +13,7 @@ type WebsiteDomainRepo struct {
type IWebsiteDomainRepo interface {
WithWebsiteId(websiteId uint) DBOption
WithWebsiteIds(websiteIds []uint) DBOption
WithPort(port int) DBOption
WithDomain(domain string) DBOption
WithDomainLike(domain string) DBOption
@@ -36,6 +37,15 @@ func (w WebsiteDomainRepo) WithWebsiteId(websiteId uint) DBOption {
}
}
func (w WebsiteDomainRepo) WithWebsiteIds(websiteIds []uint) DBOption {
return func(db *gorm.DB) *gorm.DB {
if len(websiteIds) == 0 {
return db
}
return db.Where("website_id in (?)", websiteIds)
}
}
func (w WebsiteDomainRepo) WithPort(port int) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("port = ?", port)
+139 -6
View File
@@ -7,6 +7,7 @@ import (
"os"
"path"
"sort"
"strconv"
"strings"
"time"
@@ -19,16 +20,22 @@ import (
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/docker"
terminalai "github.com/1Panel-dev/1Panel/agent/utils/terminal/ai"
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
"github.com/docker/docker/api/types/container"
"gorm.io/gorm"
)
type IAgentService interface {
Create(req dto.AgentCreateReq) (*dto.AgentItem, error)
Page(req dto.SearchWithPage) (int64, []dto.AgentItem, error)
DeleteCheck(req dto.AgentIDReq) ([]dto.AppResource, error)
Delete(req dto.AgentDeleteReq) error
ResetToken(req dto.AgentTokenResetReq) error
UpdateRemark(req dto.AgentRemarkUpdateReq) error
BindWebsite(req dto.AgentWebsiteBindReq) error
GetModelConfig(req dto.AgentIDReq) (*dto.AgentModelConfig, error)
UpdateModelConfig(req dto.AgentModelConfigUpdateReq) error
GetOverview(req dto.AgentOverviewReq) (*dto.AgentOverview, error)
GetProviders() ([]dto.ProviderInfo, error)
@@ -43,6 +50,15 @@ type IAgentService interface {
UpdateSkill(req dto.AgentSkillUpdateReq) error
InstallSkill(req dto.AgentSkillInstallReq) error
CreateRole(req dto.AgentRoleCreateReq) (*dto.AgentRoleCreateResp, error)
DeleteRole(req dto.AgentRoleDeleteReq) error
BindRole(req dto.AgentRoleBindReq) error
UnbindRole(req dto.AgentRoleBindReq) error
GetConfiguredAgents(req dto.AgentConfiguredAgentsReq) ([]dto.AgentConfiguredAgentItem, error)
GetRoleChannels(req dto.AgentRoleChannelsReq) ([]dto.AgentRoleChannelItem, error)
GetRoleMarkdownFiles(req dto.AgentRoleMarkdownFilesReq) ([]dto.AgentRoleMarkdownFileItem, error)
UpdateRoleMarkdownFiles(req dto.AgentRoleMarkdownFilesUpdateReq) error
CreateAccount(req dto.AgentAccountCreateReq) error
UpdateAccount(req dto.AgentAccountUpdateReq) error
SyncAgentsByAccount(account *model.AgentAccount) error
@@ -68,6 +84,8 @@ type IAgentService interface {
GetQQBotConfig(req dto.AgentIDReq) (*dto.AgentQQBotConfig, error)
UpdateQQBotConfig(req dto.AgentQQBotConfigUpdateReq) error
InstallPlugin(req dto.AgentPluginInstallReq) error
UpgradePlugin(req dto.AgentPluginUpgradeReq) error
UninstallPlugin(req dto.AgentPluginUninstallReq) error
CheckPlugin(req dto.AgentPluginCheckReq) (*dto.AgentPluginStatus, error)
ApproveChannelPairing(req dto.AgentChannelPairingApproveReq) error
}
@@ -154,7 +172,7 @@ func (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) {
return nil, buserr.New("ErrAgentAccountNotVerified")
}
provider = account.Provider
baseURL = strings.TrimSpace(account.BaseURL)
baseURL = account.BaseURL
resolvedRuntime, err := resolveOpenclawAccountModelRuntimeByID(account, req.Model)
if err != nil {
return nil, err
@@ -234,6 +252,7 @@ func (a AgentService) Create(req dto.AgentCreateReq) (*dto.AgentItem, error) {
}
agent := &model.Agent{
Name: req.Name,
Remark: req.Remark,
AgentType: agentType,
Provider: provider,
Model: storedModel,
@@ -267,13 +286,22 @@ func (a AgentService) Page(req dto.SearchWithPage) (int64, []dto.AgentItem, erro
return 0, nil, err
}
items := make([]dto.AgentItem, 0, len(list))
appInstalls := make([]model.AppInstall, 0, len(list))
for _, item := range list {
appInstall, _ := appInstallRepo.GetFirst(repo.WithByID(item.AppInstallID))
appInstalls = append(appInstalls, appInstall)
}
syncAgentAppInstalls(appInstalls)
for index, item := range list {
appInstall := appInstalls[index]
envMap := readInstallEnv(appInstall.Env)
agentItem := buildAgentItem(&item, &appInstall, envMap)
agentItem.Upgradable = checkAgentUpgradable(appInstall)
items = append(items, agentItem)
}
if err := hydrateAgentWebsiteItems(items); err != nil {
return 0, nil, err
}
return count, items, nil
}
@@ -282,6 +310,13 @@ func (a AgentService) Delete(req dto.AgentDeleteReq) error {
if err != nil {
return err
}
resources, err := a.deleteCheckByAgent(agent)
if err != nil {
return err
}
if len(resources) > 0 {
return buserr.New("ErrAgentWebsiteBound")
}
if agent.AppInstallID == 0 {
return agentRepo.DeleteByID(agent.ID)
}
@@ -297,6 +332,58 @@ func (a AgentService) Delete(req dto.AgentDeleteReq) error {
return nil
}
func (a AgentService) DeleteCheck(req dto.AgentIDReq) ([]dto.AppResource, error) {
agent, err := agentRepo.GetFirst(repo.WithByID(req.AgentID))
if err != nil {
return nil, err
}
return a.deleteCheckByAgent(agent)
}
func (a AgentService) deleteCheckByAgent(agent *model.Agent) ([]dto.AppResource, error) {
if agent == nil || agent.WebsiteID == 0 {
return nil, nil
}
website, err := websiteRepo.GetFirst(repo.WithByID(agent.WebsiteID))
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
websiteName, err := loadAgentWebsiteResourceName(website)
if err != nil {
return nil, err
}
return []dto.AppResource{{Type: "website", Name: websiteName}}, nil
}
func syncAgentAppInstalls(appInstalls []model.AppInstall) {
if len(appInstalls) == 0 {
return
}
var containersMap map[string]container.Summary
cli, err := docker.NewClient()
if err == nil {
defer cli.Close()
containers, err := cli.ListAllContainers()
if err == nil {
containersMap = make(map[string]container.Summary, len(containers))
for _, contain := range containers {
containersMap[contain.Names[0]] = contain
}
}
}
for index := range appInstalls {
if appInstalls[index].ID == 0 || doNotNeedSync(appInstalls[index]) {
continue
}
synAppInstall(containersMap, &appInstalls[index], false)
}
}
func (a AgentService) ResetToken(req dto.AgentTokenResetReq) error {
agent, err := loadOpenclawAgentByID(req.ID)
if err != nil {
@@ -323,6 +410,39 @@ func (a AgentService) ResetToken(req dto.AgentTokenResetReq) error {
return agentRepo.Save(agent)
}
func (a AgentService) UpdateRemark(req dto.AgentRemarkUpdateReq) error {
agent, err := agentRepo.GetFirst(repo.WithByID(req.ID))
if err != nil {
return err
}
agent.Remark = req.Remark
return agentRepo.Save(agent)
}
func (a AgentService) GetModelConfig(req dto.AgentIDReq) (*dto.AgentModelConfig, error) {
agent, _, conf, err := a.loadOpenclawAgentConfig(req.AgentID)
if err != nil {
return nil, err
}
account, err := agentAccountRepo.GetFirst(repo.WithByID(agent.AccountID))
if err != nil {
return nil, err
}
models, err := loadAgentAccountModels(account)
if err != nil {
return nil, err
}
model := extractOpenclawPrimaryModelID(conf, account, models)
if model == "" {
model = agent.Model
}
return &dto.AgentModelConfig{
AccountID: agent.AccountID,
Model: model,
Fallbacks: extractOpenclawFallbackModelIDs(conf, account, models, model),
}, nil
}
func (a AgentService) UpdateModelConfig(req dto.AgentModelConfigUpdateReq) error {
agent, err := loadOpenclawAgentByID(req.AgentID)
if err != nil {
@@ -339,7 +459,7 @@ func (a AgentService) UpdateModelConfig(req dto.AgentModelConfigUpdateReq) error
modelName := resolvedRuntime.StoredModel
apiType, maxTokens, contextWindow := resolvedRuntime.APIType, resolvedRuntime.MaxTokens, resolvedRuntime.ContextWindow
confDir := path.Dir(agent.ConfigPath)
if err := writeOpenclawConfig(confDir, account, modelName, agent.Token, nil); err != nil {
if err := writeOpenclawConfig(confDir, account, modelName, agent.Token, nil, req.Fallbacks); err != nil {
return err
}
agent.Provider = account.Provider
@@ -442,6 +562,7 @@ func (a AgentService) UpdateAccount(req dto.AgentAccountUpdateReq) error {
return err
}
terminalai.InvalidateTerminalRuntimeCache()
terminalai.InvalidateFileAIRuntimeCache()
if req.SyncAgents {
if err := a.syncAgentsByAccount(account); err != nil {
return err
@@ -582,6 +703,7 @@ func (a AgentService) UpdateAccountModel(req dto.AgentAccountModelUpdateReq) err
return err
}
terminalai.InvalidateTerminalRuntimeCache()
terminalai.InvalidateFileAIRuntimeCache()
return a.syncAgentsByAccount(account)
}
@@ -614,6 +736,7 @@ func (a AgentService) DeleteAccountModel(req dto.AgentAccountModelDeleteReq) err
return err
}
terminalai.InvalidateTerminalRuntimeCache()
terminalai.InvalidateFileAIRuntimeCache()
return a.syncAgentsByAccount(account)
}
@@ -633,10 +756,16 @@ func (a AgentService) DeleteAccount(req dto.AgentAccountDeleteReq) error {
if exists, _ := agentRepo.GetFirst(repo.WithByAccountID(req.ID)); exists != nil && exists.ID > 0 {
return buserr.New("ErrAgentAccountBound")
}
if aiStatus, _ := settingRepo.GetValueByKey("AIStatus"); strings.EqualFold(strings.TrimSpace(aiStatus), constant.StatusEnable) {
if aiAccountID, _ := settingRepo.GetValueByKey("AIAccountID"); strings.TrimSpace(aiAccountID) == strconv.FormatUint(uint64(req.ID), 10) {
return buserr.New("ErrTerminalAIAccountInUse")
}
}
if err := agentAccountModelRepo.Delete(repo.WithByAccountID(req.ID)); err != nil {
return err
}
terminalai.InvalidateTerminalRuntimeCache()
terminalai.InvalidateFileAIRuntimeCache()
return agentAccountRepo.DeleteByID(req.ID)
}
@@ -753,7 +882,7 @@ func (a AgentService) UpdateConfigFile(req dto.AgentConfigFileUpdateReq) error {
}
func getOpenclawNPMRegistry(containerName string) (string, error) {
registry, err := cmd.RunDefaultWithStdoutBashCfAndTimeOut("docker exec %s npm get registry", 20*time.Second, containerName)
registry, err := runDockerExecWithStdout(20*time.Second, containerName, "npm", "get", "registry")
if err != nil {
return "", err
}
@@ -840,7 +969,6 @@ func (a AgentService) syncAgentsByAccount(account *model.AgentAccount) error {
if len(accountModels) == 0 {
return nil
}
baseURL := resolveAccountBaseURL(account)
for _, agent := range agents {
confDir := path.Dir(agent.ConfigPath)
modelName := strings.TrimSpace(agent.Model)
@@ -853,16 +981,21 @@ func (a AgentService) syncAgentsByAccount(account *model.AgentAccount) error {
} else {
selectedAccountModel = accountModels[0]
}
conf, err := readOpenclawConfig(agent.ConfigPath)
if err != nil {
return err
}
fallbacks := extractOpenclawFallbackModelIDs(conf, account, accountModels, selectedAccountModel.ID)
resolvedRuntime, err := buildOpenclawAccountModelRuntime(account, selectedAccountModel)
if err != nil {
return err
}
modelName = resolvedRuntime.StoredModel
apiType, maxTokens, contextWindow := resolvedRuntime.APIType, resolvedRuntime.MaxTokens, resolvedRuntime.ContextWindow
if err := writeOpenclawConfig(confDir, account, modelName, agent.Token, nil); err != nil {
if err := writeOpenclawConfig(confDir, account, modelName, agent.Token, nil, fallbacks); err != nil {
return err
}
agent.BaseURL = baseURL
agent.BaseURL = account.BaseURL
agent.APIKey = account.APIKey
agent.Provider = account.Provider
agent.Model = modelName
+533
View File
@@ -0,0 +1,533 @@
package service
import (
"os"
"path"
"sort"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
)
var agentMarkdownFileNames = []string{"AGENTS.md", "SOUL.md", "USER.md", "IDENTITY.md", "TOOLS.md", "HEARTBEAT.md", "BOOT.md", "BOOTSTRAP.md"}
func (a AgentService) CreateRole(req dto.AgentRoleCreateReq) (*dto.AgentRoleCreateResp, error) {
_, install, err := a.loadAgentAndInstall(req.AgentID)
if err != nil {
return nil, err
}
args := []string{"exec", install.ContainerName, "openclaw", "agents", "add", req.Name}
workspace := "/home/node/.openclaw/workspace-agent_" + req.Name
agentDir := "/home/node/.openclaw/agents/" + req.Name
args = append(args, "--workspace", workspace)
if model := strings.TrimSpace(req.Model); model != "" {
args = append(args, "--model", model)
}
for _, binding := range req.Bindings {
channel := binding.Channel
if channel == "" {
continue
}
if accountID := binding.AccountID; accountID != "" {
channel = channel + ":" + accountID
}
args = append(args, "--bind", channel)
}
args = append(args, "--agent-dir", agentDir)
args = append(args, "--non-interactive", "--json")
mgr := cmd.NewCommandMgr(cmd.WithTimeout(5 * time.Minute))
output, err := mgr.RunWithStdout("docker", args...)
if err != nil {
return nil, err
}
return &dto.AgentRoleCreateResp{Output: strings.TrimSpace(output)}, nil
}
func (a AgentService) GetConfiguredAgents(req dto.AgentConfiguredAgentsReq) ([]dto.AgentConfiguredAgentItem, error) {
agent, _, conf, err := a.loadAgentConfig(req.AgentID)
if err != nil {
return nil, err
}
agents, ok := conf["agents"].(map[string]interface{})
if !ok {
return []dto.AgentConfiguredAgentItem{}, nil
}
rawList, ok := agents["list"].([]interface{})
if !ok {
return []dto.AgentConfiguredAgentItem{}, nil
}
result := make([]dto.AgentConfiguredAgentItem, 0, len(rawList))
baseDir := path.Join(global.Dir.AppInstallDir, agent.AgentType, agent.Name, "data")
for _, item := range rawList {
record, ok := item.(map[string]interface{})
if !ok {
continue
}
configured := extractConfiguredAgentItem(baseDir, record)
if strings.EqualFold(configured.ID, "main") {
continue
}
result = append(result, configured)
}
applyConfiguredAgentBindings(result, conf["bindings"])
return result, nil
}
func (a AgentService) GetRoleChannels(req dto.AgentRoleChannelsReq) ([]dto.AgentRoleChannelItem, error) {
_, _, conf, err := a.loadAgentConfig(req.AgentID)
if err != nil {
return nil, err
}
channels, ok := conf["channels"].(map[string]interface{})
if !ok || len(channels) == 0 {
return []dto.AgentRoleChannelItem{}, nil
}
boundBindings := loadBoundChannelBindings(conf["bindings"])
result := make([]dto.AgentRoleChannelItem, 0, len(channels))
for key := range channels {
if key == "" {
continue
}
accountIDs := extractRoleChannelAccountIDs(conf, key)
availableAccountIDs := filterAvailableChannelAccountIDs(boundBindings, key, accountIDs)
result = append(result, dto.AgentRoleChannelItem{
Name: key,
Bound: isRoleChannelFullyBound(boundBindings, key, accountIDs, availableAccountIDs),
AccountIDs: availableAccountIDs,
})
}
sort.Slice(result, func(i, j int) bool {
return result[i].Name < result[j].Name
})
return result, nil
}
func (a AgentService) DeleteRole(req dto.AgentRoleDeleteReq) error {
agent, install, conf, err := a.loadAgentConfig(req.AgentID)
if err != nil {
return err
}
baseDir := path.Join(global.Dir.AppInstallDir, agent.AgentType, agent.Name, "data")
if req.ID == "" {
return buserr.New("ErrRecordNotFound")
}
target, ok := findConfiguredAgentByID(baseDir, conf, req.ID)
if !ok {
return buserr.New("ErrRecordNotFound")
}
args := []string{"exec", install.ContainerName, "openclaw", "agents", "delete", req.ID, "--force"}
mgr := cmd.NewCommandMgr(cmd.WithTimeout(5 * time.Minute))
if _, err = mgr.RunWithStdout("docker", args...); err != nil {
return err
}
if target.Workspace != "" {
if err := os.RemoveAll(target.Workspace); err != nil {
return err
}
}
if target.AgentDir != "" {
if err := os.RemoveAll(target.AgentDir); err != nil {
return err
}
}
return nil
}
func (a AgentService) BindRole(req dto.AgentRoleBindReq) error {
return a.operateRoleBinding(req, "bind")
}
func (a AgentService) UnbindRole(req dto.AgentRoleBindReq) error {
return a.operateRoleBinding(req, "unbind")
}
func (a AgentService) operateRoleBinding(req dto.AgentRoleBindReq, action string) error {
agent, install, conf, err := a.loadAgentConfig(req.AgentID)
if err != nil {
return err
}
baseDir := path.Join(global.Dir.AppInstallDir, agent.AgentType, agent.Name, "data")
if req.ID == "" {
return buserr.New("ErrRecordNotFound")
}
if _, ok := findConfiguredAgentByID(baseDir, conf, req.ID); !ok {
return buserr.New("ErrRecordNotFound")
}
binding := formatRoleBinding(req.Channel, req.AccountID)
if binding == "" {
return buserr.New("ErrInvalidParams")
}
args := []string{
"exec",
install.ContainerName,
"openclaw",
"agents",
action,
"--agent",
req.ID,
"--bind",
binding,
}
args = append(args, "--json")
mgr := cmd.NewCommandMgr(cmd.WithTimeout(5 * time.Minute))
_, err = mgr.RunWithStdout("docker", args...)
return err
}
func (a AgentService) GetRoleMarkdownFiles(req dto.AgentRoleMarkdownFilesReq) ([]dto.AgentRoleMarkdownFileItem, error) {
agent, err := loadOpenclawAgentByID(req.AgentID)
if err != nil {
return nil, err
}
baseDir := path.Join(global.Dir.AppInstallDir, agent.AgentType, agent.Name, "data")
workspaceDir, err := resolveMarkdownWorkspaceDir(baseDir, req.Workspace)
if err != nil {
return nil, err
}
items := make([]dto.AgentRoleMarkdownFileItem, 0, len(agentMarkdownFileNames))
for _, name := range agentMarkdownFileNames {
item := dto.AgentRoleMarkdownFileItem{
Name: name,
}
content, readErr := os.ReadFile(path.Join(workspaceDir, name))
if readErr == nil {
item.Content = string(content)
} else if !os.IsNotExist(readErr) {
return nil, readErr
}
items = append(items, item)
}
return items, nil
}
func (a AgentService) UpdateRoleMarkdownFiles(req dto.AgentRoleMarkdownFilesUpdateReq) error {
agent, install, err := a.loadOpenclawAgentAndInstall(req.AgentID)
if err != nil {
return err
}
baseDir := path.Join(global.Dir.AppInstallDir, agent.AgentType, agent.Name, "data")
dirPath, err := resolveMarkdownWorkspaceDir(baseDir, req.Workspace)
if err != nil {
return err
}
if err = os.MkdirAll(dirPath, 0755); err != nil {
return err
}
for _, item := range req.Files {
file := path.Join(dirPath, item.Name)
if err = os.WriteFile(file, []byte(item.Content), 0644); err != nil {
return err
}
}
if req.Restart {
return NewIAppInstalledService().Operate(request.AppInstalledOperate{
InstallId: install.ID,
Operate: constant.Restart,
})
}
return nil
}
func extractConfiguredAgentItem(installDir string, record map[string]interface{}) dto.AgentConfiguredAgentItem {
item := dto.AgentConfiguredAgentItem{Bindings: []dto.AgentRoleBinding{}}
if id, ok := record["id"].(string); ok {
item.ID = id
} else if id, ok := record["agentId"].(string); ok {
item.ID = id
}
if name, ok := record["name"].(string); ok {
item.Name = name
}
if workspace, ok := record["workspace"].(string); ok {
item.Workspace = resolveRoleDir(installDir, workspace)
}
if model, ok := record["model"].(string); ok {
item.Model = model
}
if agentDir, ok := record["agentDir"].(string); ok {
item.AgentDir = agentDir
} else if agentDir, ok := record["agent_dir"].(string); ok {
item.AgentDir = agentDir
}
item.AgentDir = resolveRoleDir(installDir, item.AgentDir)
return item
}
func findConfiguredAgentByID(baseDir string, conf map[string]interface{}, id string) (dto.AgentConfiguredAgentItem, bool) {
agents, ok := conf["agents"].(map[string]interface{})
if !ok {
return dto.AgentConfiguredAgentItem{}, false
}
rawList, ok := agents["list"].([]interface{})
if !ok {
return dto.AgentConfiguredAgentItem{}, false
}
for _, item := range rawList {
record, ok := item.(map[string]interface{})
if !ok {
continue
}
configured := extractConfiguredAgentItem(baseDir, record)
if strings.EqualFold(configured.ID, id) {
return configured, true
}
}
return dto.AgentConfiguredAgentItem{}, false
}
func formatRoleBinding(channel, accountID string) string {
if channel == "" {
return ""
}
if accountID == "" {
return channel
}
return channel + ":" + accountID
}
func applyConfiguredAgentBindings(agents []dto.AgentConfiguredAgentItem, value interface{}) {
bindings, ok := value.([]interface{})
if !ok || len(agents) == 0 {
return
}
indexByID := make(map[string]int, len(agents))
indexByName := make(map[string]int, len(agents))
for i, agent := range agents {
if agent.ID != "" {
indexByID[agent.ID] = i
}
if agent.Name != "" {
indexByName[agent.Name] = i
}
}
for _, binding := range bindings {
record, ok := binding.(map[string]interface{})
if !ok {
continue
}
if bindingType, _ := record["type"].(string); !strings.EqualFold(bindingType, "route") {
continue
}
targetID, _ := record["agentId"].(string)
if targetID == "" {
continue
}
match, ok := record["match"].(map[string]interface{})
if !ok {
continue
}
channel, _ := match["channel"].(string)
if channel == "" {
continue
}
index, ok := indexByID[targetID]
if !ok {
index, ok = indexByName[targetID]
}
if !ok {
continue
}
accountID, _ := match["accountId"].(string)
if accountID == "" {
accountID, _ = record["accountId"].(string)
}
agents[index].Bindings = append(agents[index].Bindings, dto.AgentRoleBinding{
Channel: channel,
AccountID: accountID,
})
}
}
func loadBoundChannelBindings(value interface{}) map[string]map[string]struct{} {
result := make(map[string]map[string]struct{})
bindings, ok := value.([]interface{})
if !ok {
return result
}
for _, binding := range bindings {
record, ok := binding.(map[string]interface{})
if !ok {
continue
}
if bindingType, _ := record["type"].(string); !strings.EqualFold(bindingType, "route") {
continue
}
match, ok := record["match"].(map[string]interface{})
if !ok {
continue
}
channel, _ := match["channel"].(string)
if channel == "" {
continue
}
accountID, _ := match["accountId"].(string)
if accountID == "" {
accountID, _ = record["accountId"].(string)
}
if _, ok := result[channel]; !ok {
result[channel] = make(map[string]struct{})
}
result[channel][accountID] = struct{}{}
}
return result
}
func extractRoleChannelAccountIDs(conf map[string]interface{}, channel string) []string {
switch channel {
case "feishu":
config := extractFeishuConfig(conf)
accountIDs := make([]string, 0, len(config.Bots))
for _, item := range config.Bots {
if accountID := item.AccountID; accountID != "" {
accountIDs = append(accountIDs, accountID)
}
}
sort.Strings(accountIDs)
return accountIDs
case "telegram":
config := extractTelegramConfig(conf)
accountIDs := make([]string, 0, len(config.Bots))
for _, item := range config.Bots {
if accountID := item.AccountID; accountID != "" {
accountIDs = append(accountIDs, accountID)
}
}
sort.Strings(accountIDs)
return accountIDs
case "discord":
config := extractDiscordConfig(conf)
accountIDs := make([]string, 0, len(config.Bots))
for _, item := range config.Bots {
if accountID := item.AccountID; accountID != "" {
accountIDs = append(accountIDs, accountID)
}
}
sort.Strings(accountIDs)
return accountIDs
case "qqbot":
config := extractQQBotConfig(conf)
accountIDs := make([]string, 0, len(config.Bots))
for _, item := range config.Bots {
if accountID := item.AccountID; accountID != "" {
accountIDs = append(accountIDs, accountID)
}
}
sort.Strings(accountIDs)
return accountIDs
case "dingtalk-connector":
config := extractDingTalkConfig(conf)
accountIDs := make([]string, 0, len(config.Bots))
for _, item := range config.Bots {
if accountID := item.AccountID; accountID != "" {
accountIDs = append(accountIDs, accountID)
}
}
sort.Strings(accountIDs)
return accountIDs
case "wecom":
return []string{}
default:
return extractRawChannelAccountIDs(getChannelConfig(conf, channel))
}
}
func filterAvailableChannelAccountIDs(bindings map[string]map[string]struct{}, channel string, accountIDs []string) []string {
channelBindings, ok := bindings[channel]
if !ok || len(channelBindings) == 0 {
return append([]string(nil), accountIDs...)
}
if _, ok := channelBindings[""]; ok {
return []string{}
}
result := make([]string, 0, len(accountIDs))
for _, accountID := range accountIDs {
if _, ok := channelBindings[accountID]; ok {
continue
}
result = append(result, accountID)
}
return result
}
func isRoleChannelFullyBound(bindings map[string]map[string]struct{}, channel string, allAccountIDs, availableAccountIDs []string) bool {
channelBindings, ok := bindings[channel]
if !ok || len(channelBindings) == 0 {
return false
}
if _, ok := channelBindings[""]; ok {
return true
}
if len(allAccountIDs) == 0 {
return true
}
return len(availableAccountIDs) == 0
}
func extractRawChannelAccountIDs(channel map[string]interface{}) []string {
if len(channel) == 0 {
return []string{}
}
accounts, ok := channel["accounts"].(map[string]interface{})
if !ok || len(accounts) == 0 {
return []string{}
}
result := make([]string, 0, len(accounts))
for key := range accounts {
if key == "" {
continue
}
result = append(result, key)
}
sort.Strings(result)
return result
}
func resolveRoleDir(installDir, workspace string) string {
if workspace == "" {
return ""
}
if strings.HasPrefix(workspace, "/home/node/.openclaw/workspace/") {
return strings.ReplaceAll(workspace, "/home/node/.openclaw", installDir)
}
return strings.ReplaceAll(workspace, "/home/node/.openclaw", path.Join(installDir, "conf"))
}
func resolveMarkdownWorkspaceDir(installDir, workspace string) (string, error) {
if workspace == "" {
return "", buserr.New("ErrRecordNotFound")
}
confDir := path.Join(installDir, "conf")
allowedOpenclawPrefixes := []string{
"/home/node/.openclaw/workspace/",
"/home/node/.openclaw/workspace-agent_",
}
for _, prefix := range allowedOpenclawPrefixes {
if strings.HasPrefix(workspace, prefix) {
return resolveRoleDir(installDir, workspace), nil
}
}
cleanWorkspace := path.Clean(workspace)
cleanConfDir := path.Clean(confDir)
if cleanWorkspace == cleanConfDir || strings.HasPrefix(cleanWorkspace, cleanConfDir+"/") {
return cleanWorkspace, nil
}
return "", buserr.New("ErrRecordNotFound")
}
File diff suppressed because it is too large Load Diff
+1 -6
View File
@@ -10,7 +10,6 @@ import (
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
)
const (
@@ -102,11 +101,7 @@ func countOpenclawConfiguredChannels(conf map[string]interface{}) int {
}
func loadOpenclawOverviewSkillStats(containerName string) (int, error) {
output, err := cmd.RunDefaultWithStdoutBashCfAndTimeOut(
"docker exec %s openclaw skills list --json 2>&1",
30*time.Second,
containerName,
)
output, err := runDockerExecWithStdout(5*time.Minute, containerName, "sh", "-c", "openclaw skills list --json 2>&1")
if err != nil {
return 0, err
}
+59 -29
View File
@@ -13,6 +13,9 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
)
const clawhubGlobalRegistry = "https://clawhub.com"
const clawhubChinaRegistry = "https://mirror-cn.clawhub.com"
type openclawSkillsList struct {
Skills []openclawSkillListItem `json:"skills"`
}
@@ -35,6 +38,7 @@ type skillhubSearchPayload struct {
}
var clawhubSearchLinePattern = regexp.MustCompile(`^(\S+)\s+(.+?)\s+\(([\d.]+)\)$`)
var ansiEscapePattern = regexp.MustCompile(`\x1b\[[0-9;?]*[ -/]*[@-~]`)
func (a AgentService) ListSkills(req dto.AgentIDReq) ([]dto.AgentSkillItem, error) {
_, install, err := a.loadOpenclawAgentAndInstall(req.AgentID)
@@ -44,11 +48,7 @@ func (a AgentService) ListSkills(req dto.AgentIDReq) ([]dto.AgentSkillItem, erro
if err := ensureContainerRunning(install.ContainerName); err != nil {
return nil, err
}
output, err := cmd.RunDefaultWithStdoutBashCfAndTimeOut(
"docker exec %s openclaw skills list --json 2>&1",
30*time.Second,
install.ContainerName,
)
output, err := runDockerExecWithStdout(5*time.Minute, install.ContainerName, "sh", "-c", "openclaw skills list --json 2>&1")
if err != nil {
return nil, err
}
@@ -77,7 +77,7 @@ func (a AgentService) SearchSkills(req dto.AgentSkillSearchReq) ([]dto.AgentSkil
case "skillhub":
return parseSkillhubSearchResult(output)
default:
return parseClawhubSearchResult(output), nil
return parseClawhubSearchResult(output, req.Source), nil
}
}
@@ -114,7 +114,7 @@ func (a AgentService) InstallSkill(req dto.AgentSkillInstallReq) error {
return err
}
installTask.AddSubTask("Install OpenClaw skill", func(t *task.Task) error {
mgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(10*time.Minute))
mgr := cmd.NewCommandMgr(cmd.WithTask(*t), cmd.WithContext(t.TaskCtx), cmd.WithTimeout(20*time.Minute))
return mgr.Run("docker", "exec", install.ContainerName, "sh", "-c", buildOpenclawSkillInstallCommand(req.Source, req.Slug))
}, nil)
go func() {
@@ -126,8 +126,15 @@ func (a AgentService) InstallSkill(req dto.AgentSkillInstallReq) error {
}
func parseOpenclawSkillsList(output string) ([]dto.AgentSkillItem, error) {
payloadBytes, err := extractEmbeddedJSON(output)
if err != nil {
return nil, err
}
if len(payloadBytes) == 0 {
return nil, nil
}
var payload openclawSkillsList
if err := json.Unmarshal([]byte(strings.TrimSpace(output)), &payload); err != nil {
if err := json.Unmarshal(payloadBytes, &payload); err != nil {
return nil, err
}
items := make([]dto.AgentSkillItem, 0, len(payload.Skills))
@@ -146,18 +153,14 @@ func parseOpenclawSkillsList(output string) ([]dto.AgentSkillItem, error) {
func loadOpenclawSkillSearchOutput(containerName, source, keyword string) (string, error) {
switch source {
case "skillhub":
return cmd.RunDefaultWithStdoutBashCfAndTimeOut(
"docker exec %s skillhub search %q --json 2>&1",
30*time.Second,
containerName,
keyword,
)
return runDockerExecWithStdout(2*time.Minute, containerName, "skillhub", "search", keyword, "--json")
default:
return cmd.RunDefaultWithStdoutBashCfAndTimeOut(
"docker exec %s clawhub search %q 2>&1",
30*time.Second,
return runDockerExecWithStdout(
2*time.Minute,
containerName,
keyword,
"sh",
"-c",
fmt.Sprintf("CLAWHUB_REGISTRY=%q clawhub search %q", resolveClawhubRegistry(source), keyword),
)
}
}
@@ -188,7 +191,7 @@ func parseSkillhubSearchResult(output string) ([]dto.AgentSkillSearchItem, error
return items, nil
}
func parseClawhubSearchResult(output string) []dto.AgentSkillSearchItem {
func parseClawhubSearchResult(output, source string) []dto.AgentSkillSearchItem {
lines := strings.Split(strings.TrimSpace(output), "\n")
items := make([]dto.AgentSkillSearchItem, 0, len(lines))
for _, line := range lines {
@@ -200,7 +203,7 @@ func parseClawhubSearchResult(output string) []dto.AgentSkillSearchItem {
Slug: matches[1],
Name: matches[2],
Score: matches[3],
Source: "clawhub",
Source: source,
})
}
return items
@@ -208,10 +211,11 @@ func parseClawhubSearchResult(output string) []dto.AgentSkillSearchItem {
func buildOpenclawSkillInstallCommand(source, slug string) string {
switch source {
case "clawhub":
case "clawhub-global", "clawhub-cn":
return fmt.Sprintf(
"mkdir -p %s && clawhub --workdir /home/node/.openclaw --dir skills install %q",
"mkdir -p %s && CLAWHUB_REGISTRY=%q clawhub --workdir /home/node/.openclaw --dir skills install %q",
openclawManagedSkillsDir,
resolveClawhubRegistry(source),
slug,
)
default:
@@ -224,13 +228,17 @@ func buildOpenclawSkillInstallCommand(source, slug string) string {
}
}
func resolveClawhubRegistry(source string) string {
switch source {
case "clawhub-cn":
return clawhubChinaRegistry
default:
return clawhubGlobalRegistry
}
}
func getOpenclawSkillKey(containerName, name string) (string, error) {
output, err := cmd.RunDefaultWithStdoutBashCfAndTimeOut(
"docker exec %s openclaw skills info %q --json 2>&1",
30*time.Second,
containerName,
name,
)
output, err := runDockerExecWithStdout(2*time.Minute, containerName, "sh", "-c", fmt.Sprintf("openclaw skills info %q --json 2>&1", name))
if err != nil {
return "", err
}
@@ -238,8 +246,12 @@ func getOpenclawSkillKey(containerName, name string) (string, error) {
}
func parseOpenclawSkillKey(name, output string) (string, error) {
payloadBytes, err := extractEmbeddedJSON(output)
if err != nil {
return "", err
}
var payload openclawSkillInfo
if err := json.Unmarshal([]byte(strings.TrimSpace(output)), &payload); err != nil {
if err := json.Unmarshal(payloadBytes, &payload); err != nil {
return "", err
}
if payload.SkillKey == "" {
@@ -248,6 +260,24 @@ func parseOpenclawSkillKey(name, output string) (string, error) {
return payload.SkillKey, nil
}
func extractEmbeddedJSON(output string) ([]byte, error) {
trimmed := strings.TrimSpace(ansiEscapePattern.ReplaceAllString(output, ""))
if trimmed == "" {
return nil, nil
}
for i := 0; i < len(trimmed); i++ {
if trimmed[i] != '{' && trimmed[i] != '[' {
continue
}
decoder := json.NewDecoder(strings.NewReader(trimmed[i:]))
var raw json.RawMessage
if err := decoder.Decode(&raw); err == nil {
return raw, nil
}
}
return nil, fmt.Errorf("json payload not found")
}
func setOpenclawSkillEnabled(conf map[string]interface{}, skillKey string, enabled bool) {
skills := ensureChildMap(conf, "skills")
entries := ensureChildMap(skills, "entries")
+198 -179
View File
@@ -19,6 +19,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/1Panel-dev/1Panel/agent/utils/req_helper"
@@ -59,6 +60,11 @@ func ensureContainerRunning(containerName string) error {
return nil
}
func runDockerExecWithStdout(timeout time.Duration, containerName string, args ...string) (string, error) {
commandArgs := append([]string{"exec", containerName}, args...)
return cmd.NewCommandMgr(cmd.WithTimeout(timeout)).RunWithStdout("docker", commandArgs...)
}
func resolveAgentAccountInput(provider, apiKey, baseURL string) (resolvedAgentAccountInput, error) {
resolvedAPIKey := strings.TrimSpace(apiKey)
resolvedBaseURL := strings.TrimSpace(baseURL)
@@ -333,13 +339,11 @@ func setOtherConfig(conf map[string]interface{}, config dto.AgentOtherConfig) {
}
func buildAgentItem(agent *model.Agent, appInstall *model.AppInstall, envMap map[string]interface{}) dto.AgentItem {
agentType := normalizeAgentType(agent.AgentType)
if appInstall != nil && appInstall.ID > 0 && appInstall.App.Key == constant.AppCopaw {
agentType = constant.AppCopaw
}
agentType := agent.AgentType
item := dto.AgentItem{
ID: agent.ID,
Name: agent.Name,
Remark: agent.Remark,
AgentType: agentType,
Provider: agent.Provider,
ProviderName: providercatalog.DisplayName(agent.Provider),
@@ -353,6 +357,7 @@ func buildAgentItem(agent *model.Agent, appInstall *model.AppInstall, envMap map
Status: agent.Status,
Message: agent.Message,
AppInstallID: agent.AppInstallID,
WebsiteID: agent.WebsiteID,
AccountID: agent.AccountID,
ConfigPath: agent.ConfigPath,
CreatedAt: agent.CreatedAt,
@@ -653,7 +658,8 @@ type agentDefaults struct {
}
type modelRef struct {
Primary string `json:"primary"`
Primary string `json:"primary"`
Fallbacks []string `json:"fallbacks,omitempty"`
}
type modelsConfig struct {
@@ -679,6 +685,23 @@ type modelEntry struct {
Cost modelCost `json:"cost"`
}
func requiresOpenclawProviderModels(provider string) bool {
return provider != "gemini"
}
func applyOpenclawModelsConfig(conf map[string]interface{}, models *modelsConfig) error {
if models == nil {
delete(conf, "models")
return nil
}
modelsMap, err := structToMap(models)
if err != nil {
return err
}
conf["models"] = modelsMap
return nil
}
type modelCost struct {
Input float64 `json:"input"`
Output float64 `json:"output"`
@@ -694,7 +717,7 @@ type browserConfig struct {
DefaultProfile string `json:"defaultProfile"`
}
func writeOpenclawConfig(confDir string, account *model.AgentAccount, modelName, token string, allowedOrigins []string) error {
func writeOpenclawConfig(confDir string, account *model.AgentAccount, modelName, token string, allowedOrigins []string, fallbacks []string) error {
if strings.TrimSpace(confDir) == "" {
return fmt.Errorf("config dir is required")
}
@@ -717,6 +740,10 @@ func writeOpenclawConfig(confDir string, account *model.AgentAccount, modelName,
if err != nil {
return err
}
resolvedFallbacks, err := resolveOpenclawFallbackModels(account, modelName, fallbacks)
if err != nil {
return err
}
cfg := openclawConfig{
Gateway: gatewayConfig{
@@ -736,7 +763,7 @@ func writeOpenclawConfig(confDir string, account *model.AgentAccount, modelName,
Agents: agentsConfig{
Defaults: agentDefaults{
UserTimezone: resolveServerTimezone(),
Model: modelRef{Primary: primaryModel},
Model: modelRef{Primary: primaryModel, Fallbacks: resolvedFallbacks},
Models: defaultsModels,
},
},
@@ -775,12 +802,8 @@ func writeOpenclawConfig(confDir string, account *model.AgentAccount, modelName,
}
conf = initial
} else {
if cfg.Models != nil {
modelsMap, err := structToMap(cfg.Models)
if err != nil {
return err
}
conf["models"] = modelsMap
if err := applyOpenclawModelsConfig(conf, cfg.Models); err != nil {
return err
}
if _, ok := conf["browser"]; !ok {
browserMap, err := structToMap(cfg.Browser)
@@ -804,6 +827,11 @@ func writeOpenclawConfig(confDir string, account *model.AgentAccount, modelName,
}
modelMap := ensureChildMap(defaultsMap, "model")
modelMap["primary"] = cfg.Agents.Defaults.Model.Primary
if len(cfg.Agents.Defaults.Model.Fallbacks) > 0 {
modelMap["fallbacks"] = cfg.Agents.Defaults.Model.Fallbacks
} else {
delete(modelMap, "fallbacks")
}
if cfg.Agents.Defaults.Models != nil {
defaultsMap["models"] = cfg.Agents.Defaults.Models
}
@@ -833,19 +861,135 @@ func writeOpenclawConfig(confDir string, account *model.AgentAccount, modelName,
}
envPath := path.Join(confDir, ".env")
lines := []string{fmt.Sprintf("OPENCLAW_GATEWAY_TOKEN=%s", token)}
if envKey := providercatalog.EnvKey(account.Provider); envKey != "" && strings.TrimSpace(account.APIKey) != "" {
if envKey := providercatalog.EnvKey(account.Provider); envKey != "" && account.APIKey != "" {
lines = append(lines, fmt.Sprintf("%s=%s", envKey, account.APIKey))
}
content := strings.Join(lines, "\n") + "\n"
return fileOp.SaveFile(envPath, content, 0600)
}
func resolveOpenclawFallbackModels(account *model.AgentAccount, primaryModel string, fallbackIDs []string) ([]string, error) {
accountModels, err := loadAgentAccountModels(account)
if err != nil {
return nil, err
}
return resolveOpenclawFallbackModelsFromPool(account, accountModels, primaryModel, fallbackIDs, true)
}
func resolveOpenclawFallbackModelsFromPool(account *model.AgentAccount, accountModels []dto.AgentAccountModel, primaryModel string, fallbackIDs []string, strict bool) ([]string, error) {
if len(fallbackIDs) == 0 {
return nil, nil
}
result := make([]string, 0, len(fallbackIDs))
seen := make(map[string]struct{}, len(fallbackIDs))
for _, item := range fallbackIDs {
target := strings.TrimSpace(item)
if target == "" {
continue
}
accountModel, err := requireAgentAccountModelForProvider(account.Provider, accountModels, target)
if err != nil {
if strict {
return nil, err
}
continue
}
if sameProviderModelID(account.Provider, accountModel.ID, primaryModel) {
if strict {
return nil, buserr.New("ErrAgentFallbackModelPrimary")
}
continue
}
resolvedPrimary, _, _, _, err := buildOpenclawAccountModelConfig(account, accountModel)
if err != nil {
return nil, err
}
if _, ok := seen[resolvedPrimary]; ok {
if strict {
return nil, buserr.New("ErrAgentFallbackModelDuplicate")
}
continue
}
seen[resolvedPrimary] = struct{}{}
result = append(result, resolvedPrimary)
}
return result, nil
}
func extractOpenclawPrimaryModelID(conf map[string]interface{}, account *model.AgentAccount, accountModels []dto.AgentAccountModel) string {
modelMap := openclawModelRefMap(conf)
primary := strings.TrimSpace(fmt.Sprintf("%v", modelMap["primary"]))
if primary == "" {
return ""
}
resolved, err := buildOpenclawResolvedModelIDMap(account, accountModels)
if err != nil {
return ""
}
return resolved[primary]
}
func extractOpenclawFallbackModelIDs(conf map[string]interface{}, account *model.AgentAccount, accountModels []dto.AgentAccountModel, primaryModel string) []string {
modelMap := openclawModelRefMap(conf)
raw, ok := modelMap["fallbacks"].([]interface{})
if !ok || len(raw) == 0 {
return nil
}
resolved, err := buildOpenclawResolvedModelIDMap(account, accountModels)
if err != nil {
return nil
}
result := make([]string, 0, len(raw))
seen := make(map[string]struct{}, len(raw))
for _, item := range raw {
target := strings.TrimSpace(fmt.Sprintf("%v", item))
modelID, ok := resolved[target]
if !ok || sameProviderModelID(account.Provider, modelID, primaryModel) {
continue
}
if _, ok := seen[modelID]; ok {
continue
}
seen[modelID] = struct{}{}
result = append(result, modelID)
}
return result
}
func openclawModelRefMap(conf map[string]interface{}) map[string]interface{} {
agentsMap, ok := conf["agents"].(map[string]interface{})
if !ok {
return nil
}
defaultsMap, ok := agentsMap["defaults"].(map[string]interface{})
if !ok {
return nil
}
modelMap, ok := defaultsMap["model"].(map[string]interface{})
if !ok {
return nil
}
return modelMap
}
func buildOpenclawResolvedModelIDMap(account *model.AgentAccount, accountModels []dto.AgentAccountModel) (map[string]string, error) {
result := make(map[string]string, len(accountModels))
for _, item := range accountModels {
resolvedPrimary, _, _, _, err := buildOpenclawAccountModelConfig(account, item)
if err != nil {
return nil, err
}
result[resolvedPrimary] = item.ID
}
return result, nil
}
func prepareOpenclawInstallFiles(appInstall *model.AppInstall, account *model.AgentAccount, modelName, token string, allowedOrigins []string) error {
if appInstall == nil {
return fmt.Errorf("app install is required")
}
confDir := path.Join(appInstall.GetPath(), "data", "conf")
if err := writeOpenclawConfig(confDir, account, modelName, token, allowedOrigins); err != nil {
if err := writeOpenclawConfig(confDir, account, modelName, token, allowedOrigins, nil); err != nil {
return err
}
dataDir := path.Join(appInstall.GetPath(), "data")
@@ -879,7 +1023,7 @@ func buildOpenclawModelsFromAccount(account *model.AgentAccount, selectedModel s
primaryModel := ""
defaultsModels := make(map[string]map[string]interface{}, len(accountModels))
for _, item := range accountModels {
resolvedPrimary, entry, key, baseCfg, err := buildOpenclawCatalogModel(account, item)
resolvedPrimary, entry, key, baseCfg, err := buildOpenclawAccountModelConfig(account, item)
if err != nil {
return "", nil, nil, err
}
@@ -899,6 +1043,9 @@ func buildOpenclawModelsFromAccount(account *model.AgentAccount, selectedModel s
if primaryModel == "" {
return "", nil, nil, buserr.New("ErrAgentModelNotInAccount")
}
if !requiresOpenclawProviderModels(account.Provider) {
return primaryModel, defaultsModels, nil, nil
}
providerCfg.Models = entries
return primaryModel, defaultsModels, &modelsConfig{
Mode: "merge",
@@ -908,25 +1055,33 @@ func buildOpenclawModelsFromAccount(account *model.AgentAccount, selectedModel s
}, nil
}
func buildOpenclawCatalogModel(account *model.AgentAccount, model dto.AgentAccountModel) (string, modelEntry, string, modelProvider, error) {
primaryModel, inferredEntry, providerKey, providerCfg, err := inferOpenclawCatalogModel(account, model.ID, model.Reasoning, model.MaxTokens, model.ContextWindow)
func buildOpenclawAccountModelConfig(account *model.AgentAccount, model dto.AgentAccountModel) (string, modelEntry, string, modelProvider, error) {
providerPatch, err := providercatalog.BuildOpenClawProviderPatch(account.Provider, model.ID, account.APIType, account.BaseURL, account.APIKey)
if err != nil {
return "", modelEntry{}, "", modelProvider{}, err
}
if strings.TrimSpace(model.Name) != "" {
inferredEntry.Name = strings.TrimSpace(model.Name)
return providerPatch.PrimaryModel, buildOpenclawModelEntry(providerPatch.ModelID, model), providerPatch.ProviderKey, modelProvider{
ApiKey: providerPatch.APIKey,
BaseUrl: providerPatch.BaseURL,
Api: providerPatch.APIType,
AuthHeader: providerPatch.AuthHeader,
}, nil
}
func buildOpenclawModelEntry(modelID string, model dto.AgentAccountModel) modelEntry {
name := strings.TrimSpace(model.Name)
if name == "" {
name = strings.TrimSpace(modelID)
}
if len(model.Input) > 0 {
inferredEntry.Input = sanitizeAgentAccountModelInputs(model.Input)
return modelEntry{
ID: strings.TrimSpace(modelID),
Name: name,
Reasoning: model.Reasoning,
Input: sanitizeAgentAccountModelInputs(model.Input),
ContextWindow: model.ContextWindow,
MaxTokens: model.MaxTokens,
Cost: modelCost{},
}
inferredEntry.Reasoning = model.Reasoning
if model.ContextWindow > 0 {
inferredEntry.ContextWindow = model.ContextWindow
}
if model.MaxTokens > 0 {
inferredEntry.MaxTokens = model.MaxTokens
}
return primaryModel, inferredEntry, providerKey, providerCfg, nil
}
type openclawAccountModelRuntime struct {
@@ -938,22 +1093,16 @@ type openclawAccountModelRuntime struct {
}
func buildOpenclawAccountModelRuntime(account *model.AgentAccount, model dto.AgentAccountModel) (openclawAccountModelRuntime, error) {
apiType, maxTokens, contextWindow := providercatalog.ResolveRuntimeParams(
account.Provider,
account.APIType,
model.MaxTokens,
model.ContextWindow,
)
primaryModel, _, _, _, err := buildOpenclawCatalogModel(account, model)
primaryModel, _, _, _, err := buildOpenclawAccountModelConfig(account, model)
if err != nil {
return openclawAccountModelRuntime{}, err
}
return openclawAccountModelRuntime{
StoredModel: model.ID,
PrimaryModel: primaryModel,
APIType: apiType,
MaxTokens: maxTokens,
ContextWindow: contextWindow,
APIType: account.APIType,
MaxTokens: model.MaxTokens,
ContextWindow: model.ContextWindow,
}, nil
}
@@ -969,44 +1118,6 @@ func resolveOpenclawAccountModelRuntimeByID(account *model.AgentAccount, modelID
return buildOpenclawAccountModelRuntime(account, selectedAccountModel)
}
func inferOpenclawCatalogModel(account *model.AgentAccount, modelID string, reasoning bool, maxTokens, contextWindow int) (string, modelEntry, string, modelProvider, error) {
baseURL := resolveAccountBaseURL(account)
resolvedAPIType, resolvedMaxTokens, resolvedContextWindow := providercatalog.ResolveRuntimeParams(account.Provider, account.APIType, maxTokens, contextWindow)
patch, err := providercatalog.BuildOpenClawPatch(account.Provider, modelID, resolvedAPIType, reasoning, resolvedMaxTokens, resolvedContextWindow, baseURL, account.APIKey)
if err != nil {
return "", modelEntry{}, "", modelProvider{}, err
}
if patch.Models == nil {
return "", modelEntry{}, "", modelProvider{}, fmt.Errorf("models patch is required")
}
modelsCfg, err := mapToModelsConfig(patch.Models)
if err != nil {
return "", modelEntry{}, "", modelProvider{}, err
}
for key, providerCfg := range modelsCfg.Providers {
if len(providerCfg.Models) == 0 {
continue
}
return patch.PrimaryModel, providerCfg.Models[0], key, modelProvider{
ApiKey: providerCfg.ApiKey,
BaseUrl: providerCfg.BaseUrl,
Api: providerCfg.Api,
AuthHeader: providerCfg.AuthHeader,
}, nil
}
return "", modelEntry{}, "", modelProvider{}, fmt.Errorf("models patch is invalid")
}
func resolveAccountBaseURL(account *model.AgentAccount) string {
baseURL := strings.TrimSpace(account.BaseURL)
if baseURL == "" {
if defaultURL, ok := providercatalog.DefaultBaseURL(account.Provider); ok {
baseURL = defaultURL
}
}
return baseURL
}
func buildInitialAgentAccountModels(account *model.AgentAccount, requested []dto.AgentAccountModel) ([]dto.AgentAccountModel, error) {
if account == nil {
return nil, fmt.Errorf("account is required")
@@ -1169,12 +1280,6 @@ func replacePersistedAgentAccountModelsWithTx(tx *gorm.DB, accountID uint, model
return nil
}
func replacePersistedAgentAccountModels(accountID uint, models []dto.AgentAccountModel) error {
return global.DB.Transaction(func(tx *gorm.DB) error {
return replacePersistedAgentAccountModelsWithTx(tx, accountID, models)
})
}
func normalizeAgentAccountModels(account *model.AgentAccount, models []dto.AgentAccountModel) ([]dto.AgentAccountModel, error) {
requested := append([]dto.AgentAccountModel(nil), models...)
if len(requested) == 0 {
@@ -1207,76 +1312,37 @@ func normalizeAgentAccountModel(account *model.AgentAccount, model dto.AgentAcco
if modelID == "" {
return dto.AgentAccountModel{}, fmt.Errorf("model is required")
}
inferredReasoning := model.Reasoning
if !model.Reasoning && model.Name == "" && model.MaxTokens == 0 && model.ContextWindow == 0 && len(model.Input) == 0 {
if catalogModel, ok := providercatalog.FindModel(account.Provider, modelID); ok {
inferredReasoning = catalogModel.Reasoning
}
}
primaryModel, inferredEntry, _, _, err := inferOpenclawCatalogModel(account, modelID, inferredReasoning, model.MaxTokens, model.ContextWindow)
if err != nil {
return dto.AgentAccountModel{}, err
}
name := strings.TrimSpace(model.Name)
if name == "" {
name = strings.TrimSpace(inferredEntry.Name)
}
reasoning := model.Reasoning
if !model.Reasoning && model.Name == "" && model.MaxTokens == 0 && model.ContextWindow == 0 && len(model.Input) == 0 {
reasoning = inferredEntry.Reasoning
name = modelID
}
inputs := sanitizeAgentAccountModelInputs(model.Input)
if len(inputs) == 0 {
inputs = sanitizeAgentAccountModelInputs(inferredEntry.Input)
}
contextWindow := model.ContextWindow
if contextWindow <= 0 {
contextWindow = inferredEntry.ContextWindow
}
maxTokens := model.MaxTokens
if maxTokens <= 0 {
maxTokens = inferredEntry.MaxTokens
}
return dto.AgentAccountModel{
ID: normalizeAgentAccountModelID(account.Provider, primaryModel, modelID),
ID: normalizeAgentAccountModelID(account.Provider, modelID),
Name: name,
ContextWindow: contextWindow,
MaxTokens: maxTokens,
Reasoning: reasoning,
ContextWindow: model.ContextWindow,
MaxTokens: model.MaxTokens,
Reasoning: model.Reasoning,
Input: inputs,
}, nil
}
func normalizeAgentAccountModelID(provider, primaryModel, requestedID string) string {
func normalizeAgentAccountModelID(provider, requestedID string) string {
switch provider {
case "custom", "vllm":
target := requestedID
if strings.TrimSpace(target) == "" {
target = primaryModel
}
return normalizeCustomModel(target)
return normalizeCustomModel(requestedID)
case "ollama":
target := strings.TrimSpace(primaryModel)
if strings.HasPrefix(target, "ollama/") {
return target
}
target = strings.TrimSpace(requestedID)
target := strings.TrimSpace(requestedID)
if strings.HasPrefix(target, "ollama/") {
return target
}
target = strings.TrimLeft(strings.TrimSpace(target), "/")
if target == "" {
target = strings.TrimLeft(strings.TrimSpace(primaryModel), "/")
}
if target == "" {
return ""
}
return "ollama/" + target
default:
target := strings.TrimSpace(requestedID)
if target == "" {
target = strings.TrimSpace(primaryModel)
}
if target == "" {
return ""
}
@@ -1432,19 +1498,6 @@ func extractStringList(value interface{}) []string {
}
}
func extractGatewayToken(conf map[string]interface{}) string {
gateway, ok := conf["gateway"].(map[string]interface{})
if !ok {
return ""
}
auth, ok := gateway["auth"].(map[string]interface{})
if !ok {
return ""
}
token, _ := auth["token"].(string)
return token
}
func ensureChildMap(parent map[string]interface{}, key string) map[string]interface{} {
if child, ok := parent[key].(map[string]interface{}); ok {
return child
@@ -1466,18 +1519,6 @@ func structToMap(value interface{}) (map[string]interface{}, error) {
return result, nil
}
func mapToModelsConfig(value map[string]interface{}) (*modelsConfig, error) {
payload, err := json.Marshal(value)
if err != nil {
return nil, err
}
result := &modelsConfig{}
if err := json.Unmarshal(payload, result); err != nil {
return nil, err
}
return result, nil
}
func readInstallEnv(envStr string) map[string]interface{} {
if strings.TrimSpace(envStr) == "" {
return nil
@@ -1527,24 +1568,6 @@ func normalizeCustomModel(modelName string) string {
return trim
}
func normalizeAgentType(agentType string) string {
trim := agentType
if trim == "" {
return constant.AppOpenclaw
}
return trim
}
func modelMatchesProvider(provider, modelName string) bool {
target := strings.TrimSpace(modelName)
for _, prefix := range supportedProviderModelPrefixes(provider) {
if prefix != "" && strings.HasPrefix(target, prefix+"/") {
return true
}
}
return false
}
func runtimeProviderModelPrefix(provider string) string {
switch provider {
case "gemini":
@@ -1594,11 +1617,7 @@ func generateToken() string {
}
func asyncReportAIProviderInstall(provider string) {
if global.CONF.Base.Mode != "stable" {
return
}
provider = provider
if provider == "" {
if global.CONF.Base.Mode != "stable" || provider == "" {
return
}
go func(provider string) {
+315
View File
@@ -0,0 +1,315 @@
package service
import (
"context"
"errors"
"fmt"
"net"
"sort"
"strings"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"gorm.io/gorm"
)
func (a AgentService) BindWebsite(req dto.AgentWebsiteBindReq) error {
agent, err := agentRepo.GetFirst(repo.WithByID(req.AgentID))
if err != nil {
return err
}
if agent.WebsiteID != 0 {
return buserr.New("ErrAgentWebsiteBound")
}
website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))
if err != nil {
return err
}
if !isBindableAgentWebsiteType(website.Type) {
return buserr.New("ErrAgentWebsiteTypeUnsupported")
}
boundAgent, err := agentRepo.GetFirst(repo.WithByWebsiteID(req.WebsiteID))
if err == nil && boundAgent.ID > 0 {
return buserr.New("ErrAgentWebsiteInUse")
}
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
agent.WebsiteID = req.WebsiteID
if err := agentRepo.Save(agent); err != nil {
return err
}
return ensureOpenclawWebsiteAllowedOrigin(agent, &website)
}
func hydrateAgentWebsiteItems(items []dto.AgentItem) error {
explicitWebsiteMap, err := loadAgentWebsiteMapByID(items)
if err != nil {
return err
}
websiteDomainMap, err := loadAgentWebsiteDomainMapByWebsiteID(items)
if err != nil {
return err
}
fillAgentWebsiteItems(items, explicitWebsiteMap, websiteDomainMap)
return nil
}
func loadAgentWebsiteMapByID(items []dto.AgentItem) (map[uint]model.Website, error) {
websiteIDs := make([]uint, 0, len(items))
for _, item := range items {
if item.WebsiteID > 0 {
websiteIDs = append(websiteIDs, item.WebsiteID)
}
}
if len(websiteIDs) == 0 {
return map[uint]model.Website{}, nil
}
websites, err := websiteRepo.GetBy(repo.WithByIDs(uniqueUintList(websiteIDs)))
if err != nil {
return nil, err
}
websiteMap := make(map[uint]model.Website, len(websites))
for _, website := range websites {
websiteMap[website.ID] = website
}
return websiteMap, nil
}
func loadAgentWebsiteDomainMapByWebsiteID(items []dto.AgentItem) (map[uint][]model.WebsiteDomain, error) {
websiteIDs := make([]uint, 0, len(items))
for _, item := range items {
if item.WebsiteID > 0 {
websiteIDs = append(websiteIDs, item.WebsiteID)
}
}
if len(websiteIDs) == 0 {
return map[uint][]model.WebsiteDomain{}, nil
}
websiteDomains, err := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteIds(uniqueUintList(websiteIDs)), repo.WithOrderAsc("id"))
if err != nil {
return nil, err
}
websiteDomainMap := make(map[uint][]model.WebsiteDomain, len(websiteDomains))
for _, websiteDomain := range websiteDomains {
websiteDomainMap[websiteDomain.WebsiteID] = append(websiteDomainMap[websiteDomain.WebsiteID], websiteDomain)
}
return websiteDomainMap, nil
}
func loadAgentWebsiteResourceName(website model.Website) (string, error) {
websiteDomains, err := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(website.ID), repo.WithOrderAsc("id"))
if err != nil {
return "", err
}
if len(websiteDomains) > 0 {
return websiteDomains[0].Domain, nil
}
if website.PrimaryDomain != "" {
return website.PrimaryDomain, nil
}
return fmt.Sprintf("%d", website.ID), nil
}
func fillAgentWebsiteItems(items []dto.AgentItem, explicitWebsiteMap map[uint]model.Website, websiteDomainMap map[uint][]model.WebsiteDomain) {
for index := range items {
if items[index].WebsiteID == 0 {
continue
}
website, ok := explicitWebsiteMap[items[index].WebsiteID]
if !ok {
items[index].WebsiteID = 0
items[index].WebsitePrimaryDomain = ""
items[index].WebsiteProtocol = ""
continue
}
items[index].WebsiteProtocol = website.Protocol
websiteDomains := websiteDomainMap[items[index].WebsiteID]
if len(websiteDomains) == 0 {
items[index].WebsitePrimaryDomain = ""
continue
}
items[index].WebsitePrimaryDomain = websiteDomains[0].Domain
}
}
func uniqueDeploymentWebsiteMapByAppInstall(websites []model.Website) map[uint]model.Website {
websiteMap := make(map[uint]model.Website)
duplicateAppInstallIDs := make(map[uint]struct{})
for _, website := range websites {
if website.AppInstallID == 0 {
continue
}
if _, duplicated := duplicateAppInstallIDs[website.AppInstallID]; duplicated {
continue
}
if _, exists := websiteMap[website.AppInstallID]; exists {
delete(websiteMap, website.AppInstallID)
duplicateAppInstallIDs[website.AppInstallID] = struct{}{}
continue
}
websiteMap[website.AppInstallID] = website
}
return websiteMap
}
func UniqueDeploymentWebsiteMapForMigration(websites []model.Website) map[uint]model.Website {
return uniqueDeploymentWebsiteMapByAppInstall(websites)
}
func uniqueUintList(items []uint) []uint {
itemMap := make(map[uint]struct{}, len(items))
uniq := make([]uint, 0, len(items))
for _, item := range items {
if item == 0 {
continue
}
if _, exists := itemMap[item]; exists {
continue
}
itemMap[item] = struct{}{}
uniq = append(uniq, item)
}
return uniq
}
func isBindableAgentWebsiteType(websiteType string) bool {
return websiteType == constant.Proxy || websiteType == constant.Static
}
func bindDeploymentWebsiteToAgentByAppInstall(website *model.Website) error {
if website.ID == 0 || website.Type != constant.Deployment || website.AppInstallID == 0 {
return nil
}
appInstall, err := appInstallRepo.GetFirst(repo.WithByID(website.AppInstallID))
if err != nil {
return err
}
if appInstall.App.Key != constant.AppOpenclaw && appInstall.App.Key != constant.AppCopaw {
return nil
}
agent, err := agentRepo.GetFirst(repo.WithByAppInstallID(website.AppInstallID))
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil
}
if err != nil {
return err
}
if agent.WebsiteID != 0 {
return nil
}
agent.WebsiteID = website.ID
if err := agentRepo.Save(agent); err != nil {
return err
}
return ensureOpenclawWebsiteAllowedOrigin(agent, website)
}
func ensureOpenclawWebsiteAllowedOrigin(agent *model.Agent, website *model.Website) error {
if agent == nil || website == nil || agent.AgentType == constant.AppCopaw {
return nil
}
origins, err := buildWebsiteAllowedOrigins(website)
if err != nil {
return err
}
if len(origins) == 0 {
return nil
}
install, err := appInstallRepo.GetFirst(repo.WithByID(agent.AppInstallID))
if err != nil {
return err
}
conf, err := readOpenclawConfig(agent.ConfigPath)
if err != nil {
return err
}
allowedOrigins := extractSecurityConfig(conf).AllowedOrigins
allowedOrigins, err = normalizeAllowedOrigins(append(allowedOrigins, origins...))
if err != nil {
return err
}
setSecurityConfig(conf, dto.AgentSecurityConfig{AllowedOrigins: allowedOrigins})
if err := writeOpenclawConfigRaw(agent.ConfigPath, conf); err != nil {
return err
}
if err := syncOpenclawAllowedOriginEnv(&install, allowedOrigins); err != nil {
return err
}
return appInstallRepo.Save(context.Background(), &install)
}
func buildWebsiteAllowedOrigins(website *model.Website) ([]string, error) {
websiteDomains, err := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(website.ID), repo.WithOrderAsc("id"))
if err != nil {
return nil, err
}
if len(websiteDomains) == 0 {
return nil, nil
}
defaultHTTPSPort := 443
if strings.EqualFold(website.Protocol, "https") {
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err == nil && nginxInstall.HttpsPort > 0 {
defaultHTTPSPort = nginxInstall.HttpsPort
}
}
return buildWebsiteAllowedOriginsFromDomains(website, websiteDomains, defaultHTTPSPort)
}
func buildWebsiteAllowedOriginsFromDomains(website *model.Website, websiteDomains []model.WebsiteDomain, defaultHTTPSPort int) ([]string, error) {
if len(websiteDomains) == 0 {
return nil, nil
}
sort.Slice(websiteDomains, func(i, j int) bool {
return websiteDomains[i].ID < websiteDomains[j].ID
})
origins := make([]string, 0, len(websiteDomains))
for _, websiteDomain := range websiteDomains {
origin, err := buildWebsiteDomainOrigin(website.Protocol, websiteDomain, defaultHTTPSPort)
if err != nil {
return nil, err
}
origins = append(origins, origin)
}
return origins, nil
}
func buildWebsiteDomainOrigin(protocol string, websiteDomain model.WebsiteDomain, defaultHTTPSPort int) (string, error) {
origin := strings.ToLower(strings.TrimSpace(protocol)) + "://" + formatWebsiteDomainHost(websiteDomain.Domain)
switch strings.ToLower(strings.TrimSpace(protocol)) {
case "http":
if websiteDomain.Port > 0 && websiteDomain.Port != 80 {
origin = fmt.Sprintf("%s:%d", origin, websiteDomain.Port)
}
case "https":
port := websiteDomain.Port
if !websiteDomain.SSL {
port = defaultHTTPSPort
}
if port > 0 && port != 443 {
origin = fmt.Sprintf("%s:%d", origin, port)
}
}
return normalizeAllowedOrigin(origin)
}
func formatWebsiteDomainHost(domain string) string {
host := strings.Trim(strings.TrimSpace(domain), "[]")
if ip := net.ParseIP(host); ip != nil && strings.Contains(host, ":") {
return "[" + host + "]"
}
return host
}
+130 -4
View File
@@ -1,11 +1,14 @@
package service
import (
"bytes"
"context"
"fmt"
"os"
"os/exec"
"path"
"strings"
"syscall"
"time"
"github.com/1Panel-dev/1Panel/agent/app/dto"
@@ -19,6 +22,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/re"
"github.com/jinzhu/copier"
)
@@ -107,8 +111,7 @@ func (u *AIToolService) Create(req dto.OllamaModelName) error {
}
go func() {
taskItem.AddSubTask(i18n.GetWithName("OllamaModelPull", req.Name), func(t *task.Task) error {
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem), cmd.WithTimeout(time.Hour))
return cmdMgr.Run("docker", "exec", containerName, "ollama", "pull", info.Name)
return runOllamaPullWithProcess(t, containerName, info.Name)
}, nil)
taskItem.AddSubTask(i18n.GetWithName("OllamaModelSize", req.Name), func(t *task.Task) error {
itemSize, err := loadModelSize(info.Name, containerName)
@@ -162,8 +165,7 @@ func (u *AIToolService) Recreate(req dto.OllamaModelName) error {
}
go func() {
taskItem.AddSubTask(i18n.GetWithName("OllamaModelPull", req.Name), func(t *task.Task) error {
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem), cmd.WithTimeout(time.Hour))
return cmdMgr.Run("docker", "exec", containerName, "ollama", "pull", req.Name)
return runOllamaPullWithProcess(t, containerName, req.Name)
}, nil)
taskItem.AddSubTask(i18n.GetWithName("OllamaModelSize", req.Name), func(t *task.Task) error {
itemSize, err := loadModelSize(modelInfo.Name, containerName)
@@ -394,3 +396,127 @@ func loadModelSize(name string, containerName string) (string, error) {
}
return "", fmt.Errorf("no such model %s in ollama list, std: %s", name, stdout)
}
func runOllamaPullWithProcess(taskItem *task.Task, containerName, modelName string) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Hour)
defer cancel()
cmdItem := exec.CommandContext(ctx, "docker", "exec", containerName, "ollama", "pull", modelName)
cmdItem.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
writer := &ollamaPullLogWriter{taskItem: taskItem}
cmdItem.Stdout = writer
cmdItem.Stderr = writer
if err := cmdItem.Start(); err != nil {
return fmt.Errorf("failed to start ollama pull %s: %w", modelName, err)
}
waitErr := cmdItem.Wait()
writer.Flush()
if ctx.Err() == context.DeadlineExceeded {
if cmdItem.Process != nil && cmdItem.Process.Pid > 0 {
_ = syscall.Kill(-cmdItem.Process.Pid, syscall.SIGKILL)
}
return buserr.New("ErrCmdTimeout")
}
if waitErr == nil {
return nil
}
if len(strings.TrimSpace(writer.errBuf.String())) > 0 {
return fmt.Errorf("%s", strings.TrimSpace(writer.errBuf.String()))
}
return waitErr
}
type ollamaPullLogWriter struct {
taskItem *task.Task
errBuf bytes.Buffer
}
func (w *ollamaPullLogWriter) Write(p []byte) (n int, err error) {
w.errBuf.Write(p)
for _, segment := range splitOllamaPullSegments(string(p)) {
w.logLine(segment)
}
return len(p), nil
}
func (w *ollamaPullLogWriter) Flush() {}
func (w *ollamaPullLogWriter) logLine(line string) {
if w == nil || w.taskItem == nil {
return
}
line = sanitizeOllamaPullLine(line)
if line == "" {
return
}
if prefix := resolveOllamaPullLogPrefix(line); prefix != "" {
_ = replaceOllamaPullLogLine(prefix, line, w.taskItem)
return
}
w.taskItem.Log(line)
}
func sanitizeOllamaPullLine(line string) string {
line = re.StripAnsiControlSeq(line)
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "pulling manifes") {
return ""
}
return line
}
func splitOllamaPullSegments(chunk string) []string {
if chunk == "" {
return nil
}
chunk = strings.ReplaceAll(chunk, "\r", "\n")
parts := strings.Split(chunk, "\x1b[1G")
segments := make([]string, 0, len(parts))
for _, part := range parts {
for _, line := range strings.Split(part, "\n") {
line = strings.TrimSpace(line)
if line == "" {
continue
}
segments = append(segments, line)
}
}
return segments
}
func resolveOllamaPullLogPrefix(line string) string {
if strings.HasPrefix(line, "pulling ") {
if idx := strings.Index(line, ":"); idx > 0 {
return strings.TrimSpace(line[:idx])
}
fields := strings.Fields(line)
if len(fields) >= 2 {
return strings.TrimSpace(fields[0] + " " + fields[1])
}
}
return ""
}
func replaceOllamaPullLogLine(prefix, newLine string, taskItem *task.Task) error {
if taskItem == nil || taskItem.Task == nil {
return nil
}
data, err := os.ReadFile(taskItem.Task.LogFile)
if err != nil {
return err
}
lines := strings.Split(string(data), "\n")
for idx, line := range lines {
trimmed := strings.TrimSpace(line)
if trimmed == "" {
continue
}
if strings.Contains(trimmed, prefix) {
lines[idx] = time.Now().Format("2006/01/02 15:04:05") + " " + newLine
return os.WriteFile(taskItem.Task.LogFile, []byte(strings.Join(lines, "\n")), os.ModePerm)
}
}
taskItem.Log(newLine)
return nil
}
+22
View File
@@ -650,6 +650,28 @@ func sendAlerts(alert dto.AlertDTO, alertType, quota, quotaType string, params [
continue
}
alertUtil.CreateNewAlertTask(quota, alertType, quotaType, m)
case constant.Bark:
todayCount, isValid := canSendAlertToday(alertType, quotaType, alert.SendCount, m)
if !isValid {
continue
}
var create = dto.AlertLogCreate{
Type: alertUtil.GetCronJobType(alert.Type),
AlertId: alert.ID,
Count: todayCount + 1,
}
alertInfo := alert
alertInfo.Type = alertType
create.AlertRule = alertUtil.ProcessAlertRule(alert)
create.AlertDetail = alertUtil.ProcessAlertDetail(alertInfo, quotaType, params, m)
transport := xpack.LoadRequestTransport()
agentInfo, _ := xpack.GetAgentInfo()
alertErr := alertUtil.CreateBarkAlertLog(create, alertInfo, params, transport, agentInfo)
if alertErr != nil {
global.LOG.Infof("%s alert %s push failed, err: %v", alertType, m, alertErr.Error())
continue
}
alertUtil.CreateNewAlertTask(quota, alertType, quotaType, m)
default:
}
}
+53
View File
@@ -30,6 +30,8 @@ func (s *AlertSender) Send(quota string, params []dto.Param) {
s.sendSMS(quota, params)
case constant.Email:
s.sendEmail(quota, params)
case constant.Bark:
s.sendBark(quota, params)
case constant.WeCom, constant.DingTalk, constant.FeiShu:
s.sendWebhook(quota, params, method)
}
@@ -45,6 +47,8 @@ func (s *AlertSender) ResourceSend(quota string, params []dto.Param) {
s.sendResourceSMS(quota, params)
case constant.Email:
s.sendResourceEmail(quota, params)
case constant.Bark:
s.sendResourceBark(quota, params)
case constant.WeCom, constant.DingTalk, constant.FeiShu:
s.sendResourceWebhook(quota, params, method)
}
@@ -101,6 +105,31 @@ func (s *AlertSender) sendEmail(quota string, params []dto.Param) {
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.Email)
}
func (s *AlertSender) sendBark(quota string, params []dto.Param) {
totalCount, isValid := s.canSendAlert(constant.Bark)
if !isValid {
return
}
create := dto.AlertLogCreate{
Status: constant.AlertSuccess,
Count: totalCount + 1,
AlertId: s.alert.ID,
Type: s.alert.Type,
AlertRule: alertUtil.ProcessAlertRule(s.alert),
AlertDetail: alertUtil.ProcessAlertDetail(s.alert, quota, params, constant.Bark),
}
transport := xpack.LoadRequestTransport()
agentInfo, _ := xpack.GetAgentInfo()
err := alertUtil.CreateBarkAlertLog(create, s.alert, params, transport, agentInfo)
if err != nil {
global.LOG.Errorf("%s alert bark push failed: %v", s.alert.Type, err)
return
}
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.Bark)
}
func (s *AlertSender) sendWebhook(quota string, params []dto.Param, method string) {
totalCount, isValid := s.canSendAlert(method)
if !isValid {
@@ -171,6 +200,30 @@ func (s *AlertSender) sendResourceEmail(quota string, params []dto.Param) {
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.Email)
}
func (s *AlertSender) sendResourceBark(quota string, params []dto.Param) {
todayCount, isValid := s.canResourceSendAlert(constant.Bark)
if !isValid {
return
}
create := dto.AlertLogCreate{
Status: constant.AlertSuccess,
Count: todayCount + 1,
AlertId: s.alert.ID,
Type: s.alert.Type,
AlertRule: alertUtil.ProcessAlertRule(s.alert),
AlertDetail: alertUtil.ProcessAlertDetail(s.alert, quota, params, constant.Bark),
}
transport := xpack.LoadRequestTransport()
agentInfo, _ := xpack.GetAgentInfo()
if err := alertUtil.CreateBarkAlertLog(create, s.alert, params, transport, agentInfo); err != nil {
global.LOG.Errorf("failed to send Bark alert: %v", err)
return
}
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.Bark)
}
func (s *AlertSender) sendResourceWebhook(quota string, params []dto.Param, method string) {
todayCount, isValid := s.canResourceSendAlert(method)
if !isValid {
+4
View File
@@ -853,6 +853,10 @@ func syncAppInstallStatus(appInstall *model.AppInstall, force bool) error {
return nil
}
func SyncAppInstallStatus(appInstall *model.AppInstall, force bool) error {
return syncAppInstallStatus(appInstall, force)
}
func updateInstallInfoInDB(appKey, appName, param string, value interface{}) error {
if param != "password" && param != "port" && param != "user-password" {
return nil
+39 -21
View File
@@ -49,6 +49,7 @@ func (u *DeviceService) Scan() dto.CleanData {
Size: uint64(originalSize),
IsCheck: originalSize > 0,
IsRecommend: true,
CanDelete: false,
Type: "1panel_original",
Children: loadTreeWithDir(true, "1panel_original", originalPath, fileOp),
})
@@ -68,6 +69,7 @@ func (u *DeviceService) Scan() dto.CleanData {
Size: rollbackSize,
IsCheck: rollbackSize > 0,
IsRecommend: true,
CanDelete: false,
Type: "rollback",
Children: rollBackTree,
})
@@ -105,11 +107,7 @@ func (u *DeviceService) Clean(req []dto.Clean) {
case "tmp_backup":
dropFileOrDir(path.Join(global.Dir.LocalBackupDir, "tmp"))
case "unknown_backup":
if strings.HasPrefix(item.Name, path.Join(global.Dir.LocalBackupDir, "log/website")) {
dropFileOrDir(item.Name)
} else {
dropFile(item.Name)
}
dropFileOrDir(item.Name)
case "rollback":
dropFileOrDir(path.Join(global.Dir.BaseDir, rollbackPath, "app"))
@@ -269,6 +267,7 @@ func loadUpgradeTree(fileOp fileUtils.FileOp) dto.CleanTree {
Size: uint64(upgradeSize),
IsCheck: false,
IsRecommend: true,
CanDelete: false,
Type: "upgrade",
Children: loadTreeWithDir(true, "upgrade", upgradePath, fileOp),
}
@@ -302,6 +301,7 @@ func loadAgentPackage(fileOp fileUtils.FileOp) dto.CleanTree {
Label: "agent_packages",
IsCheck: false,
IsRecommend: true,
CanDelete: false,
Type: "agent",
}
files, _ := os.ReadDir(pathItem)
@@ -316,6 +316,7 @@ func loadAgentPackage(fileOp fileUtils.FileOp) dto.CleanTree {
Size: uint64(itemSize),
IsCheck: true,
IsRecommend: true,
CanDelete: true,
Type: "agent",
})
} else {
@@ -333,6 +334,7 @@ func loadAgentPackage(fileOp fileUtils.FileOp) dto.CleanTree {
Size: uint64(itemSize.Size()),
IsCheck: !isCurrentVersion,
IsRecommend: true,
CanDelete: true,
Type: "agent",
})
}
@@ -353,6 +355,7 @@ func loadBackupTree(fileOp fileUtils.FileOp) []dto.CleanTree {
Size: uint64(tmpSize),
IsCheck: tmpSize != 0,
IsRecommend: true,
CanDelete: true,
Type: "tmp_backup",
})
backupRecords, _ := backupRepo.ListRecord()
@@ -391,6 +394,7 @@ func loadUnknownApps(fileOp fileUtils.FileOp, recordMap map[string][]string) dto
Label: "unknown_app",
IsCheck: false,
IsRecommend: false,
CanDelete: false,
Name: backupPath,
Type: "unknown_backup",
}
@@ -419,6 +423,7 @@ func loadUnknownDbs(fileOp fileUtils.FileOp, recordMap map[string][]string) dto.
Name: backupPath,
IsCheck: false,
IsRecommend: false,
CanDelete: false,
Type: "unknown_backup",
}
_ = loadFileOrDirWithExclude(fileOp, 0, backupPath, &treeData, excludePaths)
@@ -442,6 +447,7 @@ func loadUnknownWebsites(fileOp fileUtils.FileOp, recordMap map[string][]string)
Name: backupPath,
IsCheck: false,
IsRecommend: false,
CanDelete: false,
Type: "unknown_backup",
}
_ = loadFileOrDirWithExclude(fileOp, 0, backupPath, &treeData, excludePaths)
@@ -460,6 +466,7 @@ func loadUnknownSnapshot(fileOp fileUtils.FileOp) dto.CleanTree {
Name: backupPath,
IsCheck: false,
IsRecommend: false,
CanDelete: false,
Type: "unknown_backup",
}
entries, _ := os.ReadDir(backupPath)
@@ -473,6 +480,7 @@ func loadUnknownSnapshot(fileOp fileUtils.FileOp) dto.CleanTree {
Label: entry.Name(),
IsCheck: false,
IsRecommend: false,
CanDelete: true,
Name: childPath,
Type: "unknown_backup",
}
@@ -499,6 +507,7 @@ func loadUnknownWebsiteLog(fileOp fileUtils.FileOp) dto.CleanTree {
Label: "unknown_website_log",
IsCheck: false,
IsRecommend: true,
CanDelete: false,
Type: "unknown_backup",
}
dir := path.Join(global.Dir.LocalBackupDir, "log/website")
@@ -522,6 +531,7 @@ func loadUnknownWebsiteLog(fileOp fileUtils.FileOp) dto.CleanTree {
Label: dirName,
IsCheck: true,
IsRecommend: true,
CanDelete: true,
Name: dirPath,
Type: "unknown_backup",
Size: uint64(itemSize),
@@ -552,6 +562,7 @@ func loadFileOrDirWithExclude(fileOp fileUtils.FileOp, index uint, dir string, r
Label: entry.Name(),
IsCheck: false,
IsRecommend: false,
CanDelete: true,
Name: childPath,
Type: "unknown_backup",
}
@@ -564,18 +575,19 @@ func loadFileOrDirWithExclude(fileOp fileUtils.FileOp, index uint, dir string, r
for _, child := range childNode.Children {
childNode.Size += child.Size
}
rootTree.Size += childNode.Size
} else {
itemSize, _ := fileOp.GetDirSize(childPath)
childNode.Size = uint64(itemSize)
rootTree.Size += childNode.Size
}
if childNode.Size == 0 {
continue
}
} else {
info, _ := entry.Info()
childNode.Size = uint64(info.Size())
rootTree.Size += childNode.Size
}
rootTree.Size += childNode.Size
rootTree.Children = append(rootTree.Children, childNode)
}
return nil
@@ -633,6 +645,7 @@ func loadDownloadTree(fileOp fileUtils.FileOp) []dto.CleanTree {
Label: "app_tmp_download",
IsCheck: true,
IsRecommend: true,
CanDelete: false,
Type: "app_tmp_download",
Name: "apps",
}
@@ -653,19 +666,19 @@ func loadLogTree(fileOp fileUtils.FileOp) []dto.CleanTree {
for _, file := range list1 {
size += file.Size
}
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "system_log", Size: size, Children: list1, Type: "system_log", IsRecommend: true})
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "system_log", Size: size, Children: list1, Type: "system_log", IsRecommend: true, CanDelete: false})
path2 := path.Join(global.Dir.TaskDir)
list2 := loadTreeWithDir(false, "task_log", path2, fileOp)
size2, _ := fileOp.GetDirSize(path2)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "task_log", Size: uint64(size2), Children: list2, Type: "task_log"})
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "task_log", Size: uint64(size2), Children: list2, Type: "task_log", CanDelete: false})
websiteLogList := loadWebsiteLogTree(fileOp)
logTotalSize := uint64(0)
for _, websiteLog := range websiteLogList {
logTotalSize += websiteLog.Size
}
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "website_log", Size: logTotalSize, Children: websiteLogList, Type: "website_log", IsRecommend: false})
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "website_log", Size: logTotalSize, Children: websiteLogList, Type: "website_log", IsRecommend: false, CanDelete: false})
return treeData
}
@@ -679,11 +692,12 @@ func loadWebsiteLogTree(fileOp fileUtils.FileOp) []dto.CleanTree {
for _, website := range websites {
size3, _ := fileOp.GetDirSize(path.Join(GetSiteDir(website.Alias), "log"))
res = append(res, dto.CleanTree{
ID: uuid.NewString(),
Label: website.PrimaryDomain,
Size: uint64(size3),
Type: "website_log",
Name: website.Alias,
ID: uuid.NewString(),
Label: website.PrimaryDomain,
Size: uint64(size3),
Type: "website_log",
Name: website.Alias,
CanDelete: true,
})
}
return res
@@ -733,6 +747,7 @@ func loadAppTmpDownloadTree(fileOp fileUtils.FileOp) []dto.CleanTree {
appTree.Name = appKey
appTree.IsRecommend = true
appTree.IsCheck = true
appTree.CanDelete = false
for _, version := range missingVersions {
versionPath := filepath.Join(appPath, version)
size, _ := fileOp.GetDirSize(versionPath)
@@ -743,6 +758,7 @@ func loadAppTmpDownloadTree(fileOp fileUtils.FileOp) []dto.CleanTree {
Size: uint64(size),
IsCheck: true,
IsRecommend: true,
CanDelete: true,
Type: "app_tmp_download_version",
Name: path.Join(appKey, version),
})
@@ -770,7 +786,7 @@ func loadContainerTree() []dto.CleanTree {
imageSize += uint64(file.Size)
}
}
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "container_images", Size: imageSize, Children: nil, Type: "images", IsRecommend: true})
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "container_images", Size: imageSize, Children: nil, Type: "images", IsRecommend: true, CanDelete: true})
containerSize := uint64(0)
for _, file := range diskUsage.Containers {
@@ -778,7 +794,7 @@ func loadContainerTree() []dto.CleanTree {
containerSize += uint64(file.SizeRw)
}
}
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "container_containers", Size: containerSize, Children: nil, Type: "containers", IsRecommend: true})
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "container_containers", Size: containerSize, Children: nil, Type: "containers", IsRecommend: true, CanDelete: true})
volumeSize := uint64(0)
for _, file := range diskUsage.Volumes {
@@ -786,7 +802,7 @@ func loadContainerTree() []dto.CleanTree {
volumeSize += uint64(file.UsageData.Size)
}
}
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "container_volumes", Size: volumeSize, IsCheck: volumeSize > 0, Children: nil, Type: "volumes", IsRecommend: true})
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "container_volumes", Size: volumeSize, IsCheck: volumeSize > 0, Children: nil, Type: "volumes", IsRecommend: true, CanDelete: true})
var buildCacheTotalSize int64
for _, cache := range diskUsage.BuildCache {
@@ -794,7 +810,7 @@ func loadContainerTree() []dto.CleanTree {
buildCacheTotalSize += cache.Size
}
}
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "build_cache", Size: uint64(buildCacheTotalSize), IsCheck: buildCacheTotalSize > 0, Type: "build_cache", IsRecommend: true})
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "build_cache", Size: uint64(buildCacheTotalSize), IsCheck: buildCacheTotalSize > 0, Type: "build_cache", IsRecommend: true, CanDelete: true})
return treeData
}
@@ -804,7 +820,7 @@ func loadTreeWithCheck(treeData []dto.CleanTree, pathItem, treeType string, file
return treeData
}
list := loadTreeWithAllFile(true, pathItem, treeType, pathItem, fileOp)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: treeType, Size: uint64(size), IsCheck: size > 0, Children: list, Type: treeType, IsRecommend: true})
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: treeType, Size: uint64(size), IsCheck: size > 0, Children: list, Type: treeType, IsRecommend: true, CanDelete: false})
return treeData
}
@@ -831,6 +847,7 @@ func loadTreeWithDir(isCheck bool, treeType, pathItem string, fileOp fileUtils.F
Name: strings.TrimPrefix(file.Name(), "/"),
IsCheck: isCheck,
IsRecommend: isCheck,
CanDelete: true,
}
lists = append(lists, item)
}
@@ -878,6 +895,7 @@ func loadTreeWithAllFile(isCheck bool, originalPath, treeType, pathItem string,
Name: name,
IsCheck: isCheck,
IsRecommend: isCheck,
CanDelete: true,
}
if file.IsDir() {
item.Children = loadTreeWithAllFile(isCheck, originalPath, treeType, path.Join(pathItem, file.Name()), fileOp)
+2 -1
View File
@@ -49,7 +49,8 @@ var (
runtimeRepo = repo.NewIRunTimeRepo()
phpExtensionsRepo = repo.NewIPHPExtensionsRepo()
favoriteRepo = repo.NewIFavoriteRepo()
favoriteRepo = repo.NewIFavoriteRepo()
fileShareRepo = repo.NewIFileShareRepo()
taskRepo = repo.NewITaskRepo()
+180 -12
View File
@@ -41,6 +41,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/files"
terminalai "github.com/1Panel-dev/1Panel/agent/utils/terminal/ai"
"github.com/pkg/errors"
)
@@ -78,10 +79,7 @@ type IFileService interface {
ConvertLog(req dto.PageInfo) (int64, []response.FileConvertLog, error)
BatchGetRemarks(req request.FileRemarkBatch) map[string]string
SetRemark(req request.FileRemarkUpdate) error
}
var filteredPaths = []string{
"/.1panel_clash",
AISearch(req request.FileAISearch) (*response.FileAISearchResult, error)
}
const (
@@ -106,10 +104,29 @@ func (f *FileService) GetFileList(op request.FileOption) (response.FileInfo, err
if err != nil {
return fileInfo, err
}
shareMap, err := NewIFileShareService().SharePathCodeMap()
if err != nil {
return fileInfo, err
}
applyFileShares(info, shareMap)
fileInfo.FileInfo = *info
return fileInfo, nil
}
func applyFileShares(info *files.FileInfo, shareMap map[string]string) {
if info == nil {
return
}
if code, ok := shareMap[info.Path]; ok {
info.ShareCode = code
} else {
info.ShareCode = ""
}
for _, item := range info.Items {
applyFileShares(item, shareMap)
}
}
func (f *FileService) SearchUploadWithPage(req request.SearchUploadWithPage) (int64, interface{}, error) {
var (
files []response.UploadInfo
@@ -170,14 +187,7 @@ func (f *FileService) GetFileTree(op request.FileOption) ([]response.FileTree, e
}
func shouldFilterPath(path string) bool {
cleanedPath := filepath.Clean(path)
for _, filteredPath := range filteredPaths {
cleanedFilteredPath := filepath.Clean(filteredPath)
if cleanedFilteredPath == cleanedPath || strings.HasPrefix(cleanedPath, cleanedFilteredPath+"/") {
return true
}
}
return false
return files.ShouldFilterSensitivePath(path)
}
func (f *FileService) buildFileTree(node *response.FileTree, items []*files.FileInfo, op request.FileOption, level int) error {
@@ -1019,3 +1029,161 @@ func (f *FileService) ConvertLog(req dto.PageInfo) (total int64, data []response
return total, data, nil
}
func (f *FileService) AISearch(req request.FileAISearch) (*response.FileAISearchResult, error) {
root := filepath.Clean(strings.TrimSpace(req.Path))
if root == "" {
return nil, buserr.WithDetail("ErrInvalidParams", "path is required", nil)
}
query := strings.TrimSpace(req.Query)
if query == "" {
return nil, buserr.WithDetail("ErrInvalidParams", "query is required", nil)
}
st, err := os.Stat(root)
if err != nil {
if os.IsNotExist(err) {
return nil, buserr.New("ErrPathNotFound")
}
return nil, err
}
if !st.IsDir() {
return nil, buserr.New("ErrPathNotFound")
}
maxItems := req.MaxItems
if maxItems <= 0 {
maxItems = files.DefaultFileAIMaxItems
}
if maxItems > 2000 {
maxItems = 2000
}
containSub := true
if req.ContainSub != nil {
containSub = *req.ContainSub
}
searchOpts, err := files.MergeContentSearchOptions(
req.MatchCase, req.WholeWord, req.UseRegex,
req.Extensions,
req.MinSize, req.MaxSize,
req.ModifiedAfter, req.ModifiedBefore,
req.MaxScanFiles,
req.MaxFileBytes,
req.MaxHitsPerFile, req.MaxTotalHits,
req.ContentHitsPromptMaxBytes,
req.LlmMaxOutputTokens,
)
if err != nil {
return nil, buserr.WithDetail("ErrInvalidParams", err.Error(), nil)
}
matchFn, err := files.NewContentLineMatcher(query, searchOpts)
if err != nil {
return nil, buserr.WithDetail("ErrFileAISearchBadPattern", err.Error(), nil)
}
cfg, timeout, err := terminalai.LoadFileAIRuntimeConfig()
aiEnabled := err == nil
if err != nil && !errors.Is(err, os.ErrNotExist) {
return nil, err
}
items, truncated, err := files.CollectDirInventory(root, containSub, maxItems)
if err != nil {
return nil, err
}
preFiltered := false
llmItems := items
qLower := strings.ToLower(query)
if len(llmItems) > 0 && query != "" {
filtered := make([]files.AISearchInventoryItem, 0, len(llmItems))
for _, it := range llmItems {
rel := strings.TrimSpace(it.RelPath)
if rel == "" {
continue
}
if !req.UseRegex && !req.MatchCase && !req.WholeWord && strings.Contains(strings.ToLower(rel), qLower) {
filtered = append(filtered, it)
}
}
if len(filtered) >= 8 {
llmItems = filtered
preFiltered = true
}
}
start := time.Now()
contentHits, scannedFiles, hitsTrunc := files.SearchFileAIContentHits(root, llmItems, searchOpts, matchFn)
hitsDTO := make([]response.FileAIContentHit, 0, len(contentHits))
for _, h := range contentHits {
hitsDTO = append(hitsDTO, response.FileAIContentHit{Path: h.Path, Line: h.Line, Text: h.Text})
}
matchDesc := searchOpts.ContentMatchDescription()
result := &response.FileAISearchResult{
Hits: hitsDTO,
ContentScannedFiles: scannedFiles,
ContentHitsTruncated: hitsTrunc,
Truncated: truncated,
PreFiltered: preFiltered,
ItemCount: len(llmItems),
}
if len(llmItems) == 0 {
if aiEnabled {
result.Mode = "ai"
result.Summary = i18n.GetMsgByKey("FileAISearchEmptyDir")
if result.Summary == "" || result.Summary == "FileAISearchEmptyDir" {
result.Summary = "No files or directories found under this path (or all entries were filtered)."
}
} else {
result.Mode = "grep"
result.Summary = ""
}
result.Duration = time.Since(start).Round(time.Millisecond).String()
return result, nil
}
if !aiEnabled {
result.Mode = "grep"
result.Summary = ""
result.Duration = time.Since(start).Round(time.Millisecond).String()
return result, nil
}
result.Mode = "ai"
clientTimeout := timeout
if clientTimeout < 30*time.Second {
clientTimeout = 90 * time.Second
}
if clientTimeout > 5*time.Minute {
clientTimeout = 5 * time.Minute
}
runCtx, cancel := context.WithTimeout(context.Background(), timeout+time.Minute)
defer cancel()
llmMaxOut := searchOpts.LlmMaxOutputTokens
summary, usage, err := files.RunFileAISearchLLM(runCtx, cfg, clientTimeout, root, query, req.ResponseLanguage, llmItems, truncated, preFiltered, contentHits, scannedFiles, hitsTrunc, matchDesc, searchOpts.ContentHitsPromptMaxBytes, llmMaxOut)
if err != nil {
result.Mode = "grep"
result.Summary = ""
result.Duration = time.Since(start).Round(time.Millisecond).String()
if errors.Is(err, context.DeadlineExceeded) || strings.Contains(strings.ToLower(err.Error()), "timeout") {
return result, nil
}
return result, nil
}
result.Summary = summary
result.PromptTokens = usage.PromptTokens
result.CompletionTokens = usage.CompletionTokens
result.TotalTokens = usage.TotalTokens
if result.TotalTokens == 0 {
result.TotalTokens = usage.PromptTokens + usage.CompletionTokens
}
result.Duration = time.Since(start).Round(time.Millisecond).String()
return result, nil
}
+381
View File
@@ -0,0 +1,381 @@
package service
import (
"crypto/rand"
"crypto/sha256"
"crypto/subtle"
"encoding/hex"
"errors"
"os"
"path/filepath"
"regexp"
"strings"
"time"
"unicode/utf8"
"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"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
"gorm.io/gorm"
)
type FileShareService struct{}
const (
fileShareCodeMinLength = 10
fileShareCodeMaxLength = 16
fileShareCodeDefaultLength = 13
fileShareCharset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
)
var fileShareCodeRegexp = regexp.MustCompile(`^[A-Za-z0-9]{10,16}$`)
type IFileShareService interface {
Create(req request.FileShareCreate) (*response.FileShareInfo, error)
Page(req dto.PageInfo) (int64, []response.FileShareInfo, error)
GetByPath(path string) (*response.FileShareInfo, error)
GetByCode(code string) (*response.FileShareInfo, error)
GetPublicByCode(code string) (*response.FileSharePublicInfo, error)
DeleteByPath(path string) error
SharePathCodeMap() (map[string]string, error)
Check(code, password string) error
PrepareDownload(code, password string) (filePath, fileName string, err error)
}
func NewIFileShareService() IFileShareService {
return &FileShareService{}
}
func randomShareCode(length int) (string, error) {
if length < fileShareCodeMinLength || length > fileShareCodeMaxLength {
length = fileShareCodeDefaultLength
}
b := make([]byte, length)
if _, err := rand.Read(b); err != nil {
return "", err
}
buf := make([]byte, length)
for i := range b {
buf[i] = fileShareCharset[int(b[i])%len(fileShareCharset)]
}
return string(buf), nil
}
func randomSalt() (string, error) {
b := make([]byte, 16)
if _, err := rand.Read(b); err != nil {
return "", err
}
return hex.EncodeToString(b), nil
}
func hashPassword(salt, password string) string {
sum := sha256.Sum256([]byte(salt + ":" + password))
return hex.EncodeToString(sum[:])
}
func shareModelToInfo(item model.FileShare) response.FileShareInfo {
return response.FileShareInfo{
Code: item.Token,
Path: item.Path,
FileName: item.FileName,
ExpiresAt: item.ExpiresUnix,
Permanent: item.ExpiresUnix == 0,
HasPassword: item.PasswordHash != "",
}
}
func fillSharePassword(info *response.FileShareInfo, item model.FileShare) {
if info == nil || item.PasswordEnc == "" {
return
}
password, err := encrypt.StringDecrypt(item.PasswordEnc)
if err != nil {
return
}
info.Password = password
}
func shareModelToPublicInfo(item model.FileShare) response.FileSharePublicInfo {
return response.FileSharePublicInfo{
FileName: item.FileName,
ExpiresAt: item.ExpiresUnix,
Permanent: item.ExpiresUnix == 0,
HasPassword: item.PasswordHash != "",
}
}
func (s *FileShareService) generateUniqueCode() (string, error) {
for i := 0; i < 8; i++ {
code, err := randomShareCode(fileShareCodeDefaultLength)
if err != nil {
return "", err
}
_, err = fileShareRepo.GetFirst(fileShareRepo.WithByCode(code))
if errors.Is(err, gorm.ErrRecordNotFound) {
return code, nil
}
if err != nil {
return "", err
}
}
return "", errors.New("failed to generate unique file share code")
}
func (s *FileShareService) Create(req request.FileShareCreate) (*response.FileShareInfo, error) {
path := strings.TrimSpace(req.Path)
if path == "" || strings.Contains(path, "..") {
return nil, buserr.New("ErrFileSharePath")
}
info, err := os.Stat(path)
if err != nil || info.IsDir() {
return nil, buserr.New("ErrFileSharePath")
}
item, err := fileShareRepo.GetFirst(fileShareRepo.WithByPath(path))
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return nil, err
}
isNew := errors.Is(err, gorm.ErrRecordNotFound)
if isNew {
code, err := s.generateUniqueCode()
if err != nil {
return nil, err
}
item = model.FileShare{
Path: path,
Token: code,
FileName: filepath.Base(path),
}
} else if !fileShareCodeRegexp.MatchString(item.Token) {
code, err := s.generateUniqueCode()
if err != nil {
return nil, err
}
item.Token = code
}
item.FileName = filepath.Base(path)
item.MaxDownloads = 0
item.DownloadCount = 0
item.ExpiresUnix = 0
if req.ExpireMinutes > 0 {
item.ExpiresUnix = time.Now().Add(time.Duration(req.ExpireMinutes) * time.Minute).Unix()
}
if req.Password != nil {
pw := strings.TrimSpace(*req.Password)
if pw == "" {
item.PasswordEnc = ""
item.PasswordSalt = ""
item.PasswordHash = ""
} else {
pwLen := utf8.RuneCountInString(pw)
if pwLen < 4 || pwLen > 256 {
return nil, buserr.New("ErrFileSharePasswordPolicy")
}
enc, err := encrypt.StringEncrypt(pw)
if err != nil {
return nil, err
}
item.PasswordEnc = enc
salt, err := randomSalt()
if err != nil {
return nil, err
}
item.PasswordSalt = salt
item.PasswordHash = hashPassword(salt, pw)
}
}
if isNew {
if err := fileShareRepo.Create(&item); err != nil {
return nil, err
}
} else {
if err := fileShareRepo.Save(&item); err != nil {
return nil, err
}
}
res := shareModelToInfo(item)
fillSharePassword(&res, item)
return &res, nil
}
func (s *FileShareService) Page(req dto.PageInfo) (int64, []response.FileShareInfo, error) {
items, err := fileShareRepo.All()
if err != nil {
return 0, nil, err
}
result := make([]response.FileShareInfo, 0, len(items))
for _, item := range items {
if err := s.pruneInvalidShare(item); err != nil {
return 0, nil, err
}
if item.ExpiresUnix > 0 && time.Now().Unix() > item.ExpiresUnix {
continue
}
result = append(result, shareModelToInfo(item))
}
total := len(result)
start := (req.Page - 1) * req.PageSize
if start >= total {
return int64(total), []response.FileShareInfo{}, nil
}
end := start + req.PageSize
if end > total {
end = total
}
return int64(total), result[start:end], nil
}
func (s *FileShareService) GetByPath(path string) (*response.FileShareInfo, error) {
item, err := fileShareRepo.GetFirst(fileShareRepo.WithByPath(strings.TrimSpace(path)))
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, err
}
if err := s.pruneInvalidShare(item); err != nil {
return nil, err
}
if item.ExpiresUnix > 0 && time.Now().Unix() > item.ExpiresUnix {
return nil, nil
}
info := shareModelToInfo(item)
fillSharePassword(&info, item)
return &info, nil
}
func (s *FileShareService) GetByCode(code string) (*response.FileShareInfo, error) {
item, err := fileShareRepo.GetFirst(fileShareRepo.WithByCode(strings.TrimSpace(code)))
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, buserr.New("ErrFileShareInvalid")
}
return nil, err
}
if err := s.pruneInvalidShare(item); err != nil {
return nil, err
}
if item.ExpiresUnix > 0 && time.Now().Unix() > item.ExpiresUnix {
return nil, buserr.New("ErrFileShareExpired")
}
info := shareModelToInfo(item)
return &info, nil
}
func (s *FileShareService) GetPublicByCode(code string) (*response.FileSharePublicInfo, error) {
item, err := fileShareRepo.GetFirst(fileShareRepo.WithByCode(strings.TrimSpace(code)))
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, buserr.New("ErrFileShareInvalid")
}
return nil, err
}
if err := s.pruneInvalidShare(item); err != nil {
return nil, err
}
if item.ExpiresUnix > 0 && time.Now().Unix() > item.ExpiresUnix {
return nil, buserr.New("ErrFileShareExpired")
}
info := shareModelToPublicInfo(item)
return &info, nil
}
func (s *FileShareService) DeleteByPath(path string) error {
item, err := fileShareRepo.GetFirst(fileShareRepo.WithByPath(strings.TrimSpace(path)))
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return buserr.New("ErrFileShareInvalid")
}
return err
}
return fileShareRepo.Delete(repo.WithByID(item.ID))
}
func (s *FileShareService) SharePathCodeMap() (map[string]string, error) {
items, err := fileShareRepo.All()
if err != nil {
return nil, err
}
result := make(map[string]string, len(items))
now := time.Now().Unix()
for _, item := range items {
if item.ExpiresUnix > 0 && now > item.ExpiresUnix {
continue
}
if _, err := os.Stat(item.Path); err != nil {
continue
}
result[item.Path] = item.Token
}
return result, nil
}
func (s *FileShareService) Check(code, password string) error {
_, err := s.check(code, password)
return err
}
func (s *FileShareService) PrepareDownload(code, password string) (string, string, error) {
item, err := s.check(code, password)
if err != nil {
return "", "", err
}
return item.Path, item.FileName, nil
}
func (s *FileShareService) pruneInvalidShare(item model.FileShare) error {
now := time.Now().Unix()
if item.ExpiresUnix > 0 && now > item.ExpiresUnix {
return fileShareRepo.Delete(repo.WithByID(item.ID))
}
info, err := os.Stat(item.Path)
if err != nil || info.IsDir() {
return fileShareRepo.Delete(repo.WithByID(item.ID))
}
return nil
}
func (s *FileShareService) check(code, password string) (*model.FileShare, error) {
code = strings.TrimSpace(code)
password = strings.TrimSpace(password)
if code == "" {
return nil, buserr.New("ErrFileShareInvalid")
}
item, err := fileShareRepo.GetFirst(fileShareRepo.WithByCode(code))
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, buserr.New("ErrFileShareInvalid")
}
return nil, err
}
now := time.Now().Unix()
if item.ExpiresUnix > 0 && now > item.ExpiresUnix {
_ = fileShareRepo.Delete(repo.WithByID(item.ID))
return nil, buserr.New("ErrFileShareExpired")
}
if item.PasswordHash != "" {
if subtle.ConstantTimeCompare([]byte(hashPassword(item.PasswordSalt, password)), []byte(item.PasswordHash)) != 1 {
return nil, buserr.New("ErrFileSharePassword")
}
}
info, err := os.Stat(item.Path)
if err != nil || info.IsDir() {
_ = fileShareRepo.Delete(repo.WithByID(item.ID))
return nil, buserr.New("ErrFileSharePath")
}
return &item, nil
}
+46
View File
@@ -23,8 +23,10 @@ type SettingService struct{}
type ISettingService interface {
GetSettingInfo() (*dto.SettingInfo, error)
GetTerminalAIInfo() (*dto.TerminalAIInfo, error)
GetFileManageAIInfo() (*dto.FileManageAIInfo, error)
Update(key, value string) error
UpdateTerminalAI(req dto.TerminalAIInfo) error
UpdateFileManageAI(req dto.FileManageAIInfo) error
TestConnByInfo(req dto.SSHConnData) bool
SaveConnInfo(req dto.SSHConnData) error
@@ -87,6 +89,20 @@ func (u *SettingService) GetTerminalAIInfo() (*dto.TerminalAIInfo, error) {
return info, nil
}
func (u *SettingService) GetFileManageAIInfo() (*dto.FileManageAIInfo, error) {
info := &dto.FileManageAIInfo{
AIStatus: constant.StatusDisable,
AIAccountID: "",
}
if value, err := settingRepo.GetValueByKey("FileAIStatus"); err == nil && value != "" {
info.AIStatus = value
}
if value, err := settingRepo.GetValueByKey("FileAIAccountID"); err == nil {
info.AIAccountID = value
}
return info, nil
}
func (u *SettingService) Update(key, value string) error {
return settingRepo.UpdateOrCreate(key, value)
}
@@ -123,6 +139,36 @@ func (u *SettingService) UpdateTerminalAI(req dto.TerminalAIInfo) error {
return nil
}
func (u *SettingService) UpdateFileManageAI(req dto.FileManageAIInfo) error {
if strings.EqualFold(strings.TrimSpace(req.AIStatus), constant.StatusEnable) {
accountID, err := strconv.ParseUint(strings.TrimSpace(req.AIAccountID), 10, 64)
if err != nil || accountID == 0 {
return buserr.New("ErrAgentAccountIDRequired")
}
currentStatus, _ := settingRepo.GetValueByKey("FileAIStatus")
currentAccountID, _ := settingRepo.GetValueByKey("FileAIAccountID")
needValidate := !strings.EqualFold(strings.TrimSpace(currentStatus), constant.StatusEnable) ||
strings.TrimSpace(currentAccountID) != strings.TrimSpace(req.AIAccountID)
if needValidate {
if err := terminalai.ValidateTerminalAccount(uint(accountID)); err != nil {
return buserr.WithErr("ErrAgentAccountUnavailable", err)
}
}
}
accountVal := strings.TrimSpace(req.AIAccountID)
if !strings.EqualFold(strings.TrimSpace(req.AIStatus), constant.StatusEnable) {
accountVal = ""
}
if err := settingRepo.UpdateOrCreate("FileAIStatus", req.AIStatus); err != nil {
return err
}
if err := settingRepo.UpdateOrCreate("FileAIAccountID", accountVal); err != nil {
return err
}
terminalai.InvalidateFileAIRuntimeCache()
return nil
}
func (u *SettingService) TestConnByInfo(req dto.SSHConnData) bool {
if req.AuthMode == "password" && len(req.Password) != 0 {
password, err := base64.StdEncoding.DecodeString(req.Password)
+570 -123
View File
@@ -9,6 +9,7 @@ import (
"os"
"os/user"
"path"
"path/filepath"
"sort"
"strconv"
"strings"
@@ -33,6 +34,8 @@ import (
)
const sshPath = "/etc/ssh/sshd_config"
const defaultSSHPort = "22"
const sshManagedMarker = "# config by 1panel"
type SSHService struct{}
@@ -41,7 +44,7 @@ type ISSHService interface {
OperateSSH(operation string) error
Update(req dto.SSHUpdate) error
LoadSSHFile(name string) (string, error)
UpdateByFile(req dto.SettingUpdate) error
UpdateByFile(req dto.SSHConfUpdate) error
LoadLog(ctx *gin.Context, req dto.SearchSSHLog) (int64, []dto.SSHHistory, error)
ExportLog(ctx *gin.Context, req dto.SearchSSHLog) (string, error)
@@ -57,6 +60,28 @@ func NewISSHService() ISSHService {
return &SSHService{}
}
type sshDirective struct {
Key string
Value string
File string
Line int
InMatch bool
}
type sshConfigFile struct {
Path string `json:"path"`
Priority int `json:"priority"`
}
type sshManagedBlock struct {
Start int
End int
}
func (b sshManagedBlock) contains(line int) bool {
return line >= b.Start && line < b.End
}
func (u *SSHService) GetSSHInfo() (*dto.SSHInfo, error) {
data := dto.SSHInfo{
AutoStart: true,
@@ -70,64 +95,9 @@ func (u *SSHService) GetSSHInfo() (*dto.SSHInfo, error) {
PermitRootLogin: "yes",
UseDNS: "yes",
}
serviceName, err := loadServiceName()
if err != nil {
data.IsExist = false
data.Message = err.Error()
} else {
active, err := controller.CheckActive(serviceName)
data.IsActive = active
if !active && err != nil {
data.Message = err.Error()
}
}
enable, err := controller.CheckEnable(serviceName)
if err != nil {
data.AutoStart = false
} else {
data.AutoStart = enable
}
sshConf, err := os.ReadFile(sshPath)
if err != nil {
data.Message = err.Error()
data.IsActive = false
}
lines := strings.Split(string(sshConf), "\n")
for _, line := range lines {
if strings.HasPrefix(line, "Port ") {
data.Port = strings.ReplaceAll(line, "Port ", "")
}
if strings.HasPrefix(line, "ListenAddress ") {
itemAddr := strings.ReplaceAll(line, "ListenAddress ", "")
if len(data.ListenAddress) != 0 {
data.ListenAddress += ("," + itemAddr)
} else {
data.ListenAddress = itemAddr
}
}
if strings.HasPrefix(line, "PasswordAuthentication ") {
data.PasswordAuthentication = strings.ReplaceAll(line, "PasswordAuthentication ", "")
}
if strings.HasPrefix(line, "PubkeyAuthentication ") {
data.PubkeyAuthentication = strings.ReplaceAll(line, "PubkeyAuthentication ", "")
}
if strings.HasPrefix(line, "PermitRootLogin ") {
data.PermitRootLogin = strings.ReplaceAll(strings.ReplaceAll(line, "PermitRootLogin ", ""), "prohibit-password", "without-password")
}
if strings.HasPrefix(line, "UseDNS ") {
data.UseDNS = strings.ReplaceAll(line, "UseDNS ", "")
}
}
currentUser, err := user.Current()
if err != nil || len(currentUser.Name) == 0 {
data.CurrentUser = "root"
} else {
data.CurrentUser = currentUser.Name
}
loadSSHServiceStatus(&data)
loadSSHConfigInfo(&data)
data.CurrentUser = loadCurrentUserName()
return &data, nil
}
@@ -160,58 +130,175 @@ func (u *SSHService) Update(req dto.SSHUpdate) error {
return err
}
sshConf, err := os.ReadFile(sshPath)
directives, _, err := parseSSHConfigTree(sshPath)
if err != nil {
return err
}
lines := strings.Split(string(sshConf), "\n")
newFiles := updateSSHConf(lines, req.Key, req.NewValue)
file, err := os.OpenFile(sshPath, os.O_WRONLY|os.O_TRUNC, constant.FilePerm)
if err != nil {
oldPortValue := strings.Join(loadSSHPortValues(directives), ",")
if err := updateSSHDirectiveValue(req.Key, req.NewValue, directives); err != nil {
return err
}
defer file.Close()
if _, err = file.WriteString(strings.Join(newFiles, "\n")); err != nil {
return err
}
sudo := cmd.SudoHandleCmd()
if req.Key == "Port" {
stdout, _ := cmd.RunDefaultWithStdoutBashCf("%s getenforce", sudo)
if stdout == "Enforcing\n" {
_ = cmd.RunDefaultBashCf("%s semanage port -a -t ssh_port_t -p tcp %s", sudo, req.NewValue)
}
ruleItem := dto.PortRuleUpdate{
OldRule: dto.PortRuleOperate{
Operation: "remove",
Port: req.OldValue,
Protocol: "tcp",
Strategy: "accept",
},
NewRule: dto.PortRuleOperate{
Operation: "add",
Port: req.NewValue,
Protocol: "tcp",
Strategy: "accept",
},
}
if err := NewIFirewallService().UpdatePortRule(ruleItem); err != nil {
global.LOG.Errorf("reset firewall rules %s -> %s failed, err: %v", req.OldValue, req.NewValue, err)
}
newPort, _ := strconv.Atoi(req.NewValue)
if err := updateLocalConn(uint(newPort)); err != nil {
global.LOG.Errorf("update local conn for terminal failed, err: %v", err)
}
if err := updateSSHSocketFile(req.NewValue); err != nil {
global.LOG.Errorf("update port for ssh.socket failed, err: %v", err)
}
handleSSHPortUpdate(oldPortValue, req.NewValue)
}
_ = controller.HandleRestart(serviceName)
return nil
}
func loadSSHServiceStatus(data *dto.SSHInfo) {
serviceName, err := loadServiceName()
if err != nil {
data.IsExist = false
data.Message = err.Error()
return
}
active, err := controller.CheckActive(serviceName)
data.IsActive = active
if !active && err != nil {
data.Message = err.Error()
}
enable, err := controller.CheckEnable(serviceName)
if err != nil {
data.AutoStart = false
return
}
data.AutoStart = enable
}
func loadSSHConfigInfo(data *dto.SSHInfo) {
directives, _, err := parseSSHConfigTree(sshPath)
if err != nil {
data.Message = err.Error()
data.IsActive = false
return
}
if port := strings.Join(loadSSHPortValues(directives), ","); port != "" {
data.Port = port
}
data.ListenAddress = strings.Join(loadSSHDirectiveValues(directives, "ListenAddress"), ",")
if value, ok := loadFirstSSHDirectiveValue(directives, "PasswordAuthentication"); ok {
data.PasswordAuthentication = value
}
if value, ok := loadFirstSSHDirectiveValue(directives, "PubkeyAuthentication"); ok {
data.PubkeyAuthentication = value
}
if value, ok := loadFirstSSHDirectiveValue(directives, "PermitRootLogin"); ok {
data.PermitRootLogin = strings.ReplaceAll(value, "prohibit-password", "without-password")
}
if value, ok := loadFirstSSHDirectiveValue(directives, "UseDNS"); ok {
data.UseDNS = value
}
}
func loadCurrentUserName() string {
currentUser, err := user.Current()
if err != nil || currentUser.Name == "" {
return "root"
}
return currentUser.Name
}
func handleSSHPortUpdate(oldValue, newValue string) {
sudo := cmd.SudoHandleCmd()
newPorts := splitSSHPorts(newValue)
oldPorts := splitSSHPorts(oldValue)
stdout, _ := runWithOptionalSudo(sudo, "getenforce")
if stdout == "Enforcing\n" {
for _, port := range diffSSHPorts(oldPorts, newPorts) {
if _, err := runWithOptionalSudo(sudo, "semanage", "port", "-d", "-t", "ssh_port_t", "-p", "tcp", port); err != nil {
global.LOG.Warnf("remove selinux ssh port %s failed, err: %v", port, err)
}
}
for _, port := range newPorts {
_, _ = runWithOptionalSudo(sudo, "semanage", "port", "-a", "-t", "ssh_port_t", "-p", "tcp", port)
}
}
removedPorts, err := parseSSHPortsToInts(diffSSHPorts(oldPorts, newPorts))
if err != nil {
global.LOG.Errorf("parse removed ssh ports failed, err: %v", err)
} else {
addedPorts, err := parseSSHPortsToInts(diffSSHPorts(newPorts, oldPorts))
if err != nil {
global.LOG.Errorf("parse added ssh ports failed, err: %v", err)
} else if err := OperateFirewallPort(removedPorts, addedPorts); err != nil {
global.LOG.Errorf("reset firewall rules %s -> %s failed, err: %v", oldValue, newValue, err)
}
}
primaryPort, err := loadPrimarySSHPort(newValue)
if err != nil {
global.LOG.Errorf("load primary ssh port from %s failed, err: %v", newValue, err)
return
}
if err := updateLocalConn(uint(primaryPort)); err != nil {
global.LOG.Errorf("update local conn for terminal failed, err: %v", err)
}
if err := updateSSHSocketFile(strconv.Itoa(primaryPort)); err != nil {
global.LOG.Errorf("update port for ssh.socket failed, err: %v", err)
}
}
func splitSSHPorts(value string) []string {
var ports []string
for _, item := range strings.Split(value, ",") {
port := strings.TrimSpace(item)
if port != "" {
ports = append(ports, port)
}
}
return ports
}
func diffSSHPorts(left, right []string) []string {
rightSet := make(map[string]struct{}, len(right))
for _, item := range right {
rightSet[item] = struct{}{}
}
var diff []string
for _, item := range left {
if _, ok := rightSet[item]; ok {
continue
}
diff = append(diff, item)
}
return diff
}
func loadPrimarySSHPort(value string) (int, error) {
ports := splitSSHPorts(value)
if len(ports) == 0 {
return 0, fmt.Errorf("ssh port is empty")
}
return strconv.Atoi(ports[0])
}
func parseSSHPortsToInts(ports []string) ([]int, error) {
var values []int
for _, port := range ports {
value, err := strconv.Atoi(port)
if err != nil {
return nil, err
}
values = append(values, value)
}
return values, nil
}
func runWithOptionalSudo(sudo, name string, args ...string) (string, error) {
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(20 * time.Second))
if sudo != "" {
commandArgs := append([]string{name}, args...)
return cmdMgr.RunWithStdout("sudo", commandArgs...)
}
return cmdMgr.RunWithStdout(name, args...)
}
func (u *SSHService) SyncRootCert() error {
currentUser, err := user.Current()
if err != nil {
@@ -582,8 +669,25 @@ func (u *SSHService) LoadSSHFile(name string) (string, error) {
fileName = currentUser.HomeDir + "/.ssh/authorized_keys"
case "sshdConf":
fileName = "/etc/ssh/sshd_config"
case "sshdConfOptions":
_, fileList, err := parseSSHConfigTree(sshPath)
if err != nil {
return "", err
}
content, err := json.Marshal(fileList)
if err != nil {
return "", err
}
return string(content), nil
default:
return "", buserr.WithName("ErrNotSupportType", name)
if strings.HasPrefix(name, "sshdConfPath:") {
fileName = strings.TrimPrefix(name, "sshdConfPath:")
if !isSSHConfigPathAllowed(fileName) {
return "", buserr.WithName("ErrNotSupportType", name)
}
} else {
return "", buserr.WithName("ErrNotSupportType", name)
}
}
if _, err := os.Stat(fileName); err != nil {
return "", buserr.WithErr("ErrHttpReqNotFound", err)
@@ -595,7 +699,7 @@ func (u *SSHService) LoadSSHFile(name string) (string, error) {
return string(content), nil
}
func (u *SSHService) UpdateByFile(req dto.SettingUpdate) error {
func (u *SSHService) UpdateByFile(req dto.SSHConfUpdate) error {
var fileName string
switch req.Key {
case "authKeys":
@@ -606,6 +710,11 @@ func (u *SSHService) UpdateByFile(req dto.SettingUpdate) error {
fileName = currentUser.HomeDir + "/.ssh/authorized_keys"
case "sshdConf":
fileName = "/etc/ssh/sshd_config"
case "sshdConfPath":
fileName = req.Path
if !isSSHConfigPathAllowed(fileName) {
return buserr.WithName("ErrNotSupportType", req.Key)
}
default:
return buserr.WithName("ErrNotSupportType", req.Key)
}
@@ -647,35 +756,372 @@ func sortFileList(fileNames []sshFileItem) []sshFileItem {
return fileNames
}
func updateSSHConf(oldFiles []string, param string, value string) []string {
var valueItems []string
if param != "ListenAddress" {
valueItems = append(valueItems, value)
} else {
if value != "" {
valueItems = strings.Split(value, ",")
}
func parseSSHConfigTree(root string) ([]sshDirective, []sshConfigFile, error) {
rootPath, err := filepath.Abs(root)
if err != nil {
return nil, nil, err
}
var newFiles []string
for _, line := range oldFiles {
lineItem := strings.TrimSpace(line)
if (strings.HasPrefix(lineItem, param) || strings.HasPrefix(lineItem, fmt.Sprintf("#%s", param))) && len(valueItems) != 0 {
newFiles = append(newFiles, fmt.Sprintf("%s %s", param, valueItems[0]))
valueItems = valueItems[1:]
var (
directives []sshDirective
fileList []sshConfigFile
order int
priority int
)
visited := make(map[string]bool)
if err := parseSSHConfigFile(rootPath, false, visited, &directives, &fileList, &order, &priority); err != nil {
return nil, nil, err
}
return directives, fileList, nil
}
func parseSSHConfigFile(
fileName string,
inMatch bool,
visited map[string]bool,
directives *[]sshDirective,
fileList *[]sshConfigFile,
order *int,
priority *int,
) error {
absolutePath, err := filepath.Abs(fileName)
if err != nil {
return err
}
if visited[absolutePath] {
return nil
}
visited[absolutePath] = true
content, err := os.ReadFile(absolutePath)
if err != nil {
return err
}
lines := strings.Split(string(content), "\n")
currentMatch := inMatch
fileRegistered := false
for index, rawLine := range lines {
line := strings.TrimSpace(rawLine)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
if strings.HasPrefix(lineItem, param) && len(valueItems) == 0 {
newFiles = append(newFiles, fmt.Sprintf("#%s", line))
key, value, ok := parseSSHConfigLine(line)
if !ok {
continue
}
newFiles = append(newFiles, line)
if strings.EqualFold(key, "Match") {
currentMatch = true
continue
}
if strings.EqualFold(key, "Include") {
for _, includePath := range expandSSHIncludePaths(absolutePath, value) {
if err := parseSSHConfigFile(includePath, currentMatch, visited, directives, fileList, order, priority); err != nil {
global.LOG.Warnf("parse ssh include %s failed, err: %v", includePath, err)
}
}
continue
}
if !fileRegistered {
registerSSHConfigFile(absolutePath, fileList, priority)
fileRegistered = true
}
*order++
*directives = append(*directives, sshDirective{
Key: key,
Value: normalizeSSHDirectiveValue(key, value),
File: absolutePath,
Line: index,
InMatch: currentMatch,
})
}
if len(valueItems) != 0 {
for _, item := range valueItems {
newFiles = append(newFiles, fmt.Sprintf("%s %s", param, item))
if !fileRegistered {
registerSSHConfigFile(absolutePath, fileList, priority)
}
return nil
}
func registerSSHConfigFile(fileName string, fileList *[]sshConfigFile, priority *int) {
for _, item := range *fileList {
if item.Path == fileName {
return
}
}
return newFiles
*priority++
*fileList = append(*fileList, sshConfigFile{Path: fileName, Priority: *priority})
}
func parseSSHConfigLine(line string) (string, string, bool) {
line = trimSSHInlineComment(line)
fields := strings.Fields(line)
if len(fields) < 2 {
return "", "", false
}
return fields[0], strings.Join(fields[1:], " "), true
}
func trimSSHInlineComment(line string) string {
inQuote := false
for i := 0; i < len(line); i++ {
switch line[i] {
case '"':
inQuote = !inQuote
case '#':
if !inQuote && (i == 0 || line[i-1] == ' ' || line[i-1] == '\t') {
return strings.TrimSpace(line[:i])
}
}
}
return strings.TrimSpace(line)
}
func expandSSHIncludePaths(baseFile, value string) []string {
var includeFiles []string
for _, item := range strings.Fields(strings.ReplaceAll(value, "\"", "")) {
pattern := item
if !filepath.IsAbs(pattern) {
pattern = filepath.Join(filepath.Dir(baseFile), pattern)
}
matches, err := filepath.Glob(pattern)
if err != nil {
continue
}
sort.Strings(matches)
includeFiles = append(includeFiles, matches...)
}
return includeFiles
}
func normalizeSSHDirectiveValue(key, value string) string {
if strings.EqualFold(key, "PermitRootLogin") && value == "prohibit-password" {
return "without-password"
}
return value
}
func loadFirstSSHDirectiveValue(directives []sshDirective, key string) (string, bool) {
for _, item := range directives {
if item.InMatch || !strings.EqualFold(item.Key, key) {
continue
}
return item.Value, true
}
return "", false
}
func loadSSHDirectiveValues(directives []sshDirective, key string) []string {
var values []string
for _, item := range directives {
if item.InMatch || !strings.EqualFold(item.Key, key) {
continue
}
values = append(values, item.Value)
}
return values
}
func loadSSHPortValues(directives []sshDirective) []string {
values := loadSSHDirectiveValues(directives, "Port")
if len(values) == 0 {
return []string{defaultSSHPort}
}
return values
}
func updateSSHDirectiveValue(key, value string, directives []sshDirective) error {
if key == "Port" || key == "ListenAddress" {
return rewriteSSHMultiValueDirective(key, value, directives)
}
return updateSSHSingleDirectiveValue(key, value, directives)
}
func updateSSHSingleDirectiveValue(key, value string, directives []sshDirective) error {
block, err := loadSSHManagedBlockFromFile(sshPath)
if err != nil {
return err
}
for _, item := range directives {
if item.InMatch || !strings.EqualFold(item.Key, key) {
continue
}
if item.File == sshPath && block.contains(item.Line) {
continue
}
if err := commentSSHConfigLines(item.File, []int{item.Line}); err != nil {
return err
}
}
return rewriteSSHManagedDirectives(sshPath, key, []string{fmt.Sprintf("%s %s", key, value)})
}
func rewriteSSHMultiValueDirective(key, value string, directives []sshDirective) error {
block, err := loadSSHManagedBlockFromFile(sshPath)
if err != nil {
return err
}
targetFiles := make(map[string][]int)
for _, item := range directives {
if item.InMatch || !strings.EqualFold(item.Key, key) {
continue
}
if item.File == sshPath && block.contains(item.Line) {
continue
}
targetFiles[item.File] = append(targetFiles[item.File], item.Line)
}
for fileName, lines := range targetFiles {
if err := commentSSHConfigLines(fileName, lines); err != nil {
return err
}
}
return rewriteSSHManagedDirectives(sshPath, key, buildSSHDirectiveLines(key, value))
}
func buildSSHDirectiveLines(key, value string) []string {
var directives []string
for _, item := range strings.Split(value, ",") {
if item != "" {
directives = append(directives, fmt.Sprintf("%s %s", key, item))
}
}
return directives
}
func rewriteSSHManagedDirectives(fileName, key string, newDirectives []string) error {
content, err := os.ReadFile(fileName)
if err != nil {
return err
}
lines := strings.Split(string(content), "\n")
lines, insertAt := ensureSSHManagedMarker(lines)
block, ok := loadSSHManagedBlock(lines)
if ok {
var filtered []string
for index := block.Start; index < block.End; index++ {
line := strings.TrimSpace(lines[index])
if line == "" || strings.HasPrefix(line, "#") {
filtered = append(filtered, lines[index])
continue
}
itemKey, _, ok := parseSSHConfigLine(line)
if ok && strings.EqualFold(itemKey, key) {
continue
}
filtered = append(filtered, lines[index])
}
lines = append(append(lines[:block.Start], filtered...), lines[block.End:]...)
insertAt = block.Start
}
if len(newDirectives) != 0 {
lines = insertSSHDirectivesAt(lines, insertAt, newDirectives)
}
return os.WriteFile(fileName, []byte(strings.Join(lines, "\n")), constant.FilePerm)
}
func commentSSHConfigLines(fileName string, targetLines []int) error {
content, err := os.ReadFile(fileName)
if err != nil {
return err
}
lines := strings.Split(string(content), "\n")
targetSet := make(map[int]struct{}, len(targetLines))
for _, line := range targetLines {
targetSet[line] = struct{}{}
}
for index := range lines {
if _, ok := targetSet[index]; !ok {
continue
}
trimmed := strings.TrimSpace(lines[index])
if trimmed == "" || strings.HasPrefix(trimmed, "#") {
continue
}
lines[index] = "#" + lines[index]
}
return os.WriteFile(fileName, []byte(strings.Join(lines, "\n")), constant.FilePerm)
}
func loadSSHInsertIndex(lines []string) int {
for index, line := range lines {
trimmed := strings.TrimSpace(line)
if strings.HasPrefix(strings.ToLower(trimmed), "match ") {
return index
}
}
return len(lines)
}
func ensureSSHManagedMarker(lines []string) ([]string, int) {
for index, line := range lines {
if strings.TrimSpace(line) == sshManagedMarker {
return lines, index + 1
}
}
insertAt := loadSSHInsertIndex(lines)
lines = insertSSHDirectivesAt(lines, insertAt, []string{sshManagedMarker})
return lines, insertAt + 1
}
func loadSSHManagedBlock(lines []string) (sshManagedBlock, bool) {
for index, line := range lines {
if strings.TrimSpace(line) != sshManagedMarker {
continue
}
end := len(lines)
for next := index + 1; next < len(lines); next++ {
if strings.HasPrefix(strings.ToLower(strings.TrimSpace(lines[next])), "match ") {
end = next
break
}
}
return sshManagedBlock{Start: index + 1, End: end}, true
}
return sshManagedBlock{}, false
}
func loadSSHManagedBlockFromFile(fileName string) (sshManagedBlock, error) {
content, err := os.ReadFile(fileName)
if err != nil {
return sshManagedBlock{}, err
}
block, ok := loadSSHManagedBlock(strings.Split(string(content), "\n"))
if !ok {
return sshManagedBlock{}, nil
}
return block, nil
}
func insertSSHDirectivesAt(lines []string, insertAt int, directives []string) []string {
newLines := make([]string, 0, len(lines)+len(directives))
newLines = append(newLines, lines[:insertAt]...)
newLines = append(newLines, directives...)
newLines = append(newLines, lines[insertAt:]...)
return newLines
}
func isSSHConfigPathAllowed(fileName string) bool {
if fileName == "" {
return false
}
absolutePath, err := filepath.Abs(fileName)
if err != nil {
return false
}
if absolutePath == sshPath {
return true
}
_, fileList, err := parseSSHConfigTree(sshPath)
if err != nil {
return false
}
for _, item := range fileList {
if item.Path == absolutePath {
return true
}
}
return false
}
func loadSSHData(ctx *gin.Context, command string, showCountFrom, showCountTo, currentYear int, nyc *time.Location) ([]dto.SSHHistory, int, int) {
@@ -804,7 +1250,8 @@ func checkIsStandard(item dto.SSHHistory) bool {
}
func handleGunzip(path string) error {
if err := cmd.RunDefaultBashCf("gunzip %s", path); err != nil {
cmdMgr := cmd.NewCommandMgr()
if err := cmdMgr.Run("gunzip", path); err != nil {
return err
}
return nil
+10 -1
View File
@@ -538,7 +538,13 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
createTask.AddSubTaskWithIgnoreErr(i18n.GetWithName("ConfigFTP", create.FtpUser), createFtpUser)
}
return createTask.Execute()
if err := createTask.Execute(); err != nil {
return err
}
if err := bindDeploymentWebsiteToAgentByAppInstall(website); err != nil {
global.LOG.Errorf("bind deployment website to agent failed: %v", err)
}
return nil
}
func (w WebsiteService) OpWebsite(req request.WebsiteOp) error {
@@ -733,6 +739,9 @@ func (w WebsiteService) DeleteWebsite(req request.WebsiteDelete) error {
if err := websiteRepo.DeleteBy(ctx, repo.WithByID(req.ID)); err != nil {
return err
}
if err := agentRepo.ClearWebsiteIDByWebsiteIDWithCtx(ctx, req.ID); err != nil {
return err
}
if err := websiteDomainRepo.DeleteBy(ctx, websiteDomainRepo.WithWebsiteId(req.ID)); err != nil {
return err
}
+62 -30
View File
@@ -5,8 +5,7 @@ import (
"crypto/x509"
"encoding/pem"
"fmt"
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
"github.com/go-acme/lego/v4/certificate"
"io"
"log"
"os"
"path"
@@ -14,6 +13,10 @@ import (
"strings"
"time"
"github.com/go-acme/lego/v4/certificate"
legoLogger "github.com/go-acme/lego/v4/log"
"github.com/jinzhu/gorm"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
"github.com/1Panel-dev/1Panel/agent/app/model"
@@ -27,8 +30,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/1Panel-dev/1Panel/agent/utils/req_helper"
"github.com/1Panel-dev/1Panel/agent/utils/ssl"
legoLogger "github.com/go-acme/lego/v4/log"
"github.com/jinzhu/gorm"
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
)
type WebsiteSSLService struct {
@@ -45,6 +47,7 @@ type IWebsiteSSLService interface {
Update(update request.WebsiteSSLUpdate) error
Upload(req request.WebsiteSSLUpload) error
ObtainSSL(apply request.WebsiteSSLApply) error
AutoRenewSSL(id uint) error
SyncForRestart() error
DownloadFile(id uint) (*os.File, error)
ImportMasterSSL(create model.WebsiteSSL) error
@@ -210,13 +213,35 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
return create, nil
}
func printSSLLog(logger *log.Logger, msgKey string, params map[string]interface{}, disableLog bool) {
if disableLog {
func printSSLLog(logger *log.Logger, msgKey string, params map[string]interface{}) {
if logger == nil {
return
}
logger.Println(i18n.GetMsgWithMap(msgKey, params))
}
func newWebsiteSSLLogger(websiteSSL *model.WebsiteSSL, autoRenew bool) (*os.File, *log.Logger) {
flags := os.O_CREATE | os.O_WRONLY | os.O_TRUNC
if autoRenew {
flags = os.O_CREATE | os.O_WRONLY | os.O_APPEND
}
logFile, err := os.OpenFile(websiteSSL.GetLogPath(), flags, constant.FilePerm)
if err != nil {
global.LOG.Errorf("open ssl log file failed, domain: %s, err: %v", websiteSSL.PrimaryDomain, err)
return nil, log.New(io.Discard, "", log.LstdFlags)
}
if autoRenew {
if info, statErr := logFile.Stat(); statErr == nil && info.Size() > 0 {
_, _ = logFile.WriteString("\n")
}
_, _ = logFile.WriteString(fmt.Sprintf("========== [%s] auto renew attempt ==========\n", time.Now().Format(constant.DateTimeLayout)))
}
return logFile, log.New(logFile, "", log.LstdFlags)
}
func reloadSystemSSL(websiteSSL *model.WebsiteSSL, logger *log.Logger) {
if !global.IsMaster {
return
@@ -226,7 +251,7 @@ func reloadSystemSSL(websiteSSL *model.WebsiteSSL, logger *log.Logger) {
fileOp := files.NewFileOp()
certPath := path.Join(global.Dir.DataDir, "secret/server.crt")
keyPath := path.Join(global.Dir.DataDir, "secret/server.key")
printSSLLog(logger, "StartUpdateSystemSSL", nil, logger == nil)
printSSLLog(logger, "StartUpdateSystemSSL", nil)
if err := fileOp.WriteFile(certPath, strings.NewReader(websiteSSL.Pem), 0600); err != nil {
logger.Printf("Failed to update the SSL certificate File for 1Panel System domain [%s] , err:%s", websiteSSL.PrimaryDomain, err.Error())
return
@@ -239,11 +264,19 @@ func reloadSystemSSL(websiteSSL *model.WebsiteSSL, logger *log.Logger) {
logger.Printf("Failed to update the SSL certificate for 1Panel System domain [%s] , err:%s", websiteSSL.PrimaryDomain, err.Error())
return
}
printSSLLog(logger, "UpdateSystemSSLSuccess", nil, logger == nil)
printSSLLog(logger, "UpdateSystemSSLSuccess", nil)
}
}
func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
return w.obtainSSL(apply.ID, false)
}
func (w WebsiteSSLService) AutoRenewSSL(id uint) error {
return w.obtainSSL(id, true)
}
func (w WebsiteSSLService) obtainSSL(id uint, autoRenew bool) error {
var (
err error
websiteSSL *model.WebsiteSSL
@@ -254,7 +287,7 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
resource certificate.Resource
)
websiteSSL, err = websiteSSLRepo.GetFirst(repo.WithByID(apply.ID))
websiteSSL, err = websiteSSLRepo.GetFirst(repo.WithByID(id))
if err != nil {
return err
}
@@ -306,17 +339,16 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
}
go func() {
logFile, _ := os.OpenFile(path.Join(global.Dir.SSLLogDir, fmt.Sprintf("%s-ssl-%d.log", websiteSSL.PrimaryDomain, websiteSSL.ID)), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, constant.FilePerm)
defer logFile.Close()
logger := log.New(logFile, "", log.LstdFlags)
legoLogger.Logger = logger
if !apply.DisableLog {
startMsg := i18n.GetMsgWithMap("ApplySSLStart", map[string]interface{}{"domain": strings.Join(domains, ","), "type": i18n.GetMsgByKey(websiteSSL.Provider)})
if websiteSSL.Provider == constant.DNSAccount {
startMsg = startMsg + i18n.GetMsgWithMap("DNSAccountName", map[string]interface{}{"name": dnsAccount.Name, "type": dnsAccount.Type})
}
logger.Println(startMsg)
logFile, logger := newWebsiteSSLLogger(websiteSSL, autoRenew)
if logFile != nil {
defer logFile.Close()
}
legoLogger.Logger = logger
startMsg := i18n.GetMsgWithMap("ApplySSLStart", map[string]interface{}{"domain": strings.Join(domains, ","), "type": i18n.GetMsgByKey(websiteSSL.Provider)})
if websiteSSL.Provider == constant.DNSAccount {
startMsg = startMsg + i18n.GetMsgWithMap("DNSAccountName", map[string]interface{}{"name": dnsAccount.Name, "type": dnsAccount.Type})
}
logger.Println(startMsg)
if websiteSSL.Provider != constant.DnsManual {
privateKey, err := ssl.GetPrivateKeyByType(websiteSSL.KeyType, websiteSSL.PrivateKey)
if err != nil {
@@ -361,7 +393,7 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
websiteSSL.Organization = cert.Issuer.Organization[0]
}
websiteSSL.Status = constant.SSLReady
printSSLLog(logger, "ApplySSLSuccess", map[string]interface{}{"domain": strings.Join(domains, ",")}, apply.DisableLog)
printSSLLog(logger, "ApplySSLSuccess", map[string]interface{}{"domain": strings.Join(domains, ",")})
saveCertificateFile(websiteSSL, logger)
if websiteSSL.ExecShell {
@@ -369,12 +401,12 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
if websiteSSL.PushDir {
workDir = websiteSSL.Dir
}
printSSLLog(logger, "ExecShellStart", nil, apply.DisableLog)
printSSLLog(logger, "ExecShellStart", nil)
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(30*time.Minute), cmd.WithLogger(logger), cmd.WithWorkDir(workDir))
if err = cmdMgr.RunBashC(websiteSSL.Shell); err != nil {
printSSLLog(logger, "ErrExecShell", map[string]interface{}{"err": err.Error()}, apply.DisableLog)
printSSLLog(logger, "ErrExecShell", map[string]interface{}{"err": err.Error()})
} else {
printSSLLog(logger, "ExecShellSuccess", nil, apply.DisableLog)
printSSLLog(logger, "ExecShellSuccess", nil)
}
}
@@ -386,9 +418,9 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
websites, _ := websiteRepo.GetBy(websiteRepo.WithWebsiteSSLID(websiteSSL.ID))
if len(websites) > 0 {
for _, website := range websites {
printSSLLog(logger, "ApplyWebSiteSSLLog", map[string]interface{}{"name": website.PrimaryDomain}, apply.DisableLog)
printSSLLog(logger, "ApplyWebSiteSSLLog", map[string]interface{}{"name": website.PrimaryDomain})
if err := createPemFile(website, *websiteSSL); err != nil {
printSSLLog(logger, "ErrUpdateWebsiteSSL", map[string]interface{}{"name": website.PrimaryDomain, "err": err.Error()}, apply.DisableLog)
printSSLLog(logger, "ErrUpdateWebsiteSSL", map[string]interface{}{"name": website.PrimaryDomain, "err": err.Error()})
}
}
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
@@ -396,19 +428,19 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
return
}
if err := opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {
printSSLLog(logger, "ErrSSLApply", nil, apply.DisableLog)
printSSLLog(logger, "ErrSSLApply", nil)
return
}
printSSLLog(logger, "ApplyWebSiteSSLSuccess", nil, apply.DisableLog)
printSSLLog(logger, "ApplyWebSiteSSLSuccess", nil)
}
reloadSystemSSL(websiteSSL, logger)
if websiteSSL.PushNode {
printSSLLog(logger, "StartPushSSLToNode", nil, apply.DisableLog)
printSSLLog(logger, "StartPushSSLToNode", nil)
if err = xpack.PushSSLToNode(websiteSSL); err != nil {
printSSLLog(logger, "PushSSLToNodeFailed", map[string]interface{}{"err": err.Error()}, apply.DisableLog)
printSSLLog(logger, "PushSSLToNodeFailed", map[string]interface{}{"err": err.Error()})
return
}
printSSLLog(logger, "PushSSLToNodeSuccess", nil, apply.DisableLog)
printSSLLog(logger, "PushSSLToNodeSuccess", nil)
}
}()
+13
View File
@@ -127,6 +127,19 @@ func CheckResourceTaskIsExecuting(operate, scope string, resourceID uint) bool {
return task.ID != ""
}
func CheckScopeTaskIsExecuting(scope string, resourceID uint) error {
taskRepo := repo.NewITaskRepo()
task, _ := taskRepo.GetFirst(
taskRepo.WithByStatus(constant.StatusExecuting),
taskRepo.WithResourceID(resourceID),
repo.WithByType(scope),
)
if task.ID != "" {
return buserr.New("TaskIsExecuting")
}
return nil
}
func NewTask(name, operate, taskScope, taskID string, resourceID uint) (*Task, error) {
if taskID == "" {
taskID = uuid.New().String()
+1
View File
@@ -25,4 +25,5 @@ const (
DingTalk = "dingTalk"
FeiShu = "feiShu"
Custom = "custom"
Bark = "bark"
)
+1 -1
View File
@@ -41,7 +41,7 @@ func Run() {
if _, err := global.Cron.AddJob("@daily", job.NewWebsiteJob()); err != nil {
global.LOG.Errorf("can not add website corn job: %s", err.Error())
}
if _, err := global.Cron.AddJob("@daily", job.NewSSLJob()); err != nil {
if _, err := global.Cron.AddJob("0 */6 * * *", job.NewSSLJob()); err != nil {
global.LOG.Errorf("can not add ssl corn job: %s", err.Error())
}
minuteRand, err := rand.Int(rand.Reader, big.NewInt(60))
+1 -4
View File
@@ -46,10 +46,7 @@ func (ssl *ssl) Run() {
continue
}
} else {
if err := sslService.ObtainSSL(request.WebsiteSSLApply{
ID: s.ID,
DisableLog: true,
}); err != nil {
if err := sslService.AutoRenewSSL(s.ID); err != nil {
global.LOG.Errorf("Failed to update the SSL certificate for the [%s] domain , err:%s", s.PrimaryDomain, err.Error())
continue
}
+50 -48
View File
@@ -9,18 +9,18 @@ require (
github.com/aws/aws-sdk-go v1.55.0
github.com/compose-spec/compose-go/v2 v2.9.1
github.com/creack/pty v1.1.24
github.com/docker/cli v29.2.0+incompatible
github.com/docker/cli v29.2.1+incompatible
github.com/docker/compose/v2 v2.40.3
github.com/docker/docker v28.5.2+incompatible
github.com/docker/go-connections v0.6.0
github.com/fsnotify/fsnotify v1.9.0
github.com/gin-gonic/gin v1.10.0
github.com/glebarez/sqlite v1.11.0
github.com/go-acme/lego/v4 v4.30.1
github.com/go-acme/lego/v4 v4.33.0
github.com/go-gormigrate/gormigrate/v2 v2.1.2
github.com/go-playground/validator/v10 v10.26.0
github.com/go-redis/redis v6.15.9+incompatible
github.com/go-resty/resty/v2 v2.17.0
github.com/go-resty/resty/v2 v2.17.1
github.com/go-sql-driver/mysql v1.9.3
github.com/goh-chunlin/go-onedrive v1.1.1
github.com/google/uuid v1.6.0
@@ -29,9 +29,9 @@ require (
github.com/jinzhu/copier v0.4.0
github.com/jinzhu/gorm v1.9.16
github.com/joho/godotenv v1.5.1
github.com/klauspost/compress v1.18.2
github.com/klauspost/compress v1.18.4
github.com/mholt/archiver/v4 v4.0.0-alpha.8
github.com/miekg/dns v1.1.69
github.com/miekg/dns v1.1.72
github.com/minio/minio-go/v7 v7.0.74
github.com/nicksnyder/go-i18n/v2 v2.4.0
github.com/opencontainers/image-spec v1.1.1
@@ -50,13 +50,13 @@ require (
github.com/tencentyun/cos-go-sdk-v5 v0.7.54
github.com/tomasen/fcgi_client v0.0.0-20180423082037-2bb3d819fd19
github.com/upyun/go-sdk v2.1.0+incompatible
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.40.0
golang.org/x/text v0.32.0
golang.org/x/crypto v0.48.0
golang.org/x/net v0.51.0
golang.org/x/oauth2 v0.35.0
golang.org/x/sys v0.41.0
golang.org/x/text v0.35.0
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38
gopkg.in/ini.v1 v1.67.0
gopkg.in/ini.v1 v1.67.1
gopkg.in/yaml.v3 v3.0.1
gorm.io/gorm v1.30.0
)
@@ -64,32 +64,32 @@ require (
require (
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/BurntSushi/toml v1.6.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 // indirect
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13 // indirect
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.15 // indirect
github.com/alibabacloud-go/debug v1.0.1 // indirect
github.com/alibabacloud-go/tea v1.3.14 // indirect
github.com/alibabacloud-go/tea v1.4.0 // indirect
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 // indirect
github.com/aliyun/credentials-go v1.4.7 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/aws/aws-sdk-go-v2 v1.41.0 // indirect
github.com/aws/aws-sdk-go-v2/config v1.32.5 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.19.5 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 // indirect
github.com/aws/aws-sdk-go-v2 v1.41.1 // indirect
github.com/aws/aws-sdk-go-v2/config v1.32.8 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.19.8 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 // indirect
github.com/aws/aws-sdk-go-v2/service/route53 v1.62.0 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.0.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.30.7 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 // indirect
github.com/aws/aws-sdk-go-v2/service/route53 v1.62.1 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.30.9 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.14 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 // indirect
github.com/aws/smithy-go v1.24.0 // indirect
github.com/baidubce/bce-sdk-go v0.9.254 // indirect
github.com/baidubce/bce-sdk-go v0.9.260 // indirect
github.com/bodgit/plumbing v1.2.0 // indirect
github.com/bodgit/sevenzip v1.3.0 // indirect
github.com/bodgit/windows v1.0.0 // indirect
@@ -129,16 +129,16 @@ require (
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/glebarez/go-sqlite v1.22.0 // indirect
github.com/go-acme/alidns-20150109/v4 v4.7.0 // indirect
github.com/go-acme/esa-20240910/v2 v2.40.3 // indirect
github.com/go-acme/tencentclouddnspod v1.1.25 // indirect
github.com/go-acme/esa-20240910/v2 v2.48.0 // indirect
github.com/go-acme/tencentclouddnspod v1.3.24 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/goccy/go-yaml v1.9.8 // indirect
github.com/gofrs/flock v0.13.0 // indirect
@@ -146,7 +146,7 @@ require (
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/go-querystring v1.2.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect
github.com/h2non/filetype v1.1.1 // indirect
@@ -154,8 +154,9 @@ require (
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-version v1.8.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.180 // indirect
github.com/in-toto/in-toto-golang v0.9.0 // indirect
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.187 // indirect
github.com/in-toto/attestation v1.1.2 // indirect
github.com/in-toto/in-toto-golang v0.10.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
@@ -177,13 +178,13 @@ require (
github.com/minio/md5-simd v1.1.2 // indirect
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/buildkit v0.27.0 // indirect
github.com/moby/buildkit v0.28.1 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/go-archive v0.2.0 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/moby/api v1.52.0 // indirect
github.com/moby/moby/client v0.2.1 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/patternmatcher v0.6.1 // indirect
github.com/moby/sys/atomicwriter v0.1.0 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/signal v0.7.1 // indirect
@@ -215,14 +216,15 @@ require (
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.9.1 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.10.0 // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
github.com/sigstore/sigstore v1.10.0 // indirect
github.com/sigstore/sigstore-go v1.1.4-0.20251124094504-b5fe07a5a7d7 // indirect
github.com/sigstore/sigstore v1.10.4 // indirect
github.com/sigstore/sigstore-go v1.1.4 // indirect
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.12 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.48 // indirect
github.com/therootcompany/xz v1.0.1 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/tklauser/go-sysconf v0.3.16 // indirect
@@ -235,7 +237,7 @@ require (
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/ulikunitz/xz v0.5.15 // indirect
github.com/volcengine/volc-sdk-golang v1.0.230 // indirect
github.com/volcengine/volc-sdk-golang v1.0.237 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.mongodb.org/mongo-driver v1.17.6 // indirect
@@ -257,15 +259,15 @@ require (
go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/exp v0.0.0-20250911091902-df9299821621 // indirect
golang.org/x/mod v0.31.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/term v0.38.0 // indirect
golang.org/x/mod v0.33.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/term v0.40.0 // indirect
golang.org/x/time v0.14.0 // indirect
golang.org/x/tools v0.39.0 // indirect
golang.org/x/tools v0.42.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20251103181224-f26f9409b101 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 // indirect
google.golang.org/grpc v1.77.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // indirect
google.golang.org/grpc v1.79.3 // indirect
google.golang.org/protobuf v1.36.11 // indirect
modernc.org/fileutil v1.3.3 // indirect
modernc.org/libc v1.66.1 // indirect
+206 -204
View File
@@ -30,6 +30,8 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
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=
cyphar.com/go-pathrs v0.2.1 h1:9nx1vOgwVvX1mNBWDu93+vaceedpbsDqo+XuBGL40b8=
cyphar.com/go-pathrs v0.2.1/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.1.1 h1:YpjwWWlNmGIDyXOn8zLzqiD+9TyIlPhGFG96P39uBpw=
filippo.io/edwards25519 v1.1.1/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
@@ -38,8 +40,8 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
@@ -50,6 +52,8 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA
github.com/Microsoft/hcsshim v0.14.0-rc.1 h1:qAPXKwGOkVn8LlqgBN8GS0bxZ83hOJpcjxzmlQKxKsQ=
github.com/Microsoft/hcsshim v0.14.0-rc.1/go.mod h1:hTKFGbnDtQb1wHiOWv4v0eN+7boSWAHyK/tNAaYZL0c=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
@@ -77,8 +81,10 @@ github.com/alibabacloud-go/darabonba-encode-util v0.0.2 h1:1uJGrbsGEVqWcWxrS9MyC
github.com/alibabacloud-go/darabonba-encode-util v0.0.2/go.mod h1:JiW9higWHYXm7F4PKuMgEUETNZasrDM6vqVr/Can7H8=
github.com/alibabacloud-go/darabonba-map v0.0.2 h1:qvPnGB4+dJbJIxOOfawxzF3hzMnIpjmafa0qOTp6udc=
github.com/alibabacloud-go/darabonba-map v0.0.2/go.mod h1:28AJaX8FOE/ym8OUFWga+MtEzBunJwQGceGQlvaPGPc=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13 h1:Q00FU3H94Ts0ZIHDmY+fYGgB7dV9D/YX6FGsgorQPgw=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13/go.mod h1:lxFGfobinVsQ49ntjpgWghXmIF0/Sm4+wvBJ1h5RtaE=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.14/go.mod h1:lxFGfobinVsQ49ntjpgWghXmIF0/Sm4+wvBJ1h5RtaE=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.15 h1:Mubp9hXZMTPWZK+WxrR+kKOVFp4Q/PDZrIIM7ByXI9Y=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.15/go.mod h1:lxFGfobinVsQ49ntjpgWghXmIF0/Sm4+wvBJ1h5RtaE=
github.com/alibabacloud-go/darabonba-signature-util v0.0.7 h1:UzCnKvsjPFzApvODDNEYqBHMFt1w98wC7FOo0InLyxg=
github.com/alibabacloud-go/darabonba-signature-util v0.0.7/go.mod h1:oUzCYV2fcCH797xKdL6BDH8ADIHlzrtKVjeRtunBNTQ=
github.com/alibabacloud-go/darabonba-string v1.0.2 h1:E714wms5ibdzCqGeYJ9JCFywE5nDyvIXIIQbZVFkkqo=
@@ -100,8 +106,8 @@ github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy
github.com/alibabacloud-go/tea v1.1.20/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk=
github.com/alibabacloud-go/tea v1.3.13/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg=
github.com/alibabacloud-go/tea v1.3.14 h1:/Uzj5ZCFPpbPR+Bs7jfzsyXkYIVsi5TOIuQNOWwc/9c=
github.com/alibabacloud-go/tea v1.3.14/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg=
github.com/alibabacloud-go/tea v1.4.0 h1:MSKhu/kWLPX7mplWMngki8nNt+CyUZ+kfkzaR5VpMhA=
github.com/alibabacloud-go/tea v1.4.0/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg=
github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=
github.com/alibabacloud-go/tea-utils/v2 v2.0.5/go.mod h1:dL6vbUT35E4F4bFTHL845eUloqaerYBYPsdWR2/jhe4=
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 h1:WDx5qW3Xa5ZgJ1c8NfqJkF6w+AU5wB8835UdhPr6Ax0=
@@ -114,9 +120,8 @@ github.com/aliyun/credentials-go v1.3.6/go.mod h1:1LxUuX7L5YrZUWzBrRyk0SwSdH4OmP
github.com/aliyun/credentials-go v1.4.5/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
github.com/aliyun/credentials-go v1.4.7 h1:T17dLqEtPUFvjDRRb5giVvLh6dFT8IcNFJJb7MeyCxw=
github.com/aliyun/credentials-go v1.4.7/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc=
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=
github.com/anchore/go-struct-converter v0.1.0 h1:2rDRssAl6mgKBSLNiVCMADgZRhoqtw9dedlWa0OhD30=
github.com/anchore/go-struct-converter v0.1.0/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
@@ -126,51 +131,55 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV
github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/aws/aws-sdk-go v1.55.0 h1:hVALKPjXz33kP1R9nTyJpUK7qF59dO2mleQxUW9mCVE=
github.com/aws/aws-sdk-go v1.55.0/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
github.com/aws/aws-sdk-go-v2 v1.41.0 h1:tNvqh1s+v0vFYdA1xq0aOJH+Y5cRyZ5upu6roPgPKd4=
github.com/aws/aws-sdk-go-v2 v1.41.0/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0=
github.com/aws/aws-sdk-go-v2/config v1.32.5 h1:pz3duhAfUgnxbtVhIK39PGF/AHYyrzGEyRD9Og0QrE8=
github.com/aws/aws-sdk-go-v2/config v1.32.5/go.mod h1:xmDjzSUs/d0BB7ClzYPAZMmgQdrodNjPPhd6bGASwoE=
github.com/aws/aws-sdk-go-v2/credentials v1.19.5 h1:xMo63RlqP3ZZydpJDMBsH9uJ10hgHYfQFIk1cHDXrR4=
github.com/aws/aws-sdk-go-v2/credentials v1.19.5/go.mod h1:hhbH6oRcou+LpXfA/0vPElh/e0M3aFeOblE1sssAAEk=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 h1:80+uETIWS1BqjnN9uJ0dBUaETh+P1XwFy5vwHwK5r9k=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16/go.mod h1:wOOsYuxYuB/7FlnVtzeBYRcjSRtQpAW0hCP7tIULMwo=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 h1:rgGwPzb82iBYSvHMHXc8h9mRoOUBZIGFgKb9qniaZZc=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16/go.mod h1:L/UxsGeKpGoIj6DxfhOWHWQ/kGKcd4I1VncE4++IyKA=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 h1:1jtGzuV7c82xnqOVfx2F0xmJcOw5374L7N6juGW6x6U=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16/go.mod h1:M2E5OQf+XLe+SZGmmpaI2yy+J326aFf6/+54PoxSANc=
github.com/aws/aws-sdk-go-v2 v1.41.1 h1:ABlyEARCDLN034NhxlRUSZr4l71mh+T5KAeGh6cerhU=
github.com/aws/aws-sdk-go-v2 v1.41.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0=
github.com/aws/aws-sdk-go-v2/config v1.32.8 h1:iu+64gwDKEoKnyTQskSku72dAwggKI5sV6rNvgSMpMs=
github.com/aws/aws-sdk-go-v2/config v1.32.8/go.mod h1:MI2XvA+qDi3i9AJxX1E2fu730syEBzp/jnXrjxuHwgI=
github.com/aws/aws-sdk-go-v2/credentials v1.19.8 h1:Jp2JYH1lRT3KhX4mshHPvVYsR5qqRec3hGvEarNYoR0=
github.com/aws/aws-sdk-go-v2/credentials v1.19.8/go.mod h1:fZG9tuvyVfxknv1rKibIz3DobRaFw1Poe8IKtXB3XYY=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 h1:I0GyV8wiYrP8XpA70g1HBcQO1JlQxCMTW9npl5UbDHY=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17/go.mod h1:tyw7BOl5bBe/oqvoIeECFJjMdzXoa/dfVz3QQ5lgHGA=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 h1:xOLELNKGp2vsiteLsvLPwxC+mYmO6OZ8PYgiuPJzF8U=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17/go.mod h1:5M5CI3D12dNOtH3/mk6minaRwI2/37ifCURZISxA/IQ=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 h1:WWLqlh79iO48yLkj1v3ISRNiv+3KdQoZ6JWyfcsyQik=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17/go.mod h1:EhG22vHRrvF8oXSTYStZhJc1aUgKtnJe+aOiFEV90cM=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 h1:oHjJHeUy0ImIV0bsrX0X91GkV5nJAyv1l1CC9lnO0TI=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16/go.mod h1:iRSNGgOYmiYwSCXxXaKb9HfOEj40+oTKn8pTxMlYkRM=
github.com/aws/aws-sdk-go-v2/service/route53 v1.62.0 h1:80pDB3Tpmb2RCSZORrK9/3iQxsd+w6vSzVqpT1FGiwE=
github.com/aws/aws-sdk-go-v2/service/route53 v1.62.0/go.mod h1:6EZUGGNLPLh5Unt30uEoA+KQcByERfXIkax9qrc80nA=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.4 h1:HpI7aMmJ+mm1wkSHIA2t5EaFFv5EFYXePW30p1EIrbQ=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.4/go.mod h1:C5RdGMYGlfM0gYq/tifqgn4EbyX99V15P2V3R+VHbQU=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.7 h1:eYnlt6QxnFINKzwxP5/Ucs1vkG7VT3Iezmvfgc2waUw=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.7/go.mod h1:+fWt2UHSb4kS7Pu8y+BMBvJF0EWx+4H0hzNwtDNRTrg=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 h1:AHDr0DaHIAo8c9t1emrzAlVDFp+iMMKnPdYy6XO4MCE=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12/go.mod h1:GQ73XawFFiWxyWXMHWfhiomvP3tXtdNar/fi8z18sx0=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 h1:SciGFVNZ4mHdm7gpD1dgZYnCuVdX1s+lFTg4+4DOy70=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.5/go.mod h1:iW40X4QBmUxdP+fZNOpfmkdMZqsovezbAeO+Ubiv2pk=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 h1:RuNSMoozM8oXlgLG/n6WLaFGoea7/CddrCfIiSA+xdY=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17/go.mod h1:F2xxQ9TZz5gDWsclCtPQscGpP0VUOc8RqgFM3vDENmU=
github.com/aws/aws-sdk-go-v2/service/route53 v1.62.1 h1:1jIdwWOulae7bBLIgB36OZ0DINACb1wxM6wdGlx4eHE=
github.com/aws/aws-sdk-go-v2/service/route53 v1.62.1/go.mod h1:tE2zGlMIlxWv+7Otap7ctRp3qeKqtnja7DZguj3Vu/Y=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 h1:VrhDvQib/i0lxvr3zqlUwLwJP4fpmpyD9wYG1vfSu+Y=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.5/go.mod h1:k029+U8SY30/3/ras4G/Fnv/b88N4mAfliNn08Dem4M=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.9 h1:v6EiMvhEYBoHABfbGB4alOYmCIrcgyPPiBE1wZAEbqk=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.9/go.mod h1:yifAsgBxgJWn3ggx70A3urX2AN49Y5sJTD1UQFlfqBw=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.14 h1:0jbJeuEHlwKJ9PfXtpSFc4MF+WIWORdhN1n30ITZGFM=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.14/go.mod h1:sTGThjphYE4Ohw8vJiRStAcu3rbjtXRsdNB0TvZ5wwo=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 h1:5fFjR/ToSOzB2OQ/XqWpZBmNvmP/pJ1jOWYlFDJTjRQ=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.6/go.mod h1:qgFDZQSD/Kys7nJnVqYlWKnh0SSdMjAi0uSwON4wgYQ=
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk=
github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
github.com/baidubce/bce-sdk-go v0.9.254 h1:A7GtBOt7z2lnV7fqlZPZefhcBFg7z6iliUAhEOiIhoE=
github.com/baidubce/bce-sdk-go v0.9.254/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
github.com/baidubce/bce-sdk-go v0.9.260 h1:1v1+2GTP+NGK3L24rJ+bnoiTaDaIy2YoaUM+ot2GTcw=
github.com/baidubce/bce-sdk-go v0.9.260/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/bodgit/plumbing v1.2.0 h1:gg4haxoKphLjml+tgnecR4yLBV5zo4HAZGCtAh3xCzM=
github.com/bodgit/plumbing v1.2.0/go.mod h1:b9TeRi7Hvc6Y05rjm8VML3+47n4XTZPtQ/5ghqic2n8=
github.com/bodgit/sevenzip v1.3.0 h1:1ljgELgtHqvgIp8W8kgeEGHIWP4ch3xGI8uOBZgLVKY=
@@ -204,6 +213,8 @@ github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5P
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
@@ -213,21 +224,16 @@ github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnht
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
github.com/compose-spec/compose-go/v2 v2.9.0 h1:UHSv/QHlo6QJtrT4igF1rdORgIUhDo1gWuyJUoiNNIM=
github.com/compose-spec/compose-go/v2 v2.9.0/go.mod h1:Oky9AZGTRB4E+0VbTPZTUu4Kp+oEMMuwZXZtPPVT1iE=
github.com/compose-spec/compose-go/v2 v2.9.1 h1:8UwI+ujNU+9Ffkf/YgAm/qM9/eU7Jn8nHzWG721W4rs=
github.com/compose-spec/compose-go/v2 v2.9.1/go.mod h1:Oky9AZGTRB4E+0VbTPZTUu4Kp+oEMMuwZXZtPPVT1iE=
github.com/connesc/cipherio v0.2.1 h1:FGtpTPMbKNNWByNrr9aEBtaJtXjqOzkIXNYJp6OEycw=
github.com/connesc/cipherio v0.2.1/go.mod h1:ukY0MWJDFnJEbXMQtOcn2VmTpRfzcTz4OoVrWGGJZcA=
github.com/containerd/cgroups/v3 v3.1.0 h1:azxYVj+91ZgSnIBp2eI3k9y2iYQSR/ZQIgh9vKO+HSY=
github.com/containerd/cgroups/v3 v3.1.0/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
github.com/containerd/cgroups/v3 v3.1.2 h1:OSosXMtkhI6Qove637tg1XgK4q+DhR0mX8Wi8EhrHa4=
github.com/containerd/cgroups/v3 v3.1.2/go.mod h1:PKZ2AcWmSBsY/tJUVhtS/rluX0b1uq1GmPO1ElCmbOw=
github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc=
github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/containerd/containerd/api v1.10.0 h1:5n0oHYVBwN4VhoX9fFykCV9dF1/BvAXeg2F8W6UYq1o=
github.com/containerd/containerd/api v1.10.0/go.mod h1:NBm1OAk8ZL+LG8R0ceObGxT5hbUYj7CzTmR3xh0DlMM=
github.com/containerd/containerd/v2 v2.2.0 h1:K7TqcXy+LnFmZaui2DgHsnp2gAHhVNWYaHlx7HXfys8=
github.com/containerd/containerd/v2 v2.2.0/go.mod h1:YCMjKjA4ZA7egdHNi3/93bJR1+2oniYlnS+c0N62HdE=
github.com/containerd/containerd/v2 v2.2.1 h1:TpyxcY4AL5A+07dxETevunVS5zxqzuq7ZqJXknM11yk=
github.com/containerd/containerd/v2 v2.2.1/go.mod h1:NR70yW1iDxe84F2iFWbR9xfAN0N2F0NcjTi1OVth4nU=
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
@@ -240,19 +246,15 @@ github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY
github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/nydus-snapshotter v0.15.2 h1:qsHI4M+Wwrf6Jr4eBqhNx8qh+YU0dSiJ+WPmcLFWNcg=
github.com/containerd/nydus-snapshotter v0.15.2/go.mod h1:FfwH2KBkNYoisK/e+KsmNr7xTU53DmnavQHMFOcXwfM=
github.com/containerd/nydus-snapshotter v0.15.4 h1:l59kGRVMtwMLDLh322HsWhEsBCkRKMkGWYV5vBeLYCE=
github.com/containerd/nydus-snapshotter v0.15.10 h1:hphjuKOqSHLGznNJiAvmsOWkdu4qFXjf4DzGrWSuIsM=
github.com/containerd/nydus-snapshotter v0.15.11 h1:YTdF4rsjFRsfyaIhnWVUSLz8FqJwOyRZ5FhvFjHh7Uc=
github.com/containerd/nydus-snapshotter v0.15.11/go.mod h1:EWRd/QJ0b6UKHAqYgiV5gHlqLC2qq5cQiSlXEdVovrA=
github.com/containerd/platforms v1.0.0-rc.2 h1:0SPgaNZPVWGEi4grZdV8VRYQn78y+nm6acgLGv/QzE4=
github.com/containerd/platforms v1.0.0-rc.2/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4=
github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y=
github.com/containerd/plugin v1.0.0/go.mod h1:hQfJe5nmWfImiqT1q8Si3jLv3ynMUIBB47bQ+KexvO8=
github.com/containerd/stargz-snapshotter v0.16.3 h1:zbQMm8dRuPHEOD4OqAYGajJJUwCeUzt4j7w9Iaw58u4=
github.com/containerd/stargz-snapshotter v0.17.0 h1:djNS4KU8ztFhLdEDZ1bsfzOiYuVHT6TgSU5qwRk+cNc=
github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRccTampEyKpjpOnS3CyiV1Ebr8=
github.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU=
github.com/containerd/stargz-snapshotter/estargz v0.17.0 h1:+TyQIsR/zSFI1Rm31EQBwpAA1ovYgIKHy7kctL3sLcE=
github.com/containerd/stargz-snapshotter v0.18.2 h1:Ev/sxfQUjwzJQ9eqy3XzttcQ3osMIqkQgMYlcET+10M=
github.com/containerd/stargz-snapshotter/estargz v0.18.2 h1:yXkZFYIzz3eoLwlTUZKz2iQ4MrckBxJjkmD16ynUTrw=
github.com/containerd/stargz-snapshotter/estargz v0.18.2/go.mod h1:XyVU5tcJ3PRpkA9XS2T5us6Eg35yM0214Y+wvrZTBrY=
github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ=
github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
@@ -264,6 +266,10 @@ github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6N
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 h1:uX1JmpONuD549D73r6cgnxyUu18Zb7yHAy5AYU0Pm4Q=
github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw=
github.com/cyphar/filepath-securejoin v0.6.0 h1:BtGB77njd6SVO6VztOHfPxKitJvd/VPT+OFBFMOi1Is=
github.com/cyphar/filepath-securejoin v0.6.0/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
@@ -272,36 +278,26 @@ github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xb
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73 h1:OGNva6WhsKst5OZf7eZOklDztV3hwtTHovdrLHV+MsA=
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 h1:ge14PCmCvPjpMQMIAH7uKg0lrtNSOdpYsRXlwk3QbaE=
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc=
github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 h1:lxmTCgmHE1GUYL7P0MlNa00M67axePTq+9nBSGddR8I=
github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7/go.mod h1:GvWntX9qiTlOud0WkQ6ewFm0LPy5JUR1Xo0Ngbd1w6Y=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/docker/buildx v0.29.1 h1:58hxM5Z4mnNje3G5NKfULT9xCr8ooM8XFtlfUK9bKaA=
github.com/docker/buildx v0.29.1/go.mod h1:J4EFv6oxlPiV1MjO0VyJx2u5tLM7ImDEl9zyB8d4wPI=
github.com/docker/buildx v0.30.1 h1:3vthfaTQOLt5QfN2nl7rKuPLUvx69nL5ZikFIXp//c8=
github.com/docker/buildx v0.30.1/go.mod h1:8nwT0V6UNYNo9rXq6WO/BQd9KrJ0JYcY/QX6x0y1Oro=
github.com/docker/buildx v0.31.1 h1:zbvbrb9nxBNVV8nnI33f2F+4aAZBA1gY+AmeBFflMqY=
github.com/docker/buildx v0.31.1/go.mod h1:SD+jYLnt3S4SXqohVtV+8z+dihnOgwMJ8t+bLQvsaCk=
github.com/docker/cli v28.5.1+incompatible h1:ESutzBALAD6qyCLqbQSEf1a/U8Ybms5agw59yGVc+yY=
github.com/docker/cli v28.5.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v29.2.0+incompatible h1:9oBd9+YM7rxjZLfyMGxjraKBKE4/nVyvVfN4qNl9XRM=
github.com/docker/cli v29.2.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/compose/v2 v2.40.2 h1:h2bDBJkOuqmj93XvT2oI0ArPQonE0lGtWiILXdiXvbA=
github.com/docker/compose/v2 v2.40.2/go.mod h1:CbSJpKGw20LInVsPjglZ8z7Squ3OBQOD7Ux5nkjGfIU=
github.com/docker/cli v29.2.1+incompatible h1:n3Jt0QVCN65eiVBoUTZQM9mcQICCJt3akW4pKAbKdJg=
github.com/docker/cli v29.2.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/compose/v2 v2.40.3 h1:XeYkQu1svDtyfZPv5nTwFryQ25ZJMkIlc4pz9HalMPI=
github.com/docker/compose/v2 v2.40.3/go.mod h1:iNY1tvoHTyN3C3QHCuWAgj3OjR2T6mGkk/qxfbBF/4M=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v28.5.1+incompatible h1:Bm8DchhSD2J6PsFzxC35TZo4TLGR2PdW/E69rU45NhM=
github.com/docker/docker v28.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY=
github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q=
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
@@ -334,8 +330,6 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
@@ -368,12 +362,12 @@ github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GM
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
github.com/go-acme/alidns-20150109/v4 v4.7.0 h1:PqJ/wR0JTpL4v0Owu1uM7bPQ1Yww0eQLAuuSdLjjQaQ=
github.com/go-acme/alidns-20150109/v4 v4.7.0/go.mod h1:btQvB6xZoN6ykKB74cPhiR+uvhrEE2AFVXm6RDmCHm0=
github.com/go-acme/esa-20240910/v2 v2.40.3 h1:xXOMRex148wKEHbv7Izn73/HdAxSmz5GOaF4HdnqN+M=
github.com/go-acme/esa-20240910/v2 v2.40.3/go.mod h1:ZYdN9EN9ikn26SNapxCVjZ65pHT/1qm4fzuJ7QGVX6g=
github.com/go-acme/lego/v4 v4.30.1 h1:tmb6U0lvy8Mc3lQbqKwTat7oAhE8FUYNJ3D0gSg6pJU=
github.com/go-acme/lego/v4 v4.30.1/go.mod h1:V7m/Ip+EeFkjOe028+zeH+SwWtESxw1LHelwMIfAjm4=
github.com/go-acme/tencentclouddnspod v1.1.25 h1:7H3ZKshkaHzCXfRpAHVB5nvxeDDl2XLeNZfrNHiZj/s=
github.com/go-acme/tencentclouddnspod v1.1.25/go.mod h1:XXfzp0AYV7UAUsHKT6R0KAUJFhqAUXmWGF07Elpa5cE=
github.com/go-acme/esa-20240910/v2 v2.48.0 h1:muSDyhjDTejxUGe3FTthCPCqRaEdYY9cG3N/AmU52Lc=
github.com/go-acme/esa-20240910/v2 v2.48.0/go.mod h1:shPb6hzc1rJL15IJBY8HQ4GZk4E8RC52+52twutEwIg=
github.com/go-acme/lego/v4 v4.33.0 h1:2KrRKieG+VczT4zvVXKAY7Tp/S3XLvh/QImofBALRAM=
github.com/go-acme/lego/v4 v4.33.0/go.mod h1:lI2fZNdgeM/ymf9xQ9YKbgZm6MeDuf91UrohMQE4DhI=
github.com/go-acme/tencentclouddnspod v1.3.24 h1:uCSiOW1EJttcnOON+MVVyVDJguFL/Q4NIGkq1CrT9p8=
github.com/go-acme/tencentclouddnspod v1.3.24/go.mod h1:RKcB2wSoZncjBA0OEFj59s1ko1XDy+ZsAtk+9uMxUF0=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -381,8 +375,8 @@ github.com/go-gormigrate/gormigrate/v2 v2.1.2 h1:F/d1hpHbRAvKezziV2CC5KUE82cVe9z
github.com/go-gormigrate/gormigrate/v2 v2.1.2/go.mod h1:9nHVX6z3FCMCQPA7PThGcA55t22yKQfK/Dnsf5i7hUo=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs=
@@ -399,6 +393,48 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-openapi/analysis v0.24.1 h1:Xp+7Yn/KOnVWYG8d+hPksOYnCYImE3TieBa7rBOesYM=
github.com/go-openapi/analysis v0.24.1/go.mod h1:dU+qxX7QGU1rl7IYhBC8bIfmWQdX4Buoea4TGtxXY84=
github.com/go-openapi/errors v0.22.6 h1:eDxcf89O8odEnohIXwEjY1IB4ph5vmbUsBMsFNwXWPo=
github.com/go-openapi/errors v0.22.6/go.mod h1:z9S8ASTUqx7+CP1Q8dD8ewGH/1JWFFLX/2PmAYNQLgk=
github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4=
github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80=
github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8=
github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4=
github.com/go-openapi/loads v0.23.2 h1:rJXAcP7g1+lWyBHC7iTY+WAF0rprtM+pm8Jxv1uQJp4=
github.com/go-openapi/loads v0.23.2/go.mod h1:IEVw1GfRt/P2Pplkelxzj9BYFajiWOtY2nHZNj4UnWY=
github.com/go-openapi/runtime v0.29.2 h1:UmwSGWNmWQqKm1c2MGgXVpC2FTGwPDQeUsBMufc5Yj0=
github.com/go-openapi/runtime v0.29.2/go.mod h1:biq5kJXRJKBJxTDJXAa00DOTa/anflQPhT0/wmjuy+0=
github.com/go-openapi/spec v0.22.3 h1:qRSmj6Smz2rEBxMnLRBMeBWxbbOvuOoElvSvObIgwQc=
github.com/go-openapi/spec v0.22.3/go.mod h1:iIImLODL2loCh3Vnox8TY2YWYJZjMAKYyLH2Mu8lOZs=
github.com/go-openapi/strfmt v0.25.0 h1:7R0RX7mbKLa9EYCTHRcCuIPcaqlyQiWNPTXwClK0saQ=
github.com/go-openapi/strfmt v0.25.0/go.mod h1:nNXct7OzbwrMY9+5tLX4I21pzcmE6ccMGXl3jFdPfn8=
github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochMU=
github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ=
github.com/go-openapi/swag/cmdutils v0.25.4 h1:8rYhB5n6WawR192/BfUu2iVlxqVR9aRgGJP6WaBoW+4=
github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0=
github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4=
github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU=
github.com/go-openapi/swag/fileutils v0.25.4 h1:2oI0XNW5y6UWZTC7vAxC8hmsK/tOkWXHJQH4lKjqw+Y=
github.com/go-openapi/swag/fileutils v0.25.4/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk=
github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI=
github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag=
github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA=
github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY=
github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s=
github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE=
github.com/go-openapi/swag/mangling v0.25.4 h1:2b9kBJk9JvPgxr36V23FxJLdwBrpijI26Bx5JH4Hp48=
github.com/go-openapi/swag/mangling v0.25.4/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg=
github.com/go-openapi/swag/netutils v0.25.4 h1:Gqe6K71bGRb3ZQLusdI8p/y1KLgV4M/k+/HzVSqT8H0=
github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg=
github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8=
github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0=
github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw=
github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE=
github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw=
github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc=
github.com/go-openapi/validate v0.25.1 h1:sSACUI6Jcnbo5IWqbYHgjibrhhmt3vR6lCzKZnmAgBw=
github.com/go-openapi/validate v0.25.1/go.mod h1:RMVyVFYte0gbSTaZ0N4KmTn6u/kClvAFp+mAVfS/DQc=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
@@ -413,17 +449,15 @@ github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-resty/resty/v2 v2.17.0 h1:pW9DeXcaL4Rrym4EZ8v7L19zZiIlWPg5YXAcVmt+gN0=
github.com/go-resty/resty/v2 v2.17.0/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA=
github.com/go-resty/resty/v2 v2.17.1 h1:x3aMpHK1YM9e4va/TMDRlusDDoZiQ+ViDu/WpA6xTM4=
github.com/go-resty/resty/v2 v2.17.1/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
@@ -481,6 +515,8 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/certificate-transparency-go v1.3.2 h1:9ahSNZF2o7SYMaKaXhAumVEzXB2QaayzII9C8rv7v+A=
github.com/google/certificate-transparency-go v1.3.2/go.mod h1:H5FpMUaGa5Ab2+KCYsxg6sELw3Flkl7pGZzWdBoYLXs=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -496,9 +532,12 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-containerregistry v0.20.7 h1:24VGNpS0IwrOZ2ms2P1QE3Xa5X9p4phx0aUgzYzHW6I=
github.com/google/go-containerregistry v0.20.7/go.mod h1:Lx5LCZQjLH1QBaMPeGwsME9biPeo1lPx6lbGj/UmzgM=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0=
github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -509,9 +548,8 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY=
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
@@ -536,8 +574,6 @@ github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w7
github.com/grafov/m3u8 v0.12.0/go.mod h1:nqzOkfBiZJENr52zTVd/Dcl03yzphIMbJqkXGu+u080=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4=
github.com/h2non/filetype v1.1.1 h1:xvOwnXKAckvtLWsN398qS9QhlxlnVXBjXBydK2/UFB4=
@@ -578,27 +614,23 @@ github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg
github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.180 h1:uia+R3K1izQRGpxTV+bS4q3/ueMW7ProAMWqM6OlqOU=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.180/go.mod h1:M+yna96Fx9o5GbIUnF3OvVvQGjgfVSyeJbV9Yb1z/wI=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.187 h1:J+U6+eUjIsBhefolFdZW5hQNJbkMj+7msxZrv56Cg2g=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.187/go.mod h1:M+yna96Fx9o5GbIUnF3OvVvQGjgfVSyeJbV9Yb1z/wI=
github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU=
github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo=
github.com/in-toto/attestation v1.1.2 h1:MBFn6lsMq6dptQZJBhalXTcWMb/aJy3V+GX3VYj/V1E=
github.com/in-toto/attestation v1.1.2/go.mod h1:gYFddHMZj3DiQ0b62ltNi1Vj5rC879bTmBbrv9CRHpM=
github.com/in-toto/in-toto-golang v0.10.0 h1:+s2eZQSK3WmWfYV85qXVSBfqgawi/5L02MaqA4o/tpM=
github.com/in-toto/in-toto-golang v0.10.0/go.mod h1:wjT4RiyFlLWCmLUJjwB8oZcjaq7HA390aMJcD3xXgmg=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs=
github.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc=
@@ -643,10 +675,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co=
github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0=
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
@@ -682,8 +712,6 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
@@ -692,7 +720,6 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
@@ -709,10 +736,8 @@ github.com/mholt/archiver/v4 v4.0.0-alpha.8/go.mod h1:5f7FUYGXdJWUjESffJaYR4R60V
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
github.com/miekg/dns v1.1.69 h1:Kb7Y/1Jo+SG+a2GtfoFUfDkG//csdRPwRLkCsxDG9Sc=
github.com/miekg/dns v1.1.69/go.mod h1:7OyjD9nEba5OkqQ/hB4fy3PIoxafSZJtducccIelz3g=
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
@@ -730,26 +755,22 @@ github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/buildkit v0.25.1 h1:j7IlVkeNbEo+ZLoxdudYCHpmTsbwKvhgc/6UJ/mY/o8=
github.com/moby/buildkit v0.25.1/go.mod h1:phM8sdqnvgK2y1dPDnbwI6veUCXHOZ6KFSl6E164tkc=
github.com/moby/buildkit v0.26.3 h1:D+ruZVAk/3ipRq5XRxBH9/DIFpRjSlTtMbghT5gQP9g=
github.com/moby/buildkit v0.26.3/go.mod h1:4T4wJzQS4kYWIfFRjsbJry4QoxDBjK+UGOEOs1izL7w=
github.com/moby/buildkit v0.27.0 h1:1gtNaMcVE0XXCZrybC32L79A7Ga1JeB7V3PfpCt1bDc=
github.com/moby/buildkit v0.27.0/go.mod h1:4STUkNc5t1nf03HS+01UmI2X6FdfOI3XaKt9QNoTsms=
github.com/moby/buildkit v0.28.1 h1:Tq6H6gOMU2JyEQ5rA0pa7Ey3VGNR3qpw90liSIpMQoo=
github.com/moby/buildkit v0.28.1/go.mod h1:xO6wb9VBXszkIBxaGTLXc1rQORVQFIJRt3GSX7KzCFc=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=
github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo=
github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8=
github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/moby/api v1.52.0 h1:00BtlJY4MXkkt84WhUZPRqt5TvPbgig2FZvTbe3igYg=
github.com/moby/moby/api v1.52.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=
github.com/moby/moby/client v0.2.1 h1:1Grh1552mvv6i+sYOdY+xKKVTvzJegcVMhuXocyDz/k=
github.com/moby/moby/client v0.2.1/go.mod h1:O+/tw5d4a1Ha/ZA/tPxIZJapJRUS6LNZ1wiVRxYHyUE=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U=
github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/policy-helpers v0.0.0-20260211190020-824747bfdd3c h1:hRUo0Ir9PEaa0PQCgg8WvGku0sgmTo/NgnCzMb83iII=
github.com/moby/policy-helpers v0.0.0-20260211190020-824747bfdd3c/go.mod h1:2P1OGoTVIrybI4M7yhpkDpqiwOnI3yR+HnNhEyo8ovs=
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
@@ -773,8 +794,6 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/morikuni/aec v1.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ=
github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw=
github.com/mozillazg/go-httpheader v0.2.1 h1:geV7TrjbL8KXSyvghnFm+NyTux/hxwueTSrwhe88TQQ=
@@ -812,6 +831,8 @@ github.com/nwaples/rardecode/v2 v2.2.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsR
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
@@ -827,18 +848,18 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww=
github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.3.0 h1:YZupQUdctfhpZy3TM39nN9Ika5CBWT5diQ8ibYCRkxg=
github.com/opencontainers/selinux v1.12.0 h1:6n5JV4Cf+4y0KNXW48TLj5DwfXpvWlxXplUkdTrmPb8=
github.com/opencontainers/selinux v1.12.0/go.mod h1:BTPX+bjVbWGXw7ZZWUbdENt8w0htPSrlgOOysQaU62U=
github.com/opencontainers/runtime-spec v1.3.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.13.1 h1:A8nNeceYngH9Ow++M+VVEwJVpdFmrlxsN22F+ISDCJE=
github.com/opencontainers/selinux v1.13.1/go.mod h1:S10WXZ/osk2kWOYKy1x2f/eXF5ZHJoUs8UU/2caNRbg=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE=
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
github.com/ovh/go-ovh v1.9.0 h1:6K8VoL3BYjVV3In9tPJUdT7qMx9h0GExN9EXx1r2kKE=
github.com/ovh/go-ovh v1.9.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c=
github.com/package-url/packageurl-go v0.1.1 h1:KTRE0bK3sKbFKAk3yy63DpeskU7Cvs/x/Da5l+RtzyU=
github.com/package-url/packageurl-go v0.1.1/go.mod h1:uQd4a7Rh3ZsVg5j0lNyAfyxIeGde9yrlhjF78GzeW0c=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
@@ -894,16 +915,14 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
github.com/qiniu/go-sdk/v7 v7.26.4 h1:D/k6cPbhjKvSx9hVBQSh97GuPu1ZtM3OLPCclSpusjo=
github.com/qiniu/go-sdk/v7 v7.26.4/go.mod h1:ri7fGwbio0pRDFr8EK5TUpx0DbnpIMJ2bMSDxGWfCbk=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
@@ -926,28 +945,34 @@ github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWR
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/secure-systems-lab/go-securesystemslib v0.6.0 h1:T65atpAVCJQK14UA57LMdZGpHi4QYSH/9FZyNGqMYIA=
github.com/secure-systems-lab/go-securesystemslib v0.6.0/go.mod h1:8Mtpo9JKks/qhPG4HGZ2LGMvrPbzuxwfz/f/zLfEWkk=
github.com/secure-systems-lab/go-securesystemslib v0.9.1 h1:nZZaNz4DiERIQguNy0cL5qTdn9lR8XKHf4RUyG1Sx3g=
github.com/secure-systems-lab/go-securesystemslib v0.9.1/go.mod h1:np53YzT0zXGMv6x4iEWc9Z59uR+x+ndLwCLqPYpLXVU=
github.com/secure-systems-lab/go-securesystemslib v0.10.0 h1:l+H5ErcW0PAehBNrBxoGv1jjNpGYdZ9RcheFkB2WI14=
github.com/secure-systems-lab/go-securesystemslib v0.10.0/go.mod h1:MRKONWmRoFzPNQ9USRF9i1mc7MvAVvF1LlW8X5VWDvk=
github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI=
github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE=
github.com/shirou/gopsutil/v4 v4.25.11 h1:X53gB7muL9Gnwwo2evPSE+SfOrltMoR6V3xJAXZILTY=
github.com/shirou/gopsutil/v4 v4.25.11/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sigstore/sigstore v1.10.0 h1:lQrmdzqlR8p9SCfWIpFoGUqdXEzJSZT2X+lTXOMPaQI=
github.com/sigstore/sigstore v1.10.0/go.mod h1:Ygq+L/y9Bm3YnjpJTlQrOk/gXyrjkpn3/AEJpmk1n9Y=
github.com/sigstore/sigstore-go v1.1.4-0.20251124094504-b5fe07a5a7d7 h1:94NLPmq4bxvdmslzcG670IOkrlS98CGpmob8cjpFHuI=
github.com/sigstore/sigstore-go v1.1.4-0.20251124094504-b5fe07a5a7d7/go.mod h1:4r/PNX0G7uzkLpc3PSdYs5E2k4bWEJNXTK6kwAyw9TM=
github.com/sigstore/protobuf-specs v0.5.0 h1:F8YTI65xOHw70NrvPwJ5PhAzsvTnuJMGLkA4FIkofAY=
github.com/sigstore/protobuf-specs v0.5.0/go.mod h1:+gXR+38nIa2oEupqDdzg4qSBT0Os+sP7oYv6alWewWc=
github.com/sigstore/rekor v1.5.0 h1:rL7SghHd5HLCtsCrxw0yQg+NczGvM75EjSPPWuGjaiQ=
github.com/sigstore/rekor v1.5.0/go.mod h1:D7JoVCUkxwQOpPDNYeu+CE8zeBC18Y5uDo6tF8s2rcQ=
github.com/sigstore/rekor-tiles/v2 v2.0.1 h1:1Wfz15oSRNGF5Dzb0lWn5W8+lfO50ork4PGIfEKjZeo=
github.com/sigstore/rekor-tiles/v2 v2.0.1/go.mod h1:Pjsbhzj5hc3MKY8FfVTYHBUHQEnP0ozC4huatu4x7OU=
github.com/sigstore/sigstore v1.10.4 h1:ytOmxMgLdcUed3w1SbbZOgcxqwMG61lh1TmZLN+WeZE=
github.com/sigstore/sigstore v1.10.4/go.mod h1:tDiyrdOref3q6qJxm2G+JHghqfmvifB7hw+EReAfnbI=
github.com/sigstore/sigstore-go v1.1.4 h1:wTTsgCHOfqiEzVyBYA6mDczGtBkN7cM8mPpjJj5QvMg=
github.com/sigstore/sigstore-go v1.1.4/go.mod h1:2U/mQOT9cjjxrtIUeKDVhL+sHBKsnWddn8URlswdBsg=
github.com/sigstore/timestamp-authority/v2 v2.0.3 h1:sRyYNtdED/ttLCMdaYnwpf0zre1A9chvjTnCmWWxN8Y=
github.com/sigstore/timestamp-authority/v2 v2.0.3/go.mod h1:mDaHxkt3HmZYoIlwYj4QWo0RUr7VjYU52aVO5f5Qb3I=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
@@ -955,15 +980,12 @@ github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJ
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spdx/tools-golang v0.5.5 h1:61c0KLfAcNqAjlg6UNMdkwpMernhw3zVRwDZ2x9XOmk=
github.com/spdx/tools-golang v0.5.5/go.mod h1:MVIsXx8ZZzaRWNQpUDhC4Dud34edUYJYecciXgrw5vE=
github.com/spdx/tools-golang v0.5.7 h1:+sWcKGnhwp3vLdMqPcLdA6QK679vd86cK9hQWH3AwCg=
github.com/spdx/tools-golang v0.5.7/go.mod h1:jg7w0LOpoNAw6OxKEzCoqPC2GCTj45LyTlVmXubDsYw=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
@@ -979,6 +1001,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -989,21 +1012,23 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.25/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.12 h1:/ABtv4x4FSGxGW0d6Sc88iQn6Up2LalWKwt/Tj7Dtz8=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.12/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.24/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.48 h1:bCs+z6dxRaHWm/C1D/XkSOcCZ0+W2+/6HmIXjpAj+fY=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.48/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0=
github.com/tencentyun/cos-go-sdk-v5 v0.7.54 h1:FRamEhNBbSeggyYfWfzFejTLftgbICocSYFk4PKTSV4=
github.com/tencentyun/cos-go-sdk-v5 v0.7.54/go.mod h1:UN+VdbCl1hg+kKi5RXqZgaP+Boqfmk+D04GRc4XFk70=
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c=
github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=
github.com/theupdateframework/go-tuf v0.7.0 h1:CqbQFrWo1ae3/I0UCblSbczevCCbS31Qvs5LdxRWqRI=
github.com/theupdateframework/go-tuf/v2 v2.4.1 h1:K6ewW064rKZCPkRo1W/CTbTtm/+IB4+coG1iNURAGCw=
github.com/theupdateframework/go-tuf/v2 v2.4.1/go.mod h1:Nex2enPVYDFCklrnbTzl3OVwD7fgIAj0J5++z/rvCj8=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
@@ -1015,8 +1040,6 @@ github.com/tomasen/fcgi_client v0.0.0-20180423082037-2bb3d819fd19 h1:ZCmSnT6CLGh
github.com/tomasen/fcgi_client v0.0.0-20180423082037-2bb3d819fd19/go.mod h1:SXTY+QvI+KTTKXQdg0zZ7nx0u94QWh8ZAwBQYsW9cqk=
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 h1:r0p7fK56l8WPequOaR3i9LBqfPtEdXIQbUTzT55iqT4=
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323/go.mod h1:3Iuxbr0P7D3zUzBMAZB+ois3h/et0shEz0qApgHYGpY=
github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f h1:MoxeMfHAe5Qj/ySSBfL8A7l1V+hxuluj8owsIEEZipI=
github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f/go.mod h1:BKdcez7BiVtBvIcef90ZPc6ebqIWr4JWD7+EvLm6J98=
github.com/tonistiigi/fsutil v0.0.0-20251211185533-a2aa163d723f h1:Z4NEQ86qFl1mHuCu9gwcE+EYCwDKfXAYXZbdIXyxmEA=
github.com/tonistiigi/fsutil v0.0.0-20251211185533-a2aa163d723f/go.mod h1:BKdcez7BiVtBvIcef90ZPc6ebqIWr4JWD7+EvLm6J98=
github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 h1:2f304B10LaZdB8kkVEaoXvAMVan2tl9AiK4G0odjQtE=
@@ -1025,6 +1048,10 @@ github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk=
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab h1:H6aJ0yKQ0gF49Qb2z5hI1UHxSQt4JMyxebFR15KnApw=
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab/go.mod h1:ulncasL3N9uLrVann0m+CDlJKWsIAP34MPcOJF6VRvc=
github.com/transparency-dev/formats v0.0.0-20251017110053-404c0d5b696c h1:5a2XDQ2LiAUV+/RjckMyq9sXudfrPSuCY4FuPC1NyAw=
github.com/transparency-dev/formats v0.0.0-20251017110053-404c0d5b696c/go.mod h1:g85IafeFJZLxlzZCDRu4JLpfS7HKzR+Hw9qRh3bVzDI=
github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG81+twTK4=
github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
@@ -1036,11 +1063,10 @@ github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0o
github.com/upyun/go-sdk v2.1.0+incompatible h1:OdjXghQ/TVetWV16Pz3C1/SUpjhGBVPr+cLiqZLLyq0=
github.com/upyun/go-sdk v2.1.0+incompatible/go.mod h1:eu3F5Uz4b9ZE5bE5QsCL6mgSNWRwfj0zpJ9J626HEqs=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo=
github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
github.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4=
github.com/volcengine/volc-sdk-golang v1.0.230 h1:84/MOF0zUPtAHt3e1+MsFq5qrnQRC+e3XzTUwIOzZxw=
github.com/volcengine/volc-sdk-golang v1.0.230/go.mod h1:zHJlaqiMbIB+0mcrsZPTwOb3FB7S/0MCfqlnO8R7hlM=
github.com/vbatts/tar-split v0.12.2/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
github.com/volcengine/volc-sdk-golang v1.0.237 h1:hpLKiS2BwDcSBtZWSz034foCbd0h3FrHTKlUMqHIdc4=
github.com/volcengine/volc-sdk-golang v1.0.237/go.mod h1:zHJlaqiMbIB+0mcrsZPTwOb3FB7S/0MCfqlnO8R7hlM=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
@@ -1062,7 +1088,6 @@ go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQc
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0=
go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk=
go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo=
go.mongodb.org/mongo-driver v1.17.6 h1:87JUG1wZfWsr6rIz3ZmpH90rL5tea7O3IHuSwHUpsss=
go.mongodb.org/mongo-driver v1.17.6/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
@@ -1076,35 +1101,22 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 h1:0tY123n7CdWMem7MOVdKOt0YfshufLCwfE5Bob+hQuM=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0/go.mod h1:CosX/aS4eHnG9D7nESYpV753l4j9q5j3SL/PUYd2lR8=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.63.0 h1:2pn7OzMewmYRiNtv1doZnLo3gONcnMHlFnmOR8Vgt+8=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.63.0/go.mod h1:rjbQTDEPQymPE0YnRQp9/NuPwwtL0sesz/fnqRW/v84=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 h1:QcFwRrZLc82r8wODjvyCbP7Ifp3UANaBSmhDSFjnqSc=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0/go.mod h1:CXIWhUomyWBG/oY2/r/kLp6K/cmx9e/7DLpBuuGdLCA=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 h1:vl9obrcoWVKp/lwl8tRE33853I8Xru9HFbw/skNeLs8=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0/go.mod h1:GAXRxmLJcVM3u22IjTg74zWBrRCKq8BnOqUVLodpcpw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4=
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=
@@ -1114,8 +1126,6 @@ go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
@@ -1129,9 +1139,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
go4.org v0.0.0-20200411211856-f5505b9728dd h1:BNJlw5kRTzdmyfh5U8F93HA2OwkP7ZGwA51eJ/0wKOU=
@@ -1169,8 +1178,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1184,8 +1193,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
golang.org/x/exp v0.0.0-20250911091902-df9299821621 h1:2id6c1/gto0kaHYyrixvknJ8tUK/Qs5IsmBtrc+FtgU=
golang.org/x/exp v0.0.0-20250911091902-df9299821621/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
@@ -1216,10 +1223,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1276,8 +1281,8 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1285,8 +1290,8 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1303,8 +1308,8 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1369,9 +1374,7 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220406163625-3f8b81556e12/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1384,8 +1387,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.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.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=
@@ -1400,8 +1403,8 @@ golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1419,8 +1422,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1482,8 +1485,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1552,12 +1555,10 @@ google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxH
google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 h1:Q3nlH8iSQSRUwOskjbcSMcF2jiYMNiQYZ0c2KEJLKKU=
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38/go.mod h1:xBI+tzfqGGN2JBeSebfKXFSdBpWVQ7sLW40PTupVRm4=
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4=
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo=
google.golang.org/genproto/googleapis/api v0.0.0-20251103181224-f26f9409b101 h1:vk5TfqZHNn0obhPIYeS+cxIFKFQgser/M2jnI+9c6MM=
google.golang.org/genproto/googleapis/api v0.0.0-20251103181224-f26f9409b101/go.mod h1:E17fc4PDhkr22dE3RgnH2hEubUaky6ZwW4VhANxyspg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 h1:Wgl1rcDNThT+Zn47YyCXOXyX/COgMTIdhJ717F0l4xk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M=
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -1575,8 +1576,8 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -1591,8 +1592,6 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
@@ -1606,8 +1605,9 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.1 h1:tVBILHy0R6e4wkYOn3XmiITt/hEVH4TFMYvAX2Ytz6k=
gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
@@ -1663,6 +1663,8 @@ modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+17 -1
View File
@@ -2,6 +2,8 @@ ErrInvalidParams: 'Bad request: {{ .detail }}'
ErrTokenParse: 'Token error: {{ .detail }}'
ErrInitialPassword: 'Original password is incorrect'
ErrInternalServer: 'Internal server error: {{ .detail }}'
ErrFileAINotEnabled: 'File-management AI summary requires an enabled AI account in File Management. Content-only search still works when AI is off.'
ErrFileAISearchBadPattern: 'Invalid file search pattern (regex or whole word): {{ .detail }}'
ErrWgetRemoteFailed: 'Remote download failed or URL is unreachable: {{ .detail }}'
ErrWgetInvalidContentType: 'Remote URL did not return a file (possibly an error page), {{ .detail }}'
ErrRecordExist: 'Record already exists'
@@ -42,6 +44,7 @@ Decrypt: "Decrypt"
#agent
ErrAgentAccountBound: 'Account is bound to an agent and cannot be deleted'
ErrTerminalAIAccountInUse: 'This model account is currently used by Terminal AI. Switch the account in Terminal AI settings or disable Terminal AI and try again.'
ErrAgentAccountUnavailable: 'Account connection unavailable: {{ .err }}'
ErrAgentProviderNotSupported: 'Unsupported agent provider'
ErrAgentAccountRequired: 'Select an agent account first'
@@ -49,13 +52,18 @@ ErrAgentAccountNotVerified: 'Agent account not verified'
ErrAgentAccountModelsRequired: 'Please configure at least one model'
ErrAgentAccountSingleInitialModel: 'Only one initial model can be configured during account creation'
ErrAgentModelNotInAccount: 'Model is not configured in the selected account'
ErrAgentFallbackModelDuplicate: 'Fallback models cannot be duplicated'
ErrAgentFallbackModelPrimary: 'Fallback model cannot match the primary model'
ErrAgentModelInUse: 'A bound agent is still using this model: {{ .name }}'
ErrAgentBaseURLRequired: 'Base URL is required'
ErrAgentApiKeyRequired: 'API key is required'
ErrAgentComposeRequired: 'Compose content is required'
ErrAgentIDRequired: 'Agent ID is required'
ErrAgentAccountIDRequired: 'Account ID is required'
ErrAgentLimitReached: 'Community Edition supports up to {{ .max }} AI agents. Upgrade to Professional Edition for unlimited agents'
ErrAgentLimitReached: 'Community Edition supports up to {{ .max }} AI agents. Upgrade to Pro Edition for unlimited agents'
ErrAgentWebsiteBound: 'This agent is already bound to a website'
ErrAgentWebsiteTypeUnsupported: 'Only proxy or static websites can be bound'
ErrAgentWebsiteInUse: 'This website is already bound to another agent'
#backup
Localhost: 'Local'
@@ -135,6 +143,13 @@ ErrLinkPathNotFound: 'The target path does not exist!'
ErrFileIsExist: 'The file or folder already exists!'
ErrFileUpload: 'Upload failed: {{ .name }} {{ .detail }}'
ErrFileDownloadDir: 'Directory download is not supported'
ErrFileSharePath: 'Invalid file path or target is not a file'
ErrFileShareInvalid: 'Invalid or expired share link'
ErrFileShareExpired: 'Share link has expired'
ErrFileSharePassword: 'Incorrect share password'
ErrFileShareExhausted: 'Download limit for this share has been reached'
ErrFileSharePasswordPolicy: 'Password must be at least 4 characters'
ErrFileShareRateLimit: 'Too many requests, please try again later'
ErrCmdNotFound: 'Command not found: {{ .name }}'
ErrSourcePathNotFound: 'Source directory does not exist'
ErrFavoriteExist: 'Path already in favorites'
@@ -573,3 +588,4 @@ TerminalAIBlockedRiskyCommand: 'blocked risky command: {{ .command }}'
TerminalAIThinking: 'AI is thinking...'
TerminalAIReadyToExecute: 'Thinking complete, press Enter to execute (duration: {{ .duration }}, tokens: {{ .tokens }})'
TerminalAIRequestFailed: 'AI request failed: {{ .err }}'
FileAISearchEmptyDir: 'No files or directories found under this path (or all entries were filtered).'
+22 -6
View File
@@ -2,6 +2,8 @@ ErrInvalidParams: 'Solicitud inválida: {{ .detail }}'
ErrTokenParse: 'Token inválido: {{ .detail }}'
ErrInitialPassword: 'Contraseña original incorrecta'
ErrInternalServer: 'Error interno: {{ .detail }}'
ErrFileAINotEnabled: 'La búsqueda con IA de archivos está desactivada o sin cuenta. En Gestión de archivos, use Cuenta de IA, active y elija una cuenta.'
ErrFileAISearchBadPattern: 'Patrón de búsqueda no válido (regex u otra opción): {{ .detail }}'
ErrWgetRemoteFailed: 'La descarga remota falló o la URL no es accesible: {{ .detail }}'
ErrWgetInvalidContentType: 'La URL remota no devolvió un archivo (posiblemente una página de error), {{ .detail }}'
ErrRecordExist: 'Registro ya existe'
@@ -37,6 +39,7 @@ ErrGroupIsDefault: 'Grupo predeterminado, no se puede eliminar'
ErrGroupIsInWebsiteUse: 'El grupo está siendo usado por otro sitio web y no se puede eliminar.'
Decrypt: 'Descifrar'
ErrAgentAccountBound: 'Cuenta vinculada a agente'
ErrTerminalAIAccountInUse: 'Esta cuenta de modelo esta siendo usada por Terminal AI. Cambia la cuenta en la configuracion de Terminal AI o desactiva Terminal AI y vuelve a intentarlo.'
ErrAgentAccountUnavailable: 'Conexión de cuenta no disponible: {{ .err }}'
ErrAgentProviderNotSupported: 'Proveedor de agente no soportado'
ErrAgentAccountRequired: 'Elige una cuenta de agente primero'
@@ -44,13 +47,18 @@ ErrAgentAccountNotVerified: 'Cuenta de agente no verificada'
ErrAgentAccountModelsRequired: 'Configure al menos un modelo'
ErrAgentAccountSingleInitialModel: 'Solo se puede configurar un modelo inicial al crear la cuenta'
ErrAgentModelNotInAccount: 'El modelo no está configurado en la cuenta seleccionada'
ErrAgentFallbackModelDuplicate: 'Los modelos de respaldo no pueden duplicarse'
ErrAgentFallbackModelPrimary: 'El modelo de respaldo no puede ser igual al modelo principal'
ErrAgentModelInUse: 'Un agente vinculado todavía usa este modelo: {{ .name }}'
ErrAgentBaseURLRequired: 'URL base requerida'
ErrAgentApiKeyRequired: 'Clave API requerida'
ErrAgentComposeRequired: 'Contenido compose requerido'
ErrAgentIDRequired: 'ID de agente requerido'
ErrAgentAccountIDRequired: 'ID de cuenta requerido'
ErrAgentLimitReached: 'La edición Community admite hasta {{ .max }} agentes de IA. Actualice a la edición Professional para eliminar el límite'
ErrAgentLimitReached: 'La edición Community admite hasta {{ .max }} agentes de IA. Actualiza a la edición Pro para usar agentes ilimitados'
ErrAgentWebsiteBound: 'Este agente ya está vinculado a un sitio web'
ErrAgentWebsiteTypeUnsupported: 'Solo se pueden vincular sitios proxy o estáticos'
ErrAgentWebsiteInUse: 'Este sitio web ya está vinculado a otro agente'
Localhost: 'Máquina local'
ErrBackupInUsed: 'Cuenta de respaldo en uso por tarea programada'
ErrBackupCheck: 'Conexión de respaldo falló: {{ .err }}'
@@ -122,6 +130,13 @@ 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'
ErrFileSharePath: 'Ruta no válida o el destino no es un archivo'
ErrFileShareInvalid: 'Enlace de uso compartido no válido o caducado'
ErrFileShareExpired: 'El enlace de uso compartido ha caducado'
ErrFileSharePassword: 'Contraseña de uso compartido incorrecta'
ErrFileShareExhausted: 'Se alcanzó el límite de descargas de este recurso compartido'
ErrFileSharePasswordPolicy: 'La contraseña debe tener al menos 4 caracteres'
ErrFileShareRateLimit: 'Demasiadas solicitudes, inténtelo de nuevo más tarde'
ErrCmdNotFound: 'El comando {{ .name }} no existe, instálelo primero en el host'
ErrSourcePathNotFound: 'El directorio fuente no existe'
ErrFavoriteExist: 'Esta ruta ya ha sido marcada como favorita'
@@ -169,7 +184,7 @@ ErrSSLCertificateNotFound: 'El archivo de certificado no existe'
ErrSSLKeyFormat: 'Error en la verificación del archivo de clave privada'
ErrSSLCertificateFormat: 'Formato de certificado incorrecto, use formato pem'
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid o EabHmacKey no pueden estar en blanco'
ErrOpenrestyNotFound: 'El modo Http requiere tener Openresty instalado primero'
ErrOpenrestyNotFound: 'El modo HTTP requiere OpenResty'
ApplySSLStart: 'Iniciando solicitud de certificado, dominio [{{ .domain }}] método de solicitud [{{ .type }}]'
dnsAccount: 'DNS Automático'
dnsManual: 'DNS Manual'
@@ -343,9 +358,9 @@ sqlInject: 'Inyección SQL'
ErrDBNotExist: 'La base de datos no existe'
allow: 'permitir'
deny: 'denegar'
OpenrestyNotFound: 'Openresty no está instalado'
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'
OpenrestyVersionErr: 'La versión de OpenResty es demasiado antigua. Actualízalo 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]'
@@ -369,7 +384,7 @@ TaskRollback: 'Revertir'
TaskPull: 'Descargar'
TaskCommit: 'Commit'
TaskBuild: 'Construir'
TaskPush: 'Subir'
TaskPush: 'Enviar'
TaskClean: 'Limpieza'
TaskHandle: 'Ejecutar'
TaskImport: 'Importar'
@@ -379,7 +394,7 @@ App: 'Aplicación'
Runtime: 'Entorno de ejecución'
Database: 'Base de datos'
ConfigFTP: 'Crear usuario FTP {{ .name }}'
ConfigOpenresty: 'Crear archivo de configuración Openresty'
ConfigOpenresty: 'Crear archivo de configuración de OpenResty'
InstallAppSuccess: 'Aplicación {{ .name }} instalada correctamente'
ConfigRuntime: 'Configurar entorno de ejecución'
ConfigApp: 'Configurar aplicación'
@@ -515,3 +530,4 @@ TerminalAIBlockedRiskyCommand: 'Comando de riesgo bloqueado: {{ .command }}'
TerminalAIThinking: 'La IA está pensando...'
TerminalAIReadyToExecute: 'Pensamiento completado, pulsa Enter para ejecutar (duración: {{ .duration }}, token: {{ .tokens }})'
TerminalAIRequestFailed: 'La solicitud de AI ha fallado: {{ .err }}'
FileAISearchEmptyDir: 'No hay archivos ni carpetas bajo esta ruta (o todos fueron filtrados).'
+22 -6
View File
@@ -2,6 +2,8 @@ ErrInvalidParams: '不正なリクエスト: {{ .detail }}'
ErrTokenParse: 'トークンエラー: {{ .detail }}'
ErrInitialPassword: '元のパスワードが違います'
ErrInternalServer: '内部エラー: {{ .detail }}'
ErrFileAINotEnabled: 'ファイル管理の「AI アカウント」でファイル AI 検索を有効にし、アカウントを選んでください。'
ErrFileAISearchBadPattern: '検索パターンが無効です(正規表現など): {{ .detail }}'
ErrWgetRemoteFailed: 'リモートのダウンロード URL が無効か、到達できません:{{ .detail }}'
ErrWgetInvalidContentType: 'リモート URL がファイルではなくエラーページ等を返しました、{{ .detail }}'
ErrRecordExist: 'レコードが既に存在します'
@@ -37,6 +39,7 @@ ErrGroupIsDefault: 'デフォルト グループ、削除できません'
ErrGroupIsInWebsiteUse: 'グループは別の Web サイトで使用されているため、削除できません。'
Decrypt: '復号化'
ErrAgentAccountBound: 'アカウントはエージェントに紐づいています'
ErrTerminalAIAccountInUse: 'このモデルアカウントは Terminal AI で使用中です。Terminal AI の設定で別のアカウントへ切り替えるか、Terminal AI を無効にしてから再試行してください。'
ErrAgentAccountUnavailable: 'アカウント接続不可: {{ .err }}'
ErrAgentProviderNotSupported: 'エージェントプロバイダ非対応'
ErrAgentAccountRequired: 'まずエージェントアカウントを選択'
@@ -44,13 +47,18 @@ ErrAgentAccountNotVerified: 'アカウント未検証です'
ErrAgentAccountModelsRequired: '少なくとも 1 つのモデルを設定してください'
ErrAgentAccountSingleInitialModel: 'アカウント作成時に設定できる初期モデルは 1 つだけです'
ErrAgentModelNotInAccount: '選択したモデルはこのアカウントに設定されていません'
ErrAgentFallbackModelDuplicate: 'フォールバックモデルは重複できません'
ErrAgentFallbackModelPrimary: 'フォールバックモデルは主モデルと同じにできません'
ErrAgentModelInUse: 'このモデルはまだエージェントで使用中です: {{ .name }}'
ErrAgentBaseURLRequired: 'ベースURLが必要です'
ErrAgentApiKeyRequired: 'APIキーが必要です'
ErrAgentComposeRequired: 'Composeが必要です'
ErrAgentIDRequired: 'エージェントIDが必要です'
ErrAgentAccountIDRequired: 'アカウントIDが必要です'
ErrAgentLimitReached: 'Community Edition では AI エージェントを最大 {{ .max }} 件まで作成できます。Professional Edition にアップグレードすると数の制限はありません'
ErrAgentLimitReached: 'Community Edition では AI エージェントを最大 {{ .max }} 件まで作成できます。Pro Edition にアップグレードすると無制限で利用できます'
ErrAgentWebsiteBound: 'このエージェントはすでにサイトに関連付けられています'
ErrAgentWebsiteTypeUnsupported: '関連付けできるのはプロキシサイトまたは静的サイトのみです'
ErrAgentWebsiteInUse: 'このサイトはすでに別のエージェントに関連付けられています'
Localhost: 'ローカルマシン'
ErrBackupInUsed: 'バックアップアカウントがスケジュールで使用中'
ErrBackupCheck: '接続テストに失敗しました: {{ .err }}'
@@ -122,6 +130,13 @@ ErrLinkPathNotFound: 'ターゲット パスが存在しません'
ErrFileIsExist: 'ファイルまたはフォルダーは既に存在します'
ErrFileUpload: '{{ .name }} はファイル {{ .detail }} のアップロードに失敗しました'
ErrFileDownloadDir: 'ダウンロード フォルダーはサポートされていません'
ErrFileSharePath: '無効なパスか、対象がファイルではありません'
ErrFileShareInvalid: '共有リンクが無効か期限切れです'
ErrFileShareExpired: '共有リンクの有効期限が切れました'
ErrFileSharePassword: '共有パスワードが正しくありません'
ErrFileShareExhausted: 'この共有のダウンロード上限に達しました'
ErrFileSharePasswordPolicy: 'パスワードは4文字以上にしてください'
ErrFileShareRateLimit: 'アクセスが多すぎます。しばらくしてから再試行してください'
ErrCmdNotFound: '{{ .name }} コマンドが存在しません。まずこのコマンドをホストにインストールしてください'
ErrSourcePathNotFound: 'ソース ディレクトリが存在しません'
ErrFavoriteExist: 'このパスはすでにお気に入りに登録されています'
@@ -169,7 +184,7 @@ ErrSSLCertificateNotFound: '証明書ファイルが存在しません'
ErrSSLKeyFormat: '秘密鍵ファイルの検証に失敗しました'
ErrSSLCertificateFormat: '証明書ファイルの形式が正しくありません。pem 形式を使用してください'
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid または EabHmacKey は空白にできません'
ErrOpenrestyNotFound: 'Http モードでは、まず Openresty をインストールする必要があります'
ErrOpenrestyNotFound: 'HTTP モードでは OpenResty が必要です'
ApplySSLStart: '証明書の申請を開始します。ドメイン名 [{{ .domain }}] 申請方法 [{{ .type }}]'
dnsAccount: 'DNS 自動'
dnsManual: 'DNS マニュアル'
@@ -343,9 +358,9 @@ sqlInject: 'SQL インジェクション'
ErrDBNotExist: 'データベースが存在しません'
allow: '許可'
deny: '拒否'
OpenrestyNotFound: 'Openresty がインストールされていません'
OpenrestyNotFound: 'OpenResty がインストールされていません'
remoteIpIsNull: 'IPリストが空です'
OpenrestyVersionErr: 'Openrestyのバージョンが低すぎます。Openrestyを1.27.1.2-2-2-focalにアップグレードしてください'
OpenrestyVersionErr: 'OpenResty のバージョンが古すぎます。1.27.1.2-2-2-focal 以降にアップグレードしてください'
ErrFileTooLarge: 'ファイルが 1MB を超えているためアップロードできません'
TaskStart: '{{ .name }} タスクが開始されました [START]'
TaskEnd: '{{ .name }} タスクが完了しました [COMPLETED]'
@@ -369,7 +384,7 @@ TaskRollback: 'ロールバック'
TaskPull: 'プル'
TaskCommit: 'コミット'
TaskBuild: 'ビルド'
TaskPush: 'プッシュ'
TaskPush: '送信'
TaskClean: 'クリーンアップ'
TaskHandle: '実行'
TaskImport: 'インポート'
@@ -379,7 +394,7 @@ App: 'アプリケーション'
Runtime: 'ランタイム環境'
Database: 'データベース'
ConfigFTP: 'FTP ユーザー {{ .name }} を作成します'
ConfigOpenresty: 'Openresty 構成ファイルを作成する'
ConfigOpenresty: 'OpenResty の設定ファイルを作成'
InstallAppSuccess: 'アプリケーション {{ .name }} が正常にインストールされました'
ConfigRuntime: 'ランタイム環境を構成する'
ConfigApp: '構成アプリケーション'
@@ -515,3 +530,4 @@ TerminalAIBlockedRiskyCommand: '危険なコマンドをブロックしました
TerminalAIThinking: 'AI が考えています...'
TerminalAIReadyToExecute: '思考が完了しました。Enter で実行してください(所要時間 {{ .duration }}、token: {{ .tokens }})'
TerminalAIRequestFailed: 'AI リクエストに失敗しました: {{ .err }}'
FileAISearchEmptyDir: 'このパス以下に一覧できるファイルまたはフォルダーがありません(またはすべて除外されました)。'
+22 -6
View File
@@ -2,6 +2,8 @@ ErrInvalidParams: '잘못된 요청: {{ .detail }}'
ErrTokenParse: '토큰 오류: {{ .detail }}'
ErrInitialPassword: '원래 비밀번호가 틀립니다'
ErrInternalServer: '서버 오류: {{ .detail }}'
ErrFileAINotEnabled: '파일 관리에서 「AI 계정」으로 파일 AI 검색을 켜고 계정을 선택하세요.'
ErrFileAISearchBadPattern: '검색 패턴이 잘못되었습니다(정규식 등): {{ .detail }}'
ErrWgetRemoteFailed: '원격 다운로드 주소가 잘못되었거나 접근할 수 없습니다: {{ .detail }}'
ErrWgetInvalidContentType: '원격 주소가 파일이 아닌 콘텐츠를 반환했습니다 (오류 페이지일 수 있음), {{ .detail }}'
ErrRecordExist: '레코드가 이미 있습니다'
@@ -37,6 +39,7 @@ ErrGroupIsDefault: '기본 그룹, 삭제할 수 없습니다'
ErrGroupIsInWebsiteUse: '그룹이 다른 웹사이트에서 사용 중이므로 삭제할 수 없습니다.'
Decrypt: '복호화'
ErrAgentAccountBound: '계정이 에이전트에 묶여 있습니다'
ErrTerminalAIAccountInUse: '이 모델 계정은 현재 터미널 AI에서 사용 중입니다. 터미널 AI 설정에서 다른 계정으로 변경하거나 터미널 AI를 비활성화한 뒤 다시 시도하세요.'
ErrAgentAccountUnavailable: '계정 연결 불가: {{ .err }}'
ErrAgentProviderNotSupported: '지원되지 않는 에이전트 공급자'
ErrAgentAccountRequired: '먼저 에이전트 계정을 선택하세요'
@@ -44,13 +47,18 @@ ErrAgentAccountNotVerified: '에이전트 계정 미확인'
ErrAgentAccountModelsRequired: '최소 한 개의 모델을 구성해 주세요'
ErrAgentAccountSingleInitialModel: '계정 생성 시 초기 모델은 하나만 구성할 수 있습니다'
ErrAgentModelNotInAccount: '선택한 모델이 현재 계정에 구성되어 있지 않습니다'
ErrAgentFallbackModelDuplicate: '폴백 모델은 중복될 수 없습니다'
ErrAgentFallbackModelPrimary: '폴백 모델은 기본 모델과 같을 수 없습니다'
ErrAgentModelInUse: '이 모델을 아직 사용 중인 에이전트가 있습니다: {{ .name }}'
ErrAgentBaseURLRequired: '기본 URL 필요'
ErrAgentApiKeyRequired: 'API 키 필요'
ErrAgentComposeRequired: 'Compose 내용 필요'
ErrAgentIDRequired: '에이전트 ID 필요'
ErrAgentAccountIDRequired: '계정 ID 필요'
ErrAgentLimitReached: '커뮤니티 에디션에서는 AI 에이전트를 최대 {{ .max }}개까지 생성할 수 있으며, Professional 에디션으로 업그레이드하면 수량 제한이 없습니다'
ErrAgentLimitReached: '커뮤니티 에디션에서는 AI 에이전트를 최대 {{ .max }}개까지 만들 수 있습니다. Pro Edition으로 업그레이드하면 제한 없이 사용할 수 있습니다'
ErrAgentWebsiteBound: '이 에이전트는 이미 웹사이트에 연결되어 있습니다'
ErrAgentWebsiteTypeUnsupported: '프록시 또는 정적 웹사이트만 연결할 수 있습니다'
ErrAgentWebsiteInUse: '이 웹사이트는 이미 다른 에이전트에 연결되어 있습니다'
Localhost: '로컬 머신'
ErrBackupInUsed: '백업 계정이 예약에 사용 중'
ErrBackupCheck: '연결 테스트 실패: {{ .err }}'
@@ -122,6 +130,13 @@ ErrLinkPathNotFound: '대상 경로가 존재하지 않습니다'
ErrFileIsExist: '파일이나 폴더가 이미 존재합니다'
ErrFileUpload: '{{ .name }}이 파일 {{ .detail }}을 업로드하지 못했습니다.'
ErrFileDownloadDir: '다운로드 폴더가 지원되지 않습니다'
ErrFileSharePath: '잘못된 파일 경로이거나 대상이 파일이 아닙니다'
ErrFileShareInvalid: '유효하지 않거나 만료된 공유 링크입니다'
ErrFileShareExpired: '공유 링크가 만료되었습니다'
ErrFileSharePassword: '공유 비밀번호가 올바르지 않습니다'
ErrFileShareExhausted: '이 공유의 다운로드 한도에 도달했습니다'
ErrFileSharePasswordPolicy: '비밀번호는 4자 이상이어야 합니다'
ErrFileShareRateLimit: '요청이 너무 많습니다. 잠시 후 다시 시도해 주세요'
ErrCmdNotFound: '{{ .name }} 명령이 존재하지 않습니다. 먼저 호스트에 이 명령을 설치하세요'
ErrSourcePathNotFound: '소스 디렉토리가 존재하지 않습니다'
ErrFavoriteExist: '이 경로는 이미 즐겨찾기되었습니다'
@@ -169,7 +184,7 @@ ErrSSLCertificateNotFound: '인증서 파일이 존재하지 않습니다'
ErrSSLKeyFormat: '개인 키 파일 검증에 실패했습니다'
ErrSSLCertificateFormat: '인증서 파일 형식이 올바르지 않습니다. pem 형식을 사용하세요.'
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid 또는 EabHmacKey는 비워둘 수 없습니다'
ErrOpenrestyNotFound: 'Http 모드를 사용하려면 먼저 Openresty를 설치해야 합니다.'
ErrOpenrestyNotFound: 'HTTP 모드에서는 OpenResty가 필요합니다'
ApplySSLStart: '인증서 신청 시작, 도메인 이름 [{{ .domain }}] 신청 방법 [{{ .type }}]'
dnsAccount: 'DNS 자동'
dnsManual: 'DNS 매뉴얼'
@@ -343,9 +358,9 @@ sqlInject: 'SQL 주입'
ErrDBNotExist: '데이터베이스가 존재하지 않습니다'
allow: '허용하다'
deny: '거부하다'
OpenrestyNotFound: 'Openresty가 설치되지 않았습니다'
OpenrestyNotFound: 'OpenResty가 설치되어 있지 않습니다'
remoteIpIsNull: 'IP 목록이 비어 있습니다'
OpenrestyVersionErr: 'Openresty 버전이 너무 낮습니다. Openresty를 1.27.1.2-2-2-focal로 업그레이드하세요'
OpenrestyVersionErr: 'OpenResty 버전이 너무 낮습니다. 1.27.1.2-2-2-focal 이상으로 업그레이드하세요'
ErrFileTooLarge: '파일이 1MB를 초과하여 업로드할 수 없습니다'
TaskStart: '[START] {{ .name }} 작업이 시작됩니다.'
TaskEnd: '{{ .name }} 작업이 완료되었습니다 [완료]'
@@ -369,7 +384,7 @@ TaskRollback: '롤백'
TaskPull: '당기기'
TaskCommit: '커밋'
TaskBuild: '빌드'
TaskPush: '푸시'
TaskPush: '전송'
TaskClean: '정리'
TaskHandle: '실행'
TaskImport: '가져오기'
@@ -379,7 +394,7 @@ App: '애플리케이션'
Runtime: '런타임 환경'
Database: '데이터베이스'
ConfigFTP: 'FTP 사용자 {{ .name }} 생성'
ConfigOpenresty: 'Openresty 구성 파일 생성'
ConfigOpenresty: 'OpenResty 구성 파일 생성'
InstallAppSuccess: '애플리케이션 {{ .name }}이 성공적으로 설치되었습니다.'
ConfigRuntime: '런타임 환경 구성'
ConfigApp: '구성 애플리케이션'
@@ -515,3 +530,4 @@ TerminalAIBlockedRiskyCommand: '위험한 명령이 차단되었습니다: {{ .c
TerminalAIThinking: 'AI가 생각 중입니다...'
TerminalAIReadyToExecute: '생각이 완료되었습니다. Enter를 눌러 실행하세요 (소요 시간 {{ .duration }}, token: {{ .tokens }})'
TerminalAIRequestFailed: 'AI 요청 실패: {{ .err }}'
FileAISearchEmptyDir: '이 경로에 나열할 파일이나 디렉터리가 없습니다(또는 모두 필터링됨).'
+26 -10
View File
@@ -2,6 +2,8 @@ ErrInvalidParams: 'Permintaan tidak sah: {{ .detail }}'
ErrTokenParse: 'Token tidak sah: {{ .detail }}'
ErrInitialPassword: 'Kata laluan asal salah'
ErrInternalServer: 'Ralat pelayan: {{ .detail }}'
ErrFileAINotEnabled: 'Carian AI fail dimatikan atau tiada akaun. Dalam Pengurusan Fail, gunakan Akaun AI, aktifkan dan pilih akaun.'
ErrFileAISearchBadPattern: 'Corak carian tidak sah (regex dsb.): {{ .detail }}'
ErrWgetRemoteFailed: 'Muat turun jauh gagal atau URL tidak boleh diakses: {{ .detail }}'
ErrWgetInvalidContentType: 'URL jauh tidak mengembalikan fail (mungkin halaman ralat), {{ .detail }}'
ErrRecordExist: 'Rekod sudah wujud'
@@ -37,6 +39,7 @@ ErrGroupIsDefault: 'Kumpulan lalai, tidak boleh dipadamkan'
ErrGroupIsInWebsiteUse: 'Kumpulan sedang digunakan oleh tapak web lain dan tidak boleh dipadamkan.'
Decrypt: 'Dekripsi'
ErrAgentAccountBound: 'Akaun terikat kepada ejen'
ErrTerminalAIAccountInUse: 'Akaun model ini sedang digunakan oleh Terminal AI. Tukar akaun dalam tetapan Terminal AI atau nyahaktifkan Terminal AI, kemudian cuba lagi.'
ErrAgentAccountUnavailable: 'Sambungan akaun tidak tersedia: {{ .err }}'
ErrAgentProviderNotSupported: 'Penyedia ejen tidak disokong'
ErrAgentAccountRequired: 'Pilih akaun ejen terlebih dahulu'
@@ -44,13 +47,18 @@ ErrAgentAccountNotVerified: 'Akaun ejen belum disahkan'
ErrAgentAccountModelsRequired: 'Sila konfigur sekurang-kurangnya satu model'
ErrAgentAccountSingleInitialModel: 'Hanya satu model awal boleh dikonfigurasi semasa mencipta akaun'
ErrAgentModelNotInAccount: 'Model tidak dikonfigurasikan dalam akaun yang dipilih'
ErrAgentFallbackModelDuplicate: 'Model fallback tidak boleh berulang'
ErrAgentFallbackModelPrimary: 'Model fallback tidak boleh sama dengan model utama'
ErrAgentModelInUse: 'Masih ada ejen terikat yang menggunakan model ini: {{ .name }}'
ErrAgentBaseURLRequired: 'URL asas diperlukan'
ErrAgentApiKeyRequired: 'Kunci API diperlukan'
ErrAgentComposeRequired: 'Kandungan compose diperlukan'
ErrAgentIDRequired: 'ID ejen diperlukan'
ErrAgentAccountIDRequired: 'ID akaun diperlukan'
ErrAgentLimitReached: 'Edisi Community menyokong sehingga {{ .max }} ejen AI. Naik taraf ke edisi Professional untuk tiada had bilangan'
ErrAgentLimitReached: 'Edisi Community menyokong sehingga {{ .max }} ejen AI. Naik taraf ke Edisi Pro untuk penggunaan tanpa had'
ErrAgentWebsiteBound: 'Ejen ini sudah dipautkan ke laman web'
ErrAgentWebsiteTypeUnsupported: 'Hanya laman web proxy atau statik boleh dipautkan'
ErrAgentWebsiteInUse: 'Laman web ini sudah dipautkan ke ejen lain'
Localhost: 'Mesin Tempatan'
ErrBackupInUsed: 'Akaun sandaran sedang digunakan oleh tugas'
ErrBackupCheck: 'Ujian sambungan gagal: {{ .err }}'
@@ -122,6 +130,13 @@ ErrLinkPathNotFound: 'Laluan sasaran tidak wujud'
ErrFileIsExist: 'Fail atau folder sudah wujud'
ErrFileUpload: '{{ .name }} gagal memuat naik fail {{ .detail }}'
ErrFileDownloadDir: 'Folder muat turun tidak disokong'
ErrFileSharePath: 'Laluan fail tidak sah atau sasaran bukan fail'
ErrFileShareInvalid: 'Pautan kongsi tidak sah atau telah tamat tempoh'
ErrFileShareExpired: 'Pautan kongsi telah tamat tempoh'
ErrFileSharePassword: 'Kata laluan kongsi tidak betul'
ErrFileShareExhausted: 'Had muat turun untuk kongsi ini telah dicapai'
ErrFileSharePasswordPolicy: 'Kata laluan mestilah sekurang-kurangnya 4 aksara'
ErrFileShareRateLimit: 'Terlalu banyak permintaan, sila cuba lagi kemudian'
ErrCmdNotFound: 'Arahan {{ .name }} tidak wujud, sila pasang arahan ini pada hos dahulu'
ErrSourcePathNotFound: 'Direktori sumber tidak wujud'
ErrFavoriteExist: 'Laluan ini telah digemari'
@@ -169,7 +184,7 @@ ErrSSLCertificateNotFound: 'Fail sijil tidak wujud'
ErrSSLKeyFormat: 'Pengesahan fail kunci peribadi gagal'
ErrSSLCertificateFormat: 'Format fail sijil tidak betul, sila gunakan format pem'
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid atau EabHmacKey tidak boleh kosong'
ErrOpenrestyNotFound: 'Mod Http memerlukan Openresty dipasang dahulu'
ErrOpenrestyNotFound: 'Mod HTTP memerlukan OpenResty dipasang terlebih dahulu'
ApplySSLStart: 'Mula memohon sijil, nama domain [{{ .domain }}] kaedah permohonan [{{ .type }}]'
dnsAccount: 'DNS Auto'
dnsManual: 'Manual DNS'
@@ -254,8 +269,8 @@ 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'
ErrContainerNameIsNull: 'Nama kontena tidak wujud'
ErrPHPPortIsDefault: 'Порт 9000 является портом по умолчанию, пожалуйста, измените и попробуйте снова'
ErrPHPRuntimePortFailed: 'Порт {{ .name }} уже используется текущей средой выполнения, пожалуйста, измените и попробуйте снова'
ErrPHPPortIsDefault: 'Port 9000 ialah port lalai. Sila tukar dan cuba lagi'
ErrPHPRuntimePortFailed: 'Port {{ .name }} sudah digunakan oleh runtime semasa. Sila tukar dan cuba lagi'
ErrConfigNotFound: 'Fail konfigurasi tidak wujud'
ErrConfigParse: 'Format fail konfigurasi tidak betul'
ErrConfigIsNull: 'Fail konfigurasi tidak boleh kosong'
@@ -343,10 +358,10 @@ sqlInject: 'Suntikan SQL'
ErrDBNotExist: 'Pangkalan data tidak wujud'
allow: 'membenarkan'
deny: 'menafikan'
OpenrestyNotFound: 'Openresty tidak dipasang'
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'
ErrFileTooLarge: 'File is larger than 1 MB and cannot be uploaded'
OpenrestyVersionErr: 'Versi OpenResty terlalu lama. Naik taraf ke 1.27.1.2-2-2-focal'
ErrFileTooLarge: 'Fail melebihi 1 MB dan tidak boleh dimuat naik'
TaskStart: '{{ .name }} Task mula [MULA]'
TaskEnd: '{{ .name }} Task selesai [SELESAI]'
TaskFailed: 'tugas {{ .name }} gagal'
@@ -369,7 +384,7 @@ TaskRollback: 'Rollback'
TaskPull: 'Tarik'
TaskCommit: 'Komit'
TaskBuild: 'Bina'
TaskPush: 'Tolak'
TaskPush: 'Hantar'
TaskClean: 'Pembersihan'
TaskHandle: 'Laksanakan'
TaskImport: 'Import'
@@ -379,7 +394,7 @@ App: 'Aplikasi'
Runtime: 'Persekitaran masa jalan'
Database: 'Pangkalan Data'
ConfigFTP: 'Buat pengguna FTP {{ .name }}'
ConfigOpenresty: 'Buat fail konfigurasi Openresty'
ConfigOpenresty: 'Buat fail konfigurasi OpenResty'
InstallAppSuccess: 'Aplikasi {{ .name }} berjaya dipasang'
ConfigRuntime: 'Konfigurasikan persekitaran masa jalan'
ConfigApp: 'Aplikasi Konfigurasi'
@@ -436,7 +451,7 @@ 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'
SnapshotRecover: 'Pulihkan syot kilat'
RecoverDownload: 'Muat turun fail syot kilat'
Download: 'Muat turun'
RecoverDownloadAccount: 'Dapatkan syot kilat muat turun akaun sandaran {{ .name }}'
@@ -515,3 +530,4 @@ TerminalAIBlockedRiskyCommand: 'Perintah berisiko telah disekat: {{ .command }}'
TerminalAIThinking: 'AI sedang berfikir...'
TerminalAIReadyToExecute: 'Pemikiran selesai, tekan Enter untuk jalankan (tempoh: {{ .duration }}, token: {{ .tokens }})'
TerminalAIRequestFailed: 'Permintaan AI gagal: {{ .err }}'
FileAISearchEmptyDir: 'Tiada fail atau direktori di bawah laluan ini (atau semua ditapis).'
+25 -9
View File
@@ -2,6 +2,8 @@ ErrInvalidParams: 'Requisição inválida: {{ .detail }}'
ErrTokenParse: 'Erro de token: {{ .detail }}'
ErrInitialPassword: 'Senha original incorreta'
ErrInternalServer: 'Erro interno: {{ .detail }}'
ErrFileAINotEnabled: 'A pesquisa com IA de arquivos está desativada ou sem conta. Em Gerenciamento de arquivos, use Conta de IA, ative e escolha uma conta.'
ErrFileAISearchBadPattern: 'Padrão de busca inválido (regex ou opção): {{ .detail }}'
ErrWgetRemoteFailed: 'Falha no download remoto ou URL inacessível: {{ .detail }}'
ErrWgetInvalidContentType: 'A URL remota não retornou um arquivo (possivelmente uma página de erro), {{ .detail }}'
ErrRecordExist: 'Registro já existe'
@@ -37,6 +39,7 @@ 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'
ErrAgentAccountBound: 'Conta vinculada a agente'
ErrTerminalAIAccountInUse: 'Esta conta de modelo esta sendo usada pelo Terminal AI. Troque a conta nas configuracoes do Terminal AI ou desative o Terminal AI e tente novamente.'
ErrAgentAccountUnavailable: 'Conexão da conta indisponível: {{ .err }}'
ErrAgentProviderNotSupported: 'Provedor de agente não suportado'
ErrAgentAccountRequired: 'Selecione uma conta de agente primeiro'
@@ -44,13 +47,18 @@ ErrAgentAccountNotVerified: 'Conta de agente não verificada'
ErrAgentAccountModelsRequired: 'Configure pelo menos um modelo'
ErrAgentAccountSingleInitialModel: 'Apenas um modelo inicial pode ser configurado ao criar a conta'
ErrAgentModelNotInAccount: 'O modelo não está configurado na conta selecionada'
ErrAgentFallbackModelDuplicate: 'Os modelos de fallback não podem ser duplicados'
ErrAgentFallbackModelPrimary: 'O modelo de fallback não pode ser igual ao modelo principal'
ErrAgentModelInUse: 'Ainda há um agente vinculado usando este modelo: {{ .name }}'
ErrAgentBaseURLRequired: 'URL base obrigatória'
ErrAgentApiKeyRequired: 'Chave API obrigatória'
ErrAgentComposeRequired: 'Conteúdo de compose obrigatório'
ErrAgentIDRequired: 'ID do agente obrigatório'
ErrAgentAccountIDRequired: 'ID da conta obrigatório'
ErrAgentLimitReached: 'A edição Community suporta até {{ .max }} agentes de IA. Atualize para a edição Professional para remover o limite'
ErrAgentLimitReached: 'A edição Community suporta até {{ .max }} agentes de IA. Faça upgrade para a edição Pro para usar agentes ilimitados'
ErrAgentWebsiteBound: 'Este agente já está vinculado a um site'
ErrAgentWebsiteTypeUnsupported: 'Somente sites proxy ou estáticos podem ser vinculados'
ErrAgentWebsiteInUse: 'Este site já está vinculado a outro agente'
Localhost: 'Máquina Local'
ErrBackupInUsed: 'Conta de backup em uso por tarefa'
ErrBackupCheck: 'Teste de conexão falhou: {{ .err }}'
@@ -122,6 +130,13 @@ 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'
ErrFileSharePath: 'Caminho inválido ou o destino não é um arquivo'
ErrFileShareInvalid: 'Link de compartilhamento inválido ou expirado'
ErrFileShareExpired: 'O link de compartilhamento expirou'
ErrFileSharePassword: 'Senha de compartilhamento incorreta'
ErrFileShareExhausted: 'O limite de downloads deste compartilhamento foi atingido'
ErrFileSharePasswordPolicy: 'A senha deve ter pelo menos 4 caracteres'
ErrFileShareRateLimit: 'Muitas solicitações, tente novamente mais tarde'
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'
@@ -162,14 +177,14 @@ SSL: 'Certificado'
Set: 'Configurações'
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 concluída, mas a recarga do OpenResty falhou. 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'
ErrSSLKeyFormat: 'Falha na verificação do arquivo de chave privada'
ErrSSLCertificateFormat: 'O formato do arquivo de certificado está incorreto, use o formato pem'
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid ou EabHmacKey não podem estar em branco'
ErrOpenrestyNotFound: 'O modo HTTP requer que o Openresty seja instalado primeiro'
ErrOpenrestyNotFound: 'O modo HTTP requer o OpenResty instalado'
ApplySSLStart: 'Comece a solicitar um certificado, nome de domínio [{{ .domain }}] método de solicitação [{{ .type }}]'
dnsAccount: 'DNS Automático'
dnsManual: 'Manual DNS'
@@ -254,8 +269,8 @@ ErrScriptsNotFound: 'O item de configuração de scripts não foi encontrado em
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'
ErrPHPPortIsDefault: 'A porta 9000 é a porta padrão. Altere-a e tente novamente'
ErrPHPRuntimePortFailed: 'A porta {{ .name }} já está em uso neste runtime. Altere-a e tente novamente'
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'
@@ -343,9 +358,9 @@ sqlInject: 'injeção de SQL'
ErrDBNotExist: 'O banco de dados não existe'
allow: 'permitir'
deny: 'negar'
OpenrestyNotFound: 'Openresty não está instalado'
OpenrestyNotFound: 'OpenResty não está instalado'
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'
OpenrestyVersionErr: 'A versão do OpenResty é muito antiga. Atualize 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]'
@@ -369,7 +384,7 @@ TaskRollback: 'Reverter'
TaskPull: 'Puxar'
TaskCommit: 'Commit'
TaskBuild: 'Construir'
TaskPush: 'Empurrar'
TaskPush: 'Enviar'
TaskClean: 'Limpeza'
TaskHandle: 'Executar'
TaskImport: 'Importar'
@@ -379,7 +394,7 @@ App: 'Aplicativo'
Runtime: 'Ambiente de tempo de execução'
Database: 'Banco de dados'
ConfigFTP: 'Criar usuário FTP {{ .name }}'
ConfigOpenresty: 'Criar arquivo de configuração Openresty'
ConfigOpenresty: 'Criar arquivo de configuração do OpenResty'
InstallAppSuccess: 'Aplicativo {{ .name }} instalado com sucesso'
ConfigRuntime: 'Configurar o ambiente de tempo de execução'
ConfigApp: 'Aplicativo de configuração'
@@ -515,3 +530,4 @@ TerminalAIBlockedRiskyCommand: 'Comando de risco bloqueado: {{ .command }}'
TerminalAIThinking: 'A IA está pensando...'
TerminalAIReadyToExecute: 'Pensamento concluído, pressione Enter para executar (duração: {{ .duration }}, token: {{ .tokens }})'
TerminalAIRequestFailed: 'Falha na solicitação de AI: {{ .err }}'
FileAISearchEmptyDir: 'Nenhum arquivo ou pasta encontrado neste caminho (ou todos foram filtrados).'
+25 -9
View File
@@ -2,6 +2,8 @@ ErrInvalidParams: 'Неверный запрос: {{ .detail }}'
ErrTokenParse: 'Ошибка токена: {{ .detail }}'
ErrInitialPassword: 'Исходный пароль неверен'
ErrInternalServer: 'Внутренняя ошибка: {{ .detail }}'
ErrFileAINotEnabled: 'Поиск с AI в файлах выключен или не выбран аккаунт. В управлении файлами откройте «AI-аккаунт», включите и выберите аккаунт.'
ErrFileAISearchBadPattern: 'Недопустимый шаблон поиска (regex и т.п.): {{ .detail }}'
ErrWgetRemoteFailed: 'Неверный или недоступный URL удалённой загрузки: {{ .detail }}'
ErrWgetInvalidContentType: 'Удалённый сервер вернул не файл (возможно, страницу ошибки), {{ .detail }}'
ErrRecordExist: 'Запись уже существует'
@@ -37,6 +39,7 @@ ErrGroupIsDefault: 'Группа по умолчанию, не может быт
ErrGroupIsInWebsiteUse: 'Группа используется другим веб-сайтом и не может быть удалена.'
Decrypt: 'Расшифровать'
ErrAgentAccountBound: 'Акаунт привязан к агенту'
ErrTerminalAIAccountInUse: 'Эта учетная запись модели сейчас используется Terminal AI. Смените учетную запись в настройках Terminal AI или отключите Terminal AI и повторите попытку.'
ErrAgentAccountUnavailable: 'Связь с аккаунтом недоступна: {{ .err }}'
ErrAgentProviderNotSupported: 'Провайдер агента не поддерживается'
ErrAgentAccountRequired: 'Выберите аккаунт агента'
@@ -44,13 +47,18 @@ ErrAgentAccountNotVerified: 'Аккаунт не подтверждён'
ErrAgentAccountModelsRequired: 'Настройте хотя бы одну модель'
ErrAgentAccountSingleInitialModel: 'При создании аккаунта можно указать только одну начальную модель'
ErrAgentModelNotInAccount: 'Модель не настроена в выбранном аккаунте'
ErrAgentFallbackModelDuplicate: 'Резервные модели не могут дублироваться'
ErrAgentFallbackModelPrimary: 'Резервная модель не может совпадать с основной'
ErrAgentModelInUse: 'Эта модель всё ещё используется агентом: {{ .name }}'
ErrAgentBaseURLRequired: 'Нужен базовый URL'
ErrAgentApiKeyRequired: 'Нужен API-ключ'
ErrAgentComposeRequired: 'Нужен compose'
ErrAgentIDRequired: 'Нужен ID агента'
ErrAgentAccountIDRequired: 'Нужен ID аккаунта'
ErrAgentLimitReached: 'Community Edition поддерживает до {{ .max }} AI-агентов. Перейдите на Professional Edition, чтобы снять ограничение'
ErrAgentLimitReached: 'Community Edition поддерживает до {{ .max }} AI-агентов. Обновитесь до Pro Edition, чтобы использовать их без ограничений'
ErrAgentWebsiteBound: 'Этот агент уже связан с сайтом'
ErrAgentWebsiteTypeUnsupported: 'Можно связывать только proxy- или static-сайты'
ErrAgentWebsiteInUse: 'Этот сайт уже связан с другим агентом'
Localhost: 'Локальная машина'
ErrBackupInUsed: 'Аккаунт бэкапа занят задачей'
ErrBackupCheck: 'Проверка подключения не удалась: {{ .err }}'
@@ -122,6 +130,13 @@ ErrLinkPathNotFound: 'Целевой путь не существует'
ErrFileIsExist: 'Файл или папка уже существует'
ErrFileUpload: '{{ .name }} не удалось загрузить файл {{ .detail }}'
ErrFileDownloadDir: 'Папка загрузки не поддерживается'
ErrFileSharePath: 'Неверный путь или цель не является файлом'
ErrFileShareInvalid: 'Недействительная или просроченная ссылка'
ErrFileShareExpired: 'Срок действия ссылки истёк'
ErrFileSharePassword: 'Неверный пароль доступа'
ErrFileShareExhausted: 'Лимит скачиваний по этой ссылке исчерпан'
ErrFileSharePasswordPolicy: 'Пароль должен содержать не менее 4 символов'
ErrFileShareRateLimit: 'Слишком много запросов, попробуйте позже'
ErrCmdNotFound: 'Команда {{ .name }} не существует, сначала установите эту команду на хосте'
ErrSourcePathNotFound: 'Исходный каталог не существует'
ErrFavoriteExist: 'Этот путь уже добавлен в избранное'
@@ -169,7 +184,7 @@ ErrSSLCertificateNotFound: 'Файл сертификата не существ
ErrSSLKeyFormat: 'Проверка файла закрытого ключа не удалась'
ErrSSLCertificateFormat: 'Неверный формат файла сертификата, используйте формат pem'
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid или EabHmacKey не могут быть пустыми'
ErrOpenrestyNotFound: 'Для режима HTTP необходимо сначала установить Openresty'
ErrOpenrestyNotFound: 'Для режима HTTP требуется OpenResty'
ApplySSLStart: 'Начать подачу заявки на сертификат, доменное имя [{{ .domain }}], метод подачи заявки [{{ .type }}]'
dnsAccount: 'DNS Авто'
dnsManual: 'Руководство по DNS'
@@ -254,8 +269,8 @@ ErrScriptsNotFound: 'Элемент конфигурации скриптов н
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: 'Порт 9000 зарезервирован как стандартный. Укажите другой порт'
ErrPHPRuntimePortFailed: 'Порт {{ .name }} уже используется этой средой выполнения'
ErrConfigNotFound: 'Файл конфигурации не существует'
ErrConfigParse: 'Неверный формат файла конфигурации'
ErrConfigIsNull: 'Файл конфигурации не может быть пустым'
@@ -343,10 +358,10 @@ sqlInject: 'SQL-инъекция'
ErrDBNotExist: 'База данных не существует'
allow: 'разрешить'
deny: 'отрицать'
OpenrestyNotFound: 'Openresty не установлен'
OpenrestyNotFound: 'OpenResty не установлен'
remoteIpIsNull: 'Список IP пуст'
OpenrestyVersionErr: 'Версия Openresty слишком низкая, пожалуйста, обновите Openresty до 1.27.1.2-2-2-focal'
ErrFileTooLarge: 'File is larger than 1 MB and cannot be uploaded'
OpenrestyVersionErr: 'Версия OpenResty слишком старая. Обновите до 1.27.1.2-2-2-focal'
ErrFileTooLarge: 'Файл больше 1 МБ и не может быть загружен'
TaskStart: '{{ .name }} Задача начинается [START]'
TaskEnd: '{{ .name }} Задача выполнена [ЗАВЕРШЕНО]'
TaskFailed: 'Задача {{ .name }} не выполнена'
@@ -369,7 +384,7 @@ TaskRollback: 'Откат'
TaskPull: 'Вытянуть'
TaskCommit: 'Kоммит'
TaskBuild: 'Сборка'
TaskPush: 'Push'
TaskPush: 'Отправить'
TaskClean: 'Очистка'
TaskHandle: 'Выполнить'
TaskImport: 'Импорт'
@@ -379,7 +394,7 @@ App: 'Приложение'
Runtime: 'Среда выполнения'
Database: 'База данных'
ConfigFTP: 'Создать пользователя FTP {{ .name }}'
ConfigOpenresty: 'Создать файл конфигурации Openresty'
ConfigOpenresty: 'Создать файл конфигурации OpenResty'
InstallAppSuccess: 'Приложение {{ .name }} успешно установлено'
ConfigRuntime: 'Настройка среды выполнения'
ConfigApp: 'Приложение конфигурации'
@@ -515,3 +530,4 @@ TerminalAIBlockedRiskyCommand: 'Опасная команда заблокиро
TerminalAIThinking: 'AI думает...'
TerminalAIReadyToExecute: 'Обдумывание завершено, нажмите Enter для выполнения (время: {{ .duration }}, token: {{ .tokens }})'
TerminalAIRequestFailed: 'Ошибка запроса AI: {{ .err }}'
FileAISearchEmptyDir: 'В этом пути нет файлов или каталогов для отображения (или все отфильтрованы).'
+22 -6
View File
@@ -2,6 +2,8 @@ ErrInvalidParams: 'İstek geçersiz: {{ .detail }}'
ErrTokenParse: 'Token hatası: {{ .detail }}'
ErrInitialPassword: 'Orijinal şifre yanlış'
ErrInternalServer: 'Sunucu hatası: {{ .detail }}'
ErrFileAINotEnabled: 'Dosya yönetimi AI araması kapalı veya hesap yok. Dosya Yönetimi’nde AI hesabını açıp etkinleştirin ve bir hesap seçin.'
ErrFileAISearchBadPattern: 'Geçersiz arama deseni (regex vb.): {{ .detail }}'
ErrWgetRemoteFailed: 'Uzak indirme başarısız veya URL erişilemiyor: {{ .detail }}'
ErrWgetInvalidContentType: 'Uzak URL bir dosya değil, muhtemelen bir hata sayfası döndürdü, {{ .detail }}'
ErrRecordExist: 'Kayıt zaten var'
@@ -37,6 +39,7 @@ ErrGroupIsDefault: 'Varsayılan grup, silinemez'
ErrGroupIsInWebsiteUse: 'Grup başka bir web sitesi tarafından kullanılıyor ve silinemez.'
Decrypt: 'Şifre Çöz'
ErrAgentAccountBound: 'Hesap bir ajana bağlı'
ErrTerminalAIAccountInUse: 'Bu model hesabi su anda Terminal AI tarafindan kullaniliyor. Terminal AI ayarlarinda baska bir hesaba gecin veya Terminal AI yi devre disi birakip tekrar deneyin.'
ErrAgentAccountUnavailable: 'Hesap bağlantısı yok: {{ .err }}'
ErrAgentProviderNotSupported: 'Ajans sağlayıcısı desteklenmiyor'
ErrAgentAccountRequired: 'Önce bir ajans hesabı seçin'
@@ -44,13 +47,18 @@ ErrAgentAccountNotVerified: 'Ajans hesabı doğrulanmadı'
ErrAgentAccountModelsRequired: 'Lütfen en az bir model yapılandırın'
ErrAgentAccountSingleInitialModel: 'Hesap oluştururken yalnızca bir başlangıç modeli yapılandırılabilir'
ErrAgentModelNotInAccount: 'Model seçilen hesapta yapılandırılmadı'
ErrAgentFallbackModelDuplicate: 'Yedek modeller tekrar edemez'
ErrAgentFallbackModelPrimary: 'Yedek model birincil modelle aynı olamaz'
ErrAgentModelInUse: 'Bu modeli hâlâ kullanan bağlı bir ajan var: {{ .name }}'
ErrAgentBaseURLRequired: 'Temel URL gerekli'
ErrAgentApiKeyRequired: 'API anahtarı gerekli'
ErrAgentComposeRequired: 'Compose içeriği gerekli'
ErrAgentIDRequired: 'Ajans ID gerekli'
ErrAgentAccountIDRequired: 'Hesap ID gerekli'
ErrAgentLimitReached: 'Community sürümü en fazla {{ .max }} AI ajanını destekler. Sınırı kaldırmak için Professional sürüme yükseltin'
ErrAgentLimitReached: 'Community sürümü en fazla {{ .max }} AI ajanını destekler. Sınırsız kullanım için Pro Sürüme yükseltin'
ErrAgentWebsiteBound: 'Bu ajan zaten bir web sitesine bağlı'
ErrAgentWebsiteTypeUnsupported: 'Yalnızca proxy veya statik web siteleri bağlanabilir'
ErrAgentWebsiteInUse: 'Bu web sitesi zaten başka bir ajana bağlı'
Localhost: 'Yerel Makine'
ErrBackupInUsed: 'Yedek hesabı görevde kullanılıyor'
ErrBackupCheck: 'Bağlantı testi başarısız: {{ .err }}'
@@ -122,6 +130,13 @@ 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'
ErrFileSharePath: 'Geçersiz dosya yolu veya hedef bir dosya değil'
ErrFileShareInvalid: 'Geçersiz veya süresi dolmuş paylaşım bağlantısı'
ErrFileShareExpired: 'Paylaşım bağlantısının süresi doldu'
ErrFileSharePassword: 'Yanlış paylaşım parolası'
ErrFileShareExhausted: 'Bu paylaşım için indirme sınırına ulaşıldı'
ErrFileSharePasswordPolicy: 'Parola en az 4 karakter olmalıdır'
ErrFileShareRateLimit: 'Çok fazla istek gönderildi, lütfen daha sonra tekrar deneyin'
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'
@@ -169,7 +184,7 @@ ErrSSLCertificateNotFound: 'Sertifika dosyası mevcut değil'
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'
ErrOpenrestyNotFound: 'HTTP modu için OpenResty gereklidir'
ApplySSLStart: 'Sertifika başvurusu başlatılıyor, alan adı [{{ .domain }}] başvuru yöntemi [{{ .type }}]'
dnsAccount: 'DNS Otomatik'
dnsManual: 'DNS Manuel'
@@ -343,10 +358,10 @@ sqlInject: 'SQL enjeksiyonu'
ErrDBNotExist: 'Veritabanı mevcut değil'
allow: 'izin ver'
deny: 'reddet'
OpenrestyNotFound: 'Openresty yüklü değil'
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'
ErrFileTooLarge: 'File is larger than 1 MB and cannot be uploaded'
OpenrestyVersionErr: 'OpenResty sürümü çok eski. 1.27.1.2-2-2-focal sürümüne yükseltin'
ErrFileTooLarge: 'Dosya 1 MB''den büyük olduğu için yüklenemiyor'
TaskStart: '{{ .name }} görevi başlıyor [BAŞLAT]'
TaskEnd: '{{ .name }} Görev tamamlandı [TAMAMLANDI]'
TaskFailed: '{{ .name }} görev başarısız'
@@ -379,7 +394,7 @@ App: 'Uygulama'
Runtime: 'Çalışma ortamı'
Database: 'Veritabanı'
ConfigFTP: 'FTP kullanıcısı {{ .name }} oluştur'
ConfigOpenresty: 'Openresty yapılandırma dosyası oluştur'
ConfigOpenresty: 'OpenResty yapılandırma dosyası oluştur'
InstallAppSuccess: 'Uygulama {{ .name }} başarıyla yüklendi'
ConfigRuntime: 'Çalışma ortamını yapılandır'
ConfigApp: 'Uygulama Yapılandırması'
@@ -515,3 +530,4 @@ TerminalAIBlockedRiskyCommand: 'Riskli komut engellendi: {{ .command }}'
TerminalAIThinking: 'AI dusunuyor...'
TerminalAIReadyToExecute: 'Dusunme tamamlandi, calistirmak icin Enter tusuna basin (sure: {{ .duration }}, token: {{ .tokens }})'
TerminalAIRequestFailed: 'AI istegi basarisiz oldu: {{ .err }}'
FileAISearchEmptyDir: 'Bu yol altında listelenecek dosya veya klasör yok (veya tümü filtrelendi).'
+19 -3
View File
@@ -2,6 +2,8 @@ ErrInvalidParams: '請求參數錯誤: {{ .detail }}'
ErrTokenParse: 'Token 產生錯誤: {{ .detail }}'
ErrInitialPassword: '原密碼錯誤'
ErrInternalServer: '服務內部錯誤: {{ .detail }}'
ErrFileAINotEnabled: '請先在檔案管理頁透過「AI 帳號」啟用檔案 AI 搜尋並選擇帳號。'
ErrFileAISearchBadPattern: '搜尋條件無效(正則或全詞): {{ .detail }}'
ErrWgetRemoteFailed: '遠端下載網址無效或無法存取:{{ .detail }}'
ErrWgetInvalidContentType: '遠端網址未回傳檔案內容(可能是錯誤頁),{{ .detail }}'
ErrRecordExist: '記錄已存在'
@@ -37,6 +39,7 @@ ErrGroupIsDefault: '預設分組,無法刪除'
ErrGroupIsInWebsiteUse: '分組正在被其他網站使用,無法刪除'
Decrypt: '解密'
ErrAgentAccountBound: '該帳號已綁定到智能體,無法刪除,請重試。'
ErrTerminalAIAccountInUse: '該模型帳號正被終端 AI 使用,請在終端 AI 設定中更換帳號或關閉終端 AI 後再試。'
ErrAgentAccountUnavailable: '帳號連線資訊不可用,錯誤:{{ .err }},請重試'
ErrAgentProviderNotSupported: '暫不支援該智能體提供商,請重試'
ErrAgentAccountRequired: '請選擇智能體帳號後重試'
@@ -44,13 +47,18 @@ ErrAgentAccountNotVerified: '帳號未驗證通過,請重試'
ErrAgentAccountModelsRequired: '請至少配置一個模型'
ErrAgentAccountSingleInitialModel: '建立帳號時只能配置一個初始模型'
ErrAgentModelNotInAccount: '所選模型未配置在當前帳號中,請重試'
ErrAgentFallbackModelDuplicate: '備用模型不能重複'
ErrAgentFallbackModelPrimary: '備用模型不能與主模型相同'
ErrAgentModelInUse: '仍有智能體正在使用該模型:{{ .name }}'
ErrAgentBaseURLRequired: 'Base URL 不可為空,請重試'
ErrAgentApiKeyRequired: 'API Key 不可為空,請重試'
ErrAgentComposeRequired: '自訂編排內容不可為空,請重試'
ErrAgentIDRequired: '智能體 ID 不可為空,請重試'
ErrAgentAccountIDRequired: '帳號 ID 不可為空,請重試'
ErrAgentLimitReached: '社群版最多支援建立 {{ .max }} 個智能體,升級至專業版則無數量限制'
ErrAgentLimitReached: '社群版最多支援建立 {{ .max }} 個 AI 智能體,升級至 Pro 版即可無限制使用'
ErrAgentWebsiteBound: '該智能體已關聯網站'
ErrAgentWebsiteTypeUnsupported: '只能關聯反向代理或靜態網站'
ErrAgentWebsiteInUse: '該網站已被其他智能體關聯'
Localhost: '本機'
ErrBackupInUsed: '此備份帳號已在排程任務中使用,無法刪除'
ErrBackupCheck: '備份帳號測試連線失敗{{ .err }}'
@@ -122,6 +130,13 @@ ErrLinkPathNotFound: '目標路徑不存在!'
ErrFileIsExist: '檔案或資料夾已存在!'
ErrFileUpload: '{{ .name }} 上傳檔案失敗{{ .detail }}'
ErrFileDownloadDir: '不支援下載資料夾'
ErrFileSharePath: '無效的分享路徑或目標不是檔案'
ErrFileShareInvalid: '分享連結無效或已失效'
ErrFileShareExpired: '分享連結已過期'
ErrFileSharePassword: '存取密碼錯誤'
ErrFileShareExhausted: '此分享連結的下載次數已用盡'
ErrFileSharePasswordPolicy: '存取密碼至少 4 位'
ErrFileShareRateLimit: '操作過於頻繁,請稍後再試'
ErrCmdNotFound: '{{ .name }} 指令不存在,請先在宿主機安裝此指令'
ErrSourcePathNotFound: '來源目錄不存在'
ErrFavoriteExist: '已收藏此路徑'
@@ -169,7 +184,7 @@ ErrSSLCertificateNotFound: '憑證檔案不存在'
ErrSSLKeyFormat: '私鑰檔案驗證失敗'
ErrSSLCertificateFormat: '憑證檔案格式錯誤,請使用 pem 格式'
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid 或 EabHmacKey 不可為空'
ErrOpenrestyNotFound: 'Http 模式需要先安裝OpenResty'
ErrOpenrestyNotFound: 'HTTP 模式需要先安裝 OpenResty'
ApplySSLStart: '開始申請憑證,網域名稱[{{ .domain }}] 申請方式[{{ .type }}]'
dnsAccount: 'DNS 自動'
dnsManual: 'DNS 手排'
@@ -379,7 +394,7 @@ App: '應用程式'
Runtime: '運作環境'
Database: '資料庫'
ConfigFTP: '建立FTP 使用者{{ .name }}'
ConfigOpenresty: '建立OpenResty 設定檔'
ConfigOpenresty: '建立 OpenResty 設定檔'
InstallAppSuccess: '應用程式{{ .name }} 安裝成功'
ConfigRuntime: '設定執行環境'
ConfigApp: '設定應用程式'
@@ -515,3 +530,4 @@ TerminalAIBlockedRiskyCommand: '已攔截風險命令:{{ .command }}'
TerminalAIThinking: 'AI 正在思考...'
TerminalAIReadyToExecute: '思考完成,請按 Enter 執行(耗時 {{ .duration }},token:{{ .tokens }})'
TerminalAIRequestFailed: 'AI 請求失敗:{{ .err }}'
FileAISearchEmptyDir: '目前目錄下沒有可列出的檔案或資料夾(或均被過濾)。'
+16
View File
@@ -2,6 +2,8 @@ ErrInvalidParams: "参数错误: {{ .detail }}"
ErrTokenParse: "Token 错误: {{ .detail }}"
ErrInitialPassword: "原密码错误"
ErrInternalServer: "服务错误: {{ .detail }}"
ErrFileAINotEnabled: "生成 AI 总结需在文件管理中启用并保存文件 AI 账号。关闭 AI 时仍可使用纯内容检索。"
ErrFileAISearchBadPattern: "搜索条件无效(正则或全词匹配): {{ .detail }}"
ErrWgetRemoteFailed: "远程下载地址无效或无法访问:{{ .detail }}"
ErrWgetInvalidContentType: "远程地址返回的不是文件内容(可能是错误页),{{ .detail }}"
ErrRecordExist: "记录已存在"
@@ -42,6 +44,7 @@ Decrypt: "解密"
#agent
ErrAgentAccountBound: "该账号已绑定智能体,无法删除"
ErrTerminalAIAccountInUse: "该模型账号正被终端 AI 使用,请在终端 AI 设置中更换账号或关闭终端 AI 后重试"
ErrAgentAccountUnavailable: "账号连接信息不可用: {{ .err }}"
ErrAgentProviderNotSupported: "不支持该智能体提供商"
ErrAgentAccountRequired: "请选择智能体账号后重试"
@@ -49,6 +52,8 @@ ErrAgentAccountNotVerified: "账号未通过验证"
ErrAgentAccountModelsRequired: "请至少配置一个模型"
ErrAgentAccountSingleInitialModel: "创建账号时只能配置一个初始模型"
ErrAgentModelNotInAccount: "所选模型未配置在当前账号中"
ErrAgentFallbackModelDuplicate: "备用模型不能重复"
ErrAgentFallbackModelPrimary: "备用模型不能与主模型相同"
ErrAgentModelInUse: "仍有智能体正在使用该模型: {{ .name }}"
ErrAgentBaseURLRequired: "Base URL 不能为空"
ErrAgentApiKeyRequired: "API Key 不能为空"
@@ -56,6 +61,9 @@ ErrAgentComposeRequired: "自定义编排内容不能为空"
ErrAgentIDRequired: "智能体 ID 不能为空"
ErrAgentAccountIDRequired: "账号 ID 不能为空"
ErrAgentLimitReached: "社区版最多支持创建 {{ .max }} 个智能体,升级至专业版无数量限制"
ErrAgentWebsiteBound: "该智能体已关联网站"
ErrAgentWebsiteTypeUnsupported: "只能关联反向代理或静态网站"
ErrAgentWebsiteInUse: "该网站已被其他智能体关联"
#backup
Localhost: '本机'
@@ -135,6 +143,13 @@ ErrLinkPathNotFound: "目标路径不存在!"
ErrFileIsExist: "文件或文件夹已存在!"
ErrFileUpload: "{{ .name }} 上传文件失败 {{ .detail }}"
ErrFileDownloadDir: "不支持下载目录"
ErrFileSharePath: "无效的分享路径或目标不是文件"
ErrFileShareInvalid: "分享链接无效或已失效"
ErrFileShareExpired: "分享链接已过期"
ErrFileSharePassword: "访问密码错误"
ErrFileShareExhausted: "该分享链接的下载次数已用尽"
ErrFileSharePasswordPolicy: "访问密码至少 4 位"
ErrFileShareRateLimit: "访问过于频繁,请稍后再试"
ErrCmdNotFound: "{{ .name }} 命令不存在,请先在宿主机安装此命令"
ErrSourcePathNotFound: "源目录不存在"
ErrFavoriteExist: "已收藏此路径"
@@ -573,3 +588,4 @@ TerminalAIBlockedRiskyCommand: "已拦截风险命令:{{ .command }}"
TerminalAIThinking: "AI 正在思考..."
TerminalAIReadyToExecute: "思考完成,请回车执行(耗时 {{ .duration }},token:{{ .tokens }})"
TerminalAIRequestFailed: "AI 请求失败:{{ .err }}"
FileAISearchEmptyDir: "当前目录下没有可列出的文件或文件夹(或均被过滤)。"
+4
View File
@@ -77,6 +77,10 @@ func InitAgentDB() {
migrations.AddAITerminalSettings,
migrations.UpdateAgentQuickJumpTitle,
migrations.FixOpenclaw20260323HTTPPort,
migrations.AddAgentRemarkColumn,
migrations.AddAgentWebsiteBinding,
migrations.AddFileManageAISettings,
migrations.AddFileShareTable,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)
+74
View File
@@ -51,6 +51,7 @@ var AddTable = &gormigrate.Migration{
&model.DatabaseMysql{},
&model.DatabasePostgresql{},
&model.Favorite{},
&model.FileShare{},
&model.Firewall{},
&model.Host{},
&model.Ftp{},
@@ -1151,3 +1152,76 @@ WHERE version = ?
).Error
},
}
var AddAgentRemarkColumn = &gormigrate.Migration{
ID: "20260330-add-agent-remark-column",
Migrate: func(tx *gorm.DB) error {
return tx.AutoMigrate(&model.Agent{})
},
}
var AddAgentWebsiteBinding = &gormigrate.Migration{
ID: "20260403-add-agent-website-binding",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.Agent{}); err != nil {
return err
}
var agents []model.Agent
if err := tx.Find(&agents).Error; err != nil {
return err
}
if len(agents) == 0 {
return nil
}
var websites []model.Website
if err := tx.Where("type = ? AND app_install_id > 0", constant.Deployment).Find(&websites).Error; err != nil {
return err
}
websiteMap := service.UniqueDeploymentWebsiteMapForMigration(websites)
for _, agent := range agents {
if agent.WebsiteID != 0 || agent.AppInstallID == 0 {
continue
}
website, ok := websiteMap[agent.AppInstallID]
if !ok {
continue
}
if err := tx.Model(&model.Agent{}).Where("id = ?", agent.ID).Update("website_id", website.ID).Error; err != nil {
return err
}
}
return nil
},
}
var AddFileManageAISettings = &gormigrate.Migration{
ID: "20260330-add-file-manage-ai-settings",
Migrate: func(tx *gorm.DB) error {
rows := []model.Setting{
{Key: "FileAIStatus", Value: constant.StatusDisable},
{Key: "FileAIAccountID", Value: ""},
}
for i := range rows {
var exist model.Setting
if err := tx.Where("`key` = ?", rows[i].Key).First(&exist).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
if err := tx.Create(&rows[i]).Error; err != nil {
return err
}
} else {
return err
}
}
}
return nil
},
}
var AddFileShareTable = &gormigrate.Migration{
ID: "20260410-add-file-share-table",
Migrate: func(tx *gorm.DB) error {
return tx.AutoMigrate(&model.FileShare{})
},
}
+87
View File
@@ -0,0 +1,87 @@
package middleware
import (
"net/http"
"regexp"
"strings"
"sync"
"time"
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/gin-gonic/gin"
"golang.org/x/time/rate"
)
var (
fileShareCodePattern = regexp.MustCompile(`^[A-Za-z0-9]{10,16}$`)
fileShareLimiterCleanupLock sync.Mutex
fileShareLimiterCleanupAt time.Time
publicIPLimiters sync.Map
publicCodeLimiters sync.Map
)
type visitorLimiter struct {
limiter *rate.Limiter
lastSeen time.Time
}
func FileSharePublicAccess() gin.HandlerFunc {
return func(c *gin.Context) {
code := strings.TrimSpace(c.Query("code"))
if code != "" && !fileShareCodePattern.MatchString(code) {
helper.ErrorWithDetail(c, http.StatusBadRequest, "ErrFileShareInvalid", buserr.New("ErrFileShareInvalid"))
return
}
ip := c.ClientIP()
if !allowLimiter(&publicIPLimiters, "ip:"+ip, rate.Every(time.Second), 20) {
helper.ErrorWithDetail(c, http.StatusTooManyRequests, "ErrFileShareRateLimit", buserr.New("ErrFileShareRateLimit"))
return
}
if code != "" && (strings.HasSuffix(c.Request.URL.Path, "/share/check") || strings.HasSuffix(c.Request.URL.Path, "/share/download")) {
if !allowLimiter(&publicCodeLimiters, "code:"+ip+":"+code, rate.Every(5*time.Second), 4) {
helper.ErrorWithDetail(c, http.StatusTooManyRequests, "ErrFileShareRateLimit", buserr.New("ErrFileShareRateLimit"))
return
}
}
maybeCleanupLimiters()
c.Next()
}
}
func allowLimiter(store *sync.Map, key string, refill rate.Limit, burst int) bool {
now := time.Now()
value, _ := store.LoadOrStore(key, &visitorLimiter{
limiter: rate.NewLimiter(refill, burst),
lastSeen: now,
})
item := value.(*visitorLimiter)
item.lastSeen = now
return item.limiter.Allow()
}
func maybeCleanupLimiters() {
fileShareLimiterCleanupLock.Lock()
defer fileShareLimiterCleanupLock.Unlock()
now := time.Now()
if !fileShareLimiterCleanupAt.IsZero() && now.Sub(fileShareLimiterCleanupAt) < 10*time.Minute {
return
}
fileShareLimiterCleanupAt = now
cleanupLimiterMap(&publicIPLimiters, now)
cleanupLimiterMap(&publicCodeLimiters, now)
}
func cleanupLimiterMap(store *sync.Map, now time.Time) {
store.Range(func(key, value any) bool {
item, ok := value.(*visitorLimiter)
if ok && now.Sub(item.lastSeen) > 30*time.Minute {
store.Delete(key)
}
return true
})
}
+14
View File
@@ -42,8 +42,12 @@ func (a *AIToolsRouter) InitRouter(Router *gin.RouterGroup) {
aiToolsRouter.POST("/agents", baseApi.CreateAgent)
aiToolsRouter.POST("/agents/search", baseApi.PageAgents)
aiToolsRouter.POST("/agents/delete/check", baseApi.DeleteCheckAgent)
aiToolsRouter.POST("/agents/delete", baseApi.DeleteAgent)
aiToolsRouter.POST("/agents/token/reset", baseApi.ResetAgentToken)
aiToolsRouter.POST("/agents/remark", baseApi.UpdateAgentRemark)
aiToolsRouter.POST("/agents/website/bind", baseApi.BindAgentWebsite)
aiToolsRouter.POST("/agents/model/get", baseApi.GetAgentModelConfig)
aiToolsRouter.POST("/agents/model/update", baseApi.UpdateAgentModelConfig)
aiToolsRouter.POST("/agents/overview", baseApi.GetAgentOverview)
aiToolsRouter.GET("/agents/providers", baseApi.GetAgentProviders)
@@ -56,6 +60,14 @@ func (a *AIToolsRouter) InitRouter(Router *gin.RouterGroup) {
aiToolsRouter.POST("/agents/accounts/models/delete", baseApi.DeleteAgentAccountModel)
aiToolsRouter.POST("/agents/accounts/verify", baseApi.VerifyAgentAccount)
aiToolsRouter.POST("/agents/accounts/delete", baseApi.DeleteAgentAccount)
aiToolsRouter.POST("/agents/agent/create", baseApi.CreateAgentRole)
aiToolsRouter.POST("/agents/agent/delete", baseApi.DeleteAgentRole)
aiToolsRouter.POST("/agents/agent/bind", baseApi.BindAgentRole)
aiToolsRouter.POST("/agents/agent/unbind", baseApi.UnbindAgentRole)
aiToolsRouter.POST("/agents/agent/list", baseApi.GetConfiguredAgentRoles)
aiToolsRouter.POST("/agents/agent/channels", baseApi.GetAgentRoleChannels)
aiToolsRouter.POST("/agents/agent/md/list", baseApi.GetAgentRoleMarkdownFiles)
aiToolsRouter.POST("/agents/agent/md/update", baseApi.UpdateAgentRoleMarkdownFile)
aiToolsRouter.POST("/agents/channel/feishu/get", baseApi.GetAgentFeishuConfig)
aiToolsRouter.POST("/agents/channel/feishu/update", baseApi.UpdateAgentFeishuConfig)
aiToolsRouter.POST("/agents/channel/telegram/get", baseApi.GetAgentTelegramConfig)
@@ -70,6 +82,8 @@ func (a *AIToolsRouter) InitRouter(Router *gin.RouterGroup) {
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/upgrade", baseApi.UpgradeAgentPlugin)
aiToolsRouter.POST("/agents/plugin/uninstall", baseApi.UninstallAgentPlugin)
aiToolsRouter.POST("/agents/plugin/check", baseApi.CheckAgentPlugin)
aiToolsRouter.POST("/agents/security/get", baseApi.GetAgentSecurityConfig)
aiToolsRouter.POST("/agents/security/update", baseApi.UpdateAgentSecurityConfig)
+15
View File
@@ -2,6 +2,7 @@ package router
import (
v2 "github.com/1Panel-dev/1Panel/agent/app/api/v2"
"github.com/1Panel-dev/1Panel/agent/middleware"
"github.com/gin-gonic/gin"
)
@@ -13,6 +14,7 @@ func (f *FileRouter) InitRouter(Router *gin.RouterGroup) {
baseApi := v2.ApiGroupApp.BaseApi
{
fileRouter.POST("/search", baseApi.ListFiles)
fileRouter.POST("/ai-search", baseApi.FileAISearch)
fileRouter.POST("/upload/search", baseApi.SearchUploadWithPage)
fileRouter.POST("/tree", baseApi.GetFileTree)
fileRouter.POST("", baseApi.CreateFile)
@@ -36,6 +38,11 @@ func (f *FileRouter) InitRouter(Router *gin.RouterGroup) {
fileRouter.POST("/wget/stop", baseApi.StopWget)
fileRouter.POST("/move", baseApi.MoveFile)
fileRouter.GET("/download", baseApi.Download)
fileRouter.POST("/share/search", baseApi.SearchFileShare)
fileRouter.POST("/share/detail", baseApi.GetFileShareDetail)
fileRouter.POST("/share/create", baseApi.CreateFileShare)
fileRouter.POST("/share/del", baseApi.DeleteFileShare)
fileRouter.GET("/share/qrcode", baseApi.GetFileShareQRCode)
fileRouter.POST("/chunkdownload", baseApi.DownloadChunkFiles)
fileRouter.POST("/size", baseApi.Size)
fileRouter.POST("/depth/size", baseApi.DepthDirSize)
@@ -59,4 +66,12 @@ func (f *FileRouter) InitRouter(Router *gin.RouterGroup) {
fileRouter.POST("/convert", baseApi.ConvertFile)
fileRouter.POST("/convert/log", baseApi.ConvertLog)
}
publicShareRouter := fileRouter.Group("/share")
publicShareRouter.Use(middleware.FileSharePublicAccess())
{
publicShareRouter.GET("/info", baseApi.GetPublicFileShareInfo)
publicShareRouter.GET("/check", baseApi.CheckFileShare)
publicShareRouter.GET("/download", baseApi.DownloadFileShare)
}
}
+2
View File
@@ -13,9 +13,11 @@ func (s *SettingRouter) InitRouter(Router *gin.RouterGroup) {
{
settingRouter.POST("/search", baseApi.GetSettingInfo)
settingRouter.POST("/terminal/ai/search", baseApi.GetTerminalAISettingInfo)
settingRouter.POST("/files/ai/search", baseApi.GetFileManageAISettingInfo)
settingRouter.GET("/search/available", baseApi.GetSystemAvailable)
settingRouter.POST("/update", baseApi.UpdateSetting)
settingRouter.POST("/terminal/ai/update", baseApi.UpdateTerminalAISetting)
settingRouter.POST("/files/ai/update", baseApi.UpdateFileManageAISetting)
settingRouter.GET("/get/:key", baseApi.GetSettingByKey)
settingRouter.POST("/description/save", baseApi.SaveDescription)
+35
View File
@@ -11,6 +11,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/utils/bark"
"github.com/1Panel-dev/1Panel/agent/utils/email"
"github.com/1Panel-dev/1Panel/agent/utils/psutil"
"github.com/1Panel-dev/1Panel/agent/utils/re"
@@ -103,6 +104,40 @@ func CreateEmailAlertLog(create dto.AlertLogCreate, alert dto.AlertDTO, params [
}
}
func CreateBarkAlertLog(create dto.AlertLogCreate, alert dto.AlertDTO, params []dto.Param, transport *http.Transport, agentInfo *dto.AgentInfo) error {
var alertLog model.AlertLog
alertRepo := repo.NewIAlertRepo()
create.Method = constant.Bark
barkConfig, err := alertRepo.GetConfig(alertRepo.WithByType(constant.Bark))
if err != nil {
return err
}
var barkInfo dto.AlertWebhookConfig
err = json.Unmarshal([]byte(barkConfig.Config), &barkInfo)
if err != nil {
return err
}
if barkInfo.Url == "" {
create.Message = "bark config url is required"
create.Status = constant.AlertError
return SaveAlertLog(create, &alertLog)
}
content := GetSendContent(alert.Type, params, agentInfo)
if content == "" {
content = i18n.GetMsgWithMap("CommonAlert", map[string]interface{}{"msg": alert.Title})
}
if err = bark.SendMessage(barkInfo.Url, i18n.GetMsgByKey("PanelAlertTitle"), content, transport); err != nil {
create.Message = err.Error()
create.Status = constant.AlertError
return SaveAlertLog(create, &alertLog)
}
create.Status = constant.AlertSuccess
return SaveAlertLog(create, &alertLog)
}
func SaveAlertLog(create dto.AlertLogCreate, alertLog *model.AlertLog) error {
alertRepo := repo.NewIAlertRepo()
if err := copier.Copy(&alertLog, &create); err != nil {
+23
View File
@@ -66,6 +66,29 @@ func PushAlert(pushAlert dto.PushAlert) error {
continue
}
alertUtil.CreateNewAlertTask(strconv.Itoa(int(pushAlert.EntryID)), alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), constant.Email)
case constant.Bark:
todayCount, _, err := alertRepo.LoadTaskCount(alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), constant.Bark)
if err != nil || alert.SendCount <= todayCount {
continue
}
var create = dto.AlertLogCreate{
Type: alertUtil.GetCronJobType(alert.Type),
AlertId: alert.ID,
Count: todayCount + 1,
}
transport := xpack.LoadRequestTransport()
agentInfo, _ := xpack.GetAgentInfo()
params := alertUtil.CreateAlertParams(alertUtil.GetCronJobTypeName(pushAlert.Param))
alertDetail := alertUtil.ProcessAlertDetail(alert, pushAlert.TaskName, params, constant.Bark)
alertRule := alertUtil.ProcessAlertRule(alert)
create.AlertRule = alertRule
create.AlertDetail = alertDetail
err = alertUtil.CreateBarkAlertLog(create, alert, params, transport, agentInfo)
if err != nil {
global.LOG.Errorf("%s alert bark push failed: %v", alert.Type, err)
continue
}
alertUtil.CreateNewAlertTask(strconv.Itoa(int(pushAlert.EntryID)), alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), constant.Bark)
case constant.WeCom, constant.DingTalk, constant.FeiShu:
todayCount, _, err := alertRepo.LoadTaskCount(alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), m)
if err != nil || alert.SendCount <= todayCount {
+46
View File
@@ -0,0 +1,46 @@
package bark
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
type BarkMessage struct {
Title string `json:"title"`
Body string `json:"body"`
}
func SendMessage(url string, title string, body string, transport *http.Transport) error {
msg := BarkMessage{
Title: title,
Body: body,
}
data, err := json.Marshal(msg)
if err != nil {
return err
}
client := &http.Client{
Transport: transport,
}
req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json; charset=utf-8")
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("bark push failed with status code %d", resp.StatusCode)
}
return nil
}
+9 -5
View File
@@ -54,13 +54,17 @@ func MediaFile(inputFile, outputFile, outputFormat string, deleteSource bool) (s
if !flag {
return status, fmt.Errorf("ffmpeg not found, cannot convert file")
}
logFile, logErr := os.OpenFile(filepath.Join(global.Dir.ConvertLogDir, "convert.log"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
allLogFile, allErr := os.OpenFile(filepath.Join(global.Dir.ConvertLogDir, "convert-all.log"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if logErr != nil || allErr != nil {
return status, fmt.Errorf("cannot open log file: %w", err)
logFile, err := os.OpenFile(filepath.Join(global.Dir.ConvertLogDir, "convert.log"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return status, fmt.Errorf("cannot open convert.log: %w", err)
}
defer logFile.Close()
allLogFile, err := os.OpenFile(filepath.Join(global.Dir.ConvertLogDir, "convert-all.log"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return status, fmt.Errorf("cannot open convert-all.log: %w", err)
}
defer allLogFile.Close()
args, fileType, err := buildFFmpegArgs(inputFile, outputFile, outputFormat)
if err != nil {
return status, fmt.Errorf("FFmpeg args failed: %w", err)
+205
View File
@@ -0,0 +1,205 @@
package files
import (
"bufio"
"os"
"path/filepath"
"strconv"
"strings"
"unicode/utf8"
)
type FileAIContentHit struct {
Path string `json:"path"`
Line int `json:"line"`
Text string `json:"text"`
}
var fileAIBinaryExt = map[string]struct{}{
".png": {}, ".jpg": {}, ".jpeg": {}, ".gif": {}, ".webp": {}, ".ico": {}, ".bmp": {},
".zip": {}, ".gz": {}, ".tgz": {}, ".bz2": {}, ".xz": {}, ".7z": {}, ".rar": {},
".exe": {}, ".dll": {}, ".so": {}, ".dylib": {}, ".bin": {},
".pdf": {}, ".doc": {}, ".docx": {}, ".xls": {}, ".xlsx": {},
".mp3": {}, ".mp4": {}, ".avi": {}, ".mkv": {}, ".mov": {},
".woff": {}, ".woff2": {}, ".ttf": {}, ".eot": {},
}
func SearchFileAIContentHits(root string, inventory []AISearchInventoryItem, opts ContentSearchOptions, match func(string) bool) (hits []FileAIContentHit, scannedFiles int, scanTruncated bool) {
if len(inventory) == 0 || match == nil {
return nil, 0, false
}
root = filepath.Clean(root)
maxFile := opts.MaxFileBytes
if maxFile <= 0 {
maxFile = int64(fileAIDefaultMaxFileBytes)
}
if maxFile > int64(fileAIHardMaxFileBytes) {
maxFile = int64(fileAIHardMaxFileBytes)
}
maxScan := opts.MaxScanFiles
if maxScan <= 0 {
maxScan = fileAIDefaultMaxScanFiles
}
if maxScan > fileAIHardMaxScanFiles {
maxScan = fileAIHardMaxScanFiles
}
maxPerFile := opts.MaxHitsPerFile
if maxPerFile <= 0 {
maxPerFile = fileAIDefaultMaxHitsPerFile
}
if maxPerFile > fileAIHardMaxHitsPerFile {
maxPerFile = fileAIHardMaxHitsPerFile
}
maxTotal := opts.MaxTotalHits
if maxTotal <= 0 {
maxTotal = fileAIDefaultMaxTotalHits
}
if maxTotal > fileAIHardMaxTotalHits {
maxTotal = fileAIHardMaxTotalHits
}
maxTextRunes := opts.MaxHitTextRunes
if maxTextRunes <= 0 {
maxTextRunes = fileAIDefaultMaxHitTextRunes
}
if maxTextRunes > fileAIHardMaxHitTextRunes {
maxTextRunes = fileAIHardMaxHitTextRunes
}
hits = make([]FileAIContentHit, 0, 32)
totalHits := 0
for _, it := range inventory {
rel := strings.TrimSpace(it.RelPath)
if rel == "" {
continue
}
if !opts.passesInventoryMeta(it, rel) {
continue
}
if it.Size > maxFile {
continue
}
if it.Size < 0 {
continue
}
ext := strings.ToLower(filepath.Ext(rel))
if _, bad := fileAIBinaryExt[ext]; bad {
continue
}
fullPath := filepath.Join(root, filepath.FromSlash(rel))
fullPath = filepath.Clean(fullPath)
underRoot, err := filepath.Rel(root, fullPath)
if err != nil || strings.HasPrefix(underRoot, "..") {
continue
}
if ShouldFilterSensitivePath(fullPath) {
continue
}
if scannedFiles >= maxScan {
scanTruncated = true
break
}
st, err := os.Stat(fullPath)
if err != nil || st.IsDir() {
continue
}
if st.Size() > maxFile {
continue
}
fileHits, err := scanFileForMatch(fullPath, match, maxPerFile, maxTextRunes)
scannedFiles++
if err != nil {
continue
}
for _, h := range fileHits {
if totalHits >= maxTotal {
scanTruncated = true
return hits, scannedFiles, scanTruncated
}
hits = append(hits, h)
totalHits++
}
}
return hits, scannedFiles, scanTruncated
}
func scanFileForMatch(absPath string, match func(string) bool, maxPerFile, maxTextRunes int) ([]FileAIContentHit, error) {
f, err := os.Open(absPath)
if err != nil {
return nil, err
}
defer f.Close()
scanner := bufio.NewScanner(f)
buf := make([]byte, 0, 64*1024)
scanner.Buffer(buf, 1024*1024)
var out []FileAIContentHit
lineNo := 0
for scanner.Scan() {
lineNo++
if len(out) >= maxPerFile {
break
}
line := scanner.Text()
if !match(line) {
continue
}
out = append(out, FileAIContentHit{
Path: absPath,
Line: lineNo,
Text: truncateHitLine(line, maxTextRunes),
})
}
return out, scanner.Err()
}
func truncateHitLine(s string, maxRunes int) string {
if maxRunes <= 0 {
maxRunes = fileAIDefaultMaxHitTextRunes
}
if utf8.RuneCountInString(s) <= maxRunes {
return strings.TrimRight(s, "\r\n")
}
var b strings.Builder
n := 0
for _, r := range s {
if n >= maxRunes {
b.WriteString("…")
break
}
b.WriteRune(r)
n++
}
return strings.TrimRight(b.String(), "\r\n")
}
func FormatContentHitsForPrompt(hits []FileAIContentHit, maxBytes int, matchDesc string) string {
if len(hits) == 0 || maxBytes <= 0 {
return ""
}
var b strings.Builder
if matchDesc == "" {
matchDesc = "search"
}
b.WriteString("Content line matches (" + matchDesc + "). Do not invent lines; only reference these.\n")
for _, h := range hits {
var line string
if h.Line <= 0 {
line = h.Path + " (filename): " + h.Text + "\n"
} else {
line = h.Path + ":" + strconv.Itoa(h.Line) + ": " + h.Text + "\n"
}
if b.Len()+len(line) > maxBytes {
b.WriteString("... (further hits omitted for prompt size)\n")
break
}
b.WriteString(line)
}
return b.String()
}
@@ -0,0 +1,264 @@
package files
import (
"fmt"
"path/filepath"
"regexp"
"strings"
"time"
"unicode/utf8"
"github.com/1Panel-dev/1Panel/agent/constant"
)
const (
fileAIHardMaxScanFiles = 800
fileAIDefaultMaxScanFiles = 150
fileAIUserMaxScanFiles = 500
fileAIHardMaxFileBytes = 4 * 1024 * 1024
fileAIDefaultMaxFileBytes = 512 * 1024
fileAIUserMaxFileBytes = 4 * 1024 * 1024
fileAIHardMaxHitsPerFile = 200
fileAIDefaultMaxHitsPerFile = 40
fileAIHardMaxTotalHits = 2000
fileAIDefaultMaxTotalHits = 500
fileAIHardMaxHitTextRunes = 2000
fileAIDefaultMaxHitTextRunes = 500
fileAIHardMaxPromptHitBytes = 50000
fileAIDefaultPromptHitBytes = 12000
fileAIMaxRegexPatternRunes = 512
)
type ContentSearchOptions struct {
MatchCase bool
WholeWord bool
UseRegex bool
ExtensionAllow map[string]struct{}
MinSize int64
MaxSize int64
ModAfter *time.Time
ModBefore *time.Time
MaxScanFiles int
MaxFileBytes int64
MaxHitsPerFile int
MaxTotalHits int
MaxHitTextRunes int
ContentHitsPromptMaxBytes int
LlmMaxOutputTokens int
}
func defaultContentSearchOptions() ContentSearchOptions {
return ContentSearchOptions{
MaxScanFiles: fileAIDefaultMaxScanFiles,
MaxFileBytes: int64(fileAIDefaultMaxFileBytes),
MaxHitsPerFile: fileAIDefaultMaxHitsPerFile,
MaxTotalHits: fileAIDefaultMaxTotalHits,
MaxHitTextRunes: fileAIDefaultMaxHitTextRunes,
ContentHitsPromptMaxBytes: fileAIDefaultPromptHitBytes,
}
}
func MergeContentSearchOptions(
matchCase, wholeWord, useRegex bool,
extensions []string,
minSize, maxSize int64,
modAfter, modBefore string,
maxScanFiles int,
maxFileBytes int64,
maxHitsPerFile, maxTotalHits, promptHitBytes, llmMaxOut int,
) (ContentSearchOptions, error) {
o := defaultContentSearchOptions()
o.MatchCase = matchCase
o.WholeWord = wholeWord
o.UseRegex = useRegex
if len(extensions) > 0 {
o.ExtensionAllow = make(map[string]struct{}, len(extensions))
for _, e := range extensions {
e = strings.TrimSpace(strings.ToLower(e))
if e == "" {
continue
}
if !strings.HasPrefix(e, ".") {
e = "." + e
}
o.ExtensionAllow[e] = struct{}{}
}
if len(o.ExtensionAllow) == 0 {
o.ExtensionAllow = nil
}
}
o.MinSize = minSize
o.MaxSize = maxSize
if minSize > 0 && maxSize > 0 && minSize > maxSize {
return ContentSearchOptions{}, fmt.Errorf("minSize must be <= maxSize")
}
if strings.TrimSpace(modAfter) != "" {
t, e := ParseFileSearchTime(modAfter)
if e != nil {
return ContentSearchOptions{}, fmt.Errorf("modifiedAfter: %w", e)
}
o.ModAfter = &t
}
if strings.TrimSpace(modBefore) != "" {
t, e := ParseFileSearchTime(modBefore)
if e != nil {
return ContentSearchOptions{}, fmt.Errorf("modifiedBefore: %w", e)
}
o.ModBefore = &t
}
if o.ModAfter != nil && o.ModBefore != nil && o.ModAfter.After(*o.ModBefore) {
return ContentSearchOptions{}, fmt.Errorf("modifiedAfter must be before modifiedBefore")
}
if maxScanFiles > 0 {
o.MaxScanFiles = clampInt(maxScanFiles, 1, fileAIUserMaxScanFiles)
}
if o.MaxScanFiles > fileAIHardMaxScanFiles {
o.MaxScanFiles = fileAIHardMaxScanFiles
}
if maxFileBytes > 0 {
o.MaxFileBytes = clampInt64(maxFileBytes, 1024, fileAIUserMaxFileBytes)
}
if o.MaxFileBytes > int64(fileAIHardMaxFileBytes) {
o.MaxFileBytes = int64(fileAIHardMaxFileBytes)
}
if maxHitsPerFile > 0 {
o.MaxHitsPerFile = clampInt(maxHitsPerFile, 1, fileAIHardMaxHitsPerFile)
}
if maxTotalHits > 0 {
o.MaxTotalHits = clampInt(maxTotalHits, 1, fileAIHardMaxTotalHits)
}
if promptHitBytes > 0 {
o.ContentHitsPromptMaxBytes = clampInt(promptHitBytes, 2048, fileAIHardMaxPromptHitBytes)
}
if llmMaxOut > 0 {
o.LlmMaxOutputTokens = clampInt(llmMaxOut, 256, 4096)
}
return o, nil
}
func clampInt(v, lo, hi int) int {
if v < lo {
return lo
}
if v > hi {
return hi
}
return v
}
func clampInt64(v, lo, hi int64) int64 {
if v < lo {
return lo
}
if v > hi {
return hi
}
return v
}
func ParseFileSearchTime(s string) (time.Time, error) {
s = strings.TrimSpace(s)
if s == "" {
return time.Time{}, fmt.Errorf("empty time")
}
if t, err := time.Parse(time.RFC3339, s); err == nil {
return t, nil
}
loc := time.Local
if t, err := time.ParseInLocation(constant.DateTimeLayout, s, loc); err == nil {
return t, nil
}
return time.Time{}, fmt.Errorf("invalid time format, use RFC3339 or %s", constant.DateTimeLayout)
}
func (o ContentSearchOptions) ContentMatchDescription() string {
switch {
case o.UseRegex:
if o.MatchCase {
return "regex (case-sensitive)"
}
return "regex (case-insensitive)"
case o.WholeWord:
if o.MatchCase {
return "whole-word literal (case-sensitive)"
}
return "whole-word literal (case-insensitive)"
default:
if o.MatchCase {
return "substring (case-sensitive)"
}
return "substring (case-insensitive)"
}
}
func NewContentLineMatcher(query string, o ContentSearchOptions) (func(string) bool, error) {
q := strings.TrimSpace(query)
if q == "" {
return nil, fmt.Errorf("empty query")
}
if o.UseRegex {
if utf8.RuneCountInString(q) > fileAIMaxRegexPatternRunes {
return nil, fmt.Errorf("regex pattern too long (max %d runes)", fileAIMaxRegexPatternRunes)
}
pat := q
if !o.MatchCase {
pat = "(?i)" + pat
}
re, err := regexp.Compile(pat)
if err != nil {
return nil, err
}
return func(line string) bool { return re.FindStringIndex(line) != nil }, nil
}
if o.WholeWord {
escaped := regexp.QuoteMeta(q)
pat := `\b` + escaped + `\b`
if !o.MatchCase {
pat = "(?i)" + pat
}
re, err := regexp.Compile(pat)
if err != nil {
return nil, err
}
return func(line string) bool { return re.FindStringIndex(line) != nil }, nil
}
qCmp := q
if !o.MatchCase {
qCmp = strings.ToLower(q)
return func(line string) bool {
return strings.Contains(strings.ToLower(line), qCmp)
}, nil
}
return func(line string) bool { return strings.Contains(line, q) }, nil
}
func (o ContentSearchOptions) passesInventoryMeta(it AISearchInventoryItem, rel string) bool {
if it.IsDir {
return false
}
ext := strings.ToLower(filepath.Ext(rel))
if len(o.ExtensionAllow) > 0 {
if _, ok := o.ExtensionAllow[ext]; !ok {
return false
}
}
if o.MinSize > 0 && it.Size < o.MinSize {
return false
}
if o.MaxSize > 0 && it.Size > o.MaxSize {
return false
}
if o.ModAfter != nil || o.ModBefore != nil {
mt, err := time.ParseInLocation(constant.DateTimeLayout, it.ModTime, time.Local)
if err != nil {
return true
}
if o.ModAfter != nil && mt.Before(*o.ModAfter) {
return false
}
if o.ModBefore != nil && mt.After(*o.ModBefore) {
return false
}
}
return true
}
+98
View File
@@ -0,0 +1,98 @@
package files
import (
"errors"
"io/fs"
"os"
"path/filepath"
"github.com/1Panel-dev/1Panel/agent/constant"
)
const DefaultFileAIMaxItems = 500
type AISearchInventoryItem struct {
RelPath string `json:"relPath"`
IsDir bool `json:"isDir"`
Size int64 `json:"size"`
ModTime string `json:"modTime"`
}
func CollectDirInventory(root string, containSub bool, maxItems int) (items []AISearchInventoryItem, truncated bool, err error) {
if maxItems <= 0 {
maxItems = DefaultFileAIMaxItems
}
root = filepath.Clean(root)
st, err := os.Stat(root)
if err != nil {
return nil, false, err
}
if !st.IsDir() {
return nil, false, errors.New("path is not a directory")
}
items = make([]AISearchInventoryItem, 0, maxItems)
if !containSub {
entries, err := os.ReadDir(root)
if err != nil {
return nil, false, err
}
for _, e := range entries {
full := filepath.Join(root, e.Name())
if ShouldFilterSensitivePath(full) {
continue
}
if len(items) >= maxItems {
truncated = true
break
}
info, err := e.Info()
if err != nil {
continue
}
items = append(items, AISearchInventoryItem{
RelPath: filepath.ToSlash(e.Name()),
IsDir: e.IsDir(),
Size: info.Size(),
ModTime: info.ModTime().Format(constant.DateTimeLayout),
})
}
return items, truncated, nil
}
err = filepath.WalkDir(root, func(path string, d fs.DirEntry, walkErr error) error {
if walkErr != nil {
return nil
}
if ShouldFilterSensitivePath(path) {
if d.IsDir() {
return filepath.SkipDir
}
return nil
}
rel, err := filepath.Rel(root, path)
if err != nil || rel == "." {
return nil
}
if len(items) >= maxItems {
truncated = true
return filepath.SkipAll
}
info, err := d.Info()
if err != nil {
return nil
}
items = append(items, AISearchInventoryItem{
RelPath: filepath.ToSlash(rel),
IsDir: d.IsDir(),
Size: info.Size(),
ModTime: info.ModTime().Format(constant.DateTimeLayout),
})
return nil
})
if err != nil {
return nil, false, err
}
return items, truncated, nil
}
+164
View File
@@ -0,0 +1,164 @@
package files
import (
"context"
"fmt"
"strings"
"time"
terminalai "github.com/1Panel-dev/1Panel/agent/utils/terminal/ai"
)
const fileAISearchMaxPathRunes = 240
func buildFileAISearchSystemPrompt(responseLanguage string) string {
lines := []string{
"You are a file browser assistant for a server panel.",
"Answer using Markdown (headings, bullet lists).",
"Only mention files and directories that appear in the provided inventory. Do not invent paths.",
"If a \"Content line matches\" section is present, you may reference those lines and numbers only; never invent line numbers or snippets that are not listed there.",
"If the inventory was truncated or incomplete, say so and suggest narrowing the directory or increasing limits.",
"Group or rank results by relevance to the user's question when helpful.",
}
if lang := normalizeFileAISearchLanguage(responseLanguage); lang != "" {
lines = append(lines, "Respond in "+lang+".")
} else {
lines = append(lines, "Prefer responding in the same language as the user's query (e.g. Chinese if the query is in Chinese).")
}
return strings.Join(lines, "\n")
}
func buildFileAISearchUserPrompt(root, query, responseLanguage string, items []AISearchInventoryItem, truncated, preFiltered bool, contentHits []FileAIContentHit, contentScannedFiles int, contentHitsTruncated bool, matchDesc string, promptHitMaxBytes int) string {
var b strings.Builder
b.WriteString("User question:\n")
b.WriteString(strings.TrimSpace(query))
b.WriteString("\n\nRoot directory:\n")
b.WriteString(root)
if lang := normalizeFileAISearchLanguage(responseLanguage); lang != "" {
b.WriteString("\n\nPanel reply language:\n")
b.WriteString(lang)
}
b.WriteString("\n\nInventory notes:\n")
if truncated {
b.WriteString("- Listing was truncated; not all files under the root were included.\n")
} else {
b.WriteString("- Full listing within limits.\n")
}
if preFiltered {
b.WriteString("- Inventory was pre-filtered by keyword match on relative paths.\n")
}
b.WriteString("\nInventory (relative_path | dir | size_bytes | mtime):\n")
for _, it := range items {
rel := strings.TrimSpace(it.RelPath)
if rel == "" {
continue
}
runes := []rune(rel)
if len(runes) > fileAISearchMaxPathRunes {
rel = string(runes[:fileAISearchMaxPathRunes]) + "…"
}
dirMark := "f"
if it.IsDir {
dirMark = "d"
}
b.WriteString(fmt.Sprintf("%s | %s | %d | %s\n", rel, dirMark, it.Size, it.ModTime))
}
if promptHitMaxBytes <= 0 {
promptHitMaxBytes = fileAIDefaultPromptHitBytes
}
if promptHitMaxBytes > fileAIHardMaxPromptHitBytes {
promptHitMaxBytes = fileAIHardMaxPromptHitBytes
}
b.WriteString("\nContent line search (" + matchDesc + ", scanned plain-text-sized files only):\n")
if contentScannedFiles > 0 {
b.WriteString(fmt.Sprintf("- Files scanned for line matches: %d\n", contentScannedFiles))
} else {
b.WriteString("- No eligible files were content-scanned (limits, binary extensions, filters, or only directories in inventory).\n")
}
if contentHitsTruncated {
b.WriteString("- Scan or hit list was truncated by safety limits; not all files or matches are shown.\n")
}
if len(contentHits) > 0 {
b.WriteString(FormatContentHitsForPrompt(contentHits, promptHitMaxBytes, matchDesc))
} else if contentScannedFiles > 0 {
b.WriteString("- No lines matched the search in scanned files. The summary may still use path/metadata matches.\n")
}
b.WriteString("\nOutput requirement:\nSummarize what matches the user's intent and list the most relevant paths. Use Markdown.\n")
return b.String()
}
func RunFileAISearchLLM(ctx context.Context, cfg terminalai.GeneratorConfig, clientTimeout time.Duration, root, query, responseLanguage string, items []AISearchInventoryItem, truncated, preFiltered bool, contentHits []FileAIContentHit, contentScannedFiles int, contentHitsTruncated bool, matchDesc string, promptHitMaxBytes, llmMaxOutputTokens int) (string, terminalai.ResponseUsage, error) {
timeout := clientTimeout
if timeout <= 0 {
timeout = 2 * time.Minute
}
client, err := terminalai.NewClient(terminalai.ClientConfig{
Provider: cfg.Provider,
BaseURL: cfg.BaseURL,
APIKey: cfg.APIKey,
Model: cfg.Model,
APIType: cfg.APIType,
MaxTokens: cfg.MaxTokens,
Timeout: timeout,
})
if err != nil {
return "", terminalai.ResponseUsage{}, err
}
outTokens := cfg.MaxTokens
if llmMaxOutputTokens > 0 {
outTokens = llmMaxOutputTokens
}
if outTokens < 512 {
outTokens = 2048
}
if outTokens > 4096 {
outTokens = 4096
}
resp, err := client.ChatCompletion(ctx, terminalai.ChatCompletionRequest{
Messages: []terminalai.ChatMessage{
{Role: "system", Content: buildFileAISearchSystemPrompt(responseLanguage)},
{Role: "user", Content: buildFileAISearchUserPrompt(root, query, responseLanguage, items, truncated, preFiltered, contentHits, contentScannedFiles, contentHitsTruncated, matchDesc, promptHitMaxBytes)},
},
MaxTokens: outTokens,
})
if err != nil {
return "", terminalai.ResponseUsage{}, err
}
summary := strings.TrimSpace(resp.Content)
if summary == "" {
return "", resp.Usage, fmt.Errorf("model returned empty summary")
}
return summary, resp.Usage, nil
}
func normalizeFileAISearchLanguage(lang string) string {
lang = strings.TrimSpace(strings.ToLower(lang))
switch {
case lang == "", lang == "*":
return "English"
case strings.HasPrefix(lang, "zh-hant"), strings.HasPrefix(lang, "zh-tw"), strings.HasPrefix(lang, "zh-hk"):
return "Traditional Chinese"
case strings.HasPrefix(lang, "zh"):
return "Simplified Chinese"
case strings.HasPrefix(lang, "en"):
return "English"
case strings.HasPrefix(lang, "ja"):
return "Japanese"
case strings.HasPrefix(lang, "ko"):
return "Korean"
case strings.HasPrefix(lang, "ru"):
return "Russian"
case strings.HasPrefix(lang, "ms"):
return "Malay"
case strings.HasPrefix(lang, "tr"):
return "Turkish"
case strings.HasPrefix(lang, "pt-br"):
return "Brazilian Portuguese"
case strings.HasPrefix(lang, "es"):
return "Spanish"
default:
return lang
}
}
+6 -2
View File
@@ -120,10 +120,11 @@ func (f FileOp) CreateDirWithPath(isDir bool, pathItem string) (string, error) {
}
func (f FileOp) CreateFile(dst string) error {
if _, err := f.Fs.Create(dst); err != nil {
file, err := f.Fs.Create(dst)
if err != nil {
return err
}
return nil
return file.Close()
}
func (f FileOp) CreateFileWithMode(dst string, mode fs.FileMode) error {
@@ -822,6 +823,7 @@ func (f FileOp) Compress(srcRiles []string, dst string, name string, cType Compr
if err != nil {
return err
}
defer out.Close()
switch cType {
case Zip:
@@ -936,6 +938,7 @@ func (f FileOp) decompressWithSDK(srcFile string, dst string, cType CompressType
if err != nil {
return err
}
defer input.Close()
if err := format.Extract(context.Background(), input, nil, handler); err != nil {
return err
}
@@ -1007,6 +1010,7 @@ func ZipFile(files []archiver.File, dst afero.File) error {
return err
}
_, err = io.Copy(w, fileReader)
fileReader.Close()
if err != nil {
return err
}
+1
View File
@@ -45,6 +45,7 @@ type FileInfo struct {
Items []*FileInfo `json:"items"`
ItemTotal int `json:"itemTotal"`
FavoriteID uint `json:"favoriteID"`
ShareCode string `json:"shareCode"`
IsDetail bool `json:"isDetail"`
}
+21
View File
@@ -0,0 +1,21 @@
package files
import (
"path/filepath"
"strings"
)
var sensitivePanelPaths = []string{
"/.1panel_clash",
}
func ShouldFilterSensitivePath(path string) bool {
cleanedPath := filepath.Clean(path)
for _, filteredPath := range sensitivePanelPaths {
cleanedFilteredPath := filepath.Clean(filteredPath)
if cleanedFilteredPath == cleanedPath || strings.HasPrefix(cleanedPath, cleanedFilteredPath+string(filepath.Separator)) {
return true
}
}
return false
}
+6
View File
@@ -27,6 +27,7 @@ const (
DurationWithOptionalUnitPattern = `^(\d+)([smhdw]?)$`
MysqlGroupPattern = `\[*\]`
AnsiEscapePattern = "\x1b\\[[0-9;?]*[A-Za-z]|\x1b=|\x1b>"
AnsiControlSeqPattern = `\x1b\[[0-9;?]*[ -/]*[@-~]`
RecycleBinFilePattern = `_1p_file_1p_(.+)_p_(\d+)_(\d+)`
OrderByValidationPattern = `^[a-zA-Z_][a-zA-Z0-9_]*$`
SQLIdentifierPattern = `^[A-Za-z_][A-Za-z0-9_]*$`
@@ -59,6 +60,7 @@ func Init() {
DurationWithOptionalUnitPattern,
MysqlGroupPattern,
AnsiEscapePattern,
AnsiControlSeqPattern,
RecycleBinFilePattern,
OrderByValidationPattern,
SQLIdentifierPattern,
@@ -82,3 +84,7 @@ func GetRegex(pattern string) *regexp.Regexp {
func RegisterRegex(pattern string) {
regexMap[pattern] = regexp.MustCompile(pattern)
}
func StripAnsiControlSeq(value string) string {
return GetRegex(AnsiControlSeqPattern).ReplaceAllString(value, "")
}
+35
View File
@@ -20,6 +20,7 @@ import (
var agentAccountRepo = repo.NewIAgentAccountRepo()
var agentAccountModelRepo = repo.NewIAgentAccountModelRepo()
var terminalRuntimeVersion atomic.Uint64
var fileAIRuntimeVersion atomic.Uint64
type TerminalRuntimeSettings struct {
AccountID uint
@@ -35,6 +36,10 @@ func InvalidateTerminalRuntimeCache() {
terminalRuntimeVersion.Add(1)
}
func InvalidateFileAIRuntimeCache() {
fileAIRuntimeVersion.Add(1)
}
func ResolveGeneratorConfig(accountID uint) (GeneratorConfig, time.Duration, error) {
account, err := loadAgentAccount(accountID)
if err != nil {
@@ -122,6 +127,36 @@ func ResolveGeneratorConfigFromAgentSettings() (GeneratorConfig, uint, time.Dura
return config, uint(accountID), timeout, err
}
// ResolveGeneratorConfigFromFileSettings loads file-management AI search settings (independent from terminal AI).
func ResolveGeneratorConfigFromFileSettings() (GeneratorConfig, uint, time.Duration, error) {
status, err := loadAgentSettingValue("FileAIStatus")
if err != nil {
if os.IsNotExist(err) {
return GeneratorConfig{}, 0, 0, os.ErrNotExist
}
return GeneratorConfig{}, 0, 0, err
}
if !strings.EqualFold(strings.TrimSpace(status), "Enable") {
return GeneratorConfig{}, 0, 0, os.ErrNotExist
}
accountValue, err := loadAgentSettingValue("FileAIAccountID")
if err != nil {
return GeneratorConfig{}, 0, 0, err
}
accountID, err := strconv.ParseUint(strings.TrimSpace(accountValue), 10, 64)
if err != nil || accountID == 0 {
return GeneratorConfig{}, 0, 0, os.ErrNotExist
}
config, timeout, err := ResolveGeneratorConfig(uint(accountID))
return config, uint(accountID), timeout, err
}
// LoadFileAIRuntimeConfig returns LLM client config for file-management AI search.
func LoadFileAIRuntimeConfig() (GeneratorConfig, time.Duration, error) {
cfg, _, timeout, err := ResolveGeneratorConfigFromFileSettings()
return cfg, timeout, err
}
func LoadTerminalRuntimeSettings() (TerminalRuntimeSettings, GeneratorConfig, time.Duration, error) {
config, accountID, timeout, err := ResolveGeneratorConfigFromAgentSettings()
if err != nil {
+38 -3
View File
@@ -8,6 +8,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/i18n"
terminalai "github.com/1Panel-dev/1Panel/agent/utils/terminal/ai"
"github.com/gorilla/websocket"
"github.com/pkg/errors"
)
@@ -20,6 +21,7 @@ type LocalWsSession struct {
writeMutex sync.Mutex
lang string
aiInterceptor *aiInputInterceptor
aiVersion uint64
}
func NewLocalWsSession(cols, rows int, wsConn *websocket.Conn, slave *LocalCommand, allowCtrlC bool) (*LocalWsSession, error) {
@@ -35,6 +37,7 @@ func NewLocalWsSession(cols, rows int, wsConn *websocket.Conn, slave *LocalComma
allowCtrlC: allowCtrlC,
lang: lang,
aiInterceptor: newAIInputInterceptor("", lang),
aiVersion: terminalai.CurrentTerminalRuntimeVersion(),
}, nil
}
@@ -116,6 +119,7 @@ func (sws *LocalWsSession) receiveWsMsg(exitCh chan bool) {
global.LOG.Errorf("websock cmd string base64 decoding failed, err: %v", err)
}
if isEnterInput(decodeBytes) {
sws.ensureAIInterceptor()
if sws.aiInterceptor != nil {
sws.aiInterceptor.SetCurrentLine(msgObj.Line)
}
@@ -130,7 +134,9 @@ func (sws *LocalWsSession) receiveWsMsg(exitCh chan bool) {
}
sws.sendWebsocketInputCommandToSshSessionStdinPipe(decodeBytes)
case WsMsgHeartbeat:
sws.writeMutex.Lock()
err = wsConn.WriteMessage(websocket.TextMessage, wsData)
sws.writeMutex.Unlock()
if err != nil {
global.LOG.Errorf("ssh sending heartbeat to webSocket failed, err: %v", err)
}
@@ -139,11 +145,23 @@ func (sws *LocalWsSession) receiveWsMsg(exitCh chan bool) {
}
}
func (sws *LocalWsSession) ensureAIInterceptor() {
if sws == nil || sws.aiInterceptor != nil {
return
}
currentVersion := terminalai.CurrentTerminalRuntimeVersion()
if sws.aiVersion == currentVersion {
return
}
sws.aiVersion = currentVersion
sws.aiInterceptor = newAIInputInterceptor("", sws.lang)
}
func (sws *LocalWsSession) notifyAIThinking() {
if sws == nil {
return
}
if err := sws.masterWrite([]byte("\r\n" + i18n.GetMsgByKeyAndLang(sws.lang, "TerminalAIThinking") + "\r\n")); err != nil {
if err := sws.writeAINotice("info", i18n.GetMsgByKeyAndLang(sws.lang, "TerminalAIThinking")); err != nil {
global.LOG.Errorf("write terminal ai thinking message failed, err: %v", err)
}
}
@@ -152,7 +170,7 @@ func (sws *LocalWsSession) notifyAIDone(message string) {
if sws == nil || strings.TrimSpace(message) == "" {
return
}
if err := sws.masterWrite([]byte(message + "\r\n")); err != nil {
if err := sws.writeAINotice("success", message); err != nil {
global.LOG.Errorf("write terminal ai done message failed, err: %v", err)
}
}
@@ -161,11 +179,28 @@ func (sws *LocalWsSession) notifyAIError(message string) {
if sws == nil || strings.TrimSpace(message) == "" {
return
}
if err := sws.masterWrite([]byte(message + "\r\n")); err != nil {
if err := sws.writeAINotice("error", message); err != nil {
global.LOG.Errorf("write terminal ai error message failed, err: %v", err)
}
}
func (sws *LocalWsSession) writeAINotice(level, message string) error {
if sws == nil || strings.TrimSpace(message) == "" {
return nil
}
wsData, err := json.Marshal(WsMsg{
Type: WsMsgAINotice,
Level: strings.TrimSpace(level),
Message: strings.TrimSpace(message),
})
if err != nil {
return err
}
sws.writeMutex.Lock()
defer sws.writeMutex.Unlock()
return sws.wsConn.WriteMessage(websocket.TextMessage, wsData)
}
func (sws *LocalWsSession) sendWebsocketInputCommandToSshSessionStdinPipe(cmdBytes []byte) {
if _, err := sws.slave.Write(cmdBytes); err != nil {
global.LOG.Errorf("ws cmd bytes write to ssh.stdin pipe failed, err: %v", err)
+50 -10
View File
@@ -11,6 +11,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/i18n"
terminalai "github.com/1Panel-dev/1Panel/agent/utils/terminal/ai"
"github.com/gorilla/websocket"
"golang.org/x/crypto/ssh"
)
@@ -40,12 +41,15 @@ const (
WsMsgCmd = "cmd"
WsMsgResize = "resize"
WsMsgHeartbeat = "heartbeat"
WsMsgAINotice = "ai_notice"
)
type WsMsg struct {
Type string `json:"type"`
Data string `json:"data,omitempty"` // WsMsgCmd
Line string `json:"line,omitempty"` // WsMsgCmd
Level string `json:"level,omitempty"` // WsMsgAINotice
Message string `json:"message,omitempty"` // WsMsgAINotice
Cols int `json:"cols,omitempty"` // WsMsgResize
Rows int `json:"rows,omitempty"` // WsMsgResize
Timestamp int `json:"timestamp,omitempty"` // WsMsgHeartbeat
@@ -57,10 +61,12 @@ type LogicSshWsSession struct {
logBuff *safeBuffer
session *ssh.Session
wsConn *websocket.Conn
writeMutex sync.Mutex
lang string
isAdmin bool
IsFlagged bool
aiInterceptor *aiInputInterceptor
aiVersion uint64
}
func NewLogicSshWsSession(cols, rows int, sshClient *ssh.Client, wsConn *websocket.Conn, initCmd string) (*LogicSshWsSession, error) {
@@ -105,6 +111,7 @@ func NewLogicSshWsSession(cols, rows int, sshClient *ssh.Client, wsConn *websock
isAdmin: true,
IsFlagged: false,
aiInterceptor: newAIInputInterceptor("", lang),
aiVersion: terminalai.CurrentTerminalRuntimeVersion(),
}, nil
}
@@ -157,6 +164,7 @@ func (sws *LogicSshWsSession) receiveWsMsg(exitCh chan bool) {
global.LOG.Errorf("websock cmd string base64 decoding failed, err: %v", err)
}
if isEnterInput(decodeBytes) {
sws.ensureAIInterceptor()
if sws.aiInterceptor != nil {
sws.aiInterceptor.SetCurrentLine(msgObj.Line)
}
@@ -171,7 +179,7 @@ func (sws *LogicSshWsSession) receiveWsMsg(exitCh chan bool) {
}
sws.sendWebsocketInputCommandToSshSessionStdinPipe(decodeBytes)
case WsMsgHeartbeat:
err = wsConn.WriteMessage(websocket.TextMessage, wsData)
err = sws.writeWSMessage(websocket.TextMessage, wsData)
if err != nil {
global.LOG.Errorf("ssh sending heartbeat to webSocket failed, err: %v", err)
}
@@ -180,29 +188,41 @@ func (sws *LogicSshWsSession) receiveWsMsg(exitCh chan bool) {
}
}
func (sws *LogicSshWsSession) notifyAIThinking() {
if sws == nil || sws.comboOutput == nil {
func (sws *LogicSshWsSession) ensureAIInterceptor() {
if sws == nil || sws.aiInterceptor != nil {
return
}
if _, err := sws.comboOutput.Write([]byte("\r\n" + i18n.GetMsgByKeyAndLang(sws.lang, "TerminalAIThinking") + "\r\n")); err != nil {
currentVersion := terminalai.CurrentTerminalRuntimeVersion()
if sws.aiVersion == currentVersion {
return
}
sws.aiVersion = currentVersion
sws.aiInterceptor = newAIInputInterceptor("", sws.lang)
}
func (sws *LogicSshWsSession) notifyAIThinking() {
if sws == nil {
return
}
if err := sws.writeAINotice("info", i18n.GetMsgByKeyAndLang(sws.lang, "TerminalAIThinking")); err != nil {
global.LOG.Errorf("write terminal ai thinking message failed, err: %v", err)
}
}
func (sws *LogicSshWsSession) notifyAIDone(message string) {
if sws == nil || sws.comboOutput == nil || strings.TrimSpace(message) == "" {
if sws == nil || strings.TrimSpace(message) == "" {
return
}
if _, err := sws.comboOutput.Write([]byte(message + "\r\n")); err != nil {
if err := sws.writeAINotice("success", message); err != nil {
global.LOG.Errorf("write terminal ai done message failed, err: %v", err)
}
}
func (sws *LogicSshWsSession) notifyAIError(message string) {
if sws == nil || sws.comboOutput == nil || strings.TrimSpace(message) == "" {
if sws == nil || strings.TrimSpace(message) == "" {
return
}
if _, err := sws.comboOutput.Write([]byte(message + "\r\n")); err != nil {
if err := sws.writeAINotice("error", message); err != nil {
global.LOG.Errorf("write terminal ai error message failed, err: %v", err)
}
}
@@ -213,8 +233,28 @@ func (sws *LogicSshWsSession) sendWebsocketInputCommandToSshSessionStdinPipe(cmd
}
}
func (sws *LogicSshWsSession) writeAINotice(level, message string) error {
if sws == nil || strings.TrimSpace(message) == "" {
return nil
}
wsData, err := json.Marshal(WsMsg{
Type: WsMsgAINotice,
Level: strings.TrimSpace(level),
Message: strings.TrimSpace(message),
})
if err != nil {
return err
}
return sws.writeWSMessage(websocket.TextMessage, wsData)
}
func (sws *LogicSshWsSession) writeWSMessage(messageType int, data []byte) error {
sws.writeMutex.Lock()
defer sws.writeMutex.Unlock()
return sws.wsConn.WriteMessage(messageType, data)
}
func (sws *LogicSshWsSession) sendComboOutput(exitCh chan bool) {
wsConn := sws.wsConn
defer setQuit(exitCh)
tick := time.NewTicker(time.Millisecond * time.Duration(60))
@@ -235,7 +275,7 @@ func (sws *LogicSshWsSession) sendComboOutput(exitCh chan bool) {
global.LOG.Errorf("encoding combo output to json failed, err: %v", err)
continue
}
err = wsConn.WriteMessage(websocket.TextMessage, wsData)
err = sws.writeWSMessage(websocket.TextMessage, wsData)
if err != nil {
global.LOG.Errorf("ssh sending combo output to webSocket failed, err: %v", err)
}
+25 -2
View File
@@ -12,6 +12,7 @@ import (
"github.com/1Panel-dev/1Panel/core/buserr"
"github.com/1Panel-dev/1Panel/core/constant"
"github.com/1Panel-dev/1Panel/core/global"
initauth "github.com/1Panel-dev/1Panel/core/init/auth"
"github.com/1Panel-dev/1Panel/core/utils/captcha"
"github.com/1Panel-dev/1Panel/core/utils/common"
"github.com/gin-gonic/gin"
@@ -33,6 +34,10 @@ func (b *BaseApi) Login(c *gin.Context) {
}
ip := common.GetRealClientIP(c)
if global.IPTracker.IsLocked(ip) {
helper.BadAuth(c, "ErrLoginLocked", nil)
return
}
needCaptcha := global.IPTracker.NeedCaptcha(ip)
if needCaptcha {
if errMsg := captcha.VerifyCode(req.CaptchaID, req.Captcha); errMsg != "" {
@@ -59,17 +64,21 @@ func (b *BaseApi) Login(c *gin.Context) {
}
if msgKey == "ErrAuth" || msgKey == "ErrEntrance" {
if msgKey == "ErrAuth" {
global.IPTracker.RecordFailure(ip)
global.IPTracker.SetNeedCaptcha(ip)
}
helper.BadAuth(c, msgKey, err)
return
}
if err != nil {
global.IPTracker.RecordFailure(ip)
global.IPTracker.SetNeedCaptcha(ip)
helper.InternalServer(c, err)
return
}
global.IPTracker.Clear(ip)
if user == nil || user.MfaStatus != constant.StatusEnable {
global.IPTracker.Clear(ip)
}
helper.SuccessWithData(c, user)
}
@@ -85,6 +94,11 @@ func (b *BaseApi) MFALogin(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
ip := common.GetRealClientIP(c)
if global.IPTracker.IsLocked(ip) {
helper.BadAuth(c, "ErrLoginLocked", nil)
return
}
entranceItem := c.Request.Header.Get("EntranceCode")
var entrance []byte
@@ -94,14 +108,23 @@ 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" || msgKey == "ErrMFA" {
if msgKey == "ErrMFA" {
global.IPTracker.RecordFailure(ip)
failures := initauth.GetMFASessionStore().RecordFailure(req.SessionID)
if failures >= initauth.MFASessionMaxFailures {
global.IPTracker.SetNeedCaptcha(ip)
helper.BadAuth(c, "ErrCaptchaCode", nil)
return
}
helper.BadAuth(c, msgKey, err)
return
}
if err != nil {
global.IPTracker.RecordFailure(ip)
helper.InternalServer(c, err)
return
}
global.IPTracker.Clear(ip)
helper.SuccessWithData(c, user)
}
+1 -1
View File
@@ -18,7 +18,7 @@ func ErrorWithDetail(ctx *gin.Context, code int, msgKey string, err error) {
Code: code,
Message: "",
}
if msgKey == "ErrCaptchaCode" || msgKey == "ErrAuth" {
if msgKey == "ErrCaptchaCode" || msgKey == "ErrAuth" || msgKey == "ErrLoginLocked" {
res.Code = 401
res.Message = msgKey
}
+2 -2
View File
@@ -12,6 +12,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/repo"
"github.com/1Panel-dev/1Panel/core/app/service"
"github.com/1Panel-dev/1Panel/core/buserr"
"github.com/1Panel-dev/1Panel/core/constant"
"github.com/1Panel-dev/1Panel/core/global"
@@ -113,8 +114,7 @@ func (b *BaseApi) UpdateSetting(c *gin.Context) {
return
}
if req.Key == "SecurityEntrance" {
entranceValue := base64.StdEncoding.EncodeToString([]byte(req.Value))
c.SetCookie("SecurityEntrance", entranceValue, 0, "", "", false, true)
service.SetSecurityEntranceCookie(c, req.Value)
}
helper.Success(c)
}
+6 -6
View File
@@ -6,9 +6,10 @@ type CaptchaResponse struct {
}
type UserLoginInfo struct {
Name string `json:"name"`
Token string `json:"token"`
MfaStatus string `json:"mfaStatus"`
Name string `json:"name"`
Token string `json:"token"`
MfaStatus string `json:"mfaStatus"`
MfaSession string `json:"mfaSession"`
}
type PasskeyBeginResponse struct {
@@ -36,9 +37,8 @@ type Login struct {
}
type MFALogin struct {
Name string `json:"name" validate:"required"`
Password string `json:"password" validate:"required"`
Code string `json:"code" validate:"required"`
SessionID string `json:"sessionId" validate:"required"`
Code string `json:"code" validate:"required"`
}
type SystemSetting struct {
+2 -1
View File
@@ -238,13 +238,14 @@ type TerminalInfo struct {
}
type AppstoreUpdate struct {
Scope string `json:"scope" validate:"required,oneof=UninstallDeleteImage UpgradeBackup UninstallDeleteBackup"`
Scope string `json:"scope" validate:"required,oneof=UninstallDeleteImage UpgradeBackup UninstallDeleteBackup InstallAllowPort"`
Status string `json:"status" validate:"required,oneof=Disable Enable"`
}
type AppstoreConfig struct {
UninstallDeleteImage string `json:"uninstallDeleteImage"`
UpgradeBackup string `json:"upgradeBackup"`
UninstallDeleteBackup string `json:"uninstallDeleteBackup"`
InstallAllowPort string `json:"installAllowPort"`
}
type LoginSetting struct {
+30 -30
View File
@@ -17,6 +17,9 @@ import (
"github.com/1Panel-dev/1Panel/core/buserr"
"github.com/1Panel-dev/1Panel/core/constant"
"github.com/1Panel-dev/1Panel/core/global"
initauth "github.com/1Panel-dev/1Panel/core/init/auth"
"github.com/1Panel-dev/1Panel/core/init/session/psession"
"github.com/1Panel-dev/1Panel/core/utils/common"
"github.com/1Panel-dev/1Panel/core/utils/encrypt"
"github.com/1Panel-dev/1Panel/core/utils/mfa"
"github.com/1Panel-dev/1Panel/core/utils/passkey"
@@ -75,35 +78,30 @@ func (u *AuthService) Login(c *gin.Context, info dto.Login, entrance string) (*d
return nil, "", err
}
if mfa.Value == constant.StatusEnable {
return &dto.UserLoginInfo{Name: nameSetting.Value, MfaStatus: mfa.Value}, "", nil
ip := common.GetRealClientIP(c)
mfaSession := initauth.GetMFASessionStore().Set(nameSetting.Value, entrance, ip)
return &dto.UserLoginInfo{Name: nameSetting.Value, MfaStatus: mfa.Value, MfaSession: mfaSession}, "", nil
}
res, err := u.generateSession(c, info.Name)
if err != nil {
return nil, "", err
}
if entrance != "" {
entranceValue := base64.StdEncoding.EncodeToString([]byte(entrance))
c.SetCookie("SecurityEntrance", entranceValue, 0, "", "", false, true)
SetSecurityEntranceCookie(c, entrance)
}
return res, "", nil
}
func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin, entrance string) (*dto.UserLoginInfo, string, error) {
nameSetting, err := settingRepo.Get(repo.WithByKey("UserName"))
if err != nil {
return nil, "", buserr.New("ErrRecordNotFound")
mfaSessions := initauth.GetMFASessionStore()
session, ok := mfaSessions.Get(info.SessionID)
if !ok {
return nil, "ErrMFA", nil
}
if nameSetting.Value != info.Name {
return nil, "ErrAuth", nil
if session.IP != common.GetRealClientIP(c) {
return nil, "ErrMFA", nil
}
if err = checkPassword(info.Password); err != nil {
return nil, "ErrAuth", err
}
entranceSetting, err := settingRepo.Get(repo.WithByKey("SecurityEntrance"))
if err != nil {
return nil, "", err
}
if len(entranceSetting.Value) != 0 && entranceSetting.Value != entrance {
if session.Entrance != entrance {
return nil, "", buserr.New("ErrEntrance")
}
mfaSecret, err := settingRepo.Get(repo.WithByKey("MFASecret"))
@@ -118,13 +116,13 @@ func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin, entrance strin
if !success {
return nil, "ErrMFA", nil
}
res, err := u.generateSession(c, info.Name)
res, err := u.generateSession(c, session.Name)
if err != nil {
return nil, "", err
}
mfaSessions.Delete(info.SessionID)
if entrance != "" {
entranceValue := base64.StdEncoding.EncodeToString([]byte(entrance))
c.SetCookie("SecurityEntrance", entranceValue, 0, "", "", false, true)
SetSecurityEntranceCookie(c, entrance)
}
return res, "", nil
}
@@ -143,15 +141,8 @@ func (u *AuthService) generateSession(c *gin.Context, name string) (*dto.UserLog
return nil, err
}
sessionUser, err := global.SESSION.Get(c)
if err != nil {
err := global.SESSION.Set(c, sessionUser, httpsSetting.Value == constant.StatusEnable, lifeTime)
if err != nil {
return nil, err
}
return &dto.UserLoginInfo{Name: name}, nil
}
if err := global.SESSION.Set(c, sessionUser, httpsSetting.Value == constant.StatusEnable, lifeTime); err != nil {
sessionUser := psession.SessionUser{Name: name}
if err := global.SESSION.SetFresh(c, sessionUser, httpsSetting.Value == constant.StatusEnable, lifeTime); err != nil {
return nil, err
}
@@ -166,6 +157,7 @@ func (u *AuthService) LogOut(c *gin.Context) error {
sID, _ := c.Cookie(constant.SessionName)
if sID != "" {
c.SetCookie(constant.SessionName, sID, -1, "", "", httpsSetting.Value == constant.StatusEnable, true)
c.SetCookie(constant.CSRFTokenName, "", -1, "/", "", httpsSetting.Value == constant.StatusEnable, false)
err := global.SESSION.Delete(c)
if err != nil {
return err
@@ -315,12 +307,20 @@ func (u *AuthService) PasskeyFinishLogin(c *gin.Context, sessionID, entrance str
return nil, "", err
}
if entrance != "" {
entranceValue := base64.StdEncoding.EncodeToString([]byte(entrance))
c.SetCookie("SecurityEntrance", entranceValue, 0, "", "", false, true)
SetSecurityEntranceCookie(c, entrance)
}
return res, "", nil
}
func SetSecurityEntranceCookie(c *gin.Context, entrance string) {
entranceValue := base64.StdEncoding.EncodeToString([]byte(entrance))
sslEnabled := false
if setting, err := settingRepo.Get(repo.WithByKey("SSL")); err == nil {
sslEnabled = setting.Value == constant.StatusEnable
}
c.SetCookie("SecurityEntrance", entranceValue, 0, "/", "", sslEnabled, true)
}
func (u *AuthService) PasskeyBeginRegister(c *gin.Context, name string) (*dto.PasskeyBeginResponse, string, error) {
config, msgKey, err := u.passkeyConfig(c)
if err != nil {
+9 -3
View File
@@ -308,6 +308,7 @@ func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error {
secretDir := path.Join(global.CONF.Base.InstallDir, "1panel/secret")
if req.SSL == constant.StatusDisable {
c.SetCookie(constant.SessionName, "", -1, "/", "", false, true)
c.SetCookie(constant.CSRFTokenName, "", -1, "/", "", false, false)
if err := settingRepo.Update("SSL", constant.StatusDisable); err != nil {
return err
}
@@ -718,15 +719,19 @@ func (u *SettingService) GetAppstoreConfig() (*dto.AppstoreConfig, error) {
res := &dto.AppstoreConfig{}
res.UninstallDeleteImage, _ = settingRepo.GetValueByKey("UninstallDeleteImage")
if res.UninstallDeleteImage == "" {
res.UninstallDeleteImage = "False"
res.UninstallDeleteImage = constant.StatusDisable
}
res.UpgradeBackup, _ = settingRepo.GetValueByKey("UpgradeBackup")
if res.UpgradeBackup == "" {
res.UpgradeBackup = "False"
res.UpgradeBackup = constant.StatusDisable
}
res.UninstallDeleteBackup, _ = settingRepo.GetValueByKey("UninstallDeleteBackup")
if res.UninstallDeleteBackup == "" {
res.UninstallDeleteBackup = "False"
res.UninstallDeleteBackup = constant.StatusDisable
}
res.InstallAllowPort, _ = settingRepo.GetValueByKey("InstallAllowPort")
if res.InstallAllowPort == "" {
res.InstallAllowPort = constant.StatusDisable
}
return res, nil
}
@@ -776,6 +781,7 @@ func checkProxy(req dto.ProxyUpdate) error {
}
transport = http.Transport{DialContext: dialContext}
case "", "close":
return nil
default:
return buserr.WithDetail("ErrNotSupportType", req.ProxyType, nil)
}
+2493 -841
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+9 -11
View File
@@ -57,17 +57,14 @@ var WebUrlMap = map[string]struct{}{
"/apps/upgrade": {},
"/apps/setting": {},
"/ai": {},
"/ai/model": {},
"/ai/gpu": {},
"/ai/gpu/current": {},
"/ai/gpu/history": {},
"/ai/mcp": {},
"/ai/model/tensorrt": {},
"/ai/model/vllm": {},
"/ai/model/ollama": {},
"/ai/agents/agent": {},
"/ai/agents/model": {},
"/ai": {},
"/ai/model/account": {},
"/ai/model/local": {},
"/ai/gpu": {},
"/ai/gpu/current": {},
"/ai/gpu/history": {},
"/ai/mcp": {},
"/ai/agents/agent": {},
"/containers": {},
"/containers/container/operate": {},
@@ -188,6 +185,7 @@ var DynamicRoutes = []string{
`^/databases/mysql/setting/[^/]+/[^/]+$`,
`^/databases/postgresql/setting/[^/]+/[^/]+$`,
`^/websites/[^/]+/config/[^/]+$`,
`^/s/[A-Za-z0-9]{10,16}$`,
}
var CertStore atomic.Value
+2
View File
@@ -3,6 +3,8 @@ package constant
const (
AuthMethodSession = "session"
SessionName = "psession"
CSRFTokenName = "pcsrftoken"
CSRFHeaderName = "X-CSRF-Token"
PasswordExpiredName = "expired"
)
+8 -12
View File
@@ -14,8 +14,6 @@ require (
github.com/go-resty/resty/v2 v2.15.3
github.com/go-webauthn/webauthn v0.15.0
github.com/google/uuid v1.6.0
github.com/gorilla/securecookie v1.1.2
github.com/gorilla/sessions v1.4.0
github.com/gorilla/websocket v1.5.3
github.com/jinzhu/copier v0.4.0
github.com/nicksnyder/go-i18n/v2 v2.4.0
@@ -31,13 +29,12 @@ require (
github.com/spf13/viper v1.19.0
github.com/swaggo/files/v2 v2.0.2
github.com/swaggo/swag v1.16.3
github.com/wader/gormstore/v2 v2.0.3
github.com/xlzd/gotp v0.1.0
golang.org/x/crypto v0.45.0
golang.org/x/net v0.47.0
golang.org/x/sys v0.38.0
golang.org/x/term v0.37.0
golang.org/x/text v0.31.0
golang.org/x/crypto v0.48.0
golang.org/x/net v0.50.0
golang.org/x/sys v0.41.0
golang.org/x/term v0.40.0
golang.org/x/text v0.35.0
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
gorm.io/gorm v1.30.0
@@ -100,12 +97,11 @@ require (
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
golang.org/x/image v0.28.0 // indirect
golang.org/x/tools v0.38.0 // indirect
google.golang.org/protobuf v1.34.1 // indirect
golang.org/x/image v0.38.0 // indirect
golang.org/x/tools v0.42.0 // indirect
google.golang.org/protobuf v1.36.10 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gorm.io/driver/sqlite v1.4.4 // indirect
modernc.org/libc v1.66.1 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
+22 -187
View File
@@ -1,11 +1,9 @@
github.com/1panel-dev/base64Captcha v1.3.8 h1:GbQ2IuGMp4ai4erpwf4BMjm5eLC8Efb+dATVwgpPIII=
github.com/1panel-dev/base64Captcha v1.3.8/go.mod h1:gVpwyGm9+g4rg3pXdYnsFAouP73qMOSBfnT3bxCFzco=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
@@ -18,11 +16,7 @@ github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
@@ -52,8 +46,6 @@ github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GM
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
github.com/go-gormigrate/gormigrate/v2 v2.1.2 h1:F/d1hpHbRAvKezziV2CC5KUE82cVe9zTgHSBoOOZ4CY=
github.com/go-gormigrate/gormigrate/v2 v2.1.2/go.mod h1:9nHVX6z3FCMCQPA7PThGcA55t22yKQfK/Dnsf5i7hUo=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
@@ -74,9 +66,6 @@ github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-resty/resty/v2 v2.15.3 h1:bqff+hcqAflpiF591hhJzNdkRsFhlB96CYfBwSFvql8=
github.com/go-resty/resty/v2 v2.15.3/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-webauthn/webauthn v0.15.0 h1:LR1vPv62E0/6+sTenX35QrCmpMCzLeVAcnXeH4MrbJY=
@@ -85,101 +74,39 @@ github.com/go-webauthn/x v0.1.26 h1:eNzreFKnwNLDFoywGh9FA8YOMebBWTUNlNSdolQRebs=
github.com/go-webauthn/x v0.1.26/go.mod h1:jmf/phPV6oIsF6hmdVre+ovHkxjDOmNH0t6fekWUxvg=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-tpm v0.9.6 h1:Ku42PT4LmjDu1H5C5ISWLlpI1mj+Zq7sPGKoRw2XROA=
github.com/google/go-tpm v0.9.6/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys=
github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y=
github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w=
github.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E=
github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -187,31 +114,19 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -230,7 +145,6 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo=
@@ -242,22 +156,13 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
@@ -277,15 +182,10 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -305,88 +205,51 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/wader/gormstore/v2 v2.0.3 h1:/29GWPauY8xZkpLnB8hsp+dZfP3ivA9fiDw1YVNTp6U=
github.com/wader/gormstore/v2 v2.0.3/go.mod h1:sr3N3a8F1+PBc3fHoKaphFqDXLRJ9Oe6Yow0HxKFbbg=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xlzd/gotp v0.1.0 h1:37blvlKCh38s+fkem+fFh7sMnceltoIEBYTVXyoa5Po=
github.com/xlzd/gotp v0.1.0/go.mod h1:ndLJ3JKzi3xLmUProq4LLxCuECL93dG9WASNLpHz8qg=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
golang.org/x/image v0.28.0 h1:gdem5JW1OLS4FbkWgLO+7ZeFzYtL3xClb97GaUzYMFE=
golang.org/x/image v0.28.0/go.mod h1:GUJYXtnGKEUgggyzh+Vxt+AviiCcyiwpsl8iQ8MvwGY=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/image v0.38.0 h1:5l+q+Y9JDC7mBOMjo4/aPhMDcxEptsX+Tt3GgRQRPuE=
golang.org/x/image v0.38.0/go.mod h1:/3f6vaXC+6CEanU4KJxbcUZyEePbyKbaLoDOe4ehFYY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -394,51 +257,35 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
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=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -448,20 +295,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.4.0 h1:P+gpa0QGyNma39khn1vZMS/eXEJxTwHz4Q26NR4C8fw=
gorm.io/driver/mysql v1.4.0/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c=
gorm.io/driver/postgres v1.4.1 h1:DutsKq2LK2Ag65q/+VygWth0/L4GAVOp+sCtg6WzZjs=
gorm.io/driver/postgres v1.4.1/go.mod h1:whNfh5WhhHs96honoLjBAMwJGYEuA3m1hvgUbNXhPCw=
gorm.io/driver/sqlite v1.4.1/go.mod h1:AKZZCAoFfOWHF7Nd685Iq8Uywc0i9sWJlzpoE/INzsw=
gorm.io/driver/sqlite v1.4.4 h1:gIufGoR0dQzjkyqDyYSCvsYR6fba1Gw5YKDqKeChxFc=
gorm.io/driver/sqlite v1.4.4/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
gorm.io/gorm v1.23.7/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.23.10/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s=
modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
+13 -12
View File
@@ -22,6 +22,7 @@ ErrPasskeyLimit: "Passkey limit reached (max 5)"
ErrPasskeySession: "Passkey session expired or invalid"
ErrPasskeyDuplicate: "Passkey already exists"
ErrPasskeyVerify: "Passkey verification failed"
ErrLoginLocked: "Too many failed attempts. Login is temporarily locked for 5 minutes."
# request
ErrNoSuchHost: "Host resolution failed: {{ .err }}"
@@ -69,23 +70,23 @@ ErrXpackVersion: "License validation failed, this license is version-limited, ca
ErrLicenseSave: "Failed to save license information, error {{ .err }}, retry!"
ErrLicenseSync: "License synchronization failed, no license info detected in database!"
ErrLicenseExist: "This license record already exists. You can directly go to the license page for node binding."
ErrXpackNotFound: "This section requires Professional Edition. Import a license in Panel Settings - License"
ErrXpackExceptional: "This section requires Professional Edition. Sync license status in Panel Settings - License"
ErrXpackLost: "License reached max retry count. Go to [Panel Settings] [License] and run manual sync"
ErrXpackNotFound: "This section requires Pro Edition. Import a license in Panel Settings > License."
ErrXpackExceptional: "This section requires Pro Edition. Sync the license status in Panel Settings > License."
ErrXpackLost: "The license retry limit has been reached. Go to Panel Settings > License and run a manual sync."
ErrDeviceLost: "Required files for license verification are missing, check and try again!"
ErrDeviceErr: "Current environment does not match the license import environment. Edit the license and re-import."
ErrXpackTimeout: "Request timeout, network connection might be unstable, try again later!"
ErrUnbindMaster: "Nodes exist in node management. Remove nodes first, then unbind the current license."
ErrFreeNodeLimit: "Community edition node limit reached. Go to www.lxware.cn/1panel to purchase and retry."
ErrFreeNodeLimit: "Community Edition node limit reached. Visit www.lxware.cn/1panel to upgrade and try again."
ErrNodeBound: "This license is bound to another node, check and retry!"
ErrNodeBoundDelete: "This license is bound and does not support delete operations. Check and try again!"
ErrNodeBoundLimit: "The current free node has reached its limit, check and try again!"
ErrLicenseFree: "Free nodes are available only when the license is correctly bound to a node. Verify and retry."
ErrLicenseUnbind: "Community Edition nodes detected for this license. Unbind in [Panel Settings - License] and retry."
ErrLicenseUnbind: "Community Edition nodes are still linked to this license. Unbind them in Panel Settings > License and try again."
ErrNoSuchNode: "Node information not found, check and retry!"
ErrNodeUnbind: "This node is not within the license binding range, check and retry!"
ErrNodeBind: "This node is already bound to a license, check and retry!"
ErrNodeLocalRollback: "The primary node does not support direct rollback. Manually execute the '1pctl restore' command to rollback!"
ErrNodeLocalRollback: "The primary node does not support direct rollback. Run '1pctl restore' manually to roll back."
ErrIntlLicense: "The current version does not support importing international licenses yet. Stay tuned!"
InvalidRequestBodyType: "Invalid request body format. Ensure content matches the required format and retry."
@@ -202,14 +203,14 @@ ResetEntrance: "Cancel 1Panel secure entrance"
ResetIPs: "Cancel 1Panel authorized ip restrictions"
ResetDomain: "Cancel 1Panel domain binding"
ResetPasskey: "Clear 1Panel passkeys"
RestoreCommands: "Rollback 1Panel service and data"
RestoreCommands: "Roll back 1Panel service and data"
RestoreNoSuchFile: "No rollback files found"
RestoreStep1: "(1/5) Starting rollback of 1Panel service and data from {{ .name }} directory..."
RestoreStep2: "(2/5) 1Panel binary rollback successful"
RestoreStep3: "(3/5) 1Panel script rollback successful"
RestoreStep4: "(4/5) 1Panel service rollback successful"
RestoreStep5: "(5/5) 1Panel data rollback successful"
RestoreSuccessful: "Rollback succeeded. Reverted to {{ .version }}, restarting service..."
RestoreStep2: "(2/5) 1Panel binary rollback completed"
RestoreStep3: "(3/5) 1Panel script rollback completed"
RestoreStep4: "(4/5) 1Panel service rollback completed"
RestoreStep5: "(5/5) 1Panel data rollback completed"
RestoreSuccessful: "Rollback completed. Reverted to {{ .version }}. Restarting service..."
UpdateCommands: "Update panel info"
UpdateUser: "Update panel user"
UpdatePassword: "Update panel password"
+15 -14
View File
@@ -16,6 +16,7 @@ ErrApiConfigKeyInvalid: 'Clave API inválida: {{ .detail }}'
ErrApiConfigIPInvalid: 'IP no permitida: {{ .detail }}'
ErrApiConfigDisable: 'API no permitida: {{ .detail }}'
ErrApiConfigKeyTimeInvalid: 'Timestamp inválido: {{ .detail }}'
ErrLoginLocked: "Demasiados intentos fallidos. El inicio de sesión se ha bloqueado temporalmente durante 5 minutos."
PushAppInstallTaskToNode: "Enviar tarea de instalación de aplicación al nodo [{{ .name }}]"
TaskPush: "Enviar"
AppInstallTask: "Tarea de instalación de aplicación"
@@ -68,24 +69,24 @@ ErrXpackVersion: "Validación de licencia fallida, esta licencia está limitada
ErrLicenseSave: "Error al guardar la información de la licencia, error {{ .err }}, reintente"
ErrLicenseSync: "Sincronización de licencia fallida, no se detectó información de licencia en la base de datos"
ErrLicenseExist: "Este registro de licencia ya existe. Puede ir directamente a la página de licencia para la vinculación del nodo."
ErrXpackNotFound: "Esta sección es para la edición profesional, importe la licencia en Panel > Ajustes > Licencia"
ErrXpackExceptional: "Esta sección es para la edición profesional, sincronice el estado de la licencia en Panel > Ajustes > Licencia"
ErrXpackNotFound: "Esta sección requiere la edición Pro. Importa una licencia en Configuración del panel > Licencia."
ErrXpackExceptional: "Esta sección requiere la edición Pro. Sincroniza el estado de la licencia en Configuración del panel > Licencia."
ErrXpackOutOfDate: "La licencia actual ha expirado, importe nuevamente la licencia en Panel > Ajustes > Licencia"
ErrXpackLost: "La licencia ha alcanzado el número máximo de reintentos, vaya a [Ajustes del Panel] [Licencia] y haga clic en el botón de sincronización manualmente para asegurar el funcionamiento correcto de las funciones profesionales"
ErrXpackLost: "La licencia alcanzó el límite de reintentos. Ve a Configuración del panel > Licencia y ejecuta una sincronización manual."
ErrDeviceLost: "Faltan archivos necesarios para la verificación de licencia, verifique e intente de nuevo"
ErrDeviceErr: "El entorno actual no coincide con el entorno de importación de la licencia. Edite la licencia e impórtela de nuevo"
ErrXpackTimeout: "Tiempo de espera de la solicitud, puede que la conexión de red sea inestable, intente más tarde"
ErrUnbindMaster: "Se detectaron nodos en la gestión de nodos, no se puede desvincular la licencia actual, elimine los nodos primero e intente de nuevo"
ErrFreeNodeLimit: "Se alcanzó el límite de nodos en la versión comunitaria, vaya a www.lxware.cn/1panel para comprar y reintentar"
ErrFreeNodeLimit: "Se alcanzó el límite de nodos de la edición Community. Ve a www.lxware.cn/1panel para actualizar y vuelve a intentarlo."
ErrNodeBound: "Esta licencia está vinculada a otro nodo, verifique e intente de nuevo"
ErrNodeBoundDelete: "Esta licencia está vinculada y no admite operaciones de eliminación. Verifique e intente de nuevo"
ErrNodeBoundLimit: "El nodo gratuito actual ha alcanzado su límite, verifique e intente de nuevo"
ErrLicenseFree: "Los nodos gratuitos solo pueden usarse si la licencia está correctamente vinculada a un nodo. Verifique e intente de nuevo"
ErrLicenseUnbind: "Se detectaron nodos de la Edición Comunitaria para esta licencia. Desvincule en [Panel > Ajustes > Licencia] y vuelva a intentarlo"
ErrLicenseUnbind: "Esta licencia todavía tiene nodos de la edición Community vinculados. Desvincúlalos en Configuración del panel > Licencia y vuelve a intentarlo."
ErrNoSuchNode: "Información del nodo no encontrada, verifique e intente de nuevo"
ErrNodeUnbind: "Este nodo no está dentro del rango de vinculación de la licencia, verifique e intente de nuevo"
ErrNodeBind: "Este nodo ya está vinculado a una licencia, verifique e intente de nuevo"
ErrNodeLocalRollback: "El nodo principal no admite la reversión directa. Ejecute manualmente el comando '1pctl restore' para revertir"
ErrNodeLocalRollback: "El nodo principal no admite la reversión directa. Ejecuta manualmente '1pctl restore' para revertir."
ErrIntlLicense: "La versión actual aún no admite importar licencias de la edición internacional. Próximamente"
InvalidRequestBodyType: "Formato del cuerpo de la solicitud no válido, por favor revisa y asegúrate de que el contenido cumpla con el formato requerido antes de reintentar."
InvalidLicenseCodeType: "Formato de código de licencia no válido, por favor revisa e inténtalo de nuevo."
@@ -196,14 +197,14 @@ ResetEntrance: "Cancelar entrada segura de 1Panel"
ResetIPs: "Cancelar restricciones de IP autorizadas de 1Panel"
ResetDomain: "Cancelar vinculación de dominio de 1Panel"
ResetPasskey: "Eliminar passkeys de 1Panel"
RestoreCommands: "Revertir servicio y datos de 1Panel"
RestoreNoSuchFile: "No hay archivos disponibles para revertir"
RestoreStep1: "(1/5) Iniciando reversión del servicio y datos de 1Panel desde el directorio {{ .name }}..."
RestoreStep2: "(2/5) Reversión de binarios de 1Panel finalizada con éxito"
RestoreStep3: "(3/5) Reversión de scripts de 1Panel finalizada con éxito"
RestoreStep4: "(4/5) Reversión de servicio de 1Panel finalizada con éxito"
RestoreStep5: "(5/5) Reversión de datos de 1Panel finalizada con éxito"
RestoreSuccessful: "Reversión exitosa Revertido a {{ .version }}, reiniciando servicio, espere por favor..."
RestoreCommands: "Revertir el servicio y los datos de 1Panel"
RestoreNoSuchFile: "No se encontraron archivos de reversión"
RestoreStep1: "(1/5) Iniciando la reversión del servicio y los datos de 1Panel desde el directorio {{ .name }}..."
RestoreStep2: "(2/5) Reversión del binario de 1Panel completada"
RestoreStep3: "(3/5) Reversión del script de 1Panel completada"
RestoreStep4: "(4/5) Reversión del servicio de 1Panel completada"
RestoreStep5: "(5/5) Reversión de los datos de 1Panel completada"
RestoreSuccessful: "Reversión completada. Se volvió a {{ .version }}. Reiniciando el servicio..."
UpdateCommands: "Actualizar información del panel"
UpdateUser: "Actualizar usuario del panel"
UpdatePassword: "Actualizar contraseña del panel"
+18 -17
View File
@@ -16,6 +16,7 @@ ErrApiConfigKeyInvalid: 'APIキーが無効です: {{ .detail }}'
ErrApiConfigIPInvalid: 'IPが許可されていません: {{ .detail }}'
ErrApiConfigDisable: 'APIは無効です: {{ .detail }}'
ErrApiConfigKeyTimeInvalid: 'タイムスタンプが無効です: {{ .detail }}'
ErrLoginLocked: "失敗回数が多すぎます。ログインは 5 分間一時的にロックされています。"
# request
ErrNoSuchHost: "要求されたサーバーが見つかりません {{ .err }}"
@@ -64,23 +65,23 @@ ErrXpackVersion: "ライセンスの確認に失敗しました、このライ
ErrLicenseSave: "ライセンス情報の保存に失敗しました、エラー {{ .err }}、再試行してください"
ErrLicenseSync: "ライセンス情報の同期に失敗しました、データベースにライセンス情報が検出されませんでした"
ErrLicenseExist: "このライセンス記録は既に存在します。ライセンスページで直接ノードの紐付けが可能です。"
ErrXpackNotFound: "この部分はプロフェッショナル版機能です、まず「パネル設定-ライセンス」画面でライセンスをインポートしてください"
ErrXpackExceptional: "この部分はプロフェッショナル版機能です、まず「パネル設定-ライセンス」画面でライセンス状態を同期してください"
ErrXpackLost: "ライセンスの最大試行回数に達しました、「パネル設定」「ライセンス」ページに移動し、手動で同期ボタンをクリックして、プロフェッショナル版機能が正常に使用できることを確認してください"
ErrXpackNotFound: "この機能は Pro Edition で利用できます。パネル設定 > ライセンス でライセンスをインポートしてください"
ErrXpackExceptional: "この機能は Pro Edition で利用できます。パネル設定 > ライセンス でライセンス状態を同期してください"
ErrXpackLost: "ライセンスの再試行回数が上限に達しました。パネル設定 > ライセンス で手動同期を実行してください"
ErrDeviceLost: "ライセンス検証に必要なファイルが失われました。確認して再試行してください!"
ErrDeviceErr: "現在の環境とライセンスのインポート環境が一致しません。ライセンスを編集して再度インポートしてください!"
ErrXpackTimeout: "リクエストタイムアウト、ネットワーク接続が不安定な可能性があります。後で再試行してください"
ErrUnbindMaster: "ノード管理にノードが存在するため、現在のライセンスを解除できません。ノードを削除してから再試行してください"
ErrFreeNodeLimit: "コミュニティ版のノード数が無料上限に達しました、www.lxware.cn/1panel で購入して再試行してください"
ErrFreeNodeLimit: "Community Edition のノード上限に達しました。www.lxware.cn/1panel からアップグレードして、もう一度お試しください"
ErrNodeBound: "このライセンスは他のノードにバインドされています、確認して再試行してください"
ErrNodeBoundDelete: "このライセンスはバインドされており、削除操作はサポートされていません。確認して再試行してください!"
ErrNodeBoundLimit: "現在の無料ノードは上限に達しました。確認して再試行してください!"
ErrLicenseFree: "ライセンスがノードに正常にバインドされている場合のみ、その無料ノードを使用できます。確認して再試行してください!"
ErrLicenseUnbind: "このライセンスにコミュニティ版ノードが存在します。[パネル設定 - ライセンス]でバインド解除してから再試行してください!"
ErrLicenseUnbind: "このライセンスには Community Edition のノードがまだ紐付いています。パネル設定 > ライセンス で解除してから、もう一度お試しください"
ErrNoSuchNode: "そのノード情報が見つかりませんでした、確認して再試行してください"
ErrNodeUnbind: "そのノードはライセンスのバインド範囲内ではありません、確認して再試行してください"
ErrNodeBind: "そのノードはライセンスにバインドされています、確認して再試行してください"
ErrNodeLocalRollback: "マスターノードは直接ロールバックをサポートしていません。手動で「1pctl restore」コマンドを実行してロールバックしてください!"
ErrNodeLocalRollback: "マスターノードでは直接ロールバックできません。'1pctl restore' を手動で実行してロールバックしてください"
ErrIntlLicense: "現在のバージョンでは国際版ライセンスのインポートはまだサポートされていません。しばらくお待ちください!"
InvalidRequestBodyType: "リクエストボディの形式が無効です。内容が要求された形式に準拠しているか確認してから再試行してください!"
@@ -115,10 +116,10 @@ FailedStatus: "{{ .name }} 失敗 {{ .err }}"
Start: "開始"
SubTask: "サブタスク"
Skip: "エラーを無視して続行..."
PushAppInstallTaskToNode: "アプリインストールタスクをノード [{{ .name }}] にプッシュ"
TaskPush: "プッシュ"
PushAppInstallTaskToNode: "ノード [{{ .name }}] にアプリインストールタスクを送信"
TaskPush: "送信"
AppInstallTask: "アプリインストールタスク"
PushAppFailed: "アプリインストールタスクのプッシュに失敗しました"
PushAppFailed: "アプリインストールタスクの送信に失敗しました"
Success: "成功"
#script
@@ -197,14 +198,14 @@ ResetEntrance: "1パネルの安全な入り口をキャンセルします"
ResetIPs: "1パネル認定IP制限をキャンセルします"
ResetDomain: "1パネルドメインバインディングをキャンセルします"
ResetPasskey: "1Panel のパスキーをクリアします"
RestoreCommands: "ロールバック1パネルサービスとデータ"
RestoreNoSuchFile: "ロールバックに使用できるファイルはありません"
RestoreStep1: "(1/5){{ .name }}ディレクトリからの1パネルサービスとデータの開始ロールバック..."
RestoreStep2: "(2/5)1パネルバイナリロールバックが成功しました"
RestoreStep3: "(3/5)1パネルスクリプトロールバック成功"
RestoreStep4: "(4/5)1パネルサービスロールバックが成功しました"
RestoreStep5: "(5/5)1パネルデータロールバックが成功しました"
RestoreSuccessful: "ロールバック成功!{{ .version }} にロールバックしました。サービスを再起動中です。しばらくお待ちください..."
RestoreCommands: "1Panel のサービスとデータをロールバック"
RestoreNoSuchFile: "ロールバックに使えるファイルが見つかりません"
RestoreStep1: "(1/5){{ .name }} ディレクトリから 1Panel のサービスとデータのロールバックを開始しています..."
RestoreStep2: "(2/5)1Panel バイナリのロールバックが完了しました"
RestoreStep3: "(3/5)1Panel スクリプトのロールバックが完了しました"
RestoreStep4: "(4/5)1Panel サービスのロールバックが完了しました"
RestoreStep5: "(5/5)1Panel データのロールバックが完了しました"
RestoreSuccessful: "ロールバックが完了しました。{{ .version }} に戻し、サービスを再起動しています..."
UpdateCommands: "パネル情報を更新します"
UpdateUser: "パネルユーザーを更新します"
UpdatePassword: "パスワードを更新します"
+18 -17
View File
@@ -16,6 +16,7 @@ ErrApiConfigKeyInvalid: 'API 키가 잘못됨: {{ .detail }}'
ErrApiConfigIPInvalid: 'IP 허용되지 않음: {{ .detail }}'
ErrApiConfigDisable: 'API 접근 금지: {{ .detail }}'
ErrApiConfigKeyTimeInvalid: '타임스탬프 오류: {{ .detail }}'
ErrLoginLocked: "실패한 시도가 너무 많습니다. 로그인이 5분 동안 일시적으로 잠겼습니다."
# request
ErrNoSuchHost: "요청한 서버를 찾을 수 없습니다 {{ .err }}"
@@ -63,23 +64,23 @@ ErrXpackVersion: "라이선스 검증 실패, 이 라이선스는 버전 제한
ErrLicenseSave: "라이선스 정보 저장 실패, 오류 {{ .err }}, 다시 시도해 주세요"
ErrLicenseSync: "라이선스 정보 동기화 실패, 데이터베이스에서 라이선스 정보를 찾을 수 없습니다"
ErrLicenseExist: "해당 라이선스 기록이 이미 존재합니다. 라이선스 페이지에서 직접 노드 바인딩이 가능합니다."
ErrXpackNotFound: "이 섹션은 프로페셔널 에디션 기능입니다. 먼저 [패널 설정]-[라이선스] 인터페이스에서 라이선스를 가져오세요."
ErrXpackExceptional: "이 섹션은 프로페셔널 에디션 기능입니다. 먼저 [패널 설정]-[라이선스] 인터페이스에서 라이선스 상태를 동기화하세요."
ErrXpackLost: "라이선스가 최대 재시도 횟수에 도달했습니다. [패널 설정][라이선스] 페이지로 이동하여 수동으로 동기화 버튼을 클릭하여 프로페셔널 기능을 정상적으로 사용할 수 있도록 하세요."
ErrXpackNotFound: "이 기능은 Pro Edition에서 사용할 수 있습니다. 패널 설정 > 라이선스에서 라이선스를 가져오세요"
ErrXpackExceptional: "이 기능은 Pro Edition에서 사용할 수 있습니다. 패널 설정 > 라이선스에서 라이선스 상태를 동기화하세요"
ErrXpackLost: "라이선스 재시도 한도에 도달했습니다. 패널 설정 > 라이선스에서 수동 동기화를 실행하세요"
ErrDeviceLost: "라이센스 검증에 필요한 파일이 누락되었습니다. 확인 후 다시 시도해 주세요"
ErrDeviceErr: "현재 환경이 라이선스 가져오기 환경과 일치하지 않습니다. 라이선스를 편집하고 다시 가져오십시오"
ErrXpackTimeout: "요청 시간 초과, 네트워크 연결이 불안정할 수 있습니다. 나중에 다시 시도해 주세요"
ErrUnbindMaster: "노드 관리 내에 노드가 존재함을 감지하였습니다. 현재 라이선스를 해제할 수 없습니다. 먼저 제거 후 다시 시도해 주세요"
ErrFreeNodeLimit: "커뮤니티 에디션 노드 수가 무료 한도에 도달하였습니다. www.lxware.cn/1panel 에서 구매 후 다시 시도해 주세요"
ErrFreeNodeLimit: "커뮤니티 에디션 노드 한도에 도달했습니다. www.lxware.cn/1panel에서 업그레이드한 뒤 다시 시도해 주세요"
ErrNodeBound: "이 라이선스는 다른 노드에 바인딩되어 있습니다. 다시 확인하고 시도해 주세요"
ErrNodeBoundDelete: "이 라이센스는 바인딩되어 있으며 삭제 작업을 지원하지 않습니다. 확인 후 다시 시도해 주세요"
ErrNodeBoundLimit: "현재 무료 노드가 한도에 도달했습니다. 확인 후 다시 시도해 주세요"
ErrLicenseFree: "라이선스가 노드에 정상적으로 바인딩된 경우에만 무료 노드를 사용할 수 있습니다. 확인 후 다시 시도하세요"
ErrLicenseUnbind: "이 라이선스에 커뮤니티 에디션 노드가 존재합니다. [패널 설정 - 라이선스]에서 바인딩 해제 후 다시 시도하세요"
ErrLicenseUnbind: "이 라이선스에는 아직 커뮤니티 에디션 노드가 연결되어 있습니다. 패널 설정 > 라이선스에서 연결을 해제한 뒤 다시 시도해 주세요"
ErrNoSuchNode: "노드 정보를 찾을 수 없습니다. 다시 확인하고 시도해 주세요"
ErrNodeUnbind: "이 노드가 라이선스 바인딩 범위에 있지 않음을 감지하였습니다. 다시 확인하고 시도해 주세요"
ErrNodeBind: "이 노드가 이미 라이선스에 바인딩되어 있음을 감지하였습니다. 다시 확인하고 시도해 주세요"
ErrNodeLocalRollback: "마스터 노드는 직접 롤백을 지원하지 않습니다. 수동으로 '1pctl restore' 명령어를 실행하여 롤백하세요"
ErrNodeLocalRollback: "마스터 노드는 직접 롤백을 지원하지 않습니다. '1pctl restore'를 수동으로 실행해 롤백하세요"
ErrIntlLicense: "현재 버전에서는 국제판 라이선스 가져오기를 아직 지원하지 않습니다. 곧 제공될 예정입니다"
InvalidRequestBodyType: "요청 본문 형식이 잘못되었습니다. 내용이 형식 요구 사항을 충족하는지 확인한 후 다시 시도하세요"
@@ -114,10 +115,10 @@ FailedStatus: "{{ .name }} 실패 {{ .err }}"
Start: "시작"
SubTask: "서브 작업"
Skip: "오류 무시하고 계속..."
PushAppInstallTaskToNode: "노드 [{{ .name }}]에 앱 설치 작업 푸시"
TaskPush: "푸시"
PushAppInstallTaskToNode: "노드 [{{ .name }}]로 앱 설치 작업 전송"
TaskPush: "전송"
AppInstallTask: "앱 설치 작업"
PushAppFailed: "앱 설치 작업 푸시 실패"
PushAppFailed: "앱 설치 작업 전송 실패"
Success: "성공"
#script
@@ -196,14 +197,14 @@ ResetEntrance: "1Panel 보안 입구 취소"
ResetIPs: "1Panel 허용 IP 제한 취소"
ResetDomain: "1Panel 도메인 바인딩 취소"
ResetPasskey: "1Panel 패스키 초기화"
RestoreCommands: "1Panel 서비스 및 데이터 복구"
RestoreNoSuchFile: "복구 가능한 파일이 없습니다."
RestoreStep1: "(1/5) {{ .name }} 디렉토리에서 1Panel 서비스 및 데이터 복구 시작..."
RestoreStep2: "(2/5) 1Panel 바이너리 복구 성공"
RestoreStep3: "(3/5) 1Panel 스크립트 복구 성공"
RestoreStep4: "(4/5) 1Panel 서비스 복구 성공"
RestoreStep5: "(5/5) 1Panel 데이터 복구 성공"
RestoreSuccessful: "롤백 성공 {{ .version }}(으)로 롤백되었습니다. 서비스를 재시작 중입니다. 잠시만 기다려 주세요..."
RestoreCommands: "1Panel 서비스와 데이터를 롤백"
RestoreNoSuchFile: "롤백 파일을 찾을 수 없습니다"
RestoreStep1: "(1/5) {{ .name }} 디렉터리에서 1Panel 서비스와 데이터의 롤백을 시작합니다..."
RestoreStep2: "(2/5) 1Panel 바이너리 롤백 완료"
RestoreStep3: "(3/5) 1Panel 스크립트 롤백 완료"
RestoreStep4: "(4/5) 1Panel 서비스 롤백 완료"
RestoreStep5: "(5/5) 1Panel 데이터 롤백 완료"
RestoreSuccessful: "롤백이 완료되었습니다. {{ .version }}(으)로 되돌리고 서비스를 다시 시작합니다..."
UpdateCommands: "패널 정보 업데이트"
UpdateUser: "패널 사용자 업데이트"
UpdatePassword: "패널 비밀번호 업데이트"
+18 -17
View File
@@ -16,6 +16,7 @@ ErrApiConfigKeyInvalid: 'Kunci API tidak sah: {{ .detail }}'
ErrApiConfigIPInvalid: 'IP tidak dibenarkan: {{ .detail }}'
ErrApiConfigDisable: 'API tidak dibenarkan: {{ .detail }}'
ErrApiConfigKeyTimeInvalid: 'Cap masa tidak sah: {{ .detail }}'
ErrLoginLocked: "Terlalu banyak percubaan gagal. Log masuk dikunci sementara selama 5 minit."
# request
ErrNoSuchHost: "Pelayan yang diminta tidak dapat ditemui {{ .err }}"
@@ -58,23 +59,23 @@ ErrXpackVersion: "Pengesahan lesen gagal, lesen ini terhad kepada versi tertentu
ErrLicenseSave: "Gagal menyimpan maklumat lesen {{ .err }}, sila cuba lagi"
ErrLicenseSync: "Penyelarasan maklumat lesen gagal, maklumat lesen tidak ditemui dalam pangkalan data"
ErrLicenseExist: "Rekod lesen ini sudah wujud. Anda boleh terus ke halaman lesen untuk mengikat nod."
ErrXpackNotFound: "Bahagian ini adalah fungsi profesional, sila muatkan lesen di Tetapan Papan Pemuka terlebih dahulu."
ErrXpackExceptional: "Bahagian ini adalah fungsi profesional, sila selaraskan lesen di Tetapan Papan Pemuka terlebih dahulu."
ErrXpackLost: "Lesen telah mencapai bilangan percubaan maksimum, sila ke [Tetapan Papan Pemuka][Lesen] dan klik butang penyelarasan secara manual."
ErrXpackNotFound: "Bahagian ini memerlukan Edisi Pro. Import lesen di Tetapan Panel > Lesen"
ErrXpackExceptional: "Bahagian ini memerlukan Edisi Pro. Segerakkan status lesen di Tetapan Panel > Lesen"
ErrXpackLost: "Lesen telah mencapai had percubaan semula. Pergi ke Tetapan Panel > Lesen dan jalankan penyelarasan manual"
ErrDeviceLost: "Fail yang diperlukan untuk pengesahan lesen hilang, sila semak dan cuba lagi"
ErrDeviceErr: "Persekitaran semasa tidak sepadan dengan persekitaran import lesen. Sila edit lesen dan import semula"
ErrXpackTimeout: "Permintaan tamat masa, sambungan rangkaian mungkin tidak stabil, sila cuba lagi kemudian"
ErrUnbindMaster: "Terdapat nod dalam pengurusan nod, sila keluarkan dahulu dan cuba lagi"
ErrFreeNodeLimit: "Jumlah nod versi komuniti telah mencapai had percuma, sila lawati www.lxware.cn/1panel untuk pembelian"
ErrFreeNodeLimit: "Had nod Edisi Community telah dicapai. Pergi ke www.lxware.cn/1panel untuk naik taraf dan cuba lagi"
ErrNodeBound: "Lesen ini telah diikat dengan nod lain, sila semak dan cuba lagi"
ErrNodeBoundDelete: "Lisensi ini telah diikat dan tidak menyokong operasi penghapusan. Sila semak dan cuba lagi"
ErrNodeBoundLimit: "Nod percuma semasa telah mencapai had, sila semak dan cuba lagi"
ErrLicenseFree: "Nod percuma hanya boleh digunakan apabila lesen terikat dengan betul pada nod. Sila semak dan cuba lagi"
ErrLicenseUnbind: "Nod Edisi Komuniti dikesan untuk lesen ini. Sila lepaskan ikatan dalam [Tetapan Panel - Lesen] dan cuba lagi"
ErrLicenseUnbind: "Lesen ini masih terikat pada nod Edisi Community. Nyahikat di Tetapan Panel > Lesen dan cuba lagi"
ErrNoSuchNode: "Maklumat nod tidak ditemui, sila semak dan cuba lagi"
ErrNodeUnbind: "Nod di luar skop lesen dikesan, sila semak dan cuba lagi"
ErrNodeBind: "Nod ini telah diikat dengan lesen, sila semak dan cuba lagi"
ErrNodeLocalRollback: "Nod utama tidak menyokong rollback secara langsung. Sila laksanakan arahan '1pctl restore' secara manual untuk rollback"
ErrNodeLocalRollback: "Nod utama tidak menyokong rollback secara langsung. Jalankan '1pctl restore' secara manual untuk rollback"
ErrIntlLicense: "Versi semasa belum menyokong import lesen edisi antarabangsa. Nantikan"
InvalidRequestBodyType: "Format badan permintaan tidak sah, sila periksa dan pastikan kandungan memenuhi format yang diperlukan sebelum mencuba semula"
@@ -109,10 +110,10 @@ FailedStatus: "{{ .name }} gagal {{ .err }}"
Start: "Mula"
SubTask: "Tugas Sub"
Skip: "Abaikan ralat dan teruskan..."
PushAppInstallTaskToNode: "Tolak tugas pemasangan aplikasi ke nod [{{ .name }}]"
TaskPush: "Tolak"
PushAppInstallTaskToNode: "Hantar tugas pemasangan aplikasi ke nod [{{ .name }}]"
TaskPush: "Hantar"
AppInstallTask: "Tugas pemasangan aplikasi"
PushAppFailed: "Gagal menolak tugas pemasangan aplikasi"
PushAppFailed: "Gagal menghantar tugas pemasangan aplikasi"
Success: "Berjaya"
#script
@@ -191,14 +192,14 @@ ResetEntrance: "Batal pintu masuk keselamatan 1Panel"
ResetIPs: "Batal sekatan IP yang dibenarkan 1Panel"
ResetDomain: "Batal pengikatan domain 1Panel"
ResetPasskey: "Kosongkan passkey 1Panel"
RestoreCommands: "Pulihkan perkhidmatan dan data 1Panel"
RestoreNoSuchFile: "Tiada fail tersedia untuk pemulihan"
RestoreStep1: "(1/5) Memulakan pemulihan perkhidmatan dan data 1Panel daripada direktori {{ .name }}..."
RestoreStep2: "(2/5) Pemulihan binari 1Panel berjaya"
RestoreStep3: "(3/5) Pemulihan skrip 1Panel berjaya"
RestoreStep4: "(4/5) Pemulihan perkhidmatan 1Panel berjaya"
RestoreStep5: "(5/5) Pemulihan data 1Panel berjaya"
RestoreSuccessful: "Pulangan semula berjaya Dikembalikan ke {{ .version }}, memulakan semula perkhidmatan, sila tunggu..."
RestoreCommands: "Rollback perkhidmatan dan data 1Panel"
RestoreNoSuchFile: "Tiada fail rollback ditemui"
RestoreStep1: "(1/5) Memulakan rollback perkhidmatan dan data 1Panel daripada direktori {{ .name }}..."
RestoreStep2: "(2/5) Rollback binari 1Panel berjaya"
RestoreStep3: "(3/5) Rollback skrip 1Panel berjaya"
RestoreStep4: "(4/5) Rollback perkhidmatan 1Panel berjaya"
RestoreStep5: "(5/5) Rollback data 1Panel berjaya"
RestoreSuccessful: "Rollback berjaya. Dikembalikan ke {{ .version }}. Perkhidmatan sedang dimulakan semula..."
UpdateCommands: "Kemas kini maklumat panel"
UpdateUser: "Kemas kini pengguna panel"
UpdatePassword: "Kemas kini kata laluan panel"
+14 -13
View File
@@ -16,6 +16,7 @@ ErrApiConfigKeyInvalid: 'Chave API inválida: {{ .detail }}'
ErrApiConfigIPInvalid: 'IP sem permissão: {{ .detail }}'
ErrApiConfigDisable: 'API bloqueada: {{ .detail }}'
ErrApiConfigKeyTimeInvalid: 'Timestamp inválido: {{ .detail }}'
ErrLoginLocked: "Muitas tentativas falharam. O login foi bloqueado temporariamente por 5 minutos."
# request
ErrNoSuchHost: "Servidor solicitado não encontrado {{ .err }}"
@@ -63,19 +64,19 @@ ErrXpackVersion: "Falha na verificação da licença, esta licença é restrita
ErrLicenseSave: "Falha ao salvar as informações da licença {{ .err }}, por favor tente novamente"
ErrLicenseSync: "Falha ao sincronizar as informações da licença, nenhuma informação foi encontrada no banco de dados"
ErrLicenseExist: "Este registro de licença já existe. Você pode ir diretamente para a página de licenças para vincular nós."
ErrXpackNotFound: "Esta parte é uma funcionalidade profissional, por favor importe a licença em Configurações do Painel - Licença."
ErrXpackExceptional: "Esta parte é uma funcionalidade profissional, por favor sincronize a licença em Configurações do Painel - Licença."
ErrXpackLost: "A licença atingiu o número máximo de tentativas, acesse [Configurações do Painel][Licença] e clique no botão de sincronização manual."
ErrXpackNotFound: "Este recurso requer a edição Pro. Importe uma licença em Configurações do Painel > Licença."
ErrXpackExceptional: "Este recurso requer a edição Pro. Sincronize o status da licença em Configurações do Painel > Licença."
ErrXpackLost: "A licença atingiu o limite de tentativas. Acesse Configurações do Painel > Licença e execute uma sincronização manual."
ErrDeviceLost: "Arquivos necessários para a verificação da licença estão faltando, por favor verifique e tente novamente"
ErrDeviceErr: "O ambiente atual não corresponde ao ambiente de importação da licença. Por favor, edite a licença e reimporte"
ErrXpackTimeout: "Requisição expirou, a conexão de rede pode estar instável, por favor tente novamente mais tarde"
ErrUnbindMaster: "Foram encontrados nós na gestão de nós, remova-os primeiro e tente novamente"
ErrFreeNodeLimit: "O número de nós da versão comunitária atingiu o limite gratuito, por favor acesse www.lxware.cn/1panel para comprar"
ErrFreeNodeLimit: "O limite de nós da edição Community foi atingido. Acesse www.lxware.cn/1panel para fazer upgrade e tente novamente."
ErrNodeBound: "Esta licença já está vinculada a outro nó, por favor verifique e tente novamente"
ErrNodeBoundDelete: "Esta licença está vinculada e não suporta operações de exclusão. Por favor, verifique e tente novamente"
ErrNodeBoundLimit: "O nó gratuito atual atingiu seu limite, por favor verifique e tente novamente"
ErrLicenseFree: "Nós gratuitos só podem ser usados quando a licença está devidamente vinculada a um nó. Por favor verifique e tente novamente"
ErrLicenseUnbind: "Nós da Edição Comunitária detectados para esta licença. Por favor desvincule em [Configurações do Painel - Licença] e tente novamente"
ErrLicenseUnbind: "Ainda há nós da edição Community vinculados a esta licença. Desvincule-os em Configurações do Painel > Licença e tente novamente."
ErrNoSuchNode: "As informações deste nó não foram encontradas, por favor verifique e tente novamente"
ErrNodeUnbind: "Nó fora do escopo da licença detectado, por favor verifique e tente novamente"
ErrNodeBind: "Este nó já está vinculado a uma licença, por favor verifique e tente novamente"
@@ -196,14 +197,14 @@ ResetEntrance: "Cancelar entrada segura do 1Panel"
ResetIPs: "Cancelar restrições de IP autorizado do 1Panel"
ResetDomain: "Cancelar vinculação de domínio do 1Panel"
ResetPasskey: "Redefinir passkeys do 1Panel"
RestoreCommands: "Restaurar serviço e dados do 1Panel"
RestoreNoSuchFile: "Nenhum arquivo disponível para restauração"
RestoreStep1: "(1/5) Iniciando a restauração do serviço e dados do 1Panel a partir do diretório {{ .name }}..."
RestoreStep2: "(2/5) Restauração do binário do 1Panel bem-sucedida"
RestoreStep3: "(3/5) Restauração dos scripts do 1Panel bem-sucedida"
RestoreStep4: "(4/5) Restauração do serviço do 1Panel bem-sucedida"
RestoreStep5: "(5/5) Restauração dos dados do 1Panel bem-sucedida"
RestoreSuccessful: "Reversão bem-sucedida Revertido para {{ .version }}, reiniciando serviço, por favor aguarde..."
RestoreCommands: "Fazer rollback do serviço e dos dados do 1Panel"
RestoreNoSuchFile: "Nenhum arquivo de rollback encontrado"
RestoreStep1: "(1/5) Iniciando o rollback do serviço e dos dados do 1Panel a partir do diretório {{ .name }}..."
RestoreStep2: "(2/5) Rollback do binário do 1Panel concluído"
RestoreStep3: "(3/5) Rollback dos scripts do 1Panel concluído"
RestoreStep4: "(4/5) Rollback do serviço do 1Panel concluído"
RestoreStep5: "(5/5) Rollback dos dados do 1Panel concluído"
RestoreSuccessful: "Rollback concluído. Revertido para {{ .version }}. Reiniciando o serviço..."
UpdateCommands: "Atualizar informações do painel"
UpdateUser: "Atualizar usuário do painel"
UpdatePassword: "Atualizar senha do painel"
+15 -14
View File
@@ -16,6 +16,7 @@ ErrApiConfigKeyInvalid: 'Неверный API-ключ: {{ .detail }}'
ErrApiConfigIPInvalid: 'IP не в списке: {{ .detail }}'
ErrApiConfigDisable: 'Доступ по API запрещён: {{ .detail }}'
ErrApiConfigKeyTimeInvalid: 'Неверная метка времени: {{ .detail }}'
ErrLoginLocked: "Слишком много неудачных попыток. Вход временно заблокирован на 5 минут."
# request
ErrNoSuchHost: "Не удалось найти запрашиваемый сервер {{ .err }}"
@@ -63,23 +64,23 @@ ErrXpackVersion: "Ошибка проверки лицензии, лицензи
ErrLicenseSave: "Ошибка сохранения информации о лицензии {{ .err }}, повторите попытку"
ErrLicenseSync: "Ошибка синхронизации информации о лицензии, информация не найдена в базе данных"
ErrLicenseExist: "Данная лицензия уже существует. Вы можете перейти на страницу лицензий для привязки узлов."
ErrXpackNotFound: "Эта часть доступна только в профессиональной версии, сначала импортируйте лицензию в разделе Настройки панели - Лицензия."
ErrXpackExceptional: "Эта часть доступна только в профессиональной версии, сначала синхронизируйте лицензию в разделе Настройки панели - Лицензия."
ErrXpackLost: "Лицензия достигла максимального количества попыток, перейдите в [Настройки панели][Лицензия] и нажмите кнопку синхронизации вручную."
ErrXpackNotFound: "Этот раздел доступен в Pro Edition. Импортируйте лицензию в Настройки панели > Лицензия"
ErrXpackExceptional: "Этот раздел доступен в Pro Edition. Синхронизируйте статус лицензии в Настройки панели > Лицензия"
ErrXpackLost: "Достигнут лимит повторных попыток по лицензии. Перейдите в Настройки панели > Лицензия и выполните ручную синхронизацию"
ErrDeviceLost: "Необходимые файлы для проверки лицензии отсутствуют, пожалуйста, проверьте и попробуйте снова"
ErrDeviceErr: "Текущая среда не соответствует среде импорта лицензии. Отредактируйте лицензию и повторите импорт"
ErrXpackTimeout: "Время ожидания запроса истекло, возможно нестабильное сетевое соединение, повторите попытку позже"
ErrUnbindMaster: "В управлении узлами обнаружены узлы, сначала удалите их и повторите попытку"
ErrFreeNodeLimit: "Количество узлов в версии сообщества достигло бесплатного лимита, посетите www.lxware.cn/1panel для покупки"
ErrFreeNodeLimit: "Достигнут лимит узлов Community Edition. Перейдите на www.lxware.cn/1panel, обновите лицензию и повторите попытку"
ErrNodeBound: "Эта лицензия уже связана с другим узлом, проверьте и повторите попытку"
ErrNodeBoundDelete: "Эта лицензия привязана и не поддерживает операции удаления. Пожалуйста, проверьте и попробуйте снова"
ErrNodeBoundLimit: "Текущий бесплатный узел достиг предела, пожалуйста, проверьте и попробуйте снова"
ErrLicenseFree: "Бесплатные узлы доступны только при правильной привязке лицензии к узлу. Проверьте и повторите попытку"
ErrLicenseUnbind: "Обнаружены узлы Community Edition для этой лицензии. Отвяжите в [Настройки панели - Лицензия] и повторите попытку"
ErrLicenseUnbind: "К этой лицензии всё ещё привязаны узлы Community Edition. Отвяжите их в Настройки панели > Лицензия и повторите попытку"
ErrNoSuchNode: "Информация об узле не найдена, проверьте и повторите попытку"
ErrNodeUnbind: "Обнаружен узел вне области действия лицензии, проверьте и повторите попытку"
ErrNodeBind: "Этот узел уже связан с лицензией, проверьте и повторите попытку"
ErrNodeLocalRollback: "Основной узел не поддерживает прямой откат. Пожалуйста, вручную выполните команду '1pctl restore' для отката"
ErrNodeLocalRollback: "Прямой откат на основном узле не поддерживается. Выполните '1pctl restore' вручную для отката"
ErrIntlLicense: "Текущая версия пока не поддерживает импорт международной лицензии. Скоро будет доступно"
InvalidRequestBodyType: "Неверный формат тела запроса. Проверьте, соответствует ли содержимое требуемому формату, и повторите попытку"
@@ -195,14 +196,14 @@ ResetEntrance: "Отменить безопасный вход 1Panel"
ResetIPs: "Отменить ограничения авторизованных IP для 1Panel"
ResetDomain: "Отменить привязку домена доступа 1Panel"
ResetPasskey: "Сбросить passkey 1Panel"
RestoreCommands: "Откатить сервисы и данные 1Panel"
RestoreNoSuchFile: "Нет доступных для отката файлов"
RestoreStep1: "(1/5)Начинаем откат сервисов и данных 1Panel из каталога {{ .name }}..."
RestoreStep2: "(2/5)Откат бинарного файла 1Panel завершён"
RestoreStep3: "(3/5)Откат скриптов 1Panel завершён"
RestoreStep4: "(4/5)Откат сервисов 1Panel завершён"
RestoreStep5: "(5/5)Откат данных 1Panel завершён"
RestoreSuccessful: "Откат успешен Откатился к {{ .version }}, перезапускаю службу, пожалуйста, подождите..."
RestoreCommands: "Откатить сервис и данные 1Panel"
RestoreNoSuchFile: "Файлы для отката не найдены"
RestoreStep1: "(1/5) Начинаем откат сервиса и данных 1Panel из каталога {{ .name }}..."
RestoreStep2: "(2/5) Откат бинарного файла 1Panel завершён"
RestoreStep3: "(3/5) Откат скриптов 1Panel завершён"
RestoreStep4: "(4/5) Откат сервиса 1Panel завершён"
RestoreStep5: "(5/5) Откат данных 1Panel завершён"
RestoreSuccessful: "Откат завершён. Возвращено к версии {{ .version }}. Перезапускаем сервис..."
UpdateCommands: "Изменить информацию панели"
UpdateUser: "Изменить пользователя панели"
UpdatePassword: "Изменить пароль панели"
+14 -13
View File
@@ -16,6 +16,7 @@ ErrApiConfigKeyInvalid: 'Geçersiz API anahtarı: {{ .detail }}'
ErrApiConfigIPInvalid: 'IP izinli değil: {{ .detail }}'
ErrApiConfigDisable: 'API erişimi kapalı: {{ .detail }}'
ErrApiConfigKeyTimeInvalid: 'Zaman damgası hatalı: {{ .detail }}'
ErrLoginLocked: "Çok fazla başarısız deneme yapıldı. Giriş 5 dakika boyunca geçici olarak kilitlendi."
# request
ErrNoSuchHost: "İstenen sunucu bulunamıyor {{ .err }}"
@@ -63,22 +64,22 @@ ErrXpackVersion: "Lisans doğrulama başarısız, bu lisans sürüm sınırlıd
ErrLicenseSave: "Lisans bilgilerini kaydetme başarısız, hata {{ .err }}, lütfen tekrar deneyin"
ErrLicenseSync: "Lisans senkronizasyonu başarısız, veritabanında lisans bilgisi tespit edilmedi"
ErrLicenseExist: "Bu lisans kaydı zaten mevcut. Düğüm bağlama için doğrudan lisans sayfasına gidebilirsiniz."
ErrXpackNotFound: "Bu bölüm profesyonel sürüm içindir, lütfen Panel Ayarları - Lisans'ta lisansı içe aktarın"
ErrXpackExceptional: "Bu bölüm profesyonel sürüm içindir, lütfen Panel Ayarları - Lisans'ta lisans durumunu senkronize edin"
ErrXpackLost: "Lisans maksimum yeniden deneme sayısına ulaştı, lütfen [Panel Ayarları] [Lisans] sayfasına gidin ve profesyonel özelliklerin doğru çalışması için manuel olarak senkronize butonuna tıklayın"
ErrXpackNotFound: "Bu bölüm Pro Sürüm gerektirir. Lisansı Panel Ayarları > Lisans bölümünden içe aktarın"
ErrXpackExceptional: "Bu bölüm Pro Sürüm gerektirir. Lisans durumunu Panel Ayarları > Lisans bölümünden senkronize edin"
ErrXpackLost: "Lisans yeniden deneme sınırına ulaştı. Panel Ayarları > Lisans bölümüne gidip manuel senkronizasyon çalıştırın"
ErrDeviceLost: "Lisans doğrulama için gerekli dosyalar eksik, lütfen kontrol edip tekrar deneyin"
ErrXpackTimeout: "İstek zaman aşımı, ağ bağlantısı kararsız olabilir, lütfen daha sonra tekrar deneyin"
ErrUnbindMaster: "Düğüm yönetiminde düğümler tespit edildi, mevcut lisansın bağı çözülemiyor, lütfen önce düğümleri kaldırın ve tekrar deneyin"
ErrFreeNodeLimit: "Topluluk sürümü düğüm limiti ulaşıldı, lütfen www.lxware.cn/1panel adresinden satın alın ve tekrar deneyin"
ErrFreeNodeLimit: "Community Sürümü düğüm sınırına ulaşıldı. www.lxware.cn/1panel üzerinden yükseltip tekrar deneyin"
ErrNodeBound: "Bu lisans başka bir düğüme bağlı, lütfen kontrol edip tekrar deneyin"
ErrNodeBoundDelete: "Bu lisans bağlı ve silme işlemlerini desteklemiyor. Lütfen kontrol edip tekrar deneyin"
ErrNodeBoundLimit: "Mevcut ücretsiz düğüm limitine ulaştı, lütfen kontrol edip tekrar deneyin"
ErrLicenseFree: "Ücretsiz düğümler yalnızca lisans bir düğüme düzgün şekilde bağlandığında kullanılabilir. Lütfen doğrulayın ve tekrar deneyin"
ErrLicenseUnbind: "Bu lisans için Topluluk Sürümü düğümleri tespit edildi. Lütfen [Panel Ayarları - Lisans]'ta bağı çözün ve tekrar deneyin"
ErrLicenseUnbind: "Bu lisansa hala Community Sürümü düğümleri bağlı. Panel Ayarları > Lisans bölümünden bağlantıyı kaldırıp tekrar deneyin"
ErrNoSuchNode: "Düğüm bilgisi bulunamadı, lütfen kontrol edip tekrar deneyin"
ErrNodeUnbind: "Bu düğüm lisans bağlama aralığında değil, lütfen kontrol edip tekrar deneyin"
ErrNodeBind: "Bu düğüm zaten bir lisansa bağlı, lütfen kontrol edip tekrar deneyin"
ErrNodeLocalRollback: "Ana düğüm doğrudan geri alma desteklemiyor. Lütfen geri almak için '1pctl restore' komutunu manuel olarak çalıştırın"
ErrNodeLocalRollback: "Ana düğüm doğrudan rollback'i desteklemez. Rollback için '1pctl restore' komutunu manuel çalıştırın"
ErrIntlLicense: "Mevcut sürüm uluslararası sürüm lisansının içe aktarılmasını henüz desteklemiyor. Yakında"
InvalidRequestBodyType: "Geçersiz istek gövdesi formatı, lütfen içeriğin format gereksinimlerine uygun olduğunu kontrol edip tekrar deneyin"
@@ -196,13 +197,13 @@ ResetIPs: "1Panel yetkili IP kısıtlamalarını iptal et"
ResetDomain: "1Panel alan adı bağlamasını iptal et"
ResetPasskey: "1Panel passkey'lerini sıfırla"
RestoreCommands: "1Panel servisini ve verilerini geri al"
RestoreNoSuchFile: "Geri alma için mevcut dosya yok"
RestoreStep1: "(1/5) {{ .name }} dizininden 1Panel servisi ve verilerin geri alınması başlatılıyor..."
RestoreStep2: "(2/5) 1Panel binary geri alımı başarılı"
RestoreStep3: "(3/5) 1Panel betik geri alımı başarılı"
RestoreStep4: "(4/5) 1Panel servis geri alımı başarılı"
RestoreStep5: "(5/5) 1Panel veri geri alımı başarılı"
RestoreSuccessful: "Geri alma başarılı {{ .version }} sürümüne geri alındı, hizmet yeniden başlatılıyor, lütfen bekleyin..."
RestoreNoSuchFile: "Rollback için dosya bulunamadı"
RestoreStep1: "(1/5) {{ .name }} dizininden 1Panel hizmeti ve verileri geri alınıyor..."
RestoreStep2: "(2/5) 1Panel ikili dosyası için geri alma tamamlandı"
RestoreStep3: "(3/5) 1Panel betikleri için geri alma tamamlandı"
RestoreStep4: "(4/5) 1Panel hizmeti için geri alma tamamlandı"
RestoreStep5: "(5/5) 1Panel verileri için geri alma tamamlandı"
RestoreSuccessful: "Geri alma tamamlandı. {{ .version }} sürümüne dönüldü. Hizmet yeniden başlatılıyor..."
UpdateCommands: "Panel bilgilerini güncelle"
UpdateUser: "Panel kullanıcısını güncelle"
UpdatePassword: "Panel şifresini güncelle"

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