How the Prompt Reporting System Handles Spam, Inappropriate Content, and Copyright Violations in prompts.chat
The prompts.chat reporting system allows authenticated users to flag content for spam, inappropriate material, or copyright violations through a three-layer architecture consisting of a React dialog UI, a Next.js API route with validation logic, and an admin interface for status management.
The f/prompts.chat repository implements a comprehensive content moderation workflow that empowers users to report problematic prompts while preventing abuse through strict business rules. This system captures six distinct report categories—including spam, copyright infringement, and misleading content—and routes them through a structured review pipeline. All report data persists in a dedicated Prisma model with indexed relationships to both users and prompts.
Three-Layer Architecture Overview
The reporting workflow splits responsibilities across distinct application layers to maintain separation of concerns and security boundaries.
UI Layer: The ReportPromptDialog component in src/components/prompts/report-prompt-dialog.tsx renders a modal interface for selecting report reasons and submitting details.
API Layer: The src/app/api/reports/route.ts endpoint enforces authentication, validates input against Zod schemas, and applies business rules such as self-reporting prevention and duplicate detection.
Admin Layer: The src/app/api/admin/reports/[id]/route.ts endpoint restricts status updates to users with the ADMIN role, allowing transitions between PENDING, REVIEWED, and DISMISSED states.
Database Schema and Enums
The reporting functionality centers on the PromptReport model defined in prisma/schema.prisma. This schema establishes foreign key relationships to both the Prompt and User models with cascading deletion rules.
model PromptReport {
id String @id @default(cuid())
reason ReportReason
details String?
status ReportStatus @default(PENDING)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
promptId String
reporterId String
prompt Prompt @relation(fields: [promptId], references: [id], onDelete: Cascade)
reporter User @relation(fields: [reporterId], references: [id], onDelete: Cascade)
@@index([promptId])
@@index([reporterId])
@@index([status])
@@map("prompt_reports")
}
The system classifies reports using two dedicated enums. ReportReason supports six categories: SPAM, INAPPROPRIATE, COPYRIGHT, MISLEADING, RELIST_REQUEST, and OTHER. ReportStatus tracks the review lifecycle through PENDING, REVIEWED, and DISMISSED values.
User Interface Implementation
The ReportPromptDialog component operates as a client-side React component ("use client") that encapsulates the reporting experience. It renders a flag icon button that triggers a modal containing a reason selector and optional details textarea.
Upon submission, the component sends a POST request to /api/reports with the promptId, selected reason, and optional details. The implementation fires an analytics event via analyticsPrompt.report(promptId, reason) to track moderation metrics. The component also handles loading states and displays success or error toast notifications based on the API response.
API Validation and Business Rules
The src/app/api/reports/route.ts endpoint implements a strict validation pipeline to prevent abuse and ensure data integrity.
Authentication: The handler requires a valid session from auth() and returns 401 Unauthorized for unauthenticated requests.
Input Validation: A Zod schema ensures the promptId is a non-empty string and the reason matches one of the defined enum values, returning 400 Bad Request for malformed input.
Existence Check: The system verifies the target prompt exists via db.prompt.findUnique before accepting the report, returning 404 Not Found for invalid prompt IDs.
Self-Reporting Protection: The endpoint rejects reports where the reporter's ID matches the prompt's authorId, unless the reason is RELIST_REQUEST, preventing users from flagging their own content inappropriately.
Duplicate Prevention: The system queries for existing PENDING reports from the same user for the same prompt, returning 400 Bad Request if a duplicate exists to prevent report spamming.
After passing all checks, the endpoint creates a new PromptReport record and returns { success: true }.
Admin Report Management
Administrators manage report queues through the src/app/api/admin/reports/[id]/route.ts endpoint. This route accepts PATCH requests exclusively from users with the ADMIN role to update the status field of existing reports.
The endpoint validates that the incoming status value conforms to the ReportStatus enum before persisting changes, ensuring the review workflow progresses from PENDING to either REVIEWED or DISMISSED states.
Implementation Examples
Embedding the Report Dialog
Integrate the reporting functionality into any prompt display using the ReportPromptDialog component:
import { ReportPromptDialog } from "@/components/prompts/report-prompt-dialog";
export function PromptActions({ promptId, isLoggedIn }) {
return (
<div className="flex space-x-2">
<ReportPromptDialog promptId={promptId} isLoggedIn={isLoggedIn} />
</div>
);
}
Programmatic API Access
Submit reports programmatically using standard fetch calls with session credentials:
async function reportPrompt(promptId: string, reason: string, details?: string) {
const res = await fetch("https://your-instance.com/api/reports", {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ promptId, reason, details }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error ?? "Report failed");
return data;
}
// Usage example
reportPrompt("abc123", "COPYRIGHT", "Contains proprietary code without license")
.then(() => console.log("Copyright violation reported"))
.catch(console.error);
Admin Status Updates
Administrators can resolve reports by updating their status through the administrative API:
async function updateReportStatus(reportId: string, status: "REVIEWED" | "DISMISSED") {
const res = await fetch(`/api/admin/reports/${reportId}`, {
method: "PATCH",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ status }),
});
if (!res.ok) {
const err = await res.json();
throw new Error(err.error ?? "Failed to update");
}
return await res.json();
}
Summary
- The reporting system in f/prompts.chat uses a three-tier architecture separating UI components, API validation, and admin controls.
- Six report categories cover spam, inappropriate content, copyright violations, misleading information, relist requests, and other issues.
- Business rules prevent self-reporting (except for relist requests) and block duplicate pending reports from the same user.
- Database indexing on
promptId,reporterId, andstatusfields ensures efficient query performance for moderation queues. - Role-based access control restricts report status modifications to users with the
ADMINrole.
Frequently Asked Questions
What report reasons are available in the prompts.chat system?
The system supports six distinct report reasons defined in the ReportReason enum: SPAM for unsolicited promotional content, INAPPROPRIATE for content violating community standards, COPYRIGHT for intellectual property violations, MISLEADING for deceptive or inaccurate prompts, RELIST_REQUEST for authors requesting content republication, and OTHER for edge cases not covered by standard categories.
How does the system prevent users from spamming the report feature?
The API endpoint in src/app/api/reports/route.ts enforces a duplicate check that queries for existing PENDING reports from the same user targeting the same prompt. If such a record exists, the endpoint returns 400 Bad Request, preventing users from flooding the moderation queue with duplicate reports while allowing new reports after existing ones are reviewed or dismissed.
Can users report their own prompts in prompts.chat?
The system generally prohibits self-reporting through a specific business rule that compares the reporter's ID against the prompt's authorId. However, the RELIST_REQUEST reason serves as an exception to this rule, allowing authors to flag their own content specifically for republication requests without triggering the self-reporting protection mechanism.
What database indexes support the reporting functionality?
The PromptReport model in prisma/schema.prisma defines three database indexes: @@index([promptId]) optimizes queries fetching all reports for a specific prompt, @@index([reporterId]) accelerates lookups of a user's reporting history, and @@index([status]) enables efficient filtering of pending reports for the admin moderation queue.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →