import { query, queryOne, insertReturning } from '../../../../utils/db' import { requireAuth } from '../../../../utils/session' import { getValidGoogleToken } from '../../../../utils/google-token' /** * 주간보고 그룹 공유 (Gmail 발송) * POST /api/report/weekly/[id]/share */ export default defineEventHandler(async (event) => { const user = await requireAuth(event) const reportId = parseInt(getRouterParam(event, 'id') || '0') const body = await readBody(event) if (!reportId) { throw createError({ statusCode: 400, message: '보고서 ID가 필요합니다.' }) } const groupIds = body.groupIds as number[] if (!groupIds?.length) { throw createError({ statusCode: 400, message: '공유할 그룹을 선택해주세요.' }) } // 보고서 조회 const report = await queryOne(` SELECT r.*, e.employee_name, e.employee_email, p.project_name, p.project_code FROM wr_weekly_report r JOIN wr_employee_info e ON r.employee_id = e.employee_id LEFT JOIN wr_project_info p ON r.project_id = p.project_id WHERE r.report_id = $1 `, [reportId]) if (!report) { throw createError({ statusCode: 404, message: '보고서를 찾을 수 없습니다.' }) } // Google 토큰 확인 const accessToken = await getValidGoogleToken(user.employeeId) if (!accessToken) { throw createError({ statusCode: 401, message: 'Google 계정 연결이 필요합니다.' }) } // 선택된 그룹 조회 const groups = await query(` SELECT group_id, group_email, group_name FROM wr_google_group WHERE group_id = ANY($1) AND is_active = true `, [groupIds]) if (!groups.length) { throw createError({ statusCode: 400, message: '유효한 그룹이 없습니다.' }) } // 이메일 제목 및 본문 생성 const weekInfo = `${report.report_year}년 ${report.report_week}주차` const subject = `[주간보고] ${report.project_name || '개인'} - ${weekInfo} (${report.employee_name})` const emailBody = buildEmailBody(report) // 각 그룹에 발송 const results: any[] = [] for (const group of groups) { try { const rawEmail = createRawEmail({ to: group.group_email, subject, body: emailBody, from: user.employeeEmail }) const sendRes = await fetch('https://gmail.googleapis.com/gmail/v1/users/me/messages/send', { method: 'POST', headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ raw: rawEmail }) }) if (sendRes.ok) { results.push({ groupId: group.group_id, groupName: group.group_name, success: true }) } else { const err = await sendRes.json() results.push({ groupId: group.group_id, groupName: group.group_name, success: false, error: err.error?.message }) } } catch (e: any) { results.push({ groupId: group.group_id, groupName: group.group_name, success: false, error: e.message }) } } return { success: results.some(r => r.success), message: `${results.filter(r => r.success).length}/${groups.length}개 그룹에 공유됨`, results } }) function buildEmailBody(report: any): string { return `

📋 주간보고 - ${report.report_year}년 ${report.report_week}주차

작성자: ${report.employee_name} | 프로젝트: ${report.project_name || '개인'}


📌 금주 실적

${(report.this_week_work || '').replace(/\n/g, '
')}

📅 차주 계획

${(report.next_week_plan || '').replace(/\n/g, '
')}
${report.issues ? `

⚠️ 이슈

${report.issues.replace(/\n/g, '
')}
` : ''}

업무관리프로그램에서 발송

` } function createRawEmail(opts: { to: string; subject: string; body: string; from: string }): string { const email = [`From: ${opts.from}`, `To: ${opts.to}`, `Subject: =?UTF-8?B?${Buffer.from(opts.subject).toString('base64')}?=`, 'MIME-Version: 1.0', 'Content-Type: text/html; charset=UTF-8', '', opts.body].join('\r\n') return Buffer.from(email).toString('base64url') }