feat: 补全皮肤站管理台 Mod 页上传与批量操作
Sync source to CNB / Sync Git ref (push) Skipped
Prepare pnpm cache / prepare (push) Canceled after 0s

- 新增 Mod 上传弹窗:拖拽或选择 .jar、多文件、大小限制、规则标签附加
- 上传流程:xxHash64 查重、同名检测、名称冲突确认(覆盖/跳过)、429 限流退避重试、HashDiff 重传
- Mod 列表支持多选模式:全选本页、取消选择、批量删除
- 新增服务器地址过滤,配合名称搜索与标签筛选
- 补充 Mod 相关 API:文件哈希、按哈希/名称查重、上传、批量更新
- 引入 xxhash-wasm 依赖
- 中英文各补 32 个文案
This commit is contained in:
AxTps committed 2026-10-18 12:02:39 +08:00
1 parent fbcbb803ac
commit 87de306245
7 files changed
+767 -4

No files matched your search

@@ -2015,3 +2015,59 @@ export function skinSiteAdminInviteUser(email: string): Promise<SkinSiteResponse
body: { email },
})
}
export function skinSiteModHashFromFile(file: File): Promise<string> {
return import('xxhash-wasm').then(async ({ default: xxhash }) => {
const { create64 } = await xxhash()
const hasher = create64()
const reader = file.stream().getReader()
for (;;) {
const { done, value } = await reader.read()
if (done) break
hasher.update(value)
}
const raw: bigint = hasher.digest()
const mask = (1n << 64n) - 1n
const v = raw & mask
const signed = v >= 1n << 63n ? v - (1n << 64n) : v
return signed.toString()
})
}
export function skinSiteModExistsByHash(hash: string): Promise<SkinSiteResponse<unknown>> {
return skinSiteRequest<unknown>('query/hash/' + encodeURIComponent(hash), { type: 'mod' })
}
export function skinSiteModExistsByName(modName: string): Promise<SkinSiteResponse<unknown>> {
const q = new URLSearchParams({ mod_name: modName })
return skinSiteRequest<unknown>('query/name?' + q.toString(), { type: 'mod' })
}
export async function skinSiteUploadMod(
file: File,
ruleTagIds: number[] = [],
overwrite = false,
): Promise<SkinSiteResponse<string>> {
const hash = await skinSiteModHashFromFile(file)
const form = new FormData()
form.append('mods', file, file.name)
const query = new URLSearchParams()
if (ruleTagIds.length) query.set('rule_tag_ids', ruleTagIds.join(','))
if (overwrite) query.set('overwrite', 'true')
const suffix = query.size ? '?' + query.toString() : ''
return skinSiteRequest<string>('upload/' + hash + suffix, {
method: 'POST',
type: 'mod',
form,
})
}
export function skinSiteUpdateMultiModInfo(
mods: SkinSiteModInfo[],
): Promise<SkinSiteResponse<SkinSiteModInfo[]>> {
return skinSiteRequest<SkinSiteModInfo[]>('update/info/multi', {
method: 'POST',
type: 'mod',
body: mods,
})
}