Files
email/README.md
2026-03-03 16:23:30 -06:00

302 lines
9.5 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
---
## Logo & Image Hosting
All images referenced in the signature template must be publicly accessible via HTTPS.
The default logo URL is:
```
https://alwisp.com/uploads/logo.png
```
Upload your logo and any other signature images to `https://alwisp.com/uploads/` and
reference them by full URL in the template. The logo URL can also be changed live
in the Template Editor UI without rebuilding the container.
---
## 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.
| Variable | Description | Default |
|---|---|---|
| `GOOGLE_ADMIN_EMAIL` | Workspace admin email (e.g. jason@messagepoint.tv) | *(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 Variables.
### For Local Development
```bash
cp .env.example .env
# fill in values
docker-compose --env-file .env up -d --build
```
---
## Part 1 — Google Cloud Console Setup
### Step 1 — Create a Google Cloud Project
1. Open https://console.cloud.google.com — sign in with your Google Workspace admin account
2. Click the **project dropdown** (top nav bar) → **New Project**
3. Name: `email-sig-manager`**Create**
4. Select the new project from the dropdown to make it active
### Step 2 — Enable Required APIs
**Admin SDK API:**
1. Go to **APIs & Services → Library**
2. Search `Admin SDK API` → click result → **Enable**
**Gmail API:**
1. Go back to **APIs & Services → Library**
2. Search `Gmail API` → click result → **Enable**
### Step 3 — Create the Service Account
1. Go to **IAM & Admin → Service Accounts**
2. Click **+ Create Service Account**
3. Name: `email-sig-manager`**Create and Continue**
4. Skip role assignment → **Continue** → skip user access → **Done**
### Step 4 — Enable Domain-Wide Delegation
1. Click the service account name to open it
2. Click **Edit** (pencil icon)
3. Expand **Show advanced settings**
4. Check **Enable Google Workspace Domain-wide Delegation**
5. Enter product name: `Email Signature Manager`
6. Click **Save**
7. Back on the service account detail page, note the **Client ID** (long number) — copy it
### Step 5 — Download the JSON Key
1. Click the **Keys** tab on the service account
2. **Add Key → Create New Key → JSON → Create**
3. Rename the downloaded file to `sa.json`
4. Place it at `secrets/sa.json` on your Unraid host — never commit to GitHub
---
## Part 2 — Google Admin Console Authorization
### Step 6 — Authorize Domain-Wide Delegation
1. Go to https://admin.google.com — sign in as super admin
2. Navigate to: **Security → Access and data control → API controls**
3. Click **Manage Domain Wide Delegation**
4. Click **Add new**
5. Fill in:
- **Client ID:** *(paste the number from Step 4)*
- **OAuth Scopes:**
```
https://www.googleapis.com/auth/admin.directory.user.readonly,https://www.googleapis.com/auth/gmail.settings.basic
```
6. Click **Authorize**
> If you get "client ID not found" — wait 23 minutes after Step 4 and try again.
### Step 7 — Verify
1. Click **View details** next to your new entry
2. Confirm both scopes are listed
3. If a scope is missing, click Edit, re-enter the full scope string, and re-authorize
---
## Part 3 — Unraid Deployment
### Step 8 — Clone the Repo
```bash
cd /mnt/user/appdata
git clone https://github.com/YOURUSERNAME/email-sig-manager.git email-sigs
cd email-sigs
```
### Step 9 — Place the Service Account Key
```bash
# From your PC (run on your PC):
scp sa.json root@UNRAID-IP:/mnt/user/appdata/email-sigs/secrets/sa.json
```
Or copy via SMB: `\\UNRAID-IP\appdata\email-sigs\secrets\`
### Step 10 — Upload Logo
Upload your logo to `https://alwisp.com/uploads/logo.png` via your web server.
The template references this URL directly — no local file needed.
### Step 11 — Build the Docker Image
SSH into Unraid and run:
```bash
cd /mnt/user/appdata/email-sigs
docker build -t email-sigs:latest .
```
### Step 12 — Configure Container in Unraid Docker UI
- **Name:** `email-sigs`
- **Repository:** `email-sigs` *(local image built in Step 11)*
**Volume mappings:**
| Host Path | Container Path |
|---|---|
| `/mnt/user/appdata/email-sigs/secrets` | `/app/secrets` |
| `/mnt/user/appdata/email-sigs/data` | `/app/data` |
| `/mnt/user/appdata/email-sigs/public/assets` | `/app/public/assets` |
**Port mapping:** Host `3000` → Container `3000`
**Variables:**
| Name | Value |
|---|---|
| `GOOGLE_ADMIN_EMAIL` | `jason@messagepoint.tv` |
| `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` |
---
## Part 4 — First Run & Verification
### Step 13 — Access the UI
```
http://UNRAID-IP:3000
```
### Step 14 — Preview and Test
1. Go to **Template Editor** — verify the preview renders correctly
2. Back on **Dashboard** → enter your own email → **Push Single User**
3. Open Gmail → compose a new message → verify signature appears
4. Check Gmail mobile app (force-close and reopen if needed)
### Step 15 — Push to All Users
Once verified, click **Push to All Users** on the Dashboard.
The nightly cron will keep all signatures in sync automatically.
---
## Updating
```bash
cd /mnt/user/appdata/email-sigs
git pull
docker build -t email-sigs:latest .
docker restart email-sigs
```
---
## Project Structure
```
email-sig-manager/
├── src/
│ ├── index.js # Express app (no dotenv — vars injected by Docker)
│ ├── 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/ # Local asset dir (optional, logo served remotely)
├── secrets/ # Gitignored — sa.json goes here
├── data/ # Gitignored — SQLite DB lives here
├── Dockerfile # Uses npm install (not npm ci)
├── 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
**"Cannot find module" errors on startup**
Rebuild the image: `docker build -t email-sigs:latest .` then restart.
**Container won't start**
Run `docker logs email-sigs` — 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.
**"Client ID not found" in Google Admin**
Wait 23 minutes after enabling domain-wide delegation in GCP, then try again.
**Signatures not showing on mobile**
Gmail iOS/Android uses the web signature automatically. Force-close and reopen the app.
**Template changes not saving**
Verify the container has write access to `templates/`. A `.bak` file is created on every save.
**401 / permission denied errors**
Go to Google Admin → Security → API Controls → Domain Wide Delegation → View Details
and verify both OAuth scopes are listed correctly.
---
## Security Notes
- `sa.json` grants impersonation rights to every user in your domain — treat it like a master key
- Never commit `sa.json` to GitHub — it is gitignored
- Change `ADMIN_PASSWORD` before first deployment
- Consider HTTPS via a reverse proxy if the UI is accessible outside your LAN