From 444239a5e5ab89f4493e79383959a7f57503e7e8 Mon Sep 17 00:00:00 2001 From: Cosmo Date: Wed, 22 Apr 2026 13:10:06 +0000 Subject: [PATCH] fix: switch to service account auth for Google Calendar --- app/api/calendar/route.ts | 52 ++++++++++++++++++++++++++++----------- google-sa.json | 13 ++++++++++ 2 files changed, 51 insertions(+), 14 deletions(-) create mode 100644 google-sa.json diff --git a/app/api/calendar/route.ts b/app/api/calendar/route.ts index aa76a49..4b051c0 100644 --- a/app/api/calendar/route.ts +++ b/app/api/calendar/route.ts @@ -1,41 +1,61 @@ 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() { + // Service account JSON (inline or from file) + const saJson = process.env.GOOGLE_SA_JSON + if (saJson) { + const sa = JSON.parse(saJson) + return new google.auth.GoogleAuth({ + credentials: sa, + scopes: ['https://www.googleapis.com/auth/calendar.readonly'], + }) + } + // Fallback: file + const saPath = path.join(process.cwd(), 'google-sa.json') + if (fs.existsSync(saPath)) { + return new google.auth.GoogleAuth({ + keyFile: saPath, + scopes: ['https://www.googleapis.com/auth/calendar.readonly'], + }) + } + return null +} export async function GET(req: Request) { const { searchParams } = new URL(req.url) const range = searchParams.get('range') || 'today' - const clientId = process.env.GOOGLE_CLIENT_ID - const clientSecret = process.env.GOOGLE_CLIENT_SECRET - const refreshToken = process.env.GOOGLE_REFRESH_TOKEN - const svetaCalendarId = process.env.SVETA_CALENDAR_ID - - if (!clientId || !clientSecret || !refreshToken) { + const auth = getAuth() + if (!auth) { return NextResponse.json({ events: [], error: 'not_configured' }) } - const auth = new google.auth.OAuth2(clientId, clientSecret) - auth.setCredentials({ refresh_token: refreshToken }) + const daniilCalendarId = process.env.DANIIL_CALENDAR_ID || 'daniilklimov25@gmail.com' + const svetaCalendarId = process.env.SVETA_CALENDAR_ID || '' const now = new Date() - const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()) - let timeMin = todayStart.toISOString() + 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 { - timeMax = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59).toISOString() timeMin = new Date(now.getFullYear(), now.getMonth(), 1).toISOString() + timeMax = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59).toISOString() } - const calendarClient = google.calendar({ version: 'v3', auth }) + const calendarClient = google.calendar({ version: 'v3', auth: auth as any }) const calendars = [ - { id: 'daniilklimov25@gmail.com', owner: 'daniil', color: '#6366f1', name: 'Даниил' }, + { id: daniilCalendarId, owner: 'daniil', color: '#6366f1', name: 'Даниил' }, ...(svetaCalendarId ? [{ id: svetaCalendarId, owner: 'sveta', color: '#ec4899', name: 'Света' }] : []) ] @@ -71,5 +91,9 @@ export async function GET(req: Request) { }) .sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime()) - return NextResponse.json({ events: allEvents, fetchedAt: new Date().toISOString() }) + 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() }) } diff --git a/google-sa.json b/google-sa.json new file mode 100644 index 0000000..6207aa2 --- /dev/null +++ b/google-sa.json @@ -0,0 +1,13 @@ +{ + "type": "service_account", + "project_id": "cosmo-486412", + "private_key_id": "97159754f1652d3231d5cc9381760da69796b7f1", + "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCl6SmxATP8+lPG\ncAMg1FgchlSSx26ESpZXJMXpxtFGSKy7MVjLU43OJEa9MW/ZjLpn6fuHtsGTe12U\neDlltKdsrCuLq5InTvYCTfNKGUUm4RkquQa5sLmKTIWS0VObIyvCE2mtQz+q6vlE\nu9fs4rrJguquG7fOfLfodaJ4vEeR7daDdpohWG5NXNBgfc2wWVzoHMBsVMmj7lmY\n3CnUvzFiIfr1Nlp2x/x82eIb4zw3dpc89W5X+rCkseMV1Tup90XFAxzVEM+F7loQ\nAjnsaxaZJ3cqo/eo0kMOdmMJEyreXFIPkH4OZfjq7EyNrbtd0sMv19ghZh1j8wwy\nKf6CqnuDAgMBAAECggEABW5cePQ8xV4wKgQEQHwVTyyX+7pa2wXtwUE/1+TrCrkH\nmV39Y31npVKrZdv0XZhEP98CeRpZqbujCTv4R+TUQWGq4maFxcnJbbMrZ5kQmhdu\nuouD41hlTep3ycaETTK5ncRMNDwA+Qze0IDWieVHBjKztY86TA4y+rhQzuNrhcls\n8bmJrnT7jeCvMKQqbhnulG89JGh4r6pFfAKMCn3dJOu5ATaTY0xNLG2H9h41doKx\nwKBm/4T3HJTtIuAPdwKxzf96QuSIl1WfvomYIz4Lpb8DGqEMqMtvIH7G0LNj00dU\nn0us6yqevLGbVqF+0P0ndtJxvPzqoSkfhTebnM8Z6QKBgQDQr6Ty/Ej0/Y8pr7MS\nL4VqEa85W4nvAuSr9NPo+bSxTRNbZnXlhBsVeYedXrfBQrsSDuigeRG31kTmwxfD\nUaEFA7134J68OZGD/rhCzjBxzRpVi7YgOXz0PxkgXlZvqLf4YZmbAF73sRYCxcJj\nw938dSieDTCYWwAvr6a840HCqQKBgQDLhsuUEe5xxKXHd390MLUda1pkV0AOcYiS\n3oqjWOijQic43fEHMLYrs4A/lOBjWqdJ1fn2DQJHfRl7RvaAi2yUqWyMWtPVAhBs\nUvsckFOuepu6fO2RhxF4lUNqH9mFKw3sn/EmlDKsUAQ5tijLKdNyaQHam6cfV3qG\nCsLGoa9USwKBgQCxy7PhQYh3EkCS55rNd6dXQ1HisFbIR9LDnoedCoIkPOKtEJKJ\nxQ++MBiWv0gXY98193XCouOxmOCDKtxoEHf7acBXDgyvmOydZLtgT4N+sZwqHipB\nMjl/bvLdXQKPh1OWTrEsGhjPNxTlr896aDoNCVRdtCce5wk1l5WbgJNaYQKBgH54\n7Aa+QdL2pSHXcx8rqVB3xnr18PtIt9q0aahp9l6FHERtPnr+XSW47KgWBn4W9j+e\ntS6eFN2BspT1mvZ0LWwQAEETq/EA0F3QDvVIBog07pKrUSGOsl+hOXw4AH6NK6Dw\nHvWfQAHt00JdnOnquteswxcqhGaogJ3NEA5IqOATAoGBAMoXSmrSxn2YDmlYIxI7\nskML3wEHZWezTpwMiTTqCSgF2bJVHeXNYpupJjh6t95gMmqtdC7Ulg/UGbgYX7Fi\nicompJeZgBmcorcgcxWk9umBK8F8sMwHDEO70RiYHOv6MdEcwy0Ev5hTl1WG44g4\nyOcojdbhrK0Ji9lNa0BZjz1H\n-----END PRIVATE KEY-----\n", + "client_email": "homedashboard@cosmo-486412.iam.gserviceaccount.com", + "client_id": "115741671545733594404", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/homedashboard%40cosmo-486412.iam.gserviceaccount.com", + "universe_domain": "googleapis.com" +}