mirror of
https://github.com/R0m1k3/noteflow.git
synced 2026-10-11 17:29:37 +02:00
Feature: Ajout du support OAuth2 et Service Account pour Google Calendar
Ajout de deux méthodes d'authentification au choix pour Google Calendar : 1. **OAuth 2.0** (méthode existante améliorée) : - ✅ Prompt de sélection de compte Google ('select_account consent') - ✅ Gestion dynamique du redirect_uri avec APP_URL - ✅ Idéal pour accès au calendrier personnel avec consentement 2. **Service Account** (nouvelle méthode) : - ✅ Authentification via clé JSON sans interaction utilisateur - ✅ Parfait pour calendriers partagés en entreprise - ✅ Synchronisation automatique en arrière-plan - ✅ Configuration de l'email du calendrier à synchroniser ## Backend (routes/calendar.routes.js) : - Ajout des constantes AUTH_TYPES (oauth2, service_account) - Fonction getAuthType() pour récupérer la méthode configurée - Fonction getServiceAccountClient() pour Service Account - Fonction getCalendarClient() unifiée qui choisit la bonne méthode - Route /auth-status mise à jour pour retourner le type d'auth - Route /sync adaptée pour supporter les deux méthodes - Support du calendrier email personnalisé pour Service Account ## Frontend (src/pages/Index.tsx) : - Menu déroulant pour choisir entre OAuth2 et Service Account - Interface conditionnelle selon la méthode choisie - Champ textarea pour la clé JSON du Service Account - Champ email pour spécifier le calendrier à synchroniser - Affichage du type d'authentification dans le statut - Amélioration de l'UI avec descriptions claires ## Documentation : - OAUTH_SETUP.md : Guide complet mis à jour avec les deux méthodes - Section dédiée au choix de la méthode - Guide pas à pas pour Service Account - Dépannage pour les deux méthodes - README_GOOGLE_CALENDAR.md : Guide de démarrage rapide (nouveau) - Comparaison des deux méthodes - Configuration rapide pour chaque méthode - Liens vers la documentation complète Cette implémentation offre une flexibilité maximale pour s'adapter à différents cas d'usage : personnel (OAuth2) ou professionnel (Service Account).
This commit is contained in:
4 files changed
+415
-65
No files matched your search
+120
-4
@@ -1,6 +1,25 @@
|
|||||||
# Configuration OAuth Google Calendar
|
# Configuration Google Calendar
|
||||||
|
|
||||||
Ce guide vous aide à résoudre les erreurs d'authentification OAuth Google.
|
Ce guide vous aide à configurer l'accès à Google Calendar avec deux méthodes disponibles : **OAuth 2.0** et **Service Account**.
|
||||||
|
|
||||||
|
## Choisir la bonne méthode
|
||||||
|
|
||||||
|
### OAuth 2.0 (Recommandé pour usage personnel)
|
||||||
|
- ✅ Accès au calendrier personnel de l'utilisateur
|
||||||
|
- ✅ Sécurisé avec consentement explicite
|
||||||
|
- ✅ Choix du compte Google à la connexion
|
||||||
|
- ⚠️ Nécessite une interaction utilisateur (popup de consentement)
|
||||||
|
|
||||||
|
### Service Account (Pour applications automatisées)
|
||||||
|
- ✅ Aucune interaction utilisateur requise
|
||||||
|
- ✅ Idéal pour calendriers partagés en entreprise
|
||||||
|
- ✅ Fonctionne en arrière-plan
|
||||||
|
- ⚠️ Nécessite de partager le calendrier avec le Service Account
|
||||||
|
- ⚠️ Configuration plus technique
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Configuration OAuth 2.0
|
||||||
|
|
||||||
## Problème : Erreur 400 - redirect_uri_mismatch
|
## Problème : Erreur 400 - redirect_uri_mismatch
|
||||||
|
|
||||||
@@ -86,10 +105,107 @@ Si vous ne voyez toujours pas le choix du compte :
|
|||||||
- [ ] Application redémarrée après modification du `.env`
|
- [ ] Application redémarrée après modification du `.env`
|
||||||
- [ ] Attendu 5-10 minutes après modification dans Google Cloud Console
|
- [ ] Attendu 5-10 minutes après modification dans Google Cloud Console
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Configuration Service Account
|
||||||
|
|
||||||
|
Le Service Account permet un accès automatisé sans interaction utilisateur, idéal pour des calendriers partagés.
|
||||||
|
|
||||||
|
## Étapes de configuration
|
||||||
|
|
||||||
|
### 1. Créer un Service Account
|
||||||
|
|
||||||
|
1. Allez sur [Google Cloud Console - Service Accounts](https://console.cloud.google.com/iam-admin/serviceaccounts)
|
||||||
|
|
||||||
|
2. Sélectionnez votre projet (ou créez-en un nouveau)
|
||||||
|
|
||||||
|
3. Cliquez sur **"+ CREATE SERVICE ACCOUNT"**
|
||||||
|
|
||||||
|
4. Donnez un nom au compte (ex: `noteflow-calendar`)
|
||||||
|
|
||||||
|
5. Cliquez sur **"CREATE AND CONTINUE"**
|
||||||
|
|
||||||
|
6. Sautez les étapes optionnelles et cliquez sur **"DONE"**
|
||||||
|
|
||||||
|
### 2. Générer une clé JSON
|
||||||
|
|
||||||
|
1. Dans la liste des Service Accounts, cliquez sur celui que vous venez de créer
|
||||||
|
|
||||||
|
2. Allez dans l'onglet **"KEYS"**
|
||||||
|
|
||||||
|
3. Cliquez sur **"ADD KEY"** > **"Create new key"**
|
||||||
|
|
||||||
|
4. Choisissez le format **JSON**
|
||||||
|
|
||||||
|
5. Téléchargez le fichier JSON (conservez-le en sécurité !)
|
||||||
|
|
||||||
|
### 3. Activer l'API Google Calendar
|
||||||
|
|
||||||
|
1. Allez sur [Google Cloud Console - API Library](https://console.cloud.google.com/apis/library)
|
||||||
|
|
||||||
|
2. Recherchez **"Google Calendar API"**
|
||||||
|
|
||||||
|
3. Cliquez sur **"ENABLE"**
|
||||||
|
|
||||||
|
### 4. Partager votre calendrier
|
||||||
|
|
||||||
|
1. Ouvrez [Google Calendar](https://calendar.google.com)
|
||||||
|
|
||||||
|
2. Trouvez votre calendrier dans la liste à gauche
|
||||||
|
|
||||||
|
3. Cliquez sur les **3 points** à côté du nom du calendrier
|
||||||
|
|
||||||
|
4. Sélectionnez **"Paramètres et partage"**
|
||||||
|
|
||||||
|
5. Faites défiler jusqu'à **"Partager avec des personnes en particulier"**
|
||||||
|
|
||||||
|
6. Cliquez sur **"+ Ajouter des personnes"**
|
||||||
|
|
||||||
|
7. Collez l'**email du Service Account** (trouvé dans le fichier JSON, champ `client_email`)
|
||||||
|
|
||||||
|
8. Donnez les permissions **"Afficher tous les détails des événements"**
|
||||||
|
|
||||||
|
9. Cliquez sur **"Envoyer"**
|
||||||
|
|
||||||
|
### 5. Configurer NoteFlow
|
||||||
|
|
||||||
|
1. Dans NoteFlow, allez dans **Administration** > **Google Calendar**
|
||||||
|
|
||||||
|
2. Sélectionnez **"Service Account"** dans le menu déroulant
|
||||||
|
|
||||||
|
3. Ouvrez le fichier JSON téléchargé et **copiez tout son contenu**
|
||||||
|
|
||||||
|
4. Collez le contenu dans le champ **"Clé Service Account (JSON)"**
|
||||||
|
|
||||||
|
5. Entrez votre email (celui du calendrier partagé) dans **"Email du calendrier à synchroniser"**
|
||||||
|
|
||||||
|
6. Cliquez sur **"Enregistrer"**
|
||||||
|
|
||||||
|
7. Cliquez sur **"Synchroniser"** pour tester
|
||||||
|
|
||||||
|
## Dépannage Service Account
|
||||||
|
|
||||||
|
### Erreur : "Clé Service Account invalide"
|
||||||
|
- Vérifiez que vous avez copié l'intégralité du fichier JSON
|
||||||
|
- Assurez-vous qu'il n'y a pas d'espaces ou de caractères en trop
|
||||||
|
|
||||||
|
### Erreur : "Calendar not found" ou "Permission denied"
|
||||||
|
- Vérifiez que vous avez bien partagé le calendrier avec l'email du Service Account
|
||||||
|
- Vérifiez que l'email du calendrier est correct
|
||||||
|
- Attendez quelques minutes après avoir partagé le calendrier
|
||||||
|
|
||||||
|
### Les événements ne se synchronisent pas
|
||||||
|
- Vérifiez que l'API Google Calendar est activée dans votre projet
|
||||||
|
- Vérifiez les logs de l'application pour plus de détails
|
||||||
|
- Assurez-vous que le Service Account a les bonnes permissions
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Besoin d'aide ?
|
## Besoin d'aide ?
|
||||||
|
|
||||||
Si le problème persiste :
|
Si le problème persiste :
|
||||||
1. Vérifiez les logs de l'application pour des messages d'erreur détaillés
|
1. Vérifiez les logs de l'application pour des messages d'erreur détaillés
|
||||||
2. Assurez-vous que votre navigateur accepte les cookies tiers
|
2. Pour OAuth 2.0 : Assurez-vous que votre navigateur accepte les cookies tiers
|
||||||
3. Essayez en navigation privée pour éliminer les problèmes de cache
|
3. Pour OAuth 2.0 : Essayez en navigation privée pour éliminer les problèmes de cache
|
||||||
4. Vérifiez que l'API Google Calendar est bien activée dans votre projet Google Cloud
|
4. Vérifiez que l'API Google Calendar est bien activée dans votre projet Google Cloud
|
||||||
|
5. Pour Service Account : Vérifiez que le calendrier est bien partagé avec le bon email
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# Google Calendar - Deux méthodes d'authentification
|
||||||
|
|
||||||
|
NoteFlow supporte maintenant **deux méthodes** pour se connecter à Google Calendar :
|
||||||
|
|
||||||
|
## 🔐 OAuth 2.0 (Recommandé pour usage personnel)
|
||||||
|
**Avantages** :
|
||||||
|
- Accès sécurisé à votre calendrier personnel
|
||||||
|
- Choix du compte Google lors de la connexion
|
||||||
|
- Consentement explicite de l'utilisateur
|
||||||
|
|
||||||
|
**Utilisation** : Administration > Google Calendar > Méthode : OAuth 2.0
|
||||||
|
|
||||||
|
[📖 Guide complet OAuth 2.0](./OAUTH_SETUP.md#configuration-oauth-20)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🤖 Service Account (Pour automatisation)
|
||||||
|
**Avantages** :
|
||||||
|
- Aucune interaction utilisateur requise
|
||||||
|
- Parfait pour calendriers partagés en entreprise
|
||||||
|
- Fonctionne en arrière-plan automatiquement
|
||||||
|
|
||||||
|
**Utilisation** : Administration > Google Calendar > Méthode : Service Account
|
||||||
|
|
||||||
|
[📖 Guide complet Service Account](./OAUTH_SETUP.md#configuration-service-account)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Problèmes courants résolus
|
||||||
|
|
||||||
|
### ✅ Erreur 400 : redirect_uri_mismatch
|
||||||
|
- Variable `APP_URL` ajoutée dans `.env.example`
|
||||||
|
- Guide de configuration détaillé dans OAUTH_SETUP.md
|
||||||
|
|
||||||
|
### ✅ Pas de choix de compte Google
|
||||||
|
- Le prompt `select_account` force maintenant l'affichage du sélecteur
|
||||||
|
|
||||||
|
### ✅ Besoin d'accès automatisé sans popup
|
||||||
|
- Utilisez Service Account pour une authentification sans interaction
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration rapide
|
||||||
|
|
||||||
|
### Pour OAuth 2.0 :
|
||||||
|
1. Créez `.env` avec `APP_URL=http://localhost:2222`
|
||||||
|
2. Configurez OAuth 2.0 dans Google Cloud Console
|
||||||
|
3. Dans NoteFlow : Administration > Google Calendar > OAuth 2.0
|
||||||
|
4. Ajoutez Client ID et Client Secret
|
||||||
|
5. Cliquez sur "Se connecter avec Google"
|
||||||
|
|
||||||
|
### Pour Service Account :
|
||||||
|
1. Créez un Service Account sur Google Cloud Console
|
||||||
|
2. Téléchargez la clé JSON
|
||||||
|
3. Partagez votre calendrier avec l'email du Service Account
|
||||||
|
4. Dans NoteFlow : Administration > Google Calendar > Service Account
|
||||||
|
5. Collez le JSON et l'email du calendrier
|
||||||
|
6. Cliquez sur "Synchroniser"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Documentation complète
|
||||||
|
|
||||||
|
📚 Consultez [OAUTH_SETUP.md](./OAUTH_SETUP.md) pour :
|
||||||
|
- Guide pas à pas complet
|
||||||
|
- Résolution des erreurs
|
||||||
|
- Checklist de vérification
|
||||||
|
- Conseils de dépannage
|
||||||
+103
-17
@@ -12,6 +12,22 @@ router.use(authenticateToken);
|
|||||||
// Scopes Google Calendar requis
|
// Scopes Google Calendar requis
|
||||||
const SCOPES = ['https://www.googleapis.com/auth/calendar.readonly'];
|
const SCOPES = ['https://www.googleapis.com/auth/calendar.readonly'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Types d'authentification supportés
|
||||||
|
*/
|
||||||
|
const AUTH_TYPES = {
|
||||||
|
OAUTH2: 'oauth2',
|
||||||
|
SERVICE_ACCOUNT: 'service_account'
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupérer le type d'authentification configuré
|
||||||
|
*/
|
||||||
|
async function getAuthType() {
|
||||||
|
const authType = await getOne("SELECT value FROM settings WHERE key = 'google_auth_type'");
|
||||||
|
return authType?.value || AUTH_TYPES.OAUTH2; // Par défaut OAuth2
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Créer un client OAuth2
|
* Créer un client OAuth2
|
||||||
* @param {string} [customRedirectUri] - URI de redirection personnalisée (optionnel)
|
* @param {string} [customRedirectUri] - URI de redirection personnalisée (optionnel)
|
||||||
@@ -35,6 +51,51 @@ async function getOAuth2Client(customRedirectUri = null) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Créer un client avec Service Account
|
||||||
|
*/
|
||||||
|
async function getServiceAccountClient() {
|
||||||
|
const serviceAccountKey = await getOne("SELECT value FROM settings WHERE key = 'google_service_account_key'");
|
||||||
|
|
||||||
|
if (!serviceAccountKey || !serviceAccountKey.value) {
|
||||||
|
throw new Error('Clé Service Account non configurée');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const credentials = JSON.parse(serviceAccountKey.value);
|
||||||
|
|
||||||
|
const auth = new google.auth.GoogleAuth({
|
||||||
|
credentials,
|
||||||
|
scopes: SCOPES
|
||||||
|
});
|
||||||
|
|
||||||
|
return await auth.getClient();
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Erreur lors de la création du client Service Account:', error);
|
||||||
|
throw new Error('Clé Service Account invalide');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Créer un client Google Calendar selon la méthode d'authentification
|
||||||
|
* @param {number} userId - ID de l'utilisateur (pour OAuth2)
|
||||||
|
*/
|
||||||
|
async function getCalendarClient(userId = null) {
|
||||||
|
const authType = await getAuthType();
|
||||||
|
|
||||||
|
if (authType === AUTH_TYPES.SERVICE_ACCOUNT) {
|
||||||
|
const auth = await getServiceAccountClient();
|
||||||
|
return google.calendar({ version: 'v3', auth });
|
||||||
|
} else {
|
||||||
|
// OAuth2
|
||||||
|
if (!userId) {
|
||||||
|
throw new Error('User ID requis pour OAuth2');
|
||||||
|
}
|
||||||
|
const oauth2Client = await getValidTokens(userId);
|
||||||
|
return google.calendar({ version: 'v3', auth: oauth2Client });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET /api/calendar/auth-url
|
* GET /api/calendar/auth-url
|
||||||
* Générer l'URL d'authentification OAuth
|
* Générer l'URL d'authentification OAuth
|
||||||
@@ -153,19 +214,36 @@ router.get('/oauth-callback', async (req, res) => {
|
|||||||
*/
|
*/
|
||||||
router.get('/auth-status', async (req, res) => {
|
router.get('/auth-status', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const tokens = await getOne(
|
const authType = await getAuthType();
|
||||||
'SELECT access_token, expiry_date FROM google_oauth_tokens WHERE user_id = ?',
|
|
||||||
[req.user.id]
|
|
||||||
);
|
|
||||||
|
|
||||||
const isAuthenticated = !!tokens;
|
if (authType === AUTH_TYPES.SERVICE_ACCOUNT) {
|
||||||
const isExpired = tokens && tokens.expiry_date && tokens.expiry_date < Date.now();
|
// Vérifier si la clé Service Account est configurée
|
||||||
|
const serviceAccountKey = await getOne("SELECT value FROM settings WHERE key = 'google_service_account_key'");
|
||||||
|
const isAuthenticated = !!serviceAccountKey?.value;
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
isAuthenticated,
|
authType: AUTH_TYPES.SERVICE_ACCOUNT,
|
||||||
isExpired,
|
isAuthenticated,
|
||||||
needsReauth: !isAuthenticated || isExpired
|
isExpired: false,
|
||||||
});
|
needsReauth: !isAuthenticated
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// OAuth2
|
||||||
|
const tokens = await getOne(
|
||||||
|
'SELECT access_token, expiry_date FROM google_oauth_tokens WHERE user_id = ?',
|
||||||
|
[req.user.id]
|
||||||
|
);
|
||||||
|
|
||||||
|
const isAuthenticated = !!tokens;
|
||||||
|
const isExpired = tokens && tokens.expiry_date && tokens.expiry_date < Date.now();
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
authType: AUTH_TYPES.OAUTH2,
|
||||||
|
isAuthenticated,
|
||||||
|
isExpired,
|
||||||
|
needsReauth: !isAuthenticated || isExpired
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Erreur lors de la vérification du statut OAuth:', error);
|
logger.error('Erreur lors de la vérification du statut OAuth:', error);
|
||||||
res.status(500).json({ error: 'Erreur serveur' });
|
res.status(500).json({ error: 'Erreur serveur' });
|
||||||
@@ -246,15 +324,23 @@ router.get('/events', async (req, res) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* POST /api/calendar/sync
|
* POST /api/calendar/sync
|
||||||
* Synchroniser avec Google Calendar via OAuth
|
* Synchroniser avec Google Calendar (OAuth2 ou Service Account)
|
||||||
*/
|
*/
|
||||||
router.post('/sync', async (req, res) => {
|
router.post('/sync', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
// Obtenir un client OAuth valide
|
const authType = await getAuthType();
|
||||||
const oauth2Client = await getValidTokens(req.user.id);
|
let calendarId = 'primary'; // Par défaut : calendrier principal
|
||||||
|
|
||||||
// Créer un client Google Calendar avec OAuth
|
// Si Service Account, permettre de spécifier l'email du calendrier
|
||||||
const calendar = google.calendar({ version: 'v3', auth: oauth2Client });
|
if (authType === AUTH_TYPES.SERVICE_ACCOUNT) {
|
||||||
|
const calendarEmail = await getOne("SELECT value FROM settings WHERE key = 'google_calendar_email'");
|
||||||
|
if (calendarEmail?.value) {
|
||||||
|
calendarId = calendarEmail.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Créer un client Google Calendar selon la méthode d'authentification
|
||||||
|
const calendar = await getCalendarClient(req.user.id);
|
||||||
|
|
||||||
// Récupérer les événements des 30 prochains jours
|
// Récupérer les événements des 30 prochains jours
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
@@ -262,7 +348,7 @@ router.post('/sync', async (req, res) => {
|
|||||||
futureDate.setDate(futureDate.getDate() + 30);
|
futureDate.setDate(futureDate.getDate() + 30);
|
||||||
|
|
||||||
const response = await calendar.events.list({
|
const response = await calendar.events.list({
|
||||||
calendarId: 'primary', // Calendrier principal de l'utilisateur
|
calendarId, // Utilise le calendrier configuré
|
||||||
timeMin: now.toISOString(),
|
timeMin: now.toISOString(),
|
||||||
timeMax: futureDate.toISOString(),
|
timeMax: futureDate.toISOString(),
|
||||||
maxResults: 50,
|
maxResults: 50,
|
||||||
|
|||||||
+124
-44
@@ -1345,52 +1345,129 @@ const Index = () => {
|
|||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="flex items-center gap-2">
|
<CardTitle className="flex items-center gap-2">
|
||||||
<CalendarIcon className="h-5 w-5" />
|
<CalendarIcon className="h-5 w-5" />
|
||||||
Configuration Google Calendar OAuth 2.0
|
Configuration Google Calendar
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4">
|
<CardContent className="space-y-4">
|
||||||
|
{/* Choix du type d'authentification */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="google-client-id">Client ID Google</Label>
|
<Label htmlFor="google-auth-type">Méthode d'authentification</Label>
|
||||||
<Input
|
<Select
|
||||||
id="google-client-id"
|
value={settings.google_auth_type || 'oauth2'}
|
||||||
type="text"
|
onValueChange={(value) => setSettings({ ...settings, google_auth_type: value })}
|
||||||
placeholder="123456789-abcdef.apps.googleusercontent.com"
|
>
|
||||||
value={settings.google_client_id || ''}
|
<SelectTrigger id="google-auth-type">
|
||||||
onChange={(e) => setSettings({ ...settings, google_client_id: e.target.value })}
|
<SelectValue placeholder="Sélectionnez une méthode" />
|
||||||
/>
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="oauth2">
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="font-medium">OAuth 2.0</span>
|
||||||
|
<span className="text-xs text-muted-foreground">
|
||||||
|
Calendrier personnel avec consentement utilisateur
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="service_account">
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="font-medium">Service Account</span>
|
||||||
|
<span className="text-xs text-muted-foreground">
|
||||||
|
Clé JSON pour calendriers partagés (sans interaction)
|
||||||
|
</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.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
{/* Configuration OAuth2 */}
|
||||||
<Label htmlFor="google-client-secret">Client Secret Google</Label>
|
{(!settings.google_auth_type || settings.google_auth_type === 'oauth2') && (
|
||||||
<Input
|
<>
|
||||||
id="google-client-secret"
|
<div className="space-y-2">
|
||||||
type="password"
|
<Label htmlFor="google-client-id">Client ID Google</Label>
|
||||||
placeholder="GOCSPX-..."
|
<Input
|
||||||
value={settings.google_client_secret || ''}
|
id="google-client-id"
|
||||||
onChange={(e) => setSettings({ ...settings, google_client_secret: e.target.value })}
|
type="text"
|
||||||
/>
|
placeholder="123456789-abcdef.apps.googleusercontent.com"
|
||||||
<p className="text-sm text-muted-foreground">
|
value={settings.google_client_id || ''}
|
||||||
Créez un projet OAuth 2.0 sur{' '}
|
onChange={(e) => setSettings({ ...settings, google_client_id: e.target.value })}
|
||||||
<a href="https://console.cloud.google.com/apis/credentials" target="_blank" rel="noopener noreferrer" className="text-primary hover:underline">
|
/>
|
||||||
Google Cloud Console
|
</div>
|
||||||
</a>
|
|
||||||
{' '}et activez l'API Google Calendar.
|
<div className="space-y-2">
|
||||||
</p>
|
<Label htmlFor="google-client-secret">Client Secret Google</Label>
|
||||||
<div className="p-3 bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg">
|
<Input
|
||||||
<p className="text-sm font-semibold text-yellow-900 dark:text-yellow-200 mb-1">
|
id="google-client-secret"
|
||||||
⚠️ IMPORTANT : URI de redirection autorisée
|
type="password"
|
||||||
</p>
|
placeholder="GOCSPX-..."
|
||||||
<p className="text-xs text-yellow-800 dark:text-yellow-300 mb-2">
|
value={settings.google_client_secret || ''}
|
||||||
Ajoutez cette URL exacte dans la console Google Cloud (OAuth 2.0 Client IDs → URIs de redirection autorisées) :
|
onChange={(e) => setSettings({ ...settings, google_client_secret: e.target.value })}
|
||||||
</p>
|
/>
|
||||||
<code className="block p-2 bg-white dark:bg-gray-800 border border-yellow-300 dark:border-yellow-700 rounded text-xs font-mono break-all">
|
<p className="text-sm text-muted-foreground">
|
||||||
{window.location.origin}/api/calendar/oauth-callback
|
Créez un projet OAuth 2.0 sur{' '}
|
||||||
</code>
|
<a href="https://console.cloud.google.com/apis/credentials" target="_blank" rel="noopener noreferrer" className="text-primary hover:underline">
|
||||||
<p className="text-xs text-yellow-800 dark:text-yellow-300 mt-2">
|
Google Cloud Console
|
||||||
💡 Si vous utilisez un domaine différent, créez un fichier .env avec APP_URL=https://votre-domaine.com
|
</a>
|
||||||
</p>
|
{' '}et activez l'API Google Calendar.
|
||||||
</div>
|
</p>
|
||||||
</div>
|
<div className="p-3 bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg">
|
||||||
|
<p className="text-sm font-semibold text-yellow-900 dark:text-yellow-200 mb-1">
|
||||||
|
⚠️ IMPORTANT : URI de redirection autorisée
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-yellow-800 dark:text-yellow-300 mb-2">
|
||||||
|
Ajoutez cette URL exacte dans la console Google Cloud (OAuth 2.0 Client IDs → URIs de redirection autorisées) :
|
||||||
|
</p>
|
||||||
|
<code className="block p-2 bg-white dark:bg-gray-800 border border-yellow-300 dark:border-yellow-700 rounded text-xs font-mono break-all">
|
||||||
|
{window.location.origin}/api/calendar/oauth-callback
|
||||||
|
</code>
|
||||||
|
<p className="text-xs text-yellow-800 dark:text-yellow-300 mt-2">
|
||||||
|
💡 Si vous utilisez un domaine différent, créez un fichier .env avec APP_URL=https://votre-domaine.com
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Configuration Service Account */}
|
||||||
|
{settings.google_auth_type === 'service_account' && (
|
||||||
|
<>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="google-service-account-key">Clé Service Account (JSON)</Label>
|
||||||
|
<textarea
|
||||||
|
id="google-service-account-key"
|
||||||
|
className="w-full min-h-[200px] p-3 text-xs font-mono border rounded-md"
|
||||||
|
placeholder='Collez ici le contenu du fichier JSON de votre Service Account { "type": "service_account", "project_id": "...", "private_key_id": "...", ... }'
|
||||||
|
value={settings.google_service_account_key || ''}
|
||||||
|
onChange={(e) => setSettings({ ...settings, google_service_account_key: e.target.value })}
|
||||||
|
/>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Créez un Service Account sur{' '}
|
||||||
|
<a href="https://console.cloud.google.com/iam-admin/serviceaccounts" target="_blank" rel="noopener noreferrer" className="text-primary hover:underline">
|
||||||
|
Google Cloud Console
|
||||||
|
</a>
|
||||||
|
{' '}et téléchargez la clé JSON.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="google-calendar-email">Email du calendrier à synchroniser</Label>
|
||||||
|
<Input
|
||||||
|
id="google-calendar-email"
|
||||||
|
type="email"
|
||||||
|
placeholder="votre-email@gmail.com"
|
||||||
|
value={settings.google_calendar_email || ''}
|
||||||
|
onChange={(e) => setSettings({ ...settings, google_calendar_email: e.target.value })}
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
⚠️ Important : Vous devez partager votre calendrier avec l'email du Service Account (visible dans le JSON)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button onClick={handleSaveSettings} className="flex items-center gap-2">
|
<Button onClick={handleSaveSettings} className="flex items-center gap-2">
|
||||||
@@ -1399,14 +1476,17 @@ const Index = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{settings.google_client_id && settings.google_client_secret && (
|
{(
|
||||||
|
((!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)
|
||||||
|
) && (
|
||||||
<>
|
<>
|
||||||
<div className="border-t pt-4">
|
<div className="border-t pt-4">
|
||||||
<h3 className="font-semibold mb-2">Statut de connexion</h3>
|
<h3 className="font-semibold mb-2">Statut de connexion</h3>
|
||||||
{calendarAuthStatus.isAuthenticated && !calendarAuthStatus.isExpired ? (
|
{calendarAuthStatus.isAuthenticated && !calendarAuthStatus.isExpired ? (
|
||||||
<div className="flex items-center gap-2 text-green-600">
|
<div className="flex items-center gap-2 text-green-600">
|
||||||
<div className="h-2 w-2 rounded-full bg-green-600"></div>
|
<div className="h-2 w-2 rounded-full bg-green-600"></div>
|
||||||
<span>Connecté à Google Calendar</span>
|
<span>Connecté à Google Calendar ({calendarAuthStatus.authType === 'service_account' ? 'Service Account' : 'OAuth 2.0'})</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center gap-2 text-yellow-600">
|
<div className="flex items-center gap-2 text-yellow-600">
|
||||||
@@ -1417,12 +1497,12 @@ const Index = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
{calendarAuthStatus.needsReauth ? (
|
{calendarAuthStatus.needsReauth && (!settings.google_auth_type || settings.google_auth_type === 'oauth2') ? (
|
||||||
<Button onClick={handleGoogleConnect} className="flex items-center gap-2">
|
<Button onClick={handleGoogleConnect} className="flex items-center gap-2">
|
||||||
<CalendarIcon className="h-4 w-4" />
|
<CalendarIcon className="h-4 w-4" />
|
||||||
Se connecter avec Google
|
Se connecter avec Google
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : !calendarAuthStatus.needsReauth && (!settings.google_auth_type || settings.google_auth_type === 'oauth2') ? (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
|
|||||||
Reference in new issue
Block a user