你家里书架上几百本书,朋友借走一本,三个月后你想起来问,对方说早还了,你翻遍书架找不到——这种场景每个爱囤书的人都经历过。Vaultisse 就是为此而生的开源 Web 应用:它把每本实体书当成一个可追踪的“库存条目”,记录它现在放在哪个书架、被谁借走了、状态是可用还是损坏,并且每本书的封面、作者、分类等元数据都能通过扫描 ISBN(国际标准书号,书背上的那串数字)自动从 Google Books 或 Open Library 拉取。整个项目以 MIT 协议开源,后端是 Express + PostgreSQL,前端是 Vue 3 单页应用,最妙的是生产环境下前端构建产物直接由后端静态托管,整个应用跑在同一个 Node 进程、同一个端口上,部署成本极低。
先看它解决了什么问题:传统图书管理要么靠 Excel 表格,要么靠记忆,要么用商业软件。Vaultisse 把“书”和“库存副本”分开——同一本书你买了两本,就对应两条 stock 记录,每条独立状态(可借、已借出、损坏、不可用)。借出时在客户目录里选谁借的,系统记下日期。还能为每本书打印条形码/ISBN 标签,方便重新扫描归架。Dashboard 上展示藏书的统计图表,支持全文搜索,UI 内置英、西、加泰、意四种语言。这些功能听起来不复杂,但真正的技术含量在架构和认证细节上。
项目是经典的 monorepo:client/ 目录存放 Vue 3 + Vuetify 组件库 + Vite 构建工具的 SPA,server/ 是 Express + TypeScript 的 REST API(REST 指一种基于 HTTP 的接口风格,用 URL 和动词定义操作)。前端永不直接连数据库,所有数据通过 /api/rest/* 走 HTTP 请求。结构上值得称道的地方在于,它把 Vue 组件拆成了三块:views(页面组件)、controller(页面逻辑与状态)、service(封装 HTTP 调用)。controller 继承自一个公共 BaseController,把状态管理从模板中剥离,.vue 文件只保留标记——这比把一切塞进组件里的写法清晰得多,也更容易测试。服务层为每种资源建一个 service,统一用 Axios 封装,配置集中在 plugins/axiosInstance.ts。这种分层对中小型项目非常友好,既没引入 Vuex/Pinia 这类状态库,也避免了组件内逻辑臃肿。
认证部分是最值得技术人员细看的设计。它用 JWT(JSON Web Token,一种无状态令牌)放入 httpOnly cookie(浏览器无法通过 JS 读取的 cookie,防 XSS 攻击),但并不是纯粹无状态:每次受保护请求,中间件都会检查数据库中的 per-user token_version 计数器(用户改密码时递增,让所有已发令牌立刻失效)和 per-login user_sessions 行(用于在“设置”里单独撤销某个设备)。这种“混合会话”模型既保留了 JWT 的简单性,又解决了纯 JWT 无法服务端撤销的痛点——如果只靠 JWT,用户改密码后旧令牌还会一直有效,而 Vaultisse 用一次数据库查询就彻底堵住了这个漏洞。密码用 bcrypt(一种加盐哈希算法)12 轮哈希,绝不明文存储;每台设备独立 session,登出只删对应行。另外,它默认启用了 helmet(安全 HTTP 头)、express-rate-limit(限流防暴力破解)、CORS 限制前端来源。这些安全细节在自托管应用里常常被忽略,Vaultisse 直接开箱即用,非常良心。
部署策略同样务实。开发环境用 Vite 的代理机制把 /api/rest 转发到后端,前端和后端各跑一个 dev server;生产环境用 build.sh 一次性编译前端(Vite)和后端(tsc),产物全部组装到 dist/ 下,Express 直接托管静态文件——一个进程解决所有服务。这让我想起很多企业项目里把前后端拆得稀碎、引入微服务网关甚至容器编排,最后运维复杂得喘不过气,而 Vaultisse 用单进程完成了同样的事。它还提供了 Docker 镜像和 docker-compose 样例(一个 PostgreSQL 容器 + 一个应用容器),以及 PM2(Node.js 的进程守护管理器)配置文件。数据库结构用一套完整的 SQL schema 文件管理,新装只需执行一次,升级则按版本号顺序应用 upgrade 目录下的增量 SQL,这种约定清晰明了。
另一个亮点是国际化方案:所有 UI 标签存储在数据库的 app_languages 和 app_labels 表里,前端通过 Vue I18n 动态加载——这意味着改翻译只需要更新一条记录,不需要重新部署前端。帮助文档 /docs 则用 Markdown 编写,在客户端按语言渲染。这种“数据驱动 i18n”的做法对社区贡献者极其友好,想加新语言只需往数据库插数据,不用动代码。
如果你恰好要管一个小型图书馆、学校图书角,或者自己家里藏书上千册,Vaultisse 可以直接部署起来用。但更值得借鉴的是它的架构选择:用单进程承载前后端、用数据库做会话撤销、用 SQL 文件管理 schema 升级——这些思路放到任何中小型 Web 项目上都成立。需要注意的坑有两个:一是权限系统很简单,只有用户注册和登录,没有角色分级(比如管理员 vs 普通用户),若要做多用户共享,可能要自己加控制;二是登录时如果忘了设置强 JWT_SECRET(可用 openssl rand -hex 32 生成),整个认证体系形同虚设。另外,开发环境的 ALLOW_DEV_AUTH=true 一定不能在生产环境打开,否则等于免登录。总体而言,这是一个结构干净、文档齐全、部署单步的实用项目,尤其适合想在真实代码里学习 SPA + REST API + JWT 会话管理如何协作的开发者。
内容与图片版权归原作者所有 · 原文: https://github.com/AlbertAmat/vaultisse