Browse Source

fix(media): recognize absolute /public/assets/ URLs in markdown for refs sync

Made-with: Cursor
tags/邮箱功能前置
npmrun 3 weeks ago
parent
commit
dc74dcf4e2
  1. 34
      server/utils/post-media-urls.test.ts
  2. 18
      server/utils/post-media-urls.ts

34
server/utils/post-media-urls.test.ts

@ -0,0 +1,34 @@
import { describe, expect, test } from "bun:test";
import { extractMediaUrlsFromMarkdown, publicAssetUrlToStorageKey } from "./post-media-urls";
describe("extractMediaUrlsFromMarkdown", () => {
test("accepts site-relative /public/assets/ URL", () => {
expect(extractMediaUrlsFromMarkdown("![](/public/assets/a.webp)")).toEqual(["/public/assets/a.webp"]);
});
test("accepts absolute URL with same path and normalizes to relative", () => {
expect(
extractMediaUrlsFromMarkdown("![](https://blog.example.com/public/assets/b.webp)"),
).toEqual(["/public/assets/b.webp"]);
});
test("strips query on relative asset URL", () => {
expect(extractMediaUrlsFromMarkdown("![](/public/assets/c.webp?v=1)")).toEqual(["/public/assets/c.webp"]);
});
test("strips query on absolute asset URL via pathname", () => {
expect(
extractMediaUrlsFromMarkdown("![](https://x.example/public/assets/d.webp?cache=1)"),
).toEqual(["/public/assets/d.webp"]);
});
test("ignores non-asset absolute URLs", () => {
expect(extractMediaUrlsFromMarkdown("![](https://evil.com/other.png)")).toEqual([]);
});
});
describe("publicAssetUrlToStorageKey", () => {
test("maps normalized path to storage key", () => {
expect(publicAssetUrlToStorageKey("/public/assets/z.webp")).toBe("z.webp");
});
});

18
server/utils/post-media-urls.ts

@ -1,11 +1,25 @@
import { POST_MEDIA_PUBLIC_PREFIX } from "#server/constants/media";
import { POST_MEDIA_PUBLIC_PREFIX } from "../constants/media";
const MD_IMG_RE = /!\[[^\]]*]\(([^)]+)\)/g;
function stripQueryHash(path: string): string {
return path.split("?")[0]?.split("#")[0] ?? path;
}
/** 统一为 `/public/assets/<key>`,供 `publicAssetUrlToStorageKey` 与引用同步使用。 */
function normalizeUrl(raw: string): string {
const t = raw.trim().replace(/^<|>$/g, "").split(/\s+/)[0] ?? "";
if (t.startsWith(POST_MEDIA_PUBLIC_PREFIX)) {
return t;
return stripQueryHash(t);
}
try {
const u = new URL(t);
const path = stripQueryHash(u.pathname);
if (path.startsWith(POST_MEDIA_PUBLIC_PREFIX)) {
return path;
}
} catch {
/* 非绝对 URL */
}
return "";
}

Loading…
Cancel
Save