Brings in the full backend CRM stack on top of the consolidated
migration (60abe8c).
Services (CRM)
- quoteService — full lifecycle (draft → sent → accepted → converted
to event/invoice), Skonto + Storno + reissue paths
- invoiceService — spawnInstallmentInvoices, updateInstallmentPlan,
monthly-billing accumulator, payment-check tokens, dunning ladder
- contractService — block-composable contract editor, in-browser
signature flow, wet-PDF upload path, integrity check, audit trail
- customerHoursService — per-entry locking, billing integration
- dealsService — cross-document lineage (deal_uuid)
- taxReportService — quarterly aggregates + CSV/PDF export
- eventReminderService — pre-event customer reminder cron pass
- _renderContext — shared issuer/recipient blocks across PDF types
- pdfService extensions — custom-font registration, font picker
Routes (admin + public)
- adminQuotes, adminInvoices, adminContracts, adminCalendar,
adminDeals, adminTaxReport, adminDev, adminBusinessProfile
- publicQuotes (accept/decline), publicContracts (sign),
publicPaymentCheck
- Extensions on adminEvents, adminCustomers, adminSettings,
adminEmail, adminFeatureFlags, adminThumbnails, adminPhotos,
adminCategories, adminUsers, adminArchives, adminDashboard
- server.js wires the new mounts (kept upstream's noStoreCache on
customer routes per 3-way merge)
Utilities
- schemaCache (cached hasColumn lookups across services)
- documentSequences (atomic gap-free numbering — §14 UStG)
- safePath (path-containment guards at fs stream boundaries)
- clientIp (sanctioned XFF reader for audit logs)
- publicTokenGuards (pre-multer token validation + attempt counters)
- numericHelpers (ensureInt / ensureNumber consolidation)
- dateFormatter (formatShortDate + dateInputLang)
- dbCompat extensions, iban + pdfFilename helpers, resolveLogoFile
Infrastructure
- Bundled PDF fonts (Comic-Neue / IBM-Plex-Sans / Inter / Jost /
Montserrat / Noto-Sans / Playfair-Display / Poppins)
- Backend package.json + lock updates (pdfkit, signature_pad,
qrcode, et al.)
- Sample storage layout under storage/business-docs/quote/
Tests
- 14 new test files covering quote/invoice/contract lifecycle,
installment plan reshape, line-item hierarchy, customer hours,
payment check, tax report PDF, IBAN parsing, filename sanitiser
36 lines
1.5 KiB
JavaScript
36 lines
1.5 KiB
JavaScript
/**
|
|
* clientIp — resolve the originating client IP for audit-trail
|
|
* recording (contract signing, quote responses, payment-check
|
|
* actions, etc.).
|
|
*
|
|
* **Why this helper exists:** the public-facing routes used to read
|
|
* `req.headers['x-forwarded-for']` directly and take the first
|
|
* comma-segment as the source IP. That bypasses Express's `trust
|
|
* proxy` safety net entirely — any direct (non-proxied) POST to the
|
|
* signing endpoint can spoof the audit IP by setting the header,
|
|
* which defeats the legal-evidence promise of the contract feature.
|
|
*
|
|
* **Correct path:** trust ONLY `req.ip`, and rely on
|
|
* `app.set('trust proxy', ...)` in `server.js` to populate it
|
|
* correctly. Express's trust-proxy machinery is the only thing that
|
|
* knows which upstream hops are trustworthy. The default in
|
|
* `server.js` (`'loopback, linklocal, uniquelocal'`) is correct for
|
|
* picpeak's standard deployment (nginx in front, Docker network);
|
|
* operators with unusual topologies override via `TRUST_PROXY` env.
|
|
*
|
|
* **Returns:** the resolved IPv4/IPv6 string, or `null` when Express
|
|
* couldn't determine one (very rare — happens with abusive raw
|
|
* sockets / malformed connections).
|
|
*
|
|
* **Storage:** call sites still gate persistence on a separate
|
|
* privacy setting (e.g. `crm_contracts_store_ip`). This helper only
|
|
* concerns itself with *which* IP to record, not *whether* to
|
|
* record one.
|
|
*/
|
|
function clientIpForAudit(req) {
|
|
if (!req) return null;
|
|
return req.ip || null;
|
|
}
|
|
|
|
module.exports = { clientIpForAudit };
|