mirror of
https://github.com/arthur-pbty/contact.git
synced 2026-06-03 23:36:30 +02:00
feat: add contact page with form handling and validation
- Implemented a contact page with a form for user inquiries. - Added validation for form fields using Zod schema. - Integrated PostgreSQL database for storing contact messages. - Created necessary API endpoints for form submission. - Added admin authentication and session management. - Developed CGU, cookies policy, privacy policy, and legal mentions pages. - Set up Docker configuration for PostgreSQL and application services. - Enhanced UI with responsive design and accessibility features.
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
import { createHmac, timingSafeEqual } from "crypto";
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
const COOKIE_NAME = "admin_session";
|
||||
const SESSION_TTL_SECONDS = 60 * 60 * 12;
|
||||
|
||||
type SessionPayload = {
|
||||
sub: string;
|
||||
exp: number;
|
||||
};
|
||||
|
||||
function getSecret() {
|
||||
const secret = process.env.ADMIN_SESSION_SECRET;
|
||||
if (!secret) {
|
||||
throw new Error("ADMIN_SESSION_SECRET is missing.");
|
||||
}
|
||||
return secret;
|
||||
}
|
||||
|
||||
function encodeBase64Url(value: string) {
|
||||
return Buffer.from(value).toString("base64url");
|
||||
}
|
||||
|
||||
function decodeBase64Url(value: string) {
|
||||
return Buffer.from(value, "base64url").toString();
|
||||
}
|
||||
|
||||
function sign(value: string) {
|
||||
return createHmac("sha256", getSecret()).update(value).digest("base64url");
|
||||
}
|
||||
|
||||
function safeEqual(a: string, b: string) {
|
||||
const aBuffer = Buffer.from(a);
|
||||
const bBuffer = Buffer.from(b);
|
||||
if (aBuffer.length !== bBuffer.length) {
|
||||
return false;
|
||||
}
|
||||
return timingSafeEqual(aBuffer, bBuffer);
|
||||
}
|
||||
|
||||
export function createAdminToken(username: string) {
|
||||
const payload: SessionPayload = {
|
||||
sub: username,
|
||||
exp: Math.floor(Date.now() / 1000) + SESSION_TTL_SECONDS,
|
||||
};
|
||||
|
||||
const encodedPayload = encodeBase64Url(JSON.stringify(payload));
|
||||
const signature = sign(encodedPayload);
|
||||
return `${encodedPayload}.${signature}`;
|
||||
}
|
||||
|
||||
function verifyToken(token: string): SessionPayload | null {
|
||||
const [payloadPart, signaturePart] = token.split(".");
|
||||
if (!payloadPart || !signaturePart) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const expectedSignature = sign(payloadPart);
|
||||
if (!safeEqual(signaturePart, expectedSignature)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const payload = JSON.parse(decodeBase64Url(payloadPart)) as SessionPayload;
|
||||
if (!payload?.sub || !payload?.exp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (payload.exp < Math.floor(Date.now() / 1000)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return payload;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function isAdminAuthenticated() {
|
||||
const cookieStore = await cookies();
|
||||
const token = cookieStore.get(COOKIE_NAME)?.value;
|
||||
if (!token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Boolean(verifyToken(token));
|
||||
}
|
||||
|
||||
export function getAdminCredentials() {
|
||||
return {
|
||||
username: process.env.ADMIN_USERNAME || "admin",
|
||||
password: process.env.ADMIN_PASSWORD || "change-me",
|
||||
};
|
||||
}
|
||||
|
||||
export function getAdminCookieName() {
|
||||
return COOKIE_NAME;
|
||||
}
|
||||
Reference in New Issue
Block a user