Google Integrations
OAuth Connections
Section titled “OAuth Connections”Google integration uses a provider-agnostic OAuth system:
oauth_connections— Stores tokens per (teacherId, provider, providerAccountId)integration_settings— Stores typed JSONB config per (teacherId, integrationType)
Integration types: calendar_read, calendar_write, drive, meet
Use ConnectionService methods — never read/write tokens directly from the teachers table.
Google Calendar
Section titled “Google Calendar”Auto-Create PinTeach Calendar on Signup
Section titled “Auto-Create PinTeach Calendar on Signup”When a new teacher completes Google OAuth for the first time, the platform automatically creates a dedicated secondary Google Calendar:
- OAuth callback exchanges code for tokens + user info
TeacherAuthService.findOrCreateByGoogle()creates the teacher record- Calls
GoogleCalendarService.createCalendar(teacherId, 'PinTeach', 'Clases gestionadas con PinTeach') - Stores the calendar ID in
integration_settings(type:calendar_write, config:{ writeCalendarId }) - All future session events are created on this secondary calendar
Fallback: If calendar creation fails, the system gracefully falls back to 'primary' (the teacher’s main Google Calendar). No broken state.
Post-signup: Teachers can switch the write target via PATCH /teacher/settings/google/calendars in Settings.
OAuth Scopes
Section titled “OAuth Scopes”userinfo.profile, userinfo.email, calendar (full R/W), drive.fileThe calendar scope grants full access to all Google Calendars the teacher owns (create, read, update, delete). Consent is always forced (prompt: 'consent') to ensure a refresh token is granted.
Reading Availability
Section titled “Reading Availability”The Slot Engine uses Google Calendar’s freebusy.query API to check teacher availability:
- Fetch selected calendars from
integration_settings(calendar_read config) - Query FreeBusy API for the date range
- Merge busy blocks with DB sessions
- Remove overlapping slots
Writing Events
Section titled “Writing Events”When sessions are confirmed, calendar events are created/updated:
Session confirmed → Create/update Google Calendar event (+ Google Meet link)Session cancelled → Cancel calendar eventSession rescheduled → Update event timeSync tracked in session_calendar_sync:
syncStatus: synced / pending / failedgoogleCalendarEventId: event referencegoogleMeetLink: Meet link generado por GooglemeetingUrl: URL que se expone al profesor y al alumno (populated fromgoogleMeetLink)
Google Meet — via Calendar Events (modo existente)
Section titled “Google Meet — via Calendar Events (modo existente)”Cada evento de calendario creado por PinTeach solicita automaticamente una sala de Google Meet.
No existe separacion de API — el link se genera via el parametro conferenceData de la
Calendar Events API al insertar el evento.
Flujo:
calendar-sync-worker.tsllama aGoogleCalendarService.createEvent(teacherId, event, { requestMeetLink: true })- El service envia
conferenceDataVersion=1+conferenceData.createRequestcon unrequestIdunico (pinteach-{teacherId8}-{timestamp}) - Google devuelve el evento con
conferenceData.entryPoints— se extrae la URI del entry point de tipovideo - El worker guarda tanto
googleMeetLinkcomomeetingUrlensession_calendar_sync - El frontend lee
meetingUrly muestra el boton “Unirse a la clase” en el panel de evento, el portal del alumno y el perfil de alumno
Requisito: El profesor debe tener Google Calendar conectado. Si no lo tiene, no se genera Meet link pero la sesion se crea normalmente.
Componentes afectados:
| Archivo | Que muestra |
|---|---|
apps/app/src/components/calendar/event-detail-panel.tsx |
Boton “Unirse” en panel lateral del calendario |
apps/app/src/components/student/tab-classes.tsx |
Boton “Unirse” en portal del alumno |
apps/app/src/components/students/student-profile-view.tsx |
Boton “Enlace de clase” en perfil del alumno |
Google Meet — Meet Spaces API v2 (modo independiente)
Section titled “Google Meet — Meet Spaces API v2 (modo independiente)”Un segundo mecanismo genera links de Meet directamente via la Meet Spaces API v2 (googleapis v146+), independiente del calendar sync. Permite tener links incluso si el profesor no tiene Google Calendar conectado (solo necesita el scope meetings.space.created).
OAuth incremental: El profesor activa esta integracion en Settings → Integraciones → Google Meet. Esto lanza un OAuth que añade el scope meetings.space.created a la conexion existente sin re-autorizar todo (include_granted_scopes: true). El scope se guarda en oauth_connections.scopes (array JSONB).
Flujo de creacion:
- Se crea una sesion →
session-service.tsllamaqueueMeetLink(sessionId, teacherId)(fire-and-forget, junto aqueueCalendarSync) queueMeetLinkcompruebahasMeetScopeleyendooauth_connections.scopes— si el scope no existe, no encola nadameet-link-workerrecoge el job → llamagoogle.meet({ version: 'v2', auth }).spaces.create({})- Obtiene
data.meetingUri→ llamaupsertSessionCalendarSync(sessionId, { meetingUrl }) - Worker salta si
meetingUrlya esta poblado (el calendar sync puede haberlo hecho antes)
Coexistencia: Ambos mecanismos (calendar events + Spaces API) escriben en session_calendar_sync.meetingUrl. El primero en llegar gana; el segundo salta por el check de meetingUrl existente. No hay conflicto.
Circuit breaker: Provider 'google-meet' — threshold 5 fallos / ventana 60s / recuperacion 30s.
Calendar Sync Worker
Section titled “Calendar Sync Worker”A cron job (every 5 minutes) retries pending calendar sync operations:
- Batch of 20 records at a time
- Skips if google-calendar circuit breaker is open
- Automatic retry with backoff
Google Drive
Section titled “Google Drive”Resource Integration
Section titled “Resource Integration”Teachers can add Drive files as resources via the file picker:
- Teacher opens Drive file picker
- Selects files to import as resources
- Files tracked in
teacher_resources(provider: ‘drive’)
Auto-Copy to Student Folders
Section titled “Auto-Copy to Student Folders”When Drive resources are linked to a session, files are automatically copied to student folders:
1. Resource linked to session2. DriveCopyService queues BullMQ job3. Worker copies file to student's folder (students.driveFolderId)4. Progress tracked in drive_file_copies table Status: pending → copying → copied / failedDrive Sync Worker
Section titled “Drive Sync Worker”Daily cron (3 AM) verifies Drive file integrity:
- Checks if files still exist in Drive
- Marks trashed/deleted files
- Reports inconsistencies
Circuit Breaker
Section titled “Circuit Breaker”Both Calendar and Drive API calls are protected by circuit breakers:
| Service | Failure Threshold | Window | Recovery |
|---|---|---|---|
| Google Calendar | 5 failures | 60s | 30s |
| Google Drive | 5 failures | 60s | 60s |
| Google Meet | 5 failures | 60s | 30s |
Only infrastructure errors (429, 5xx, timeouts) trip the circuit.
Calendar fallback: Sets syncStatus='pending', session creation never fails. Calendar-sync cron retries later.
Drive fallback: Workers skip and retry later via BullMQ DelayedError.
Rate Limiting
Section titled “Rate Limiting”Redis-backed sliding-window rate limiting at two levels:
| Service | Per-Teacher | Global |
|---|---|---|
| Google Calendar | 10 req/10s | 400/100s |
| Google Drive | 5 req/10s | 10,000/60s |
Execution order: Circuit Breaker → Rate Limiter → Token refresh → API call → 429 Retry