Contact
Contact owns public lead messages, signed-in contact forms, message validation, rate limiting, persistence, admin replies, and user message history.
On this page
Where to look
| Area | Paths |
|---|---|
| Public route | src/app/(marketing)/contact/page.tsx |
| API route | src/app/api/contact/route.ts |
| Dashboard routes | src/app/(dashboard)/communications/inbox/page.tsx, src/app/(dashboard)/account/messages/page.tsx |
| Server | src/features/contact/server/actions.ts, services.ts, queries.ts, repository.ts, rate-limit.ts |
| Shared schema | src/features/contact/shared/schemas.ts |
| UI | src/features/contact/ui/form.tsx, messages-dashboard.tsx, message-reply-thread.tsx, user-message-history.tsx |
| Database | contact_messages, contact_message_replies |
Code-backed contact map
Public and signed-in entrypoints
Marketing and dashboard contact surfaces submit to a server-owned contact flow.
src/app/(marketing)/contact/page.tsxsrc/app/api/contact/route.tssrc/features/contact/ui/form.tsx
Validation, rate limiting, and persistence
Contact messages validate payloads, enforce public rate limits, write through the repository, and queue notifications.
src/features/contact/shared/schemas.tssrc/features/contact/server/rate-limit.tssrc/features/contact/server/services.tssrc/features/contact/server/repository.ts
Admin inbox and user replies
Admins triage and reply from the communications inbox while signed-in users review their message history and replies from the account area.
src/app/(dashboard)/communications/inbox/page.tsxsrc/app/(dashboard)/account/messages/page.tsxsrc/features/contact/server/queries.tssrc/features/contact/server/actions.tssrc/features/contact/ui/messages-dashboard.tsxsrc/features/contact/ui/message-reply-thread.tsxsrc/features/contact/ui/user-message-history.tsx
Contact message flow
- 1
Validate the submitted message
The public API and signed-in form use contact-owned schemas before persistence.
src/app/api/contact/route.tssrc/features/contact/shared/schemas.ts
- 2
Rate-limit public writes
The service checks the public contact rate-limit policy before storing a message.
src/features/contact/server/rate-limit.tssrc/features/contact/server/services.ts
- 3
Persist and notify
Messages are stored through the repository and related notifications are queued through the notification boundary.
src/features/contact/server/repository.tssrc/features/contact/server/services.ts
- 4
Triage and reply
Admin inbox queries and actions own filtering, status changes, priority, replies, and dashboard counts while users can review the resulting thread.
src/features/contact/server/queries.tssrc/features/contact/server/actions.ts
Public contact rate limit
Source: src/features/contact/server/services.ts
Public contact writes are limited by IP and email before persistence or notifications run.
async function enforcePublicContactRateLimit(params: {
clientIp: string | null;
email: string;
requestId: string;
start: number;
}): Promise<PublicContactServiceResult | null> {
const ratePolicy = getRateLimitPolicy("public_contact");
const rateLimit = await checkRateLimit({
key: buildRateLimitKey({
prefix: ratePolicy.prefix,
ip: params.clientIp,
identifier: params.email,
}),
limit: ratePolicy.limit,
windowSeconds: ratePolicy.windowSeconds,
});
if (rateLimit.allowed) {
return null;
}Contact rules
- Validate public form input at the server boundary.
- Rate-limit public writes.
- Require Turnstile for the public contact API when configured.
- Keep recipient routing server-owned.
- Do not expose internal admin state on public pages.