const CALENDAR_READONLY_SCOPE = 'https://www.googleapis.com/auth/calendar.readonly'
const GMAIL_READONLY_SCOPE = 'https://www.googleapis.com/auth/gmail.readonly'
export const GOOGLE_CALENDAR_CONNECT_URL = 'https://kody.codes/connect/oauth?provider=google'
export const GOOGLE_INBOX_CONNECT_URL =
'https://kody.codes/connect/oauth?provider=google&authorizeUrl=https%3A%2F%2Faccounts.google.com%2Fo%2Foauth2%2Fv2%2Fauth&tokenUrl=https%3A%2F%2Foauth2.googleapis.com%2Ftoken&flow=confidential&scopes=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcalendar.readonly%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.readonly&allowedHosts=www.googleapis.com%2Cgmail.googleapis.com&extraAuthorizeParams=%7B%22access_type%22%3A%22offline%22%2C%22prompt%22%3A%22consent%22%7D'
function errorMessage(error: unknown): string {
if (error instanceof Error && error.message) return error.message
if (error && typeof error === 'object' && 'message' in error) {
const message = (error as { message?: unknown }).message
if (typeof message === 'string' && message.trim()) return message
}
return String(error || 'Unknown error')
}
function httpStatus(error: unknown): number | undefined {
if (!error || typeof error !== 'object') return undefined
const status = (error as { status?: unknown }).status
return typeof status === 'number' ? status : undefined
}
function looksLikeInsufficientScope(error: unknown): boolean {
const status = httpStatus(error)
const message = errorMessage(error)
return (
status === 403 ||
/insufficient[\s._-]?scope|ACCESS_TOKEN_SCOPE|accessNotConfigured|has not been used in project/i.test(
message,
)
)
}
function looksLikeMissingAuth(error: unknown): boolean {
return /Could not authenticate|not found|no refresh|Missing required Google account/i.test(
errorMessage(error),
)
}
export function googleCalendarSetupError(error: unknown, account: string): Error {
const original = errorMessage(error)
if (!looksLikeInsufficientScope(error) && !looksLikeMissingAuth(error)) {
return error instanceof Error ? error : new Error(original)
}
return new Error(
`Google Calendar could not run for account "${account}". It needs the ${CALENDAR_READONLY_SCOPE} scope (or calendar.events.readonly). Connect or reconnect the built-in integration at ${GOOGLE_CALENDAR_CONNECT_URL} and grant Calendar, then retry. Original error: ${original}`,
)
}
export function googleInboxSetupError(error: unknown, account: string): Error {
const original = errorMessage(error)
if (!looksLikeInsufficientScope(error) && !looksLikeMissingAuth(error)) {
return error instanceof Error ? error : new Error(original)
}
return new Error(
`Gmail inbox reading could not run for account "${account}". Inbox access needs the ${GMAIL_READONLY_SCOPE} scope, which is restricted and is not on Kody's built-in Google OAuth menu. Bring your own Google OAuth client, enable the Gmail API, publish the app to Production (Testing refresh tokens expire after 7 days), and connect at ${GOOGLE_INBOX_CONNECT_URL}. Original error: ${original}`,
)
}