Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Warning Rate limit exceeded
To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthrough인증(회원가입/로그인) 및 XRPL 지갑/암호화 기능과 Redis 세션이 추가되고, Buyer/Company 스키마가 users 모듈로 통합되며 Admin/Insights/Consultants 관련 모듈·컨트롤러·서비스·테스트가 제거됩니다. 여러 컨트롤러/서비스/스키마 경로와 테스트가 대규모로 변경되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Client
participant AuthController
participant AuthService
participant XrplService
participant Database
participant Scheduler
Client->>AuthController: POST /auth/register/seller (DTO)
AuthController->>AuthService: registerSeller(dto)
AuthService->>Database: check User.exists(email)
Database-->>AuthService: available
AuthService->>XrplService: generateWallet()
XrplService-->>AuthService: wallet {address, seed, publicKey}
AuthService->>XrplService: encrypt(seed)
XrplService-->>AuthService: encryptedSeed
AuthService->>Database: save user/company (PENDING_ACTIVATION)
AuthService-->>AuthController: respond created
par Async funding
AuthService->>XrplService: fundAccount(wallet)
XrplService->>XrplService: ensure connected
alt success
XrplService-->>AuthService: ok
AuthService->>Database: update status ACTIVE
else failure
XrplService-->>AuthService: error
AuthService->>Database: update status FAILED_ACTIVATION
end
end
Scheduler->>AuthService: retryFailedActivations()
AuthService->>Database: find FAILED_ACTIVATION docs
Database-->>AuthService: [docs]
AuthService->>XrplService: decrypt(encryptedSeed)
XrplService-->>AuthService: seed
AuthService->>XrplService: fundAccount(wallet)
XrplService-->>AuthService: result
AuthService->>Database: update status accordingly
Client->>AuthController: POST /auth/login (email,password)
AuthController->>AuthService: login(dto)
AuthService->>Database: find user (include password)
Database-->>AuthService: user with hashed password
AuthService->>AuthService: bcrypt.compare
alt match
AuthService-->>AuthController: user info
AuthController->>Client: set session {userId,type}, respond 200
else mismatch
AuthService-->>AuthController: UnauthorizedException
AuthController-->>Client: 401
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
시
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 18
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/modules/matches/matches.service.ts (1)
50-70:⚠️ Potential issue | 🟠 Major필드 전환 과도기 호환성이 빠져 매칭 점수가 왜곡될 수 있습니다.
현재는
company.exportItems만 사용합니다. 그런데 기존 데이터/테스트 픽스처(src/modules/matches/matches.service.spec.tsLine 32-41)는offerings를 사용하고 있어, 백필 완료 전까지needs매칭이 과소평가될 수 있습니다.수정 예시 (과도기 호환)
- const companyExportItems = toSet(company.exportItems); + const companyExportItems = toSet( + Array.isArray(company.exportItems) && company.exportItems.length > 0 + ? company.exportItems + : company.offerings, + ); @@ - reasons.push(`needs-exportItems overlap x${needMatches}`); + reasons.push(`needs-exportItems overlap x${needMatches}`);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/matches/matches.service.ts` around lines 50 - 70, The matching logic only uses company.exportItems (variable companyExportItems) when computing needMatches against buyerNeeds via intersectCount, but legacy fixtures/tests use company.offerings so needs matches are undercounted; update the code that builds companyExportItems (or the place where intersectCount is called) to include both sources (exportItems and offerings) by normalizing/merging them into a single set before calling intersectCount (reference symbols: companyExportItems, company.exportItems, company.offerings, intersectCount, buyerNeeds) so the score and reasons correctly account for both old and new fields during the migration.src/modules/buyers/buyers.service.ts (1)
27-30:⚠️ Potential issue | 🟡 Minor
profileText필드는 스키마에 존재하지 않음User 및 Buyer 스키마에
profileText필드가 정의되어 있지 않습니다. 이 검색 조건은 어떤 문서도 매칭하지 않습니다.companyIntroduction또는productIntroduction필드를 사용해야 합니다.수정 제안
filter.$or = [ { name: { $regex: q, $options: "i" } }, - { profileText: { $regex: q, $options: "i" } }, + { companyIntroduction: { $regex: q, $options: "i" } }, + { productIntroduction: { $regex: q, $options: "i" } }, ];🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/buyers/buyers.service.ts` around lines 27 - 30, The search filter currently uses a non-existent field profileText in the filter.$or clause inside buyers.service.ts, so replace that clause to search actual schema fields; update the $or array to use companyIntroduction and productIntroduction (e.g., { companyIntroduction: { $regex: q, $options: "i" } } and { productIntroduction: { $regex: q, $options: "i" } }) alongside the existing name regex so the filter.$or (and any function that builds it) matches real Buyer/User schema fields.src/modules/companies/dto/create-company.dto.ts (1)
43-48:⚠️ Potential issue | 🟠 Major
CreateCompanyDto의 optionality가 스키마와 맞지 않습니다.
exportItems,companyIntroduction,productIntroduction는 스키마에서 required인데 DTO에서는 optional이라 요청이 validator를 통과한 뒤 Mongoose 단계에서야 실패할 수 있습니다. 생성 DTO라면 여기서도 필수로 막아 400 응답으로 일관되게 처리하는 편이 낫습니다.Also applies to: 57-67
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/companies/dto/create-company.dto.ts` around lines 43 - 48, The DTO marks exportItems, companyIntroduction, and productIntroduction as optional but the Mongoose schema requires them; update CreateCompanyDto to make these properties required by removing `@IsOptional`() and ensuring their validation decorators reflect required fields (keep `@IsArray/`@IsString/@IsString as appropriate and keep `@Transform` on exportItems), so validation fails early with a 400 instead of deferring to Mongoose; specifically modify the exportItems, companyIntroduction, and productIntroduction properties in CreateCompanyDto to be non-optional and validated accordingly.
🧹 Nitpick comments (4)
src/main.ts (2)
24-30: Redis 설정 접근 방식 불일치.
env.config.ts에redis.host,redis.port,redis.password설정이 정의되어 있지만, 여기서는process.env를 직접 읽고 있습니다.ConfigService를 통해 일관되게 접근하는 것이 좋습니다.♻️ ConfigService 사용 제안
const redisClient = createClient({ socket: { - host: process.env.REDIS_HOST || "localhost", - port: Number(process.env.REDIS_PORT) || 6379, + host: configService.get<string>("redis.host") || "localhost", + port: configService.get<number>("redis.port") || 6379, }, - password: process.env.REDIS_PASSWORD || "", + password: configService.get<string>("redis.password") || undefined, });참고:
password가 빈 문자열일 경우 일부 Redis 설정에서 인증 오류가 발생할 수 있으므로, 비밀번호가 없을 때는undefined를 전달하는 것이 안전합니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main.ts` around lines 24 - 30, The Redis client is created by reading process.env directly; switch to using the ConfigService (or the existing env.config.ts values) to keep configuration consistent: replace direct env access in the createClient call (where redisClient is created) with calls to ConfigService.get('redis.host'), ConfigService.get('redis.port'), and ConfigService.get('redis.password') (or the corresponding keys from env.config.ts), and ensure you pass undefined for password when the config value is an empty string so an empty password is not sent to createClient.
40-45: 로컬 개발 환경에서secure: true주의.
secure: true는 HTTPS 연결에서만 쿠키가 전송되므로, 로컬 HTTP 개발 환경에서는 세션이 동작하지 않습니다. 환경에 따라 조건부로 설정하는 것이 좋습니다.♻️ 환경별 조건부 설정 제안
+ const isProduction = configService.get<string>("NODE_ENV") === "production"; + app.use( session({ store: new RedisStore({ client: redisClient, ttl: sessionTtl }), secret: sessionSecret, resave: false, saveUninitialized: false, cookie: { httpOnly: true, - secure: true, - sameSite: "none", + secure: isProduction, + sameSite: isProduction ? "none" : "lax", maxAge: sessionTtl * 1000, }, }), );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main.ts` around lines 40 - 45, The cookie configuration currently forces secure: true which breaks sessions on local HTTP; update the session cookie object (the cookie: { ... } in your session setup where sessionTtl is used) to set secure conditionally (e.g. secure = process.env.NODE_ENV === 'production' or an isProduction flag) and, if your app is behind a proxy in production, ensure the Express app trust proxy setting is enabled (e.g. app.set('trust proxy', 1)) so secure cookies work correctly; keep maxAge and sameSite behavior but only force secure in production.src/modules/users/schemas/company.schema.ts (1)
18-19:location서브도큐먼트의 타입 안전성 개선 권장
location필드의 기본값이 빈 객체{}이지만, TypeScript 타입은city,state,country가 모두 필수string으로 정의되어 있습니다. 실제 런타임에서는 이 필드들이undefined일 수 있어 타입 불일치가 발생할 수 있습니다.♻️ 타입 안전성을 위한 수정 제안
- `@Prop`({ type: { city: String, state: String, country: String }, default: {} }) - location: { city: string; state: string; country: string }; + `@Prop`({ type: { city: String, state: String, country: String }, default: {} }) + location?: { city?: string; state?: string; country?: string };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/users/schemas/company.schema.ts` around lines 18 - 19, The Prop for 'location' uses default: {} but TypeScript declares required city/state/country strings, causing a type mismatch; update the schema so runtime defaults match the TS type by either (A) making the TS type optional/partial (e.g. change the property to location?: { city?: string; state?: string; country?: string } and/or mark fields optional) or (B) provide concrete defaults for each subfield via the decorator (e.g. set default: { city: '', state: '', country: '' } or use per-field defaults) so that the '@Prop({ type: { city: String, state: String, country: String }, default: {} })' and the 'location' property are consistent.src/modules/auth/auth.service.spec.ts (1)
167-174: DTO 타입 캐스팅 확인 필요
SELLER_DTO as any타입 캐스팅이 사용되고 있습니다. 실제RegisterSellerDto에 필수 필드가 누락된 것인지, 아니면 테스트 목적상 의도적인 것인지 확인이 필요합니다.테스트 DTO가 실제 DTO 타입과 일치하도록 수정하거나, 의도적인 캐스팅이라면 주석으로 명시하는 것을 권장합니다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/auth/auth.service.spec.ts` around lines 167 - 174, The test is casting SELLER_DTO as any when calling service.registerSeller which hides missing required fields from the real RegisterSellerDto; update the test to supply a SELLER_DTO that matches the RegisterSellerDto shape (add any missing required properties) and remove the unsafe "as any" cast, or if the cast is intentional for a negative/partial test, replace it with an explicit comment above the test explaining why the DTO is partial and keep assertions around userModel.exists and companyModel._instance.save unchanged; ensure the test calls service.registerSeller with a correctly-typed DTO so TypeScript will catch mismatches.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.coderabbit.yaml:
- Line 7: The config key is wrong: replace the singular auto_review.draft with
the schema-correct plural auto_review.drafts; open the .coderabbit.yaml and
rename the property from "draft" to "drafts" (keeping the boolean value true) so
the validator recognizes it and draft PRs are included by the auto_review logic.
- Around line 9-12: The YAML contains an unsupported field
reviews.review_instructions; remove or relocate it by deleting the
review_instructions node under reviews and adding its text under a top-level
tone_instructions key instead (ensure you preserve the multi-line string value);
update any schema/validation logic that expects reviews to only contain allowed
child fields so the file validates correctly and consumers read the new
tone_instructions value.
In `@src/app.module.ts`:
- Line 17: 현재 app.module.ts의 envFilePath 설정이 process.env.NODE_ENV ||
"development"로 기본값을 "development"로 두어 NODE_ENV 미설정 시 `.env.development`가 우선
로드됩니다; 수정하려면 envFilePath 설정을 조건문으로 바꿔 process.env.NODE_ENV가 정의된 경우에만
[`.env.${process.env.NODE_ENV}`, '.env']를 사용하고, 미정의일 때는 단독으로 ['.env']만 사용하도록
변경하여 잘못된 운영 설정 로드를 방지하세요; 관련 위치는 app.module.ts의 envFilePath 배열 설정을 수정하면 됩니다.
In `@src/common/decorators/current-user.decorator.ts`:
- Around line 10-12: current-user.decorator.ts currently assumes req.session
exists (ctx.switchToHttp().getRequest()) and returns { userId:
req.session.userId, type: req.session.type }; update the decorator to first
check req.session (and req.session.userId) and if missing throw an
UnauthorizedException instead of allowing a TypeError/500; otherwise return the
same object so callers of the decorator receive { userId, type } as before.
In `@src/common/guards/session.guard.ts`:
- Around line 12-14: The guard currently only checks req.session?.userId and
allows sessions with a missing session.type; update the validation in the
session guard to require both req.session?.userId and req.session?.type (or
equivalent role/type field) before allowing access and throw the same
UnauthorizedException("Login required") when either is missing so downstream
authorization logic has a stable authenticated session shape.
In `@src/config/env.config.ts`:
- Around line 72-76: The redis.port value is left as a string from process.env
and should be converted to a number like the other numeric env vars; update the
redis.port assignment in env.config.ts (the redis object) to use
Number(process.env.REDIS_PORT) with a fallback of 6379 so the final type is a
number (e.g., replace the current process.env.REDIS_PORT || 6379 with
Number(process.env.REDIS_PORT) || 6379).
- Around line 5-8: The default MongoDB URI in mongodb.uri is missing the
protocol prefix; update the default value used in src config (mongodb.uri in
env.config.ts) from "localhost:27017" to include the MongoDB protocol (e.g.,
"mongodb://localhost:27017") so any fallback connection string is valid when
process.env.MONGODB_URI is unset.
In `@src/main.ts`:
- Around line 20-21: Remove the non-null assertions on sessionSecret and
sessionTtl returned by configService.get and add explicit validation: call
configService.get for "session.secret" and "session.ttl", check if either is
undefined/null (or invalid type), and if so throw a clear startup Error
indicating which config key(s) are missing; otherwise continue using the
validated values. Reference the variables sessionSecret and sessionTtl and the
call site using configService.get to implement the guard and descriptive error.
In `@src/modules/auth/auth.controller.spec.ts`:
- Around line 122-127: The logout test fails because it doesn't pass a mocked
response object into controller.logout and asserts the wrong success message;
update the test to call controller.logout with both req and a mocked res that
has clearCookie as a jest.fn and ensure req.session.destroy is a jest.fn calling
its callback, then assert that req.session.destroy and res.clearCookie have been
called and that the returned value (or response behavior) matches the
controller.logout implementation (use the actual success message returned by
logout).
In `@src/modules/auth/auth.controller.ts`:
- Around line 24-27: Update the Swagger ApiResponse for
duplicate-email/validation failures in auth.controller.ts to match the actual
exception: change the status code from 400 to 409 for the responses documenting
ConflictException thrown by registerSeller() and registerBuyer(); locate the
`@ApiResponse` blocks associated with registerSeller and registerBuyer and replace
the 400 status (and adjust description text if needed) so the documented status
code (409) matches the service behavior.
- Around line 62-71: 로그아웃 핸들러 logout(`@Req`() req, `@Res`({ passthrough: true })
res: Response)에서 req.session.destroy 콜백 후 res가 undefined일 경우
res.clearCookie("connect.sid")에서 TypeError가 발생하므로, 콜백 내부에서 res가 존재하고
clearCookie가 함수인지 안전검사한 뒤에만 호출하도록 방어 코드를 추가하고(예: if (res && typeof
res.clearCookie === "function") ...), 세션 파기 실패와 성공 경로 모두에서 적절히 resolve/reject
하도록 유지하여 직접 호출하는 테스트나 호출부에서 res를 넘기지 않아도 안전하게 동작하도록 수정하세요.
- Around line 51-55: In the login method of AuthController (async login in
src/modules/auth/auth.controller.ts) replace the direct writes to req.session
with a session regeneration flow: after successful authentication call
req.session.regenerate (or await a promisified regenerate) to create a new
session, then set req.session.userId = user._id.toString() and req.session.type
= user.type, and only then return the success response; ensure you handle errors
from regenerate (reject/throw or return a 500) so failures don’t leave the user
in an inconsistent state.
In `@src/modules/auth/auth.service.ts`:
- Around line 174-187: The queries that populate failedSellers and failedBuyers
use .lean() but do not include the non-selected wallet.seed field, so retryOne
(which calls this.xrplService.decrypt(doc.wallet.seed)) receives undefined;
update the two queries (this.companyModel.find and this.buyerModel.find) to
explicitly include the hidden field by chaining .select("+wallet.seed") before
.lean(), and add a small defensive check inside retryOne (e.g., verify
doc.wallet?.seed exists) to skip/log and avoid calling this.xrplService.decrypt
when seed is missing.
In `@src/modules/auth/dto/register.dto.ts`:
- Around line 114-117: RegisterSellerDto's websiteUrl property only has
`@IsString`() so invalid URLs and overly long values can be saved; update the
websiteUrl field to match RegisterBuyerDto.websiteUrl by replacing/augmenting
`@IsString`() with the same URL and length validators used in RegisterBuyerDto
(e.g., add `@IsUrl`(...) with the same protocols/options and `@MaxLength`(...) used
there) so protocol and max-length checks are consistent across buyer and seller
DTOs.
In `@src/modules/partners/partners.service.ts`:
- Around line 385-387: The code maps the source body to companyIntroduction
(location/companyIntroduction/websiteUrl) but the country keyword re-scoring
logic still reads item.profileText, causing the rescoring to fail; update the
code so the rescoring uses the same field (use item.content or
companyIntroduction) instead of item.profileText — i.e., either set
item.profileText = item.content before the country-keyword logic or change the
rescoring references from item.profileText to item.content/companyIntroduction;
apply the same fix for the other occurrence referenced (the block around lines
393-401).
- Line 179: The code uses process.env.ATLAS_VECTOR_INDEX directly for the vector
index (the index: property in the vector search options in partners.service.ts),
causing a runtime error if the env var is missing; change it to use a safe
fallback like process.env.ATLAS_VECTOR_INDEX || "vector_index" (same pattern as
matches.service.ts) so the vector search still has a default index value when
the environment variable is unset.
In `@src/modules/payments/xrpl.service.ts`:
- Around line 63-72: The connect() method can be called concurrently (e.g., from
retryFailedActivations() when multiple fundAccount() calls run in parallel)
which can create duplicate websocket connections; fix by introducing an
in-flight connect promise (e.g., this.connectPromise) inside the XRPL service so
connect() first returns/awaits an existing this.connectPromise if present,
otherwise sets this.connectPromise = (async () => { create client if missing,
await client.connect(), log, return client })(); ensure you clear or reset
this.connectPromise on success or failure (and propagate errors) so subsequent
calls can retry; update references to this.client/isConnected() accordingly to
rely on the serialized connect() behavior.
In `@test/xrpl.e2e-spec.ts`:
- Around line 23-37: The test currently depends on the live XRPL testnet because
beforeAll builds a Test.createTestingModule with ConfigModule.forRoot providing
real xrpl.wsUrl and XrplService, which makes CI flaky; modify the test to avoid
external network calls by making it opt-in or mocked: either gate the beforeAll
with an environment flag (e.g., process.env.RUN_XRPL_E2E) and call
jest.skip/return early when not set, or replace the real XrplService with a
mocked provider in the Test.createTestingModule (provide a mock for XrplService
or override the xrpl.wsUrl to a local stub) so CI runs stable; apply same change
to the other E2E blocks referenced (lines ~92-122) that construct the module or
call live faucet.
---
Outside diff comments:
In `@src/modules/buyers/buyers.service.ts`:
- Around line 27-30: The search filter currently uses a non-existent field
profileText in the filter.$or clause inside buyers.service.ts, so replace that
clause to search actual schema fields; update the $or array to use
companyIntroduction and productIntroduction (e.g., { companyIntroduction: {
$regex: q, $options: "i" } } and { productIntroduction: { $regex: q, $options:
"i" } }) alongside the existing name regex so the filter.$or (and any function
that builds it) matches real Buyer/User schema fields.
In `@src/modules/companies/dto/create-company.dto.ts`:
- Around line 43-48: The DTO marks exportItems, companyIntroduction, and
productIntroduction as optional but the Mongoose schema requires them; update
CreateCompanyDto to make these properties required by removing `@IsOptional`() and
ensuring their validation decorators reflect required fields (keep
`@IsArray/`@IsString/@IsString as appropriate and keep `@Transform` on exportItems),
so validation fails early with a 400 instead of deferring to Mongoose;
specifically modify the exportItems, companyIntroduction, and
productIntroduction properties in CreateCompanyDto to be non-optional and
validated accordingly.
In `@src/modules/matches/matches.service.ts`:
- Around line 50-70: The matching logic only uses company.exportItems (variable
companyExportItems) when computing needMatches against buyerNeeds via
intersectCount, but legacy fixtures/tests use company.offerings so needs matches
are undercounted; update the code that builds companyExportItems (or the place
where intersectCount is called) to include both sources (exportItems and
offerings) by normalizing/merging them into a single set before calling
intersectCount (reference symbols: companyExportItems, company.exportItems,
company.offerings, intersectCount, buyerNeeds) so the score and reasons
correctly account for both old and new fields during the migration.
---
Nitpick comments:
In `@src/main.ts`:
- Around line 24-30: The Redis client is created by reading process.env
directly; switch to using the ConfigService (or the existing env.config.ts
values) to keep configuration consistent: replace direct env access in the
createClient call (where redisClient is created) with calls to
ConfigService.get('redis.host'), ConfigService.get('redis.port'), and
ConfigService.get('redis.password') (or the corresponding keys from
env.config.ts), and ensure you pass undefined for password when the config value
is an empty string so an empty password is not sent to createClient.
- Around line 40-45: The cookie configuration currently forces secure: true
which breaks sessions on local HTTP; update the session cookie object (the
cookie: { ... } in your session setup where sessionTtl is used) to set secure
conditionally (e.g. secure = process.env.NODE_ENV === 'production' or an
isProduction flag) and, if your app is behind a proxy in production, ensure the
Express app trust proxy setting is enabled (e.g. app.set('trust proxy', 1)) so
secure cookies work correctly; keep maxAge and sameSite behavior but only force
secure in production.
In `@src/modules/auth/auth.service.spec.ts`:
- Around line 167-174: The test is casting SELLER_DTO as any when calling
service.registerSeller which hides missing required fields from the real
RegisterSellerDto; update the test to supply a SELLER_DTO that matches the
RegisterSellerDto shape (add any missing required properties) and remove the
unsafe "as any" cast, or if the cast is intentional for a negative/partial test,
replace it with an explicit comment above the test explaining why the DTO is
partial and keep assertions around userModel.exists and
companyModel._instance.save unchanged; ensure the test calls
service.registerSeller with a correctly-typed DTO so TypeScript will catch
mismatches.
In `@src/modules/users/schemas/company.schema.ts`:
- Around line 18-19: The Prop for 'location' uses default: {} but TypeScript
declares required city/state/country strings, causing a type mismatch; update
the schema so runtime defaults match the TS type by either (A) making the TS
type optional/partial (e.g. change the property to location?: { city?: string;
state?: string; country?: string } and/or mark fields optional) or (B) provide
concrete defaults for each subfield via the decorator (e.g. set default: { city:
'', state: '', country: '' } or use per-field defaults) so that the '@Prop({
type: { city: String, state: String, country: String }, default: {} })' and the
'location' property are consistent.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 2032ce31-c2cc-4d7e-a795-b39b71cfb497
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (74)
.coderabbit.yaml.env.example.gitignoredocker-compose.ymlpackage.jsonsrc/app.module.tssrc/common/decorators/current-user.decorator.tssrc/common/guards/session.guard.tssrc/common/schemas/wallet-info.schema.tssrc/config/env.config.tssrc/main.tssrc/modules/admin/admin.controller.spec.tssrc/modules/admin/admin.controller.tssrc/modules/admin/admin.module.tssrc/modules/admin/admin.service.spec.tssrc/modules/admin/admin.service.tssrc/modules/admin/dto/audit-logs-query.dto.tssrc/modules/admin/dto/list-payments-query.dto.tssrc/modules/admin/dto/match-logs-query.dto.tssrc/modules/admin/dto/payment-stats-query.dto.tssrc/modules/admin/guards/admin-token.guard.spec.tssrc/modules/admin/guards/admin-token.guard.tssrc/modules/admin/schemas/audit-log.schema.tssrc/modules/auth/auth.controller.spec.tssrc/modules/auth/auth.controller.tssrc/modules/auth/auth.module.tssrc/modules/auth/auth.service.spec.tssrc/modules/auth/auth.service.tssrc/modules/auth/dto/login.dto.tssrc/modules/auth/dto/register.dto.tssrc/modules/buyers/buyers.controller.spec.tssrc/modules/buyers/buyers.controller.tssrc/modules/buyers/buyers.module.tssrc/modules/buyers/buyers.service.spec.tssrc/modules/buyers/buyers.service.tssrc/modules/buyers/dto/create-buyer.dto.tssrc/modules/buyers/dto/update-buyer.dto.tssrc/modules/buyers/schemas/buyer.schema.tssrc/modules/companies/companies.module.tssrc/modules/companies/companies.service.spec.tssrc/modules/companies/companies.service.tssrc/modules/companies/company-images.controller.spec.tssrc/modules/companies/company-images.controller.tssrc/modules/companies/company-images.service.spec.tssrc/modules/companies/company-images.service.tssrc/modules/companies/dto/create-company.dto.tssrc/modules/companies/dto/upload-image.dto.tssrc/modules/companies/schemas/company.schema.tssrc/modules/consultants/consultants.controller.spec.tssrc/modules/consultants/consultants.controller.tssrc/modules/consultants/consultants.module.tssrc/modules/consultants/consultants.service.spec.tssrc/modules/consultants/consultants.service.tssrc/modules/consultants/dto/create-consultant-request.dto.tssrc/modules/consultants/schemas/consultant-request.schema.tssrc/modules/consultations/consultations.service.tssrc/modules/insights/insights.controller.spec.tssrc/modules/insights/insights.controller.tssrc/modules/insights/insights.module.tssrc/modules/insights/insights.service.spec.tssrc/modules/insights/insights.service.tssrc/modules/matches/matches.service.spec.tssrc/modules/matches/matches.service.tssrc/modules/partners/partners.module.tssrc/modules/partners/partners.service.spec.tssrc/modules/partners/partners.service.tssrc/modules/payments/xrpl.service.tssrc/modules/users/schemas/buyer.schema.tssrc/modules/users/schemas/company.schema.tssrc/modules/users/schemas/user.schema.tssrc/modules/users/users.module.tssrc/types/express-session.d.tstest/jest-e2e.jsontest/xrpl.e2e-spec.ts
💤 Files with no reviewable changes (34)
- src/modules/admin/guards/admin-token.guard.ts
- src/modules/buyers/dto/update-buyer.dto.ts
- src/modules/companies/dto/upload-image.dto.ts
- src/modules/admin/dto/audit-logs-query.dto.ts
- .env.example
- src/modules/insights/insights.controller.ts
- src/modules/consultants/consultants.module.ts
- src/modules/admin/admin.controller.spec.ts
- src/modules/admin/schemas/audit-log.schema.ts
- src/modules/admin/dto/match-logs-query.dto.ts
- src/modules/buyers/schemas/buyer.schema.ts
- src/modules/admin/dto/payment-stats-query.dto.ts
- src/modules/consultants/consultants.service.spec.ts
- src/modules/companies/company-images.controller.ts
- src/modules/buyers/dto/create-buyer.dto.ts
- src/modules/admin/admin.module.ts
- src/modules/companies/company-images.controller.spec.ts
- src/modules/consultants/consultants.controller.ts
- src/modules/insights/insights.controller.spec.ts
- src/modules/consultants/dto/create-consultant-request.dto.ts
- src/modules/admin/dto/list-payments-query.dto.ts
- src/modules/admin/guards/admin-token.guard.spec.ts
- src/modules/companies/schemas/company.schema.ts
- src/modules/consultants/consultants.controller.spec.ts
- src/modules/admin/admin.service.spec.ts
- src/modules/consultants/consultants.service.ts
- src/modules/admin/admin.controller.ts
- src/modules/companies/company-images.service.ts
- src/modules/companies/company-images.service.spec.ts
- src/modules/insights/insights.service.spec.ts
- src/modules/insights/insights.service.ts
- src/modules/admin/admin.service.ts
- src/modules/consultants/schemas/consultant-request.schema.ts
- src/modules/insights/insights.module.ts
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/modules/payments/xrpl.service.ts (1)
184-186:⚠️ Potential issue | 🟠 Major
checkPayment()도 끊긴 연결을 복구하도록 맞춰 주세요.
fundAccount()는 필요하면connect()를 다시 타는데,checkPayment()는 같은 상황에서 바로 실패합니다. 운영 중 일시적인 WebSocket 끊김만으로 결제 확인이 영구 실패 상태가 되는 건 너무 취약합니다.🔄 수정 예시
async checkPayment( destTag: number, expectedAmountXrp: number, ): Promise<PaymentCheckResult> { - if (!this.client?.isConnected()) { - throw new Error("XRPL client is not connected"); - } + if (!this.client?.isConnected()) { + await this.connect(); + } const expectedDrops = BigInt(xrpToDrops(expectedAmountXrp));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/payments/xrpl.service.ts` around lines 184 - 186, checkPayment() currently throws immediately when this.client?.isConnected() is false; change it to mirror fundAccount() by attempting to recover the WebSocket: call await this.connect() (or the existing connection routine used by fundAccount()) when the client is not connected, re-check this.client?.isConnected(), and only then throw an Error if still disconnected; update references in checkPayment() around the this.client?.isConnected() check so transient disconnects are retried rather than failing instantly.src/modules/partners/partners.service.ts (2)
381-390:⚠️ Potential issue | 🟡 Minor웹 검색 결과에도
productIntroduction를 채워서 응답 형태를 맞춰 주세요.같은 서비스의 DB 경로는
SEARCH_PROJECTION에productIntroduction를 포함하는데, 웹 폴백은 이 필드를 아예 생략하고 있습니다.provider에 따라 payload shape가 달라져 클라이언트가 분기 처리를 강제당합니다.🧩 수정 예시
return { _id: `web_${index}`, name: item.title, industry: "Web Result", location: { country: "Global", city: "", state: "" }, companyIntroduction: item.content, + productIntroduction: "", websiteUrl: item.url, tags: ["Web"], score: Math.min(1.0, Math.max(0.1, score)), };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/partners/partners.service.ts` around lines 381 - 390, The web-fallback return object in partners.service.ts omits productIntroduction causing inconsistent payload shapes compared to SEARCH_PROJECTION; update the object constructed for web results (the block that returns _id: `web_${index}`, name: item.title, etc.) to include productIntroduction (e.g., set it from item.content or a trimmed snippet) so web provider responses match other providers' shape and avoid client-side branching.
176-215:⚠️ Potential issue | 🟠 Major
$vectorSearch.filter로 필터를 벡터 검색 단계 내부로 이동하세요.현재는
type: "seller"와 기타 필터가 벡터 후보를 뽑은 뒤$match에서 적용되어, 벡터 단계의numCandidates와limit슬롯을 매칭되지 않는 문서가 먼저 소모합니다. MongoDB Atlas$vectorSearch는filter옵션을 지원하므로, 이 필터들을 벡터 검색 단계에서 직접 적용하면 recall 손실 없이 더 효율적으로 동작합니다. 특히 이 PR의 목적이 Buyer가 Seller 결과에 섞이는 문제를 해결하는 것이라면, 해당 필터는 벡터 단계 자체에서 걸어야 합니다.수정 방향
+ const vectorFilter: Record<string, any> = { type: "seller" }; const pipeline: any[] = [ { $vectorSearch: { index: process.env.ATLAS_VECTOR_INDEX || "vector_index", path: "embedding", queryVector: vector, numCandidates: 100, limit: Number(limit) * 2, + filter: vectorFilter, }, }, ]; - const matchStage: Record<string, any> = { type: "seller" };필터 필드들(
type,industry,country등)이 vectorSearch 인덱스에서 filter 타입으로 인덱싱되어 있는지 확인하세요.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/partners/partners.service.ts` around lines 176 - 215, The pipeline currently builds matchStage and pushes a subsequent {$match} after the $vectorSearch, which wastes numCandidates/limit on non-matching docs; move the matchStage into the $vectorSearch filter option so filtering is applied during candidate retrieval. Concretely, update the pipeline construction so the $vectorSearch stage (the object using index/process.env.ATLAS_VECTOR_INDEX, queryVector `vector`, `numCandidates`, and `limit`) includes a `filter: matchStage` property (after you build matchStage using INDUSTRY_MAPPING and predictedIndustry logic), and remove the later pipeline.push({ $match: matchStage }) call; also ensure the fields used (type, industry, location.country, tags, sizeBucket, name) are indexed as vectorSearch filters in your Atlas index before deploying.
🧹 Nitpick comments (1)
src/common/decorators/current-user.decorator.ts (1)
14-18:getRequest()을 타입 지정해서any기반 세션 접근을 제거해 주세요.Line 14에서
req가any로 타입되어 Line 15/18의session접근이 unsafe 경고를 냅니다. 런타임 동작은 그대로 두고 타입만 고정하는 쪽이 안전합니다.🔧 제안 수정안
import { createParamDecorator, ExecutionContext, UnauthorizedException, } from "@nestjs/common"; +import type { Request } from "express"; @@ export const CurrentUser = createParamDecorator( (_: unknown, ctx: ExecutionContext): SessionUser => { - const req = ctx.switchToHttp().getRequest(); + const req = ctx.switchToHttp().getRequest<Request>(); if (!req.session?.userId || !req.session?.type) { throw new UnauthorizedException("User not authenticated"); } return { userId: req.session.userId, type: req.session.type }; }, );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/common/decorators/current-user.decorator.ts` around lines 14 - 18, The current code reads req via ctx.switchToHttp().getRequest() and treats it as any, causing unsafe session access; create a typed request interface (e.g., AuthenticatedRequest or extend Express.Request) that includes session: { userId?: string; type?: string } and use it when assigning req (replace the any inference on ctx.switchToHttp().getRequest() with this typed request) so the checks and return { userId: req.session.userId, type: req.session.type } are type-safe; update the declaration where req is obtained in current-user.decorator (the getRequest() call) to use that interface.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/common/guards/session.guard.ts`:
- Around line 12-13: The guard currently only checks presence of
req.session?.type and accepts any truthy value; update the session guard (e.g.,
in the canActivate logic in session.guard.ts where it checks req.session?.userId
and req.session?.type) to validate that req.session.type is one of the allowed
literal values ("buyer" or "seller") before permitting access; if it is missing
or not one of these values, throw the same UnauthorizedException("User not
authenticated") so malformed or poisoned sessions are rejected.
In `@src/config/env.config.ts`:
- Around line 78-80: The session secret currently falls back to a weak default;
update the session config so that when NODE_ENV === "production" and
process.env.SESSION_SECRET is missing/empty the application fails fast (throw an
Error) instead of using "dev-secret-change-in-production"; modify the
session.secret resolution logic in env.config.ts (the session object /
session.secret) to mirror how ENCRYPTION_KEY is handled: validate presence in
production and throw a clear error including the symbol SESSION_SECRET if
absent.
In `@src/modules/payments/xrpl.service.ts`:
- Around line 96-103: In fundAccount, before calling
Wallet.fromSeed(wallet.seed) and client.fundWallet, derive the XRPL address from
the seed (using Wallet.fromSeed(...).address) and compare it to the stored
wallet.address; if they differ, abort the operation (throw or return an
error/exception) and log a clear message so you don't fund the wrong account.
Ensure this check happens after ensuring the client is connected (connect) and
before client.fundWallet is invoked so Wallet.fromSeed, wallet.address,
fundAccount, and client.fundWallet are the primary symbols to locate and update.
- Around line 54-56: The onModuleInit() currently awaits this.connect(), which
blocks application boot if XRPL connection fails; change it to start connection
asynchronously and never throw during startup: call this.connect() without await
(or schedule it via setImmediate/void Promise) and ensure connect() errors are
caught and handled inside (or attach .catch() to log a warning via the service
logger and optionally schedule retries), so onModuleInit() returns immediately
while XRPL connection proceeds in background; refer to the onModuleInit and
connect methods to implement the non-blocking startup and robust error
logging/retry.
---
Outside diff comments:
In `@src/modules/partners/partners.service.ts`:
- Around line 381-390: The web-fallback return object in partners.service.ts
omits productIntroduction causing inconsistent payload shapes compared to
SEARCH_PROJECTION; update the object constructed for web results (the block that
returns _id: `web_${index}`, name: item.title, etc.) to include
productIntroduction (e.g., set it from item.content or a trimmed snippet) so web
provider responses match other providers' shape and avoid client-side branching.
- Around line 176-215: The pipeline currently builds matchStage and pushes a
subsequent {$match} after the $vectorSearch, which wastes numCandidates/limit on
non-matching docs; move the matchStage into the $vectorSearch filter option so
filtering is applied during candidate retrieval. Concretely, update the pipeline
construction so the $vectorSearch stage (the object using
index/process.env.ATLAS_VECTOR_INDEX, queryVector `vector`, `numCandidates`, and
`limit`) includes a `filter: matchStage` property (after you build matchStage
using INDUSTRY_MAPPING and predictedIndustry logic), and remove the later
pipeline.push({ $match: matchStage }) call; also ensure the fields used (type,
industry, location.country, tags, sizeBucket, name) are indexed as vectorSearch
filters in your Atlas index before deploying.
In `@src/modules/payments/xrpl.service.ts`:
- Around line 184-186: checkPayment() currently throws immediately when
this.client?.isConnected() is false; change it to mirror fundAccount() by
attempting to recover the WebSocket: call await this.connect() (or the existing
connection routine used by fundAccount()) when the client is not connected,
re-check this.client?.isConnected(), and only then throw an Error if still
disconnected; update references in checkPayment() around the
this.client?.isConnected() check so transient disconnects are retried rather
than failing instantly.
---
Nitpick comments:
In `@src/common/decorators/current-user.decorator.ts`:
- Around line 14-18: The current code reads req via
ctx.switchToHttp().getRequest() and treats it as any, causing unsafe session
access; create a typed request interface (e.g., AuthenticatedRequest or extend
Express.Request) that includes session: { userId?: string; type?: string } and
use it when assigning req (replace the any inference on
ctx.switchToHttp().getRequest() with this typed request) so the checks and
return { userId: req.session.userId, type: req.session.type } are type-safe;
update the declaration where req is obtained in current-user.decorator (the
getRequest() call) to use that interface.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: e2cb40b5-8abb-4129-bfec-c5a3aecec4bb
📒 Files selected for processing (10)
.coderabbit.yamlsrc/common/decorators/current-user.decorator.tssrc/common/guards/session.guard.tssrc/config/env.config.tssrc/main.tssrc/modules/auth/auth.controller.spec.tssrc/modules/auth/auth.controller.tssrc/modules/auth/dto/register.dto.tssrc/modules/partners/partners.service.tssrc/modules/payments/xrpl.service.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- .coderabbit.yaml
- src/main.ts
- src/modules/auth/auth.controller.ts
- src/modules/auth/auth.controller.spec.ts
- src/modules/auth/dto/register.dto.ts
개요 #4
buyer/seller 회원가입, 로그인, 로그아웃 기능을 구현하고 관련 보안 이슈를 수정합니다.
주요 변경사항
기능 구현
리팩터링
버그 수정
보안
기타
Summary by CodeRabbit
릴리스 노트
새 기능
제거됨
개선