mirror of
https://gitbruv.vercel.app/api/git/bruv/gitbruv.git
synced 2025-12-20 23:24:09 +01:00
wip
This commit is contained in:
parent
a63628e659
commit
dffc97239e
17 changed files with 1220 additions and 24 deletions
239
actions/settings.ts
Normal file
239
actions/settings.ts
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
"use server";
|
||||
|
||||
import { db } from "@/db";
|
||||
import { users, accounts, repositories } from "@/db/schema";
|
||||
import { getSession } from "@/lib/session";
|
||||
import { eq, and } from "drizzle-orm";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { r2Put, r2Delete } from "@/lib/r2";
|
||||
|
||||
export async function updateProfile(data: {
|
||||
name: string;
|
||||
username: string;
|
||||
bio?: string;
|
||||
location?: string;
|
||||
website?: string;
|
||||
pronouns?: string;
|
||||
}) {
|
||||
const session = await getSession();
|
||||
if (!session?.user) {
|
||||
throw new Error("Unauthorized");
|
||||
}
|
||||
|
||||
const normalizedUsername = data.username.toLowerCase().replace(/\s+/g, "-");
|
||||
|
||||
if (!/^[a-zA-Z0-9_-]+$/.test(normalizedUsername)) {
|
||||
throw new Error("Username can only contain letters, numbers, underscores, and hyphens");
|
||||
}
|
||||
|
||||
if (normalizedUsername.length < 3) {
|
||||
throw new Error("Username must be at least 3 characters");
|
||||
}
|
||||
|
||||
const existingUser = await db.query.users.findFirst({
|
||||
where: and(
|
||||
eq(users.username, normalizedUsername),
|
||||
),
|
||||
});
|
||||
|
||||
if (existingUser && existingUser.id !== session.user.id) {
|
||||
throw new Error("Username is already taken");
|
||||
}
|
||||
|
||||
await db
|
||||
.update(users)
|
||||
.set({
|
||||
name: data.name,
|
||||
username: normalizedUsername,
|
||||
bio: data.bio || null,
|
||||
location: data.location || null,
|
||||
website: data.website || null,
|
||||
pronouns: data.pronouns || null,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(users.id, session.user.id));
|
||||
|
||||
revalidatePath("/settings");
|
||||
revalidatePath(`/${normalizedUsername}`);
|
||||
|
||||
return { success: true, username: normalizedUsername };
|
||||
}
|
||||
|
||||
export async function updateSocialLinks(data: {
|
||||
github?: string;
|
||||
twitter?: string;
|
||||
linkedin?: string;
|
||||
custom?: string[];
|
||||
}) {
|
||||
const session = await getSession();
|
||||
if (!session?.user) {
|
||||
throw new Error("Unauthorized");
|
||||
}
|
||||
|
||||
const socialLinks = {
|
||||
github: data.github || undefined,
|
||||
twitter: data.twitter || undefined,
|
||||
linkedin: data.linkedin || undefined,
|
||||
custom: data.custom?.filter(Boolean) || undefined,
|
||||
};
|
||||
|
||||
await db
|
||||
.update(users)
|
||||
.set({
|
||||
socialLinks,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(users.id, session.user.id));
|
||||
|
||||
revalidatePath("/settings");
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
export async function updateAvatar(formData: FormData) {
|
||||
const session = await getSession();
|
||||
if (!session?.user) {
|
||||
throw new Error("Unauthorized");
|
||||
}
|
||||
|
||||
const file = formData.get("avatar") as File;
|
||||
if (!file || file.size === 0) {
|
||||
throw new Error("No file provided");
|
||||
}
|
||||
|
||||
if (!file.type.startsWith("image/")) {
|
||||
throw new Error("File must be an image");
|
||||
}
|
||||
|
||||
if (file.size > 5 * 1024 * 1024) {
|
||||
throw new Error("File size must be less than 5MB");
|
||||
}
|
||||
|
||||
const ext = file.name.split(".").pop() || "png";
|
||||
const key = `avatars/${session.user.id}.${ext}`;
|
||||
|
||||
const buffer = Buffer.from(await file.arrayBuffer());
|
||||
await r2Put(key, buffer);
|
||||
|
||||
const avatarUrl = `/api/avatar/${session.user.id}.${ext}`;
|
||||
|
||||
await db
|
||||
.update(users)
|
||||
.set({
|
||||
image: avatarUrl,
|
||||
avatarUrl,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(users.id, session.user.id));
|
||||
|
||||
revalidatePath("/settings");
|
||||
revalidatePath("/");
|
||||
|
||||
return { success: true, avatarUrl };
|
||||
}
|
||||
|
||||
export async function updateEmail(data: { email: string }) {
|
||||
const session = await getSession();
|
||||
if (!session?.user) {
|
||||
throw new Error("Unauthorized");
|
||||
}
|
||||
|
||||
const existingUser = await db.query.users.findFirst({
|
||||
where: eq(users.email, data.email),
|
||||
});
|
||||
|
||||
if (existingUser && existingUser.id !== session.user.id) {
|
||||
throw new Error("Email is already in use");
|
||||
}
|
||||
|
||||
await db
|
||||
.update(users)
|
||||
.set({
|
||||
email: data.email,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(users.id, session.user.id));
|
||||
|
||||
revalidatePath("/settings/account");
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
export async function updatePassword(data: {
|
||||
currentPassword: string;
|
||||
newPassword: string;
|
||||
}) {
|
||||
const session = await getSession();
|
||||
if (!session?.user) {
|
||||
throw new Error("Unauthorized");
|
||||
}
|
||||
|
||||
const user = await db.query.users.findFirst({
|
||||
where: eq(users.id, session.user.id),
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new Error("User not found");
|
||||
}
|
||||
|
||||
try {
|
||||
await auth.api.signInEmail({
|
||||
body: { email: user.email, password: data.currentPassword },
|
||||
});
|
||||
} catch {
|
||||
throw new Error("Current password is incorrect");
|
||||
}
|
||||
|
||||
await auth.api.changePassword({
|
||||
body: {
|
||||
currentPassword: data.currentPassword,
|
||||
newPassword: data.newPassword,
|
||||
},
|
||||
headers: {
|
||||
cookie: `better-auth.session_token=${session.session.token}`,
|
||||
},
|
||||
});
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
export async function deleteAccount() {
|
||||
const session = await getSession();
|
||||
if (!session?.user) {
|
||||
throw new Error("Unauthorized");
|
||||
}
|
||||
|
||||
const userRepos = await db.query.repositories.findMany({
|
||||
where: eq(repositories.ownerId, session.user.id),
|
||||
});
|
||||
|
||||
const { r2DeletePrefix } = await import("@/lib/r2");
|
||||
for (const repo of userRepos) {
|
||||
try {
|
||||
await r2DeletePrefix(`repos/${session.user.id}/${repo.name}.git`);
|
||||
} catch {}
|
||||
}
|
||||
|
||||
try {
|
||||
await r2Delete(`avatars/${session.user.id}`);
|
||||
} catch {}
|
||||
|
||||
await db.delete(users).where(eq(users.id, session.user.id));
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
export async function getCurrentUser() {
|
||||
const session = await getSession();
|
||||
if (!session?.user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const user = await db.query.users.findFirst({
|
||||
where: eq(users.id, session.user.id),
|
||||
});
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue