2026-01-16 21:38:54 +09:00
|
|
|
import bcrypt from 'bcrypt';
|
|
|
|
|
|
|
|
|
|
/**
|
2026-01-17 13:01:35 +09:00
|
|
|
* 인증 라우트
|
|
|
|
|
* /api/auth/*
|
2026-01-16 21:38:54 +09:00
|
|
|
*/
|
2026-01-17 13:01:35 +09:00
|
|
|
export default async function authRoutes(fastify, opts) {
|
2026-01-16 21:38:54 +09:00
|
|
|
/**
|
2026-01-17 13:01:35 +09:00
|
|
|
* POST /api/auth/login
|
2026-01-16 21:38:54 +09:00
|
|
|
* 관리자 로그인
|
|
|
|
|
*/
|
2026-01-17 13:01:35 +09:00
|
|
|
fastify.post('/login', {
|
|
|
|
|
schema: {
|
|
|
|
|
tags: ['auth'],
|
|
|
|
|
summary: '관리자 로그인',
|
|
|
|
|
body: {
|
|
|
|
|
type: 'object',
|
|
|
|
|
required: ['username', 'password'],
|
|
|
|
|
properties: {
|
|
|
|
|
username: { type: 'string', description: '관리자 아이디' },
|
|
|
|
|
password: { type: 'string', description: '비밀번호' },
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
response: {
|
|
|
|
|
200: {
|
|
|
|
|
type: 'object',
|
|
|
|
|
properties: {
|
|
|
|
|
message: { type: 'string' },
|
|
|
|
|
token: { type: 'string' },
|
|
|
|
|
user: {
|
|
|
|
|
type: 'object',
|
|
|
|
|
properties: {
|
|
|
|
|
id: { type: 'integer' },
|
|
|
|
|
username: { type: 'string' },
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}, async (request, reply) => {
|
2026-01-16 21:38:54 +09:00
|
|
|
const { username, password } = request.body || {};
|
|
|
|
|
|
|
|
|
|
if (!username || !password) {
|
|
|
|
|
return reply.status(400).send({ error: '아이디와 비밀번호를 입력해주세요.' });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const [users] = await fastify.db.query(
|
|
|
|
|
'SELECT * FROM admin_users WHERE username = ?',
|
|
|
|
|
[username]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (users.length === 0) {
|
|
|
|
|
return reply.status(401).send({ error: '아이디 또는 비밀번호가 올바르지 않습니다.' });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const user = users[0];
|
|
|
|
|
const isValidPassword = await bcrypt.compare(password, user.password_hash);
|
|
|
|
|
|
|
|
|
|
if (!isValidPassword) {
|
|
|
|
|
return reply.status(401).send({ error: '아이디 또는 비밀번호가 올바르지 않습니다.' });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// JWT 토큰 생성
|
|
|
|
|
const token = fastify.jwt.sign({
|
|
|
|
|
id: user.id,
|
|
|
|
|
username: user.username,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
message: '로그인 성공',
|
|
|
|
|
token,
|
|
|
|
|
user: { id: user.id, username: user.username },
|
|
|
|
|
};
|
|
|
|
|
} catch (err) {
|
|
|
|
|
fastify.log.error(err);
|
|
|
|
|
return reply.status(500).send({ error: '로그인 처리 중 오류가 발생했습니다.' });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
2026-01-17 13:01:35 +09:00
|
|
|
* GET /api/auth/verify
|
2026-01-16 21:38:54 +09:00
|
|
|
* 토큰 검증
|
|
|
|
|
*/
|
|
|
|
|
fastify.get('/verify', {
|
2026-01-17 13:01:35 +09:00
|
|
|
schema: {
|
|
|
|
|
tags: ['auth'],
|
|
|
|
|
summary: '토큰 검증',
|
|
|
|
|
security: [{ bearerAuth: [] }],
|
|
|
|
|
response: {
|
|
|
|
|
200: {
|
|
|
|
|
type: 'object',
|
|
|
|
|
properties: {
|
|
|
|
|
valid: { type: 'boolean' },
|
|
|
|
|
user: {
|
|
|
|
|
type: 'object',
|
|
|
|
|
properties: {
|
|
|
|
|
id: { type: 'integer' },
|
|
|
|
|
username: { type: 'string' },
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
2026-01-16 21:38:54 +09:00
|
|
|
preHandler: [fastify.authenticate],
|
|
|
|
|
}, async (request, reply) => {
|
|
|
|
|
return { valid: true, user: request.user };
|
|
|
|
|
});
|
|
|
|
|
}
|