Add backend support for /warn command#38
Conversation
Add warning schema to the database Add TRPC endpoints for managing warnings Add loggerProcedure is used to log any errors that occur during the execution of these procedures, to replace the try/catch statements in the TRPC endpoint handlers Add support to automatically expire warnings, kick and ban users
- Fixed a bug where `deleteById`, `getTotalActiveCount`, and `getActiveCountInGroup` did not account for warning expiration states - Updated `deleteById` to return a success/failure status to the client.
WalkthroughChangesWarning moderation
Procedure error logging
Project configuration updates
Sequence Diagram(s)sequenceDiagram
participant TelegramClient
participant WarningsRouter
participant WarningDatabase
participant Cron
TelegramClient->>WarningsRouter: create, retrieve, delete, or count warnings
WarningsRouter->>WarningDatabase: execute warning operation
WarningDatabase-->>WarningsRouter: return rows, counts, or deletion result
Cron->>WarningDatabase: mark warnings older than one year expired
WarningDatabase-->>Cron: return updated row count
🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
✨ Finishing Touches📝 Generate docstrings
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 |
…prove warning creation logic
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
src/cron.ts (1)
26-29: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winOptimize
.returning()to prevent excessive memory allocations.By default,
.returning()fetches all columns for the updated rows. Since this cron job only usesupdated.lengthfor logging, returning just theidcolumn reduces the amount of data transferred and allocated into memory when many warnings expire at once.⚡ Proposed optimization
const updated = await DB.update(SCHEMA.TG.warnings) .set({ isExpired: true }) .where(and(lt(SCHEMA.TG.warnings.createdAt, twelveMonthsAgo), eq(SCHEMA.TG.warnings.isExpired, false))) - .returning() + .returning({ id: SCHEMA.TG.warnings.id })🤖 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 `@src/cron.ts` around lines 26 - 29, Update the warnings update query in the cron job to make returning() select only the warning id column, while preserving updated.length for logging and the existing update conditions.src/routers/tg/warnings.ts (1)
32-34: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winEnforce safe integers for Telegram IDs using
.int(). Telegram User and Group IDs are large numbers that comfortably fit within JS safe integer limits, but standardz.number()accepts float/decimal values. In Zod 4,.int()explicitly guarantees precision and prevents float validation issues.
src/routers/tg/warnings.ts#L32-L34: append.int()totargetId,adminId, andgroupId.src/routers/tg/warnings.ts#L56-L56: append.int()totargetId.src/routers/tg/warnings.ts#L96-L97: append.int()toidandgroupId.src/routers/tg/warnings.ts#L125-L125: append.int()totargetId.src/routers/tg/warnings.ts#L143-L144: append.int()totargetIdandgroupId.🤖 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 `@src/routers/tg/warnings.ts` around lines 32 - 34, Update the Zod schemas in src/routers/tg/warnings.ts at lines 32-34, 56, 96-97, 125, and 143-144 to append .int() to every listed Telegram ID field: targetId, adminId, groupId, and id. Preserve the existing numeric validation while rejecting decimal values at all affected sites.
🤖 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 `@src/routers/tg/warnings.ts`:
- Around line 129-131: Update both warning-count queries in
src/routers/tg/warnings.ts at lines 129-131 and 149-151 to destructure the first
row from await DB.select(...), then return result?.count ?? 0 instead of the raw
Drizzle array. Ensure both procedures continue returning a single numeric count.
In `@src/utils/loggerProc.ts`:
- Around line 12-16: In the !result.ok branch, remove the new TRPCError throw so
the middleware returns the original result and preserves tRPC error codes and
payloads. Keep the existing action/router context for logging, and optionally
downgrade expected client-error codes such as BAD_REQUEST from logger.error to
logger.warn or logger.info.
---
Nitpick comments:
In `@src/cron.ts`:
- Around line 26-29: Update the warnings update query in the cron job to make
returning() select only the warning id column, while preserving updated.length
for logging and the existing update conditions.
In `@src/routers/tg/warnings.ts`:
- Around line 32-34: Update the Zod schemas in src/routers/tg/warnings.ts at
lines 32-34, 56, 96-97, 125, and 143-144 to append .int() to every listed
Telegram ID field: targetId, adminId, groupId, and id. Preserve the existing
numeric validation while rejecting decimal values at all affected sites.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 873849cd-db34-4e92-949d-8608807986b4
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (12)
drizzle/0015_bitter_nighthawk.sqldrizzle/meta/0015_snapshot.jsondrizzle/meta/_journal.jsonpnpm-workspace.yamlsrc/auth/index.tssrc/cron.tssrc/db/schema/tg/audit-log.tssrc/db/schema/tg/index.tssrc/db/schema/tg/warnings.tssrc/routers/tg/index.tssrc/routers/tg/warnings.tssrc/utils/loggerProc.ts
| return await DB.select({ count: count() }) | ||
| .from(SCHEMA.TG.warnings) | ||
| .where(and(eq(SCHEMA.TG.warnings.targetId, input.targetId), isNull(SCHEMA.TG.warnings.deletedAt), eq(SCHEMA.TG.warnings.isExpired, false))) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Extract the numeric count instead of returning the raw Drizzle array. Drizzle's count() coupled with .select() returns an array of objects (e.g., [{ count: 2 }]). Exposing this internal ORM structure directly to the tRPC client breaks the expected single-number return format implied by the procedure names.
src/routers/tg/warnings.ts#L129-L131: destructure[result]fromawait DB.select(...)and returnresult?.count ?? 0.src/routers/tg/warnings.ts#L149-L151: destructure[result]fromawait DB.select(...)and returnresult?.count ?? 0.
📍 Affects 1 file
src/routers/tg/warnings.ts#L129-L131(this comment)src/routers/tg/warnings.ts#L149-L151
🤖 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 `@src/routers/tg/warnings.ts` around lines 129 - 131, Update both warning-count
queries in src/routers/tg/warnings.ts at lines 129-131 and 149-151 to
destructure the first row from await DB.select(...), then return result?.count
?? 0 instead of the raw Drizzle array. Ensure both procedures continue returning
a single numeric count.
| if (!result.ok) { | ||
| const { action, router } = getActionInfo(path) | ||
| logger.error({ error: result.error }, `Error while executing ${action} in ${router} router`) | ||
| throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "INTERNAL_SERVER_ERROR" }) | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
Do not mask original tRPC errors.
Throwing a new INTERNAL_SERVER_ERROR overrides all legitimate client-side errors, such as Zod validation failures (BAD_REQUEST) or authorization issues (UNAUTHORIZED). This prevents clients from properly handling and displaying invalid input errors.
Remove the throw statement and let the middleware return the unmodified result. tRPC natively handles result payloads and will correctly propagate the original error status and payload back to the client.
Additionally, consider checking result.error.code to log expected client errors (like BAD_REQUEST) as .warn() or .info() instead of .error() to reduce log noise.
🐛 Proposed fix
if (!result.ok) {
const { action, router } = getActionInfo(path)
logger.error({ error: result.error }, `Error while executing ${action} in ${router} router`)
- throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "INTERNAL_SERVER_ERROR" })
}📝 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 (!result.ok) { | |
| const { action, router } = getActionInfo(path) | |
| logger.error({ error: result.error }, `Error while executing ${action} in ${router} router`) | |
| throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "INTERNAL_SERVER_ERROR" }) | |
| } | |
| if (!result.ok) { | |
| const { action, router } = getActionInfo(path) | |
| logger.error({ error: result.error }, `Error while executing ${action} in ${router} router`) | |
| } |
🤖 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 `@src/utils/loggerProc.ts` around lines 12 - 16, In the !result.ok branch,
remove the new TRPCError throw so the middleware returns the original result and
preserves tRPC error codes and payloads. Keep the existing action/router context
for logging, and optionally downgrade expected client-error codes such as
BAD_REQUEST from logger.error to logger.warn or logger.info.
Related to telegram#82