diff --git a/blechreiz/calendarCredentials.dat b/blechreiz/calendarCredentials.dat new file mode 100644 index 0000000..95dd964 Binary files /dev/null and b/blechreiz/calendarCredentials.dat differ diff --git a/eventplanner_gcal/google_sync.py b/eventplanner_gcal/google_sync.py index 6501f63..e582b1f 100644 --- a/eventplanner_gcal/google_sync.py +++ b/eventplanner_gcal/google_sync.py @@ -308,9 +308,7 @@ def delete_all_gcal_events(service=None): return 0 # Use batch request for efficiency - from googleapiclient.http import BatchHttpRequest - - batch = BatchHttpRequest() + batch = service.new_batch_http_request() for gcal_id in gcal_ids: batch.add(service.events().delete(calendarId="primary", eventId=gcal_id)) @@ -359,9 +357,7 @@ def sync_from_local_to_google(service=None): events_to_create_django_id = local_events_django_id - events_at_google_django_id events_to_delete_google_id = events_at_google_google_id - local_events_google_id - from googleapiclient.http import BatchHttpRequest - - batch = BatchHttpRequest() + batch = service.new_batch_http_request() batch_is_empty = True for event_django_id in events_to_create_django_id: diff --git a/eventplanner_gcal/management/commands/gcal_setup.py b/eventplanner_gcal/management/commands/gcal_setup.py new file mode 100644 index 0000000..bb5e22b --- /dev/null +++ b/eventplanner_gcal/management/commands/gcal_setup.py @@ -0,0 +1,80 @@ +""" +One-time OAuth2 setup command for Google Calendar integration. + +Run this locally (not in Docker) to authorize the app and generate +the credentials token file (calendarCredentials.dat). + +Usage: + python manage.py gcal_setup --client-secrets /path/to/client_secret_*.json + +The command opens a browser for the OAuth2 consent flow, then saves +the access+refresh token to the path configured in settings.GCAL_COUPLING['credentials_file']. +Copy that file to the server afterwards. +""" + +import os +import pickle + +from django.conf import settings +from django.core.management.base import BaseCommand, CommandError + +SCOPES = ["https://www.googleapis.com/auth/calendar"] + + +class Command(BaseCommand): + help = ( + "Perform one-time OAuth2 authorization for Google Calendar. " + "Run locally, then copy the resulting credentials file to the server." + ) + + def add_arguments(self, parser): + parser.add_argument( + "--client-secrets", + required=True, + metavar="FILE", + help="Path to the client_secret_*.json downloaded from Google Cloud Console", + ) + + def handle(self, *args, **options): + try: + from google_auth_oauthlib.flow import InstalledAppFlow + except ImportError: + raise CommandError( + "google-auth-oauthlib is not installed. " + "Run: pip install google-auth-oauthlib" + ) + + client_secrets = options["client_secrets"] + if not os.path.exists(client_secrets): + raise CommandError(f"Client secrets file not found: {client_secrets}") + + credentials_file = settings.GCAL_COUPLING["credentials_file"] + + self.stdout.write( + "Starting OAuth2 flow. A browser window will open for authorization." + ) + self.stdout.write( + "Make sure you are authorizing with the Google account whose " + "calendar you want to use.\n" + ) + + flow = InstalledAppFlow.from_client_secrets_file(client_secrets, SCOPES) + creds = flow.run_local_server(port=0) + + os.makedirs(os.path.dirname(credentials_file), exist_ok=True) if os.path.dirname(credentials_file) else None + + with open(credentials_file, "wb") as token: + pickle.dump(creds, token) + + self.stdout.write( + self.style.SUCCESS( + f"Credentials saved to: {credentials_file}\n" + f"Copy this file to the server at the same path inside the container." + ) + ) + self.stdout.write( + "\nTo copy to server (adjust paths as needed):\n" + f" docker cp {credentials_file} blechreiz:/app/calendarCredentials.dat\n" + "Or via scp:\n" + f" scp {credentials_file} core@server.fritz.box:/docker/blechreiz-website/calendarCredentials.dat" + )