rCodenow 扩展仓库
  • JavaScript 90.7%
  • CSS 9.2%
Find a file
2026-10-23 18:32:10 +08:00
packages/cli 更新扩展模板 2026-10-19 22:14:36 +08:00
scripts 移除rc-mod.json 2026-10-11 13:18:40 +08:00
server 与rCodenow互动修复 2026-10-22 06:38:55 +08:00
shared 与rCodenow互动修复 2026-10-22 06:38:55 +08:00
test 与rCodenow互动修复 2026-10-22 06:38:55 +08:00
web 文案正式化 2026-10-23 18:32:10 +08:00
.gitignore rCodenow Gallery——社区 2026-10-06 12:51:32 +08:00
package-lock.json rCodenow Gallery——社区 2026-10-06 12:51:32 +08:00
package.json rCodenow Gallery——社区 2026-10-06 12:51:32 +08:00
README.md 接入rCodenow 2026-10-11 17:17:27 +08:00
vite.config.js rCodenow Gallery——社区 2026-10-06 12:51:32 +08:00

rCodenow Gallery · rCodenow 画廊

rCodenow 社区:作品、扩展与插件的共享平台与交互社区。 前端用 GTK4 / libadwaita 的视觉语言(@gtk-js/adwaita),后端是 Express + Node 内置的 SQLite。配套命令行工具 rcodenow-gallery。

  • 包名:rcodenow-gallery
  • 需要 Node ≥ 22.5(node:sqlite 在 22 上是 experimental,会有一条 warning;24 起转正)
  • 许可:GPL-3.0

快速开始

npm install
npm run build            # 构建前端到 dist/
GALLERY_SECURE_COOKIE=0 npm start
# 打开 http://localhost:8080/Gallery/

本地开发(前后端一起起,Vite 代理 /Gallery 与 WebSocket 到 8080):

npm run dev              # 前端 5173,后端 8080
npm run seed             # 另开一个终端,灌一批演示数据

GALLERY_SECURE_COOKIE=0 只用于本地 HTTP 调试 —— 生产必须走 HTTPS,让会话 Cookie 带上 Secure。

GALLERY_ALLOWED_ORIGINS=origin1,origin2 放行跨源访问(默认关闭)。编辑器与 画廊同源时不需要;桌面端或本地开发把编辑器跑在别的源上时用它放行,编辑器会 用登录时签发的 Bearer Token 调接口。

编辑器本地开发时,在 rCodenow-GUI 里用 GALLERY_PROXY_TARGET=http://127.0.0.1:8601 npm start 把 /Gallery 代理到本服务端,两边保持同源。


目录结构

extension-gallery/
├── shared/              # 唯一真源:清单、依赖分析、semver、类型元数据
├── server/              # Express + node:sqlite
│   ├── db.js            # 连接与迁移
│   ├── migrations/      # SQL 迁移
│   ├── auth/            # 密码、会话、API Token、device code 授权
│   ├── routes/          # v2 接口 + 编辑器用的仓库列表 + 旧路径重定向
│   ├── services/        # 文件落盘、zip、sb3 解析、发布、序列化
│   └── realtime/        # WebSocket
├── web/src/             # 前端(React + gtk-js/adwaita)
│   ├── lib/             # 路由、接口客户端、全局状态、高亮、格式化
│   ├── components/      # 展示框、分栏外壳、通用控件、代码/文档查看器、评论
│   └── pages/           # 发现 / 全部 / 项目 / 消息 / 我的 / 创作 / 上传 / 关于
├── packages/cli/        # npm 包 rcodenow-gallery
├── test/                # 服务端与 shared 的 node:test
└── scripts/             # 端到端验证、演示数据、开发脚本

三类项目

类型 产物 项目页的「代码」 说明
作品(work) .sb3 跳转到 rCodenow 创作页 概览上方嵌一个可运行的 rCodenow 窗口
扩展(extension) 单文件 .js 源码树 + Prism 高亮 装进编辑器的扩展库
插件(plugin) 单文件 ESM .js 源码树 + Prism 高亮 改造编辑器界面与工作流

元数据:唯一真源

rcodenow-gallery.json 是元数据的唯一来源。网页端上传表单生成它,命令行读它, 服务端只做形状校验后原样存进 project_versions.manifest_json。

必填:名称、类型、语义化版本、许可证(SPDX)、支持的 rCodenow 版本范围、简介 可选:详细描述、标签、仓库、主页、封面、展示色、示例、文档

依赖不需要填。发布时服务端用 shared/deps.js 分析出来 —— CLI 与服务端 import 的是同一份代码,所以「上传前看到的」和「上传后看到的」必然一致:

  • 作品:读 sb3 里的 modconfigs.json 依赖列表,并扫描 project.json 里对画廊扩展的引用
  • 扩展 / 插件:扫描产物中的画廊项目地址,加上清单里显式声明的 dependencies

依赖到一个还没发布的东西也不会被吞掉 —— 它会以「未解析」的形式列出来。


接口

全部在 /Gallery/api/v2/ 下。浏览器用会话 Cookie,命令行与编辑器用 Authorization: Bearer <token>。

分组 路径
账号 POST /auth/register、/auth/login、/auth/logout、GET /auth/me、POST /auth/password、/auth/password/reset
Token GET/POST /auth/tokens、DELETE /auth/tokens/:id
浏览器授权 POST /auth/device/code、GET /auth/device/:userCode、POST /auth/device/approve、POST /auth/device/token、POST /auth/authorize
用户 GET /users/:username、PATCH /users/:username、POST /users/:username/avatar、GET /users/:username/projects?scope=assets|stars、GET /users/search
项目 GET /projects、GET /projects/discover、GET /projects/:owner/:slug、POST /projects、PATCH、DELETE、POST /transfer、POST/DELETE /star、POST /share、DELETE /share/:token、POST /cover
版本与文件 GET/POST /projects/:owner/:slug/versions、GET .../files?kind=、GET .../raw?path=、GET .../download?path=、GET .../bundle、GET .../readme、GET .../project.sb3、GET .../samples/:name、GET .../cover、GET .../icon
依赖 GET /projects/:owner/:slug/dependencies
评论 GET/POST /projects/:owner/:slug/comments、PATCH/DELETE .../comments/:id、GET .../comments/count
消息 GET /chat/channels、POST /chat/channels、POST /chat/channels/direct、GET/POST /chat/channels/:id/messages、POST /chat/attachments
编辑器 GET /repos/extensions.json、GET /repos/mods.json
实时 WS /Gallery/api/v2/ws

给 rCodenow 编辑器的两个文件

/Gallery/api/v2/repos/extensions.json 与 mods.json 是编辑器的输入契约,字段 形状与旧版一致(id / name / compatibility / extensionURL / modURL / dependencies …),只是数据来源从 JSON 文件换成了数据库。颜色永远有值、封面可能 为 null,编辑器拿到 null 会用颜色生成占位图标。

旧路径

v1 的对外路径(/extensionList.json、/extensions/<id>.js、/samples/<x>.sb3、 /images/<id>.png、/mods/<id>.sb3)全部 302 重定向 到 v2 对应地址,而不是 404。理由很实际:已发布插件把 meta.namespace 硬编码成了 https://rc.0832.ink/Gallery/extensions/<id>.js——那是它们的部署契约,服务器 单方面换成 v2 会让这些插件的自动更新静默失效。

旧上传接口 /api/upload、/api/upload-mod 返回 410 并说明换成什么。


权限

  • 密码:scrypt;会话与 API Token:sha256,库里没有明文
  • 浏览器会话:HttpOnly Cookie,SameSite=Lax(编辑器与画廊同源,弹窗授权不受第三方 Cookie 限制)
  • API Token:明文只在签发时返回一次,packages/cli 存在 ~/.config/rcodenow-gallery/config.json(0600)
  • 私有项目:只有所有者与持有未吊销分享链接的人能看到;对外一律表现为「不存在」
  • 忘记密码:已登录 + 注册邮箱匹配即可重置(会同时吊销所有 Token)。没有邮件服务, 所以没登记邮箱的账号需要联系管理员

命令行工具

见 packages/cli/README.md。

npm i -g rcodenow-gallery
rcodenow-gallery login
rcodenow-gallery init extension
npm install && npm run build
rcodenow-gallery upload

测试与验证

npm test                # 服务端 + shared + CLI(node:test,39 项)
npm run test:e2e        # 无头 Chrome 端到端(113 项,含截图)
npm run test:e2e:upload # 命令行上传端到端(不需要 Chrome)

npm test 跑的是真实链路:真的开 SQLite、真的收 multipart、真的解析 sb3, 不是在打桩的接口上数 200。CLI 那一组会真的构建模板工程、真的把产物传上去。

npm run test:e2e 会自己起一个服务端(临时数据目录)、灌演示数据、用无头 Chrome 把画廊点一遍:发现页 → 全部页 → 分类与标签筛选 → 项目页五个页签 → 评论区 → 收藏 → 登录注册 → 创作 → 上传 → 消息 → 我的三个分区 → 关于 → 未登录状态 → 404 → 窄屏布局, 共 113 项断言,截图落在 data/e2e-screenshots/。

其中有几项专门盯着「整页变空白 / 元素溢出视口」这类问题,每个页面都过一遍:

  • 没有元素横向溢出视口:遍历 DOM,凡是有元素超出视口且祖先不可横向滚动就失败。 库的 reset 把整页设成 content-box(贴近 GTK 的盒模型),而我们的布局大量用 「宽度 + 内边距」,很容易一个按钮比容器宽出一截、把内容面板顶到视口之外。
  • 未登录也能打开「…」页:/auth/me 回来之前 user 还是 null,页面如果在首帧就 解引用它(比如写在 hook 的依赖数组里),React 会卸载整棵树 —— 于是所有页面都 变成一片空白。逐页确认一次,顺便保证有 ErrorBoundary 兜底。
  • 侧栏的宽屏停靠 / 收起 / 展开、点侧栏入口不误收起,以及 320px 下的单列卡片。

npm run test:e2e:upload 只覆盖命令行上传那条链路,方便在没有浏览器的环境里验证。


已知限制

  • @gtk-js/adwaita 0.1.5 没有 AdwViewStack / AdwTabView / AdwTreeListView / 文件选择器:标签页用受控 AdwViewSwitcher + GtkStack,文件树自绘,目录上传用 <input webkitdirectory>
  • 同版本的 AdwOverlaySplitView 有个语义 bug:展开状态下 show-sidebar=false 会被 忽略(collapsed ? showSidebar : true),侧栏收不起来。应用外壳改用 web/src/components/SplitView.jsx,DOM 结构与 类名保持一致,只是把这条语义补上并给内容面板加了 min-width: 0
  • 同版本的 GtkButton 的 iconName 只渲染一个空的 <span class="gtk-image"/>, 不查图标表。带图标的按钮因此走 AdwButtonContent;busy 属性库不支持,由 components/common.jsx 的 Button 自己接(转圈 + 禁用)
  • 库的 reset 把整页设成 content-box,而我们的布局几乎每处都是「宽度 + 内边距」。 styles/base.css 用一条「类名里没有 gtk- / adw- 前缀就改回 border-box」的 规则统一处理 —— 新增组件自动被覆盖,不用回来改清单
  • 写操作全部串行在单进程里(node:sqlite 同步 API)。要横向扩到多实例得换 Postgres
  • 忘记旧密码的自助重置需要已登录态
  • 编辑器侧的对接(embed-project.html 播放入口、登录、上传作品、设封面)在 rCodenow-GUI 里实现;本仓库只提供接口与嵌入协议