Skip to content

Repository files navigation

English · 中文

ScanDex — 把一堆扫描件变成能搜的库

家里十几年的扫描件、单据、证件、说明书、孩子的画、老照片……堆在硬盘上等于没有。 ScanDex 先用本地离线 OCR 把它们建成可搜索索引,再给你一个网页界面找图、看图、整理、问 AI。手机电脑浏览器都能用,也能装成 App。

所有 OCR 都在你自己的机器上跑,图片不出本机。

ScanDex 检索界面:多关键词命中,卡片带标签、备注与命中片段

截图里的内容全部是合成的演示数据(一个专门为截图造的假库),不是任何人的真实文件。


目录


先看看长什么样

不输任何条件 = 按年月浏览

每个月一张卡片、2×2 封面缩略图 + 张数,点进去看那个月的全部。

按年月浏览:每月一张卡片,四宫格封面加张数

搜一个词,七路字段一起找

文件名 / 别名 / OCR 全文 / 自动关键词 / 手动标签 / 分组名 / 备注——七路一起搜,多个词是 AND。 排序按相关性:文件名·标签·分组命中 > 自动关键词 > 备注 > OCR 全文,所以"真的身份证"排在"提到身份证的表单"前面。

检索结果:命中片段预览、标签、备注、分组

点开大图:OCR 全文、日期、标签、分组、备注都在这

左右滑 / 方向键翻页,双击进全屏,滚轮或双指缩放 1–6 倍。有原图的还能一键调阅未裁剪的原始扫描。

大图查看:右侧是 OCR 全文、日期、标签、分组、备注

标签云 & 分组树

自动关键词由本地流水线抽取(文字图用 jieba 分词,低文字图用视觉模型识主题),手动标签你自己加。分组是两级可折叠树,能拖拽归类。

标签云:按词频展示,点击即搜 分组与类别:两级可折叠树

用大白话找图

描述你要找什么,AI 自己调检索工具、换词重试,用中文回答并附上命中的图。

AI 对话找图:自然语言提问,回答附命中图片卡片

隐私边界:发给 AI 平台的只有候选图的 OCR 文字图片本身不上传

上传与一键入库(本地端)

拖放图片 / PDF / zip,选组、附标签备注,然后一键跑完整流水线:PDF 拆页 → zip 解包 → OCR → 自动标签 → 年月派生 → 归组 → 发布。

上传页:待入库文件按组分列,顶部显示待入库件数

设置页

AI 供应商选择与优先级链、模型名覆盖、自定义 OpenAI 兼容接口。

设置页:供应商列表,key 只显示已配/未配

注意截图里每一家都显示**(无 key)——设置页永远不回显密钥**,只告诉你配没配。

手机上就是个 App

浏览器菜单「添加到主屏幕」,独立图标、全屏运行。Service Worker 会缓存缩略图,二次浏览很快、弱网也顺。

手机端:按年月浏览 手机端:检索结果 登录页


两种用法:本地 / 在线

这套东西一个仓库两半,可以只用一半,也可以两半一起用:

本地端local/ 在线端(仓库根)
跑在哪 你自己的电脑 Cloudflare / Zeabur / 你的 VPS
语言 Python JavaScript(同一份代码跑两个平台)
建库(OCR) 只有本地能做
上传 / 入库 / 拍照矫正
检索 / 浏览 / 看图
改标签 / 备注 / 分组 / 日期
AI 对话找图
证件 PDF / 发消息
手机随时随地打开 要自己开隧道 ✅ 开箱即用
图片会离开你的机器吗 不会 看你选的存图方案(§D
花钱吗 电费 0 元起(Cloudflare 免费额度)

怎么选

  • 只在家用、最在意隐私 → 只装本地端。功能最全,完全不上网。
  • 想在外面用手机翻 → 两个都要:家里电脑建库,把库和图同步到在线端。
  • 想先试试这个界面好不好用 → 只部署在线端。空库也能完整跑起来,五分钟。

优缺点摊开说

本地部署

  • 👍 功能最全(建库 / 拍照矫正 / 证件 PDF / 发消息只有本地有)
  • 👍 图片和 OCR 全文一个字节都不出本机
  • 👍 不花钱,不依赖任何第三方服务
  • 👎 装 Python 环境要花点工夫(首次装依赖约 1 GB,含 OCR 引擎)
  • 👎 电脑关机就访问不了;想在外面用得自己开内网穿透
  • 👎 首轮建库慢(几千张图可能要跑几个小时)

在线部署

  • 👍 五分钟上线,手机随时打开,HTTPS 自动
  • 👍 不用管电脑开没开机
  • 👍 Cloudflare 路线免费额度足够个人使用
  • 👎 建不了库——OCR 流水线只在本地端,在线端只能读已有的库
  • 👎 图和 OCR 全文要放到平台或网盘上,隐私面变大
  • 👎 上传 / 拍照 / 证件 PDF 这些用不了

完整功能

标注说明:不标 = 本地 / 在线都能用;【本地】 = 需要本地后端(文件系统 / 本机通道),在线端会自动隐藏入口或礼貌提示。

🔎 检索
  • 多词 AND 检索,一次覆盖七路字段:文件名 / 别名 / OCR 全文 / 自动关键词 / 手动标签 / 分组名 / 备注
  • 相关性排序:文件名·标签·分组命中 > 自动关键词 > 备注 > OCR 全文
  • 命中片段上下文预览(snippet)
  • 年月筛选、"图片类"(低文字图)筛选、组内筛选,可与关键词叠加
  • 分页加载(每页 60,顺序稳定不跳动)
📅 按时间浏览
  • 不输任何条件即是年月卡片总览:每月 2×2 封面缩略图 + 张数,点卡片进该月,「← 返回所有年月」一键回来
  • 悬浮「时间」侧条随时切换年月
🏷️ 标签
  • 自动关键词(本地流水线生成:文字图 jieba 抽词、低文字图视觉模型识主题)→ 标签云按词频展示、点击即搜
  • 手动特征标签(多选):大图里直接编辑,或批量统一加;与自动词分区显示,可删可点搜
  • 标签云 / 分组列表都带即时过滤框
📁 分组与类别
  • 分组 = 一图一组(单组模型),本地物理对应 _groups\<组名>\ 文件夹
  • 类别(分组的上一级):两级可折叠树、默认全展开、自动"未分类"节点
  • 类别管理弹窗:新建 / 改名(重名自动合并)/ 解散;复选 + 长按选中;拖拽归类(选中多组整批拖)
  • 类别排序两模式(记忆在浏览器):自定义——拖动类别标题排列,顺序服务端持久化;最近更新——按类别下成员组的最新入库时间降序,往组里加了新照片的类别自动上浮
  • 组内浏览:改组名 ✎【本地】、上传到本组 📤【本地】、指派类别 🏷
  • 分组 ⇄ 年月双向可达:有分组的图物理住在组文件夹里,但保留年月字段——按组、按时间都能找到同一张
🖼️ 看图
  • 大图查看:上下张(按钮 / 左右滑 / 方向键,循环)、OCR 全文、日期(含来源标记)、标签、分组、备注
  • 全屏模式:双击或 ⛶ 进入,滚轮 / 双指缩放 1–6 倍、拖动平移、双击复位、Esc 退出
  • 原图调阅:若这张图关联了一张未裁剪的原图,大图里会出现「原图」按钮,随时调阅那张更完整的原始扫描
✏️ 编辑(在线端一样能改)
  • 备注、手动标签、分组(组合框选现有或输入新建)、修正日期(YYYY-MM,单张或批量)
  • 多选批量条:点选 / Shift 区间选,统一加标签 / 设分组 / 改日期 / 发送 / 证件 PDF / 删除(管理员)
  • 双写编辑日志:每次修改 ① 即时写库 ② 追加一条带 content_hash + 时间戳的可移植日志——本地、在线各写各的,合并时按"最后写入胜",文件改名、换机都不丢改动
🗑️ 删除 / 重裁 / 旋转 / 撤回【本地 · 管理员】
  • 删除已入库图:数据库记录 + 物理文件一并移除;带内容哈希护栏防误删、全程留痕;多选批量条可一次删多张
  • 重裁替换:对关联了原图的图,基于原图重新裁剪后替换当前图,保留原编号 / 日期 / 标签 / 分组 / 备注与原图关联,仅像素与 OCR 文字更新
  • 旋转:把已入库图顺时针 90°。旋转是图片的物理改变,统一走入库通道落地——界面即时反馈"已排队",下次入库时在主盘物理旋转、重跑 OCR,其余元数据全部保留
  • 撤回待入库:入库前撤销收件箱里某个待入库文件
  • 对外受限实例上删除 / 重裁 / 撤回走延迟队列,由下次入库在主盘落地
📤 输出【本地】
  • 发送到微信 / 飞书 / Telegram:单张、批量(≤9)、可选发送渠道精确到端
  • 证件 PDF 拼版:20 种证件真实尺寸 + A4 整页,每图独立裁剪框、水印、每页张数、画质档(300 / 500 DPI),下载或直接发送
🤖 AI 对话找图
  • 自然语言描述要找什么,AI 自主调用检索工具、多轮换词再试,用中文回答并附命中图
  • 供应商:内置 DeepSeek / 阿里云 Qwen / MiniMax / Kimi + 自定义 OpenAI 兼容接口(最多 6 个);优先级链失败自动落到下一家;设置页一键测试连通
  • 隐私边界:只发送候选图的 OCR 文字给 AI 平台,不上传图片本身;可用 AI_LIBRARY_HINT 私下告诉 AI 你库里的内容类型,提升选词准确度
  • 查某张图的处理历史:AI 只读调取该图的全生命时间线(入库 / 加原图 / 重裁 / 旋转 / 删除,按内容哈希归集、挪夹改名不丢)——只查不改,AI 永远碰不到文件
📷 拍照 / 扫描【本地】
  • 上传页「📷 拍照 / 扫描」→ 手机拍照或选图 → 全屏四角编辑器:四个手柄拖动定位纸张四角,触屏可拖、带放大镜精准对齐
  • 默认全选、可一键找角:默认四角=铺满整张图(拍得干净时直接下一步),可手动微调,或点「自动找边」让程序找纸张四角
  • 旋转键:入库前当场顺时针 90°、即时重绘
  • 四档增强预览,即点即换:
    • 彩色去阴影——保留颜色、抹平打光不均
    • 黑白自适应阈值——纯文字件最清晰、体积最小
    • 文档白化——分通道除背景 + 抬白纸面 + 轻锐化,发黄 / 偏灰的文档变纯白,但保留印章、彩色、淡铅笔字,且明显瘦身
    • 原图仅矫正——只做透视校正、不改像素
  • 纯传统图像处理:透视矫正 + 适度锐化,绝不做 AI 超分或"补字"——证件、合同一个像素都不臆造
  • 可保留原图:勾选后未裁剪的整张原图一并留档
  • 确认即加入上传队列,走与普通上传完全相同的入库流水线
📥 上传与入库【本地】
  • 网页上传暂存:拖放 / 多选,图片 / PDF / zip,选组或新建组、附标签备注,逐文件进度
  • 待入库可见性:顶部统计显示「📥 待入库 N 件」,上传页列出收件箱里按组分列的待入库文件——入库前就知道传了什么
  • 一键入库流水线:PDF 拆页 → zip 解包 → 子文件夹自动成组 → OCR → 自动标签 → 套用上传时的备注标签 → 多源年月派生 → 组物理归位 → 视觉打标 → 发布
  • 重传对账:感知哈希(pHash)识别"同图不同字节"的重传,保留新版不产生重复
🧾 图转文 / 成绩单【本地 · 需伴随识别服务】
  • 把图片(票据 / 表单 / 成绩单 / 书页…)识别成结构化文档(md / xlsx / docx):选图 → 选模式 → 一键跑 → 下载产物
  • 由 ScanDex 后端反向代理到本机一个独立的识别服务(回环 127.0.0.1绝不经公网暴露);只透传 5 条"干活"端点,白名单之外一律 404
  • 复用 ScanDex 的登录 + 权限:挂在登录墙内侧,需 upload 权限才显示——不单独开隧道、不额外放行
  • 开关:环境变量 IMGTODOC_ENABLED(默认开;设 0 整体隐藏)
🔐 登录、用户与权限
  • 多用户 + 权限系统:管理员在「用户管理」里建用户(无开放注册)、逐人开权限;新用户默认零权限,首次登录强制改密
  • 权限项(依赖自动带上):查看/搜索 · 上传(→查看) · AI 对话(→查看) · 编辑信息(→查看) · 结构管理(→编辑) · 发送/导出(→查看) · 修改密码
  • 管理员专属(不下放):改组名 · 类别管理 · 删除已入库图 · 撤回待入库 · 重裁 · 旋转 · 用户管理 · 设置 / 供应商测试 · 一键入库 · 审核队列
  • 密码只存 PBKDF2 加盐哈希——任何人(含管理员)无法查看,管理员只能重置;重置后旧会话立即失效
  • 权限在服务端逐端点强制(前端隐藏只是体验);用户管理与编辑操作全部留审计日志,署名到人
  • 在线端 fail-closed(无任何账号整站 503)、Cookie HttpOnly + SameSite=Strict(HTTPS 下加 Secure)、会话带 TTL
  • 静态页面也在登录墙内,未登录连界面都拿不到
  • 密钥只存平台 Secrets / 本地 gitignore 配置;设置页只显示"已配 / 未配",永不回显 key
⚙️ 设置
  • 对话 / 视觉供应商选择、优先级顺序、模型名覆盖(留空用默认)、自定义 API
  • 默认打开界面记忆(关键词 / 标签云 / 分组 / AI)
  • 本地专属【本地】:一键入库按钮、重传对账开关
📱 装成 App(PWA)
  • 添加到主屏幕:手机浏览器菜单里一点,即得独立图标、全屏运行(桌面 Chrome / Edge 地址栏也有安装入口)
  • 缩略图离线缓存:Service Worker 只对库内缩略图做缓存优先(上限约 3000 张、FIFO 滚动);页面、/api/*、待入库图一律不缓存——避免钉住旧登录态或过期数据
  • 需 HTTPS 才启用:Service Worker 仅在安全上下文注册,本地裸 HTTP 自动跳过(不影响使用)
  • 全部静态实现(manifest.webmanifest + sw.js + icons/),部署即带

快速开始

我要在自己电脑上跑(全功能)

下载整个仓库(Code → Download ZIPgit clone),然后看 local/ —— 那里写了一份给 AI 读的部署指引:在解压出来的文件夹里打开 Claude Code / Cursor / 任何能读文件的 AI 助手,让它读 local/部署指引-给AI.md,它会问你几个问题(图片放在哪、要不要 AI 功能、密码设什么…)然后一步步装好。

别只拆 local\ 出来——本地后端要读仓库根的 public\ 才有完整界面。

我要部署到网上

DEPLOY.md,三条路线任选:

路线 一句话 门槛
Cloudflare Workers 全云端、零服务器、免费额度够个人用 有 Cloudflare + GitHub 账号即可
Zeabur 认 Dockerfile 自动构建,挂个磁盘就完事 最省心
VPS / Docker docker run 一行,或直接 node server/node.mjs 要有台机器

三条路线跑同一份代码,功能与接口完全一致。自托管零 npm 运行时依赖(D1 → node:sqlite、KV → 同一个 SQLite 文件、静态资源 → 读 public/,全是 Node 内置能力)。

本地开发

npm install          # 只装 wrangler(开发用)
npx wrangler dev     # Cloudflare 本地模拟,配 .dev.vars 放测试密码(已 gitignore)

node server/node.mjs # 或者直接用 Node 跑自托管形态
node server/smoke.mjs # 冒烟自检:十几条断言,全绿才算适配层没坏

前端就是 public/ 下两个自包含的 HTML,改文件刷新即见,没有构建步骤。


架构

浏览器 ── 静态前端 (public/index.html,纯 HTML/CSS/JS 内联) ──┐
                                                              │ 调 /api/*
                        ┌─────────────────────────────────────┴────────┐
            本地:      │ serve.py (Python,在 local/ 目录)              │  ← 本机日常,功能最全
            在线:      │ worker/index.js                              │
                        │   ├─ Cloudflare Workers(原生)               │
                        │   └─ server/node.mjs(自托管:Zeabur/VPS/本机)│
                        └───────┬──────────────┬───────────────┬───────┘
                             库(D1 或          设置/会话/类别      图片
                             SQLite 文件)      (KV 或同一文件)   (WebDAV 或磁盘)

三条要点:

  • 前端单一源:本地后端与在线后端读的是同一份 public/,改一处两端生效
  • 后端单一源worker/index.js 是唯一的在线实现,Cloudflare 与自托管只是绑定不同(server/shims/ 里三个替身),业务逻辑一行不重写
  • API 契约一致:在线端按本地 serve.py 的接口逐句对齐,前端无感切换;空库时读接口返回空结构,界面完整可用

/api/* 接口契约

GET /api/facets(年月+封面+组数) · search(q/ym/low/group/off 分页) · tags · groups · supergroups(类别字典·键序=自定义顺序 + updated=每类别成员组最新入库时间) · inbox_pending · config · settings · channels · thumb?id= · image?id= · original?hash= · zip_list/zip_thumb/zip_image · cert_types

写(POST JSON)login/logout · change_password · 用户管理(管理员) users/user_create/user_delete/user_perms/user_reset/user_rename · note · manual_tags · set_date(单/批) · groups_set · group_add(单组=替换) · group_rename【本地】 · supergroup_assign/rename/delete · supergroup_order · tag_add · upload【本地】 · scan_process/scan_detect/scan_ai【本地】 · image_delete/inbox_cancel/image_recrop/image_rotate【本地·管理员】 · ingest_inbox【本地】 · send/zip_send【本地】 · chat · cert_pdf【本地】 · test_provider

图转文代理【本地】GET /api/imgtodoc/status·progress·download + POST /api/imgtodoc/upload·run → 反代本机识别服务(白名单 5 条,其余 404);同受登录墙 + upload 权限约束。

模式开关(/api/config 返回)

  • local_inbox — 有本地收件箱(可上传 / 入库)
  • public — 对外受限实例:服务端封禁入库 / 设置写 / 供应商测试 / 关机;编辑类全部可用(备注 / 标签 / 分组 / 改日期 / 类别管理即时生效,改组名走延迟落地)
  • cloud — 在线端
  • imgtodoc — 图转文模块是否启用

数据模型:图片 → 分组 = 一图一组;分组 → 类别 = 一组一类别(纯显示层);图片 → 标签 = 多选;年月来源优先级 手动 > 原件 > EXIF > 文件名 > 派生 > mtime。


目录结构

scandex/
├─ public/                静态前端 = 平台的资产目录
│  ├─ index.html          主界面(自包含,无构建步骤)
│  ├─ login.html          登录页
│  ├─ sw.js               Service Worker(只缓存缩略图)
│  ├─ manifest.webmanifest
│  └─ icons/              PWA 图标 + 安装预览图(未登录可访问的最小集合之一)
├─ worker/
│  ├─ index.js            ★ 唯一的在线后端实现:登录墙 / 检索 / 类别 / 编辑双写 / AI 对话 / 图片代理
│  └─ schema.sql          初始 schema(空库即可跑;含 edits 编辑日志表)
├─ server/                自托管适配层(让同一份 worker 跑在 Node 上)
│  ├─ node.mjs            HTTP 入口:node:http ⇄ Web Request/Response
│  ├─ shims/d1.mjs        D1 → node:sqlite
│  ├─ shims/kv.mjs        KV → 同一个 SQLite 文件里的 kv 表
│  ├─ shims/assets.mjs    ASSETS → 读 public/
│  ├─ shims/localfiles.mjs 本地磁盘图源(IMAGE_ROOT)
│  └─ smoke.mjs           冒烟自检
├─ Dockerfile             Zeabur / 任意 Docker 主机(node:24-alpine,零 npm 依赖)
├─ wrangler.toml          Cloudflare 绑定(d1/kv id 按 DEPLOY.md 填;零密钥)
├─ docs/img/              README 截图(全部为合成演示数据)
├─ local/                 ★ 本地端(Python):离线 OCR 流水线 + 全功能后端
│  ├─ scripts/            流水线:ocr_index / generate_tags / organize_files / update …
│  │  └─ _paths.py        全部路径的唯一真源(环境变量 / config.local.json 驱动)
│  ├─ webui/serve.py      本地后端(读上一级的 public/ 当界面)
│  ├─ client/             只读数据接口客户端
│  ├─ requirements.txt
│  ├─ README.md           本地端说明
│  └─ 部署指引-给AI.md    ★ 交给 AI 读的安装指引
├─ DEPLOY.md              ★ 部署手册:三条路线 + 环境变量总表 + 存图方案 + 验证清单
├─ README.md              本文件
└─ README.en.md           English

隐私与安全

这个项目处理的是你家全部证件、票据、合同的原件影像和全文,所以隐私不是加分项而是前提:

  • OCR 全程离线:识别在你自己的机器上跑(RapidOCR / ONNX Runtime),图片不发给任何人
  • AI 只拿文字:用 AI 对话找图时,发出去的是候选图的 OCR 文字,图片本身不上传
  • 仓库零密钥:所有 key 存平台 Secrets 或本地 gitignore 的配置文件;设置页只显示"已配 / 未配"
  • 登录墙在静态页之前:未登录连界面都拿不到。未登录可访问的只有登录页、manifest.webmanifestsw.jsicons/icon-*.png——PWA 安装必需且零敏感
  • fail-closed:没配管理员就整站 503,不存在"忘了设密码裸奔"的窗口
  • 密码只存哈希:PBKDF2 加盐,常数时间校验,任何人看不到明文

详细的威胁模型与加固建议见 DEPLOY.md §I

一句实在话:最安全的用法是只在本地跑。要上公网,请用密码管理器生成的长随机密码,并认真读一遍 §D 图片存哪——那一节决定了你的原件影像会落在谁的硬盘上。


许可

MIT。可自由使用、修改、分发、商用,保留版权声明即可,不提供任何担保。


English · 中文

About

把扫描件和照片离线 OCR 成可搜索索引,网页端做全文检索、看图、标签分组与 AI 对话找图,可部署到 Cloudflare Workers、Zeabur 或任意Docker主机。Turn your scans and photos into a searchable index with offline OCR — full-text search, image viewer, tags & groups, and AI chat-to-find on the web; deploys to Cloudflare Workers, Zeabur, or any Docker host.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages