2026-03-13 14:03:18 -05:00
2026-03-13 13:43:13 -05:00
2026-03-13 13:43:13 -05:00
2026-03-03 16:23:30 -06:00
2026-03-03 16:23:30 -06:00
2026-03-03 16:23:30 -06:00
2026-03-13 14:03:18 -05:00
2026-03-13 14:03:18 -05:00
2026-03-03 10:20:07 -06:00
2026-03-13 14:03:18 -05:00

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 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
  • Template Versioning — save multiple versions and recall them later
  • 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

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-managerCreate
  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-managerCreate 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

cd /mnt/user/appdata
git clone https://git.alwisp.com/jason/email.git email-sigs
cd email-sigs

Step 9 — Place the Service Account Key

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

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:

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

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)
│   │   ├── 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
Description
No description provided
Readme 104 KiB
Languages
HTML 54.1%
JavaScript 41.9%
Handlebars 3.5%
Dockerfile 0.5%