- Wire up real user data to sidebar, header, and account modal - Add functional profile editing (first name, last name) via WorkOS API - Add password change functionality via WorkOS API - Add logout functionality to sidebar and header dropdowns - Migrate from manual WorkOS SDK to @workos-inc/authkit-nextjs - Add server-side input validation with Zod schemas for all auth routes - Add shared validation schemas for auth, users, teams, schedule, financial - Fix 30-second auto-logout by properly handling refresh tokens - Add SidebarUser type and toSidebarUser helper for UI components - Add getInitials utility for avatar fallbacks - Document rate limiting configuration for Cloudflare WAF - Fix login page Suspense boundary for Next.js 15 compatibility - Remove obsolete workos-client.ts in favor of authkit helpers Co-authored-by: Nicholai <nicholaivogelfilms@gmail.com>
134 lines
3.3 KiB
TypeScript
Executable File
134 lines
3.3 KiB
TypeScript
Executable File
"use server"
|
|
|
|
import { getWorkOS, signOut } from "@workos-inc/authkit-nextjs"
|
|
import { getCloudflareContext } from "@opennextjs/cloudflare"
|
|
import { getDb } from "@/db"
|
|
import { users } from "@/db/schema"
|
|
import { eq } from "drizzle-orm"
|
|
import { requireAuth } from "@/lib/auth"
|
|
import {
|
|
updateProfileSchema,
|
|
changePasswordSchema,
|
|
type UpdateProfileInput,
|
|
type ChangePasswordInput,
|
|
} from "@/lib/validations/profile"
|
|
|
|
type ActionResult<T = undefined> =
|
|
| { success: true; data?: T }
|
|
| { success: false; error: string }
|
|
|
|
/**
|
|
* Update the current user's profile (first name, last name)
|
|
*/
|
|
export async function updateProfile(
|
|
input: UpdateProfileInput
|
|
): Promise<ActionResult> {
|
|
try {
|
|
// Validate input
|
|
const parsed = updateProfileSchema.safeParse(input)
|
|
if (!parsed.success) {
|
|
return {
|
|
success: false,
|
|
error: parsed.error.issues[0]?.message ?? "Invalid input",
|
|
}
|
|
}
|
|
|
|
const { firstName, lastName } = parsed.data
|
|
|
|
// Get current authenticated user
|
|
const currentUser = await requireAuth()
|
|
|
|
// Update in WorkOS
|
|
const workos = getWorkOS()
|
|
await workos.userManagement.updateUser({
|
|
userId: currentUser.id,
|
|
firstName,
|
|
lastName,
|
|
})
|
|
|
|
// Update in local database
|
|
const { env } = await getCloudflareContext()
|
|
if (env?.DB) {
|
|
const db = getDb(env.DB)
|
|
const now = new Date().toISOString()
|
|
const displayName = `${firstName} ${lastName}`.trim()
|
|
|
|
await db
|
|
.update(users)
|
|
.set({
|
|
firstName,
|
|
lastName,
|
|
displayName,
|
|
updatedAt: now,
|
|
})
|
|
.where(eq(users.id, currentUser.id))
|
|
.run()
|
|
}
|
|
|
|
return { success: true }
|
|
} catch (error) {
|
|
console.error("Error updating profile:", error)
|
|
return {
|
|
success: false,
|
|
error: error instanceof Error ? error.message : "Failed to update profile",
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Change the current user's password
|
|
* Note: WorkOS doesn't verify the current password via API - this is a UX-only field.
|
|
* For production, consider implementing a proper password verification flow.
|
|
*/
|
|
export async function changePassword(
|
|
input: ChangePasswordInput
|
|
): Promise<ActionResult> {
|
|
try {
|
|
// Validate input
|
|
const parsed = changePasswordSchema.safeParse(input)
|
|
if (!parsed.success) {
|
|
return {
|
|
success: false,
|
|
error: parsed.error.issues[0]?.message ?? "Invalid input",
|
|
}
|
|
}
|
|
|
|
const { newPassword } = parsed.data
|
|
|
|
// Get current authenticated user
|
|
const currentUser = await requireAuth()
|
|
|
|
// Update password in WorkOS
|
|
const workos = getWorkOS()
|
|
await workos.userManagement.updateUser({
|
|
userId: currentUser.id,
|
|
password: newPassword,
|
|
})
|
|
|
|
return { success: true }
|
|
} catch (error) {
|
|
console.error("Error changing password:", error)
|
|
|
|
// Handle specific WorkOS errors
|
|
const errorMessage =
|
|
error instanceof Error ? error.message : "Failed to change password"
|
|
|
|
// Check for common error patterns
|
|
if (errorMessage.includes("password")) {
|
|
return {
|
|
success: false,
|
|
error: "Unable to change password. You may have signed in with a social provider.",
|
|
}
|
|
}
|
|
|
|
return { success: false, error: errorMessage }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sign out the current user
|
|
*/
|
|
export async function logout(): Promise<void> {
|
|
await signOut()
|
|
}
|