feat(dashboard): tableau de bord Second Brain configurable avec chargement progressif
All checks were successful
CI / Lint, Unit Tests & Build (push) Successful in 7m3s
CI / Deploy production (on server) (push) Successful in 23s

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:
Antigravity
2026-07-14 16:50:53 +00:00
parent d38a99586b
commit 30da592ba2
62 changed files with 7741 additions and 335 deletions

View File

@@ -0,0 +1,41 @@
import { NextRequest, NextResponse } from 'next/server'
import prisma from '@/lib/prisma'
import { agentSuggestionService } from '@/lib/ai/services/agent-suggestion.service'
export const dynamic = 'force-dynamic'
function authorizeCron(request: NextRequest): boolean {
const cronSecret = process.env.CRON_SECRET
if (!cronSecret) return false
const authHeader = request.headers.get('authorization')
const bearer = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : null
const querySecret = new URL(request.url).searchParams.get('secret')
return bearer === cronSecret || querySecret === cronSecret
}
/**
* POST /api/cron/agent-suggestions
* Génère des suggestions d'agents à partir des clusters sémantiques existants.
*/
export async function POST(request: NextRequest) {
if (!authorizeCron(request)) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const users = await prisma.user.findMany({
where: { noteClusters: { some: {} } },
select: { id: true },
take: 30,
})
let total = 0
for (const user of users) {
try {
total += await agentSuggestionService.generateForUser(user.id)
} catch (err) {
console.error('[CronAgentSuggestions] user', user.id, err)
}
}
return NextResponse.json({ success: true, users: users.length, suggestionsUpserted: total })
}

View File

@@ -0,0 +1,49 @@
import { NextRequest, NextResponse } from 'next/server'
import prisma from '@/lib/prisma'
import { gmailScannerService } from '@/lib/integrations/gmail-scanner.service'
export const dynamic = 'force-dynamic'
function authorize(request: NextRequest): boolean {
const cronSecret = process.env.CRON_SECRET
if (!cronSecret) return false
const authHeader = request.headers.get('authorization')
const bearer = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : null
return bearer === cronSecret
}
/** POST /api/cron/gmail — scan Gmail for connected users (1×/jour en prod via entrypoint) */
export async function POST(request: NextRequest) {
if (!authorize(request)) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const users = await prisma.userAISettings.findMany({
select: { userId: true, integrationTokens: true },
take: 200,
})
const connected = users.filter(u => {
if (!u.integrationTokens) return false
try {
const meta = typeof u.integrationTokens === 'string'
? JSON.parse(u.integrationTokens)
: u.integrationTokens
return !!(meta?.gmailAccessToken && meta?.gmailRefreshToken)
} catch {
return false
}
})
let totalCreated = 0
for (const row of connected) {
try {
const { created } = await gmailScannerService.scanUser(row.userId)
totalCreated += created
} catch (err) {
console.error('[CronGmail]', row.userId, err)
}
}
return NextResponse.json({ success: true, users: connected.length, notesCreated: totalCreated })
}