feat(storage): 存储实例与 Alist 虚拟路径(迁移 0090、设置接口、注册表、vfs、健康检查)
- storage_instance 表 + secret 用途 storage_credential;令牌 / 密码 / 路径密码只写不读 - /storage-instances CRUD 与测试连接(版本、用户、根路径、容量);被库根引用不能删 - 远端路径 /@alist/<id>/<路径>,vfs 统一本地 / Alist 的 stat、列目录、建目录、读写、移动、删除 - 整理矩阵中涉及 Alist 的传输(storage/transfer.ts)与远端 ffprobe(直链只读头) - 媒体库目录可登记在 Alist 上(不写哨兵,目录可访问即在线);健康检查列出存储实例 Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
1 parent
5ff24110f8
commit
c2bf8a071a
20 files changed
+1625
-8
No files matched your search
@@ -0,0 +1,23 @@
|
||||
import { ffprobe, type ProbeResult, redactAlistText } from "@umt/adapters";
|
||||
import { vrawUrl } from "./vfs.ts";
|
||||
import { parseVPath } from "./vpath.ts";
|
||||
|
||||
// 远端文件的 ffprobe(docs/design/storage-alist.md §7):不下载,直接对签名直链探测,ffprobe 只读容器头与少量包(HTTP Range)。
|
||||
// 拿不到直链或探测失败返回 unknown(不阻塞,同本地 ffprobe 失败的口径)。直链进错误信息前脱敏。
|
||||
|
||||
export async function probeAny(
|
||||
path: string,
|
||||
local: (p: string) => Promise<ProbeResult> = (p) => ffprobe(p),
|
||||
): Promise<ProbeResult> {
|
||||
if (!parseVPath(path)) return local(path);
|
||||
let url: string;
|
||||
try {
|
||||
url = await vrawUrl(path);
|
||||
} catch (e) {
|
||||
return { status: "unknown", reason: "probe_failed", message: redactAlistText(String((e as Error)?.message ?? e)) };
|
||||
}
|
||||
const r = await ffprobe(url, { inputArgs: ["-rw_timeout", "30000000"], timeoutMs: 60_000 });
|
||||
if (r.status === "unknown" && r.message)
|
||||
return { ...r, message: redactAlistText(r.message.split(url).join("<直链>")) };
|
||||
return r;
|
||||
}
|
||||
Reference in new issue
Block a user