Merge pull request #42 from R0m1k3/claude/integrate-google-calendar-api-011CV44Nie9HB7U35ArSUnNa

Feature: Ajout du support d'API Key pour Google Calendar
This commit is contained in:
LogiFlow authored and GitHub committed 2025-11-12 14:24:00 +01:00
commit 76d1837ebc
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;
}