Roundcube as its own image built by the workflow (no config file or newer Compose needed)
build-container / build (push) Successful in 6m6s

- roundcube/: official Roundcube plus hub.php settings
- workflow builds and pushes git.necloud.us/nate/hub-roundcube, and runs the player and webmail tests
- compose uses the image instead of an inline config

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-21 08:59:50 -04:00
1 parent cdf862427e
commit a7a5b155e3
5 files changed
+67 -49

No files matched your search

+3 -47
View File
@@ -33,64 +33,20 @@ services:
- hub-data:/data # passkeys, calendar links and the session secret live here
# Webmail shown inside the Mail tab. Only the hub can reach it (no published port), so it sits behind the passkey sign-in.
# Sign in to it with your mail address and an app password (Gmail, iCloud and Yahoo are preset in the config below).
# Sign in to it with your mail address and an app password (Gmail, iCloud and Yahoo are preset in roundcube/hub.php).
roundcube:
image: roundcube/roundcubemail:latest-apache
image: git.necloud.us/nate/hub-roundcube:latest # built by the Gitea workflow from ./roundcube; `docker compose build` builds locally instead
build: ./roundcube
restart: unless-stopped
environment:
ROUNDCUBEMAIL_DB_TYPE: sqlite
ROUNDCUBEMAIL_REQUEST_PATH: /webmail/
ROUNDCUBEMAIL_PLUGINS: archive,zipdownload
ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE: 30M
# Microsoft 365 sign-in (optional, see README -> Mail). Leave empty to skip.
M365_CLIENT_ID: ${M365_CLIENT_ID:-}
M365_CLIENT_SECRET: ${M365_CLIENT_SECRET:-}
M365_TENANT: ${M365_TENANT:-}
PUBLIC_URL: ${PUBLIC_URL:-}
configs:
- source: roundcube_hub
target: /var/roundcube/config/hub.php
volumes:
- roundcube-data:/var/roundcube/db # address book, settings and the encryption key
configs:
roundcube_hub:
content: |
<?php
// Settings for the Roundcube that comes with NE Hub.
$$config['product_name'] = 'Mail';
$$config['x_frame_options'] = false; // the hub shows this page inside its Mail tab (same address, so only the hub can frame it)
$$hosts = [
'ssl://imap.gmail.com:993' => 'Gmail',
'ssl://imap.mail.me.com:993' => 'iCloud Mail',
'ssl://imap.mail.yahoo.com:993' => 'Yahoo Mail',
];
// Microsoft 365 through OAuth 2.0 (needs an app registration, see README). It goes first because that is where OAuth sign-ins are sent.
if (getenv('M365_CLIENT_ID')) {
$$hosts = ['ssl://outlook.office365.com:993' => 'Microsoft 365 (use the Microsoft button)'] + $$hosts;
$$tenant = getenv('M365_TENANT') ?: 'organizations';
$$config['oauth_provider'] = 'generic';
$$config['oauth_provider_name'] = 'Microsoft 365';
$$config['oauth_client_id'] = getenv('M365_CLIENT_ID');
$$config['oauth_client_secret'] = getenv('M365_CLIENT_SECRET');
$$config['oauth_auth_uri'] = "https://login.microsoftonline.com/$${tenant}/oauth2/v2.0/authorize";
$$config['oauth_token_uri'] = "https://login.microsoftonline.com/$${tenant}/oauth2/v2.0/token";
$$config['oauth_identity_uri'] = 'https://graph.microsoft.com/oidc/userinfo';
$$config['oauth_identity_fields'] = ['email', 'preferred_username'];
$$config['oauth_scope'] = 'email openid profile offline_access https://outlook.office365.com/IMAP.AccessAsUser.All https://outlook.office365.com/SMTP.Send';
$$config['oauth_login_redirect'] = false;
if (getenv('PUBLIC_URL')) $$config['oauth_redirect_uri'] = rtrim(getenv('PUBLIC_URL'), '/') . '/webmail/index.php/login/oauth';
}
$$config['imap_host'] = $$hosts;
$$config['smtp_host'] = 'tls://smtp.%z:587'; // smtp.gmail.com, smtp.mail.me.com, smtp.mail.yahoo.com
$$config['session_lifetime'] = 43200; // minutes (30 days); the hub's own sign-in is what protects access
// Behind the hub: believe its forwarded address and https.
if (($$_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https') $$_SERVER['HTTPS'] = 'on';
if (!empty($$_SERVER['HTTP_X_FORWARDED_HOST'])) $$_SERVER['HTTP_HOST'] = $$_SERVER['HTTP_X_FORWARDED_HOST'];
// A fixed encryption key kept with the data, so saved settings and sessions survive restarts and updates.
$$kf = '/var/roundcube/db/des_key';
if (!is_file($$kf)) @file_put_contents($$kf, bin2hex(random_bytes(12)));
if (is_file($$kf)) $$config['des_key'] = trim(file_get_contents($$kf));
volumes:
hub-data:
name: nehub-data # fixed name so the data survives redeploys and renamed stacks