gucci
This commit is contained in:
80
lib/auth.ts
Normal file
80
lib/auth.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { cookies } from "next/headers";
|
||||
import { SignJWT, jwtVerify } from "jose";
|
||||
import bcrypt from "bcryptjs";
|
||||
import { prisma } from "./db";
|
||||
|
||||
const SESSION_COOKIE_NAME = "ftp_session";
|
||||
const SESSION_MAX_AGE = 60 * 60 * 24 * 7; // 7 days
|
||||
|
||||
type SessionPayload = {
|
||||
userId: string;
|
||||
email: string;
|
||||
};
|
||||
|
||||
function getJwtSecret() {
|
||||
const secret = process.env.JWT_SECRET;
|
||||
if (!secret) {
|
||||
throw new Error("JWT_SECRET is not set");
|
||||
}
|
||||
return new TextEncoder().encode(secret);
|
||||
}
|
||||
|
||||
export async function hashPassword(password: string) {
|
||||
return bcrypt.hash(password, 10);
|
||||
}
|
||||
|
||||
export async function verifyPassword(password: string, hash: string) {
|
||||
return bcrypt.compare(password, hash);
|
||||
}
|
||||
|
||||
export async function createSessionToken(payload: SessionPayload) {
|
||||
const secret = getJwtSecret();
|
||||
return new SignJWT(payload)
|
||||
.setProtectedHeader({ alg: "HS256" })
|
||||
.setIssuedAt()
|
||||
.setExpirationTime(`${SESSION_MAX_AGE}s`)
|
||||
.sign(secret);
|
||||
}
|
||||
|
||||
export async function verifySessionToken(token: string) {
|
||||
try {
|
||||
const { payload } = await jwtVerify<SessionPayload>(token, getJwtSecret());
|
||||
return payload;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function setSessionCookie(token: string) {
|
||||
const store = await cookies();
|
||||
store.set(SESSION_COOKIE_NAME, token, {
|
||||
httpOnly: true,
|
||||
sameSite: "lax",
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
path: "/",
|
||||
maxAge: SESSION_MAX_AGE,
|
||||
});
|
||||
}
|
||||
|
||||
export async function clearSessionCookie() {
|
||||
const store = await cookies();
|
||||
store.delete(SESSION_COOKIE_NAME);
|
||||
}
|
||||
|
||||
export async function getSessionUser() {
|
||||
const store = await cookies();
|
||||
const token = store.get(SESSION_COOKIE_NAME)?.value;
|
||||
if (!token) return null;
|
||||
const payload = await verifySessionToken(token);
|
||||
if (!payload) return null;
|
||||
return payload;
|
||||
}
|
||||
|
||||
export async function getCurrentUserRecord() {
|
||||
const session = await getSessionUser();
|
||||
if (!session) return null;
|
||||
return prisma.user.findUnique({ where: { id: session.userId } });
|
||||
}
|
||||
|
||||
export { SESSION_COOKIE_NAME, SESSION_MAX_AGE };
|
||||
|
||||
16
lib/db.ts
Normal file
16
lib/db.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
const globalForPrisma = globalThis as unknown as {
|
||||
prisma?: PrismaClient;
|
||||
};
|
||||
|
||||
export const prisma =
|
||||
globalForPrisma.prisma ??
|
||||
new PrismaClient({
|
||||
log: process.env.NODE_ENV === "development" ? ["query", "error"] : ["error"],
|
||||
});
|
||||
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
globalForPrisma.prisma = prisma;
|
||||
}
|
||||
|
||||
74
lib/r2.ts
Normal file
74
lib/r2.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import {
|
||||
S3Client,
|
||||
PutObjectCommand,
|
||||
DeleteObjectCommand,
|
||||
GetObjectCommand,
|
||||
} from "@aws-sdk/client-s3";
|
||||
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
||||
|
||||
let cachedClient: S3Client | null = null;
|
||||
let cachedBucket: string | null = null;
|
||||
|
||||
function getClient() {
|
||||
if (cachedClient && cachedBucket) {
|
||||
return { s3: cachedClient, bucket: cachedBucket };
|
||||
}
|
||||
|
||||
const {
|
||||
R2_ENDPOINT,
|
||||
R2_REGION = "auto",
|
||||
R2_ACCESS_KEY_ID,
|
||||
R2_SECRET_ACCESS_KEY,
|
||||
R2_BUCKET,
|
||||
} = process.env;
|
||||
|
||||
if (!R2_ENDPOINT || !R2_ACCESS_KEY_ID || !R2_SECRET_ACCESS_KEY || !R2_BUCKET) {
|
||||
throw new Error("R2 configuration is missing. Check env variables.");
|
||||
}
|
||||
|
||||
cachedBucket = R2_BUCKET;
|
||||
cachedClient = new S3Client({
|
||||
endpoint: R2_ENDPOINT,
|
||||
region: R2_REGION,
|
||||
credentials: {
|
||||
accessKeyId: R2_ACCESS_KEY_ID,
|
||||
secretAccessKey: R2_SECRET_ACCESS_KEY,
|
||||
},
|
||||
});
|
||||
|
||||
return { s3: cachedClient, bucket: cachedBucket };
|
||||
}
|
||||
|
||||
export async function uploadToR2(params: {
|
||||
key: string;
|
||||
contentType: string;
|
||||
body: Buffer;
|
||||
}) {
|
||||
const { s3, bucket } = getClient();
|
||||
const command = new PutObjectCommand({
|
||||
Bucket: bucket,
|
||||
Key: params.key,
|
||||
Body: params.body,
|
||||
ContentType: params.contentType,
|
||||
});
|
||||
await s3.send(command);
|
||||
}
|
||||
|
||||
export async function getSignedDownloadUrl(key: string, expiresInSeconds = 300) {
|
||||
const { s3, bucket } = getClient();
|
||||
const command = new GetObjectCommand({
|
||||
Bucket: bucket,
|
||||
Key: key,
|
||||
});
|
||||
return getSignedUrl(s3, command, { expiresIn: expiresInSeconds });
|
||||
}
|
||||
|
||||
export async function deleteFromR2(key: string) {
|
||||
const { s3, bucket } = getClient();
|
||||
const command = new DeleteObjectCommand({
|
||||
Bucket: bucket,
|
||||
Key: key,
|
||||
});
|
||||
await s3.send(command);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user