--- title: "Editor Search Emails" author: - "Mark Padgham" date: "`r Sys.Date()`" vignette: > %\VignetteIndexEntry{Editor search emails} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} knitr::opts_chunk$set ( collapse = TRUE, warning = TRUE, message = TRUE, width = 120, comment = "#>", fig.retina = 2, fig.path = "README-" ) ``` This vignette describes how `roreviewapi` sends editor-search emails through the Gmail API, sending "from" the `reviewbot@ropensci.org` alias but authorized under a single Google account. Set that account once as an environment variable, and every later step refers back to it: ```bash export USER_EMAIL=mark@ropensci.org ``` This vignette complements the [admin setup vignette](https://docs.ropensci.org/roreviewapi/articles/admin-setup.html), covering only the Gmail-specific pieces of the editor volunteer search workflow. ## Prerequisites - A Google Cloud Console project owned by the rOpenSci Workspace's Cloud Identity org - A local machine with a browser, for the one-time OAuth authorization - SSH access to the Digital Ocean droplet ## 1. Google Cloud Console In the `ro-auto-email` project: 1. Enable the **Gmail API**. 2. Configure the **OAuth consent screen** with **Audience: Internal** (requires the project to be owned by the Workspace's Cloud Identity org). Internal governs who can *authorize* the app, not who receives mail — it skips Google verification and the 7-day refresh-token expiry that applies to unverified External apps. Request scope `https://www.googleapis.com/auth/gmail.send`. 3. Create an **OAuth 2.0 Client ID**, type **Desktop app**. Download `client_secret_.apps.googleusercontent.com.json`. ## 2. Authorize once, locally Run this on a local machine, not the droplet (it needs a browser and a `localhost` redirect): ```{r auth, eval = FALSE} library (gmailr) gm_auth_configure (path = "client_secret_.apps.googleusercontent.com.json") gm_auth ( email = Sys.getenv ("USER_EMAIL"), scopes = c ("gmail.modify", "gmail.settings_basic"), cache = "gmailr_token" ) ``` Log in as `$USER_EMAIL` and grant consent. This caches a refresh token to the `gmailr_token/` directory, which is portable to the droplet afterward since it is tied to the Google account, not the machine. Always pass the same `scopes` used here in any later `gm_auth()` call — a mismatch forces a fresh interactive re-authorization, which does not work headless on the droplet. ## 3. Send-as alias Add `reviewbot@ropensci.org` as a "Send mail as" address under `$USER_EMAIL`'s Gmail settings (Settings → Accounts → Send mail as). No separate authorization is needed, as the `gmail.send` scope already covers sending as any verified alias of that account. This is set via `gm_from()` on each outgoing message. ## 4. Code [`R/editor-search.R`](https://github.com/ropensci-review-tools/roreviewapi/blob/main/R/editor-search.R): - `gmail_auth()` configures and authorizes `gmailr` from the cached credentials. - `gmail_send(to, subject, html_body)` builds and sends one message via `gm_mime()`/`gm_from()`/`gm_to()`/`gm_subject()`/`gm_html_body()`/ `gm_send_message()`. - `gmail_send_batch(emails, links, subject, repo, issue_id)` loops `gmail_send()` once per recipient with `Sys.sleep(0.1)` between sends, since there is no Gmail equivalent of a batch-send endpoint for personalized messages. The `send_search` and `handle_click` endpoints each take an injectable `sender` parameter (default `gmail_send_batch` / `gmail_send`) for testing. ## 5. Environment variables | Variable | Value | |---|---| | `GMAIL_SENDER` | `reviewbot@ropensci.org` | | `GMAIL_OAUTH_CLIENT_SECRET` | `/data/email/client_secret_.apps.googleusercontent.com.json` | | `GMAIL_TOKEN_CACHE` | `/data/email/gmailr_token` | | `GMAIL_AUTH_EMAIL` | `$USER_EMAIL` | Set these in a local `.env` file for local runs. In production they are baked into the image via `Dockerfile` `ENV` placeholders plus a `RUN echo ... >> ~/.Renviron` line — fill in the real values directly in the `Dockerfile` on the droplet before building. Do **not** add them to `docker-compose.yml`'s `environment:` block: the droplet has no `.env`, so `${VAR}` there resolves to an empty string and overrides the Dockerfile's baked-in value. ## 6. Deploy credentials to the droplet Copy `client_secret_.apps.googleusercontent.com.json` and the `gmailr_token/` directory to `/srv/roreviewapi/data/` (bind-mounted into the container at `/data/email`). That directory is Docker/root-owned, so `scp` to your home directory first, then move into place: ```bash scp client_secret_.apps.googleusercontent.com.json youruser@droplet:~/ scp -r gmailr_token youruser@droplet:~/ # on the droplet: sudo mv ~/client_secret_.apps.googleusercontent.com.json /srv/roreviewapi/data/ sudo mv ~/gmailr_token /srv/roreviewapi/data/ sudo chmod 600 /srv/roreviewapi/data/client_secret_.apps.googleusercontent.com.json sudo chmod 700 /srv/roreviewapi/data/gmailr_token sudo chmod 600 /srv/roreviewapi/data/gmailr_token/* ``` ## 7. Verify - Call `send_search()` against a real/test issue; confirm delivery from `reviewbot@ropensci.org`. - Click a tracking link; confirm the notify-email arrives via `gmail_send()`. - Run the test suite. ## 8. Transfer maintenance **Operations only** (a new person deploys/debugs, auth stays as `$USER_EMAIL`) — nothing about the token changes: 1. Add them to the `ro-auto-email` Google Cloud project's IAM. 2. Add their SSH key / sudo access on the droplet. 3. Add them to the `roreviewapi` GitHub repo. 4. Hand over this document. **Authorizing identity itself** (needed if `mark@ropensci.org` becomes unusable, e.g. leaving the org): 1. Add `reviewbot@ropensci.org` as a "Send mail as" alias under the new maintainer's Gmail account. 2. They run the authorization in Section 2 as themselves, with the same `client_secret_....json`, producing their own `gmailr_token/`. 3. Update `GMAIL_AUTH_EMAIL` (local `.env` and `Dockerfile` placeholder) to their address; `GMAIL_SENDER` stays `reviewbot@ropensci.org`. 4. Deploy their `client_secret_....json` and `gmailr_token/` to `/srv/roreviewapi/data/`, replacing the old ones (Section 6). 5. Rebuild and redeploy (`restart.sh`). 6. Revoke the old grant from the departing account's Google Account → Security → "Third-party apps with account access".