Files
pc_builder/config/shield.ts
T
Kevin L'Hereec 62ec796e05
Build & Deploy / build (push) Failing after 4s
Build & Deploy / docker (push) Skipped
fix: repair broken CSP and compare live merchant prices for new parts
CSP fix:
- The previous shield.ts config used '@viteDevUrl'/'@viteHmrUrl' CSP
  keywords that don't exist in this version of @adonisjs/shield (only
  '@viteUrl' is registered by @adonisjs/vite, and it resolves to an empty
  string here since Vite assets are proxied through AdonisJS on the same
  origin). Both placeholders were sent to the browser literally, breaking
  Google Fonts, the Vite HMR websocket, and pc-builder.io/favicon images.
- Replaced with concrete directives: 'self' covers same-origin Vite
  assets, ws://localhost:{VITE_HMR_PORT|24678} is added only in dev for
  the HMR socket, and explicit allowlists for Google Fonts
  (fonts.googleapis.com/fonts.gstatic.com), pc-builder.io product images,
  and Google's favicon service (www.google.com) used by the merchant
  comparison UI

Real purchase links + live price comparison:
- part_scraper_service.ts: ScrapedPart.sourceUrl now stores the actual
  Amazon.fr purchase link scraped from pc-builder.io's category pages
  (https://www.amazon.fr/dp/{ASIN}?tag=... or a search fallback), instead
  of the pc-builder.io product page, which isn't a place anyone can buy
  anything from
- New merchant_price_service.ts: on-demand live price lookup across
  Amazon, LDLC, Grosbill and TopAchat. Of 6 requested merchants,
  Materiel.net (search requires JS/Turbo, no results via plain GET) and
  Cdiscount/PCComponentes (empty body / 403) were dropped as not scrapable
  without a headless browser or paid API
- Matching strategy: pc-builder.io's full product names (e.g. "AMD Ryzen 3
  3200G 3.6 GHz Quad-Core Processor") return zero results on every
  merchant's search engine, so we search on a short brand+series query
  and then pick the specific result whose label contains the extracted
  model token (e.g. "3200G"). No token match means no offer returned,
  rather than risk quoting the price of a different, unrelated product
- Fixed price parsing for LDLC/Grosbill, which render prices as nested
  price/sup elements for whole euros vs cents (and LDLC additionally
  wraps that in a duplicate outer .price div) - naive text concatenation
  was misreading "64,95" as "6495"
- New GET /scraped-parts/compare endpoint (authenticated), called on
  demand per search result rather than for every row, since it makes
  several outbound requests and can take a few seconds
- NewPartSearch.vue: "Compare prices" button per result shows the
  cheapest merchant with its favicon and links to the actual offer;
  importing a part now prefers the live cheapest price/link over
  pc-builder.io's potentially stale catalog price (confirmed stale in
  practice: pc-builder.io listed a Ryzen 3 3200G at 128 euros while
  Amazon sells it for 66.89 euros)
- 3 new functional tests for the compare endpoint (auth required, 404 on
  unknown part, 422 on invalid id)
2026-07-23 14:58:33 +02:00

117 lines
2.9 KiB
TypeScript

import { defineConfig } from '@adonisjs/shield'
import app from '@adonisjs/core/services/app'
/**
* In development, Vite's dev server assets (scripts, styles, /@vite/client)
* are proxied through AdonisJS on the same origin, so 'self' already covers
* them — there is no working "@viteDevUrl"/"@viteHmrUrl" CSP keyword in this
* version of @adonisjs/shield (only "@viteUrl" exists, and it resolves to an
* empty string here since assetsUrl is a relative path, not an absolute
* dev-server URL). The one thing that truly lives on a different origin is
* the Vite HMR websocket, which runs on its own port (see VITE_HMR_PORT,
* default 24678) and therefore needs an explicit connect-src entry.
*/
const viteHmrPort = process.env.VITE_HMR_PORT ?? '24678'
const devConnectSrc = app.inDev ? [`ws://localhost:${viteHmrPort}`] : []
const shieldConfig = defineConfig({
/**
* Configure CSP policies for your app. Refer documentation
* to learn more.
*/
csp: {
/**
* Enable the Content-Security-Policy header.
*/
enabled: true,
/**
* Per-resource CSP directives.
*/
directives: {
defaultSrc: [`'self'`],
connectSrc: [`'self'`, ...devConnectSrc],
scriptSrc: [`'self'`],
styleSrc: [`'self'`, `'unsafe-inline'`, 'https://fonts.googleapis.com'],
imgSrc: [`'self'`, 'data:', 'https://fr.pc-builder.io', 'https://www.google.com'],
fontSrc: [`'self'`, 'data:', 'https://fonts.gstatic.com'],
},
/**
* Report violations without blocking resources.
*/
reportOnly: false,
},
/**
* Configure CSRF protection options. Refer documentation
* to learn more.
*/
csrf: {
/**
* Enable CSRF token verification for state-changing requests.
*/
enabled: true,
/**
* Route patterns to exclude from CSRF checks.
* Useful for external webhooks or API endpoints.
*/
exceptRoutes: [],
/**
* Expose an encrypted XSRF-TOKEN cookie for frontend HTTP clients.
*/
enableXsrfCookie: true,
/**
* HTTP methods protected by CSRF validation.
*/
methods: ['POST', 'PUT', 'PATCH', 'DELETE'],
},
/**
* Control how your website should be embedded inside
* iframes.
*/
xFrame: {
/**
* Enable the X-Frame-Options header.
*/
enabled: true,
/**
* Block all framing attempts. Default value is DENY.
*/
action: 'DENY',
},
/**
* Force browser to always use HTTPS.
*/
hsts: {
/**
* Enable the Strict-Transport-Security header.
*/
enabled: true,
/**
* HSTS policy duration remembered by browsers.
*/
maxAge: '180 days',
},
/**
* Disable browsers from sniffing content types and rely only
* on the response content-type header.
*/
contentTypeSniffing: {
/**
* Enable X-Content-Type-Options: nosniff.
*/
enabled: true,
},
})
export default shieldConfig