feat: add OAuth 2.0 support for GSC monitoring
Some checks are pending
Build and Deploy / build (push) Waiting to run
Some checks are pending
Build and Deploy / build (push) Waiting to run
- Updated monitor.py to support both OAuth and Service Account - Created setup-oauth-local.py for easy local authorization - Created cronjob-oauth.yaml for OAuth-based deployment - Updated README with both authentication options - OAuth is now the recommended method (no key file needed)
This commit is contained in:
@@ -8,6 +8,82 @@ This setup creates an automated monitoring system for Google Search Console that
|
||||
2. Access to Google Search Console for manoonoils.com
|
||||
3. kubectl access to your Kubernetes cluster
|
||||
|
||||
## Authentication Methods
|
||||
|
||||
Choose one of the following authentication methods:
|
||||
|
||||
### Option A: OAuth 2.0 (Recommended - No Service Account Key)
|
||||
|
||||
This is the **easiest method** if you can't create service account keys.
|
||||
|
||||
#### Step 1: Enable Search Console API
|
||||
1. Go to https://console.cloud.google.com
|
||||
2. Create/select project: `manoonoils-monitoring`
|
||||
3. Go to **APIs & Services → Library**
|
||||
4. Search: "Google Search Console API"
|
||||
5. Click: **Enable**
|
||||
|
||||
#### Step 2: Create OAuth Credentials
|
||||
1. Go to **APIs & Services → Credentials**
|
||||
2. Click: **Create Credentials → OAuth client ID**
|
||||
3. Click: **Configure Consent Screen**
|
||||
4. User Type: **External**
|
||||
5. Fill in:
|
||||
- App name: `ManoonOils GSC Monitor`
|
||||
- User support email: your email
|
||||
- Developer contact: your email
|
||||
6. Click: **Save and Continue** (3 times)
|
||||
7. Click: **Back to Dashboard**
|
||||
8. Back on Credentials page
|
||||
9. Click: **Create Credentials → OAuth client ID**
|
||||
10. Application type: **Desktop app**
|
||||
11. Name: `GSC Desktop Client`
|
||||
12. Click: **Create**
|
||||
13. Click: **DOWNLOAD JSON**
|
||||
|
||||
#### Step 3: Run Local Authorization
|
||||
On your local machine (laptop):
|
||||
|
||||
```bash
|
||||
# Go to the monitoring directory
|
||||
cd scripts/gsc-monitoring
|
||||
|
||||
# Install dependencies
|
||||
pip3 install google-auth google-auth-oauthlib google-auth-httplib2 google-api-python-client
|
||||
|
||||
# Run the OAuth setup
|
||||
python3 setup-oauth-local.py
|
||||
```
|
||||
|
||||
This will:
|
||||
- Open a browser for you to authorize the app
|
||||
- Generate a `gsc-oauth-credentials.json` file
|
||||
- The refresh token never expires!
|
||||
|
||||
#### Step 4: Deploy to Kubernetes
|
||||
|
||||
```bash
|
||||
# Copy the credentials to server
|
||||
scp gsc-oauth-credentials.json doorwaysftw:/tmp/
|
||||
|
||||
# Create the secret
|
||||
ssh doorwaysftw "kubectl create secret generic gsc-oauth-credentials \
|
||||
--namespace=manoonoils \
|
||||
--from-file=oauth-credentials.json=/tmp/gsc-oauth-credentials.json"
|
||||
|
||||
# Deploy the monitoring
|
||||
ssh doorwaysftw "kubectl apply -f -" < cronjob-oauth.yaml
|
||||
|
||||
# Verify
|
||||
ssh doorwaysftw "kubectl get cronjob gsc-monitoring-oauth -n manoonoils"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Option B: Service Account (Requires Key Creation)
|
||||
|
||||
**Note:** This only works if you can create service account keys in Google Cloud.
|
||||
|
||||
## Setup Steps
|
||||
|
||||
### Step 1: Create Google Cloud Project
|
||||
|
||||
32
scripts/gsc-monitoring/cronjob-oauth.yaml
Normal file
32
scripts/gsc-monitoring/cronjob-oauth.yaml
Normal file
@@ -0,0 +1,32 @@
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: gsc-monitoring-oauth
|
||||
namespace: manoonoils
|
||||
spec:
|
||||
schedule: "0 9 * * *" # Run daily at 9 AM UTC
|
||||
jobTemplate:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: gsc-monitor
|
||||
image: gcr.io/manoonoils/gsc-monitoring:latest
|
||||
env:
|
||||
- name: GSC_OAUTH_FILE
|
||||
value: /etc/gsc-monitoring/oauth-credentials.json
|
||||
- name: PYTHONUNBUFFERED
|
||||
value: "1"
|
||||
volumeMounts:
|
||||
- name: gsc-oauth-credentials
|
||||
mountPath: /etc/gsc-monitoring
|
||||
readOnly: true
|
||||
- name: logs
|
||||
mountPath: /var/log/gsc-monitoring
|
||||
volumes:
|
||||
- name: gsc-oauth-credentials
|
||||
secret:
|
||||
secretName: gsc-oauth-credentials
|
||||
- name: logs
|
||||
emptyDir: {}
|
||||
restartPolicy: OnFailure
|
||||
@@ -2,6 +2,10 @@
|
||||
"""
|
||||
Google Search Console Monitoring Script
|
||||
Monitors search performance, crawl errors, and indexing status
|
||||
|
||||
Supports both:
|
||||
1. Service Account (with JSON key file)
|
||||
2. OAuth 2.0 (user authentication)
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -9,6 +13,8 @@ import json
|
||||
import sys
|
||||
from datetime import datetime, timedelta
|
||||
from google.oauth2 import service_account
|
||||
from google.oauth2.credentials import Credentials as OAuthCredentials
|
||||
from google.auth.transport.requests import Request
|
||||
from googleapiclient.discovery import build
|
||||
from googleapiclient.errors import HttpError
|
||||
|
||||
@@ -16,14 +22,54 @@ from googleapiclient.errors import HttpError
|
||||
SITE_URL = "https://manoonoils.com/"
|
||||
SCOPES = ["https://www.googleapis.com/auth/webmasters.readonly"]
|
||||
KEY_FILE = os.environ.get("GSC_KEY_FILE", "/etc/gsc-monitoring/service-account.json")
|
||||
OAUTH_FILE = os.environ.get(
|
||||
"GSC_OAUTH_FILE", "/etc/gsc-monitoring/oauth-credentials.json"
|
||||
)
|
||||
|
||||
|
||||
def get_service():
|
||||
"""Authenticate and return Search Console service"""
|
||||
credentials = service_account.Credentials.from_service_account_file(
|
||||
KEY_FILE, scopes=SCOPES
|
||||
)
|
||||
return build("webmasters", "v3", credentials=credentials)
|
||||
|
||||
# Try OAuth first
|
||||
if os.path.exists(OAUTH_FILE):
|
||||
print("Using OAuth authentication...")
|
||||
with open(OAUTH_FILE, "r") as f:
|
||||
creds_info = json.load(f)
|
||||
|
||||
creds = OAuthCredentials(
|
||||
token=creds_info["token"],
|
||||
refresh_token=creds_info["refresh_token"],
|
||||
token_uri=creds_info["token_uri"],
|
||||
client_id=creds_info["client_id"],
|
||||
client_secret=creds_info["client_secret"],
|
||||
scopes=creds_info["scopes"],
|
||||
)
|
||||
|
||||
# Refresh if expired
|
||||
if creds.expired:
|
||||
creds.refresh(Request())
|
||||
# Save updated credentials
|
||||
creds_info["token"] = creds.token
|
||||
with open(OAUTH_FILE, "w") as f:
|
||||
json.dump(creds_info, f, indent=2)
|
||||
|
||||
return build("webmasters", "v3", credentials=creds)
|
||||
|
||||
# Fall back to service account
|
||||
elif os.path.exists(KEY_FILE):
|
||||
print("Using Service Account authentication...")
|
||||
credentials = service_account.Credentials.from_service_account_file(
|
||||
KEY_FILE, scopes=SCOPES
|
||||
)
|
||||
return build("webmasters", "v3", credentials=credentials)
|
||||
|
||||
else:
|
||||
raise FileNotFoundError(
|
||||
f"No credentials found. Please set up either:\n"
|
||||
f" 1. OAuth: {OAUTH_FILE}\n"
|
||||
f" 2. Service Account: {KEY_FILE}\n"
|
||||
f"\nSee README.md for setup instructions."
|
||||
)
|
||||
|
||||
|
||||
def get_search_analytics(service, days=7):
|
||||
@@ -157,12 +203,6 @@ def main():
|
||||
"""Main monitoring function"""
|
||||
print("🔍 Starting Google Search Console monitoring...")
|
||||
|
||||
# Check for credentials
|
||||
if not os.path.exists(KEY_FILE):
|
||||
print(f"❌ Error: Service account key file not found at {KEY_FILE}")
|
||||
print("Please set up Google Cloud credentials first.")
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
service = get_service()
|
||||
|
||||
@@ -182,6 +222,9 @@ def main():
|
||||
f.write(report)
|
||||
print(f"\n💾 Report saved to: {report_file}")
|
||||
|
||||
except FileNotFoundError as e:
|
||||
print(f"❌ {e}")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"❌ Error: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
164
scripts/gsc-monitoring/setup-oauth-local.py
Normal file
164
scripts/gsc-monitoring/setup-oauth-local.py
Normal file
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
OAuth Setup for Google Search Console Monitoring
|
||||
Run this locally (not on the server) to generate OAuth credentials
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import webbrowser
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def setup_oauth():
|
||||
"""Interactive OAuth setup"""
|
||||
|
||||
print("=" * 70)
|
||||
print("GOOGLE SEARCH CONSOLE - OAUTH 2.0 SETUP")
|
||||
print("=" * 70)
|
||||
print()
|
||||
print("This method uses OAuth 2.0 (no service account key needed)")
|
||||
print("You'll authenticate once with your Google account.")
|
||||
print()
|
||||
|
||||
# Step 1: Enable API
|
||||
print("STEP 1: Enable Search Console API")
|
||||
print("-" * 70)
|
||||
print("1. Go to: https://console.cloud.google.com")
|
||||
print("2. Create/select project: manoonoils-monitoring")
|
||||
print("3. Go to: APIs & Services → Library")
|
||||
print("4. Search: 'Google Search Console API'")
|
||||
print("5. Click: Enable")
|
||||
print()
|
||||
input("Press Enter when you've enabled the API...")
|
||||
|
||||
# Step 2: Create OAuth credentials
|
||||
print()
|
||||
print("STEP 2: Create OAuth Credentials")
|
||||
print("-" * 70)
|
||||
print("1. Go to: APIs & Services → Credentials")
|
||||
print("2. Click: Create Credentials → OAuth client ID")
|
||||
print("3. Click: Configure Consent Screen")
|
||||
print("4. User Type: External")
|
||||
print("5. App name: ManoonOils GSC Monitor")
|
||||
print("6. User support email: your-email@manoonoils.com")
|
||||
print("7. Developer contact: your-email@manoonoils.com")
|
||||
print("8. Click: Save and Continue (3 times)")
|
||||
print("9. Click: Back to Dashboard")
|
||||
print()
|
||||
print("10. Back on Credentials page:")
|
||||
print("11. Click: Create Credentials → OAuth client ID")
|
||||
print("12. Application type: Desktop app")
|
||||
print("13. Name: GSC Desktop Client")
|
||||
print("14. Click: Create")
|
||||
print("15. Click: DOWNLOAD JSON")
|
||||
print()
|
||||
|
||||
# Get the file path
|
||||
json_path = input("Enter the path to the downloaded JSON file: ").strip()
|
||||
|
||||
if not os.path.exists(json_path):
|
||||
print(f"❌ File not found: {json_path}")
|
||||
return
|
||||
|
||||
# Load credentials
|
||||
with open(json_path, "r") as f:
|
||||
client_config = json.load(f)
|
||||
|
||||
# Step 3: Install dependencies and run auth
|
||||
print()
|
||||
print("STEP 3: Install Dependencies")
|
||||
print("-" * 70)
|
||||
print("Run these commands:")
|
||||
print()
|
||||
print(
|
||||
" pip3 install google-auth google-auth-oauthlib google-auth-httplib2 google-api-python-client"
|
||||
)
|
||||
print()
|
||||
input("Press Enter after installing...")
|
||||
|
||||
# Step 4: Authorization
|
||||
print()
|
||||
print("STEP 4: Authorize Application")
|
||||
print("-" * 70)
|
||||
print("Running authorization...")
|
||||
|
||||
# Import here so we can check if installed
|
||||
try:
|
||||
from google_auth_oauthlib.flow import InstalledAppFlow
|
||||
from google.auth.transport.requests import Request
|
||||
import pickle
|
||||
except ImportError:
|
||||
print("❌ Please install the required packages first (Step 3)")
|
||||
return
|
||||
|
||||
SCOPES = ["https://www.googleapis.com/auth/webmasters.readonly"]
|
||||
|
||||
# Create flow
|
||||
flow = InstalledAppFlow.from_client_secrets_file(
|
||||
json_path,
|
||||
SCOPES,
|
||||
redirect_uri="urn:ietf:wg:oauth:2.0:oob", # For console-based auth
|
||||
)
|
||||
|
||||
# Get authorization URL
|
||||
auth_url, _ = flow.authorization_url(prompt="consent")
|
||||
|
||||
print()
|
||||
print("📱 Open this URL in your browser:")
|
||||
print(auth_url)
|
||||
print()
|
||||
|
||||
# Try to open browser automatically
|
||||
try:
|
||||
webbrowser.open(auth_url)
|
||||
print("(Browser should open automatically)")
|
||||
except:
|
||||
pass
|
||||
|
||||
# Get the code
|
||||
print()
|
||||
code = input("Enter the authorization code from the browser: ").strip()
|
||||
|
||||
# Exchange code for credentials
|
||||
flow.fetch_token(code=code)
|
||||
creds = flow.credentials
|
||||
|
||||
# Save credentials
|
||||
creds_info = {
|
||||
"token": creds.token,
|
||||
"refresh_token": creds.refresh_token,
|
||||
"token_uri": creds.token_uri,
|
||||
"client_id": creds.client_id,
|
||||
"client_secret": creds.client_secret,
|
||||
"scopes": creds.scopes,
|
||||
}
|
||||
|
||||
output_file = "gsc-oauth-credentials.json"
|
||||
with open(output_file, "w") as f:
|
||||
json.dump(creds_info, f, indent=2)
|
||||
|
||||
print()
|
||||
print("=" * 70)
|
||||
print("✅ SUCCESS! OAuth credentials saved to:", output_file)
|
||||
print("=" * 70)
|
||||
print()
|
||||
print("NEXT STEPS:")
|
||||
print("1. Copy this file to your server:")
|
||||
print(f" scp {output_file} doorwaysftw:/tmp/")
|
||||
print()
|
||||
print("2. Create Kubernetes secret:")
|
||||
print(" ssh doorwaysftw")
|
||||
print(" kubectl create secret generic gsc-oauth-credentials \\")
|
||||
print(" --namespace=manoonoils \\")
|
||||
print(" --from-file=oauth-credentials.json=/tmp/gsc-oauth-credentials.json")
|
||||
print()
|
||||
print("3. Deploy monitoring:")
|
||||
print(" kubectl apply -f scripts/gsc-monitoring/cronjob-oauth.yaml")
|
||||
print()
|
||||
print("Your refresh token is valid indefinitely (until revoked).")
|
||||
print("The monitoring will run automatically every day!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
setup_oauth()
|
||||
133
scripts/gsc-monitoring/setup-oauth.py
Normal file
133
scripts/gsc-monitoring/setup-oauth.py
Normal file
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Google Search Console OAuth Setup Script
|
||||
Generates OAuth credentials and stores refresh token
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def create_oauth_credentials():
|
||||
"""Guide user through OAuth setup"""
|
||||
|
||||
print("=" * 70)
|
||||
print("GOOGLE SEARCH CONSOLE - OAUTH SETUP (No Service Account Key Needed)")
|
||||
print("=" * 70)
|
||||
print()
|
||||
print("This method uses OAuth 2.0 instead of service account keys.")
|
||||
print("You'll authenticate once with your Google account.")
|
||||
print()
|
||||
|
||||
# Step 1: Create credentials
|
||||
print("STEP 1: Create OAuth Credentials")
|
||||
print("-" * 70)
|
||||
print("1. Go to: https://console.cloud.google.com")
|
||||
print("2. Select/create project: manoonoils-monitoring")
|
||||
print("3. Go to: APIs & Services → Credentials")
|
||||
print("4. Click: Create Credentials → OAuth client ID")
|
||||
print("5. Application type: Desktop app")
|
||||
print("6. Name: GSC Monitor")
|
||||
print("7. Click Create")
|
||||
print("8. Download the JSON file (client_secret_*.json)")
|
||||
print()
|
||||
input("Press Enter when you have downloaded the credentials file...")
|
||||
|
||||
# Step 2: Get credentials file path
|
||||
print()
|
||||
print("STEP 2: Upload Credentials")
|
||||
print("-" * 70)
|
||||
print("Copy the downloaded file to this server:")
|
||||
print()
|
||||
print(" scp /path/to/client_secret_*.json doorwaysftw:/tmp/gsc-credentials.json")
|
||||
print()
|
||||
input("Press Enter after uploading...")
|
||||
|
||||
# Step 3: Run authorization
|
||||
print()
|
||||
print("STEP 3: Authorize Application")
|
||||
print("-" * 70)
|
||||
print("Running authorization flow...")
|
||||
print()
|
||||
|
||||
# Create auth script
|
||||
auth_script = """#!/usr/bin/env python3
|
||||
import os
|
||||
import json
|
||||
import pickle
|
||||
from google_auth_oauthlib.flow import InstalledAppFlow
|
||||
from google.auth.transport.requests import Request
|
||||
|
||||
SCOPES = ['https://www.googleapis.com/auth/webmasters.readonly']
|
||||
CREDS_FILE = '/tmp/gsc-credentials.json'
|
||||
TOKEN_FILE = '/tmp/gsc-token.pickle'
|
||||
|
||||
def main():
|
||||
creds = None
|
||||
|
||||
if os.path.exists(TOKEN_FILE):
|
||||
with open(TOKEN_FILE, 'rb') as token:
|
||||
creds = pickle.load(token)
|
||||
|
||||
if not creds or not creds.valid:
|
||||
if creds and creds.expired and creds.refresh_token:
|
||||
creds.refresh(Request())
|
||||
else:
|
||||
flow = InstalledAppFlow.from_client_secrets_file(
|
||||
CREDS_FILE, SCOPES)
|
||||
creds = flow.run_local_server(port=0)
|
||||
|
||||
with open(TOKEN_FILE, 'wb') as token:
|
||||
pickle.dump(creds, token)
|
||||
|
||||
print("\\n✅ Authorization successful!")
|
||||
print(f"Token saved to: {TOKEN_FILE}")
|
||||
|
||||
# Save credentials info
|
||||
creds_info = {
|
||||
'token': creds.token,
|
||||
'refresh_token': creds.refresh_token,
|
||||
'token_uri': creds.token_uri,
|
||||
'client_id': creds.client_id,
|
||||
'client_secret': creds.client_secret,
|
||||
'scopes': creds.scopes
|
||||
}
|
||||
|
||||
with open('/tmp/gsc-token.json', 'w') as f:
|
||||
json.dump(creds_info, f, indent=2)
|
||||
|
||||
print(f"Credentials saved to: /tmp/gsc-token.json")
|
||||
print("\\nYou can now deploy the monitoring system!")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
"""
|
||||
|
||||
# Save and run auth script
|
||||
with open("/tmp/gsc-auth.py", "w") as f:
|
||||
f.write(auth_script)
|
||||
|
||||
print("Authorization script created at: /tmp/gsc-auth.py")
|
||||
print()
|
||||
print("Run this on the server to authorize:")
|
||||
print()
|
||||
print(" ssh doorwaysftw")
|
||||
print(" cd /tmp")
|
||||
print(" python3 gsc-auth.py")
|
||||
print()
|
||||
print("This will open a browser for you to authorize the app.")
|
||||
print("If running on a remote server without browser, use SSH tunnel:")
|
||||
print()
|
||||
print(" ssh -L 8080:localhost:8080 doorwaysftw")
|
||||
print(" Then run python3 gsc-auth.py")
|
||||
print()
|
||||
|
||||
|
||||
def main():
|
||||
create_oauth_credentials()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user