# 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: ```bash 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 ``` 6. Click **Authorize** > If you receive an error saying "client ID not found", wait 2–3 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: ```bash 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: ```bash # 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\ ``` ### Step 10 — Add Your Company Logo 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 ```bash 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 ```bash 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 2–3 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