mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2025-11-17 14:33:41 +08:00
cap nhat auth he thong de nhung voi chebichat
This commit is contained in:
174
app/api/auth/callback/route.ts
Normal file
174
app/api/auth/callback/route.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { createClient } from "@supabase/supabase-js";
|
||||
|
||||
const SUPABASE_URL = process.env.SUPABASE_URL!;
|
||||
const SUPABASE_ANON_KEY = process.env.SUPABASE_ANON_KEY!;
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
const url = new URL(req.url);
|
||||
const authToken = url.searchParams.get("token");
|
||||
const access_token = url.searchParams.get("access_token");
|
||||
const refresh_token = url.searchParams.get("refresh_token");
|
||||
const redirectTo = url.searchParams.get("redirect_to") || "/";
|
||||
|
||||
console.log("[Auth Callback] Processing authentication callback");
|
||||
console.log("[Auth Callback] authToken:", authToken);
|
||||
console.log("[Auth Callback] access_token:", access_token);
|
||||
console.log("[Auth Callback] refresh_token:", refresh_token);
|
||||
|
||||
// Use either authToken or access_token (flexible for different auth flows)
|
||||
const token = authToken || access_token;
|
||||
|
||||
if (!token) {
|
||||
console.log(
|
||||
"[Auth Callback] No authentication token found in query string",
|
||||
);
|
||||
return NextResponse.redirect(new URL("/login?error=no_token", req.url));
|
||||
}
|
||||
|
||||
try {
|
||||
// Validate the token with Supabase
|
||||
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
|
||||
global: {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { data, error } = await supabase.auth.getUser();
|
||||
|
||||
if (error || !data?.user) {
|
||||
console.error("[Auth Callback] Invalid token:", error);
|
||||
return NextResponse.redirect(
|
||||
new URL("/login?error=invalid_token", req.url),
|
||||
);
|
||||
}
|
||||
|
||||
console.log(
|
||||
"[Auth Callback] Token validated successfully for user:",
|
||||
data.user.id,
|
||||
);
|
||||
|
||||
// Create response with redirect
|
||||
const response = NextResponse.redirect(new URL(redirectTo, req.url));
|
||||
|
||||
// Set authentication cookies
|
||||
const cookieOptions = {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: "lax" as const,
|
||||
maxAge: 60 * 60 * 24 * 7, // 7 days
|
||||
path: "/",
|
||||
};
|
||||
|
||||
// Set access token cookie
|
||||
response.cookies.set("sb-access-token", token, cookieOptions);
|
||||
|
||||
// Set refresh token if available
|
||||
if (refresh_token) {
|
||||
response.cookies.set("sb-refresh-token", refresh_token, cookieOptions);
|
||||
}
|
||||
|
||||
// Set user info cookie (optional, for quick access)
|
||||
response.cookies.set(
|
||||
"sb-user-info",
|
||||
JSON.stringify({
|
||||
id: data.user.id,
|
||||
email: data.user.email,
|
||||
user_metadata: data.user.user_metadata,
|
||||
}),
|
||||
{
|
||||
...cookieOptions,
|
||||
httpOnly: false, // Allow client-side access for user info
|
||||
},
|
||||
);
|
||||
|
||||
console.log("[Auth Callback] Authentication cookies set successfully");
|
||||
|
||||
return response;
|
||||
} catch (err) {
|
||||
console.error("[Auth Callback] Error processing authentication:", err);
|
||||
return NextResponse.redirect(new URL("/login?error=auth_failed", req.url));
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
// Handle POST requests for programmatic token setting
|
||||
try {
|
||||
const body = await req.json();
|
||||
const { access_token, refresh_token, redirect_to = "/" } = body;
|
||||
|
||||
if (!access_token) {
|
||||
return NextResponse.json(
|
||||
{ error: "access_token is required" },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
// Validate the token
|
||||
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
|
||||
global: {
|
||||
headers: {
|
||||
Authorization: `Bearer ${access_token}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { data, error } = await supabase.auth.getUser();
|
||||
|
||||
if (error || !data?.user) {
|
||||
return NextResponse.json(
|
||||
{ error: "Invalid token", details: error },
|
||||
{ status: 401 },
|
||||
);
|
||||
}
|
||||
|
||||
// Create response
|
||||
const response = NextResponse.json({
|
||||
success: true,
|
||||
user: {
|
||||
id: data.user.id,
|
||||
email: data.user.email,
|
||||
user_metadata: data.user.user_metadata,
|
||||
},
|
||||
redirect_to,
|
||||
});
|
||||
|
||||
// Set authentication cookies
|
||||
const cookieOptions = {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: "lax" as const,
|
||||
maxAge: 60 * 60 * 24 * 7, // 7 days
|
||||
path: "/",
|
||||
};
|
||||
|
||||
response.cookies.set("sb-access-token", access_token, cookieOptions);
|
||||
|
||||
if (refresh_token) {
|
||||
response.cookies.set("sb-refresh-token", refresh_token, cookieOptions);
|
||||
}
|
||||
|
||||
response.cookies.set(
|
||||
"sb-user-info",
|
||||
JSON.stringify({
|
||||
id: data.user.id,
|
||||
email: data.user.email,
|
||||
user_metadata: data.user.user_metadata,
|
||||
}),
|
||||
{
|
||||
...cookieOptions,
|
||||
httpOnly: false,
|
||||
},
|
||||
);
|
||||
|
||||
return response;
|
||||
} catch (err) {
|
||||
console.error("[Auth Callback POST] Error:", err);
|
||||
return NextResponse.json(
|
||||
{ error: "Internal server error" },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,54 @@
|
||||
// /app/api/auth/check/route.ts
|
||||
import { NextRequest } from "next/server";
|
||||
import { checkAuth } from "../../supabase";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { checkAuthWithRefresh, getUserInfoFromCookie } from "../../supabase";
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
const user = await checkAuth(req);
|
||||
try {
|
||||
const authResult = await checkAuthWithRefresh(req);
|
||||
const userInfo = getUserInfoFromCookie(req);
|
||||
|
||||
console.log("[Auth] user ", user);
|
||||
console.log("[Auth Check] user:", authResult.user?.email || "none");
|
||||
|
||||
if (!user) {
|
||||
return new Response(JSON.stringify({ authenticated: false }), {
|
||||
status: 401,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
if (!authResult.user) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
authenticated: false,
|
||||
error: "No valid authentication found",
|
||||
},
|
||||
{ status: 401 },
|
||||
);
|
||||
}
|
||||
|
||||
// Create response
|
||||
const response = NextResponse.json({
|
||||
authenticated: true,
|
||||
user: authResult.user,
|
||||
userInfo: userInfo, // Include cached user info for quick access
|
||||
});
|
||||
}
|
||||
|
||||
return new Response(JSON.stringify({ authenticated: true, user }), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
// If token was refreshed, merge the refreshed cookies
|
||||
if (authResult.needsRefresh && authResult.response) {
|
||||
// Copy cookies from refresh response
|
||||
authResult.response.cookies.getAll().forEach((cookie) => {
|
||||
response.cookies.set(cookie.name, cookie.value, {
|
||||
httpOnly: cookie.httpOnly,
|
||||
secure: cookie.secure,
|
||||
sameSite: cookie.sameSite,
|
||||
maxAge: cookie.maxAge,
|
||||
path: cookie.path,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error("[Auth Check] Error:", error);
|
||||
return NextResponse.json(
|
||||
{
|
||||
authenticated: false,
|
||||
error: "Authentication check failed",
|
||||
},
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
68
app/api/auth/logout/route.ts
Normal file
68
app/api/auth/logout/route.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
console.log("[Auth Logout] Processing logout request");
|
||||
|
||||
const redirectTo =
|
||||
new URL(req.url).searchParams.get("redirect_to") || "/login";
|
||||
|
||||
// Create response
|
||||
const response = NextResponse.json({
|
||||
success: true,
|
||||
message: "Logged out successfully",
|
||||
});
|
||||
|
||||
// Clear authentication cookies
|
||||
const cookieOptions = {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: "lax" as const,
|
||||
maxAge: 0, // Expire immediately
|
||||
path: "/",
|
||||
};
|
||||
|
||||
response.cookies.set("sb-access-token", "", cookieOptions);
|
||||
response.cookies.set("sb-refresh-token", "", cookieOptions);
|
||||
response.cookies.set("sb-user-info", "", {
|
||||
...cookieOptions,
|
||||
httpOnly: false,
|
||||
});
|
||||
|
||||
console.log("[Auth Logout] Authentication cookies cleared");
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
// Handle GET requests with redirect
|
||||
const url = new URL(req.url);
|
||||
const redirectTo = url.searchParams.get("redirect_to") || "/login";
|
||||
|
||||
console.log("[Auth Logout] Processing logout request with redirect");
|
||||
|
||||
// Create redirect response
|
||||
const response = NextResponse.redirect(new URL(redirectTo, req.url));
|
||||
|
||||
// Clear authentication cookies
|
||||
const cookieOptions = {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: "lax" as const,
|
||||
maxAge: 0, // Expire immediately
|
||||
path: "/",
|
||||
};
|
||||
|
||||
response.cookies.set("sb-access-token", "", cookieOptions);
|
||||
response.cookies.set("sb-refresh-token", "", cookieOptions);
|
||||
response.cookies.set("sb-user-info", "", {
|
||||
...cookieOptions,
|
||||
httpOnly: false,
|
||||
});
|
||||
|
||||
console.log(
|
||||
"[Auth Logout] Authentication cookies cleared, redirecting to:",
|
||||
redirectTo,
|
||||
);
|
||||
|
||||
return response;
|
||||
}
|
||||
99
app/api/auth/refresh/route.ts
Normal file
99
app/api/auth/refresh/route.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { createClient } from "@supabase/supabase-js";
|
||||
|
||||
const SUPABASE_URL = process.env.SUPABASE_URL!;
|
||||
const SUPABASE_ANON_KEY = process.env.SUPABASE_ANON_KEY!;
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
console.log("[Auth Refresh] Processing token refresh request");
|
||||
|
||||
const refreshToken = req.cookies.get("sb-refresh-token")?.value;
|
||||
|
||||
if (!refreshToken) {
|
||||
console.log("[Auth Refresh] No refresh token found");
|
||||
return NextResponse.json(
|
||||
{ error: "No refresh token found" },
|
||||
{ status: 401 },
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
|
||||
|
||||
const { data, error } = await supabase.auth.refreshSession({
|
||||
refresh_token: refreshToken,
|
||||
});
|
||||
|
||||
if (error || !data?.session) {
|
||||
console.error("[Auth Refresh] Token refresh failed:", error);
|
||||
return NextResponse.json(
|
||||
{ error: "Token refresh failed", details: error },
|
||||
{ status: 401 },
|
||||
);
|
||||
}
|
||||
|
||||
console.log(
|
||||
"[Auth Refresh] Token refreshed successfully for user:",
|
||||
data.session.user.id,
|
||||
);
|
||||
|
||||
// Create response
|
||||
const response = NextResponse.json({
|
||||
success: true,
|
||||
user: {
|
||||
id: data.session.user.id,
|
||||
email: data.session.user.email,
|
||||
user_metadata: data.session.user.user_metadata,
|
||||
},
|
||||
session: {
|
||||
access_token: data.session.access_token,
|
||||
expires_at: data.session.expires_at,
|
||||
},
|
||||
});
|
||||
|
||||
// Update authentication cookies
|
||||
const cookieOptions = {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: "lax" as const,
|
||||
maxAge: 60 * 60 * 24 * 7, // 7 days
|
||||
path: "/",
|
||||
};
|
||||
|
||||
response.cookies.set(
|
||||
"sb-access-token",
|
||||
data.session.access_token,
|
||||
cookieOptions,
|
||||
);
|
||||
|
||||
if (data.session.refresh_token) {
|
||||
response.cookies.set(
|
||||
"sb-refresh-token",
|
||||
data.session.refresh_token,
|
||||
cookieOptions,
|
||||
);
|
||||
}
|
||||
|
||||
// Update user info cookie
|
||||
response.cookies.set(
|
||||
"sb-user-info",
|
||||
JSON.stringify({
|
||||
id: data.session.user.id,
|
||||
email: data.session.user.email,
|
||||
user_metadata: data.session.user.user_metadata,
|
||||
}),
|
||||
{
|
||||
...cookieOptions,
|
||||
httpOnly: false,
|
||||
},
|
||||
);
|
||||
|
||||
return response;
|
||||
} catch (err) {
|
||||
console.error("[Auth Refresh] Error:", err);
|
||||
return NextResponse.json(
|
||||
{ error: "Internal server error" },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createClient } from "@supabase/supabase-js";
|
||||
import { NextRequest } from "next/server";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
const SUPABASE_URL = process.env.SUPABASE_URL!;
|
||||
const SUPABASE_ANON_KEY = process.env.SUPABASE_ANON_KEY!;
|
||||
@@ -42,3 +42,155 @@ export async function checkAuth(req: NextRequest) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Enhanced auth check with token refresh capability
|
||||
export async function checkAuthWithRefresh(req: NextRequest): Promise<{
|
||||
user: any | null;
|
||||
response?: NextResponse;
|
||||
needsRefresh?: boolean;
|
||||
}> {
|
||||
const authToken = req.cookies.get("sb-access-token")?.value;
|
||||
const refreshToken = req.cookies.get("sb-refresh-token")?.value;
|
||||
|
||||
if (!authToken) {
|
||||
console.log("[Supabase] No auth token found");
|
||||
return { user: null };
|
||||
}
|
||||
|
||||
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
|
||||
global: {
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const { data, error } = await supabase.auth.getUser();
|
||||
|
||||
if (error || !data?.user) {
|
||||
// Token might be expired, try to refresh if refresh token is available
|
||||
if (refreshToken && error?.message?.includes("JWT expired")) {
|
||||
console.log("[Supabase] Access token expired, attempting refresh");
|
||||
|
||||
try {
|
||||
const { data: refreshData, error: refreshError } =
|
||||
await supabase.auth.refreshSession({
|
||||
refresh_token: refreshToken,
|
||||
});
|
||||
|
||||
if (refreshError || !refreshData?.session) {
|
||||
console.error("[Supabase] Token refresh failed:", refreshError);
|
||||
return { user: null };
|
||||
}
|
||||
|
||||
console.log("[Supabase] Token refreshed successfully");
|
||||
|
||||
// Create response with updated cookies
|
||||
const response = new NextResponse();
|
||||
const cookieOptions = {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: "lax" as const,
|
||||
maxAge: 60 * 60 * 24 * 7, // 7 days
|
||||
path: "/",
|
||||
};
|
||||
|
||||
response.cookies.set(
|
||||
"sb-access-token",
|
||||
refreshData.session.access_token,
|
||||
cookieOptions,
|
||||
);
|
||||
|
||||
if (refreshData.session.refresh_token) {
|
||||
response.cookies.set(
|
||||
"sb-refresh-token",
|
||||
refreshData.session.refresh_token,
|
||||
cookieOptions,
|
||||
);
|
||||
}
|
||||
|
||||
// Update user info cookie
|
||||
response.cookies.set(
|
||||
"sb-user-info",
|
||||
JSON.stringify({
|
||||
id: refreshData.session.user.id,
|
||||
email: refreshData.session.user.email,
|
||||
user_metadata: refreshData.session.user.user_metadata,
|
||||
}),
|
||||
{
|
||||
...cookieOptions,
|
||||
httpOnly: false,
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
user: refreshData.session.user,
|
||||
response,
|
||||
needsRefresh: true,
|
||||
};
|
||||
} catch (refreshErr) {
|
||||
console.error("[Supabase] Error during token refresh:", refreshErr);
|
||||
return { user: null };
|
||||
}
|
||||
}
|
||||
|
||||
console.error("[Supabase] Error getting user:", error);
|
||||
return { user: null };
|
||||
}
|
||||
|
||||
console.log("[Supabase] Authenticated user:", data.user);
|
||||
return { user: data.user };
|
||||
} catch (err) {
|
||||
console.error("[Supabase] Error fetching user data:", err);
|
||||
return { user: null };
|
||||
}
|
||||
}
|
||||
|
||||
// Utility function to get user info from cookie (client-side accessible)
|
||||
export function getUserInfoFromCookie(req: NextRequest) {
|
||||
const userInfoCookie = req.cookies.get("sb-user-info")?.value;
|
||||
|
||||
if (!userInfoCookie) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(userInfoCookie);
|
||||
} catch (err) {
|
||||
console.error("[Supabase] Error parsing user info cookie:", err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Middleware helper to protect routes
|
||||
export function createAuthMiddleware(protectedPaths: string[] = []) {
|
||||
return async function authMiddleware(req: NextRequest) {
|
||||
const { pathname } = req.nextUrl;
|
||||
|
||||
// Check if this path requires authentication
|
||||
const isProtectedPath = protectedPaths.some(
|
||||
(path) => pathname.startsWith(path) || pathname === path,
|
||||
);
|
||||
|
||||
if (!isProtectedPath) {
|
||||
return NextResponse.next();
|
||||
}
|
||||
|
||||
const authResult = await checkAuthWithRefresh(req);
|
||||
|
||||
if (!authResult.user) {
|
||||
// Redirect to login if not authenticated
|
||||
const loginUrl = new URL("/login", req.url);
|
||||
loginUrl.searchParams.set("redirect_to", pathname);
|
||||
return NextResponse.redirect(loginUrl);
|
||||
}
|
||||
|
||||
// If token was refreshed, return the response with updated cookies
|
||||
if (authResult.needsRefresh && authResult.response) {
|
||||
return authResult.response;
|
||||
}
|
||||
|
||||
return NextResponse.next();
|
||||
};
|
||||
}
|
||||
|
||||
102
app/api/user/route.ts
Normal file
102
app/api/user/route.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { checkAuthWithRefresh } from "../supabase";
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
console.log("[User API] Processing user info request");
|
||||
|
||||
try {
|
||||
const authResult = await checkAuthWithRefresh(req);
|
||||
|
||||
if (!authResult.user) {
|
||||
return NextResponse.json(
|
||||
{ error: "Authentication required" },
|
||||
{ status: 401 },
|
||||
);
|
||||
}
|
||||
|
||||
console.log("[User API] Returning user info for:", authResult.user.email);
|
||||
|
||||
// Create response
|
||||
const response = NextResponse.json({
|
||||
user: {
|
||||
id: authResult.user.id,
|
||||
email: authResult.user.email,
|
||||
user_metadata: authResult.user.user_metadata,
|
||||
created_at: authResult.user.created_at,
|
||||
updated_at: authResult.user.updated_at,
|
||||
last_sign_in_at: authResult.user.last_sign_in_at,
|
||||
},
|
||||
});
|
||||
|
||||
// If token was refreshed, merge the refreshed cookies
|
||||
if (authResult.needsRefresh && authResult.response) {
|
||||
authResult.response.cookies.getAll().forEach((cookie: any) => {
|
||||
response.cookies.set(cookie.name, cookie.value, {
|
||||
httpOnly: cookie.httpOnly,
|
||||
secure: cookie.secure,
|
||||
sameSite: cookie.sameSite,
|
||||
maxAge: cookie.maxAge,
|
||||
path: cookie.path,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error("[User API] Error:", error);
|
||||
return NextResponse.json(
|
||||
{ error: "Failed to fetch user information" },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function PUT(req: NextRequest) {
|
||||
console.log("[User API] Processing user update request");
|
||||
|
||||
try {
|
||||
const authResult = await checkAuthWithRefresh(req);
|
||||
|
||||
if (!authResult.user) {
|
||||
return NextResponse.json(
|
||||
{ error: "Authentication required" },
|
||||
{ status: 401 },
|
||||
);
|
||||
}
|
||||
|
||||
const body = await req.json();
|
||||
const { user_metadata } = body;
|
||||
|
||||
// Note: In a real application, you would update the user in Supabase here
|
||||
// For now, we'll just return the current user data
|
||||
console.log("[User API] User update requested for:", authResult.user.email);
|
||||
console.log("[User API] Update data:", user_metadata);
|
||||
|
||||
// Create response
|
||||
const response = NextResponse.json({
|
||||
user: authResult.user,
|
||||
message: "User update functionality would be implemented here",
|
||||
});
|
||||
|
||||
// If token was refreshed, merge the refreshed cookies
|
||||
if (authResult.needsRefresh && authResult.response) {
|
||||
authResult.response.cookies.getAll().forEach((cookie: any) => {
|
||||
response.cookies.set(cookie.name, cookie.value, {
|
||||
httpOnly: cookie.httpOnly,
|
||||
secure: cookie.secure,
|
||||
sameSite: cookie.sameSite,
|
||||
maxAge: cookie.maxAge,
|
||||
path: cookie.path,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error("[User API] Error:", error);
|
||||
return NextResponse.json(
|
||||
{ error: "Failed to update user information" },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user