Some checks failed
Release / build-and-release (push) Failing after 1m31s
- Go 后端 (Gin + GORM + SQLite) 提供 API 和纯文本脚本服务 - Vite + React + TypeScript + Tailwind 前端 - 单二进制部署 (Go embed 前端静态文件) - Gitea Actions CI/CD: 打标签自动构建多平台 Release - 支持 bash/zsh/sh/fish/python3/node/ruby/php 8种运行环境 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
195 lines
6.6 KiB
Markdown
195 lines
6.6 KiB
Markdown
# ScriptForge 设计文档
|
||
|
||
## 概述
|
||
|
||
脚本快速转运行链接服务。用户粘贴脚本内容,选择运行环境,生成一个短链。在终端执行 `curl URL | <runtime>` 即可运行脚本。
|
||
|
||
- 前端:Next.js (App Router + Tailwind CSS + TypeScript)
|
||
- 后端:Go (Gin + GORM + SQLite)
|
||
- 部署:Go embed 前端静态文件,单二进制部署
|
||
- CI/CD:Gitea Actions
|
||
|
||
## 核心流程
|
||
|
||
1. 用户打开首页 `/`
|
||
2. 粘贴脚本内容(上限 16KB)
|
||
3. 选择运行环境(bash / zsh / sh / fish / python3 / node / ruby / php)
|
||
4. 选择过期时间(1小时 / 24小时 / 7天 / 30天)
|
||
5. 提交 → 后端存入 SQLite → 返回短链 ID
|
||
6. 页面展示生成的命令:`curl https://example.com/raw/xK8mPq | bash`
|
||
7. 用户可以复制命令到终端执行
|
||
|
||
## API 设计
|
||
|
||
### POST /api/scripts — 创建脚本
|
||
|
||
请求体:
|
||
```json
|
||
{
|
||
"content": "#!/bin/bash\necho hello",
|
||
"runtime": "bash",
|
||
"expires_in": "24h"
|
||
}
|
||
```
|
||
|
||
响应:
|
||
```json
|
||
{
|
||
"id": "xK8mPq",
|
||
"admin_token": "dGhpcyBpcyBhIHRva2Vu",
|
||
"url": "https://example.com/raw/xK8mPq",
|
||
"command": "curl https://example.com/raw/xK8mPq | bash",
|
||
"runtime": "bash",
|
||
"expires_at": "2026-05-29T16:27:00Z"
|
||
}
|
||
```
|
||
|
||
### GET /api/scripts/:id — 获取脚本元数据
|
||
|
||
响应:
|
||
```json
|
||
{
|
||
"id": "xK8mPq",
|
||
"runtime": "bash",
|
||
"content_length": 25,
|
||
"created_at": "2026-05-28T16:27:00Z",
|
||
"expires_at": "2026-05-29T16:27:00Z",
|
||
"expired": false
|
||
}
|
||
```
|
||
|
||
> 注意:此接口不返回脚本原始内容(内容通过 `/raw/:id` 获取),避免前端二次暴露。
|
||
|
||
### GET /raw/:id — 获取脚本纯文本内容
|
||
|
||
- 响应头 `Content-Type`: 根据 runtime 设置对应 MIME(见运行时配置表)
|
||
- 响应头 `Content-Disposition`: `attachment; filename="script.<ext>"`(根据 runtime 设置扩展名)
|
||
- 响应体:脚本纯文本内容
|
||
- 如脚本已过期,返回 404
|
||
|
||
### DELETE /api/scripts/:id?token=xxx — 删除脚本
|
||
|
||
- 需要 `admin_token` 参数
|
||
- 成功返回 204
|
||
|
||
## 数据模型
|
||
|
||
```go
|
||
type Script struct {
|
||
ID string `gorm:"primaryKey;size:8"`
|
||
Content string `gorm:"type:text;not null"`
|
||
Runtime string `gorm:"size:16;not null;index"`
|
||
AdminToken string `gorm:"size:64;not null"`
|
||
ExpiresAt time.Time `gorm:"not null;index"`
|
||
CreatedAt time.Time `gorm:"not null"`
|
||
}
|
||
```
|
||
|
||
## 运行时配置
|
||
|
||
| Runtime | 扩展名 | MIME 类型 | 命令模板 |
|
||
|---------|--------|-----------|----------|
|
||
| bash | `.sh` | `text/x-shellscript` | `curl {url} \| bash` |
|
||
| zsh | `.zsh` | `text/x-shellscript` | `curl {url} \| zsh` |
|
||
| sh | `.sh` | `text/x-shellscript` | `curl {url} \| sh` |
|
||
| fish | `.fish` | `text/x-shellscript` | `curl {url} \| fish` |
|
||
| python3 | `.py` | `text/x-python` | `curl {url} \| python3` |
|
||
| node | `.js` | `text/javascript` | `curl {url} \| node` |
|
||
| ruby | `.rb` | `text/x-ruby` | `curl {url} \| ruby` |
|
||
| php | `.php` | `text/x-php` | `curl {url} \| php` |
|
||
|
||
运行环境列表在 Go 后端定义为常量数组,用于校验请求中的 runtime 字段。
|
||
|
||
## 过期策略
|
||
|
||
- 用户保存时选择:1小时 / 24小时 / 7天 / 30天
|
||
- 后端定时任务(每分钟扫描一次),删除过期记录
|
||
- 过期后 `/raw/:id` 返回 404,详情页标记为已过期
|
||
|
||
## 限流策略
|
||
|
||
- 创建接口:10 次/分钟/IP
|
||
- 其他接口:60 次/分钟/IP
|
||
- 请求体上限:17KB
|
||
|
||
## 安全设计
|
||
|
||
- 完全公开,无需注册/登录
|
||
- 创建时返回 `admin_token`(随机 64 位字符串),持有者可删除脚本
|
||
- 无内容过滤,靠过期机制和限流控制风险
|
||
- 编辑功能:无(`admin_token` 仅用于删除,不能改内容。设计上 script 是一次性的,编辑会带来版本管理等复杂问题)
|
||
|
||
## 前端路由
|
||
|
||
| 路由 | 页面 | 说明 |
|
||
|------|------|------|
|
||
| `/` | 首页 | 粘贴脚本 + 选择运行环境/过期时间 + 提交 + 结果展示 |
|
||
| `/s/[id]` | 脚本详情页 | 展示脚本内容(语法高亮)、元数据、curl 命令卡片 |
|
||
| `/s/[id]/delete` | 删除确认页 | 输入 admin_token 确认删除 |
|
||
|
||
## 项目目录结构
|
||
|
||
```
|
||
scriptforge/
|
||
├── frontend/ # Next.js 项目
|
||
│ ├── src/
|
||
│ │ ├── app/
|
||
│ │ │ ├── page.tsx # 首页
|
||
│ │ │ ├── layout.tsx # 根布局
|
||
│ │ │ └── s/[id]/
|
||
│ │ │ ├── page.tsx # 脚本详情页
|
||
│ │ │ └── delete/
|
||
│ │ │ └── page.tsx # 删除确认页
|
||
│ │ ├── components/
|
||
│ │ │ ├── ScriptForm.tsx # 脚本提交表单
|
||
│ │ │ ├── ResultCard.tsx # 结果展示卡(含复制按钮)
|
||
│ │ │ ├── ScriptViewer.tsx # 脚本内容展示(语法高亮)
|
||
│ │ │ └── CommandCard.tsx # curl 命令展示卡
|
||
│ │ └── lib/
|
||
│ │ └── api.ts # API 调用封装
|
||
│ ├── package.json
|
||
│ └── next.config.js
|
||
├── backend/ # Go 项目
|
||
│ ├── cmd/
|
||
│ │ └── server/
|
||
│ │ └── main.go # 入口
|
||
│ ├── internal/
|
||
│ │ ├── handler/
|
||
│ │ │ ├── script.go # 脚本 CRUD handlers
|
||
│ │ │ └── raw.go # 原始内容 handler
|
||
│ │ ├── model/
|
||
│ │ │ └── script.go # GORM model
|
||
│ │ ├── service/
|
||
│ │ │ └── script.go # 业务逻辑
|
||
│ │ ├── middleware/
|
||
│ │ │ └── ratelimit.go # 限流中间件
|
||
│ │ └── config/
|
||
│ │ └── runtime.go # 运行时配置列表
|
||
│ ├── go.mod
|
||
│ └── go.sum
|
||
├── .gitea/
|
||
│ └── workflows/
|
||
│ └── deploy.yml # Gitea Actions 部署流程
|
||
├── Makefile # 统一构建命令
|
||
├── DESIGN.md # 本设计文档
|
||
└── README.md
|
||
```
|
||
|
||
## CI/CD 部署流程(Gitea Actions)
|
||
|
||
1. 触发条件:push 到 `main` 分支
|
||
2. 构建步骤:
|
||
- `cd frontend && npm ci && npm run build` → 输出 `out/`
|
||
- `cd backend && cp -r ../frontend/out ./internal/embed && go build -o server ./cmd/server`
|
||
3. 部署步骤:
|
||
- `scp` 二进制到服务器
|
||
- `ssh` 执行 `systemctl restart scriptforge`
|
||
|
||
## 后续可扩展方向
|
||
|
||
- 大模型集成:根据脚本内容自动推荐运行环境
|
||
- 更多运行时:dockerfile、powershell、deno、bun 等
|
||
- 脚本版本管理(不基于 admin_token,需要真正的用户系统)
|
||
- 统计数据:脚本执行次数、来源 IP 等(需要跟踪 curl 请求)
|
||
- WebSocket 实时执行输出流
|