Architectural Issues

Medium

Business Logic in Mongoose Hooks

The most complex business logic is embedded in Mongoose pre/post hooks on models (especially Ledger, License, Invoice, Booking). This makes it:

  • Hard to test — logic is tightly coupled to database operations
  • Hard to debug — hooks fire implicitly, creating non-obvious side effects
  • Hard to reason about — a single save() call can trigger chains of hooks across models

Example: The Ledger model's pre-save hook alone handles: currency derivation, org validation, permission checks, fund validation, wallet splitting, overflow logic, and parking wallet auto-creation.

Recommendation: Extract to explicit service layer functions that can be tested independently.

Medium

Mixed Authentication Patterns

Three different auth mechanisms with unclear precedence:

  1. Session-based — Express sessions with Redis store
  2. API keyx-api-key header for service-to-service
  3. JWT tokens — in query parameters for login flows

No unified authentication middleware enforces consistency or priority. Different routes use different mechanisms ad-hoc.

Medium

Tight Coupling Between Packages

Despite being a monorepo with separate packages, the services share models directly via file paths. The openmembers package imports models from ../../models/ directly rather than going through the API.

Impact: Cannot deploy or test packages independently. Changes to models affect all services simultaneously.

Medium

eval() in Cron Scheduler

packages/cron/cron.js

Schedule records store JavaScript code strings in the action field, executed at runtime via eval(). This is a code injection risk if the database is compromised, and makes it impossible to statically analyse what the cron system does.

Code Quality Issues

Medium

No Input Validation

The codebase lacks input validation middleware. req.body is passed directly to database operations without schema validation.

// CURRENT - no validation
const organisation = await req.apihelper.getOne("organisation", req.params.organisation_id);
if (!req.body.industrysector_id) { req.body.industrysector_id = [] }
await req.apihelper.put("organisation", req.session.user.organisation_id, req.body);

Recommendation: Add schema validation (Zod, Joi, or express-validator) at every API boundary.

Medium

Inadequate Error Handling

Multiple patterns of poor error handling:

  • packages/api/libs/messagequeue.js: console.error() with no re-throw (errors silently swallowed)
  • packages/openmembers/routes/login.js: Generic error messages provide no debugging info
  • packages/api/bin/server.js: MongoDB errors only logged, not handled

Errors may contain sensitive information in some paths, while being swallowed entirely in others.

Medium

Inconsistent Secret Management

Secrets are sourced from at least four different locations with no centralised management:

SourceUsage
config.secretSession encryption in some modules
config.openmembers.secretOpenMembers-specific encryption
process.env.APIKEYAPI authentication in some routes
JSON keyfiles on diskGoogle Cloud credentials

No rotation mechanism exists for any of these secrets.

Low

Mixed Frontend Technologies

The platform uses three different frontend approaches:

  • Handlebars — server-rendered templates (OpenMembers, CRM)
  • Svelte — Xero integration, Doors module
  • Vue 2 — POS, some components

This creates cognitive overhead for developers and inconsistent user experiences.

Low

Legacy Dependencies

  • PhantomJS 2.1.16 — deprecated headless browser, used for PDF generation
  • Node 8 "carbon" — in OpenMembers Dockerfile (EOL Dec 2019)
  • Node 16 — in API Dockerfile (EOL Sept 2023)
  • Vue 2 — end of life (Dec 2023)
  • Requires --openssl-legacy-provider flag for Webpack builds

Test Coverage

Medium

Minimal Test Coverage

For a platform of this size and complexity, the test suite is notably sparse:

  • ~9 test files total across the entire codebase
  • API tests cover basic CRUD and login only
  • No security-focused tests (auth bypass, injection, etc.)
  • No integration tests for complex workflows (invoice run, booking lifecycle)
  • Selenium tests exist but cover limited UI scenarios
  • No tests for the wallet/ledger deduction logic (the most complex code)

What exists:

SuiteCommandScope
APIyarn test:apiBasic CRUD, login
OpenMembersyarn test:openmembersLimited
Sharedyarn test:sharedShared utilities
Reportsyarn test:reportsReport generation
Seleniumyarn test:seleniumBasic UI flows

Positive Patterns Worth Preserving

Not everything is technical debt. These patterns work well and should inform the replacement:

PatternWhereWhy It Works
Audit logging via deep-diff Log model, post-validate hooks Every model change is tracked with before/after diffs. Critical for financial compliance.
Reserve-to-debit pattern Ledger/Booking models Elegant approach to hold funds during booking without immediate deduction.
Track-based task templates Opportunity/Task/Track models Templated sales processes with auto-generated task chains and cascading due dates.
Multi-instance federation External services, Lead model Users can be looked up across multiple WSM instances, enabling network-wide access.
Configurable feature set OpenMembers config "components" array Features can be enabled/disabled per deployment without code changes.
Priority-based wallet deduction Wallet/Ledger models Flexible credit system supporting multiple currencies with overflow between wallets.

Technical Debt Summary

CategoryIssue CountImpact
Architecture4Limits scalability, testability, and independent deployment
Code Quality4Increases bug risk, slows development, creates security gaps
Testing1 (systemic)No safety net for refactoring or adding features
Dependencies5+Known CVEs in EOL runtimes and libraries
Frontend3 frameworksDeveloper cognitive overhead, inconsistent UX