Feature/supportcomponent#1050
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted. Error details |
There was a problem hiding this comment.
Actionable comments posted: 10
🧹 Nitpick comments (7)
backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportApiController.kt (2)
182-198: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winAdd
@Validto theIncomingEmailDtoparameter for defense-in-depth input validation.The webhook endpoint deserializes
IncomingEmailDtowithout triggering bean validation. Even though the payload comes from an email service, adding@Validensures malformed payloads are rejected early rather than causing downstream errors insupportService.processIncomingEmail.♻️ Proposed fix
- `@RequestBody` dto: IncomingEmailDto + `@Valid` `@RequestBody` dto: IncomingEmailDto🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportApiController.kt` around lines 182 - 198, Add the `@Valid` annotation to the IncomingEmailDto parameter of SupportApiController.incomingEmail so bean validation runs immediately after deserialization, before supportService.processIncomingEmail is called.
182-198: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winAdd
@Validto theIncomingEmailDtoparameter for defense-in-depth input validation.The webhook endpoint deserializes
IncomingEmailDtowithout triggering bean validation. Even though the payload comes from an email service, adding@Validensures malformed payloads are rejected early rather than causing downstream errors insupportService.processIncomingEmail.♻️ Proposed fix
- `@RequestBody` dto: IncomingEmailDto + `@Valid` `@RequestBody` dto: IncomingEmailDto🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportApiController.kt` around lines 182 - 198, Add the `@Valid` annotation to the `IncomingEmailDto` parameter in `incomingEmail` so bean validation runs during request deserialization before `supportService.processIncomingEmail` is called.backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportStatsDashboard.kt (1)
67-71: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
resolveDisplayNameis duplicated inSupportService.kt.The same
resolveDisplayNamelogic exists in bothSupportStatsDashboard(lines 67-71) andSupportService(lines 383-387). Consider extracting it to a shared helper or service to avoid divergence.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportStatsDashboard.kt` around lines 67 - 71, The resolveDisplayName logic is duplicated across SupportStatsDashboard and SupportService. Extract it into a shared helper or service, then update both classes to delegate to that implementation while preserving the existing fallback order and behavior.backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportService.kt (2)
389-411: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
pickAvailableSupportUserduplicates schedule filtering logic fromSupportStatsDashboard.buildCurrentScheduleCard.The schedule availability filtering logic (lines 392-397) is nearly identical to
SupportStatsDashboardlines 77-82. Consider extracting a sharedisScheduleActive(schedule)helper to avoid divergence.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportService.kt` around lines 389 - 411, Extract the duplicated schedule availability predicate from pickAvailableSupportUser into a shared isScheduleActive(schedule) helper, alongside the equivalent logic in SupportStatsDashboard.buildCurrentScheduleCard. Update both callers to use the helper while preserving the existing validation of supportUserId, from/to values, timezone conversion, and active time-range behavior.
307-317: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value
decodeSrsEmailuses a simplified SRS parsing strategy.The decoder splits on
=and assumes the last two parts are domain and user. Real SRS format isSRS=hash=timestamp=domain=user, but some implementations useSRS0=hash=timestamp=domain=useror encode the separator differently. If the user's email address contains=, the parsing would break. This is acceptable as a best-effort decoder but should be documented as such.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportService.kt` around lines 307 - 317, The decodeSrsEmail method uses best-effort simplified SRS parsing; document this limitation directly near the method or parsing logic. Note that it supports the expected SRS forms only heuristically, may not handle alternate separators or “=” characters in the original address, and returns the raw address when decoding is uncertain.backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportThreadEntity.kt (1)
52-59: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRedundant enum persistence annotations.
Both
@Enumerated(EnumType.STRING)and@JdbcTypeCode(SqlTypes.VARCHAR)are present on thestatusfield. In Hibernate 6,@JdbcTypeCode(SqlTypes.VARCHAR)is the preferred approach and makes@Enumeratedredundant. Consider removing@Enumeratedto reduce confusion, or keep both if the project requires backward compatibility with Hibernate 5.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportThreadEntity.kt` around lines 52 - 59, Remove the redundant `@Enumerated` annotation from the status property in SupportThreadEntity, retaining `@JdbcTypeCode`(SqlTypes.VARCHAR) so the enum continues to persist as a string. Keep the existing column, JSON view, and generated input/overview annotations unchanged.frontend/src/pages/support/supportThread.page.tsx (1)
92-92: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winFix prettier formatting violations.
Static analysis reports formatting issues on lines 92, 120, and 130 where multi-line JSX attributes should be collapsed to single lines per the project's prettier configuration.
♻️ Proposed formatting fixes
<div - key={msg.id} - className={cn( - 'rounded-lg border p-4', - msg.fromAdmin ? 'bg-primary/5 border-primary/20' : 'bg-card' - )} + key={msg.id} className={cn('rounded-lg border p-4', msg.fromAdmin ? 'bg-primary/5 border-primary/20' : 'bg-card')} ><Button - onClick={handleReply} - disabled={addMessageMutation.isPending || !replyText.trim()} - className="self-start" + onClick={handleReply} disabled={addMessageMutation.isPending || !replyText.trim()} className="self-start" >{thread.status === SupportThreadStatus.DONE && ( - <p className="text-muted-foreground text-sm mt-2">Ez az üzenetváltás le van zárva.</p> + <p className="text-muted-foreground text-sm mt-2">Ez az üzenetváltás le van zárva.</p> )}Also applies to: 120-120, 130-130
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/pages/support/supportThread.page.tsx` at line 92, Apply the project’s Prettier formatting to the JSX in the support thread page, specifically the multi-line attributes near the div elements at the referenced locations. Collapse attributes that fit on one line while preserving the existing JSX structure and behavior.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportAdminSettingsExtension.kt`:
- Around line 24-33: Update getCardFormHtml to avoid calling userEntity.get()
when the repository result is empty, and escape the fallback full name before
interpolating it into the placeholder attribute. Derive the placeholder safely
from the Optional with an empty/default value, while preserving the existing
currentName handling and form output.
In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportApiController.kt`:
- Around line 79-100: Update createThread and ThreadListView to return
SupportThreadView DTOs instead of serializing SupportThreadEntity, ensuring
uuidSecret and userInternalId are not exposed; follow the existing getThread
mapping, or use a dedicated create-response DTO only if the secret is required
by the creator.
In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportService.kt`:
- Around line 258-280: Guard the admin-reply notification flow in SupportService
before calling emailService.sendTemplatedEmail: only send when thread.userEmail
is non-blank. Preserve the existing template lookup, email values, recipient,
and missing-template warning behavior, while skipping the send entirely for
blank addresses.
- Around line 319-354: Update processIncomingEmail to validate the resolved user
and email content before calling addCustomerMessage or createThread: reject
blocked users via isBlockedUser, reject bodies exceeding limits via
isContentTooLong, and enforce hasTooManyConsecutiveCustomerResponses for
existing threads. Return without modifying support data when any validation
fails, while preserving the existing routing and creation behavior for valid
emails.
In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportStatsDashboard.kt`:
- Around line 77-82: Update the inSchedule filtering logic to compare the
schedule’s full start and end timestamps as instants rather than converting them
to LocalTime. Preserve the existing validation and timezone handling while
ensuring schedules spanning midnight or multiple days are classified correctly.
In `@backend/src/main/resources/templates/settings.html`:
- Line 59: Update SupportAdminSettingsExtension’s formHtml construction to
HTML-escape userEntity.get().fullName before interpolating it, matching the
existing escaping applied to currentName; preserve the raw th:utext rendering
while ensuring the generated placeholder cannot contain unescaped user input.
In `@backend/src/main/resources/templates/supportThreadView.html`:
- Around line 116-117: Update the Thymeleaf condition on the message author
display span to compare realAuthorName and authorName null-safely, replacing the
direct msg.realAuthorName.equals(...) call with Thymeleaf’s null-safe strings
comparison while preserving the existing display behavior.
In `@frontend/src/api/hooks/support/useAddSupportMessageMutation.ts`:
- Around line 19-23: Reformat the axios.post call in
useAddSupportMessageMutation so the method invocation and its arguments are on a
single line, matching the repository’s Prettier output while preserving the
existing URL, payload, and params.
- Around line 19-23: Reformat the axios.post call in
useAddSupportMessageMutation so the complete invocation, including its URL,
payload, and params arguments, is on a single line as required by Prettier.
Preserve the existing request behavior and values.
In `@frontend/src/pages/support/supportList.page.tsx`:
- Around line 23-27: Update the status label lookup using statusLabel and ensure
unmapped thread.status values fall back to displaying thread.status instead of
rendering undefined; preserve the existing labels for the three
SupportThreadStatus values.
---
Nitpick comments:
In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportApiController.kt`:
- Around line 182-198: Add the `@Valid` annotation to the IncomingEmailDto
parameter of SupportApiController.incomingEmail so bean validation runs
immediately after deserialization, before supportService.processIncomingEmail is
called.
- Around line 182-198: Add the `@Valid` annotation to the `IncomingEmailDto`
parameter in `incomingEmail` so bean validation runs during request
deserialization before `supportService.processIncomingEmail` is called.
In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportService.kt`:
- Around line 389-411: Extract the duplicated schedule availability predicate
from pickAvailableSupportUser into a shared isScheduleActive(schedule) helper,
alongside the equivalent logic in
SupportStatsDashboard.buildCurrentScheduleCard. Update both callers to use the
helper while preserving the existing validation of supportUserId, from/to
values, timezone conversion, and active time-range behavior.
- Around line 307-317: The decodeSrsEmail method uses best-effort simplified SRS
parsing; document this limitation directly near the method or parsing logic.
Note that it supports the expected SRS forms only heuristically, may not handle
alternate separators or “=” characters in the original address, and returns the
raw address when decoding is uncertain.
In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportStatsDashboard.kt`:
- Around line 67-71: The resolveDisplayName logic is duplicated across
SupportStatsDashboard and SupportService. Extract it into a shared helper or
service, then update both classes to delegate to that implementation while
preserving the existing fallback order and behavior.
In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportThreadEntity.kt`:
- Around line 52-59: Remove the redundant `@Enumerated` annotation from the status
property in SupportThreadEntity, retaining `@JdbcTypeCode`(SqlTypes.VARCHAR) so
the enum continues to persist as a string. Keep the existing column, JSON view,
and generated input/overview annotations unchanged.
In `@frontend/src/pages/support/supportThread.page.tsx`:
- Line 92: Apply the project’s Prettier formatting to the JSX in the support
thread page, specifically the multi-line attributes near the div elements at the
referenced locations. Collapse attributes that fit on one line while preserving
the existing JSX structure and behavior.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 308f6672-3e8b-4a6e-b4c5-02c13eecf89e
📒 Files selected for processing (42)
.coderabbit.yamlbackend/src/main/kotlin/hu/bme/sch/cmsch/component/debt/SoldProductEntity.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/component/email/EmailService.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/component/location/LocationService.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportAdminController.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportAdminSettingsExtension.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportApiController.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportComponent.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportComponentController.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportComponentEntityConfiguration.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportMessageEntity.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportMessageRepository.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportScheduleAdminController.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportScheduleEntity.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportScheduleRepository.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportService.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportStatsDashboard.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportThreadEntity.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportThreadRepository.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportThreadViewExtension.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/component/support/dto/IncomingEmailDto.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/controller/admin/AdminSettingsExtension.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/controller/admin/SettingsController.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/dto/SiteContext.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/repository/EntityPageDataSource.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/repository/ManualRepository.ktbackend/src/main/kotlin/hu/bme/sch/cmsch/service/PermissionsService.ktbackend/src/main/resources/config/application.propertiesbackend/src/main/resources/templates/settings.htmlbackend/src/main/resources/templates/supportThreadView.htmlfrontend/src/App.tsxfrontend/src/api/contexts/config/types.tsfrontend/src/api/hooks/queryKeys.tsfrontend/src/api/hooks/support/useAddSupportMessageMutation.tsfrontend/src/api/hooks/support/useCreateSupportThreadMutation.tsfrontend/src/api/hooks/support/useSupportThreadQuery.tsfrontend/src/api/hooks/support/useSupportThreadsQuery.tsfrontend/src/pages/support/supportList.page.tsxfrontend/src/pages/support/supportNew.page.tsxfrontend/src/pages/support/supportThread.page.tsxfrontend/src/util/paths.tsfrontend/src/util/views/support.view.ts
| override fun getCardFormHtml(user: CmschUser): String { | ||
| val userEntity = userRepository.findByInternalId(user.internalId) | ||
| val currentName = userEntity | ||
| .map { userService.resolveConfig(it.config).supportDefaultName } | ||
| .orElse("") | ||
| return """ | ||
| <form action="/admin/control/support/settings/default-name" method="post"> | ||
| <div class="field-group"> | ||
| <label>Megjelenő név</label> | ||
| <input type="text" name="supportDefaultName" value="${escapeHtml(currentName)}" placeholder="${userEntity.get().fullName}" maxlength="255"> |
There was a problem hiding this comment.
🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win
userEntity.get() will throw NoSuchElementException when the user is not found.
On line 25, userRepository.findByInternalId(user.internalId) returns an Optional. Lines 26–28 correctly handle the empty case for currentName via .orElse(""), but line 33 calls userEntity.get().fullName directly in the placeholder without an isPresent check. If the repository returns an empty Optional, this will crash with NoSuchElementException.
Additionally, userEntity.get().fullName is interpolated into the placeholder attribute without escapeHtml(), creating an XSS vector if the full name contains characters like " or <.
🐛 Proposed fix
override fun getCardFormHtml(user: CmschUser): String {
val userEntity = userRepository.findByInternalId(user.internalId)
val currentName = userEntity
.map { userService.resolveConfig(it.config).supportDefaultName }
.orElse("")
+ val fallbackName = userEntity.map { it.fullName }.orElse(user.fullName)
return """
<form action="/admin/control/support/settings/default-name" method="post">
<div class="field-group">
<label>Megjelenő név</label>
- <input type="text" name="supportDefaultName" value="${escapeHtml(currentName)}" placeholder="${userEntity.get().fullName}" maxlength="255">
+ <input type="text" name="supportDefaultName" value="${escapeHtml(currentName)}" placeholder="${escapeHtml(fallbackName)}" maxlength="255">
</div>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| override fun getCardFormHtml(user: CmschUser): String { | |
| val userEntity = userRepository.findByInternalId(user.internalId) | |
| val currentName = userEntity | |
| .map { userService.resolveConfig(it.config).supportDefaultName } | |
| .orElse("") | |
| return """ | |
| <form action="/admin/control/support/settings/default-name" method="post"> | |
| <div class="field-group"> | |
| <label>Megjelenő név</label> | |
| <input type="text" name="supportDefaultName" value="${escapeHtml(currentName)}" placeholder="${userEntity.get().fullName}" maxlength="255"> | |
| override fun getCardFormHtml(user: CmschUser): String { | |
| val userEntity = userRepository.findByInternalId(user.internalId) | |
| val currentName = userEntity | |
| .map { userService.resolveConfig(it.config).supportDefaultName } | |
| .orElse("") | |
| val fallbackName = userEntity.map { it.fullName }.orElse(user.fullName) | |
| return """ | |
| <form action="/admin/control/support/settings/default-name" method="post"> | |
| <div class="field-group"> | |
| <label>Megjelenő név</label> | |
| <input type="text" name="supportDefaultName" value="${escapeHtml(currentName)}" placeholder="${escapeHtml(fallbackName)}" maxlength="255"> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportAdminSettingsExtension.kt`
around lines 24 - 33, Update getCardFormHtml to avoid calling userEntity.get()
when the repository result is empty, and escape the fallback full name before
interpolating it into the placeholder attribute. Derive the placeholder safely
from the Optional with an empty/default value, while preserving the existing
currentName handling and form output.
| fun createThread(@RequestBody request: CreateThreadRequest, auth: Authentication?): ResponseEntity<SupportThreadEntity> { | ||
| if (request.title.isBlank()) return ResponseEntity.badRequest().build() | ||
| val user = auth?.getUserOrNull() | ||
| return if (user != null && supportComponent.minRole.isAvailableForRole(user.role)) { | ||
| val email = getEmail(user) | ||
| if (supportService.isBlockedUser(user.internalId, email)) | ||
| return ResponseEntity.status(HttpStatus.FORBIDDEN).build() | ||
| if (supportService.countOpenThreadsForUser(user.internalId, "") >= supportComponent.maxOpenThreads) | ||
| return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).build() | ||
| ResponseEntity.ok(supportService.createThread(request.title, request.content, user.internalId, email, user.userName)) | ||
| } else { | ||
| if (request.authorEmail.isBlank()) return ResponseEntity.badRequest().build() | ||
| if (supportService.isBlockedUser("", request.authorEmail)) | ||
| return ResponseEntity.status(HttpStatus.FORBIDDEN).build() | ||
| if (supportService.countOpenThreadsForUser("", request.authorEmail) >= supportComponent.maxOpenThreads) | ||
| return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).build() | ||
| ResponseEntity.ok(supportService.createThread( | ||
| request.title, request.content, "", request.authorEmail, | ||
| request.authorName.ifBlank { request.authorEmail } | ||
| )) | ||
| } | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify which fields SupportThreadEntity exposes and whether `@JsonIgnore` or `@JsonView` is applied
fd "SupportThreadEntity.kt" --exec cat -nRepository: kir-dev/cmsch
Length of output: 7482
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Map relevant symbols and inspect the controller + DTOs that shape support-thread responses.
fd "SupportApiController.kt" "SupportThreadView.kt" "ThreadListView.kt" "CreateThreadRequest.kt" --exec sh -c 'printf "\n== %s ==\n" "$1"; ast-grep outline "$1" --view expanded' sh {}
for f in \
backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportApiController.kt \
backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportThreadView.kt \
backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/ThreadListView.kt
do
if [ -f "$f" ]; then
printf "\n===== %s =====\n" "$f"
cat -n "$f" | sed -n '1,260p'
fi
doneRepository: kir-dev/cmsch
Length of output: 392
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find all places where SupportThreadEntity is returned from controllers or used in JSON views.
rg -n --hidden --glob '!**/build/**' \
'SupportThreadEntity|SupportThreadView|ThreadListView|JsonView|ResponseEntity<SupportThreadEntity>|List<SupportThreadEntity>' \
backend/src/main/kotlin/hu/bme/sch/cmsch/component/supportRepository: kir-dev/cmsch
Length of output: 11458
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Read the support API controller and DTOs with line numbers.
for f in \
backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportApiController.kt \
backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportThreadEntity.kt
do
printf "\n===== %s =====\n" "$f"
cat -n "$f" | sed -n '1,240p'
doneRepository: kir-dev/cmsch
Length of output: 18624
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check whether this app configures Jackson JSON views globally or on these endpoints.
rg -n --hidden --glob '!**/build/**' \
'DEFAULT_VIEW_INCLUSION|MappingJacksonValue|`@JsonView`|JsonView|Jackson2ObjectMapperBuilder|ObjectMapper|SerializationView|MappingJackson2HttpMessageConverter' \
backend/src/main/kotlin backend/src/main/resources
printf "\n===== SupportApiController annotations =====\n"
sed -n '1,220p' backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportApiController.kt | nl -ba | sed -n '1,140p'Repository: kir-dev/cmsch
Length of output: 50372
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the thread creation path to see which fields are populated and returned.
cat -n backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportService.kt | sed -n '150,220p'
printf "\n===== JSON view usage in support APIs =====\n"
rg -n --hidden --glob '!**/build/**' '`@JsonView`|MappingJacksonValue|ResponseEntity<SupportThreadEntity>|ThreadListView|SupportThreadView\.from' \
backend/src/main/kotlin/hu/bme/sch/cmsch/component/supportRepository: kir-dev/cmsch
Length of output: 4752
Return support threads as DTOs, not SupportThreadEntity (SupportApiController.kt:79-114). createThread and ThreadListView serialize the entity directly, which can expose uuidSecret and userInternalId. getThread already uses SupportThreadView; use that here too, or return a dedicated create-response DTO if the secret must be returned to the creator.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportApiController.kt`
around lines 79 - 100, Update createThread and ThreadListView to return
SupportThreadView DTOs instead of serializing SupportThreadEntity, ensuring
uuidSecret and userInternalId are not exposed; follow the existing getThread
mapping, or use a dedicated create-response DTO only if the secret is required
by the creator.
| if (!internalOnly && supportComponent.sendEmailOnAdminReply) { | ||
| val threadUrl = buildThreadUrl(thread) | ||
| val template = emailService.getTemplateBySelector(supportComponent.answerEmailTemplateSelector) | ||
| if (template != null) { | ||
| emailService.sendTemplatedEmail( | ||
| responsible = adminUser, | ||
| template = template, | ||
| values = mapOf( | ||
| "title" to thread.title, | ||
| "message" to content, | ||
| "messageHtml" to toMessageHtml(content), | ||
| "solver" to displayName, | ||
| "threadUrl" to threadUrl, | ||
| "creationDate" to formatHungarianDate(thread.createdAt), | ||
| "lastAnswerDate" to formatHungarianDate(now) | ||
| ), | ||
| to = listOf(thread.userEmail), | ||
| subjectOverride = "RE: {{title}}" | ||
| ) | ||
| } else { | ||
| log.warn("Email template '{}' not found, skipping notification", supportComponent.answerEmailTemplateSelector) | ||
| } | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Admin reply email sent even when thread.userEmail is blank.
emailService.sendTemplatedEmail is called with to = listOf(thread.userEmail) without checking if the email is non-blank. Threads created via the email webhook always have an email, but threads created through the UI by unauthenticated users may have a blank email. This could cause the email service to attempt sending to an empty address.
🛡️ Proposed fix: guard against blank email
- if (!internalOnly && supportComponent.sendEmailOnAdminReply) {
+ if (!internalOnly && supportComponent.sendEmailOnAdminReply && thread.userEmail.isNotBlank()) {
val threadUrl = buildThreadUrl(thread)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (!internalOnly && supportComponent.sendEmailOnAdminReply) { | |
| val threadUrl = buildThreadUrl(thread) | |
| val template = emailService.getTemplateBySelector(supportComponent.answerEmailTemplateSelector) | |
| if (template != null) { | |
| emailService.sendTemplatedEmail( | |
| responsible = adminUser, | |
| template = template, | |
| values = mapOf( | |
| "title" to thread.title, | |
| "message" to content, | |
| "messageHtml" to toMessageHtml(content), | |
| "solver" to displayName, | |
| "threadUrl" to threadUrl, | |
| "creationDate" to formatHungarianDate(thread.createdAt), | |
| "lastAnswerDate" to formatHungarianDate(now) | |
| ), | |
| to = listOf(thread.userEmail), | |
| subjectOverride = "RE: {{title}}" | |
| ) | |
| } else { | |
| log.warn("Email template '{}' not found, skipping notification", supportComponent.answerEmailTemplateSelector) | |
| } | |
| } | |
| if (!internalOnly && supportComponent.sendEmailOnAdminReply && thread.userEmail.isNotBlank()) { | |
| val threadUrl = buildThreadUrl(thread) | |
| val template = emailService.getTemplateBySelector(supportComponent.answerEmailTemplateSelector) | |
| if (template != null) { | |
| emailService.sendTemplatedEmail( | |
| responsible = adminUser, | |
| template = template, | |
| values = mapOf( | |
| "title" to thread.title, | |
| "message" to content, | |
| "messageHtml" to toMessageHtml(content), | |
| "solver" to displayName, | |
| "threadUrl" to threadUrl, | |
| "creationDate" to formatHungarianDate(thread.createdAt), | |
| "lastAnswerDate" to formatHungarianDate(now) | |
| ), | |
| to = listOf(thread.userEmail), | |
| subjectOverride = "RE: {{title}}" | |
| ) | |
| } else { | |
| log.warn("Email template '{}' not found, skipping notification", supportComponent.answerEmailTemplateSelector) | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportService.kt`
around lines 258 - 280, Guard the admin-reply notification flow in
SupportService before calling emailService.sendTemplatedEmail: only send when
thread.userEmail is non-blank. Preserve the existing template lookup, email
values, recipient, and missing-template warning behavior, while skipping the
send entirely for blank addresses.
| @Transactional | ||
| fun processIncomingEmail(dto: IncomingEmailDto) { | ||
| val rawFrom = dto.addresses.from.address.trim() | ||
| val toAddress = dto.addresses.to.address.trim() | ||
| val resentFrom = dto.addresses.resentFrom.address.trim() | ||
| val subject = dto.subject.trim() | ||
| val body = stripEmailQuotes(dto.body.text.ifBlank { dto.body.html }) | ||
|
|
||
| if (rawFrom.isBlank() || subject.isBlank()) { | ||
| log.warn("Incoming email ignored: from='{}', subject='{}'", rawFrom, subject) | ||
| return | ||
| } | ||
|
|
||
| val allowedTo = supportComponent.allowedToAddress.trim() | ||
| if (allowedTo.isNotBlank() && !toAddress.equals(allowedTo, ignoreCase = true)) { | ||
| log.warn("Incoming email rejected: 'To' address '{}' does not match configured '{}'", toAddress, allowedTo) | ||
| return | ||
| } | ||
|
|
||
| val allowedResentFrom = supportComponent.allowedResentFromAddress.trim() | ||
| if (allowedResentFrom.isNotBlank() && !resentFrom.equals(allowedResentFrom, ignoreCase = true)) { | ||
| log.warn("Incoming email rejected: 'Resent-From' '{}' does not match configured '{}'", resentFrom, allowedResentFrom) | ||
| return | ||
| } | ||
|
|
||
| val realEmail = decodeSrsEmail(rawFrom) | ||
|
|
||
| val normalized = normalizeSubject(subject) | ||
| val existing = findMatchingThread(normalized, realEmail) | ||
| val user = userRepository.findByEmailIgnoreCase(realEmail).orElse(null) | ||
| if (existing != null) { | ||
| addCustomerMessage(existing.uuid, body, user?.fullName ?: realEmail, realEmail) | ||
| } else { | ||
| createThread(subject, body, user?.internalId ?: "", realEmail, user?.fullName ?: realEmail) | ||
| } | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
Blocked users and content limits are not enforced in the email processing path.
processIncomingEmail does not call isBlockedUser or isContentTooLong before creating a thread or appending a message. This means blocked emails/user IDs can still submit support requests via the incoming email webhook, bypassing the blocklist. Similarly, hasTooManyConsecutiveCustomerResponses is not checked, allowing rate limit bypass via email.
🔒️ Proposed fix: add validation checks in `processIncomingEmail`
val realEmail = decodeSrsEmail(rawFrom)
+ val userInternalId = user?.internalId ?: ""
+
+ if (isBlocked(userInternalId, realEmail)) {
+ log.warn("Incoming email from blocked user/email '{}', ignoring", realEmail)
+ return
+ }
+ if (isContentTooLong(body)) {
+ log.warn("Incoming email body too long from '{}', ignoring", realEmail)
+ return
+ }
val normalized = normalizeSubject(subject)
val existing = findMatchingThread(normalized, realEmail)
- val user = userRepository.findByEmailIgnoreCase(realEmail).orElse(null)
+ if (existing != null) {
+ if (hasTooManyConsecutiveCustomerResponses(existing.uuid)) {
+ log.warn("Too many consecutive customer responses for thread '{}', ignoring", existing.uuid)
+ return
+ }
+ addCustomerMessage(existing.uuid, body, user?.fullName ?: realEmail, realEmail)
+ } else {
- if (existing != null) {
- addCustomerMessage(existing.uuid, body, user?.fullName ?: realEmail, realEmail)
- } else {
- createThread(subject, body, user?.internalId ?: "", realEmail, user?.fullName ?: realEmail)
+ createThread(subject, body, userInternalId, realEmail, user?.fullName ?: realEmail)
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportService.kt`
around lines 319 - 354, Update processIncomingEmail to validate the resolved
user and email content before calling addCustomerMessage or createThread: reject
blocked users via isBlockedUser, reject bodies exceeding limits via
isContentTooLong, and enforce hasTooManyConsecutiveCustomerResponses for
existing threads. Return without modifying support data when any validation
fails, while preserving the existing routing and creation behavior for valid
emails.
| val inSchedule = schedules.filter { schedule -> | ||
| if (schedule.supportUserId.isBlank() || schedule.from <= 0 || schedule.to <= 0) return@filter false | ||
| val fromTime = ZonedDateTime.ofInstant(Instant.ofEpochSecond(schedule.from), clock.timeZone).toLocalTime() | ||
| val toTime = ZonedDateTime.ofInstant(Instant.ofEpochSecond(schedule.to), clock.timeZone).toLocalTime() | ||
| nowTime >= fromTime && nowTime <= toTime | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
cat -n backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportScheduleEntity.ktRepository: kir-dev/cmsch
Length of output: 3707
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== SupportStatsDashboard =="
cat -n backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportStatsDashboard.kt
echo
echo "== Search for SupportScheduleEntity field usage =="
rg -n "SupportScheduleEntity|supportSchedules|supportUserId|supportUserName|from_time|to_time|\.from\b|\.to\b" backend/src/main/kotlin -g'*.kt'Repository: kir-dev/cmsch
Length of output: 13626
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== SupportScheduleAdminController =="
cat -n backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportScheduleAdminController.kt
echo
echo "== SupportService schedule selection =="
sed -n '380,410p' backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportService.kt
echo
echo "== Search for schedule descriptions or docs =="
rg -n "Elérhető:|support schedule|supportSchedules|from_time|to_time|InputType.DATE" backend/src/main/kotlin backend/src/main/resources -g'*.kt' -g'*.md' -g'*.yml' -g'*.yaml'Repository: kir-dev/cmsch
Length of output: 12141
Compare full timestamps instead of LocalTime The date component is dropped here, so any schedule that spans midnight or multiple days will be classified incorrectly. If these fields are real start/end timestamps, compare instants directly; if they’re meant to be daily windows, store time-of-day and handle overnight wraparound explicitly.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportStatsDashboard.kt`
around lines 77 - 82, Update the inSchedule filtering logic to compare the
schedule’s full start and end timestamps as instants rather than converting them
to LocalTime. Preserve the existing validation and timezone handling while
ensuring schedules spanning midnight or multiple days are classified correctly.
| <div class="card wide-false settings-extension-card" th:each="ext : ${settingsExtensions}"> | ||
| <h4 th:text="${ext.title}">Bővítmény</h4> | ||
| <div class="desc" th:text="${ext.description}"></div> | ||
| <div th:utext="${ext.formHtml}"></div> |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Trace where formHtml is populated in settings extensions
rg -rn "formHtml" --type kotlin -g '!**/test/**'
rg -rn "settingsExtensions" --type kotlin -g '!**/test/**'Repository: kir-dev/cmsch
Length of output: 151
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find the relevant template and likely model/population code.
git ls-files | rg 'settings\.html|formHtml|settingsExtensions|extension|Settings|settings'
# Search broadly for the symbols in the repository.
rg -n --hidden --glob '!**/test/**' 'formHtml|settingsExtensions' .Repository: kir-dev/cmsch
Length of output: 1881
🏁 Script executed:
#!/bin/bash
set -euo pipefail
git ls-files | rg 'settings\.html|formHtml|settingsExtensions|extension|Settings|settings'
rg -n --hidden --glob '!**/test/**' 'formHtml|settingsExtensions' .Repository: kir-dev/cmsch
Length of output: 1881
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Identify the source of ext.formHtml in the template context.
rg -n --hidden --glob '!**/test/**' 'ext\.formHtml|formHtml|settingsExtensions' backend src .Repository: kir-dev/cmsch
Length of output: 2275
🏁 Script executed:
#!/bin/bash
set -euo pipefail
git ls-files | rg 'settings\.html|formHtml|settingsExtensions|Settings'
rg -n --hidden --glob '!**/test/**' 'formHtml|settingsExtensions|th:utext|formHtml' backend src .Repository: kir-dev/cmsch
Length of output: 5732
🏁 Script executed:
#!/bin/bash
set -euo pipefail
ast-grep outline backend/src/main/kotlin/hu/bme/sch/cmsch/controller/admin/AdminSettingsExtension.kt --view expanded
ast-grep outline backend/src/main/kotlin/hu/bme/sch/cmsch/controller/admin/SettingsController.kt --view expanded
ast-grep outline backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportAdminSettingsExtension.kt --view expanded
ast-grep outline backend/src/main/kotlin/hu/bme/sch/cmsch/component/app/SettingsApiController.kt --view expandedRepository: kir-dev/cmsch
Length of output: 1361
🏁 Script executed:
#!/bin/bash
set -euo pipefail
cat -n backend/src/main/kotlin/hu/bme/sch/cmsch/controller/admin/AdminSettingsExtension.kt
printf '\n---\n'
cat -n backend/src/main/kotlin/hu/bme/sch/cmsch/controller/admin/SettingsController.kt
printf '\n---\n'
cat -n backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportAdminSettingsExtension.kt
printf '\n---\n'
cat -n backend/src/main/kotlin/hu/bme/sch/cmsch/component/app/SettingsApiController.ktRepository: kir-dev/cmsch
Length of output: 8818
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect where fullName comes from and whether other AdminSettingsExtension implementations exist.
rg -n --hidden --glob '!**/test/**' 'class .*AdminSettingsExtension|: AdminSettingsExtension|fullName|supportDefaultName' backend/src/main/kotlin/hu/bme/sch/cmschRepository: kir-dev/cmsch
Length of output: 32283
Escape userEntity.get().fullName in SupportAdminSettingsExtension. settings.html renders ext.formHtml raw via th:utext, and this form fragment still interpolates the placeholder value without escaping. currentName is already escaped; apply the same here or stop building HTML by string concatenation.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/src/main/resources/templates/settings.html` at line 59, Update
SupportAdminSettingsExtension’s formHtml construction to HTML-escape
userEntity.get().fullName before interpolating it, matching the existing
escaping applied to currentName; preserve the raw th:utext rendering while
ensuring the generated placeholder cannot contain unescaped user input.
| <span th:if="${msg.fromAdmin && !msg.realAuthorName.equals(msg.authorName)}" | ||
| th:text="| (megjelenítve: ${msg.authorName})|" style="font-style: italic;"></span> |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
NPE risk: msg.realAuthorName.equals() may throw if realAuthorName is null.
If msg.realAuthorName is null, calling .equals(msg.authorName) will throw a NullPointerException in the Thymeleaf expression. Use a null-safe comparison instead.
🐛 Proposed fix using null-safe comparison
- <span th:if="${msg.fromAdmin && !msg.realAuthorName.equals(msg.authorName)}"
+ <span th:if="${msg.fromAdmin && msg.realAuthorName != null && !msg.realAuthorName.equals(msg.authorName)}"Alternatively, use Thymeleaf's #strings.equals() utility which handles nulls safely:
- <span th:if="${msg.fromAdmin && !msg.realAuthorName.equals(msg.authorName)}"
+ <span th:if="${msg.fromAdmin && !`#strings.equals`(msg.realAuthorName, msg.authorName)}"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <span th:if="${msg.fromAdmin && !msg.realAuthorName.equals(msg.authorName)}" | |
| th:text="| (megjelenítve: ${msg.authorName})|" style="font-style: italic;"></span> | |
| <span th:if="${msg.fromAdmin && msg.realAuthorName != null && !msg.realAuthorName.equals(msg.authorName)}" | |
| th:text="| (megjelenítve: ${msg.authorName})|" style="font-style: italic;"></span> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/src/main/resources/templates/supportThreadView.html` around lines 116
- 117, Update the Thymeleaf condition on the message author display span to
compare realAuthorName and authorName null-safely, replacing the direct
msg.realAuthorName.equals(...) call with Thymeleaf’s null-safe strings
comparison while preserving the existing display behavior.
| const res = await axios.post<SupportMessageView>( | ||
| `${ApiPaths.SUPPORT_THREAD}/${uuid}/message`, | ||
| { content, authorName }, | ||
| { params } | ||
| ) |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Fix linter failure: reformat axios.post call to a single line.
The run-linters check fails because Prettier expects the axios.post arguments on a single line. This will block CI.
🎨 Proposed fix
- const res = await axios.post<SupportMessageView>(
- `${ApiPaths.SUPPORT_THREAD}/${uuid}/message`,
- { content, authorName },
- { params }
- )
+ const res = await axios.post<SupportMessageView>(`${ApiPaths.SUPPORT_THREAD}/${uuid}/message`, { content, authorName }, { params })📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const res = await axios.post<SupportMessageView>( | |
| `${ApiPaths.SUPPORT_THREAD}/${uuid}/message`, | |
| { content, authorName }, | |
| { params } | |
| ) | |
| const res = await axios.post<SupportMessageView>(`${ApiPaths.SUPPORT_THREAD}/${uuid}/message`, { content, authorName }, { params }) |
🧰 Tools
🪛 GitHub Check: run-linters
[failure] 19-19:
Replace ⏎········${ApiPaths.SUPPORT_THREAD}/${uuid}/message,⏎········{·content,·authorName·},⏎········{·params·}⏎······ with ``${ApiPaths.SUPPORT_THREAD}/${uuid}/message,·{·content,·authorName·},·{·params·}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/api/hooks/support/useAddSupportMessageMutation.ts` around lines
19 - 23, Reformat the axios.post call in useAddSupportMessageMutation so the
method invocation and its arguments are on a single line, matching the
repository’s Prettier output while preserving the existing URL, payload, and
params.
Source: Linters/SAST tools
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Fix linter failure: reformat axios.post call to a single line.
The run-linters check fails because Prettier expects the axios.post arguments on a single line. This will block CI.
🎨 Proposed fix
- const res = await axios.post<SupportMessageView>(
- `${ApiPaths.SUPPORT_THREAD}/${uuid}/message`,
- { content, authorName },
- { params }
- )
+ const res = await axios.post<SupportMessageView>(`${ApiPaths.SUPPORT_THREAD}/${uuid}/message`, { content, authorName }, { params })📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const res = await axios.post<SupportMessageView>( | |
| `${ApiPaths.SUPPORT_THREAD}/${uuid}/message`, | |
| { content, authorName }, | |
| { params } | |
| ) | |
| const res = await axios.post<SupportMessageView>(`${ApiPaths.SUPPORT_THREAD}/${uuid}/message`, { content, authorName }, { params }) |
🧰 Tools
🪛 GitHub Check: run-linters
[failure] 19-19:
Replace ⏎········${ApiPaths.SUPPORT_THREAD}/${uuid}/message,⏎········{·content,·authorName·},⏎········{·params·}⏎······ with ``${ApiPaths.SUPPORT_THREAD}/${uuid}/message,·{·content,·authorName·},·{·params·}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/api/hooks/support/useAddSupportMessageMutation.ts` around lines
19 - 23, Reformat the axios.post call in useAddSupportMessageMutation so the
complete invocation, including its URL, payload, and params arguments, is on a
single line as required by Prettier. Preserve the existing request behavior and
values.
Source: Linters/SAST tools
| const statusLabel: Record<string, string> = { | ||
| [SupportThreadStatus.WAITING_FOR_ADMIN]: `Rendező válaszára vár`, | ||
| [SupportThreadStatus.WAITING_FOR_CUSTOMER]: 'A te válaszodra vár', | ||
| [SupportThreadStatus.DONE]: 'Lezárva' | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
statusLabel[thread.status] may return undefined.
If thread.status contains a value not present in the three mapped keys, the Badge will render undefined (nothing visible). Consider adding a fallback label (e.g., statusLabel[thread.status] ?? thread.status).
💚 Proposed fix
- {statusLabel[thread.status]}
+ {statusLabel[thread.status] ?? thread.status}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/pages/support/supportList.page.tsx` around lines 23 - 27, Update
the status label lookup using statusLabel and ensure unmapped thread.status
values fall back to displaying thread.status instead of rendering undefined;
preserve the existing labels for the three SupportThreadStatus values.
Summary by SlopRabbit