feat: add local signup and Kakao verification gate
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
import { TRPCError } from '@trpc/server';
|
||||
import { z } from 'zod';
|
||||
|
||||
import type { PasswordEnvelopeInput, PasswordEnvelopeService } from './passwordEnvelope.js';
|
||||
|
||||
const utf8Length = (value: string): number => Buffer.byteLength(value, 'utf8');
|
||||
|
||||
const isWideCodePoint = (codePoint: number): boolean =>
|
||||
codePoint >= 0x1100 &&
|
||||
(codePoint <= 0x115f ||
|
||||
codePoint === 0x2329 ||
|
||||
codePoint === 0x232a ||
|
||||
(codePoint >= 0x2e80 && codePoint <= 0xa4cf && codePoint !== 0x303f) ||
|
||||
(codePoint >= 0xac00 && codePoint <= 0xd7a3) ||
|
||||
(codePoint >= 0xf900 && codePoint <= 0xfaff) ||
|
||||
(codePoint >= 0xfe10 && codePoint <= 0xfe19) ||
|
||||
(codePoint >= 0xfe30 && codePoint <= 0xfe6f) ||
|
||||
(codePoint >= 0xff00 && codePoint <= 0xff60) ||
|
||||
(codePoint >= 0xffe0 && codePoint <= 0xffe6) ||
|
||||
(codePoint >= 0x1f300 && codePoint <= 0x1f64f) ||
|
||||
(codePoint >= 0x1f900 && codePoint <= 0x1f9ff) ||
|
||||
(codePoint >= 0x20000 && codePoint <= 0x3fffd));
|
||||
|
||||
export const legacyStringWidth = (value: string): number =>
|
||||
Array.from(value).reduce((width, character) => {
|
||||
const codePoint = character.codePointAt(0) ?? 0;
|
||||
return width + (isWideCodePoint(codePoint) ? 2 : 1);
|
||||
}, 0);
|
||||
|
||||
export const zPasswordEnvelope = z.object({
|
||||
keyId: z.string().min(1).max(128),
|
||||
ciphertext: z.string().min(1).max(4096),
|
||||
});
|
||||
|
||||
export const zRegistrationUsername = z
|
||||
.string()
|
||||
.trim()
|
||||
.transform((value) => value.toLocaleLowerCase('en-US'))
|
||||
.superRefine((value, context) => {
|
||||
const length = utf8Length(value);
|
||||
if (length < 4 || length > 64) {
|
||||
context.addIssue({
|
||||
code: 'custom',
|
||||
message: '계정명은 UTF-8 기준 4~64바이트여야 합니다.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const zDisplayName = z
|
||||
.string()
|
||||
.trim()
|
||||
.superRefine((value, context) => {
|
||||
const width = legacyStringWidth(value);
|
||||
if (width < 1 || width > 18) {
|
||||
context.addIssue({
|
||||
code: 'custom',
|
||||
message: '닉네임은 영문 기준 1~18자 너비여야 합니다.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const openPassword = (service: PasswordEnvelopeService, envelope: PasswordEnvelopeInput): string => {
|
||||
let password: string;
|
||||
try {
|
||||
password = service.open(envelope);
|
||||
} catch (error) {
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: error instanceof Error ? error.message : '비밀번호 봉인을 열 수 없습니다.',
|
||||
});
|
||||
}
|
||||
if (Array.from(password).length < 6 || Buffer.byteLength(password, 'utf8') > 128) {
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: '비밀번호는 6자 이상, UTF-8 기준 128바이트 이하여야 합니다.',
|
||||
});
|
||||
}
|
||||
return password;
|
||||
};
|
||||
Reference in New Issue
Block a user