mirror of
https://github.com/saltbo/zpan.git
synced 2026-08-29 00:01:42 +08:00
7048ecd417
* fix(security): constant-time compare for internal API token The internal telemetry endpoint compared the Bearer token with `!==`, a timing side-channel. Reuse a shared constantTimeEqual helper (extracted from download-tokens.ts so both call sites share one implementation). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(licensing): drop cached entitlement once the certificate expires loadEntitlement cached the verified summary for 60s keyed only on wall-clock age, so a certificate expiring mid-window kept granting Pro/Business features until the cache lapsed. On a cache hit, also verify nowSeconds is still before certificateExpiresAt and licenseValidUntil; otherwise re-verify (which yields null for the expired cert). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(ui): dedupe currency and size formatters into @/lib/format Add formatCurrency (locale-aware, cents→currency) and replace four identical local formatMoney copies in the store components. Replace the duplicated formatFileSize/formatTrackSize and the verbatim users-list formatDate with the shared formatSize/formatDate. The null-handling formatDate variants are left alone — they intentionally render '—' for null, which the shared helper does not. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(server): dedupe MIME→ext into lib/mime-utils Three services each hand-rolled a MIME_TO_EXT map + lookup. Consolidate into lib/mime-utils.mimeToExt (the superset of all keys, 'bin' fallback); each endpoint keeps its own allow-list and uses the shared lookup for naming. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(server): central domain-error → HTTP mapper Routes hand-rolled the same StorageQuotaExceededError→422 and NameConflictError→409 (with NAME_CONFLICT body) mappings — 6 sites in objects.ts, 4 in webdav.ts, 1 in ihost.ts, plus the WebDavPathError mapping. Add lib/http-errors.mapDomainError returning { status, message, json } and use it in each catch (JSON routes render json, WebDAV renders text). Removes the per-route instanceof ladders and the duplicated conflictBody helper. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(quota): route getEffectiveQuota through the batch aggregation getEffectiveQuota (single org, ~8 queries) and getEffectiveQuotasByOrg (batch, 2 queries + in-memory aggregation) reimplemented the same EffectiveQuota assembly. Have the single path call the batch path with one id and return its entry; delete the now-unused per-resource SQL helpers (activeExtraEntitlement Bytes/Names/Where). One aggregation path, fewer queries per call, -75 lines. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
20 lines
618 B
TypeScript
20 lines
618 B
TypeScript
/**
|
|
* Canonical image MIME → file-extension map. Endpoints keep their own
|
|
* allow-lists (which subset of these they accept); this is only the lookup
|
|
* for naming the stored object, so it is the superset of every allow-list.
|
|
*/
|
|
const MIME_TO_EXT: Record<string, string> = {
|
|
'image/png': 'png',
|
|
'image/jpeg': 'jpg',
|
|
'image/gif': 'gif',
|
|
'image/webp': 'webp',
|
|
'image/svg+xml': 'svg',
|
|
'image/x-icon': 'ico',
|
|
'image/vnd.microsoft.icon': 'ico',
|
|
}
|
|
|
|
/** File extension for an image MIME type, or 'bin' when unknown. */
|
|
export function mimeToExt(mime: string): string {
|
|
return MIME_TO_EXT[mime] ?? 'bin'
|
|
}
|