Initial Commit
This commit is contained in:
@@ -219,8 +219,8 @@ If you develop a new program, and you want it to be of the greatest possible use
|
||||
|
||||
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
ammids
|
||||
Copyright (C) 2026 Main
|
||||
Ammids
|
||||
Copyright (C) 2026 cortex
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
|
||||
|
||||
@@ -1,2 +1,186 @@
|
||||
# ammids
|
||||
# AMMIDS
|
||||
**Automated Modular Matrix Infrastructure Deployment Script**
|
||||
|
||||
> **Status:** Beta / Experimental
|
||||
> **Target OS:** Linux (Ubuntu/Debian recommended)
|
||||
> **License:** AGPL-3.0
|
||||
> **Maintainer:** Cortex@Fossgate
|
||||
> **Source:** [https://git.fossgate.uk/ammids](https://git.fossgate.uk/ammids)
|
||||
|
||||
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.
|
||||
|
||||
## ⚠️ Important Prerequisites
|
||||
|
||||
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.
|
||||
|
||||
### Required Dependencies
|
||||
|
||||
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. Clone the Repository
|
||||
```
|
||||
git clone https://git.fossgate.uk/ammids.git
|
||||
cd ammids
|
||||
```
|
||||
### 2. Install Dependencies
|
||||
Ensure `docker`, `docker-compose`, and `yq` are installed (see **Prerequisites** above).
|
||||
|
||||
### 3. Run the Orchestrator
|
||||
Execute the main setup script. This will guide you through configuration and generate all necessary files.
|
||||
|
||||
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.
|
||||
|
||||
### 4. 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/set_compose.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`.
|
||||
|
||||
## 🛡️ Security Notes
|
||||
|
||||
* **Secrets Management:** The `.env` file contains database passwords and API keys. It is set to `chmod 600` automatically. **Do not commit this file to Git.**
|
||||
* **Firewall:** Ensure ports `80` and `443` are open for the proxy, and `8448` for Matrix federation.
|
||||
* **Backups:** Regularly back up the `matrix/` directory and your `.env` file. Losing `.env` means losing access to your database credentials.
|
||||
* **Permissions:** The scripts attempt to fix directory ownership automatically. If you encounter permission errors, ensure your user has `sudo` privileges.
|
||||
|
||||
## 🐛 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 `link_synapse.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/ammids/issues)
|
||||
* **Feature Requests:** [Discussions](https://git.fossgate.uk/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.
|
||||
|
||||
---
|
||||
*AMMIDS is provided "as is", without warranty of any kind. Use at your own risk.*
|
||||
@@ -0,0 +1,19 @@
|
||||
version: 1
|
||||
formatters:
|
||||
precise:
|
||||
format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(message)s'
|
||||
handlers:
|
||||
file:
|
||||
class: logging.handlers.RotatingFileHandler
|
||||
filename: /var/log/synapse/{{WORKER_NAME}}.log
|
||||
maxBytes: {{MAX_BYTES}}
|
||||
backupCount: {{BACKUP_COUNT}}
|
||||
formatter: precise
|
||||
root:
|
||||
level: {{LOG_LEVEL}}
|
||||
handlers: [file]
|
||||
loggers:
|
||||
synapse:
|
||||
level: {{SYNAPSE_LOG_LEVEL}}
|
||||
synapse.storage:
|
||||
level: WARNING
|
||||
@@ -0,0 +1,45 @@
|
||||
# Auto-generated config for {{WORKER_NAME}}
|
||||
# Generated by generate-workers.sh
|
||||
|
||||
worker_app: synapse.app.generic_worker
|
||||
|
||||
instance_map:
|
||||
master:
|
||||
host: synapse
|
||||
port: 9090
|
||||
{{WORKER_NAME}}:
|
||||
host: {{WORKER_NAME}}
|
||||
port: {{PORT}}
|
||||
|
||||
worker_name: {{WORKER_NAME}}
|
||||
worker_listeners:
|
||||
- port: {{PORT}}
|
||||
bind_addresses: ['::']
|
||||
type: http
|
||||
resources:
|
||||
- names: [replication]
|
||||
|
||||
worker_replication_host: synapse
|
||||
worker_replication_http_port: {{PORT}}
|
||||
|
||||
server_name: {{SERVER_NAME}}
|
||||
|
||||
database:
|
||||
name: psycopg2
|
||||
args:
|
||||
user: {{DB_USER}}
|
||||
password: {{DB_PASSWORD}}
|
||||
database: {{DB_NAME}}
|
||||
host: db
|
||||
cp_min: 5
|
||||
cp_max: 10
|
||||
|
||||
redis:
|
||||
enabled: true
|
||||
host: redis
|
||||
port: 6379
|
||||
|
||||
log_config: "/data/workers/{{WORKER_NAME}}.log.config"
|
||||
worker_daemonize: true
|
||||
worker_pid_file: "/var/run/synapse/{{WORKER_NAME}}.pid"
|
||||
metrics_enabled: true
|
||||
Executable
+67
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/functions.sh"
|
||||
resolve_paths
|
||||
|
||||
TEMPLATE_FILE="${INIT_DIR}/templates/compose.yaml.template"
|
||||
OUTPUT_FILE="${ROOT_DIR}/compose.yaml"
|
||||
|
||||
require_file "$TEMPLATE_FILE" "Compose template"
|
||||
require_env
|
||||
|
||||
log_info "Configuring optional services..."
|
||||
|
||||
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
|
||||
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
|
||||
else
|
||||
INCLUDE_LIVEKIT=false
|
||||
fi
|
||||
|
||||
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 ;;
|
||||
2) INCLUDE_ELEMENT=false; INCLUDE_CINNY=true; INCLUDE_FLUFFY=false ;;
|
||||
3) INCLUDE_ELEMENT=false; INCLUDE_CINNY=false; INCLUDE_FLUFFY=true ;;
|
||||
*) INCLUDE_ELEMENT=false; INCLUDE_CINNY=false; INCLUDE_FLUFFY=false ;;
|
||||
esac
|
||||
|
||||
echo "Select Reverse Proxy: 1) Traefik 2) nginx"
|
||||
read -r -p "Default [1]: " PROXY_INPUT
|
||||
PROXY_INPUT="${PROXY_INPUT:-1}"
|
||||
case "$PROXY_INPUT" in
|
||||
1) INCLUDE_NGINX=false; INCLUDE_TRAEFIK=true ;;
|
||||
*) INCLUDE_NGINX=true; INCLUDE_TRAEFIK=false ;;
|
||||
esac
|
||||
|
||||
cp "$TEMPLATE_FILE" "$OUTPUT_FILE"
|
||||
|
||||
toggle_block() {
|
||||
[[ "$3" == "true" ]] && sed -i "/$1/d; /$2/d" "$OUTPUT_FILE" || sed -i "/$1/,/$2/d" "$OUTPUT_FILE"
|
||||
}
|
||||
|
||||
toggle_block "# \[LIVEKIT_START\]" "# \[LIVEKIT_END\]" "$INCLUDE_LIVEKIT"
|
||||
toggle_block "# \[ELEMENT_START\]" "# \[ELEMENT_END\]" "$INCLUDE_ELEMENT"
|
||||
toggle_block "# \[CINNY_START\]" "# \[CINNY_END\]" "$INCLUDE_CINNY"
|
||||
toggle_block "# \[FLUFFYCHAT_START\]" "# \[FLUFFYCHAT_END\]" "$INCLUDE_FLUFFY"
|
||||
toggle_block "# \[NGINX_START\]" "# \[NGINX_END\]" "$INCLUDE_NGINX"
|
||||
toggle_block "# \[TRAEFIK_START\]" "# \[TRAEFIK_END\]" "$INCLUDE_TRAEFIK"
|
||||
|
||||
[[ "$INCLUDE_TRAEFIK" == "true" ]] && sed -i "/# \[TRAEFIK_LABELS_START\]/d; /# \[TRAEFIK_LABELS_END\]/d" "$OUTPUT_FILE" || \
|
||||
sed -i "/# \[TRAEFIK_LABELS_START\]/,/# \[TRAEFIK_LABELS_END\]/d" "$OUTPUT_FILE"
|
||||
|
||||
log_info "Compose file generated: $OUTPUT_FILE"
|
||||
Executable
+63
@@ -0,0 +1,63 @@
|
||||
#!/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_docker
|
||||
resolve_volume_path
|
||||
|
||||
mkdir -p "${VOLUME_PATH}/synapse" "${VOLUME_PATH}/mas" "${VOLUME_PATH}/postgres"
|
||||
|
||||
log_info "Initializing PostgreSQL..."
|
||||
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d --wait --timeout 60 matrix-db
|
||||
|
||||
# Wait for database readiness (silent except on failure)
|
||||
for ATTEMPT in $(seq 1 30); 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
|
||||
[[ $ATTEMPT -eq 30 ]] && { log_error "Database timeout"; exit 1; }
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# Database initialization SQL
|
||||
INIT_SQL=$(cat <<EOF
|
||||
DO \$\$ BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'mas') THEN
|
||||
CREATE ROLE mas WITH LOGIN PASSWORD '${PG_PASSWORD_MAS}';
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'matrix') THEN
|
||||
CREATE ROLE matrix WITH LOGIN PASSWORD '${PG_PASSWORD_MATRIX}';
|
||||
END IF;
|
||||
END \$\$;
|
||||
|
||||
SELECT CASE WHEN NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'auth')
|
||||
THEN format('CREATE DATABASE %I OWNER %I', 'auth', 'mas') ELSE NULL END AS cmd \gexec
|
||||
|
||||
SELECT CASE WHEN NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'synapse')
|
||||
THEN format('CREATE DATABASE %I OWNER %I', 'synapse', 'matrix') ELSE NULL END AS cmd \gexec
|
||||
EOF
|
||||
)
|
||||
|
||||
echo "$INIT_SQL" | docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" exec -T matrix-db psql -U "$PG_USER" -f -
|
||||
|
||||
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
|
||||
|
||||
fix_ownership "${VOLUME_PATH}/synapse"
|
||||
|
||||
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"
|
||||
|
||||
fix_ownership "${VOLUME_PATH}/mas"
|
||||
log_info "Configuration complete"
|
||||
Executable
+54
@@ -0,0 +1,54 @@
|
||||
#!/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"
|
||||
@@ -0,0 +1,129 @@
|
||||
#!/usr/bin/env bash
|
||||
# AMMIDS Shared Functions Library
|
||||
# Location: init/scripts/functions.sh
|
||||
# Sourced by all other scripts in this directory
|
||||
|
||||
# Prevent direct execution
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
echo "This is a library, not a script. Source it instead."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Color Codes ---
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
TITLE='\033[38;2;17;97;2m'
|
||||
NC='\033[0m'
|
||||
|
||||
# --- Path Resolution ---
|
||||
# Standardised for project structure: /init/scripts/xxx.sh
|
||||
# Sets: SCRIPT_DIR, INIT_DIR, ROOT_DIR
|
||||
resolve_paths() {
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[1]}")" && pwd)"
|
||||
INIT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
ROOT_DIR="$(dirname "$INIT_DIR")"
|
||||
export SCRIPT_DIR INIT_DIR ROOT_DIR
|
||||
}
|
||||
|
||||
# --- Logging ---
|
||||
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
|
||||
# --- State Management (init.ini) ---
|
||||
write_state() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
local init_file="${INIT_DIR}/init.ini"
|
||||
local upper_value
|
||||
upper_value=$(echo "$value" | tr '[:lower:]' '[:upper:]')
|
||||
echo "${key}=${upper_value}" >> "$init_file"
|
||||
}
|
||||
|
||||
load_state() {
|
||||
local init_file="${INIT_DIR}/init.ini"
|
||||
if [[ -f "$init_file" ]]; then
|
||||
source "$init_file"
|
||||
else
|
||||
log_warn "State file not found at ${init_file}. Using defaults."
|
||||
fi
|
||||
}
|
||||
|
||||
clear_state() {
|
||||
local init_file="${INIT_DIR}/init.ini"
|
||||
> "$init_file"
|
||||
}
|
||||
|
||||
# --- Secrets ---
|
||||
generate_secret() {
|
||||
local length="${1:-32}"
|
||||
if command -v openssl &> /dev/null; then
|
||||
openssl rand -hex "$((length / 2))"
|
||||
elif command -v pwgen &> /dev/null; then
|
||||
pwgen -s "$length" 1
|
||||
else
|
||||
tr -dc 'A-Za-z0-9' < /dev/urandom | head -c "$length"
|
||||
fi
|
||||
}
|
||||
|
||||
# --- File Permissions ---
|
||||
fix_ownership() {
|
||||
local target="$1"
|
||||
local uid="${2:-${USER_ID:-1000}}"
|
||||
local gid="${3:-${GROUP_ID:-1000}}"
|
||||
|
||||
if [[ -e "$target" ]] && [[ "$(stat -c '%U' "$target" 2>/dev/null)" != "$USER" ]]; then
|
||||
log_warn "Fixing ownership on $target..."
|
||||
sudo chown -R "${uid}:${gid}" "$target"
|
||||
fi
|
||||
}
|
||||
|
||||
# --- Dependency Checks ---
|
||||
require_command() {
|
||||
local cmd="$1"
|
||||
if ! command -v "$cmd" &> /dev/null; then
|
||||
log_error "Required command '${cmd}' not found. Install it and retry."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
require_file() {
|
||||
local file="$1"
|
||||
local description="${2:-File}"
|
||||
if [[ ! -f "$file" ]]; then
|
||||
log_error "${description} not found at ${file}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
require_env() {
|
||||
local file="${ROOT_DIR}/.env"
|
||||
if [[ ! -f "$file" ]]; then
|
||||
log_error ".env file not found. Run set_env.sh first."
|
||||
exit 1
|
||||
fi
|
||||
source "$file"
|
||||
}
|
||||
|
||||
require_docker() {
|
||||
require_command "docker"
|
||||
require_command "docker compose"
|
||||
}
|
||||
|
||||
# --- Volume Path Resolution ---
|
||||
resolve_volume_path() {
|
||||
VOLUME_PATH="${VOLUME_PATH:-./matrix}"
|
||||
if [[ "$VOLUME_PATH" != /* ]]; then
|
||||
VOLUME_PATH="${ROOT_DIR}/${VOLUME_PATH}"
|
||||
fi
|
||||
export VOLUME_PATH
|
||||
}
|
||||
|
||||
# Add to functions.sh
|
||||
|
||||
|
||||
quiet_echo() {
|
||||
[[ "${QUIET_MODE:-0}" == "1" ]] || echo "$1"
|
||||
}
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/functions.sh"
|
||||
resolve_paths
|
||||
|
||||
require_command "openssl" || require_command "pwgen"
|
||||
|
||||
main() {
|
||||
> "$ENV_FILE"
|
||||
|
||||
read -r -p "Base Domain (e.g. example.org) [example.org]: " DOMAIN_INPUT
|
||||
BASE_DOMAIN="${DOMAIN_INPUT:-example.org}"
|
||||
|
||||
cat >> "$ENV_FILE" <<EOF
|
||||
DOMAIN="${BASE_DOMAIN}"
|
||||
HOMESERVER_FQDN="matrix.${BASE_DOMAIN}"
|
||||
MAS_FQDN="auth.${BASE_DOMAIN}"
|
||||
APP_FQDN="app.${BASE_DOMAIN}"
|
||||
EOF
|
||||
|
||||
log_info "Base Domain set to: ${BASE_DOMAIN}"
|
||||
|
||||
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 "$@"
|
||||
Executable
+87
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env bash
|
||||
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
|
||||
resolve_volume_path
|
||||
|
||||
TEMPLATE_COMPOSE="${INIT_DIR}/templates/worker.yaml"
|
||||
TEMPLATE_CONFIG="${INIT_DIR}/config/worker.yaml"
|
||||
TEMPLATE_LOG="${INIT_DIR}/config/worker-log.yaml"
|
||||
|
||||
require_file "$TEMPLATE_COMPOSE" "Compose template"
|
||||
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"
|
||||
|
||||
TRAEFIK_ENABLED="${PROXY:-0}"
|
||||
TEMP_COMPOSE="${ROOT_DIR}/.tmp/compose_workers.yaml"
|
||||
> "$TEMP_COMPOSE"
|
||||
|
||||
for ((i=1; i<=WORKER_COUNT; i++)); do
|
||||
worker_name="synapse-worker-${i}"
|
||||
port=$((9093 + i))
|
||||
|
||||
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" \
|
||||
"$TEMPLATE_CONFIG" > "${WORKERS_DIR}/${worker_name}.yaml"
|
||||
|
||||
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" \
|
||||
-e "s/{{LOG_LEVEL}}/${LOG_LEVEL:-INFO}/g" \
|
||||
-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
|
||||
|
||||
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"
|
||||
|
||||
echo "" >> "$TEMP_COMPOSE"
|
||||
done
|
||||
|
||||
if [[ -f "$COMPOSE_FILE" ]]; then
|
||||
if ! grep -qE "^ synapse-worker-[0-9]+:" "$COMPOSE_FILE"; then
|
||||
cat "$TEMP_COMPOSE" >> "$COMPOSE_FILE"
|
||||
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"
|
||||
fi
|
||||
else
|
||||
log_warn "compose.yaml not found. Generated: ${TEMP_COMPOSE}"
|
||||
fi
|
||||
|
||||
rm -f "$TEMP_COMPOSE"
|
||||
log_info "Worker generation complete"
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"default_server_config": {
|
||||
"m.homeserver": {
|
||||
"base_url": "https://${HOMESERVER_FQDN}",
|
||||
"server_name": "${HOMESERVER_FQDN}"
|
||||
}
|
||||
},
|
||||
"default_server_name": "${HOMESERVER_FQDN}",
|
||||
"brand": "Element"
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
# COMPOSE TEMPLATE - DO NOT EDIT MANUALLY
|
||||
# Generated by set_compose.sh
|
||||
|
||||
networks:
|
||||
matrix_network:
|
||||
name: matrix_network
|
||||
|
||||
services:
|
||||
# --- CORE SERVICES (Always Included) ---
|
||||
matrix-db:
|
||||
image: postgres:latest
|
||||
container_name: "matrix-db"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ${VOLUME_PATH}/postgres:/var/lib/postgresql:rw
|
||||
networks:
|
||||
- matrix_network
|
||||
environment:
|
||||
POSTGRES_USER: ${PG_USER}
|
||||
POSTGRES_PASSWORD: ${PG_PASSWORD}
|
||||
POSTGRES_INITDB_ARGS: --encoding=UTF8 --locale=C
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U '${PG_USER}'"]
|
||||
start_period: "1s"
|
||||
interval: "1s"
|
||||
timeout: "5s"
|
||||
|
||||
redis:
|
||||
image: redis:latest
|
||||
container_name: "matrix-redis"
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- matrix_network
|
||||
command: redis-server --appendonly yes
|
||||
|
||||
synapse:
|
||||
image: ghcr.io/element-hq/synapse:latest
|
||||
container_name: "synapse"
|
||||
user: ${USER_ID}:${GROUP_ID}
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ${VOLUME_PATH}/synapse:/data:rw
|
||||
networks:
|
||||
- matrix_network
|
||||
environment:
|
||||
SYNAPSE_CONFIG_DIR: /data
|
||||
SYNAPSE_CONFIG_PATH: /data/homeserver.yaml
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_started
|
||||
matrix-db:
|
||||
condition: service_healthy
|
||||
# [TRAEFIK_LABELS_START]
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.synapse.rule=Host(`${HOMESERVER_FQDN}`)"
|
||||
- "traefik.http.routers.synapse.entrypoints=websecure"
|
||||
- "traefik.http.routers.synapse.tls.certresolver=myresolver"
|
||||
- "traefik.http.services.synapse.loadbalancer.server.port=8008"
|
||||
# [TRAEFIK_LABELS_END]
|
||||
|
||||
mas:
|
||||
image: ghcr.io/element-hq/matrix-authentication-service:latest
|
||||
container_name: "mas"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ${VOLUME_PATH}/mas:/data:rw
|
||||
networks:
|
||||
- matrix_network
|
||||
command: "server --config=/data/config.yaml"
|
||||
depends_on:
|
||||
matrix-db:
|
||||
condition: service_healthy
|
||||
# [TRAEFIK_LABELS_START]
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.auth.rule=Host(`${MAS_FQDN}`)"
|
||||
- "traefik.http.routers.auth.entrypoints=websecure"
|
||||
- "traefik.http.routers.auth.tls.certresolver=myresolver"
|
||||
- "traefik.http.services.auth.loadbalancer.server.port=8000"
|
||||
# [TRAEFIK_LABELS_END]
|
||||
|
||||
# --- OPTIONAL: LIVEKIT RTC STACK ---
|
||||
# [LIVEKIT_START]
|
||||
livekit:
|
||||
image: livekit/livekit-server:latest
|
||||
container_name: "livekit"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ${VOLUME_PATH}/livekit:/etc/livekit
|
||||
environment:
|
||||
- LIVEKIT_KEYS=access-key=${LIVEKIT_API_KEY},secret=${LIVEKIT_API_SECRET}
|
||||
- LIVEKIT_PORT=7880
|
||||
- LIVEKIT_WS_PORT=7881
|
||||
- LIVEKIT_SERVER_URL=https://${LIVEKIT_FQDN}
|
||||
networks:
|
||||
- matrix_network
|
||||
depends_on:
|
||||
- redis
|
||||
- matrix-db
|
||||
ports:
|
||||
- "${LIVEKIT_UDP_RANGE}:${LIVEKIT_UDP_RANGE}/udp"
|
||||
# [TRAEFIK_LABELS_START]
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.rtc.rule=Host(`${LIVEKIT_FQDN}`)"
|
||||
- "traefik.http.routers.rtc.entrypoints=websecure"
|
||||
- "traefik.http.routers.rtc.tls.certresolver=myresolver"
|
||||
- "traefik.http.services.rtc.loadbalancer.server.port=7880"
|
||||
# [TRAEFIK_LABELS_END]
|
||||
|
||||
# The JWT Bridge Service
|
||||
lk-jwt-service:
|
||||
image: ghcr.io/element-hq/lk-jwt-service:latest
|
||||
container_name: "lk-jwt-service"
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- LK_JWT_PORT=8080
|
||||
- LIVEKIT_URL=wss://${LIVEKIT_FQDN}
|
||||
- LIVEKIT_KEY=${LIVEKIT_API_KEY}
|
||||
- LIVEKIT_SECRET=${LIVEKIT_API_SECRET}
|
||||
- LIVEKIT_LOCAL_HOMESERVERS=${HOMESERVER_FQDN}
|
||||
networks:
|
||||
- matrix_network
|
||||
depends_on:
|
||||
- livekit
|
||||
# [TRAEFIK_LABELS_START]
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.jwt.rule=Host(`${JWT_FQDN}`)"
|
||||
- "traefik.http.routers.jwt.entrypoints=websecure"
|
||||
- "traefik.http.routers.jwt.tls.certresolver=myresolver"
|
||||
- "traefik.http.services.jwt.loadbalancer.server.port=8080"
|
||||
# [TRAEFIK_LABELS_END]
|
||||
# [LIVEKIT_END]
|
||||
|
||||
# --- OPTIONAL: WEB HOSTED APP ---
|
||||
# [ELEMENT_START]
|
||||
element-web:
|
||||
image: vectorim/element-web:latest
|
||||
container_name: "matrix-web-client"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ${VOLUME_PATH}/element-web:/usr/share/nginx/html:ro
|
||||
networks:
|
||||
- matrix_network
|
||||
environment:
|
||||
- ELEMENT_WEB_CONFIG_PATH=/usr/share/nginx/html/config.json
|
||||
# [TRAEFIK_LABELS_START]
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.app.rule=Host(`${APP_FQDN}`)"
|
||||
- "traefik.http.routers.app.entrypoints=websecure"
|
||||
- "traefik.http.routers.app.tls.certresolver=myresolver"
|
||||
- "traefik.http.services.app.loadbalancer.server.port=80"
|
||||
# [TRAEFIK_LABELS_END]
|
||||
# [ELEMENT_END]
|
||||
|
||||
# [CINNY_START]
|
||||
cinny:
|
||||
image: cinny-web:latest
|
||||
container_name: "matrix-web-client"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ${VOLUME_PATH}/cinny:/usr/share/nginx/html:ro
|
||||
networks:
|
||||
- matrix_network
|
||||
# [TRAEFIK_LABELS_START]
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.app.rule=Host(`${APP_FQDN}`)"
|
||||
- "traefik.http.routers.app.entrypoints=websecure"
|
||||
- "traefik.http.routers.app.tls.certresolver=myresolver"
|
||||
- "traefik.http.services.app.loadbalancer.server.port=80"
|
||||
# [TRAEFIK_LABELS_END]
|
||||
# [CINNY_END]
|
||||
|
||||
# [FLUFFYCHAT_START]
|
||||
fluffy-chat:
|
||||
image: kibaek/fluffy-chat:latest
|
||||
container_name: "matrix-web-client"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ${VOLUME_PATH}/fluffy-chat:/usr/share/nginx/html:ro
|
||||
networks:
|
||||
- matrix_network
|
||||
# [TRAEFIK_LABELS_START]
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.app.rule=Host(`${APP_FQDN}`)"
|
||||
- "traefik.http.routers.app.entrypoints=websecure"
|
||||
- "traefik.http.routers.app.tls.certresolver=myresolver"
|
||||
- "traefik.http.services.app.loadbalancer.server.port=80"
|
||||
# [TRAEFIK_LABELS_END]
|
||||
# [FLUFFYCHAT_END]
|
||||
|
||||
# --- OPTIONAL: REVERSE PROXY ---
|
||||
# [NGINX_START]
|
||||
nginx:
|
||||
image: nginx:latest
|
||||
container_name: "matrix-nginx"
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
stop_grace_period: 0s
|
||||
volumes:
|
||||
- ${VOLUME_PATH}/nginx/conf.d:/etc/nginx/conf.d:ro
|
||||
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
|
||||
networks:
|
||||
matrix_network:
|
||||
aliases:
|
||||
- ${DOMAIN}
|
||||
- ${HOMESERVER_FQDN}
|
||||
- ${MAS_FQDN}
|
||||
- ${APP_FQDN}
|
||||
# [NGINX_END]
|
||||
|
||||
# [TRAEFIK_START]
|
||||
traefik:
|
||||
image: traefik:latest
|
||||
container_name: "traefik"
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- ${VOLUME_PATH}/traefik:/etc/traefik
|
||||
command:
|
||||
- "--api.insecure=true"
|
||||
- "--providers.docker=true"
|
||||
- "--providers.docker.exposedbydefault=false"
|
||||
- "--entrypoints.web.address=:80"
|
||||
- "--entrypoints.websecure.address=:443"
|
||||
- "--certificatesresolvers.myresolver.acme.tlschallenge=true"
|
||||
- "--certificatesresolvers.myresolver.acme.email=${PG_USER}@${DOMAIN}"
|
||||
- "--certificatesresolvers.myresolver.acme.storage=/etc/traefik/acme.json"
|
||||
networks:
|
||||
- matrix_network
|
||||
# [TRAEFIK_END]
|
||||
@@ -0,0 +1,20 @@
|
||||
{{WORKER_NAME}}:
|
||||
image: ghcr.io/element-hq/synapse:latest
|
||||
container_name: "{{WORKER_NAME}}"
|
||||
user: ${USER_ID}:${GROUP_ID}
|
||||
restart: unless-stopped
|
||||
entrypoint: ["/start.py", "run", "--config-path=/data/homeserver.yaml", "--config-path=/data/workers/{{WORKER_NAME}}.yaml"]
|
||||
networks:
|
||||
- matrix_network
|
||||
volumes:
|
||||
- ${VOLUME_PATH}/synapse:/data:rw
|
||||
- ${VOLUME_PATH}/synapse/logs:/var/log/synapse:rw
|
||||
environment:
|
||||
SYNAPSE_WORKER: {{APP_TYPE}}
|
||||
depends_on:
|
||||
- synapse
|
||||
- db
|
||||
- redis
|
||||
ports:
|
||||
- "{{PORT}}:{{PORT}}"
|
||||
{{TRAEFIK_LABELS}}
|
||||
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env bash
|
||||
# AMMIDS Setup Orchestrator
|
||||
# Author: Cortex@Fossgate
|
||||
# Src: https://git.fossgate.uk/ammids
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/init/scripts/functions.sh"
|
||||
resolve_paths
|
||||
|
||||
ENV_FILE="${ROOT_DIR}/.env"
|
||||
BACKUP_DIR="${ROOT_DIR}/backups"
|
||||
|
||||
log_info "=== AMMIDS Setup Orchestrator ==="
|
||||
|
||||
# 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="${BACKUP_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."
|
||||
fi
|
||||
else
|
||||
log_info "No .env found. Running initial setup..."
|
||||
"${SCRIPT_DIR}/init/scripts/set_env.sh"
|
||||
fi
|
||||
|
||||
# 2. Load Environment & Resolve Paths
|
||||
source "$ENV_FILE"
|
||||
resolve_volume_path
|
||||
|
||||
# 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"
|
||||
|
||||
# 4. Stack Generation
|
||||
log_info "Generating compose and configurations..."
|
||||
"${SCRIPT_DIR}/init/scripts/compose_gen.sh"
|
||||
"${SCRIPT_DIR}/init/scripts/config_gen.sh"
|
||||
"${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
|
||||
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."
|
||||
Reference in New Issue
Block a user