@mandujs/core 0.45.0 → 0.45.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/runtime/server.ts +30 -0
package/package.json
CHANGED
package/src/runtime/server.ts
CHANGED
|
@@ -1638,17 +1638,38 @@ async function isPathSafe(filePath: string, allowedDir: string): Promise<boolean
|
|
|
1638
1638
|
}
|
|
1639
1639
|
}
|
|
1640
1640
|
|
|
1641
|
+
/**
|
|
1642
|
+
* Issue #251 — public 폴더의 자산을 root URL로도 서빙하기 위한 화이트리스트.
|
|
1643
|
+
*
|
|
1644
|
+
* `mandu build --static` 은 `public/*` 을 dist 루트로 평탄화하므로 prod 에서는
|
|
1645
|
+
* `/images/foo.webp` 가 동작한다. dev 에서는 평탄화가 없어서 같은 URL 이 404
|
|
1646
|
+
* 였다 — 작성자는 `/public/...` (dev OK, prod 도 vercel rewrite 로 OK) 또는
|
|
1647
|
+
* `/...` (dev 깨짐, prod OK) 중 하나를 골라야 했다. 이제 dev 도 자산 확장자가
|
|
1648
|
+
* 있는 경로에 한해 `public/<path>` 를 fallback 으로 시도한다.
|
|
1649
|
+
*
|
|
1650
|
+
* 자산 확장자만 fallback 하므로 `/api/foo` 같은 라우트가 가려질 위험은 없다.
|
|
1651
|
+
* 파일이 없으면 `{ handled: false }` 를 반환해 라우터가 정상 매칭하도록 한다.
|
|
1652
|
+
*/
|
|
1653
|
+
const PUBLIC_FLAT_ASSET_EXTENSIONS = new Set<string>([
|
|
1654
|
+
".webp", ".avif", ".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico",
|
|
1655
|
+
".pdf", ".zip", ".mp4", ".webm", ".mp3", ".wav",
|
|
1656
|
+
".woff", ".woff2", ".ttf", ".otf", ".eot",
|
|
1657
|
+
".css", ".js", ".map",
|
|
1658
|
+
]);
|
|
1659
|
+
|
|
1641
1660
|
/**
|
|
1642
1661
|
* 정적 파일 서빙
|
|
1643
1662
|
* - /.mandu/client/* : 클라이언트 번들 (Island hydration)
|
|
1644
1663
|
* - /public/* : 정적 에셋 (이미지, CSS 등)
|
|
1645
1664
|
* - /favicon.ico : 파비콘
|
|
1665
|
+
* - /<asset>.<ext> : public/<asset>.<ext> fallback (issue #251)
|
|
1646
1666
|
*
|
|
1647
1667
|
* 보안: Path traversal 공격 방지를 위해 모든 경로를 검증합니다.
|
|
1648
1668
|
*/
|
|
1649
1669
|
async function serveStaticFile(pathname: string, settings: ServerRegistrySettings, request?: Request): Promise<StaticFileResult> {
|
|
1650
1670
|
let filePath: string | null = null;
|
|
1651
1671
|
let isBundleFile = false;
|
|
1672
|
+
let isPublicFlatFallback = false;
|
|
1652
1673
|
let allowedBaseDir: string;
|
|
1653
1674
|
let relativePath: string;
|
|
1654
1675
|
|
|
@@ -1678,6 +1699,12 @@ async function serveStaticFile(pathname: string, settings: ServerRegistrySetting
|
|
|
1678
1699
|
) {
|
|
1679
1700
|
relativePath = path.basename(pathname);
|
|
1680
1701
|
allowedBaseDir = path.join(settings.rootDir, settings.publicDir);
|
|
1702
|
+
}
|
|
1703
|
+
// 5. Public flat fallback (#251) — `mandu build --static` 의 평탄화와 dev 패리티
|
|
1704
|
+
else if (PUBLIC_FLAT_ASSET_EXTENSIONS.has(path.extname(pathname).toLowerCase())) {
|
|
1705
|
+
relativePath = pathname.slice(1);
|
|
1706
|
+
allowedBaseDir = path.join(settings.rootDir, settings.publicDir);
|
|
1707
|
+
isPublicFlatFallback = true;
|
|
1681
1708
|
} else {
|
|
1682
1709
|
return { handled: false }; // 정적 파일이 아님
|
|
1683
1710
|
}
|
|
@@ -1717,6 +1744,9 @@ async function serveStaticFile(pathname: string, settings: ServerRegistrySetting
|
|
|
1717
1744
|
const exists = await file.exists();
|
|
1718
1745
|
|
|
1719
1746
|
if (!exists) {
|
|
1747
|
+
// #251 — flat fallback 은 라우트와 path 충돌이 가능하므로 미존재 시
|
|
1748
|
+
// 404 대신 라우터로 흘려보낸다 (e.g. `/foo.json` 라우트가 가려지지 않도록).
|
|
1749
|
+
if (isPublicFlatFallback) return { handled: false };
|
|
1720
1750
|
return { handled: true, response: createStaticErrorResponse(404) };
|
|
1721
1751
|
}
|
|
1722
1752
|
|