CI / quality (push) Waiting to run
- Add src/core/ (config, errors, events, http, observability, storage, query) - Add src/Spreadsheet/ engine with native POI backend and Excel bridge - Add Envelope C spreadsheet flow and tender bidding envelope modules - Consolidate components into src/components/, remove legacy Component/ and Hooks/ - Add registration state/validation, auth session guard, profile edit modules - Add unit tests across axios, redux, bidding, registration, encryption, core - Add CI workflow, docs (architecture, audit, DSC API spec), keystore example - Remove tracked keystore binary; ignore scripts.zip Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
364 lines
37 KiB
Markdown
364 lines
37 KiB
Markdown
# ProcureLinc — Principal-Level Architectural Audit & Redesign
|
||
|
||
**Subject:** ProcureLinc React Native mobile app (e-procurement: auctions + tenders + eKYC + sealed-bid bidding)
|
||
**Stack:** React Native 0.79.0, React 19, Redux Toolkit + redux-persist, React Query, STOMP/SockJS, Axios, Formik/Yup, Hermes + New Architecture (Fabric/TurboModules) enabled.
|
||
**Scope:** Full-project architectural review — no code was modified. Every claim below is anchored to `file:line` evidence.
|
||
**Verdict:** The app *works*, but it is at **prototype maturity carrying production-grade risk**. It is not currently safe to ship to a real user base handling money and Aadhaar/PAN PII, and it is not structured to scale to a large team or user count without significant rework. This document is the redesign blueprint.
|
||
|
||
---
|
||
|
||
## 1. Executive Summary
|
||
|
||
ProcureLinc is a functional e-procurement client with genuinely good *ideas* in places (a clean `AppText` typography primitive, a correct paginated-list template in `LiveTender.tsx`, a working dark-mode theme system, STOMP-based live bidding). But the codebase is in a state common to fast-moving startups: **multiple half-finished architectural attempts layered on top of each other, none fully adopted, with security and correctness debt that compounds as it grows.**
|
||
|
||
The five findings that matter most, in priority order:
|
||
|
||
| # | Finding | Severity | Where |
|
||
|---|---------|----------|-------|
|
||
| 1 | **A binary signing keystore (`appKeyStore`) is committed to git**, and **release builds are signed with the public debug keystore** (`storePassword 'android'`). Anyone with repo access can forge signed ProcureLinc APKs. | 🔴 Critical | `android/app/appKeyStore` (git-tracked), `android/app/build.gradle:103` |
|
||
| 2 | **A global Axios interceptor logs every request/response — full URLs, JWT headers, login passwords, Aadhaar/PAN/OTP, and sealed-bid amounts — to logcat, ungated by `__DEV__`, in release builds.** | 🔴 Critical | `src/Axios/Axios.ts:47-99` |
|
||
| 3 | **The data layer is four competing API stacks across three different backend hosts**, and the dominant one (`apiCall`) *swallows every error and returns `{success:false}` cast to the success type* — defeating TypeScript, React Query error/retry/cache, and forcing brittle status-code checks everywhere. | 🔴 Critical (architecture) | `Axios.ts:124-141`, `api/client.ts`, `apis.ts`, `Constant/Variable.tsx` |
|
||
| 4 | **App-wide cleartext HTTP is permitted in release, with no SSL pinning**; auth JWT + full login response (incl. PII) is persisted **unencrypted** in AsyncStorage; device binding is client-spoofable and DSC "binding" is UI-only. | 🔴 Critical (security) | `network_security_config.xml`, `Redux/Store.js:20-24`, `DeviceBinding.tsx`, `DscBinding.tsx` |
|
||
| 5 | **God-components with mixed concerns and massive duplication** — `TenderBidding.tsx` is 1205 lines (crypto + REST + upload + validation + UI); `StartBidding`/`StartReverseBidding` and their card components are ~90% copy-paste forks. | 🟠 High (maintainability) | `TenderBidding.tsx`, `Bidding/*` |
|
||
|
||
**Bottom line:** before any new feature work, the project needs (a) a security remediation sprint (secrets, transport, logging, storage), and (b) a data-layer consolidation onto a single typed client + properly-configured React Query. Everything else — modularization, design-system unification, testing, CI/CD — follows from those two.
|
||
|
||
**On the "AI-powered operating system" vision:** I am challenging the premise directly (see §16). The current codebase contains **zero AI, agent, RAG, voice, or knowledge-graph subsystems**. That is not a criticism — an e-procurement app doesn't need them today. But the audit cannot review AI architecture that doesn't exist; instead §16 lays out what an AI-native evolution would *actually* require and why the current foundation must be fixed first.
|
||
|
||
---
|
||
|
||
## 2. Current Architecture Overview
|
||
|
||
**Shape:** A single-module React Native app. No feature-module boundaries; organized by loose type-folders (`Screens/`, `Component/` **and** `components/`, `Hooks/`, `Services/`, `Redux/`, `socket/`, `theme/` **and** `Constant/Color`, `Styles/`).
|
||
|
||
**Runtime composition** (`App.tsx:19-41`): `NavigationContainer → QueryClientProvider → GestureHandlerRootView → AlertNotificationRoot → Redux Provider → PersistGate → Routes`. `LogBox.ignoreAllLogs(true)` is set at module load (`App.tsx:15`).
|
||
|
||
**Navigation** (`Navigation/MainStack.jsx`): one flat native-stack with 23 sibling screens; `Home` hosts a custom animated `BottomTab` (4 tabs). No logged-in/out navigator split — auth gating is a one-shot imperative redirect inside `Splash.tsx`.
|
||
|
||
**State:** Redux Toolkit, 6 slices (`token`, `auction`, `selectPurchaseAuction`, `selectPurchaseTender`, `tender`, `theme`), only `token` + `theme` persisted. `serializableCheck:false` globally (`Store.js:30`).
|
||
|
||
**Data:** Four coexisting mechanisms — `apiCall` wrapper (dominant), a second `apiClient` axios instance, raw `axios` calls in `apis.ts`, and raw `axios` in screens — hitting **three** hardcoded hosts: `eproc.godemo.in` (REST), `procurelinc.in` (sockets), `procurelinc.com` (dead `client.ts`).
|
||
|
||
**Real-time:** STOMP-over-SockJS (`socket/Socket.tsx`) for receiving bid updates; bids are *placed* over REST, not STOMP.
|
||
|
||
**Crypto:** JS-side AES (ECB mode, SHA-256 key truncated to 128 bits) + RSA (jsencrypt) for sealed-bid envelopes (`Services/Encryption/encrypt.tsx`).
|
||
|
||
**Native:** No custom Java/Kotlin — stock RN host. Hermes on, New Architecture on. No env abstraction, no tests, no CI/CD.
|
||
|
||
Total: ~26k LOC of TS/JS across 138 source files.
|
||
|
||
---
|
||
|
||
## 3. Architectural Strengths (worth preserving)
|
||
|
||
An honest audit names what's good so it isn't lost in a refactor:
|
||
|
||
- **`components/AppText.tsx`** — a proper typographic primitive: variant + theme color + Android-safe font-family merging. This is the seed of a real design system. Keep and expand.
|
||
- **`useTheme` + `ThemeSlice`** — a working light/dark/system theme with a user switcher (`CustomDrawer.tsx`). Solid foundation; the problem is only that half the app bypasses it.
|
||
- **`LiveTender.tsx`** — the *correct* FlatList pattern: memoized `renderItem`/`renderFooter`, `keyExtractor`, infinite-scroll pagination via React Query `fetchNextPage`/`hasNextPage`, and tuned perf props (`initialNumToRender`, `windowSize`, `removeClippedSubviews`). **This should be the template every list screen follows.**
|
||
- **`socket/Socket.tsx`** — a reasonable `useReducer`-based STOMP abstraction with a pending-subscription queue and reconnect/heartbeat config. The core is sound; the consumers misuse it.
|
||
- **Modern baseline** — RN 0.79, Hermes, New Architecture, React Query, Redux Toolkit, Formik/Yup are all present. The *ingredients* of a good architecture exist; they are simply half-wired.
|
||
- **`android:allowBackup="false"`** is correctly set — limits `adb backup` extraction of the (unfortunately plaintext) token.
|
||
|
||
---
|
||
|
||
## 4. Architectural Weaknesses
|
||
|
||
Grouped by subsystem. Severity: 🔴 Critical, 🟠 High, 🟡 Medium.
|
||
|
||
### 4.1 Data / API layer
|
||
- 🔴 **Four parallel API stacks, three hosts, two auth-header names.** `apiCall` sends `Authentication: Bearer` (note: not `Authorization`) reading `token.token` (`Axios.ts:118`); `apiClient` sends `Authorization` reading `token.token.token` (`client.ts:17`); screens send raw axios with `Authentication: Bearer ${Token.token}` (`BiddingCard.tsx:82`). There is no single place to add retry, refresh, tracing, or error handling.
|
||
- 🔴 **`apiCall` swallows errors and casts `{success:false}` to `R`** (`Axios.ts:124-141`). This is the single most damaging decision in the codebase — it defeats the type system, makes React Query believe every call succeeded (`isError` never fires, retries never happen, bad responses get cached as valid), and forces defensive `res?.statusCode===200` checks in every consumer. `try/catch` blocks around `apiCall` (e.g. `apis.ts:76-82`) are dead code because it never throws.
|
||
- 🟠 **React Query is present but mis-configured.** `new QueryClient()` with no defaults (`App.tsx:20`) ⇒ `staleTime:0`, refetch-on-mount/focus ⇒ refetch storms. It's also instantiated *inside the component body*, so it's recreated on every re-render. Broken query keys: `useAucitonItemDetail` uses `queryKey:[]` (all instances collide), `useGetAuciton` uses static `['getAuction']` ignoring the id. Missing `enabled` guards fire `?tenderId=undefined` calls. No `invalidateQueries` anywhere — lists go stale after writes.
|
||
- 🟡 **Dead/broken config.** `Constant/AllUrls.tsx` builds ~30 URLs from `${Variable.Main_Base}` — but `Main_Base` doesn't exist, so every URL is `undefined/...`. This whole file is leftover from a different (e-commerce) app. The generic hooks (`useFetch/useUpdate/useDelete`) are imported nowhere.
|
||
|
||
### 4.2 State management
|
||
- 🟠 **The token slice lies about its type.** Declared `{token:''}` (`Token.tsx:3`) but at runtime holds a full object (`Login.tsx:212`). No `RootState`/`AppDispatch` types exist; every `useSelector` is `(state:any)`. Zero compile-time safety on auth state.
|
||
- 🟡 **Two inconsistent logout paths** — `removeToken()` (sets `''`) vs `setToken({})` (leaves a truthy empty object) vs direct `AsyncStorage.removeItem('persist:root')`. These can desync in-memory state from disk and confuse the splash auth check.
|
||
- 🟡 **`serializableCheck:false` globally** hides genuine non-serializable leaks, and the slices store whole API response objects.
|
||
|
||
### 4.3 UI / screens
|
||
- 🟠 **God-components:** `TenderBidding.tsx` (1205), `ProfileTab.tsx` (1031), `Login.tsx` (792), `Ragistration.tsx` (725, ~25 `useState`), `Emd.tsx` (608). Networking + crypto + validation + sub-components + rendering all in one file.
|
||
- 🟠 **Copy-paste forks:** `StartBidding`≈`StartReverseBidding` (~90%), `BiddingCard`≈`ReverseBiddingCard` (identical StyleSheets) — diverging subtly (one card is `memo`'d, the other isn't; wrong color import in reverse). Every fix must be made twice, and they *will* drift.
|
||
- 🟠 **Split-brain design system:** `src/Component/` (old) vs `src/components/` (new); two `MyInput`, two `PrimaryButton`, three side-drawers (one dead), two color palettes with **two different brand oranges** (`Constant/Color:#F97316` vs `theme/colors:#e39546ff`).
|
||
|
||
### 4.4 Navigation
|
||
- 🟠 **No route type safety** (`createNavigationContainerRef<any>()`, `useNavigation<any>()`, `useRoute<any>()` everywhere), **no route guards** (every screen reachable), **no deep linking** (`NavigationContainer` has no `linking`).
|
||
|
||
### 4.5 Real-time
|
||
- 🟠 **`subscribe()` called in the render body** (`StartBidding.tsx:137`, `StartReverseBidding.tsx:171`), re-registering the STOMP callback every render; **no `disconnect()`/`unsubscribe()` on unmount** ⇒ leaked connections that keep reconnecting (5s) and draining battery/data. Bids split across REST *and* STOMP for the same feature.
|
||
|
||
---
|
||
|
||
## 5. Design Smells
|
||
|
||
- **Misspelled, leaked-through names as public API:** folder `Ragistration/`, components `Serach/SeacrhInput`, props `isVarify`/`maxlength`, constant `FORARD_AUCTION_ITEM`, string `'Failed load cities'`. Names are the cheapest documentation; these signal no review gate.
|
||
- **Duplicate constants pointing at the same endpoint** (`SELECTED_AUCTION_ITEM` == `EMD_CHECK`).
|
||
- **`useState` used as a value holder**: `const tenderId = useState(route.params?.tenderId)` captures the `[state,setState]` tuple and never uses it (`TenderBidding.tsx:71`).
|
||
- **Sub-components declared inside a parent's render body** (`InfoRow`, `EnvelopeButton`, `renderEnvelopeItem` in `TenderBidding.tsx:113-309`) — recreated every render, defeating memoization and remounting subtrees.
|
||
- **A test/debug screen wired into production** (`TestScreen/DecryptionScreen.tsx`) with a hardcoded real AES key copyable to clipboard.
|
||
- **`LogBox.ignoreAllLogs(true)`** — silencing all warnings is hiding the smoke detector.
|
||
|
||
## 6. Anti-Patterns
|
||
|
||
- **Error-swallowing service layer** (`apiCall` returning `{success:false} as R`) — the "exceptions considered harmful, return sentinels typed as success" anti-pattern.
|
||
- **Effect logic in render** (`subscribe()` outside `useEffect`).
|
||
- **Fat controller / god object** (the 1205-line screen).
|
||
- **Shotgun surgery by construction** (forward/reverse duplication guarantees every change touches ≥2 files).
|
||
- **Config-by-comment** (base URLs selected by commenting/uncommenting lines in `ApiUrls.tsx`).
|
||
- **Boolean-flip refetch** (`setAllBoolean(reload:!prev.reload)` to trigger a parent `useEffect` instead of query invalidation).
|
||
- **Primitive obsession in state** (~25 discrete boolean `useState`s in `Ragistration.tsx` instead of a reducer/state machine).
|
||
|
||
## 7. Bottlenecks
|
||
|
||
- **Refetch storms** from `staleTime:0` + refetch-on-focus on dashboards/lists.
|
||
- **Whole-list re-renders** on every live bid tick (inline `renderItem`, unmemoized reverse card) — worsens as bidder lists grow.
|
||
- **Per-second `setState` timers** (`CountdownTimer` *and* the parallel `AuctionTimer`) re-rendering timer subtrees every second; two implementations doing it.
|
||
- **Nested non-virtualized `FlatList scrollEnabled={false}` inside `ScrollView`** (`TenderBidding.tsx:1056,1099`) renders all rows eagerly.
|
||
- **Synchronous blocking API call on the Splash screen** (registration-state fetch gates app entry for tender users, `Splash.tsx:45-107`).
|
||
- **Startup polyfills** (`Buffer`, `TextEncoder`) set synchronously; `QueryClient` recreated per render.
|
||
|
||
## 8. Tight Coupling
|
||
|
||
- **Transport layer knows about UI and navigation:** `apiCall` dispatches `setToken({})` on 401 (`Axios.ts:129`); `Socket.placeBid` calls `showSuccessAlert`/`showErrorAlert` from inside the service (`Socket.tsx:200-207`). Data/transport should emit events, not drive UI.
|
||
- **Screens coupled directly to the crypto implementation** (`TenderBidding` calls `encrypt`/`encryptWithRSA` inline with hardcoded ECB assumptions).
|
||
- **Redux token shape coupled across ≥3 readers** with different nesting (`token.token` vs `token.token.token`) — change one, break the others silently.
|
||
- **Components coupled to two different color modules**, so a rebrand touches both.
|
||
|
||
## 9. Hidden Dependencies
|
||
|
||
- **`jsencrypt` is in `devDependencies`** (`package.json:91`) but imported by runtime code (`encrypt.tsx`). A production install with `--production` (or a clean CI install) **breaks the sealed-bid crypto path**. This is a latent production outage.
|
||
- **`global.Buffer` / `global.TextEncoder`** monkey-patched at startup (`index.js:9-10`, `App.tsx:22`) — any code depending on them has an invisible ordering dependency on app bootstrap.
|
||
- **Socket token captured at hook-init** (`StartBidding.tsx:55`) — if the token refreshes mid-session, the socket keeps the stale one with no re-read.
|
||
- **`text-encoding`, `react-native-get-random-values`** are crypto polyfills whose absence would silently weaken/break encryption.
|
||
|
||
## 10. Single Points of Failure
|
||
|
||
- **The one signing key** — committed to git, so its secrecy is already lost; simultaneously the *only* thing standing between the app and forged updates.
|
||
- **`apiCall` / the default axios instance** — every feature funnels through it; its error-swallowing behavior means one backend contract change produces silent `undefined`s app-wide rather than a visible failure.
|
||
- **The single flat navigator + imperative Splash gate** — if Splash's registration-state call fails, the recovery path *wipes persisted state and forces re-login* (`Splash.tsx:103`); a backend hiccup logs everyone out.
|
||
- **`Constant/Variable.tsx` socket host** — hardcoded to a single origin; no failover.
|
||
|
||
---
|
||
|
||
## 11. Security Risks
|
||
|
||
> Full evidence in the security appendix; the highest-priority items:
|
||
|
||
| Severity | Risk | Evidence |
|
||
|----------|------|----------|
|
||
| 🔴 Critical | **Signing keystore committed to git** (`.gitignore` `*.keystore` missed it — no extension) + **release signed with debug key** (`storePassword 'android'`). Forgeable/ tamperable APKs. | `android/app/appKeyStore` (git-tracked, verified), `build.gradle:88-104` |
|
||
| 🔴 Critical | **Global request/response logger dumps JWTs, passwords, Aadhaar/PAN/OTP, and sealed-bid values to logcat in release** (no `__DEV__` gate). 378 `console.*` calls across 34 files, none stripped (no `transform-remove-console`). | `Axios/Axios.ts:47-99`, `Login.tsx:213-216`, `encrypt.tsx:30-155`, `TenderBidding.tsx:474,561` |
|
||
| 🔴 Critical | **Committed SonarQube auth token.** | `sonar-project.properties:13` (verified git-tracked) |
|
||
| 🟠 High | **App-wide cleartext HTTP permitted in release + no SSL pinning.** A hardcoded `http://` base with attached Bearer token also lives in `client.ts`. | `network_security_config.xml`, `AndroidManifest.xml:19`, `api/client.ts:6` |
|
||
| 🟠 High | **JWT + full login response persisted unencrypted in AsyncStorage.** Readable on rooted/backed-up devices. No Keychain/Keystore/EncryptedStorage. | `Redux/Store.js:20-24`, `Login.tsx:212` |
|
||
| 🟠 High | **Device binding is client-spoofable** (`getUniqueId()`/`getIpAddress()`, no Play Integrity attestation) and **silently falls back to fabricated GPS coords** on permission denial. This is the *fraud control* for bidding. | `DeviceBinding.tsx:47-86` |
|
||
| 🟠 High | **Weak sealed-bid crypto:** AES-**ECB** mode, SHA-256 key **truncated to 128 bits**, and the symmetric key is **echoed back to the server in plaintext alongside the ciphertext** (`randomKeyGenerated`) — adding no real confidentiality. | `encrypt.tsx:9-71`, `TenderBidding.tsx:546` |
|
||
| 🟡 Medium | **WebView KYC success detected by page-title string match** (spoofable); loads **raw server-supplied HTML** with JS enabled; logs Aadhaar callback URLs. | `KycWebViewScreen.tsx:85-143` |
|
||
| 🟡 Medium | **No root/tamper/Frida detection, no `FLAG_SECURE`** (sealed bids & Aadhaar OTP are screenshot-/screen-recordable and appear in the app-switcher thumbnail), **no ProGuard/R8** minification. | grep-confirmed absent; `build.gradle:60,104` |
|
||
| 🟡 Medium | **`loginWithoutDecryption` endpoints** + **`ResetPassword` sends new password as a URL query param** (`?newPassword=...`) — lands in server/proxy logs. | `apis.ts:29-46`, `ResetPassword.tsx:220` |
|
||
| 🟡 Medium | **Repo-committed bulk-user script with default password `P@ssword123`** targeting the live backend. | `scripts/create-users.js:10` |
|
||
|
||
## 12. Privacy Risks
|
||
|
||
- **Regulated PII logged in the clear.** Full Aadhaar number, PAN, GST, DOB are `console.log`-ed (`Ragistration.tsx:363`) and the **Aadhaar OTP is displayed in an `Alert` dialog** (`Ragistration.tsx:422`). Under UIDAI/Aadhaar Act and India's DPDP Act, logging full Aadhaar is a compliance exposure, not just a bug.
|
||
- **PII travels over a transport that permits cleartext** (§11) and through the global logger (§11) on every request.
|
||
- **No data-minimization or masking layer** anywhere — the app has no concept of "sensitive field."
|
||
- **Unjustified permissions:** `READ_MEDIA_AUDIO` (no audio feature) and always-on FINE+COARSE location whose only use is a device-binding geo-tag that fabricates coordinates anyway.
|
||
- **Cached profile PII** (from `getcontractorfulldetails`) may persist unencrypted via React Query + any persisted slice.
|
||
|
||
## 13. Scalability Risks
|
||
|
||
- **Team scalability** is the nearer-term problem than user scalability: no module boundaries, god-components, and duplicated screens mean two developers *cannot* work on bidding without colliding, and every change risks the forward/reverse drift.
|
||
- **No env/config abstraction** ⇒ dev/staging/prod require source edits and recompiles; you cannot run a QA build against staging without a code change. This blocks a release pipeline.
|
||
- **The error-swallowing data layer** means production incidents are invisible (no error surfaced, bad data cached as good) — you scale the user base but lose the ability to see failures.
|
||
- **No observability** (no crash reporting, no analytics, no structured logging) — at 10k+ users you'd be flying blind.
|
||
- **Backend coupling by string concatenation** (`${BASE_URL}path?query=`) with no client SDK/contract means backend changes break the app silently.
|
||
|
||
## 14. Performance & Battery Risks
|
||
|
||
- Refetch storms (§7), per-second timer re-renders (§7), whole-list re-renders on bid ticks (§7).
|
||
- **Leaked STOMP connections** (no unmount disconnect) keep reconnecting every 5s with 1s heartbeats — a measurable battery/data drain when a user leaves a bidding screen without killing the app.
|
||
- **No `getItemLayout`** on any list — every list pays layout measurement cost.
|
||
- **No release minification/shrinkResources** — larger APK, and no ABI splits/App Bundle so users download all architectures.
|
||
- **Duplicate heavy libs shipped:** `exceljs` **and** `xlsx`; `react-native-fs` **and** `react-native-blob-util`; two icon systems — dead weight in the bundle.
|
||
|
||
## 15. Maintainability Risks
|
||
|
||
- **~26k LOC, zero tests, no CI, lint/format enforced only by developer discipline** (no husky/lint-staged). There is no automated safety net for any refactor.
|
||
- **`.jsx`/`.js` files bypass TypeScript** entirely (`Store.js`, `MainStack.jsx`, `Routes.jsx`, `ImagePath.jsx`).
|
||
- **Duplication tax:** every bidding change is a 2-file (often 4-file) edit; the two color palettes and two component roots triple the surface for any visual change.
|
||
- **`any` everywhere** (navigation, selectors, socket callbacks, `apiCall` generics) — the compiler cannot help.
|
||
- **Dead code** (`AllUrls.tsx`, `DrawerStack`, `CustomDrawer`, generic hooks, `useLogin`, `userLogin`) increases cognitive load and hides which path is live.
|
||
|
||
---
|
||
|
||
## 16. AI System Evaluation
|
||
|
||
**Finding: there is no AI subsystem in this codebase.** No LLM/model integration, no agent/orchestration layer, no intent recognition, no task planning, no RAG/retrieval, no knowledge graph, no vector/embedding index, no voice pipeline, no local or cloud inference. Grep confirms none of the associated libraries or patterns are present.
|
||
|
||
I'm flagging this plainly because the brief framed the review around an "AI-powered operating system," and a Principal Engineer's job is to challenge the premise rather than invent findings. **An e-procurement bidding app does not need any of that to succeed, and bolting an "AI OS" onto the current foundation would be a strategic mistake** — you'd be building a second story on a cracked foundation (committed keys, cleartext transport, error-swallowing data layer).
|
||
|
||
If AI capabilities *are* a genuine roadmap goal, here is the honest prerequisite ordering and what each would require:
|
||
|
||
1. **Fix the foundation first** (§17–19). An AI feature that calls a cloud LLM over the current cleartext-permitted transport, logging prompts (which would contain tender/bid PII) through the global logger, is a data-leak generator.
|
||
2. **Realistic, high-value AI features for *this* domain** (in priority order):
|
||
- **Tender discovery & ranking** — semantic search over live tenders (embeddings + a vector index server-side; the app just calls a ranked-search endpoint). Low risk, high user value.
|
||
- **Document intelligence** — extract/summarize tender documents and envelope requirements (server-side LLM + RAG over the tender corpus; app renders results).
|
||
- **Bid-assist / anomaly detection** — flag unusual bid patterns or missing mandatory documents before submission (server-side).
|
||
- **Conversational support agent** — scoped, tool-using assistant answering "what tenders match my class?" via typed tool calls to existing endpoints.
|
||
3. **Architectural requirement:** all of the above should be **server-mediated** (model keys never on-device; PII never sent to a third-party model without consent + DPA). The mobile app's role is a thin, typed client to an AI *gateway* service — which is exactly the clean data-layer boundary §17 prescribes. **Build the clean API/domain layer, and AI features become just more endpoints behind it.**
|
||
|
||
**Verdict:** AI is a *future* concern gated on foundation work. Do not start it now.
|
||
|
||
---
|
||
|
||
## 17. Android System Evaluation
|
||
|
||
- **Build config is not release-ready.** No release keystore (debug-signed release), no minify/shrink/ProGuard, no ABI splits or App Bundle, hardcoded `versionCode 1`/`versionName 1.0` with no bump automation.
|
||
- **New Architecture + Hermes are on** (good, modern), and confirmed in native host code (`MainApplication.kt`). No custom native modules — everything is JS + autolinked libs, which keeps the native surface small but means device-binding/DSC/crypto get *no* native security guarantees (no Keystore-backed keys, no attestation).
|
||
- **Manifest:** app-wide cleartext, no orientation lock, single exported `MainActivity` with only MAIN/LAUNCHER (no deep links). `allowBackup=false` is correct.
|
||
- **Cross-platform:** iOS project exists and is set up (`ios/` with Podfile, AppDelegate.swift, PrivacyInfo.xcprivacy) — this is *not* Android-only, so all remediation must be applied per-platform (e.g. ATS on iOS mirrors the network-security-config fix).
|
||
- **Tooling gaps:** no tests, no CI/CD (`.github` contains only a Java-upgrade tooling artifact), stock ESLint/Prettier with no enforcement, committed Sonar token pointing at `localhost:9000` scanning `node_modules`.
|
||
|
||
## 18. Data Architecture Review
|
||
|
||
**Current:** string-concatenated endpoints off a hardcoded base, four transport styles, error-swallowing wrapper, mis-configured React Query, plaintext persisted auth, three backend hosts.
|
||
|
||
**Target (see §20):** one typed API client → a **domain/repository layer** (feature-scoped: `auth`, `tenders`, `auctions`, `bidding`, `kyc`, `profile`) → React Query hooks with correct keys/`staleTime`/invalidation → screens. Errors *thrown* and normalized once. Secrets in secure storage. Env-driven base URLs. This is the highest-leverage change in the whole document.
|
||
|
||
## 19. UX Architecture Review
|
||
|
||
- **Strong primitives, inconsistent application.** `AppText` + `useTheme` are good, but per-element inline styles + `color=` overrides dilute the tokens, and half the app uses the *other* palette/components.
|
||
- **No accessibility** (zero `accessibilityLabel`/`Role`/`Hint` — icon-only touchables and steppers are unusable with TalkBack), **no i18n** (hardcoded English + inline `₹`), **no responsive scaling** (fixed pixels), **no error boundary** (an unhandled render error crashes the app; warnings are globally silenced).
|
||
- **Inconsistent loading/empty/error states** — no shared wrapper; main lists lack `ListEmptyComponent`; most `catch` blocks only `console.log`, so React Query `isError` never reaches the user.
|
||
- **Forms are backwards:** the *simplest* screens (Login, SignUp) use Formik+Yup; the *hardest* (multi-step registration, envelope bidding) use hand-rolled `useState` state machines.
|
||
|
||
---
|
||
|
||
## 20. Recommended Target Architecture
|
||
|
||
**Principle:** feature-modular, layered, with dependencies pointing inward (Clean/Hexagonal). The mobile app is a *thin typed client* over well-defined domain boundaries.
|
||
|
||
```
|
||
app/
|
||
├─ core/ # cross-cutting, no feature knowledge
|
||
│ ├─ config/ env.ts # ← react-native-config; NO hardcoded hosts
|
||
│ ├─ http/ client.ts # ONE axios instance; throws on error
|
||
│ │ interceptors # auth (single token read), tracing, refresh
|
||
│ ├─ storage/ secure.ts # react-native-keychain for token; MMKV/AsyncStorage for prefs
|
||
│ ├─ crypto/ envelope.ts # AES-GCM (not ECB), key never echoed to server
|
||
│ ├─ realtime/ stomp.ts # subscribe in useEffect; disconnect on unmount
|
||
│ ├─ errors/ AppError.ts # normalized error type + ErrorBoundary
|
||
│ ├─ observability/ # crash reporting + structured, PII-scrubbed logging (DEV-gated)
|
||
│ └─ ui/ # ONE design system: AppText, Button, Input, Modal, List, tokens
|
||
├─ features/
|
||
│ ├─ auth/ { api, hooks, screens, state }
|
||
│ ├─ tenders/ { api, hooks, screens }
|
||
│ ├─ auctions/ { api, hooks, screens }
|
||
│ ├─ bidding/ { api, hooks, screens } # forward+reverse UNIFIED via config/props
|
||
│ ├─ kyc/ { api, hooks, screens }
|
||
│ └─ profile/ { api, hooks, screens }
|
||
├─ navigation/ RootNavigator # typed ParamList; Auth vs App stack split; deep linking
|
||
└─ App.tsx # QueryClient (module-const, tuned defaults) + ErrorBoundary + providers
|
||
```
|
||
|
||
**Key decisions & justifications:**
|
||
|
||
| Decision | Replaces | Principle |
|
||
|----------|----------|-----------|
|
||
| **One `apiClient` that throws typed `AppError`** | 4 stacks + error-swallowing | Single Responsibility; lets React Query do retry/error/cache; restores TypeScript safety |
|
||
| **`react-native-config` env files** (dev/staging/prod) | Hardcoded/commented hosts | 12-factor config; enables CI + QA builds without code edits |
|
||
| **`react-native-keychain` for token**, MMKV for prefs | Plaintext AsyncStorage | Defense in depth for auth/PII |
|
||
| **Typed navigation** (`RootStackParamList`) + **Auth/App stack split** | `any` + imperative Splash gate | Compile-time route safety; declarative auth gating; enables deep links |
|
||
| **Feature modules** (`api/hooks/screens` per domain) | Type-folders + god-components | High cohesion, low coupling; two devs work without collision |
|
||
| **Unify forward/reverse bidding** behind one config-driven screen | 90% duplicated forks | DRY; one place to fix bugs |
|
||
| **One design system** (`core/ui`), delete `src/Component` + one palette | Split-brain components/colors | Single source of visual truth |
|
||
| **AES-GCM, key wrapped by server RSA public key, never echoed back** | AES-ECB + echoed key | Actual confidentiality for sealed bids |
|
||
| **`transform-remove-console` + PII-scrubbing logger, DEV-gated** | 378 raw console calls | Privacy/compliance |
|
||
| **Server-mediated integrity** (Play Integrity / SafetyNet) for device binding | Client-spoofable IDs | Real anti-fraud |
|
||
| **ErrorBoundary + shared Loading/Empty/Error wrappers** | Per-screen ad-hoc + crashes | Consistent, resilient UX |
|
||
|
||
---
|
||
|
||
## 21. Migration Roadmap
|
||
|
||
Sequenced so each phase is shippable and de-risks the next. **Phase 0 is non-negotiable and blocks release.**
|
||
|
||
### Phase 0 — Security stop-the-bleed (days, do first)
|
||
1. **Rotate and remove signing keys from git.** Purge `appKeyStore` + `debug.keystore` from history (`git filter-repo`), rotate on Play Console if `appKeyStore` was the release key, move signing to `keystore.properties` (gitignored) / CI secrets. Create a real release signing config.
|
||
2. **Rotate the leaked Sonar token**, remove it from `sonar-project.properties`, fix `sonar.sources` to exclude `node_modules`.
|
||
3. **Gate/remove the global logger** behind `__DEV__` + add `babel-plugin-transform-remove-console` for release; scrub PII. Remove `TestScreen/DecryptionScreen` and the hardcoded key.
|
||
4. **Disable cleartext** (`usesCleartextTraffic=false`, restrictive network-security-config for release; iOS ATS) once backends are HTTPS. Fix/remove the `http://` `client.ts`.
|
||
5. **Move the token to Keychain**; stop persisting the full login response.
|
||
6. **Move `jsencrypt` to `dependencies`** (latent prod outage).
|
||
|
||
### Phase 1 — Data-layer consolidation (1–2 sprints)
|
||
7. Introduce `core/config` env + a **single throwing `apiClient`**; migrate feature by feature off `apiCall`/raw axios/`client.ts`. Delete `AllUrls.tsx` and the dead generic hooks.
|
||
8. Tune `QueryClient` defaults (`staleTime`, `retry`), fix query keys, add `enabled` guards and `invalidateQueries` on mutations. Move `QueryClient` to a module const.
|
||
9. Add typed `RootState`/`AppDispatch`; fix the token slice type; unify logout on `persistor.purge()` + Keychain clear + socket disconnect + query cache clear.
|
||
|
||
### Phase 2 — Structure & correctness (2–3 sprints)
|
||
10. Typed navigation + **Auth/App stack split**; move Splash gating into declarative navigator logic.
|
||
11. **Unify forward/reverse bidding** and the two card components into one config-driven module; move `subscribe` into `useEffect` with cleanup; disconnect on unmount.
|
||
12. Break up `TenderBidding`/`ProfileTab`/`Ragistration`: extract crypto, API, and validation into `core`/feature modules; convert registration to a reducer/state machine (or Formik).
|
||
13. Fix crypto (AES-GCM, no echoed key); add `FLAG_SECURE` to bidding/KYC; add Play Integrity to device binding.
|
||
|
||
### Phase 3 — Consistency & platform (2 sprints)
|
||
14. Collapse to **one design system + one palette**; delete `src/Component` and `Constant/Color` after migration. Add ErrorBoundary + shared state wrappers.
|
||
15. Add responsive scaling + accessibility labels; introduce i18n scaffolding (even if only `en` at first).
|
||
16. Remove duplicate deps (`xlsx` or `exceljs`; `fs` or `blob-util`; one icon lib); enable release minify/shrink + App Bundle + ABI splits; automate version bumps.
|
||
|
||
### Phase 4 — Quality gates (ongoing, start in Phase 1)
|
||
17. CI (GitHub Actions): install → typecheck → lint → test → build. Add husky + lint-staged.
|
||
18. Testing: unit tests for `core/http`, crypto, and domain hooks; component tests for the design system; a Detox smoke test for login→bid. Add crash reporting + analytics.
|
||
|
||
## 22. Prioritized Refactoring Plan (single ranked backlog)
|
||
|
||
| Rank | Item | Effort | Risk if skipped |
|
||
|------|------|--------|-----------------|
|
||
| 1 | Purge & rotate committed keys; real release signing | S | Critical — forgeable app |
|
||
| 2 | DEV-gate/strip logging; scrub PII | S | Critical — credential/PII leak |
|
||
| 3 | Rotate Sonar token | XS | Critical — exposed secret |
|
||
| 4 | HTTPS-only + kill cleartext + fix `client.ts` | S | High — MITM |
|
||
| 5 | Token → Keychain; stop persisting login response | S | High — PII at rest |
|
||
| 6 | `jsencrypt` → dependencies | XS | High — prod crypto outage |
|
||
| 7 | Single throwing `apiClient` + env config | M | Critical — invisible failures, no envs |
|
||
| 8 | React Query defaults + keys + invalidation | M | High — stale data, refetch storms |
|
||
| 9 | Typed navigation + Auth/App split | M | High — no auth enforcement |
|
||
| 10 | Unify bidding forward/reverse + socket cleanup | M | High — drift + leaks |
|
||
| 11 | Break up god-components | L | High — unmaintainable |
|
||
| 12 | Crypto (AES-GCM) + FLAG_SECURE + Play Integrity | M | High — weak sealed bids/fraud |
|
||
| 13 | One design system + one palette | L | Medium — visual drift |
|
||
| 14 | ErrorBoundary + shared states + a11y + i18n | M | Medium — UX/compliance |
|
||
| 15 | Dedup deps + release optimization + CI/CD + tests | M | Medium — quality/velocity |
|
||
|
||
## 23. Risk Assessment (residual, if nothing is done)
|
||
|
||
- **Compliance/legal:** logging full Aadhaar + storing PII in plaintext over cleartext-capable transport is a DPDP/UIDAI exposure — **the highest business risk**, not merely technical.
|
||
- **Financial fraud:** spoofable device binding + UI-only DSC + weak sealed-bid crypto undermine the integrity guarantees a tendering platform legally depends on.
|
||
- **Supply-chain/integrity:** committed + debug signing key means a malicious actor with repo access (or the public debug key) can distribute a trojaned "ProcureLinc."
|
||
- **Operational:** error-swallowing + no observability means production incidents are silent; you learn about outages from users, not dashboards.
|
||
- **Velocity:** duplication + god-components + no tests means feature velocity *decreases* as the app grows, and every bidding change risks a regression in the un-fixed twin.
|
||
|
||
## 24. Future Vision
|
||
|
||
Once the foundation is fixed, ProcureLinc is well-positioned:
|
||
|
||
- **Short term (foundation solid):** a secure, observable, typed, feature-modular app where two teams can build tenders and auctions in parallel; QA runs against staging without code edits; releases are automated and signed correctly.
|
||
- **Medium term:** offline-first for tender/auction browsing (React Query persistence + optimistic bid queueing over a resilient socket), full accessibility + i18n for a national user base, and a real design system that supports white-labeling for multiple procurement authorities.
|
||
- **Long term (only after the above):** the AI features in §16 — semantic tender discovery, document intelligence, bid-assist — added as *endpoints behind the clean data layer*, with all model access server-mediated and PII-governed. The "AI-powered OS" framing is better realized as **an intelligent procurement platform with a thin, secure mobile client**, not an AI runtime on the device.
|
||
|
||
**The single most important sentence in this report:** *Fix the security foundation and consolidate the data layer first; every other improvement — modularity, design system, testing, and eventually AI — compounds cleanly on top of those two, and none of them is safe or durable without them.*
|
||
|
||
---
|
||
|
||
### Appendix A — Evidence index (key files)
|
||
- Data/transport: `src/Axios/Axios.ts`, `src/api/client.ts`, `src/Services/ApiCall/{apis.ts,ApiUrls.tsx,Hooks.ts}`, `src/Hooks/use{Fetch,Create,Update,Delete}.ts`, `src/Constant/{Variable.tsx,AllUrls.tsx}`
|
||
- State: `src/Redux/Store.js`, `src/Redux/Slices/*`
|
||
- Realtime/crypto: `src/socket/Socket.tsx`, `src/Services/Encryption/encrypt.tsx`
|
||
- UI: `src/Screens/Tender/tenderBidding/TenderBidding.tsx`, `src/Screens/Bidding/{StartBidding,StartReverseBidding,Emd}.tsx` + `Component/{BiddingCard,ReverseBiddingCard}.tsx`, `src/Screens/Ragistration/Ragistration.tsx`, `src/Screens/Tender/LiveTender.tsx` (good template), `src/components/AppText.tsx`
|
||
- Navigation: `src/Navigation/{MainStack.jsx,Bottom.tsx,NavigationService.ts}`, `src/Screens/Splash/Splash.tsx`
|
||
- Android/build: `android/app/build.gradle`, `android/gradle.properties`, `android/app/src/main/{AndroidManifest.xml,res/xml/network_security_config.xml}`, `android/app/appKeyStore` (remove), `sonar-project.properties` (remove token)
|
||
- Security-sensitive: `src/Screens/Home/KycWebViewScreen.tsx`, `src/Screens/Tender/{DeviceBinding,DscBinding}.tsx`, `src/Screens/TestScreen/DecryptionScreen.tsx` (remove), `scripts/create-users.js`
|