Compare commits

..

13 Commits

Author SHA1 Message Date
hsiegeln
0125694572 chore: ignore local install dir created by installers
The install scripts default to ./cameleer/ for local install state.
Ignoring it prevents accidental commits of generated .env files,
TLS certs, and bootstrap data.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 09:43:26 +02:00
hsiegeln
f7a57edacc docs: update default registry to registry.cameleer.io and drop JWT row
Updates the documented default registry from gitea.siegeln.net/cameleer
to registry.cameleer.io/cameleer in both CLAUDE.md and README.md, and
removes the now-obsolete CAMELEER_SERVER_SECURITY_JWTSECRET row from
the auto-generated secrets table to match the prior installer change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 09:43:26 +02:00
hsiegeln
7bb9ef3283 refactor: drop JWT secret env vars from installers and compose
Removes CAMELEER_SERVER_SECURITY_JWTSECRET and
CAMELEER_SAAS_PROVISIONING_JWTSECRET — a better solution has been
adopted upstream, so the installers no longer need to generate or
forward a shared JWT secret.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 09:40:35 +02:00
hsiegeln
531a17397b fix: validate admin email format in SaaS mode
Require user@domain.tld format (must contain @ and dot in domain).
Interactive mode loops until valid; silent mode exits with error.
Default changed from 'admin' to 'admin@<PUBLIC_HOST>' in SaaS mode.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 21:03:45 +02:00
hsiegeln
21ea9515a2 feat: unify admin identity — SAAS_ADMIN_USER is the email in SaaS mode
Move deployment mode question before admin credentials so the installer
can validate email format in SaaS mode. Remove separate SAAS_ADMIN_EMAIL
— the admin user value IS the email address. In standalone mode, any
username is still accepted.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 20:45:25 +02:00
hsiegeln
0da26160c6 feat: add SAAS_ADMIN_EMAIL to both installers
Derive admin email from <ADMIN_USER>@<PUBLIC_HOST> by default.
Supports override via --admin-email CLI flag, SAAS_ADMIN_EMAIL env var,
or admin_email in cameleer.conf. Written to .env for bootstrap.
2026-04-25 20:26:38 +02:00
hsiegeln
b2259328d3 feat: enforce email as primary user identity in SaaS mode
Add SAAS_ADMIN_EMAIL env var (defaults to <user>@<host>). Pass to
bootstrap for admin user creation with primaryEmail. Update README
config reference and .env.example to document the email identity
requirement.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 20:23:15 +02:00
8227483580 install.ps1 aktualisiert 2026-04-25 19:50:24 +02:00
hsiegeln
1ef0016965 docs: update README and .env.example for SMTP removal
SMTP configuration is now managed at runtime via the vendor admin UI.
Remove SMTP config reference table, CLI flags from silent install example,
and env vars from .env.example.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 18:16:12 +02:00
hsiegeln
ec1c1f92d7 docs: update installer CLAUDE.md to reflect SMTP removal 2026-04-25 18:09:50 +02:00
hsiegeln
4037fb9dfb feat: remove SMTP configuration from PowerShell installer 2026-04-25 18:08:48 +02:00
hsiegeln
35240e0374 feat: remove SMTP configuration from bash installer 2026-04-25 18:08:44 +02:00
hsiegeln
4885c240e3 feat: remove SMTP env vars from Logto compose template
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 18:05:27 +02:00
8 changed files with 107 additions and 183 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
# Local install state created by install.sh / install.ps1 (default --install-dir)
/cameleer/

View File

@@ -25,15 +25,13 @@ The installer uses static docker-compose templates in `templates/`. Templates ar
## SMTP configuration ## SMTP configuration
The installer prompts for SMTP settings in SaaS mode when the user opts in ("Configure SMTP for email verification?"). SMTP is required for self-service sign-up — without it, only admin-created users can sign in. SMTP / email connector configuration has been moved from the installer to the SaaS vendor admin UI (Email Connector page at `/vendor/email`). The installer no longer prompts for or persists SMTP settings.
Env vars: `SMTP_HOST`, `SMTP_PORT` (default 587), `SMTP_USER`, `SMTP_PASS`, `SMTP_FROM_EMAIL` (default `noreply@<PUBLIC_HOST>`). Passed to the `cameleer-logto` container. The bootstrap script discovers the SMTP connector factory and creates the connector with Cameleer-branded email templates. Previously, SMTP env vars (`SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASS`, `SMTP_FROM_EMAIL`) were passed to the `cameleer-logto` container and configured via the bootstrap script. This one-shot approach was fragile — email delivery is now configured at runtime through the Logto Management API.
CLI args: `--smtp-host`, `--smtp-port`, `--smtp-user`, `--smtp-pass`, `--smtp-from-email`. Persisted in `cameleer.conf` for upgrades/reconfigure.
## Registry configuration ## Registry configuration
The installer supports pulling images from a custom Docker registry via `--registry`. Default: `gitea.siegeln.net/cameleer`. The installer supports pulling images from a custom Docker registry via `--registry`. Default: `registry.cameleer.io/cameleer`.
When a registry is configured, the installer writes `*_IMAGE` env vars to `.env` (e.g. `TRAEFIK_IMAGE`, `POSTGRES_IMAGE`, `CAMELEER_IMAGE`) which override the defaults baked into the compose templates. In SaaS mode, provisioning image refs (`CAMELEER_SAAS_PROVISIONING_*IMAGE`) are also set from the registry. When a registry is configured, the installer writes `*_IMAGE` env vars to `.env` (e.g. `TRAEFIK_IMAGE`, `POSTGRES_IMAGE`, `CAMELEER_IMAGE`) which override the defaults baked into the compose templates. In SaaS mode, provisioning image refs (`CAMELEER_SAAS_PROVISIONING_*IMAGE`) are also set from the registry.
@@ -45,7 +43,6 @@ For private registries, pass `--registry-user` / `--registry-token`. The install
- `CAMELEER_SERVER_*` — server config (consumed by cameleer-server) - `CAMELEER_SERVER_*` — server config (consumed by cameleer-server)
- `CAMELEER_SAAS_*` — SaaS management plane config - `CAMELEER_SAAS_*` — SaaS management plane config
- `CAMELEER_SAAS_PROVISIONING_*` — "SaaS forwards this to provisioned tenant servers" - `CAMELEER_SAAS_PROVISIONING_*` — "SaaS forwards this to provisioned tenant servers"
- `SMTP_*` — email delivery config for Logto (consumed by bootstrap, SaaS mode only)
- No prefix (e.g. `POSTGRES_PASSWORD`, `PUBLIC_HOST`) — shared infrastructure, consumed by multiple components - No prefix (e.g. `POSTGRES_PASSWORD`, `PUBLIC_HOST`) — shared infrastructure, consumed by multiple components
## Development ## Development

View File

@@ -84,9 +84,11 @@ Settings can be provided via CLI flags, environment variables, config file (`cam
| Setting | CLI Flag | Env Var | Config Key | Default | | Setting | CLI Flag | Env Var | Config Key | Default |
|---------|----------|---------|------------|---------| |---------|----------|---------|------------|---------|
| Admin username | `--admin-user` | `SAAS_ADMIN_USER` | `admin_user` | `admin` | | Admin login | `--admin-user` | `SAAS_ADMIN_USER` | `admin_user` | `admin` (standalone) / `admin@<PUBLIC_HOST>` (SaaS) |
| Admin password | `--admin-password` | `SAAS_ADMIN_PASS` | `admin_password` | auto-generated | | Admin password | `--admin-password` | `SAAS_ADMIN_PASS` | `admin_password` | auto-generated |
In SaaS mode, `SAAS_ADMIN_USER` must be an email address — it is used as both the Logto username and primaryEmail. The installer validates email format in SaaS mode and auto-appends `@<PUBLIC_HOST>` if the `@` is missing. In standalone mode, any username is accepted.
In standalone mode, the env vars are `SERVER_ADMIN_USER` / `SERVER_ADMIN_PASS`. In standalone mode, the env vars are `SERVER_ADMIN_USER` / `SERVER_ADMIN_PASS`.
### TLS Certificates ### TLS Certificates
@@ -133,24 +135,18 @@ The Docker socket is required for tenant provisioning (SaaS mode) — the platfo
| Setting | CLI Flag | Env Var | Config Key | Default | | Setting | CLI Flag | Env Var | Config Key | Default |
|---------|----------|---------|------------|---------| |---------|----------|---------|------------|---------|
| Registry | `--registry` | `REGISTRY` | `registry` | `gitea.siegeln.net/cameleer` | | Registry | `--registry` | `REGISTRY` | `registry` | `registry.cameleer.io/cameleer` |
| Registry username | `--registry-user` | `REGISTRY_USER` | `registry_user` | — | | Registry username | `--registry-user` | `REGISTRY_USER` | `registry_user` | — |
| Registry token | `--registry-token` | `REGISTRY_TOKEN` | `registry_token` | — | | Registry token | `--registry-token` | `REGISTRY_TOKEN` | `registry_token` | — |
| Image version | `--version` | `VERSION` | `version` | `latest` | | Image version | `--version` | `VERSION` | `version` | `latest` |
For private registries, provide credentials and the installer runs `docker login` before pulling. The registry prefix is applied to all container images. For private registries, provide credentials and the installer runs `docker login` before pulling. The registry prefix is applied to all container images.
### SMTP (SaaS Mode) ### Email / SMTP
| Setting | CLI Flag | Env Var | Config Key | Default | Email connector configuration (SMTP, SES, etc.) is managed at runtime via the vendor admin UI at `/vendor/email`. The installer does not configure email delivery.
|---------|----------|---------|------------|---------|
| SMTP host | `--smtp-host` | `SMTP_HOST` | `smtp_host` | — |
| SMTP port | `--smtp-port` | `SMTP_PORT` | `smtp_port` | `587` |
| SMTP username | `--smtp-user` | `SMTP_USER` | `smtp_user` | — |
| SMTP password | `--smtp-pass` | `SMTP_PASS` | `smtp_pass` | — |
| From email | `--smtp-from-email` | `SMTP_FROM_EMAIL` | `smtp_from_email` | `noreply@<PUBLIC_HOST>` |
SMTP is required for self-service sign-up (email verification codes). Without it, only admin-created users can sign in. Self-service registration is disabled by default and is enabled automatically when the admin configures an email connector.
### Monitoring ### Monitoring
@@ -176,7 +172,6 @@ These are generated automatically and never need to be set manually:
| Secret | Env Var | Description | | Secret | Env Var | Description |
|--------|---------|-------------| |--------|---------|-------------|
| JWT signing secret | `CAMELEER_SERVER_SECURITY_JWTSECRET` | Shared secret for JWT token signing across provisioned tenant servers |
| Bootstrap token | `BOOTSTRAP_TOKEN` | Server initialization token (standalone mode only) | | Bootstrap token | `BOOTSTRAP_TOKEN` | Server initialization token (standalone mode only) |
--- ---
@@ -246,9 +241,6 @@ All services share a single hostname. Routing:
--tls-mode=custom \ --tls-mode=custom \
--cert-file=/etc/ssl/cert.pem \ --cert-file=/etc/ssl/cert.pem \
--key-file=/etc/ssl/key.pem \ --key-file=/etc/ssl/key.pem \
--smtp-host=smtp.example.com \
--smtp-user=noreply@example.com \
--smtp-pass=mailpass \
--registry=registry.example.com/cameleer \ --registry=registry.example.com/cameleer \
--registry-user=deploy \ --registry-user=deploy \
--registry-token=ghp_xxx --registry-token=ghp_xxx

View File

@@ -37,11 +37,6 @@ param(
[string]$DockerSocket, [string]$DockerSocket,
[string]$NodeTlsReject, [string]$NodeTlsReject,
[string]$DeploymentMode, [string]$DeploymentMode,
[string]$SmtpHost,
[string]$SmtpPort,
[string]$SmtpUser,
[string]$SmtpPass,
[string]$SmtpFromEmail,
[string]$Registry, [string]$Registry,
[string]$RegistryUser, [string]$RegistryUser,
[string]$RegistryToken, [string]$RegistryToken,
@@ -92,11 +87,6 @@ $_ENV_COMPOSE_PROJECT = $env:COMPOSE_PROJECT
$_ENV_DOCKER_SOCKET = $env:DOCKER_SOCKET $_ENV_DOCKER_SOCKET = $env:DOCKER_SOCKET
$_ENV_NODE_TLS_REJECT = $env:NODE_TLS_REJECT $_ENV_NODE_TLS_REJECT = $env:NODE_TLS_REJECT
$_ENV_DEPLOYMENT_MODE = $env:DEPLOYMENT_MODE $_ENV_DEPLOYMENT_MODE = $env:DEPLOYMENT_MODE
$_ENV_SMTP_HOST = $env:SMTP_HOST
$_ENV_SMTP_PORT = $env:SMTP_PORT
$_ENV_SMTP_USER = $env:SMTP_USER
$_ENV_SMTP_PASS = $env:SMTP_PASS
$_ENV_SMTP_FROM_EMAIL = $env:SMTP_FROM_EMAIL
$_ENV_REGISTRY = $env:REGISTRY $_ENV_REGISTRY = $env:REGISTRY
$_ENV_REGISTRY_USER = $env:REGISTRY_USER $_ENV_REGISTRY_USER = $env:REGISTRY_USER
$_ENV_REGISTRY_TOKEN = $env:REGISTRY_TOKEN $_ENV_REGISTRY_TOKEN = $env:REGISTRY_TOKEN
@@ -110,6 +100,7 @@ $script:cfg = @{
PublicProtocol = $PublicProtocol PublicProtocol = $PublicProtocol
AdminUser = $AdminUser AdminUser = $AdminUser
AdminPass = $AdminPassword AdminPass = $AdminPassword
TlsMode = $TlsMode TlsMode = $TlsMode
CertFile = $CertFile CertFile = $CertFile
KeyFile = $KeyFile KeyFile = $KeyFile
@@ -126,11 +117,6 @@ $script:cfg = @{
DockerSocket = $DockerSocket DockerSocket = $DockerSocket
NodeTlsReject = $NodeTlsReject NodeTlsReject = $NodeTlsReject
DeploymentMode = $DeploymentMode DeploymentMode = $DeploymentMode
SmtpHost = $SmtpHost
SmtpPort = $SmtpPort
SmtpUser = $SmtpUser
SmtpPass = $SmtpPass
SmtpFromEmail = $SmtpFromEmail
Registry = $Registry Registry = $Registry
RegistryUser = $RegistryUser RegistryUser = $RegistryUser
RegistryToken = $RegistryToken RegistryToken = $RegistryToken
@@ -286,6 +272,7 @@ function Load-ConfigFile {
'public_protocol' { if (-not $script:cfg.PublicProtocol) { $script:cfg.PublicProtocol = $val } } 'public_protocol' { if (-not $script:cfg.PublicProtocol) { $script:cfg.PublicProtocol = $val } }
'admin_user' { if (-not $script:cfg.AdminUser) { $script:cfg.AdminUser = $val } } 'admin_user' { if (-not $script:cfg.AdminUser) { $script:cfg.AdminUser = $val } }
'admin_password' { if (-not $script:cfg.AdminPass) { $script:cfg.AdminPass = $val } } 'admin_password' { if (-not $script:cfg.AdminPass) { $script:cfg.AdminPass = $val } }
'tls_mode' { if (-not $script:cfg.TlsMode) { $script:cfg.TlsMode = $val } } 'tls_mode' { if (-not $script:cfg.TlsMode) { $script:cfg.TlsMode = $val } }
'cert_file' { if (-not $script:cfg.CertFile) { $script:cfg.CertFile = $val } } 'cert_file' { if (-not $script:cfg.CertFile) { $script:cfg.CertFile = $val } }
'key_file' { if (-not $script:cfg.KeyFile) { $script:cfg.KeyFile = $val } } 'key_file' { if (-not $script:cfg.KeyFile) { $script:cfg.KeyFile = $val } }
@@ -302,11 +289,6 @@ function Load-ConfigFile {
'docker_socket' { if (-not $script:cfg.DockerSocket) { $script:cfg.DockerSocket = $val } } 'docker_socket' { if (-not $script:cfg.DockerSocket) { $script:cfg.DockerSocket = $val } }
'node_tls_reject' { if (-not $script:cfg.NodeTlsReject) { $script:cfg.NodeTlsReject = $val } } 'node_tls_reject' { if (-not $script:cfg.NodeTlsReject) { $script:cfg.NodeTlsReject = $val } }
'deployment_mode' { if (-not $script:cfg.DeploymentMode) { $script:cfg.DeploymentMode = $val } } 'deployment_mode' { if (-not $script:cfg.DeploymentMode) { $script:cfg.DeploymentMode = $val } }
'smtp_host' { if (-not $script:cfg.SmtpHost) { $script:cfg.SmtpHost = $val } }
'smtp_port' { if (-not $script:cfg.SmtpPort) { $script:cfg.SmtpPort = $val } }
'smtp_user' { if (-not $script:cfg.SmtpUser) { $script:cfg.SmtpUser = $val } }
'smtp_pass' { if (-not $script:cfg.SmtpPass) { $script:cfg.SmtpPass = $val } }
'smtp_from_email' { if (-not $script:cfg.SmtpFromEmail) { $script:cfg.SmtpFromEmail = $val } }
'registry' { if (-not $script:cfg.Registry) { $script:cfg.Registry = $val } } 'registry' { if (-not $script:cfg.Registry) { $script:cfg.Registry = $val } }
'registry_user' { if (-not $script:cfg.RegistryUser) { $script:cfg.RegistryUser = $val } } 'registry_user' { if (-not $script:cfg.RegistryUser) { $script:cfg.RegistryUser = $val } }
'registry_token' { if (-not $script:cfg.RegistryToken) { $script:cfg.RegistryToken = $val } } 'registry_token' { if (-not $script:cfg.RegistryToken) { $script:cfg.RegistryToken = $val } }
@@ -323,6 +305,7 @@ function Load-EnvOverrides {
if (-not $c.PublicProtocol) { $c.PublicProtocol = $_ENV_PUBLIC_PROTOCOL } if (-not $c.PublicProtocol) { $c.PublicProtocol = $_ENV_PUBLIC_PROTOCOL }
if (-not $c.AdminUser) { $c.AdminUser = $env:SAAS_ADMIN_USER } if (-not $c.AdminUser) { $c.AdminUser = $env:SAAS_ADMIN_USER }
if (-not $c.AdminPass) { $c.AdminPass = $env:SAAS_ADMIN_PASS } if (-not $c.AdminPass) { $c.AdminPass = $env:SAAS_ADMIN_PASS }
if (-not $c.TlsMode) { $c.TlsMode = $_ENV_TLS_MODE } if (-not $c.TlsMode) { $c.TlsMode = $_ENV_TLS_MODE }
if (-not $c.CertFile) { $c.CertFile = $_ENV_CERT_FILE } if (-not $c.CertFile) { $c.CertFile = $_ENV_CERT_FILE }
if (-not $c.KeyFile) { $c.KeyFile = $_ENV_KEY_FILE } if (-not $c.KeyFile) { $c.KeyFile = $_ENV_KEY_FILE }
@@ -339,11 +322,6 @@ function Load-EnvOverrides {
if (-not $c.DockerSocket) { $c.DockerSocket = $_ENV_DOCKER_SOCKET } if (-not $c.DockerSocket) { $c.DockerSocket = $_ENV_DOCKER_SOCKET }
if (-not $c.NodeTlsReject) { $c.NodeTlsReject = $_ENV_NODE_TLS_REJECT } if (-not $c.NodeTlsReject) { $c.NodeTlsReject = $_ENV_NODE_TLS_REJECT }
if (-not $c.DeploymentMode) { $c.DeploymentMode = $_ENV_DEPLOYMENT_MODE } if (-not $c.DeploymentMode) { $c.DeploymentMode = $_ENV_DEPLOYMENT_MODE }
if (-not $c.SmtpHost) { $c.SmtpHost = $_ENV_SMTP_HOST }
if (-not $c.SmtpPort) { $c.SmtpPort = $_ENV_SMTP_PORT }
if (-not $c.SmtpUser) { $c.SmtpUser = $_ENV_SMTP_USER }
if (-not $c.SmtpPass) { $c.SmtpPass = $_ENV_SMTP_PASS }
if (-not $c.SmtpFromEmail) { $c.SmtpFromEmail = $_ENV_SMTP_FROM_EMAIL }
if (-not $c.Registry) { $c.Registry = $_ENV_REGISTRY } if (-not $c.Registry) { $c.Registry = $_ENV_REGISTRY }
if (-not $c.RegistryUser) { $c.RegistryUser = $_ENV_REGISTRY_USER } if (-not $c.RegistryUser) { $c.RegistryUser = $_ENV_REGISTRY_USER }
if (-not $c.RegistryToken) { $c.RegistryToken = $_ENV_REGISTRY_TOKEN } if (-not $c.RegistryToken) { $c.RegistryToken = $_ENV_REGISTRY_TOKEN }
@@ -472,17 +450,41 @@ function Run-SimplePrompts {
Write-Host '' Write-Host ''
Write-Host '--- Simple Installation ---' -ForegroundColor Cyan Write-Host '--- Simple Installation ---' -ForegroundColor Cyan
Write-Host '' Write-Host ''
$c.InstallDir = Prompt-Value 'Install directory' (Coalesce $c.InstallDir $DEFAULT_INSTALL_DIR) $c.InstallDir = Prompt-Value 'Install directory' (Coalesce $c.InstallDir $DEFAULT_INSTALL_DIR)
$c.PublicHost = Prompt-Value 'Public hostname' (Coalesce $c.PublicHost 'localhost') $c.PublicHost = Prompt-Value 'Public hostname' (Coalesce $c.PublicHost 'localhost')
$c.AdminUser = Prompt-Value 'Admin username' (Coalesce $c.AdminUser $DEFAULT_ADMIN_USER)
Write-Host ''
Write-Host ' Deployment mode:'
Write-Host ' [1] Multi-tenant SaaS -- manage platform, provision tenants on demand'
Write-Host ' [2] Single-tenant -- one server instance, local auth, no identity provider'
Write-Host ''
$deployChoice = Read-Host ' Select mode [1]'
if ($deployChoice -eq '2') { $c.DeploymentMode = 'standalone' } else { $c.DeploymentMode = 'saas' }
Write-Host ''
if ($c.DeploymentMode -eq 'saas') {
$defaultEmail = Coalesce $c.AdminUser "admin@$($c.PublicHost)"
if ($defaultEmail -and $defaultEmail -notmatch '@.+\..+') {
$defaultEmail = "admin@$($c.PublicHost)"
}
while ($true) {
$c.AdminUser = Prompt-Value 'Admin email' $defaultEmail
if ($c.AdminUser -match '^[^@]+@[^@]+\.[^@]+$') { break }
Write-Host ' Invalid email address. Must be a valid email (e.g. admin@company.com).' -ForegroundColor Red
$c.AdminUser = $null
$defaultEmail = $null
}
} else {
$c.AdminUser = Prompt-Value 'Admin username' (Coalesce $c.AdminUser $DEFAULT_ADMIN_USER)
}
if (Prompt-YesNo 'Auto-generate admin password?' 'y') { if (Prompt-YesNo 'Auto-generate admin password?' 'y') {
$c.AdminPass = '' $c.AdminPass = ''
} else { } else {
$c.AdminPass = Prompt-Password 'Admin password' $c.AdminPass = Prompt-Password 'Admin password'
} }
Write-Host '' Write-Host ''
if (Prompt-YesNo 'Use custom TLS certificates? (no = self-signed)') { if (Prompt-YesNo 'Use custom TLS certificates? (no = self-signed)') {
$c.TlsMode = 'custom' $c.TlsMode = 'custom'
@@ -494,7 +496,7 @@ function Run-SimplePrompts {
} else { } else {
$c.TlsMode = 'self-signed' $c.TlsMode = 'self-signed'
} }
Write-Host '' Write-Host ''
$c.MonitoringNetwork = Prompt-Value 'Monitoring network name (empty = skip)' '' $c.MonitoringNetwork = Prompt-Value 'Monitoring network name (empty = skip)' ''
@@ -505,25 +507,6 @@ function Run-SimplePrompts {
$c.RegistryToken = Prompt-Password 'Registry token/password' (Coalesce $c.RegistryToken '') $c.RegistryToken = Prompt-Password 'Registry token/password' (Coalesce $c.RegistryToken '')
} }
Write-Host ''
Write-Host ' Deployment mode:'
Write-Host ' [1] Multi-tenant SaaS -- manage platform, provision tenants on demand'
Write-Host ' [2] Single-tenant -- one server instance, local auth, no identity provider'
Write-Host ''
$deployChoice = Read-Host ' Select mode [1]'
if ($deployChoice -eq '2') { $c.DeploymentMode = 'standalone' } else { $c.DeploymentMode = 'saas' }
# SMTP for email verification (SaaS mode only)
if ($c.DeploymentMode -eq 'saas') {
Write-Host ''
if (Prompt-YesNo 'Configure SMTP for email verification? (required for self-service sign-up)') {
$c.SmtpHost = Prompt-Value 'SMTP host' (Coalesce $c.SmtpHost '')
$c.SmtpPort = Prompt-Value 'SMTP port' (Coalesce $c.SmtpPort '587')
$c.SmtpUser = Prompt-Value 'SMTP username' (Coalesce $c.SmtpUser '')
$c.SmtpPass = Prompt-Password 'SMTP password' (Coalesce $c.SmtpPass '')
$c.SmtpFromEmail = Prompt-Value 'From email address' (Coalesce $c.SmtpFromEmail "noreply@$($c.PublicHost)")
}
}
} }
function Run-ExpertPrompts { function Run-ExpertPrompts {
@@ -574,7 +557,15 @@ function Merge-Config {
if (-not $c.InstallDir) { $c.InstallDir = $DEFAULT_INSTALL_DIR } if (-not $c.InstallDir) { $c.InstallDir = $DEFAULT_INSTALL_DIR }
if (-not $c.PublicHost) { $c.PublicHost = 'localhost' } if (-not $c.PublicHost) { $c.PublicHost = 'localhost' }
if (-not $c.PublicProtocol) { $c.PublicProtocol = $DEFAULT_PUBLIC_PROTOCOL } if (-not $c.PublicProtocol) { $c.PublicProtocol = $DEFAULT_PUBLIC_PROTOCOL }
if (-not $c.AdminUser) { $c.AdminUser = $DEFAULT_ADMIN_USER } if ($c.DeploymentMode -eq 'saas') {
if (-not $c.AdminUser) { $c.AdminUser = "admin@$($c.PublicHost)" }
if ($c.AdminUser -notmatch '^[^@]+@[^@]+\.[^@]+$') {
Write-Host "ERROR: SAAS_ADMIN_USER must be a valid email address (e.g. admin@company.com), got: '$($c.AdminUser)'" -ForegroundColor Red
exit 1
}
} else {
if (-not $c.AdminUser) { $c.AdminUser = $DEFAULT_ADMIN_USER }
}
if (-not $c.TlsMode) { $c.TlsMode = $DEFAULT_TLS_MODE } if (-not $c.TlsMode) { $c.TlsMode = $DEFAULT_TLS_MODE }
if (-not $c.HttpPort) { $c.HttpPort = $DEFAULT_HTTP_PORT } if (-not $c.HttpPort) { $c.HttpPort = $DEFAULT_HTTP_PORT }
if (-not $c.HttpsPort) { $c.HttpsPort = $DEFAULT_HTTPS_PORT } if (-not $c.HttpsPort) { $c.HttpsPort = $DEFAULT_HTTPS_PORT }
@@ -667,7 +658,6 @@ function Generate-EnvFile {
$ts = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss') + ' UTC' $ts = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss') + ' UTC'
$bt = Generate-Password $bt = Generate-Password
$jwtSecret = Generate-Password
if ($c.DeploymentMode -eq 'standalone') { if ($c.DeploymentMode -eq 'standalone') {
$content = @" $content = @"
@@ -690,9 +680,6 @@ SERVER_ADMIN_USER=$($c.AdminUser)
# Bootstrap token # Bootstrap token
BOOTSTRAP_TOKEN=$bt BOOTSTRAP_TOKEN=$bt
# JWT signing secret (required by server, must be non-empty)
CAMELEER_SERVER_SECURITY_JWTSECRET=$jwtSecret
# Docker # Docker
DOCKER_SOCKET=$($c.DockerSocket) DOCKER_SOCKET=$($c.DockerSocket)
DOCKER_GID=$gid DOCKER_GID=$gid
@@ -772,21 +759,12 @@ CAMELEER_SAAS_PROVISIONING_SERVERIMAGE=$reg/cameleer-server:$($c.Version)
CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE=$reg/cameleer-server-ui:$($c.Version) CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE=$reg/cameleer-server-ui:$($c.Version)
CAMELEER_SAAS_PROVISIONING_RUNTIMEBASEIMAGE=$reg/cameleer-runtime-base:$($c.Version) CAMELEER_SAAS_PROVISIONING_RUNTIMEBASEIMAGE=$reg/cameleer-runtime-base:$($c.Version)
# JWT signing secret (forwarded to provisioned tenant servers, must be non-empty)
CAMELEER_SERVER_SECURITY_JWTSECRET=$jwtSecret
# SMTP (for email verification during registration)
SMTP_HOST=$($c.SmtpHost)
SMTP_PORT=$(if ($c.SmtpPort) { $c.SmtpPort } else { '587' })
SMTP_USER=$($c.SmtpUser)
SMTP_FROM_EMAIL=$(if ($c.SmtpFromEmail) { $c.SmtpFromEmail } else { "noreply@$($c.PublicHost)" })
"@ "@
$content += $provisioningBlock $content += $provisioningBlock
# Passwords appended with single-quoting for special character safety # Passwords appended with single-quoting for special character safety
$content += "`n$(Format-EnvVal 'POSTGRES_PASSWORD' $c.PostgresPassword)" $content += "`n$(Format-EnvVal 'POSTGRES_PASSWORD' $c.PostgresPassword)"
$content += "`n$(Format-EnvVal 'CLICKHOUSE_PASSWORD' $c.ClickhousePassword)" $content += "`n$(Format-EnvVal 'CLICKHOUSE_PASSWORD' $c.ClickhousePassword)"
$content += "`n$(Format-EnvVal 'SAAS_ADMIN_PASS' $c.AdminPass)" $content += "`n$(Format-EnvVal 'SAAS_ADMIN_PASS' $c.AdminPass)"
$content += "`n$(Format-EnvVal 'SMTP_PASS' $c.SmtpPass)"
$composeFile = 'docker-compose.yml;docker-compose.saas.yml' $composeFile = 'docker-compose.yml;docker-compose.saas.yml'
if ($c.TlsMode -eq 'custom') { $composeFile += ';docker-compose.tls.yml' } if ($c.TlsMode -eq 'custom') { $composeFile += ';docker-compose.tls.yml' }
if ($c.MonitoringNetwork) { $composeFile += ';docker-compose.monitoring.yml' } if ($c.MonitoringNetwork) { $composeFile += ';docker-compose.monitoring.yml' }
@@ -1025,15 +1003,10 @@ compose_project=$($c.ComposeProject)
docker_socket=$($c.DockerSocket) docker_socket=$($c.DockerSocket)
node_tls_reject=$($c.NodeTlsReject) node_tls_reject=$($c.NodeTlsReject)
deployment_mode=$($c.DeploymentMode) deployment_mode=$($c.DeploymentMode)
smtp_host=$($c.SmtpHost)
smtp_port=$($c.SmtpPort)
smtp_user=$($c.SmtpUser)
smtp_from_email=$($c.SmtpFromEmail)
registry=$($c.Registry) registry=$($c.Registry)
registry_user=$($c.RegistryUser) registry_user=$($c.RegistryUser)
"@ "@
# Passwords appended with single-quoting for special character safety # Passwords appended with single-quoting for special character safety
$txt += "`n$(Format-EnvVal 'smtp_pass' $c.SmtpPass)"
$txt += "`n$(Format-EnvVal 'registry_token' $c.RegistryToken)" $txt += "`n$(Format-EnvVal 'registry_token' $c.RegistryToken)"
Write-Utf8File $f $txt Write-Utf8File $f $txt
Log-Info 'Saved installer config to cameleer.conf' Log-Info 'Saved installer config to cameleer.conf'
@@ -1079,10 +1052,10 @@ ClickHouse: default / $($c.ClickhousePassword)
Admin Console: $($c.PublicProtocol)://$($c.PublicHost)/platform/ Admin Console: $($c.PublicProtocol)://$($c.PublicHost)/platform/
Admin User: $($c.AdminUser) Admin User: $($c.AdminUser)
Admin Password: $($c.AdminPass) Admin Password: $($c.AdminPass)
PostgreSQL: cameleer / $($c.PostgresPassword) PostgreSQL: cameleer / $($c.PostgresPassword)
ClickHouse: default / $($c.ClickhousePassword) ClickHouse: default / $($c.ClickhousePassword)
$logtoLine $logtoLine
"@ "@
} }
@@ -1533,10 +1506,6 @@ function Main {
Generate-Passwords Generate-Passwords
# Resolve to absolute path NOW. # Resolve to absolute path NOW.
# [System.IO.File]::WriteAllText uses .NET's Environment.CurrentDirectory,
# which differs from PowerShell's $PWD on Windows (e.g. resolves ./cameleer
# to C:\Users\Hendrik\cameleer instead of the script's working directory).
# GetUnresolvedProviderPathFromPSPath always uses PowerShell's current location.
$script:cfg.InstallDir = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath( $script:cfg.InstallDir = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath(
$script:cfg.InstallDir) $script:cfg.InstallDir)

View File

@@ -46,11 +46,6 @@ _ENV_COMPOSE_PROJECT="${COMPOSE_PROJECT:-}"
_ENV_DOCKER_SOCKET="${DOCKER_SOCKET:-}" _ENV_DOCKER_SOCKET="${DOCKER_SOCKET:-}"
_ENV_NODE_TLS_REJECT="${NODE_TLS_REJECT:-}" _ENV_NODE_TLS_REJECT="${NODE_TLS_REJECT:-}"
_ENV_DEPLOYMENT_MODE="${DEPLOYMENT_MODE:-}" _ENV_DEPLOYMENT_MODE="${DEPLOYMENT_MODE:-}"
_ENV_SMTP_HOST="${SMTP_HOST:-}"
_ENV_SMTP_PORT="${SMTP_PORT:-}"
_ENV_SMTP_USER="${SMTP_USER:-}"
_ENV_SMTP_PASS="${SMTP_PASS:-}"
_ENV_SMTP_FROM_EMAIL="${SMTP_FROM_EMAIL:-}"
_ENV_REGISTRY="${REGISTRY:-}" _ENV_REGISTRY="${REGISTRY:-}"
_ENV_REGISTRY_USER="${REGISTRY_USER:-}" _ENV_REGISTRY_USER="${REGISTRY_USER:-}"
_ENV_REGISTRY_TOKEN="${REGISTRY_TOKEN:-}" _ENV_REGISTRY_TOKEN="${REGISTRY_TOKEN:-}"
@@ -61,6 +56,7 @@ AUTH_HOST=""
PUBLIC_PROTOCOL="" PUBLIC_PROTOCOL=""
ADMIN_USER="" ADMIN_USER=""
ADMIN_PASS="" ADMIN_PASS=""
TLS_MODE="" TLS_MODE=""
CERT_FILE="" CERT_FILE=""
KEY_FILE="" KEY_FILE=""
@@ -77,11 +73,6 @@ COMPOSE_PROJECT=""
DOCKER_SOCKET="" DOCKER_SOCKET=""
NODE_TLS_REJECT="" NODE_TLS_REJECT=""
DEPLOYMENT_MODE="" DEPLOYMENT_MODE=""
SMTP_HOST=""
SMTP_PORT=""
SMTP_USER=""
SMTP_PASS=""
SMTP_FROM_EMAIL=""
REGISTRY="" REGISTRY=""
REGISTRY_USER="" REGISTRY_USER=""
REGISTRY_TOKEN="" REGISTRY_TOKEN=""
@@ -178,6 +169,7 @@ parse_args() {
--public-protocol) PUBLIC_PROTOCOL="$2"; shift ;; --public-protocol) PUBLIC_PROTOCOL="$2"; shift ;;
--admin-user) ADMIN_USER="$2"; shift ;; --admin-user) ADMIN_USER="$2"; shift ;;
--admin-password) ADMIN_PASS="$2"; shift ;; --admin-password) ADMIN_PASS="$2"; shift ;;
--tls-mode) TLS_MODE="$2"; shift ;; --tls-mode) TLS_MODE="$2"; shift ;;
--cert-file) CERT_FILE="$2"; shift ;; --cert-file) CERT_FILE="$2"; shift ;;
--key-file) KEY_FILE="$2"; shift ;; --key-file) KEY_FILE="$2"; shift ;;
@@ -194,11 +186,6 @@ parse_args() {
--docker-socket) DOCKER_SOCKET="$2"; shift ;; --docker-socket) DOCKER_SOCKET="$2"; shift ;;
--node-tls-reject) NODE_TLS_REJECT="$2"; shift ;; --node-tls-reject) NODE_TLS_REJECT="$2"; shift ;;
--deployment-mode) DEPLOYMENT_MODE="$2"; shift ;; --deployment-mode) DEPLOYMENT_MODE="$2"; shift ;;
--smtp-host) SMTP_HOST="$2"; shift ;;
--smtp-port) SMTP_PORT="$2"; shift ;;
--smtp-user) SMTP_USER="$2"; shift ;;
--smtp-pass) SMTP_PASS="$2"; shift ;;
--smtp-from-email) SMTP_FROM_EMAIL="$2"; shift ;;
--registry) REGISTRY="$2"; shift ;; --registry) REGISTRY="$2"; shift ;;
--registry-user) REGISTRY_USER="$2"; shift ;; --registry-user) REGISTRY_USER="$2"; shift ;;
--registry-token) REGISTRY_TOKEN="$2"; shift ;; --registry-token) REGISTRY_TOKEN="$2"; shift ;;
@@ -277,6 +264,7 @@ load_config_file() {
public_protocol) [ -z "$PUBLIC_PROTOCOL" ] && PUBLIC_PROTOCOL="$value" ;; public_protocol) [ -z "$PUBLIC_PROTOCOL" ] && PUBLIC_PROTOCOL="$value" ;;
admin_user) [ -z "$ADMIN_USER" ] && ADMIN_USER="$value" ;; admin_user) [ -z "$ADMIN_USER" ] && ADMIN_USER="$value" ;;
admin_password) [ -z "$ADMIN_PASS" ] && ADMIN_PASS="$value" ;; admin_password) [ -z "$ADMIN_PASS" ] && ADMIN_PASS="$value" ;;
tls_mode) [ -z "$TLS_MODE" ] && TLS_MODE="$value" ;; tls_mode) [ -z "$TLS_MODE" ] && TLS_MODE="$value" ;;
cert_file) [ -z "$CERT_FILE" ] && CERT_FILE="$value" ;; cert_file) [ -z "$CERT_FILE" ] && CERT_FILE="$value" ;;
key_file) [ -z "$KEY_FILE" ] && KEY_FILE="$value" ;; key_file) [ -z "$KEY_FILE" ] && KEY_FILE="$value" ;;
@@ -293,11 +281,6 @@ load_config_file() {
docker_socket) [ -z "$DOCKER_SOCKET" ] && DOCKER_SOCKET="$value" ;; docker_socket) [ -z "$DOCKER_SOCKET" ] && DOCKER_SOCKET="$value" ;;
node_tls_reject) [ -z "$NODE_TLS_REJECT" ] && NODE_TLS_REJECT="$value" ;; node_tls_reject) [ -z "$NODE_TLS_REJECT" ] && NODE_TLS_REJECT="$value" ;;
deployment_mode) [ -z "$DEPLOYMENT_MODE" ] && DEPLOYMENT_MODE="$value" ;; deployment_mode) [ -z "$DEPLOYMENT_MODE" ] && DEPLOYMENT_MODE="$value" ;;
smtp_host) [ -z "$SMTP_HOST" ] && SMTP_HOST="$value" ;;
smtp_port) [ -z "$SMTP_PORT" ] && SMTP_PORT="$value" ;;
smtp_user) [ -z "$SMTP_USER" ] && SMTP_USER="$value" ;;
smtp_pass) [ -z "$SMTP_PASS" ] && SMTP_PASS="$value" ;;
smtp_from_email) [ -z "$SMTP_FROM_EMAIL" ] && SMTP_FROM_EMAIL="$value" ;;
registry) [ -z "$REGISTRY" ] && REGISTRY="$value" ;; registry) [ -z "$REGISTRY" ] && REGISTRY="$value" ;;
registry_user) [ -z "$REGISTRY_USER" ] && REGISTRY_USER="$value" ;; registry_user) [ -z "$REGISTRY_USER" ] && REGISTRY_USER="$value" ;;
registry_token) [ -z "$REGISTRY_TOKEN" ] && REGISTRY_TOKEN="$value" ;; registry_token) [ -z "$REGISTRY_TOKEN" ] && REGISTRY_TOKEN="$value" ;;
@@ -312,6 +295,7 @@ load_env_overrides() {
[ -z "$PUBLIC_PROTOCOL" ] && PUBLIC_PROTOCOL="$_ENV_PUBLIC_PROTOCOL" [ -z "$PUBLIC_PROTOCOL" ] && PUBLIC_PROTOCOL="$_ENV_PUBLIC_PROTOCOL"
[ -z "$ADMIN_USER" ] && ADMIN_USER="${SAAS_ADMIN_USER:-}" [ -z "$ADMIN_USER" ] && ADMIN_USER="${SAAS_ADMIN_USER:-}"
[ -z "$ADMIN_PASS" ] && ADMIN_PASS="${SAAS_ADMIN_PASS:-}" [ -z "$ADMIN_PASS" ] && ADMIN_PASS="${SAAS_ADMIN_PASS:-}"
[ -z "$TLS_MODE" ] && TLS_MODE="$_ENV_TLS_MODE" [ -z "$TLS_MODE" ] && TLS_MODE="$_ENV_TLS_MODE"
[ -z "$CERT_FILE" ] && CERT_FILE="$_ENV_CERT_FILE" [ -z "$CERT_FILE" ] && CERT_FILE="$_ENV_CERT_FILE"
[ -z "$KEY_FILE" ] && KEY_FILE="$_ENV_KEY_FILE" [ -z "$KEY_FILE" ] && KEY_FILE="$_ENV_KEY_FILE"
@@ -328,11 +312,6 @@ load_env_overrides() {
[ -z "$DOCKER_SOCKET" ] && DOCKER_SOCKET="$_ENV_DOCKER_SOCKET" [ -z "$DOCKER_SOCKET" ] && DOCKER_SOCKET="$_ENV_DOCKER_SOCKET"
[ -z "$NODE_TLS_REJECT" ] && NODE_TLS_REJECT="$_ENV_NODE_TLS_REJECT" [ -z "$NODE_TLS_REJECT" ] && NODE_TLS_REJECT="$_ENV_NODE_TLS_REJECT"
[ -z "$DEPLOYMENT_MODE" ] && DEPLOYMENT_MODE="$_ENV_DEPLOYMENT_MODE" [ -z "$DEPLOYMENT_MODE" ] && DEPLOYMENT_MODE="$_ENV_DEPLOYMENT_MODE"
[ -z "$SMTP_HOST" ] && SMTP_HOST="$_ENV_SMTP_HOST"
[ -z "$SMTP_PORT" ] && SMTP_PORT="$_ENV_SMTP_PORT"
[ -z "$SMTP_USER" ] && SMTP_USER="$_ENV_SMTP_USER"
[ -z "$SMTP_PASS" ] && SMTP_PASS="$_ENV_SMTP_PASS"
[ -z "$SMTP_FROM_EMAIL" ] && SMTP_FROM_EMAIL="$_ENV_SMTP_FROM_EMAIL"
[ -z "$REGISTRY" ] && REGISTRY="$_ENV_REGISTRY" [ -z "$REGISTRY" ] && REGISTRY="$_ENV_REGISTRY"
[ -z "$REGISTRY_USER" ] && REGISTRY_USER="$_ENV_REGISTRY_USER" [ -z "$REGISTRY_USER" ] && REGISTRY_USER="$_ENV_REGISTRY_USER"
[ -z "$REGISTRY_TOKEN" ] && REGISTRY_TOKEN="$_ENV_REGISTRY_TOKEN" [ -z "$REGISTRY_TOKEN" ] && REGISTRY_TOKEN="$_ENV_REGISTRY_TOKEN"
@@ -450,7 +429,37 @@ run_simple_prompts() {
prompt INSTALL_DIR "Install directory" "${INSTALL_DIR:-$DEFAULT_INSTALL_DIR}" prompt INSTALL_DIR "Install directory" "${INSTALL_DIR:-$DEFAULT_INSTALL_DIR}"
prompt PUBLIC_HOST "Public hostname" "${PUBLIC_HOST:-localhost}" prompt PUBLIC_HOST "Public hostname" "${PUBLIC_HOST:-localhost}"
prompt ADMIN_USER "Admin username" "${ADMIN_USER:-$DEFAULT_ADMIN_USER}"
echo ""
echo " Deployment mode:"
echo " [1] Multi-tenant SaaS — manage platform, provision tenants on demand"
echo " [2] Single-tenant — one server instance, local auth, no identity provider"
echo ""
local deploy_choice
read -rp " Select mode [1]: " deploy_choice
case "${deploy_choice:-1}" in
2)
DEPLOYMENT_MODE="standalone"
;;
*)
DEPLOYMENT_MODE="saas"
;;
esac
echo ""
if [ "$DEPLOYMENT_MODE" = "saas" ]; then
while true; do
prompt ADMIN_USER "Admin email" "${ADMIN_USER:-admin@${PUBLIC_HOST:-localhost}}"
# Validate email: must be user@domain.tld format
if echo "$ADMIN_USER" | grep -qE '^[^@]+@[^@]+\.[^@]+$'; then
break
fi
echo -e " ${RED}Invalid email address.${NC} Must be a valid email (e.g. admin@company.com)."
ADMIN_USER=""
done
else
prompt ADMIN_USER "Admin username" "${ADMIN_USER:-$DEFAULT_ADMIN_USER}"
fi
if prompt_yesno "Auto-generate admin password?" "y"; then if prompt_yesno "Auto-generate admin password?" "y"; then
ADMIN_PASS="" ADMIN_PASS=""
@@ -480,33 +489,6 @@ run_simple_prompts() {
prompt_password REGISTRY_TOKEN "Registry token/password" "${REGISTRY_TOKEN:-}" prompt_password REGISTRY_TOKEN "Registry token/password" "${REGISTRY_TOKEN:-}"
fi fi
echo ""
echo " Deployment mode:"
echo " [1] Multi-tenant SaaS — manage platform, provision tenants on demand"
echo " [2] Single-tenant — one server instance, local auth, no identity provider"
echo ""
local deploy_choice
read -rp " Select mode [1]: " deploy_choice
case "${deploy_choice:-1}" in
2)
DEPLOYMENT_MODE="standalone"
;;
*)
DEPLOYMENT_MODE="saas"
;;
esac
# SMTP for email verification (SaaS mode only)
if [ "$DEPLOYMENT_MODE" = "saas" ]; then
echo ""
if prompt_yesno "Configure SMTP for email verification? (required for self-service sign-up)"; then
prompt SMTP_HOST "SMTP host" "${SMTP_HOST:-}"
prompt SMTP_PORT "SMTP port" "${SMTP_PORT:-587}"
prompt SMTP_USER "SMTP username" "${SMTP_USER:-}"
prompt_password SMTP_PASS "SMTP password" "${SMTP_PASS:-}"
prompt SMTP_FROM_EMAIL "From email address" "${SMTP_FROM_EMAIL:-noreply@${PUBLIC_HOST}}"
fi
fi
} }
run_expert_prompts() { run_expert_prompts() {
@@ -558,7 +540,16 @@ merge_config() {
: "${INSTALL_DIR:=$DEFAULT_INSTALL_DIR}" : "${INSTALL_DIR:=$DEFAULT_INSTALL_DIR}"
: "${PUBLIC_HOST:=localhost}" : "${PUBLIC_HOST:=localhost}"
: "${PUBLIC_PROTOCOL:=$DEFAULT_PUBLIC_PROTOCOL}" : "${PUBLIC_PROTOCOL:=$DEFAULT_PUBLIC_PROTOCOL}"
: "${ADMIN_USER:=$DEFAULT_ADMIN_USER}" if [ "$DEPLOYMENT_MODE" = "saas" ]; then
: "${ADMIN_USER:=admin@${PUBLIC_HOST}}"
# Validate email format in SaaS mode
if ! echo "$ADMIN_USER" | grep -qE '^[^@]+@[^@]+\.[^@]+$'; then
echo -e "${RED}ERROR:${NC} SAAS_ADMIN_USER must be a valid email address (e.g. admin@company.com), got: '$ADMIN_USER'" >&2
exit 1
fi
else
: "${ADMIN_USER:=$DEFAULT_ADMIN_USER}"
fi
: "${TLS_MODE:=$DEFAULT_TLS_MODE}" : "${TLS_MODE:=$DEFAULT_TLS_MODE}"
: "${HTTP_PORT:=$DEFAULT_HTTP_PORT}" : "${HTTP_PORT:=$DEFAULT_HTTP_PORT}"
: "${HTTPS_PORT:=$DEFAULT_HTTPS_PORT}" : "${HTTPS_PORT:=$DEFAULT_HTTPS_PORT}"
@@ -633,6 +624,7 @@ generate_passwords() {
ADMIN_PASS=$(generate_password) ADMIN_PASS=$(generate_password)
log_info "Generated admin password." log_info "Generated admin password."
fi fi
if [ -z "$POSTGRES_PASSWORD" ]; then if [ -z "$POSTGRES_PASSWORD" ]; then
POSTGRES_PASSWORD=$(generate_password) POSTGRES_PASSWORD=$(generate_password)
log_info "Generated PostgreSQL password." log_info "Generated PostgreSQL password."
@@ -680,9 +672,6 @@ SERVER_ADMIN_USER=${ADMIN_USER}
# Bootstrap token (required by server, not used externally in standalone mode) # Bootstrap token (required by server, not used externally in standalone mode)
BOOTSTRAP_TOKEN=$(generate_password) BOOTSTRAP_TOKEN=$(generate_password)
# JWT signing secret (required by server, must be non-empty)
CAMELEER_SERVER_SECURITY_JWTSECRET=$(generate_password)
# Docker # Docker
DOCKER_SOCKET=${DOCKER_SOCKET} DOCKER_SOCKET=${DOCKER_SOCKET}
DOCKER_GID=$(stat -c '%g' "${DOCKER_SOCKET}" 2>/dev/null || echo "0") DOCKER_GID=$(stat -c '%g' "${DOCKER_SOCKET}" 2>/dev/null || echo "0")
@@ -774,15 +763,6 @@ CAMELEER_SAAS_PROVISIONING_SERVERIMAGE=${REGISTRY}/cameleer-server:${VERSION}
CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE=${REGISTRY}/cameleer-server-ui:${VERSION} CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE=${REGISTRY}/cameleer-server-ui:${VERSION}
CAMELEER_SAAS_PROVISIONING_RUNTIMEBASEIMAGE=${REGISTRY}/cameleer-runtime-base:${VERSION} CAMELEER_SAAS_PROVISIONING_RUNTIMEBASEIMAGE=${REGISTRY}/cameleer-runtime-base:${VERSION}
# JWT signing secret (forwarded to provisioned tenant servers, must be non-empty)
CAMELEER_SERVER_SECURITY_JWTSECRET=$(generate_password)
# SMTP (for email verification during registration)
SMTP_HOST=${SMTP_HOST}
SMTP_PORT=${SMTP_PORT:-587}
SMTP_USER=${SMTP_USER}
SMTP_FROM_EMAIL=${SMTP_FROM_EMAIL:-noreply@${PUBLIC_HOST}}
# Compose file assembly # Compose file assembly
COMPOSE_FILE=docker-compose.yml:docker-compose.saas.yml$([ "$TLS_MODE" = "custom" ] && echo ":docker-compose.tls.yml")$([ -n "$MONITORING_NETWORK" ] && echo ":docker-compose.monitoring.yml") COMPOSE_FILE=docker-compose.yml:docker-compose.saas.yml$([ "$TLS_MODE" = "custom" ] && echo ":docker-compose.tls.yml")$([ -n "$MONITORING_NETWORK" ] && echo ":docker-compose.monitoring.yml")
EOF EOF
@@ -791,7 +771,6 @@ EOF
env_val "$f" POSTGRES_PASSWORD "$POSTGRES_PASSWORD" env_val "$f" POSTGRES_PASSWORD "$POSTGRES_PASSWORD"
env_val "$f" CLICKHOUSE_PASSWORD "$CLICKHOUSE_PASSWORD" env_val "$f" CLICKHOUSE_PASSWORD "$CLICKHOUSE_PASSWORD"
env_val "$f" SAAS_ADMIN_PASS "$ADMIN_PASS" env_val "$f" SAAS_ADMIN_PASS "$ADMIN_PASS"
env_val "$f" SMTP_PASS "$SMTP_PASS"
if [ -n "$MONITORING_NETWORK" ]; then if [ -n "$MONITORING_NETWORK" ]; then
echo "" >> "$f" echo "" >> "$f"
@@ -972,15 +951,10 @@ compose_project=${COMPOSE_PROJECT}
docker_socket=${DOCKER_SOCKET} docker_socket=${DOCKER_SOCKET}
node_tls_reject=${NODE_TLS_REJECT} node_tls_reject=${NODE_TLS_REJECT}
deployment_mode=${DEPLOYMENT_MODE} deployment_mode=${DEPLOYMENT_MODE}
smtp_host=${SMTP_HOST}
smtp_port=${SMTP_PORT}
smtp_user=${SMTP_USER}
smtp_from_email=${SMTP_FROM_EMAIL}
registry=${REGISTRY} registry=${REGISTRY}
registry_user=${REGISTRY_USER} registry_user=${REGISTRY_USER}
EOF EOF
# Passwords appended with single-quoting for special character safety # Passwords appended with single-quoting for special character safety
env_val "$f" smtp_pass "$SMTP_PASS"
env_val "$f" registry_token "$REGISTRY_TOKEN" env_val "$f" registry_token "$REGISTRY_TOKEN"
log_info "Saved installer config to cameleer.conf" log_info "Saved installer config to cameleer.conf"
} }

View File

@@ -50,7 +50,9 @@ CLICKHOUSE_PASSWORD=CHANGE_ME
# ============================================================ # ============================================================
# Admin credentials (SaaS mode) # Admin credentials (SaaS mode)
# ============================================================ # ============================================================
SAAS_ADMIN_USER=admin # In SaaS mode, this must be an email address (primary user identity).
# In standalone mode, any username is accepted.
SAAS_ADMIN_USER=admin@example.com
SAAS_ADMIN_PASS=CHANGE_ME SAAS_ADMIN_PASS=CHANGE_ME
# ============================================================ # ============================================================
@@ -61,14 +63,10 @@ SAAS_ADMIN_PASS=CHANGE_ME
# BOOTSTRAP_TOKEN=CHANGE_ME # BOOTSTRAP_TOKEN=CHANGE_ME
# ============================================================ # ============================================================
# SMTP (for email verification during registration) # Email / SMTP
# ============================================================ # ============================================================
# Required for self-service sign-up. Without SMTP, only admin-created users can sign in. # Email connector configuration is managed at runtime via the vendor
SMTP_HOST= # admin UI (Email Connector page at /vendor/email). No SMTP env vars needed.
SMTP_PORT=587
SMTP_USER=
SMTP_PASS=
SMTP_FROM_EMAIL=noreply@cameleer.io
# ============================================================ # ============================================================
# TLS # TLS

View File

@@ -27,12 +27,6 @@ services:
PG_DB_SAAS: cameleer_saas PG_DB_SAAS: cameleer_saas
SAAS_ADMIN_USER: ${SAAS_ADMIN_USER:-admin} SAAS_ADMIN_USER: ${SAAS_ADMIN_USER:-admin}
SAAS_ADMIN_PASS: ${SAAS_ADMIN_PASS:?SAAS_ADMIN_PASS must be set in .env} SAAS_ADMIN_PASS: ${SAAS_ADMIN_PASS:?SAAS_ADMIN_PASS must be set in .env}
# SMTP (for email verification during registration)
SMTP_HOST: ${SMTP_HOST:-}
SMTP_PORT: ${SMTP_PORT:-587}
SMTP_USER: ${SMTP_USER:-}
SMTP_PASS: ${SMTP_PASS:-}
SMTP_FROM_EMAIL: ${SMTP_FROM_EMAIL:-noreply@cameleer.io}
extra_hosts: extra_hosts:
# Logto validates M2M tokens by fetching its own JWKS from ENDPOINT. # Logto validates M2M tokens by fetching its own JWKS from ENDPOINT.
# Route the public hostname back to the Docker host (Traefik on :443) # Route the public hostname back to the Docker host (Traefik on :443)
@@ -91,7 +85,6 @@ services:
CAMELEER_SAAS_PROVISIONING_DATASOURCEUSERNAME: ${POSTGRES_USER:-cameleer} CAMELEER_SAAS_PROVISIONING_DATASOURCEUSERNAME: ${POSTGRES_USER:-cameleer}
CAMELEER_SAAS_PROVISIONING_DATASOURCEPASSWORD: ${POSTGRES_PASSWORD} CAMELEER_SAAS_PROVISIONING_DATASOURCEPASSWORD: ${POSTGRES_PASSWORD}
CAMELEER_SAAS_PROVISIONING_CLICKHOUSEPASSWORD: ${CLICKHOUSE_PASSWORD} CAMELEER_SAAS_PROVISIONING_CLICKHOUSEPASSWORD: ${CLICKHOUSE_PASSWORD}
CAMELEER_SERVER_SECURITY_JWTSECRET: ${CAMELEER_SERVER_SECURITY_JWTSECRET:?CAMELEER_SERVER_SECURITY_JWTSECRET must be set in .env}
CAMELEER_SAAS_PROVISIONING_SERVERIMAGE: ${CAMELEER_SAAS_PROVISIONING_SERVERIMAGE:-registry.cameleer.io/cameleer/cameleer-server:latest} CAMELEER_SAAS_PROVISIONING_SERVERIMAGE: ${CAMELEER_SAAS_PROVISIONING_SERVERIMAGE:-registry.cameleer.io/cameleer/cameleer-server:latest}
CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE: ${CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE:-registry.cameleer.io/cameleer/cameleer-server-ui:latest} CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE: ${CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE:-registry.cameleer.io/cameleer/cameleer-server-ui:latest}
CAMELEER_SAAS_PROVISIONING_RUNTIMEBASEIMAGE: ${CAMELEER_SAAS_PROVISIONING_RUNTIMEBASEIMAGE:-registry.cameleer.io/cameleer/cameleer-runtime-base:latest} CAMELEER_SAAS_PROVISIONING_RUNTIMEBASEIMAGE: ${CAMELEER_SAAS_PROVISIONING_RUNTIMEBASEIMAGE:-registry.cameleer.io/cameleer/cameleer-runtime-base:latest}

View File

@@ -29,7 +29,6 @@ services:
CAMELEER_SERVER_CLICKHOUSE_USERNAME: default CAMELEER_SERVER_CLICKHOUSE_USERNAME: default
CAMELEER_SERVER_CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD} CAMELEER_SERVER_CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD}
CAMELEER_SERVER_SECURITY_BOOTSTRAPTOKEN: ${BOOTSTRAP_TOKEN:?BOOTSTRAP_TOKEN must be set in .env} CAMELEER_SERVER_SECURITY_BOOTSTRAPTOKEN: ${BOOTSTRAP_TOKEN:?BOOTSTRAP_TOKEN must be set in .env}
CAMELEER_SERVER_SECURITY_JWTSECRET: ${CAMELEER_SERVER_SECURITY_JWTSECRET:?CAMELEER_SERVER_SECURITY_JWTSECRET must be set in .env}
CAMELEER_SERVER_SECURITY_UIUSER: ${SERVER_ADMIN_USER:-admin} CAMELEER_SERVER_SECURITY_UIUSER: ${SERVER_ADMIN_USER:-admin}
CAMELEER_SERVER_SECURITY_UIPASSWORD: ${SERVER_ADMIN_PASS:?SERVER_ADMIN_PASS must be set in .env} CAMELEER_SERVER_SECURITY_UIPASSWORD: ${SERVER_ADMIN_PASS:?SERVER_ADMIN_PASS must be set in .env}
CAMELEER_SERVER_SECURITY_CORSALLOWEDORIGINS: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost} CAMELEER_SERVER_SECURITY_CORSALLOWEDORIGINS: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost}