commit 12754f719bb25aa0b1fae3877e44533fe292b8df
parent 2e2e48617364f07ae8d37c3e9d98023d2301b840
Author: Yongbin Kim <iam@yongbin.kim>
Date: Sun, 29 Jan 2023 14:42:05 +0900
refactor(model): db.query 대신 modelBehaviour를 사용하도록 수정
Signed-off-by: Yongbin Kim <iam@yongbin.kim>
Diffstat:
7 files changed, 85 insertions(+), 64 deletions(-)
diff --git a/lib/models/login_info.ts b/lib/models/login_info.ts
@@ -17,8 +17,14 @@ const SQL_GET_LOGIN_INFO = `
LIMIT 1
`
-export async function getLoginInfo (id: number): Promise<LoginInfo | null> {
- const [rows] = await db.query<RowDataPacket[]>(SQL_GET_LOGIN_INFO, [id])
+export const getLoginInfo = modelBehaviour<
+ [id: number],
+ LoginInfo | null
+>(async (conn, args) => {
+ const [rows] = await conn.query<RowDataPacket[]>(
+ SQL_GET_LOGIN_INFO,
+ args,
+ )
if (rows.length === 0) {
return null
}
@@ -32,7 +38,7 @@ export async function getLoginInfo (id: number): Promise<LoginInfo | null> {
createdAt: row[3],
updatedAt: row[4],
}
-}
+})
const SQL_GET_LOGIN_INFO_VIA_EMAIL = `
SELECT id, email, password_hash, created_at, updated_at
@@ -68,10 +74,13 @@ const SQL_CREATE_LOGIN_INFO = `
VALUES (?, ?)
`
-export async function createLoginInfo (email: string, passwordHash: string): Promise<number> {
- const [result] = await db.query<OkPacket>({
+export const createLoginInfo = modelBehaviour<
+ [email: string, passwordHash: string],
+ number
+>(async (conn, args) => {
+ const [result] = await conn.query<OkPacket>({
sql: SQL_CREATE_LOGIN_INFO,
- }, [email, passwordHash])
+ }, args)
return result.insertId
-}
+})
diff --git a/lib/models/signup_request.ts b/lib/models/signup_request.ts
@@ -1,4 +1,5 @@
import db from '@/lib/db'
+import { modelBehaviour } from '@/lib/model_helpers'
import { OkPacket, RowDataPacket } from 'mysql2'
export interface SignupRequest {
@@ -15,17 +16,16 @@ const SQL_CREATE_SIGNUP_REQUEST = `
VALUES (?, ?, ?)
`
-export async function createSignupRequest (
- email: string,
- requestedIP: string,
- requestedUserAgent: string,
-): Promise<number> {
- const [result] = await db.query<OkPacket>({
+export const createSignupRequest = modelBehaviour<
+ [ email: string, requestedIP: string, requestedUserAgent: string ],
+ number
+>(async (conn, args) => {
+ const [result] = await conn.query<OkPacket>({
sql: SQL_CREATE_SIGNUP_REQUEST,
- }, [email, requestedIP, requestedUserAgent])
+ }, args)
return result.insertId
-}
+})
const SQL_GET_UNCONFIRMED_SIGNUP_REQUEST = `
SELECT id, email, requested_ip, requested_user_agent, created_at, confirmed_at
@@ -36,21 +36,19 @@ const SQL_GET_UNCONFIRMED_SIGNUP_REQUEST = `
LIMIT 1
`
-export async function getUnconfirmedSignupRequest (id: number | string): Promise<SignupRequest | null> {
- if (typeof id === 'string') {
- id = parseInt(id, 10)
- }
-
- const [rows] = await db.query<RowDataPacket[]>({
+export const getUnconfirmedSignupRequest = modelBehaviour<
+ [ id: number ],
+ SignupRequest | null
+>(async (conn, args) => {
+ const [rows] = await conn.query<RowDataPacket[]>({
sql: SQL_GET_UNCONFIRMED_SIGNUP_REQUEST,
- }, [id])
+ }, args)
if (rows.length === 0) {
return null
}
const row = rows[0]
- console.log(row)
return {
id: row[0],
email: row[1],
@@ -59,7 +57,7 @@ export async function getUnconfirmedSignupRequest (id: number | string): Promise
createdAt: row[4],
confirmedAt: row[5],
}
-}
+})
const SQL_CONFIRM_SIGNUP_REQUEST = `
UPDATE signup_requests
@@ -67,8 +65,11 @@ const SQL_CONFIRM_SIGNUP_REQUEST = `
WHERE id = ?
`
-export async function confirmSignupRequest (id: number): Promise<void> {
- await db.query<OkPacket>({
+export const confirmSignupRequest = modelBehaviour<
+ [ id: number ],
+ void
+>(async (conn, args) => {
+ await conn.query<OkPacket>({
sql: SQL_CONFIRM_SIGNUP_REQUEST,
- }, [id])
-}
+ }, args)
+})
diff --git a/lib/models/user_profile.ts b/lib/models/user_profile.ts
@@ -1,4 +1,5 @@
import db from '@/lib/db'
+import { modelBehaviour } from '@/lib/model_helpers'
import { OkPacket, RowDataPacket } from 'mysql2'
export interface UserProfile {
@@ -13,15 +14,14 @@ const SQL_CREATE_USER_PROFILE = `
VALUES (?, ?, ?)
`
-export async function createUserProfile (
- loginId: number,
- nickname: string,
- bio: string | null,
-) {
- await db.query<OkPacket>({
+export const createUserProfile = modelBehaviour<
+ [ loginId: number, nickname: string, bio: string | null ],
+ void
+>(async (conn, args) => {
+ await conn.query<OkPacket>({
sql: SQL_CREATE_USER_PROFILE,
- }, [loginId, nickname, bio])
-}
+ }, args)
+})
const SQL_GET_USER_PROFILE = `
SELECT login_id, nickname, bio, updated_at
@@ -30,13 +30,16 @@ const SQL_GET_USER_PROFILE = `
LIMIT 1
`
-export async function getUserProfile (loginId: number): Promise<UserProfile | null> {
- const [rows] = await db.query<RowDataPacket[]>({
+export const getUserProfile = modelBehaviour<
+ [ loginId: number ],
+ UserProfile | null
+>(async (conn, args) => {
+ const [rows] = await conn.query<RowDataPacket[]>({
sql: SQL_GET_USER_PROFILE,
rowsAsArray: true,
- }, [loginId])
+ }, args)
- if (!Array.isArray(rows) || rows.length === 0) {
+ if (rows.length === 0) {
return null
}
@@ -47,4 +50,4 @@ export async function getUserProfile (loginId: number): Promise<UserProfile | nu
bio: row[2],
updatedAt: row[3],
}
-}
+})
diff --git a/lib/models/wiki_info.ts b/lib/models/wiki_info.ts
@@ -58,7 +58,7 @@ export const listWikiByOwnerId = modelBehaviour<
[ ownerId: number ],
WikiInfo[]
>(async (conn, args) => {
- const [rows] = await db.query<RowDataPacket[]>({
+ const [rows] = await conn.query<RowDataPacket[]>({
sql: SQL_LIST_WIKI_BY_OWNER_ID,
}, [args])
@@ -84,7 +84,7 @@ export const getWiki = modelBehaviour<
[ id: number ],
WikiInfo | null
>(async (conn, args) => {
- const [rows] = await db.query<RowDataPacket[]>({
+ const [rows] = await conn.query<RowDataPacket[]>({
sql: SQL_GET_WIKI,
}, [args])
@@ -116,7 +116,7 @@ export const getWikiViaSlug = modelBehaviour<
[slug: string],
WikiInfo | null
>(async (conn, args) => {
- const [rows] = await db.query<RowDataPacket[]>({
+ const [rows] = await conn.query<RowDataPacket[]>({
sql: SQL_GET_WIKI_VIA_SLUG,
}, args)
diff --git a/pages/api/users/[id]/index.ts b/pages/api/users/[id]/index.ts
@@ -32,7 +32,7 @@ export default async function handler (
}
// 사용자 정보를 받아옴
- const profile = await getUserProfile(id)
+ const profile = await getUserProfile([id])
if (profile == null) {
res.status(404).json(ERR_NOT_FOUND)
return
diff --git a/pages/api/users/signup/[id].ts b/pages/api/users/signup/[id].ts
@@ -1,10 +1,12 @@
-import { ApiError, ERR_METHOD_NOT_ALLOWED } from '@/lib/apierror'
+import { ApiError, ERR_INTERNAL, ERR_METHOD_NOT_ALLOWED } from '@/lib/apierror'
+import { withConnection } from '@/lib/model_helpers'
import { createLoginInfo } from '@/lib/models/login_info'
import {
confirmSignupRequest,
getUnconfirmedSignupRequest,
} from '@/lib/models/signup_request'
import { createUserProfile } from '@/lib/models/user_profile'
+import { parseIntOrDefault } from '@/lib/utils/number'
import bcrypt from 'bcrypt'
import { NextApiRequest, NextApiResponse } from 'next'
@@ -22,8 +24,8 @@ export default async function handler (
return
}
- const { id } = req.query
- if (typeof id !== 'string') {
+ const id = parseIntOrDefault(req.query.id, -1)
+ if (id < 0) {
res.status(500)
.json({ code: 'internal error', message: 'id is null' })
return
@@ -45,24 +47,30 @@ export default async function handler (
return
}
- // 요청 정보가 존재하는지 확인
- const signupRequest = await getUnconfirmedSignupRequest(id)
- if (signupRequest == null) {
- res.status(404).json({
- code: 'signup_request_not_found',
- message: 'signup request was not found or expired',
- })
- return
- }
-
// 패스워드 해시
const passwordHash = await bcrypt.hash(password, 10)
- // TODO: 트랜잭션으로 묶기
- await confirmSignupRequest(signupRequest.id)
+ await withConnection(async (conn) => {
+ try {
+ // 요청 정보가 존재하는지 확인
+ const signupRequest = await getUnconfirmedSignupRequest(conn, [id])
+ if (signupRequest == null) {
+ res.status(404).json({
+ code: 'signup_request_not_found',
+ message: 'signup request was not found or expired',
+ })
+ return
+ }
+
+ await confirmSignupRequest(conn, [signupRequest.id])
- const userId = await createLoginInfo(signupRequest.email, passwordHash)
- await createUserProfile(userId, nickname, null)
+ const userId = await createLoginInfo(conn, [signupRequest.email, passwordHash])
+ await createUserProfile(conn, [userId, nickname, null])
- res.status(201).json({ status: 'ok' })
+ res.status(201).json({ status: 'ok' })
+ } catch (e) {
+ console.error('failed to confirm signup request', e)
+ res.status(500).json(ERR_INTERNAL)
+ }
+ })
}
diff --git a/pages/api/users/signup/index.ts b/pages/api/users/signup/index.ts
@@ -20,7 +20,7 @@ export default async function handler (
const requestedIP = getIPAddress(req)
const requestedUserAgent = req.headers['user-agent'] ?? ''
- const requestID = await createSignupRequest(email, requestedIP, requestedUserAgent)
+ const requestID = await createSignupRequest([email, requestedIP, requestedUserAgent])
const requestURL = `${process.env.WIKI_SITE_URL ?? 'http://localhost'}/users/signup/${requestID}`
await sendEmail(