把 Claude / ChatGPT 的分享对话,完整搬进你的内网与离线环境。
自托管的 AI 对话存档与高保真阅读器 —— 连同图片、附件、思考过程、可视化工件,以及 20+ 种文件格式的浏览器内预览。
很多团队的内网用户无法直接访问公网,却需要查阅 claude.ai(及 chatgpt.com)上的对话;个人也常希望把有价值的 AI 对话永久留存、离线可读,而不是任由分享链接过期。
chat-share-proxy 解决的就是这件事:一个浏览器扩展在你已登录的源站标签页里,通过官方 API 取回对话的原生分享 JSON(连同图片、附件、工具产物),上传到你自己部署的服务端;内网/离线用户登录后,由一个 Vue 3 单页应用在浏览器端把它逐字高保真地重新渲染出来 —— Markdown、代码高亮、数学公式、思考过程、可视化工件,乃至几十种文件的在线预览,一个不少。
一句话架构:对话 = 源站原生 JSON 原样入库,服务端零转换,渲染全在浏览器。
- 🗂️ 完整存档,而非截图 —— 抓取的是对话的原生 JSON,结构化保存。换模型、改 UI、链接过期都不影响你已存下的内容。
- 🎨 高保真渲染 —— 手写的 Markdown 渲染器(GFM)、
highlight.js代码高亮、KaTeX数学公式、思考过程(thinking)折叠时间线、visualize可视化工件、图片缩略图 + 灯箱、网页引用悬浮卡片,逐一对齐源站观感。 - 📂 20+ 种文件预览,全在浏览器内 —— 代码、Office、PDF、压缩包、数据库、医学影像、抓包、邮件、网页存档……点开即看,无需下载,字节从不在服务端解析或内联执行(详见下表)。
- 🤖 同时支持 Claude 与 ChatGPT —— 两套独立解析栈,复用同一套渲染与文件预览,含分支切换、思考过程、网页引用。
- 🏠 公开首页 —— 无需登录即可了解项目、浏览公开会话,支持标题/摘要搜索、Claude / ChatGPT 筛选和分页。登录后的会话库位于
/conversations。 - 🔗 一键公开分享 —— 所有者开启分享后,会话自动展示在首页,并生成独立的分享链接;访客免登录只读访问。取消分享会立即移出公开目录并使旧链接失效。
- 🔄 多环境同步 —— 把开发环境备份好的对话,一键推送到线上环境(复用对端 ingest 接口,对端零改动)。
- 📦 标准 ZIP 批量导出 —— 列表中多选会话,一次导出原始 JSON、已转存文件、SHA-256 与逐消息文件绑定;下游无需按文件名猜关联。协议见
docs/session-export-format.md。 - 👥 用户与权限管理 —— Session + Bearer 双鉴权、自助 API Token、管理员后台(建号 / 启停 / 改角色 / 重置密码),外加按用户私有的标签体系。
- 📊 访问分析 —— 公开分享打开、登录查看、登录成功三类有意义访问的离线统计(
ip2region区域解析 + 零依赖 UA 解析),fire-and-forget 不阻塞响应。 - 🔒 安全优先 —— 上游字节一律按附件下载、绝不内联;沙箱 iframe + 注入 CSP 渲染 HTML;严格的路径穿越防护;公开分享是唯一免鉴权数据面,仅靠能力令牌(详见安全设计)。
- 🧩 零内容脚本的扩展 —— 读取源站只走它自己的 API(同源
fetch+ 你的登录 cookie),不注入内容脚本、不抓取 / 解析页面 DOM。
对话里的任何附件、工具产物、交付文件,只要是下表中的类型,都能在右侧分栏直接预览 —— 全部在浏览器端解析渲染,服务端零转换,上游字节只当数据解码、绝不内联执行。
| 类别 | 支持格式 | 预览方式 |
|---|---|---|
| 代码 / 文本 | 30+ 种语言(js/ts/py/rs/kt/java/c/cpp/go/php/swift/scala/sql/sh/yaml/toml/json/xml…),及 txt/log/csv/env/Dockerfile/Makefile 等 | 语法高亮(highlight.js)+ 行号栏 |
| Markdown | .md .markdown |
文档渲染(手写 GFM)+ KaTeX 数学公式 + YAML frontmatter 卡片 + 渲染/源码切换 |
| HTML | .html .htm |
沙箱 iframe 渲染(注入 script-src 'none' CSP)+ 源码切换 + 新标签页全屏 |
| SVG | .svg |
图片渲染 + XML 源码切换 |
| 图片 | png · jpg · gif · webp · bmp · ico · avif · apng · jfif | 缩略图 + 灯箱缩放/拖动 |
| 视频 | mp4 · webm · mov · mkv · avi · m4v · ogv | 弹窗播放 |
| 压缩包 | zip · 7z · tar 家族(tar/gz/tgz/xz/bz2/br)· rar | 浏览器内解压、浏览目录、递归预览包内文件(含包内 PDF / 图片) |
.pdf |
逐页渲染到 canvas(pdfjs-dist),垂直滚动 |
|
| Word | .docx |
分页、纸张式渲染(docx-preview) |
| Excel | .xlsx .xlsm |
每个工作表一张表格 + 标签栏(exceljs) |
| PowerPoint | .pptx .pptm .ppsx |
可滚动的幻灯片列表(@aiden0z/pptx-renderer) |
| SQLite 数据库 | sqlite · db · sqlite3 · db3 · sqlitedb | 表选择器 + 分页数据网格 + SQL 控制台(sql.js WASM) |
| 医学影像 DICOM | .dcm .dicom |
canvas 渲染 + window/level 调窗(dicom-parser + WASM 编解码) |
| 抓包 HAR | .har |
可筛选的请求列表 + 每请求 headers / query / cookies / bodies |
| 网页存档 | .mht .mhtml |
还原保存的整页(资源改写为 blob URL,沙箱 iframe) |
| 邮件 | .eml |
头部卡片 + 正文(HTML 沙箱)+ 附件 |
| 思维导图 | .opml · .mm(FreeMind / Freeplane) |
交互式 SVG 思维导图 + 源码切换 |
| 二进制对象 | .so(含 libfoo.so.1.2.3)· .dll |
解析 ELF / PE,列出函数符号(可搜索) |
💡 想先体验文件预览?点击首页导航的「文件预览」或页面下方的「打开文件预览」即可进入
/dev/viewer,无需登录,把本地文件拖进去即可 —— 纯客户端解析,不上传所选本地文件。也可只运行pnpm dev独立使用,无需部署后端。🛠️ 大量解析器是零依赖手写的(zip / 7z+LZMA / bzip2 / ELF/PE / HAR / MIME(eml·mht) / 思维导图),重型依赖(Office、PDF、DICOM、Brotli/RAR/SQLite 的 WASM)则全部按需懒加载 —— 不打开对应文件就不会下载,主包保持精简。
截图待补充。建议展示:会话列表 · 对话明细(思考过程 + 数学公式 + 工件)· 文件预览分栏 · 公开分享页。
三个解耦组件:
| 组件 | 技术栈 | 职责 |
|---|---|---|
server/ |
Kotlin 2.4 · Ktor 3.5 · Exposed 1.3 · SQLite | 鉴权、ingest、存储,提供纯 JSON 接口 + 同源托管前端构建产物(不直出任何 HTML) |
frontend/ |
Vue 3.5 · Vite 8 · TypeScript | 线上 viewer 本体:列表 + 明细 + 登录 + 分享 + 后台 SPA,渲染全在浏览器 |
extension/ |
Chrome MV3(免构建) | 在源站标签页内同源抓取原生分享 JSON 与文件,上传至服务端;支持单页备份、粘贴上传、批量全量备份 |
数据流:
flowchart LR
A["claude.ai / chatgpt.com<br/>分享对话"] -->|"扩展在源站标签页内<br/>同源 fetch(带登录 cookie)"| B["Chrome 扩展"]
B -->|"POST /api/ingest<br/>原生 JSON + 文件字节"| C["server<br/>(Kotlin · Ktor)"]
C -->|"raw.json 落盘<br/>header 列入库<br/>文件字节落盘"| D[("SQLite + 磁盘")]
U["内网 / 离线用户"] -->|"登录 (session)"| C
C -->|"同源托管 SPA"| F["Vue 3 Viewer"]
F -->|"fetch 原生 JSON"| C
F -->|"浏览器端高保真渲染"| G["对话 + 文件预览"]
- 服务端把对话的原生 JSON 落盘(
{dbDir}/conversations/{uuid}/raw.json),数据库只存顶层 header 字段列;文件字节落盘({dbDir}/files/{uuid}/{path})。主键就是对话自己的uuid,无 slug、无代理 id。 - 登录用户可在会话列表复用批量选择模式下载 ZIP(
POST /api/conversations/export):服务端先整批校验权限与大小,再逐项流式写出;根目录import-metadata.json固定记录 provider key、消息 binding、实际路径、大小和 SHA-256。 - 前端
pnpm build产物在frontend/dist,由 server 的 Gradle 构建自动同步进server/src/main/resources/web/,Ktor 用staticResources同源托管,未匹配的非 API 路径 history 回退到index.html。 - 更完整的架构、数据模型、端点与踩坑见
AGENTS.md;前端渲染管线见frontend/ARCHITECTURE.md。
需要 JDK 21、Node.js + pnpm、以及 Chrome / Chromium。
cd server
./gradlew build # 前端 pnpm build + 编译 + 测试 + 打 fat jar(SPA 一起打进去)
java -jar build/libs/*-all.jar # 启动,默认 http://localhost:8080 ,/health 返回 "ok"
./gradlew build会先在frontend/跑一次pnpm build(首次自动pnpm install),把产物同步进server/src/main/resources/web/再打包,所以拿到的 jar 总是带最新 viewer。前端没改动就跳过(Gradle UP-TO-DATE);机器上没有 Node 时加-Pfrontend=false只构建后端。
首次启动可用环境变量引导第一个管理员(见配置):
CSP_SESSION_KEY=$(openssl rand -hex 32) \
ADMIN_USERNAME=admin ADMIN_PASSWORD='change-me' \
java -jar build/libs/*-all.jarcd frontend
pnpm install
pnpm dev # 开发预览 http://localhost:5173 (/api 代理到后端 :8080)
pnpm build # 产物到 dist/ —— server 的 Gradle 构建会同步进 resources/web 供后端同源托管想更新线上 viewer:直接
./gradlew build(自动跑前端构建并把产物打进 fat jar),不必先手动pnpm build。 无后端时前端会回退内置样例对话,可直接预览;/dev/viewer还能拖本地文件试各类文件预览。
- 打开
chrome://extensions,开启右上角 开发者模式。 - 点 加载已解压的扩展程序,选择本仓库的
extension/目录。 - 在扩展选项里配置一个或多个目标环境(服务器地址 + API Token),Claude 文件转存还需填一次组织 ID。
- 在
claude.ai对话页打开扩展 → 备份本页对话,或粘贴 JSON 上传,或批量全量备份。
获取 API Token:登录内网 viewer 后,在浏览器 Console 执行
fetch('/api/tokens',{method:'POST',headers:{'content-type':'application/x-www-form-urlencoded'},body:'name=extension'}).then(r=>r.json()).then(t=>console.log(t.token)),把返回的csp_…(只显示一次)填入扩展。详见extension/README.md。
服务端通过环境变量配置:
| 变量 | 默认 | 说明 |
|---|---|---|
CSP_PORT |
8080 |
监听端口 |
CSP_DB_PATH |
./data/chat-share-proxy.sqlite |
SQLite 路径;其所在目录还存放 conversations/、files/ 与 ip2region 数据 |
CSP_SESSION_KEY |
(随机) | 会话 cookie 加密密钥,生产务必固定设置 |
CSP_COOKIE_SECURE |
true |
纯 HTTP 部署需设为 false |
CSP_PUBLIC_BASE_URL |
— | 对外基础地址(用于拼接公开分享链接等) |
CSP_IP2REGION_XDB_V4 / _V6 |
{dbDir}/ip2region_v4.xdb·_v6.xdb |
ip2region 离线区域库路径,缺失则访问分析的区域留空 |
CSP_EXPORT_MAX_CONVERSATIONS |
200 |
单次 ZIP 最多导出的会话数(请求会先去空、去重) |
CSP_EXPORT_MAX_FILE_BYTES |
104857600 |
ZIP 中单个实际文件的最大字节数(默认 100 MiB) |
CSP_EXPORT_MAX_TOTAL_BYTES |
2147483648 |
单次导出的逻辑总字节上限(原始 JSON + 文件 + metadata,默认 2 GiB) |
ADMIN_USERNAME / ADMIN_PASSWORD |
— | 冷启动种子:仅当二者非空且该用户名不存在时创建一个管理员(幂等、只增不改)。日常增删改用户走 /admin 后台 |
这是一个会把外部不可信字节搬进内网的工具,安全是第一性的:
- 上游文件字节永不内联渲染 —— 下载端点强制
application/octet-stream+attachment+Content-Security-Policy: sandbox。所有预览都在浏览器端把字节当数据解码,HTML/SVG/邮件正文/网页存档一律在sandboxiframe(无allow-scripts)+ 注入 CSP 下渲染,脚本绝不执行。 - 严格的路径穿越防护 —— 上游
uuid与文件path均不可信,任何"路径 → 磁盘位置"的映射都经过统一的词法 + canonical 双重校验(拒..、分隔符、盘符冒号、控制字符,兼防符号链接逃逸)。 - 导出归档同样防穿越 —— ZIP entry 独立规范化并处理重名,禁止绝对路径、
..与控制字符;原始 provider key 只写入 JSON metadata,不直接作为未经检查的 entry 名。 - 鉴权取信于服务端 —— Session(加密 cookie)+ Bearer(Token,仅存 SHA-256 哈希)双方案;提交者身份永远取自 principal,绝不信任客户端字段;管理员接口在每个 handler 二次校验角色,并有"不能停用/降级/删除自己或最后一个管理员"等护栏。
- 公开分享是唯一免鉴权数据面 ——
/api/share仅列出已开启分享的会话,返回分享令牌、标题、来源、摘要和分享时间。详情仍按独立随机令牌寻址,与真实uuid解耦,吊销即删行;未分享会话、管理接口仍需鉴权。开启分享即允许访客从首页发现该会话。 - 扩展不碰页面 DOM —— 只调用源站自己的 API(同源
fetch+ 你的登录 cookie),无内容脚本。
- 后端:Kotlin 2.4 · Ktor 3.5(Netty)· Exposed 1.3(DSL)· SQLite + HikariCP · BCrypt · kotlinx.serialization · ip2region
- 前端:Vue 3.5 · Vite 8(Vite+/Rolldown/Oxc)· TypeScript 5.8 · vue-router · 手写 Markdown 渲染器 ·
highlight.js·KaTeX;按需懒加载pdfjs-dist/docx-preview/exceljs/@aiden0z/pptx-renderer/dicom-parser/sql.js/brotli-dec-wasm/node-unrar-js - 扩展:Chrome MV3,原生 JS,无构建步骤
本项目是个人备份 / 内网阅读工具,仅用于抓取你自己有权访问的分享对话:通过源站官方 API、在你已登录的浏览器标签页内同源请求、携带你自己的 cookie 完成,不破解、不绕过任何鉴权、不注入内容脚本、不抓取页面 DOM。
本项目与 Anthropic、OpenAI 无任何关联,不代表其官方立场。请遵守相关平台的服务条款与你所在地的法律法规,使用风险自负。
本仓库尚未附带 LICENSE 文件。若计划开源推广,建议补充一个许可证(如 MIT)后再公开。
感谢以下开源项目:Ktor · Exposed · Vue · Vite · highlight.js · KaTeX · pdf.js · docx-preview · ExcelJS · sql.js · dicom-parser · ip2region 等。