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

13 KiB
Raw Blame History

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:

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
    
  1. 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:

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:

# 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\

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

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

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