generated from ttipo/project-base
feat(backend): 비밀번호 변경 · 메일함 관리 · 부재중 자동 응답 · 스팸 표시 · 원문 API
- PUT /api/auth/password: 직원 본인 자격으로 엔진의 x:AccountPassword/set(게이트웨이 · 관리자 권한 없이), 로그인과 같은 실패 카운터 · 진행 중 잠금.
바뀌면 리프레시 쿠키의 세션만 남기고 끊는다 — 결과를 모르는 실패에도 끊는다. 엔진이 바꾸기만 거절하면 세지 않는다
- 메일함 만들기 · 이름 바꾸기 · 지우기(안의 메일은 휴지통으로, onDestroyRemoveEmails false), 모두 읽음 · 휴지통 / 스팸메일함 비우기(조건부 지우기)
— 500통씩 · 한 요청 25초 상한(엔진 호출마다 남은 시간), 같은 직원 · 같은 메일함은 하나씩(REQ_001), 진행 없는 차례가 이어지면 멈춘다
- GET · PUT /api/mail-vacation(켤 때만 제목 · 내용 필수, 제어 문자 검사, 초 단위), PATCH /api/messages(/{id}) 의 isJunk,
GET /api/messages/{id}/source(message/rfc822)
- 에러 코드 AUTH_005 · MAIL_009 추가와 클라이언트 사전 다시 생성, 공용 길이 검사 데코레이터(JS 문자열 길이)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
import { toWholeSecondIso } from './format';
|
||||
|
||||
describe('toWholeSecondIso', () => {
|
||||
it('소수 초를 버리고 UTC ISO 문자열로 돌려준다 — 올리지 않는다', () => {
|
||||
expect(toWholeSecondIso('2026-11-02T15:00:00.999Z')).toBe(
|
||||
'2026-11-02T15:00:00.000Z',
|
||||
);
|
||||
expect(toWholeSecondIso('2026-11-02T15:00:00.5Z')).toBe(
|
||||
'2026-11-02T15:00:00.000Z',
|
||||
);
|
||||
expect(toWholeSecondIso('2026-11-02T15:00:00Z')).toBe(
|
||||
'2026-11-02T15:00:00.000Z',
|
||||
);
|
||||
expect(toWholeSecondIso('2026-11-02T15:00:01.000Z')).toBe(
|
||||
'2026-11-02T15:00:01.000Z',
|
||||
);
|
||||
});
|
||||
|
||||
it('1970년 이전 시각도 앞쪽으로 버린다 (음수 시각에서 다음 초로 올라가지 않게)', () => {
|
||||
expect(toWholeSecondIso('1969-12-31T23:59:59.500Z')).toBe(
|
||||
'1969-12-31T23:59:59.000Z',
|
||||
);
|
||||
});
|
||||
|
||||
it('시각으로 읽을 수 없는 값은 그대로 돌려준다 (형식 검사는 부르는 쪽 몫)', () => {
|
||||
expect(toWholeSecondIso('내일')).toBe('내일');
|
||||
});
|
||||
});
|
||||
@@ -11,3 +11,17 @@ export function formatDate(input: Date | string): string {
|
||||
const dd = String(date.getDate()).padStart(2, '0');
|
||||
return `${yyyy}-${mm}-${dd}`;
|
||||
}
|
||||
|
||||
/** 1초 (밀리초) */
|
||||
const SECOND_MS = 1_000;
|
||||
|
||||
/**
|
||||
* ISO 8601 시각을 초 단위로 맞춘다 — 소수 초를 버리고 UTC ISO 문자열('…:00.000Z')로 돌려준다.
|
||||
* 초 단위로만 받는 곳(메일 엔진의 UTCDate 등)에 보낼 값과, 그 값으로 하는 비교를 같게 하려고 쓴다.
|
||||
* 시각으로 읽을 수 없는 값은 그대로 돌려준다 (형식 검사는 부르는 쪽이 먼저 한다)
|
||||
*/
|
||||
export function toWholeSecondIso(iso: string): string {
|
||||
const time = Date.parse(iso);
|
||||
if (Number.isNaN(time)) return iso;
|
||||
return new Date(Math.floor(time / SECOND_MS) * SECOND_MS).toISOString();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user