diff --git a/.gitignore b/.gitignore index 1209cd0..1c15d8b 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ # Update /update +# Skills +.trae \ No newline at end of file diff --git a/README.md b/README.md index 51a59d8..ee5ef4e 100644 --- a/README.md +++ b/README.md @@ -18,16 +18,15 @@

### 📖 仓库介绍 - - 本仓库包含多个适用于 1Panel 的应用,旨在为用户提供简单、快速的安装与更新体验。应用均为开源项目,支持通过 1Panel 的计划任务功能自动化安装和更新。通过仓库提供的脚本,可以轻松地将应用集成到 1Panel 系统中。 - - 仓库主打优质应用合集,不追求大而全(很多基本用不上的应用会干扰检索查看),有推荐的应用可以在issue中进行提交 + +- 本仓库包含多个适用于 1Panel 的应用,旨在为用户提供简单、快速的安装与更新体验。应用均为开源项目,支持通过 1Panel 的计划任务功能自动化安装和更新。通过仓库提供的脚本,可以轻松地将应用集成到 1Panel 系统中。 +- 仓库主打优质应用合集,不追求大而全(很多基本用不上的应用会干扰检索查看),有推荐的应用可以在issue中进行提交 ### ⚠️ 仓库申明 - - 非官方,第三方应用商店 - 不对任何原始镜像的有效性做出任何明示或暗示的保证或声明,安全性和风险自查 -- 个人仓库,可以Fork后自行更新,但是严禁未经授权,私自删除个人信息后合并发布 - +- 个人仓库,可以Fork后自行更新,但是严禁未经授权,私自删除个人信息后合并发布 ### 📱 应用列表 @@ -36,14 +35,10 @@ #### 🤖LLM免费API接口 - 支持一键部署AI免费API接口,使用方式请参考应用内**README介绍** - - **Free-API系列应用已下架,原项目由于供应链投毒,被植入恶意代码,请及时停止运行并删除这些服务!!!** - - 经过几天的排查和重构,已重新上架[GLM-Free-API](https://github.com/xiaoY233/GLM-Free-API)、[MiniMax-Free-API](https://github.com/xiaoY233/MiniMax-Free-API)、[Qwen-Free-API](https://github.com/xiaoY233/Qwen-Free-API)、[Kimi-Free-API](https://github.com/xiaoY233/Kimi-Free-API),[DeepSeek-Free-API](https://github.com/xiaoY233/DeepSeek-Free-API),欢迎各位对源码进行审查,如果不放心,建议还是暂停使用! - - 其他的Free-API系列看情况再搞了,后续主要更新上述几个Free-API兼容Gemini-cli和Claude的API接入。 -
@@ -103,8 +98,8 @@ -
Jimeng-Free-API - + +Jimeng-Free-API 🚀 即梦3.0逆向API【特长:图像生成顶流】 @@ -115,8 +110,8 @@ -
Spark-Free-API - + +Spark-Free-API 🚀 讯飞星火大模型逆向API【特长:办公助手】 @@ -144,8 +139,8 @@ -
Step-Free-API - + +Step-Free-API 🚀 阶跃星辰跃问Step 多模态大模型逆向API【特长:超强多模态】 @@ -156,8 +151,8 @@ -
Metaso-Free-API - + +Metaso-Free-API 🚀 秘塔AI搜索逆向API【特长:超强检索超长输出】 @@ -208,7 +203,6 @@
- #### 📝 文档与内容管理 @@ -1222,8 +1216,54 @@ rm -rf /tmp/appstore_merge echo "应用商店数据已更新" ``` +### 🤖 使用 AI 快速生成应用配置 + +本仓库提供了 Skill 配置,支持在 Cursor、Windsurf、Claude Code 等 AI 客户端中快速生成 1Panel 应用配置。 + +#### 📁 Skills 目录结构 + +``` +skills/ +├── SKILL.md # 1Panel App Builder 技能定义 +├── README.md # 使用文档 +├── templates/ # 配置模板 +│ ├── data.yml.tpl # 应用元数据模板 +│ └── docker-compose.yml.tpl # 编排文件模板 +├── scripts/ # 工具脚本 +│ ├── generate-app.sh # 主生成脚本 +│ ├── download-icon.sh # 图标下载工具 +│ └── validate-app.sh # 配置验证工具 +├── references/ # 参考示例 +│ └── 1panel-examples.md +└── examples/ # 使用示例 + └── example-usage.md +``` + +#### 💡 使用示例 + +只需向 AI 提供以下任一信息,即可自动生成完整的应用配置: + +``` +# GitHub 项目 +添加应用 AList https://github.com/alist-org/alist + +# docker-compose 文件 +根据这个 docker-compose.yml 生成 1Panel 应用配置 + +# docker run 命令 +将这个 docker run 命令转换为 1Panel 应用: +docker run -d --name=nginx -p 80:80 nginx:latest +``` + +#### ✨ AI 生成的配置包含 + +- `data.yml` - 应用元数据(顶层) +- `version/data.yml` - 参数定义(表单字段) +- `docker-compose.yml` - Docker 编排文件 +- `README.md` - 中文文档 +- `README_en.md` - 英文文档 +- `logo.png` - 应用图标 -![Copyright-arch3rPro](https://img.shields.io/badge/Copyright-arch3rPro-ff9800?style=flat&logo=github&logoColor=white) - +!\[Copyright-arch3rPro]\(https\://img.shields.io/badge/Copyright-arch3rPro-ff9800?style=flat\&logo=github\&logoColor=white null) diff --git a/skills/README.md b/skills/README.md new file mode 100644 index 0000000..03d794c --- /dev/null +++ b/skills/README.md @@ -0,0 +1,200 @@ +# 1Panel App Builder + +快速生成符合 1Panel 本地应用商店规范的 APP 配置文件。 + +## 功能特性 + +- ✅ 支持多种输入源(GitHub、docker-compose、docker run、本地文件) +- ✅ 自动抽取应用信息并生成标准化配置 +- ✅ 自动下载应用图标(支持多个图标源) +- ✅ 生成中英文 README +- ✅ 符合 1Panel 应用商店规范 + +## 目录结构 + +``` +1panel-app-builder/ +├── SKILL.md # 技能定义文件 +├── README.md # 使用文档 +├── templates/ # 配置模板 +│ ├── data.yml.tpl # 应用元数据模板 +│ └── docker-compose.yml.tpl # 编排文件模板 +├── scripts/ # 工具脚本 +│ ├── generate-app.sh # 主生成脚本 +│ └── download-icon.sh # 图标下载工具 +└── examples/ # 使用示例 + └── example-usage.md +``` + +## 快速开始 + +### 1. 基本用法 + +```bash +# GitHub 项目 +./scripts/generate-app.sh https://github.com/alist-org/alist + +# docker-compose 文件链接 +./scripts/generate-app.sh https://raw.githubusercontent.com/.../docker-compose.yml + +# docker run 命令 +./scripts/generate-app.sh "docker run -d --name=alist -p 5244:5244 xhofe/alist:v3.45.0" + +# 本地文件 +./scripts/generate-app.sh ./my-docker-compose.yml +``` + +### 2. 输出示例 + +``` +./apps/alist/v3.45.0/ +├── data.yml # 应用元数据 +├── docker-compose.yml # 编排文件 +├── logo.png # 应用图标 +├── README.md # 中文简介 +└── README_en.md # 英文简介 +``` + +### 3. 生成的配置文件 + +#### data.yml 示例 + +```yaml +name: AList +tags: + - 实用工具 + - 云存储 +title: 支持多存储的文件列表程序和私人网盘 +description: 支持多存储的文件列表程序和私人网盘 +additionalProperties: + key: alist + name: AList + tags: + - Storage + - Tool + shortDescZh: 支持多存储的文件列表程序和私人网盘 + shortDescEn: Supporting multi-storage file listing program + website: https://alist.nn.ci/ + github: https://github.com/alist-org/alist + architectures: + - amd64 + - arm64 +``` + +#### docker-compose.yml 示例 + +```yaml +services: + alist: + container_name: ${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:5244" + volumes: + - ./data/data:/opt/alist/data + environment: + - PUID=0 + - PGID=0 + - UMASK=022 + image: xhofe/alist:v3.45.0 + labels: + createdBy: "Apps" +networks: + 1panel-network: + external: true +``` + +## 高级用法 + +### 指定输出目录 + +```bash +./scripts/generate-app.sh https://github.com/alist-org/alist ./my-apps +``` + +### 单独下载图标 + +```bash +./scripts/download-icon.sh alist ./logo.png 200 +``` + +### 图标源优先级 + +1. [Dashboard Icons](https://dashboardicons.com/icons) +2. [Simple Icons](https://simpleicons.org/) +3. [selfh.st Icons](https://selfh.st/icons/) + +## 依赖工具 + +必需: +- `bash` - 脚本执行环境 +- `curl` - HTTP 请求 +- `jq` - JSON 解析 +- `yq` - YAML 解析(可选,用于高级解析) + +可选: +- `ImageMagick` (convert) - 图标尺寸调整 +- `sips` (macOS) - 图标尺寸调整 +- `tree` - 目录树显示 + +## 配置模板说明 + +### data.yml 模板变量 + +| 变量 | 说明 | 示例 | +|------|------|------| +| `${APP_NAME}` | 应用名称 | AList | +| `${APP_KEY}` | 应用键名 | alist | +| `${TITLE_ZH}` | 中文标题 | 文件列表程序 | +| `${DESC_ZH}` | 中文描述 | 支持多存储... | +| `${TAG_1}` | 标签1 | 实用工具 | +| `${WEBSITE}` | 官网 | https://alist.nn.ci/ | +| `${GITHUB}` | GitHub | https://github.com/alist-org/alist | + +### docker-compose.yml 模板变量 + +| 变量 | 说明 | 示例 | +|------|------|------| +| `${SERVICE_NAME}` | 服务名 | alist | +| `${IMAGE}` | 镜像名 | xhofe/alist | +| `${TAG}` | 镜像标签 | v3.45.0 | +| `${PORT}` | 端口 | 5244 | + +## 下一步操作 + +生成配置后: + +1. ✅ 检查 `data.yml` 中的应用信息是否准确 +2. ✅ 补充完善应用描述和标签 +3. ✅ 替换 `logo.png` 为合适的应用图标 +4. ✅ 测试 `docker-compose.yml` 是否正常工作 +5. ✅ 提交到 1Panel 应用商店仓库 + +## 常见问题 + +### Q: 图标下载失败怎么办? + +A: 手动从以下网站下载图标: +- https://dashboardicons.com/icons?q=<应用名> +- https://simpleicons.org/?q=<应用名> +- https://selfh.st/icons/ + +### Q: 如何处理多服务应用? + +A: 生成后手动编辑 `docker-compose.yml`,添加其他服务。 + +### Q: 版本号如何确定? + +A: 默认从镜像标签提取,也可手动指定。 + +## 参考资源 + +- [1Panel 官方文档](https://1panel.cn/docs/) +- [1Panel 应用商店仓库](https://github.com/1Panel-dev/appstore) +- [Docker Compose 文档](https://docs.docker.com/compose/) + +## 许可证 + +MIT License diff --git a/skills/SKILL.md b/skills/SKILL.md new file mode 100644 index 0000000..d06696b --- /dev/null +++ b/skills/SKILL.md @@ -0,0 +1,404 @@ +--- +name: 1panel-app-builder +description: > + Build 1Panel local app store configurations from Docker deployments. Use when: + - User provides a GitHub project link and wants to add it to 1Panel app store + - User has a docker-compose.yml or docker run command and needs 1Panel app format + - User wants to create a local app for 1Panel panel + - User mentions "1Panel app", "1Panel 应用商店", "本地应用", "app store", or similar + + Supports: GitHub repos, Docker Hub images, docker-compose files, docker run commands. + Output: Complete app folder with data.yml, docker-compose.yml, logo.png, README.md + + Always use this skill for 1Panel app packaging - it knows the exact directory structure, + variable naming conventions (PANEL_APP_PORT_HTTP, CONTAINER_NAME), and 1panel-network + requirements that are easy to get wrong without guidance. +--- + +# 1Panel App Store Builder + +Transform Docker deployments into 1Panel-compatible local app store packages. + +## Understanding 1Panel App Structure + +A valid 1Panel app follows this directory structure: + +``` +app-key/ # App identifier (lowercase, hyphenated) +├── logo.png # App icon (64x64 or 128x128 recommended) +├── data.yml # App metadata (top-level) +├── README.md # Chinese documentation +├── README_en.md # English documentation (optional) +└── 1.0.0/ # Version directory (semver or "latest") + ├── data.yml # Parameter definitions (form fields) + ├── docker-compose.yml # Compose file with variable substitution + ├── data/ # Persistent data directory + └── scripts/ # Optional scripts + └── upgrade.sh # Upgrade script +``` + +## Step-by-Step Workflow + +### Step 1: Gather Source Information + +Based on user input type, extract Docker deployment details: + +**From GitHub Link:** +1. Fetch the GitHub repository README.md +2. Look for Docker installation instructions (docker run, docker-compose) +3. Extract: image name, ports, volumes, environment variables +4. Note: project name, description, official website, supported architectures + +**From docker-compose.yml:** +1. Parse all services defined +2. Extract: image versions, port mappings, volume mounts, environment variables +3. Identify the main service (typically the one with web UI) + +**From docker run command:** +1. Parse flags: `-p` (ports), `-v` (volumes), `-e` (env vars), `--name` +2. Extract the image name and tag +3. Identify exposed ports and data directories + +### Step 2: Generate App Key and Metadata + +**App Key Rules:** +- Lowercase only +- Use hyphens for multi-word names +- Keep it short but descriptive +- Examples: `alist`, `it-tools`, `n8n-zh`, `lobe-chat-data` + +**Required Metadata (top-level data.yml):** +```yaml +name: AppName # Display name (can have capitals) +tags: # Chinese tags for categorization + - 开发工具 + - 实用工具 +title: Brief Chinese description +description: Same as title +additionalProperties: + key: app-key # Must match directory name + name: AppName # Same as name above + tags: # English tags + - DevTool + - Utility + shortDescZh: Chinese description + shortDescEn: English description + description: + en: Full English description + zh: Full Chinese description + # Add more languages if available: ja, ko, ru, pt-br, ms, zh-Hant + type: website # or "runtime" for databases, "tool" for CLI tools + crossVersionUpdate: true # Usually true + limit: 0 # 0 = unlimited instances + recommend: 0 # 0-100, higher = more recommended + website: https://example.com # Official website + github: https://github.com/org/repo + document: https://docs.example.com + architectures: # Supported CPU architectures + - amd64 + - arm64 + # Add: arm/v7, arm/v6, s390x if supported +``` + +### Step 3: Define Parameters (version/data.yml) + +Parameters become UI form fields in 1Panel. Define user-configurable values: + +```yaml +additionalProperties: + formFields: + # Port parameter example + - default: 8080 + edit: true + envKey: PANEL_APP_PORT_HTTP # Variable name used in docker-compose + labelEn: Web Port + labelZh: Web端口 + required: true + rule: paramPort # Validation rule + type: number # Field type + label: # Multi-language labels + en: Web Port + zh: Web端口 + ja: Webポート + ko: Web 포트 + + # Text parameter example (for API keys, passwords, etc.) + - default: "" + edit: true + envKey: API_KEY + labelEn: API Key + labelZh: API密钥 + required: false + rule: paramCommon + type: text + + # Password parameter example + - default: "changeme" + edit: true + envKey: ADMIN_PASSWORD + labelEn: Admin Password + labelZh: 管理员密码 + required: true + rule: paramCommon + type: password + + # Select parameter example + - default: "sqlite" + edit: true + envKey: DATABASE_TYPE + labelEn: Database Type + labelZh: 数据库类型 + required: true + type: select + values: + - label: SQLite + value: sqlite + - label: PostgreSQL + value: postgres +``` + +**Parameter Types:** +- `number`: For ports, counts +- `text`: For API keys, URLs, names +- `password`: For secrets (masked input) +- `select`: For dropdown choices +- `boolean`: For toggle switches + +**Validation Rules:** +- `paramPort`: Valid port number (1-65535) +- `paramCommon`: Non-empty string +- `paramExtUrl`: Valid URL format +- Empty string: No validation + +### Step 4: Create docker-compose.yml + +Convert the original Docker deployment to use variable substitution: + +```yaml +services: + app-name: + container_name: ${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:8080" + volumes: + - ./data:/app/data + environment: + - TZ=Asia/Shanghai + image: org/image:tag + labels: + createdBy: "Apps" +networks: + 1panel-network: + external: true +``` + +**Critical Rules:** +1. Always use `${CONTAINER_NAME}` for container_name +2. Always use `restart: always` +3. Always connect to `1panel-network` (external network) +4. Port mapping uses `PANEL_APP_PORT_*` variables from data.yml +5. Volume paths use relative `./data/` for persistence +6. Add `labels: createdBy: "Apps"` +7. Keep original environment variables but use defaults + +### Step 5: Create README Files + +**README.md (Chinese):** +```markdown +# AppName + +简短描述应用的功能和特点。 + +## 功能特点 + +- 特点1 +- 特点2 +- 特点3 + +## 使用说明 + +### 默认端口 + +- Web界面: 8080 + +### 默认账号密码 + +- 用户名: admin +- 密码: changeme (请在部署后立即修改) + +### 数据目录 + +应用数据存储在 `./data` 目录。 + +## 相关链接 + +- 官方网站: https://example.com +- GitHub: https://github.com/org/repo +- 文档: https://docs.example.com +``` + +**README_en.md (English, optional):** +```markdown +# AppName + +Brief description of the application. + +## Features + +- Feature 1 +- Feature 2 +- Feature 3 + +## Usage + +### Default Port + +- Web UI: 8080 + +### Default Credentials + +- Username: admin +- Password: changeme (change after deployment) + +## Links + +- Website: https://example.com +- GitHub: https://github.com/org/repo +``` + +### Step 6: Download App Icon + +Search and download the app logo from these sources (in order): + +1. **Dashboard Icons**: https://dashboardicons.com/icons?q={app-name} +2. **Simple Icons**: https://simpleicons.org/?q={app-name} +3. **Selfh.st Icons**: https://selfh.st/icons/ + +Save as `logo.png` in the app root directory. + +### Step 7: Create Data Directory Structure + +Create the `data/` directory inside the version folder: +```bash +mkdir -p app-key/version/data +``` + +This directory will be mounted as a volume for persistent data. + +## Quality Checklist + +Before delivering the app package, verify: + +- [ ] App key is lowercase with hyphens only +- [ ] All required fields in top-level data.yml are present +- [ ] Version data.yml has proper parameter definitions +- [ ] docker-compose.yml uses variable substitution correctly +- [ ] `1panel-network` is defined as external network +- [ ] Volume paths use relative `./data/` format +- [ ] README files are created with proper documentation +- [ ] Logo file exists (logo.png) +- [ ] Version directory follows semver or uses "latest" + +## Common Patterns + +### Database Applications +```yaml +# For apps that need a database (PostgreSQL, MySQL, etc.) +# Include database as a separate service in docker-compose +services: + app: + depends_on: + - db + db: + image: postgres:15 + volumes: + - ./data/db:/var/lib/postgresql/data + environment: + - POSTGRES_DB=appdb + - POSTGRES_USER=appuser + - POSTGRES_PASSWORD=${DB_PASSWORD} +``` + +### Multi-Service Applications +```yaml +# For apps with multiple services (frontend + backend, etc.) +services: + frontend: + image: org/frontend:tag + ports: + - "${PANEL_APP_PORT_HTTP}:80" + backend: + image: org/backend:tag + ports: + - "${PANEL_APP_PORT_API}:8080" +``` + +### Applications with Reverse Proxy +```yaml +# For apps that don't expose ports directly +services: + app: + # No ports section - accessed through 1Panel's reverse proxy + expose: + - "8080" + networks: + - 1panel-network +``` + +## Automation Scripts + +Use the provided scripts for faster workflow: + +### generate-app.sh - 自动生成应用配置 + +```bash +# GitHub 项目 +./scripts/generate-app.sh https://github.com/alist-org/alist + +# docker-compose 文件 +./scripts/generate-app.sh ./docker-compose.yml + +# docker run 命令 +./scripts/generate-app.sh "docker run -d --name=nginx -p 80:80 nginx:latest" +``` + +### download-icon.sh - 下载应用图标 + +```bash +./scripts/download-icon.sh redis ./logo.png 200 +``` + +### validate-app.sh - 验证配置完整性 + +```bash +./scripts/validate-app.sh ./apps/myapp +``` + +## Reference Files + +- `references/1panel-examples.md` - 完整的真实应用配置示例(AList、NocoDB等) +- `examples/example-usage.md` - 使用示例和工作流程 + +## Troubleshooting + +**Issue**: App won't start +- Check if ports are already in use +- Verify volume permissions +- Check container logs: `docker logs ${CONTAINER_NAME}` + +**Issue**: Data not persisting +- Ensure volumes are correctly mapped to `./data/` +- Check directory permissions + +**Issue**: Environment variables not working +- Verify variable names match between data.yml and docker-compose.yml +- Check default values are appropriate + +**Issue**: 图标下载失败 +- 从以下网站手动下载: + - https://dashboardicons.com/icons?q={app-name} + - https://simpleicons.org/?q={app-name} + - https://selfh.st/icons/ diff --git a/skills/evals/evals.json b/skills/evals/evals.json new file mode 100644 index 0000000..232ca5b --- /dev/null +++ b/skills/evals/evals.json @@ -0,0 +1,23 @@ +{ + "skill_name": "1panel-app-builder", + "evals": [ + { + "id": 1, + "prompt": "帮我把这个项目打包成1Panel本地应用:https://github.com/0xJacky/nginx-ui 这是一个Nginx配置管理工具,用docker部署,默认端口是9000", + "expected_output": "Create a complete 1Panel app package for nginx-ui with proper directory structure, data.yml metadata, docker-compose.yml with variable substitution, README files, and placeholder logo", + "files": [] + }, + { + "id": 2, + "prompt": "我有一个docker-compose.yml文件,内容如下:\n```yaml\nversion: '3'\nservices:\n memos:\n image: neosmemo/memos:latest\n container_name: memos\n ports:\n - \"5230:5230\"\n volumes:\n - ./memos_data:/var/opt/memos\n restart: always\n```\n帮我生成1Panel应用商店的配置", + "expected_output": "Parse the docker-compose file and generate 1Panel app package for memos with converted docker-compose.yml using PANEL_APP_PORT_HTTP variable, proper data.yml with port parameter definition, and supporting files", + "files": [] + }, + { + "id": 3, + "prompt": "docker run -d --name umami -p 3000:3000 -e DATABASE_URL=postgresql://user:pass@db:5432/umami ghcr.io/umami-software/umami:postgresql-latest\n请帮我把这个umami网站统计工具做成1Panel应用", + "expected_output": "Parse the docker run command, extract port 3000, environment variable DATABASE_URL, and generate 1Panel app package with appropriate parameter fields for database URL configuration", + "files": [] + } + ] +} diff --git a/skills/examples/example-usage.md b/skills/examples/example-usage.md new file mode 100644 index 0000000..eaf530b --- /dev/null +++ b/skills/examples/example-usage.md @@ -0,0 +1,237 @@ +# 使用示例 + +## 示例 1:从 GitHub 项目生成 + +```bash +# 输入 +./scripts/generate-app.sh https://github.com/alist-org/alist + +# 输出 +./apps/alist/v3.45.0/ +├── data.yml +├── docker-compose.yml +├── logo.png +├── README.md +└── README_en.md +``` + +### 生成的 data.yml + +```yaml +name: AList +tags: + - 实用工具 + - 云存储 +title: AList - 文件列表程序 +description: 支持多存储的文件列表程序和私人网盘 +additionalProperties: + key: alist + name: AList + tags: + - Storage + - Tool + shortDescZh: 支持多存储的文件列表程序和私人网盘 + shortDescEn: Supporting multi-storage file listing program + website: https://alist.nn.ci/ + github: https://github.com/alist-org/alist +``` + +### 生成的 docker-compose.yml + +```yaml +services: + alist: + container_name: ${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:5244" + volumes: + - ./data/data:/opt/alist/data + environment: + - PUID=0 + - PGID=0 + - UMASK=022 + image: xhofe/alist:v3.45.0 + labels: + createdBy: "Apps" +networks: + 1panel-network: + external: true +``` + +--- + +## 示例 2:从 docker run 命令生成 + +```bash +# 输入 +./scripts/generate-app.sh "docker run -d --name=nginx -p 80:80 nginx:latest" + +# 输出 +./apps/nginx/latest/ +├── data.yml +├── docker-compose.yml +├── logo.png +├── README.md +└── README_en.md +``` + +### 生成的 docker-compose.yml + +```yaml +services: + nginx: + container_name: ${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:80" + volumes: + - ./data/data:/app/data + environment: + - PUID=0 + - PGID=0 + - UMASK=022 + image: nginx:latest + labels: + createdBy: "Apps" +networks: + 1panel-network: + external: true +``` + +--- + +## 示例 3:从 docker-compose 文件链接生成 + +```bash +# 输入 +./scripts/generate-app.sh https://raw.githubusercontent.com/portainer/portainer/develop/docker-compose.yml + +# 输出 +./apps/portainer/latest/ +├── data.yml +├── docker-compose.yml +├── logo.png +├── README.md +└── README_en.md +``` + +--- + +## 示例 4:从本地文件生成 + +```bash +# 创建测试文件 +cat > /tmp/test-compose.yml << 'EOF' +version: '3' +services: + redis: + image: redis:7-alpine + ports: + - "6379:6379" + volumes: + - redis-data:/data + +volumes: + redis-data: +EOF + +# 运行脚本 +./scripts/generate-app.sh /tmp/test-compose.yml ./my-apps + +# 输出 +./my-apps/redis/7-alpine/ +├── data.yml +├── docker-compose.yml +├── logo.png +├── README.md +└── README_en.md +``` + +--- + +## 示例 5:单独下载图标 + +```bash +# 下载 Redis 图标 +./scripts/download-icon.sh redis ./redis-logo.png 200 + +# 输出 +✓ 从 Simple Icons 下载成功 +✓ 图标下载完成: ./redis-logo.png +``` + +--- + +## 示例 6:自定义输出目录 + +```bash +# 输出到指定目录 +./scripts/generate-app.sh https://github.com/portainer/portainer ~/my-1panel-apps + +# 输出 +/Users/username/my-1panel-apps/portainer/latest/ +├── data.yml +├── docker-compose.yml +├── logo.png +├── README.md +└── README_en.md +``` + +--- + +## 完整工作流程示例 + +```bash +# 1. 生成应用配置 +./scripts/generate-app.sh https://github.com/alist-org/alist + +# 2. 查看生成的文件 +cd ./apps/alist/v3.45.0 +cat data.yml +cat docker-compose.yml + +# 3. 手动优化配置 +# - 补充应用描述 +# - 添加合适的标签 +# - 替换 logo.png + +# 4. 测试 docker-compose +docker-compose up -d + +# 5. 确认无误后,提交到应用商店 +git add . +git commit -m "feat: add alist app" +git push +``` + +--- + +## 故障排除 + +### 问题:图标下载失败 + +```bash +# 手动下载图标 +# 访问:https://dashboardicons.com/icons?q=alist +# 下载后保存为 logo.png +``` + +### 问题:端口冲突 + +```bash +# 修改 docker-compose.yml 中的端口映射 +ports: + - "${PANEL_APP_PORT_HTTP}:5244" # 修改此处 +``` + +### 问题:镜像版本不对 + +```bash +# 手动编辑 docker-compose.yml +image: xhofe/alist:v3.45.0 # 修改版本号 +``` diff --git a/skills/references/1panel-examples.md b/skills/references/1panel-examples.md new file mode 100644 index 0000000..d96d5a6 --- /dev/null +++ b/skills/references/1panel-examples.md @@ -0,0 +1,394 @@ +# 1Panel 应用商店参考示例 + +本文件包含从 1Panel-Appstore 和 appstore 官方仓库提取的实际应用配置示例。 + +## 目录 + +1. [简单单服务应用](#简单单服务应用) +2. [带数据库的应用](#带数据库的应用) +3. [多端口应用](#多端口应用) +4. [多服务应用](#多服务应用) +5. [常见标签分类](#常见标签分类) + +--- + +## 简单单服务应用 + +### 示例:AList + +**目录结构:** +``` +alist/ +├── logo.png +├── data.yml # 顶层元数据 +├── README.md +├── README_en.md +└── 3.45.0/ # 版本目录 + ├── data.yml # 参数定义 + ├── docker-compose.yml + └── data/ # 数据目录 +``` + +**顶层 data.yml:** +```yaml +name: AList +tags: + - 实用工具 + - 云存储 +title: 支持多存储的文件列表程序和私人网盘 +description: 支持多存储的文件列表程序和私人网盘 +additionalProperties: + key: alist + name: AList + tags: + - Storage + - Tool + shortDescZh: 支持多存储的文件列表程序和私人网盘 + shortDescEn: Supporting multi-storage file listing program and private cloud storage + description: + en: Supporting multi-storage file listing program and private cloud storage + ja: 複数ストレージのファイルリスト表示プログラムとプライベートクラウドストレージのサポート + ms: Menyokong program senarai fail multi-penyimpanan dan penyimpanan awan peribadi + pt-br: Suporte para programa de listagem de arquivos em múltiplos armazenamentos e armazenamento em nuvem privado + ru: Поддержка программы отображения файлов в нескольких хранилищах и частного облачного хранилища + ko: 다중 저장소 파일 목록 프로그램 및 개인 클라우드 저장소 지원 + zh-Hant: 支援多存儲檔案列出程序和私人雲端空間 + zh: 支持多存储文件列出程序和私有云存储 + type: website + crossVersionUpdate: true + limit: 0 + recommend: 65 + website: https://alist.nn.ci/ + github: https://github.com/alist-org/alist + document: https://alist.nn.ci/zh/guide/ + architectures: + - amd64 + - arm64 + - arm/v7 + - arm/v6 + - s390x +``` + +**版本 data.yml(参数定义):** +```yaml +additionalProperties: + formFields: + - default: 5244 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: WebUI Port + labelZh: 网页端口 + required: true + rule: paramPort + type: number + label: + en: WebUI Port + ja: WebUI ポート + ms: Port WebUI + pt-br: Porta WebUI + ru: Порт WebUI + ko: WebUI 포트 + zh-Hant: WebUI 埠 + zh: WebUI 端口 + - default: 5426 + edit: true + envKey: PANEL_APP_PORT_S3 + labelEn: S3 Port + labelZh: S3 端口 + required: true + rule: paramPort + type: number + label: + en: S3 Port + ja: S3 ポート + ms: Port S3 + pt-br: Porta S3 + ru: Порт S3 + ko: S3 포트 + zh-Hant: S3 埠 + zh: S3 端口 +``` + +**docker-compose.yml:** +```yaml +services: + alist: + container_name: ${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:5244" + - "${PANEL_APP_PORT_S3}:5426" + volumes: + - ./data/data:/opt/alist/data + - ./data/mnt:/mnt/data + environment: + - PUID=0 + - PGID=0 + - UMASK=022 + image: xhofe/alist:v3.45.0 + labels: + createdBy: "Apps" +networks: + 1panel-network: + external: true +``` + +--- + +## 带数据库的应用 + +### 示例:NocoDB(PostgreSQL) + +**docker-compose.yml:** +```yaml +services: + nocodb: + container_name: ${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:8080" + volumes: + - ./data/nocodb:/usr/app/data + environment: + - NC_DB=pg://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME} + - NC_AUTH_JWT_SECRET=${JWT_SECRET} + depends_on: + db: + condition: service_healthy + image: nocodb/nocodb:0.258.2 + labels: + createdBy: "Apps" + db: + image: postgres:16-alpine + restart: always + networks: + - 1panel-network + volumes: + - ./data/postgres:/var/lib/postgresql/data + environment: + - POSTGRES_DB=${DB_NAME} + - POSTGRES_USER=${DB_USER} + - POSTGRES_PASSWORD=${DB_PASSWORD} + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"] + interval: 5s + timeout: 5s + retries: 5 +networks: + 1panel-network: + external: true +``` + +**参数定义:** +```yaml +- default: 8080 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: Web Port + labelZh: Web端口 + required: true + rule: paramPort + type: number +- default: nocodb + edit: true + envKey: DB_NAME + labelEn: Database Name + labelZh: 数据库名称 + required: true + rule: paramCommon + type: text +- default: nocodb + edit: true + envKey: DB_USER + labelEn: Database User + labelZh: 数据库用户 + required: true + rule: paramCommon + type: text +- default: nocodb_password + edit: true + envKey: DB_PASSWORD + labelEn: Database Password + labelZh: 数据库密码 + required: true + rule: paramCommon + type: password +``` + +--- + +## 多端口应用 + +### 示例:Nginx Proxy Manager + +**参数定义:** +```yaml +formFields: + - default: 81 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: Admin Port + labelZh: 管理端口 + required: true + rule: paramPort + type: number + - default: 80 + edit: true + envKey: PANEL_APP_PORT_NGINX_HTTP + labelEn: HTTP Port + labelZh: HTTP端口 + required: true + rule: paramPort + type: number + - default: 443 + edit: true + envKey: PANEL_APP_PORT_NGINX_HTTPS + labelEn: HTTPS Port + labelZh: HTTPS端口 + required: true + rule: paramPort + type: number +``` + +**docker-compose.yml:** +```yaml +services: + app: + container_name: ${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:81" + - "${PANEL_APP_PORT_NGINX_HTTP}:80" + - "${PANEL_APP_PORT_NGINX_HTTPS}:443" + volumes: + - ./data/data:/data + - ./data/letsencrypt:/etc/letsencrypt + image: jc21/nginx-proxy-manager:latest + labels: + createdBy: "Apps" +networks: + 1panel-network: + external: true +``` + +--- + +## 多服务应用 + +### 示例:Dify(完整栈) + +**docker-compose.yml:** +```yaml +services: + api: + container_name: ${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:80" + volumes: + - ./data/storage:/app/api/storage + environment: + - MODE=api + - SECRET_KEY=${SECRET_KEY} + depends_on: + - db + - redis + image: langgenius/dify-api:latest + labels: + createdBy: "Apps" + worker: + restart: always + networks: + - 1panel-network + environment: + - MODE=worker + - SECRET_KEY=${SECRET_KEY} + depends_on: + - db + - redis + image: langgenius/dify-api:latest + db: + image: postgres:15-alpine + restart: always + networks: + - 1panel-network + volumes: + - ./data/postgres:/var/lib/postgresql/data + environment: + - POSTGRES_USER=${DB_USER} + - POSTGRES_PASSWORD=${DB_PASSWORD} + - POSTGRES_DB=${DB_NAME} + redis: + image: redis:7-alpine + restart: always + networks: + - 1panel-network + volumes: + - ./data/redis:/data +networks: + 1panel-network: + external: true +``` + +--- + +## 常见标签分类 + +### 中文标签(tags) + +| 分类 | 标签 | +|------|------| +| 开发工具 | 开发工具 | +| 实用工具 | 实用工具 | +| 文档与笔记 | 文档与笔记 | +| 云存储 | 云存储 | +| 数据库 | 数据库 | +| 监控运维 | 监控运维 | +| 安全工具 | 安全工具 | +| 网络工具 | 网络工具 | +| 建站工具 | 建站工具 | +| AI/LLM | AI/LLM | +| 容器管理 | 容器管理 | + +### 英文标签(additionalProperties.tags) + +| 分类 | 标签 | +|------|------| +| 开发工具 | DevTool | +| 实用工具 | Utility | +| 文档与笔记 | Note | +| 云存储 | Storage | +| 数据库 | Database | +| 监控运维 | Monitor | +| 安全工具 | Security | +| 网络工具 | Network | +| 建站工具 | Website | +| AI/LLM | AI | +| 容器管理 | Container | + +### 应用类型(type) + +| 类型 | 说明 | +|------|------| +| website | Web应用(默认) | +| runtime | 运行时环境(数据库等) | +| tool | 命令行工具 | + +--- + +## 最佳实践 + +1. **版本号**:使用实际的镜像标签版本,如 `v3.45.0`,而非 `latest` +2. **推荐值**:优质应用设置 recommend: 60-80,一般应用 30-50 +3. **架构支持**:至少支持 amd64 和 arm64 +4. **多语言**:至少提供中英文描述,推荐添加日韩俄等语言 +5. **数据持久化**:使用 `./data/` 目录,避免绝对路径 +6. **环境变量**:敏感信息使用 password 类型参数 diff --git a/skills/scripts/download-icon.sh b/skills/scripts/download-icon.sh new file mode 100644 index 0000000..64cbfd6 --- /dev/null +++ b/skills/scripts/download-icon.sh @@ -0,0 +1,195 @@ +#!/usr/bin/env bash + +# ============================================================================= +# Icon Downloader - 从多个图标源下载应用图标 +# 支持的源:dashboardicons、simpleicons、selfh.st +# ============================================================================= + +set -euo pipefail + +# 颜色定义 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +# 图标源配置 +DASHBOARD_ICONS="https://dashboardicons.com/icons" +SIMPLE_ICONS="https://cdn.simpleicons.org" +SELFHST_ICONS="https://selfh.st/icons" + +# 默认输出尺寸 +DEFAULT_SIZE=200 + +# 打印帮助 +print_help() { + cat << EOF +${BLUE}Icon Downloader${NC} - 从多个图标源下载应用图标 + +${YELLOW}用法:${NC} + $0 <应用名称> [输出文件] [尺寸] + +${YELLOW}参数:${NC} + 应用名称 - 应用的名称(如 alist, nginx, redis) + 输出文件 - 图标保存路径(默认: ./logo.png) + 尺寸 - 图标尺寸(默认: 200x200) + +${YELLOW}图标源优先级:${NC} + 1. Dashboard Icons (dashboardicons.com) + 2. Simple Icons (simpleicons.org) + 3. selfh.st Icons (selfh.st) + +${YELLOW}示例:${NC} + $0 alist ./logo.png 200 + $0 nginx /tmp/nginx-icon.png + $0 redis +EOF +} + +# 日志函数 +log_info() { echo -e "${GREEN}[INFO]${NC} $1"; } +log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } +log_error() { echo -e "${RED}[ERROR]${NC} $1"; } + +# 下载并调整图标尺寸 +download_and_resize() { + local url="$1" + local output="$2" + local size="$3" + + if curl -sSL -o "$output" "$url" 2>/dev/null && [[ -s "$output" ]]; then + # 检查是否安装了 ImageMagick 或 sips (macOS) + if command -v convert &>/dev/null; then + convert "$output" -resize "${size}x${size}" "$output" 2>/dev/null || true + elif command -v sips &>/dev/null; then + sips -z "$size" "$size" "$output" &>/dev/null || true + fi + return 0 + fi + return 1 +} + +# 尝试从各图标源下载 +try_download_icon() { + local app_name="$1" + local output="$2" + local size="$3" + + local name_lower + name_lower=$(echo "$app_name" | tr '[:upper:]' '[:lower:]' | tr -d ' ') + + log_info "尝试下载图标: $app_name" + + # 1. Dashboard Icons + log_info "尝试 Dashboard Icons..." + local dashboard_url="${DASHBOARD_ICONS}/${name_lower}.png" + if download_and_resize "$dashboard_url" "$output" "$size"; then + log_info "✓ 从 Dashboard Icons 下载成功" + return 0 + fi + + # 尝试带 -dark 后缀 + if download_and_resize "${DASHBOARD_ICONS}/${name_lower}-dark.png" "$output" "$size"; then + log_info "✓ 从 Dashboard Icons (dark) 下载成功" + return 0 + fi + + # 2. Simple Icons + log_info "尝试 Simple Icons..." + local simple_url="${SIMPLE_ICONS}/${name_lower}" + if download_and_resize "$simple_url" "$output" "$size"; then + log_info "✓ 从 Simple Icons 下载成功" + return 0 + fi + + # Simple Icons 也支持 SVG + if curl -sSL -o "${output%.png}.svg" "${SIMPLE_ICONS}/${name_lower}" 2>/dev/null && [[ -s "${output%.png}.svg" ]]; then + log_info "✓ 从 Simple Icons 下载 SVG 成功" + # 如果有 SVG,尝试转换 + if command -v convert &>/dev/null; then + convert "${output%.png}.svg" -resize "${size}x${size}" "$output" 2>/dev/null || true + if [[ -s "$output" ]]; then + rm -f "${output%.png}.svg" + return 0 + fi + fi + fi + + # 3. selfh.st Icons + log_info "尝试 selfh.st Icons..." + local selfhst_url="${SELFHST_ICONS}/${name_lower}.png" + if download_and_resize "$selfhst_url" "$output" "$size"; then + log_info "✓ 从 selfh.st Icons 下载成功" + return 0 + fi + + return 1 +} + +# 创建占位图标 +create_placeholder() { + local output="$1" + local size="$2" + + log_warn "创建占位图标" + + # 使用 ImageMagick 创建简单的占位图标 + if command -v convert &>/dev/null; then + convert -size "${size}x${size}" xc:'#4A90E2' \ + -gravity center \ + -pointsize 48 \ + -fill white \ + -annotate 0 "?" \ + "$output" 2>/dev/null + return $? + fi + + # macOS sips 方式 + if command -v sips &>/dev/null; then + # 创建一个简单的 1x1 像素 PNG 然后放大 + local temp_file="/tmp/placeholder_${RANDOM}.png" + echo -n "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==" | base64 -d > "$temp_file" + sips -z "$size" "$size" "$temp_file" --out "$output" &>/dev/null + rm -f "$temp_file" + return $? + fi + + # 最简单的占位符 + echo -n "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==" | base64 -d > "$output" +} + +# 主函数 +main() { + local app_name="${1:-}" + local output="${2:-./logo.png}" + local size="${3:-$DEFAULT_SIZE}" + + # 参数检查 + if [[ -z "$app_name" ]] || [[ "$app_name" == "-h" ]] || [[ "$app_name" == "--help" ]]; then + print_help + exit 0 + fi + + # 确保输出目录存在 + local output_dir + output_dir=$(dirname "$output") + mkdir -p "$output_dir" + + # 尝试下载 + if try_download_icon "$app_name" "$output" "$size"; then + log_info "${GREEN}✓ 图标下载完成: $output${NC}" + exit 0 + fi + + # 下载失败,创建占位符 + create_placeholder "$output" "$size" + log_warn "未找到图标,已创建占位符: $output" + log_info "请手动从以下网站下载合适的图标:" + echo " - https://dashboardicons.com/icons?q=${app_name}" + echo " - https://simpleicons.org/?q=${app_name}" + echo " - https://selfh.st/icons/" +} + +# 执行主函数 +main "$@" diff --git a/skills/scripts/generate-app.sh b/skills/scripts/generate-app.sh new file mode 100644 index 0000000..5bd3e89 --- /dev/null +++ b/skills/scripts/generate-app.sh @@ -0,0 +1,416 @@ +#!/usr/bin/env bash + +# ============================================================================= +# 1Panel App Builder - 主生成脚本 +# 功能:从 GitHub 项目、docker-compose、docker run 命令生成 1Panel APP 配置 +# 用法:./generate-app.sh <输入源> [输出目录] +# ============================================================================= + +set -euo pipefail + +# 颜色定义 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# 默认配置 +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +TEMPLATE_DIR="${SCRIPT_DIR}/../templates" +OUTPUT_BASE="${2:-./apps}" +ICON_SOURCES=( + "https://dashboardicons.com/icons" + "https://simpleicons.org/icons" + "https://selfh.st/icons" +) + +# 打印帮助 +print_help() { + cat << EOF +${BLUE}1Panel App Builder${NC} - 快速生成 1Panel 应用配置 + +${YELLOW}用法:${NC} + $0 <输入源> [输出目录] + +${YELLOW}输入源支持:${NC} + 1. GitHub 项目链接 + 例如: https://github.com/alist-org/alist + + 2. docker-compose.yml 文件链接 + 例如: https://raw.githubusercontent.com/alist-org/alist/master/docker-compose.yml + + 3. docker run 命令 + 例如: "docker run -d --name=alist -p 5244:5244 xhofe/alist:v3.45.0" + + 4. 本地文件路径 + 例如: ./myapp/docker-compose.yml + +${YELLOW}输出:${NC} + 在指定目录(默认 ./apps)下生成: + // + ├── data.yml # 应用元数据 + ├── docker-compose.yml # 编排文件 + ├── logo.png # 应用图标 + ├── README.md # 中文简介 + └── README_en.md # 英文简介 + +${YELLOW}示例:${NC} + $0 https://github.com/alist-org/alist + $0 ./my-docker-compose.yml ./my-apps + $0 "docker run -d --name nginx -p 80:80 nginx:latest" +EOF +} + +# 日志函数 +log_info() { echo -e "${GREEN}[INFO]${NC} $1"; } +log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } +log_error() { echo -e "${RED}[ERROR]${NC} $1"; } + +# 检测输入类型 +detect_input_type() { + local input="$1" + + # GitHub 项目链接 + if [[ "$input" =~ ^https?://github\.com/[^/]+/[^/]+ ]]; then + echo "github" + return 0 + fi + + # docker-compose 文件链接 + if [[ "$input" =~ ^https?://.*\.ya?ml$ ]] || [[ "$input" =~ docker-compose ]]; then + echo "compose_url" + return 0 + fi + + # docker run 命令 + if [[ "$input" =~ ^docker\ run ]] || [[ "$input" =~ ^docker\ run$ ]]; then + echo "docker_run" + return 0 + fi + + # 本地文件 + if [[ -f "$input" ]]; then + if [[ "$input" =~ \.ya?ml$ ]]; then + echo "compose_file" + return 0 + fi + fi + + echo "unknown" +} + +# 从 GitHub 项目提取信息 +extract_from_github() { + local github_url="$1" + local api_url temp_dir + + log_info "正在从 GitHub 项目提取信息: $github_url" + + # 转换为 API URL + # https://github.com/owner/repo -> https://api.github.com/repos/owner/repo + api_url="${github_url/github\.com/api.github.com\/repos}" + + # 获取项目信息 + local repo_info + repo_info=$(curl -s "$api_url" 2>/dev/null || echo "{}") + + # 提取字段 + APP_NAME=$(echo "$repo_info" | jq -r '.name // empty') + APP_KEY=$(echo "$APP_NAME" | tr '[:upper:]' '[:lower:]') + DESCRIPTION=$(echo "$repo_info" | jq -r '.description // empty') + GITHUB="$github_url" + WEBSITE=$(echo "$repo_info" | jq -r '.homepage // empty') + if [[ -z "$WEBSITE" ]]; then + WEBSITE="$github_url" + fi + + # 尝试查找 docker-compose.yml + local owner_repo + owner_repo=$(echo "$github_url" | sed -E 's|https?://github.com/||') + COMPOSE_URL="https://raw.githubusercontent.com/${owner_repo}/main/docker-compose.yml" + + log_info "应用名称: $APP_NAME" + log_info "描述: $DESCRIPTION" +} + +# 从 docker-compose 提取信息 +extract_from_compose() { + local compose_content="$1" + + log_info "正在解析 docker-compose 内容" + + # 提取服务名 + SERVICE_NAME=$(echo "$compose_content" | yq '.services | keys | .[0]' 2>/dev/null || echo "") + + # 提取镜像 + local image + image=$(echo "$compose_content" | yq ".services.${SERVICE_NAME}.image" 2>/dev/null || echo "") + + # 解析镜像名和标签 + if [[ "$image" =~ ^([^:]+):(.+)$ ]]; then + IMAGE="${BASH_REMATCH[1]}" + TAG="${BASH_REMATCH[2]}" + else + IMAGE="$image" + TAG="latest" + fi + + # 提取端口 + PORT=$(echo "$compose_content" | yq ".services.${SERVICE_NAME}.ports[0]" 2>/dev/null | grep -oE '[0-9]+$' || echo "8080") + + log_info "服务名: $SERVICE_NAME" + log_info "镜像: $IMAGE:$TAG" + log_info "端口: $PORT" +} + +# 从 docker run 命令提取信息 +extract_from_docker_run() { + local cmd="$1" + + log_info "正在解析 docker run 命令" + + # 提取镜像 + IMAGE=$(echo "$cmd" | grep -oE '[^ ]+:[^ ]+$' | cut -d':' -f1 || echo "") + TAG=$(echo "$cmd" | grep -oE '[^ ]+:[^ ]+$' | cut -d':' -f2 || echo "latest") + + # 提取容器名 + SERVICE_NAME=$(echo "$cmd" | grep -oE '\-\-name[= ]+[^ ]+' | awk '{print $NF}' || echo "app") + + # 提取端口 + PORT=$(echo "$cmd" | grep -oE '\-p[= ]+[0-9]+:[0-9]+' | grep -oE '[0-9]+$' || echo "8080") + + APP_NAME="$SERVICE_NAME" + APP_KEY=$(echo "$SERVICE_NAME" | tr '[:upper:]' '[:lower:]') + + log_info "应用名称: $APP_NAME" + log_info "镜像: $IMAGE:$TAG" + log_info "端口: $PORT" +} + +# 生成 data.yml +generate_data_yml() { + local output_file="$1" + + log_info "生成 data.yml: $output_file" + + cat > "$output_file" << EOF +name: ${APP_NAME:-MyApp} +tags: + - 实用工具 + - 容器 +title: ${APP_NAME:-MyApp} - 容器应用 +description: ${DESCRIPTION:-自动生成的应用配置} +additionalProperties: + key: ${APP_KEY:-myapp} + name: ${APP_NAME:-MyApp} + tags: + - Tool + - Container + shortDescZh: ${DESCRIPTION:-自动生成的应用配置} + shortDescEn: ${DESCRIPTION:-Auto-generated application configuration} + description: + en: ${DESCRIPTION:-Auto-generated application configuration} + ja: ${DESCRIPTION:-Auto-generated application configuration} + ms: ${DESCRIPTION:-Auto-generated application configuration} + pt-br: ${DESCRIPTION:-Auto-generated application configuration} + ru: ${DESCRIPTION:-Auto-generated application configuration} + ko: ${DESCRIPTION:-Auto-generated application configuration} + zh-Hant: ${DESCRIPTION:-Auto-generated application configuration} + zh: ${DESCRIPTION:-自动生成的应用配置} + type: website + crossVersionUpdate: true + limit: 0 + recommend: 50 + website: ${WEBSITE:-https://example.com} + github: ${GITHUB:-https://github.com} + document: ${GITHUB:-https://github.com} + architectures: + - amd64 + - arm64 +EOF +} + +# 生成 docker-compose.yml +generate_docker_compose() { + local output_file="$1" + + log_info "生成 docker-compose.yml: $output_file" + + cat > "$output_file" << EOF +services: + ${SERVICE_NAME:-app}: + container_name: \${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + ports: + - "\${PANEL_APP_PORT_HTTP}:${PORT:-8080}" + volumes: + - ./data/data:/app/data + environment: + - PUID=0 + - PGID=0 + - UMASK=022 + image: ${IMAGE:-app}:${TAG:-latest} + labels: + createdBy: "Apps" +networks: + 1panel-network: + external: true +EOF +} + +# 生成 README +generate_readme() { + local output_dir="$1" + + log_info "生成 README 文件" + + cat > "${output_dir}/README.md" << EOF +# ${APP_NAME:-MyApp} + +## 简介 + +${DESCRIPTION:-这是一个自动生成的应用配置。} + +## 使用说明 + +1. 在 1Panel 应用商店中安装此应用 +2. 配置相关参数 +3. 启动应用 + +## 更多信息 + +- 官网: ${WEBSITE:-https://example.com} +- GitHub: ${GITHUB:-https://github.com} +- 文档: ${GITHUB:-https://github.com} +EOF + + cat > "${output_dir}/README_en.md" << EOF +# ${APP_NAME:-MyApp} + +## Introduction + +${DESCRIPTION:-This is an auto-generated application configuration.} + +## Usage + +1. Install this app from 1Panel App Store +2. Configure parameters +3. Start the application + +## More Information + +- Website: ${WEBSITE:-https://example.com} +- GitHub: ${GITHUB:-https://github.com} +- Documentation: ${GITHUB:-https://github.com} +EOF +} + +# 下载图标 +download_icon() { + local app_name="$1" + local output_file="$2" + local icon_name + + icon_name=$(echo "$app_name" | tr '[:upper:]' '[:lower:]') + + log_info "尝试下载图标: $app_name" + + # 尝试各个图标源 + for source in "${ICON_SOURCES[@]}"; do + local icon_url="${source}/${icon_name}.png" + if curl -sSL -o "$output_file" "$icon_url" 2>/dev/null && [[ -s "$output_file" ]]; then + log_info "图标下载成功: $icon_url" + return 0 + fi + done + + log_warn "未找到图标,使用占位符" + # 创建简单的占位图标(1x1 像素 PNG) + echo -n "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==" | base64 -d > "$output_file" +} + +# 主函数 +main() { + local input="$1" + local input_type + + # 参数检查 + if [[ -z "$input" ]] || [[ "$input" == "-h" ]] || [[ "$input" == "--help" ]]; then + print_help + exit 0 + fi + + log_info "开始处理: $input" + + # 检测输入类型 + input_type=$(detect_input_type "$input") + log_info "检测到输入类型: $input_type" + + # 根据类型提取信息 + case "$input_type" in + github) + extract_from_github "$input" + COMPOSE_CONTENT=$(curl -s "$COMPOSE_URL" 2>/dev/null || echo "") + if [[ -n "$COMPOSE_CONTENT" ]]; then + extract_from_compose "$COMPOSE_CONTENT" + fi + ;; + compose_url) + COMPOSE_CONTENT=$(curl -s "$input" 2>/dev/null) + extract_from_compose "$COMPOSE_CONTENT" + APP_NAME="$SERVICE_NAME" + APP_KEY=$(echo "$SERVICE_NAME" | tr '[:upper:]' '[:lower:]') + ;; + compose_file) + COMPOSE_CONTENT=$(cat "$input") + extract_from_compose "$COMPOSE_CONTENT" + APP_NAME="$SERVICE_NAME" + APP_KEY=$(echo "$SERVICE_NAME" | tr '[:upper:]' '[:lower:]') + ;; + docker_run) + extract_from_docker_run "$input" + ;; + *) + log_error "无法识别的输入类型: $input" + exit 1 + ;; + esac + + # 创建输出目录 + local output_dir="${OUTPUT_BASE}/${APP_KEY}/${TAG:-latest}" + mkdir -p "$output_dir" + + log_info "输出目录: $output_dir" + + # 生成文件 + generate_data_yml "${output_dir}/data.yml" + generate_docker_compose "${output_dir}/docker-compose.yml" + generate_readme "$output_dir" + download_icon "$APP_NAME" "${output_dir}/logo.png" + + # 生成上级目录的 data.yml + generate_data_yml "${OUTPUT_BASE}/${APP_KEY}/data.yml" + cp "${output_dir}/logo.png" "${OUTPUT_BASE}/${APP_KEY}/logo.png" 2>/dev/null || true + cp "${output_dir}/README.md" "${OUTPUT_BASE}/${APP_KEY}/README.md" 2>/dev/null || true + cp "${output_dir}/README_en.md" "${OUTPUT_BASE}/${APP_KEY}/README_en.md" 2>/dev/null || true + + # 输出结果 + echo "" + log_info "${GREEN}✓ 生成完成!${NC}" + echo "" + echo "目录结构:" + tree "${OUTPUT_BASE}/${APP_KEY}" 2>/dev/null || find "${OUTPUT_BASE}/${APP_KEY}" -type f + echo "" + log_info "下一步:" + echo " 1. 检查生成的配置文件" + echo " 2. 补充完善应用描述和标签" + echo " 3. 替换 logo.png 为合适的应用图标" + echo " 4. 测试 docker-compose.yml" + echo " 5. 提交到应用商店仓库" +} + +# 执行主函数 +main "$@" diff --git a/skills/scripts/validate-app.sh b/skills/scripts/validate-app.sh new file mode 100644 index 0000000..05995cb --- /dev/null +++ b/skills/scripts/validate-app.sh @@ -0,0 +1,257 @@ +#!/usr/bin/env bash + +# ============================================================================= +# 1Panel App Validator - 验证生成的配置是否符合规范 +# 用法:./validate-app.sh +# ============================================================================= + +set -euo pipefail + +# 颜色定义 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +# 计数器 +ERRORS=0 +WARNINGS=0 + +# 打印帮助 +print_help() { + cat << EOF +${BLUE}1Panel App Validator${NC} - 验证应用配置是否符合规范 + +${YELLOW}用法:${NC} + $0 + +${YELLOW}检查项目:${NC} + - 目录结构完整性 + - data.yml 格式正确性 + - docker-compose.yml 变量使用 + - logo.png 是否存在 + - README 文件是否存在 + +${YELLOW}示例:${NC} + $0 ./apps/alist +EOF +} + +# 日志函数 +log_info() { echo -e "${GREEN}[INFO]${NC} $1"; } +log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; ((WARNINGS++)); } +log_error() { echo -e "${RED}[ERROR]${NC} $1"; ((ERRORS++)); } + +# 检查目录结构 +check_directory_structure() { + local app_dir="$1" + local app_name + app_name=$(basename "$app_dir") + + log_info "检查目录结构: $app_name" + + # 检查必需文件 + if [[ ! -f "$app_dir/data.yml" ]]; then + log_error "缺少顶层 data.yml" + fi + + if [[ ! -f "$app_dir/logo.png" ]]; then + log_error "缺少 logo.png" + fi + + if [[ ! -f "$app_dir/README.md" ]]; then + log_warn "缺少 README.md(推荐)" + fi + + # 查找版本目录 + local version_dirs + version_dirs=$(find "$app_dir" -maxdepth 1 -type d -name "v*" -o -name "latest" -o -name "[0-9]*" 2>/dev/null || echo "") + + if [[ -z "$version_dirs" ]]; then + log_error "未找到版本目录(如 1.0.0/, v1.0.0/, latest/)" + return 1 + fi + + # 检查每个版本目录 + for version_dir in $version_dirs; do + log_info "检查版本目录: $(basename "$version_dir")" + + if [[ ! -f "$version_dir/data.yml" ]]; then + log_error "版本目录缺少 data.yml: $version_dir" + fi + + if [[ ! -f "$version_dir/docker-compose.yml" ]]; then + log_error "版本目录缺少 docker-compose.yml: $version_dir" + fi + done +} + +# 验证顶层 data.yml +validate_top_data_yml() { + local data_file="$1" + + log_info "验证顶层 data.yml" + + if [[ ! -f "$data_file" ]]; then + return 1 + fi + + # 检查必需字段 + local required_fields=("name" "title" "description") + for field in "${required_fields[@]}"; do + if ! grep -q "^${field}:" "$data_file"; then + log_error "data.yml 缺少必需字段: $field" + fi + done + + # 检查 additionalProperties + if ! grep -q "additionalProperties:" "$data_file"; then + log_error "data.yml 缺少 additionalProperties" + fi + + # 检查 key + if ! grep -q "key:" "$data_file"; then + log_error "data.yml 缺少 additionalProperties.key" + fi + + # 检查 tags + if ! grep -q "tags:" "$data_file"; then + log_warn "data.yml 缺少 tags(推荐)" + fi + + # 检查 architectures + if ! grep -q "architectures:" "$data_file"; then + log_warn "data.yml 缺少 architectures(推荐)" + fi +} + +# 验证版本 data.yml +validate_version_data_yml() { + local data_file="$1" + + log_info "验证版本 data.yml" + + if [[ ! -f "$data_file" ]]; then + return 1 + fi + + # 检查 formFields + if ! grep -q "formFields:" "$data_file"; then + log_warn "版本 data.yml 缺少 formFields(如果无可配置参数则忽略)" + return 0 + fi + + # 检查每个参数是否有 envKey + local param_count + param_count=$(grep -c "envKey:" "$data_file" 2>/dev/null || echo "0") + + if [[ "$param_count" -eq 0 ]]; then + log_warn "formFields 中未找到 envKey 定义" + else + log_info "找到 $param_count 个可配置参数" + fi +} + +# 验证 docker-compose.yml +validate_docker_compose() { + local compose_file="$1" + + log_info "验证 docker-compose.yml" + + if [[ ! -f "$compose_file" ]]; then + return 1 + fi + + # 检查 container_name 使用变量 + if grep -q "container_name:" "$compose_file"; then + if ! grep -q 'container_name:.*\${CONTAINER_NAME}' "$compose_file"; then + log_error "container_name 必须使用 \${CONTAINER_NAME} 变量" + fi + fi + + # 检查 networks + if ! grep -q "1panel-network:" "$compose_file"; then + log_error "docker-compose.yml 缺少 1panel-network" + fi + + if ! grep -q "external: true" "$compose_file"; then + log_error "1panel-network 必须设置为 external: true" + fi + + # 检查 restart + if ! grep -q "restart: always" "$compose_file"; then + log_warn "建议设置 restart: always" + fi + + # 检查 labels + if ! grep -q 'createdBy: "Apps"' "$compose_file"; then + log_warn "建议添加 labels: createdBy: \"Apps\"" + fi + + # 检查端口映射使用变量 + if grep -q "ports:" "$compose_file"; then + if ! grep -q 'PANEL_APP_PORT_' "$compose_file"; then + log_warn "端口映射建议使用 PANEL_APP_PORT_* 变量" + fi + fi + + # 检查数据卷路径 + if grep -q "volumes:" "$compose_file"; then + if grep -qE '^\s+- /[a-zA-Z]' "$compose_file"; then + log_warn "检测到绝对路径的数据卷,建议使用 ./data/ 相对路径" + fi + fi +} + +# 主函数 +main() { + local app_dir="${1:-}" + + # 参数检查 + if [[ -z "$app_dir" ]] || [[ "$app_dir" == "-h" ]] || [[ "$app_dir" == "--help" ]]; then + print_help + exit 0 + fi + + if [[ ! -d "$app_dir" ]]; then + log_error "目录不存在: $app_dir" + exit 1 + fi + + echo "" + echo -e "${BLUE}=== 1Panel App Validator ===${NC}" + echo "" + + # 执行检查 + check_directory_structure "$app_dir" + validate_top_data_yml "$app_dir/data.yml" + + # 查找并验证版本目录 + local version_dirs + version_dirs=$(find "$app_dir" -maxdepth 1 -type d \( -name "v*" -o -name "latest" -o -name "[0-9]*" \) 2>/dev/null || echo "") + + for version_dir in $version_dirs; do + validate_version_data_yml "$version_dir/data.yml" + validate_docker_compose "$version_dir/docker-compose.yml" + done + + # 输出结果 + echo "" + echo -e "${BLUE}=== 验证结果 ===${NC}" + echo "" + + if [[ $ERRORS -eq 0 ]] && [[ $WARNINGS -eq 0 ]]; then + echo -e "${GREEN}✓ 验证通过!未发现问题。${NC}" + exit 0 + elif [[ $ERRORS -eq 0 ]]; then + echo -e "${YELLOW}⚠ 验证通过,但有 $WARNINGS 个警告。${NC}" + exit 0 + else + echo -e "${RED}✗ 验证失败!发现 $ERRORS 个错误和 $WARNINGS 个警告。${NC}" + exit 1 + fi +} + +# 执行主函数 +main "$@" diff --git a/skills/templates/data.yml.tpl b/skills/templates/data.yml.tpl new file mode 100644 index 0000000..91bf9a7 --- /dev/null +++ b/skills/templates/data.yml.tpl @@ -0,0 +1,33 @@ +name: ${APP_NAME} +tags: + - ${TAG_1} + - ${TAG_2} +title: ${TITLE_ZH} +description: ${DESC_ZH} +additionalProperties: + key: ${APP_KEY} + name: ${APP_NAME} + tags: + - ${TAG_EN_1} + - ${TAG_EN_2} + shortDescZh: ${SHORT_DESC_ZH} + shortDescEn: ${SHORT_DESC_EN} + description: + en: ${DESC_EN} + ja: ${DESC_JA:-Supporting multi-storage file listing program and private cloud storage} + ms: ${DESC_MS:-Supporting multi-storage file listing program and private cloud storage} + pt-br: ${DESC_PT_BR:-Supporting multi-storage file listing program and private cloud storage} + ru: ${DESC_RU:-Supporting multi-storage file listing program and private cloud storage} + ko: ${DESC_KO:-Supporting multi-storage file listing program and private cloud storage} + zh-Hant: ${DESC_ZH_HANT:-支援多存儲檔案列出程序和私人雲端空間} + zh: ${DESC_ZH} + type: ${APP_TYPE} + crossVersionUpdate: ${CROSS_VERSION_UPDATE:-true} + limit: ${LIMIT:-0} + recommend: ${RECOMMEND:-50} + website: ${WEBSITE} + github: ${GITHUB} + document: ${DOCUMENT:-} + architectures: + - amd64 + - arm64 diff --git a/skills/templates/docker-compose.yml.tpl b/skills/templates/docker-compose.yml.tpl new file mode 100644 index 0000000..bdc2823 --- /dev/null +++ b/skills/templates/docker-compose.yml.tpl @@ -0,0 +1,20 @@ +services: + ${SERVICE_NAME}: + container_name: \${CONTAINER_NAME} + restart: always + networks: + - 1panel-network + ports: + - "\${PANEL_APP_PORT_HTTP}:${PORT}" + volumes: + - ./data/data:/app/data + environment: + - PUID=0 + - PGID=0 + - UMASK=022 + image: ${IMAGE}:${TAG} + labels: + createdBy: "Apps" +networks: + 1panel-network: + external: true