feat(integration): Google Search Console search terms#4117
Conversation
|
@boutterudy is attempting to deploy a commit to the Umami Software Team on Vercel. A member of the Team first needs to authorize it. |
Greptile SummaryThis PR adds a Google Search Console integration to Umami, allowing website owners to view GSC search term data (clicks, impressions, CTR, position) filtered by the active referrer/country context. It introduces a new Confidence Score: 4/5Most prior review concerns are resolved; one previously flagged data-integrity issue (missing FK constraint in the handwritten migration) remains open and should be confirmed before merging. Seven of the eight issues from the previous review round have been fixed. The two new findings in this pass are both P2. The score reflects the unresolved migration concern from the prior round rather than any new P0/P1 finding introduced here. prisma/migrations/20_add_google_auth/migration.sql — the handwritten SQL is still missing the foreign-key constraint that enforces onDelete: Cascade declared in schema.prisma. Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant UI as WebsiteGoogleSearchConsole
participant AuthRoute as /api/auth/google
participant Google as Google OAuth
participant Callback as /api/auth/google/callback
participant DB as website_google_auth
User->>UI: Click "Connect with Google"
UI->>AuthRoute: GET /api/auth/google?websiteId=...
AuthRoute->>AuthRoute: jwt.sign({websiteId}, APP_SECRET, 10m)
AuthRoute-->>UI: { url: google_auth_url }
UI->>Google: Redirect to OAuth consent
Google-->>Callback: GET /callback?code=...&state=jwt
Callback->>Callback: jwt.verify(state, APP_SECRET)
Callback->>Google: Exchange code for tokens
Google-->>Callback: access_token, refresh_token, expires_in
Callback->>Google: GET /userinfo (Bearer access_token)
Google-->>Callback: { email }
Callback->>DB: upsertWebsiteGoogleAuth (encrypted tokens)
Callback-->>User: Redirect to /websites/{id}/settings
User->>UI: Select GSC property
UI->>DB: PUT /google-auth/property { propertyUrl }
DB-->>UI: { ok: true }
User->>UI: View search terms panel
UI->>AuthRoute: GET /websites/{id}/google-auth
AuthRoute-->>UI: { connected, email, propertyUrl }
UI->>AuthRoute: GET /websites/{id}/search-terms?startAt&endAt&...
AuthRoute->>DB: getValidAccessToken (refresh if needed)
AuthRoute->>Google: POST searchAnalytics/query
Google-->>AuthRoute: { rows: [...] }
AuthRoute-->>UI: { rows, connected: true }
Reviews (20): Last reviewed commit: "fix: return accurate value of `connected..." | Re-trigger Greptile |
|
Amazing Work. Hope it will merged soon. |
Thanks @Yashh56 🙏 I hope too 😊 |
|
@greptileai I think the issue about a missing FK constraint in migration is a false positive.
|
|
You're absolutely right, and I apologize for the false positive. Looking at the schema at Your migration 20 is entirely consistent with the project's approach. Thanks for the correction! |
|
@greptileai did you find any issue? I don't see any in your comment, but still you mentioned "P2" issues 🤔 |
|
I did find two P2 issues, both now resolved:
Both were fixed in your 377a2ea commit. |
@greptileai so does that mean the issues are still in the code right now? Or not? |
|
@greptileai I think the issue about a missing FK constraint in migration is a false positive.
|
|
You're absolutely right. I can confirm:
This is indeed a false positive. The lack of FK constraints in migration 20 is intentional and matches the project's architecture. Thanks for catching this! |
|
@boutterudy This is awesome work. Do you have, by any chance, docker image built? So I run your version until umami merges this feature? |
@kolosek Thanks! 🙏 Unfortunately, no, I don’t have one, but I reckon you could generate one by switching to the branch of this PR and then running the build 🤗 If you need info on how to fetch branches from other forks, this thread should help ✨ |
|
@boutterudy Ok, I did just that. Just so you know, in order for this to work on production I needed to make few changes kolosek@430645a (most important is the one that url is not 0.0.0.0:3000 but the url from domain that is used) |
@kolosek, thank you very much for your feedback! 🙏 |
|
@boutterudy something that AI proposed as it wasn't able to build docker image. As if it needed ENV variables in order to build the images, not exactly sure. |
@kolosek That’s right, in fact, this issue about It was because the pattern was updated to match Prisma v7 pattern 💯 With that in mind, I’ve cherry-picked your commit to include it in the PR, so thank you very much for your help! 🙏 |
👾 Feature
This PR includes one of the features most requested by users (see this discussion): integration of Google Search Console (GSC) data in Umami ✨
The GSC data is filtered according to the filters applied on the page and is based on:
referrer(when the domain is a Google domain)country(when the Google domain is not already region-specific)Displayed data:
Visitors(the number of clicks from Google Search results)ImpressionsCTRPosition🔀 Changes
.envGOOGLE_CLIENT_ID→ Google API client IDGOOGLE_CLIENT_SECRET→ Google API client secretwebsite_google_authtablesrc/lib/google.ts→ OAuth flow, token refresh, GSC API client/api/auth/googleand/api/websites/{id}/google-auth+/search-termsuseWebsiteGoogleAuthQueryuseWebsiteSearchTermsQueryuseWebsiteGscPropertiesQueryuseWebsiteGscPropertyMutationWebsiteGoogleSearchConsole.tsx(settings)WebsiteSearchTerms.tsx(panel)WebsiteSearchTermsExpandedView.tsx(modal)GOOGLE_DOMAINSGOOGLE_DOMAIN_TO_COUNTRYCOUNTRY_ALPHA2_TO_ALPHA3✨ Wonderful Results
Demo.-.Search.terms.global.mp4
Demo.-.Search.terms.filtered.by.country.mp4
🔧 Configuration
Setting up Google API for Umami
To configure the Google API and enable Umami to retrieve data from Google Search Console, you need to:
http://localhost:3000) → Add authorized redirect URIs (for dev:http://localhost:3000/api/auth/google/callback) → Create → Download JSON (contains yourGOOGLE_CLIENT_IDandGOOGLE_CLIENT_SECRET))Google Search Console API(or click here) → Enable)/auth/webmasters.readonly(View Search Console data for your verified sites) → Update → Save)GOOGLE_CLIENT_IDandGOOGLE_CLIENT_SECRETin.env(available in the previously downloaded JSON)Normally, after all that, logging in via Google should work 💯
Note: For the application in a production environment, I assume you’ll need to go through Google’s application validation process.
Closes #645