a870c7f03e
A folder-picked Fortnox export failed 50 of 50 attaches with UNDERLAG_REF_MISMATCH although the preview had matched every file. The preview is built from File.name, a bare filename by spec, while the attach route read the multipart filename, which Chrome fills with the folder-relative path for folder selections (2026/06/Leverantorsfakturor/A166_x.pdf). The guard that requires a file to land where the preview said compared the previewed basename with a path the parser cannot read, and refused. The route now reduces the multipart filename to its basename once, at the boundary, before the resolver check and before archiving, so the archived file_name is the name the user reviewed rather than a path. The parser keeps its no-directory-stripping rule: the manual-reference box shares it, and a typed 2024/01/31 there is a date, not voucher 31. Both separators are stripped; nothing else is normalized. Claude-Session: https://claude.ai/code/session_014uwXchJvF5YMgz8vRfuxLe Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
24 lines
1.2 KiB
TypeScript
24 lines
1.2 KiB
TypeScript
/**
|
|
* The name of an uploaded file as the user saw it: the last path segment of
|
|
* whatever the browser wrote into the multipart `filename` parameter.
|
|
*
|
|
* `File.name` in the browser is a bare filename by spec. The `filename` the
|
|
* same browser writes into multipart/form-data is not guaranteed to be that
|
|
* string: Chrome fills it with `webkitRelativePath` for files that came from
|
|
* a folder selection. A receipt picked out of a Fortnox export as
|
|
* `2026/06/Leverantörsfakturor/A166_Hetzner.pdf` therefore arrives server-side
|
|
* under that whole path, while every client-side read of `file.name`, and so
|
|
* every preview the user approved, said `A166_Hetzner.pdf`.
|
|
*
|
|
* Any server logic that compares an uploaded name to something the client
|
|
* computed from `File.name`, or stores the name for the user to read back,
|
|
* must go through this first. Both separators are stripped: Chrome writes `/`
|
|
* on every platform, legacy Windows clients sent `\`. Nothing else is
|
|
* normalized, on purpose: the voucher-ref parser must see the name exactly as
|
|
* the exporting system wrote it.
|
|
*/
|
|
export function uploadedFileBaseName(name: string): string {
|
|
const cut = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\'))
|
|
return cut === -1 ? name : name.slice(cut + 1)
|
|
}
|