Files
smart-home-tablet/app/api/calendar/route.ts
Cosmo 408be1d0c4
Some checks failed
Deploy / deploy (push) Failing after 2m6s
fix: restore template literals in POST handler dateTime fields
2026-04-22 20:03:31 +00:00

233 lines
7.4 KiB
TypeScript

export const dynamic = 'force-dynamic'
import { NextResponse } from 'next/server'
import { google } from 'googleapis'
import * as fs from 'fs'
import * as path from 'path'
function getAuth(readonly = true) {
const scopes = readonly
? ['https://www.googleapis.com/auth/calendar.readonly']
: ['https://www.googleapis.com/auth/calendar']
const saJson = process.env.GOOGLE_SA_JSON
if (saJson) {
const sa = JSON.parse(saJson)
return new google.auth.GoogleAuth({
credentials: sa,
scopes,
})
}
const saPath = path.join(process.cwd(), 'google-sa.json')
if (fs.existsSync(saPath)) {
return new google.auth.GoogleAuth({
keyFile: saPath,
scopes,
})
}
return null
}
export async function GET(req: Request) {
const { searchParams } = new URL(req.url)
const range = searchParams.get('range') || 'today'
const auth = getAuth(true)
if (!auth) {
return NextResponse.json({ events: [], error: 'not_configured' })
}
const daniilCalendarId = process.env.DANIIL_CALENDAR_ID || 'daniilklimov25@gmail.com'
const svetaCalendarId = process.env.SVETA_CALENDAR_ID || ''
const now = new Date()
let timeMin: string
let timeMax: string
if (range === 'today') {
timeMin = new Date(now.getFullYear(), now.getMonth(), now.getDate()).toISOString()
timeMax = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1).toISOString()
} else if (range === 'week') {
timeMin = new Date(now.getFullYear(), now.getMonth(), now.getDate()).toISOString()
timeMax = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 7).toISOString()
} else {
// month — support year/month query params
const targetYear = parseInt(searchParams.get('year') || String(now.getFullYear()))
const targetMonth = parseInt(searchParams.get('month') || String(now.getMonth()))
timeMin = new Date(targetYear, targetMonth, 1).toISOString()
timeMax = new Date(targetYear, targetMonth + 1, 0, 23, 59).toISOString()
}
const calendarClient = google.calendar({ version: 'v3', auth: auth as any })
const calendars = [
{ id: daniilCalendarId, owner: 'daniil', color: '#6366f1', name: 'Даниил' },
...(svetaCalendarId ? [{ id: svetaCalendarId, owner: 'sveta', color: '#ec4899', name: 'Света' }] : [])
]
const results = await Promise.allSettled(
calendars.map(cal =>
calendarClient.events.list({
calendarId: cal.id,
timeMin,
timeMax,
singleEvents: true,
orderBy: 'startTime',
maxResults: 100,
}).then(r => ({ ...cal, events: r.data.items || [] }))
)
)
const allEvents = results
.filter(r => r.status === 'fulfilled')
.flatMap(r => {
const val = (r as PromiseFulfilledResult<any>).value
return val.events.map((e: any) => ({
id: e.id,
title: e.summary || '(без названия)',
start: e.start?.dateTime || e.start?.date,
end: e.end?.dateTime || e.end?.date,
allDay: !e.start?.dateTime,
description: e.description || null,
location: e.location || null,
owner: val.owner,
ownerName: val.name,
color: val.color,
}))
})
.sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime())
const errors = results
.filter(r => r.status === 'rejected')
.map(r => (r as PromiseRejectedResult).reason?.message || 'unknown')
return NextResponse.json({ events: allEvents, errors: errors.length ? errors : undefined, fetchedAt: new Date().toISOString() })
}
export async function POST(req: Request) {
const body = await req.json()
const { title, date, startTime, endTime, allDay, owner } = body
const auth = getAuth(false)
if (!auth) return NextResponse.json({ error: 'not_configured' }, { status: 500 })
const calendarClient = google.calendar({ version: 'v3', auth: auth as any })
const daniilCalendarId = process.env.DANIIL_CALENDAR_ID || 'daniilklimov25@gmail.com'
const svetaCalendarId = process.env.SVETA_CALENDAR_ID || ''
const calendars: Record<string, { id: string; name: string; color: string }> = {
daniil: { id: daniilCalendarId, name: 'Даниил', color: '#6366f1' },
...(svetaCalendarId ? { sveta: { id: svetaCalendarId, name: 'Света', color: '#ec4899' } } : {}),
}
const selectedOwner = owner && calendars[owner] ? owner : 'daniil'
const cal = calendars[selectedOwner]
let start: any, end: any
if (allDay) {
start = { date }
end = { date }
} else {
start = { dateTime: `${date}T${startTime}:00`, timeZone: 'Europe/Moscow' }
end = { dateTime: `${date}T${endTime}:00`, timeZone: 'Europe/Moscow' }
}
try {
const res = await calendarClient.events.insert({
calendarId: cal.id,
requestBody: { summary: title, start, end },
})
const e = res.data
return NextResponse.json({
event: {
id: e.id,
title: e.summary || title,
start: e.start?.dateTime || e.start?.date,
end: e.end?.dateTime || e.end?.date,
allDay: !e.start?.dateTime,
owner: selectedOwner,
ownerName: cal.name,
color: cal.color,
}
})
} catch (err: any) {
return NextResponse.json({ error: err.message || 'Failed to create event' }, { status: 500 })
}
}
export async function PUT(req: Request) {
const body = await req.json()
const { eventId, title, date, startTime, endTime, allDay, calendarId } = body
if (!eventId) {
return NextResponse.json({ error: 'eventId is required' }, { status: 400 })
}
const auth = getAuth(false)
if (!auth) return NextResponse.json({ error: 'not_configured' }, { status: 500 })
const targetCalendarId = calendarId || process.env.DANIIL_CALENDAR_ID || 'daniilklimov25@gmail.com'
const calendarClient = google.calendar({ version: 'v3', auth: auth as any })
let start: any, end: any
if (allDay) {
start = { date }
end = { date }
} else {
start = { dateTime: `${date}T${startTime}:00`, timeZone: 'Europe/Moscow' }
end = { dateTime: `${date}T${endTime}:00`, timeZone: 'Europe/Moscow' }
}
try {
const res = await calendarClient.events.patch({
calendarId: targetCalendarId,
eventId,
requestBody: {
summary: title,
start,
end,
},
})
const e = res.data
return NextResponse.json({
event: {
id: e.id,
title: e.summary || title,
start: e.start?.dateTime || e.start?.date,
end: e.end?.dateTime || e.end?.date,
allDay: !e.start?.dateTime,
}
})
} catch (err: any) {
return NextResponse.json({ error: err.message || 'Failed to update event' }, { status: 500 })
}
}
export async function DELETE(req: Request) {
const { searchParams } = new URL(req.url)
const eventId = searchParams.get('eventId')
const calendarId = searchParams.get('calendarId')
if (!eventId) {
return NextResponse.json({ error: 'eventId is required' }, { status: 400 })
}
const auth = getAuth(false)
if (!auth) {
return NextResponse.json({ error: 'not_configured' }, { status: 500 })
}
const targetCalendarId = calendarId || process.env.DANIIL_CALENDAR_ID || 'daniilklimov25@gmail.com'
const calendarClient = google.calendar({ version: 'v3', auth: auth as any })
try {
await calendarClient.events.delete({
calendarId: targetCalendarId,
eventId,
})
return NextResponse.json({ success: true })
} catch (err: any) {
return NextResponse.json({ error: err.message || 'Failed to delete event' }, { status: 500 })
}
}