140 lines
4.3 KiB
TypeScript
140 lines
4.3 KiB
TypeScript
import { queryOne, execute } from '../../utils/db'
|
|
import { requireAuth } from '../../utils/session'
|
|
|
|
/**
|
|
* 구글 그룹 메시지 조회
|
|
* GET /api/google-group/messages
|
|
*
|
|
* Gmail API로 그룹 이메일 조회
|
|
*
|
|
* Query params:
|
|
* - groupEmail: 그룹 이메일 주소 (예: dev-team@company.com)
|
|
* - maxResults: 최대 결과 수 (기본 20)
|
|
* - after: 이 날짜 이후 메시지 (YYYY-MM-DD)
|
|
*/
|
|
export default defineEventHandler(async (event) => {
|
|
const session = await requireAuth(event)
|
|
const query = getQuery(event)
|
|
|
|
const groupEmail = query.groupEmail as string
|
|
const maxResults = parseInt(query.maxResults as string) || 20
|
|
const after = query.after as string
|
|
|
|
if (!groupEmail) {
|
|
throw createError({ statusCode: 400, message: '그룹 이메일이 필요합니다.' })
|
|
}
|
|
|
|
// 사용자의 Google 토큰 조회
|
|
const employee = await queryOne<any>(`
|
|
SELECT google_access_token, google_refresh_token, google_token_expires_at
|
|
FROM wr_employee_info WHERE employee_id = $1
|
|
`, [session.employeeId])
|
|
|
|
if (!employee?.google_access_token) {
|
|
throw createError({
|
|
statusCode: 401,
|
|
message: 'Google 계정이 연결되지 않았습니다.'
|
|
})
|
|
}
|
|
|
|
let accessToken = employee.google_access_token
|
|
|
|
// 토큰 만료 확인 및 갱신
|
|
if (employee.google_token_expires_at && new Date(employee.google_token_expires_at) < new Date()) {
|
|
accessToken = await refreshGoogleToken(session.employeeId, employee.google_refresh_token)
|
|
}
|
|
|
|
try {
|
|
// Gmail API로 그룹 메일 검색
|
|
let searchQuery = `list:${groupEmail}`
|
|
if (after) {
|
|
searchQuery += ` after:${after}`
|
|
}
|
|
|
|
const listResponse = await $fetch<any>('https://gmail.googleapis.com/gmail/v1/users/me/messages', {
|
|
headers: { 'Authorization': `Bearer ${accessToken}` },
|
|
query: {
|
|
q: searchQuery,
|
|
maxResults: maxResults
|
|
}
|
|
})
|
|
|
|
if (!listResponse.messages || listResponse.messages.length === 0) {
|
|
return { messages: [], total: 0 }
|
|
}
|
|
|
|
// 각 메시지의 상세 정보 조회
|
|
const messages = await Promise.all(
|
|
listResponse.messages.slice(0, maxResults).map(async (msg: any) => {
|
|
const detail = await $fetch<any>(`https://gmail.googleapis.com/gmail/v1/users/me/messages/${msg.id}`, {
|
|
headers: { 'Authorization': `Bearer ${accessToken}` },
|
|
query: { format: 'metadata', metadataHeaders: ['Subject', 'From', 'Date', 'To'] }
|
|
})
|
|
|
|
const headers = detail.payload?.headers || []
|
|
const getHeader = (name: string) => headers.find((h: any) => h.name === name)?.value || ''
|
|
|
|
return {
|
|
id: msg.id,
|
|
threadId: msg.threadId,
|
|
subject: getHeader('Subject'),
|
|
from: getHeader('From'),
|
|
to: getHeader('To'),
|
|
date: getHeader('Date'),
|
|
snippet: detail.snippet
|
|
}
|
|
})
|
|
)
|
|
|
|
return {
|
|
messages,
|
|
total: listResponse.resultSizeEstimate || messages.length
|
|
}
|
|
} catch (e: any) {
|
|
console.error('Gmail API error:', e)
|
|
|
|
if (e.status === 403) {
|
|
throw createError({
|
|
statusCode: 403,
|
|
message: 'Gmail 접근 권한이 없습니다. Google 로그인 시 권한을 허용해주세요.'
|
|
})
|
|
}
|
|
|
|
throw createError({
|
|
statusCode: 500,
|
|
message: '그룹 메시지를 가져오는데 실패했습니다.'
|
|
})
|
|
}
|
|
})
|
|
|
|
/**
|
|
* Google 토큰 갱신
|
|
*/
|
|
async function refreshGoogleToken(employeeId: number, refreshToken: string): Promise<string> {
|
|
const config = useRuntimeConfig()
|
|
|
|
const response = await $fetch<any>('https://oauth2.googleapis.com/token', {
|
|
method: 'POST',
|
|
body: new URLSearchParams({
|
|
client_id: config.googleClientId,
|
|
client_secret: config.googleClientSecret,
|
|
refresh_token: refreshToken,
|
|
grant_type: 'refresh_token'
|
|
}).toString(),
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
|
})
|
|
|
|
if (response.access_token) {
|
|
await execute(`
|
|
UPDATE wr_employee_info
|
|
SET google_access_token = $1,
|
|
google_token_expires_at = NOW() + INTERVAL '${response.expires_in} seconds'
|
|
WHERE employee_id = $2
|
|
`, [response.access_token, employeeId])
|
|
|
|
return response.access_token
|
|
}
|
|
|
|
throw new Error('토큰 갱신 실패')
|
|
}
|