Files
sam-react-prod/src/components/settings/CompanyInfoManagement/actions.ts
유병철 55e0791e16 refactor(WEB): Server Action 공통화 및 보안 강화
- executeServerAction 공통 유틸 도입으로 actions.ts 대폭 간소화 (50+개 파일)
- sanitize 유틸 추가 (XSS 방지)
- middleware CSP 헤더 추가 및 Open Redirect 방지
- 프록시 라우트 로깅 개발환경 한정으로 변경
- 프로덕션 불필요 console.log 제거

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 16:14:06 +09:00

132 lines
4.4 KiB
TypeScript

'use server';
import { executeServerAction, type ActionResult } from '@/lib/api/execute-server-action';
import type { CompanyFormData } from './types';
const API_URL = process.env.NEXT_PUBLIC_API_URL;
// API 응답 타입
interface TenantApiData {
id: number;
company_name: string;
code?: string;
email?: string;
phone?: string;
address?: string;
business_num?: string;
corp_reg_no?: string;
ceo_name?: string;
homepage?: string;
fax?: string;
logo?: string;
options?: {
business_type?: string;
business_category?: string;
zip_code?: string;
address_detail?: string;
tax_invoice_email?: string;
manager_name?: string;
payment_bank?: string;
payment_account?: string;
payment_account_holder?: string;
payment_day?: string;
};
created_at?: string;
updated_at?: string;
}
// ===== 테넌트 정보 조회 =====
export async function getCompanyInfo(): Promise<ActionResult<CompanyFormData & { tenantId: number }>> {
return executeServerAction({
url: `${API_URL}/api/v1/tenants`,
transform: (data: TenantApiData) => transformApiToFrontend(data),
errorMessage: '회사 정보 조회에 실패했습니다.',
});
}
// ===== 테넌트 정보 수정 =====
export async function updateCompanyInfo(
tenantId: number,
data: Partial<CompanyFormData>
): Promise<ActionResult<CompanyFormData & { tenantId: number }>> {
return executeServerAction({
url: `${API_URL}/api/v1/tenants`,
method: 'PUT',
body: transformFrontendToApi(tenantId, data),
transform: (d: TenantApiData) => transformApiToFrontend(d),
errorMessage: '회사 정보 수정에 실패했습니다.',
});
}
// ===== 회사 로고 업로드 =====
export async function uploadCompanyLogo(formData: FormData): Promise<ActionResult<{ logoUrl: string }>> {
return executeServerAction({
url: `${API_URL}/api/v1/tenants/logo`,
method: 'POST',
body: formData,
transform: (data: { logo_url?: string }) => ({
logoUrl: toAbsoluteUrl(data?.logo_url) || '',
}),
errorMessage: '로고 업로드에 실패했습니다.',
});
}
// ===== 유틸리티 =====
function toAbsoluteUrl(path: string | undefined): string | undefined {
if (!path) return undefined;
if (path.startsWith('http://') || path.startsWith('https://')) return path;
const apiUrl = process.env.NEXT_PUBLIC_API_URL || '';
return `${apiUrl}${path}`;
}
function transformApiToFrontend(apiData: TenantApiData): CompanyFormData & { tenantId: number } {
const opts = apiData.options || {};
return {
tenantId: apiData.id,
companyName: apiData.company_name || '',
representativeName: apiData.ceo_name || '',
email: apiData.email || '',
managerPhone: apiData.phone || '',
businessNumber: apiData.business_num || '',
address: apiData.address || '',
companyLogo: toAbsoluteUrl(apiData.logo),
businessType: opts.business_type || '',
businessCategory: opts.business_category || '',
zipCode: opts.zip_code || '',
addressDetail: opts.address_detail || '',
taxInvoiceEmail: opts.tax_invoice_email || '',
managerName: opts.manager_name || '',
businessLicense: undefined,
paymentBank: opts.payment_bank || '',
paymentAccount: opts.payment_account || '',
paymentAccountHolder: opts.payment_account_holder || '',
paymentDay: opts.payment_day || '',
};
}
function transformFrontendToApi(tenantId: number, data: Partial<CompanyFormData>): Record<string, unknown> {
let logoPath: string | null = null;
if (data.companyLogo && typeof data.companyLogo === 'string') {
const apiUrl = process.env.NEXT_PUBLIC_API_URL || '';
logoPath = data.companyLogo.startsWith(apiUrl) ? data.companyLogo.replace(apiUrl, '') : data.companyLogo;
}
return {
tenant_id: tenantId,
company_name: data.companyName,
ceo_name: data.representativeName,
email: data.email,
phone: data.managerPhone,
business_num: data.businessNumber,
logo: logoPath,
address: [data.zipCode, data.address, data.addressDetail].filter(Boolean).join(' '),
options: {
business_type: data.businessType, business_category: data.businessCategory,
zip_code: data.zipCode, address_detail: data.addressDetail,
tax_invoice_email: data.taxInvoiceEmail, manager_name: data.managerName,
payment_bank: data.paymentBank, payment_account: data.paymentAccount,
payment_account_holder: data.paymentAccountHolder, payment_day: data.paymentDay,
},
};
}