# 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. See [INSTALL.md](INSTALL.md) for Unraid-specific instructions. ## 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 2–3 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 2–3 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