Centralize Dr. Green API client, export condition mapping, and harden utilities#4
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e3a04c062e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
📝 WalkthroughWalkthroughThe PR extracts a mapping function to a shared module, narrows HTTP method types for improved safety, introduces a new authenticated Dr. Green API client with ECDSA signature generation and request handling, adds migration deadline validation, and migrates components from React Router to Next.js routing. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant callDrGreenAPI as callDrGreenAPI Function
participant generateDrGreenSignature as generateDrGreenSignature Function
participant DrGreenAPI as Dr. Green API
Client->>callDrGreenAPI: callDrGreenAPI(endpoint, options)
callDrGreenAPI->>callDrGreenAPI: Validate credentials (apiKey, secretKey)
alt Missing Credentials
callDrGreenAPI-->>Client: Throw MISSING_CREDENTIALS error
else Valid Credentials
callDrGreenAPI->>callDrGreenAPI: Build x-auth-apikey header
alt Non-GET Request
callDrGreenAPI->>generateDrGreenSignature: generateDrGreenSignature(payload, secretKey)
generateDrGreenSignature-->>callDrGreenAPI: Return ECDSA SHA-256 signature
callDrGreenAPI->>callDrGreenAPI: Add x-auth-signature header
end
callDrGreenAPI->>DrGreenAPI: HTTP Request (with headers & payload)
DrGreenAPI-->>callDrGreenAPI: HTTP Response
callDrGreenAPI->>callDrGreenAPI: Check response status (2xx?)
alt Non-2xx Status
callDrGreenAPI-->>Client: Throw error with response data
else Success (2xx)
alt validateSuccessFlag Enabled
callDrGreenAPI->>callDrGreenAPI: Verify data.success === 'true'
alt Success Flag Missing/False
callDrGreenAPI-->>Client: Throw error with fallback message
else Success Flag Present
callDrGreenAPI-->>Client: Return parsed response (type T)
end
else validateSuccessFlag Disabled
callDrGreenAPI-->>Client: Return parsed response (type T)
end
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
nextjs_space/lib/encryption.ts (2)
1-3: Duplicate crypto import will cause syntax/runtime error.The file imports
cryptotwice (lines 1 and 3), which will cause a parsing error.🐛 Proposed fix
import crypto from "crypto"; -import crypto from 'crypto'; - const ALGORITHM = 'aes-256-gcm';
36-72: Migration helpers are defined but not integrated withdecrypt.
DecryptOptionsandisMigrationAllowedare added butdecryptstill unconditionally returns original text for invalid formats (line 72). The PR summary states "fail on unexpected decrypt formats unless explicit migration flags/deadline provided," but the function signature wasn't updated to accept options.🔧 Suggested integration
-export function decrypt(text: string): string { +export function decrypt(text: string, options?: DecryptOptions): string { if (!text) return ""; const parts = text.split(":"); if (parts.length !== 3) { - // Fallback: If it's not in our format, return as is (maybe it wasn't encrypted yet) - // Or throw error. For migration safety, we can return null or error. - console.warn("Invalid encrypted format, returning original"); - return text; + if (isMigrationAllowed(options)) { + console.warn("Invalid encrypted format, returning original (migration mode)"); + return text; + } + throw new Error("Invalid encrypted format and migration not allowed"); }nextjs_space/app/api/consultation/submit/route.ts (1)
240-251: Remove or guard debug logging before production.These console.log statements expose sensitive operational details (API key existence, phone numbers, full payload with PII). Consider removing them or wrapping with a debug flag check.
🔧 Suggested fix
- console.log('\n=== DR GREEN API DEBUG ==='); - console.log('API URL:', `${drGreenApiUrl}/dapp/clients`); - console.log('Has API Key:', !!apiKey); - console.log('Has Secret Key:', !!secretKey); - console.log('\n=== PHONE FIELDS DEBUG ==='); - console.log('Raw phoneCode from form:', JSON.stringify(body.phoneCode)); - console.log('Raw phoneNumber from form:', JSON.stringify(body.phoneNumber)); - console.log('Cleaned phoneCode:', JSON.stringify(body.phoneCode.replace(/[^\+\d]/g, ''))); - console.log('Cleaned contactNumber:', JSON.stringify(body.phoneNumber.replace(/\D/g, ''))); - console.log('\n=== PAYLOAD ==='); - console.log(JSON.stringify(drGreenPayload, null, 2)); - console.log('\n===================\n'); + if (process.env.NODE_ENV === 'development') { + console.log('[DrGreen] Submitting to:', `${drGreenApiUrl}/dapp/clients`); + }nextjs_space/lib/lovable-converter.ts (2)
449-450: Overbroadto=replacement may break unrelated attributes.
content.replace(/to=/g, "href=")replaces every occurrence ofto=in the file, which could unintentionally modify:
- Non-Link JSX attributes (e.g.,
scrollTo=, custom props)- String literals containing "to="
🔧 Suggested targeted replacement
// Replace <Link to="..."> with <Link href="..."> - content = content.replace(/to=/g, "href="); + content = content.replace(/<Link\s+([^>]*)to=/g, '<Link $1href=');
475-486: Conflicting replacement logic in header refactor.The sequence is:
- Line 475-478: Removes all
react-router-domimports- Line 479-482: Tries to replace a specific import pattern (already removed)
- Line 483-486: Removes all
react-router-domimports againThe replacement at lines 479-482 will never match because line 475-478 already removed those imports. Reorder to replace specific patterns first, then remove remaining imports.
🔧 Suggested fix
- // Replace React Router imports - content = content.replace( - /import \{[^}]*\} from ["']react-router-dom["'];?/g, - "", - ); + // Replace specific React Router imports with Next.js equivalents first content = content.replace( /import \{ Link, useLocation \} from ["']react-router-dom["'];?/g, "import Link from 'next/link';\nimport { usePathname } from 'next/navigation';", ); + + // Remove any remaining react-router-dom imports content = content.replace( /import \{[^}]*\} from ["']react-router-dom["'];?/g, '' );
🧹 Nitpick comments (2)
nextjs_space/app/api/consultation/submit/route.ts (1)
13-33: Consider using the centralizedgenerateDrGreenSignaturefromdrgreen-api-client.ts.This local
generateSignatureduplicates the logic now available in@/lib/drgreen-api-client.ts. Given the PR's goal to centralize Dr. Green API client code, consider importing and usinggenerateDrGreenSignatureinstead.nextjs_space/lib/drgreen-api-client.ts (1)
17-34: Use ES module import forcryptoand add error handling.Two issues:
require('crypto')should be a top-level ES import for consistency with modern Node.js/Next.js patternssign.sign()can throw (e.g., invalid key format) without contextual error wrapping🔧 Suggested improvement
Add at top of file:
import crypto from 'crypto';Then update the function:
export function generateDrGreenSignature(payload: string, secretKey: string): string { - const crypto = require('crypto'); - let privateKeyPEM = secretKey; if (!secretKey.includes('BEGIN PRIVATE KEY')) { try { privateKeyPEM = Buffer.from(secretKey, 'base64').toString('utf-8'); } catch (error) { // Keep original key if not base64-encoded. } } - const sign = crypto.createSign('SHA256'); - sign.update(payload); - sign.end(); - - return sign.sign(privateKeyPEM).toString('base64'); + try { + const sign = crypto.createSign('SHA256'); + sign.update(payload); + sign.end(); + return sign.sign(privateKeyPEM).toString('base64'); + } catch (error) { + throw new Error(`Failed to generate Dr. Green signature: ${error instanceof Error ? error.message : 'Unknown error'}`); + } }
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
nextjs_space/app/api/consultation/submit/route.tsnextjs_space/lib/doctor-green-api.tsnextjs_space/lib/drgreen-api-client.tsnextjs_space/lib/encryption.tsnextjs_space/lib/lovable-converter.ts
🔇 Additional comments (6)
nextjs_space/app/api/consultation/submit/route.ts (1)
8-8: LGTM!Good refactor to centralize the medical condition mapping into a shared module.
nextjs_space/lib/lovable-converter.ts (1)
435-438: LGTM!Good use of a targeted regex to handle the specific
{ Link, useLocation }import pattern from react-router-dom.nextjs_space/lib/doctor-green-api.ts (1)
63-68: LGTM!Good type safety improvement by narrowing
methodfromstringto specific HTTP method literals.nextjs_space/lib/drgreen-api-client.ts (3)
1-12: LGTM on interface and defaults.The interface is well-typed with appropriate optional fields, and the environment variable fallback for the API URL is sensible.
66-68: Empty body on non-GET requests may cause unexpected behavior.When
bodyisundefinedor empty,payloadbecomes an empty string. For non-GET requests, this still generates a signature over an empty string, but line 73 sendsundefinedas body (since'' || undefinedisundefined). This inconsistency could cause signature verification failures if the API expects an empty JSON object.Consider whether non-GET requests without a body should send
'{}'or if the signature should be skipped for empty payloads.
86-88: The string'true'check is consistent across all Dr. Green API client implementations; verify against official Dr. Green API documentation.The comparison
data?.success !== 'true'(string) appears intentional—the same pattern is used consistently indrgreen-orders.tsanddrgreen-cart.ts. This differs from internal API endpoints in this codebase, which returnsuccess: true(boolean), suggesting the external Dr. Green API returnssuccessas a string. Confirm this matches the actual Dr. Green API response format by consulting their official API documentation.
Motivation
Description
mapMedicalConditionsForDrGreeninlib/dr-green-mapping.tsand removed the duplicate implementation in the consultation submit route, which now imports the shared function viaimport { mapMedicalConditionsForDrGreen } from '@/lib/dr-green-mapping'.lib/drgreen-api-client.tsthat implementsgenerateDrGreenSignatureandcallDrGreenAPI, and refactoredlib/drgreen-cart.ts,lib/drgreen-orders.ts, andlib/doctor-green-api.tsto use it (preserves signing algorithm and response checks); switched these callers to the canonicalDOCTOR_GREEN_API_URLenv var fallback.orders.createanddrGreenCart.deleteManyin a singleprisma.$transaction, and clearing the remote Dr. Green cart after the transaction commits.lib/encryption.tsby removing theNEXT_PUBLICfallback for the key, failing on unexpected decrypt formats unless an explicit migration flag/deadline is provided viadecrypt(text, { allowUnencryptedMigration, migrationDeadline })andENCRYPTION_MIGRATION_DEADLINE.lib/webhook.tsto avoid throwing on malformed signatures by converting toBuffers, checking length equality, and only then callingcrypto.timingSafeEqual.lib/lovable-converter.tsso the specificLink/useLocationimport is handled before the generic removal and replaced only for<Link ... to=>occurrences (uses a targeted regex), preventing overbroad replacements.lib/queue.tsand masked/secured tenant Dr. Green keys in tenant settings, encrypting new keys on update (lib/tenant-admin/settings/route.ts) and adding an audit log entry viacreateAuditLogwhen settings are changed.lib/tenant-context.ts, set tenant context inlib/tenant.ts(middleware header flow setsx-tenant-*), and wired a Prisma request middleware inlib/db.tsto automatically scope reads/writes to tenant-scoped models when a tenant context exists.lib/rate-limit.ts, made the public APIasync(checkRateLimit/getRateLimitStatus) and usedINCR+PEXPIREsemantics to preserve fixed-window behavior.findUniqueemail lookups withfindFirstplus normalizedemail.toLowerCase()orid-based lookups for authenticated operations across several API routes and server pages.Testing
Codex Task
Summary by CodeRabbit
Refactor
Chores
✏️ Tip: You can customize this high-level summary in your review settings.