'use server'; import { executeServerAction, type ActionResult } from '@/lib/api/execute-server-action'; import type { AccountInfo, TermsAgreement, MarketingConsent } from './types'; const API_URL = process.env.NEXT_PUBLIC_API_URL; /** * 상대 경로를 표시 가능한 URL로 변환 * R2 전환 후: /api/proxy/files/{id}/view 사용 * 레거시 경로는 그대로 반환 (표시 불가할 수 있음) */ function toAbsoluteUrl(path: string | undefined): string | undefined { if (!path) return undefined; // 이미 절대 URL이면 그대로 반환 if (path.startsWith('http://') || path.startsWith('https://')) { return path; } // 프록시 경로면 그대로 반환 if (path.startsWith('/api/proxy/')) { return path; } // R2 전환 후 /storage/ 직접 접근 불가 — 경로만 보존 return path; } // ===== 계정 정보 조회 ===== export async function getAccountInfo(): Promise<{ success: boolean; data?: { accountInfo: AccountInfo; termsAgreements: TermsAgreement[]; marketingConsent: MarketingConsent; }; error?: string; __authError?: boolean; }> { // 1. 사용자 기본 정보 조회 // eslint-disable-next-line @typescript-eslint/no-explicit-any const userResult = await executeServerAction({ url: `${API_URL}/api/v1/users/me`, errorMessage: '계정 정보를 불러올 수 없습니다.', }); if (userResult.__authError) return { success: false, __authError: true }; if (!userResult.success || !userResult.data) return { success: false, error: userResult.error }; const user = userResult.data; // 2. 프로필 정보 조회 (프로필 이미지 포함 - 실패해도 계속 진행) let profileImage: string | undefined; // eslint-disable-next-line @typescript-eslint/no-explicit-any const profileResult = await executeServerAction({ url: `${API_URL}/api/v1/profiles/me`, errorMessage: '프로필 조회 실패', }); if (profileResult.success && profileResult.data) { profileImage = toAbsoluteUrl(profileResult.data.profile_photo_path); } return { success: true, data: { accountInfo: { id: user.id?.toString() || '', email: user.email || '', profileImage, role: user.role?.name || user.role || '', status: user.status || 'active', isTenantMaster: user.is_tenant_master || false, createdAt: user.created_at || '', updatedAt: user.updated_at || '', }, termsAgreements: user.terms_agreements || [], marketingConsent: user.marketing_consent || { email: { agreed: false }, sms: { agreed: false }, }, }, }; } // ===== 계정 탈퇴 ===== export async function withdrawAccount(password: string): Promise { return executeServerAction({ url: `${API_URL}/api/v1/users/withdraw`, method: 'POST', body: { password }, errorMessage: '계정 탈퇴에 실패했습니다.', }); } // ===== 테넌트 사용 중지 ===== export async function suspendTenant(): Promise { return executeServerAction({ url: `${API_URL}/api/v1/tenants/suspend`, method: 'POST', body: {}, errorMessage: '사용 중지에 실패했습니다.', }); } // ===== 약관 동의 수정 ===== export async function updateAgreements( agreements: Array<{ type: string; agreed: boolean }> ): Promise { return executeServerAction({ url: `${API_URL}/api/v1/account/agreements`, method: 'PUT', body: { agreements }, errorMessage: '약관 동의 수정에 실패했습니다.', }); } // ===== 프로필 이미지 업로드 ===== export async function uploadProfileImage(formData: FormData): Promise<{ success: boolean; data?: { imageUrl: string }; error?: string; __authError?: boolean; }> { // 1. 파일 업로드 // eslint-disable-next-line @typescript-eslint/no-explicit-any const uploadResult = await executeServerAction({ url: `${API_URL}/api/v1/files/upload`, method: 'POST', body: formData, errorMessage: '파일 업로드에 실패했습니다.', }); if (uploadResult.__authError) return { success: false, __authError: true }; if (!uploadResult.success || !uploadResult.data) return { success: false, error: uploadResult.error }; const uploadedPath = uploadResult.data.file_path || uploadResult.data.path || uploadResult.data.url; if (!uploadedPath) return { success: false, error: '업로드된 파일 경로를 가져올 수 없습니다.' }; // 2. 프로필 업데이트 (업로드된 파일 경로로) const updateResult = await executeServerAction({ url: `${API_URL}/api/v1/profiles/me`, method: 'PATCH', body: { profile_photo_path: uploadedPath }, errorMessage: '프로필 업데이트에 실패했습니다.', }); if (updateResult.__authError) return { success: false, __authError: true }; if (!updateResult.success) return { success: false, error: updateResult.error }; // R2 전환: file_id 기반 프록시 경로 사용 const fileId = uploadResult.data.id; const viewUrl = fileId ? `/api/proxy/files/${fileId}/view` : uploadedPath; return { success: true, data: { imageUrl: viewUrl }, }; }