feat(web): Next.js 15 app shell, auth, i18n, base UI
App Router setup with next-intl (en/fr), Better Auth wiring, shadcn/ui components, Tailwind, AES-256-GCM encrypt util, Redis rate limiter, tier limit checker, site_settings helper for runtime .env overrides.
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
import { createCipheriv, createDecipheriv, randomBytes, createHash } from "crypto";
|
||||
|
||||
const ALGORITHM = "aes-256-gcm";
|
||||
|
||||
function getKey(): Buffer {
|
||||
const secret = process.env["BETTER_AUTH_SECRET"] ?? "dev-secret-needs-32-bytes-padding!";
|
||||
return createHash("sha256").update(secret).digest();
|
||||
}
|
||||
|
||||
export function encrypt(plaintext: string): string {
|
||||
const key = getKey();
|
||||
const iv = randomBytes(12);
|
||||
const cipher = createCipheriv(ALGORITHM, key, iv);
|
||||
const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
||||
const authTag = (cipher as ReturnType<typeof createCipheriv> & { getAuthTag(): Buffer }).getAuthTag();
|
||||
return `${iv.toString("hex")}:${authTag.toString("hex")}:${encrypted.toString("hex")}`;
|
||||
}
|
||||
|
||||
export function decrypt(ciphertext: string): string {
|
||||
const parts = ciphertext.split(":");
|
||||
if (parts.length !== 3) throw new Error("Invalid ciphertext format");
|
||||
const [ivHex, authTagHex, encryptedHex] = parts as [string, string, string];
|
||||
const key = getKey();
|
||||
const iv = Buffer.from(ivHex, "hex");
|
||||
const authTag = Buffer.from(authTagHex, "hex");
|
||||
const encrypted = Buffer.from(encryptedHex, "hex");
|
||||
const decipher = createDecipheriv(ALGORITHM, key, iv);
|
||||
(decipher as ReturnType<typeof createDecipheriv> & { setAuthTag(tag: Buffer): void }).setAuthTag(authTag);
|
||||
return Buffer.concat([decipher.update(encrypted), decipher.final()]).toString("utf8");
|
||||
}
|
||||
Reference in New Issue
Block a user