Helpers utilitaires
Index des libs internes (`lib/intl.ts`, `lib/auth-guards.ts`, `lib/sanitize.ts`, etc.) pour ne pas réinventer la roue.
Liste des helpers réutilisables — quand tu construis une nouvelle feature, vérifie qu'il n'y en a pas déjà un qui fait le job avant de coder.
lib/auth-guards.ts
import { requireApiAuth } from "@/lib/auth-guards";
const guard = await requireApiAuth({ admin: true });
if (guard instanceof NextResponse) return guard;
const session = guard;- Sans options → user authentifié + 2FA validée
{ admin: true }→ restreint aux ADMIN{ allowPending2fa: true }→ pour les routes 2FA elles-mêmes
lib/intl.ts
| Fonction | Rôle |
|---|---|
intlLocaleFor(locale) | "fr" → "fr-FR", "en" → "en-US", etc. (pour Intl APIs) |
localeLabel(code) | "fr" → "Français", etc. (pour UI) |
stripHtml(html) | retire toutes les balises (pour notifications, twitter cards…) |
stripAndShorten(html, max) | strip + tronque à N caractères |
READABLE_ALPHABET | "ABCDEFGHJKLMNPQRSTUVWXYZ23456789" (sans 0/O/1/I) |
lib/sanitize.ts
Voir Sanitization HTML.
lib/rate-limit.ts + lib/cron-auth.ts
Voir Rate limiting & cron auth.
lib/feature-flags.ts
Voir Feature flags.
lib/two-factor-verify.ts
import { verifyTwoFactorAttempt } from "@/lib/two-factor-verify";
const result = await verifyTwoFactorAttempt(userId, email, code);
// "ok" | "locked" | "invalid"Anti-bruteforce + replay + backup codes atomiques. Voir 2FA.
lib/webhook-idempotence.ts
import { claimWebhookEvent } from "@/lib/webhook-idempotence";
if ((await claimWebhookEvent(event.id, event.type)) === "duplicate") {
return ack();
}Voir Idempotence des webhooks.
lib/email-log.ts
import { logEmail } from "@/lib/email-log";
await logEmail({
to, subject, templateKey: "welcome", providerId: result.data?.id,
});Voir Email logs.
lib/profile-completeness.ts
Pure function. Voir Profile completeness.
lib/totp.ts
import { generateTotpSecret, totpUri, totpQrCode, verifyTotp } from "@/lib/totp";Voir 2FA.
lib/site-config.ts
import { getCachedSiteConfig, pickLocalized } from "@/lib/site-config";
const cfg = await getCachedSiteConfig(); // appName, appUrl, supportEmail…
const text = pickLocalized(plan.name, "fr");lib/audit.ts
import { logAudit } from "@/lib/audit";
await logAudit({
userId: session.user.id,
action: "user.something",
entity: "User",
entityId: targetId,
metadata: { /* ... */ },
req, // optionnel — capture IP + UA
});Voir Audit log.
components/ui/data-list.tsx
Voir DataList. Toutes les listes admin l'utilisent (users, billing, sessions, email logs, activity).
components/ui/responsive-tabs.tsx
Tabs qui se transforment en <select> sur mobile (sm/md/lg breakpoints).
Utilisé partout où on a des tabs (settings, fiche user, etc.).
components/ui/responsive-dialog.tsx
Modal qui adapte son rendu selon le viewport :
- Desktop (≥768px) →
<Dialog>Radix centré, click-outside, X - Mobile (
<768px) →<Drawer>vaul drag-to-close natif
API unique :
<ResponsiveDialog
open={open}
onOpenChange={setOpen}
title="Email"
subheader={<MetadataChips />}
desktopMaxWidth="max-w-3xl"
mobileHeight="h-[90vh]"
>
{body}
</ResponsiveDialog>À utiliser à chaque fois que tu ouvres un panneau de détail / preview /
édition substantielle. Pour les confirms tout courts un <Dialog> simple
suffit — pas besoin du drawer mobile.
lib/use-is-desktop.ts
const isDesktop = useIsDesktop(); // true ≥ 768px, listens to resizeForce mobile via ?mobile=1 dans l'URL pour tester depuis Chrome desktop.
Utilisé par <ResponsiveDialog>, <NotificationBell>, etc.