Commit Graph

11 Commits

Author SHA1 Message Date
Cauê Faleiros
eae3dad306 feat: offer credit card, debit card and PIX, with the bank's 3-D Secure step
All checks were successful
Build and deploy / Validate source (push) Successful in 5s
Build and deploy / Integration suite on a real stack (push) Successful in 2m3s
Build and deploy / Secret scan and release gate (push) Successful in 5s
Build and deploy / Publish images (push) Successful in 1m31s
The payment page lists credit card (preselected), debit card and PIX. Each
card option limits Mercado Pago's form to its kind; debit is paid at once.
Card payments ask for 3-D Secure when the issuer requires it, and a
challenge opens the bank's page in a frame, which needs
PAYMENT_CHALLENGE_SOURCES=https: (frames and form posts only). A card left
waiting for that confirmation stops blocking a new attempt after ten
minutes, and a refusal reported by the notification returns the customer to
the payment choice. Written from the documentation; not yet run with a real
debit card.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 13:24:06 -03:00
Cauê Faleiros
7b6675d4d4 feat: two-column payment page with card by default and PIX on its own page
All checks were successful
Build and deploy / Validate source (push) Successful in 7s
Build and deploy / Integration suite on a real stack (push) Successful in 2m19s
Build and deploy / Secret scan and release gate (push) Successful in 5s
Build and deploy / Publish images (push) Successful in 1m38s
The payment page puts paying on the left and the order summary on the
right (above it on phones). Card is preselected and paid on the page with
Mercado Pago's form, in the Site's colours and with the order's e-mail;
choosing PIX shows a Pagar button that opens /pagamento/pix with the QR code
and copy-and-paste code, waiting there for the confirmation. A confirmed
payment shows "Pagamento confirmado" with the order number and a button to
the customer's orders. The payment buttons no longer restyle every button
inside the form.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 13:10:05 -03:00
Cauê Faleiros
43043c361c feat: give payment its own page
All checks were successful
Build and deploy / Validate source (push) Successful in 6s
Build and deploy / Integration suite on a real stack (push) Successful in 2m12s
Build and deploy / Secret scan and release gate (push) Successful in 6s
Build and deploy / Publish images (push) Successful in 1m35s
The PIX and card choices appeared under the cart, on the same page as the
customer's details. "Ir para o pagamento" now sends the order and opens
/pagamento, step 3 of the progress bar: the server's order summary, then PIX
or card, each opening below. The cart keeps only the sending progress and its
errors; a changed cart is sent again instead of offering the old quote.
Portal links open the payment page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 11:45:44 -03:00
Cauê Faleiros
b81ff8d03d feat: give each Site product and the cart its own page
All checks were successful
Build and deploy / Validate source (push) Successful in 5s
Build and deploy / Integration suite on a real stack (push) Successful in 2m28s
Build and deploy / Secret scan and release gate (push) Successful in 8s
Build and deploy / Publish images (push) Successful in 1m52s
The home, each product's Montagem and the cart now have their own
addresses (/artes-avulsas, /arquivo-por-metro, /uv-artes-avulsas,
/uv-arquivo-por-metro, /carrinho) and show only their own content, with
Back, Forward, reload and direct links working as in any store. They stay
one document so uploaded artworks survive moving between pages; nginx
serves index.html for these addresses.

"Adicionar ao carrinho" puts the item in the cart and opens it, and an
empty cart says so. Portal quote links open in the cart. Also fixes the
"57 cm" line break on the ready-sheet option, returns "Novo pedido" to
the home, and says PDF depends on the product.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 16:56:58 -03:00
Cauê Faleiros
4c01e932c3 feat: place PDF artwork in print files, add card payment, count only failed logins
All checks were successful
Build and deploy / Validate source (push) Successful in 6s
Build and deploy / Integration suite on a real stack (push) Successful in 2m23s
Build and deploy / Secret scan and release gate (push) Successful in 5s
Build and deploy / Publish images and notify Portainer (push) Has been skipped
PDF artwork: a single-page PDF source is placed in the print file as a
vector form through pikepdf, never rasterised, using the CropBox and
inherited /Rotate the Site measured with pdf.js. Multi-page and protected
PDFs go to hand preparation. PyMuPDF was not used because of its AGPL
licence. Raster tests cover crop, page rotation, placement rotation and
mirroring, and fail when the rotation or crop handling is broken.

Card payment: Mercado Pago's Card Payment Brick on the Site when
MP_PUBLIC_KEY is set; the card becomes a one-time token in Mercado Pago's
secure fields. Each card attempt has its own idempotency key, and the intent
route refuses new attempts once a payment is approved or a card is in
review, so a quote cannot be charged twice. The Site CSP admits Mercado
Pago's origins only through PAYMENT_CSP_SOURCES, empty by default.

Logins: every attempt counts against the source address, only failures
against the account. Counting successful sign-ins let ordinary use lock an
operator out and made CI's final browser sign-in fail.

No new required settings; production behaviour is unchanged until the
provider credentials are configured. Verified with the full CI integration
sequence locally.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 13:14:56 -03:00
Cauê Faleiros
da903db32a feat: serve pdf.js from this origin instead of a CDN
All checks were successful
Build and deploy / Validate source (push) Successful in 5s
Build and deploy / Integration suite on a real stack (push) Successful in 1m10s
Build and deploy / Secret scan and release gate (push) Successful in 5s
Build and deploy / Publish images and notify Portainer (push) Successful in 1m43s
The Site pulled pdf.js 3.11.174 from cdnjs with no integrity attribute, and the
policy trusted the whole of cdnjs.cloudflare.com for both script-src and
worker-src. Anything that host served would have executed, and a customer
measuring a PDF sheet depended on it being reachable.

Vendor both files instead of pinning a hash: it removes the dependency rather
than constraining it, and lets the policy name only 'self'. Provenance and
SHA-256 digests are recorded in local/static/vendor/README.md, verified on
download against the SRI digests cdnjs publishes for that release.

cdnjs is now absent from script-src, worker-src and connect-src in both gateway
templates. Workers are 'self' plus blob:, which the Site needs for the worker it
constructs itself.

Verified in a browser against the running stack: pdf.js loads from /vendor/, the
blob worker starts, and a real seven-page PDF parses with no CSP violation. Both
browser suites and the full integration suite pass.

The version is deliberately unchanged. 3.11.174 is old, but its known eval path
is already closed by isEvalSupported:false, and upgrading is an API change that
needs its own testing rather than riding along with this.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 13:56:02 -03:00
Cauê Faleiros
3b92813491 fix: recover the customer address behind the host reverse proxy
Some checks failed
Build and deploy / Validate source (push) Successful in 7s
Build and deploy / Integration suite on a real stack (push) Failing after 51s
Build and deploy / Secret scan and release gate (push) Successful in 9s
Build and deploy / Publish images and notify Portainer (push) Has been skipped
The production gateway does not face the internet: nginx-proxy-manager owns
80/443 on the host and proxies to it. So $remote_addr inside the gateway is that
proxy, and overwriting X-Forwarded-For with it discarded the customer address
the proxy had already recorded. Every request would have been attributed to one
internal address, which is exactly the fault 2.1 set out to fix, reintroduced in
production only.

Use real_ip to take the customer address from the proxy's header, trusting only
private networks. A request that reaches the published port directly from the
internet is not trusted, so its header is ignored and $remote_addr stays the
real peer: the anti-spoofing property is kept.

Also downgrade 2.9. TLS is not missing, it is terminated by that proxy. The gap
is that the repository never says so, which would break every session cookie if
the stack moved to a host without one.

Validated with nginx -t against the rendered production configuration.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 13:07:56 -03:00
Cauê Faleiros
52ae13c9a1 fix: rate-limit and audit by real client address
uvicorn does not trust forwarded headers from a peer outside
forwarded_allow_ips, so request.client.host was the web gateway for every
request. The auth-source bucket therefore counted all customers together:
60 failed logins from one attacker locked out everyone. Security events
recorded the gateway address, which made the audit trail useless for
attribution.

The gateway now overwrites X-Forwarded-For with the peer address it observed
instead of appending to whatever the client sent, so the header carries one
value the client cannot choose, and client_ip() resolves it with a fallback to
the connection peer.

The guest-session limiter was keyed on the environment name, making it one
global bucket of 120 per 15 minutes: roughly eight new visitors a minute for
the whole site before legitimate traffic started receiving 429. It is now per
source, and the ceiling is deliberately generous because offices and mobile
carriers put many real customers behind a single address.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 18:48:31 -03:00
Cauê Faleiros
88a2a6f060 fix: prevent stale Kanban script after deployment
All checks were successful
Build and deploy / Validate source (push) Successful in 7s
Build and deploy / Publish images and notify Portainer (push) Successful in 53s
2026-09-18 14:27:33 -03:00
Cauê Faleiros
fbb620bd85 fix: keep web services up during API rollout 2026-09-18 12:17:34 -03:00
Cauê Faleiros
98c951d374 first commit
Some checks failed
Validate, publish and deploy / validate (push) Successful in 2m2s
Validate, publish and deploy / publish-and-deploy (push) Failing after 8s
2026-09-15 16:42:34 -03:00