diff --git a/.env.sample b/.env.sample index 166f851..c8356a8 100644 --- a/.env.sample +++ b/.env.sample @@ -1,8 +1,34 @@ +# Core Settings TYPE=revoltchat - DOMAIN=revoltchat.example.com - -## Domain aliases -#EXTRA_DOMAINS=', `www.revoltchat.example.com`' - LETS_ENCRYPT_ENV=production + +# Secrets +SECRET_FILE_ENCRYPT_TOKEN_VERSION=v1 +SECRET_PUBLIC_KEY_VERSION=v1 +SECRET_PRIVATE_KEY_VERSION=v1 +SECRET_RABBIT_PASSWORD_VERSION=v1 +SECRET_SMTP_PASSWORD_VERSION=v1 + +# Registration Settings +INVITE_ONLY_ENABLED=false + +# Email Verification +EMAIL_VERIFICATION_ENABLED=false +SMTP_HOST=smtp.example.com +SMTP_USERNAME=username +SMTP_FROM_ADDRESS=noreply@example.com +SMTP_REPLY_TO=support@example.com +SMTP_PORT=587 +SMTP_USE_TLS=true + +# Security +SECURITY_CAPTCHA_ENABLED=false +SECURITY_CLAMV_HOST=clamav + +# Notifications +NOTIFICATIONS_WEBOOKS_ENABLED=true +NOTIFICATIONS_MASS_MENTIONS_PUSH_NOTIF=true +NOTIFICATIONS_MASS_MENTIONS_ENABLED=true +NOTIFICATIONS_FCM_ENABLED=false +NOTIFICATIONS_APN_ENABLED=false \ No newline at end of file diff --git a/.env.web b/.env.web new file mode 100644 index 0000000..ec78450 --- /dev/null +++ b/.env.web @@ -0,0 +1,2 @@ +HOSTNAME=https://example.com +REVOLT_PUBLIC_URL=https://example.com/api diff --git a/Revolt.toml.tmpl b/Revolt.toml.tmpl new file mode 100644 index 0000000..da1bc0f --- /dev/null +++ b/Revolt.toml.tmpl @@ -0,0 +1,170 @@ +[hosts] +app = "https://{{ env "DOMAIN" }}" +api = "https://{{ env "DOMAIN" }}/api" +events = "wss://{{ env "DOMAIN" }}/ws" +autumn = "https://{{ env "DOMAIN" }}/autumn" +january = "https://{{ env "DOMAIN" }}/january" + +[database] +mongodb = "mongodb://database" +redis = "redis://redis/" + +[rabbit] +host = "rabbit" +port = 5672 +username = "rabbituser" +password = "{{ secret "rabbit_password" }}" + +[api] + +[api.registration] +invite_only = {{ env "INVITE_ONLY_ENABLED" }} + +{{ if eq (env "EMAIL_VERIFICATION_ENABLED") "true" }} +[api.smtp] +host = "{{ env "SMTP_HOST" }}" +username = "{{ env "SMTP_USERNAME" }}" +password = "{{ secret "smtp_password" }}" +from_address = "{{ env "SMTP_FROM_ADDRESS" }}" +reply_to = "{{ env "SMTP_REPLY_TO" }}" +port = {{ env "SMTP_PORT" }} +use_tls = {{ env "SMTP_USE_TLS" }} +{{ end }} + +[api.security] +authifier_shield_key = "" +voso_legacy_token = "" +trust_cloudflare = false + +{{ if eq "SECURITY_CAPTCHA_ENABLED" "true"}} +[api.security.captcha] +hcaptcha_key = "{{ }}" +hcaptcha_sitekey = "" +{{ end }} + +[api.workers] +# Maximum concurrent connections (to proxy server) +max_concurrent_connections = 50 + +[api.users] + + +[pushd] +production = true +mass_mention_chunk_size = 200 +exchange = "revolt.notifications" +message_queue = "notifications.origin.message" +mass_mention_queue = "notifications.origin.mass_mention" +fr_accepted_queue = "notifications.ingest.fr_accepted" +fr_received_queue = "notifications.ingest.fr_received" +generic_queue = "notifications.ingest.generic" +ack_queue = "notifications.process.ack" + + +[pushd.vapid] +private_key = "{{ secret "private_key" }}" +public_key = "{{ secret "public_key" }}" + +{{ if eq "NOTIFICATIONS_FCM_ENABLED" "true"}} +[pushd.fcm] +queue = "notifications.outbound.fcm" +key_type = "" +project_id = "" +private_key_id = "" +private_key = "" +client_email = "" +client_id = "" +auth_uri = "" +token_uri = "" +auth_provider_x509_cert_url = "" +client_x509_cert_url = "" +{{ end }} + +{{ if eq "NOTIFICATIONS_APN_ENABLED" "true"}} +[pushd.apn] +sandbox = false +queue = "notifications.outbound.apn" +pkcs8 = "" +key_id = "" +team_id = "" +{{ end }} + +[files] +webp_quality = 80.0 +blocked_mime_types = ["application/vnd.microsoft.portable-executable", "application/vnd.android.package-archive"] +clamd_host = "{{ env "SECURITY_CLAMV_HOST" }}" +encryption_key = "{{ secret "file_encrypt_token" }}" +production = true + +[files.limit] +min_file_size = 1 +min_resolution = [1, 1] +max_mega_pixels = 40 +max_pixel_side = 10_000 + +[files.preview] +attachments = [1280, 1280] +avatars = [128, 128] +backgrounds = [1280, 720] +icons = [128, 128] +banners = [480, 480] +emojis = [128, 128] + +[files.s3] +endpoint = "http://minio:9000" +path_style_buckets = false +region = "minio" +access_key_id = "minioautumn" +secret_access_key = "minioautumn" +default_bucket = "revolt-uploads" + +[features] +webhooks_enabled = {{ env "NOTIFICATIONS_WEBOOKS_ENABLED" }} +mass_mentions_send_notifications = {{ env "NOTIFICATIONS_MASS_MENTIONS_PUSH_NOTIF" }} +mass_mentions_enabled = {{ env "NOTIFICATIONS_MASS_MENTIONS_ENABLED" }} + +[features.limits] + +[features.limits.global] +group_size = 100 +message_embeds = 5 +message_replies = 5 +message_reactions = 20 +server_emoji = 100 +server_roles = 200 +server_channels = 200 +new_user_hours = 72 +body_limit_size = 20_000_000 + +[features.limits.new_user] +outgoing_friend_requests = 5 +bots = 2 +message_length = 2000 +message_attachments = 5 +servers = 50 + +[features.limits.new_user.file_upload_size_limit] +attachments = 20_000_000 +avatars = 4_000_000 +backgrounds = 6_000_000 +icons = 2_500_000 +banners = 6_000_000 +emojis = 500_000 + +[features.limits.default] +outgoing_friend_requests = 10 +bots = 5 +message_length = 2000 +message_attachments = 5 +servers = 100 + +[features.limits.default.file_upload_size_limit] +attachments = 20_000_000 +avatars = 4_000_000 +backgrounds = 6_000_000 +icons = 2_500_000 +banners = 6_000_000 +emojis = 500_000 + +[features.advanced] +process_message_delay_limit = 5 diff --git a/abra.sh b/abra.sh new file mode 100644 index 0000000..e69de29 diff --git a/compose.yml b/compose.yml index a2c3805..5ef92b9 100644 --- a/compose.yml +++ b/compose.yml @@ -2,31 +2,187 @@ version: "3.8" services: + app: - image: nginx:1.20.0 - networks: - - proxy + image: ghcr.io/revoltchat/server:20250210-1 + depends_on: + database: + condition: service_healthy + redis: + condition: service_started + rabbit: + condition: service_healthy deploy: restart_policy: condition: on-failure labels: - "traefik.enable=true" - "traefik.http.services.${STACK_NAME}.loadbalancer.server.port=80" - - "traefik.http.routers.${STACK_NAME}.rule=Host(`${DOMAIN}`${EXTRA_DOMAINS})" + - "traefik.http.routers.${STACK_NAME}.rule=Host(`${DOMAIN}`)" - "traefik.http.routers.${STACK_NAME}.entrypoints=web-secure" - "traefik.http.routers.${STACK_NAME}.tls.certresolver=${LETS_ENCRYPT_ENV}" - ## Redirect from EXTRA_DOMAINS to DOMAIN - #- "traefik.http.routers.${STACK_NAME}.middlewares=${STACK_NAME}-redirect" - #- "traefik.http.middlewares.${STACK_NAME}-redirect.headers.SSLForceHost=true" - #- "traefik.http.middlewares.${STACK_NAME}-redirect.headers.SSLHost=${DOMAIN}" - - "coop-cloud.${STACK_NAME}.version=" + - "coop-cloud.${STACK_NAME}.version=0.1.0+0.8.2" + configs: + - source: revolt_toml + target: /Revolt.toml + networks: + internal: + proxy: + + database: + image: docker.io/mongo + restart: always + volumes: + - db:/data/db healthcheck: - test: ["CMD", "curl", "-f", "http://localhost"] - interval: 30s + test: echo 'db.runCommand("ping").ok' | mongosh localhost:27017/test --quiet + interval: 10s timeout: 10s - retries: 10 - start_period: 1m + retries: 5 + start_period: 10s + networks: + internal: + + redis: + image: docker.io/eqalpha/keydb + networks: + internal: + + rabbit: + image: docker.io/rabbitmq:4 + restart: always + environment: + RABBITMQ_DEFAULT_USER: rabbituser + RABBITMQ_DEFAULT_PASS: rabbitpass + volumes: + - ./data/rabbit:/var/lib/rabbitmq + healthcheck: + test: rabbitmq-diagnostics -q ping + interval: 10s + timeout: 10s + retries: 3 + start_period: 20s + networks: + internal: + + minio: + image: docker.io/minio/minio + command: server /data + volumes: + - ./data/minio:/data + environment: + MINIO_ROOT_USER: minioautumn + MINIO_ROOT_PASSWORD: minioautumn + MINIO_DOMAIN: minio + networks: + internal: + + events: + image: ghcr.io/revoltchat/bonfire:20250210-1 + depends_on: + database: + condition: service_healthy + redis: + condition: service_started + configs: + - source: revolt_toml + target: /Revolt.toml + networks: + internal: + + + web: + image: ghcr.io/revoltchat/client:1.0.1 + networks: + internal: + + autumn: + image: ghcr.io/revoltchat/autumn:20250210-1 + depends_on: + database: + condition: service_healthy + createbuckets: + condition: service_started + configs: + - source: revolt_toml + target: /Revolt.toml + networks: + internal: + + # Metadata and image proxy + january: + image: ghcr.io/revoltchat/january:20250210-1 + configs: + - source: revolt_toml + target: /Revolt.toml + networks: + internal: + + # Regular task daemon + crond: + image: ghcr.io/revoltchat/crond:20250210-1-debug + depends_on: + database: + condition: service_healthy + minio: + condition: service_started + configs: + - source: revolt_toml + target: /Revolt.toml + networks: + internal: + + # Push notification daemon + pushd: + image: ghcr.io/revoltchat/pushd:20250210-1 + depends_on: + database: + condition: service_healthy + redis: + condition: service_started + rabbit: + condition: service_healthy + configs: + - source: revolt_toml + target: /Revolt.toml + networks: + internal: + + createbuckets: + image: docker.io/minio/mc + depends_on: + - minio + entrypoint: > + /bin/sh -c " while ! /usr/bin/mc ready minio; do + /usr/bin/mc config host add minio http://minio:9000 minioautumn minioautumn; + echo 'Waiting minio...' && sleep 1; + done; /usr/bin/mc mb minio/revolt-uploads; exit 0; " + networks: + internal: networks: proxy: external: true + +configs: + revolt_toml: + name: ${STACK_NAME}_entrypoint_${ENTRYPOINT_VERSION} + file: Revolt.toml.tmpl + template_driver: golang + +secrets: + file_encrypt_token: + name: ${STACK_NAME}_file_encrypt_token_${SECRET_FILE_ENCRYPT_TOKEN_VERSION} + external: true + public_key: + name: ${STACK_NAME}_public_key_${SECRET_PUBLIC_KEY_VERSION} + external: true + private_key: + name: ${STACK_NAME}_private_key_${SECRET_PRIVATE_KEY_VERSION} + external: true + rabbit_password: + name: ${STACK_NAME}_rabbit_password_${SECRET_RABBIT_PASSWORD_VERSION} + external: true + smtp_password: + name: ${STACK_NAME}_smtp_password_${SECRET_SMTP_PASSWORD_VERSION} + external: true \ No newline at end of file