portal_css — Portal Client Side Server
Orientation
This doc lets a future engineer/agent understand and run
portal_css. Base branch documented: devel (tip019f1705), 422 tracked files (recursive,git ls-tree -r --name-only devel). Alldevel-specific claims below were confirmed viagit show devel:<path>; see Sources. Sibling docs: architecture-overview, vuer_oss, customization-architecture, customization-branch-catalog.
The working tree was checked out on
customization/instacash(HEADb0a4a37a), NOTdevel.b0a4a37ais the tip ofcustomization/instacash(confirmed:git branch --contains b0a4a37a→ onlycustomization/instacash);devel's tip is019f1705. Everything in this doc was verified against thedevelbranch viagit show devel:<path>. The checked-out customization branch layers in the instacash / MBH integration and carries 684 tracked files (git ls-tree -r --name-only HEAD) — the 262 extra files (e.g. the fullcustomization/instacash overlay) are not on devel and are flagged inline.
1. Purpose & role in FaceKom
portal_css is the Portal Client Side Server (package.json:2-4 — "name": "portal_css", "description": "Portal Client Side Server"). It is a customer-facing Node.js web app that renders the FaceKom Portal front end (login, registration, password flows, personal-data, session timeout) and proxies all customer/auth/business operations to other FaceKom services over RabbitMQ RPC. It is a sibling/cousin of the operator-facing CSS; readme.md:5 states it “Keeped the core functionality from CSS”.
Verified feature set (readme.md:4-18):
- Customer registration; login with credentials (configurable via OSS PortalData attributes); login with JWT token (authenticated by OSS).
- Customer data cached in Redis with configurable TTL.
- Direct redirect to CSS (authenticated by CSS); auto-expiring session with a front-end timer.
- Personal-data change, password change, password recovery, strong customer authentication, access to Open Hours settings, logout.
"CSS" here means Client Side Server, not stylesheets. The repo name
portal_css= "portal client side server".
It is heavily customization-driven: the in-tree customization/ overlay on the checked-out worktree branch customization/instacash (HEAD b0a4a37a) is the instacash / MBH integration (customization/branding-options.json:5-8 references mbh_logo*). On devel itself the customization/ tree carries the neutral defaults (git show devel:customization/branding-options.json references facekom/vur logos, not mbh_logo*). Per-tenant variants live on dedicated customization/* and update/customization/* branches (see branch list in customization-branch-catalog).
2. Tech stack & runtime
- Language/runtime: Node.js, CommonJS. Engine requirement
node >=16.14.2(package.json:6-8). CI (Travis) runs Node 20 (.travis.yml:6-7); changelog notes a “Travis Node 20 update” (FKITDEV-3703,changelog.md:16). - Package manager: Yarn with an offline mirror at
/workspace/npm-packages-offline-cacheand--install.ignore-optional true(.yarnrc)..npmrcsetsscripts-prepend-node-path=true. Ayarn.lock(~347 KB) is committed; npm scripts are also used (npm run build,npm test). - Web framework: Express 4 (
package.json:62) + express-session with connect-redis store (server/web/WebServer.js:1-7,259-290). - Realtime: socket.io v4 server + client (
package.json:96-97;server/socket/socket-server.js:1-15). - Templating: Twig server-side (
twigdep;engines/twig/render-server.js). (Dustjs deps are present —dustjs-helpers,dustjs-linkedin— and abrowserifyDusttransform is referenced by the client bundler atbin/script/script.compiler.js:4; note recent commits “Remove Dust” / “replace mmagic with wasmagic”,git log.) - Client build: Browserify + Babel (
@babel/preset-env,@babel/preset-react) for JS, Stylus + clean-css + autoprefixer/poststylus for CSS (bin/script/script.compiler.js,bin/style/style.compiler.js). - Front-end libs: React 18 (loaded as a browserify external, see §3/§6), Bootstrap 5.3.3 + jQuery 3.7.1 (
web/libs/...). - Messaging client:
@techteamer/mq(alpha) —ConnectionPool,RPCClient,RPCServer,QueueClient(package.json:39;server/bootstrap/connection/rabbitmq.js:5). - Cache/session store: Redis via
redisv4,async-redis,connect-redisv7 (package.json:42,55,90). - Config:
getconfigv4 +dot-object+ optional Spring Cloud Config client (config.js). - Logging:
log4jswith console/file/syslog/papertrail appenders (server/logger.js). - Security middleware:
helmet,csurf,cors,nocache, custom CSP + IP filter (server/web/WebServer.js,server/web/routes.js,server/service/IpFilterService.js). - Image processing deps:
heic-convert,png-to-jpeg,wasmagic(server/util/magic.js;package.json:48,86,105). - Error reporting:
@sentry/browser(client) + a Raven-JS endpoint (server/web/api/sentry.js,web/libs/raven-js/).
3. Build & run
Entry points
- Main / start:
server.js(package.json:5"main": "server.js"; start scriptnode server.js). server.jsboot sequence (server.js:190-200):config.loaded→rabbitmqbootstrap →setupQueue→setupServices→setupCustomizations→StartWebServer→StartSocketServer.- Two listeners, both bound to
127.0.0.1behind nginx: Web server on port10383(server.js:155-156) and Socket.IO server on port10382(server.js:173-175). nginx fronts them (nginx_portal_css_docker.conf:1-7).
package.json scripts (verbatim — package.json:9-22)
| Script | Command | What it does |
|---|---|---|
build | node bin/build/build.js | Runs the full client build: externals + scripts + styles in parallel (bin/build/build.js:3-7), then exits unless --watch. |
jest | jest --config ./test/jest.config.js --runInBand | Runs the Jest unit suite serially. Note: no test files committed yet (only test/tests/.gitkeep). |
test | npm run lint --silent && echo 'npm test: OK' | The test task is lint-only (then prints OK). Used by CI. |
lint | eslint . ./bin/**/* ./bin/* && echo 'npm run lint: OK' | ESLint over the repo + bin/. |
lint:fix | eslint . ./bin/**/* ./bin/* --fix | ESLint autofix. |
start | node server.js | Starts the portal server (web + socket + queue). |
style | node bin/style/style.task | Compile Stylus → CSS only (bin/style/style.task.js). |
script | node bin/script/script.task | Browserify+Babel bundle the page/layout scripts only (bin/script/script.task.js). |
script:external | node bin/script/external.task | Bundle the React externals only (bin/script/external.task.js). |
script:all | node bin/script/external.task && node bin/script/script.task | Externals then page scripts. |
watch | node bin/watch/watch | Build externals+scripts+styles in --watch mode with livereload (bin/watch/watch.js). |
credits | node bin/credits > CREDITS.html | Generate an HTML license report via license-checker (bin/credits). |
Dockerfile(s)
No Dockerfile is tracked in this repo (verified:
find -iname 'Dockerfile*'excluding node_modules returns nothing;git ls-treeshows none). The container image is built elsewhere. What is in-repo: supervisor + nginx configs that the image mounts under/workspace/portal_css.
supervisor_portal_css_docker.confruns three programs as usertechteamer: nginx (daemon off), redis (/usr/bin/redis-server /etc/redis/redis.conf), and portal_css (directory=/workspace/portal_css,environment=NODE_ENV="docker",command=node server.js,autorestart=true,exitcodes=0,2,stopsignal=TERM).supervisor_portal_css_dev.confis the dev variant (present, not opened line-by-line).nginx_portal_css_docker.conf: nginx listens:80, root/workspace/portal_css/web, proxies/socket.io→127.0.0.1:10382(upstreamsocketio-css) and everything else →127.0.0.1:10383(upstreamportal-css) viatry_files $uri @nodejs; long-cache static dirs (/js /css /img /font /libs /icon /audio /entry,expires 1y);client_max_body_size 25M;X-Content-Type-Options nosniff; rewrites upstream500→400serving emptyerror.html, and502→/502.html(loader).nginx_portal_css_dev.confis the dev variant.
Local run shape (inferred from configs — see gaps)
The dev/docker model expects: Redis reachable via unix socket /var/run/redis/redis-server.sock (config/dev.json:36-39, config/docker.json:32-35), a RabbitMQ over TLS (amqps://) with client certs under /workspace/vuer_mq_cert/... (config/*.json queue), and host names derived from DEV_DOMAIN//etc/hostname when NODE_ENV=dev|travisci (config.js:42-60). NODE_ENV selects the getconfig file (dev → config/dev.json, docker → config/docker.json).
4. Top-level structure (meaningful dirs)
| Path | Role |
|---|---|
server.js | Process entry: signal handlers, boot pipeline, web+socket listeners (server.js). |
config.js | getconfig wrapper: dev host derivation, ms→seconds coercion, secure-by-default CORS/socket origins, Spring Cloud Config loader, config.get/has/getSecureConfig (config.js). |
config/ | Env config JSON: dev.json, docker.json (getconfig profiles). |
server/ | Back-end: web server, routes, API handlers, services, queue RPC/queue clients & server, socket server, logger. (See §6.) |
server/web/ | Express WebServer.js, routes.js, routes/*.endpoint.js (page renderers), api/* (form/AJAX handlers), middleware/*, helper/*, Template.js. |
server/queue/ | RabbitMQ surface: rpc_client/*, rpc_server/PortalRPCServer.js, queue_client/Customer.js. |
server/service/ | Stateful services: Portal, Branding, Compatibility, Socket, IpFilter, CustomerData. |
server/socket/ | socket.io server (socket-server.js), client.js, events/*, models/UserData.js. |
server/bootstrap/connection/ | rabbitmq.js — builds the @techteamer/mq ConnectionPool, exits process on connection close. |
server/logger* | log4js setup + custom syslog/papertrail appenders. |
client/ | Front-end source compiled into web/. Subtrees: engine/ (MVC-ish view/service/radio framework), ui/ (pages, layouts, elements, regions, styles, libs/bootstrap), react/ + externals/react/ (React loader/hooks), features/, resources/ (breakpoints, colors, device-sizes, translations), utils/. |
customization/ | Tenant overlay (instacash/MBH on devel): customizations.js (hooks/overrides registry), branding-options.json, listeners/*, server/ overrides, ui/ (branding styles, custom pages/layouts/regions, translations). |
engines/ | Build & render engines: build/ (build/bundle/watch/livereload), twig/render-server.js, translator/ (Dictionary + config), util/* (twig filters, inheritance, getSrc). |
bin/ | First-party CLI build helpers (see §5). |
web/ | Build output + static assets served by nginx: compiled js/,css/,branding/ (generated), plus committed font/,icon/,img/,libs/,entry/, error.html, 502.html, robots.txt. |
test/ | Jest config, custom test sequencer, travis.sh, testconfigs/local.json ({}), coverage dir. |
maintenance/, security/ | Monthly markdown logs (2023-09 … 2026-04) — operational/audit records, not code. |
logs/ | Runtime log dir (logs/.gitkeep); log4js writes logs/server.log (server/logger.js:33). |
.github/ | CODEOWNERS, workflows pr-title-lint.yaml, release-caller.yaml. |
.husky/ | git hooks: commit-msg, pre-commit, prepare-commit-msg (commitlint present, commitlint.config.js). |
.agents/ | Agent-tooling scaffolding dirs (ao/, knowledge/, plans/, …); all empty except .gitignore. Not part of the app. |
5. First-party bin/ scripts (each READ)
All paths under
bin/. These are the build pipeline; none are runtime server code.
bin/build/build.js— Orchestrator fornpm run build. Pushes--no-exitso sub-builds don’t exit early, thenPromise.allofexternal.task+script.task+style.task; logsdone Buildandprocess.exit()unless--watch(bin/build/build.js:1-12).bin/credits(shebang#!/usr/bin/env node) — Runslicense-checkeroverprocess.cwd()(production:false) and prints an HTML<table>of name/license/repo/publisher. Used bynpm run credits→CREDITS.html(bin/credits:1-24).bin/script/external.task.js— Defines the React externals bundle config (inputclient/externals/react/react.*.ext.jsandreact-dom.*.ext.js, outputweb/js/externals) and callsengines/build/build('externals', …). react-dom is bundled with react markedexternalto avoid a 2nd React instance. Adds watch globs + livereload when--watch(bin/script/external.task.js).bin/script/external.compiler.js— FactorycreateCompiler({externalModules, exposeName})returning a browserify compiler that.external(...).require(path,{expose})to produce an externally-requirable bundle; honorsbuild.generateSourceMaps(bin/script/external.compiler.js).bin/script/script.task.js— Page/layout JS bundle config: globsclient/ui/pages/**/*.script.js,customization/ui/pages/**/*.script.js,customization/ui/layouts/**/*.layout.js,client/ui/layouts/**/*.layout.js→web/js; indev|traviscialso bundlesclient/ui/test/*.js; watch globs include.js/.jsx/.dust. Callsbuild('scripts', …)(bin/script/script.task.js).bin/script/script.compiler.js— The per-file browserify compiler:.external(['react','react-dom']),transform(browserifyDust),babelifywith@babel/preset-env+@babel/preset-react(+ optionalbabel-plugin-istanbulwhenbuild.istanbul),plugin(browserify-require-not-found-parent); returns{output, stats.size}(bin/script/script.compiler.js).bin/style/style.task.js— Stylus bundle config: page styles, layout styles,customization/ui/branding/**/*.branding.styl→web/branding, custom pages/layouts →web/css; watch includescustomization/branding-options.json. Callsbuild('styles', …)(bin/style/style.task.js).bin/style/style.compiler.js—compileStylus: importsclient/ui/styles/global.styl,defines breakpoints/device-sizes/colors and branding resources+values into Stylus, runs autoprefixer via@techteamer/poststylus, and minifies with clean-css whenbuild.minify; inline sourcemaps when enabled (bin/style/style.compiler.js).bin/watch/watch.js— Pushes--watchto argv then requiresexternal.task,script.task,style.task(4 lines). Backsnpm run watch(bin/watch/watch.js:1-4).
Shared build engine they all call: engines/build/build.js (build(name, {watch, bundle, liveReload}) — runs engines/build/bundle.js per config, optional chokidar watch + livereload; instantiates a BrandingService to feed branding into compilers) and engines/build/bundle.js (glob→read→compile→write outputs/sourcemaps under outputRoot).
6. Key modules/services/entities (READ)
Boot & DI
server/service_container.js— Exports a singleton{ emitter: new ServiceBus() }.ServiceBus extends WildEmitterand adds the hook/override system used everywhere:addHook/removeHook/callHooks/callOnlyHook(parallel/first-only handlers) andregisterOverride/callOverride(replace a default block). This is the core of the customization mechanism (server/service_container.js).server/bootstrap/connection/rabbitmq.js— BuildsConnectionPool({defaultConnectionName:'default'}),setupQueueManagers(config.queue), and registers aclosehandler thatprocess.exit(2)if any RabbitMQ connection drops. PopulatesserviceContainer.queue/rpcServer/rpcClient/queueClient/...(rabbitmq.js).config.js— getconfig loader with a custom JSON-syntax-error reporter (prints offending file slice,exit(2)); dev host derivation; ms-string→seconds coercion for session/sms timeouts; secure CORS/socket-origin defaults; optional Spring Cloud Config;config.get(path,default),config.has(path),config.getSecureConfig()(redactssecurity.sensitiveConfigFields).
Web layer
server/web/WebServer.js— Builds the Express app. Notable: disablesx-powered-by; host-header allowlist (onlyconfig.hosts.*accepted, else400); per-requestnonce(uuid); richreq.browseruser-agent classification incl.isCompatible/isIncompatibleviaCompatibilityService; helmet with config-driven options; a per-request CSP builder (nonce script-src,wss://hosts.portalconnect-src, route-specificsandboxflags for/loan/application,/lobby,/videochat,/documents, downloads; livereload:11083allowances;instacash.cspDirectivesmerge); body-parser (25 MB JSON + csp-report), cookie-parser,localenegotiation. Chainable setup methods:setupLocale → setupRedis → setupSession → setupTwig → setupRoutes → setupCSPReportViolation → setup405Handler → setup404Handler → setupErrorHandler(matchesserver.js:144-153). Redis session store viaconnect-redis; iOS-12 SameSite workaround; 404 → 303 redirect to/404or/for HTML, else404(server/web/WebServer.js).server/web/routes.js— Central route table. MountscustomerDataCache, firesmiddlewareshook, then wires routes gated by config flags and each wrapped incallOverride('routes:<name>', …)so customizations can replace them:/(landing, opt.customerAuthWithToken),/videochat,/404,/login+/api/submit-login(+ ipFilter), password-change/recovery/reset endpoints + APIs,/logout(destroys session), strong-auth/api/submit-token&/api/submit-resend-token(whenuseStrongAuthentication),/registration(whenfeatures.registration),/personal-data(whenfeatures.personalDataChange),/ui-kit(whenbuild.uikit),/api/sentry; CORS applied to/api; finally fires therouteshook (server/web/routes.js).server/web/middleware/requireSession.js— Redirects to/login(or403for/api/*) if nosession.customerId; stashesredirectTo(requireSession.js).server/web/middleware/customerAuthWithToken.js— On/with a?token, callsrpcClient.jwtAuth.auth(token); on valid setssession.customerId+ redirects/(JWT login from OSS) (customerAuthWithToken.js).server/web/api/submit-login.js— Credential login flow:rpcClient.portal.authLoginCredentials, then if strong-auth required sends SMS token (respectingsmsTokenResendIntervalandPortalService.smsTokenLimitExceeded), verifies token viarpcClient.portal.verifyToken, sets session, IP-filter release on success, firescustomer:loginhook (submit-login.js). (Representative of theserver/web/api/*handlers — see gaps.)server/web/Template.js,engines/twig/render-server.js— Twig rendering;render-serverregisters filterstimestamp,filesize,documentTypeand functionnameOrder(render-server.js).
Services (server/service/)
PortalService.js— Strong-auth token orchestration over RPC:sendSmsToken/sendEmailToken(generate viarpcClient.portal.generateToken, deliver viasendSmsTemplate/sendEmailTemplate),sendPasswordRecoveryEmail(find customer, reject external customers viacustomizations.rpcClient.instaCash.isExternal, generate redirect token, email recovery URL), and Redis-backed per-customer token-count rate limiting (smsTokenLimitExceeded) (PortalService.js).CustomerDataService.js— Redis cache of customer data keyedcustomer-data:<id>with sliding TTL; cache miss →rpcClient.getCustomer.get(id);invalidate(id)deletes key (called byPortalRPCServerand on data updates) (CustomerDataService.js).BrandingService.js— Loadscustomization/branding-options.jsondefaults, overrides resource paths/values fromconfig.branding, verifies branded files exist on disk (fallback otherwise). Used both at runtime (server.js:131) and by the style compiler/build engine (BrandingService.js).IpFilterService.js— In-memory per-IP(+tag) throttling:createIpFilter/createIpMonitorAndFilter/monitorIp/shouldThrottle/releaseIp; driven bysecurity.ipFilter(suppressAfterAttempts,throttlePeriodMs,coolDownPeriodMs); blocked responses return HTTP 200 bodiesERROR_FKIPFT01/02. Self-pruningsetInterval(IpFilterService.js).CompatibilityService.js,SocketService.js— browser-rule checks (used by WebServerreq.browser) and socket-side service (present; not opened line-by-line — see gaps).
Realtime
server/socket/socket-server.js—start(httpServer)→io(httpServer, config.web.socket);setupEventsloads modulesclient,events/auth,events/pagevisit, firessockethook, and invokes each module per connection (socket-server.js). Socket tuning inconfig.*.jsonweb.socket(maxHttpBufferSize 25MB,pingTimeout 30000,allowEIO3 true).
Customization overlay
customization/customizations.js— Registers, via the ServiceBus, the instacash integration: extra RPC clients (instacash-rpc→ InstaCash,rpc-callback-request→ RequestCallback), services (FakeData, InstaCash, PostalCode), acallbackRequestmiddleware, a large block of loan/application/document/account routes & APIs under/loan*,/documents,/applications,/api/loan/*,/api/request-callback,/api/submit-document, downloads, etc., plus overrides forroutes:landingandroutes:videochat, and core listeners (customer-registration,customer-password-policy,customer-login,customer-data-change,config-socketio-settings).module.exports = () => Promiseis thesetupCustomizationsinvoked inserver.js:138(customizations.js).
7. Configuration
- Mechanism:
getconfigselects a profile JSON byNODE_ENV(dev→config/dev.json,docker→config/docker.json;traviscioverrides queue toamqp://localhost:5672,config.js:62-65). No AJV/JSON-schema validation found; validation is ad-hoc (config.jssyntax reporter +config.get/has). - Env vars (verified):
NODE_ENV(profile + dev host logic,config.js:42-65; supervisor setsdocker),DEV_DOMAIN(config.js:43),TZis forced toEtc/UTC(server.js:56),NODE_TLS_REJECT_UNAUTHORIZED='0'set whensettings.allowSelfSignedCerts(server.js:58-60). Travis setsNODE_ENV=dev,SLACK_ROOM,PATH(.travis.yml:12-22). - Key config domains (from
config/dev.json/config/docker.json):features.{login,registration,personalDataChange,sessionTimeout}— route gating.settings.*—sessionTimeout,sessionTimeoutModalLimit,customerDataCacheTimeout,passwordResetExpiryPeriod,useStrongAuthentication,smsToken*limits/intervals. ms-strings coerced to seconds inconfig.js:108-123.redis.socket.path=/var/run/redis/redis-server.sock.web.session(cookie namepsid,maxAge 3600000,rolling),web.socketioSettings,web.socket,web.trustedProxy(loopback, linklocal, uniquelocal),web.cors.origin(*→ narrowed tohosts.portalbyconfig.js:126-128).security.ipFilter,security.sensitiveConfigFields(redaction list — session secret, queue TLS cert/key/ca,jwt.secret,jwt.keystore.*.k), and (consumed by WebServer) optionalsecurity.helmet.*,security.csrf.cookie,security.cookie.sameSite.hosts.{portal,css,esign}(auto-filled in dev,config.js:50-59).queue.{url,options(cert/key/ca),rpcTimeoutMs,rpcQueueMaxSize}— RabbitMQ overamqps://with client certs.jwt.{secret,expiryPeriod},documentUpload.mimeTypes,callbackRequest.*,browsers.{compatible,incompatible},locales,logging,branding,build.*(sourceMaps/minify/liveReload/uikit/react.production),timezone,version.instacash.*(e.g.disablePortal,presetReferrerPolicy,cspDirectives) — tenant-specific (config/dev.json:1-4,config/docker.json:1-5,WebServer.js:201).
- Spring Cloud Config: if
config.springCloudConfigServeris set,config.js:155-185loads remote config viacloud-config-clientand merges withdot-object.
Secrets are committed in the config JSON (
web.session.secret,jwt.secretare hard-coded literals inconfig/dev.json:43,182andconfig/docker.json:39,158). These are dev/default values; production is expected to override via Cloud Config / deploy-time config (security.sensitiveConfigFieldsexists precisely to redact them ingetSecureConfig). Treat repo values as non-production.
8. Tests
- Frameworks: Jest (
jest,@jest/test-sequencer,eslint-plugin-jest,istanbul-lib-instrument) and ESLint (eslint-config-standard). - Jest config:
test/jest.config.js— customtestSequencer(test/lib/jest/test.sequencer),roots/rootDir= repo root, ignores/node_modules/,/web/,/yarn-offline-cache/; coverage viav8→lcovonlyintotest/coverage/jest;testTimeout 30000;transform: {}(no transform — expects plain CJS test files). - Run:
npm run jest(jest --config ./test/jest.config.js --runInBand). The defaultnpm testis lint-only (package.json:12). - CI (
.travis.yml→test/travis.sh): Node 20 on Ubuntu focal. Steps:npm run build;yarn install --production=false --force --frozen-lockfile;yarn audit(fail only on severity ≥16 = critical);npm test;git diff --exit-code(excluding.yarnrc) to ensure build output is committed/clean; and a guard that no executable files exist outsidebin/(test/travis.sh:1-28). Branch filter regex restricts which branches run (.travis.yml:28). - Coverage tooling:
sonar-project.properties→ SonarCloud (projectKey=portal-css, orgtechteamer, lcov attest/coverage/lcov.info). - Status: No committed test specs (
test/tests/.gitkeep,customization/testexcluded);test/testconfigs/local.json={}.
9. Dependencies on other FaceKom services
All cross-service calls go through RabbitMQ (@techteamer/mq). There are no first-party HTTP calls to other services in the code read (HTTP is inbound only, fronted by nginx). DB access is Redis only (session store + caches). Verified queue surface:
RPC clients (request/reply) — registered in server.js:91-97:
| Queue name | Client | Methods → action (file) |
|---|---|---|
rpc-vuer-portal | PortalRPCClient | getLoginFields, authLoginCredentials, findCustomerId, generateRedirectToken, createCustomer, updateCustomerData, currentToken, generateToken, verifyToken, sendEmailTemplate, sendSmsTemplate, clientErrorLog (rpc_client/PortalRPCClient.js) |
rpc-customer-portal-data | PortalDataRPCClient | getRegistrationFields(lang, customerId) (PortalDataRPCClient.js) |
rpc-jwt-auth | JwtAuth | auth(token) → {jwtToken} (JwtAuth.js) |
rpc-get-customer | GetCustomer | get(id) (GetCustomer.js) |
rpc-openhours | OpenHours | get(callbackRequestDays, openingHourDays, calendarName) (OpenHours.js) |
rpc-openhours-calendars | OpenHoursCalendars | getCalendars(calendarNames, locale) (OpenHoursCalendars.js) |
background-room-export | RoomExport | exportRoom(roomId, blackoutOperator) (300 s timeout) (RoomExport.js) |
RPC server (this service answers) — server.js:80:
rpc-portal-vuer→ PortalRPCServer: handles actioninvalidateCustomerCache→CustomerDataService.invalidate(customerId)(rpc_server/PortalRPCServer.js).
Queue client (fire-and-forget) — server.js:102:
queue-customer→ Customer:setConnectionInfo(customerId, ip, locale, userAgent)(queue_client/Customer.js).
Customization (instacash) adds — customization/customizations.js:12-15:
instacash-rpc→ InstaCashRPCClient,rpc-callback-request→ RequestCallbackRPCClient.
Infra deps:
- RabbitMQ over TLS (
amqps://rabbitmq:5671docker /amqps://localhost:5671dev), client certs at/workspace/vuer_mq_cert/*. Process exits(2) on connection loss (rabbitmq.js:14-19). - Redis via unix socket
/var/run/redis/redis-server.sock— session store + customer-data/token caches (config/*.json,WebServer.js,CustomerDataService.js,PortalService.js). - The naming (
rpc-vuer-portal,rpc-portal-vuer,rpc-customer-*,rpc-get-customer,rpc-jwt-auth,rpc-openhours*) indicates the counterpart is the vuer/OSS stack — see architecture-overview and vuer_oss. (Counterpart server implementations are out of this repo — see gaps.)
10. Verified gotchas
Working tree ≠ devel. Checked out on
customization/instacash(HEADb0a4a37a), NOTdevel(tip019f1705). Usegit show devel:<path>to read the core product; the branch adds the instacash/MBHcustomization/overlay (684 files vs 422 on devel) that is not on devel. All devel facts here are confirmed againstdevel.
No Dockerfile in repo. The image is built externally; only
supervisor_*andnginx_*configs +web/static output live here. Don't expectdocker buildto work from this tree.
npm testdoes not run tests. It runs lint only (package.json:12). Unit tests arenpm run jest, and no test specs are committed yet (test/tests/.gitkeep).
Build output is committed under
web/and CI enforces it.test/travis.sh:19runsgit diff --exit-codeafternpm run build— if compiledweb/js,web/css,web/brandingdiffer from committed output, CI fails. Alwaysnpm run buildand commit the result.
Hard process exits. Any RabbitMQ connection close →
process.exit(2)(rabbitmq.js). getconfig JSON syntax error →exit(2)(config.js:39).uncaughtException/unhandledRejectionare logged but the process is supervised (supervisor … autorestart=true, exitcodes=0,2).
Servers bind to
127.0.0.1only (ports 10383 web / 10382 socket). They are unreachable without the nginx reverse proxy in front (server.js:156,175;nginx_portal_css_docker.conf).
Host-header allowlist is strict. Requests whose
Hostis not one ofconfig.hosts.{portal,css,esign}get a bare400(WebServer.js:30-37). In dev these are auto-derived fromDEV_DOMAIN//etc/hostname(config.js); misconfigured hosts → every request 400s.
Committed secrets are dev defaults.
web.session.secretandjwt.secretare literals inconfig/*.json; override in production.getSecureConfig()+security.sensitiveConfigFieldsexist to redact them in logs/exports.
Customization is hook-driven, not fork-driven (within a branch).
customization/customizations.jsinjects routes/services/middleware viaserviceContainer.emitter.addHook(...)and replaces core routes viaregisterOverride('routes:<name>'), consumed inserver/web/routes.jscallOverride(...). Per-tenant content still lives on separatecustomization/*branches.
@techteamer/mqis pinned toalpha(package.json:39) — the messaging layer tracks a pre-release tag.
Strong-auth & rate limits are time-string config.
settings.sessionTimeout,smsTokenResendInterval, etc. acceptms-style strings ("15m","15s") and are coerced to seconds at load (config.js:108-123); integers are treated as already-seconds.
Unverified / gaps
- Surveyed structurally (listed/sampled), not read line-by-line:
client/(the entire front-end source tree —engine/,ui/pages|layouts|elements|regions|styles|libs,react/,resources/,utils/). I enumerated every file viagit ls-treeand read the build configs that consume them, but did not open each.script.js/.styl/.twig/.trans.js.- Most
server/web/api/*handlers (readsubmit-login.jsas representative) and mostserver/web/routes/*.endpoint.jspage renderers (readroutes.jswhich wires them). server/socket/{client.js,events/*,models/UserData.js}andserver/service/{CompatibilityService,SocketService}.js(referenced/instantiated; not opened).server/queue/*were all read;server/util/magic.js,server/logger/{syslog,papertrail}.js,server/web/helper/formatOpenHours.js,server/web/middleware/{customerDataCache,Template}.jsnot opened.engines/translator/*,engines/util/*(referenced fromrender-server.js; not opened individually).customization/server/**(the instacash route/api/service implementations thatcustomizations.jsrequires) — directory exists but those*.endpoint.js/service files were not opened; some referenced files (e.g.server/web/middleware/callbackRequest, severalserver/web/routes/*.endpoint) are required bycustomizations.jsbut I did not confirm each exists on disk.web/static/compiled assets,maintenance/,security/monthly logs (not analyzed individually).
- Counterpart services behind the RabbitMQ queues (vuer/OSS, customer/portal-data providers) are out of repo and inferred only from queue names.
supervisor_portal_css_dev.conf/nginx_portal_css_dev.confread only by name (docker variants opened fully).- Other branches (
customization/cib,unicredit-srb,takarek-mfszh,dunatakarek,demo, etc.) not inspected here — see customization-branch-catalog.
Sources
package.json,readme.md,changelog.md,config.js,server.jsconfig/dev.json,config/docker.jsonbin/build/build.js,bin/credits,bin/script/external.task.js,bin/script/external.compiler.js,bin/script/script.task.js,bin/script/script.compiler.js,bin/style/style.task.js,bin/style/style.compiler.js,bin/watch/watch.jsengines/build/build.js,engines/build/bundle.js,engines/twig/render-server.jsserver/service_container.js,server/bootstrap/connection/rabbitmq.js,server/logger.jsserver/web/WebServer.js,server/web/routes.js,server/web/api/submit-login.js,server/web/middleware/requireSession.js,server/web/middleware/customerAuthWithToken.jsserver/service/PortalService.js,server/service/BrandingService.js,server/service/CustomerDataService.js,server/service/IpFilterService.jsserver/socket/socket-server.jsserver/queue/rpc_client/{PortalRPCClient,PortalDataRPCClient,JwtAuth,GetCustomer,OpenHours,OpenHoursCalendars,RoomExport}.js,server/queue/rpc_server/PortalRPCServer.js,server/queue/queue_client/Customer.jscustomization/customizations.js,customization/branding-options.jsontest/jest.config.js,test/travis.sh,test/testconfigs/local.json.travis.yml,.npmrc,.yarnrc,sonar-project.properties,.github/CODEOWNERS,client/features/readme.mdnginx_portal_css_docker.conf,supervisor_portal_css_docker.conf- Repo file inventory:
git -C portal_css ls-tree -r --name-only devel(422 files);git -C portal_css log --oneline;git -C portal_css branch -a;find -iname 'Dockerfile*'(none)