Merge pull request 'application rewrite.' (#1) from wizard_switchup into main
Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
@@ -1,212 +1,126 @@
|
||||
# AMMIDS
|
||||
**Automated Modular Matrix Infrastructure Deployment Script**
|
||||
# MIMIC
|
||||
**Modular Infrastructure for Matrix Integration & Configuration**
|
||||
|
||||
> **Status:** Beta / Experimental
|
||||
> **Target OS:** Linux (Ubuntu/Debian recommended)
|
||||
> **License:** AGPL-3.0
|
||||
> **Maintainer:** Cortex@Fossgate
|
||||
> **Source:** [https://git.fossgate.uk/main/ammids](https://git.fossgate.uk/main/ammids)
|
||||
> **Source:** [https://git.fossgate.uk/main/mimic](https://git.fossgate.uk/main/mimic)
|
||||
|
||||
AMMIDS is a modular, automated deployment suite designed to spin up a production-ready, upstreamable **Matrix** infrastructure on a fresh VPS. It orchestrates **Synapse**, **Matrix Authentication Service (MAS)**, **PostgreSQL**, and optional components like **LiveKit** and various web clients, all behind a reverse proxy.
|
||||
mimic is an interactive deployment wizard for self-hosting a Matrix homeserver stack. It automates the provisioning and configuration of Synapse, the Matrix Authentication Service (MAS), PostgreSQL, reverse proxying, optional web clients, and Synapse workers — all driven from a single guided setup session.
|
||||
|
||||
## ⚠️ Important Prerequisites
|
||||
### What it deploys
|
||||
|
||||
This script is **tested exclusively on Linux** (specifically Ubuntu 22.04+ and Debian 11+). It relies on GNU-specific utilities (e.g., `sed -i`, `stat -c`) and will **not work** on macOS, BSD, or Windows without significant modification.
|
||||
- **Synapse** — the Matrix homeserver, with optional generic workers for horizontal scaling
|
||||
- **MAS** — Matrix Authentication Service, linked to Synapse via OIDC
|
||||
- **PostgreSQL** — dedicated databases and roles for Synapse and MAS
|
||||
- **Reverse proxy** — Traefik (automatic TLS) or Nginx (behind an existing proxy)
|
||||
- **Web client** — Element, Cinny, or FluffyChat (optional)
|
||||
- **LiveKit** — real-time communication stack (optional)
|
||||
|
||||
### Required Dependencies
|
||||
### How it works
|
||||
|
||||
Before running the setup, ensure the following are installed on your VPS:
|
||||
|
||||
| Dependency | Purpose | Install Command (Debian/Ubuntu) |
|
||||
| :--- | :--- | :--- |
|
||||
| **Docker & Docker Compose** | Container orchestration | <!-- curl -fsSL https://get.docker.com | sh --> |
|
||||
| **yq** | YAML processing (Critical) | <!-- wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 && chmod +x /usr/local/bin/yq --> |
|
||||
| **OpenSSL** | Secure password generation | <!-- apt install openssl --> (Usually pre-installed) |
|
||||
| **Git** | Repository cloning | <!-- apt install git --> |
|
||||
| **Sudo** | Permission management | <!-- apt install sudo --> |
|
||||
|
||||
> **Note:** If `yq` is missing, the stack initialization will fail immediately. Ensure it is in your `$PATH` and executable.
|
||||
|
||||
## 🏗️ Architecture Overview
|
||||
|
||||
AMMIDS generates a modular Docker Compose stack with the following components:
|
||||
|
||||
* **Core Services:**
|
||||
* **Synapse:** The Matrix homeserver (supports generic workers for scaling).
|
||||
* **MAS (Matrix Auth Service):** Handles OIDC authentication and user sessions.
|
||||
* **PostgreSQL:** Centralized database for Synapse and MAS.
|
||||
* **Redis:** Required for Synapse replication and worker communication.
|
||||
* **Proxy Layer:**
|
||||
* **Traefik** (Default) or **nginx**: Manages SSL termination and routing.
|
||||
* **Optional Modules:**
|
||||
* **LiveKit:** For voice/video call support.
|
||||
* **Web Clients:** Element, Cinny, or FluffyChat.
|
||||
* **Workers:** Scalable Synapse workers (Federation Senders, Media Repos, etc.).
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### 1. DNS Configuration
|
||||
Before running the installer, ensure your DNS records point to your VPS IP address. You will need the following records:
|
||||
|
||||
| Record Type | Host/Subdomain | Purpose |
|
||||
| :--- | :--- | :--- |
|
||||
| `A` | `matrix` | Matrix Homeserver (Synapse) |
|
||||
| `A` | `auth` | Authentication Service (MAS) |
|
||||
| `A` | `app` | Web Client (Element/Cinny/FluffyChat) |
|
||||
| `A` | `rtc` | LiveKit (if enabled) |
|
||||
| `A` | `jwt` | LiveKit JWT Bridge (if enabled) |
|
||||
|
||||
*These subdomains can be adjusted in the generated .env file.*
|
||||
|
||||
### 2. Clone and Execute
|
||||
Clone the repository and navigate to the project root:
|
||||
|
||||
<!--
|
||||
git clone https://git.fossgate.uk/main/ammids.git
|
||||
cd ammids
|
||||
-->
|
||||
|
||||
Run the main orchestrator script:
|
||||
|
||||
<!--
|
||||
chmod +x setup.sh
|
||||
./setup.sh
|
||||
-->
|
||||
|
||||
**What happens during setup:**
|
||||
1. **Environment Generation:** Prompts for your domain, database credentials, and installation path. Creates `.env`.
|
||||
2. **Directory Prep:** Creates the data volume directory and fixes ownership.
|
||||
3. **Stack Initialization:**
|
||||
* Starts the database and waits for readiness.
|
||||
* Runs database migration scripts.
|
||||
* Generates Synapse and MAS configuration files.
|
||||
* Links Synapse and MAS via Native OIDC.
|
||||
4. **Compose Generation:** Creates the final `compose.yaml` with your selected modules.
|
||||
|
||||
### 3. Launch the Stack
|
||||
Once the script completes, start the services:
|
||||
|
||||
<!--
|
||||
docker compose up -d
|
||||
-->
|
||||
|
||||
Verify the status:
|
||||
|
||||
<!--
|
||||
docker compose ps
|
||||
-->
|
||||
|
||||
## 📂 Project Structure
|
||||
|
||||
<!--
|
||||
/
|
||||
├── setup.sh # Main orchestrator
|
||||
├── .env # Generated environment variables (DO NOT COMMIT)
|
||||
├── compose.yaml # Generated Docker Compose file
|
||||
├── init/
|
||||
│ ├── scripts/
|
||||
│ │ ├── set_env.sh # Interactive environment generator
|
||||
│ │ ├── stack_gen.sh # Initializes DB and generates configs
|
||||
│ │ ├── config_gen.sh # Alternative config generator (legacy)
|
||||
│ │ ├── compose_gen.sh# Generates the compose file with toggles
|
||||
│ │ ├── set_compose.sh# Updated compose file generator
|
||||
│ │ ├── worker_gen.sh # Generates scalable Synapse workers
|
||||
│ │ ├── set_workers.sh# Generates specific worker configs
|
||||
│ │ └── link_synapse.sh# Configures OIDC between Synapse & MAS
|
||||
│ ├── templates/ # YAML templates for workers and compose
|
||||
│ └── config/ # Log and config templates
|
||||
└── matrix/ # Data volume (created at runtime)
|
||||
├── synapse/
|
||||
├── mas/
|
||||
└── postgres/
|
||||
-->
|
||||
|
||||
## ⚙️ Configuration & Customization
|
||||
|
||||
### Environment Variables (`.env`)
|
||||
The `set_env.sh` script generates a secure `.env` file. Key variables include:
|
||||
* `DOMAIN`: Your base domain (e.g., `example.org`).
|
||||
* `HOMESERVER_FQDN`: Your Matrix server address.
|
||||
* `VOLUME_PATH`: Where data is stored (defaults to `./matrix`).
|
||||
* `PG_*`: Database credentials (auto-generated if left blank).
|
||||
* `LIVEKIT_API_KEY`: (Optional) Enable voice/video calls.
|
||||
|
||||
### Adding Workers
|
||||
To scale your Synapse instance, use the worker generator:
|
||||
|
||||
<!--
|
||||
./init/scripts/worker_gen.sh -c 2
|
||||
-->
|
||||
|
||||
This adds 2 generic workers and updates `compose.yaml`.
|
||||
|
||||
### Changing Web Clients
|
||||
Re-run `./init/scripts/compose_gen.sh` to toggle between Element, Cinny, FluffyChat, or disable web clients entirely.
|
||||
|
||||
### OIDC Integration
|
||||
The `config_link.sh` script automatically configures **Native OIDC** between Synapse and MAS. It:
|
||||
1. Generates a Client ID/Secret in MAS.
|
||||
2. Injects the OIDC provider configuration into Synapse.
|
||||
3. Optionally configures **LiveKit** if enabled in `.env`.
|
||||
|
||||
## 🛡️ Proxy & TLS Selection
|
||||
|
||||
During setup, you will be asked to choose a reverse proxy. This choice dictates how TLS/SSL is handled:
|
||||
|
||||
* **Traefik (Default)**:
|
||||
* **Use this if**: You want the stack to manage SSL certificates automatically via Let's Encrypt.
|
||||
* **Behavior**: Traefik listens on ports 80/443, handles ACME challenges, and serves HTTPS traffic directly.
|
||||
* **Requirement**: Ports 80 and 443 must be open to the public internet.
|
||||
|
||||
* **Nginx**:
|
||||
* **Use this if**: You already have a reverse proxy (e.g., Cloudflare, Caddy, or an existing Nginx instance) handling TLS termination.
|
||||
* **Behavior**: Nginx in this stack acts purely as an internal router. It does **not** request certificates.
|
||||
* **Requirement**: Your external proxy must forward traffic to the Nginx container (or the internal services) over HTTP.
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
### "Command not found: yq"
|
||||
The script relies on `yq` for YAML manipulation. Install it manually:
|
||||
|
||||
<!--
|
||||
# Linux AMD64
|
||||
wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
|
||||
chmod +x /usr/local/bin/yq
|
||||
-->
|
||||
|
||||
### "compose.yaml not found"
|
||||
If the script reports success but `compose.yaml` is missing:
|
||||
1. Check if you are running the script from the correct directory.
|
||||
2. Ensure `init/templates/compose.yaml.template` exists.
|
||||
3. Check `/var/log/syslog` or `dmesg` for permission errors on the root directory.
|
||||
|
||||
### Database Not Ready
|
||||
If the stack fails to start with a database timeout:
|
||||
|
||||
<!--
|
||||
docker compose logs matrix-db
|
||||
-->
|
||||
|
||||
Ensure your VPS has sufficient RAM (minimum 2GB recommended for Synapse + Postgres).
|
||||
|
||||
### OIDC Linking Failed
|
||||
If `config_link.sh` fails:
|
||||
1. Verify `yq` is installed and functional.
|
||||
2. Check that `mas/config.yaml` and `synapse/homeserver.yaml` were generated successfully.
|
||||
3. Ensure `MAS_FQDN` is correctly set in `.env`.
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
Contributions are welcome! Please open an issue on the Gitea repository before submitting large changes.
|
||||
|
||||
* **Report Bugs:** [Issues Page](https://git.fossgate.uk/main/ammids/issues)
|
||||
* **Feature Requests:** [Discussions](https://git.fossgate.uk/main/ammids/discussions)
|
||||
|
||||
## 📜 License
|
||||
|
||||
This project is licensed under the **GNU Affero General Public License v3.0 (AGPL-3.0)**.
|
||||
|
||||
See the [LICENSE](LICENSE) file for details.
|
||||
The `setup.sh` wizard collects your preferences, writes a `.env`, then orchestrates three stages: compose file generation (`compose_gen.sh`), database and config initialisation (`config_gen.sh`) and worker generation (`worker_gen.sh`). Everything is template-driven — no hand-editing of YAML required.
|
||||
|
||||
---
|
||||
*AMMIDS is provided "as is", without warranty of any kind. Use at your own risk.*
|
||||
|
||||
## Dependencies
|
||||
|
||||
mimic targets **Linux** and has only been tested on **Ubuntu 24.04**.
|
||||
|
||||
| Dependency | Purpose |
|
||||
|---|---|
|
||||
| Docker | Container runtime |
|
||||
| Docker Compose (v2 plugin) | Container orchestration |
|
||||
| yq | YAML processor (Mike Farah's Go-based `yq`) |
|
||||
| openssl | Secret / password generation |
|
||||
|
||||
```bash
|
||||
# Docker & Docker Compose plugin
|
||||
sudo apt update && sudo apt install -y docker.io docker-compose-plugin
|
||||
|
||||
# yq — Mike Farah's Go-based version (NOT the Python yq)
|
||||
sudo wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/bin/yq
|
||||
sudo chmod +x /usr/bin/yq
|
||||
|
||||
# openssl (usually pre-installed on Ubuntu 24.04)
|
||||
sudo apt install -y openssl
|
||||
|
||||
# Add your user to the docker group (log out/in afterwards)
|
||||
sudo usermod -aG docker "$USER"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### 1. Configure DNS and Firewall
|
||||
|
||||
Before running mimic, create **A records** pointing to your server's public IP for every subdomain you plan to use:
|
||||
|
||||
<span style="color: #bdac57">_Subdomains shown are defaults, custom values can be configured in the wizard._</span>
|
||||
|
||||
| Subdomain | Required? | Purpose |
|
||||
|---|---|---|
|
||||
| `<domain>` | Always | Base Url |
|
||||
| `matrix.<domain>` | Always | Synapse homeserver |
|
||||
| `auth.<domain>` | Always | Matrix Authentication Service |
|
||||
| `app.<domain>` | If a web client is selected | Element / Cinny / FluffyChat |
|
||||
| `rtc.<domain>` | If LiveKit is enabled | LiveKit media server |
|
||||
| `jwt.<domain>` | If LiveKit is enabled | LiveKit JWT token issuer |
|
||||
|
||||
Example for `example.org` with server IP `203.0.113.42`:
|
||||
```
|
||||
example.org IN A 203.0.113.42
|
||||
matrix.example.org IN A 203.0.113.42
|
||||
auth.example.org IN A 203.0.113.42
|
||||
app.example.org IN A 203.0.113.42
|
||||
rtc.example.org IN A 203.0.113.42
|
||||
jwt.example.org IN A 203.0.113.42
|
||||
```
|
||||
|
||||
### Firewall Requirements
|
||||
| Port(s) | Protocol | Required For |
|
||||
|---|---|---|
|
||||
| 80 | TCP | Traefik ACME challenges |
|
||||
| 443 | TCP | HTTPS traffic |
|
||||
| 10000–20000 | UDP | LiveKit media (if enabled) |
|
||||
|
||||
Adjust the UDP range via `LIVEKIT_UDP_RANGE` in `.env` if needed.
|
||||
If using Traefik, ensure ports **80** and **443** are open — Traefik uses HTTP-01 ACME challenges for automatic TLS certificate provisioning.
|
||||
|
||||
### 2. Clone the repository
|
||||
|
||||
```bash
|
||||
git clone https://git.fossgate.uk/main/mimic.git
|
||||
cd mimic
|
||||
chmod +x setup.sh
|
||||
```
|
||||
|
||||
### 3. Run the setup wizard
|
||||
|
||||
```bash
|
||||
./setup.sh
|
||||
```
|
||||
|
||||
The wizard walks you through:
|
||||
|
||||
1. **Base domain** — e.g. `example.org`
|
||||
2. **Service toggles** — LiveKit on/off, web client choice, proxy choice
|
||||
3. **Worker count** — number of Synapse generic workers (0 = single-process)
|
||||
4. **Credentials** — auto-generate secure passwords, or enter them manually
|
||||
|
||||
On completion, `.env` and `compose.yaml` are written to the project root and all service configs are placed under the volume path (default `./matrix/`).
|
||||
|
||||
### 4. Launch
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Verify the stack is healthy:
|
||||
```bash
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
|
||||
|
||||
+31
-40
@@ -11,51 +11,41 @@ OUTPUT_FILE="${ROOT_DIR}/compose.yaml"
|
||||
|
||||
require_file "$TEMPLATE_FILE" "Compose template"
|
||||
|
||||
log_info "Configuring optional services..."
|
||||
log_info "Generating compose.yaml based on .env configuration..."
|
||||
|
||||
read -r -p "Enable LiveKit RTC Stack? (Used for Element call) (Y/n) [Y]: " LIVEKIT_INPUT
|
||||
LIVEKIT_INPUT="${LIVEKIT_INPUT:-Y}"
|
||||
if [[ "$LIVEKIT_INPUT" =~ ^[Yy]$ ]]; then
|
||||
INCLUDE_LIVEKIT=true
|
||||
LIVEKIT_KEY=$(openssl rand -hex 16)
|
||||
LIVEKIT_SECRET=$(openssl rand -hex 32)
|
||||
cat >> "$ENV_FILE" <<EOF
|
||||
LIVEKIT_FQDN="rtc.${DOMAIN}"
|
||||
JWT_FQDN="jwt.${DOMAIN}"
|
||||
LIVEKIT_API_KEY="${LIVEKIT_KEY}"
|
||||
LIVEKIT_API_SECRET="${LIVEKIT_SECRET}"
|
||||
LIVEKIT_UDP_RANGE="10000-20000"
|
||||
EOF
|
||||
# Read configuration from .env
|
||||
# Defaults: LiveKit=off, WebApp=None, Proxy=Traefik
|
||||
INCLUDE_LIVEKIT="${INCLUDE_LIVEKIT:-false}"
|
||||
WEBAPP="${WEBAPP:-NONE}"
|
||||
PROXY="${PROXY:-TRAEFIK}"
|
||||
|
||||
# Determine inclusion flags based on single-value variables
|
||||
if [[ "$WEBAPP" == "ELEMENT" ]]; then
|
||||
INCLUDE_ELEMENT="true"
|
||||
INCLUDE_CINNY="false"
|
||||
INCLUDE_FLUFFY="false"
|
||||
elif [[ "$WEBAPP" == "CINNY" ]]; then
|
||||
INCLUDE_ELEMENT="false"
|
||||
INCLUDE_CINNY="true"
|
||||
INCLUDE_FLUFFY="false"
|
||||
elif [[ "$WEBAPP" == "FLUFFY" ]]; then
|
||||
INCLUDE_ELEMENT="false"
|
||||
INCLUDE_CINNY="false"
|
||||
INCLUDE_FLUFFY="true"
|
||||
else
|
||||
INCLUDE_LIVEKIT=false
|
||||
INCLUDE_ELEMENT="false"
|
||||
INCLUDE_CINNY="false"
|
||||
INCLUDE_FLUFFY="false"
|
||||
fi
|
||||
write_state LIVEKIT "$INCLUDE_LIVEKIT"
|
||||
|
||||
echo "Select Web Client: 1) Element 2) Cinny 3) FluffyChat 4) None"
|
||||
read -r -p "Default [1]: " WEB_APP_INPUT
|
||||
WEB_APP_INPUT="${WEB_APP_INPUT:-1}"
|
||||
case "$WEB_APP_INPUT" in
|
||||
1) INCLUDE_ELEMENT=true; INCLUDE_CINNY=false; INCLUDE_FLUFFY=false; write_state "APP" "ELEMENT" ;;
|
||||
2) INCLUDE_ELEMENT=false; INCLUDE_CINNY=true; INCLUDE_FLUFFY=false; write_state "APP" "CINNY" ;;
|
||||
3) INCLUDE_ELEMENT=false; INCLUDE_CINNY=false; INCLUDE_FLUFFY=true; write_state "APP" "FLUFFY" ;;
|
||||
*) INCLUDE_ELEMENT=false; INCLUDE_CINNY=false; INCLUDE_FLUFFY=false; write_state "APP" "NONE" ;;
|
||||
esac
|
||||
if [[ "$PROXY" == "NGINX" ]]; then
|
||||
INCLUDE_NGINX="true"
|
||||
INCLUDE_TRAEFIK="false"
|
||||
else
|
||||
INCLUDE_NGINX="false"
|
||||
INCLUDE_TRAEFIK="true"
|
||||
fi
|
||||
|
||||
echo "Select Reverse Proxy: 1) Traefik 2) nginx"
|
||||
read -r -p "Default [1]: " PROXY_INPUT
|
||||
PROXY_INPUT="${PROXY_INPUT:-1}"
|
||||
case "$PROXY_INPUT" in
|
||||
2)
|
||||
INCLUDE_NGINX=true
|
||||
INCLUDE_TRAEFIK=false
|
||||
write_state "PROXY" "NGINX"
|
||||
;;
|
||||
*)
|
||||
INCLUDE_NGINX=false
|
||||
INCLUDE_TRAEFIK=true
|
||||
write_state "PROXY" "TRAEFIK"
|
||||
;;
|
||||
esac
|
||||
cp "$TEMPLATE_FILE" "$OUTPUT_FILE"
|
||||
|
||||
toggle_block "$OUTPUT_FILE" "# \[LIVEKIT_START\]" "# \[LIVEKIT_END\]" "$INCLUDE_LIVEKIT"
|
||||
@@ -65,6 +55,7 @@ toggle_block "$OUTPUT_FILE" "# \[FLUFFYCHAT_START\]" "# \[FLUFFYCHAT_END\]" "$IN
|
||||
toggle_block "$OUTPUT_FILE" "# \[NGINX_START\]" "# \[NGINX_END\]" "$INCLUDE_NGINX"
|
||||
toggle_block "$OUTPUT_FILE" "# \[TRAEFIK_START\]" "# \[TRAEFIK_END\]" "$INCLUDE_TRAEFIK"
|
||||
|
||||
# Handle Traefik Labels
|
||||
if [[ "$INCLUDE_TRAEFIK" == "true" ]]; then
|
||||
sed -i "/# \[TRAEFIK_LABELS_START\]/d; /# \[TRAEFIK_LABELS_END\]/d" "$OUTPUT_FILE"
|
||||
else
|
||||
|
||||
@@ -6,16 +6,26 @@ source "${SCRIPT_DIR}/functions.sh"
|
||||
resolve_paths
|
||||
|
||||
require_env
|
||||
require_command "yq"
|
||||
require_command "openssl"
|
||||
require_docker
|
||||
|
||||
# ============================================================================
|
||||
# PHASE 1: Infrastructure Setup
|
||||
# ============================================================================
|
||||
|
||||
resolve_volume_path
|
||||
|
||||
mkdir -p "${VOLUME_PATH}/synapse" "${VOLUME_PATH}/mas" "${VOLUME_PATH}/postgres"
|
||||
mkdir -p "${VOLUME_PATH}/synapse" "${VOLUME_PATH}/mas" "${VOLUME_PATH}/postgres" "${VOLUME_PATH}/proxy"
|
||||
if [[ "${WORKER_COUNT}" -gt 0 ]]; then
|
||||
mkdir -p "${VOLUME_PATH}/webapp"
|
||||
fi
|
||||
|
||||
log_info "Initializing PostgreSQL..."
|
||||
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d --wait --timeout 60 matrix-db
|
||||
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d --wait matrix-db
|
||||
|
||||
# Wait for database readiness (silent except on failure)
|
||||
for ATTEMPT in $(seq 1 30); do
|
||||
# Wait for database readiness
|
||||
for ATTEMPT in $(seq 1 60); do
|
||||
if docker compose -f "$COMPOSE_FILE" exec -T matrix-db psql -U "$PG_USER" -c "SELECT 1;" > /dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
@@ -23,7 +33,7 @@ for ATTEMPT in $(seq 1 30); do
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# Database initialization SQL
|
||||
# Database initialization
|
||||
INIT_SQL=$(cat <<EOF
|
||||
DO \$\$ BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'mas') THEN
|
||||
@@ -42,14 +52,29 @@ SELECT CASE WHEN NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'synapse'
|
||||
EOF
|
||||
)
|
||||
|
||||
echo "$INIT_SQL" | docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" exec -T matrix-db psql -U "$PG_USER" -f -
|
||||
echo "$INIT_SQL" | docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" exec -T matrix-db psql -U "$PG_USER" -f - > /dev/null
|
||||
|
||||
# ============================================================================
|
||||
# PHASE 2: Configuration Generation
|
||||
# ============================================================================
|
||||
|
||||
log_info "Generating Synapse configuration..."
|
||||
docker run -it --rm --network matrix_network \
|
||||
-v "${VOLUME_PATH}/synapse:/data" \
|
||||
-e SYNAPSE_SERVER_NAME="$HOMESERVER_FQDN" \
|
||||
-e SYNAPSE_REPORT_STATS=no \
|
||||
ghcr.io/element-hq/synapse:latest generate
|
||||
ghcr.io/element-hq/synapse:latest generate || { log_error "Synapse config generation failed"; exit 1; }
|
||||
|
||||
SYNAPSE_CFG="${VOLUME_PATH}/synapse/homeserver.yaml"
|
||||
yq -y -i '.database.name = "psycopg2"' "$SYNAPSE_CFG"
|
||||
yq -y -i ".database.args.user = \"${PG_USER_MATRIX}\"" "$SYNAPSE_CFG"
|
||||
yq -y -i ".database.args.password = \"${PG_PASSWORD_MATRIX}\"" "$SYNAPSE_CFG"
|
||||
yq -y -i ".database.args.database = \"${PG_DB_MATRIX}\"" "$SYNAPSE_CFG"
|
||||
yq -y -i ".database.args.host = \"matrix-db\"" "$SYNAPSE_CFG"
|
||||
yq -y -i ".database.args.port = 5432" "$SYNAPSE_CFG"
|
||||
yq -y -i ".database.args.sslmode = \"prefer\"" "$SYNAPSE_CFG"
|
||||
yq -y -i ".public_baseurl = \"https://${HOMESERVER_FQDN}\"" "$SYNAPSE_CFG"
|
||||
yq -y -i ".server_name = \"${HOMESERVER_FQDN}\"" "$SYNAPSE_CFG"
|
||||
|
||||
fix_ownership "${VOLUME_PATH}/synapse"
|
||||
|
||||
@@ -57,7 +82,72 @@ log_info "Generating MAS configuration..."
|
||||
docker run -it --rm --network matrix_network \
|
||||
-v "${VOLUME_PATH}/mas:/data" \
|
||||
ghcr.io/element-hq/matrix-authentication-service:latest config generate \
|
||||
| grep -v '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}' > "${VOLUME_PATH}/mas/config.yaml"
|
||||
| grep -v '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}' > "${VOLUME_PATH}/mas/config.yaml" || { log_error "Mas config generation failed"; exit 1; }
|
||||
|
||||
MAS_CFG="${VOLUME_PATH}/mas/config.yaml"
|
||||
if ! yq '.clients' "$MAS_CFG" > /dev/null 2>&1; then
|
||||
yq -y -i '.clients = []' "$MAS_CFG"
|
||||
fi
|
||||
yq -y -i ".public_base_url = \"https://${MAS_FQDN}\"" "$MAS_CFG"
|
||||
yq -y -i ".http.listen = \"0.0.0.0:8000\"" "$MAS_CFG"
|
||||
yq -y -i ".database.uri = \"postgres://${PG_USER_MAS}:${PG_PASSWORD_MAS}@matrix-db:5432/auth?sslmode=prefer\"" "$MAS_CFG"
|
||||
|
||||
fix_ownership "${VOLUME_PATH}/mas"
|
||||
log_info "Configuration complete"
|
||||
|
||||
# ============================================================================
|
||||
# PHASE 3: OIDC Linking
|
||||
# ============================================================================
|
||||
|
||||
log_info "Linking Synapse and MAS via OIDC..."
|
||||
|
||||
CLIENT_ID="synapse-client"
|
||||
CLIENT_SECRET=$(openssl rand -hex 32)
|
||||
|
||||
TEMP_FILTER=$(mktemp)
|
||||
trap "rm -f $TEMP_FILTER" EXIT
|
||||
|
||||
cat > "$TEMP_FILTER" <<EOF
|
||||
clients:
|
||||
- client_id: "${CLIENT_ID}"
|
||||
client_secret: "${CLIENT_SECRET}"
|
||||
redirect_uris:
|
||||
- "https://${HOMESERVER_FQDN}/_synapse/client/oidc/callback"
|
||||
grant_types:
|
||||
- "authorization_code"
|
||||
- "refresh_token"
|
||||
response_types:
|
||||
- "code"
|
||||
scopes:
|
||||
- "openid"
|
||||
- "profile"
|
||||
- "email"
|
||||
application_type: "web"
|
||||
EOF
|
||||
|
||||
EXISTING_CLIENT=$(yq '.clients[] | select(.client_id == "'"${CLIENT_ID}"'")' "$MAS_CFG" 2>/dev/null || echo "")
|
||||
|
||||
if [[ -z "$EXISTING_CLIENT" ]]; then
|
||||
yq -y -i "$(cat "$TEMP_FILTER")" "$MAS_CFG" || { log_error "Failed to register Synapse client in MAS"; exit 1; }
|
||||
log_info " -> Registered Synapse as OIDC client in MAS"
|
||||
else
|
||||
log_info " -> Synapse client already registered in MAS, skipping"
|
||||
fi
|
||||
|
||||
ISSUER_URL="https://${MAS_FQDN}"
|
||||
|
||||
fix_ownership "$SYNAPSE_CFG"
|
||||
|
||||
cat > "$TEMP_FILTER" <<EOF
|
||||
.oidc_providers += [{"idp_id": "mas", "idp_name": "Matrix Authentication Service", "idp_brand": "mas", "issuer": "${ISSUER_URL}", "client_id": "${CLIENT_ID}", "client_secret": "${CLIENT_SECRET}", "scopes": ["openid", "profile"], "skip_verification": false}]
|
||||
EOF
|
||||
|
||||
if ! yq '.oidc_providers' "$SYNAPSE_CFG" 2>/dev/null | grep -q "idp_id"; then
|
||||
yq -y -i "$(cat "$TEMP_FILTER")" "$SYNAPSE_CFG" || { log_error "Failed to inject OIDC config"; exit 1; }
|
||||
fi
|
||||
|
||||
if [[ "${INCLUDE_LIVEKIT:-FALSE}" == "TRUE" ]]; then
|
||||
JWT_URL="https://${JWT_FQDN:-jwt.${DOMAIN}}"
|
||||
yq -y -i ".livekit.url = \"$JWT_URL\"" "$SYNAPSE_CFG"
|
||||
fi
|
||||
|
||||
log_info "Config generation complete"
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/functions.sh"
|
||||
resolve_paths
|
||||
|
||||
require_env
|
||||
require_command "yq"
|
||||
require_command "openssl"
|
||||
|
||||
SYNAPSE_CFG="${VOLUME_PATH}/synapse/homeserver.yaml"
|
||||
MAS_CFG="${VOLUME_PATH}/mas/config.yaml"
|
||||
|
||||
require_file "$SYNAPSE_CFG" "Synapse config"
|
||||
require_file "$MAS_CFG" "MAS config"
|
||||
|
||||
CLIENT_ID="synapse-client"
|
||||
CLIENT_SECRET=$(openssl rand -hex 32)
|
||||
|
||||
TEMP_FILTER=$(mktemp)
|
||||
trap "rm -f $TEMP_FILTER" EXIT
|
||||
|
||||
cat > "$TEMP_FILTER" <<EOF
|
||||
.oidc.providers += [{"id": "synapse", "client_id": "${CLIENT_ID}", "client_secret": "${CLIENT_SECRET}", "scopes": ["openid", "profile", "email"], "issuer": "http://mas:8000"}]
|
||||
EOF
|
||||
|
||||
yq -y -i "$(cat "$TEMP_FILTER")" "$MAS_CFG" || { log_error "Failed to update MAS config"; exit 1; }
|
||||
|
||||
CLIENT_ID=$(yq '.oidc.providers[0].client_id' "$MAS_CFG" | tr -d '"')
|
||||
CLIENT_SECRET=$(yq '.oidc.providers[0].client_secret' "$MAS_CFG" | tr -d '"')
|
||||
|
||||
ISSUER_URL="https://${MAS_FQDN}"
|
||||
|
||||
fix_ownership "$SYNAPSE_CFG"
|
||||
|
||||
cat > "$TEMP_FILTER" <<EOF
|
||||
.oidc_providers += [{"idp_id": "mas", "idp_name": "Matrix Authentication Service", "idp_brand": "mas", "issuer": "${ISSUER_URL}", "client_id": "${CLIENT_ID}", "client_secret": "${CLIENT_SECRET}", "scopes": ["openid", "profile"], "skip_verification": false}]
|
||||
EOF
|
||||
|
||||
if ! yq '.oidc_providers' "$SYNAPSE_CFG" 2>/dev/null | grep -q "idp_id"; then
|
||||
yq -y -i "$(cat "$TEMP_FILTER")" "$SYNAPSE_CFG" || { log_error "Failed to inject OIDC config"; exit 1; }
|
||||
fi
|
||||
|
||||
if [[ -n "${LIVEKIT_API_KEY:-}" ]]; then
|
||||
JWT_URL="https://${JWT_FQDN:-jwt.${DOMAIN}}"
|
||||
if yq '.livekit.url' "$SYNAPSE_CFG" 2>/dev/null | grep -q "."; then
|
||||
yq -y -i ".livekit.url = \"$JWT_URL\"" "$SYNAPSE_CFG"
|
||||
else
|
||||
yq -y -i ".livekit.url = \"$JWT_URL\"" "$SYNAPSE_CFG"
|
||||
fi
|
||||
fi
|
||||
|
||||
log_info "OIDC and LiveKit linking complete"
|
||||
@@ -1,62 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/functions.sh"
|
||||
resolve_paths
|
||||
|
||||
main() {
|
||||
> "$ENV_FILE"
|
||||
|
||||
read -r -p "Base Domain (e.g. example.org) [example.org]: " DOMAIN_INPUT
|
||||
DOMAIN="${DOMAIN_INPUT:-example.org}"
|
||||
auto_var "DOMAIN" $DOMAIN "no"
|
||||
log_info "Base Domain set to: ${DOMAIN}"
|
||||
|
||||
read -r -p "Auto-generate secure defaults for remaining fields? [Y/n]: " GEN_INPUT
|
||||
GEN_INPUT="${GEN_INPUT:-Y}"
|
||||
|
||||
if [[ "$GEN_INPUT" =~ ^[Yy]$ ]]; then
|
||||
log_info "Generating secure defaults..."
|
||||
|
||||
auto_var "USER_ID" "1000" "no"
|
||||
auto_var "GROUP_ID" "1000" "no"
|
||||
auto_var "VOLUME_PATH" "./matrix" "no"
|
||||
auto_var "COUNTRY" "GB" "no"
|
||||
auto_var "PG_USER" "postgres" "no"
|
||||
auto_var "PG_PASSWORD" "generated" "yes"
|
||||
auto_var "PG_USER_MATRIX" "matrix" "no"
|
||||
auto_var "PG_DB_MATRIX" "synapse" "no"
|
||||
auto_var "PG_PASSWORD_MATRIX" "generated" "yes"
|
||||
auto_var "PG_USER_MAS" "mas" "no"
|
||||
auto_var "PG_DB_MAS" "auth" "no"
|
||||
auto_var "PG_PASSWORD_MAS" "generated" "yes"
|
||||
|
||||
log_info "All fields auto-generated."
|
||||
else
|
||||
log_info "Manual entry mode."
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
prompt_var "USER_ID" "Docker UserId" "1000" "no"
|
||||
prompt_var "GROUP_ID" "Docker GroupID" "1000" "no"
|
||||
prompt_var "VOLUME_PATH" "Install directory" "./matrix" "no"
|
||||
prompt_var "COUNTRY" "Country Code (e.g. GB)" "GB" "no"
|
||||
prompt_var "PG_USER" "PostgreSQL Superuser" "postgres" "no"
|
||||
prompt_var "PG_PASSWORD" "PostgreSQL Superuser Password" "generated" "yes"
|
||||
prompt_var "PG_USER_MATRIX" "Synapse Database User" "matrix" "no"
|
||||
prompt_var "PG_DB_MATRIX" "Synapse Database Name" "synapse" "no"
|
||||
prompt_var "PG_PASSWORD_MATRIX" "Synapse Database Password" "generated" "yes"
|
||||
prompt_var "PG_USER_MAS" "MAS Database User" "mas" "no"
|
||||
prompt_var "PG_DB_MAS" "MAS Database Name" "auth" "no"
|
||||
prompt_var "PG_PASSWORD_MAS" "MAS Database Password" "generated" "yes"
|
||||
|
||||
chmod 600 "$ENV_FILE"
|
||||
|
||||
log_info "Environment file created: $ENV_FILE"
|
||||
log_warn "Add $ENV_FILE to .gitignore and backup securely"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
+64
-34
@@ -4,16 +4,8 @@ set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/functions.sh"
|
||||
resolve_paths
|
||||
|
||||
WORKER_COUNT=1
|
||||
while getopts "c:" opt; do
|
||||
case $opt in
|
||||
c) WORKER_COUNT="$OPTARG" ;;
|
||||
*) echo "Usage: $0 [-c count]"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
require_env
|
||||
require_command "yq"
|
||||
resolve_volume_path
|
||||
|
||||
TEMPLATE_COMPOSE="${INIT_DIR}/templates/worker.yaml"
|
||||
@@ -25,24 +17,29 @@ require_file "$TEMPLATE_CONFIG" "Config template"
|
||||
require_file "$TEMPLATE_LOG" "Log template"
|
||||
|
||||
WORKERS_DIR="${VOLUME_PATH}/synapse/workers"
|
||||
mkdir -p "$WORKERS_DIR" "${ROOT_DIR}/.tmp"
|
||||
mkdir -p "$WORKERS_DIR" "${SCRIPT_DIR}/tmp"
|
||||
|
||||
TRAEFIK_ENABLED="${PROXY:-0}"
|
||||
TEMP_COMPOSE="${ROOT_DIR}/.tmp/compose_workers.yaml"
|
||||
TEMP_COMPOSE="${SCRIPT_DIR}/tmp/compose_workers.yaml"
|
||||
# Initialize empty compose file for workers
|
||||
> "$TEMP_COMPOSE"
|
||||
|
||||
for ((i=1; i<=WORKER_COUNT; i++)); do
|
||||
worker_name="synapse-worker-${i}"
|
||||
port=$((9093 + i))
|
||||
TEMP_WORKER_COMPOSE="${SCRIPT_DIR}/tmp/${worker_name}.yaml"
|
||||
|
||||
log_info "Generating configuration for ${worker_name}..."
|
||||
|
||||
# 1. Generate Worker Config (synapse config file)
|
||||
sed -e "s/{{WORKER_NAME}}/${worker_name}/g" \
|
||||
-e "s/{{PORT}}/${port}/g" \
|
||||
-e "s/{{SERVER_NAME}}/${HOMESERVER_FQDN}/g" \
|
||||
-e "s/{{DB_USER}}/${DB_USER:-synapse}/g" \
|
||||
-e "s/{{DB_PASSWORD}}/${DB_PASSWORD:-synapse_password}/g" \
|
||||
-e "s/{{DB_NAME}}/${DB_NAME:-synapse}/g" \
|
||||
-e "s/{{DB_USER}}/${PG_USER_MATRIX}/g" \
|
||||
-e "s/{{DB_PASSWORD}}/${PG_PASSWORD_MATRIX}/g" \
|
||||
-e "s/{{DB_NAME}}/${PG_DB_MATRIX}/g" \
|
||||
"$TEMPLATE_CONFIG" > "${WORKERS_DIR}/${worker_name}.yaml"
|
||||
|
||||
# 2. Generate Log Config
|
||||
sed -e "s/{{WORKER_NAME}}/${worker_name}/g" \
|
||||
-e "s/{{MAX_BYTES}}/${LOG_MAX_BYTES:-10485760}/g" \
|
||||
-e "s/{{BACKUP_COUNT}}/${LOG_BACKUP_COUNT:-5}/g" \
|
||||
@@ -50,37 +47,70 @@ for ((i=1; i<=WORKER_COUNT; i++)); do
|
||||
-e "s/{{SYNAPSE_LOG_LEVEL}}/${SYNAPSE_LOG_LEVEL:-INFO}/g" \
|
||||
"$TEMPLATE_LOG" > "${WORKERS_DIR}/${worker_name}.log.config"
|
||||
|
||||
TRAEFIK_LABELS=""
|
||||
if [[ "$TRAEFIK_ENABLED" == "1" && -n "$DOMAIN" ]]; then
|
||||
TRAEFIK_LABELS=" labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.services.${worker_name}.loadbalancer.server.port=${port}
|
||||
- traefik.http.routers.synapse-worker-${worker_name}.rule=Host(\`${DOMAIN}\`) && PathPrefix(\`/_matrix\`)
|
||||
- traefik.http.routers.synapse-worker-${worker_name}.entrypoints=websecure
|
||||
- traefik.http.routers.synapse-worker-${worker_name}.service=${worker_name}
|
||||
- traefik.http.routers.synapse-worker-${worker_name}.priority=10
|
||||
"
|
||||
fi
|
||||
|
||||
# 3. Generate Temporary Compose Snippet for this worker
|
||||
# Start with the base template
|
||||
sed -e "s/{{WORKER_NAME}}/${worker_name}/g" \
|
||||
-e "s/{{PORT}}/${port}/g" \
|
||||
-e "s/{{APP_TYPE}}/synapse.app.generic_worker/g" \
|
||||
-e "s/{{TRAEFIK_LABELS}}/${TRAEFIK_LABELS}/g" \
|
||||
"$TEMPLATE_COMPOSE" >> "$TEMP_COMPOSE"
|
||||
"$TEMPLATE_COMPOSE" > "$TEMP_WORKER_COMPOSE"
|
||||
|
||||
echo "" >> "$TEMP_COMPOSE"
|
||||
# 4. Conditionally Inject Traefik Labels using yq
|
||||
if [[ "$PROXY" == "TRAEFIK" && -n "$DOMAIN" ]]; then
|
||||
log_info " -> Adding Traefik labels for ${worker_name}"
|
||||
|
||||
# Construct the labels array safely
|
||||
# Note: We escape backticks for the shell, but yq handles the YAML structure
|
||||
yq -i '.services."'"${worker_name}"'".labels = [
|
||||
"traefik.enable=true",
|
||||
"traefik.http.services.'"${worker_name}"'.loadbalancer.server.port='"${port}"'",
|
||||
"traefik.http.routers.synapse-worker-'"${worker_name}"'.rule=Host(`'"${DOMAIN}"'`) && PathPrefix(`/_matrix`)",
|
||||
"traefik.http.routers.synapse-worker-'"${worker_name}"'.entrypoints=websecure",
|
||||
"traefik.http.routers.synapse-worker-'"${worker_name}"'.service='"${worker_name}"'",
|
||||
"traefik.http.routers.synapse-worker-'"${worker_name}"'.priority=10"
|
||||
]' "$TEMP_WORKER_COMPOSE"
|
||||
else
|
||||
# Ensure labels key doesn't exist if not using Traefik
|
||||
yq -i 'del(.services."'"${worker_name}"'".labels)' "$TEMP_WORKER_COMPOSE" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# 5. Merge this worker into the temporary aggregate compose file
|
||||
# If TEMP_COMPOSE is empty, just copy. Otherwise, merge.
|
||||
if [[ ! -s "$TEMP_COMPOSE" ]]; then
|
||||
cp "$TEMP_WORKER_COMPOSE" "$TEMP_COMPOSE"
|
||||
else
|
||||
# Merge: existing content * new worker content
|
||||
yq eval-all "select(fileIndex == 0) * select(fileIndex == 1)" \
|
||||
"$TEMP_COMPOSE" "$TEMP_WORKER_COMPOSE" > "${TEMP_COMPOSE}.merged"
|
||||
mv "${TEMP_COMPOSE}.merged" "$TEMP_COMPOSE"
|
||||
fi
|
||||
|
||||
# Cleanup temp worker file
|
||||
rm -f "$TEMP_WORKER_COMPOSE"
|
||||
done
|
||||
|
||||
# 6. Final Merge into main compose.yaml
|
||||
if [[ -f "$COMPOSE_FILE" ]]; then
|
||||
# Check if workers already exist to avoid duplicates
|
||||
if ! grep -qE "^ synapse-worker-[0-9]+:" "$COMPOSE_FILE"; then
|
||||
cat "$TEMP_COMPOSE" >> "$COMPOSE_FILE"
|
||||
# Merge main compose with generated workers
|
||||
yq eval-all "select(fileIndex == 0) * select(fileIndex == 1)" \
|
||||
"$COMPOSE_FILE" "$TEMP_COMPOSE" > "${COMPOSE_FILE}.new"
|
||||
|
||||
if mv "${COMPOSE_FILE}.new" "$COMPOSE_FILE"; then
|
||||
log_info "Appended ${WORKER_COUNT} worker(s) to compose.yaml"
|
||||
else
|
||||
log_warn "Some workers already exist. Manual merge required"
|
||||
cp "$TEMP_COMPOSE" "${ROOT_DIR}/.tmp/compose_workers.yaml"
|
||||
log_error "Failed to update compose.yaml"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
log_warn "Some workers already exist in compose.yaml. Manual merge required."
|
||||
cp "$TEMP_COMPOSE" "${SCRIPT_DIR}/tmp/compose_workers.yaml"
|
||||
log_warn "Saved new worker definitions to ${SCRIPT_DIR}/tmp/compose_workers.yaml"
|
||||
fi
|
||||
else
|
||||
log_warn "compose.yaml not found. Generated: ${TEMP_COMPOSE}"
|
||||
# If no main compose exists, the generated file becomes the main one
|
||||
mv "$TEMP_COMPOSE" "$COMPOSE_FILE"
|
||||
log_info "Created new compose.yaml with ${WORKER_COUNT} worker(s)"
|
||||
fi
|
||||
|
||||
rm -f "$TEMP_COMPOSE"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# AMMIDS Setup Orchestrator
|
||||
# Author: Cortex@Fossgate
|
||||
# Src: https://git.fossgate.uk/ammids
|
||||
# AMMIDS Master Setup Wizard
|
||||
# Orchestrates the entire deployment flow via a single interactive session
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@@ -9,60 +8,144 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/init/scripts/functions.sh"
|
||||
resolve_paths
|
||||
|
||||
ENV_FILE="${ROOT_DIR}/.env"
|
||||
# Ensure dependencies
|
||||
require_command "openssl" || require_command "pwgen"
|
||||
|
||||
log_info "=== AMMIDS Setup Orchestrator ==="
|
||||
log_info "=== AMMIDS Interactive Setup Wizard ==="
|
||||
|
||||
# 1. Environment Setup
|
||||
if [[ -f "$ENV_FILE" ]]; then
|
||||
log_warn ".env exists."
|
||||
read -r -p " Overwrite and create backup? [y/N]: " confirm
|
||||
if [[ "$confirm" =~ ^[Yy]$ ]]; then
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
BACKUP_FILE="${SCRIPT_DIR}/.env.$(date +%Y%m%d_%H%M%S)"
|
||||
cp "$ENV_FILE" "$BACKUP_FILE"
|
||||
log_info "Backup created: $BACKUP_FILE"
|
||||
"${SCRIPT_DIR}/init/scripts/set_env.sh"
|
||||
else
|
||||
log_info "Preserving existing .env. Skipping regeneration."
|
||||
# 1. Base Configuration
|
||||
read -r -p "Base Domain (e.g. example.org) [example.org]: " DOMAIN_INPUT
|
||||
BASE_DOMAIN="${DOMAIN_INPUT:-example.org}"
|
||||
|
||||
log_info "Domain set: ${BASE_DOMAIN}"
|
||||
|
||||
# 2. Service Selection
|
||||
echo ""
|
||||
log_info "Select Services:"
|
||||
|
||||
# LiveKit
|
||||
read -r -p "Enable LiveKit RTC Stack? (Y/n) [Y]: " LIVEKIT_INPUT
|
||||
LIVEKIT_INPUT="${LIVEKIT_INPUT:-Y}"
|
||||
if [[ "$LIVEKIT_INPUT" =~ ^[Yy]$ ]]; then
|
||||
INCLUDE_LIVEKIT="true"
|
||||
else
|
||||
INCLUDE_LIVEKIT="false"
|
||||
fi
|
||||
|
||||
# Web Client (Set to a single value: ELEMENT, CINNY, FLUFFY, or NONE)
|
||||
echo "Select Web Client: 1) Element 2) Cinny 3) FluffyChat 4) None"
|
||||
read -r -p "Default [1]: " WEB_APP_INPUT
|
||||
WEB_APP_INPUT="${WEB_APP_INPUT:-1}"
|
||||
case "$WEB_APP_INPUT" in
|
||||
1) WEBAPP="ELEMENT" ;;
|
||||
2) WEBAPP="CINNY" ;;
|
||||
3) WEBAPP="FLUFFY" ;;
|
||||
*) WEBAPP="NONE" ;;
|
||||
esac
|
||||
log_info "Web Client selected: ${WEBAPP}"
|
||||
|
||||
# Reverse Proxy (Set to a single value: TRAEFIK or NGINX)
|
||||
echo "Select Reverse Proxy: 1) Traefik (Handles TLS) 2) Nginx (Behind existing proxy)"
|
||||
read -r -p "Default [1]: " PROXY_INPUT
|
||||
PROXY_INPUT="${PROXY_INPUT:-1}"
|
||||
case "$PROXY_INPUT" in
|
||||
1) PROXY="TRAEFIK" ;;
|
||||
*) PROXY="NGINX" ;;
|
||||
esac
|
||||
log_info "Proxy selected: ${PROXY}"
|
||||
|
||||
# Worker Count
|
||||
read -r -p "Number of Synapse Workers? [Default: 1]: " WORKER_INPUT
|
||||
WORKER_INPUT="${WORKER_INPUT:-1}"
|
||||
if ! [[ "$WORKER_INPUT" =~ ^[0-9]+$ ]]; then
|
||||
log_error "Invalid number. Defaulting to 1."
|
||||
WORKER_INPUT=1
|
||||
fi
|
||||
|
||||
# 3. Environment Variables
|
||||
echo ""
|
||||
read -r -p "Auto-generate secure passwords and defaults? [Y/n]: " AUTO_GEN_INPUT
|
||||
AUTO_GEN_INPUT="${AUTO_GEN_INPUT:-Y}"
|
||||
|
||||
# Initialize .env
|
||||
> "$ENV_FILE"
|
||||
|
||||
# --- Generate Secrets & Defaults ---
|
||||
if [[ "$AUTO_GEN_INPUT" =~ ^[Yy]$ ]]; then
|
||||
log_info "Generating secure defaults..."
|
||||
auto_var "VOLUME_PATH" "./matrix" "no"
|
||||
auto_var "DOMAIN" "${BASE_DOMAIN}" "no"
|
||||
auto_var "HOMESERVER_FQDN" "matrix.${BASE_DOMAIN}" "no"
|
||||
auto_var "MAS_FQDN" "auth.${BASE_DOMAIN}" "no"
|
||||
if [[ "$WEBAPP" != "NONE" ]]; then
|
||||
auto_var "APP_FQDN" "app.${BASE_DOMAIN}" "no"
|
||||
fi
|
||||
auto_var "USER_ID" "1000" "no"
|
||||
auto_var "GROUP_ID" "1000" "no"
|
||||
auto_var "COUNTRY" "GB" "no"
|
||||
auto_var "PG_USER" "postgres" "no"
|
||||
auto_var "PG_PASSWORD" "generated" "yes"
|
||||
auto_var "PG_USER_MATRIX" "matrix" "no"
|
||||
auto_var "PG_DB_MATRIX" "synapse" "no"
|
||||
auto_var "PG_PASSWORD_MATRIX" "generated" "yes"
|
||||
auto_var "PG_USER_MAS" "mas" "no"
|
||||
auto_var "PG_DB_MAS" "auth" "no"
|
||||
auto_var "PG_PASSWORD_MAS" "generated" "yes"
|
||||
# Generate LiveKit keys if enabled
|
||||
if [[ "$INCLUDE_LIVEKIT" == "true" ]]; then
|
||||
echo "LIVEKIT_API_KEY=\"$(openssl rand -hex 16)\"" >> "$ENV_FILE"
|
||||
echo "LIVEKIT_API_SECRET=\"$(openssl rand -hex 32)\"" >> "$ENV_FILE"
|
||||
echo "LIVEKIT_FQDN=\"rtc.${BASE_DOMAIN}\"" >> "$ENV_FILE"
|
||||
echo "JWT_FQDN=\"jwt.${BASE_DOMAIN}\"" >> "$ENV_FILE"
|
||||
echo "LIVEKIT_UDP_RANGE=\"10000-20000\"" >> "$ENV_FILE"
|
||||
fi
|
||||
else
|
||||
log_info "No .env found. Running initial setup..."
|
||||
"${SCRIPT_DIR}/init/scripts/set_env.sh"
|
||||
log_info "Manual entry mode."
|
||||
prompt_var "VOLUME_PATH" "Install directory" "./matrix" "no"
|
||||
auto_var "DOMAIN" "${BASE_DOMAIN}" "no"
|
||||
prompt_var "HOMESERVER_FQDN" "Homeserver SubDomain" "matrix" "no"
|
||||
prompt_var "MAS_FQDN" "authentication SubDomain" "auth" "no"
|
||||
if [[ "$WEBAPP" != "NONE" ]]; then
|
||||
prompt_var "APP_FQDN" "Web App SubDomain" "app" "no"
|
||||
fi
|
||||
prompt_var "USER_ID" "Docker UserID" "1000" "no"
|
||||
prompt_var "GROUP_ID" "Docker GroupID" "1000" "no"
|
||||
prompt_var "COUNTRY" "Country Code (e.g. GB)" "GB" "no"
|
||||
prompt_var "PG_USER" "PostgreSQL Superuser" "postgres" "no"
|
||||
prompt_var "PG_PASSWORD" "PostgreSQL Superuser Password" "generated" "yes"
|
||||
prompt_var "PG_USER_MATRIX" "Synapse Database User" "matrix" "no"
|
||||
prompt_var "PG_DB_MATRIX" "Synapse Database Name" "synapse" "no"
|
||||
prompt_var "PG_PASSWORD_MATRIX" "Synapse Database Password" "generated" "yes"
|
||||
prompt_var "PG_USER_MAS" "MAS Database User" "mas" "no"
|
||||
prompt_var "PG_DB_MAS" "MAS Database Name" "auth" "no"
|
||||
prompt_var "PG_PASSWORD_MAS" "MAS Database Password" "generated" "yes"
|
||||
fi
|
||||
|
||||
# 2. Load Environment & Resolve Paths
|
||||
source "$ENV_FILE"
|
||||
resolve_volume_path
|
||||
auto_var "WORKER_COUNT" "${WORKER_INPUT}" "no"
|
||||
auto_var "INCLUDE_LIVEKIT" "${INCLUDE_LIVEKIT}" "no"
|
||||
auto_var "WEBAPP" "${WEBAPP}" "no"
|
||||
auto_var "PROXY" "${PROXY}" "no"
|
||||
|
||||
# 3. Directory & Permission Management
|
||||
if [[ ! -d "$VOLUME_PATH" ]]; then
|
||||
log_info "Creating data directory: $VOLUME_PATH"
|
||||
mkdir -p "$VOLUME_PATH"
|
||||
fi
|
||||
|
||||
fix_ownership "$VOLUME_PATH"
|
||||
chmod 600 "$ENV_FILE"
|
||||
log_info "Configuration saved to ${ENV_FILE}"
|
||||
|
||||
# 4. Stack Generation
|
||||
log_info "Generating compose and configurations..."
|
||||
# 4. Execution Phase
|
||||
echo ""
|
||||
log_info "=== Executing Deployment Plan ==="
|
||||
|
||||
log_info "1. Generating Compose File..."
|
||||
"${SCRIPT_DIR}/init/scripts/compose_gen.sh"
|
||||
|
||||
log_info "2. Initializing Database & Configs..."
|
||||
"${SCRIPT_DIR}/init/scripts/config_gen.sh"
|
||||
|
||||
log_info "3. Linking Services (OIDC)..."
|
||||
"${SCRIPT_DIR}/init/scripts/config_link.sh"
|
||||
|
||||
# 5. Worker Generation
|
||||
read -r -p "Number of worker processes? [Default: 1]: " worker_count
|
||||
worker_count="${worker_count:-1}"
|
||||
|
||||
if ! [[ "$worker_count" =~ ^[0-9]+$ ]]; then
|
||||
log_error "Invalid number. Aborting worker generation."
|
||||
exit 1
|
||||
if [[ ${WORKER_INPUT} -gt 1 ]]; then
|
||||
log_info "4. Generating Workers (${WORKER_INPUT})..."
|
||||
"${SCRIPT_DIR}/init/scripts/worker_gen.sh" -c "$WORKER_INPUT"
|
||||
fi
|
||||
|
||||
if [[ "$worker_count" -gt 0 ]]; then
|
||||
log_info "Generating $worker_count worker(s)..."
|
||||
"${SCRIPT_DIR}/init/scripts/worker_gen.sh" -c "$worker_count"
|
||||
else
|
||||
log_info "Skipping worker generation (count: 0)."
|
||||
fi
|
||||
|
||||
log_info "Setup complete. Run 'docker compose up -d' to start services."
|
||||
log_info "=== Setup Complete ==="
|
||||
log_info "Run 'docker compose up -d' to start your stack."
|
||||
|
||||
Reference in New Issue
Block a user