"use client" import { useState } from "react" import { ExternalLink, KeyRound, Plus, Trash2 } from "lucide-react" import { Button } from "@/components/ui/button" import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { ApiTokenCreatedDialog } from "@/components/gmail/settings/automation/api-token-created-dialog" import { ApiTokenDriveScopeEditor } from "@/components/gmail/settings/automation/api-token-drive-scope-editor" import { ApiTokenAgendaScopeEditor } from "@/components/gmail/settings/automation/api-token-agenda-scope-editor" import { ApiTokenMailScopeEditor } from "@/components/gmail/settings/automation/api-token-mail-scope-editor" import { ApiTokenPermissionEditor } from "@/components/gmail/settings/automation/api-token-permission-editor" import { AutomationTabMasonry } from "@/components/gmail/settings/automation/automation-tab-masonry" import { SettingsSyncBanner } from "@/components/gmail/settings/settings-sync-banner" import { useApiTokens, useCreateApiToken, useRevokeApiToken, } from "@/lib/api/hooks/use-api-tokens" import { useAuthReady } from "@/lib/api/use-auth-ready" import type { ApiTokenCreated } from "@/lib/api/types" import { defaultDriveScope, defaultAgendaScope, defaultMailScope, emptyPermissionGrants, hasAgendaPermissions, hasAnyPermission, hasDrivePermissions, hasMailPermissions, summarizePermissions, type ApiTokenPermissionGrant, } from "@/lib/mail-automation/api-token-permissions" function formatDate(value?: string) { if (!value) return "—" try { return new Intl.DateTimeFormat("fr-FR", { dateStyle: "medium", timeStyle: "short", }).format(new Date(value)) } catch { return value } } export function ApiTokensPanel() { const { ready, authenticated } = useAuthReady() const { data: tokens = [], isFetching, isError, refetch, isPending } = useApiTokens() const createToken = useCreateApiToken() const revokeToken = useRevokeApiToken() const [name, setName] = useState("") const [permissions, setPermissions] = useState(emptyPermissionGrants) const [mailScope, setMailScope] = useState(defaultMailScope) const [driveScope, setDriveScope] = useState(defaultDriveScope) const [agendaScope, setAgendaScope] = useState(defaultAgendaScope) const [created, setCreated] = useState(null) const [createdOpen, setCreatedOpen] = useState(false) const showInitialLoad = ready && authenticated && isPending && tokens.length === 0 const mailScopeEnabled = hasMailPermissions(permissions) const driveScopeEnabled = hasDrivePermissions(permissions) const agendaScopeEnabled = hasAgendaPermissions(permissions) const canSubmit = name.trim().length > 0 && hasAnyPermission(permissions) && (!mailScopeEnabled || mailScope.all_accounts || mailScope.account_ids.length > 0) && (!driveScopeEnabled || driveScope.all_folders || driveScope.folder_paths.length > 0) && (!agendaScopeEnabled || agendaScope.all_calendars || agendaScope.calendar_ids.length > 0) async function handleCreate() { const result = await createToken.mutateAsync({ name: name.trim(), permissions: permissions.filter((g) => g.read || g.write), mail_scope: mailScopeEnabled ? mailScope : defaultMailScope(), drive_scope: driveScopeEnabled ? driveScope : defaultDriveScope(), agenda_scope: agendaScopeEnabled ? agendaScope : defaultAgendaScope(), }) setCreated(result) setCreatedOpen(true) setName("") setPermissions(emptyPermissionGrants()) setMailScope(defaultMailScope()) setDriveScope(defaultDriveScope()) setAgendaScope(defaultAgendaScope()) } return (
refetch()} /> Nouveau token Jetons fine-grained pour agents IA, scripts et intégrations externes. Choisissez les permissions, puis restreignez le périmètre mail, Drive et agenda si nécessaire.

Documentation API interactive :{" "} /api/docs

setName(e.target.value)} placeholder="Agent tri boîte support" />
Tokens actifs Révoquez un jeton compromis ou inutilisé à tout moment. {showInitialLoad ? (

Chargement…

) : tokens.length === 0 ? (

Aucun token API pour le moment.

) : (
    {tokens.map((token) => { const summary = summarizePermissions(token.permissions) return (
  • {token.name}

    {token.token_prefix}…

    {summary.length > 0 && (
      {summary.slice(0, 4).map((line) => (
    • {line}
    • ))} {summary.length > 4 && (
    • + {summary.length - 4} autre(s) permission(s)
    • )}
    )}

    Créé {formatDate(token.created_at)} {token.last_used_at ? ` · Dernière utilisation ${formatDate(token.last_used_at)}` : ""}

  • ) })}
)}
Bonnes pratiques

Limitez chaque token au périmètre strict nécessaire (principe du moindre privilège).

Le secret n'est affiché qu'une fois à la création — stockez-le dans un gestionnaire de secrets, jamais dans le code source.

La permission « Super admin — Tokens API » permet aussi de gérer d'autres tokens via l'API — réservez-la aux agents d'administration de confiance.

Préférez des tokens dédiés par agent ou intégration pour faciliter la révocation.

) }