File name
Commit message
Commit date
File name
Commit message
Commit date
09-14
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
'use server';
import { unstable_rethrow } from 'next/navigation';
import { revalidatePath } from 'next/cache';
import { verifySession } from '@/lib/auth/dal';
import { BackendRequestError } from '@/lib/http/backend-fetch';
import {
createAdminMember,
deleteAdminMember,
isAdminLoginIdTaken,
updateAdminMember,
} from '@/lib/data/repositories/admin-member-repository';
import {
formatPhoneNumber,
validateAdminLoginId,
validateAdminMemberCreate,
validateAdminMemberUpdate,
type AdminMemberEditableValues,
type AdminMemberFormState,
} from '@/lib/domain/admin-member-form';
import { ADMIN_MEMBERS_PATH } from '@/lib/domain/admin-member-query';
const INVALID_REQUEST_MESSAGE = '요청이 올바르지 않습니다.';
const SELF_DELETE_MESSAGE = '현재 로그인한 본인 계정은 삭제할 수 없습니다.';
const DUPLICATE_LOGIN_ID_MESSAGE = '이미 사용 중인 ID입니다.';
const LOGIN_ID_CHECK_FAILED_MESSAGE =
'중복 확인에 실패했습니다. 잠시 후 다시 시도해 주세요.';
function readString(formData: FormData, key: string): string {
const value = formData.get(key);
return typeof value === 'string' ? value : '';
}
function readEditableValues(formData: FormData): AdminMemberEditableValues {
const phoneNumber = formatPhoneNumber(readString(formData, 'phoneNumber'));
return {
password: readString(formData, 'password'),
phoneNumber: phoneNumber ?? '',
email: readString(formData, 'email'),
roleCode: readString(formData, 'roleCode'),
};
}
async function runWrite(
write: () => Promise<void>
): Promise<AdminMemberFormState | null> {
try {
await write();
return null;
} catch (error) {
// 이슈: 세션이 밀렸을 때 backendFetch가 던지는 redirect를 이 catch가 삼키면 안 된다.
unstable_rethrow(error);
if (error instanceof BackendRequestError) {
return { status: 'error', message: error.message };
}
throw error;
}
}
export async function createAdminMemberAction(
_prevState: AdminMemberFormState,
formData: FormData
): Promise<AdminMemberFormState> {
await verifySession();
const validation = validateAdminMemberCreate({
...readEditableValues(formData),
name: readString(formData, 'name'),
loginId: readString(formData, 'loginId'),
});
if (!validation.ok) {
return { status: 'error', errors: validation.errors };
}
const { name, loginId, password, phoneNumber, email, roleCode } =
validation.values;
if (await isAdminLoginIdTaken(loginId)) {
return { status: 'error', errors: { loginId: DUPLICATE_LOGIN_ID_MESSAGE } };
}
const failure = await runWrite(() =>
createAdminMember({ name, loginId, password, phoneNumber, email, roleCode })
);
if (failure) {
return failure;
}
revalidatePath(ADMIN_MEMBERS_PATH);
return { status: 'success' };
}
export async function updateAdminMemberAction(
_prevState: AdminMemberFormState,
formData: FormData
): Promise<AdminMemberFormState> {
await verifySession();
const id = readString(formData, 'id');
if (!id) {
return { status: 'error', message: INVALID_REQUEST_MESSAGE };
}
const validation = validateAdminMemberUpdate(readEditableValues(formData));
if (!validation.ok) {
return { status: 'error', errors: validation.errors };
}
const { password, phoneNumber, email, roleCode } = validation.values;
const failure = await runWrite(() =>
updateAdminMember(id, {
password,
phoneNumber,
email,
roleCode,
isLocked: readString(formData, 'isLocked') === 'true',
})
);
if (failure) {
return failure;
}
revalidatePath(ADMIN_MEMBERS_PATH);
return { status: 'success' };
}
export async function deleteAdminMemberAction(
id: string
): Promise<AdminMemberFormState> {
const admin = await verifySession();
if (!id) {
return { status: 'error', message: INVALID_REQUEST_MESSAGE };
}
if (id === admin.id) {
return { status: 'error', message: SELF_DELETE_MESSAGE };
}
await deleteAdminMember(id);
revalidatePath(ADMIN_MEMBERS_PATH);
return { status: 'success' };
}
export type LoginIdCheckResult =
| { status: 'idle' }
| { status: 'available'; loginId: string }
| { status: 'unavailable'; message: string }
| { status: 'failed'; message: string };
export async function checkAdminLoginId(
loginId: string
): Promise<LoginIdCheckResult> {
await verifySession();
const normalized = loginId.trim();
const formatError = validateAdminLoginId(normalized);
if (formatError) {
return { status: 'unavailable', message: formatError };
}
try {
if (await isAdminLoginIdTaken(normalized)) {
return { status: 'unavailable', message: DUPLICATE_LOGIN_ID_MESSAGE };
}
} catch (error) {
unstable_rethrow(error);
if (error instanceof BackendRequestError) {
return { status: 'failed', message: error.message };
}
return { status: 'failed', message: LOGIN_ID_CHECK_FAILED_MESSAGE };
}
return { status: 'available', loginId: normalized };
}