WayYourChild · Assessment Backend Core Panel AI Opportunities Market Index

Codebase Assessment / Confidential

WayYourChild Platform

Expert review of three repositories ahead of engineering handover

REPOSITORIES  3 TOTAL CODE  ~166,000 LOC DOMAIN  School management + online payments DATE  2026-06-08

Cross-Cutting Themes

  • Payments are the highest-risk surface in every repo — forgeable callbacks (backend), the merchant salt hardcoded (core) and exposed in the browser (panel). Treat as an immediate incident.
  • Secrets and build artifacts are committed across all three — hardcoded keys, committed build/ directories, and ignored lockfiles. Reproducibility and secret hygiene are broken platform-wide.
  • The core library's missing indexes and default autopopulate are the single highest-leverage performance fix — they are the root cause of the backend's slow queries and fan-out.
  • No automated tests and no CI anywhere across ~166K LOC — the prerequisite before any safe refactor.
01

wyc-backend

Users Microservice

Express · Mongoose 8 · TypeDI · Bull · ~71,000 LOC TypeScript

BRANCH uatHEAD 33a8ce4LARGEST FILE exam.ts — 10,498 lines

Verdict

Functional, but high-risk. An early-prototype-grade codebase running in production and handling real money. The skeleton of a sound design exists — layered structure, dependency injection, a job queue — but the discipline that makes 71K LOC safe is absent: no tests, type-safety effectively off, no verification on the payment flow, and weak multi-tenant isolation. The payment and data-isolation defects are exploitable today.

10,498Lines in exam.ts
~1,450Lines × 2 twin methods
~1,600@ts-ignore directives
0Automated tests

Critical

Fix before trusting with money
C1

PayU payment success is forgeable

Critical

Success callbacks accept unauthenticated, unsigned, client-supplied JSON and mark fees paid. No reverse-hash verification, no server-side call to PayU; the paymentVerify function exists but is never called. Anyone can POST a fake success body and get a receipt with no money received.

routes/payUBiz.ts · controllers/payUBiz.ts · services/payu.ts

C2

Amount tampering and no idempotency

Critical

The recorded amount comes straight from the forgeable callback; the only guard is a weak lower bound, not equality against a gateway-confirmed amount. No txnid uniqueness check, so a callback can be replayed.

controllers/feePayment.ts:401–419

C3

Trivial SuperAdmin takeover

Critical

POST /api/auth/register is unauthenticated and accepts an arbitrary role — anyone can register as SuperAdmin. The /permission routes including _create are also unauthenticated.

routes/auth.ts · routes/permission.ts

C4

Cross-tenant data access and NoSQL injection

Critical

Many by-id update/delete routes query by { _id } only, never scoped to schoolId — one school can modify another's records by guessing ObjectIds. List endpoints pass req.query.filter straight into Mongoose (operator injection, $where).

routes/routes.ts · controllers/routes.ts · routes/student.ts

C5

Account-takeover primitives and leaked credentials

Critical

forgotPassword returns the reset token in the response; new users get a password equal to their mobile number; a hardcoded PayU key and static hash are committed. Passwords and tokens are logged.

controllers/auth.ts · controllers/student.ts · controllers/schoolRequest.ts

C6

No automated tests on revenue-handling code

Critical

Zero tests across 71K LOC including the fee and payment logic, with no test script wired up. Every change is blind.

Index

High

Fix early
H1

Systemic data-layer performance problems

High

Bulk fee payment re-runs calculateFee (6+ queries each) per month inside a per-student loop — roughly O(M²); a 40-student upload can fire tens of thousands of queries in one request. .lean() is used exactly once and autopopulate is on by default (15-collection fan-out). List endpoints have no enforced pagination; attendance bulk-create has a fire-and-forget N+1 that is also a data-race bug.

studentFeeBulkPayment.ts · business/feePayment.ts · studentAttendance.ts

H2

Type safety is effectively off

High

No strict in tsconfig; dev runs ts-node --transpile-only; ~1,600 @ts-ignore and ~1,200 eslint-disable. The compiler — the only free safety net on untested code — is muted.

H3

Repository hygiene is inverted

High

The compiled build/ (502 files) is committed while package-lock.json is gitignored — non-reproducible installs and stale shipped artifacts.

H4

No CI, container, or process safety

High

No CI, no Dockerfile, no graceful shutdown, no unhandledRejection handlers. Deploys kill in-flight payment requests; an unhandled rejection crashes silently.

H5

Inverted and cyclic dependencies

High

The business layer imports up into controllers and routes; res is passed deep into business logic (there is a createMockRes hack), making it untestable and risking "headers already sent".

Index

Medium

Quality debt
M1

Status codes silently wrong

Medium

res.json(...).status(200) appears 162 times — .status() after .json() is a no-op, so 201/4xx codes are sent as 200.

M2

Inconsistent responses and error leakage

Medium

Three competing response shapes; central error handler does not log and returns raw error text to clients.

M3

Dead code, stray logging, typos

Medium

~70 stray console.logs, ~2,300 lines of commented-out code, dead built-in imports, and typos baked into DI tokens (feeStrucuture, penality). An abandoned V2 refactor coexists with V1.

Index

Already Sound

Layered architecture and DI are real and consistent.
49 of 63 routes already validate via celebrate definitions.
Route handlers consistently use try/catch with next(e); no swallowed errors.
Bull queue has sensible retries, backoff, and stalled-job handling.

Action Plan

Week 1 — Stop the bleeding
  1. Verify every PayU callback (reverse hash + server-side verify); validate amount equality; add txnid idempotency.
  2. Lock down /auth/register and /permission/*; never accept role from unauthenticated input.
  3. Enforce schoolId scoping on all by-id read/update/delete; whitelist query-filter keys.
  4. Stop logging passwords/tokens; remove the returned reset token; remove hardcoded PayU creds and rotate.
  5. Commit package-lock.json; stop committing build/.
Weeks 2-3 — Build the safety net
  1. Add CI running typecheck + lint + jest; wire up the test script.
  2. Write integration tests for the payment path first.
  3. Add graceful shutdown and process-level error handlers.
Ongoing — Pay down debt
  1. Fix the bulk-payment query explosion (offload to Bull, compute fees once); add lean reads and pagination caps.
  2. Turn on TS strict incrementally; add complexity and floating-promise lint guardrails.
  3. Decompose exam.ts — merge the two ~1,450-line twins, then split by domain.
Back to index
02

wyc-core

Shared Library

Mongoose 8 · TypeDI · express-jwt · AWS SDK · 115 models · ~5,200 LOC

BRANCH masterROLE Git-distributed dependencyCONSUMED BY every service

Verdict

Functional, but high-risk for a trust anchor. This library defines the data models, auth, and config that every service depends on — its defects propagate everywhere. JWT verification is configured correctly (the one bright spot). But it commits live secrets into source and a compiled build/, defines zero database indexes across 115 models, enables ref auto-population by default (the root cause of the consumers' fan-out), and has no versioning, tests, or type declarations.

0Query indexes defined
251Autopopulate flags (83 files)
84%Commits touching build/
0Tests · tags · type defs

Consumed by other services via a raw git reference to master. With the version frozen at 1.0.0 and no tags, every consumer silently tracks the latest commit and inherits breaking changes with no warning and no rollback.

Critical

Propagates to every service
C1

Live PayU merchant key and salt hardcoded

Critical

The merchant key and salt — the secret that signs payments and verifies callbacks — are hardcoded and also committed compiled. With the salt an attacker can forge valid payment hashes. Config already reads these from env, so the hardcoded copies are redundant and dangerous. Rotate the salt with PayU immediately.

services/payUHash.ts:3-4 · build/services/payUHash.js

C2

Hardcoded SuperAdmin hash, auto-seeded on import

Critical

A SuperAdmin user with a committed bcrypt hash is created unconditionally whenever the model is imported — a crackable, public, full-privilege backdoor provisioned in any consumer's database.

models/user.ts:197-198, 219

C3

No indexes on any of the 115 models

Critical

Not a single compound index exists. The consumers' hot query path (filter by schoolId/classId/sectionId/studentId/month, aggregate dues) runs as a full collection scan every time. This is the primary cause of the backend's slow queries.

models/feePayment.ts · studentAttendance.ts · carryForward.ts

C4

Autopopulate by default causes transitive fan-out

Critical

autopopulate: true appears 251 times across 83 of 115 models, and populated docs themselves auto-populate — a single read explodes into a recursive cascade across 15+ collections, and blocks lean reads and aggregation.

models/student.ts:98-99 · examSchedule.ts:13-19

C5

Compiled build/ committed, baking secrets in

Critical

The 492-file build/ (with source maps) is tracked and is the distribution channel. Every secret above is re-published as readable JS, and 84% of commits are build noise. A single bad hand-committed build reaches all consumers.

build/ (492 files) · .gitignore

Index

High

Unsafe to consume or change
H1

No semantic versioning

High

Version frozen at 1.0.0, zero git tags, no changelog. Consumers can only reference a branch or SHA, so any model change is an invisible, simultaneous breaking change to all services.

H2

No type declarations emitted

High

declaration is off and no .d.ts ship. Consumers import compiled JS with no types — defeating the library's main purpose.

H3

NoSQL injection and ReDoS in the filter pipeline

High

The shared filter middleware builds unbounded regexes from raw input with $regex allowed and only the top-level operator validated — catastrophic-backtracking DoS and operator injection, for every consumer.

middlewares/getFilters.ts:45-65 · config/index.ts:254

H4

Hardcoded Agendash creds and personal defaults

High

Job dashboard ships with agendash / 123456; SES identities default to a personal Gmail. Anyone reaching the dashboard controls background jobs.

config/index.ts:64-66, 238-244

H5

No tests/CI; fragile connection lifecycle

High

No tests gate the build that reaches every consumer. Loaders connect once with no pooling, timeouts, error handlers, retry, or graceful shutdown.

loaders/mongoose.ts · loaders/agenda.ts

H6

Interface and schema drift

High

113 hand-maintained interfaces mirror 115 schemas; six models have none, and existing ones already mistype fields and omit enum values. The TS contract no longer matches runtime documents.

interfaces/IFeePayment.ts:13-25

Index

Medium

Landmines & debt
M1

Duplicate model-name registration trap

Medium

A dead oldclasssubjectMapping.ts registers the same model name as the live file; uncommenting or importing it crashes every consumer at boot.

M2

Broken PayU hash implementation

Medium

A unary-plus concatenation bug corrupts the hash string; the function returns the hash object instead of the hex digest, and logs the salt-bearing string.

M3

Malformed and untyped schema fields

Medium

{ String } shorthand defines empty subdocuments, and schemaless [] arrays become untyped, unindexable Mixed types.

M4

Lockfile ignored, not strict, throws on import

Medium

package-lock.json gitignored; TS strict off; config throws on a missing .env at import — a surprising side effect in containerized consumers.

Index

Already Sound

JWT verification is correct — HS256 pinned, secret from env, token from the Authorization header.
Dependency versions are modern and current.
Source layout is clean and predictable.
S3 presigned uploads use a tight 60-second TTL; EJS templates auto-escape.

Action Plan

Week 1 — Stop the bleeding
  1. Remove the hardcoded PayU key and salt; rotate the salt; read from config only.
  2. Delete the hardcoded SuperAdmin credentials and the auto-seed; rotate that account everywhere.
  3. Replace Agendash credentials with strong env values; restrict access.
  4. Stop committing build/; commit package-lock.json.
  5. Lock down the filter pipeline: drop/escape $regex, cap length, validate the full shape.
Weeks 2-3 — Fix consumers' performance at source
  1. Add compound indexes to fee, attendance, and carry-forward on the tenant tuple.
  2. Remove default autopopulate; make population opt-in per query.
  3. Adopt semantic versioning with tags and a changelog; pin consumers to versions.
Ongoing — Make it a real library
  1. Emit type declarations and a proper package entry; choose one distribution model.
  2. Add CI running install, lint, typecheck, and tests gating master and releases.
  3. Harden the loaders; generate interfaces from schemas to end drift.
Back to index
03

wyc-panel

Admin Frontend

React 16 · CRA 3.4.3 · Redux · ~94,500 LOC · 406 files · forked Vuexy template

BRANCH masterLARGEST FILE ViewSchool.js — 2,600 linesTESTS 1 (default stub)

Verdict

Functional, but operationally fragile. An admin template forked and grown into a large app without re-architecting — a working "big ball of mud." It almost certainly will not build on a modern Node version, installs are non-reproducible, it has effectively zero tests, and authorization is entirely client-side. One real strength: route-based code-splitting is done thoroughly — don't touch it. The work is in tooling, security, data fetching, and breaking up the giant components.

1Test file (stub)
45Endpoints fetch full tables
0Memoized components
2,600Lines in largest file

The toolchain is end-of-life: react-scripts 3.4.3 and node-sass 4.14 do not build cleanly on the installed Node 20, and both lockfiles are gitignored — a fresh clone cannot reliably be run without workarounds. This blocks everything else.

Critical

Fix before active development
C1

Will not build or install on modern Node

Critical

node-sass 4.14 only ships bindings for Node ≤14; react-scripts 3.4.3 uses a webpack 4 stack that breaks on Node 17+. On Node 20 a fresh clone cannot start without workarounds.

package.json · runtime Node v20.14.0

C2

Non-reproducible installs; lockfiles ignored

Critical

Both package-lock.json and yarn.lock are gitignored, and install:clean deletes the lockfile. Every install floats to the newest matching versions — different trees per machine.

.gitignore · package.json

C3

PayU salt handled in the frontend in plaintext

Critical

The school screen holds the merchant key and salt in state, renders the salt in a plaintext input populated from the API, and submits it back. The salt signs payments — returning it to the browser exposes it to any admin and any XSS.

components/school/ManageSchool.js:68-77, 1118-1129

C4

Authorization is entirely client-side

Critical

Route and role gating is driven by jwt-decode, which performs no signature verification. The SuperAdmin/SubAdmin boundary is cosmetic — a user can edit decoded permissions or call privileged endpoints directly. Safe only if the backend re-validates every role.

src/router.js:145-158 · utils/localStorageConstants.js

C5

Effectively zero test coverage

Critical

Only the default App.test.js exists across ~94,500 LOC, and it would itself fail. Any change is unverifiable, which makes the Node upgrade and every refactor blind.

C6

Entire collections fetched client-side

Critical

45 endpoints hardcode a "fetch everything" parameter, so every list view downloads the full collection and paginates in JS. A school with thousands of students transfers megabytes per mount. Server-side pagination scaffolding already exists, unused.

components/API.js · components/dataTable/DataTable.js:48

Index

High

Security, reliability, perf
H1

Token in localStorage and committed secrets

High

The session JWT is in localStorage (XSS-stealable). The .env is committed with a Google Maps key, and history leaks backend origin IPs. Move the token to an HttpOnly cookie; rotate and restrict the key.

views/authentication/Login.js:37 · .env

H2

Outdated, vulnerable dependency stack

High

axios 0.19.2 (SSRF/ReDoS), xlsx/xlsx-style (prototype pollution, parses untrusted uploads), prismjs 1.20, node-sass 4, and the react-scripts 3.4.3 tree carry known advisories.

H3

No central HTTP error handling

High

Raw fetch with no status check — 401s pass as success, no central logout. 183 of 192 catch blocks only console.log; failed saves leave users with no feedback.

components/API.js:10-21

H4

God-components

High

16 files exceed 800 lines, 42 exceed 500. ViewSchool.js is a single 2,600-line class with ~22 state fields and 13 API calls — untestable, regression-prone.

views/superAdmin/schoolSection/ViewSchool.js

H5

Hidden full-dataset table render; heavy libs eager

High

The main table rebuilds two hidden copies of the whole dataset on every render. ag-grid-enterprise, html2canvas, and jspdf are imported into common chunks; a 4 MB logo is bundled into three dashboards.

components/clientTable/ClientDataTable.js:7-8, 2046

H6

Hardcoded default login credentials

High

The login form ships with a real-looking mobile number pre-filled as both username and password.

views/authentication/Login.js:17-18

Index

Medium

Bloat & structure
M1

Three UI frameworks plus accidental jQuery

Medium

Material-UI and Ant Design ship in full but are used in ~12 and ~5 files for trivial widgets reactstrap already provides. jQuery and dom7 are pulled in only by accidental unused imports.

M2

No catch-all route

Medium

The route switch has no fallback — any unknown URL renders a blank screen with no 404 page.

M3

Shallow, mis-scoped error boundary

Medium

The boundary renders a spinner as its fallback (infinite loading on crash) and is mounted below the layout, so layout/store errors escape it.

M4

Redux barely used; no server-state caching

Medium

Domain data never enters Redux and is re-fetched on every mount. Auth state is split across two slices; a duplicate store config has a broken import; the login thunk is a commented-out shell.

Index

Already Sound

Route-based code-splitting is thorough (137 lazy routes) — first paint loads only the login screen.
Consistent, predictable patterns make refactors mechanical rather than a rewrite.
API calls are centralized in one layer — a sound instinct.
reactstrap is a clear dominant UI system; no dangerous HTML sinks in active code.

Action Plan

Week 1 — Make it buildable and safe
  1. Migrate node-sass to sass; add .nvmrc and engines; get a clean build on a pinned Node.
  2. Commit a single package-lock.json; stop deleting it.
  3. Untrack and rotate committed secrets; remove the default login credentials.
  4. Stop returning and displaying the PayU salt; make payment secrets write-only.
  5. Confirm the backend enforces every role check server-side.
Weeks 2-3 — Build the safety net
  1. Add a test setup + smoke tests (login, auth redirect, API layer); stand up CI.
  2. Upgrade vulnerable dependencies and add an audit gate.
  3. Rebuild the HTTP layer on one axios instance with interceptors; add a 404 route and a real error-boundary UI.
Ongoing — Performance and structure
  1. Replace fetch-everything endpoints with server-side pagination; add server-state caching.
  2. Fix the hidden table render and dynamic-import heavy libs; remove MUI, antd, jQuery, and the duplicate xlsx; compress assets.
  3. Break up the god-components; de-duplicate role-parallel screens; adopt hooks for new code.
Back to index