之前总有几个场景觉得别扭:临时想在另一台机器上传段代码、传个截图给朋友,或者往服务器上塞个小文件,翻来覆去要么开网盘、要么敲 scp。后来看到 fars.ee ↗ 和 hastebin 这类在线剪切板,就想着自己是不是也能整一个。用 Go 写了个 pastego,单个二进制,丢到服务器就能跑,前后端我一个人说了算,用起来顺手最重要。
开源在 git.liyp.cc/xmengnet/pastego ↗,我自己部署的实例在 p.liyp.cc ↗。下面把项目思路和用法记一下。
主要特性#
- 多类型支持:文本、代码、图片、任意文件都可以传,按扩展名识别
mimetype,图片能直接在查看页渲染 - 自动语法高亮:前端用
highlight.js根据内容自动检测语言 - 私有模式:公开的用 6-8 位
short链接,私有的只生成 24 位不可猜测的长链接 - 定时过期:1 小时 / 1 天 / 7 天 / 30 天 / 永久,后台 goroutine 每 60 秒清扫
- CLI 工具:独立二进制,管道友好,脚本里随手
echo xxx | clip就能发 - 零依赖部署:单二进制 + 一个 SQLite 文件,前端用
go:embed直接打进二进制里 - 安全防护:限流、CSRF、XSS 兜底、安全响应头,为公网部署而做
技术选型#
| 层面 | 技术 |
|---|---|
| 后端 | Go(标准库 net/http,没引 web 框架) |
| 数据库 | SQLite(mattn/go-sqlite3) |
| 前端 | React + TypeScript + Tailwind(Vite) |
| 渲染 | goldmark 渲染 Markdown(about 页面) |
| CI | Gitea Actions,打标自动出 Linux/Windows/Arch 包 |
后端只用标准库,连路由都是 http.NewServeMux(),没用 gin 之类的东西。依赖就三个:SQLite 驱动、UUID 和 goldmark,对一个服务来说算够克制了。
构建与启动#
编译#
git clone https://git.liyp.cc/xmengnet/pastego.git
cd pastego
# 后端
go build -ldflags="-s -w" -o pastego .
# 前端(构建产物会被 go:embed 打进二进制)
cd frontend && npm install && npm run buildbash也可以直接下 release 里的预编译二进制,或者 Arch 用户直接装 pastego 包。项目 CI 打 tag 时自动交叉编译 Linux amd64/arm64、Windows amd64 和 Arch x86_64 包。
启动#
# 默认端口 4832
./pastego
# 自定义端口 / 数据库路径
./pastego -addr :8080 -dsn /data/pastego.dbbash配置也支持环境变量(CLIP_ADDR、CLIP_DSN 等),用 systemd 托管的话更直观。默认所有东西都在,前端界面在 http://localhost:4832,就是个 hastebin 风格的编辑器。
使用#
Web 界面#
打开就是个大编辑框,支持 Tab 缩进,底部任务栏能选语法类型(留空自动检测)、过期时间、是否私有模式。点 paste! 拿到链接:

查看页自动高亮,还带个二维码方便手机上扫。比如把 pastego 的 README 传上去,代码高亮和 Markdown 表格都能正常展示:

CLI#
CLI 是独立二进制 clip,先配一下服务器地址:
export CLIP_SERVER=https://p.liyp.cc
# 或者写进配置
./clip config --server https://p.liyp.ccbash然后基本就是拿来即用:
# 管道上传,自动检测语言
echo "SELECT * FROM users" | ./clip
# 指定语法、带过期时间
./clip -f query.sql -s sql
echo "临时消息" | ./clip -t 3600 # 1 小时后过期
# 传文件 / 私有模式
./clip -f screenshot.png
cat secret.txt | ./clip --private
# 只看 URL,方便接脚本
echo "data" | ./clip -ubash查看、改、删:
./clip abc123 # 看原始内容
./clip abc123 --meta # 看 JSON 元数据
./clip abc123 --open # 浏览器打开
echo "new" | ./clip --update <uuid>
./clip --delete <uuid>bash设计与安全上的一些细节#
ID 体系#
一个剪切板有三套标识,各管一摊:
| 类型 | 格式 | 用途 |
|---|---|---|
short | 6-8 位 base62 | 公开分享链接,私有模式为空 |
long_id | 24 位 base62 | 私有访问链接,不可猜测 |
uuid | UUID v4 | 更新/删除认证,仅修改操作用 |
uuid 只出现一次#
出于安全考虑,uuid(唯一的写凭证)只在创建响应里返回一次,之后任何读取接口都不再带出 uuid/short/long。结果就是:只有创建者手里那份凭证能改/删自己的剪切板,别人拿到分享链接也看不了删除按钮,更没法通过读取接口扒出写凭证。这个设计我觉得挺关键,值得单独提一句。
限流别乱信代理头#
限流用的令牌桶,默认每秒 10 个、突发 20 个。默认不信任 X-Forwarded-For / X-Real-Ip——因为公网直连时这些头是能被伪造的,伪造了等于绕过 IP 限流。只有部署在反向代理后面才开 CLIP_TRUSTED_PROXY=1,让限流按真实客户端 IP 算。
一个前端的坑:markdown 表格断行#
前端高亮用 Prism,highlight.js 检测语言时,markdown 表格里带 display: table 的 token 会被拆开、导致表格被断成一行一行,很难看。最后是靠 CSS 强制行内排列把表格拉回一行显示的。
静态资源缓存策略#
前端构建产物文件名带内容 hash(index-xxxx.js),这类资源是 immutable 的,可以放心长缓存;index.html 则 no-store 每次重新拉取,这样能拿到最新的 hash 资源,避免浏览器把旧 JS 包当新包用。
部署到生产#
公网裸奔还是建议套个反向代理 + HTTPS。几个环境变量按需配:
export CLIP_ORIGINS="p.liyp.cc" # 启用 CSRF,只放行指定域名
export CLIP_TRUSTED_PROXY=1 # 在 Nginx/Caddy 后,保证限流按真实 IP
export CLIP_MAX_SIZE=10485760 # 单文件上限 10MBbash然后 Nginx 反代 4832 端口、配好证书就行。数据库就一个 pastego.db,定时备份一下就完事。
小结#
折腾下来最大的感受是:零依赖部署 + 双端入口(Web + CLI)这套组合真的很实用。要传个东西的时候,管道一接 | clip 链路就通了,比开网盘顺手得多。代码也没用重的框架,标准库就能撑起一个够用的服务,后期维护不大。
项目我还在慢慢加东西,有需要可以直接去仓库提 issue;部署在 https://p.liyp.cc ↗ 的这个实例就是它自己。