9.5 KiB
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
sendAsAPI (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
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
- Open https://console.cloud.google.com — sign in with your Google Workspace admin account
- Click the project dropdown (top nav bar) → New Project
- Name:
email-sig-manager→ Create - Select the new project from the dropdown to make it active
Step 2 — Enable Required APIs
Admin SDK API:
- Go to APIs & Services → Library
- Search
Admin SDK API→ click result → Enable
Gmail API:
- Go back to APIs & Services → Library
- Search
Gmail API→ click result → Enable
Step 3 — Create the Service Account
- Go to IAM & Admin → Service Accounts
- Click + Create Service Account
- Name:
email-sig-manager→ Create and Continue - Skip role assignment → Continue → skip user access → Done
Step 4 — Enable Domain-Wide Delegation
- Click the service account name to open it
- Click Edit (pencil icon)
- Expand Show advanced settings
- Check Enable Google Workspace Domain-wide Delegation
- Enter product name:
Email Signature Manager - Click Save
- Back on the service account detail page, note the Client ID (long number) — copy it
Step 5 — Download the JSON Key
- Click the Keys tab on the service account
- Add Key → Create New Key → JSON → Create
- Rename the downloaded file to
sa.json - Place it at
secrets/sa.jsonon your Unraid host — never commit to GitHub
Part 2 — Google Admin Console Authorization
Step 6 — Authorize Domain-Wide Delegation
- Go to https://admin.google.com — sign in as super admin
- Navigate to: Security → Access and data control → API controls
- Click Manage Domain Wide Delegation
- Click Add new
- 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
- Click Authorize
If you get "client ID not found" — wait 2–3 minutes after Step 4 and try again.
Step 7 — Verify
- Click View details next to your new entry
- Confirm both scopes are listed
- 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://github.com/YOURUSERNAME/email-sig-manager.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\
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:
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
- Go to Template Editor — verify the preview renders correctly
- Back on Dashboard → enter your own email → Push Single User
- Open Gmail → compose a new message → verify signature appears
- 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)
│ ├── 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.jsongrants impersonation rights to every user in your domain — treat it like a master key- Never commit
sa.jsonto GitHub — it is gitignored - Change
ADMIN_PASSWORDbefore first deployment - Consider HTTPS via a reverse proxy if the UI is accessible outside your LAN