-
-
Notifications
You must be signed in to change notification settings - Fork 0
feat(api): align wildcard and ucd-store routes #460
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,16 @@ | ||
| /* eslint-disable no-console */ | ||
| import type { Entry } from "apache-autoindex-parse"; | ||
| import { trimLeadingSlash, trimTrailingSlash } from "@luxass/utils"; | ||
| import { createGlobMatcher } from "@ucdjs-internal/shared"; | ||
| import { | ||
| UCD_STAT_CHILDREN_DIRS_HEADER, | ||
| UCD_STAT_CHILDREN_FILES_HEADER, | ||
| UCD_STAT_CHILDREN_HEADER, | ||
| UCD_STAT_SIZE_HEADER, | ||
| UCD_STAT_TYPE_HEADER, | ||
| } from "@ucdjs/env"; | ||
| import { parse } from "apache-autoindex-parse"; | ||
| import { HTML_EXTENSIONS } from "../constants"; | ||
|
|
||
| /** | ||
| * Parses an HTML directory listing from Unicode.org and extracts file/directory entries. | ||
|
|
@@ -75,15 +84,13 @@ export function applyDirectoryFiltersAndSort( | |
|
|
||
| // Apply query filter (prefix search, case-insensitive) | ||
| if (options.query) { | ||
| // eslint-disable-next-line no-console | ||
| console.info(`[v1_files]: applying query filter: ${options.query}`); | ||
| const queryLower = options.query.toLowerCase(); | ||
| filtered = filtered.filter((entry) => entry.name.toLowerCase().startsWith(queryLower)); | ||
| } | ||
|
|
||
| // Apply pattern filter if provided | ||
| if (options.pattern) { | ||
| // eslint-disable-next-line no-console | ||
| console.info(`[v1_files]: applying glob pattern filter: ${options.pattern}`); | ||
| const matcher = createGlobMatcher(options.pattern); | ||
| filtered = filtered.filter((entry) => matcher(entry.name)); | ||
|
|
@@ -123,3 +130,97 @@ export function applyDirectoryFiltersAndSort( | |
|
|
||
| return filtered; | ||
| } | ||
|
|
||
| export function buildDirectoryHeaders( | ||
| files: Entry[], | ||
| baseHeaders: Record<string, string>, | ||
| ): Record<string, string> { | ||
| return { | ||
| ...baseHeaders, | ||
| [UCD_STAT_TYPE_HEADER]: "directory", | ||
| [UCD_STAT_CHILDREN_HEADER]: `${files.length}`, | ||
| [UCD_STAT_CHILDREN_FILES_HEADER]: `${files.filter((f) => f.type === "file").length}`, | ||
| [UCD_STAT_CHILDREN_DIRS_HEADER]: `${files.filter((f) => f.type === "directory").length}`, | ||
| }; | ||
| } | ||
|
|
||
| export function buildFileHeaders( | ||
| contentType: string, | ||
| baseHeaders: Record<string, string>, | ||
| response: Response, | ||
| actualContentLength: number, | ||
| ): Record<string, string> { | ||
| const headers: Record<string, string> = { | ||
| "Content-Type": contentType, | ||
| ...baseHeaders, | ||
| [UCD_STAT_TYPE_HEADER]: "file", | ||
| [UCD_STAT_SIZE_HEADER]: `${actualContentLength}`, | ||
| "Content-Length": `${actualContentLength}`, | ||
| }; | ||
|
|
||
| const cd = response.headers.get("Content-Disposition"); | ||
| if (cd) headers["Content-Disposition"] = cd; | ||
|
|
||
| return headers; | ||
| } | ||
|
|
||
| export interface FileResponseOptions { | ||
| contentType: string; | ||
| baseHeaders: Record<string, string>; | ||
| response: Response; | ||
| isHeadRequest: boolean; | ||
| } | ||
|
|
||
| export async function handleFileResponse( | ||
| c: any, | ||
| options: FileResponseOptions, | ||
| ): Promise<Response> { | ||
| const { contentType, baseHeaders, response, isHeadRequest } = options; | ||
|
|
||
| if (isHeadRequest) { | ||
| const blob = await response.blob(); | ||
| const actualSize = blob.size; | ||
| const headers = buildFileHeaders(contentType, baseHeaders, response, actualSize); | ||
| console.log(`[file-handler]: HEAD request, calculated size: ${actualSize}`); | ||
| return c.newResponse(null, 200, headers); | ||
| } | ||
|
|
||
| const headers: Record<string, string> = { | ||
| "Content-Type": contentType, | ||
| ...baseHeaders, | ||
| [UCD_STAT_TYPE_HEADER]: "file", | ||
| }; | ||
|
|
||
| const cd = response.headers.get("Content-Disposition"); | ||
| if (cd) headers["Content-Disposition"] = cd; | ||
|
|
||
| console.log(`[file-handler]: binary file, streaming without buffering`); | ||
|
Comment on lines
+184
to
+197
|
||
|
|
||
| return c.newResponse(response.body, 200, headers); | ||
| } | ||
|
|
||
| export interface DirectoryResponseOptions { | ||
| files: Entry[]; | ||
| baseHeaders: Record<string, string>; | ||
| } | ||
|
|
||
| export function handleDirectoryResponse( | ||
| c: any, | ||
|
||
| options: DirectoryResponseOptions, | ||
| ): Response { | ||
| const { files, baseHeaders } = options; | ||
| const headers = buildDirectoryHeaders(files, baseHeaders); | ||
| return c.json(files, 200, headers); | ||
| } | ||
|
Comment on lines
+134
to
+214
|
||
|
|
||
| export function determineFileExtension(leaf: string): string { | ||
| return leaf.includes(".") ? leaf.split(".").pop()!.toLowerCase() : ""; | ||
| } | ||
|
|
||
| export function isHtmlFile(extName: string): boolean { | ||
| return HTML_EXTENSIONS.includes(`.${extName}`); | ||
| } | ||
|
|
||
| export function isDirectoryListing(contentType: string, extName: string): boolean { | ||
| return contentType.includes("text/html") && !isHtmlFile(extName); | ||
| } | ||
|
Comment on lines
+134
to
+226
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The parameter
chas typeanywhich bypasses type checking. Consider usingContext<HonoEnv>imported from "hono" for better type safety.