import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { gmailLabelSelectorContract } from '@/lib/api/contracts/selectors/google' import { parseRequest } from '@/lib/api/server' import { authorizeCredentialUse } from '@/lib/auth/credential-access' import { validateAlphanumericId } from '@/lib/core/security/input-validation' import { generateRequestId } from '@/lib/core/utils/request' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { getScopesForService } from '@/lib/oauth/utils' import { refreshAccessTokenIfNeeded, ServiceAccountTokenError } from '@/app/api/auth/oauth/utils' export const dynamic = 'force-dynamic' const logger = createLogger('GmailLabelAPI') export const GET = withRouteHandler(async (request: NextRequest) => { const requestId = generateRequestId() try { const parsed = await parseRequest(gmailLabelSelectorContract, request, {}) if (!parsed.success) return parsed.response const { credentialId, labelId } = parsed.data.query const impersonateEmail = parsed.data.query.impersonateEmail || undefined const labelIdValidation = validateAlphanumericId(labelId, 'labelId', 255) if (!labelIdValidation.isValid) { logger.warn(`[${requestId}] Invalid label ID: ${labelIdValidation.error}`) return NextResponse.json({ error: labelIdValidation.error }, { status: 400 }) } const credAccess = await authorizeCredentialUse(request, { credentialId, requireWorkflowIdForInternal: false, }) if (!credAccess.ok || !credAccess.credentialOwnerUserId) { logger.warn(`[${requestId}] Credential access denied`, { error: credAccess.error }) return NextResponse.json({ error: credAccess.error || 'Unauthorized' }, { status: 401 }) } const accessToken = await refreshAccessTokenIfNeeded( credentialId, credAccess.credentialOwnerUserId, requestId, getScopesForService('gmail'), impersonateEmail ) if (!accessToken) { return NextResponse.json({ error: 'Failed to obtain valid access token' }, { status: 401 }) } logger.info(`[${requestId}] Fetching label ${labelId} from Gmail API`) const response = await fetch( `https://gmail.googleapis.com/gmail/v1/users/me/labels/${labelId}`, { headers: { Authorization: `Bearer ${accessToken}`, }, } ) logger.info(`[${requestId}] Gmail API response status: ${response.status}`) if (!response.ok) { const errorText = await response.text() logger.error(`[${requestId}] Gmail API error response: ${errorText}`) try { const error = JSON.parse(errorText) return NextResponse.json({ error }, { status: response.status }) } catch (_e) { return NextResponse.json({ error: errorText }, { status: response.status }) } } const label = await response.json() let formattedName = label.name if (label.type === 'system') { formattedName = label.name.charAt(0).toUpperCase() + label.name.slice(1).toLowerCase() } const formattedLabel = { id: label.id, name: formattedName, type: label.type, messagesTotal: label.messagesTotal || 0, messagesUnread: label.messagesUnread || 0, } return NextResponse.json({ label: formattedLabel }, { status: 200 }) } catch (error) { if (error instanceof ServiceAccountTokenError) { return NextResponse.json({ error: error.message }, { status: 400 }) } logger.error(`[${requestId}] Error fetching Gmail label:`, error) return NextResponse.json({ error: 'Failed to fetch Gmail label' }, { status: 500 }) } })