"use server"; import { revalidatePath } from "next/cache"; import { addSshKey, deleteSshKey, KeyError } from "@/lib/keys"; import { canWrite, findRepoById, repoAccess } from "@/lib/repos"; import { createToken, deleteToken, TokenError, type TokenPermission } from "@/lib/tokens"; import { setPassword, updateProfile, UserError, validatePassword, verifyUserPassword } from "@/lib/users"; import { viewerOrThrow } from "./guard"; import type { FormState } from "./types"; export async function updateProfileAction(_prev: FormState, formData: FormData): Promise { const viewer = await viewerOrThrow(); try { updateProfile(viewer.id, { name: String(formData.get("name") ?? ""), email: String(formData.get("email") ?? ""), bio: String(formData.get("bio") ?? ""), }); } catch (e) { if (e instanceof UserError) return { error: e.message }; throw e; } revalidatePath("/", "layout"); return { success: "Profile updated." }; } export async function changePasswordAction(_prev: FormState, formData: FormData): Promise { const viewer = await viewerOrThrow(); const current = String(formData.get("current") ?? ""); const next = String(formData.get("password") ?? ""); if (!(await verifyUserPassword(viewer.id, current))) return { error: "Your current password is incorrect." }; if (next !== formData.get("confirm")) return { error: "The new passwords don't match." }; const problem = validatePassword(next); if (problem) return { error: problem }; await setPassword(viewer.id, next); return { success: "Password changed." }; } export async function addSshKeyAction(_prev: FormState, formData: FormData): Promise { const viewer = await viewerOrThrow(); try { addSshKey(viewer.id, String(formData.get("title") ?? ""), String(formData.get("key") ?? "")); } catch (e) { if (e instanceof KeyError) return { error: e.message }; throw e; } revalidatePath("/settings/keys"); return { success: "SSH key added." }; } export async function deleteSshKeyAction(keyId: number) { const viewer = await viewerOrThrow(); deleteSshKey(viewer.id, keyId); revalidatePath("/settings/keys"); } export async function createTokenAction(_prev: FormState, formData: FormData): Promise { const viewer = await viewerOrThrow(); const permission: TokenPermission = formData.get("permission") === "read" ? "read" : "write"; const expiry = String(formData.get("expiry") ?? "30"); let repoIds: number[] | null = null; if (formData.get("scope") === "selected") { repoIds = formData .getAll("repos") .map(Number) .filter((id) => { const repo = findRepoById(id); const access = repo ? repoAccess(repo, viewer.id) : null; return access && (permission === "read" || canWrite(access)); }); } try { const token = createToken(viewer.id, { name: String(formData.get("name") ?? ""), permission, expiresInDays: expiry === "never" ? null : Number(expiry) || 30, repoIds, }); revalidatePath("/settings/tokens"); return { token }; } catch (e) { if (e instanceof TokenError) return { error: e.message }; throw e; } } export async function deleteTokenAction(tokenId: number) { const viewer = await viewerOrThrow(); deleteToken(viewer.id, tokenId); revalidatePath("/settings/tokens"); }