- web/: Vue 3 + Vite + UnoCSS + Pinia, dark tactical theme (amber/#0d1117) - AppNav, ListingCard, SearchView with filters/sort, composables (useSnipeMode, useKonamiCode, useMotion), Pinia search store - Steal shimmer, auction countdown, Snipe Mode easter egg all native in Vue - docker/web/: nginx + multi-stage Dockerfile (node build → nginx serve) - compose.yml: api (8510) + web (8509) services - Dockerfile CMD updated to uvicorn for upcoming FastAPI layer - Clean build: 0 TS errors, 380 modules
49 lines
No EOL
895 B
JavaScript
49 lines
No EOL
895 B
JavaScript
const qs = require('querystring');
|
|
|
|
/**
|
|
* @typedef ParsedURL
|
|
* @type {import('.').ParsedURL}
|
|
*/
|
|
|
|
/**
|
|
* @typedef Request
|
|
* @property {string} url
|
|
* @property {ParsedURL} _parsedUrl
|
|
*/
|
|
|
|
/**
|
|
* @param {Request} req
|
|
* @returns {ParsedURL|void}
|
|
*/
|
|
function parse(req) {
|
|
let raw = req.url;
|
|
if (raw == null) return;
|
|
|
|
let prev = req._parsedUrl;
|
|
if (prev && prev.raw === raw) return prev;
|
|
|
|
let pathname=raw, search='', query, hash;
|
|
|
|
if (raw.length > 1) {
|
|
let idx = raw.indexOf('#', 1);
|
|
|
|
if (idx !== -1) {
|
|
hash = raw.substring(idx);
|
|
pathname = raw.substring(0, idx);
|
|
}
|
|
|
|
idx = pathname.indexOf('?', 1);
|
|
|
|
if (idx !== -1) {
|
|
search = pathname.substring(idx);
|
|
pathname = pathname.substring(0, idx);
|
|
if (search.length > 1) {
|
|
query = qs.parse(search.substring(1));
|
|
}
|
|
}
|
|
}
|
|
|
|
return req._parsedUrl = { pathname, search, query, hash, raw };
|
|
}
|
|
|
|
exports.parse = parse; |