Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 22x 22x 2x 20x 20x 22x 20x 20x 20x 20x 20x 640x 20x | /**
* Shared URL normalization + idempotency hashing for pro imports.
*
* Used by admin import enqueue routes. The idempotency key is
* `SHA-256(proId | normalized_url)` and lives on `pro_imports.idempotencyKey`
* with a unique constraint, so the same Pro cannot queue the same URL twice
* concurrently.
*/
import { ValidationError } from "../errors";
/**
* Strip fragment, drop trailing slash, lowercase origin. Keeps query string
* verbatim because Houzz includes user-scoped query params that distinguish
* profiles (e.g. `?irs=…`).
*/
export function normalizeUrl(raw: string): string {
let url: URL;
try {
url = new URL(raw);
} catch {
throw new ValidationError("sourceUrl is not a valid URL");
}
url.hash = "";
const cleanedPath =
url.pathname.endsWith("/") && url.pathname.length > 1
? url.pathname.slice(0, -1)
: url.pathname;
return `${url.origin.toLowerCase()}${cleanedPath}${url.search}`;
}
/** SHA-256 hex digest of `${proId}|${normalizedUrl}`. */
export async function hashIdempotencyKey(
proId: string,
normalizedUrl: string,
): Promise<string> {
const data = new TextEncoder().encode(`${proId}|${normalizedUrl}`);
const digest = await crypto.subtle.digest("SHA-256", data);
const bytes = new Uint8Array(digest);
let hex = "";
for (let i = 0; i < bytes.length; i++) {
hex += bytes[i].toString(16).padStart(2, "0");
}
return hex;
}
|