Files
email/README.md
2026-03-03 10:22:00 -06:00

370 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Email Signature Manager
A self-hosted, Dockerized Google Workspace email signature manager.
Runs as a single container — designed for Unraid but works on any Docker host.
## Features
- Pulls user data automatically from Google Workspace Directory API
- Renders per-user HTML signatures via Handlebars templates
- Pushes signatures directly to Gmail via `sendAs` API (web + mobile)
- Nightly batch push via configurable cron schedule
- Web admin UI — live template editor with real-time preview
- Single-user push for testing and onboarding
- SQLite audit log of every push event
- Basic auth protected UI
- Zero external services — single container, no database server
---
## Environment Variables
All secrets and configuration are passed as **environment variables at runtime**.
No `.env` file is committed to this repo — see `.env.example` for all variable names and descriptions.
| Variable | Description | Default |
|---|---|---|
| `GOOGLE_ADMIN_EMAIL` | Workspace admin email for Directory API | *(required)* |
| `GOOGLE_CUSTOMER_ID` | Use `my_customer` for primary domain | `my_customer` |
| `SERVICE_ACCOUNT_KEY_PATH` | Path to sa.json inside container | `/app/secrets/sa.json` |
| `ADMIN_USERNAME` | Web UI login username | *(required)* |
| `ADMIN_PASSWORD` | Web UI login password | *(required)* |
| `PORT` | Internal app port | `3000` |
| `CRON_SCHEDULE` | Cron expression for nightly push | `0 2 * * *` |
| `NODE_ENV` | Node environment | `production` |
### On Unraid
Set each variable directly in the **Docker container template UI** under the
"Variables" section. No file needed on the host.
### For Local Development
Copy `.env.example` to `.env`, fill in your values, then run:
```bash
docker-compose --env-file .env up -d --build
```
---
## Part 1 — Google Cloud Console Setup
This section walks through creating the service account and downloading the key
that allows the app to impersonate users in your Google Workspace domain.
### Step 1 — Create a Google Cloud Project
1. Open https://console.cloud.google.com and sign in with your Google Workspace admin account
2. Click the **project dropdown** in the top navigation bar (next to the Google Cloud logo)
3. Click **New Project** in the top right of the dialog
4. Enter a project name: `email-sig-manager`
5. Leave the organization as your Workspace domain → click **Create**
6. Wait a few seconds, then select the new project from the dropdown to make it active
> **Note:** If your org already has a GCP project you prefer to use, you can skip
> project creation and just enable the APIs inside the existing project.
---
### Step 2 — Enable Required APIs
The app needs two Google APIs enabled in your project.
**Enable Admin SDK API:**
1. In the GCP Console, go to **APIs & Services → Library** (left sidebar)
2. Search for `Admin SDK API`
3. Click the result → click **Enable**
**Enable Gmail API:**
1. Go back to **APIs & Services → Library**
2. Search for `Gmail API`
3. Click the result → click **Enable**
Both should now show as **Enabled** under **APIs & Services → Enabled APIs & Services**.
---
### Step 3 — Create the Service Account
1. In the left sidebar, go to **IAM & Admin → Service Accounts**
2. Click **+ Create Service Account** at the top
3. Fill in the details:
- **Service account name:** `email-sig-manager`
- **Service account ID:** will auto-fill as `email-sig-manager`
- **Description:** `Email signature push service for Google Workspace`
4. Click **Create and Continue**
5. On the "Grant this service account access to project" step — **skip this, click Continue**
6. On the "Grant users access" step — **skip this too, click Done**
You will land back on the service accounts list and see your new account listed.
---
### Step 4 — Enable Domain-Wide Delegation on the Service Account
1. Click the service account name (`email-sig-manager`) in the list to open it
2. Click the **Edit** button (pencil icon) at the top
3. Scroll down to find **"Show advanced settings"** — click it to expand
4. Check the box for **"Enable Google Workspace Domain-wide Delegation"**
5. Enter a product name for the consent screen if prompted (e.g., `Email Signature Manager`)
6. Click **Save**
After saving, return to the service account detail page. You should now see a
**"Domain-wide delegation"** line showing a **Client ID** (a long numeric string,
e.g., `112233445566778899`). **Copy this Client ID** — you will need it in Part 2.
---
### Step 5 — Download the Service Account JSON Key
1. On the service account detail page, click the **Keys** tab
2. Click **Add Key → Create New Key**
3. Select **JSON** as the key type → click **Create**
4. The key file downloads automatically to your computer
5. **Rename the file to `sa.json`**
6. Store it securely — this file grants impersonation access to all users in your domain
> This file goes in the `secrets/` folder on your Unraid host. It is gitignored
> and must never be committed to GitHub.
---
## Part 2 — Google Admin Console Authorization
This section delegates authority to the service account inside Google Admin,
allowing it to impersonate users and update their Gmail signatures.
### Step 6 — Authorize the Service Account in Google Admin
1. Open a new tab and go to https://admin.google.com
2. Sign in with your **Google Workspace super admin** account
3. In the left sidebar, navigate to:
**Security → Access and data control → API controls**
4. On the API controls page, click **Manage Domain Wide Delegation**
5. Click **Add new** to add a new authorized client
Fill in the form:
- **Client ID:** Paste the numeric Client ID you copied in Step 4
(find it again at GCP Console → IAM & Admin → Service Accounts → your account → Details)
- **OAuth Scopes:** Paste the following exactly, as one comma-separated line:
```
https://www.googleapis.com/auth/admin.directory.user.readonly,https://www.googleapis.com/auth/gmail.settings.basic
```
6. Click **Authorize**
> If you receive an error saying "client ID not found", wait 23 minutes after
> enabling domain-wide delegation in Step 4 and try again — GCP propagation
> can take a moment.
---
### Step 7 — Verify the Authorization
1. After clicking Authorize you should see the new entry in the list
2. Click **View details** next to your entry
3. Confirm both scopes are listed:
- `https://www.googleapis.com/auth/admin.directory.user.readonly`
- `https://www.googleapis.com/auth/gmail.settings.basic`
4. If a scope is missing, click **Edit**, re-enter the full comma-separated scope string, and click **Authorize** again
> **Multi-party approval note:** If your Google Workspace org has multi-party
> approval enabled for admin actions, another super admin will need to approve
> this delegation before it takes effect.
---
## Part 3 — Unraid Deployment
### Step 8 — Clone the Repository
SSH into your Unraid server or use the Unraid terminal:
```bash
cd /mnt/user/appdata
git clone https://github.com/YOURUSERNAME/email-sig-manager.git
cd email-sig-manager
```
### Step 9 — Place the Service Account Key
Copy `sa.json` (downloaded in Step 5) into the `secrets/` folder:
```bash
# From your PC to Unraid (run this on your PC):
scp sa.json root@UNRAID-IP:/mnt/user/appdata/email-sig-manager/secrets/sa.json
```
Or copy it via your Unraid SMB share using Windows Explorer:
```
\\UNRAID-IP\appdata\email-sig-manager\secrets\
```
### Step 10 — Add Your Company Logo
Place your logo file at:
```
/mnt/user/appdata/email-sig-manager/public/assets/logo.png
```
Recommended: PNG, transparent background, 160×160px or smaller.
### Step 11 — Configure Container in Unraid Docker UI
1. In the Unraid web UI, go to **Docker → Add Container**
2. Set the following:
- **Name:** `email-sig-manager`
- **Repository:** *(leave blank if building locally — use the path method below)*
- **Network Type:** Bridge
**Add these Volume mappings:**
| Container Path | Host Path |
|---|---|
| `/app/secrets` | `/mnt/user/appdata/email-sig-manager/secrets` |
| `/app/data` | `/mnt/user/appdata/email-sig-manager/data` |
| `/app/public/assets` | `/mnt/user/appdata/email-sig-manager/public/assets` |
**Add these Port mapping:**
| Container Port | Host Port |
|---|---|
| `3000` | `3000` *(or any open port)* |
**Add these Variables** (click Add Variable for each):
| Name | Value |
|---|---|
| `GOOGLE_ADMIN_EMAIL` | `admin@messagepointmedia.com` |
| `GOOGLE_CUSTOMER_ID` | `my_customer` |
| `SERVICE_ACCOUNT_KEY_PATH` | `/app/secrets/sa.json` |
| `ADMIN_USERNAME` | `admin` |
| `ADMIN_PASSWORD` | *(your chosen password)* |
| `PORT` | `3000` |
| `CRON_SCHEDULE` | `0 2 * * *` |
| `NODE_ENV` | `production` |
### Step 12 — Build and Start
```bash
cd /mnt/user/appdata/email-sig-manager
docker-compose up -d --build
```
---
## Part 4 — First Run & Verification
### Step 13 — Access the Admin UI
Open a browser and navigate to:
```
http://UNRAID-IP:3000
```
Log in with your `ADMIN_USERNAME` and `ADMIN_PASSWORD`.
### Step 14 — Test with a Single User First
1. Go to the **Template Editor** page
2. Verify the live preview renders correctly with your info
3. Make any template adjustments needed
4. Return to the **Dashboard**
5. In the **Push Single User** field, enter your own email address
6. Click **Push Single User**
7. Open Gmail in a new tab → compose a new email → verify the signature appears correctly
8. Check the Gmail mobile app as well (force-close and reopen if needed)
### Step 15 — Push to All Users
Once you've verified your own signature looks correct:
1. Click **Push to All Users** on the Dashboard
2. Confirm the dialog
3. Watch the audit log populate with success/error statuses
4. The nightly cron will take over automatically from here
---
## Updating the Project
```bash
cd /mnt/user/appdata/email-sig-manager
git pull
docker-compose up -d --build
```
`secrets/`, `data/`, and host-managed files are gitignored and unaffected by pulls.
---
## Project Structure
```
email-sig-manager/
├── src/
│ ├── index.js # Express app entry
│ ├── routes/
│ │ ├── admin.js # Template, logs, users API
│ │ └── push.js # Signature push logic + batch runner
│ ├── services/
│ │ ├── googleAdmin.js # Directory API — fetch all users
│ │ ├── gmailApi.js # Gmail sendAs patch
│ │ ├── renderer.js # Handlebars template renderer
│ │ └── scheduler.js # node-cron nightly job
│ └── db/
│ ├── sqlite.js # DB init and connection
│ └── audit.js # Audit log read/write
├── templates/
│ └── default.hbs # 2-column HTML signature template
├── public/
│ ├── dashboard.html # Admin dashboard UI
│ ├── editor.html # Template editor with live preview
│ └── assets/
│ └── logo.png # Company logo (add your own)
├── secrets/ # Gitignored — sa.json goes here
├── data/ # Gitignored — SQLite DB lives here
├── Dockerfile
├── docker-compose.yml
├── package.json
└── .env.example # Variable reference — safe to commit
```
---
## Cron Schedule Reference
| Expression | Meaning |
|---|---|
| `0 2 * * *` | 2:00 AM every day *(default)* |
| `0 6 * * *` | 6:00 AM every day |
| `0 2 * * 1` | 2:00 AM every Monday |
| `0 2 1 * *` | 2:00 AM on the 1st of each month |
---
## Troubleshooting
**Container won't start**
Run `docker logs email-sig-manager`. Most likely a missing env variable or malformed `sa.json`.
**"No primary sendAs" error**
The user may not have an active Gmail account, or domain-wide delegation scopes were not saved correctly in Google Admin.
**"Client ID not found" when authorizing in Google Admin**
Wait 23 minutes after enabling domain-wide delegation in GCP and try again.
**Signatures not showing on mobile**
Gmail iOS/Android automatically uses the web signature. Have the user force-close and reopen the Gmail app after the push.
**Template changes not saving**
Verify the container has write access to the `templates/` directory. A `.bak` file is created on every save.
**401 / permission denied errors in logs**
The OAuth scopes in Google Admin may not have saved correctly. Go back to Admin Console → Security → API Controls → Domain Wide Delegation → View Details and verify both scopes are listed.
---
## Security Notes
- `sa.json` grants impersonation rights to every user in your domain — treat it like a master key
- Never commit `sa.json` or a populated `.env` to GitHub
- Change `ADMIN_PASSWORD` before first deployment
- Consider placing the UI behind HTTPS if accessible outside your LAN