Feature: Ajout du support d'API Key pour Google Calendar

Ajout d'une troisième méthode d'authentification pour Google Calendar :
- OAuth 2.0 (existant)
- Service Account (existant)
- API Key (nouveau)

Modifications :
- Interface d'administration avec sélection API Key
- Champs pour configurer la clé API et l'ID du calendrier
- Backend mis à jour pour gérer l'authentification par API Key
- Routes calendar adaptées pour supporter le nouveau type
- Affichage du statut de connexion mis à jour

L'utilisateur peut maintenant :
1. Sélectionner "API externe" dans les paramètres Google Calendar
2. Entrer sa clé API Google Calendar
3. Spécifier l'ID du calendrier (ou "primary")
4. Synchroniser directement avec Google Calendar
This commit is contained in:
Claude committed 2025-11-12 13:22:34 +00:00
1 parent dc4f88d045
commit e480d50bea
3 files changed
+104 -11

No files matched your search

+38 -2
View File
@@ -17,7 +17,8 @@ const SCOPES = ['https://www.googleapis.com/auth/calendar.readonly'];
*/
const AUTH_TYPES = {
OAUTH2: 'oauth2',
SERVICE_ACCOUNT: 'service_account'
SERVICE_ACCOUNT: 'service_account',
API_KEY: 'api_externe'
};
/**
@@ -76,6 +77,19 @@ async function getServiceAccountClient() {
}
}
/**
* Créer un client avec API Key
*/
async function getApiKeyClient() {
const apiKey = await getOne("SELECT value FROM settings WHERE key = 'google_calendar_api_key'");
if (!apiKey || !apiKey.value) {
throw new Error('Clé API non configurée');
}
return apiKey.value;
}
/**
* Créer un client Google Calendar selon la méthode d'authentification
* @param {number} userId - ID de l'utilisateur (pour OAuth2)
@@ -86,6 +100,9 @@ async function getCalendarClient(userId = null) {
if (authType === AUTH_TYPES.SERVICE_ACCOUNT) {
const auth = await getServiceAccountClient();
return google.calendar({ version: 'v3', auth });
} else if (authType === AUTH_TYPES.API_KEY) {
const apiKey = await getApiKeyClient();
return google.calendar({ version: 'v3', auth: apiKey });
} else {
// OAuth2
if (!userId) {
@@ -227,6 +244,17 @@ router.get('/auth-status', async (req, res) => {
isExpired: false,
needsReauth: !isAuthenticated
});
} else if (authType === AUTH_TYPES.API_KEY) {
// Vérifier si la clé API est configurée
const apiKey = await getOne("SELECT value FROM settings WHERE key = 'google_calendar_api_key'");
const isAuthenticated = !!apiKey?.value;
res.json({
authType: AUTH_TYPES.API_KEY,
isAuthenticated,
isExpired: false,
needsReauth: !isAuthenticated
});
} else {
// OAuth2
const tokens = await getOne(
@@ -324,7 +352,7 @@ router.get('/events', async (req, res) => {
/**
* POST /api/calendar/sync
* Synchroniser avec Google Calendar (OAuth2 ou Service Account)
* Synchroniser avec Google Calendar (OAuth2, Service Account ou API Key)
*/
router.post('/sync', async (req, res) => {
try {
@@ -339,6 +367,14 @@ router.post('/sync', async (req, res) => {
}
}
// Si API Key, permettre de spécifier l'ID du calendrier
if (authType === AUTH_TYPES.API_KEY) {
const calendarIdSetting = await getOne("SELECT value FROM settings WHERE key = 'google_calendar_id'");
if (calendarIdSetting?.value) {
calendarId = calendarIdSetting.value;
}
}
// Créer un client Google Calendar selon la méthode d'authentification
const calendar = await getCalendarClient(req.user.id);
+65 -7
View File
@@ -58,7 +58,7 @@ const Index = () => {
isAuthenticated: boolean;
isExpired: boolean;
needsReauth: boolean;
authType?: 'oauth2' | 'service_account';
authType?: 'oauth2' | 'service_account' | 'api_externe';
}>({
isAuthenticated: false,
isExpired: false,
@@ -1359,7 +1359,7 @@ const Index = () => {
<Label htmlFor="google-auth-type">Méthode d'authentification</Label>
<Select
value={settings.google_auth_type || 'oauth2'}
onValueChange={(value) => setSettings({ ...settings, google_auth_type: value as 'oauth2' | 'service_account' })}
onValueChange={(value) => setSettings({ ...settings, google_auth_type: value as 'oauth2' | 'service_account' | 'api_externe' })}
>
<SelectTrigger id="google-auth-type">
<SelectValue placeholder="Sélectionnez une méthode" />
@@ -1381,11 +1381,20 @@ const Index = () => {
</span>
</div>
</SelectItem>
<SelectItem value="api_externe">
<div className="flex flex-col">
<span className="font-medium">API externe</span>
<span className="text-xs text-muted-foreground">
Connectez-vous à une API externe pour Google Calendar
</span>
</div>
</SelectItem>
</SelectContent>
</Select>
<p className="text-xs text-muted-foreground">
<strong>OAuth 2.0</strong> : Pour accéder au calendrier personnel avec autorisation de l'utilisateur.<br/>
<strong>Service Account</strong> : Pour accéder à des calendriers partagés sans interaction utilisateur.
<strong>Service Account</strong> : Pour accéder à des calendriers partagés sans interaction utilisateur.<br/>
<strong>API externe</strong> : Utilisez votre propre API backend pour gérer la connexion à Google Calendar.
</p>
</div>
@@ -1474,6 +1483,50 @@ const Index = () => {
</>
)}
{/* Configuration API Key */}
{settings.google_auth_type === 'api_externe' && (
<>
<div className="space-y-2">
<Label htmlFor="google-api-key">Clé API Google Calendar</Label>
<Input
id="google-api-key"
type="password"
placeholder="AIzaSy..."
value={settings.google_calendar_api_key || ''}
onChange={(e) => setSettings({ ...settings, google_calendar_api_key: e.target.value })}
/>
<p className="text-sm text-muted-foreground">
Entrez votre clé API Google Calendar.
</p>
</div>
<div className="space-y-2">
<Label htmlFor="google-calendar-id-api">ID du calendrier</Label>
<Input
id="google-calendar-id-api"
type="text"
placeholder="primary ou votre-email@gmail.com"
value={settings.google_calendar_id || ''}
onChange={(e) => setSettings({ ...settings, google_calendar_id: e.target.value })}
/>
<p className="text-sm text-muted-foreground">
Utilisez "primary" pour votre calendrier principal ou l'email du calendrier spécifique.
</p>
<div className="p-3 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg">
<p className="text-sm font-semibold text-blue-900 dark:text-blue-200 mb-1">
ℹ️ Obtenir une clé API
</p>
<p className="text-xs text-blue-800 dark:text-blue-300 mb-2">
1. Allez sur <a href="https://console.cloud.google.com/apis/credentials" target="_blank" rel="noopener noreferrer" className="underline">Google Cloud Console</a><br/>
2. Créez une clé API<br/>
3. Activez l'API Google Calendar<br/>
4. Copiez la clé API ici
</p>
</div>
</div>
</>
)}
<div className="flex gap-2">
<Button onClick={handleSaveSettings} className="flex items-center gap-2">
<Key className="h-4 w-4" />
@@ -1483,7 +1536,8 @@ const Index = () => {
{(
((!settings.google_auth_type || settings.google_auth_type === 'oauth2') && settings.google_client_id && settings.google_client_secret) ||
(settings.google_auth_type === 'service_account' && settings.google_service_account_key)
(settings.google_auth_type === 'service_account' && settings.google_service_account_key) ||
(settings.google_auth_type === 'api_externe' && settings.google_calendar_api_key)
) && (
<>
<div className="border-t pt-4">
@@ -1491,7 +1545,11 @@ const Index = () => {
{calendarAuthStatus.isAuthenticated && !calendarAuthStatus.isExpired ? (
<div className="flex items-center gap-2 text-green-600">
<div className="h-2 w-2 rounded-full bg-green-600"></div>
<span>Connecté à Google Calendar ({calendarAuthStatus.authType === 'service_account' ? 'Service Account' : 'OAuth 2.0'})</span>
<span>Connecté à Google Calendar ({
calendarAuthStatus.authType === 'service_account' ? 'Service Account' :
calendarAuthStatus.authType === 'api_externe' ? 'API Key' :
'OAuth 2.0'
})</span>
</div>
) : (
<div className="flex items-center gap-2 text-yellow-600">
@@ -1538,7 +1596,7 @@ const Index = () => {
Déconnecter
</Button>
</>
) : (
) : settings.google_auth_type === 'api_externe' || settings.google_auth_type === 'service_account' ? (
<Button
onClick={async () => {
try {
@@ -1554,7 +1612,7 @@ const Index = () => {
<RefreshCw className="h-4 w-4" />
Synchroniser
</Button>
)}
) : null}
</div>
</>
)}
+1 -2
View File
@@ -7,10 +7,9 @@ export interface Settings {
theme?: 'light' | 'dark' | 'auto';
google_client_id?: string;
google_client_secret?: string;
google_auth_type?: 'oauth2' | 'service_account';
google_auth_type?: 'oauth2' | 'service_account' | 'api_externe';
google_service_account_key?: string;
google_calendar_email?: string;
// Legacy fields (kept for backward compatibility)
google_calendar_api_key?: string;
google_calendar_id?: string;
}