From 3fcc9f2b93bdf81080fdbfac5bea0b573ae45c58 Mon Sep 17 00:00:00 2001 From: Harikrishnan Dhanasekaran Date: Fri, 28 Nov 2025 12:39:47 +0530 Subject: [PATCH] Feat : Add Fonoster template -#45 (#509) * feat: Add Fonoster template - open source Twilio alternative * fixed the build issue * fixed the service log issue --- blueprints/fonoster/docker-compose.yml | 206 +++++++++++++++ blueprints/fonoster/fonoster.svg | 11 + blueprints/fonoster/template.toml | 331 +++++++++++++++++++++++++ meta.json | 20 ++ 4 files changed, 568 insertions(+) create mode 100644 blueprints/fonoster/docker-compose.yml create mode 100644 blueprints/fonoster/fonoster.svg create mode 100644 blueprints/fonoster/template.toml diff --git a/blueprints/fonoster/docker-compose.yml b/blueprints/fonoster/docker-compose.yml new file mode 100644 index 00000000..38ddb571 --- /dev/null +++ b/blueprints/fonoster/docker-compose.yml @@ -0,0 +1,206 @@ +services: + dashboard: + image: fonoster/dashboard:0.15.15 + restart: unless-stopped + ports: + - 3030 + environment: + - SERVER_DASHBOARD_SESSION_SECRET=${SERVER_DASHBOARD_SESSION_SECRET} + + apiserver: + image: fonoster/apiserver:0.15.15 + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + environment: + - APISERVER_APP_URL=${APISERVER_APP_URL} + - APISERVER_ASTERISK_ARI_PROXY_URL=${APISERVER_ASTERISK_ARI_PROXY_URL} + - APISERVER_ASTERISK_ARI_SECRET=${APISERVER_ASTERISK_ARI_SECRET} + - APISERVER_ASTERISK_ARI_USERNAME=${APISERVER_ASTERISK_ARI_USERNAME} + - APISERVER_AUTHZ_SERVICE_ENABLED=${APISERVER_AUTHZ_SERVICE_ENABLED} + - APISERVER_AUTHZ_SERVICE_HOST=${APISERVER_AUTHZ_SERVICE_HOST} + - APISERVER_AUTHZ_SERVICE_METHODS=${APISERVER_AUTHZ_SERVICE_METHODS} + - APISERVER_AUTHZ_SERVICE_PORT=${APISERVER_AUTHZ_SERVICE_PORT} + - APISERVER_CLOAK_ENCRYPTION_KEY=${APISERVER_CLOAK_ENCRYPTION_KEY} + - APISERVER_DATABASE_URL=${APISERVER_DATABASE_URL} + - APISERVER_IDENTITY_DATABASE_URL=${APISERVER_IDENTITY_DATABASE_URL} + - APISERVER_IDENTITY_ISSUER=${APISERVER_IDENTITY_ISSUER} + - APISERVER_IDENTITY_CONTACT_VERIFICATION_REQUIRED=${APISERVER_IDENTITY_CONTACT_VERIFICATION_REQUIRED} + - APISERVER_IDENTITY_TWO_FACTOR_AUTHENTICATION_REQUIRED=${APISERVER_IDENTITY_TWO_FACTOR_AUTHENTICATION_REQUIRED} + - APISERVER_IDENTITY_WORKSPACE_INVITE_EXPIRATION=${APISERVER_IDENTITY_WORKSPACE_INVITE_EXPIRATION} + - APISERVER_IDENTITY_WORKSPACE_INVITE_FAIL_URL=${APISERVER_IDENTITY_WORKSPACE_INVITE_FAIL_URL} + - APISERVER_IDENTITY_WORKSPACE_INVITE_URL=${APISERVER_IDENTITY_WORKSPACE_INVITE_URL} + - APISERVER_IDENTITY_OAUTH2_GITHUB_CLIENT_ID=${APISERVER_IDENTITY_OAUTH2_GITHUB_CLIENT_ID} + - APISERVER_IDENTITY_OAUTH2_GITHUB_CLIENT_SECRET=${APISERVER_IDENTITY_OAUTH2_GITHUB_CLIENT_SECRET} + - APISERVER_IDENTITY_OAUTH2_GITHUB_ENABLED=${APISERVER_IDENTITY_OAUTH2_GITHUB_ENABLED} + - APISERVER_INFLUXDB_INIT_ORG=${APISERVER_INFLUXDB_INIT_ORG} + - APISERVER_INFLUXDB_INIT_PASSWORD=${APISERVER_INFLUXDB_INIT_PASSWORD} + - APISERVER_INFLUXDB_INIT_TOKEN=${APISERVER_INFLUXDB_INIT_TOKEN} + - APISERVER_INFLUXDB_INIT_USERNAME=${APISERVER_INFLUXDB_INIT_USERNAME} + - APISERVER_INFLUXDB_URL=${APISERVER_INFLUXDB_URL} + - APISERVER_LOGS_FORMAT=${APISERVER_LOGS_FORMAT} + - APISERVER_LOGS_LEVEL=${APISERVER_LOGS_LEVEL} + - APISERVER_LOGS_TRANSPORT=${APISERVER_LOGS_TRANSPORT} + - APISERVER_NATS_URL=${APISERVER_NATS_URL} + - APISERVER_OWNER_EMAIL=${APISERVER_OWNER_EMAIL} + - APISERVER_OWNER_NAME=${APISERVER_OWNER_NAME} + - APISERVER_OWNER_PASSWORD=${APISERVER_OWNER_PASSWORD} + - APISERVER_ROOT_DOMAIN=${APISERVER_ROOT_DOMAIN} + - APISERVER_SMTP_AUTH_PASS=${APISERVER_SMTP_AUTH_PASS} + - APISERVER_SMTP_AUTH_USER=${APISERVER_SMTP_AUTH_USER} + - APISERVER_SMTP_HOST=${APISERVER_SMTP_HOST} + - APISERVER_SMTP_PORT=${APISERVER_SMTP_PORT} + - APISERVER_SMTP_SECURE=${APISERVER_SMTP_SECURE} + - APISERVER_SMTP_SENDER=${APISERVER_SMTP_SENDER} + - APISERVER_SIGNALING_SERVER=${APISERVER_SIGNALING_SERVER} + - APISERVER_TWILIO_ACCOUNT_SID=${APISERVER_TWILIO_ACCOUNT_SID} + - APISERVER_TWILIO_AUTH_TOKEN=${APISERVER_TWILIO_AUTH_TOKEN} + - APISERVER_TWILIO_PHONE_NUMBER=${APISERVER_TWILIO_PHONE_NUMBER} + expose: + - 50051 + volumes: + - ../files/config/keys:/opt/fonoster/keys:ro + - ../files/config/integrations.json:/opt/fonoster/integrations.json:ro + + autopilot: + image: fonoster/autopilot:0.15.15 + restart: unless-stopped + expose: + - 50061 + environment: + - AUTOPILOT_AWS_S3_ACCESS_KEY_ID=${AUTOPILOT_AWS_S3_ACCESS_KEY_ID} + - AUTOPILOT_AWS_S3_ENDPOINT=${AUTOPILOT_AWS_S3_ENDPOINT} + - AUTOPILOT_AWS_S3_REGION=${AUTOPILOT_AWS_S3_REGION} + - AUTOPILOT_AWS_S3_SECRET_ACCESS_KEY=${AUTOPILOT_AWS_S3_SECRET_ACCESS_KEY} + - AUTOPILOT_CONVERSATION_PROVIDER=${AUTOPILOT_CONVERSATION_PROVIDER} + - AUTOPILOT_KNOWLEDGE_BASE_ENABLED=${AUTOPILOT_KNOWLEDGE_BASE_ENABLED} + - AUTOPILOT_LOGS_FORMAT=${AUTOPILOT_LOGS_FORMAT} + - AUTOPILOT_LOGS_LEVEL=${AUTOPILOT_LOGS_LEVEL} + - AUTOPILOT_LOGS_TRANSPORT=${AUTOPILOT_LOGS_TRANSPORT} + - AUTOPILOT_OPENAI_API_KEY=${AUTOPILOT_OPENAI_API_KEY} + - AUTOPILOT_UNSTRUCTURED_API_KEY=${AUTOPILOT_UNSTRUCTURED_API_KEY} + - AUTOPILOT_UNSTRUCTURED_API_URL=${AUTOPILOT_UNSTRUCTURED_API_URL} + volumes: + - ../files/config/integrations.json:/opt/fonoster/integrations.json:ro + + routr: + image: fonoster/routr-one:2.13.13 + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + environment: + DATABASE_URL: ${ROUTR_DATABASE_URL} + EXTERNAL_ADDRS: ${ROUTR_EXTERNAL_ADDRS} + LOGS_FORMAT: ${ROUTR_LOGS_FORMAT} + LOGS_LEVEL: ${ROUTR_LOGS_LEVEL} + LOGS_TRANSPORT: ${ROUTR_LOGS_TRANSPORT} + NATS_PUBLISHER_ENABLED: "true" + NATS_PUBLISHER_URL: ${ROUTR_NATS_PUBLISHER_URL} + RTPENGINE_HOST: ${ROUTR_RTPENGINE_HOST} + START_INTERNAL_DB: "false" + CONNECT_VERIFIER_PUBLIC_KEY_PATH: /etc/routr/keys/public.pem + expose: + - 51907 + - 51908 + ports: + - 5060:5060/udp + - 5060-5063:5060-5063 + volumes: + - ../files/config/keys/public.pem:/etc/routr/keys/public.pem + + # RTPEngine uses a range of ports to handle RTP traffic. Because exposing a large range of ports + # is not possible in Docker, we need to use network_mode: host. + # + # Unfortunately, network_mode: host is not supported for Windows or Mac. + # In those cases, we need to use a different approach. + # + # By default we are opening a small range of ports (10000-10100) to handle RTP traffic. + # However, this is not enough for production environments. + # + # We recommend that when using Linux you use network_mode: host and remove the ports section. + rtpengine: + image: fonoster/rtpengine:0.3.17 + restart: unless-stopped + platform: linux/x86_64 + ports: + - 10000-10100:10000-10100/udp + - 8080:8080 + environment: + PORT_MAX: ${RTPENGINE_PORT_MAX} + PORT_MIN: ${RTPENGINE_PORT_MIN} + PUBLIC_IP: ${RTPENGINE_PUBLIC_IP} + + asterisk: + image: fonoster/asterisk:20 + restart: unless-stopped + environment: + ARI_PROXY_URL: ${ASTERISK_ARI_PROXY_URL} + ARI_SECRET: ${ASTERISK_ARI_SECRET} + ARI_USERNAME: ${ASTERISK_ARI_USERNAME} + CODECS: ${ASTERISK_CODECS} + DTMF_MODE: ${ASTERISK_DTMF_MODE} + RTP_PORT_END: ${ASTERISK_RTP_PORT_END} + RTP_PORT_START: ${ASTERISK_RTP_PORT_START} + SIPPROXY_HOST: ${ASTERISK_SIPPROXY_HOST} + SIPPROXY_PORT: ${ASTERISK_SIPPROXY_PORT} + SIPPROXY_SECRET: ${ASTERISK_SIPPROXY_SECRET} + SIPPROXY_USERNAME: ${ASTERISK_SIPPROXY_USERNAME} + expose: + - 6060 + + postgres: + image: postgres:16.10-alpine3.22 + restart: unless-stopped + environment: + PGTZ: UTC + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_DB: ${POSTGRES_DB} + TZ: UTC + expose: + - 5432 + volumes: + - db:/var/lib/postgresql/data + - ../files/config/init-db.sh:/docker-entrypoint-initdb.d/init-db.sh:ro + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] + interval: 5s + timeout: 5s + retries: 10 + + influxdb: + image: influxdb:2.7 + restart: unless-stopped + expose: + - 8086 + environment: + DOCKER_INFLUXDB_INIT_ADMIN_TOKEN: ${INFLUXDB_INIT_TOKEN} + DOCKER_INFLUXDB_INIT_BUCKET: calls + DOCKER_INFLUXDB_INIT_MODE: setup + DOCKER_INFLUXDB_INIT_ORG: ${INFLUXDB_INIT_ORG} + DOCKER_INFLUXDB_INIT_PASSWORD: ${INFLUXDB_INIT_PASSWORD} + DOCKER_INFLUXDB_INIT_USERNAME: ${INFLUXDB_INIT_USERNAME} + volumes: + - influxdb:/var/lib/influxdb2 + + nats: + image: nats:2.11.8 + restart: unless-stopped + expose: + - 4222 + + envoy: + image: envoyproxy/envoy:v1.35.0 + restart: unless-stopped + command: ["/usr/local/bin/envoy", "-c", "/etc/envoy/envoy.yaml"] + volumes: + - ../files/config:/etc/envoy:ro + ports: + - 8449:8449 + +volumes: + db: + influxdb: + diff --git a/blueprints/fonoster/fonoster.svg b/blueprints/fonoster/fonoster.svg new file mode 100644 index 00000000..549df7e6 --- /dev/null +++ b/blueprints/fonoster/fonoster.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/blueprints/fonoster/template.toml b/blueprints/fonoster/template.toml new file mode 100644 index 00000000..e9733d12 --- /dev/null +++ b/blueprints/fonoster/template.toml @@ -0,0 +1,331 @@ +[variables] +main_domain = "${domain}" + +# Critical: Set these to your host machine's IP address +# For cloud deployments, use the public IP of your server +# For local deployments, use your local machine's IP +host_ip = "CHANGE_ME_TO_HOST_IP" + +# Secrets - auto-generated strong passwords +dashboard_session_secret = "${password:64}" +ari_secret = "${password:32}" +asterisk_ari_secret = "${password:32}" +asterisk_sipproxy_secret = "${password:32}" +postgres_password = "${password:32}" +influxdb_password = "${password:32}" +influxdb_token = "${password:64}" +cloak_encryption_key = "${base64:32}" + +# Database credentials +postgres_user = "postgres" +postgres_db_fonoster = "fonoster" +postgres_db_identity = "fnidentity" +postgres_db_routr = "routr" + +# API Server Configuration +api_server_app_url = "https://${main_domain}" +api_server_root_domain = "${main_domain}" +api_server_owner_email = "admin@${main_domain}" +api_server_owner_name = "Admin User" +api_server_owner_password = "${password:32}" + +# InfluxDB Configuration +influxdb_init_org = "fonoster" +influxdb_init_username = "influxdb" + +# Asterisk Configuration +asterisk_codecs = "g722,ulaw,alaw" +asterisk_dtmf_mode = "auto_info" +asterisk_rtp_port_start = "10000" +asterisk_rtp_port_end = "20000" +asterisk_sipproxy_port = "5060" +asterisk_sipproxy_username = "voice" + +# RTP Engine Configuration +rtpengine_port_min = "10000" +rtpengine_port_max = "20000" + +# Routr Configuration +routr_nats_publisher_url = "nats://nats:4222" + +# Logging Configuration +logs_format = "json" +logs_level = "verbose" +logs_transport = "none" + +[config] +env = [ + # Dashboard + "SERVER_DASHBOARD_SESSION_SECRET=${dashboard_session_secret}", + + # API Server - Core + "APISERVER_APP_URL=${api_server_app_url}", + "APISERVER_ROOT_DOMAIN=${api_server_root_domain}", + "APISERVER_OWNER_EMAIL=${api_server_owner_email}", + "APISERVER_OWNER_NAME=${api_server_owner_name}", + "APISERVER_OWNER_PASSWORD=${api_server_owner_password}", + + # API Server - Database + "APISERVER_DATABASE_URL=postgresql://${postgres_user}:${postgres_password}@postgres:5432/${postgres_db_fonoster}", + "APISERVER_IDENTITY_DATABASE_URL=postgresql://${postgres_user}:${postgres_password}@postgres:5432/${postgres_db_identity}", + + # API Server - Asterisk ARI + "APISERVER_ASTERISK_ARI_PROXY_URL=http://asterisk:8088", + "APISERVER_ASTERISK_ARI_SECRET=${ari_secret}", + "APISERVER_ASTERISK_ARI_USERNAME=ari", + + # API Server - Identity + "APISERVER_IDENTITY_ISSUER=https://${api_server_root_domain}", + "APISERVER_IDENTITY_CONTACT_VERIFICATION_REQUIRED=false", + "APISERVER_IDENTITY_TWO_FACTOR_AUTHENTICATION_REQUIRED=false", + "APISERVER_IDENTITY_WORKSPACE_INVITE_EXPIRATION=1d", + "APISERVER_IDENTITY_WORKSPACE_INVITE_FAIL_URL=${api_server_app_url}/invite-fail", + "APISERVER_IDENTITY_WORKSPACE_INVITE_URL=https://${api_server_root_domain}:8449/api/identity/accept-invite", + "APISERVER_IDENTITY_OAUTH2_GITHUB_CLIENT_ID=", + "APISERVER_IDENTITY_OAUTH2_GITHUB_CLIENT_SECRET=", + "APISERVER_IDENTITY_OAUTH2_GITHUB_ENABLED=false", + + # API Server - InfluxDB + "APISERVER_INFLUXDB_URL=http://influxdb:8086", + "APISERVER_INFLUXDB_INIT_ORG=${influxdb_init_org}", + "APISERVER_INFLUXDB_INIT_USERNAME=${influxdb_init_username}", + "APISERVER_INFLUXDB_INIT_PASSWORD=${influxdb_password}", + "APISERVER_INFLUXDB_INIT_TOKEN=${influxdb_token}", + + # API Server - NATS + "APISERVER_NATS_URL=nats://nats:4222", + + # API Server - Logging + "APISERVER_LOGS_FORMAT=${logs_format}", + "APISERVER_LOGS_LEVEL=${logs_level}", + "APISERVER_LOGS_TRANSPORT=${logs_transport}", + + # API Server - Encryption + "APISERVER_CLOAK_ENCRYPTION_KEY=${cloak_encryption_key}", + + # API Server - AuthZ Service (disabled by default) + "APISERVER_AUTHZ_SERVICE_ENABLED=false", + "APISERVER_AUTHZ_SERVICE_HOST=fnauthz", + "APISERVER_AUTHZ_SERVICE_METHODS=/fonoster.calls.v1beta2.Calls/CreateCall,/fonoster.identity.v1beta2.Identity/CreateWorkspace", + "APISERVER_AUTHZ_SERVICE_PORT=50071", + + # API Server - SMTP (optional, configure if needed) + "APISERVER_SMTP_HOST=your-smtp-server", + "APISERVER_SMTP_PORT=587", + "APISERVER_SMTP_SECURE=true", + "APISERVER_SMTP_AUTH_USER=postmaster@${api_server_root_domain}", + "APISERVER_SMTP_AUTH_PASS=secret", + "APISERVER_SMTP_SENDER=Fonoster Info ", + + # API Server - Signaling + "APISERVER_SIGNALING_SERVER=ws://${api_server_root_domain}:5062", + + # API Server - Twilio (optional, configure if needed) + "APISERVER_TWILIO_ACCOUNT_SID=", + "APISERVER_TWILIO_AUTH_TOKEN=", + "APISERVER_TWILIO_PHONE_NUMBER=", + + # Autopilot + "AUTOPILOT_CONVERSATION_PROVIDER=api", + "AUTOPILOT_KNOWLEDGE_BASE_ENABLED=false", + "AUTOPILOT_LOGS_FORMAT=${logs_transport}", + "AUTOPILOT_LOGS_LEVEL=${logs_level}", + "AUTOPILOT_LOGS_TRANSPORT=${logs_transport}", + "AUTOPILOT_AWS_S3_ACCESS_KEY_ID=", + "AUTOPILOT_AWS_S3_ENDPOINT=", + "AUTOPILOT_AWS_S3_REGION=us-east-1", + "AUTOPILOT_AWS_S3_SECRET_ACCESS_KEY=", + "AUTOPILOT_OPENAI_API_KEY=", + "AUTOPILOT_UNSTRUCTURED_API_KEY=", + "AUTOPILOT_UNSTRUCTURED_API_URL=", + + # Routr + "ROUTR_DATABASE_URL=postgresql://${postgres_user}:${postgres_password}@postgres:5432/${postgres_db_routr}", + "ROUTR_EXTERNAL_ADDRS=${host_ip}", + "ROUTR_RTPENGINE_HOST=rtpengine", + "ROUTR_NATS_PUBLISHER_URL=${routr_nats_publisher_url}", + "ROUTR_LOGS_FORMAT=${logs_transport}", + "ROUTR_LOGS_LEVEL=${logs_level}", + "ROUTR_LOGS_TRANSPORT=${logs_transport}", + + # Asterisk + "ASTERISK_ARI_PROXY_URL=http://asterisk:8088", + "ASTERISK_ARI_SECRET=${asterisk_ari_secret}", + "ASTERISK_ARI_USERNAME=ari", + "ASTERISK_CODECS=${asterisk_codecs}", + "ASTERISK_DTMF_MODE=${asterisk_dtmf_mode}", + "ASTERISK_RTP_PORT_START=${asterisk_rtp_port_start}", + "ASTERISK_RTP_PORT_END=${asterisk_rtp_port_end}", + "ASTERISK_SIPPROXY_HOST=${host_ip}", + "ASTERISK_SIPPROXY_PORT=${asterisk_sipproxy_port}", + "ASTERISK_SIPPROXY_SECRET=${asterisk_sipproxy_secret}", + "ASTERISK_SIPPROXY_USERNAME=${asterisk_sipproxy_username}", + + # RTP Engine + "RTPENGINE_PUBLIC_IP=${host_ip}", + "RTPENGINE_PORT_MIN=${rtpengine_port_min}", + "RTPENGINE_PORT_MAX=${rtpengine_port_max}", + + # InfluxDB + "INFLUXDB_INIT_ORG=${influxdb_init_org}", + "INFLUXDB_INIT_USERNAME=${influxdb_init_username}", + "INFLUXDB_INIT_PASSWORD=${influxdb_password}", + "INFLUXDB_INIT_TOKEN=${influxdb_token}", + + # PostgreSQL + "POSTGRES_USER=${postgres_user}", + "POSTGRES_PASSWORD=${postgres_password}", + "POSTGRES_DB=${postgres_db_fonoster}", +] + +[[config.domains]] +serviceName = "dashboard" +port = 3030 +host = "${main_domain}" + +[[config.mounts]] +filePath = "config/integrations.json" +content = """ +{ + "integrations": [] +} +""" + +[[config.mounts]] +filePath = "config/init-db.sh" +content = """#!/bin/bash +set -e + +# Create additional databases (ignore error if they already exist) +psql -v ON_ERROR_STOP=0 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL + CREATE DATABASE fnidentity; + CREATE DATABASE routr; +EOSQL + +# Grant privileges +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL + GRANT ALL PRIVILEGES ON DATABASE fnidentity TO $POSTGRES_USER; + GRANT ALL PRIVILEGES ON DATABASE routr TO $POSTGRES_USER; +EOSQL +""" + +[[config.mounts]] +filePath = "config/keys/.gitkeep" +content = "# Placeholder - RSA keys need to be generated manually after deployment" + +[[config.mounts]] +filePath = "config/keys/public.pem" +content = """-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyVvQZ8v3xq7p8BmXdS3C +G9fk654bAbl30tsqq4h9d3N4F11hlue8bGAY= +-----END PUBLIC KEY----- +""" + +[[config.mounts]] +filePath = "config/envoy.yaml" +content = """ +# Envoy configuration without tls for development and testing. +# Do not use this configuration in production. Please check the docs for examples using tls. +static_resources: + listeners: + - name: listener_http + address: + socket_address: { address: 0.0.0.0, port_value: 8449 } + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + codec_type: auto + stat_prefix: ingress_http + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: ["*"] + routes: + - match: + prefix: "/api" + route: + cluster: apiserver-cluster-http + timeout: 0s + - match: + prefix: "/" + headers: + - name: "content-type" + safe_regex_match: + google_re2: {} + regex: "^(application/grpc|application/grpc-web-text)$" + route: + cluster: apiserver-cluster + timeout: 0s + max_stream_duration: + grpc_timeout_header_max: 0s + - match: + prefix: "/" + route: + cluster: dashboard-cluster + timeout: 0s + cors: + allow_origin_string_match: + - prefix: "*" + allow_methods: GET, PUT, DELETE, POST, OPTIONS + allow_headers: token,accesskeyid,keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout + max_age: "1728000" + expose_headers: grpc-status,grpc-message + http_filters: + - name: envoy.filters.http.grpc_web + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb + - name: envoy.filters.http.cors + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + + clusters: + - name: apiserver-cluster + type: logical_dns + connect_timeout: 20s + http2_protocol_options: {} + lb_policy: round_robin + load_assignment: + cluster_name: apiserver-cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: apiserver + port_value: 50051 + + - name: apiserver-cluster-http + type: logical_dns + connect_timeout: 20s + lb_policy: round_robin + load_assignment: + cluster_name: apiserver-cluster-http + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: apiserver + port_value: 9876 + + - name: dashboard-cluster + type: logical_dns + connect_timeout: 20s + lb_policy: round_robin + load_assignment: + cluster_name: dashboard-cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: dashboard + port_value: 3030 +""" + diff --git a/meta.json b/meta.json index ffc2d558..5cea3110 100644 --- a/meta.json +++ b/meta.json @@ -2307,6 +2307,26 @@ "kanban" ] }, + { + "id": "fonoster", + "name": "Fonoster", + "version": "0.15.15", + "description": "Fonoster is an open-source alternative to Twilio. A complete telephony stack for building voice applications with SIP, WebRTC, and PSTN connectivity.", + "logo": "fonoster.svg", + "links": { + "github": "https://github.com/fonoster/fonoster", + "website": "https://fonoster.com/", + "docs": "https://docs.fonoster.com/" + }, + "tags": [ + "telephony", + "voip", + "sip", + "webrtc", + "pstn", + "communication" + ] + }, { "id": "forgejo", "name": "Forgejo",