first commit
This commit is contained in:
46
src/lib/auth.ts
Normal file
46
src/lib/auth.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { SignJWT, jwtVerify } from 'jose';
|
||||
import bcrypt from 'bcryptjs';
|
||||
|
||||
// Astro dev (Vite) expose les .env via import.meta.env ; Node runtime prod via process.env.
|
||||
// On lit les deux pour couvrir tous les cas (dev, preview, prod Plesk Node).
|
||||
const env = {
|
||||
...(typeof process !== 'undefined' ? process.env : {}),
|
||||
...(import.meta.env as Record<string, string | undefined>),
|
||||
};
|
||||
|
||||
const ADMIN_PASSWORD_HASH = (env.ADMIN_PASSWORD_HASH as string | undefined) ?? '';
|
||||
const JWT_SECRET =
|
||||
(env.ADMIN_JWT_SECRET as string | undefined) ?? 'dev-secret-change-me-in-production';
|
||||
const SECRET_KEY = new TextEncoder().encode(JWT_SECRET);
|
||||
|
||||
export const ADMIN_COOKIE = 'portfolio_admin';
|
||||
|
||||
export async function verifyPassword(password: string): Promise<boolean> {
|
||||
if (!ADMIN_PASSWORD_HASH) {
|
||||
console.error('[auth] ADMIN_PASSWORD_HASH manquant dans .env');
|
||||
return false;
|
||||
}
|
||||
return bcrypt.compare(password, ADMIN_PASSWORD_HASH);
|
||||
}
|
||||
|
||||
export async function createSession(): Promise<string> {
|
||||
return new SignJWT({ role: 'admin' })
|
||||
.setProtectedHeader({ alg: 'HS256' })
|
||||
.setIssuedAt()
|
||||
.setExpirationTime('7d')
|
||||
.sign(SECRET_KEY);
|
||||
}
|
||||
|
||||
export async function verifySession(token: string | undefined): Promise<boolean> {
|
||||
if (!token) return false;
|
||||
try {
|
||||
await jwtVerify(token, SECRET_KEY);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function hashPasswordCLI(password: string): string {
|
||||
return bcrypt.hashSync(password, 12);
|
||||
}
|
||||
40
src/lib/content.ts
Normal file
40
src/lib/content.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { readFile, writeFile } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
import { CONTENT_SCHEMAS, type ContentKey } from '../content/schemas';
|
||||
import type { z } from 'zod';
|
||||
|
||||
const CONTENT_DIR = join(process.cwd(), 'public', 'content');
|
||||
|
||||
type ContentData<K extends ContentKey> = z.infer<(typeof CONTENT_SCHEMAS)[K]>;
|
||||
|
||||
export async function loadContent<K extends ContentKey>(key: K): Promise<ContentData<K>> {
|
||||
const filePath = join(CONTENT_DIR, `${key}.json`);
|
||||
const raw = await readFile(filePath, 'utf-8');
|
||||
const parsed = JSON.parse(raw);
|
||||
const schema = CONTENT_SCHEMAS[key];
|
||||
return schema.parse(parsed) as ContentData<K>;
|
||||
}
|
||||
|
||||
export async function saveContent<K extends ContentKey>(
|
||||
key: K,
|
||||
data: unknown,
|
||||
): Promise<ContentData<K>> {
|
||||
const schema = CONTENT_SCHEMAS[key];
|
||||
const validated = schema.parse(data) as ContentData<K>;
|
||||
const filePath = join(CONTENT_DIR, `${key}.json`);
|
||||
await writeFile(filePath, JSON.stringify(validated, null, 2) + '\n', 'utf-8');
|
||||
return validated;
|
||||
}
|
||||
|
||||
export async function loadAll() {
|
||||
const [profile, skills, experiences, formations, projects, interests, site] = await Promise.all([
|
||||
loadContent('profile'),
|
||||
loadContent('skills'),
|
||||
loadContent('experiences'),
|
||||
loadContent('formations'),
|
||||
loadContent('projects'),
|
||||
loadContent('interests'),
|
||||
loadContent('site'),
|
||||
]);
|
||||
return { profile, skills, experiences, formations, projects, interests, site };
|
||||
}
|
||||
Reference in New Issue
Block a user