feat(dashboard): tableau de bord Second Brain configurable avec chargement progressif
Briefing granulaire, pistes rapides puis enrichissement async, layout persisté v5, suggestions agents, intégration Gmail et navigation sidebar alignée sur /home. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
66
memento-note/lib/integrations/google-integration-tokens.ts
Normal file
66
memento-note/lib/integrations/google-integration-tokens.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import prisma from '@/lib/prisma'
|
||||
|
||||
export function getGoogleOAuthCredentials() {
|
||||
return {
|
||||
clientId: process.env.GOOGLE_CALENDAR_CLIENT_ID || process.env.AUTH_GOOGLE_ID || '',
|
||||
clientSecret: process.env.GOOGLE_CALENDAR_CLIENT_SECRET || process.env.AUTH_GOOGLE_SECRET || '',
|
||||
}
|
||||
}
|
||||
|
||||
export async function readIntegrationMeta(userId: string): Promise<Record<string, unknown>> {
|
||||
const aiSettings = await prisma.userAISettings.findUnique({
|
||||
where: { userId },
|
||||
select: { integrationTokens: true },
|
||||
})
|
||||
try {
|
||||
const raw = aiSettings?.integrationTokens
|
||||
if (!raw) return {}
|
||||
return typeof raw === 'string' ? JSON.parse(raw) : (raw as Record<string, unknown>)
|
||||
} catch {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
export async function writeIntegrationMeta(userId: string, meta: Record<string, unknown>) {
|
||||
await prisma.userAISettings.upsert({
|
||||
where: { userId },
|
||||
update: { integrationTokens: JSON.stringify(meta) },
|
||||
create: { userId, integrationTokens: JSON.stringify(meta) },
|
||||
})
|
||||
}
|
||||
|
||||
export async function getGoogleTokens(
|
||||
userId: string,
|
||||
prefix: 'calendar' | 'gmail',
|
||||
): Promise<{ accessToken: string; refreshToken: string } | null> {
|
||||
const meta = await readIntegrationMeta(userId)
|
||||
const accessToken = meta[`${prefix}AccessToken`] as string | undefined
|
||||
const refreshToken = meta[`${prefix}RefreshToken`] as string | undefined
|
||||
if (accessToken && refreshToken) return { accessToken, refreshToken }
|
||||
return null
|
||||
}
|
||||
|
||||
export async function refreshGoogleAccessToken(
|
||||
userId: string,
|
||||
prefix: 'calendar' | 'gmail',
|
||||
refreshToken: string,
|
||||
): Promise<string | null> {
|
||||
const { clientId, clientSecret } = getGoogleOAuthCredentials()
|
||||
const res = await fetch('https://oauth2.googleapis.com/token', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: new URLSearchParams({
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
refresh_token: refreshToken,
|
||||
grant_type: 'refresh_token',
|
||||
}),
|
||||
})
|
||||
const data = await res.json()
|
||||
if (!data.access_token) return null
|
||||
|
||||
const meta = await readIntegrationMeta(userId)
|
||||
meta[`${prefix}AccessToken`] = data.access_token
|
||||
await writeIntegrationMeta(userId, meta)
|
||||
return data.access_token as string
|
||||
}
|
||||
Reference in New Issue
Block a user