Architecture

Design Architecture ​

设计架构 ​

OpenList Worker is a Serverless-first rewrite of the OpenList Go backend in TypeScript. The system is divided into three layers: edge runtime, data access, and frontend static assets.

OpenList Worker 是将 OpenList Go 后端以 TypeScript 重写的 Serverless 优先架构。系统分为边缘运行时、数据访问和前端静态资源三层。

Tech Stack ​

技术栈 ​

Backend ​

后端 ​

ComponentTechnologyDescription
HTTP frameworkHono.jsLightweight, edge-native web framework
RuntimeCloudflare Workers / EdgeOne Functions / ESAEdge compute platforms
LanguageTypeScriptFully typed, compiled via esbuild / Vite
ORMDrizzle ORMType-safe SQL query builder for D1 / MySQL
Build toolesbuild / ViteSingle-file Worker bundle
组件技术说明
HTTP 框架Hono.js轻量级、边缘原生 Web 框架
运行时Cloudflare Workers / EdgeOne 云函数 / ESA边缘计算平台
语言TypeScript全类型,使用 esbuild / Vite 编译
ORMDrizzle ORM为 D1 / MySQL 提供类型安全的 SQL 查询构建器
构建工具esbuild / Vite单文件 Worker 产物

Frontend ​

前端 ​

ComponentTechnologyDescription
FrameworkSolidJS + TypeScriptReactive SPA frontend
UI libraryHope UI (@hope-ui/solid)Component library
Build toolViteFast frontend build
Bundled withWorkers Static AssetsServed from the same origin as the API
组件技术说明
框架SolidJS + TypeScript响应式 SPA 前端
UI 库Hope UI(@hope-ui/solid)组件库
构建工具Vite快速前端构建
与 Worker 同源Workers Static AssetsAPI 与前端同源部署,无跨域问题

Data Storage ​

数据存储 ​

Storage Format (DB_FORMAT) ​

存储格式(DB_FORMAT) ​

The DB_FORMAT variable controls how data is serialized:

ValueDescriptionBest for
map (default)Whole object serialized as a single JSON valueKV / Blob storage
keyPer-key storage, one record per entityKV with high read frequency
sqlRelational tables, identical schema to the Go backendD1 / MySQL — enables Go ↔ TS migration

DB_FORMAT 控制数据序列化方式:

值说明最适用场景
map(默认)整对象序列化为单个 JSON 值KV / Blob 存储
key分 key 存储,每实体一条记录高频读写 KV
sql关系表,与 Go 后端 schema 完全一致D1 / MySQL——支持 Go ↔ TS 迁移

Storage Driver (DB_DRIVER) ​

存储驱动(DB_DRIVER) ​

The DB_DRIVER variable selects the physical storage backend:

ValuePlatformDescription
auto (default)UniversalAuto-detect: blob → cfkv → kv → d1 → memory
blobEdgeOne / ESAEdgeOne Blob or Alibaba ESA Blob
cfkvUniversalCloudflare KV via REST API (cross-platform)
kvCloudflare WorkersCloudflare KV binding
d1Cloudflare WorkersCloudflare D1 (SQLite)
doCloudflare WorkersDurable Objects (strong consistency)
mysqlNode.js containerExternal MySQL / MariaDB

DB_DRIVER 选择物理存储后端:

值平台说明
auto(默认)通用自动检测:blob → cfkv → kv → d1 → memory
blobEdgeOne / ESAEdgeOne Blob 或阿里云 ESA Blob
cfkv通用Cloudflare KV REST API(跨平台远程调用)
kvCloudflare WorkersCloudflare KV 绑定
d1Cloudflare WorkersCloudflare D1(SQLite)
doCloudflare WorkersDurable Objects(强一致性)
mysqlNode.js 容器外部 MySQL / MariaDB

SQL Table Alignment with Go Backend ​

与 Go 后端的 SQL 表对齐 ​

When DB_FORMAT = "sql", the TS Worker uses the same table names and schema as the Go backend (GORM, default prefix x_), so the two backends can share the same physical database:

Go structTable name
SettingItemx_setting_items
SharingDBx_sharing_dbs
Storagex_storages
Userx_users
Metax_metas
(TS only)x_plugins
(TS runtime)x_singleflight

The prefix is fixed to x_, matching the Go backend default.

x_singleflight is a runtime table used for cross-isolate request deduplication (see below). It is created automatically by the SQL driver on initialization, holds no configuration, and is intentionally excluded from the configuration tables — the Go backend has no such table, so sharing a physical database is unaffected.

当 DB_FORMAT = "sql" 时,TS Worker 使用与 Go 后端(GORM,默认前缀 x_)相同的表名与 schema,两个后端可共享同一物理数据库:

Go 结构体表名
SettingItemx_setting_items
SharingDBx_sharing_dbs
Storagex_storages
Userx_users
Metax_metas
(仅 TS)x_plugins
(TS 运行时)x_singleflight

表名前缀固定为 x_,与 Go 后端默认值一致。

x_singleflight 是用于跨 isolate 请求去重的运行时表(见下文)。它由 SQL 驱动在初始化时自动创建,不承载任何配置,并被刻意排除在配置表之外——Go 后端没有这张表,因此不影响共享同一物理数据库。

Concurrent Deduplication (singleflight) ​

并发去重(singleflight) ​

A single deployment runs across multiple isolates, so an in-process map alone cannot merge duplicate calls across instances. Every concurrent request for the same directory listing or metadata would then hit the upstream cloud-drive API separately — a major source of rate limiting, account bans and CPU timeouts. OpenList Worker deduplicates these calls at two levels:

LevelMechanismEffective when
L1 — in-processMap<key, Promise> mergeAll modes
L2 — cross-instanceGrab a lock in the x_singleflight table and share the resultdb mode only

The mode is controlled by SINGLEFLIGHT (auto / db / memory / off, default auto): when a SQL backend (d1 / mysql / do) is available the table is used for coordination, otherwise it falls back to in-process merging.

Design notes:

  • Locking without transactions: INSERT OR IGNORE (MySQL: INSERT IGNORE) plus a read-back of owner to confirm ownership.
  • Heartbeat renewal: the executor renews the lock every lockTtl / 3; if it crashes, the lock expires and another instance can take it over.
  • Waiting side: polls for the result; on timeout it executes the call itself, so requests are never left hanging.
  • No result caching: only calls that observe a running executor on arrival reuse the result; a newly arrived call reclaims the old record and re-executes, which rules out stale reads such as "a deleted file still shows up after refresh".
  • Fault isolation: exceptions thrown by the business function are rethrown as-is without retry; coordination-layer failures (missing table, DB timeout, non-serializable result, executor crash) all degrade to "each caller executes once", so the business call never fails.

Integration scope: listItems / getItem in the storage operation layer, with the dedup key fs.list|<storage.id>:<storage.modified>:<virtualPath>. One integration therefore covers /fs/list, /fs/get, /fs/dir, search, WebDAV and MCP. Runtime statistics are exposed in the singleflight field of /api/debug/info.

一个部署会同时运行多个 isolate,仅靠进程内的 Map 无法跨实例合并重复调用,导致同一目录列表 / 元信息的并发请求各自打一次上游网盘 API——这正是限流、封号、CPU 超时的主要来源。OpenList Worker 对这类调用做两级去重:

层级机制生效条件
L1 进程内Map<key, Promise> 合并所有模式
L2 跨实例在 x_singleflight 表中抢锁并共享结果仅 db 模式

模式由 SINGLEFLIGHT 控制(auto / db / memory / off,默认 auto):有 SQL 库(d1 / mysql / do)时用表协调,否则退回进程内合并。

设计要点:

  • 不依赖事务抢锁:INSERT OR IGNORE(MySQL 为 INSERT IGNORE)+ 回读 owner 确认归属。
  • 心跳续租:执行者按 lockTtl / 3 周期续租;崩溃后锁到期,可被其他实例接管。
  • 等待方:轮询读取结果,超时后自行执行,不会把请求挂死。
  • 不做结果缓存:只有「到达时观察到执行者正在运行」的调用才复用结果;新到达的调用会回收旧记录并重新执行,因此不会出现「删除文件后刷新仍看到被删文件」这类脏读。
  • 故障隔离:业务函数抛出的异常原样抛出且不重试;协调层异常(表缺失 / 数据库超时 / 结果不可序列化 / 执行者崩溃)一律降级为「各自执行一次」,业务不会失败。

接入范围:存储操作层的 listItems / getItem,去重键为 fs.list|<storage.id>:<storage.modified>:<virtualPath>。因此一次接入即覆盖 /fs/list、/fs/get、/fs/dir、搜索、WebDAV 与 MCP。运行统计见 /api/debug/info 的 singleflight 字段。

Project Structure ​

项目结构 ​

OpenList-Worker/
├── src/
│   ├── backend/          # Hono.js Worker entry & backend logic
│   │   ├── worker.ts     # Cloudflare Workers entry
│   │   ├── drivers/      # Storage driver implementations (kv / d1 / blob / mysql …)
│   │   ├── server/       # Route registrations & middleware
│   │   ├── pkg/          # Shared utilities & helpers
│   │   └── internal/     # Core business logic (auth, storage, meta, …)
│   └── frontend/         # Built-in frontend (SolidJS + Vite)
├── dist/                 # Build output (Worker bundle + frontend assets)
├── esa-entry.ts          # Alibaba Cloud ESA entry
├── wrangler.toml         # Cloudflare Workers configuration
├── esa.jsonc             # Alibaba Cloud ESA configuration
├── edgeone.json          # EdgeOne schedules configuration
└── package.json
OpenList-Worker/
├── src/
│   ├── backend/          # Hono.js Worker 入口与后端逻辑
│   │   ├── worker.ts     # Cloudflare Workers 入口
│   │   ├── drivers/      # 存储驱动实现(kv / d1 / blob / mysql …)
│   │   ├── server/       # 路由注册与中间件
│   │   ├── pkg/          # 公共工具与辅助函数
│   │   └── internal/     # 核心业务逻辑(认证、存储、元数据 …)
│   └── frontend/         # 内置前端(SolidJS + Vite)
├── dist/                 # 构建产物(Worker bundle + 前端资源)
├── esa-entry.ts          # 阿里云 ESA 入口
├── wrangler.toml         # Cloudflare Workers 配置
├── esa.jsonc             # 阿里云 ESA 配置
├── edgeone.json          # EdgeOne 定时任务配置
└── package.json

Supported Platforms ​

支持平台 ​

PlatformEntryPersistenceNotes
Cloudflare Workersworker.tsD1 / KV / DOOne-click deploy supported
Tencent Cloud EdgeOneworker.tsBlob / KVOne-click deploy supported
Alibaba Cloud ESAesa-entry.tsEdgeKVManual build & deploy
Node.js containerworker.ts (with adapter)MySQLSelf-hosted
平台入口持久化备注
Cloudflare Workersworker.tsD1 / KV / DO支持一键部署
腾讯云 EdgeOneworker.tsBlob / KV支持一键部署
阿里云 ESAesa-entry.tsEdgeKV手动构建部署
Node.js 容器worker.ts(带适配层)MySQL自托管

Contributors