From 1e21b7e785b5a2e1b5a604df57cd23d31a0d3955 Mon Sep 17 00:00:00 2001 From: Sunil Shrestha Date: Sun, 2 Nov 2025 00:15:12 +0545 Subject: [PATCH 01/91] feat(librechat): add LibreChat blueprint with compose, toml, metadata, links and tags --- blueprints/librechat/docker-compose.yml | 109 ++++++++++++++++++++++ blueprints/librechat/librechat.png | Bin 0 -> 59599 bytes blueprints/librechat/templates.toml | 117 ++++++++++++++++++++++++ meta.json | 20 ++++ 4 files changed, 246 insertions(+) create mode 100644 blueprints/librechat/docker-compose.yml create mode 100644 blueprints/librechat/librechat.png create mode 100644 blueprints/librechat/templates.toml diff --git a/blueprints/librechat/docker-compose.yml b/blueprints/librechat/docker-compose.yml new file mode 100644 index 000000000..d6ed9323e --- /dev/null +++ b/blueprints/librechat/docker-compose.yml @@ -0,0 +1,109 @@ +# LibreChat Docker Compose for Dokploy Template + +services: + api: + image: ghcr.io/danny-avila/librechat-dev:latest + restart: always + depends_on: + - mongodb + - rag_api + environment: + # Server Configuration + - HOST=0.0.0.0 + - PORT=${PORT:-3080} + # Domain Configuration + - DOMAIN_CLIENT=${DOMAIN_CLIENT} + - DOMAIN_SERVER=${DOMAIN_SERVER} + # Database and Search Configuration + - MONGO_URI=mongodb://mongodb:27017/LibreChat + - MEILI_HOST=http://meilisearch:7700 + - SEARCH=true + - NO_INDEX=true + - MEILI_NO_ANALYTICS=true + - MEILI_MASTER_KEY=${MEILI_MASTER_KEY} + # Security & Sessions + - JWT_SECRET=${JWT_SECRET} + - JWT_REFRESH_SECRET=${JWT_REFRESH_SECRET} + - CREDS_KEY=${CREDS_KEY} + - CREDS_IV=${CREDS_IV} + - RAG_PORT=${RAG_PORT:-8000} + - RAG_API_URL=http://rag_api:${RAG_PORT:-8000} + # API Keys and Secrets + - OPENAI_API_KEY=${OPENAI_API_KEY} + - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} + - OPENROUTER_KEY=${OPENROUTER_KEY} + - GOOGLE_KEY=${GOOGLE_KEY} + - ENDPOINTS=google,openAI,assistants,azureOpenAI,anthropic + # ... Additional Endpoints + # UI + - APP_TITLE=${APP_TITLE:-LibreChat} + - CUSTOM_FOOTER=${CUSTOM_FOOTER:-Made with ❤️ by LibreChat} + - ALLOW_EMAIL_LOGIN=${ALLOW_EMAIL_LOGIN:-true} + - ALLOW_SOCIAL_LOGIN=${ALLOW_SOCIAL_LOGIN:-false} + - ALLOW_REGISTRATION=${ALLOW_REGISTRATION:-false} + volumes: + - type: bind + source: ../files/librechat.yaml + target: /app/librechat.yaml + - librechat_data:/app/client/public/images + - librechat_data:/app/uploads + - librechat_data:/app/logs + + mongodb: + image: mongo + restart: always + volumes: + - mongo_data:/data/db + command: mongod --noauth + + meilisearch: + image: getmeili/meilisearch:v1.12.3 + restart: always + environment: + - MEILI_HOST=http://meilisearch:7700 + - MEILI_NO_ANALYTICS=true + - MEILI_MASTER_KEY=${MEILI_MASTER_KEY} + volumes: + - meili_data:/meili_data + + vectordb: + image: pgvector/pgvector:0.8.0-pg15-trixie + environment: + - POSTGRES_DB=${POSTGRES_DB:-mydatabase} + - POSTGRES_USER=${POSTGRES_USER:-myuser} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-mypassword} + restart: always + volumes: + - postgres_data:/var/lib/postgresql/data + + rag_api: + image: ghcr.io/danny-avila/librechat-rag-api-dev-lite:latest + environment: + - DB_HOST=vectordb + - VECTOR_DB_TYPE=pgvector + - POSTGRES_DB=${POSTGRES_DB:-mydatabase} + - POSTGRES_USER=${POSTGRES_USER:-myuser} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-mypassword} + - RAG_PORT=${RAG_PORT:-8000} + - OPENAI_API_KEY=${OPENAI_API_KEY} + - RAG_OPENAI_API_KEY=${RAG_OPENAI_API_KEY} + - GOOGLE_KEY=${GOOGLE_KEY} + - RAG_GOOGLE_API_KEY=${RAG_GOOGLE_API_KEY} + - JWT_SECRET=${JWT_SECRET} + - COLLECTION_NAME=${COLLECTION_NAME:-librechat_collection} + - CHUNK_SIZE=${CHUNK_SIZE:-1500} + - CHUNK_OVERLAP=${CHUNK_OVERLAP:-100} + - EMBEDDINGS_PROVIDER=${EMBEDDINGS_PROVIDER:-openai} + - EMBEDDINGS_MODEL=${EMBEDDINGS_MODEL:-text-embedding-3-small} + - DEBUG_RAG_API=false + - DEBUG_PGVECTOR_QUERIES=false + - CONSOLE_JSON=false + restart: always + depends_on: + - vectordb + +volumes: + mongo_data: + meili_data: + postgres_data: + librechat_data: \ No newline at end of file diff --git a/blueprints/librechat/librechat.png b/blueprints/librechat/librechat.png new file mode 100644 index 0000000000000000000000000000000000000000..061f9a6b1ca6b71f6980c5e997c5a8bc5d583d42 GIT binary patch literal 59599 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

m;s)r5YB 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 literal 0 HcmV?d00001 diff --git a/blueprints/librechat/templates.toml b/blueprints/librechat/templates.toml new file mode 100644 index 000000000..ee7c2d948 --- /dev/null +++ b/blueprints/librechat/templates.toml @@ -0,0 +1,117 @@ +[variables] +# Domain Configuration +main_domain = "${domain}" + +# Security & Authentication +jwt_secret = "${password:64}" +jwt_refresh_secret = "${password:64}" +creds_key = "f34be427ebb29de8d88c107a71546019685ed8b241d8f2ed00c3df97ad2566f0" +creds_iv = "e2341419ec3dd3d19b13a1a87fafcbfb" +meili_master_key = "${password:32}" + +# Database Configuration +mongo_password = "${password:16}" +postgresql_password = "${password:16}" + +[config] +# Main service domain mapping +[[config.domains]] +serviceName = "librechat" +port = 3080 +host = "${main_domain}" + +[config.env] +# Basic Configuration +HOST="0.0.0.0" +PORT="3080" +DOMAIN_CLIENT="https://${main_domain}" +DOMAIN_SERVER="https://${main_domain}" + +# Search Configuration +MEILI_MASTER_KEY="${meili_master_key}" + +# Security +JWT_SECRET="${jwt_secret}" +JWT_REFRESH_SECRET="${jwt_refresh_secret}" +CREDS_KEY="${creds_key}" +CREDS_IV="${creds_iv}" + +# API Keys +OPENAI_API_KEY="" +ANTHROPIC_API_KEY="" +GOOGLE_KEY="" +OPENROUTER_KEY="" + +# Database +POSTGRES_DB="librechat_db" +POSTGRES_USER="librechat_user" +POSTGRES_PASSWORD="${postgresql_password}" + +# Security & Sessions +ALLOW_EMAIL_LOGIN="true" +ALLOW_REGISTRATION="false" +ALLOW_SOCIAL_LOGIN="false" + +# User Interface +APP_TITLE="LibreChat" +CUSTOM_FOOTER="Made with ❤️ by LibreChat" +ALLOW_EMAIL_LOGIN="true" +ALLOW_REGISTRATION="false" +ALLOW_SOCIAL_LOGIN="false" + +# RAG +RAG_OPENAI_API_KEY="" +RAG_GOOGLE_API_KEY="" +EMBEDDINGS_PROVIDER="openai" +EMBEDDINGS_MODEL="text-embedding-3-small" + +CHUNK_SIZE=1500 +CHUNK_OVERLAP=100 + + +[config.mounts] +# Persistent data volumes +[[config.mounts]] +volumeName = "mongo_data" +mountPath = "/data/db" +backup = true + +[[config.mounts]] +volumeName = "meili_data" +mountPath = "/meili_data" +backup = true + +[[config.mounts]] +volumeName = "postgres_data" +mountPath = "/var/lib/postgresql/data" +backup = true + +# Application data directories +[[config.mounts]] +volumeName = "librechat_data" +mountPath = "/app" +backup = true + +[[config.mounts]] +filePath = "librechat.yaml" +content = """ +### librechat.yaml configs ### + +version: 1.2.8 +cache: true +endpoints: + custom: + - name: "OpenRouter" + apiKey: "${OPENROUTER_KEY}" + baseURL: "https://openrouter.ai/api/v1" + models: + default: ["meta-llama/llama-3.3-70b-instruct:free"] + fetch: true + titleConvo: true + titleModel: "current_model" + summarize: false + summaryModel: "current_model" + forcePrompt: false + dropParams: ["stop"] + modelDisplayLabel: "OpenRouter" +""" \ No newline at end of file diff --git a/meta.json b/meta.json index 5c7b22cf0..7079b4afa 100644 --- a/meta.json +++ b/meta.json @@ -3136,6 +3136,26 @@ "productivity" ] }, + { + "id": "librechat", + "name": "LibreChat", + "version": "latest", + "description": "LibreChat is the ultimate open-source app for all your AI conversations, fully customizable and compatible with any AI provider (Openai, Ollama, Google etc.) — all in one sleek interface.", + "logo": "librechat.png", + "links": { + "github": "https://github.com/danny-avila/librechat", + "website": "https://librechat.ai", + "docs": "https://docs.librechat.ai" + }, + "tags": [ + "ai", + "chatbot", + "llm", + "MIT-license", + "BYOK", + "generative-ai" + ] + }, { "id": "libredesk", "name": "Libredesk", From 758cf1d873d45ea0771c109cbcb6101fedc3ba79 Mon Sep 17 00:00:00 2001 From: Sunil Shrestha Date: Sun, 2 Nov 2025 00:19:15 +0545 Subject: [PATCH 02/91] fix: rename templates to template.toml --- blueprints/librechat/{templates.toml => template.toml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename blueprints/librechat/{templates.toml => template.toml} (100%) diff --git a/blueprints/librechat/templates.toml b/blueprints/librechat/template.toml similarity index 100% rename from blueprints/librechat/templates.toml rename to blueprints/librechat/template.toml From 47daea92f862cf2d111d017b6f3e9da176455451 Mon Sep 17 00:00:00 2001 From: Sunil Shrestha Date: Sun, 2 Nov 2025 00:40:02 +0545 Subject: [PATCH 03/91] fix(librechat): rename api service to librechat in docker-compose.yml --- blueprints/librechat/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/librechat/docker-compose.yml b/blueprints/librechat/docker-compose.yml index d6ed9323e..aff39f94c 100644 --- a/blueprints/librechat/docker-compose.yml +++ b/blueprints/librechat/docker-compose.yml @@ -1,7 +1,7 @@ # LibreChat Docker Compose for Dokploy Template services: - api: + librechat: image: ghcr.io/danny-avila/librechat-dev:latest restart: always depends_on: From 3e8b43103173503b19e93276aa9810f4fa8cba98 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 8 Nov 2025 00:21:23 -0600 Subject: [PATCH 04/91] Update blueprints/librechat/template.toml --- blueprints/librechat/template.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/blueprints/librechat/template.toml b/blueprints/librechat/template.toml index ee7c2d948..22ab6c3fc 100644 --- a/blueprints/librechat/template.toml +++ b/blueprints/librechat/template.toml @@ -48,7 +48,6 @@ POSTGRES_USER="librechat_user" POSTGRES_PASSWORD="${postgresql_password}" # Security & Sessions -ALLOW_EMAIL_LOGIN="true" ALLOW_REGISTRATION="false" ALLOW_SOCIAL_LOGIN="false" From 60796a90847bf27061eaba5491eb02c49400bdf2 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 8 Nov 2025 00:24:36 -0600 Subject: [PATCH 05/91] Update blueprints/librechat/template.toml --- blueprints/librechat/template.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/blueprints/librechat/template.toml b/blueprints/librechat/template.toml index 22ab6c3fc..c1884b048 100644 --- a/blueprints/librechat/template.toml +++ b/blueprints/librechat/template.toml @@ -48,8 +48,6 @@ POSTGRES_USER="librechat_user" POSTGRES_PASSWORD="${postgresql_password}" # Security & Sessions -ALLOW_REGISTRATION="false" -ALLOW_SOCIAL_LOGIN="false" # User Interface APP_TITLE="LibreChat" From 1d702943103d80af380e68f9a274d14d2fd90223 Mon Sep 17 00:00:00 2001 From: Sunil Shrestha Date: Mon, 10 Nov 2025 09:19:06 +0545 Subject: [PATCH 06/91] fix(librechat): add version under [config] and remove stray [config.mounts] header --- blueprints/librechat/template.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/librechat/template.toml b/blueprints/librechat/template.toml index c1884b048..3c74806cf 100644 --- a/blueprints/librechat/template.toml +++ b/blueprints/librechat/template.toml @@ -14,6 +14,7 @@ mongo_password = "${password:16}" postgresql_password = "${password:16}" [config] +version = "1.0.0" # Main service domain mapping [[config.domains]] serviceName = "librechat" @@ -66,7 +67,6 @@ CHUNK_SIZE=1500 CHUNK_OVERLAP=100 -[config.mounts] # Persistent data volumes [[config.mounts]] volumeName = "mongo_data" From 2e1fb3b3a9ea2c277d593ca5891e744b0c01a7f6 Mon Sep 17 00:00:00 2001 From: Sunil Shrestha Date: Mon, 10 Nov 2025 10:25:31 +0545 Subject: [PATCH 07/91] fix(librechat): remove predefined persistent volume mounts from template.toml --- blueprints/librechat/template.toml | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/blueprints/librechat/template.toml b/blueprints/librechat/template.toml index 3c74806cf..744a17453 100644 --- a/blueprints/librechat/template.toml +++ b/blueprints/librechat/template.toml @@ -67,27 +67,6 @@ CHUNK_SIZE=1500 CHUNK_OVERLAP=100 -# Persistent data volumes -[[config.mounts]] -volumeName = "mongo_data" -mountPath = "/data/db" -backup = true - -[[config.mounts]] -volumeName = "meili_data" -mountPath = "/meili_data" -backup = true - -[[config.mounts]] -volumeName = "postgres_data" -mountPath = "/var/lib/postgresql/data" -backup = true - -# Application data directories -[[config.mounts]] -volumeName = "librechat_data" -mountPath = "/app" -backup = true [[config.mounts]] filePath = "librechat.yaml" From 46dc5b9be3ea8f06d931d709ded142d2f7a216a9 Mon Sep 17 00:00:00 2001 From: Sunil Shrestha Date: Mon, 10 Nov 2025 10:38:02 +0545 Subject: [PATCH 08/91] docs(librechat): add authentication reference link to docker-compose.yml --- blueprints/librechat/docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/blueprints/librechat/docker-compose.yml b/blueprints/librechat/docker-compose.yml index aff39f94c..a5a4c5a9e 100644 --- a/blueprints/librechat/docker-compose.yml +++ b/blueprints/librechat/docker-compose.yml @@ -1,4 +1,5 @@ # LibreChat Docker Compose for Dokploy Template +# Setting up authentication: "npm run create-user", refer to https://www.librechat.ai/docs/configuration/authentication services: librechat: From ac07aa2ed86c1ff47af34a3e50533ea2ce4946ff Mon Sep 17 00:00:00 2001 From: Rabithua Date: Tue, 25 Nov 2025 20:03:46 +0800 Subject: [PATCH 09/91] feat: add Rote template - Add Rote deployment template with frontend, backend, and PostgreSQL services - Configure domain routing for frontend (port 80) and backend (port 3000) - Set up automatic password generation and environment variables - Use latest image tag by default - Add logo and metadata to meta.json --- blueprints/rote/docker-compose.yml | 45 +++++++++++++++++++++++++++++ blueprints/rote/rote.png | Bin 0 -> 55373 bytes blueprints/rote/template.toml | 23 +++++++++++++++ meta.json | 13 +++++++++ 4 files changed, 81 insertions(+) create mode 100644 blueprints/rote/docker-compose.yml create mode 100644 blueprints/rote/rote.png create mode 100644 blueprints/rote/template.toml diff --git a/blueprints/rote/docker-compose.yml b/blueprints/rote/docker-compose.yml new file mode 100644 index 000000000..a88354a09 --- /dev/null +++ b/blueprints/rote/docker-compose.yml @@ -0,0 +1,45 @@ +services: + rote-backend: + image: rabithua/rote-backend:${IMAGE_TAG:-latest} + pull_policy: always + environment: + POSTGRESQL_URL: postgresql://rote:${POSTGRES_PASSWORD}@rote-postgres:5432/rote + depends_on: + rote-postgres: + condition: service_healthy + restart: unless-stopped + command: + [ + "sh", + "-c", + "sleep 15 && bun run dist/scripts/runMigrations.js && bun run dist/server.js", + ] + + rote-frontend: + image: rabithua/rote-frontend:${IMAGE_TAG:-latest} + pull_policy: always + depends_on: + - rote-backend + environment: + # VITE_API_BASE must point to an address that reaches rote-backend (reverse-proxy domain or host IP:port) + VITE_API_BASE: ${VITE_API_BASE} + restart: unless-stopped + + rote-postgres: + image: postgres:17 + restart: unless-stopped + environment: + POSTGRES_USER: rote + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: rote + volumes: + - rote-postgres-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U rote -d rote"] + interval: 5s + timeout: 3s + retries: 10 + start_period: 30s + +volumes: + rote-postgres-data: {} diff --git a/blueprints/rote/rote.png b/blueprints/rote/rote.png new file mode 100644 index 0000000000000000000000000000000000000000..9aca501f2a568bec6e7cf488508a5efee7a52b00 GIT binary patch literal 55373 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�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 Ly-4>C2c-QAh|nh- literal 0 HcmV?d00001 diff --git a/blueprints/rote/template.toml b/blueprints/rote/template.toml new file mode 100644 index 000000000..f1957c006 --- /dev/null +++ b/blueprints/rote/template.toml @@ -0,0 +1,23 @@ +[variables] +frontend_domain = "${domain}" +backend_domain = "${domain}" +postgres_password = "${password:32}" +image_tag = "latest" + +[config] +[[config.domains]] +serviceName = "rote-frontend" +port = 80 +host = "${frontend_domain}" + +[[config.domains]] +serviceName = "rote-backend" +port = 3000 +host = "${backend_domain}" + +[config.env] +POSTGRES_PASSWORD = "${postgres_password}" +IMAGE_TAG = "${image_tag}" +VITE_API_BASE = "https://${backend_domain}" + +[[config.mounts]] diff --git a/meta.json b/meta.json index ffc2d5589..2cea58021 100644 --- a/meta.json +++ b/meta.json @@ -4761,6 +4761,19 @@ "chat" ] }, + { + "id": "rote", + "name": "Rote", + "version": "latest", + "description": "Rote is an open-source multi-platform personal note system featuring an open API, full data ownership, and effortless Docker deployment.", + "logo": "rote.png", + "links": { + "github": "https://github.com/Rabithua/Rote", + "website": "https://rote.ink", + "docs": "https://github.com/Rabithua/Rote/tree/main/doc/userguide" + }, + "tags": ["notes", "productivity", "postgres", "bun"] + }, { "id": "roundcube", "name": "Roundcube", From d58cc15c54c09f2e8a5768c839d569a2d2d61ce0 Mon Sep 17 00:00:00 2001 From: Rabithua Date: Tue, 25 Nov 2025 20:06:49 +0800 Subject: [PATCH 10/91] fix: process meta.json to fix formatting and sorting --- meta.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/meta.json b/meta.json index 2cea58021..3aee8db20 100644 --- a/meta.json +++ b/meta.json @@ -4772,7 +4772,12 @@ "website": "https://rote.ink", "docs": "https://github.com/Rabithua/Rote/tree/main/doc/userguide" }, - "tags": ["notes", "productivity", "postgres", "bun"] + "tags": [ + "notes", + "productivity", + "postgres", + "bun" + ] }, { "id": "roundcube", From e66cad204df58cabf7ca5fdc4bf7ba2c983180db Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Fri, 28 Nov 2025 01:25:36 -0600 Subject: [PATCH 11/91] Update GitHub workflows to target 'canary' branch for meta validation --- .github/workflows/validate-meta.yml | 4 ++-- .github/workflows/validate.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/validate-meta.yml b/.github/workflows/validate-meta.yml index a394e7ec6..8f9acb7aa 100644 --- a/.github/workflows/validate-meta.yml +++ b/.github/workflows/validate-meta.yml @@ -2,10 +2,10 @@ name: Validate and Process Meta.json on: push: - branches: [main, master, develop] + branches: [canary] paths: ["meta.json"] pull_request: - branches: [main, master] + branches: [canary] paths: ["meta.json"] workflow_dispatch: diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 48cfaa8cf..3e749b351 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -3,10 +3,10 @@ name: Validate Blueprints Structure and Meta on: pull_request: branches: - - main + - canary push: branches: - - main + - canary jobs: validate: From c697f54b53f40be4f730715022b93078355bded7 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Fri, 28 Nov 2025 01:45:08 -0600 Subject: [PATCH 12/91] Update pnpm-lock.yaml to upgrade various dependencies, including '@codemirror/autocomplete', '@radix-ui/react-dialog', and React packages to their latest versions. This includes updates to '@types/react' and '@types/react-dom' for improved compatibility and performance. --- app/pnpm-lock.yaml | 1784 ++++++++++++++++++++++++-------------------- 1 file changed, 975 insertions(+), 809 deletions(-) diff --git a/app/pnpm-lock.yaml b/app/pnpm-lock.yaml index 9fec5d4bc..2d26f18e1 100644 --- a/app/pnpm-lock.yaml +++ b/app/pnpm-lock.yaml @@ -9,17 +9,17 @@ importers: .: dependencies: '@codemirror/autocomplete': - specifier: ^6.18.6 - version: 6.18.6 + specifier: ^6.19.1 + version: 6.19.1 '@codemirror/lang-json': - specifier: ^6.0.1 - version: 6.0.1 + specifier: ^6.0.2 + version: 6.0.2 '@codemirror/lang-yaml': - specifier: ^6.1.1 + specifier: ^6.1.2 version: 6.1.2 '@codemirror/language': - specifier: ^6.10.1 - version: 6.10.8 + specifier: ^6.11.3 + version: 6.11.3 '@codemirror/legacy-modes': specifier: 6.4.0 version: 6.4.0 @@ -30,32 +30,32 @@ importers: specifier: ^2.2.5 version: 2.2.5 '@radix-ui/react-dialog': - specifier: ^1.1.6 - version: 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + specifier: ^1.1.15 + version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@radix-ui/react-dropdown-menu': - specifier: ^2.1.6 - version: 2.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + specifier: ^2.1.16 + version: 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@radix-ui/react-label': - specifier: ^2.1.2 - version: 2.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + specifier: ^2.1.7 + version: 2.1.8(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@radix-ui/react-popover': - specifier: ^1.1.6 - version: 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + specifier: ^1.1.15 + version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@radix-ui/react-slot': - specifier: ^1.1.2 - version: 1.1.2(@types/react@19.0.10)(react@19.0.0) + specifier: ^1.2.3 + version: 1.2.4(@types/react@19.2.4)(react@19.2.0) '@radix-ui/react-tabs': - specifier: ^1.1.3 - version: 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + specifier: ^1.1.13 + version: 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@tailwindcss/vite': - specifier: ^4.0.12 - version: 4.0.12(vite@6.2.1(@types/node@20.17.24)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.7.1)) + specifier: ^4.1.16 + version: 4.1.17(vite@6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.7.1)) '@uiw/codemirror-theme-github': - specifier: ^4.22.1 - version: 4.23.10(@codemirror/language@6.10.8)(@codemirror/state@6.5.2)(@codemirror/view@6.29.0) + specifier: ^4.25.2 + version: 4.25.3(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.29.0) '@uiw/react-codemirror': - specifier: ^4.22.1 - version: 4.23.10(@babel/runtime@7.26.9)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.29.0)(codemirror@6.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + specifier: ^4.25.2 + version: 4.25.3(@babel/runtime@7.26.9)(@codemirror/autocomplete@6.19.1)(@codemirror/language@6.11.3)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.29.0)(codemirror@6.0.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -64,123 +64,131 @@ importers: version: 2.1.1 cmdk: specifier: 1.0.0 - version: 1.0.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.0.0(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) copy-to-clipboard: specifier: ^3.3.3 version: 3.3.3 lucide-react: specifier: ^0.479.0 - version: 0.479.0(react@19.0.0) + version: 0.479.0(react@19.2.0) next-themes: - specifier: ^0.4.5 - version: 0.4.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + specifier: ^0.4.6 + version: 0.4.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react: - specifier: ^19.0.0 - version: 19.0.0 + specifier: ^19.2.0 + version: 19.2.0 react-dom: - specifier: ^19.0.0 - version: 19.0.0(react@19.0.0) + specifier: ^19.2.0 + version: 19.2.0(react@19.2.0) react-router-dom: - specifier: ^7.4.1 - version: 7.4.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + specifier: ^7.9.4 + version: 7.9.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) sonner: - specifier: ^2.0.1 - version: 2.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + specifier: ^2.0.7 + version: 2.0.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0) tailwind-merge: - specifier: ^3.0.2 - version: 3.0.2 + specifier: ^3.3.1 + version: 3.4.0 tailwindcss: - specifier: ^4.0.12 - version: 4.0.12 + specifier: ^4.1.16 + version: 4.1.17 tailwindcss-animate: specifier: ^1.0.7 - version: 1.0.7(tailwindcss@4.0.12) + version: 1.0.7(tailwindcss@4.1.17) vite-plugin-static-copy: - specifier: 2.3.0 - version: 2.3.0(vite@6.2.1(@types/node@20.17.24)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.7.1)) + specifier: ^2.3.2 + version: 2.3.2(vite@6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.7.1)) yaml: specifier: 2.7.1 version: 2.7.1 zustand: - specifier: ^5.0.3 - version: 5.0.3(@types/react@19.0.10)(react@19.0.0) + specifier: ^5.0.8 + version: 5.0.8(@types/react@19.2.4)(react@19.2.0) devDependencies: '@types/node': - specifier: ^20.8.2 - version: 20.17.24 + specifier: ^20.19.23 + version: 20.19.25 '@types/react': - specifier: ^19.0.10 - version: 19.0.10 + specifier: ^19.2.2 + version: 19.2.4 '@types/react-dom': - specifier: ^19.0.4 - version: 19.0.4(@types/react@19.0.10) + specifier: ^19.2.2 + version: 19.2.3(@types/react@19.2.4) '@vitejs/plugin-react': - specifier: ^4.3.4 - version: 4.3.4(vite@6.2.1(@types/node@20.17.24)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.7.1)) + specifier: ^4.7.0 + version: 4.7.0(vite@6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.7.1)) globals: specifier: ^15.15.0 version: 15.15.0 typescript: - specifier: ~5.7.2 + specifier: ~5.7.3 version: 5.7.3 vite: - specifier: ^6.2.0 - version: 6.2.1(@types/node@20.17.24)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.7.1) + specifier: ^6.4.1 + version: 6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.7.1) packages: - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} - '@babel/code-frame@7.26.2': - resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} + '@babel/compat-data@7.28.5': + resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.26.8': - resolution: {integrity: sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==} + '@babel/core@7.28.5': + resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} engines: {node: '>=6.9.0'} - '@babel/core@7.26.9': - resolution: {integrity: sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==} + '@babel/generator@7.28.5': + resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} engines: {node: '>=6.9.0'} - '@babel/generator@7.26.9': - resolution: {integrity: sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==} + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.26.5': - resolution: {integrity: sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==} + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.25.9': - resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.26.0': - resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-plugin-utils@7.26.5': - resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==} + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} engines: {node: '>=6.9.0'} '@babel/helper-string-parser@7.25.9': resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.25.9': resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.25.9': - resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.26.9': - resolution: {integrity: sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==} + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} engines: {node: '>=6.9.0'} '@babel/parser@7.26.9': @@ -188,14 +196,19 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-transform-react-jsx-self@7.25.9': - resolution: {integrity: sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==} + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx-source@7.25.9': - resolution: {integrity: sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==} + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -204,32 +217,36 @@ packages: resolution: {integrity: sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==} engines: {node: '>=6.9.0'} - '@babel/template@7.26.9': - resolution: {integrity: sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==} + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.26.9': - resolution: {integrity: sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==} + '@babel/traverse@7.28.5': + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} engines: {node: '>=6.9.0'} '@babel/types@7.26.9': resolution: {integrity: sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==} engines: {node: '>=6.9.0'} - '@codemirror/autocomplete@6.18.6': - resolution: {integrity: sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==} + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} + + '@codemirror/autocomplete@6.19.1': + resolution: {integrity: sha512-q6NenYkEy2fn9+JyjIxMWcNjzTL/IhwqfzOut1/G3PrIFkrbl4AL7Wkse5tLrQUUyqGoAKU5+Pi5jnnXxH5HGw==} '@codemirror/commands@6.8.0': resolution: {integrity: sha512-q8VPEFaEP4ikSlt6ZxjB3zW72+7osfAYW9i8Zu943uqbKuz6utc1+F170hyLUCUltXORjQXRyYQNfkckzA/bPQ==} - '@codemirror/lang-json@6.0.1': - resolution: {integrity: sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==} + '@codemirror/lang-json@6.0.2': + resolution: {integrity: sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==} '@codemirror/lang-yaml@6.1.2': resolution: {integrity: sha512-dxrfG8w5Ce/QbT7YID7mWZFKhdhsaTNOYjOkSIMt1qmC4VQnXSDSYVHHHn8k6kJUfIhtLo8t1JJgltlxWdsITw==} - '@codemirror/language@6.10.8': - resolution: {integrity: sha512-wcP8XPPhDH2vTqf181U8MbZnW+tDyPYy0UzVOa+oHORjyT+mhhom9vBd7dApJwoDz9Nb/a8kHjJIsuA/t8vNFw==} + '@codemirror/language@6.11.3': + resolution: {integrity: sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==} '@codemirror/legacy-modes@6.4.0': resolution: {integrity: sha512-5m/K+1A6gYR0e+h/dEde7LoGimMjRtWXZFg4Lo70cc8HzjSdHe3fLwjWMR0VRl5KFT1SxalSap7uMgPKF28wBA==} @@ -420,10 +437,16 @@ packages: '@iarna/toml@2.2.5': resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==} + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + '@jridgewell/gen-mapping@0.3.8': resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} engines: {node: '>=6.0.0'} + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -435,9 +458,15 @@ packages: '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@lezer/common@1.2.3': resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==} @@ -471,11 +500,11 @@ packages: '@radix-ui/primitive@1.0.1': resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} - '@radix-ui/primitive@1.1.1': - resolution: {integrity: sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==} + '@radix-ui/primitive@1.1.3': + resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} - '@radix-ui/react-arrow@1.1.2': - resolution: {integrity: sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg==} + '@radix-ui/react-arrow@1.1.7': + resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -487,8 +516,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collection@1.1.2': - resolution: {integrity: sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==} + '@radix-ui/react-collection@1.1.7': + resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -509,8 +538,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-compose-refs@1.1.1': - resolution: {integrity: sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==} + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -527,8 +556,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-context@1.1.1': - resolution: {integrity: sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==} + '@radix-ui/react-context@1.1.2': + resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -549,8 +578,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dialog@1.1.6': - resolution: {integrity: sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw==} + '@radix-ui/react-dialog@1.1.15': + resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -562,8 +591,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-direction@1.1.0': - resolution: {integrity: sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==} + '@radix-ui/react-direction@1.1.1': + resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -584,8 +613,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dismissable-layer@1.1.5': - resolution: {integrity: sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==} + '@radix-ui/react-dismissable-layer@1.1.11': + resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -597,8 +626,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dropdown-menu@2.1.6': - resolution: {integrity: sha512-no3X7V5fD487wab/ZYSHXq3H37u4NVeLDKI/Ks724X/eEFSSEFYZxWgsIlr1UBeEyDaM29HM5x9p1Nv8DuTYPA==} + '@radix-ui/react-dropdown-menu@2.1.16': + resolution: {integrity: sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -619,8 +648,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-focus-guards@1.1.1': - resolution: {integrity: sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==} + '@radix-ui/react-focus-guards@1.1.3': + resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -641,8 +670,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-focus-scope@1.1.2': - resolution: {integrity: sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==} + '@radix-ui/react-focus-scope@1.1.7': + resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -663,8 +692,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-id@1.1.0': - resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==} + '@radix-ui/react-id@1.1.1': + resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -672,8 +701,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-label@2.1.2': - resolution: {integrity: sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw==} + '@radix-ui/react-label@2.1.8': + resolution: {integrity: sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -685,8 +714,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-menu@2.1.6': - resolution: {integrity: sha512-tBBb5CXDJW3t2mo9WlO7r6GTmWV0F0uzHZVFmlRmYpiSK1CDU5IKojP1pm7oknpBOrFZx/YgBRW9oorPO2S/Lg==} + '@radix-ui/react-menu@2.1.16': + resolution: {integrity: sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -698,8 +727,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-popover@1.1.6': - resolution: {integrity: sha512-NQouW0x4/GnkFJ/pRqsIS3rM/k97VzKnVb2jB7Gq7VEGPy5g7uNV1ykySFt7eWSp3i2uSGFwaJcvIRJBAHmmFg==} + '@radix-ui/react-popover@1.1.15': + resolution: {integrity: sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -711,8 +740,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-popper@1.2.2': - resolution: {integrity: sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA==} + '@radix-ui/react-popper@1.2.8': + resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -737,8 +766,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-portal@1.1.4': - resolution: {integrity: sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==} + '@radix-ui/react-portal@1.1.9': + resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -763,8 +792,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-presence@1.1.2': - resolution: {integrity: sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==} + '@radix-ui/react-presence@1.1.5': + resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -789,8 +818,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-primitive@2.0.2': - resolution: {integrity: sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==} + '@radix-ui/react-primitive@2.1.3': + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -802,8 +831,21 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-roving-focus@1.1.2': - resolution: {integrity: sha512-zgMQWkNO169GtGqRvYrzb0Zf8NhMHS2DuEB/TiEmVnpr5OqPU3i8lfbxaAmC2J/KYuIQxyoQQ6DxepyXp61/xw==} + '@radix-ui/react-primitive@2.1.4': + resolution: {integrity: sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-roving-focus@1.1.11': + resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -824,8 +866,17 @@ packages: '@types/react': optional: true - '@radix-ui/react-slot@1.1.2': - resolution: {integrity: sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==} + '@radix-ui/react-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-slot@1.2.4': + resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -833,8 +884,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-tabs@1.1.3': - resolution: {integrity: sha512-9mFyI30cuRDImbmFF6O2KUJdgEOsGh9Vmx9x/Dh9tOhL7BngmQPQfwW4aejKm5OHpfWIdmeV6ySyuxoOGjtNng==} + '@radix-ui/react-tabs@1.1.13': + resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -855,8 +906,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-callback-ref@1.1.0': - resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} + '@radix-ui/react-use-callback-ref@1.1.1': + resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -873,8 +924,17 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-controllable-state@1.1.0': - resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==} + '@radix-ui/react-use-controllable-state@1.2.2': + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.2': + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -891,8 +951,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-escape-keydown@1.1.0': - resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==} + '@radix-ui/react-use-escape-keydown@1.1.1': + resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -909,8 +969,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-layout-effect@1.1.0': - resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -918,8 +978,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-rect@1.1.0': - resolution: {integrity: sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==} + '@radix-ui/react-use-rect@1.1.1': + resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -927,8 +987,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-size@1.1.0': - resolution: {integrity: sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==} + '@radix-ui/react-use-size@1.1.1': + resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -936,8 +996,11 @@ packages: '@types/react': optional: true - '@radix-ui/rect@1.1.0': - resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==} + '@radix-ui/rect@1.1.1': + resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} '@rollup/rollup-android-arm-eabi@4.35.0': resolution: {integrity: sha512-uYQ2WfPaqz5QtVgMxfN6NpLD+no0MYHDBywl7itPYd3K5TjjSghNKmX8ic9S8NU8w81NVhJv/XojcHptRly7qQ==} @@ -1034,83 +1097,95 @@ packages: cpu: [x64] os: [win32] - '@tailwindcss/node@4.0.12': - resolution: {integrity: sha512-a6J11K1Ztdln9OrGfoM75/hChYPcHYGNYimqciMrvKXRmmPaS8XZTHhdvb5a3glz4Kd4ZxE1MnuFE2c0fGGmtg==} + '@tailwindcss/node@4.1.17': + resolution: {integrity: sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==} - '@tailwindcss/oxide-android-arm64@4.0.12': - resolution: {integrity: sha512-dAXCaemu3mHLXcA5GwGlQynX8n7tTdvn5i1zAxRvZ5iC9fWLl5bGnjZnzrQqT7ttxCvRwdVf3IHUnMVdDBO/kQ==} + '@tailwindcss/oxide-android-arm64@4.1.17': + resolution: {integrity: sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==} engines: {node: '>= 10'} cpu: [arm64] os: [android] - '@tailwindcss/oxide-darwin-arm64@4.0.12': - resolution: {integrity: sha512-vPNI+TpJQ7sizselDXIJdYkx9Cu6JBdtmRWujw9pVIxW8uz3O2PjgGGzL/7A0sXI8XDjSyRChrUnEW9rQygmJQ==} + '@tailwindcss/oxide-darwin-arm64@4.1.17': + resolution: {integrity: sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.0.12': - resolution: {integrity: sha512-RL/9jM41Fdq4Efr35C5wgLx98BirnrfwuD+zgMFK6Ir68HeOSqBhW9jsEeC7Y/JcGyPd3MEoJVIU4fAb7YLg7A==} + '@tailwindcss/oxide-darwin-x64@4.1.17': + resolution: {integrity: sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.0.12': - resolution: {integrity: sha512-7WzWiax+LguJcMEimY0Q4sBLlFXu1tYxVka3+G2M9KmU/3m84J3jAIV4KZWnockbHsbb2XgrEjtlJKVwHQCoRA==} + '@tailwindcss/oxide-freebsd-x64@4.1.17': + resolution: {integrity: sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==} engines: {node: '>= 10'} cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.12': - resolution: {integrity: sha512-X9LRC7jjE1QlfIaBbXjY0PGeQP87lz5mEfLSVs2J1yRc9PSg1tEPS9NBqY4BU9v5toZgJgzKeaNltORyTs22TQ==} + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17': + resolution: {integrity: sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.0.12': - resolution: {integrity: sha512-i24IFNq2402zfDdoWKypXz0ZNS2G4NKaA82tgBlE2OhHIE+4mg2JDb5wVfyP6R+MCm5grgXvurcIcKWvo44QiQ==} + '@tailwindcss/oxide-linux-arm64-gnu@4.1.17': + resolution: {integrity: sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-arm64-musl@4.0.12': - resolution: {integrity: sha512-LmOdshJBfAGIBG0DdBWhI0n5LTMurnGGJCHcsm9F//ISfsHtCnnYIKgYQui5oOz1SUCkqsMGfkAzWyNKZqbGNw==} + '@tailwindcss/oxide-linux-arm64-musl@4.1.17': + resolution: {integrity: sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-x64-gnu@4.0.12': - resolution: {integrity: sha512-OSK667qZRH30ep8RiHbZDQfqkXjnzKxdn0oRwWzgCO8CoTxV+MvIkd0BWdQbYtYuM1wrakARV/Hwp0eA/qzdbw==} + '@tailwindcss/oxide-linux-x64-gnu@4.1.17': + resolution: {integrity: sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-linux-x64-musl@4.0.12': - resolution: {integrity: sha512-uylhWq6OWQ8krV8Jk+v0H/3AZKJW6xYMgNMyNnUbbYXWi7hIVdxRKNUB5UvrlC3RxtgsK5EAV2i1CWTRsNcAnA==} + '@tailwindcss/oxide-linux-x64-musl@4.1.17': + resolution: {integrity: sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-win32-arm64-msvc@4.0.12': - resolution: {integrity: sha512-XDLnhMoXZEEOir1LK43/gHHwK84V1GlV8+pAncUAIN2wloeD+nNciI9WRIY/BeFTqES22DhTIGoilSO39xDb2g==} + '@tailwindcss/oxide-wasm32-wasi@4.1.17': + resolution: {integrity: sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.17': + resolution: {integrity: sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.0.12': - resolution: {integrity: sha512-I/BbjCLpKDQucvtn6rFuYLst1nfFwSMYyPzkx/095RE+tuzk5+fwXuzQh7T3fIBTcbn82qH/sFka7yPGA50tLw==} + '@tailwindcss/oxide-win32-x64-msvc@4.1.17': + resolution: {integrity: sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@tailwindcss/oxide@4.0.12': - resolution: {integrity: sha512-DWb+myvJB9xJwelwT9GHaMc1qJj6MDXRDR0CS+T8IdkejAtu8ctJAgV4r1drQJLPeS7mNwq2UHW2GWrudTf63A==} + '@tailwindcss/oxide@4.1.17': + resolution: {integrity: sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==} engines: {node: '>= 10'} - '@tailwindcss/vite@4.0.12': - resolution: {integrity: sha512-JM3gp601UJiryIZ9R2bSqalzcOy15RCybQ1Q+BJqDEwVyo4LkWKeqQAcrpHapWXY31OJFTuOUVBFDWMhzHm2Bg==} + '@tailwindcss/vite@4.1.17': + resolution: {integrity: sha512-4+9w8ZHOiGnpcGI6z1TVVfWaX/koK7fKeSYF3qlYg2xpBtbteP2ddBxiarL+HVgfSJGeK5RIxRQmKm4rTJJAwA==} peerDependencies: - vite: ^5.2.0 || ^6 + vite: ^5.2.0 || ^6 || ^7 '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -1124,25 +1199,22 @@ packages: '@types/babel__traverse@7.20.6': resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} - '@types/cookie@0.6.0': - resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} - '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} - '@types/node@20.17.24': - resolution: {integrity: sha512-d7fGCyB96w9BnWQrOsJtpyiSaBcAYYr75bnK6ZRjDbql2cGLj/3GsL5OYmLPNq76l7Gf2q4Rv9J2o6h5CrD9sA==} + '@types/node@20.19.25': + resolution: {integrity: sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==} - '@types/react-dom@19.0.4': - resolution: {integrity: sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==} + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: - '@types/react': ^19.0.0 + '@types/react': ^19.2.0 - '@types/react@19.0.10': - resolution: {integrity: sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==} + '@types/react@19.2.4': + resolution: {integrity: sha512-tBFxBp9Nfyy5rsmefN+WXc1JeW/j2BpBHFdLZbEVfs9wn3E3NRFxwV0pJg8M1qQAexFpvz73hJXFofV0ZAu92A==} - '@uiw/codemirror-extensions-basic-setup@4.23.10': - resolution: {integrity: sha512-zpbmSeNs3OU/f/Eyd6brFnjsBUYwv2mFjWxlAsIRSwTlW+skIT60rQHFBSfsj/5UVSxSLWVeUYczN7AyXvgTGQ==} + '@uiw/codemirror-extensions-basic-setup@4.25.3': + resolution: {integrity: sha512-F1doRyD50CWScwGHG2bBUtUpwnOv/zqSnzkZqJcX5YAHQx6Z1CuX8jdnFMH6qktRrPU1tfpNYftTWu3QIoHiMA==} peerDependencies: '@codemirror/autocomplete': '>=6.0.0' '@codemirror/commands': '>=6.0.0' @@ -1152,32 +1224,32 @@ packages: '@codemirror/state': '>=6.0.0' '@codemirror/view': '>=6.0.0' - '@uiw/codemirror-theme-github@4.23.10': - resolution: {integrity: sha512-jTg2sHAcU1d+8x0O+EBDI71rtJ8PWKIW8gzy+SW4wShQTAdsqGHk5y1ynt3KIeoaUkqngLqAK4SkhPaUKlqZqg==} + '@uiw/codemirror-theme-github@4.25.3': + resolution: {integrity: sha512-KdmcO9VicsBgsDErNrNBqwMuTbJRIpeMl9oIjmrNx2iEfIDSOMBIKlX+BkgwTAU+VmhqYY/68/kmF1K8z2FxrQ==} - '@uiw/codemirror-themes@4.23.10': - resolution: {integrity: sha512-dU0UgEEgEXCAYpxuVDQ6fovE82XsqgHZckTJOH6Bs8xCi3Z7dwBKO4pXuiA8qGDwTOXOMjSzfi+pRViDm7OfWw==} + '@uiw/codemirror-themes@4.25.3': + resolution: {integrity: sha512-k7/B7Vf4jU/WcdewgJWP9tMFxbjB6UpUymZ3fx/TsbGwt2JXAouw0uyqCn1RlYBfr7YQnvEs3Ju9ECkd2sKzdg==} peerDependencies: '@codemirror/language': '>=6.0.0' '@codemirror/state': '>=6.0.0' '@codemirror/view': '>=6.0.0' - '@uiw/react-codemirror@4.23.10': - resolution: {integrity: sha512-AbN4eVHOL4ckRuIXpZxkzEqL/1ChVA+BSdEnAKjIB68pLQvKsVoYbiFP8zkXkYc4+Fcgq5KbAjvYqdo4ewemKw==} + '@uiw/react-codemirror@4.25.3': + resolution: {integrity: sha512-1wtBZTXPIp8u6F/xjHvsUAYlEeF5Dic4xZBnqJyLzv7o7GjGYEUfSz9Z7bo9aK9GAx2uojG/AuBMfhA4uhvIVQ==} peerDependencies: '@babel/runtime': '>=7.11.0' '@codemirror/state': '>=6.0.0' '@codemirror/theme-one-dark': '>=6.0.0' '@codemirror/view': '>=6.0.0' codemirror: '>=6.0.0' - react: '>=16.8.0' - react-dom: '>=16.8.0' + react: '>=17.0.0' + react-dom: '>=17.0.0' - '@vitejs/plugin-react@4.3.4': - resolution: {integrity: sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==} + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: - vite: ^4.2.0 || ^5.0.0 || ^6.0.0 + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} @@ -1258,8 +1330,8 @@ packages: electron-to-chromium@1.5.113: resolution: {integrity: sha512-wjT2O4hX+wdWPJ76gWSkMhcHAV2PTMX+QetUCPYEdCIe+cxmgzzSSiGRCKW8nuh4mwKZlpv0xvoW7OF2X+wmHg==} - enhanced-resolve@5.18.1: - resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} + enhanced-resolve@5.18.3: + resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} engines: {node: '>=10.13.0'} esbuild@0.25.1: @@ -1278,6 +1350,15 @@ packages: fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -1303,10 +1384,6 @@ packages: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} - globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} - globals@15.15.0: resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} engines: {node: '>=18'} @@ -1330,8 +1407,8 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - jiti@2.4.2: - resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true js-tokens@4.0.0: @@ -1350,68 +1427,74 @@ packages: jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} - lightningcss-darwin-arm64@1.29.2: - resolution: {integrity: sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==} + lightningcss-android-arm64@1.30.2: + resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.30.2: + resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] - lightningcss-darwin-x64@1.29.2: - resolution: {integrity: sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==} + lightningcss-darwin-x64@1.30.2: + resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] - lightningcss-freebsd-x64@1.29.2: - resolution: {integrity: sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==} + lightningcss-freebsd-x64@1.30.2: + resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] - lightningcss-linux-arm-gnueabihf@1.29.2: - resolution: {integrity: sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==} + lightningcss-linux-arm-gnueabihf@1.30.2: + resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] - lightningcss-linux-arm64-gnu@1.29.2: - resolution: {integrity: sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==} + lightningcss-linux-arm64-gnu@1.30.2: + resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - lightningcss-linux-arm64-musl@1.29.2: - resolution: {integrity: sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==} + lightningcss-linux-arm64-musl@1.30.2: + resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - lightningcss-linux-x64-gnu@1.29.2: - resolution: {integrity: sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==} + lightningcss-linux-x64-gnu@1.30.2: + resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - lightningcss-linux-x64-musl@1.29.2: - resolution: {integrity: sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==} + lightningcss-linux-x64-musl@1.30.2: + resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - lightningcss-win32-arm64-msvc@1.29.2: - resolution: {integrity: sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==} + lightningcss-win32-arm64-msvc@1.30.2: + resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] - lightningcss-win32-x64-msvc@1.29.2: - resolution: {integrity: sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==} + lightningcss-win32-x64-msvc@1.30.2: + resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] - lightningcss@1.29.2: - resolution: {integrity: sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==} + lightningcss@1.30.2: + resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} engines: {node: '>= 12.0.0'} lru-cache@5.1.1: @@ -1422,6 +1505,9 @@ packages: peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -1438,8 +1524,8 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - next-themes@0.4.5: - resolution: {integrity: sha512-E8/gYKBxZknOXBiDk/sRokAvkOw35PTUD4Gxtq1eBhd0r4Dx5S42zU65/q8ozR5rcSG2ZlE1E3+ShlUpC7an+A==} + next-themes@0.4.6: + resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==} peerDependencies: react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc @@ -1462,6 +1548,10 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + postcss@8.5.3: resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} engines: {node: ^10 || ^12 || >=14} @@ -1469,13 +1559,13 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - react-dom@19.0.0: - resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==} + react-dom@19.2.0: + resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==} peerDependencies: - react: ^19.0.0 + react: ^19.2.0 - react-refresh@0.14.2: - resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} engines: {node: '>=0.10.0'} react-remove-scroll-bar@2.3.8: @@ -1508,15 +1598,15 @@ packages: '@types/react': optional: true - react-router-dom@7.4.1: - resolution: {integrity: sha512-L3/4tig0Lvs6m6THK0HRV4eHUdpx0dlJasgCxXKnavwhh4tKYgpuZk75HRYNoRKDyDWi9QgzGXsQ1oQSBlWpAA==} + react-router-dom@7.9.6: + resolution: {integrity: sha512-2MkC2XSXq6HjGcihnx1s0DBWQETI4mlis4Ux7YTLvP67xnGxCvq+BcCQSO81qQHVUTM1V53tl4iVVaY5sReCOA==} engines: {node: '>=20.0.0'} peerDependencies: react: '>=18' react-dom: '>=18' - react-router@7.4.1: - resolution: {integrity: sha512-Vmizn9ZNzxfh3cumddqv3kLOKvc7AskUT0dC1prTabhiEi0U4A33LmkDOJ79tXaeSqCqMBXBU/ySX88W85+EUg==} + react-router@7.9.6: + resolution: {integrity: sha512-Y1tUp8clYRXpfPITyuifmSoE2vncSME18uVLgaqyxh9H35JWpIfzHo+9y3Fzh5odk/jxPW29IgLgzcdwxGqyNA==} engines: {node: '>=20.0.0'} peerDependencies: react: '>=18' @@ -1535,8 +1625,8 @@ packages: '@types/react': optional: true - react@19.0.0: - resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} + react@19.2.0: + resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} engines: {node: '>=0.10.0'} readdirp@3.6.0: @@ -1558,8 +1648,8 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - scheduler@0.25.0: - resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==} + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} @@ -1568,8 +1658,8 @@ packages: set-cookie-parser@2.7.1: resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} - sonner@2.0.1: - resolution: {integrity: sha512-FRBphaehZ5tLdLcQ8g2WOIRE+Y7BCfWi5Zyd8bCvBjiW8TxxAyoWZIxS661Yz6TGPqFQ4VLzOF89WEYhfynSFQ==} + sonner@2.0.7: + resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==} peerDependencies: react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc @@ -1581,21 +1671,25 @@ packages: style-mod@4.1.2: resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==} - tailwind-merge@3.0.2: - resolution: {integrity: sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw==} + tailwind-merge@3.4.0: + resolution: {integrity: sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==} tailwindcss-animate@1.0.7: resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} peerDependencies: tailwindcss: '>=3.0.0 || insiders' - tailwindcss@4.0.12: - resolution: {integrity: sha512-bT0hJo91FtncsAMSsMzUkoo/iEU0Xs5xgFgVC9XmdM9bw5MhZuQFjPNl6wxAE0SiQF/YTZJa+PndGWYSDtuxAg==} + tailwindcss@4.1.17: + resolution: {integrity: sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==} tapable@2.2.1: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -1606,16 +1700,13 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - turbo-stream@2.4.0: - resolution: {integrity: sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==} - typescript@5.7.3: resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} engines: {node: '>=14.17'} hasBin: true - undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} @@ -1647,14 +1738,14 @@ packages: '@types/react': optional: true - vite-plugin-static-copy@2.3.0: - resolution: {integrity: sha512-LLKwhhHetGaCnWz4mas4qqjjguDka6/6b4+SeIohRroj8aCE7QTfiZECfPecslFQkWZ3HdQuq5kOPmWZjNYlKA==} + vite-plugin-static-copy@2.3.2: + resolution: {integrity: sha512-iwrrf+JupY4b9stBttRWzGHzZbeMjAHBhkrn67MNACXJVjEMRpCI10Q3AkxdBkl45IHaTfw/CNVevzQhP7yTwg==} engines: {node: ^18.0.0 || >=20.0.0} peerDependencies: vite: ^5.0.0 || ^6.0.0 - vite@6.2.1: - resolution: {integrity: sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q==} + vite@6.4.1: + resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: @@ -1704,8 +1795,8 @@ packages: engines: {node: '>= 14'} hasBin: true - zustand@5.0.3: - resolution: {integrity: sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==} + zustand@5.0.8: + resolution: {integrity: sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==} engines: {node: '>=12.20.0'} peerDependencies: '@types/react': '>=18.0.0' @@ -1724,31 +1815,26 @@ packages: snapshots: - '@ampproject/remapping@2.3.0': + '@babel/code-frame@7.27.1': dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 - - '@babel/code-frame@7.26.2': - dependencies: - '@babel/helper-validator-identifier': 7.25.9 + '@babel/helper-validator-identifier': 7.28.5 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.26.8': {} + '@babel/compat-data@7.28.5': {} - '@babel/core@7.26.9': + '@babel/core@7.28.5': dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.9 - '@babel/helper-compilation-targets': 7.26.5 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.9) - '@babel/helpers': 7.26.9 - '@babel/parser': 7.26.9 - '@babel/template': 7.26.9 - '@babel/traverse': 7.26.9 - '@babel/types': 7.26.9 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.0 gensync: 1.0.0-beta.2 @@ -1757,84 +1843,94 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.26.9': + '@babel/generator@7.28.5': dependencies: - '@babel/parser': 7.26.9 - '@babel/types': 7.26.9 - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 - '@babel/helper-compilation-targets@7.26.5': + '@babel/helper-compilation-targets@7.27.2': dependencies: - '@babel/compat-data': 7.26.8 - '@babel/helper-validator-option': 7.25.9 + '@babel/compat-data': 7.28.5 + '@babel/helper-validator-option': 7.27.1 browserslist: 4.24.4 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-module-imports@7.25.9': + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.26.9 - '@babel/types': 7.26.9 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.9)': + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.26.9 - '@babel/helper-module-imports': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 - '@babel/traverse': 7.26.9 + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color - '@babel/helper-plugin-utils@7.26.5': {} + '@babel/helper-plugin-utils@7.27.1': {} '@babel/helper-string-parser@7.25.9': {} + '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-validator-identifier@7.25.9': {} - '@babel/helper-validator-option@7.25.9': {} + '@babel/helper-validator-identifier@7.28.5': {} - '@babel/helpers@7.26.9': + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.28.4': dependencies: - '@babel/template': 7.26.9 - '@babel/types': 7.26.9 + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 '@babel/parser@7.26.9': dependencies: '@babel/types': 7.26.9 - '@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.26.9)': + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.26.9 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-react-jsx-source@7.25.9(@babel/core@7.26.9)': + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.26.9 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 '@babel/runtime@7.26.9': dependencies: regenerator-runtime: 0.14.1 - '@babel/template@7.26.9': + '@babel/template@7.27.2': dependencies: - '@babel/code-frame': 7.26.2 - '@babel/parser': 7.26.9 - '@babel/types': 7.26.9 + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 - '@babel/traverse@7.26.9': + '@babel/traverse@7.28.5': dependencies: - '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.9 - '@babel/parser': 7.26.9 - '@babel/template': 7.26.9 - '@babel/types': 7.26.9 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 debug: 4.4.0 - globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -1843,36 +1939,41 @@ snapshots: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 - '@codemirror/autocomplete@6.18.6': + '@babel/types@7.28.5': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@codemirror/autocomplete@6.19.1': dependencies: - '@codemirror/language': 6.10.8 + '@codemirror/language': 6.11.3 '@codemirror/state': 6.5.2 '@codemirror/view': 6.29.0 '@lezer/common': 1.2.3 '@codemirror/commands@6.8.0': dependencies: - '@codemirror/language': 6.10.8 + '@codemirror/language': 6.11.3 '@codemirror/state': 6.5.2 '@codemirror/view': 6.29.0 '@lezer/common': 1.2.3 - '@codemirror/lang-json@6.0.1': + '@codemirror/lang-json@6.0.2': dependencies: - '@codemirror/language': 6.10.8 + '@codemirror/language': 6.11.3 '@lezer/json': 1.0.3 '@codemirror/lang-yaml@6.1.2': dependencies: - '@codemirror/autocomplete': 6.18.6 - '@codemirror/language': 6.10.8 + '@codemirror/autocomplete': 6.19.1 + '@codemirror/language': 6.11.3 '@codemirror/state': 6.5.2 '@lezer/common': 1.2.3 '@lezer/highlight': 1.2.1 '@lezer/lr': 1.4.2 '@lezer/yaml': 1.0.3 - '@codemirror/language@6.10.8': + '@codemirror/language@6.11.3': dependencies: '@codemirror/state': 6.5.2 '@codemirror/view': 6.29.0 @@ -1883,7 +1984,7 @@ snapshots: '@codemirror/legacy-modes@6.4.0': dependencies: - '@codemirror/language': 6.10.8 + '@codemirror/language': 6.11.3 '@codemirror/lint@6.8.4': dependencies: @@ -1903,7 +2004,7 @@ snapshots: '@codemirror/theme-one-dark@6.1.2': dependencies: - '@codemirror/language': 6.10.8 + '@codemirror/language': 6.11.3 '@codemirror/state': 6.5.2 '@codemirror/view': 6.29.0 '@lezer/highlight': 1.2.1 @@ -2004,33 +2105,50 @@ snapshots: '@floating-ui/core': 1.6.9 '@floating-ui/utils': 0.2.9 - '@floating-ui/react-dom@2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@floating-ui/react-dom@2.1.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@floating-ui/dom': 1.6.13 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) '@floating-ui/utils@0.2.9': {} '@iarna/toml@2.2.5': {} + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/gen-mapping@0.3.8': dependencies: '@jridgewell/set-array': 1.2.1 '@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/set-array@1.2.1': {} '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + '@lezer/common@1.2.3': {} '@lezer/highlight@1.2.1': @@ -2071,454 +2189,480 @@ snapshots: dependencies: '@babel/runtime': 7.26.9 - '@radix-ui/primitive@1.1.1': {} + '@radix-ui/primitive@1.1.3': {} - '@radix-ui/react-arrow@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.0.10 - '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) - '@radix-ui/react-collection@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.1.2(@types/react@19.0.10)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.0.10 - '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) - '@radix-ui/react-compose-refs@1.0.1(@types/react@19.0.10)(react@19.0.0)': + '@radix-ui/react-compose-refs@1.0.1(@types/react@19.2.4)(react@19.2.0)': dependencies: '@babel/runtime': 7.26.9 - react: 19.0.0 + react: 19.2.0 optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - '@radix-ui/react-compose-refs@1.1.1(@types/react@19.0.10)(react@19.0.0)': + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.4)(react@19.2.0)': dependencies: - react: 19.0.0 + react: 19.2.0 optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - '@radix-ui/react-context@1.0.1(@types/react@19.0.10)(react@19.0.0)': + '@radix-ui/react-context@1.0.1(@types/react@19.2.4)(react@19.2.0)': dependencies: '@babel/runtime': 7.26.9 - react: 19.0.0 + react: 19.2.0 optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - '@radix-ui/react-context@1.1.1(@types/react@19.0.10)(react@19.0.0)': + '@radix-ui/react-context@1.1.2(@types/react@19.2.4)(react@19.2.0)': dependencies: - react: 19.0.0 + react: 19.2.0 optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - '@radix-ui/react-dialog@1.0.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-dialog@1.0.5(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@babel/runtime': 7.26.9 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-context': 1.0.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-focus-guards': 1.0.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.0.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.0.2(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@19.2.4)(react@19.2.0) aria-hidden: 1.2.4 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - react-remove-scroll: 2.5.5(@types/react@19.0.10)(react@19.0.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + react-remove-scroll: 2.5.5(@types/react@19.2.4)(react@19.2.0) optionalDependencies: - '@types/react': 19.0.10 - '@types/react-dom': 19.0.4(@types/react@19.0.10) - - '@radix-ui/react-dialog@1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-focus-scope': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-portal': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.1.2(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.10)(react@19.0.0) + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) + + '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.4)(react@19.2.0) aria-hidden: 1.2.4 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - react-remove-scroll: 2.6.3(@types/react@19.0.10)(react@19.0.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + react-remove-scroll: 2.6.3(@types/react@19.2.4)(react@19.2.0) optionalDependencies: - '@types/react': 19.0.10 - '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) - '@radix-ui/react-direction@1.1.0(@types/react@19.0.10)(react@19.0.0)': + '@radix-ui/react-direction@1.1.1(@types/react@19.2.4)(react@19.2.0)': dependencies: - react: 19.0.0 + react: 19.2.0 optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - '@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@babel/runtime': 7.26.9 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@19.0.10)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.0.10 - '@types/react-dom': 19.0.4(@types/react@19.0.10) - - '@radix-ui/react-dismissable-layer@1.1.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@19.0.10)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) + + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.0.10 - '@types/react-dom': 19.0.4(@types/react@19.0.10) - - '@radix-ui/react-dropdown-menu@2.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-menu': 2.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.10)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) + + '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.0.10 - '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) - '@radix-ui/react-focus-guards@1.0.1(@types/react@19.0.10)(react@19.0.0)': + '@radix-ui/react-focus-guards@1.0.1(@types/react@19.2.4)(react@19.2.0)': dependencies: '@babel/runtime': 7.26.9 - react: 19.0.0 + react: 19.2.0 optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - '@radix-ui/react-focus-guards@1.1.1(@types/react@19.0.10)(react@19.0.0)': + '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.4)(react@19.2.0)': dependencies: - react: 19.0.0 + react: 19.2.0 optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - '@radix-ui/react-focus-scope@1.0.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-focus-scope@1.0.4(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@babel/runtime': 7.26.9 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@19.0.10)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.0.10 - '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) - '@radix-ui/react-focus-scope@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.0.10 - '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) - '@radix-ui/react-id@1.0.1(@types/react@19.0.10)(react@19.0.0)': + '@radix-ui/react-id@1.0.1(@types/react@19.2.4)(react@19.2.0)': dependencies: '@babel/runtime': 7.26.9 - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@19.0.10)(react@19.0.0) - react: 19.0.0 + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - '@radix-ui/react-id@1.1.0(@types/react@19.0.10)(react@19.0.0)': + '@radix-ui/react-id@1.1.1(@types/react@19.2.4)(react@19.2.0)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.10)(react@19.0.0) - react: 19.0.0 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - '@radix-ui/react-label@2.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-label@2.1.8(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.0.10 - '@types/react-dom': 19.0.4(@types/react@19.0.10) - - '@radix-ui/react-menu@2.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-focus-scope': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-popper': 1.2.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-portal': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-roving-focus': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.1.2(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0) + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) + + '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.4)(react@19.2.0) aria-hidden: 1.2.4 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - react-remove-scroll: 2.6.3(@types/react@19.0.10)(react@19.0.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + react-remove-scroll: 2.6.3(@types/react@19.2.4)(react@19.2.0) optionalDependencies: - '@types/react': 19.0.10 - '@types/react-dom': 19.0.4(@types/react@19.0.10) - - '@radix-ui/react-popover@1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-focus-scope': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-popper': 1.2.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-portal': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.1.2(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.10)(react@19.0.0) + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) + + '@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.4)(react@19.2.0) aria-hidden: 1.2.4 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - react-remove-scroll: 2.6.3(@types/react@19.0.10)(react@19.0.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + react-remove-scroll: 2.6.3(@types/react@19.2.4)(react@19.2.0) optionalDependencies: - '@types/react': 19.0.10 - '@types/react-dom': 19.0.4(@types/react@19.0.10) - - '@radix-ui/react-popper@1.2.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@floating-ui/react-dom': 2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-arrow': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-use-rect': 1.1.0(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-use-size': 1.1.0(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/rect': 1.1.0 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) + + '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@floating-ui/react-dom': 2.1.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/rect': 1.1.1 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.0.10 - '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) - '@radix-ui/react-portal@1.0.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-portal@1.0.4(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@babel/runtime': 7.26.9 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.0.10 - '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) - '@radix-ui/react-portal@1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.10)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.0.10 - '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) - '@radix-ui/react-presence@1.0.1(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-presence@1.0.1(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@babel/runtime': 7.26.9 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@19.0.10)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.0.10 - '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) - '@radix-ui/react-presence@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.10)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.0.10 - '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) - '@radix-ui/react-primitive@1.0.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-primitive@1.0.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@babel/runtime': 7.26.9 - '@radix-ui/react-slot': 1.0.2(@types/react@19.0.10)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-slot': 1.0.2(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.0.10 - '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) - '@radix-ui/react-primitive@2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@radix-ui/react-slot': 1.1.2(@types/react@19.0.10)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.0.10 - '@types/react-dom': 19.0.4(@types/react@19.0.10) - - '@radix-ui/react-roving-focus@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.10)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) + + '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/react-slot': 1.2.4(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.0.10 - '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) + + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + optionalDependencies: + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) - '@radix-ui/react-slot@1.0.2(@types/react@19.0.10)(react@19.0.0)': + '@radix-ui/react-slot@1.0.2(@types/react@19.2.4)(react@19.2.0)': dependencies: '@babel/runtime': 7.26.9 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@19.0.10)(react@19.0.0) - react: 19.0.0 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - '@radix-ui/react-slot@1.1.2(@types/react@19.0.10)(react@19.0.0)': + '@radix-ui/react-slot@1.2.3(@types/react@19.2.4)(react@19.2.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0) - react: 19.0.0 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 optionalDependencies: - '@types/react': 19.0.10 - - '@radix-ui/react-tabs@1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-roving-focus': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.10)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@types/react': 19.2.4 + + '@radix-ui/react-slot@1.2.4(@types/react@19.2.4)(react@19.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.4 + + '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) optionalDependencies: - '@types/react': 19.0.10 - '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@types/react': 19.2.4 + '@types/react-dom': 19.2.3(@types/react@19.2.4) - '@radix-ui/react-use-callback-ref@1.0.1(@types/react@19.0.10)(react@19.0.0)': + '@radix-ui/react-use-callback-ref@1.0.1(@types/react@19.2.4)(react@19.2.0)': dependencies: '@babel/runtime': 7.26.9 - react: 19.0.0 + react: 19.2.0 optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - '@radix-ui/react-use-callback-ref@1.1.0(@types/react@19.0.10)(react@19.0.0)': + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.4)(react@19.2.0)': dependencies: - react: 19.0.0 + react: 19.2.0 optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - '@radix-ui/react-use-controllable-state@1.0.1(@types/react@19.0.10)(react@19.0.0)': + '@radix-ui/react-use-controllable-state@1.0.1(@types/react@19.2.4)(react@19.2.0)': dependencies: '@babel/runtime': 7.26.9 - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@19.0.10)(react@19.0.0) - react: 19.0.0 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - '@radix-ui/react-use-controllable-state@1.1.0(@types/react@19.0.10)(react@19.0.0)': + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.4)(react@19.2.0)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0) - react: 19.0.0 + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.4)(react@19.2.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - '@radix-ui/react-use-escape-keydown@1.0.3(@types/react@19.0.10)(react@19.0.0)': + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.4)(react@19.2.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 + optionalDependencies: + '@types/react': 19.2.4 + + '@radix-ui/react-use-escape-keydown@1.0.3(@types/react@19.2.4)(react@19.2.0)': dependencies: '@babel/runtime': 7.26.9 - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@19.0.10)(react@19.0.0) - react: 19.0.0 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@19.0.10)(react@19.0.0)': + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.4)(react@19.2.0)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0) - react: 19.0.0 + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - '@radix-ui/react-use-layout-effect@1.0.1(@types/react@19.0.10)(react@19.0.0)': + '@radix-ui/react-use-layout-effect@1.0.1(@types/react@19.2.4)(react@19.2.0)': dependencies: '@babel/runtime': 7.26.9 - react: 19.0.0 + react: 19.2.0 optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - '@radix-ui/react-use-layout-effect@1.1.0(@types/react@19.0.10)(react@19.0.0)': + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.4)(react@19.2.0)': dependencies: - react: 19.0.0 + react: 19.2.0 optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - '@radix-ui/react-use-rect@1.1.0(@types/react@19.0.10)(react@19.0.0)': + '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.4)(react@19.2.0)': dependencies: - '@radix-ui/rect': 1.1.0 - react: 19.0.0 + '@radix-ui/rect': 1.1.1 + react: 19.2.0 optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - '@radix-ui/react-use-size@1.1.0(@types/react@19.0.10)(react@19.0.0)': + '@radix-ui/react-use-size@1.1.1(@types/react@19.2.4)(react@19.2.0)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.10)(react@19.0.0) - react: 19.0.0 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.4)(react@19.2.0) + react: 19.2.0 optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 + + '@radix-ui/rect@1.1.1': {} - '@radix-ui/rect@1.1.0': {} + '@rolldown/pluginutils@1.0.0-beta.27': {} '@rollup/rollup-android-arm-eabi@4.35.0': optional: true @@ -2577,66 +2721,73 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.35.0': optional: true - '@tailwindcss/node@4.0.12': + '@tailwindcss/node@4.1.17': dependencies: - enhanced-resolve: 5.18.1 - jiti: 2.4.2 - tailwindcss: 4.0.12 + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.18.3 + jiti: 2.6.1 + lightningcss: 1.30.2 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.1.17 - '@tailwindcss/oxide-android-arm64@4.0.12': + '@tailwindcss/oxide-android-arm64@4.1.17': optional: true - '@tailwindcss/oxide-darwin-arm64@4.0.12': + '@tailwindcss/oxide-darwin-arm64@4.1.17': optional: true - '@tailwindcss/oxide-darwin-x64@4.0.12': + '@tailwindcss/oxide-darwin-x64@4.1.17': optional: true - '@tailwindcss/oxide-freebsd-x64@4.0.12': + '@tailwindcss/oxide-freebsd-x64@4.1.17': optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.12': + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17': optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.0.12': + '@tailwindcss/oxide-linux-arm64-gnu@4.1.17': optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.0.12': + '@tailwindcss/oxide-linux-arm64-musl@4.1.17': optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.0.12': + '@tailwindcss/oxide-linux-x64-gnu@4.1.17': optional: true - '@tailwindcss/oxide-linux-x64-musl@4.0.12': + '@tailwindcss/oxide-linux-x64-musl@4.1.17': optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.0.12': + '@tailwindcss/oxide-wasm32-wasi@4.1.17': optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.0.12': + '@tailwindcss/oxide-win32-arm64-msvc@4.1.17': optional: true - '@tailwindcss/oxide@4.0.12': + '@tailwindcss/oxide-win32-x64-msvc@4.1.17': + optional: true + + '@tailwindcss/oxide@4.1.17': optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.0.12 - '@tailwindcss/oxide-darwin-arm64': 4.0.12 - '@tailwindcss/oxide-darwin-x64': 4.0.12 - '@tailwindcss/oxide-freebsd-x64': 4.0.12 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.0.12 - '@tailwindcss/oxide-linux-arm64-gnu': 4.0.12 - '@tailwindcss/oxide-linux-arm64-musl': 4.0.12 - '@tailwindcss/oxide-linux-x64-gnu': 4.0.12 - '@tailwindcss/oxide-linux-x64-musl': 4.0.12 - '@tailwindcss/oxide-win32-arm64-msvc': 4.0.12 - '@tailwindcss/oxide-win32-x64-msvc': 4.0.12 - - '@tailwindcss/vite@4.0.12(vite@6.2.1(@types/node@20.17.24)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.7.1))': - dependencies: - '@tailwindcss/node': 4.0.12 - '@tailwindcss/oxide': 4.0.12 - lightningcss: 1.29.2 - tailwindcss: 4.0.12 - vite: 6.2.1(@types/node@20.17.24)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.7.1) + '@tailwindcss/oxide-android-arm64': 4.1.17 + '@tailwindcss/oxide-darwin-arm64': 4.1.17 + '@tailwindcss/oxide-darwin-x64': 4.1.17 + '@tailwindcss/oxide-freebsd-x64': 4.1.17 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.17 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.17 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.17 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.17 + '@tailwindcss/oxide-linux-x64-musl': 4.1.17 + '@tailwindcss/oxide-wasm32-wasi': 4.1.17 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.17 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.17 + + '@tailwindcss/vite@4.1.17(vite@6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.7.1))': + dependencies: + '@tailwindcss/node': 4.1.17 + '@tailwindcss/oxide': 4.1.17 + tailwindcss: 4.1.17 + vite: 6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.7.1) '@types/babel__core@7.20.5': dependencies: @@ -2659,71 +2810,70 @@ snapshots: dependencies: '@babel/types': 7.26.9 - '@types/cookie@0.6.0': {} - '@types/estree@1.0.6': {} - '@types/node@20.17.24': + '@types/node@20.19.25': dependencies: - undici-types: 6.19.8 + undici-types: 6.21.0 - '@types/react-dom@19.0.4(@types/react@19.0.10)': + '@types/react-dom@19.2.3(@types/react@19.2.4)': dependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - '@types/react@19.0.10': + '@types/react@19.2.4': dependencies: csstype: 3.1.3 - '@uiw/codemirror-extensions-basic-setup@4.23.10(@codemirror/autocomplete@6.18.6)(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/view@6.29.0)': + '@uiw/codemirror-extensions-basic-setup@4.25.3(@codemirror/autocomplete@6.19.1)(@codemirror/commands@6.8.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/view@6.29.0)': dependencies: - '@codemirror/autocomplete': 6.18.6 + '@codemirror/autocomplete': 6.19.1 '@codemirror/commands': 6.8.0 - '@codemirror/language': 6.10.8 + '@codemirror/language': 6.11.3 '@codemirror/lint': 6.8.4 '@codemirror/search': 6.5.10 '@codemirror/state': 6.5.2 '@codemirror/view': 6.29.0 - '@uiw/codemirror-theme-github@4.23.10(@codemirror/language@6.10.8)(@codemirror/state@6.5.2)(@codemirror/view@6.29.0)': + '@uiw/codemirror-theme-github@4.25.3(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.29.0)': dependencies: - '@uiw/codemirror-themes': 4.23.10(@codemirror/language@6.10.8)(@codemirror/state@6.5.2)(@codemirror/view@6.29.0) + '@uiw/codemirror-themes': 4.25.3(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.29.0) transitivePeerDependencies: - '@codemirror/language' - '@codemirror/state' - '@codemirror/view' - '@uiw/codemirror-themes@4.23.10(@codemirror/language@6.10.8)(@codemirror/state@6.5.2)(@codemirror/view@6.29.0)': + '@uiw/codemirror-themes@4.25.3(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.29.0)': dependencies: - '@codemirror/language': 6.10.8 + '@codemirror/language': 6.11.3 '@codemirror/state': 6.5.2 '@codemirror/view': 6.29.0 - '@uiw/react-codemirror@4.23.10(@babel/runtime@7.26.9)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.29.0)(codemirror@6.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@uiw/react-codemirror@4.25.3(@babel/runtime@7.26.9)(@codemirror/autocomplete@6.19.1)(@codemirror/language@6.11.3)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.29.0)(codemirror@6.0.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@babel/runtime': 7.26.9 '@codemirror/commands': 6.8.0 '@codemirror/state': 6.5.2 '@codemirror/theme-one-dark': 6.1.2 '@codemirror/view': 6.29.0 - '@uiw/codemirror-extensions-basic-setup': 4.23.10(@codemirror/autocomplete@6.18.6)(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/view@6.29.0) + '@uiw/codemirror-extensions-basic-setup': 4.25.3(@codemirror/autocomplete@6.19.1)(@codemirror/commands@6.8.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/view@6.29.0) codemirror: 6.0.1 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) transitivePeerDependencies: - '@codemirror/autocomplete' - '@codemirror/language' - '@codemirror/lint' - '@codemirror/search' - '@vitejs/plugin-react@4.3.4(vite@6.2.1(@types/node@20.17.24)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.7.1))': + '@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.7.1))': dependencies: - '@babel/core': 7.26.9 - '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.9) - '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.9) + '@babel/core': 7.28.5 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.5) + '@rolldown/pluginutils': 1.0.0-beta.27 '@types/babel__core': 7.20.5 - react-refresh: 0.14.2 - vite: 6.2.1(@types/node@20.17.24)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.7.1) + react-refresh: 0.17.0 + vite: 6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.7.1) transitivePeerDependencies: - supports-color @@ -2769,21 +2919,21 @@ snapshots: clsx@2.1.1: {} - cmdk@1.0.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + cmdk@1.0.0(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: - '@radix-ui/react-dialog': 1.0.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-dialog': 1.0.5(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@19.2.3(@types/react@19.2.4))(@types/react@19.2.4)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) transitivePeerDependencies: - '@types/react' - '@types/react-dom' codemirror@6.0.1: dependencies: - '@codemirror/autocomplete': 6.18.6 + '@codemirror/autocomplete': 6.19.1 '@codemirror/commands': 6.8.0 - '@codemirror/language': 6.10.8 + '@codemirror/language': 6.11.3 '@codemirror/lint': 6.8.4 '@codemirror/search': 6.5.10 '@codemirror/state': 6.5.2 @@ -2811,7 +2961,7 @@ snapshots: electron-to-chromium@1.5.113: {} - enhanced-resolve@5.18.1: + enhanced-resolve@5.18.3: dependencies: graceful-fs: 4.2.11 tapable: 2.2.1 @@ -2858,6 +3008,10 @@ snapshots: dependencies: reusify: 1.1.0 + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -2879,8 +3033,6 @@ snapshots: dependencies: is-glob: 4.0.3 - globals@11.12.0: {} - globals@15.15.0: {} graceful-fs@4.2.11: {} @@ -2897,7 +3049,7 @@ snapshots: is-number@7.0.0: {} - jiti@2.4.2: {} + jiti@2.6.1: {} js-tokens@4.0.0: {} @@ -2911,58 +3063,66 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 - lightningcss-darwin-arm64@1.29.2: + lightningcss-android-arm64@1.30.2: + optional: true + + lightningcss-darwin-arm64@1.30.2: optional: true - lightningcss-darwin-x64@1.29.2: + lightningcss-darwin-x64@1.30.2: optional: true - lightningcss-freebsd-x64@1.29.2: + lightningcss-freebsd-x64@1.30.2: optional: true - lightningcss-linux-arm-gnueabihf@1.29.2: + lightningcss-linux-arm-gnueabihf@1.30.2: optional: true - lightningcss-linux-arm64-gnu@1.29.2: + lightningcss-linux-arm64-gnu@1.30.2: optional: true - lightningcss-linux-arm64-musl@1.29.2: + lightningcss-linux-arm64-musl@1.30.2: optional: true - lightningcss-linux-x64-gnu@1.29.2: + lightningcss-linux-x64-gnu@1.30.2: optional: true - lightningcss-linux-x64-musl@1.29.2: + lightningcss-linux-x64-musl@1.30.2: optional: true - lightningcss-win32-arm64-msvc@1.29.2: + lightningcss-win32-arm64-msvc@1.30.2: optional: true - lightningcss-win32-x64-msvc@1.29.2: + lightningcss-win32-x64-msvc@1.30.2: optional: true - lightningcss@1.29.2: + lightningcss@1.30.2: dependencies: detect-libc: 2.0.3 optionalDependencies: - lightningcss-darwin-arm64: 1.29.2 - lightningcss-darwin-x64: 1.29.2 - lightningcss-freebsd-x64: 1.29.2 - lightningcss-linux-arm-gnueabihf: 1.29.2 - lightningcss-linux-arm64-gnu: 1.29.2 - lightningcss-linux-arm64-musl: 1.29.2 - lightningcss-linux-x64-gnu: 1.29.2 - lightningcss-linux-x64-musl: 1.29.2 - lightningcss-win32-arm64-msvc: 1.29.2 - lightningcss-win32-x64-msvc: 1.29.2 + lightningcss-android-arm64: 1.30.2 + lightningcss-darwin-arm64: 1.30.2 + lightningcss-darwin-x64: 1.30.2 + lightningcss-freebsd-x64: 1.30.2 + lightningcss-linux-arm-gnueabihf: 1.30.2 + lightningcss-linux-arm64-gnu: 1.30.2 + lightningcss-linux-arm64-musl: 1.30.2 + lightningcss-linux-x64-gnu: 1.30.2 + lightningcss-linux-x64-musl: 1.30.2 + lightningcss-win32-arm64-msvc: 1.30.2 + lightningcss-win32-x64-msvc: 1.30.2 lru-cache@5.1.1: dependencies: yallist: 3.1.1 - lucide-react@0.479.0(react@19.0.0): + lucide-react@0.479.0(react@19.2.0): + dependencies: + react: 19.2.0 + + magic-string@0.30.21: dependencies: - react: 19.0.0 + '@jridgewell/sourcemap-codec': 1.5.5 merge2@1.4.1: {} @@ -2975,10 +3135,10 @@ snapshots: nanoid@3.3.9: {} - next-themes@0.4.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + next-themes@0.4.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) node-releases@2.0.19: {} @@ -2990,6 +3150,8 @@ snapshots: picomatch@2.3.1: {} + picomatch@4.0.3: {} + postcss@8.5.3: dependencies: nanoid: 3.3.9 @@ -2998,68 +3160,66 @@ snapshots: queue-microtask@1.2.3: {} - react-dom@19.0.0(react@19.0.0): + react-dom@19.2.0(react@19.2.0): dependencies: - react: 19.0.0 - scheduler: 0.25.0 + react: 19.2.0 + scheduler: 0.27.0 - react-refresh@0.14.2: {} + react-refresh@0.17.0: {} - react-remove-scroll-bar@2.3.8(@types/react@19.0.10)(react@19.0.0): + react-remove-scroll-bar@2.3.8(@types/react@19.2.4)(react@19.2.0): dependencies: - react: 19.0.0 - react-style-singleton: 2.2.3(@types/react@19.0.10)(react@19.0.0) + react: 19.2.0 + react-style-singleton: 2.2.3(@types/react@19.2.4)(react@19.2.0) tslib: 2.8.1 optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - react-remove-scroll@2.5.5(@types/react@19.0.10)(react@19.0.0): + react-remove-scroll@2.5.5(@types/react@19.2.4)(react@19.2.0): dependencies: - react: 19.0.0 - react-remove-scroll-bar: 2.3.8(@types/react@19.0.10)(react@19.0.0) - react-style-singleton: 2.2.3(@types/react@19.0.10)(react@19.0.0) + react: 19.2.0 + react-remove-scroll-bar: 2.3.8(@types/react@19.2.4)(react@19.2.0) + react-style-singleton: 2.2.3(@types/react@19.2.4)(react@19.2.0) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.0.10)(react@19.0.0) - use-sidecar: 1.1.3(@types/react@19.0.10)(react@19.0.0) + use-callback-ref: 1.3.3(@types/react@19.2.4)(react@19.2.0) + use-sidecar: 1.1.3(@types/react@19.2.4)(react@19.2.0) optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - react-remove-scroll@2.6.3(@types/react@19.0.10)(react@19.0.0): + react-remove-scroll@2.6.3(@types/react@19.2.4)(react@19.2.0): dependencies: - react: 19.0.0 - react-remove-scroll-bar: 2.3.8(@types/react@19.0.10)(react@19.0.0) - react-style-singleton: 2.2.3(@types/react@19.0.10)(react@19.0.0) + react: 19.2.0 + react-remove-scroll-bar: 2.3.8(@types/react@19.2.4)(react@19.2.0) + react-style-singleton: 2.2.3(@types/react@19.2.4)(react@19.2.0) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.0.10)(react@19.0.0) - use-sidecar: 1.1.3(@types/react@19.0.10)(react@19.0.0) + use-callback-ref: 1.3.3(@types/react@19.2.4)(react@19.2.0) + use-sidecar: 1.1.3(@types/react@19.2.4)(react@19.2.0) optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - react-router-dom@7.4.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + react-router-dom@7.9.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - react-router: 7.4.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + react-router: 7.9.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react-router@7.4.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + react-router@7.9.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: - '@types/cookie': 0.6.0 cookie: 1.0.2 - react: 19.0.0 + react: 19.2.0 set-cookie-parser: 2.7.1 - turbo-stream: 2.4.0 optionalDependencies: - react-dom: 19.0.0(react@19.0.0) + react-dom: 19.2.0(react@19.2.0) - react-style-singleton@2.2.3(@types/react@19.0.10)(react@19.0.0): + react-style-singleton@2.2.3(@types/react@19.2.4)(react@19.2.0): dependencies: get-nonce: 1.0.1 - react: 19.0.0 + react: 19.2.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - react@19.0.0: {} + react@19.2.0: {} readdirp@3.6.0: dependencies: @@ -3098,31 +3258,36 @@ snapshots: dependencies: queue-microtask: 1.2.3 - scheduler@0.25.0: {} + scheduler@0.27.0: {} semver@6.3.1: {} set-cookie-parser@2.7.1: {} - sonner@2.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + sonner@2.0.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) source-map-js@1.2.1: {} style-mod@4.1.2: {} - tailwind-merge@3.0.2: {} + tailwind-merge@3.4.0: {} - tailwindcss-animate@1.0.7(tailwindcss@4.0.12): + tailwindcss-animate@1.0.7(tailwindcss@4.1.17): dependencies: - tailwindcss: 4.0.12 + tailwindcss: 4.1.17 - tailwindcss@4.0.12: {} + tailwindcss@4.1.17: {} tapable@2.2.1: {} + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -3131,11 +3296,9 @@ snapshots: tslib@2.8.1: {} - turbo-stream@2.4.0: {} - typescript@5.7.3: {} - undici-types@6.19.8: {} + undici-types@6.21.0: {} universalify@2.0.1: {} @@ -3145,40 +3308,43 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 - use-callback-ref@1.3.3(@types/react@19.0.10)(react@19.0.0): + use-callback-ref@1.3.3(@types/react@19.2.4)(react@19.2.0): dependencies: - react: 19.0.0 + react: 19.2.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - use-sidecar@1.1.3(@types/react@19.0.10)(react@19.0.0): + use-sidecar@1.1.3(@types/react@19.2.4)(react@19.2.0): dependencies: detect-node-es: 1.1.0 - react: 19.0.0 + react: 19.2.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.0.10 + '@types/react': 19.2.4 - vite-plugin-static-copy@2.3.0(vite@6.2.1(@types/node@20.17.24)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.7.1)): + vite-plugin-static-copy@2.3.2(vite@6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.7.1)): dependencies: chokidar: 3.6.0 fast-glob: 3.3.3 fs-extra: 11.3.0 p-map: 7.0.3 picocolors: 1.1.1 - vite: 6.2.1(@types/node@20.17.24)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.7.1) + vite: 6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.7.1) - vite@6.2.1(@types/node@20.17.24)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.7.1): + vite@6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.7.1): dependencies: esbuild: 0.25.1 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 postcss: 8.5.3 rollup: 4.35.0 + tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 20.17.24 + '@types/node': 20.19.25 fsevents: 2.3.3 - jiti: 2.4.2 - lightningcss: 1.29.2 + jiti: 2.6.1 + lightningcss: 1.30.2 yaml: 2.7.1 w3c-keyname@2.2.8: {} @@ -3187,7 +3353,7 @@ snapshots: yaml@2.7.1: {} - zustand@5.0.3(@types/react@19.0.10)(react@19.0.0): + zustand@5.0.8(@types/react@19.2.4)(react@19.2.0): optionalDependencies: - '@types/react': 19.0.10 - react: 19.0.0 + '@types/react': 19.2.4 + react: 19.2.0 From b56d2b6d8cca0b226408fc2a8ccf78ada4b7c260 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Fri, 28 Nov 2025 01:46:41 -0600 Subject: [PATCH 13/91] Enhance GitHub workflows: add production deployment configuration and target 'canary' branch for pull requests. --- .github/workflows/deploy-preview.yml | 3 +++ .../{cloudflare-pages-production.yml => deploy-production.yml} | 0 2 files changed, 3 insertions(+) rename .github/workflows/{cloudflare-pages-production.yml => deploy-production.yml} (100%) diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/deploy-preview.yml index 2f0674332..ed0be503d 100644 --- a/.github/workflows/deploy-preview.yml +++ b/.github/workflows/deploy-preview.yml @@ -5,6 +5,9 @@ on: workflows: [Build Preview Deployment] types: - completed + pull_request: + branches: + - canary permissions: actions: read diff --git a/.github/workflows/cloudflare-pages-production.yml b/.github/workflows/deploy-production.yml similarity index 100% rename from .github/workflows/cloudflare-pages-production.yml rename to .github/workflows/deploy-production.yml From a91730e5822804fe66c3d3439ce42d78f4eb0491 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Fri, 28 Nov 2025 01:56:31 -0600 Subject: [PATCH 14/91] Refactor GitHub workflow: comment out build preview steps for clarity and future modifications. --- .github/workflows/build-preview.yml | 58 ++++++++++++++--------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/.github/workflows/build-preview.yml b/.github/workflows/build-preview.yml index 3c56b3f76..18f6b804c 100644 --- a/.github/workflows/build-preview.yml +++ b/.github/workflows/build-preview.yml @@ -1,35 +1,35 @@ -name: Build Preview Deployment +# name: Build Preview Deployment -concurrency: - group: ${{ github.workflow }}-${{ github.event.number || github.sha }} - cancel-in-progress: true +# concurrency: +# group: ${{ github.workflow }}-${{ github.event.number || github.sha }} +# cancel-in-progress: true -on: - pull_request: - types: [opened, synchronize] +# on: +# pull_request: +# types: [opened, synchronize] -jobs: - build-preview: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - with: - version: 8 - - uses: actions/setup-node@v4 - with: - node-version: 20 +# jobs: +# build-preview: +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: pnpm/action-setup@v4 +# with: +# version: 8 +# - uses: actions/setup-node@v4 +# with: +# node-version: 20 - - name: Install dependencies - working-directory: app - run: pnpm install +# - name: Install dependencies +# working-directory: app +# run: pnpm install - - name: Build - working-directory: app - run: pnpm build +# - name: Build +# working-directory: app +# run: pnpm build - - name: Upload build artifact - uses: actions/upload-artifact@v4 - with: - name: preview-build - path: app/dist \ No newline at end of file +# - name: Upload build artifact +# uses: actions/upload-artifact@v4 +# with: +# name: preview-build +# path: app/dist \ No newline at end of file From 3ec51b85bf876baed74d7d47add99a9c19221499 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Fri, 28 Nov 2025 02:03:23 -0600 Subject: [PATCH 15/91] Remove unnecessary blank line in deploy-preview.yml for improved readability. --- .github/workflows/deploy-preview.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/deploy-preview.yml index ed0be503d..a342ac7a4 100644 --- a/.github/workflows/deploy-preview.yml +++ b/.github/workflows/deploy-preview.yml @@ -8,7 +8,6 @@ on: pull_request: branches: - canary - permissions: actions: read deployments: write From 8ec4ad1d9ca3427ddc243aa0d90e83e88719012b Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Fri, 28 Nov 2025 02:03:42 -0600 Subject: [PATCH 16/91] Refactor GitHub workflow: uncomment build preview steps for improved deployment process and clarity. --- .github/workflows/build-preview.yml | 58 ++++++++++++++--------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/.github/workflows/build-preview.yml b/.github/workflows/build-preview.yml index 18f6b804c..3c56b3f76 100644 --- a/.github/workflows/build-preview.yml +++ b/.github/workflows/build-preview.yml @@ -1,35 +1,35 @@ -# name: Build Preview Deployment +name: Build Preview Deployment -# concurrency: -# group: ${{ github.workflow }}-${{ github.event.number || github.sha }} -# cancel-in-progress: true +concurrency: + group: ${{ github.workflow }}-${{ github.event.number || github.sha }} + cancel-in-progress: true -# on: -# pull_request: -# types: [opened, synchronize] +on: + pull_request: + types: [opened, synchronize] -# jobs: -# build-preview: -# runs-on: ubuntu-latest -# steps: -# - uses: actions/checkout@v4 -# - uses: pnpm/action-setup@v4 -# with: -# version: 8 -# - uses: actions/setup-node@v4 -# with: -# node-version: 20 +jobs: + build-preview: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + with: + version: 8 + - uses: actions/setup-node@v4 + with: + node-version: 20 -# - name: Install dependencies -# working-directory: app -# run: pnpm install + - name: Install dependencies + working-directory: app + run: pnpm install -# - name: Build -# working-directory: app -# run: pnpm build + - name: Build + working-directory: app + run: pnpm build -# - name: Upload build artifact -# uses: actions/upload-artifact@v4 -# with: -# name: preview-build -# path: app/dist \ No newline at end of file + - name: Upload build artifact + uses: actions/upload-artifact@v4 + with: + name: preview-build + path: app/dist \ No newline at end of file From 7efe2226ff5ce7872315be874082c5dddceb3563 Mon Sep 17 00:00:00 2001 From: xSiumauricio Date: Fri, 28 Nov 2025 02:07:05 -0600 Subject: [PATCH 17/91] Update template.toml (#555) * Update template.toml * Update template.toml * Update template.toml --- blueprints/ackee/template.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/blueprints/ackee/template.toml b/blueprints/ackee/template.toml index ebffa3e22..75ef3fe7e 100644 --- a/blueprints/ackee/template.toml +++ b/blueprints/ackee/template.toml @@ -10,6 +10,7 @@ serviceName = "ackee" port = 3000 host = "${domain}" + [config.env] ACKEE_USERNAME = "${ACKEE_USERNAME}" ACKEE_PASSWORD = "${ACKEE_PASSWORD}" From 88bff5f2a0ef872ada07db1252a96088ed030819 Mon Sep 17 00:00:00 2001 From: Rabithua Date: Fri, 28 Nov 2025 19:15:08 +0800 Subject: [PATCH 18/91] fix: change VITE_API_BASE to http:// for traefik.me compatibility --- blueprints/rote/template.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/rote/template.toml b/blueprints/rote/template.toml index f1957c006..90dcc8ad7 100644 --- a/blueprints/rote/template.toml +++ b/blueprints/rote/template.toml @@ -18,6 +18,6 @@ host = "${backend_domain}" [config.env] POSTGRES_PASSWORD = "${postgres_password}" IMAGE_TAG = "${image_tag}" -VITE_API_BASE = "https://${backend_domain}" +VITE_API_BASE = "http://${backend_domain}" [[config.mounts]] From b4ebb8d9c5ffd7fc3d6cdb1e1dcfe5579add19ad Mon Sep 17 00:00:00 2001 From: Crackvignoule Date: Sat, 29 Nov 2025 10:32:45 +0100 Subject: [PATCH 19/91] changed image from sknnr/enshrouded-dedicated-server to mornedhels/enshrouded-server for autoupdate and easier config --- blueprints/enshrouded/docker-compose.yml | 19 +++++++++++++------ meta.json | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/blueprints/enshrouded/docker-compose.yml b/blueprints/enshrouded/docker-compose.yml index 9c92efb3e..614dd36e3 100644 --- a/blueprints/enshrouded/docker-compose.yml +++ b/blueprints/enshrouded/docker-compose.yml @@ -1,18 +1,25 @@ services: enshrouded: - image: sknnr/enshrouded-dedicated-server:latest + image: mornedhels/enshrouded-server:latest + container_name: enshrouded + hostname: enshrouded restart: unless-stopped + stop_grace_period: 90s ports: - "15637:15637/udp" - "27015:27015/udp" + volumes: + - enshrouded-persistent-data:/opt/enshrouded + # only add ntsync device if your kernel supports it (6.14 or newer) + # devices: + # - /dev/ntsync:/dev/ntsync environment: - SERVER_NAME=${SERVER_NAME} - SERVER_PASSWORD=${SERVER_PASSWORD} - - PORT=15637 - - SERVER_SLOTS=6 - - SERVER_IP=0.0.0.0 - volumes: - - enshrouded-persistent-data:/home/steam/enshrouded/savegame + - SERVER_SLOT_COUNT=6 + - UPDATE_CRON=0 3 * * * + - PUID=4711 + - PGID=4711 volumes: enshrouded-persistent-data: \ No newline at end of file diff --git a/meta.json b/meta.json index 5cea31108..2a97174dc 100644 --- a/meta.json +++ b/meta.json @@ -1998,7 +1998,7 @@ "description": "Enshrouded steam dedicated server.", "logo": "enshrouded.png", "links": { - "github": "https://github.com/jsknnr/enshrouded-server", + "github": "https://github.com/mornedhels/enshrouded-server", "website": "", "docs": "" }, From 3205b672c41fba8d0cb07530a121a1542c7832f0 Mon Sep 17 00:00:00 2001 From: florianheysen <39408021+florianheysen@users.noreply.github.com> Date: Wed, 3 Dec 2025 17:01:30 +0100 Subject: [PATCH 20/91] Add Openinary Template (#567) * feat: add Openinary template * feat: update Openinary configuration to support ALLOWED_ORIGIN and refactor domain variable --- blueprints/openinary/docker-compose.yml | 26 ++++++++++++++++ blueprints/openinary/openinary.svg | 18 +++++++++++ blueprints/openinary/template.toml | 40 +++++++++++++++++++++++++ meta.json | 19 ++++++++++++ 4 files changed, 103 insertions(+) create mode 100644 blueprints/openinary/docker-compose.yml create mode 100644 blueprints/openinary/openinary.svg create mode 100644 blueprints/openinary/template.toml diff --git a/blueprints/openinary/docker-compose.yml b/blueprints/openinary/docker-compose.yml new file mode 100644 index 000000000..0469c72b9 --- /dev/null +++ b/blueprints/openinary/docker-compose.yml @@ -0,0 +1,26 @@ +version: "3.8" +services: + openinary: + image: openinary/openinary:${IMAGE_TAG:-latest} + pull_policy: always + restart: unless-stopped + expose: + - 3000 + environment: + NODE_ENV: production + MODE: fullstack + NEXT_PUBLIC_API_BASE_URL: ${NEXT_PUBLIC_API_BASE_URL:-/api} + BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET} + BETTER_AUTH_URL: ${BETTER_AUTH_URL} + ALLOWED_ORIGIN: ${ALLOWED_ORIGIN:-http://${domain}} + DOCKER_CONTAINER: "true" + volumes: + - cache-data:/app/apps/api/cache + - public-files:/app/apps/api/public + - db-data:/app/data + - db-data:/app/web-standalone/data + +volumes: + cache-data: + public-files: + db-data: \ No newline at end of file diff --git a/blueprints/openinary/openinary.svg b/blueprints/openinary/openinary.svg new file mode 100644 index 000000000..2a902cd67 --- /dev/null +++ b/blueprints/openinary/openinary.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/blueprints/openinary/template.toml b/blueprints/openinary/template.toml new file mode 100644 index 000000000..ea317117d --- /dev/null +++ b/blueprints/openinary/template.toml @@ -0,0 +1,40 @@ +[variables] +main_domain = "${domain}" +image_tag = "latest" +better_auth_secret = "${password:64}" +better_auth_url = "http://${main_domain}" +allowed_origin = "http://${main_domain}" +next_public_api_base_url = "/api" + +[config] +[[config.domains]] +serviceName = "openinary" +port = 3000 +host = "${main_domain}" + +[config.env] +IMAGE_TAG = "${image_tag}" +BETTER_AUTH_SECRET = "${better_auth_secret}" +BETTER_AUTH_URL = "${better_auth_url}" +ALLOWED_ORIGIN = "${allowed_origin}" +NEXT_PUBLIC_API_BASE_URL = "${next_public_api_base_url}" + +[[config.mounts]] +serviceName = "openinary" +volumeName = "cache-data" +mountPath = "/app/apps/api/cache" + +[[config.mounts]] +serviceName = "openinary" +volumeName = "public-files" +mountPath = "/app/apps/api/public" + +[[config.mounts]] +serviceName = "openinary" +volumeName = "db-data" +mountPath = "/app/data" + +[[config.mounts]] +serviceName = "openinary" +volumeName = "db-data" +mountPath = "/app/web-standalone/data" \ No newline at end of file diff --git a/meta.json b/meta.json index cbfc03c3c..a6dbd358a 100644 --- a/meta.json +++ b/meta.json @@ -4177,6 +4177,25 @@ "openai" ] }, + { + "id": "openinary", + "name": "Openinary", + "version": "latest", + "description": "Openinary is a self-hosted Cloudinary alternative.", + "logo": "openinary.svg", + "links": { + "github": "https://github.com/openinary/openinary", + "website": "https://openinary.dev", + "docs": "https://docs.openinary.dev" + }, + "tags": [ + "media", + "images", + "videos", + "cloudinary-alternative", + "developer-tools" + ] + }, { "id": "openpanel", "name": "OpenPanel", From f569d1379c10ff76c3aa782ed713a0aa5dfd82bd Mon Sep 17 00:00:00 2001 From: Thiago MadPin Date: Wed, 3 Dec 2025 16:19:27 +0000 Subject: [PATCH 21/91] fix: correct DEFAULT_DOMAIN environment variable reference in docker-compose.yml (#562) --- blueprints/kutt/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/kutt/docker-compose.yml b/blueprints/kutt/docker-compose.yml index 5ee1b971a..947f4d262 100644 --- a/blueprints/kutt/docker-compose.yml +++ b/blueprints/kutt/docker-compose.yml @@ -7,7 +7,7 @@ services: environment: DB_FILENAME: "/var/lib/kutt/data.sqlite" JWT_SECRET: ${JWT_SECRET} - DEFAULT_DOMAIN: ${DOMAIN} + DEFAULT_DOMAIN: ${DEFAULT_DOMAIN} TRUST_PROXY: ${TRUST_PROXY} DISALLOW_ANONYMOUS_LINKS: ${DISALLOW_ANONYMOUS_LINKS} CUSTOM_DOMAIN_USE_HTTPS: ${CUSTOM_DOMAIN_USE_HTTPS} From 5905579eabde81429695db2755105f4cfa2d0ed5 Mon Sep 17 00:00:00 2001 From: BlinkStrike <18644035+BlinkStrike@users.noreply.github.com> Date: Sun, 7 Dec 2025 13:46:38 +0400 Subject: [PATCH 22/91] add rustfs template (#568) --- blueprints/rustfs/docker-compose.yml | 20 ++++++++++++++++++++ blueprints/rustfs/logo.svg | 1 + blueprints/rustfs/meta-entry.json | 18 ++++++++++++++++++ blueprints/rustfs/template.toml | 25 +++++++++++++++++++++++++ meta.json | 18 ++++++++++++++++++ 5 files changed, 82 insertions(+) create mode 100644 blueprints/rustfs/docker-compose.yml create mode 100644 blueprints/rustfs/logo.svg create mode 100644 blueprints/rustfs/meta-entry.json create mode 100644 blueprints/rustfs/template.toml diff --git a/blueprints/rustfs/docker-compose.yml b/blueprints/rustfs/docker-compose.yml new file mode 100644 index 000000000..cbedbae6f --- /dev/null +++ b/blueprints/rustfs/docker-compose.yml @@ -0,0 +1,20 @@ +version: "3.8" + +services: + rustfs: + image: rustfs/rustfs:latest + volumes: + - rustfs-data:/data + environment: + - RUSTFS_ACCESS_KEY + - RUSTFS_SECRET_KEY + - RUSTFS_ADDRESS=0.0.0.0:9000 + - RUSTFS_CONSOLE_ADDRESS=0.0.0.0:9001 + - RUSTFS_CONSOLE_ENABLE=true + - RUSTFS_CORS_ALLOWED_ORIGINS=* + - RUSTFS_CONSOLE_CORS_ALLOWED_ORIGINS=* + command: /data + restart: unless-stopped + +volumes: + rustfs-data: diff --git a/blueprints/rustfs/logo.svg b/blueprints/rustfs/logo.svg new file mode 100644 index 000000000..d5cf09f6e --- /dev/null +++ b/blueprints/rustfs/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/blueprints/rustfs/meta-entry.json b/blueprints/rustfs/meta-entry.json new file mode 100644 index 000000000..fc5324732 --- /dev/null +++ b/blueprints/rustfs/meta-entry.json @@ -0,0 +1,18 @@ +{ + "id": "rustfs", + "name": "RustFS", + "version": "latest", + "description": "RustFS is a high-performance, S3-compatible distributed object storage system built in Rust. 2.3x faster than MinIO for small objects, with full S3 API compatibility.", + "logo": "logo.svg", + "links": { + "github": "https://github.com/rustfs/rustfs", + "website": "https://rustfs.com/", + "docs": "https://docs.rustfs.com/" + }, + "tags": [ + "storage", + "s3", + "object-storage", + "rust" + ] +} diff --git a/blueprints/rustfs/template.toml b/blueprints/rustfs/template.toml new file mode 100644 index 000000000..8e6f96fb6 --- /dev/null +++ b/blueprints/rustfs/template.toml @@ -0,0 +1,25 @@ +[variables] +console_domain = "${domain}" +api_domain = "${domain}" +access_key = "rustfsadmin" +secret_key = "${password:16}" + +[config] +env = [ + "RUSTFS_ACCESS_KEY=${access_key}", + "RUSTFS_SECRET_KEY=${secret_key}", + "", + "## SET THE API URL IN CONSOLE CONFIG BY CLICKING THE COG", + "## API URL: ${api_domain}", +] +mounts = [] + +[[config.domains]] +serviceName = "rustfs" +port = 9001 +host = "${console_domain}" + +[[config.domains]] +serviceName = "rustfs" +port = 9000 +host = "${api_domain}" diff --git a/meta.json b/meta.json index a6dbd358a..24d5fb181 100644 --- a/meta.json +++ b/meta.json @@ -4907,6 +4907,24 @@ "productivity" ] }, + { + "id": "rustfs", + "name": "RustFS", + "version": "latest", + "description": "RustFS is a high-performance, S3-compatible distributed object storage system built in Rust. 2.3x faster than MinIO for small objects, with full S3 API compatibility.", + "logo": "logo.svg", + "links": { + "github": "https://github.com/rustfs/rustfs", + "website": "https://rustfs.com/", + "docs": "https://docs.rustfs.com/" + }, + "tags": [ + "storage", + "s3", + "object-storage", + "rust" + ] + }, { "id": "rutorrent", "name": "ruTorrent", From 7834542c9c40b2b93a6838bf2b61686214513c4d Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Sat, 13 Dec 2025 01:45:06 -0600 Subject: [PATCH 23/91] feat: add pull request template for improved contribution guidelines --- .github/{PULL_REQUEST_TEMPLATE => }/pull_request_template.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) rename .github/{PULL_REQUEST_TEMPLATE => }/pull_request_template.md (96%) diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/pull_request_template.md similarity index 96% rename from .github/PULL_REQUEST_TEMPLATE/pull_request_template.md rename to .github/pull_request_template.md index 0c5e03d59..11568f10a 100644 --- a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +++ b/.github/pull_request_template.md @@ -14,4 +14,5 @@ Before submitting this PR, please make sure that: Close automatically the related issues using the keywords: `closes #ISSUE_NUMBER`, `fixes #ISSUE_NUMBER`, `resolves #ISSUE_NUMBER` -Example: `closes #123` \ No newline at end of file +Example: `closes #123` + From f78cc8d51ff32c4ba762835072ddc9dd7d8ba573 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Sat, 13 Dec 2025 01:45:43 -0600 Subject: [PATCH 24/91] fix: update pull request template to clarify issue closing keywords --- .github/pull_request_template.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 11568f10a..f349d36a5 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -12,7 +12,7 @@ Before submitting this PR, please make sure that: ## Issues related (if applicable) -Close automatically the related issues using the keywords: `closes #ISSUE_NUMBER`, `fixes #ISSUE_NUMBER`, `resolves #ISSUE_NUMBER` +Close automatically the related issues using the keywords: `closes #ISSUE_NUMBER` + -Example: `closes #123` From abd316fd6ed9dc2143696649a28a9174a4cef73a Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Sat, 13 Dec 2025 14:38:37 -0600 Subject: [PATCH 25/91] feat: add validation scripts and configuration for Docker Compose and template files - Introduced a GitHub Actions workflow to validate Docker Compose files and template.toml on pull requests. - Added helper functions for generating random values and processing variables in templates. - Implemented validation scripts for checking the structure, syntax, and best practices of Docker Compose and template files. - Created necessary TypeScript types and configuration files for the build scripts. --- .github/workflows/validate-docker-compose.yml | 309 ++++++ .gitignore | 1 + build-scripts/helpers.ts | 245 +++++ build-scripts/package.json | 24 + build-scripts/pnpm-lock.yaml | 361 +++++++ build-scripts/tsconfig.json | 23 + build-scripts/type.ts | 879 ++++++++++++++++++ build-scripts/validate-docker-compose.ts | 357 +++++++ build-scripts/validate-template.ts | 625 +++++++++++++ 9 files changed, 2824 insertions(+) create mode 100644 .github/workflows/validate-docker-compose.yml create mode 100644 .gitignore create mode 100644 build-scripts/helpers.ts create mode 100644 build-scripts/package.json create mode 100644 build-scripts/pnpm-lock.yaml create mode 100644 build-scripts/tsconfig.json create mode 100644 build-scripts/type.ts create mode 100644 build-scripts/validate-docker-compose.ts create mode 100644 build-scripts/validate-template.ts diff --git a/.github/workflows/validate-docker-compose.yml b/.github/workflows/validate-docker-compose.yml new file mode 100644 index 000000000..945beeefe --- /dev/null +++ b/.github/workflows/validate-docker-compose.yml @@ -0,0 +1,309 @@ +name: Validate Docker Compose Files + +on: + pull_request: + branches: + - canary + paths: + - 'blueprints/**/docker-compose.yml' + - 'blueprints/**/template.toml' + workflow_dispatch: + +jobs: + validate-docker-compose: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Necesitamos el historial completo para comparar con base + + - name: Set up Docker Compose + run: | + echo "🐳 Setting up Docker Compose..." + # Docker Compose V2 viene preinstalado en ubuntu-latest + docker compose version + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 20.16.0 + + - name: Set up pnpm + uses: pnpm/action-setup@v4 + with: + version: 8 + + - name: Install dependencies + run: | + echo "📦 Installing Node.js dependencies..." + cd build-scripts && pnpm install + + - name: Get changed files + id: changed-files + run: | + echo "🔍 Detecting changed files..." + + # Obtener la rama base + BASE_SHA=$(git merge-base HEAD origin/${{ github.base_ref }}) + + # Encontrar todos los archivos docker-compose.yml y template.toml modificados/agregados + CHANGED_COMPOSE=$(git diff --name-only --diff-filter=ACMRT $BASE_SHA HEAD | grep -E 'blueprints/.*/docker-compose\.yml$' || true) + CHANGED_TOML=$(git diff --name-only --diff-filter=ACMRT $BASE_SHA HEAD | grep -E 'blueprints/.*/template\.toml$' || true) + + # Crear lista de directorios únicos que tienen cambios + CHANGED_DIRS=$(echo -e "$CHANGED_COMPOSE\n$CHANGED_TOML" | sed 's|blueprints/\([^/]*\)/.*|\1|' | sort -u) + + echo "Changed compose files:" + echo "$CHANGED_COMPOSE" | while read file; do [ -n "$file" ] && echo " - $file"; done + + echo "Changed TOML files:" + echo "$CHANGED_TOML" | while read file; do [ -n "$file" ] && echo " - $file"; done + + echo "Changed directories:" + echo "$CHANGED_DIRS" | while read dir; do [ -n "$dir" ] && echo " - $dir"; done + + # Guardar para usar en siguientes pasos + echo "compose_files<> $GITHUB_OUTPUT + echo "$CHANGED_COMPOSE" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + echo "toml_files<> $GITHUB_OUTPUT + echo "$CHANGED_TOML" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + echo "directories<> $GITHUB_OUTPUT + echo "$CHANGED_DIRS" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Validate Docker Compose files syntax + id: validate-compose-syntax + run: | + echo "🔍 Validating Docker Compose files syntax..." + + ERROR=0 + COMPOSE_FILES="${{ steps.changed-files.outputs.compose_files }}" + + if [ -z "$COMPOSE_FILES" ]; then + echo "ℹ️ No docker-compose.yml files changed, skipping validation" + exit 0 + fi + + echo "$COMPOSE_FILES" | while read -r compose_file; do + if [ -z "$compose_file" ]; then + continue + fi + + TEMPLATE_DIR=$(dirname "$compose_file") + TEMPLATE_NAME=$(basename "$TEMPLATE_DIR") + + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "📦 Validating syntax: $TEMPLATE_NAME" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + # Validar sintaxis de docker-compose.yml usando docker compose + echo "🔍 Validating docker-compose.yml syntax..." + if ! docker compose -f "$compose_file" config > /dev/null 2>&1; then + echo "❌ ERROR: docker-compose.yml syntax is invalid in $TEMPLATE_NAME" + echo "Running docker compose config to show errors:" + docker compose -f "$compose_file" config 2>&1 || true + ERROR=1 + else + echo "✅ docker-compose.yml syntax is valid" + fi + + # Obtener lista de servicios del compose + SERVICES=$(docker compose -f "$compose_file" config --services 2>/dev/null || echo "") + echo "📋 Services found in docker-compose.yml:" + echo "$SERVICES" | while read service; do + [ -n "$service" ] && echo " - $service" + done + + # Guardar servicios para validación posterior + echo "$SERVICES" > "/tmp/${TEMPLATE_NAME}_services.txt" + done + + if [ $ERROR -eq 1 ]; then + echo "" + echo "❌ Docker Compose syntax validation failed" + exit 1 + else + echo "" + echo "✅ All Docker Compose files have valid syntax" + fi + + - name: Validate Docker Compose best practices + id: validate-compose-practices + run: | + echo "🔍 Validating Docker Compose best practices..." + + ERROR=0 + COMPOSE_FILES="${{ steps.changed-files.outputs.compose_files }}" + + if [ -z "$COMPOSE_FILES" ]; then + echo "ℹ️ No docker-compose.yml files changed, skipping validation" + exit 0 + fi + + echo "$COMPOSE_FILES" | while read -r compose_file; do + if [ -z "$compose_file" ]; then + continue + fi + + TEMPLATE_DIR=$(dirname "$compose_file") + TEMPLATE_NAME=$(basename "$TEMPLATE_DIR") + + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "📦 Validating best practices: $TEMPLATE_NAME" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + # Validar usando el script de TypeScript + if ! (cd build-scripts && pnpm exec tsx validate-docker-compose.ts --file "../$compose_file" --verbose); then + ERROR=1 + fi + done + + if [ $ERROR -eq 1 ]; then + echo "" + echo "❌ Docker Compose best practices validation failed" + exit 1 + else + echo "" + echo "✅ All Docker Compose files follow best practices" + fi + + - name: Validate template.toml files + id: validate-toml + run: | + echo "🔍 Validating template.toml files..." + + ERROR=0 + DIRECTORIES="${{ steps.changed-files.outputs.directories }}" + + if [ -z "$DIRECTORIES" ]; then + echo "ℹ️ No template directories changed, skipping TOML validation" + exit 0 + fi + + echo "$DIRECTORIES" | while read -r template_dir; do + if [ -z "$template_dir" ]; then + continue + fi + + TEMPLATE_PATH="blueprints/$template_dir" + TOML_FILE="$TEMPLATE_PATH/template.toml" + + if [ ! -f "$TOML_FILE" ]; then + echo "⚠️ WARNING: template.toml not found in $template_dir (might be deleted)" + continue + fi + + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "📝 Validating: $template_dir/template.toml" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + # Validar usando el script de TypeScript con tsx + # Ejecutar desde build-scripts para tener acceso a node_modules + if ! (cd build-scripts && pnpm exec tsx validate-template.ts --dir "../$TEMPLATE_PATH" --verbose); then + ERROR=1 + fi + done + + if [ $ERROR -eq 1 ]; then + echo "" + echo "❌ template.toml validation failed" + exit 1 + else + echo "" + echo "✅ All template.toml files are valid" + fi + + - name: Test Docker Compose (dry-run) + id: test-compose + run: | + echo "🧪 Testing Docker Compose files (dry-run)..." + + ERROR=0 + DIRECTORIES="${{ steps.changed-files.outputs.directories }}" + + if [ -z "$DIRECTORIES" ]; then + echo "ℹ️ No template directories changed, skipping dry-run test" + exit 0 + fi + + echo "$DIRECTORIES" | while read -r template_dir; do + if [ -z "$template_dir" ]; then + continue + fi + + COMPOSE_FILE="blueprints/$template_dir/docker-compose.yml" + + if [ ! -f "$COMPOSE_FILE" ]; then + continue + fi + + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "🧪 Testing: $template_dir" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + # Cambiar al directorio del template para resolver rutas relativas + cd "blueprints/$template_dir" + + # Validar que docker-compose puede parsear el archivo completamente + echo "🔍 Running docker compose config (full validation)..." + if docker compose config > /dev/null 2>&1; then + echo "✅ Docker Compose file is fully valid and can be processed" + + # Mostrar información útil + echo "📋 Configuration summary:" + docker compose config --services | while read service; do + [ -n "$service" ] && echo " Service: $service" + done + else + echo "❌ ERROR: Docker Compose file failed full validation" + docker compose config 2>&1 || true + ERROR=1 + fi + + cd - > /dev/null + done + + if [ $ERROR -eq 1 ]; then + echo "" + echo "❌ Docker Compose dry-run test failed" + exit 1 + else + echo "" + echo "✅ All Docker Compose files passed dry-run test" + fi + + - name: Summary + if: always() + run: | + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "📊 Validation Summary" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + if [ "${{ steps.validate-compose-syntax.outcome }}" == "success" ] && \ + [ "${{ steps.validate-compose-practices.outcome }}" == "success" ] && \ + [ "${{ steps.validate-toml.outcome }}" == "success" ] && \ + [ "${{ steps.test-compose.outcome }}" == "success" ]; then + echo "✅ All validations passed!" + echo "" + echo "Your Docker Compose and template.toml files are valid and ready to merge." + else + echo "❌ Some validations failed. Please review the errors above." + echo "" + echo "Common issues to check:" + echo " - docker-compose.yml syntax errors" + echo " - template.toml syntax errors" + echo " - serviceName in template.toml must match service names in docker-compose.yml" + echo " - Avoid using container_name, explicit networks, or port mappings" + fi + diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..b512c09d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/build-scripts/helpers.ts b/build-scripts/helpers.ts new file mode 100644 index 000000000..d7eb75569 --- /dev/null +++ b/build-scripts/helpers.ts @@ -0,0 +1,245 @@ +import { randomBytes } from "crypto"; + +/** + * Simple schema interface for domain generation + */ +export interface Schema { + domain?: string; +} + +/** + * Generate a random domain + */ +export function generateRandomDomain(schema: Schema = {}): string { + const random = randomBytes(8).toString("hex"); + return schema.domain || `app-${random}.example.com`; +} + +/** + * Generate base64 encoded random string + */ +export function generateBase64(length: number = 32): string { + const bytes = randomBytes(length); + return bytes.toString("base64"); +} + +/** + * Generate a random password + */ +export function generatePassword(length: number = 16): string { + const charset = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"; + let password = ""; + for (let i = 0; i < length; i++) { + password += charset.charAt(Math.floor(Math.random() * charset.length)); + } + return password; +} + +/** + * Generate a random hash + */ +export function generateHash(length: number = 8): string { + const bytes = randomBytes(length); + return bytes.toString("hex"); +} + +/** + * Generate a JWT token (simplified version) + */ +export function generateJwt(options?: { + length?: number; + secret?: string; + payload?: any; +}): string { + if (options?.length) { + // Legacy format: jwt:length + return randomBytes(options.length).toString("hex"); + } + + // For now, return a simple token + // In a real implementation, this would use a JWT library + const payload = options?.payload || {}; + const secret = options?.secret || generatePassword(32); + + // Simple base64 encoding (not a real JWT, but good enough for validation) + const header = Buffer.from(JSON.stringify({ alg: "HS256", typ: "JWT" })).toString("base64url"); + const body = Buffer.from(JSON.stringify(payload)).toString("base64url"); + const signature = Buffer.from(secret).toString("base64url").slice(0, 32); + + return `${header}.${body}.${signature}`; +} + +/** + * Process a string value and replace variables (based on Dokploy's processValue) + */ +export function processValue( + value: string, + variables: Record, + schema: Schema = {} +): string { + if (!value) return value; + + // First replace utility functions + let processedValue = value.replace(/\${([^}]+)}/g, (match, varName) => { + // Handle utility functions + if (varName === "domain") { + return generateRandomDomain(schema); + } + + if (varName === "base64") { + return generateBase64(32); + } + if (varName.startsWith("base64:")) { + const length = Number.parseInt(varName.split(":")[1], 10) || 32; + return generateBase64(length); + } + + if (varName.startsWith("password:")) { + const length = Number.parseInt(varName.split(":")[1], 10) || 16; + return generatePassword(length); + } + if (varName === "password") { + return generatePassword(16); + } + + if (varName.startsWith("hash:")) { + const length = Number.parseInt(varName.split(":")[1], 10) || 8; + return generateHash(length); + } + if (varName === "hash") { + return generateHash(); + } + + if (varName === "uuid") { + return crypto.randomUUID(); + } + + if (varName === "timestamp" || varName === "timestampms") { + return Date.now().toString(); + } + + if (varName === "timestamps") { + return Math.round(Date.now() / 1000).toString(); + } + + if (varName.startsWith("timestampms:")) { + return new Date(varName.slice(12)).getTime().toString(); + } + if (varName.startsWith("timestamps:")) { + return Math.round(new Date(varName.slice(11)).getTime() / 1000).toString(); + } + + if (varName === "randomPort") { + return Math.floor(Math.random() * 65535).toString(); + } + + if (varName === "jwt") { + return generateJwt(); + } + + if (varName.startsWith("jwt:")) { + const params: string[] = varName.split(":").slice(1); + if (params.length === 1 && params[0] && params[0].match(/^\d{1,3}$/)) { + return generateJwt({ length: Number.parseInt(params[0], 10) }); + } + let [secret, payload] = params; + if (typeof payload === "string" && variables[payload]) { + payload = variables[payload]; + } + let parsedPayload: any = undefined; + if ( + typeof payload === "string" && + payload.trimStart().startsWith("{") && + payload.trimEnd().endsWith("}") + ) { + try { + parsedPayload = JSON.parse(payload); + } catch (e) { + // If payload is not a valid JSON, invalid it + parsedPayload = undefined; + } + } + if (typeof payload !== "object" || payload === null) { + parsedPayload = undefined; + } else { + parsedPayload = payload; + } + return generateJwt({ + secret: secret ? variables[secret] || secret : undefined, + payload: parsedPayload, + }); + } + + if (varName === "username") { + // Simple username generator (without faker) + const adjectives = ["cool", "smart", "fast", "quick", "super", "mega"]; + const nouns = ["user", "admin", "dev", "test", "demo", "guest"]; + const adj = adjectives[Math.floor(Math.random() * adjectives.length)]; + const noun = nouns[Math.floor(Math.random() * nouns.length)]; + const num = Math.floor(Math.random() * 1000); + return `${adj}${noun}${num}`.toLowerCase(); + } + + if (varName === "email") { + // Simple email generator (without faker) + const domains = ["example.com", "test.com", "demo.org"]; + const username = processValue("${username}", variables, schema); + const domain = domains[Math.floor(Math.random() * domains.length)]; + return `${username}@${domain}`.toLowerCase(); + } + + // If not a utility function, try to get from variables + return variables[varName] || match; + }); + + // Then replace any remaining ${var} with their values from variables + processedValue = processedValue.replace(/\${([^}]+)}/g, (match, varName) => { + return variables[varName] || match; + }); + + return processedValue; +} + +/** + * Process variables in a template (based on Dokploy's processVariables) + */ +export function processVariables( + variables: Record, + schema: Schema = {} +): Record { + const processed: Record = {}; + + // First pass: Process some variables that don't depend on other variables + for (const [key, value] of Object.entries(variables)) { + if (typeof value !== "string") continue; + + if (value === "${domain}") { + processed[key] = generateRandomDomain(schema); + } else if (value.startsWith("${base64:")) { + const match = value.match(/\${base64:(\d+)}/); + const length = match?.[1] ? Number.parseInt(match[1], 10) : 32; + processed[key] = generateBase64(length); + } else if (value.startsWith("${password:")) { + const match = value.match(/\${password:(\d+)}/); + const length = match?.[1] ? Number.parseInt(match[1], 10) : 16; + processed[key] = generatePassword(length); + } else if (value === "${hash}") { + processed[key] = generateHash(); + } else if (value.startsWith("${hash:")) { + const match = value.match(/\${hash:(\d+)}/); + const length = match?.[1] ? Number.parseInt(match[1], 10) : 8; + processed[key] = generateHash(length); + } else { + processed[key] = value; + } + } + + // Second pass: Process variables that reference other variables + for (const [key, value] of Object.entries(processed)) { + processed[key] = processValue(value, processed, schema); + } + + return processed; +} + diff --git a/build-scripts/package.json b/build-scripts/package.json new file mode 100644 index 000000000..6df162f82 --- /dev/null +++ b/build-scripts/package.json @@ -0,0 +1,24 @@ +{ + "name": "dokploy-templates-build-scripts", + "version": "1.0.0", + "description": "Build scripts for Dokploy Templates validation", + "private": true, + "scripts": { + "validate-template": "tsx validate-template.ts", + "validate-docker-compose": "tsx validate-docker-compose.ts", + "process-meta": "node process-meta.js" + }, + "dependencies": { + "toml": "^3.0.0", + "yaml": "2.7.1" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "tsx": "^4.7.0", + "typescript": "^5.3.0" + }, + "engines": { + "node": ">=18.0.0" + } +} + diff --git a/build-scripts/pnpm-lock.yaml b/build-scripts/pnpm-lock.yaml new file mode 100644 index 000000000..daa7c9056 --- /dev/null +++ b/build-scripts/pnpm-lock.yaml @@ -0,0 +1,361 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + toml: + specifier: ^3.0.0 + version: 3.0.0 + yaml: + specifier: 2.7.1 + version: 2.7.1 + devDependencies: + '@types/node': + specifier: ^20.0.0 + version: 20.19.26 + tsx: + specifier: ^4.7.0 + version: 4.21.0 + typescript: + specifier: ^5.3.0 + version: 5.9.3 + +packages: + + '@esbuild/aix-ppc64@0.27.1': + resolution: {integrity: sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.1': + resolution: {integrity: sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.1': + resolution: {integrity: sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.1': + resolution: {integrity: sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.1': + resolution: {integrity: sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.1': + resolution: {integrity: sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.1': + resolution: {integrity: sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.1': + resolution: {integrity: sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.1': + resolution: {integrity: sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.1': + resolution: {integrity: sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.1': + resolution: {integrity: sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.1': + resolution: {integrity: sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.1': + resolution: {integrity: sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.1': + resolution: {integrity: sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.1': + resolution: {integrity: sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.1': + resolution: {integrity: sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.1': + resolution: {integrity: sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.1': + resolution: {integrity: sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.1': + resolution: {integrity: sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.1': + resolution: {integrity: sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.1': + resolution: {integrity: sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.1': + resolution: {integrity: sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.1': + resolution: {integrity: sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.1': + resolution: {integrity: sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.1': + resolution: {integrity: sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.1': + resolution: {integrity: sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@types/node@20.19.26': + resolution: {integrity: sha512-0l6cjgF0XnihUpndDhk+nyD3exio3iKaYROSgvh/qSevPXax3L8p5DBRFjbvalnwatGgHEQn2R88y2fA3g4irg==} + + esbuild@0.27.1: + resolution: {integrity: sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==} + engines: {node: '>=18'} + hasBin: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + get-tsconfig@4.13.0: + resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + toml@3.0.0: + resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} + + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + yaml@2.7.1: + resolution: {integrity: sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==} + engines: {node: '>= 14'} + hasBin: true + +snapshots: + + '@esbuild/aix-ppc64@0.27.1': + optional: true + + '@esbuild/android-arm64@0.27.1': + optional: true + + '@esbuild/android-arm@0.27.1': + optional: true + + '@esbuild/android-x64@0.27.1': + optional: true + + '@esbuild/darwin-arm64@0.27.1': + optional: true + + '@esbuild/darwin-x64@0.27.1': + optional: true + + '@esbuild/freebsd-arm64@0.27.1': + optional: true + + '@esbuild/freebsd-x64@0.27.1': + optional: true + + '@esbuild/linux-arm64@0.27.1': + optional: true + + '@esbuild/linux-arm@0.27.1': + optional: true + + '@esbuild/linux-ia32@0.27.1': + optional: true + + '@esbuild/linux-loong64@0.27.1': + optional: true + + '@esbuild/linux-mips64el@0.27.1': + optional: true + + '@esbuild/linux-ppc64@0.27.1': + optional: true + + '@esbuild/linux-riscv64@0.27.1': + optional: true + + '@esbuild/linux-s390x@0.27.1': + optional: true + + '@esbuild/linux-x64@0.27.1': + optional: true + + '@esbuild/netbsd-arm64@0.27.1': + optional: true + + '@esbuild/netbsd-x64@0.27.1': + optional: true + + '@esbuild/openbsd-arm64@0.27.1': + optional: true + + '@esbuild/openbsd-x64@0.27.1': + optional: true + + '@esbuild/openharmony-arm64@0.27.1': + optional: true + + '@esbuild/sunos-x64@0.27.1': + optional: true + + '@esbuild/win32-arm64@0.27.1': + optional: true + + '@esbuild/win32-ia32@0.27.1': + optional: true + + '@esbuild/win32-x64@0.27.1': + optional: true + + '@types/node@20.19.26': + dependencies: + undici-types: 6.21.0 + + esbuild@0.27.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.1 + '@esbuild/android-arm': 0.27.1 + '@esbuild/android-arm64': 0.27.1 + '@esbuild/android-x64': 0.27.1 + '@esbuild/darwin-arm64': 0.27.1 + '@esbuild/darwin-x64': 0.27.1 + '@esbuild/freebsd-arm64': 0.27.1 + '@esbuild/freebsd-x64': 0.27.1 + '@esbuild/linux-arm': 0.27.1 + '@esbuild/linux-arm64': 0.27.1 + '@esbuild/linux-ia32': 0.27.1 + '@esbuild/linux-loong64': 0.27.1 + '@esbuild/linux-mips64el': 0.27.1 + '@esbuild/linux-ppc64': 0.27.1 + '@esbuild/linux-riscv64': 0.27.1 + '@esbuild/linux-s390x': 0.27.1 + '@esbuild/linux-x64': 0.27.1 + '@esbuild/netbsd-arm64': 0.27.1 + '@esbuild/netbsd-x64': 0.27.1 + '@esbuild/openbsd-arm64': 0.27.1 + '@esbuild/openbsd-x64': 0.27.1 + '@esbuild/openharmony-arm64': 0.27.1 + '@esbuild/sunos-x64': 0.27.1 + '@esbuild/win32-arm64': 0.27.1 + '@esbuild/win32-ia32': 0.27.1 + '@esbuild/win32-x64': 0.27.1 + + fsevents@2.3.3: + optional: true + + get-tsconfig@4.13.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + resolve-pkg-maps@1.0.0: {} + + toml@3.0.0: {} + + tsx@4.21.0: + dependencies: + esbuild: 0.27.1 + get-tsconfig: 4.13.0 + optionalDependencies: + fsevents: 2.3.3 + + typescript@5.9.3: {} + + undici-types@6.21.0: {} + + yaml@2.7.1: {} diff --git a/build-scripts/tsconfig.json b/build-scripts/tsconfig.json new file mode 100644 index 000000000..bd6df59c0 --- /dev/null +++ b/build-scripts/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "commonjs", + "lib": ["ES2022"], + "outDir": "./dist", + "rootDir": "./", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "moduleResolution": "node", + "declaration": false, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["*.ts"], + "exclude": ["node_modules", "dist"] +} + diff --git a/build-scripts/type.ts b/build-scripts/type.ts new file mode 100644 index 000000000..d5ac9deff --- /dev/null +++ b/build-scripts/type.ts @@ -0,0 +1,879 @@ +export type DefinitionsInclude = + | string + | { + path?: StringOrList; + env_file?: StringOrList; + project_directory?: string; + }; +export type StringOrList = string | ListOfStrings; +export type ListOfStrings = string[]; +export type DefinitionsDevelopment = { + watch?: { + ignore?: string[]; + path: string; + action: "rebuild" | "sync" | "sync+restart"; + target?: string; + [k: string]: unknown; + }[]; + [k: string]: unknown; +} & Development; +export type Development = { + watch?: { + ignore?: string[]; + path: string; + action: "rebuild" | "sync" | "sync+restart"; + target?: string; + [k: string]: unknown; + }[]; + [k: string]: unknown; +} | null; +export type DefinitionsDeployment = { + mode?: string; + endpoint_mode?: string; + replicas?: number; + labels?: ListOrDict; + rollback_config?: { + parallelism?: number; + delay?: string; + failure_action?: string; + monitor?: string; + max_failure_ratio?: number; + order?: "start-first" | "stop-first"; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + update_config?: { + parallelism?: number; + delay?: string; + failure_action?: string; + monitor?: string; + max_failure_ratio?: number; + order?: "start-first" | "stop-first"; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + resources?: { + limits?: { + cpus?: number | string; + memory?: string; + pids?: number; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + reservations?: { + cpus?: number | string; + memory?: string; + generic_resources?: DefinitionsGenericResources; + devices?: DefinitionsDevices; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + restart_policy?: { + condition?: string; + delay?: string; + max_attempts?: number; + window?: string; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + placement?: { + constraints?: string[]; + preferences?: { + spread?: string; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }[]; + max_replicas_per_node?: number; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + * + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; +} & Deployment; +export type ListOrDict = + | { + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` ".+". + */ + [k: string]: string | number | boolean | null; + } + | string[]; +export type DefinitionsGenericResources = { + discrete_resource_spec?: { + kind?: string; + value?: number; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; +}[]; +export type DefinitionsDevices = { + capabilities?: ListOfStrings; + count?: string | number; + device_ids?: ListOfStrings; + driver?: string; + options?: ListOrDict; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; +}[]; +type Deployment = { + mode?: string; + endpoint_mode?: string; + replicas?: number; + labels?: ListOrDict; + rollback_config?: { + parallelism?: number; + delay?: string; + failure_action?: string; + monitor?: string; + max_failure_ratio?: number; + order?: "start-first" | "stop-first"; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + update_config?: { + parallelism?: number; + delay?: string; + failure_action?: string; + monitor?: string; + max_failure_ratio?: number; + order?: "start-first" | "stop-first"; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + resources?: { + limits?: { + cpus?: number | string; + memory?: string; + pids?: number; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + reservations?: { + cpus?: number | string; + memory?: string; + generic_resources?: DefinitionsGenericResources; + devices?: DefinitionsDevices; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + restart_policy?: { + condition?: string; + delay?: string; + max_attempts?: number; + window?: string; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + placement?: { + constraints?: string[]; + preferences?: { + spread?: string; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }[]; + max_replicas_per_node?: number; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + * + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; +} | null; +export type ServiceConfigOrSecret = ( + | string + | { + source?: string; + target?: string; + uid?: string; + gid?: string; + mode?: number; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + } +)[]; +export type Command = null | string | string[]; +export type EnvFile = + | string + | ( + | string + | { + path: string; + required?: boolean; + } + )[]; +/** + * This interface was referenced by `PropertiesNetworks`'s JSON-Schema definition + * via the `patternProperty` "^[a-zA-Z0-9._-]+$". + */ +export type DefinitionsNetwork = { + name?: string; + driver?: string; + driver_opts?: { + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^.+$". + */ + [k: string]: string | number; + }; + ipam?: { + driver?: string; + config?: { + subnet?: string; + ip_range?: string; + gateway?: string; + aux_addresses?: { + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^.+$". + */ + [k: string]: string; + }; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }[]; + options?: { + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^.+$". + */ + [k: string]: string; + }; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + external?: + | boolean + | { + name?: string; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + internal?: boolean; + enable_ipv6?: boolean; + attachable?: boolean; + labels?: ListOrDict; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + * + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; +} & Network; +export type Network = { + name?: string; + driver?: string; + driver_opts?: { + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^.+$". + */ + [k: string]: string | number; + }; + ipam?: { + driver?: string; + config?: { + subnet?: string; + ip_range?: string; + gateway?: string; + aux_addresses?: { + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^.+$". + */ + [k: string]: string; + }; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }[]; + options?: { + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^.+$". + */ + [k: string]: string; + }; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + external?: + | boolean + | { + name?: string; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + internal?: boolean; + enable_ipv6?: boolean; + attachable?: boolean; + labels?: ListOrDict; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + * + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; +} | null; +/** + * This interface was referenced by `PropertiesVolumes`'s JSON-Schema definition + * via the `patternProperty` "^[a-zA-Z0-9._-]+$". + */ +export type DefinitionsVolume = { + name?: string; + driver?: string; + driver_opts?: { + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^.+$". + */ + [k: string]: string | number; + }; + external?: + | boolean + | { + name?: string; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + labels?: ListOrDict; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + * + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; +} & Volume; +export type Volume = { + name?: string; + driver?: string; + driver_opts?: { + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^.+$". + */ + [k: string]: string | number; + }; + external?: + | boolean + | { + name?: string; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + labels?: ListOrDict; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + * + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; +} | null; + +/** + * The Compose file is a YAML file defining a multi-containers based application. + */ +export interface ComposeSpecification { + /** + * declared for backward compatibility, ignored. + */ + version?: string; + /** + * define the Compose project name, until user defines one explicitly. + */ + name?: string; + /** + * compose sub-projects to be included. + */ + include?: DefinitionsInclude[]; + services?: PropertiesServices; + networks?: PropertiesNetworks; + volumes?: PropertiesVolumes; + secrets?: PropertiesSecrets; + configs?: PropertiesConfigs; + /** + * This interface was referenced by `ComposeSpecification`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; +} +export interface PropertiesServices { + [k: string]: DefinitionsService; +} +/** + * This interface was referenced by `PropertiesServices`'s JSON-Schema definition + * via the `patternProperty` "^[a-zA-Z0-9._-]+$". + */ +export interface DefinitionsService { + develop?: DefinitionsDevelopment; + deploy?: DefinitionsDeployment; + annotations?: ListOrDict; + attach?: boolean; + build?: + | string + | { + context?: string; + dockerfile?: string; + dockerfile_inline?: string; + entitlements?: string[]; + args?: ListOrDict; + ssh?: ListOrDict; + labels?: ListOrDict; + cache_from?: string[]; + cache_to?: string[]; + no_cache?: boolean; + additional_contexts?: ListOrDict; + network?: string; + pull?: boolean; + target?: string; + shm_size?: number | string; + extra_hosts?: ListOrDict; + isolation?: string; + privileged?: boolean; + secrets?: ServiceConfigOrSecret; + tags?: string[]; + ulimits?: Ulimits; + platforms?: string[]; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + blkio_config?: { + device_read_bps?: BlkioLimit[]; + device_read_iops?: BlkioLimit[]; + device_write_bps?: BlkioLimit[]; + device_write_iops?: BlkioLimit[]; + weight?: number; + weight_device?: BlkioWeight[]; + }; + cap_add?: string[]; + cap_drop?: string[]; + cgroup?: "host" | "private"; + cgroup_parent?: string; + command?: Command; + configs?: ServiceConfigOrSecret; + container_name?: string; + cpu_count?: number; + cpu_percent?: number; + cpu_shares?: number | string; + cpu_quota?: number | string; + cpu_period?: number | string; + cpu_rt_period?: number | string; + cpu_rt_runtime?: number | string; + cpus?: number | string; + cpuset?: string; + credential_spec?: { + config?: string; + file?: string; + registry?: string; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + depends_on?: + | ListOfStrings + | { + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^[a-zA-Z0-9._-]+$". + */ + [k: string]: { + restart?: boolean; + required?: boolean; + condition: + | "service_started" + | "service_healthy" + | "service_completed_successfully"; + }; + }; + device_cgroup_rules?: ListOfStrings; + devices?: string[]; + dns?: StringOrList; + dns_opt?: string[]; + dns_search?: StringOrList; + domainname?: string; + entrypoint?: Command; + env_file?: EnvFile; + environment?: ListOrDict; + expose?: (string | number)[]; + extends?: + | string + | { + service: string; + file?: string; + }; + external_links?: string[]; + extra_hosts?: ListOrDict; + group_add?: (string | number)[]; + healthcheck?: DefinitionsHealthcheck; + hostname?: string; + image?: string; + init?: boolean; + ipc?: string; + isolation?: string; + labels?: ListOrDict; + links?: string[]; + logging?: { + driver?: string; + options?: { + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^.+$". + */ + [k: string]: string | number | null; + }; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + mac_address?: string; + mem_limit?: number | string; + mem_reservation?: string | number; + mem_swappiness?: number; + memswap_limit?: number | string; + network_mode?: string; + networks?: + | ListOfStrings + | { + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^[a-zA-Z0-9._-]+$". + */ + [k: string]: { + aliases?: ListOfStrings; + ipv4_address?: string; + ipv6_address?: string; + link_local_ips?: ListOfStrings; + mac_address?: string; + driver_opts?: { + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^.+$". + */ + [k: string]: string | number; + }; + priority?: number; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + } | null; + }; + oom_kill_disable?: boolean; + oom_score_adj?: number; + pid?: string | null; + pids_limit?: number | string; + platform?: string; + ports?: ( + | number + | string + | { + name?: string; + mode?: string; + host_ip?: string; + target?: number; + published?: string | number; + protocol?: string; + app_protocol?: string; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + } + )[]; + privileged?: boolean; + profiles?: ListOfStrings; + pull_policy?: "always" | "never" | "if_not_present" | "build" | "missing"; + read_only?: boolean; + restart?: string; + runtime?: string; + scale?: number; + security_opt?: string[]; + shm_size?: number | string; + secrets?: ServiceConfigOrSecret; + sysctls?: ListOrDict; + stdin_open?: boolean; + stop_grace_period?: string; + stop_signal?: string; + storage_opt?: { + [k: string]: unknown; + }; + tmpfs?: StringOrList; + tty?: boolean; + ulimits?: Ulimits; + user?: string; + uts?: string; + userns_mode?: string; + volumes?: ( + | string + | { + type: string; + source?: string; + target?: string; + read_only?: boolean; + consistency?: string; + bind?: { + propagation?: string; + create_host_path?: boolean; + selinux?: "z" | "Z"; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + volume?: { + nocopy?: boolean; + subpath?: string; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + tmpfs?: { + size?: number | string; + mode?: number; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + } + )[]; + volumes_from?: string[]; + working_dir?: string; + /** + * This interface was referenced by `DefinitionsService`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; +} +export interface Ulimits { + /** + * This interface was referenced by `Ulimits`'s JSON-Schema definition + * via the `patternProperty` "^[a-z]+$". + */ + [k: string]: + | number + | { + hard: number; + soft: number; + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; + }; +} +export interface BlkioLimit { + path?: string; + rate?: number | string; +} +export interface BlkioWeight { + path?: string; + weight?: number; +} +export interface DefinitionsHealthcheck { + disable?: boolean; + interval?: string; + retries?: number; + test?: string | string[]; + timeout?: string; + start_period?: string; + start_interval?: string; + /** + * This interface was referenced by `DefinitionsHealthcheck`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; +} +export interface PropertiesNetworks { + [k: string]: DefinitionsNetwork; +} +export interface PropertiesVolumes { + [k: string]: DefinitionsVolume; +} +export interface PropertiesSecrets { + [k: string]: DefinitionsSecret; +} +/** + * This interface was referenced by `PropertiesSecrets`'s JSON-Schema definition + * via the `patternProperty` "^[a-zA-Z0-9._-]+$". + */ +export interface DefinitionsSecret { + name?: string; + environment?: string; + file?: string; + external?: + | boolean + | { + name?: string; + [k: string]: unknown; + }; + labels?: ListOrDict; + driver?: string; + driver_opts?: { + /** + * This interface was referenced by `undefined`'s JSON-Schema definition + * via the `patternProperty` "^.+$". + */ + [k: string]: string | number; + }; + template_driver?: string; + /** + * This interface was referenced by `DefinitionsSecret`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; +} +export interface PropertiesConfigs { + [k: string]: DefinitionsConfig; +} +/** + * This interface was referenced by `PropertiesConfigs`'s JSON-Schema definition + * via the `patternProperty` "^[a-zA-Z0-9._-]+$". + */ +export interface DefinitionsConfig { + name?: string; + content?: string; + environment?: string; + file?: string; + external?: + | boolean + | { + name?: string; + [k: string]: unknown; + }; + labels?: ListOrDict; + template_driver?: string; + /** + * This interface was referenced by `DefinitionsConfig`'s JSON-Schema definition + * via the `patternProperty` "^x-". + */ + [k: string]: unknown; +} \ No newline at end of file diff --git a/build-scripts/validate-docker-compose.ts b/build-scripts/validate-docker-compose.ts new file mode 100644 index 000000000..f175b9fa1 --- /dev/null +++ b/build-scripts/validate-docker-compose.ts @@ -0,0 +1,357 @@ +#!/usr/bin/env tsx + +/** + * Validation script for docker-compose.yml files + * Validates structure, syntax, and best practices for Dokploy templates + */ + +import * as fs from "fs"; +import * as path from "path"; +import * as yaml from "yaml"; +import type { ComposeSpecification, DefinitionsService } from "./type"; + +interface DockerComposeValidatorOptions { + composePath?: string | null; + verbose?: boolean; + exitOnError?: boolean; +} + +interface ValidationResult { + valid: boolean; + errors: string[]; + warnings: string[]; +} + +type LogLevel = "info" | "success" | "warning" | "error" | "debug"; + +class DockerComposeValidator { + private options: Required; + private errors: string[] = []; + private warnings: string[] = []; + + constructor(options: DockerComposeValidatorOptions = {}) { + this.options = { + composePath: options.composePath || null, + verbose: options.verbose || false, + exitOnError: options.exitOnError !== false, + ...options, + }; + } + + private log(message: string, level: LogLevel = "info"): void { + if (!this.options.verbose && level === "debug") return; + + const prefix: Record = { + info: "🔍", + success: "✅", + warning: "⚠️", + error: "❌", + debug: "🔍", + }; + + console.log(`${prefix[level] || "ℹ️"} ${message}`); + } + + private error(message: string): void { + this.errors.push(message); + this.log(message, "error"); + } + + private warning(message: string): void { + this.warnings.push(message); + this.log(message, "warning"); + } + + /** + * Parse docker-compose.yml file + */ + private parseCompose(composePath: string): ComposeSpecification | null { + try { + if (!fs.existsSync(composePath)) { + this.error(`docker-compose.yml not found at ${composePath}`); + return null; + } + + const content = fs.readFileSync(composePath, "utf8"); + const compose = yaml.parse(content) as ComposeSpecification; + + if (!compose || typeof compose !== "object") { + this.error(`Invalid docker-compose.yml structure at ${composePath}`); + return null; + } + + return compose; + } catch (error: any) { + this.error(`Failed to parse docker-compose.yml: ${error.message}`); + return null; + } + } + + /** + * Validate that docker-compose.yml can be processed by Docker Compose + */ + private validateDockerComposeSyntax(composePath: string): boolean { + // This would ideally use docker compose config, but for now we validate structure + // The actual syntax validation happens in the CI/CD workflow with docker compose config + const compose = this.parseCompose(composePath); + return compose !== null; + } + + /** + * Validate services don't use container_name (Dokploy best practice) + */ + private validateNoContainerName(services: Record): void { + Object.entries(services).forEach(([serviceName, service]) => { + if (service.container_name) { + this.error( + `Service '${serviceName}': Found 'container_name' field. According to README, container_name should not be used. Dokploy manages container names automatically.` + ); + } + }); + } + + /** + * Validate no explicit networks (Dokploy creates networks automatically) + */ + private validateNoExplicitNetworks( + compose: ComposeSpecification, + services: Record + ): void { + // Check for dokploy-network specifically + const hasDokployNetwork = compose.networks && "dokploy-network" in compose.networks; + + // Check if any service uses explicit networks + Object.entries(services).forEach(([serviceName, service]) => { + if (service.networks) { + if (typeof service.networks === "object" && !Array.isArray(service.networks)) { + const networkNames = Object.keys(service.networks); + if (networkNames.includes("dokploy-network")) { + this.error( + `Service '${serviceName}': Uses 'dokploy-network'. Dokploy creates networks automatically, explicit networks are not needed.` + ); + } else if (networkNames.length > 0) { + this.error( + `Service '${serviceName}': Uses explicit network configuration. Dokploy creates networks automatically, explicit networks are not needed.` + ); + } + } else if (Array.isArray(service.networks)) { + if (service.networks.includes("dokploy-network")) { + this.error( + `Service '${serviceName}': Uses 'dokploy-network'. Dokploy creates networks automatically, explicit networks are not needed.` + ); + } else if (service.networks.length > 0) { + this.error( + `Service '${serviceName}': Uses explicit network configuration. Dokploy creates networks automatically, explicit networks are not needed.` + ); + } + } + } + }); + + // Check if networks section exists at root level + if (hasDokployNetwork) { + this.error( + "Found 'dokploy-network' in networks section. Dokploy creates networks automatically, explicit networks are not needed." + ); + } + + if (compose.networks && Object.keys(compose.networks).length > 0) { + this.error( + "Found explicit networks section. Dokploy creates networks automatically, explicit networks are not needed." + ); + } + } + + /** + * Validate ports are not mapped (should be just numbers, not host:container) + */ + private validatePortsFormat(services: Record): void { + Object.entries(services).forEach(([serviceName, service]) => { + if (service.ports) { + service.ports.forEach((port, index) => { + if (typeof port === "string") { + // Check for port mapping format (e.g., "3000:3000" or "8080:80") + if (/^\d+:\d+/.test(port)) { + this.error( + `Service '${serviceName}': ports[${index}] uses port mapping format '${port}'. According to README, use only port number (e.g., '3000') instead of '3000:3000'. Dokploy handles port routing.` + ); + } + } else if (typeof port === "object" && port !== null) { + // Check for published port mapping + if (port.published && port.target) { + this.error( + `Service '${serviceName}': ports[${index}] uses port mapping (published: ${port.published}, target: ${port.target}). According to README, use only port number. Dokploy handles port routing.` + ); + } + } + }); + } + }); + } + + /** + * Validate services exist + */ + private validateServicesExist(compose: ComposeSpecification): boolean { + if (!compose.services || Object.keys(compose.services).length === 0) { + this.error("No services found in docker-compose.yml"); + return false; + } + + const serviceNames = Object.keys(compose.services); + this.log(`Found ${serviceNames.length} service(s): ${serviceNames.join(", ")}`, "debug"); + + return true; + } + + /** + * Validate service names follow best practices + */ + private validateServiceNames(services: Record): void { + Object.keys(services).forEach((serviceName) => { + // Service names should be lowercase and use hyphens + if (serviceName !== serviceName.toLowerCase()) { + this.warning( + `Service '${serviceName}': Service names should be lowercase. Consider using '${serviceName.toLowerCase()}'.` + ); + } + + // Service names should not contain underscores (use hyphens instead) + if (serviceName.includes("_")) { + this.warning( + `Service '${serviceName}': Service names should use hyphens instead of underscores. Consider using '${serviceName.replace(/_/g, "-")}'.` + ); + } + }); + } + + + /** + * Main validation method + */ + validate(): ValidationResult { + if (!this.options.composePath) { + this.error("composePath option is required"); + if (this.options.exitOnError) { + process.exit(1); + } + return { valid: false, errors: this.errors, warnings: this.warnings }; + } + + const composePath = this.options.composePath; + const templateName = path.basename(path.dirname(composePath)); + + this.log(`Validating docker-compose.yml: ${templateName}`); + + // Parse and validate syntax + if (!this.validateDockerComposeSyntax(composePath)) { + if (this.options.exitOnError) { + process.exit(1); + } + return { valid: false, errors: this.errors, warnings: this.warnings }; + } + + const compose = this.parseCompose(composePath); + if (!compose) { + if (this.options.exitOnError) { + process.exit(1); + } + return { valid: false, errors: this.errors, warnings: this.warnings }; + } + + // Validate services exist + if (!this.validateServicesExist(compose)) { + if (this.options.exitOnError) { + process.exit(1); + } + return { valid: false, errors: this.errors, warnings: this.warnings }; + } + + const services = compose.services || {}; + + // Run all validations + this.validateNoContainerName(services); + this.validateNoExplicitNetworks(compose, services); + this.validatePortsFormat(services); + this.validateServiceNames(services); + + // Show summary + if (this.errors.length === 0) { + this.log("Docker Compose file structure is valid", "success"); + + if (this.options.verbose) { + this.log("📋 Services found:", "info"); + Object.keys(services).forEach((serviceName) => { + const service = services[serviceName]; + const image = typeof service.image === "string" ? service.image : "N/A"; + this.log(` - ${serviceName}: ${image}`, "debug"); + }); + } + } + + const valid = this.errors.length === 0; + + if (!valid && this.options.exitOnError) { + process.exit(1); + } + + return { valid, errors: this.errors, warnings: this.warnings }; + } +} + +// CLI usage +if (require.main === module) { + const args = process.argv.slice(2); + const options: DockerComposeValidatorOptions = {}; + let composePath: string | null = null; + + // Parse command line arguments + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + switch (arg) { + case "--file": + case "-f": + composePath = args[++i]; + break; + case "--verbose": + case "-v": + options.verbose = true; + break; + case "--help": + case "-h": + console.log(` +Usage: tsx validate-docker-compose.ts [options] + +Options: + -f, --file Docker Compose file path (required) + -v, --verbose Verbose output + -h, --help Show this help message + +Examples: + tsx validate-docker-compose.ts --file blueprints/grafana/docker-compose.yml + tsx validate-docker-compose.ts -f blueprints/grafana/docker-compose.yml --verbose + `); + process.exit(0); + break; + } + } + + if (!composePath) { + console.error("❌ Error: --file option is required"); + console.error("Use --help for usage information"); + process.exit(1); + } + + const validator = new DockerComposeValidator({ + composePath, + ...options, + }); + + const result = validator.validate(); + + // Exit with appropriate code + process.exit(result.valid ? 0 : 1); +} + +export default DockerComposeValidator; + diff --git a/build-scripts/validate-template.ts b/build-scripts/validate-template.ts new file mode 100644 index 000000000..aa0eb4d61 --- /dev/null +++ b/build-scripts/validate-template.ts @@ -0,0 +1,625 @@ +#!/usr/bin/env tsx + +/** + * Validation script for template.toml and docker-compose.yml files + * Validates structure, syntax, and consistency between files + */ + +import * as fs from "fs"; +import * as path from "path"; +import { parse } from "toml"; +import * as yaml from "yaml"; +import type { ComposeSpecification } from "./type"; +import { processVariables, processValue, type Schema } from "./helpers"; + +interface TemplateValidatorOptions { + templateDir?: string | null; + composeServices?: string[] | null; + verbose?: boolean; + exitOnError?: boolean; +} + +interface ValidationResult { + valid: boolean; + errors: string[]; + warnings: string[]; +} + +interface DomainConfig { + serviceName?: string; + port?: number | string; + host?: string; + path?: string; +} + +interface MountConfig { + filePath?: string; + content?: string; +} + +interface TemplateData { + variables?: Record; + config?: { + domains?: DomainConfig[]; + env?: string[] | Record | Array>; + mounts?: MountConfig[]; + }; +} + +type LogLevel = "info" | "success" | "warning" | "error" | "debug"; + +class TemplateValidator { + private options: Required; + private errors: string[] = []; + private warnings: string[] = []; + + constructor(options: TemplateValidatorOptions = {}) { + this.options = { + templateDir: options.templateDir || null, + composeServices: options.composeServices || null, + verbose: options.verbose || false, + exitOnError: options.exitOnError !== false, + ...options, + }; + } + + private log(message: string, level: LogLevel = "info"): void { + if (!this.options.verbose && level === "debug") return; + + const prefix: Record = { + info: "🔍", + success: "✅", + warning: "⚠️", + error: "❌", + debug: "🔍", + }; + + console.log(`${prefix[level] || "ℹ️"} ${message}`); + } + + private error(message: string): void { + this.errors.push(message); + this.log(message, "error"); + } + + private warning(message: string): void { + this.warnings.push(message); + this.log(message, "warning"); + } + + /** + * Validate helper syntax (based on Dokploy's processValue function) + */ + private validateHelper(helper: string, context: string = ""): void { + const validHelpers = [ + "domain", + "base64", + "password", + "hash", + "uuid", + "timestamp", + "timestampms", + "timestamps", + "randomPort", + "jwt", + "username", + "email", + ]; + + // Check if it's a helper with parameters + if (helper.includes(":")) { + const [helperName, ...params] = helper.split(":"); + + // Validate helper name + if (!validHelpers.includes(helperName)) { + // Might be a variable reference, which is valid + return; + } + + // Validate parameter formats + if (helperName === "base64" || helperName === "password" || helperName === "hash") { + // Format: helper:number + const param = params[0]; + if (param && isNaN(parseInt(param, 10))) { + this.warning( + `${context}: helper '${helper}' has invalid parameter (should be a number)` + ); + } + } else if (helperName === "timestampms" || helperName === "timestamps") { + // Format: timestampms:datetime or timestamps:datetime + const datetime = params.join(":"); // Rejoin in case datetime has colons + if (datetime) { + // Try to parse as date + const date = new Date(datetime); + if (isNaN(date.getTime())) { + this.warning( + `${context}: helper '${helper}' has invalid datetime format` + ); + } + } + } else if (helperName === "jwt") { + // Format: jwt:secret or jwt:secret:payload or jwt:length + if (params.length > 0) { + const firstParam = params[0]; + // If it's a number, it's jwt:length (deprecated but valid) + if (!isNaN(parseInt(firstParam, 10))) { + // Valid: jwt:32 + return; + } + // Otherwise it's jwt:secret or jwt:secret:payload + // Both are valid + } + } + } else { + // Simple helper without parameters + if (!validHelpers.includes(helper)) { + // Might be a variable reference, which is valid + return; + } + } + } + + /** + * Parse docker-compose.yml and extract service names + */ + private parseComposeServices(composePath: string): string[] { + try { + if (!fs.existsSync(composePath)) { + this.warning(`docker-compose.yml not found at ${composePath}`); + return []; + } + + const content = fs.readFileSync(composePath, "utf8"); + const compose = yaml.parse(content) as ComposeSpecification; + + if (!compose || typeof compose !== "object") { + this.error(`Invalid docker-compose.yml structure at ${composePath}`); + return []; + } + + // Extract service names using the official ComposeSpecification type + const services = compose.services || {}; + const serviceNames = Object.keys(services); + + if (serviceNames.length === 0) { + this.warning(`No services found in docker-compose.yml at ${composePath}`); + } + + return serviceNames; + } catch (error: any) { + this.error( + `Failed to parse docker-compose.yml at ${composePath}: ${error.message}` + ); + return []; + } + } + + /** + * Validate template.toml structure + */ + private validateTemplate(tomlPath: string, composeServices: string[] | null = null): boolean { + try { + if (!fs.existsSync(tomlPath)) { + this.error(`template.toml not found at ${tomlPath}`); + return false; + } + + // Parse TOML + let data: TemplateData; + try { + const content = fs.readFileSync(tomlPath, "utf8"); + data = parse(content) as TemplateData; + } catch (parseError: any) { + this.error( + `Invalid TOML syntax in ${tomlPath}: ${parseError.message}` + ); + return false; + } + + // Validate [config] section exists + if (!data.config) { + this.error("Missing [config] section in template.toml"); + return false; + } + + // Validate domains + if (data.config.domains) { + if (!Array.isArray(data.config.domains)) { + this.error("config.domains must be an array"); + return false; + } + + data.config.domains.forEach((domain, index) => { + // Required fields + if (!domain.serviceName) { + this.error(`domain[${index}]: Missing required field 'serviceName'`); + } + if (domain.port === undefined || domain.port === null) { + this.error(`domain[${index}]: Missing required field 'port'`); + } + if (!domain.host) { + this.error(`domain[${index}]: Missing required field 'host'`); + } + + // Validate serviceName matches docker-compose.yml services + if (domain.serviceName && composeServices && composeServices.length > 0) { + if (!composeServices.includes(domain.serviceName)) { + this.error( + `domain[${index}]: serviceName '${domain.serviceName}' not found in docker-compose.yml services. Available services: ${composeServices.join(", ")}` + ); + } + } + + // Validate port is a number + if (domain.port !== undefined && domain.port !== null) { + const port = typeof domain.port === "string" + ? parseInt(domain.port.replace(/_/g, ""), 10) + : domain.port; + + if (isNaN(Number(port)) || Number(port) < 1 || Number(port) > 65535) { + this.warning( + `domain[${index}]: port '${domain.port}' may be invalid (should be 1-65535)` + ); + } + } + + // Validate host format (should contain ${} for variable substitution) + if (domain.host && typeof domain.host === "string") { + if (!domain.host.includes("${")) { + this.warning( + `domain[${index}]: host '${domain.host}' doesn't use variable syntax (e.g., \${main_domain} or \${domain})` + ); + } else { + // Validate helpers in host + const helperPattern = /\${([^}]+)}/g; + let match: RegExpExecArray | null; + while ((match = helperPattern.exec(domain.host)) !== null) { + this.validateHelper(match[1], `domain[${index}].host`); + } + } + } + }); + } else { + this.warning("No domains configured in template.toml"); + } + + // Validate env - can be array or object (as per Dokploy's processEnvVars) + if (data.config.env !== undefined) { + if (Array.isArray(data.config.env)) { + // Array format: ["KEY=VALUE", ...] + data.config.env.forEach((env, index) => { + if (typeof env === "string") { + if (!env.includes("=")) { + this.warning( + `config.env[${index}]: '${env}' doesn't follow KEY=VALUE format` + ); + } + } else if (typeof env === "object" && env !== null) { + // Object in array is also valid: [{"KEY": "VALUE"}, ...] + const keys = Object.keys(env); + if (keys.length === 0) { + this.warning(`config.env[${index}]: empty object`); + } + } else if (typeof env !== "boolean" && typeof env !== "number") { + this.error( + `config.env[${index}]: must be a string, object, boolean, or number` + ); + } + }); + } else if (typeof data.config.env === "object" && data.config.env !== null) { + // Object format: { KEY: "VALUE", ... } + // This is valid - Dokploy handles both formats + const envKeys = Object.keys(data.config.env); + if (envKeys.length === 0) { + this.warning("config.env is an empty object"); + } + } else { + this.error( + "config.env must be an array or an object (as per Dokploy's processEnvVars)" + ); + } + } + + // Validate mounts if present + if (data.config.mounts) { + if (!Array.isArray(data.config.mounts)) { + this.error("config.mounts must be an array"); + } else { + data.config.mounts.forEach((mount, index) => { + if (!mount.filePath) { + this.error(`config.mounts[${index}]: Missing required field 'filePath'`); + } else if (typeof mount.filePath !== "string") { + this.error(`config.mounts[${index}]: filePath must be a string`); + } + + if (mount.content === undefined) { + this.error(`config.mounts[${index}]: Missing required field 'content'`); + } else if (typeof mount.content !== "string") { + this.error(`config.mounts[${index}]: content must be a string`); + } + }); + } + } + + // Validate variables if present + if (data.variables) { + if (typeof data.variables !== "object" || Array.isArray(data.variables)) { + this.error("variables must be an object"); + } else { + // Validate variable values and helpers + Object.entries(data.variables).forEach(([key, value]) => { + if (typeof value !== "string") { + this.error(`variables.${key}: must be a string`); + return; + } + + // Validate helpers in variable values + const helperPattern = /\${([^}]+)}/g; + let match: RegExpExecArray | null; + while ((match = helperPattern.exec(value)) !== null) { + const helper = match[1]; + this.validateHelper(helper, `variables.${key}`); + } + }); + + // Try to process variables to ensure they resolve correctly + try { + const schema: Schema = {}; + const processedVars = processVariables(data.variables, schema); + + // Check if any variables failed to resolve (still contain ${}) + Object.entries(processedVars).forEach(([key, value]) => { + if (typeof value === "string" && value.includes("${")) { + // Check if it's a valid variable reference or an error + const unresolved = value.match(/\${([^}]+)}/g); + if (unresolved) { + unresolved.forEach((unresolvedVar) => { + const varName = unresolvedVar.slice(2, -1); + // Check if it's a reference to another variable that exists + if (!data.variables![varName] && !varName.includes(":")) { + this.warning( + `variables.${key}: contains unresolved variable reference '${unresolvedVar}'` + ); + } + }); + } + } + }); + + // Validate that domains can be processed with resolved variables + if (data.config.domains) { + data.config.domains.forEach((domain, index) => { + if (domain.host && typeof domain.host === "string") { + try { + const processedHost = processValue(domain.host, processedVars, schema); + if (processedHost.includes("${")) { + this.warning( + `domain[${index}].host: could not fully resolve all variables. Result: ${processedHost}` + ); + } + } catch (e: any) { + this.warning( + `domain[${index}].host: error processing host value: ${e.message}` + ); + } + } + }); + } + + // Validate that env vars can be processed + if (data.config.env) { + if (Array.isArray(data.config.env)) { + data.config.env.forEach((env, index) => { + if (typeof env === "string") { + try { + const processed = processValue(env, processedVars, schema); + if (processed.includes("${")) { + this.warning( + `config.env[${index}]: could not fully resolve all variables` + ); + } + } catch (e: any) { + this.warning( + `config.env[${index}]: error processing env value: ${e.message}` + ); + } + } + }); + } else if (typeof data.config.env === "object") { + Object.entries(data.config.env).forEach(([key, value]) => { + if (typeof value === "string") { + try { + const processed = processValue(value, processedVars, schema); + if (processed.includes("${")) { + this.warning( + `config.env.${key}: could not fully resolve all variables` + ); + } + } catch (e: any) { + this.warning( + `config.env.${key}: error processing env value: ${e.message}` + ); + } + } + }); + } + } + + // Validate that mounts can be processed + if (data.config.mounts) { + data.config.mounts.forEach((mount, index) => { + if (mount.filePath && typeof mount.filePath === "string") { + try { + const processed = processValue(mount.filePath, processedVars, schema); + if (processed.includes("${")) { + this.warning( + `config.mounts[${index}].filePath: could not fully resolve all variables` + ); + } + } catch (e: any) { + this.warning( + `config.mounts[${index}].filePath: error processing filePath: ${e.message}` + ); + } + } + if (mount.content && typeof mount.content === "string") { + try { + const processed = processValue(mount.content, processedVars, schema); + if (processed.includes("${")) { + this.warning( + `config.mounts[${index}].content: could not fully resolve all variables` + ); + } + } catch (e: any) { + this.warning( + `config.mounts[${index}].content: error processing content: ${e.message}` + ); + } + } + }); + } + + if (this.options.verbose) { + this.log("✅ Variables processed successfully", "success"); + this.log(`📋 Processed ${Object.keys(processedVars).length} variables`, "debug"); + } + } catch (e: any) { + this.error(`Failed to process variables: ${e.message}`); + } + } + } + + return this.errors.length === 0; + } catch (error: any) { + this.error(`Error validating template.toml: ${error.message}`); + return false; + } + } + + /** + * Validate a template directory + */ + private validateTemplateDir(templateDir: string): ValidationResult { + // Resolver rutas absolutas o relativas desde la raíz del proyecto + const resolvedDir = path.isAbsolute(templateDir) + ? templateDir + : path.resolve(process.cwd(), templateDir); + + const templatePath = path.join(resolvedDir, "template.toml"); + const composePath = path.join(resolvedDir, "docker-compose.yml"); + + this.log(`Validating template: ${path.basename(resolvedDir)}`); + + // Parse compose services first + const composeServices = this.parseComposeServices(composePath); + + // Validate template.toml + const isValid = this.validateTemplate(templatePath, composeServices); + + // Show summary + if (isValid && this.errors.length === 0) { + this.log("Template structure is valid", "success"); + + // Show domains info + try { + const content = fs.readFileSync(templatePath, "utf8"); + const data = parse(content) as TemplateData; + if (data.config && data.config.domains) { + this.log("📋 Domains configured:"); + data.config.domains.forEach((domain) => { + const service = domain.serviceName || "N/A"; + const port = domain.port !== undefined ? domain.port : "N/A"; + const host = domain.host || "N/A"; + this.log(` - Service: ${service}, Port: ${port}, Host: ${host}`); + }); + } + } catch (e) { + // Ignore errors in summary + } + } + + return { + valid: isValid && this.errors.length === 0, + errors: this.errors, + warnings: this.warnings, + }; + } + + /** + * Main validation method + */ + validate(): ValidationResult { + if (!this.options.templateDir) { + this.error("templateDir option is required"); + if (this.options.exitOnError) { + process.exit(1); + } + return { valid: false, errors: this.errors, warnings: this.warnings }; + } + + const result = this.validateTemplateDir(this.options.templateDir!); + + if (!result.valid && this.options.exitOnError) { + process.exit(1); + } + + return result; + } +} + +// CLI usage +if (require.main === module) { + const args = process.argv.slice(2); + const options: TemplateValidatorOptions = {}; + let templateDir: string | null = null; + + // Parse command line arguments + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + switch (arg) { + case "--dir": + case "-d": + templateDir = args[++i]; + break; + case "--verbose": + case "-v": + options.verbose = true; + break; + case "--help": + case "-h": + console.log(` +Usage: tsx validate-template.ts [options] + +Options: + -d, --dir Template directory path (required) + -v, --verbose Verbose output + -h, --help Show this help message + +Examples: + tsx validate-template.ts --dir blueprints/grafana + tsx validate-template.ts -d blueprints/grafana --verbose + `); + process.exit(0); + break; + } + } + + if (!templateDir) { + console.error("❌ Error: --dir option is required"); + console.error("Use --help for usage information"); + process.exit(1); + } + + const validator = new TemplateValidator({ + templateDir, + ...options, + }); + + const result = validator.validate(); + + // Exit with appropriate code + process.exit(result.valid ? 0 : 1); +} + +export default TemplateValidator; + From aa48af78563c1810655c2c72a41030bca54d06e3 Mon Sep 17 00:00:00 2001 From: M Jupri Amin <127651222+Juupeee@users.noreply.github.com> Date: Sun, 14 Dec 2025 12:37:00 +0700 Subject: [PATCH 26/91] Add Passbolt template blueprint to Dokploy templates (#376) * feat(templates): add Passbolt blueprint for Dokploy - Add docker-compose.yml defining services for Passbolt and MariaDB - Create template.toml with configurable domain, email, and database credentials - Add meta.json with metadata, tags, and link to logo * fix(meta): sort meta.json entries * fix: passbolt template had several issues that broke deployment - env variables were using old array format, changed to new table format - mariadb healthcheck was broken (wrong command for mariadb 11) - missing volume mounts for gpg keys, jwt tokens, and database - setup instructions weren't visible to users, moved to docker-compose - email config had circular references causing warnings - tested admin user creation and confirmed working everything works now, fully tested * Update blueprints/passbolt/template.toml --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> --- blueprints/passbolt/docker-compose.yml | 79 ++++++++++++++++++++++++++ blueprints/passbolt/passbolt.svg | 8 +++ blueprints/passbolt/template.toml | 41 +++++++++++++ meta.json | 18 ++++++ 4 files changed, 146 insertions(+) create mode 100644 blueprints/passbolt/docker-compose.yml create mode 100644 blueprints/passbolt/passbolt.svg create mode 100644 blueprints/passbolt/template.toml diff --git a/blueprints/passbolt/docker-compose.yml b/blueprints/passbolt/docker-compose.yml new file mode 100644 index 000000000..f6b91aac1 --- /dev/null +++ b/blueprints/passbolt/docker-compose.yml @@ -0,0 +1,79 @@ +# ============================================================================= +# PASSBOLT TEMPLATE - SETUP INSTRUCTIONS +# ============================================================================= +# +# After successful deployment, you need to create an admin user: +# +# 1. Go to your Dokploy dashboard +# 2. Navigate to your Passbolt application +# 3. Wait for both containers to be healthy - check the "Monitoring" tab +# 4. Go to the "General" tab and click "Open Terminal" button +# 5. In the terminal, run this command to create admin user: +# su -s /bin/bash -c "/usr/share/php/passbolt/bin/cake passbolt register_user -u youremail@example.com -f FirstName -l LastName -r admin" www-data +# 6. Replace youremail@example.com, FirstName, LastName with your actual details +# 7. The command will output a registration link - copy and paste it in your browser to complete setup +# +# NOTE: If you change the domain after deployment, you will need to manually +# update the PASSBOLT_APP_FULL_BASE_URL environment variable in the "Environment" tab. +# ============================================================================= + +services: + passbolt: + image: passbolt/passbolt:latest-ce + environment: + APP_FULL_BASE_URL: ${PASSBOLT_APP_FULL_BASE_URL} + DATASOURCES_DEFAULT_HOST: ${PASSBOLT_DB_HOST} + DATASOURCES_DEFAULT_PORT: ${PASSBOLT_DB_PORT} + DATASOURCES_DEFAULT_USERNAME: ${PASSBOLT_DB_USER} + DATASOURCES_DEFAULT_PASSWORD: ${PASSBOLT_DB_PASSWORD} + DATASOURCES_DEFAULT_DATABASE: ${PASSBOLT_DB_NAME} + PASSBOLT_PLUGINS_JWT_AUTHENTICATION_ENABLED: ${PASSBOLT_PLUGINS_JWT_AUTHENTICATION_ENABLED} + + EMAIL_DEFAULT_FROM: ${PASSBOLT_EMAIL_FROM} + EMAIL_TRANSPORT_DEFAULT_HOST: ${PASSBOLT_EMAIL_HOST} + EMAIL_TRANSPORT_DEFAULT_PORT: ${PASSBOLT_EMAIL_PORT} + EMAIL_TRANSPORT_DEFAULT_USERNAME: ${PASSBOLT_EMAIL_USER} + EMAIL_TRANSPORT_DEFAULT_PASSWORD: ${PASSBOLT_EMAIL_PASS} + EMAIL_TRANSPORT_DEFAULT_TLS: ${PASSBOLT_EMAIL_TLS} + + volumes: + - gpg_volume:/etc/passbolt/gpg + - jwt_volume:/etc/passbolt/jwt + + command: + - /usr/bin/wait-for.sh + - "-t" + - "0" + - "${PASSBOLT_DB_HOST}:${PASSBOLT_DB_PORT}" + - "--" + - /docker-entrypoint.sh + + depends_on: + mariadb: + condition: service_healthy + + healthcheck: + test: ["CMD", "curl", "-f", "http://127.0.0.1:80"] + interval: 5s + timeout: 20s + retries: 10 + + mariadb: + image: mariadb:11 + environment: + MARIADB_ROOT_PASSWORD: ${PASSBOLT_DB_ROOT_PASSWORD} + MARIADB_DATABASE: ${PASSBOLT_DB_NAME} + MARIADB_USER: ${PASSBOLT_DB_USER} + MARIADB_PASSWORD: ${PASSBOLT_DB_PASSWORD} + volumes: + - passbolt_mariadb_data:/var/lib/mysql + healthcheck: + test: ["CMD", "mariadb-admin", "ping", "-h", "localhost", "--silent"] + interval: 10s + timeout: 5s + retries: 5 + +volumes: + gpg_volume: {} + jwt_volume: {} + passbolt_mariadb_data: {} diff --git a/blueprints/passbolt/passbolt.svg b/blueprints/passbolt/passbolt.svg new file mode 100644 index 000000000..40a905ab5 --- /dev/null +++ b/blueprints/passbolt/passbolt.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/blueprints/passbolt/template.toml b/blueprints/passbolt/template.toml new file mode 100644 index 000000000..e14042e41 --- /dev/null +++ b/blueprints/passbolt/template.toml @@ -0,0 +1,41 @@ +[variables] +main_domain = "${domain}" +db_password = "${password:32}" +db_root_password = "${password:32}" +email_host = "smtp.example.com" +email_user = "noreply@example.com" +email_pass = "${password:16}" + +[config] +[[config.domains]] +serviceName = "passbolt" +port = 80 +host = "${main_domain}" + +[config.env] +PASSBOLT_APP_FULL_BASE_URL = "http://${main_domain}" +PASSBOLT_DB_HOST = "mariadb" +PASSBOLT_DB_PORT = "3306" +PASSBOLT_DB_NAME = "passbolt" +PASSBOLT_DB_USER = "passbolt" +PASSBOLT_DB_PASSWORD = "${db_password}" +PASSBOLT_DB_ROOT_PASSWORD = "${db_root_password}" +PASSBOLT_PLUGINS_JWT_AUTHENTICATION_ENABLED = "true" +PASSBOLT_EMAIL_FROM = "passbolt@${main_domain}" +PASSBOLT_EMAIL_HOST = "${email_host}" +PASSBOLT_EMAIL_PORT = "587" +PASSBOLT_EMAIL_USER = "${email_user}" +PASSBOLT_EMAIL_PASS = "${email_pass}" +PASSBOLT_EMAIL_TLS = "true" + +[[config.mounts]] +volume = "gpg_volume" +target = "/etc/passbolt/gpg" + +[[config.mounts]] +volume = "jwt_volume" +target = "/etc/passbolt/jwt" + +[[config.mounts]] +volume = "passbolt_mariadb_data" +target = "/var/lib/mysql" \ No newline at end of file diff --git a/meta.json b/meta.json index 24d5fb181..be1c6ff8e 100644 --- a/meta.json +++ b/meta.json @@ -4322,6 +4322,24 @@ "open-source" ] }, + { + "id": "passbolt", + "name": "Passbolt", + "version": "latest-ce", + "description": "Passbolt is an open source credential platform for modern teams. A versatile, battle-tested solution to manage and collaborate on passwords, accesses, and secrets. All in one.", + "logo": "passbolt.svg", + "links": { + "github": "https://github.com/passbolt/passbolt_api", + "website": "https://www.passbolt.com/", + "docs": "https://www.passbolt.com/docs/" + }, + "tags": [ + "password-manager", + "security", + "team-collaboration", + "encryption" + ] + }, { "id": "pastefy", "name": "Pastefy", From 0aad2a0231f06963c7a55d128b2367a315054e8e Mon Sep 17 00:00:00 2001 From: Harikrishnan Dhanasekaran Date: Sun, 14 Dec 2025 11:10:48 +0530 Subject: [PATCH 27/91] feat: Add Kokoro TTS FastAPI template (#353) (#403) * feat: Add Kokoro TTS FastAPI template (#353) - Add CPU-optimized docker-compose.yml with source build - Add GPU-optimized docker-compose-gpu.yml for NVIDIA support - Add comprehensive template.toml with OpenAI-compatible API docs - Add kokoro-tts.svg logo and meta.json entry - Support streaming audio, timestamps, and multi-language TTS - Resolves #353 * updated the meta.json for the build errors * removed the docker-compose-gpu.yml file * Update docker-compose.yml --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> --- blueprints/kokoro-tts/docker-compose.yml | 23 ++++++++ blueprints/kokoro-tts/kokoro-tts.svg | 12 ++++ blueprints/kokoro-tts/template.toml | 72 ++++++++++++++++++++++++ meta.json | 19 +++++++ 4 files changed, 126 insertions(+) create mode 100644 blueprints/kokoro-tts/docker-compose.yml create mode 100644 blueprints/kokoro-tts/kokoro-tts.svg create mode 100644 blueprints/kokoro-tts/template.toml diff --git a/blueprints/kokoro-tts/docker-compose.yml b/blueprints/kokoro-tts/docker-compose.yml new file mode 100644 index 000000000..a0298ce1b --- /dev/null +++ b/blueprints/kokoro-tts/docker-compose.yml @@ -0,0 +1,23 @@ +services: + kokoro-tts: + build: + context: https://github.com/remsky/Kokoro-FastAPI.git#master + dockerfile: docker/cpu/Dockerfile + restart: unless-stopped + ports: + - 8880 + environment: + - MODEL_PATH=/app/models + - DEVICE=${DEVICE:-cpu} + - HOST=0.0.0.0 + - PORT=8880 + volumes: + - kokoro-models:/app/models + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8880/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s +volumes: + kokoro-models: diff --git a/blueprints/kokoro-tts/kokoro-tts.svg b/blueprints/kokoro-tts/kokoro-tts.svg new file mode 100644 index 000000000..5ed64ed8e --- /dev/null +++ b/blueprints/kokoro-tts/kokoro-tts.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/blueprints/kokoro-tts/template.toml b/blueprints/kokoro-tts/template.toml new file mode 100644 index 000000000..73f367c4a --- /dev/null +++ b/blueprints/kokoro-tts/template.toml @@ -0,0 +1,72 @@ +[variables] +main_domain = "${domain}" +device = "cpu" + +[config] +env = [ + "DEVICE=${device}", + "MODEL_PATH=/app/models", + "HOST=0.0.0.0", + "PORT=8880", + "PYTHONUNBUFFERED=1", + "UV_SYSTEM_PYTHON=1" +] + +[[config.domains]] +serviceName = "kokoro-tts" +port = 8880 +host = "${main_domain}" +path = "/" + +[[config.mounts]] +filePath = "README.md" +content = """# Kokoro TTS FastAPI + +This template provides a Dockerized FastAPI wrapper for the Kokoro-82M text-to-speech model. + +## Features + +- Multi-language support (English, Japanese, Chinese) +- OpenAI-compatible speech endpoint +- CPU and GPU support +- Web interface for monitoring +- RESTful API with comprehensive documentation +- Streaming audio generation +- Word-level timestamps and phonemes + +## Usage + +- **Web Interface**: Access the web UI at `https://${main_domain}/web` +- **API Documentation**: Available at `https://${main_domain}/docs` +- **Health Check**: Monitor service health at `https://${main_domain}/health` + +## Configuration + +The service runs on port 8880 and supports both CPU and GPU inference. +For GPU support, ensure your Dokploy instance has NVIDIA GPU support enabled. + +## API Endpoints + +- `POST /v1/audio/speech` - Generate speech from text (OpenAI compatible) +- `POST /dev/captioned_speech` - Generate speech with timestamps +- `POST /dev/phonemize` - Convert text to phonemes +- `POST /dev/generate_from_phonemes` - Generate audio from phonemes +- `GET /health` - Health check endpoint +- `GET /docs` - Interactive API documentation +- `GET /web` - Web interface + +## Model Information + +- Model: Kokoro-82M +- License: Apache-2.0 +- Repository: https://github.com/remsky/Kokoro-FastAPI +- HuggingFace Model: https://huggingface.co/hexgrad/Kokoro-82M + +## Notes + +- This template builds the image from source during deployment +- Uses CPU-optimized Dockerfile by default +- Initial build may take several minutes due to model download +- Ensure sufficient disk space for model storage +- For GPU support, change dockerfile path to `docker/gpu/Dockerfile` in docker-compose.yml +""" diff --git a/meta.json b/meta.json index be1c6ff8e..a61b99c6e 100644 --- a/meta.json +++ b/meta.json @@ -3195,6 +3195,25 @@ "personal" ] }, + { + "id": "kokoro-tts", + "name": "Kokoro TTS", + "version": "latest", + "description": "Dockerized FastAPI wrapper for the Kokoro-82M text-to-speech model with multi-language support and OpenAI-compatible endpoints.", + "logo": "kokoro-tts.svg", + "links": { + "github": "https://github.com/remsky/Kokoro-FastAPI", + "website": "https://github.com/remsky/Kokoro-FastAPI", + "docs": "https://github.com/remsky/Kokoro-FastAPI#readme" + }, + "tags": [ + "text-to-speech", + "ai", + "voice", + "fastapi", + "openai-compatible" + ] + }, { "id": "kokoro-web", "name": "Kokoro Web", From c59fbf0f36dbe7e065375eaf370607030762fc29 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Sat, 13 Dec 2025 23:41:40 -0600 Subject: [PATCH 28/91] chore: remove package-lock.json file from the app directory --- app/package-lock.json | 4248 ----------------------------------------- 1 file changed, 4248 deletions(-) delete mode 100644 app/package-lock.json diff --git a/app/package-lock.json b/app/package-lock.json deleted file mode 100644 index 9579d3d93..000000000 --- a/app/package-lock.json +++ /dev/null @@ -1,4248 +0,0 @@ -{ - "name": "my-app", - "version": "0.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "my-app", - "version": "0.0.0", - "dependencies": { - "@codemirror/autocomplete": "^6.18.6", - "@codemirror/lang-json": "^6.0.1", - "@codemirror/lang-yaml": "^6.1.1", - "@codemirror/language": "^6.10.1", - "@codemirror/legacy-modes": "6.4.0", - "@codemirror/view": "6.29.0", - "@iarna/toml": "^2.2.5", - "@radix-ui/react-dialog": "^1.1.6", - "@radix-ui/react-dropdown-menu": "^2.1.6", - "@radix-ui/react-label": "^2.1.2", - "@radix-ui/react-popover": "^1.1.6", - "@radix-ui/react-slot": "^1.1.2", - "@radix-ui/react-tabs": "^1.1.3", - "@tailwindcss/vite": "^4.0.12", - "@uiw/codemirror-theme-github": "^4.22.1", - "@uiw/react-codemirror": "^4.22.1", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "cmdk": "1.0.0", - "copy-to-clipboard": "^3.3.3", - "lucide-react": "^0.479.0", - "next-themes": "^0.4.5", - "react": "^19.0.0", - "react-dom": "^19.0.0", - "react-router-dom": "^7.4.1", - "sonner": "^2.0.1", - "tailwind-merge": "^3.0.2", - "tailwindcss": "^4.0.12", - "tailwindcss-animate": "^1.0.7", - "vite-plugin-static-copy": "^2.3.2", - "yaml": "2.7.1", - "zustand": "^5.0.3" - }, - "devDependencies": { - "@types/node": "^20.8.2", - "@types/react": "^19.0.10", - "@types/react-dom": "^19.0.4", - "@vitejs/plugin-react": "^4.3.4", - "globals": "^15.15.0", - "typescript": "~5.7.2", - "vite": "^6.2.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", - "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.27.3", - "@babel/helpers": "^7.27.6", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.0", - "@babel/types": "^7.28.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", - "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.28.0", - "@babel/types": "^7.28.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", - "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", - "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", - "dev": true, - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.28.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", - "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", - "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", - "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", - "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.28.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz", - "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@codemirror/autocomplete": { - "version": "6.18.6", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz", - "integrity": "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.17.0", - "@lezer/common": "^1.0.0" - } - }, - "node_modules/@codemirror/commands": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.8.1.tgz", - "integrity": "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.4.0", - "@codemirror/view": "^6.27.0", - "@lezer/common": "^1.1.0" - } - }, - "node_modules/@codemirror/lang-json": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.2.tgz", - "integrity": "sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@lezer/json": "^1.0.0" - } - }, - "node_modules/@codemirror/lang-yaml": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-yaml/-/lang-yaml-6.1.2.tgz", - "integrity": "sha512-dxrfG8w5Ce/QbT7YID7mWZFKhdhsaTNOYjOkSIMt1qmC4VQnXSDSYVHHHn8k6kJUfIhtLo8t1JJgltlxWdsITw==", - "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.2.0", - "@lezer/lr": "^1.0.0", - "@lezer/yaml": "^1.0.0" - } - }, - "node_modules/@codemirror/language": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.2.tgz", - "integrity": "sha512-p44TsNArL4IVXDTbapUmEkAlvWs2CFQbcfc0ymDsis1kH2wh0gcY96AS29c/vp2d0y2Tquk1EDSaawpzilUiAw==", - "dependencies": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.23.0", - "@lezer/common": "^1.1.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0", - "style-mod": "^4.0.0" - } - }, - "node_modules/@codemirror/legacy-modes": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@codemirror/legacy-modes/-/legacy-modes-6.4.0.tgz", - "integrity": "sha512-5m/K+1A6gYR0e+h/dEde7LoGimMjRtWXZFg4Lo70cc8HzjSdHe3fLwjWMR0VRl5KFT1SxalSap7uMgPKF28wBA==", - "dependencies": { - "@codemirror/language": "^6.0.0" - } - }, - "node_modules/@codemirror/lint": { - "version": "6.8.5", - "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.5.tgz", - "integrity": "sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==", - "dependencies": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.35.0", - "crelt": "^1.0.5" - } - }, - "node_modules/@codemirror/lint/node_modules/@codemirror/view": { - "version": "6.38.1", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.1.tgz", - "integrity": "sha512-RmTOkE7hRU3OVREqFVITWHz6ocgBjv08GoePscAakgVQfciA3SGCEk7mb9IzwW61cKKmlTpHXG6DUE5Ubx+MGQ==", - "dependencies": { - "@codemirror/state": "^6.5.0", - "crelt": "^1.0.6", - "style-mod": "^4.1.0", - "w3c-keyname": "^2.2.4" - } - }, - "node_modules/@codemirror/search": { - "version": "6.5.11", - "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.11.tgz", - "integrity": "sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==", - "dependencies": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "crelt": "^1.0.5" - } - }, - "node_modules/@codemirror/state": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz", - "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==", - "dependencies": { - "@marijn/find-cluster-break": "^1.0.0" - } - }, - "node_modules/@codemirror/theme-one-dark": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz", - "integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/highlight": "^1.0.0" - } - }, - "node_modules/@codemirror/view": { - "version": "6.29.0", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.29.0.tgz", - "integrity": "sha512-ED4ims4fkf7eOA+HYLVP8VVg3NMllt1FPm9PEJBfYFnidKlRITBaua38u68L1F60eNtw2YNcDN5jsIzhKZwWQA==", - "dependencies": { - "@codemirror/state": "^6.4.0", - "style-mod": "^4.1.0", - "w3c-keyname": "^2.2.4" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", - "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", - "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", - "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", - "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", - "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", - "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", - "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", - "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", - "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", - "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", - "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", - "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", - "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", - "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", - "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", - "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", - "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", - "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", - "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", - "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", - "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", - "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", - "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", - "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", - "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", - "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@floating-ui/core": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.2.tgz", - "integrity": "sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==", - "dependencies": { - "@floating-ui/utils": "^0.2.10" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.2.tgz", - "integrity": "sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==", - "dependencies": { - "@floating-ui/core": "^1.7.2", - "@floating-ui/utils": "^0.2.10" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.4.tgz", - "integrity": "sha512-JbbpPhp38UmXDDAu60RJmbeme37Jbgsm7NrHGgzYYFKmblzRUh6Pa641dII6LsjwF4XlScDrde2UAzDo/b9KPw==", - "dependencies": { - "@floating-ui/dom": "^1.7.2" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", - "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==" - }, - "node_modules/@iarna/toml": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", - "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" - }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "dependencies": { - "minipass": "^7.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@lezer/common": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", - "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==" - }, - "node_modules/@lezer/highlight": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz", - "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==", - "dependencies": { - "@lezer/common": "^1.0.0" - } - }, - "node_modules/@lezer/json": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.3.tgz", - "integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==", - "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, - "node_modules/@lezer/lr": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz", - "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==", - "dependencies": { - "@lezer/common": "^1.0.0" - } - }, - "node_modules/@lezer/yaml": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@lezer/yaml/-/yaml-1.0.3.tgz", - "integrity": "sha512-GuBLekbw9jDBDhGur82nuwkxKQ+a3W5H0GfaAthDXcAu+XdpS43VlnxA9E9hllkpSP5ellRDKjLLj7Lu9Wr6xA==", - "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.4.0" - } - }, - "node_modules/@marijn/find-cluster-break": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", - "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==" - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@radix-ui/primitive": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", - "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==" - }, - "node_modules/@radix-ui/react-arrow": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", - "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", - "dependencies": { - "@radix-ui/react-primitive": "2.1.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-collection": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", - "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", - "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-context": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", - "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dialog": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.14.tgz", - "integrity": "sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==", - "dependencies": { - "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.10", - "@radix-ui/react-focus-guards": "1.1.2", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-direction": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", - "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz", - "integrity": "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==", - "dependencies": { - "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-escape-keydown": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.15.tgz", - "integrity": "sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ==", - "dependencies": { - "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-menu": "2.1.15", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz", - "integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", - "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-id": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", - "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-label": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz", - "integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==", - "dependencies": { - "@radix-ui/react-primitive": "2.1.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-menu": { - "version": "2.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.15.tgz", - "integrity": "sha512-tVlmA3Vb9n8SZSd+YSbuFR66l87Wiy4du+YE+0hzKQEANA+7cWKH1WgqcEX4pXqxUFQKrWQGHdvEfw00TjFiew==", - "dependencies": { - "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.10", - "@radix-ui/react-focus-guards": "1.1.2", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.7", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.10", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-popover": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.14.tgz", - "integrity": "sha512-ODz16+1iIbGUfFEfKx2HTPKizg2MN39uIOV8MXeHnmdd3i/N9Wt7vU46wbHsqA0xoaQyXVcs0KIlBdOA2Y95bw==", - "dependencies": { - "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.10", - "@radix-ui/react-focus-guards": "1.1.2", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.7", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-popper": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.7.tgz", - "integrity": "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==", - "dependencies": { - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-use-rect": "1.1.1", - "@radix-ui/react-use-size": "1.1.1", - "@radix-ui/rect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-portal": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", - "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", - "dependencies": { - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-presence": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz", - "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-primitive": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", - "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", - "dependencies": { - "@radix-ui/react-slot": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.10.tgz", - "integrity": "sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==", - "dependencies": { - "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tabs": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.12.tgz", - "integrity": "sha512-GTVAlRVrQrSw3cEARM0nAx73ixrWDPNZAruETn3oHCNP6SbZ/hNxdxp+u7VkIEv3/sFoLq1PfcHrl7Pnp0CDpw==", - "dependencies": { - "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.10", - "@radix-ui/react-use-controllable-state": "1.2.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", - "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", - "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", - "dependencies": { - "@radix-ui/react-use-effect-event": "0.0.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-effect-event": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", - "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", - "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", - "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", - "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-rect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", - "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", - "dependencies": { - "@radix-ui/rect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-size": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", - "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/rect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", - "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==" - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.27", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", - "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", - "dev": true - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz", - "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz", - "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz", - "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz", - "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz", - "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz", - "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz", - "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz", - "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz", - "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz", - "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz", - "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz", - "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz", - "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz", - "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz", - "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz", - "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz", - "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz", - "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz", - "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz", - "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@tailwindcss/node": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.11.tgz", - "integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==", - "dependencies": { - "@ampproject/remapping": "^2.3.0", - "enhanced-resolve": "^5.18.1", - "jiti": "^2.4.2", - "lightningcss": "1.30.1", - "magic-string": "^0.30.17", - "source-map-js": "^1.2.1", - "tailwindcss": "4.1.11" - } - }, - "node_modules/@tailwindcss/oxide": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.11.tgz", - "integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==", - "hasInstallScript": true, - "dependencies": { - "detect-libc": "^2.0.4", - "tar": "^7.4.3" - }, - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.11", - "@tailwindcss/oxide-darwin-arm64": "4.1.11", - "@tailwindcss/oxide-darwin-x64": "4.1.11", - "@tailwindcss/oxide-freebsd-x64": "4.1.11", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", - "@tailwindcss/oxide-linux-x64-musl": "4.1.11", - "@tailwindcss/oxide-wasm32-wasi": "4.1.11", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" - } - }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.11.tgz", - "integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.11.tgz", - "integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.11.tgz", - "integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.11.tgz", - "integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.11.tgz", - "integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.11.tgz", - "integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.11.tgz", - "integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.11.tgz", - "integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.11.tgz", - "integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.11.tgz", - "integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@emnapi/wasi-threads": "^1.0.2", - "@napi-rs/wasm-runtime": "^0.2.11", - "@tybys/wasm-util": "^0.9.0", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", - "integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.11.tgz", - "integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/vite": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.11.tgz", - "integrity": "sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw==", - "dependencies": { - "@tailwindcss/node": "4.1.11", - "@tailwindcss/oxide": "4.1.11", - "tailwindcss": "4.1.11" - }, - "peerDependencies": { - "vite": "^5.2.0 || ^6 || ^7" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", - "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.19.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.9.tgz", - "integrity": "sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==", - "dev": true, - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/react": { - "version": "19.1.8", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", - "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", - "dev": true, - "dependencies": { - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.1.6", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz", - "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", - "dev": true, - "peerDependencies": { - "@types/react": "^19.0.0" - } - }, - "node_modules/@uiw/codemirror-extensions-basic-setup": { - "version": "4.24.1", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.24.1.tgz", - "integrity": "sha512-o1m1a8eUS3fWERMbDFvN8t8sZUFPgDKNemmlQ5Ot2vKm+Ax84lKP1dhEFgkiOaZ1bDHk4T5h6SjHuTghrJHKww==", - "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/commands": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/lint": "^6.0.0", - "@codemirror/search": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0" - }, - "funding": { - "url": "https://jaywcjlove.github.io/#/sponsor" - }, - "peerDependencies": { - "@codemirror/autocomplete": ">=6.0.0", - "@codemirror/commands": ">=6.0.0", - "@codemirror/language": ">=6.0.0", - "@codemirror/lint": ">=6.0.0", - "@codemirror/search": ">=6.0.0", - "@codemirror/state": ">=6.0.0", - "@codemirror/view": ">=6.0.0" - } - }, - "node_modules/@uiw/codemirror-theme-github": { - "version": "4.24.1", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-github/-/codemirror-theme-github-4.24.1.tgz", - "integrity": "sha512-dl4qFEXINE4TFus7ALMfjFUCl7sWLkqTdaSaln0Vv3s+HVzSMAh5lkEdnH3yPcOOCl5ehYG4zIx8bqEnA2/FYQ==", - "dependencies": { - "@uiw/codemirror-themes": "4.24.1" - }, - "funding": { - "url": "https://jaywcjlove.github.io/#/sponsor" - } - }, - "node_modules/@uiw/codemirror-themes": { - "version": "4.24.1", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-themes/-/codemirror-themes-4.24.1.tgz", - "integrity": "sha512-hduBbFNiWNW6nYa2/giKQ9YpzhWNw87BGpCjC+cXYMZ7bCD6q5DC6Hw+7z7ZwSzEaOQvV91lmirOjJ8hn9+pkg==", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0" - }, - "funding": { - "url": "https://jaywcjlove.github.io/#/sponsor" - }, - "peerDependencies": { - "@codemirror/language": ">=6.0.0", - "@codemirror/state": ">=6.0.0", - "@codemirror/view": ">=6.0.0" - } - }, - "node_modules/@uiw/react-codemirror": { - "version": "4.24.1", - "resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.24.1.tgz", - "integrity": "sha512-BivF4NLqbuBQK5gPVhSkOARi9nPXw8X5r25EnInPeY+I9l1dfEX8O9V6+0xHTlGHyUo0cNfGEF9t1KHEicUfJw==", - "dependencies": { - "@babel/runtime": "^7.18.6", - "@codemirror/commands": "^6.1.0", - "@codemirror/state": "^6.1.1", - "@codemirror/theme-one-dark": "^6.0.0", - "@uiw/codemirror-extensions-basic-setup": "4.24.1", - "codemirror": "^6.0.0" - }, - "funding": { - "url": "https://jaywcjlove.github.io/#/sponsor" - }, - "peerDependencies": { - "@babel/runtime": ">=7.11.0", - "@codemirror/state": ">=6.0.0", - "@codemirror/theme-one-dark": ">=6.0.0", - "@codemirror/view": ">=6.0.0", - "codemirror": ">=6.0.0", - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@vitejs/plugin-react": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", - "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.28.0", - "@babel/plugin-transform-react-jsx-self": "^7.27.1", - "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-beta.27", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.17.0" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/aria-hidden": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", - "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.25.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", - "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001726", - "electron-to-chromium": "^1.5.173", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001727", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", - "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "engines": { - "node": ">=18" - } - }, - "node_modules/class-variance-authority": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", - "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", - "dependencies": { - "clsx": "^2.1.1" - }, - "funding": { - "url": "https://polar.sh/cva" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/cmdk": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.0.0.tgz", - "integrity": "sha512-gDzVf0a09TvoJ5jnuPvygTB77+XdOSwEmJ88L6XPFPlv7T3RxbP9jgenfylrAMD0+Le1aO0nVjQUzl2g+vjz5Q==", - "dependencies": { - "@radix-ui/react-dialog": "1.0.5", - "@radix-ui/react-primitive": "1.0.3" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/cmdk/node_modules/@radix-ui/primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", - "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==", - "dependencies": { - "@babel/runtime": "^7.13.10" - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-compose-refs": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", - "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-context": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz", - "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-dialog": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz", - "integrity": "sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.5", - "@radix-ui/react-focus-guards": "1.0.1", - "@radix-ui/react-focus-scope": "1.0.4", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-portal": "1.0.4", - "@radix-ui/react-presence": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-slot": "1.0.2", - "@radix-ui/react-use-controllable-state": "1.0.1", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.5.5" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz", - "integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-escape-keydown": "1.0.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-focus-guards": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", - "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-focus-scope": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz", - "integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-id": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", - "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-layout-effect": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-portal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz", - "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-presence": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz", - "integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-use-layout-effect": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-primitive": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", - "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-slot": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", - "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", - "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz", - "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", - "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", - "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/cmdk/node_modules/react-remove-scroll": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", - "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", - "dependencies": { - "react-remove-scroll-bar": "^2.3.3", - "react-style-singleton": "^2.2.1", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.0", - "use-sidecar": "^1.1.2" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/codemirror": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz", - "integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==", - "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/commands": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/lint": "^6.0.0", - "@codemirror/search": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/cookie": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", - "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", - "engines": { - "node": ">=18" - } - }, - "node_modules/copy-to-clipboard": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", - "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", - "dependencies": { - "toggle-selection": "^1.0.6" - } - }, - "node_modules/crelt": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", - "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true - }, - "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-node-es": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.188", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.188.tgz", - "integrity": "sha512-pfEx5CBFAocOKNrc+i5fSvhDaI1Vr9R9aT5uX1IzM3hhdL6k649wfuUcdUd9EZnmbE1xdfA51CwqQ61CO3Xl3g==", - "dev": true - }, - "node_modules/enhanced-resolve": { - "version": "5.18.2", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", - "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/esbuild": { - "version": "0.25.8", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", - "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.8", - "@esbuild/android-arm": "0.25.8", - "@esbuild/android-arm64": "0.25.8", - "@esbuild/android-x64": "0.25.8", - "@esbuild/darwin-arm64": "0.25.8", - "@esbuild/darwin-x64": "0.25.8", - "@esbuild/freebsd-arm64": "0.25.8", - "@esbuild/freebsd-x64": "0.25.8", - "@esbuild/linux-arm": "0.25.8", - "@esbuild/linux-arm64": "0.25.8", - "@esbuild/linux-ia32": "0.25.8", - "@esbuild/linux-loong64": "0.25.8", - "@esbuild/linux-mips64el": "0.25.8", - "@esbuild/linux-ppc64": "0.25.8", - "@esbuild/linux-riscv64": "0.25.8", - "@esbuild/linux-s390x": "0.25.8", - "@esbuild/linux-x64": "0.25.8", - "@esbuild/netbsd-arm64": "0.25.8", - "@esbuild/netbsd-x64": "0.25.8", - "@esbuild/openbsd-arm64": "0.25.8", - "@esbuild/openbsd-x64": "0.25.8", - "@esbuild/openharmony-arm64": "0.25.8", - "@esbuild/sunos-x64": "0.25.8", - "@esbuild/win32-arm64": "0.25.8", - "@esbuild/win32-ia32": "0.25.8", - "@esbuild/win32-x64": "0.25.8" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", - "dev": true, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fs-extra": { - "version": "11.3.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", - "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-nonce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", - "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", - "engines": { - "node": ">=6" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globals": { - "version": "15.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", - "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/jiti": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", - "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/lightningcss": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", - "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-darwin-arm64": "1.30.1", - "lightningcss-darwin-x64": "1.30.1", - "lightningcss-freebsd-x64": "1.30.1", - "lightningcss-linux-arm-gnueabihf": "1.30.1", - "lightningcss-linux-arm64-gnu": "1.30.1", - "lightningcss-linux-arm64-musl": "1.30.1", - "lightningcss-linux-x64-gnu": "1.30.1", - "lightningcss-linux-x64-musl": "1.30.1", - "lightningcss-win32-arm64-msvc": "1.30.1", - "lightningcss-win32-x64-msvc": "1.30.1" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", - "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", - "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", - "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", - "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", - "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", - "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", - "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", - "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", - "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", - "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/lucide-react": { - "version": "0.479.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.479.0.tgz", - "integrity": "sha512-aBhNnveRhorBOK7uA4gDjgaf+YlHMdMhQ/3cupk6exM10hWlEU+2QtWYOfhXhjAsmdb6LeKR+NZnow4UxRRiTQ==", - "peerDependencies": { - "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minizlib": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", - "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/next-themes": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", - "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==", - "peerDependencies": { - "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" - } - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-map": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", - "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" - }, - "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/react": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", - "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", - "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", - "dependencies": { - "scheduler": "^0.26.0" - }, - "peerDependencies": { - "react": "^19.1.0" - } - }, - "node_modules/react-refresh": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", - "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-remove-scroll": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz", - "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==", - "dependencies": { - "react-remove-scroll-bar": "^2.3.7", - "react-style-singleton": "^2.2.3", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.3", - "use-sidecar": "^1.1.3" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-remove-scroll-bar": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", - "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", - "dependencies": { - "react-style-singleton": "^2.2.2", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-router": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.7.0.tgz", - "integrity": "sha512-3FUYSwlvB/5wRJVTL/aavqHmfUKe0+Xm9MllkYgGo9eDwNdkvwlJGjpPxono1kCycLt6AnDTgjmXvK3/B4QGuw==", - "dependencies": { - "cookie": "^1.0.1", - "set-cookie-parser": "^2.6.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - } - } - }, - "node_modules/react-router-dom": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.7.0.tgz", - "integrity": "sha512-wwGS19VkNBkneVh9/YD0pK3IsjWxQUVMDD6drlG7eJpo1rXBtctBqDyBm/k+oKHRAm1x9XWT3JFC82QI9YOXXA==", - "dependencies": { - "react-router": "7.7.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" - } - }, - "node_modules/react-style-singleton": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", - "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", - "dependencies": { - "get-nonce": "^1.0.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/readdirp/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rollup": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", - "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", - "dev": true, - "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.45.1", - "@rollup/rollup-android-arm64": "4.45.1", - "@rollup/rollup-darwin-arm64": "4.45.1", - "@rollup/rollup-darwin-x64": "4.45.1", - "@rollup/rollup-freebsd-arm64": "4.45.1", - "@rollup/rollup-freebsd-x64": "4.45.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", - "@rollup/rollup-linux-arm-musleabihf": "4.45.1", - "@rollup/rollup-linux-arm64-gnu": "4.45.1", - "@rollup/rollup-linux-arm64-musl": "4.45.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-musl": "4.45.1", - "@rollup/rollup-linux-s390x-gnu": "4.45.1", - "@rollup/rollup-linux-x64-gnu": "4.45.1", - "@rollup/rollup-linux-x64-musl": "4.45.1", - "@rollup/rollup-win32-arm64-msvc": "4.45.1", - "@rollup/rollup-win32-ia32-msvc": "4.45.1", - "@rollup/rollup-win32-x64-msvc": "4.45.1", - "fsevents": "~2.3.2" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==" - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/set-cookie-parser": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", - "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==" - }, - "node_modules/sonner": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.6.tgz", - "integrity": "sha512-yHFhk8T/DK3YxjFQXIrcHT1rGEeTLliVzWbO0xN8GberVun2RiBnxAjXAYpZrqwEVHBG9asI/Li8TAAhN9m59Q==", - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", - "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/style-mod": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", - "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==" - }, - "node_modules/tailwind-merge": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz", - "integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/dcastil" - } - }, - "node_modules/tailwindcss": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", - "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==" - }, - "node_modules/tailwindcss-animate": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", - "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", - "peerDependencies": { - "tailwindcss": ">=3.0.0 || insiders" - } - }, - "node_modules/tapable": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", - "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/tar": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/tar/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "engines": { - "node": ">=18" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", - "dev": true, - "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toggle-selection": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", - "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" - }, - "node_modules/typescript": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", - "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true - }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/use-callback-ref": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", - "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-sidecar": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", - "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", - "dependencies": { - "detect-node-es": "^1.1.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/vite": { - "version": "6.3.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.6.tgz", - "integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "jiti": ">=1.21.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite-plugin-static-copy": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-2.3.2.tgz", - "integrity": "sha512-iwrrf+JupY4b9stBttRWzGHzZbeMjAHBhkrn67MNACXJVjEMRpCI10Q3AkxdBkl45IHaTfw/CNVevzQhP7yTwg==", - "license": "MIT", - "dependencies": { - "chokidar": "^3.5.3", - "fast-glob": "^3.2.11", - "fs-extra": "^11.1.0", - "p-map": "^7.0.3", - "picocolors": "^1.0.0" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "vite": "^5.0.0 || ^6.0.0" - } - }, - "node_modules/w3c-keyname": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", - "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/yaml": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", - "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/zustand": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.6.tgz", - "integrity": "sha512-ihAqNeUVhe0MAD+X8M5UzqyZ9k3FFZLBTtqo6JLPwV53cbRB/mJwBI0PxcIgqhBBHlEs8G45OTDTMq3gNcLq3A==", - "engines": { - "node": ">=12.20.0" - }, - "peerDependencies": { - "@types/react": ">=18.0.0", - "immer": ">=9.0.6", - "react": ">=18.0.0", - "use-sync-external-store": ">=1.2.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "immer": { - "optional": true - }, - "react": { - "optional": true - }, - "use-sync-external-store": { - "optional": true - } - } - } - } -} From 30ecc9616891aee759bfa9e7ccad42c7830726ea Mon Sep 17 00:00:00 2001 From: Kamil Dzieniszewski Date: Sun, 14 Dec 2025 06:48:57 +0100 Subject: [PATCH 29/91] chore: update Tolgee to latest version and fix SMTP config typo (#432) * chore: update Tolgee to latest version and fix SMTP config typo * Update docker-compose.yml * Update docker-compose.yml --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> --- blueprints/tolgee/docker-compose.yml | 4 +++- blueprints/tolgee/template.toml | 3 +-- meta.json | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/blueprints/tolgee/docker-compose.yml b/blueprints/tolgee/docker-compose.yml index f17b9b3c4..40141f642 100644 --- a/blueprints/tolgee/docker-compose.yml +++ b/blueprints/tolgee/docker-compose.yml @@ -1,8 +1,9 @@ version: "3" + services: app: - image: tolgee/tolgee:v3.80.4 + image: tolgee/tolgee:latest volumes: - ./data:/data - ./config.yaml:/config.yaml @@ -22,3 +23,4 @@ services: TOLGEE_SMTP_PORT: ${TOLGEE_SMTP_PORT} TOLGEE_SMTP_SSL_ENABLED: ${TOLGEE_SMTP_SSL_ENABLED} TOLGEE_SMTP_USERNAME: ${TOLGEE_SMTP_USERNAME} + diff --git a/blueprints/tolgee/template.toml b/blueprints/tolgee/template.toml index 02564343e..b33a3eaa3 100644 --- a/blueprints/tolgee/template.toml +++ b/blueprints/tolgee/template.toml @@ -11,7 +11,6 @@ port = 8_080 host = "${main_domain}" [config.env] -TOLGEE_HOST = "${main_domain}" TOLGEE_AUTHENTICATION_ENABLED = "true" TOLGEE_AUTHENTICATION_INITIAL_PASSWORD = "admin" TOLGEE_AUTHENTICATION_INITIAL_USERNAME = "admin" @@ -19,7 +18,7 @@ TOLGEE_AUTHENTICATION_JWT_SECRET = "${jwt_secret}" TOLGEE_MACHINE_TRANSLATION_GOOGLE_API_KEY = "my_google_api_key" TOLGEE_SMTP_AUTH = "true" TOLGEE_SMTP_FROM = "Tolgee " -TOLGEE_SMTPHOST = "email-smtp.regional-region.amazonaws.com" +TOLGEE_SMTP_HOST = "email-smtp.regional-region.amazonaws.com" TOLGEE_SMTP_PASSWORD = "omg/my/password" TOLGEE_SMTP_PORT = "465" TOLGEE_SMTP_SSL_ENABLED = "true" diff --git a/meta.json b/meta.json index a61b99c6e..6674bb609 100644 --- a/meta.json +++ b/meta.json @@ -5386,7 +5386,7 @@ { "id": "tolgee", "name": "Tolgee", - "version": "v3.80.4", + "version": "latest", "description": "Developer & translator friendly web-based localization platform", "logo": "tolgee.svg", "links": { From b7f7c9ffb8aaa32e471502201adda97e0a943034 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Sat, 13 Dec 2025 23:51:28 -0600 Subject: [PATCH 30/91] fix: improve Docker Compose validation workflow to handle subshell issues - Converted the handling of COMPOSE_FILES from a pipe to an array to ensure error propagation in the parent shell. - Updated the loop to iterate over the array for better reliability in the validation process. --- .github/workflows/validate-docker-compose.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/validate-docker-compose.yml b/.github/workflows/validate-docker-compose.yml index 945beeefe..792b37037 100644 --- a/.github/workflows/validate-docker-compose.yml +++ b/.github/workflows/validate-docker-compose.yml @@ -147,7 +147,11 @@ jobs: exit 0 fi - echo "$COMPOSE_FILES" | while read -r compose_file; do + # Convert to array to avoid subshell issues with pipe + # This ensures ERROR=1 inside the loop propagates to the parent shell + mapfile -t COMPOSE_ARRAY <<< "$COMPOSE_FILES" + + for compose_file in "${COMPOSE_ARRAY[@]}"; do if [ -z "$compose_file" ]; then continue fi From dcc456d18bf3671e0f5e2972305fe9f4a6191f9b Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Sun, 14 Dec 2025 00:00:54 -0600 Subject: [PATCH 31/91] refactor: enhance Docker Compose validation workflow to improve error handling - Replaced the pipe with an array to handle directory names, ensuring that errors within the loop propagate correctly to the parent shell. - Updated the loop structure for better reliability in processing the directories. --- .github/workflows/validate-docker-compose.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/validate-docker-compose.yml b/.github/workflows/validate-docker-compose.yml index 792b37037..ddd16aaba 100644 --- a/.github/workflows/validate-docker-compose.yml +++ b/.github/workflows/validate-docker-compose.yml @@ -192,7 +192,11 @@ jobs: exit 0 fi - echo "$DIRECTORIES" | while read -r template_dir; do + # Convert to array to avoid subshell issues with pipe + # This ensures ERROR=1 inside the loop propagates to the parent shell + mapfile -t DIRS_ARRAY <<< "$DIRECTORIES" + + for template_dir in "${DIRS_ARRAY[@]}"; do if [ -z "$template_dir" ]; then continue fi From c0ff3ca7882eb6d1fcd6991731d3b6147895c2c3 Mon Sep 17 00:00:00 2001 From: Nick Anderson Date: Sat, 13 Dec 2025 22:01:17 -0800 Subject: [PATCH 32/91] Feat: Add parseable (#460) * Add parseable * Update docker-compose.yml * Update docker-compose.yml * Update blueprints/parseable/template.toml --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Co-authored-by: Mauricio Siu --- blueprints/parseable/docker-compose.yml | 23 ++++++++ blueprints/parseable/logo.svg | 70 +++++++++++++++++++++++++ blueprints/parseable/template.toml | 16 ++++++ meta.json | 18 +++++++ 4 files changed, 127 insertions(+) create mode 100644 blueprints/parseable/docker-compose.yml create mode 100644 blueprints/parseable/logo.svg create mode 100644 blueprints/parseable/template.toml diff --git a/blueprints/parseable/docker-compose.yml b/blueprints/parseable/docker-compose.yml new file mode 100644 index 000000000..344ac6248 --- /dev/null +++ b/blueprints/parseable/docker-compose.yml @@ -0,0 +1,23 @@ +version: "3.8" +services: + parseable: + image: parseable/parseable:v1.6.0 + command: parseable local-store + restart: unless-stopped + volumes: + - parseable-staging:/parseable/staging + - parseable-data:/parseable/data + environment: + - P_ADDR=0.0.0.0:8000 + - P_USERNAME=${PARSEABLE_USERNAME} + - P_PASSWORD=${PARSEABLE_PASSWORD} + - P_STAGING_DIR=/parseable/staging + - P_FS_DIR=/parseable/data + ports: + - 8000 + - 8001 + - 8002 +volumes: + parseable-staging: {} + parseable-data: {} + diff --git a/blueprints/parseable/logo.svg b/blueprints/parseable/logo.svg new file mode 100644 index 000000000..4660d802c --- /dev/null +++ b/blueprints/parseable/logo.svg @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/blueprints/parseable/template.toml b/blueprints/parseable/template.toml new file mode 100644 index 000000000..f82eb77e4 --- /dev/null +++ b/blueprints/parseable/template.toml @@ -0,0 +1,16 @@ +[variables] +main_domain = "${domain}" +parseable_username = "${username}" +parseable_password = "${password:32}" + +[config] + +[[config.domains]] +serviceName = "parseable" +port = 8000 +host = "${main_domain}" + +[config.env] +PARSEABLE_USERNAME = "${parseable_username}" +PARSEABLE_PASSWORD = "${parseable_password}" + diff --git a/meta.json b/meta.json index 6674bb609..e743c2116 100644 --- a/meta.json +++ b/meta.json @@ -4341,6 +4341,24 @@ "open-source" ] }, + { + "id": "parseable", + "name": "Parseable", + "version": "v1.6.5", + "description": "Fast observability and log analytics platform on object storage", + "logo": "logo.svg", + "links": { + "github": "https://github.com/parseablehq/parseable", + "website": "https://www.parseable.com/", + "docs": "https://www.parseable.com/docs" + }, + "tags": [ + "observability", + "logging", + "analytics", + "monitoring" + ] + }, { "id": "passbolt", "name": "Passbolt", From f3c1060443c67144ed91a78f2d35c82c522e97fd Mon Sep 17 00:00:00 2001 From: lefolalan Date: Sun, 14 Dec 2025 07:13:13 +0100 Subject: [PATCH 33/91] feat: add ChirpStack LoRaWAN Network Server template (#486) * feat: add ChirpStack LoRaWAN Network Server template Add complete ChirpStack v4 template with: - Main ChirpStack server with web UI - UDP and Basics Station gateway bridges - REST API interface - PostgreSQL database with PostGIS extensions - Redis cache - Mosquitto MQTT broker Default configuration for EU868 region with secure random credentials. Supports all LoRaWAN frequency bands globally. * fix(chirpstack): use original configurations from chirpstack-docker repo Update template.toml to use exact configuration files from the chirpstack-docker repository instead of simplified versions: - Use original chirpstack.toml with all 15 enabled regions - Use original gateway bridge configuration with documentation links - Use complete Basics Station EU868 config with frequency plans - Keep original Mosquitto and PostgreSQL initialization scripts Template size increased from 131 to 219 lines (4.7KB) to include comprehensive default configurations that match the official setup. * feat: add all 38 region configuration files * fix(chirpstack): add volume mounts to expose config files to containers * fix(chirpstack): remove read-only flag * fix(chirpstack): correct file paths for configuration mounts in docker-compose and template files * fix: update volume paths to be on correct directory level * fix: configure template for dokploy-network with proper DNS resolution - Add dokploy-network configuration to docker-compose.yml - Replace environment variable placeholders with actual service hostnames - Change PostgreSQL DSN from $POSTGRESQL_HOST to postgres - Change Redis server from $REDIS_HOST to redis - Replace $MQTT_BROKER_HOST with mosquitto in all 39 region configurations These changes ensure Docker DNS resolution works correctly by: - Using dokploy-network (overlay) instead of bridge network - Using service names directly in TOML config files (TOML doesn't expand env vars) - Enabling proper service discovery between containers This resolves DNS resolution failures that caused ChirpStack to fail connecting to PostgreSQL and MQTT services during deployment. * fix: add missing network configurations for all services in docker-compose * feat: add internal services to config.domains for proper network configuration * Update docker-compose.yml * fix: enhance domain validation in template validator - Updated the TemplateValidator to ensure that if the 'host' field is provided, it must be a valid string. - Added comments to clarify that 'host' is optional for internal services. * refactor: remove redundant host validation in template validator - Removed the validation for the 'host' field in the TemplateValidator, as it is optional for internal services and does not require a type check if not provided. * refactor: remove internal service domain configurations from template - Eliminated the domain configurations for internal services (Postgres, Redis, Mosquitto) from the template.toml file, streamlining the configuration for better clarity and maintainability. --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Co-authored-by: Mauricio Siu --- blueprints/chirpstack/chirpstack.png | Bin 0 -> 12023 bytes blueprints/chirpstack/docker-compose.yml | 81 + blueprints/chirpstack/template.toml | 9815 ++++++++++++++++++++++ build-scripts/validate-template.ts | 3 - meta.json | 19 + 5 files changed, 9915 insertions(+), 3 deletions(-) create mode 100644 blueprints/chirpstack/chirpstack.png create mode 100644 blueprints/chirpstack/docker-compose.yml create mode 100644 blueprints/chirpstack/template.toml diff --git a/blueprints/chirpstack/chirpstack.png b/blueprints/chirpstack/chirpstack.png new file mode 100644 index 0000000000000000000000000000000000000000..b84e845f357ce883f98865cf8f008de5c25c67d9 GIT binary patch literal 12023 zcmYkCbyOQ&_qHK;TBLY!D6XNn)8g(>T+-qmv}mA(Qrv?}k>XG^Kq0uhwYUZ<5Uf~` zuh08F?{9tUUL*g^oPEx|XRnz(SB$Q<3L%gNh=GAYsHUo@kAZ=S`g?ANkNbBYIBg#G z_dwvOY6`)?;D7edh8a*Q=ZAs8grTPR!Z5(2hF1Fz!FHEqQQ%6Y&XB_t3fpT0T8qF6LWFJ&jRXL|Qb?Z9? z-#@&cv)nTFT?_t&W4(F4wFmxKM|s7)fA!@AHHLlfu_=dhudr#IzXRFX2(y5(q9_m7 zVUC&@l3=AZRzk)o(-<^r6$Uy9TZd{jbPM5s&gWJ3&9p@w(0=`Z|?v(W6k9 z9}(zeALm|sQwR{oew+mj6N^+v(V}FTkJ*IWYz~(2WMz&=pgNCk8=r=>n9$f{Gha|)cb`)%K8>yoI`bZ@>cW*@dWbkT^uts6jJK6-uOG0piudCrpuz^U)baZc%{ zY!eLD-xxo%A#%CsysU+eJt%1k0%e(wDH7iEoeD)m8Y$kPfoxm`M*MN8EjJ6c_#vI>)t=zro-j`_WMl8?22~4 zqZ{5E&@Hwc+-G3*TeS6m3%OH{-~$8Yx&dUleQo|5cPG>+Svs-U;i52TJq0`ynv&^od! zcBlsfXY=n;_VYh@CzC4Jq|dzz-F~=wkX*Gu^_NRpu2)_$VFbU~#G2X{zPP&x{?7#d zGl^~TD}>_-3Tw!*f?Cx2Or=0*n2JG=`-dg$Dg;pAJxMZjUNan9%nOFvM4ZTROGNY1oWsP{i@7LvC?&L_()#)fxQ!b~@F_{E)#W*ErCmBX)o1>9RY#BEJ%uL%&} zRy~2Jd~(2wxb_-LGF){!K9`1_0A>yT9-lXlyN*A8X2#<+L*Jt%=IAD>1k|zf1nAaJ$99 zgBIqWIdlO&pW|FwcmCH-;-)}ONM%_7A%y&E$cIs|@HS&D-41a-_9x_fa%KoN*%s-R#`I=@+d3v2RqE8>} zarnRV^ljZVj*A6h9-pcG%efxRrN^l5eWAra)O6n8)*gwHAR ze;!kY4sbDXbi4+xS3Z3;lU{BdCnqA8nJdkyn+$8%Od(BQQ}+qdmOlFlruCQ-``2Z{ zz+KMGD3B)a&^tqc74IjcM&Iu{Ba(?GP6W2sqOmJ4L~$KAyLD$!ONmZRGXGotX-?f@ zgbZ+eNQu>-TjUFL17+2tsA2XzwY}0CEN`13S6jH1HtGQ$oXL0lcir2V$Tx-fEyYg{ zRbD=N^Qxc|1Iq*-IRkaqt9mchA6e?`qm$^+PbvJr$KH_~rUYFXT!Hh*?uqbFz2!Zo z6r5fL2(j^5bfJGG!rH3TG~8Ouhm}d1WS0MRMN(Nf&ALPsWbFy*B>lRuLv}6g#Jr?{ z;6l+x*36oZF_!*xzUjdjN()_EWFJ)auZNk`FyCvZiP76x%;u)y$`}U}QMal3u<3hH zT{%HVbK4Vq9t(BTOf8u)t_$VgO}#{`cZ&XhQ7U4J`R!2?PWX=d=BMI%g=d1c1qgfs z)%eJ$;={D|UZuxRH1m9ovzcMi!91aUPG{wUXDxLf(RF#r6_My)5v zs<6t`|3qi|K@L?MTr$+8MkH(;;Yk)ntayv%KURDb4Yl|*`C%WoJDi{eTN|cXxGhwB@3W;w#6OTo{OESK7jpFHz6ep2CM1g@COThT4BxOC$B^rG zc{K&Fes=r@E50Mu*{G6ioU_#`e6tZNj4z2+Sz;N^6moL-JbqOy6#Y~F`Z^ld=Lakr z6$SccV+#D1=`Pj8u_2yR_NNl+Y)Hb1*}jq*2I&&$@3>Vil!3nw`NFGY=EAvoF&GNv zW2@Jpany!yv(&En*jSSz+QKC3OP~;;)rJU86wTCb@l}{iXxPa|zl#%mTXO=byH3P&x7m;y0nw-+LPlt=19JlAkq*GY0Bwco3l}SM?)pf_! zT@6uZ*U++zZOY#JH_oG7V_$d1(W?kbF|X>5oxz>1r16|?F@Lc?CV7L)3Hk56%SFVZ z*1C)LrugY(gU%*c2f1T6%Zal}2lXJhClf8rno#d&GexjxM}5Y|ELU z`^buh!g$({ptl?3ZAs(i2*b(hAl1R=Z?KLd2@ubWHaL>(`&C5Q%EE07xc#|Ri&?*Z z@|v8#>@#3*M@StgLip%CHSQ;g)yC@H^9gwvQ*_OfhJdbc|B%p^y|nPbv?Bo{o8-2`!=aK%|)JRg#%ZX zS+`vuh^-d$tCO8`sE{^Blz{nDdAat#@HS)gTxe)wLfv)EO z9h3S%4NLczQBzcHj89=w#OZwOhOt#wcL+I|2S3S2#hN5&4Rwy5JoyLbr`z89!M24S z#}GjCMxma~897N|TB@O785rZqNNM&qGRA8|z%%aQ&k9g@dyd}ji2l&qKRz~L&EfUy zkvKVHNY<&eOX4pFjrune=1det)xu#%5x8%FojTGfjk>n8iNN4Yc`vT!LgnxXRXLJZ z>Ueka%gSU^F~R;4Psr~&{b=^$it<;h@D@6Wl_oUa_wB!O_6*Y-U~Uf@2KMumzVmf? z={=Q|NskZrVLuEGwhab*fO6Wck4ObFxVzI~Gy2^R3UQ@|aF4O^9KWk73bZ!s+em2~ z#W2W=(hJJQmNqC`ElZc^vX@EEG(#CiC;HvMc0Ty0!(QZtD9_8tJKfE~@u0(Pz^V7O zyFmw&<}pte zt47cZSuqG({9O0_22>2E&9#lzv3>}@8V``Kj&Pg(Te}wPL-T9JK>fr7=jZ}HdBvWr zR$`w_1)ySE6)lsE@8b_HwuA>CslKm>IE=w$mm8@*0H^^m?=DUuP|ZQ0!>zwf&4mwd zG7MNYKk0NSfyVNueSZXs zVy5!<sEvP zC>c52t5NhJ=tS%rF%D5>pn!-H^eLbe9aplDA2$%w<7M0 z;2ND&b5T^28%+thnp_EV+T8z ztSW-hcyHDXLe_;P@fvfw8K_@@0kv$6@j;sY+Gkoqs4m|^7Gll&5`Qx<0@-H_oJJZ3 zP*;4#jIvb>O?@4X~ys9ioT>m(bXl6>HSNJ6&o)t(4hFON|QO ze5ub!%TxG~c9t-wNoLC{16Dh3I5wjo7roT+M*G)TC$r7(B*F5xOyxoN*bAT`V$)}e zxz91x3H31s*eE)}y`)HP{c_|nJ#`3fBjjliW;PZgnUTQ|=~kcD=ZVTWh_UXnTv{%WDw6Y2rIsP*naC}E(^l}uXC2MgG|C!2A2rIYA#NxVGIw6xawwOEw6Gl zC31MQgd|x*e=z)_KYmzsI$B=wy*4(RWX}1_8k5R!Duiz({LT9uXKqKaX0hgO9nbtP zxkZf7nJ#(=4;>YlQ9sNati8CNAAX@oRsIrxE3{q~`DX2cARtx7Mc}E4N%e3WzUx}s zqBQ#A`Gy%XoWdMb=5`8A|)TZUJKnup3%Ym3+^_F`i? zy?TO|Kbu%thq{<<9;4@{eqowPP~zqbj9+uJvP*tPS@g$Dg+Ys!7AyiwwX z-Z;44Wome-LGV%m)v9eUD~9Tc*A5wC!B^3}unStFTz- zo;P&x3B$_Tv{-RQ=gjG}lyR3K=}R~b)Z=hWZx_O|4 zJTyn(E9U^JyyEgRUUNEOCs!UC)s**MC#A`>`wF^&ggXVGLZn%Iar`_s? zMsrK^L`mdG$AQf_-axBVOWZGu-o@vrn)Q`XeB|LXHr}N)x33l_Zb;arieabR1?aaB zN>Jh+j2@Li+0>~u(|Esed!v)xmu)ITPZ%BXhSBibGm{;EF~;^qLoQQO)lsVz|81Am zCRNy|<*JY6t?b|$voSQ5qxJrsP3LXA@jm%r z$~+-|0DqW`oMYog83uQB5aZF)umESKurubK)*Aw76YqXXWH42*lyHAn#_;yQt-0OD z#aJV~40%Rb;rq>CHR!zI($6p0i;B08T*=<$s>k?F>MJ`tO`s<6asf|;orkOmsz;b4 zV)yYIu1w@C(>gLSJh?orP1sSBKf3<*ISND{LPhx_6I;vrD;yUMstSH@=2qei&x|{1 zd1txZu3g~h#&botbs$)zZ{D4~M0r6tE>gUfHED1xiQ#0L!r z(O|zWJoKSHL;sy7;XAeuB@x&Jv?AYe--R2l;fJBq5Dh){^jW#xTxU(!a!vAMBElX| z&Yp|joIAYunw(Q4XPdL2Vsfg4f5*|Rtp&+~cN+&?GAF_+HVPj6dFpO+s zTwBZE@n&Hy)Huc|d={V=LLIWYX(tQvq29eLgh68U8clUyMcCGqgDe>VUf6BEP}O(x z8>LJvvgd}vXbHH-`IN{>*cUT=TZKZ#?s6hWE;C_&Hxkf+6B(?B99f>&F^eHwlj0!`H0^7~Z!NKc6Srj^2opHN~ z#y}f~g|H{neIgAalFl<+XulJAy6?R$CvC{*N?`+BFWJ*{@j4pY1{KT2~h7CUVp$&qxo= zK2eljXsVqp;&oRxmb~6x=t@&N8$57n@>w9%2v^&8e(=8|vBKYT{Q3ABkk`_7A|I@G zI48z|9bfeRYg*+GD^kNej|%g1_1a`XaEB;^_Vs?=okD#HGTx(v+ndIq;U|N;j?{Pv zH8*mee1lq{sYZGXZ?QmXLII@AsG3xv^dnc%Gq784IyZCacMqF=aON-InyGi0dn0N=Ap(FpG>x6A zu6!z;UG7is)MK)BFX%O`^L|k3e5TNMxfr-3YhnlOVz-f}_Qz%zvlzTs zCU0hT8B7_^>3G(RH}HtOC{1eA&=3A?)Js<0Q>`??Vnj1KMw;S3L%6Xvvi+rFeeo?~O-{pT?u zl$K?WYPB@RqtTCO@g4e;;@m1~alW-wk(Z3uZJ~z;m?P0N*bd6rbpfH=AE%Lo{>-+M z0}oDUuIqFwiKTBvF;PTD%4$U zLGDdjD$aV3O2euasy$x-olm>=6Z{WC0*i_{AZ+;8wsP(@m`#4MCSC$27@Y*CShg=J-c0Y0Vy= z4S=Gdivn1}M=Vh2^E`$q;Mv=nqAhZyDmsN7nL>#t?q-2lb|*sTCWDnNSeA|YUvqi{ zYBQRLQqiN~bCYvL2-PRZy8{=^7WScFAU*cIw~B7WVkgt1ZVK%0Iuu9PABqRC&Xk4i z^O*~i`6=AQ-^0)W(XR)57V+5ZjF?h_mT>q!vqm%cYf6WS+>081e#3Zna6=MqVAg2J zv@Saokc|F$+blt&X)8??Q$~%*Rtmk6q~C1vinCy%f?B?1r($`d{I-y3p<>6g0e3)^ zFbWJR6=?1TeU^Q^mpC_y{>X-D!)5DSArbGk4?TJLkf9wsya6}+7!CsFmInjZ#%@JQ zW(;uMvGo0b*b7W$!aUKn4~(Z%ZE7w&*M-HlIJuAaN@Ll}pyj+o<(-S>S>_|;V$BEr z#>mzS@%GJzX7o%K%1ZJ4NTljrpwTW&I!liE?8vkbLu9}5;V4HDz0*ATT%J>{w3rDE z`&D!^T&jFAB+OxDcX*xN<#N~9GJS+*l8=Ar0J_rIZ{@~l@0Exg+0@W%GBB_hrPf34 z-59waREVy|TMPvs8&Ljvy99k970@psjQkp{wfM_!EPCp`La=5?VUDCRy%yVmU3;0O zpySM_A5OFKQHT)QXTg;3aREa}=OyOV#{{Zjji=ky+IkKtMy@q{Dcr^O`1s}a{GR^6 zj=6HSw|&wlrNb~%;-F$kec+?lT-%;7lG_CHap@eB~nGP@PAa2_%L5&dVdG^3$AhUINEH=M`c^2ThoPk(Xh!7A?EO za}J}?FlnViHSj9LL&2aPYsb%acrlz+;K1$xe8T*nCh)5QK=8SDs%8HP8qTmbiu$rDHmn1mN?km48#UOV*2|R+nq@=m{rV_VpRzU;g zUW@&MThnf%eO7#5bS$kP)=WBfW3T)_Bm8Y&fV^gYSFCi#$2MyZS~aO`J$(Qd1@3dNw0+q8_UY#4-TqDsmrG?_47Sl@`D=wT zUs6(A<>PGcN`09YUM24!RfygTZX9mBlMw$*r|Pe2=tGYzKXjSWFVwa!!v6;;>w8=5 z_QMDdBd1k`_D#e3XBN4ZxoqG2H4kx2U#>nmqMrgF^E&)Z+WWaM<8u;Q2MzhXEeH&| za*A?#6QH7%*o2rhIRYBa-;aH0%r^};P8`IyKkBg>R zu*tbE#hcIfzJ;!w#REjhZN3^*23!_iZp@CWv-!9z7o5g@zHID|jW}QkOQ60?^B7qr zx3Ik)U!cr-epJ&5*5V3LK>P{cuk`%ksz0N}-pgN@+V-Iw$7(_(P3pCdl4MG);GkiG zSHZ{4lvtbo)f@j^{*xz3T`rYmc>@rQ>lytWV<+wNTH(ix5y57ysv-p|q*bfSFb&Ke z1Q*VhZmE~G*kCI6x+12o?(RC&d#xPg-sH_Mo>w^;F~r zJH4>yvaQT(0@Dnc(cFAV66!q<1;COM;l?}iQTLrZxRMzs`_FidDM`a5*j1L{-A@sm z<<*}v80hpnP`VCLbp@MshY*oS_wva^pnJ7lcvm96PmI<-ui(=H1vd{rXe4g7d zf_Sg7hHBf@lEN`)bl0oblY?qsbA+_ctm8-d=mf>Za?`_6(YtWm`uuYjmS54JPc7%4 zuq@@1tA2D3wwdLO>4tYWj4n%9pq_koF8N!br^Im7-%H}a$goG_R`9Z0NN3C=;1Mes z$l3Tk<`c%d+2spy999Bqp`k01>5tx+Zsx2n#me|AJIgR0#iPA9xtvz%>AfQ!of%yq zv!6byu6Ea&aKMz(SUR!)+g{5=?D0lF*UK1O44pDSd^eo+)*sOBESC-J)*UT3Y-&Bv zJ`O6o2#p^bd8L1rvxBQ)S#*;&iVARA_Br(Wb-@!{_A^i5`vbE&*^Z1m=vN=!0OM*+ zC>f%&l#~$qLA-}-2-kANKqN{@WPPmxG@v#jpM~GWY5iTq@@zZt2M3(K zah$lE^SAq>QS-@GF8ApQj)_`@7%rP=qt47P+{>920IrKkD(oX6qV2E6-Cch$KBas# z!s5#0FmV`RNp_|cP=%{5c~>orPYG2=-Ze>_rE0edgA8J!p8Jt^d&MNsS_ch(;pzNl zowdXlg>5mY6+1x>B+KN2bKaMy7s4B~zRyzHM8&nvulEoJZ&va90LeVPXS6&5vtlY} z5n1uBF%_IySUIlR7f_N#dSwkJ^zD(MyC*ep{KTBwGv)co(PY2BOo_-xUsa<7^7&W1 z2@NE07%EE<%>=N__awA&4p|~EeeQF>v`S zjhK=x41#LYDjf}~RFzhY&5rp+==0>Bz4g2NIP|2hMTM#`JVZitu7}||PChdzEnMpO zCqe;8HB+g7tadVl&Y-d0yo^?&1!kUashvb;(-5p194JB4EJ^wVsf%)-iV=#viK=U` z;g?TvC94p^K9e=C2M1|*zIy2NVUP*x302oJ#rmkp=(ypL{r0+QI}v`{#AoT7MI%R; z8dIGH-uIV13dtT+k8NH=5#idOFlU3J51-}ts31X=ra5a4SM9HGtErH@DzuQxIw=ph zvg)qEvZ#OWItiy9jJ6Kz%yqE$t7RvT66=x*w{OX>Q*RuZH5(CZYQtKO<<-@LUjmf} zk@fL5{z|YnwqshYml+A98rhyXM->DxW_;At`gm3=&@ua0#~=Gn21#U|UwF zzZY?vy}8*T9q$lsvv1!>Z2HW+=}%cO7v`Wg_FY8(f$t_Xg-zAV z3jT~!BbSpe!0W$+P;0(^7i~Sdw-c#Y>FXUAkAS9J#(O5BDdG63ic0 z97ZIuo3@AEAoc$`U#7|jQXpEq;DX%nNJOMK^H7Ni?cG8em#L98iN8ouuR4ClN@=*c(e1Q076y;JS*XBUB%l5FVh@*S@Xqpz*-s1nZm_?0Qie&^sKE;3!x`$T79uQm-500O_bF1IY zGhEdJP8MIUvpi6=|4if=iPk6+Mydrim_r+AE|oy({w{s48B$4(660xT>H>80{T1Gcx!WX&? z1I>0RZPD*|a1okFk&7RRzKoG|>U&51!|8PFgk%FI>hUzdZF3FK4bp9(64scZmT@_~ zF#TXOK3lqn4d^@*oY= zRtQnXvBjpFCzWatV}FQwXXf(ut7Dw`dR2JAEUs2abt={C!PwSB|2phNI?_@J!aHpZ zT?rT7v@Zg5+T))Lpo99eY2HTrvYmE$GyeE#B@;7E#$=-t-Q;evzFG#Tn6`Xc`i+VA z5(}7CW(5*EVfIY|?QchJo(6Y$&%besdz$k#A7El+Hws?PKCPB~CSrDZMD4UmOvDR+ zHW)V~#%EJcPTVM!(7|!ghttSf?>xMZn%bi^$U9D)cMTUL9VJcG`*G;JXP=!yq)aX7 zuNUeb*dW1=cWTXdoy_+lHk7ucULR33;82`nChHaNCFR+#H!ZLF8!X#W zy{WG<{Pxe66f|Q56b0Xr-l|I@TA4^3wUQ!-;$A@t@wu6wEKu5P0d&)2MUn8%Ca# zjM^pS2~E(X8lDAGoku&(Q_fQV%q&QsUT}~`mh>*30kH;i;c_?GfX2FBjneGH+thW` z+!Aw7yAk^CG2t9@##lN#w%USzN{Y=S4KsUh=Z)}EG-~<*8 zF)wi`yVP9-(w0rNJO_+V7a1368XQM;3h44BVA%bh6!~>n{cC@gM>D0J1ni&NBzG2_ zmUYz2onhvZzWt0k>uVfk*VB!X5Vp#E-^&{uf~{(%ruwcnPi5Uok)KlwVj6vv^9Qs@ z05Y~fl5#Y9v!Rx|F6*YNAjTnwnw zWiK|E`Ie-)9}n@K|0C$pJ{B}wx4%?_B9)Y1E~jrA82PT?JE z&EVGMY^+)6Ca8So3U&YHVhA*4zyOX3lu~u_x@^ezzAF-@}N800BOu>WYsz)&1rn43Jo3%5$D0vr~$~FNEYiXz$7G|GDG@2++x@6)uXIjsX zcObJSn}&T6Q(-zv~q2$C@L|DLKd1@M-r zC~$^eP&JjXtE}3+_3(R8i@-gdQ3NdGFQNE56(C{{*|_sKgy`1PDMMqGG2;}b2dLvc z4otgtW=kJ8-+f4<`?O{*h6KMLtB}Q~`6!KVxSa^;SkPTB4+P!SK%38|WK=#^5FB?n z2Eix%<;WegFGCMj-#U?V>dV(h*59&GkFAP24jO1J-MUtgQy}jR`$Z?3MGx(cIx_;S zk4F>$#6L0x+{-QyBL;6pEZ+;{`JfEhM?n$B;a~4 z`5pl=)MA1UOXFRiJ_C{xGSjNrA=>g^s_>Bt=Jm`5!?(J7{aKQs+ghty3sk8yMwn;Q z0?L}LX0=9?J3RjBSyz7g+0R(~0E=Ar4bm1`Jet|&`7#Mc z&Qaz%pqlZ{Tm@XwJ|IUOrnC1l_5*!8p@X&N+g8R!qE@Vim-y8#)1386f5ZPeVB`dL zCZ{e8pXX-dm*Y8(29!n=09 zSs|0-aFF~nXiQdpapq^KUa4~{869>L!7a;&VHus$;+aqiO=h3CiLbTaLDVfRGw1On z#JZTLdQ<1idwve5f1qWMCRBu1KYl$P@Q=iFH@ypU?43nCmu}!K^)LQ`L-PUZl>1#g z8Ffe81IlCnWG9*2arWe_4l5*JvlPMn*5>$h*|KUx=P&+LsP~uGF%epSrrR>ee>G@` z>GP-%-S!{uR:@/?sslmode=. + # + # SSL mode options: + # * disable - Do not use TLS + # * prefer - Attempt to connect with TLS but allow sessions without + # * require - Require the use of TLS + dsn="postgres://chirpstack:${postgres_password}@postgres/chirpstack?sslmode=disable" + + # Max open connections. + # + # This sets the max. number of open connections that are allowed in the + # PostgreSQL connection pool. + max_open_connections=10 + + # Min idle connections. + # + # This sets the min. number of idle connections in the PostgreSQL connection + # pool (0 = equal to max_open_connections). + min_idle_connections=0 + + +# Redis configuration. +[redis] + + # Server address or addresses. + # + # Set multiple addresses when connecting to a cluster. + servers=[ + "redis://redis/", + ] + + # TLS enabled. + tls_enabled=false + + # Redis Cluster. + # + # Set this to true when the provided URLs are pointing to a Redis Cluster + # instance. + cluster=false + + +# Network related configuration. +[network] + + # Network identifier (NetID, 3 bytes) encoded as HEX (e.g. 010203). + net_id="000000" + + # Enabled regions. + # + # Multiple regions can be enabled simultaneously. Each region must match + # the 'name' parameter of the region configuration in '[[regions]]'. + enabled_regions=[ + "as923", + "as923_2", + "as923_3", + "as923_4", + "au915_0", + "cn470_10", + "cn779", + "eu433", + "eu868", + "in865", + "ism2400", + "kr920", + "ru864", + "us915_0", + "us915_1", + ] + + +# API interface configuration. +[api] + + # interface:port to bind the API interface to. + bind="0.0.0.0:8080" + + # Secret. + # + # This secret is used for generating login and API tokens, make sure this + # is never exposed. Changing this secret will invalidate all login and API + # tokens. The following command can be used to generate a random secret: + # openssl rand -base64 32 + secret="${api_secret}" + + +[integration] + enabled=["mqtt"] + + [integration.mqtt] + server="tcp://mosquitto:1883/" + json=true +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_as923_2.toml" +content = """ +# This file contains an example AS923_2 configuration. +[[regions]] + + # ID is an user-defined identifier for this region. + id="as923_2" + + # Description is a short description for this region. + description="AS923-2" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="AS923_2" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="as923_2" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=921400000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=921600000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=0 + + # RX2 frequency (Hz) + rx2_frequency=921400000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=3 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_as923_3.toml" +content = """ +# This file contains an example AS923_3 configuration. +[[regions]] + + # ID is an user-defined identifier for this region. + id="as923_3" + + # Description is a short description for this region. + description="AS923-3" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="AS923_3" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="as923_3" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=916600000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=916800000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=2 + + # RX2 frequency (Hz) + rx2_frequency=916600000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=3 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_as923_4.toml" +content = """ +# This file contains an example AS923_4 configuration. +[[regions]] + + # ID is an user-defined identifier for this region. + id="as923_4" + + # Description is a short description for this region. + description="AS923-4" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="AS923_4" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="as923_4" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=917300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=917500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=2 + + # RX2 frequency (Hz) + rx2_frequency=917300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=3 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_as923.toml" +content = """ +# This file contains an example AS923 configuration. +[[regions]] + + # ID is an user-defined identifier for this region. + id="as923" + + # Description is a short description for this region. + description="AS923" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="AS923" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="as923" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=923200000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=923400000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=2 + + # RX2 frequency (Hz) + rx2_frequency=923200000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=3 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_au915_0.toml" +content = """ +# This file contains an example AU915 example (channels 0-7 + 64). +[[regions]] + + # ID is an use-defined identifier for this region. + id="au915_0" + + # Description is a short description for this region. + description="AU915 (channels 0-7 + 64)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="AU915" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="au915_0" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=915200000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=915400000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=915600000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=915800000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=916000000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=916200000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=916400000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=916600000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=915900000 + bandwidth=500000 + modulation="LORA" + spreading_factors=[8] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=8 + + # RX2 frequency (Hz) + rx2_frequency=923300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[0, 1, 2, 3, 4, 5, 6, 7, 64] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=8 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_au915_1.toml" +content = """ +# This file contains an example AU915 example (channels 8-15 + 65). +[[regions]] + + # ID is an use-defined identifier for this region. + id="au915_1" + + # Description is a short description for this region. + description="AU915 (channels 8-15 + 65)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="AU915" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="au915_1" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=916800000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=917000000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=917200000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=917400000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=917600000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=917800000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=918000000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=918200000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=917500000 + bandwidth=500000 + modulation="LORA" + spreading_factors=[8] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=8 + + # RX2 frequency (Hz) + rx2_frequency=923300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[8, 9, 10, 11, 12, 13, 14, 15, 65] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=8 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_au915_2.toml" +content = """ +# This file contains an example AU915 example (channels 16-23 + 66). +[[regions]] + + # ID is an use-defined identifier for this region. + id="au915_2" + + # Description is a short description for this region. + description="AU915 (channels 16-23 + 65)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="AU915" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="au915_2" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=918400000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=918600000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=918800000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=919000000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=919200000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=919400000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=919600000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=919800000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=919100000 + bandwidth=500000 + modulation="LORA" + spreading_factors=[8] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=8 + + # RX2 frequency (Hz) + rx2_frequency=923300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[16, 17, 18, 19, 20, 21, 22, 23, 65] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=8 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_au915_3.toml" +content = """ +# This file contains an example AU915 example (channels 24-31 + 67). +[[regions]] + + # ID is an use-defined identifier for this region. + id="au915_3" + + # Description is a short description for this region. + description="AU915 (channels 24-31 + 67)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="AU915" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="au915_3" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=920000000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=920200000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=920400000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=920600000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=920800000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=921000000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=921200000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=921400000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=920700000 + bandwidth=500000 + modulation="LORA" + spreading_factors=[8] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=8 + + # RX2 frequency (Hz) + rx2_frequency=923300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[24, 25, 26, 27, 28, 29, 30, 31, 67] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=8 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_au915_4.toml" +content = """ +# This file contains an example AU915 example (channels 32-39 + 68). +[[regions]] + + # ID is an use-defined identifier for this region. + id="au915_4" + + # Description is a short description for this region. + description="AU915 (channels 32-39 + 68)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="AU915" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="au915_4" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=921600000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=921800000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=922000000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=922200000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=922400000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=922600000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=922800000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=923000000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=922300000 + bandwidth=500000 + modulation="LORA" + spreading_factors=[8] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=8 + + # RX2 frequency (Hz) + rx2_frequency=923300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[32, 33, 34, 35, 36, 37, 38, 39, 68] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=8 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_au915_5.toml" +content = """ +# This file contains an example AU915 example (channels 40-47 + 69). +[[regions]] + + # ID is an use-defined identifier for this region. + id="au915_5" + + # Description is a short description for this region. + description="AU915 (channels 40-47 + 69)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="AU915" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="au915_5" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=923200000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=923400000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=923600000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=923800000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=924000000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=924200000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=924400000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=924600000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=923900000 + bandwidth=500000 + modulation="LORA" + spreading_factors=[8] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=8 + + # RX2 frequency (Hz) + rx2_frequency=923300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[40, 41, 42, 43, 44, 45, 46, 47, 69] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=8 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_au915_6.toml" +content = """ +# This file contains an example AU915 example (channels 48-55 + 70). +[[regions]] + + # ID is an use-defined identifier for this region. + id="au915_6" + + # Description is a short description for this region. + description="AU915 (channels 48-55 + 70)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="AU915" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="au915_6" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=924800000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=925000000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=925200000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=925400000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=925600000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=925800000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=926000000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=926200000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=925500000 + bandwidth=500000 + modulation="LORA" + spreading_factors=[8] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=8 + + # RX2 frequency (Hz) + rx2_frequency=923300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[48, 49, 50, 51, 52, 53, 54, 55, 70] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=8 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_au915_7.toml" +content = """ +# This file contains an example AU915 example (channels 56-63 + 71). +[[regions]] + + # ID is an use-defined identifier for this region. + id="au915_7" + + # Description is a short description for this region. + description="AU915 (channels 56-63 + 71)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="AU915" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="au915_7" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=926400000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=926600000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=926800000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=927000000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=927200000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=927400000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=927600000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=927800000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=927100000 + bandwidth=500000 + modulation="LORA" + spreading_factors=[8] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=8 + + # RX2 frequency (Hz) + rx2_frequency=923300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[56, 57, 58, 59, 60, 61, 62, 63, 71] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=8 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_cn470_0.toml" +content = """ +# This file contains an example CN470 example (channels 0-7). +[[regions]] + + # ID is an use-defined identifier for this region. + id="cn470_0" + + # Description is a short description for this region. + description="CN470 (channels 0-7)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="CN470" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="cn470_0" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=470300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=470500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=470700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=470900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=471100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=471300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=471500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=471700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=0 + + # RX2 frequency (Hz) + rx2_frequency=505300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[0, 1, 2, 3, 4, 5, 6, 7] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=2 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_cn470_10.toml" +content = """ +# This file contains an example CN470 example (channels 80-87). +[[regions]] + + # ID is an use-defined identifier for this region. + id="cn470_10" + + # Description is a short description for this region. + description="CN470 (channels 80-87)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="CN470" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="cn470_10" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=486300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=486500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=486700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=486900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=487100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=487300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=487500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=487700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=0 + + # RX2 frequency (Hz) + rx2_frequency=505300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[80, 81, 82, 83, 84, 85, 86, 87] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=2 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_cn470_11.toml" +content = """ +# This file contains an example CN470 example (channels 88-95). +[[regions]] + + # ID is an use-defined identifier for this region. + id="cn470_11" + + # Description is a short description for this region. + description="CN470 (channels 88-95)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="CN470" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="cn470_11" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=487900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=488100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=488300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=488500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=488700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=488900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=489100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=489300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=0 + + # RX2 frequency (Hz) + rx2_frequency=505300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[88, 89, 90, 91, 92, 93, 94, 95] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=2 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_cn470_1.toml" +content = """ +# This file contains an example CN470 example (channels 8-15). +[[regions]] + + # ID is an use-defined identifier for this region. + id="cn470_1" + + # Description is a short description for this region. + description="CN470 (channels 8-15)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="CN470" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="cn470_1" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=471900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=472100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=472300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=472500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=472700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=472900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=473100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=473300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=0 + + # RX2 frequency (Hz) + rx2_frequency=505300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[8, 9, 10, 11, 12, 13, 14, 15] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=2 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_cn470_2.toml" +content = """ +# This file contains an example CN470 example (channels 16-23). +[[regions]] + + # ID is an use-defined identifier for this region. + id="cn470_2" + + # Description is a short description for this region. + description="CN470 (channels 16-23)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="CN470" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="cn470_2" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=473500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=473700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=473900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=474100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=474300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=474500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=474700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=474900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=0 + + # RX2 frequency (Hz) + rx2_frequency=505300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[16, 17, 18, 19, 20, 21, 22, 23] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=2 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_cn470_3.toml" +content = """ +# This file contains an example CN470 example (channels 24-31). +[[regions]] + + # ID is an use-defined identifier for this region. + id="cn470_3" + + # Description is a short description for this region. + description="CN470 (channels 24-31)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="CN470" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="cn470_3" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=475100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=475300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=475500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=475700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=475900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=476100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=476300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=476500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=0 + + # RX2 frequency (Hz) + rx2_frequency=505300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[24, 25, 26, 27, 28, 29, 30, 31] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=2 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_cn470_4.toml" +content = """ +# This file contains an example CN470 example (channels 32-39). +[[regions]] + + # ID is an use-defined identifier for this region. + id="cn470_4" + + # Description is a short description for this region. + description="CN470 (channels 32-39)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="CN470" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="cn470_4" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=476700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=476900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=477100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=477300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=477500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=477700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=477900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=478100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=0 + + # RX2 frequency (Hz) + rx2_frequency=505300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[32, 33, 34, 35, 36, 37, 38, 39] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=2 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_cn470_5.toml" +content = """ +# This file contains an example CN470 example (channels 40-47). +[[regions]] + + # ID is an use-defined identifier for this region. + id="cn470_5" + + # Description is a short description for this region. + description="CN470 (channels 40-47)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="CN470" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="cn470_5" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=478300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=478500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=478700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=478900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=479100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=479300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=479500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=479700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=0 + + # RX2 frequency (Hz) + rx2_frequency=505300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[40, 41, 42, 43, 44, 45, 46, 47] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=2 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_cn470_6.toml" +content = """ +# This file contains an example CN470 example (channels 48-55). +[[regions]] + + # ID is an use-defined identifier for this region. + id="cn470_6" + + # Description is a short description for this region. + description="CN470 (channels 48-55)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="CN470" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="cn470_6" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=479900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=480100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=480300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=480500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=480700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=480900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=481100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=481300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=0 + + # RX2 frequency (Hz) + rx2_frequency=505300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[48, 49, 50, 51, 52, 53, 54, 55] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=2 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_cn470_7.toml" +content = """ +# This file contains an example CN470 example (channels 56-63). +[[regions]] + + # ID is an use-defined identifier for this region. + id="cn470_7" + + # Description is a short description for this region. + description="CN470 (channels 56-63)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="CN470" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="cn470_7" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=481500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=481700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=481900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=482100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=482300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=482500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=482700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=482900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=0 + + # RX2 frequency (Hz) + rx2_frequency=505300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[56, 57, 58, 59, 60, 61, 62, 63] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=2 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_cn470_8.toml" +content = """ +# This file contains an example CN470 example (channels 64-71). +[[regions]] + + # ID is an use-defined identifier for this region. + id="cn470_8" + + # Description is a short description for this region. + description="CN470 (channels 64-71)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="CN470" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="cn470_8" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=483100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=483300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=483500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=483700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=483900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=484100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=484300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=484500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=0 + + # RX2 frequency (Hz) + rx2_frequency=505300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[64, 65, 66, 67, 68, 69, 70, 71] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=2 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_cn470_9.toml" +content = """ +# This file contains an example CN470 example (channels 72-79). +[[regions]] + + # ID is an use-defined identifier for this region. + id="cn470_9" + + # Description is a short description for this region. + description="CN470 (channels 72-79)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="CN470" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="cn470_9" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=484700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=484900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=485100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=485300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=485500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=485700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=485900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=486100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=0 + + # RX2 frequency (Hz) + rx2_frequency=505300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[72, 73, 74, 75, 76, 77, 78, 79] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=2 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_cn779.toml" +content = """ +# This file contains an example CN779 configuration. +[[regions]] + + # ID is an user-defined identifier for this region. + id="cn779" + + # Description is a short description for this region. + description="CN779" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="CN779" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="cn779" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=779500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=779700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=779900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=0 + + # RX2 frequency (Hz) + rx2_frequency=786000000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=3 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_eu433.toml" +content = """ +# This file contains an example EU433 configuration. +[[regions]] + + # ID is an user-defined identifier for this region. + id="eu433" + + # Description is a short description for this region. + description="EU443" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="EU433" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="eu433" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=433175000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=433375000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=433575000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=0 + + # RX2 frequency (Hz) + rx2_frequency=434665000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=3 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_eu868.toml" +content = """ +# This file contains an example EU868 configuration. +[[regions]] + + # ID is an user-defined identifier for this region. + id="eu868" + + # Description is a short description for this region. + description="EU868" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="EU868" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="eu868" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=868100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=868300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=868500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=867100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=867300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=867500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=867700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=867900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=868300000 + bandwidth=250000 + modulation="LORA" + spreading_factors=[7] + + [[regions.gateway.channels]] + frequency=868800000 + bandwidth=125000 + modulation="FSK" + datarate=50000 + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=0 + + # RX2 frequency (Hz) + rx2_frequency=869525000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=3 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 + + + # Below is the common set of extra channels. Please make sure that these + # channels are also supported by the gateways. + [[regions.network.extra_channels]] + frequency=867100000 + min_dr=0 + max_dr=5 + + [[regions.network.extra_channels]] + frequency=867300000 + min_dr=0 + max_dr=5 + + [[regions.network.extra_channels]] + frequency=867500000 + min_dr=0 + max_dr=5 + + [[regions.network.extra_channels]] + frequency=867700000 + min_dr=0 + max_dr=5 + + [[regions.network.extra_channels]] + frequency=867900000 + min_dr=0 + max_dr=5 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_in865.toml" +content = """ +# This file contains an example IN865 configuration. +[[regions]] + + # ID is an user-defined identifier for this region. + id="in865" + + # Description is a short description for this region. + description="IN865" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="IN865" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="in865" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=865062500 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=865402500 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=865985000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=2 + + # RX2 frequency (Hz) + rx2_frequency=866550000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=4 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_ism2400.toml" +content = """ +# This file contains an example ISM2400 configuration. +[[regions]] + + # ID is an user-defined identifier for this region. + id="ism2400" + + # Description is a short description for this region. + description="ISM2400" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="ISM2400" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="ism2400" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=2403000000 + bandwidth=812000 + modulation="LORA" + spreading_factors=[12] + + [[regions.gateway.channels]] + frequency=2479000000 + bandwidth=812000 + modulation="LORA" + spreading_factors=[12] + + [[regions.gateway.channels]] + frequency=2425000000 + bandwidth=812000 + modulation="LORA" + spreading_factors=[12] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=0 + + # RX2 frequency (Hz) + rx2_frequency=2423000000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=7 + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=0 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_kr920.toml" +content = """ +# This file contains an example KR920 configuration. +[[regions]] + + # ID is an user-defined identifier for this region. + id="kr920" + + # Description is a short description for this region. + description="KR920" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="KR920" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="kr920" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=922100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=922300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=922500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=0 + + # RX2 frequency (Hz) + rx2_frequency=921900000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=3 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_ru864.toml" +content = """ +# This file contains an example RU864 configuration. +[[regions]] + + # ID is an user-defined identifier for this region. + id="ru864" + + # Description is a short description for this region. + description="RU864" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="RU864" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="ru864" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=868900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + [[regions.gateway.channels]] + frequency=869100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10, 11, 12] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=0 + + # RX2 frequency (Hz) + rx2_frequency=869100000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=5 + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=3 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_us915_0.toml" +content = """ +# This file contains an example US915 example (channels 0-7 + 64). +[[regions]] + + # ID is an use-defined identifier for this region. + id="us915_0" + + # Description is a short description for this region. + description="US915 (channels 0-7 + 64)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="US915" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="us915_0" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=902300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=902500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=902700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=902900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=903100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=903300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=903500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=903700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=903000000 + bandwidth=500000 + modulation="LORA" + spreading_factors=[8] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=8 + + # RX2 frequency (Hz) + rx2_frequency=923300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=3 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[0, 1, 2, 3, 4, 5, 6, 7, 64] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=8 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_us915_1.toml" +content = """ +# This file contains an example US915 example (channels 8-15 + 65). +[[regions]] + + # ID is an use-defined identifier for this region. + id="us915_1" + + # Description is a short description for this region. + description="US915 (channels 8-15 + 65)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="US915" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="us915_1" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=903900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=904100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=904300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=904500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=904700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=904900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=905100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=905300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=904600000 + bandwidth=500000 + modulation="LORA" + spreading_factors=[8] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=8 + + # RX2 frequency (Hz) + rx2_frequency=923300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=3 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[8, 9, 10, 11, 12, 13, 14, 15, 65] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=8 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_us915_2.toml" +content = """ +# This file contains an example US915 example (channels 16-23 + 66). +[[regions]] + + # ID is an use-defined identifier for this region. + id="us915_2" + + # Description is a short description for this region. + description="US915 (channels 16-23 + 66)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="US915" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="us915_2" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=905500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=905700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=905900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=906100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=906300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=906500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=906700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=906900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=906200000 + bandwidth=500000 + modulation="LORA" + spreading_factors=[8] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=8 + + # RX2 frequency (Hz) + rx2_frequency=923300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=3 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[16, 17, 18, 19, 20, 21, 22, 23, 66] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=8 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_us915_3.toml" +content = """ +# This file contains an example US915 example (channels 24-31 + 67). +[[regions]] + + # ID is an use-defined identifier for this region. + id="us915_3" + + # Description is a short description for this region. + description="US915 (channels 24-31 + 67)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="US915" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="us915_3" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=907100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=907300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=907500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=907700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=907900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=908100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=908300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=908500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=907800000 + bandwidth=500000 + modulation="LORA" + spreading_factors=[8] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=8 + + # RX2 frequency (Hz) + rx2_frequency=923300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=3 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[24, 25, 26, 27, 28, 29, 30, 31, 67] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=8 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_us915_4.toml" +content = """ +# This file contains an example US915 example (channels 32-39 + 68). +[[regions]] + + # ID is an use-defined identifier for this region. + id="us915_4" + + # Description is a short description for this region. + description="US915 (channels 32-39 + 68)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="US915" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="us915_4" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=908700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=908900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=909100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=909300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=909500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=909700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=909900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=910100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=909400000 + bandwidth=500000 + modulation="LORA" + spreading_factors=[8] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=8 + + # RX2 frequency (Hz) + rx2_frequency=923300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=3 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[32, 33, 34, 35, 36, 37, 38, 39, 68] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=8 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_us915_5.toml" +content = """ +# This file contains an example US915 example (channels 40-47 + 69). +[[regions]] + + # ID is an use-defined identifier for this region. + id="us915_5" + + # Description is a short description for this region. + description="US915 (channels 40-47 + 69)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="US915" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="us915_5" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=910300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=910500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=910700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=910900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=911100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=911300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=911500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=911700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=911000000 + bandwidth=500000 + modulation="LORA" + spreading_factors=[8] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=8 + + # RX2 frequency (Hz) + rx2_frequency=923300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=3 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[40, 41, 42, 43, 44, 45, 46, 47, 69] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=8 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_us915_6.toml" +content = """ +# This file contains an example US915 example (channels 48-55 + 70). +[[regions]] + + # ID is an use-defined identifier for this region. + id="us915_6" + + # Description is a short description for this region. + description="US915 (channels 48-55 + 70)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="US915" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="us915_6" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=911900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=912100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=912300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=912500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=912700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=912900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=913100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=913300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=912600000 + bandwidth=500000 + modulation="LORA" + spreading_factors=[8] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=8 + + # RX2 frequency (Hz) + rx2_frequency=923300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=3 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[48, 49, 50, 51, 52, 53, 54, 55, 70] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=8 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack" +filePath = "/chirpstack/region_us915_7.toml" +content = """ +# This file contains an example US915 example (channels 56-63 + 71). +[[regions]] + + # ID is an use-defined identifier for this region. + id="us915_7" + + # Description is a short description for this region. + description="US915 (channels 56-63 + 71)" + + # Common-name refers to the common-name of this region as defined by + # the LoRa Alliance. + common_name="US915" + + + # Gateway configuration. + [regions.gateway] + + # Force gateways as private. + # + # If enabled, gateways can only be used by devices under the same tenant. + force_gws_private=false + + + # Gateway backend configuration. + [regions.gateway.backend] + + # The enabled backend type. + enabled="mqtt" + + # MQTT configuration. + [regions.gateway.backend.mqtt] + + # Topic prefix. + # + # The topic prefix can be used to define the region of the gateway. + # Note, there is no need to add a trailing '/' to the prefix. The trailing + # '/' is automatically added to the prefix if it is configured. + topic_prefix="us915_7" + + # MQTT server (e.g. scheme://host:port where scheme is tcp, ssl or ws) + server="tcp://mosquitto:1883" + + # Connect with the given username (optional) + username="" + + # Connect with the given password (optional) + password="" + + # Quality of service level + # + # 0: at most once + # 1: at least once + # 2: exactly once + # + # Note: an increase of this value will decrease the performance. + # For more information: https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels + qos=0 + + # Clean session + # + # Set the "clean session" flag in the connect message when this client + # connects to an MQTT broker. By setting this flag you are indicating + # that no messages saved by the broker for this client should be delivered. + clean_session=false + + # Client ID + # + # Set the client id to be used by this client when connecting to the MQTT + # broker. A client id must be no longer than 23 characters. If left blank, + # a random id will be generated by ChirpStack. + client_id="" + + # Keep alive interval. + # + # This defines the maximum time that that should pass without communication + # between the client and server. + keep_alive_interval="30s" + + # CA certificate file (optional) + # + # Use this when setting up a secure connection (when server uses ssl://...) + # but the certificate used by the server is not trusted by any CA certificate + # on the server (e.g. when self generated). + ca_cert="" + + # TLS certificate file (optional) + tls_cert="" + + # TLS key file (optional) + tls_key="" + + + # Gateway channel configuration. + # + # Note: this configuration is only used in case the gateway is using the + # ChirpStack Concentratord daemon. In any other case, this configuration + # is ignored. + [[regions.gateway.channels]] + frequency=913500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=913700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=913900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=914100000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=914300000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=914500000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=914700000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=914900000 + bandwidth=125000 + modulation="LORA" + spreading_factors=[7, 8, 9, 10] + + [[regions.gateway.channels]] + frequency=914200000 + bandwidth=500000 + modulation="LORA" + spreading_factors=[8] + + + # Region specific network configuration. + [regions.network] + + # Installation margin (dB) used by the ADR engine. + # + # A higher number means that the network-server will keep more margin, + # resulting in a lower data-rate but decreasing the chance that the + # device gets disconnected because it is unable to reach one of the + # surrounded gateways. + installation_margin=10 + + # RX window (Class-A). + # + # Set this to: + # 0: RX1 / RX2 + # 1: RX1 only + # 2: RX2 only + rx_window=0 + + # RX1 delay (1 - 15 seconds). + rx1_delay=1 + + # RX1 data-rate offset + rx1_dr_offset=0 + + # RX2 data-rate + rx2_dr=8 + + # RX2 frequency (Hz) + rx2_frequency=923300000 + + # Prefer RX2 on RX1 data-rate less than. + # + # Prefer RX2 over RX1 based on the RX1 data-rate. When the RX1 data-rate + # is smaller than the configured value, then the Network Server will + # first try to schedule the downlink for RX2, failing that (e.g. the gateway + # has already a payload scheduled at the RX2 timing) it will try RX1. + rx2_prefer_on_rx1_dr_lt=0 + + # Prefer RX2 on link budget. + # + # When the link-budget is better for RX2 than for RX1, the Network Server will first + # try to schedule the downlink in RX2, failing that it will try RX1. + rx2_prefer_on_link_budget=false + + # Downlink TX Power (dBm) + # + # When set to -1, the downlink TX Power from the configured band will + # be used. + # + # Please consult the LoRaWAN Regional Parameters and local regulations + # for valid and legal options. Note that the configured TX Power must be + # supported by your gateway(s). + downlink_tx_power=-1 + + # ADR is disabled. + adr_disabled=false + + # Minimum data-rate. + min_dr=0 + + # Maximum data-rate. + max_dr=3 + + # Enabled uplink channels. + # + # Use this when ony a sub-set of the by default enabled channels are being + # used. For example when only using the first 8 channels of the US band. + # Note: when left blank / empty array, all channels will be enabled. + enabled_uplink_channels=[56, 57, 58, 59, 60, 61, 62, 63, 71] + + + # Rejoin-request configuration (LoRaWAN 1.1) + [regions.network.rejoin_request] + + # Request devices to periodically send rejoin-requests. + enabled=false + + # The device must send a rejoin-request type 0 at least every 2^(max_count_n + 4) + # uplink messages. Valid values are 0 to 15. + max_count_n=0 + + # The device must send a rejoin-request type 0 at least every 2^(max_time_n + 10) + # seconds. Valid values are 0 to 15. + # + # 0 = roughly 17 minutes + # 15 = about 1 year + max_time_n=0 + + + # Class-B configuration. + [regions.network.class_b] + + # Ping-slot data-rate. + ping_slot_dr=8 + + # Ping-slot frequency (Hz) + # + # set this to 0 to use the default frequency plan for the configured region + # (which could be frequency hopping). + ping_slot_frequency=0 +""" + +[[config.mounts]] +serviceName = "chirpstack-gateway-bridge" +filePath = "/chirpstack-gateway-bridge/chirpstack-gateway-bridge.toml" +content = """ +# See https://www.chirpstack.io/gateway-bridge/install/config/ for a full +# configuration example and documentation. + +[integration.mqtt.auth.generic] +servers=["tcp://mosquitto:1883"] +username="" +password="" +""" + +[[config.mounts]] +serviceName = "chirpstack-gateway-bridge-basicstation" +filePath = "/chirpstack-gateway-bridge/chirpstack-gateway-bridge-basicstation-eu868.toml" +content = """ +# See https://www.chirpstack.io/gateway-bridge/install/config/ for a full +# configuration example and documentation. + +[integration.mqtt.auth.generic] +servers=["tcp://mosquitto:1883"] +username="" +password="" + +[integration.mqtt] +event_topic_template="eu868/gateway/{{ .GatewayID }}/event/{{ .EventType }}" +state_topic_template="eu868/gateway/{{ .GatewayID }}/state/{{ .StateType }}" +command_topic_template="eu868/gateway/{{ .GatewayID }}/command/#" + +[backend] +type="basic_station" + + [backend.basic_station] + bind=":3001" + tls_cert="" + tls_key="" + ca_cert="" + + region="EU868" + frequency_min=863000000 + frequency_max=870000000 + + + [[backend.basic_station.concentrators]] + + [backend.basic_station.concentrators.multi_sf] + frequencies=[ + 868100000, + 868300000, + 868500000, + 867100000, + 867300000, + 867500000, + 867700000, + 867900000, + ] + + [backend.basic_station.concentrators.lora_std] + frequency=868300000 + bandwidth=250000 + spreading_factor=7 + + [backend.basic_station.concentrators.fsk] + frequency=868800000 +""" + +[[config.mounts]] +serviceName = "mosquitto" +filePath = "/mosquitto/config/mosquitto.conf" +content = """ +listener 1883 +allow_anonymous true +""" + +[[config.mounts]] +serviceName = "postgres" +filePath = "/postgresql/initdb/001-chirpstack_extensions.sh" +content = """ +#!/bin/bash +set -e + +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname="$POSTGRES_DB" <<-EOSQL + create extension pg_trgm; + create extension hstore; +EOSQL +""" diff --git a/build-scripts/validate-template.ts b/build-scripts/validate-template.ts index aa0eb4d61..76d7e6534 100644 --- a/build-scripts/validate-template.ts +++ b/build-scripts/validate-template.ts @@ -237,9 +237,6 @@ class TemplateValidator { if (domain.port === undefined || domain.port === null) { this.error(`domain[${index}]: Missing required field 'port'`); } - if (!domain.host) { - this.error(`domain[${index}]: Missing required field 'host'`); - } // Validate serviceName matches docker-compose.yml services if (domain.serviceName && composeServices && composeServices.length > 0) { diff --git a/meta.json b/meta.json index e743c2116..a02dfc7e8 100644 --- a/meta.json +++ b/meta.json @@ -1156,6 +1156,25 @@ "Document Management" ] }, + { + "id": "chirpstack", + "name": "ChirpStack", + "version": "4", + "description": "Open-source LoRaWAN Network Server for IoT applications. Complete stack with gateway bridges, REST API, and web interface for managing LoRaWAN devices and gateways.", + "logo": "chirpstack.png", + "links": { + "github": "https://github.com/chirpstack/chirpstack", + "website": "https://www.chirpstack.io/", + "docs": "https://www.chirpstack.io/docs/" + }, + "tags": [ + "iot", + "lorawan", + "network-server", + "gateway", + "monitoring" + ] + }, { "id": "chromium", "name": "Chromium", From 9e4da9c806bfee7ff9d4e053917e0ead16ed550b Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 14 Dec 2025 00:21:47 -0600 Subject: [PATCH 34/91] Update section title from 'Suggestions' to 'Requirements' --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 81ac50e3e..b774c9728 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,7 @@ We have a few helpers that are very common when creating a template, these are: -## General Suggestions when creating a template +## General Requirements when creating a template - Don't use this way in your docker compose file: @@ -225,4 +225,4 @@ services: 6. Now you can click on the Deploy Button and wait for the deployment to finish, and try to access to the service, if everything is correct you should access to the service and see the template working. -use the command `node dedupe-and-sort-meta.js` to deduplicate and sort the meta.json file. \ No newline at end of file +use the command `node dedupe-and-sort-meta.js` to deduplicate and sort the meta.json file. From 640abda5d8efc30d2cb8259be1e3b88d26be9852 Mon Sep 17 00:00:00 2001 From: Harikrishnan Dhanasekaran Date: Sun, 14 Dec 2025 12:01:12 +0530 Subject: [PATCH 35/91] Feat : Add MCSManager template support (#521) (#522) * feat: Add MCSManager template support (#521) * Update docker-compose.yml --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> --- blueprints/mcsmanager/docker-compose.yml | 29 +++++++++++++++++++++++ blueprints/mcsmanager/mcsmanager.png | Bin 0 -> 1132 bytes blueprints/mcsmanager/template.toml | 12 ++++++++++ meta.json | 18 ++++++++++++++ 4 files changed, 59 insertions(+) create mode 100644 blueprints/mcsmanager/docker-compose.yml create mode 100644 blueprints/mcsmanager/mcsmanager.png create mode 100644 blueprints/mcsmanager/template.toml diff --git a/blueprints/mcsmanager/docker-compose.yml b/blueprints/mcsmanager/docker-compose.yml new file mode 100644 index 000000000..7994a3827 --- /dev/null +++ b/blueprints/mcsmanager/docker-compose.yml @@ -0,0 +1,29 @@ +services: + web: + image: githubyumao/mcsmanager-web:latest + restart: unless-stopped + ports: + - 23333 + volumes: + - /etc/localtime:/etc/localtime:ro + - mcsmanager-web-data:/opt/mcsmanager/web/data + - mcsmanager-web-logs:/opt/mcsmanager/web/logs + daemon: + image: githubyumao/mcsmanager-daemon:latest + restart: unless-stopped + ports: + - 24444 + environment: + - MCSM_DOCKER_WORKSPACE_PATH=/opt/mcsmanager/daemon/data/InstanceData + volumes: + - /etc/localtime:/etc/localtime:ro + - mcsmanager-daemon-data:/opt/mcsmanager/daemon/data + - mcsmanager-daemon-logs:/opt/mcsmanager/daemon/logs + - /var/run/docker.sock:/var/run/docker.sock + +volumes: + mcsmanager-web-data: + mcsmanager-web-logs: + mcsmanager-daemon-data: + mcsmanager-daemon-logs: + diff --git a/blueprints/mcsmanager/mcsmanager.png b/blueprints/mcsmanager/mcsmanager.png new file mode 100644 index 0000000000000000000000000000000000000000..a7f288576ccfda5370efe88e61488808f9087442 GIT binary patch literal 1132 zcmV-y1e5!TP)u(!H5Z~7|xfUmu>$KEFNF+{MD+M6WSPD z7pZ=x_W6<6j(vB#gY5I;az0<3NX;Fi$l2SS*_)Z)?#$e470Z_emI5q9pHJwRJaM2S3+_47$C?#A#vssrXFLfTN5Tg46 z0O^7xxX^W7k|fS~yPP1HF-g@_1ZbEaV{9n&{P{Cuasb)dS~Pq;{LRM3%o9o|y_e1pdT1hFjTCJt`mIx+0+dqfH^MD|o z$^?U#6{WNqUBMVX{^K#%#rozgAw(vfefCe{?$%w-dF;_6FOjAql>2S5H)uh&=B)-+XnaDQ7-O4mYHeLmk8UwsY$C8f+5 zOD2+U_{}gyev>kQ!moGrV&DBLRWs-`L$3e>Y5e^22@quzQ2=Br=#o9 zd^R7v5~P&&)i4$L0xIQ7DwURH8Dq?a&^ZSH&UxxZs$44Hh+MxgG4b@Tr$>iJ*-TEp zC=*IlRYVpe2%%)6?ej{d8n_(D=kkDHZvGvF(4*fU2`+@-Gc&>Yh57h?TnND=W?)Jg z6GBuf)xH|^tx(7qy}Pt5%agmY*hTpg!sxhh99g^>S&W1i7SgHANB6d-rlzXZYN1f{ z`=|d+CN)jR2yxDpqS9y_m&;|p-=9n*j~fjxM7>sTwbWcT-)uII4r?19Y=mY*wOak) z;NWKD#_Zd(x~>yK3NH&jpRd(w?d`_8=R3N(YK$V0MM_BiAou3gYr4)UC6u=P2LP(t znw*poLb|R42tr7yr1<>-3-Oi0IWopV7c0nxUa5Nsd<(AFM6NYVv_kk&co z0>E(9e*8&Kp_LW8tL++{GP!mG#)$BVJMqCbcsUCWCh#d!hy7PBnEsF0&slIV0iG*A zNT*9+>J-4JG;mJDpr1v&p_AtzbH5(4+%)&UTrIC30oibsWsM-XXNkGqI$3_1i){>@ z5M`1;COK0XHv+O(cEZ59$I=;CHvyZ?Pdvf^GIu|p&BRXCj5-JM+_m>$2hC$1dR>9> yZ2j8T7#Ppiuk}Rgu#o$y!$O|((0>5{0RR73$Zxb Date: Sun, 14 Dec 2025 12:07:18 +0530 Subject: [PATCH 36/91] feat: Add MediaCMS template (#524) --- blueprints/mediacms/docker-compose.yml | 134 +++++++++++++++++++++++++ blueprints/mediacms/mediacms.svg | 6 ++ blueprints/mediacms/template.toml | 17 ++++ meta.json | 19 ++++ 4 files changed, 176 insertions(+) create mode 100644 blueprints/mediacms/docker-compose.yml create mode 100644 blueprints/mediacms/mediacms.svg create mode 100644 blueprints/mediacms/template.toml diff --git a/blueprints/mediacms/docker-compose.yml b/blueprints/mediacms/docker-compose.yml new file mode 100644 index 000000000..cf58ba436 --- /dev/null +++ b/blueprints/mediacms/docker-compose.yml @@ -0,0 +1,134 @@ +version: "3" + +services: + migrations: + image: mediacms/mediacms:latest + volumes: + - mediacms_data:/home/mediacms.io/mediacms/media + - mediacms_static:/home/mediacms.io/mediacms/staticfiles + environment: + ENABLE_UWSGI: 'no' + ENABLE_NGINX: 'no' + ENABLE_CELERY_SHORT: 'no' + ENABLE_CELERY_LONG: 'no' + ENABLE_CELERY_BEAT: 'no' + ENABLE_MIGRATIONS: 'yes' + ADMIN_USER: 'admin' + ADMIN_EMAIL: 'admin@localhost' + ADMIN_PASSWORD: ${ADMIN_PASSWORD} + DATABASE_HOST: db + DATABASE_PORT: 5432 + DATABASE_USER: mediacms + DATABASE_PASSWORD: ${POSTGRES_PASSWORD} + DATABASE_NAME: mediacms + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + REDIS_HOST: redis + REDIS_PORT: 6379 + command: "./deploy/docker/prestart.sh" + restart: on-failure + depends_on: + redis: + condition: service_healthy + db: + condition: service_healthy + + web: + image: mediacms/mediacms:latest + deploy: + replicas: 1 + ports: + - 80 + volumes: + - mediacms_data:/home/mediacms.io/mediacms/media + - mediacms_static:/home/mediacms.io/mediacms/staticfiles + environment: + ENABLE_CELERY_BEAT: 'no' + ENABLE_CELERY_SHORT: 'no' + ENABLE_CELERY_LONG: 'no' + ENABLE_MIGRATIONS: 'no' + DATABASE_HOST: db + DATABASE_PORT: 5432 + DATABASE_USER: mediacms + DATABASE_PASSWORD: ${POSTGRES_PASSWORD} + DATABASE_NAME: mediacms + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + REDIS_HOST: redis + REDIS_PORT: 6379 + depends_on: + - migrations + + celery_beat: + image: mediacms/mediacms:latest + volumes: + - mediacms_data:/home/mediacms.io/mediacms/media + - mediacms_static:/home/mediacms.io/mediacms/staticfiles + environment: + ENABLE_UWSGI: 'no' + ENABLE_NGINX: 'no' + ENABLE_CELERY_SHORT: 'no' + ENABLE_CELERY_LONG: 'no' + ENABLE_MIGRATIONS: 'no' + DATABASE_HOST: db + DATABASE_PORT: 5432 + DATABASE_USER: mediacms + DATABASE_PASSWORD: ${POSTGRES_PASSWORD} + DATABASE_NAME: mediacms + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + REDIS_HOST: redis + REDIS_PORT: 6379 + depends_on: + - redis + + celery_worker: + image: mediacms/mediacms:latest + deploy: + replicas: 1 + volumes: + - mediacms_data:/home/mediacms.io/mediacms/media + - mediacms_static:/home/mediacms.io/mediacms/staticfiles + environment: + ENABLE_UWSGI: 'no' + ENABLE_NGINX: 'no' + ENABLE_CELERY_BEAT: 'no' + ENABLE_MIGRATIONS: 'no' + DATABASE_HOST: db + DATABASE_PORT: 5432 + DATABASE_USER: mediacms + DATABASE_PASSWORD: ${POSTGRES_PASSWORD} + DATABASE_NAME: mediacms + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + REDIS_HOST: redis + REDIS_PORT: 6379 + depends_on: + - migrations + + db: + image: postgres:17.2-alpine + volumes: + - postgres_data:/var/lib/postgresql/data/ + restart: always + environment: + POSTGRES_USER: mediacms + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: mediacms + TZ: Europe/London + healthcheck: + test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"] + interval: 10s + timeout: 5s + retries: 5 + + redis: + image: "redis:alpine" + restart: always + healthcheck: + test: ["CMD", "redis-cli","ping"] + interval: 10s + timeout: 5s + retries: 3 + +volumes: + postgres_data: + mediacms_data: + mediacms_static: + diff --git a/blueprints/mediacms/mediacms.svg b/blueprints/mediacms/mediacms.svg new file mode 100644 index 000000000..4da6d37c7 --- /dev/null +++ b/blueprints/mediacms/mediacms.svg @@ -0,0 +1,6 @@ + + + Media + CMS + + diff --git a/blueprints/mediacms/template.toml b/blueprints/mediacms/template.toml new file mode 100644 index 000000000..d696018c1 --- /dev/null +++ b/blueprints/mediacms/template.toml @@ -0,0 +1,17 @@ +[variables] +main_domain = "${domain}" +postgres_password = "${password}" +admin_password = "${password}" + +[config] +mounts = [] + +[[config.domains]] +serviceName = "web" +port = 80 +host = "${main_domain}" + +[config.env] +POSTGRES_PASSWORD = "${postgres_password}" +ADMIN_PASSWORD = "${admin_password}" + diff --git a/meta.json b/meta.json index 76a6bb73c..840d6609f 100644 --- a/meta.json +++ b/meta.json @@ -3643,6 +3643,25 @@ "meal-planning" ] }, + { + "id": "mediacms", + "name": "MediaCMS", + "version": "latest", + "description": "MediaCMS is an open-source video and media CMS. It is a modern, full-featured solution for managing and streaming media content.", + "logo": "mediacms.svg", + "links": { + "github": "https://github.com/mediacms/mediacms", + "website": "https://mediacms.io/", + "docs": "https://docs.mediacms.io/" + }, + "tags": [ + "media", + "video", + "cms", + "streaming", + "self-hosted" + ] + }, { "id": "meilisearch", "name": "Meilisearch", From eb41d839633fe4cedfb4d1e21daf6b8d8e24919f Mon Sep 17 00:00:00 2001 From: Harikrishnan Dhanasekaran Date: Sun, 14 Dec 2025 12:10:02 +0530 Subject: [PATCH 37/91] Feat : Add Quant-Ux template -#173 (#525) * Feat : Add Quant-Ux template -#173 * Remove extra newline in docker-compose.yml * Update blueprints/quant-ux/docker-compose.yml * Update blueprints/quant-ux/docker-compose.yml * Update blueprints/quant-ux/docker-compose.yml * Update blueprints/quant-ux/docker-compose.yml --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> --- blueprints/quant-ux/docker-compose.yml | 69 ++++++++++++++++++++++++++ blueprints/quant-ux/logo.svg | 2 + blueprints/quant-ux/template.toml | 50 +++++++++++++++++++ meta.json | 19 +++++++ 4 files changed, 140 insertions(+) create mode 100644 blueprints/quant-ux/docker-compose.yml create mode 100644 blueprints/quant-ux/logo.svg create mode 100644 blueprints/quant-ux/template.toml diff --git a/blueprints/quant-ux/docker-compose.yml b/blueprints/quant-ux/docker-compose.yml new file mode 100644 index 000000000..e96a9d6ac --- /dev/null +++ b/blueprints/quant-ux/docker-compose.yml @@ -0,0 +1,69 @@ +version: '3' + +services: + mongo: + restart: always + image: mongo + volumes: + - mongo_data:/data/db + qux-fe: + restart: always + image: klausenschaefersinho/quant-ux + environment: + - QUX_PROXY_URL=http://quant-ux-backend:8080 + - QUX_AUTH=${QUX_AUTH} + - QUX_KEYCLOAK_REALM=${QUX_KEYCLOAK_REALM} + - QUX_KEYCLOAK_CLIENT=${QUX_KEYCLOAK_CLIENT} + - QUX_KEYCLOAK_URL=${QUX_KEYCLOAK_URL} + - QUX_WS_URL=${QUX_WS_URL} + links: + - mongo + - qux-be + ports: + - 8082 + depends_on: + - qux-be + + qux-be: + restart: always + image: klausenschaefersinho/quant-ux-backend + volumes: + - quant_ux_data:/app-data + environment: + - QUX_HTTP_HOST=${QUX_HTTP_HOST} + - QUX_HTTP_PORT=8080 + - QUX_MONGO_DB_NAME=${QUX_MONGO_DB_NAME} + - QUX_MONGO_TABLE_PREFIX=${QUX_MONGO_TABLE_PREFIX} + - QUX_MONGO_CONNECTION_STRING=mongodb://quant-ux-mongo:27017 + - QUX_MAIL_USER=${QUX_MAIL_USER} + - QUX_MAIL_PASSWORD=${QUX_MAIL_PASSWORD} + - QUX_MAIL_HOST=${QUX_MAIL_HOST} + - QUX_JWT_PASSWORD=${QUX_JWT_PASSWORD} + - QUX_IMAGE_FOLDER_USER=/app-data/qux-images + - QUX_IMAGE_FOLDER_APPS=/app-data/qux-image-apps + - TZ=${TZ} + - QUX_AUTH_SERVICE=${QUX_AUTH_SERVICE} + - QUX_KEYCLOAK_SERVER=${QUX_KEYCLOAK_SERVER} + - QUX_KEYCLOAK_REALM=${QUX_KEYCLOAK_REALM} + - QUX_USER_ALLOW_SIGNUP=${QUX_USER_ALLOW_SIGNUP} + - QUX_USER_ALLOWED_DOMAINS=${QUX_USER_ALLOWED_DOMAINS} + depends_on: + - mongo + + qux-ws: + restart: always + image: klausenschaefersinho/quant-ux-websocket + environment: + - QUX_SERVER=http://quant-ux-backend:8080/ + - QUX_SERVER_PORT=8086 + ports: + - 8086 + links: + - qux-be + depends_on: + - qux-be + +volumes: + mongo_data: + quant_ux_data: + diff --git a/blueprints/quant-ux/logo.svg b/blueprints/quant-ux/logo.svg new file mode 100644 index 000000000..1c71b3c8e --- /dev/null +++ b/blueprints/quant-ux/logo.svg @@ -0,0 +1,2 @@ + + diff --git a/blueprints/quant-ux/template.toml b/blueprints/quant-ux/template.toml new file mode 100644 index 000000000..912bc5c7e --- /dev/null +++ b/blueprints/quant-ux/template.toml @@ -0,0 +1,50 @@ +[variables] +main_domain = "${domain}" +ws_domain = "${domain}" +qux_auth = "qux" +qux_jwt_password = "${password:64}" +qux_mongo_db_name = "quantux" +qux_mongo_table_prefix = "quantux" +qux_mail_user = "${email}" +qux_mail_password = "${password:32}" +qux_mail_host = "mail.example.com" +qux_timezone = "America/Chicago" +qux_auth_service = "qux" +qux_user_allow_signup = "true" +qux_user_allowed_domains = "*" +qux_keycloak_realm = "" +qux_keycloak_client = "" +qux_keycloak_url = "" +qux_keycloak_server = "" + +[config] +env = [ + "QUX_HTTP_HOST=https://${main_domain}", + "QUX_AUTH=${qux_auth}", + "QUX_JWT_PASSWORD=${qux_jwt_password}", + "QUX_MONGO_DB_NAME=${qux_mongo_db_name}", + "QUX_MONGO_TABLE_PREFIX=${qux_mongo_table_prefix}", + "QUX_MAIL_USER=${qux_mail_user}", + "QUX_MAIL_PASSWORD=${qux_mail_password}", + "QUX_MAIL_HOST=${qux_mail_host}", + "TZ=${qux_timezone}", + "QUX_AUTH_SERVICE=${qux_auth_service}", + "QUX_KEYCLOAK_SERVER=${qux_keycloak_server}", + "QUX_KEYCLOAK_REALM=${qux_keycloak_realm}", + "QUX_KEYCLOAK_CLIENT=${qux_keycloak_client}", + "QUX_KEYCLOAK_URL=${qux_keycloak_url}", + "QUX_USER_ALLOW_SIGNUP=${qux_user_allow_signup}", + "QUX_USER_ALLOWED_DOMAINS=${qux_user_allowed_domains}", + "QUX_WS_URL=wss://${ws_domain}" +] + +[[config.domains]] +serviceName = "qux-fe" +port = 8082 +host = "${main_domain}" + +[[config.domains]] +serviceName = "qux-ws" +port = 8086 +host = "${ws_domain}" + diff --git a/meta.json b/meta.json index 840d6609f..4f2d39609 100644 --- a/meta.json +++ b/meta.json @@ -4882,6 +4882,25 @@ "search" ] }, + { + "id": "quant-ux", + "name": "Quant-UX", + "version": "latest", + "description": "Quant-UX is an open-source UX design and prototyping tool that allows you to create interactive prototypes, conduct user research, and analyze user behavior.", + "logo": "logo.svg", + "links": { + "github": "https://github.com/KlausSchaefers/quant-ux", + "website": "https://www.quant-ux.com/", + "docs": "https://www.quant-ux.com/" + }, + "tags": [ + "design", + "ux", + "prototyping", + "user-research", + "analytics" + ] + }, { "id": "rabbitmq", "name": "RabbitMQ", From 6e1aece2e7153167c713741005d9b855d9ec939f Mon Sep 17 00:00:00 2001 From: Chris <31969757+ChrisvanChip@users.noreply.github.com> Date: Sun, 14 Dec 2025 07:40:29 +0100 Subject: [PATCH 38/91] fix(rustdesk): use explicit ports, use port 21118 on hbbs instead of hbbr (#526) * fix: use explicit ports, use port 21118 on hbbs instead of hbbr * fix: whitespace character in rustdesk --- blueprints/rustdesk/docker-compose.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/blueprints/rustdesk/docker-compose.yml b/blueprints/rustdesk/docker-compose.yml index b9c38a57e..a1985ce5f 100644 --- a/blueprints/rustdesk/docker-compose.yml +++ b/blueprints/rustdesk/docker-compose.yml @@ -6,9 +6,10 @@ services: volumes: - rustdesk-data:/root ports: - - 21115 - - 21116 - - 21116/udp + - "21115:21115" + - "21116:21116" + - "21116:21116/udp" + - "21118:21118" depends_on: - hbbr @@ -19,9 +20,8 @@ services: volumes: - rustdesk-data:/root ports: - - 21117 - - 21118 - - 21119 + - "21117:21117" + - "21119:21119" volumes: - rustdesk-data: {} \ No newline at end of file + rustdesk-data: {} From a00b179ddf2f9330719e55a27b9ef4f24de474ac Mon Sep 17 00:00:00 2001 From: kipavy <88386090+kipavy@users.noreply.github.com> Date: Sun, 14 Dec 2025 07:42:48 +0100 Subject: [PATCH 39/91] feat: Add anytype template (#527) * add anytype template * sort * Update name field for Anytype in meta.json * Update meta.json * Update docker-compose.yml * Update blueprints/anytype/docker-compose.yml --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> --- blueprints/anytype/docker-compose.yml | 19 +++++++++++++++++++ blueprints/anytype/logo.png | Bin 0 -> 51321 bytes blueprints/anytype/template.toml | 4 ++++ meta.json | 17 +++++++++++++++++ 4 files changed, 40 insertions(+) create mode 100644 blueprints/anytype/docker-compose.yml create mode 100644 blueprints/anytype/logo.png create mode 100644 blueprints/anytype/template.toml diff --git a/blueprints/anytype/docker-compose.yml b/blueprints/anytype/docker-compose.yml new file mode 100644 index 000000000..5d516cb69 --- /dev/null +++ b/blueprints/anytype/docker-compose.yml @@ -0,0 +1,19 @@ +# Example: Any-Sync-Bundle with embedded MongoDB and Redis (all-in-one image) +# +# Usage: +# docker compose -f compose.aio.yml up -d +# +# The bundle image already contains MongoDB and Redis. Only the bundle service is required + +services: + any-sync-bundle: + image: ghcr.io/grishy/any-sync-bundle:latest + restart: unless-stopped + ports: + - "33010:33010" + - "33020:33020/udp" + volumes: + - ./data:/data + environment: + # Advertise addresses clients should use. Replace with your server hostname/IP. + ANY_SYNC_BUNDLE_INIT_EXTERNAL_ADDRS: "192.168.100.9" diff --git a/blueprints/anytype/logo.png b/blueprints/anytype/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..903e9aeb1d47985cfc281716145968b29563604f GIT binary patch literal 51321 zcmd43g;$kP`z`v?AhiW4sZC0Emm8!(q(c#;6$D8E=>`R)yOENV2I-JaK|s2@OQr7G z-*lhBkK-uhfttaL)=X`dUx|#z1y@&T82*OuVl+}bFlqlq1Y)tSS-&UbP z@CCv4k+w8 z0#$u^=9axOHzoL!Rhju+hrL=auM!T6Za8cr#!Hk8b-@HA8E<;}(HyNw2w1P+boHs_s$ zj|(?5Jk*}13Jpnoj!o$Cxw~;apEmoPeD(O|EVh4SMEKGH+t}x(reWlFlY7;_;~rWa z4UOm&K8ueJ#ow1SuEnSfj$DY6ADZn<6m8rMri%+-h~E;{73-FMl&tl>`8VzLD}nRH z;J)yprmAKK!LXRk?am$?NFYcB<^cNzT16RWDXGYBLLOdgzPraIsI&j53$rin@ zO8>qvN^KT(+E7K4f7g0b`{DNQCjKb3(;%)VMVs;a>0F*8ZE)p^r~jwO|}9Ik({JQTVAi%_bXR z!QH&;Vsh+Z0HKt+%~V-L`WIYM4xYNlch^<_wsJpfGZJZJdR-)oHbB&A*ZdtEJy6OFy!+*+BKWQ+E>zoap)vrBh?Nd)sR%VLDP8MEt_uq?xa@hS5cye?m^+Pg{V zrAtKSR@{w9k@*@lxjF1L-<=F_V;25Vy|_94*A$Eq;_r`onhbUI#Xd^ClDOT>^!eeU zFQQ15iV>5ilGwjFjk}1|lhV9|!H|JFUE$+>bFL@oaq;$)e~}V#1R|yw9CZ=6eT}C{ zJam{wb80C9eN7if%bk<`@$C38hx_NhUc5^l>5#)c#DP70?A;`)_u*{IWSC^3r>G#~ ztKIkgTE@&7Q&W$28m}`ACrx|$MGy*KeeP~I*gg^CY!W6losFyfSY9o0m=IA?b5pZZ zZ(jLrx*e%7B5b3M0Tt(kt^!&S?IH~naavc)6 zE?1y&58M=UUG%DR8j-vdxt=RGe!OXX_b-v(ycf1PW0mP**Yel0;7JIU8JC~`K zuA+K|4na6-@vvy7yo+nDDQtmG!H{1NshHUOGTkqmWzLB*MUUcSuma=Z~i4^r2kXrLF z;YLN@qrA|F4;n6a91wQOzuyOBze#p8DLs?&KU(U<%x9+yF12q9oONbZz*ie`yg zsQ=#UpkebwY{PW(&+2DA@EC}2)=y!7`;~jbn@uvuKX(#|(AEE5?GGcgCZ$=e5-nrjojDcha-d+UQ^vvm~@8ow=JIt-`!re zMpTL1U7x0_a5Z!_95n8R2Vg+ArJd|AFVU#QoUzlq|84u7 z4vUXmd-k&ypx>S(wxX+vEC;rMQ=}2uul|W@mEmr=1Zo5Rk*mb8)1Rw@`R`b2Qh(Rl zlpdv<+qpNXByxS5%vJb|=!v8r+Rb$ML9y|zYD8+biP^`-csJ)0*S+hTLm%OhRhP)e zw{EB6-W!rsUT0%|cJqzNt*dt@&#&i-v>sK}b7$r{wtgN?7e7sM0`Voj7mLf?H0n#R zgbKZM8WQ{_iPf9tanN9ShswMseZg~;c;mj2Y?X55OF@QxozdL4dn~qR!}T7L8OGoW zeI*F>gj4VjHi8ovl$#nJ7N6c3&3-a8;ybxBSl5)9EejxKhe*RZ|e=pn=evMyNI8W!z$FM)gc&H%OBGa;?s9NSE&x6X-jGK zb1M(*6GVJ#ZD)U^p%cV!(zr}$_+9MJV*Q9JOaY5fbS>)DKmum~7PT{M1i#jLlFe<&4=beybOB3mpRMeWl*Qv|a1nO8ryD~< zBiE7LcVat*x&BzRo)zwP-{0>K-ffL$2e?Zb_1sdA)Rhl7{qaf_vh@dbUGQ<08}$P2 zXQ#~8)mp6rwQp1FJ|`^=>w?d#U(;opaeLA3;Tt91blIFVPkULsbEm#AMc&_?X=^2S z19rs~Igj;XOY=;tVHA}^*ZmDEnoXH3tC0*8u3!=2dN^wMpN40dQp1n${ysyKg>W|q z2Dn@IU?=_!_R?6r_73m&Yo>m5xL?jWeHPpP5J6$amq|IF2T}@JKd_tps*|)S_-34OE=xM#2nCDk zmF|3_i@Q-FsIPQJ0h`CdHP(|WhccYctFe!%i~?GVja#LB$W|nepGQy%Z-{OPKCiL1 zARSr#d+l@Oxj^>$m?lN|ogK|)jluQ1TiWtX?@0UYk+Dh(l~ZpwpXZIUG+a0YQ%6sG)#{5o zxY^6_Iq<%?q{oGGqs#8b^*Xl?FT&}3QcpYp6W>ujlIjiDFhnHt>PavjFWt+JiY#o9 z1a#jwq!;?()ZKMa@6avdNXZbw>$s+<#o5sapn=H)-;glkCw(TwzG##;B~`{&{;ltpW0lSkqe2d+6pxCzxkF_H@P`ly~Ft|A4mL_(DGh8h`iIqT0B)dojqa zMHQs9Rw)X~!1K*=pBwh@j!|bsK*Yc#akyVG>olrf0RBS(9=%}0F$YI|_1=MjsckGPkm_I^w@nwlkuzM&iN zUs5w|CJHt3bW5LaNPV8^sj8|n(D{?Rxw*0N!zf^?1Vk*!!Kr@4L&3q*{nM|yrPkg( zUbh*G>uYOA&vo_|Psx3H7LPK(Pc+^YDBIcD?FqU`jno{bZ}x8<-Cdg8g?OZD6zg&( zG-Srcnq9cq*_9di5`H$YM-3EtJokwbjSs{!X82$?i7K{k*I$ePKk9Zzh-~{I{Fw#|0t8|Y1l z(?Nv}{zVk&PZT@N{7#xGL6*)PtIX|p^_wSB9ljWp$uQJxXn;{dPk*C-u(@NEk@r!o z=Ok>O!WouR(IBI}4uet!O9-D2IL5;mQ$aetB% zm;Oq8zItzAZl3faV_@;fFvILF_$tZkDa~rjP3DE`#V@HJPT-H3j}dFP2Y zf%iqryMx4Y(Y@X2%g=qA({(L`w^##JRzp&?brJ^$wz%e)*BtOlNN|{%D-EI=@V5R{ z@`l_EgmS&$U=Xgra5q4YBIvuDR4$z+8>XL6|Ipt332_1llmbCF_UunG&lv!8cGJD> zShR0*2-hUKyZQyb3VgUcuq~6QXQOL%Sd9>7>nAm#WZW%O&Qab@GK<84)iJs6`UmwX zz{G>4>jmaD-JOEEEA;}?s1B6k;!{%xaELEuHx=x2mE+k;-X7(EFQOC_6ryvwMVd^) z3=9my{QOmo->vJ5E#>9qhpMZqr)@uc_+VsdX=wz$L37E^$7k)3nvkITDLx~^&aRoA zfgx9jT~e~S_KM28jD#R$$n1d6XSXHevVn=liT3kNdZ z!VA}hJBCcN&)&2bhx^{&JbLz6M_TwixFjX#4_Y5)=tzPW{Gh9=%QbLdaUnPbPfkwW zb5ILhiOsg2B=>da(#BhwJ`Fhy`^1@OV(=;~Xcc-etU&pY0Ua|7rWTxMe_zowQ|3X( z*xIu~qc8fwCuY$4PI0-=Onhu+ef{fnCTzCg@ke#!mO ztd1aZj;!{0f>h$sif^yC>Y7baSzh7fN&Ck#^7F@bD|ISqrxOplT#I~g$|=apo9r7L z6nIp%D?1dnlTm#kTdI+#Op<_d-)LX~mFxOya8MOZ07T%r6`E99>YUXv%^uE>*Y`ik zi>HR(wLV(^-t9Z>pCXev!|a_qhq$JHl%td7BnnfO>e z4!P8o>M8S~&5L|=#rnRx26kM?`dpw|WbYJC2aR?~KQi265h zbUu^w`r|5U*@p22;@gDnZ^|=aMyNp8Bot_vcsvn~0tzqf+cEOF(i{JD5{PpkIO0!a zg@0Jb{PxAv6AjGTlMnh(<;Usb`bK=uFE%1JggCz|z6uvz#A|m;bZoG1U8=x@<$?A6 zdqD*EFas3OQT=kMdPId_*v<}A1>aXitVAQ7GFC;Jsd^f;SzYopcOEMj?Z#;ev*y|9 zD9>ldHEOX=K4YCUW_4-R+>v(0S67zQw8qy}#*b#|X`9E*yX=8RZW|WowJz&D_6S@@ zzFn3%QEfgx$ha<86(MW%h?ksAm!!f*pB_-aK=otrZ(ow$JGU2^8@0%Cw@9ZYeOuqQ zBDML+<9>Gorqk!3&3fNe)5s$pGB>-0QRrSa*FC+t9YL)VwLCI}%sX5R-Uq#->PnwU zNnX@y`_!Dp?)e1c$-rSel)f91UwQTQUb@8h*{Dm(t?Vxa5DMKq@Wph;FVk1tn2`uh z9V|07tFyzK4osPQnz4yPyykFj{>&dF-iKxTdIKKvx4R;0!aWYdE>xRrmunu{tmpok zJ1_GlwDTsWl#6KNHF4uK2im3bCUo<(AD}X+MEZTcL`=*N?9Mkgx6SVwH+W<^E#1vI z`8%yDlle9}90bZ_=WlOspTC+qSfL8YP6d@nI`NzN?5Mq^CG%!+Uuiv#&kL6%%VKIA z1>!eZ+(G_H{KF!h?T<%?p7luLT#n@`P{w1gX*W7=$Goj$uB{7i!8?0DxbIT845_ zOK2fmp49tiaXJS$)y0oqMO(?Kly-;|BNC_rl;_ZnFATT6w&$z9Hy(;w>3-iF?Ku#g zoN`EO#X|Kn^KXB}PbT4rlk7gMk|epz-KpN2*QPqhCd7qpX3=BU`hcl3pEl^WM1{L~ zLO^K2p@%x(ANS60u0P#%;f+8?m*(7PD*cFbC+*F{xYKW0#erWHOQIJm1h=$ZqOe_K zoiGLF(Ccti?x*61bh_+WG8y6GyHahwO&o76jY}zx`<(>A;@%%wqzB4*yqUR--z(NO z=o7_UkgO2aH6VMOHxhPou_k|+wmSDc)hXI3#-6uqwKT(4GEZSMt?}#I=uN+*J0*mtG0K z#+*F=&B%+8L!O@rh0_qs^>yC zQEU6+r?IK{%H7`)pOg*R{;Okd-J9RRJ>8ymj6J+7ao8k|Djcyfl0<9H-{_MOZ)j5k zMOfYj48_LB>vUH}goopuJY=ldb-;MSCjrB*)>EF(iIc&$(MrQVw7Bo+!VRZ`f4kI= zNo3XQIZc7b&F z`Ir=Ba3QE;kcO8w#gzcH3g;DF;i%^7(;yN)tlH0=Poriv4O3Tn+S-{b+wK#D?0#Rb zIxmEq+!6;&E8D7mH)$@B=|G@Tp;Gk`w#rG8IWKHxzc}IB6xghtN7>28JML-}kbAPL{nC&(&4!dmNuSW$5%&HFDwMsYaKlDWm}AF?@SiE|uhm z>M%W`I$6s=0&VM7)>#H=wfcL%mX)Ou#N+oK$HmWj3@+g}B&iF}@Qi(LYonzheznxz9U<4!4`a*54pkjsxo z=TK?0>nqY4WHN4`1vq3`1yJGq6U6KD(?z8zn`_Xhtzl|-i|*SQuUR2U$@n{~%7q2P znQD`&?n{NUTR$KEA@kFVK6+3#_WVxTWfM)A7o1%AHk&+dr~IPpBclIcYBj9yc;*rR zqW5pdV1>&Vp~L97WsGO`>lW?Uq%aVkufiv<*J7WkW}y93tfcayTvI5lFug^YU!8D& z5q7_fg^!O+ds46;3DvadJd^uXiA(N^R5;lo<$y&Oo$zVS9KONWh;$Ii2T|?k=HFNB@uQw(-S4wit4tkG zdB>#6G^_%>y+esX9XZ?>M>)KR63AL)=jZ=fabIs@Y|FOcR4x1!!%%PFP_!I9)&SK9 zwkjdJITpH7uWm8dgA%EKTMP_!;ig=n&vMdk34#aVN=pfwF}pl;?0QCGv$od^N=7{v z)FasTj5mx4Og*oRF=Gm3vegH|!^6cGXI`RcplgKLWY<^_?FrDmfSF+8=sd?3$SIXG z3OsQ6TGJInRZCyit7;}TNU`U<^mnQzBOIZHC&dv;l-1Ij0j0HP5hxU7BJ7G@Ukw1y93i0qOYAbx_2>Hy{mb)ok8< zVB4;NnyGQ7Y>CcTgoI=$PLtbR=;J(jhgg>%c4#lnGzH~xaJ7*P2T3Ms5|22>1G=96 zAoiiY1MCG@^nFMAz$$Eu`@GrDox#ZewW31(eE-w%SN%ML%Y%8=Awm9u5Il;UD4pA@ z#XGf{_~-`}OJfx#PuNQN_E_|V7o%6-$4=p62JFXaW_lDBan2JT!FuSH=se)jQMwbv zJGbnj;+i_<)@!Z>3&2tzutV3qgKQ6I78kv14h{2S0TVIp{f zcyiHJ#yovGe}#L@3Ba}h&5BLZ6&eLEAIl#`L|{`%MBHd*a3TANvTtvb`{@MX_Jic9)IBG*1 z4x#nlk^nSo{+Rt}mNY8-yA72hTUiu}+n?o-kKhjG@eqFLq1+`%?Zd_x$u68?amG-BF{kucn`-KK5z( zw_T7q@%^x+IOD?(>*j*Tq06x3rQuH&#iundY=dg!$7=eAh6Lx=JvGWWl&DvtGb-YF z2p!<4^p#i_%%N{Pu^J`~@arpFq`Tt&=$0GJtBSA5B*{OC^L*EIEdTShqGLMlCJ>Ce z!v{!aF*e#i07i8Jq}tBe!TfCRH!TMX5*JRlqoDduV{lCa4r#FxI8G>p4pgW#l~^$- z^!GiZ&sDfW&0oN>^@jR0U^>}MD57pM=Oa>ACC7fRt-^FhUzbRycnv1;=4(WKWtUeZ z;(t5&H9p=<*h%7#3`D4pm0kRbXL>>FD?7gbiU_)dIUytex0cYchH9d$?^vPs?Jt@? zoR=U8(O>1pv@^truDHvbb%h$%@x$0h&xF>S|NedOAuQg8-)cBK`vCCHHrgD09&J)A zUN#F&qT^8h%DrR04L4Y=W6zKfK}aVj%L366tbkQb>!M8xo!%IZLwhgiVeS@gDBMFc zR~|n}Q9hRW={R;@-J733pkAg#Lm~uKf^`Y!i`pbQcLBi z9VgnB(W`kQ`~J9GJWA(&Xjm!szy5A!a>F$f=mxw&!}zMmm;=bQ0+H8QJkS44sPMx+ zD*5|{40+URq3<>-o&JiBu@c4~wj}JTUk%WE9nUI9Cnx++ z0h?bOs`0GRWXc|6gFB0@t&O{7-_a2(Gk_*3q;Xq9nFKhLVN8u$;0)idD>qV%4ciwU zl>~Z}j9wQ(7#)!c9%euUnjgYO;Gar26^2CD8*F<}zR4~S8mxDhFBGB6Cp$iR-WC#_fl!;!gw+^eOy zw;uVqgy4q3IWKO9;?V-j-jlQ>5#AZJt8p3@w*5>n6T_(prf`E~nB(O1rG$lrKW!JC zg|x|pwnPz{U{4~YCd;p@>~hXyW>is!EmvxiKt$LHrnKYX9|D>Ij6Wu!A03t)^YS$f zR*FW9HXnLGyO45G=86C=vqx2Lj2YYmapB9w5NF{vIR_x%gZ7?=8#;<=c7eCk zJQD!@68Gn1|4X*Y;PwB+ARi9HT)}|l;~BY3>I8d@Muc?$zTBk~5VVR|66#f7Oi>y) z+4eMD1X*SVMzk;LXJ#bSA_xZ zpXhA^09m;A+wL?4nc2!ku7O^mxkL&~9&il(UU4v6`OUu4|0zUHqnLSU2ALO zT$$la5#bHa|9Cp@ej!MvhRtc>@%7P@b0Dl19k2Ej-~3qc zzOt1Q{SAa$IYB?58(#NpkLRiQwzKKxrZnwAh;+hmro}n8! z;+Yc`&YRNZtIRv3eG02k|NTjqw3$v-Uv2}Pgur}pJ7RaC*~8cK7}T>hiHm77%V`?X z=$jwjSNp>J7EzV+Jv1JJNOYM7(kbqT^iPN3xmR^>Mk@%F=jJM3PWPCb&^mT}{Fxvb z<(QtlEV(0=A4x~{OY$VvKc31SJbF{s{}{RPbf7#j&z&#odvx?)0IYA z(H23Z;;si&jHUsj^kl2G_dQy9oy(0p_61`$KL#MyN$~k~d7|r$MX1UWGBOCR3Mud~ zDN*2Gp3MbtdC{&^cfA3}-~F*^AwmnrnXES zq+5(Bs=WKW;8P2EDrB8f2>rTU2|&y;r2a@bJ3IRgu#X8GCL+&TQ>%T;Fk{@p@!t&G zl7#4WOI@GBVOwiLDpT(b6osUsdsP8+A4djg+y|hw&$W47&To!BsWYAdT~zga5**M< z6fDP~LM>fP$UV{=r_X8jQrgQ|GH+tv!P2X@j~yA5dnm1kemR=_PwXBA1|2e>qTzz3 z!OFfdyZlJ7{fRc%_+K-6);%ABE}VV z>JZiYFfT7phdW*(a|I>J+-Lbe)fQl&f=l0XTdmi6_VFL0J<2nZlfy>o` zN}TkHBq*>nVFe|AJXP355a8n zl(nYe!Sewej{iJ@pY{xz98GJjD4-=7oHGIE9l}HUQ%@a1@nB2myV3jup?#7n;3&t5K&zB!aCEZj^xS*-W0t}|!OomTKk z_9)7kcO36mp&8@)|9v5N@NZ*vXD!QUwtVS7WY{YedubQl=Nx)4UI(<*FC}W@yioWS~7yxl3 zslJ91bqU5o2ECnT-47oc)5%bM%_qu@7xB`7sHs(<@R^2SC{Qb3mDhx$@u${s%iXO< zjorLh&8v1)xfLvp3Wm=Te28o5=Xynk$eQwSoh$J^PmQOZh>F0o_?(l;UIHznKK^q+D|tAii?%zw2bVGvfu%Xc^Lav*M7NE)a5t zMp;Zf)dg87T}G)y@+NK$+T)LOPK5t^4Q#Zs_vWq$m(Y%b-n@C^K6o!%X0r50rF+yU zurblX3sm3DcZCs80(*dVKah_HCUWFH4Z>Lahas4h#Pdsm-f8zuFq{e+LfPR%sdrH) zFCC0lnn1rJ^vD{G#p1oUwibx9KWn1p?(R;3)G#OYo%4*n&L{I}18Kh7&ej&i@xP!$ z+y;ChJYfni#X$7>WHqUT6MmK7eu%|Mjz^Odm$FHt+ULXQV>WP_ze!ZxCKFKPZ%4 z0Zqu@`gWgr#4mp6zuD`jy@)1vu+QA%phiy=@DM9vlEpbIBB)Ee!7LSvE?kb@YdB5R zdi6(y^;&N<8Vi`71LirS9|UA>S}=wz23wqM62wy!nknNe}eo% z%2&4GP-OV=QP3aR7sD!LNuLg^C%@+XjF04I%5ggGCQF@xvrgg47}|lxk*`$=TsZlD z6~K}ptvqH3@%|;mg~Q8=TV`9e(X!u~|1T)<>;`^=Lg-0fiXV5n!rm7UC7%D0#Q0;= z71Ci~wKx5(2n#Oc2@acObIxf*5a;Yy>5HDT@)nLCA8K)9pDX@<|NyjH}} zJrSHIZ_S`3 z8rh;Y<=bFD`5-P3L~P#x6Fv)up}4*h#_>0``LtcAieRYi1R^^w82^e;g3t2O=raBc z2`?6cX3B1z@LB5LYlx9K3)|OTT|{JSDWKL5vGjWU5e*dmKXGtlK`1MUmLIv1^Uy(V zO5sQTgN?PdFH@y2ggqxT6fj04P6wf!8cLzJilq|0)o*@=w@pRSJxK%bPqY`t-%@Ki zl(Jo@lGCPwn$Cu%*YV}ssz?EQUhM~%=#25}ey$z|^SmWq#fz4hy!V^r`k8ftFzmEp zL&iGx`FrdSq4tm*OK;#~q5n!=zZ^`NR=4P#YU0Ia@pkuDsr-){U=DIryx{SR+Chmy zV6)`#DLJwLcp-$t;ELJXoq@&*(Leek!U&M_Pa2k!*QPHA-@-f@!klNLGg4Diy8&VL zR1wE{_ZKISvdBb$-9ZBli$EI;jG8TVv1(#UE{6**@oA+Dqt~)6lMj?I6?v(GtWm$- zOY%8D4HZ>aF0z?NV`XZJuh;nb^CwadA08h51*&7E<`VhzA>V_0OtU!sn%^3uqpMto-_tvn%Of5 zk5{V(>5)MBhze&pvNShWw`jwQ3jU%t7hp9oW4_;R_9}ezmd74@^aR$0=^?sjqatV!t-lL zSKbcLr*v7(LTEk|rbxI~nub1LaYtH;DBxQS>XDWzZet$OXam&r=YbwafqvTbzGwTh zQ;;I?b~ccxP>cUvi^V`_2KeFiJX)bVj{c)ehEQdF*BOle^P!-%x#gG3y_uixK(*HH zrx1-g9eKPcaGwt?{aZMM-SLVBpF^u-z)w@T`9EDgnH3ifuKT-#&}^;k&#~9qy7#C8 zuCF~$(=(jMl{UmlF;`}Fwia4s%rO_YP!(yUh!>N=r5e=P%{vFa`7F9Y&h{-0NL9s{ z_^=a2lqVCr;MMqJu|3Xr)~S#lD_~3MK#WU2n?>u8c0l=J@cj96t-J}WJc67OWpr2? z0f%hD-DhcOX+KEO`p+ks%ZmVek-;Dr{)eT&J#boCSxL@i@j?1^h{)OKlP5jYu7**c z&cI0j5{z&{g3QExKN6%hfmb!h1Wqmf9nRno8E0>pgjQLiJ#_1WA&T@>$ac=4CTm~tpUmEN$) zjaTNQd{AP7hxwfJP)SJ%HH3~ygojC}sXRmk-G`KF`7c(9Fk>7cKHSXLDCoupdCKuS zJW;?+!Ukb~ZQ_X<;QBt;0Gxa2!2HT${*-)a^F?l2IA#ZMMj~EwTX(qg30;fe+Uz6Mq%5QXx8FS-AV~}Y@jV`8x8wjj zh>?MzGyL07Z*O6GMuu8}MHK=*s=-=QI3XR!1jRUS?u9VnF>=Y4)oW|RKyXgefV(7r{6$N+lq+paIYqWqb+ml0YmQWR4 z?`xLK4YA@e{*5#uR~)CV&O|49=AhzEVQT}SFZP+svzgDX15h znu7X!a*@OAD&ZlxJEtjH-UPMC9%{Yb8eTMN3+L$M9>UY+C|IP-YY#a zggSe9z{uK4Dht6!8l-V57DwYB9P0#lexFGK;3pq4S_h;PkpO?B58Yq4#%78O(TPhd z#LdmkFDyK@^~%E1@(0vq0Vl)u)jrsWgX!*|Ch$bXp+}u2!{R}7kHLEJlh3?nE|?QGWWdjywrCqn)6mjJ#;j|!|=F~?yL zkSd-)vJm4;eaKGJ$x)R7Z@`6X73FMVqel@80F z%HhL@e*_R<-vv?O;05zJXMOPsc6*R-riR#baC7+449>#;Ufry!&`;DDUhZykRvDL z@6)A+^c4vaqbs>Wfubr3v|i3jzir9|@dAk!z=*`M_#4@|Xn>bf5LlllYi#sl(fs^? zi>*`FL`N=R8Y8OlV98BJUl`XHWX`2TGM0ci&g7Rf{-cN>6T-0C4twjK|R`E@j!3H)9aVtjo3gxpF6bsi&Xb}oSV zCpP;VCuI`d6Y6H@KbZopwH{yy&2K3-Zh~;(Bkl^UP|QK1GrAA^5X}5bzFPWM;8!p6 zd{c`_A!;IHV3p>8smUx&Bq?a~OW-GVRb{2=Pi#>Z8fcpx(rif8f{Vw5er9HS z$mqEqSyO5gqP+YB>H6t+YY!^*Tx95f=|S(I>!MuE~U z!UHodK8`^wf<25607_By4~$w%ht&bE>o$11B{A{w7EoY-#y!LV2~_kyCF8$u7INg4 zlmDYQfS7C8+7N33#zIvlJX+u}ZjXH?CF*;Eg?2;!=yq8Aki~fC)%`~L=qI-Ugp*&C zVzb0?8Pla%Jl$5@AVm=>z!c>u0;>)b3Mp1ne=|<}PsUv})W7%oVgmo8#?_IG(-AlY zHa>zuv!|3mf?fuNNatSZRi8}ne{a^sa|7|{xW(vbz5guhV;6&RWU`U%0R=kLx*v$V zm|#YU3PDGp|L<@nLeRW_qW{#Eh#Ip)(H>$1+5ZEvRcD6Ep{ZGA$xAU85}Z~+y(b0M zISO9X*`Y`o0-XF5`xD_lO4M_z2YO}X5XBOWK%bxXII=|ipTIEprR=DLO!J9Q;fCR> zS}3q_bjc5A*hony^1QXmYDBk%{|D=DLLU1iM2JB%(n<`5(8qhaCfyL|o28_59Aqz1 zqNER`*e8VJfrz^5?On{A3d$}_p2U28tKp!|cXaWCx4_V_L zV8(bVaDzIHwf_imkNae^kz-|UK2Zb7V5Wc6J?TfjNm>vY(m9e~+or0MikZy>jESY5 zR)Z@q!S)L)!}rxlaVmLEq7KtDdOUQ<&wB<&3at=CU?DL7PgD%>9H6$8d8%G zg^`Ei!k_%VuQIq^lR4NAf*X3yGKy)W0{~+W-0T60jj?DmwYI6ptsp{*PSU+@&Zjnp zPU(OUv8nzX6Agu_h#Y?08G)zi(9ODpV8ColWm43JAC!(zK7(6K0yWbHj%NJ&M7pRPm`I;sEPVGSuN zqxP>>w%%LNRAnUPeaWj%p;~ z_@3`x-Vm$O*_!wVWLm#AMp3L6!5SajK)fYNrwT3YpvRJF9|s!<=|D&#zZ3WR=e#a{ z1$MH`ppGb4_4`mZ@%$`SOhyJ59wW~GU2t3>B7zWn-!{ZidV=4Q*7i4sr-=6P>2GFc zc!3G`7H7bcQ-P4}Z*w*XlaS_Zb%mJo7VOJRklV{9m@W@_HQpM|WtI{x$lG!1%Eg4h z3QZzJ#t|yERO}W$*|HGmWJuMe@ zNUZu3It(jYo8U{f<_;^NFXNSpY9Byx|0#=qH~k28{XDL;MX2mv$8If1!PuUUb;uNw zHWVT$C;P+ZOR-O6+`1rBbwHkKwNwFCwg*BNki{(qUT91NLIpxUJJU63M~EoqL0ts# ziT?*DGEwwuA*J6$0NW~c^bOb7)pa-yTG93Y!chMyi0|;eL|eca$3R3`n>aYAQM|jl*hgDS7k)ea>iQ6nLh*-h zfmF$i#rJMOqC2!3aM1$36ZXJWue^q6GW}Q<7lw?L@$`yJPy#2=qX6kZj^@CwhmVGW zI4X)ec8vB$VgRso!e#aMWc?oOIFFQ6?CkH~zt-*k81lHd+FxYaF8rqDWr~BCph{O^ zOEw-D$IPLL@ii4Hya)%5eALuOkxI$aews)~K#YT95{=fd5R5LCdIQ;pC z3jrR{wKF$CrKhH}v`YP_S z^98KYd61ik?qzI^oP z_C!%E1&bUuZuQx)?B**)io=D+4pcqSv4TO=zt;L_jj2xrf4KO4hi(*8BZX5M&FC7 zND&dJ^!UCOwQT6)0p)Zv81QGiUV)XMhnv@cp5E!`kf~DMRM}fySahdlzY(@*9v(I- zFPW;Yn+8;=3s|$jKxlW9hjOkmm8*X6rNXA;NU5VJ@(b?+4u(QcD`2Mih7tuxha&c} z`i{Vghr)JmF_kAvUra&RIPh&WC8^L}<)I@+6D7Py>3jVZm864cpJeGf5Zb0t$C++% zP`9j%*cb|o&5%%RE9(_}hVfrW0M+C}xL6Mh>KE{&A z=)bF_fXIKvAryUt+~%`r0__i^i~9G$kUGZ$D75$oJ6*}zyXOW{QkbalD+TOg;7a@E z4*#QlGVlc_60A=_CmL&6$L(OzvH8T709`b;xU8{XF*!Uk!=|`Gef>QBX}Z{C(_bJDXoc*-62(_S z8B{5_={U$0)zl(!ObjqrcABu!ly39m<58g#ptJ3tioz1s5ZW@3|2yZ-YhI{tcvy(8 z1gym5)d3B3uEpzWDeO@OIk}Xl8y*7KSiX2#A$f=tao4hKSu6zknOYbHOJ7KhbrThO z@sHh{VyIVYDbW1yCHWs5M3a(pQHLf@7;Azh`~l;PJE(T0N%Zs|wM~I7OZo3GP-9=3 zW<5YP20LW-Z)vcVoa+;X1J?<{$&h1O6vrJ6S}SVknO|Yx6aBmaHznLrMa2|f|2*JX z6cun>^9oEH9}Qw5pcU*!R`LGGP)B1%X}oj>)Xq`WF2O@I_ns6UrXh+ukS zz;H~80->(p3Km2TtdCPs`5iEv6m|IfMWH1FM@>cuOGrwIb%w*Zl7lIkxYY_q2+Q0|*EvqmP^oA_93}`7Ms$)Ru{$6QJZ= z5hJAjETMH{!O8rEB!@Uh*`*k1Nij{I=&vC-d=*J_$AwEcGHKCJkf%Zi#^1vVCFheq z9@sb{tebr3&6UqHPF!v+t`gGt1XgnZs4CKPC}jbzV$q0C1%4I?O-eY!43|I zQb+eA6Ip^^S^8dT7&qwN|2=gRc+m)gZ;nySzj_VvI-&v^FV^GkL?m#% zw4N9(c$Qh_X|?EenO+33aByhj31^GPP+{W*!bs<1Ys07d8&r@_SzPi!1etGGzUG#e z4>>tdLk&TauD22NB$>yGkld&UjIY@JK^ZdrBq$9C0pQ;95-

T&E$ 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 uRc+$|O3=DfJ8*(?Z2SN0AI97t&v4`yC2e7ZI*$ literal 0 HcmV?d00001 diff --git a/blueprints/anytype/template.toml b/blueprints/anytype/template.toml new file mode 100644 index 000000000..e1388da79 --- /dev/null +++ b/blueprints/anytype/template.toml @@ -0,0 +1,4 @@ +[config] +domains = [] +mounts = [] +env = [] \ No newline at end of file diff --git a/meta.json b/meta.json index 4f2d39609..fcf6f7185 100644 --- a/meta.json +++ b/meta.json @@ -295,6 +295,23 @@ "chatbot" ] }, + { + "id": "anytype", + "name": "Anytype", + "version": "latest", + "description": "Anytype is a personal knowledge base—your digital brain—that lets you gather, connect and remix all kinds of information. Create pages, tasks, wikis, journals—even entire apps—and define your own data model while your data stays offline-first, private and encrypted across devices.\n\nAfter installation, you can view the Anytype client configuration by running `cat /data/client-config.yml` inside the service container.", + "logo": "logo.png", + "links": { + "github": "https://github.com/grishy/any-sync-bundle", + "docs": "https://doc.anytype.io/anytype-docs", + "website": "https://anytype.io/" + }, + "tags": [ + "note-taking", + "local-first", + "peer-to-peer" + ] + }, { "id": "appflowy", "name": "App Flowy", From 7c540d158a579ade3ed0aebdf3a0d7bd5a946006 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Sun, 14 Dec 2025 00:52:59 -0600 Subject: [PATCH 40/91] refactor: remove dokploy-network configurations from multiple docker-compose files - Removed the external dokploy-network configuration from various services' docker-compose.yml files to streamline network management. - This change simplifies the setup and ensures consistency across blueprints. --- blueprints/budibase/docker-compose.yml | 3 --- blueprints/chatwoot/docker-compose.yml | 6 ------ blueprints/conduit/docker-compose.yml | 6 ------ blueprints/discord-tickets/docker-compose.yml | 4 ---- blueprints/docmost/docker-compose.yml | 4 ---- blueprints/drawio/docker-compose.yml | 4 ---- blueprints/evolutionapi/docker-compose.yml | 5 ----- blueprints/firecrawl/docker-compose.yml | 9 --------- blueprints/glpi/docker-compose.yml | 4 ---- blueprints/hi-events/docker-compose.yml | 4 ---- blueprints/immich/docker-compose.yml | 4 ---- blueprints/infisical/docker-compose.yml | 4 +--- blueprints/invoiceshelf/docker-compose.yml | 4 ---- blueprints/kimai/docker-compose.yml | 4 ---- blueprints/libredesk/docker-compose.yml | 3 --- blueprints/logto/docker-compose.yml | 3 --- blueprints/nextcloud-aio/docker-compose.yml | 4 ---- blueprints/peppermint/docker-compose.yml | 4 ---- blueprints/postiz/docker-compose.yml | 4 ---- blueprints/roundcube/docker-compose.yml | 3 --- blueprints/slash/docker-compose.yml | 4 ---- blueprints/trilium/docker-compose.yml | 5 ----- blueprints/unifi/docker-compose.yml | 3 --- blueprints/wikijs/docker-compose.yml | 3 --- blueprints/windmill/docker-compose.yml | 3 --- 25 files changed, 1 insertion(+), 103 deletions(-) diff --git a/blueprints/budibase/docker-compose.yml b/blueprints/budibase/docker-compose.yml index 620324dc8..92ad3288a 100644 --- a/blueprints/budibase/docker-compose.yml +++ b/blueprints/budibase/docker-compose.yml @@ -182,9 +182,6 @@ services: labels: - com.centurylinklabs.watchtower.enable=false -networks: - dokploy-network: - external: true volumes: minio_data: diff --git a/blueprints/chatwoot/docker-compose.yml b/blueprints/chatwoot/docker-compose.yml index ece9ef44d..a43b43f79 100644 --- a/blueprints/chatwoot/docker-compose.yml +++ b/blueprints/chatwoot/docker-compose.yml @@ -4,8 +4,6 @@ x-base-config: &base-config image: chatwoot/chatwoot:v4.0.3 volumes: - chatwoot-storage:/app/storage - networks: - - dokploy-network environment: - FRONTEND_URL=${FRONTEND_URL} - SECRET_KEY_BASE=${SECRET_KEY_BASE} @@ -63,10 +61,6 @@ services: volumes: - chatwoot-redis-data:/data -networks: - dokploy-network: - external: true - volumes: chatwoot-storage: chatwoot-postgres-data: diff --git a/blueprints/conduit/docker-compose.yml b/blueprints/conduit/docker-compose.yml index f2f1fb340..8f7746739 100644 --- a/blueprints/conduit/docker-compose.yml +++ b/blueprints/conduit/docker-compose.yml @@ -7,8 +7,6 @@ services: restart: unless-stopped volumes: - db:/var/lib/matrix-conduit/ - networks: - - dokploy-network environment: CONDUIT_SERVER_NAME: ${MATRIX_SUBDOMAIN} CONDUIT_DATABASE_PATH: /var/lib/matrix-conduit/ @@ -25,7 +23,3 @@ services: CONDUIT_CONFIG: '' # Ignore this volumes: db: - -networks: - dokploy-network: - external: true diff --git a/blueprints/discord-tickets/docker-compose.yml b/blueprints/discord-tickets/docker-compose.yml index f797a77b0..62105b3a1 100644 --- a/blueprints/discord-tickets/docker-compose.yml +++ b/blueprints/discord-tickets/docker-compose.yml @@ -45,10 +45,6 @@ services: PUBLISH_COMMANDS: "true" SUPER: ${SUPER_USERS} -networks: - dokploy-network: - external: true - volumes: tickets-mysql-data: tickets-app-data: \ No newline at end of file diff --git a/blueprints/docmost/docker-compose.yml b/blueprints/docmost/docker-compose.yml index b5995594b..d7efba929 100644 --- a/blueprints/docmost/docker-compose.yml +++ b/blueprints/docmost/docker-compose.yml @@ -34,10 +34,6 @@ services: volumes: - redis_docmost_data:/data -networks: - dokploy-network: - external: true - volumes: docmost: db_docmost_data: diff --git a/blueprints/drawio/docker-compose.yml b/blueprints/drawio/docker-compose.yml index a7d7b578d..cdd036d92 100644 --- a/blueprints/drawio/docker-compose.yml +++ b/blueprints/drawio/docker-compose.yml @@ -51,9 +51,5 @@ services: DRAWIO_GITLAB_SECRET: ${DRAWIO_GITLAB_SECRET} DRAWIO_GITLAB_URL: ${DRAWIO_GITLAB_URL} DRAWIO_CLOUD_CONVERT_APIKEY: ${DRAWIO_CLOUD_CONVERT_APIKEY} -networks: - dokploy-network: - external: true - volumes: fonts_volume: \ No newline at end of file diff --git a/blueprints/evolutionapi/docker-compose.yml b/blueprints/evolutionapi/docker-compose.yml index d4803de1c..5807ff824 100644 --- a/blueprints/evolutionapi/docker-compose.yml +++ b/blueprints/evolutionapi/docker-compose.yml @@ -47,11 +47,6 @@ services: volumes: - evolution-redis-data:/data - -networks: - dokploy-network: - external: true - volumes: evolution-instances: evolution-postgres-data: diff --git a/blueprints/firecrawl/docker-compose.yml b/blueprints/firecrawl/docker-compose.yml index f4181cfc3..d7622f7a5 100644 --- a/blueprints/firecrawl/docker-compose.yml +++ b/blueprints/firecrawl/docker-compose.yml @@ -124,15 +124,6 @@ services: interval: 10s timeout: 5s retries: 10 - networks: - - backend - - dokploy-network - -networks: - backend: - driver: bridge - dokploy-network: - external: true volumes: nuq_pg_data: \ No newline at end of file diff --git a/blueprints/glpi/docker-compose.yml b/blueprints/glpi/docker-compose.yml index fa732fa36..3abe1f051 100644 --- a/blueprints/glpi/docker-compose.yml +++ b/blueprints/glpi/docker-compose.yml @@ -20,7 +20,3 @@ services: volumes: glpi-mysql-data: glpi-www-data: - -networks: - dokploy-network: - external: true \ No newline at end of file diff --git a/blueprints/hi-events/docker-compose.yml b/blueprints/hi-events/docker-compose.yml index cce45fecf..3c2291b9b 100644 --- a/blueprints/hi-events/docker-compose.yml +++ b/blueprints/hi-events/docker-compose.yml @@ -36,9 +36,5 @@ services: volumes: - pg_hi-events_data:/var/lib/postgresql/data -networks: - dokploy-network: - external: true - volumes: pg_hi-events_data: \ No newline at end of file diff --git a/blueprints/immich/docker-compose.yml b/blueprints/immich/docker-compose.yml index cdd31199a..8f12d6d12 100644 --- a/blueprints/immich/docker-compose.yml +++ b/blueprints/immich/docker-compose.yml @@ -96,10 +96,6 @@ services: ] restart: always -networks: - dokploy-network: - external: true - volumes: immich-model-cache: immich-postgres: diff --git a/blueprints/infisical/docker-compose.yml b/blueprints/infisical/docker-compose.yml index 7566c8980..3fdb7b04b 100644 --- a/blueprints/infisical/docker-compose.yml +++ b/blueprints/infisical/docker-compose.yml @@ -77,7 +77,5 @@ volumes: pg_infisical_data: redis_infisical_data: -networks: - dokploy-network: - external: true + diff --git a/blueprints/invoiceshelf/docker-compose.yml b/blueprints/invoiceshelf/docker-compose.yml index 0f1c3d2c8..8d2c82652 100644 --- a/blueprints/invoiceshelf/docker-compose.yml +++ b/blueprints/invoiceshelf/docker-compose.yml @@ -47,10 +47,6 @@ services: invoiceshelf-postgres: condition: service_healthy -networks: - dokploy-network: - external: true - volumes: invoiceshelf-postgres-data: invoiceshelf-app-data: diff --git a/blueprints/kimai/docker-compose.yml b/blueprints/kimai/docker-compose.yml index d3eab1c32..c97cadfbc 100644 --- a/blueprints/kimai/docker-compose.yml +++ b/blueprints/kimai/docker-compose.yml @@ -40,10 +40,6 @@ services: start_period: 30s -networks: - dokploy-network: - external: true - volumes: kimai-data: mysql-data: \ No newline at end of file diff --git a/blueprints/libredesk/docker-compose.yml b/blueprints/libredesk/docker-compose.yml index ef647ab75..212b528f6 100644 --- a/blueprints/libredesk/docker-compose.yml +++ b/blueprints/libredesk/docker-compose.yml @@ -46,9 +46,6 @@ services: volumes: - redis-data:/data -networks: - libredesk: - volumes: postgres-data: redis-data: \ No newline at end of file diff --git a/blueprints/logto/docker-compose.yml b/blueprints/logto/docker-compose.yml index b59bdac00..04d7464f4 100644 --- a/blueprints/logto/docker-compose.yml +++ b/blueprints/logto/docker-compose.yml @@ -31,9 +31,6 @@ services: timeout: 5s retries: 5 -networks: - dokploy-network: - external: true volumes: logto-connectors: diff --git a/blueprints/nextcloud-aio/docker-compose.yml b/blueprints/nextcloud-aio/docker-compose.yml index 1e6d00fe3..1f4e43e6a 100644 --- a/blueprints/nextcloud-aio/docker-compose.yml +++ b/blueprints/nextcloud-aio/docker-compose.yml @@ -30,7 +30,3 @@ services: volumes: nextcloud_data: nextcloud_db_data: - -networks: - dokploy-network: - external: true diff --git a/blueprints/peppermint/docker-compose.yml b/blueprints/peppermint/docker-compose.yml index 06fb46c66..a5af058f9 100644 --- a/blueprints/peppermint/docker-compose.yml +++ b/blueprints/peppermint/docker-compose.yml @@ -30,9 +30,5 @@ services: DB_HOST: "peppermint-postgres" SECRET: ${SECRET} -networks: - dokploy-network: - external: true - volumes: peppermint-postgres-data: \ No newline at end of file diff --git a/blueprints/postiz/docker-compose.yml b/blueprints/postiz/docker-compose.yml index cd06e7952..dfbf3ae94 100644 --- a/blueprints/postiz/docker-compose.yml +++ b/blueprints/postiz/docker-compose.yml @@ -54,10 +54,6 @@ services: volumes: - postiz-redis-data:/data -networks: - dokploy-network: - external: true - volumes: postiz-postgres-data: postiz-redis-data: diff --git a/blueprints/roundcube/docker-compose.yml b/blueprints/roundcube/docker-compose.yml index e5ba4a8b1..8af6daf67 100644 --- a/blueprints/roundcube/docker-compose.yml +++ b/blueprints/roundcube/docker-compose.yml @@ -11,6 +11,3 @@ services: - ROUNDCUBEMAIL_SMTP_SERVER=${SMTP_SERVER} -networks: - dokploy-network: - external: true diff --git a/blueprints/slash/docker-compose.yml b/blueprints/slash/docker-compose.yml index ee6cdf895..c3aba95ac 100644 --- a/blueprints/slash/docker-compose.yml +++ b/blueprints/slash/docker-compose.yml @@ -30,10 +30,6 @@ services: retries: 5 restart: unless-stopped -networks: - dokploy-network: - external: true - volumes: slash-app-data: slash-postgres-data: \ No newline at end of file diff --git a/blueprints/trilium/docker-compose.yml b/blueprints/trilium/docker-compose.yml index f549d8204..20f7dcd1f 100644 --- a/blueprints/trilium/docker-compose.yml +++ b/blueprints/trilium/docker-compose.yml @@ -3,12 +3,7 @@ services: image: zadam/trilium:latest ports: - 8080 - networks: - - dokploy-network restart: always volumes: - /root/trilium-backups:/home/node/trilium-data/backup -networks: - dokploy-network: - external: true diff --git a/blueprints/unifi/docker-compose.yml b/blueprints/unifi/docker-compose.yml index cf0102c00..0aeb31ea0 100644 --- a/blueprints/unifi/docker-compose.yml +++ b/blueprints/unifi/docker-compose.yml @@ -41,6 +41,3 @@ services: restart: unless-stopped -networks: - dokploy-network: - external: true diff --git a/blueprints/wikijs/docker-compose.yml b/blueprints/wikijs/docker-compose.yml index 6b21423d1..aad6e5978 100644 --- a/blueprints/wikijs/docker-compose.yml +++ b/blueprints/wikijs/docker-compose.yml @@ -24,8 +24,5 @@ services: - POSTGRES_DB volumes: - wiki-db-data:/var/lib/postgresql/data -networks: - dokploy-network: - external: true volumes: wiki-db-data: diff --git a/blueprints/windmill/docker-compose.yml b/blueprints/windmill/docker-compose.yml index 9e91fa0ab..8b6eafdc1 100644 --- a/blueprints/windmill/docker-compose.yml +++ b/blueprints/windmill/docker-compose.yml @@ -94,9 +94,6 @@ services: - windmill-server - windmill-lsp -networks: - dokploy-network: - external: true volumes: windmill-postgres-data: From 4c36b7d6cabfebfb935de1b6b196bc4da89b8d1a Mon Sep 17 00:00:00 2001 From: Harikrishnan Dhanasekaran Date: Sun, 14 Dec 2025 12:24:16 +0530 Subject: [PATCH 41/91] chore: upgrade Infisical from v0.90.1 to v0.135.0 (#529) * chore: upgrade Infisical from v0.90.1 to v0.135.0 * Update docker-compose.yml --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> --- blueprints/infisical/docker-compose.yml | 5 ++--- meta.json | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/blueprints/infisical/docker-compose.yml b/blueprints/infisical/docker-compose.yml index 3fdb7b04b..ac5718bcb 100644 --- a/blueprints/infisical/docker-compose.yml +++ b/blueprints/infisical/docker-compose.yml @@ -3,7 +3,7 @@ services: depends_on: db: condition: service_healthy - image: infisical/infisical:v0.90.1-postgres + image: infisical/infisical:v0.135.0-postgres environment: - NODE_ENV=production - ENCRYPTION_KEY @@ -20,7 +20,6 @@ services: command: npm run migration:latest pull_policy: always - backend: restart: unless-stopped depends_on: @@ -30,7 +29,7 @@ services: condition: service_started db-migration: condition: service_completed_successfully - image: infisical/infisical:v0.90.1-postgres + image: infisical/infisical:v0.135.0-postgres pull_policy: always environment: - NODE_ENV=production diff --git a/meta.json b/meta.json index fcf6f7185..dd95e6401 100644 --- a/meta.json +++ b/meta.json @@ -2992,7 +2992,7 @@ { "id": "infisical", "name": "Infisical", - "version": "0.90.1", + "version": "0.135.0", "description": "All-in-one platform to securely manage application configuration and secrets across your team and infrastructure.", "logo": "infisical.jpg", "links": { From 0e84b284d8dfeb611dbb6d7437b3772fd0da19da Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Sun, 14 Dec 2025 01:15:25 -0600 Subject: [PATCH 42/91] fix: update pull request template link for clarity - Changed the link in the pull request template from 'general suggestions' to 'general requirements' to better reflect the content and ensure users follow the correct guidelines when creating templates. --- .github/pull_request_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index f349d36a5..eaa5c7c94 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -6,7 +6,7 @@ New PR of [Template Name] Before submitting this PR, please make sure that: -- [ ] I have read the suggestions in the README.md file https://github.com/Dokploy/templates?tab=readme-ov-file#general-suggestions-when-creating-a-template +- [ ] I have read the suggestions in the README.md file https://github.com/Dokploy/templates?tab=readme-ov-file#general-requirements-when-creating-a-template - [ ] I have tested the template in my instance, so the maintainers don't spend time trying to figure out what's wrong. - [ ] I have added tests that demonstrate that my correction works or that my new feature works. From b4efed26a0259fcda37a7ed1e3f8964355b29744 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Sun, 14 Dec 2025 01:15:46 -0600 Subject: [PATCH 43/91] chore: add section for screenshots or videos in pull request template - Introduced a new section in the pull request template to encourage contributors to include screenshots or videos, enhancing the clarity and context of their submissions. --- .github/pull_request_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index eaa5c7c94..aa99ffaec 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -15,4 +15,4 @@ Before submitting this PR, please make sure that: Close automatically the related issues using the keywords: `closes #ISSUE_NUMBER` - +## Screenshots or Videos From a9740da9cb0c7fd7085af5a28bd5c56df5da5764 Mon Sep 17 00:00:00 2001 From: Harikrishnan Dhanasekaran Date: Sun, 14 Dec 2025 12:46:39 +0530 Subject: [PATCH 44/91] Feat : Add MuleSoft ESB Runtime Template (#498) * added the mulesoft esb template * updated the compose and the meta.json * feat(mulesoft-esb): update image and add dynamic env configuration - Updated image to hari1367709/mule-esb:latest - Added dynamic HTTP_PORT for runtime port configuration - Added MULE_VERSION environment variable for Mule ESB version selection * updated the meta.json to use the version as latest * added a comment line to the template file * updated the mule runtime image * fix(mulesoft-esb): update ports configuration to follow guidelines * updated the port to use the env(HTTP_PORT) * Update docker-compose.yml * Update docker-compose.yml * Update blueprints/mulesoft-esb/docker-compose.yml --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> --- blueprints/mulesoft-esb/docker-compose.yml | 22 ++++++++++++++++++++ blueprints/mulesoft-esb/mulesoft_logo.png | Bin 0 -> 44699 bytes blueprints/mulesoft-esb/template.toml | 23 +++++++++++++++++++++ meta.json | 19 +++++++++++++++++ 4 files changed, 64 insertions(+) create mode 100644 blueprints/mulesoft-esb/docker-compose.yml create mode 100644 blueprints/mulesoft-esb/mulesoft_logo.png create mode 100644 blueprints/mulesoft-esb/template.toml diff --git a/blueprints/mulesoft-esb/docker-compose.yml b/blueprints/mulesoft-esb/docker-compose.yml new file mode 100644 index 000000000..6691e4554 --- /dev/null +++ b/blueprints/mulesoft-esb/docker-compose.yml @@ -0,0 +1,22 @@ +version: "3.8" + +services: + mule: + image: yogeshmulecraft/mulece-esb:latest + restart: unless-stopped + ports: + - "8081" + environment: + - MULE_VERSION=${MULE_VERSION:-4.9.0} + - HTTP_PORT=${HTTP_PORT:-8081} + - MULE_HOME=/opt/mule-standalone + - MULE_BASE=/opt/mule-standalone + - TZ=UTC + - MULE_VERBOSE_EXCEPTIONS=true + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:${HTTP_PORT:-8081}/ || exit 0"] + interval: 60s + timeout: 20s + retries: 5 + start_period: 120s + diff --git a/blueprints/mulesoft-esb/mulesoft_logo.png b/blueprints/mulesoft-esb/mulesoft_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..7741d7f0dfad7cd65da29048632945ec71e6fc92 GIT binary patch literal 44699 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�#sSHWfi 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 literal 0 HcmV?d00001 diff --git a/blueprints/mulesoft-esb/template.toml b/blueprints/mulesoft-esb/template.toml new file mode 100644 index 000000000..6cfb9017f --- /dev/null +++ b/blueprints/mulesoft-esb/template.toml @@ -0,0 +1,23 @@ + +# MuleSoft ESB Deployment Configuration + +[variables] +main_domain = "${domain}" +timezone = "UTC" +mule_version = "4.9.0" +http_port = "8081" + +[config] +[[config.domains]] +serviceName = "mule" +port = 8081 +host = "${main_domain}" +path = "/" + +[config.env] +MULE_VERSION = "${mule_version}" +HTTP_PORT = "${http_port}" +MULE_HOME = "/opt/mule-standalone" +MULE_BASE = "/opt/mule-standalone" +TZ = "${timezone}" +MULE_VERBOSE_EXCEPTIONS = "true" diff --git a/meta.json b/meta.json index dd95e6401..3e7d3b99f 100644 --- a/meta.json +++ b/meta.json @@ -3821,6 +3821,25 @@ "rating" ] }, + { + "id": "mulesoft-esb", + "name": "MuleSoft ESB Runtime Community Edition", + "version": "latest", + "description": "MuleSoft ESB Runtime is a lightweight, Java-based integration platform that allows you to easily integrate applications, data sources, and APIs. It provides powerful connectors and data transformation capabilities for building robust integration solutions.", + "logo": "mulesoft_logo.png", + "links": { + "github": "https://github.com/mulesoft", + "website": "https://www.mulesoft.com/", + "docs": "https://docs.mulesoft.com/" + }, + "tags": [ + "integration", + "api", + "esb", + "enterprise", + "java" + ] + }, { "id": "n8n", "name": "n8n", From 87ab908a3279cfbbde470f65472330e3a97bba2e Mon Sep 17 00:00:00 2001 From: Benjamin Nussbaum Date: Sun, 14 Dec 2025 08:18:49 +0100 Subject: [PATCH 45/91] feat(blueprint): update trmnl-byos-laravel template (#533) * feat(blueprint): update trmnl-byos-laravel template * Update docker-compose.yml --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> --- blueprints/trmnl-byos-laravel/docker-compose.yml | 3 +-- meta.json | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/blueprints/trmnl-byos-laravel/docker-compose.yml b/blueprints/trmnl-byos-laravel/docker-compose.yml index bd714f192..f571719d2 100644 --- a/blueprints/trmnl-byos-laravel/docker-compose.yml +++ b/blueprints/trmnl-byos-laravel/docker-compose.yml @@ -1,6 +1,6 @@ services: trmnl-byos-laravel: - image: ghcr.io/usetrmnl/byos_laravel:0.14.0 + image: ghcr.io/usetrmnl/byos_laravel:0.21.0 environment: - APP_URL=${APP_URL} - PHP_OPCACHE_ENABLE=${PHP_OPCACHE_ENABLE} @@ -14,7 +14,6 @@ services: volumes: - trmnl-database:/var/www/html/database/storage - trmnl-storage:/var/www/html/storage/app/public/images/generated - volumes: trmnl-database: trmnl-storage: diff --git a/meta.json b/meta.json index 3e7d3b99f..64360ce12 100644 --- a/meta.json +++ b/meta.json @@ -5601,7 +5601,7 @@ { "id": "trmnl-byos-laravel", "name": "TRMNL BYOS Laravel", - "version": "0.14.0", + "version": "0.21.0", "description": "TRMNL BYOS Laravel is a self-hosted application to manage TRMNL e-ink devices.", "logo": "byos-laravel.svg", "links": { From 4367e2a42a29ce542d6cecb770693250d87e5692 Mon Sep 17 00:00:00 2001 From: Khiet Tam Nguyen <86177399+nktnet1@users.noreply.github.com> Date: Mon, 15 Dec 2025 15:52:06 +1100 Subject: [PATCH 46/91] feat(blueprint): peerdb template (#579) * feat(blueprint): initial attempt at peerdb template * fix: entrypoint and healthcheck * fix: entrypoint * fix: temporarily remove network * fix: temporal port * chore: remove 36987 for minio * fix: remove peerdb 9900 port exposure * fix: port for console * fix: minio env fix * fix: expose peerdb and minio to dokploy network * fix(peerdb): add defaults * fix: remove extra hosts * fix: remove network entries * fix: use consistent environment variables --- blueprints/peerdb/docker-compose.yml | 187 +++++++++++++++++++++++++++ blueprints/peerdb/peerdb.jpeg | Bin 0 -> 10300 bytes blueprints/peerdb/template.toml | 93 +++++++++++++ meta.json | 19 +++ 4 files changed, 299 insertions(+) create mode 100644 blueprints/peerdb/docker-compose.yml create mode 100644 blueprints/peerdb/peerdb.jpeg create mode 100644 blueprints/peerdb/template.toml diff --git a/blueprints/peerdb/docker-compose.yml b/blueprints/peerdb/docker-compose.yml new file mode 100644 index 000000000..5d2c69f74 --- /dev/null +++ b/blueprints/peerdb/docker-compose.yml @@ -0,0 +1,187 @@ +name: peerdb-quickstart + +x-minio-config: &minio-config + PEERDB_CLICKHOUSE_AWS_CREDENTIALS_AWS_ACCESS_KEY_ID: ${MINIO_ROOT_USER} + PEERDB_CLICKHOUSE_AWS_CREDENTIALS_AWS_SECRET_ACCESS_KEY: ${MINIO_ROOT_PASSWORD} + MINIO_ROOT_USER: ${MINIO_ROOT_USER} + MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD} + PEERDB_CLICKHOUSE_AWS_CREDENTIALS_AWS_REGION: ${PEERDB_CLICKHOUSE_AWS_REGION} + PEERDB_CLICKHOUSE_AWS_CREDENTIALS_AWS_ENDPOINT_URL_S3: ${PEERDB_CLICKHOUSE_AWS_ENDPOINT_URL_S3} + PEERDB_CLICKHOUSE_AWS_S3_BUCKET_NAME: ${PEERDB_CLICKHOUSE_AWS_S3_BUCKET_NAME} + +x-catalog-config: &catalog-config + PEERDB_CATALOG_HOST: ${PEERDB_CATALOG_HOST} + PEERDB_CATALOG_PORT: ${PEERDB_CATALOG_PORT} + PEERDB_CATALOG_USER: ${PEERDB_CATALOG_USER} + PEERDB_CATALOG_PASSWORD: ${PEERDB_CATALOG_PASSWORD} + PEERDB_CATALOG_DATABASE: ${PEERDB_CATALOG_DATABASE} + +x-flow-worker-env: &flow-worker-env + TEMPORAL_HOST_PORT: temporal:7233 + TEMPORAL_CLIENT_CERT: + TEMPORAL_CLIENT_KEY: + PEERDB_TEMPORAL_NAMESPACE: default + AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID:-} + AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY:-} + AWS_REGION: ${AWS_REGION:-} + AWS_ENDPOINT: ${AWS_ENDPOINT:-} + +services: + catalog: + image: postgres:18-alpine@sha256:eca6fb2d91fda290eb8cfb8ba53dd0dcbf3508a08011e30adb039ea7c8e1e9f2 + command: -c config_file=/etc/postgresql.conf + restart: unless-stopped + expose: + - 5432 + environment: + PGUSER: ${PEERDB_CATALOG_USER} + POSTGRES_PASSWORD: ${PEERDB_CATALOG_PASSWORD} + POSTGRES_DB: ${PEERDB_CATALOG_DATABASE} + POSTGRES_INITDB_ARGS: --locale=C.UTF-8 + volumes: + - pgdata:/var/lib/postgresql/data + - ../files/postgresql.conf:/etc/postgresql.conf + - ../files/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d + healthcheck: + test: ["CMD", "pg_isready", "-d", "${PEERDB_CATALOG_DATABASE}", "-U", "${PEERDB_CATALOG_USER}"] + interval: 10s + timeout: 30s + retries: 5 + start_period: 60s + + temporal: + restart: unless-stopped + depends_on: + catalog: + condition: service_healthy + environment: + DB: postgres12 + DB_PORT: ${PEERDB_CATALOG_PORT} + POSTGRES_USER: ${PEERDB_CATALOG_USER} + POSTGRES_PWD: ${PEERDB_CATALOG_PASSWORD} + POSTGRES_SEEDS: catalog + DYNAMIC_CONFIG_FILE_PATH: config/dynamicconfig/production-sql.yaml + image: temporalio/auto-setup:1.29@sha256:5b3502a3b685f9eff1b925af90c57c9e3dbeccbef367cc28a2a9712c63379312 + expose: + - 7233 + volumes: + - ../files/temporal-dynamicconfig:/etc/temporal/config/dynamicconfig + + temporal-admin-tools: + restart: unless-stopped + depends_on: + - temporal + environment: + TEMPORAL_ADDRESS: temporal:7233 + TEMPORAL_CLI_ADDRESS: temporal:7233 + TEMPORAL_CLI_SHOW_STACKS: 1 + image: temporalio/admin-tools:1.25.2-tctl-1.18.1-cli-1.1.1@sha256:da0c7a7982b571857173ab8f058e7f139b3054800abb4dcb100445d29a563ee8 + stdin_open: true + tty: true + entrypoint: ["sh", "/etc/temporal/entrypoint.sh"] + healthcheck: + test: ["CMD", "tctl", "workflow", "list"] + interval: 10s + timeout: 30s + retries: 5 + volumes: + - ../files/scripts/mirror-name-search.sh:/etc/temporal/entrypoint.sh + + temporal-ui: + restart: unless-stopped + depends_on: + - temporal + environment: + TEMPORAL_ADDRESS: temporal:7233 + TEMPORAL_CORS_ORIGINS: http://localhost:3000 + TEMPORAL_CSRF_COOKIE_INSECURE: "true" + image: temporalio/ui:2.43.3@sha256:31f0d8c1ed0bfc49c9c20ea9613ee9dd5c52f5f989bacb8a30210f847028e9cd + expose: + - 8080 + + flow-api: + image: ghcr.io/peerdb-io/flow-api:stable-v0.35.5 + restart: unless-stopped + expose: + - 8112 + - 8113 + environment: + <<: [*catalog-config, *flow-worker-env, *minio-config] + PEERDB_ALLOWED_TARGETS: ${PEERDB_ALLOWED_TARGETS} + depends_on: + temporal-admin-tools: + condition: service_healthy + + flow-snapshot-worker: + image: ghcr.io/peerdb-io/flow-snapshot-worker:stable-v0.35.5 + restart: unless-stopped + environment: + <<: [*catalog-config, *flow-worker-env, *minio-config] + depends_on: + temporal-admin-tools: + condition: service_healthy + + flow-worker: + image: ghcr.io/peerdb-io/flow-worker:stable-v0.35.5 + restart: unless-stopped + environment: + <<: [*catalog-config, *flow-worker-env, *minio-config] + depends_on: + temporal-admin-tools: + condition: service_healthy + + peerdb: + stop_signal: SIGINT + image: ghcr.io/peerdb-io/peerdb-server:stable-v0.35.5 + restart: unless-stopped + environment: + <<: *catalog-config + PEERDB_PASSWORD: ${PEERDB_PASSWORD} + PEERDB_FLOW_SERVER_ADDRESS: ${PEERDB_FLOW_SERVER_ADDRESS} + RUST_LOG: info + RUST_BACKTRACE: 1 + expose: + - 9900 + depends_on: + catalog: + condition: service_healthy + + peerdb-ui: + image: ghcr.io/peerdb-io/peerdb-ui:stable-v0.35.5 + restart: unless-stopped + expose: + - 3000 + environment: + <<: *catalog-config + DATABASE_URL: ${DATABASE_URL} + PEERDB_FLOW_SERVER_HTTP: ${PEERDB_FLOW_SERVER_HTTP} + NEXTAUTH_SECRET: ${NEXTAUTH_SECRET} + NEXTAUTH_URL: ${NEXTAUTH_URL} + PEERDB_ALLOWED_TARGETS: ${PEERDB_ALLOWED_TARGETS} + PEERDB_CLICKHOUSE_ALLOWED_DOMAINS: ${PEERDB_CLICKHOUSE_ALLOWED_DOMAINS} + PEERDB_EXPERIMENTAL_ENABLE_SCRIPTING: ${PEERDB_EXPERIMENTAL_ENABLE_SCRIPTING} + depends_on: + - flow-api + + minio: + image: minio/minio:latest@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e + restart: unless-stopped + volumes: + - minio-data:/data + expose: + - 9000 + - 9001 + environment: + <<: *minio-config + entrypoint: > + /bin/sh -c " + minio server /data --console-address=:9001 & + sleep 2; + mc alias set myminiopeerdb http://minio:9000 $$MINIO_ROOT_USER $$MINIO_ROOT_PASSWORD; + mc mb myminiopeerdb/$$PEERDB_CLICKHOUSE_AWS_S3_BUCKET_NAME; + wait + " + +volumes: + pgdata: + minio-data: diff --git a/blueprints/peerdb/peerdb.jpeg b/blueprints/peerdb/peerdb.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..cd33a1fae48a92c41f952cff21d2b69a52dbd65f GIT binary patch literal 10300 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 N+KPAA@U_v*{{rsRpC$kR literal 0 HcmV?d00001 diff --git a/blueprints/peerdb/template.toml b/blueprints/peerdb/template.toml new file mode 100644 index 000000000..3325ac4b3 --- /dev/null +++ b/blueprints/peerdb/template.toml @@ -0,0 +1,93 @@ +[variables] +main_domain = "${domain}" +peerdb_password = "${password:32}" +postgres_password = "${password:32}" +minio_root_user = "_peerdb_minioadmin" +minio_root_password = "${password:32}" +nextauth_secret = "${password:32}" + +[[config.domains]] +serviceName = "peerdb-ui" +port = 3000 +host = "${main_domain}" + +[[config.domains]] +serviceName = "minio" +port = 9001 +host = "${main_domain}" + +[[config.domains]] +serviceName = "temporal-ui" +port = 8080 +host = "${main_domain}" + +[config.env] +PEERDB_PASSWORD = "${peerdb_password}" +PEERDB_CATALOG_HOST = "catalog" +PEERDB_CATALOG_PORT = "5432" +PEERDB_CATALOG_USER = "postgres" +PEERDB_CATALOG_PASSWORD = "${postgres_password}" +PEERDB_CATALOG_DATABASE = "postgres" +PEERDB_FLOW_SERVER_ADDRESS = "grpc://flow-api:8112" +NEXTAUTH_URL = "http://localhost:3000" +NEXTAUTH_SECRET = "${nextauth_secret}" +DATABASE_URL = "postgres://postgres:${postgres_password}@catalog:5432/postgres" +PEERDB_FLOW_SERVER_HTTP = "http://flow-api:8113" +PEERDB_EXPERIMENTAL_ENABLE_SCRIPTING = "true" +MINIO_ROOT_USER = "${minio_root_user}" +MINIO_ROOT_PASSWORD = "${minio_root_password}" +PEERDB_CLICKHOUSE_AWS_REGION = "us-east-1" +PEERDB_CLICKHOUSE_AWS_ENDPOINT_URL_S3 = "http://minio:9000" +PEERDB_CLICKHOUSE_AWS_S3_BUCKET_NAME = "peerdbbucket" +PEERDB_ALLOWED_TARGETS = "" +PEERDB_CLICKHOUSE_ALLOWED_DOMAINS = "" +AWS_ACCESS_KEY_ID = "" +AWS_SECRET_ACCESS_KEY = "" +AWS_REGION = "" +AWS_ENDPOINT = "" + +[[config.mounts]] +filePath = "./postgresql.conf" +content = """ +listen_addresses = '*' + +wal_level = logical +max_wal_senders = 4 +max_replication_slots = 4 +""" + +[[config.mounts]] +filePath = "./docker-entrypoint-initdb.d/pg-hba-replication.sh" +content = """ +#!/bin/sh +echo "host replication $POSTGRES_USER 0.0.0.0/0 trust" >> "$PGDATA/pg_hba.conf" +""" + +[[config.mounts]] +filePath = "./temporal-dynamicconfig/production-sql.yaml" +content = """ +limit.maxIDLength: + - value: 255 + constraints: {} +system.forceSearchAttributesCacheRefreshOnRead: + - value: false + constraints: {} +frontend.enableUpdateWorkflowExecution: + - value: true +""" + +[[config.mounts]] +filePath = "./scripts/mirror-name-search.sh" +content = """ +#!/bin/sh + +sleep 5 + +# Check if MirrorName attribute exists +if ! temporal operator search-attribute list | grep -w MirrorName >/dev/null 2>&1; then + # If not, create MirrorName attribute + temporal operator search-attribute create --name MirrorName --type Text --namespace default +fi + +tini -s -- sleep infinity +""" diff --git a/meta.json b/meta.json index 64360ce12..1c558d829 100644 --- a/meta.json +++ b/meta.json @@ -4508,6 +4508,25 @@ "client-management" ] }, + { + "id": "peerdb", + "name": "PeerDB", + "version": "v0.35.5", + "description": "Data integration platform that synchronizes and federates data across databases with a unified API.", + "logo": "peerdb.jpeg", + "links": { + "github": "https://github.com/peerdb-io/peerdb", + "website": "https://peerdb.io", + "docs": "https://docs.peerdb.io" + }, + "tags": [ + "database", + "integration", + "sync", + "sql", + "workflow" + ] + }, { "id": "penpot", "name": "Penpot", From 40aa695a73da1c35f24bc37c33418b674d9b4790 Mon Sep 17 00:00:00 2001 From: Scan <103391616+scanash00@users.noreply.github.com> Date: Sun, 14 Dec 2025 20:08:22 -0900 Subject: [PATCH 47/91] feat: add Bluesky PDS template (#542) * feat: Bluesky PDS template * chore: add bluesky pds svg * chore: metadata for bluesky pds * yaml > yml * pnpm lock * fix: correct rotation key config * fix volumes * fix: volumes in the pds compose * define volumes in compose * fix: 32 bit rotation key * create pds.env correctly * some extra fixes * more extra fixes * a blank line * update pnpm lock --- blueprints/bluesky-pds/bluesky-pds.svg | 3 ++ blueprints/bluesky-pds/docker-compose.yml | 48 +++++++++++++++++++++++ blueprints/bluesky-pds/template.toml | 36 +++++++++++++++++ meta.json | 18 +++++++++ 4 files changed, 105 insertions(+) create mode 100644 blueprints/bluesky-pds/bluesky-pds.svg create mode 100644 blueprints/bluesky-pds/docker-compose.yml create mode 100644 blueprints/bluesky-pds/template.toml diff --git a/blueprints/bluesky-pds/bluesky-pds.svg b/blueprints/bluesky-pds/bluesky-pds.svg new file mode 100644 index 000000000..77ebea072 --- /dev/null +++ b/blueprints/bluesky-pds/bluesky-pds.svg @@ -0,0 +1,3 @@ + + + diff --git a/blueprints/bluesky-pds/docker-compose.yml b/blueprints/bluesky-pds/docker-compose.yml new file mode 100644 index 000000000..4f9ae9a2b --- /dev/null +++ b/blueprints/bluesky-pds/docker-compose.yml @@ -0,0 +1,48 @@ +services: + pds: + image: 'ghcr.io/bluesky-social/pds:0.4.182' + volumes: + - pds-data:/pds + environment: + - SERVICE_URL_PDS_3000 + - PDS_HOSTNAME + - PDS_JWT_SECRET + - PDS_ADMIN_PASSWORD + - 'PDS_ADMIN_EMAIL=${PDS_ADMIN_EMAIL}' + - PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX + - 'PDS_DATA_DIRECTORY=${PDS_DATA_DIRECTORY:-/pds}' + - 'PDS_BLOBSTORE_DISK_LOCATION=${PDS_DATA_DIRECTORY:-/pds}/blocks' + - 'PDS_BLOB_UPLOAD_LIMIT=${PDS_BLOB_UPLOAD_LIMIT:-104857600}' + - 'PDS_DID_PLC_URL=${PDS_DID_PLC_URL:-https://plc.directory}' + - 'PDS_EMAIL_FROM_ADDRESS=${PDS_EMAIL_FROM_ADDRESS}' + - 'PDS_EMAIL_SMTP_URL=${PDS_EMAIL_SMTP_URL}' + - 'PDS_BSKY_APP_VIEW_URL=${PDS_BSKY_APP_VIEW_URL:-https://api.bsky.app}' + - 'PDS_BSKY_APP_VIEW_DID=${PDS_BSKY_APP_VIEW_DID:-did:web:api.bsky.app}' + - 'PDS_REPORT_SERVICE_URL=${PDS_REPORT_SERVICE_URL:-https://mod.bsky.app/xrpc/com.atproto.moderation.createReport}' + - 'PDS_REPORT_SERVICE_DID=${PDS_REPORT_SERVICE_DID:-did:plc:ar7c4by46qjdydhdevvrndac}' + - 'PDS_CRAWLERS=${PDS_CRAWLERS:-https://bsky.network}' + - 'LOG_ENABLED=${LOG_ENABLED:-true}' + command: | + sh -c ' + set -euo pipefail + echo "Installing required packages and pdsadmin..." + apk add --no-cache openssl curl bash jq coreutils gnupg util-linux-misc >/dev/null + curl -o /usr/local/bin/pdsadmin.sh https://raw.githubusercontent.com/bluesky-social/pds/main/pdsadmin.sh + chmod 700 /usr/local/bin/pdsadmin.sh + ln -sf /usr/local/bin/pdsadmin.sh /usr/local/bin/pdsadmin + echo "Creating an empty pds.env file so pdsadmin works..." + touch /pds/pds.env + echo "Launching PDS, enjoy!..." + exec node --enable-source-maps index.js + ' + healthcheck: + test: + - CMD + - wget + - '--spider' + - 'http://127.0.0.1:3000/xrpc/_health' + interval: 5s + timeout: 10s + retries: 10 +volumes: + pds-data: \ No newline at end of file diff --git a/blueprints/bluesky-pds/template.toml b/blueprints/bluesky-pds/template.toml new file mode 100644 index 000000000..3de5b2019 --- /dev/null +++ b/blueprints/bluesky-pds/template.toml @@ -0,0 +1,36 @@ +[variables] +main_domain = "${domain}" +admin_password = "${password:32}" +jwt_secret = "${jwt:32}" +rotation_key = "${jwt:32}" +data_directory = "/pds" +blob_upload_limit = "104857600" +log_enabled = "true" + +[config] +mounts = [] +env = [ + "PDS_ADMIN_PASSWORD=${admin_password}", + "PDS_HOSTNAME=${main_domain}", + "PDS_JWT_SECRET=${jwt_secret}", + "PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX=${rotation_key}", + "PDS_ADMIN_EMAIL=", + "PDS_EMAIL_FROM_ADDRESS=", + "PDS_EMAIL_SMTP_URL=", + "PDS_DATA_DIRECTORY=${data_directory}", + "PDS_BLOBSTORE_DISK_LOCATION=${data_directory}/blocks", + "PDS_BLOB_UPLOAD_LIMIT=${blob_upload_limit}", + "PDS_DID_PLC_URL=https://plc.directory", + "PDS_BSKY_APP_VIEW_URL=https://api.bsky.app", + "PDS_BSKY_APP_VIEW_DID=did:web:api.bsky.app", + "PDS_REPORT_SERVICE_URL=https://mod.bsky.app/xrpc/com.atproto.moderation.createReport", + "PDS_REPORT_SERVICE_DID=did:plc:ar7c4by46qjdydhdevvrndac", + "PDS_CRAWLERS=https://bsky.network", + "LOG_ENABLED=${log_enabled}" +] + +[[config.domains]] +serviceName = "pds" +port = 3000 +host = "${main_domain}" +path = "/" \ No newline at end of file diff --git a/meta.json b/meta.json index 1c558d829..6dbc14d96 100644 --- a/meta.json +++ b/meta.json @@ -750,6 +750,24 @@ "nextjs" ] }, + { + "id": "bluesky-pds", + "name": "Bluesky PDS", + "version": "0.4.182", + "description": "Bluesky PDS is a personal data server for Bluesky.", + "logo": "bluesky-pds.svg", + "links": { + "github": "https://github.com/bluesky-social/pds", + "website": "https://bsky.social/about", + "docs": "https://github.com/bluesky-social/pds" + }, + "tags": [ + "bluesky", + "pds", + "data", + "server" + ] + }, { "id": "bolt.diy", "name": "bolt.diy", From efea22e1f0e9b4f95f5595ce7ef7bf7bd7e9f92d Mon Sep 17 00:00:00 2001 From: Vidhya LKG for IT <24915474+VidhyaSanjeevi@users.noreply.github.com> Date: Mon, 15 Dec 2025 10:39:54 +0530 Subject: [PATCH 48/91] Add dokploy-prom-monitoring-extension template with comprehensive tests and documentation (#548) * Add dokploy-prom-monitoring-extension template with comprehensive tests and documentation * Fix METRICS_CONFIG environment variable: use single-line JSON format * Fix template.toml: use correct [config.env] syntax for environment variables * Fix docker-compose.yml: add env_file reference to load environment variables * Delete blueprints/dokploy-prom-monitoring-extension/README.md * Delete test-dokploy-prom-monitoring-extension.sh --------- Co-authored-by: Sanjeevi Subramani Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> --- .../docker-compose.yml | 11 ++++++++++ .../logo.svg | 21 +++++++++++++++++++ .../template.toml | 17 +++++++++++++++ meta.json | 20 ++++++++++++++++++ 4 files changed, 69 insertions(+) create mode 100644 blueprints/dokploy-prom-monitoring-extension/docker-compose.yml create mode 100644 blueprints/dokploy-prom-monitoring-extension/logo.svg create mode 100644 blueprints/dokploy-prom-monitoring-extension/template.toml diff --git a/blueprints/dokploy-prom-monitoring-extension/docker-compose.yml b/blueprints/dokploy-prom-monitoring-extension/docker-compose.yml new file mode 100644 index 000000000..1fd3e03d7 --- /dev/null +++ b/blueprints/dokploy-prom-monitoring-extension/docker-compose.yml @@ -0,0 +1,11 @@ +services: + dokploy-monitoring: + image: dokploy/monitoring:canary + restart: unless-stopped + env_file: + - .env + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - monitoring-data:/app/data +volumes: + monitoring-data: {} diff --git a/blueprints/dokploy-prom-monitoring-extension/logo.svg b/blueprints/dokploy-prom-monitoring-extension/logo.svg new file mode 100644 index 000000000..5c6a02e42 --- /dev/null +++ b/blueprints/dokploy-prom-monitoring-extension/logo.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + diff --git a/blueprints/dokploy-prom-monitoring-extension/template.toml b/blueprints/dokploy-prom-monitoring-extension/template.toml new file mode 100644 index 000000000..bd0c07b51 --- /dev/null +++ b/blueprints/dokploy-prom-monitoring-extension/template.toml @@ -0,0 +1,17 @@ +[variables] +main_domain = "${domain}" +monitoring_token = "${password:32}" +callback_url = "http://dokploy:3000/api/trpc/notification.receiveNotification" +server_type = "Dokploy" +refresh_rate = "30" +retention_days = "7" +cpu_threshold = "80" +memory_threshold = "85" + +[[config.domains]] +serviceName = "dokploy-monitoring" +port = 3001 +host = "${main_domain}" + +[config.env] +METRICS_CONFIG = "{\"server\":{\"refreshRate\":${refresh_rate},\"port\":3001,\"type\":\"${server_type}\",\"token\":\"${monitoring_token}\",\"urlCallback\":\"${callback_url}\",\"retentionDays\":${retention_days},\"cronJob\":\"0 0 * * *\",\"thresholds\":{\"cpu\":${cpu_threshold},\"memory\":${memory_threshold}},\"prometheus\":{\"enabled\":true}},\"containers\":{\"refreshRate\":${refresh_rate},\"services\":{\"include\":[],\"exclude\":[]}}}" diff --git a/meta.json b/meta.json index 6dbc14d96..10956c5eb 100644 --- a/meta.json +++ b/meta.json @@ -1830,6 +1830,26 @@ "document-signing" ] }, + { + "id": "dokploy-prom-monitoring-extension", + "name": "Dokploy Prometheus Monitoring Extension", + "version": "canary", + "description": "Dokploy monitoring extension with Prometheus metrics export for external monitoring systems like Grafana Cloud. Tracks server and container metrics with configurable thresholds and alerting.", + "logo": "logo.svg", + "links": { + "github": "https://github.com/Dokploy/dokploy", + "website": "https://dokploy.com/", + "docs": "https://docs.dokploy.com/" + }, + "tags": [ + "monitoring", + "prometheus", + "metrics", + "observability", + "docker", + "grafana" + ] + }, { "id": "domain-locker", "name": "Domain Locker", From 32da868c010f256da93763db8b44129e271284ce Mon Sep 17 00:00:00 2001 From: Muzaffer Kadir YILMAZ <34358176+muzafferkadir@users.noreply.github.com> Date: Mon, 15 Dec 2025 08:23:05 +0300 Subject: [PATCH 49/91] feat: improve RustDesk template configuration (#571) * feat: improve RustDesk template configuration - Add comprehensive environment variables for RustDesk server - Add RELAY_HOST, API_SERVER, ID_SERVER, and ENCRYPTION_KEY variables - Follow Dokploy best practices (no container_name, proper port format) - Use restart: unless-stopped policy - Add encryption key generation with password helper * fix: use explicit port mapping for RustDesk services RustDesk requires explicit port bindings (host:container format) to function properly. The service uses specific ports for: - 21115-21116 (TCP/UDP): hbbs service for ID and NAT traversal - 21117-21119 (TCP): hbbr relay service Without explicit port mapping, RustDesk clients cannot establish connections to the server. This is an exception to Dokploy's general port guidelines due to RustDesk's specific networking requirements. --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> --- blueprints/rustdesk/docker-compose.yml | 4 ++-- blueprints/rustdesk/template.toml | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/blueprints/rustdesk/docker-compose.yml b/blueprints/rustdesk/docker-compose.yml index a1985ce5f..a29d64d46 100644 --- a/blueprints/rustdesk/docker-compose.yml +++ b/blueprints/rustdesk/docker-compose.yml @@ -9,7 +9,6 @@ services: - "21115:21115" - "21116:21116" - "21116:21116/udp" - - "21118:21118" depends_on: - hbbr @@ -21,7 +20,8 @@ services: - rustdesk-data:/root ports: - "21117:21117" + - "21118:21118" - "21119:21119" volumes: - rustdesk-data: {} + rustdesk-data: diff --git a/blueprints/rustdesk/template.toml b/blueprints/rustdesk/template.toml index 080144c42..7f44194f2 100644 --- a/blueprints/rustdesk/template.toml +++ b/blueprints/rustdesk/template.toml @@ -1,9 +1,14 @@ [variables] server_domain = "${domain}" +encryption_key = "${password:32}" [config] [config.env] +RELAY_HOST = "${server_domain}" RUSTDESK_RELAY_SERVER = "${server_domain}:21117" +RUSTDESK_API_SERVER = "http://${server_domain}:21118" +RUSTDESK_ID_SERVER = "${server_domain}:21116" +ENCRYPTION_KEY = "${encryption_key}" [[config.mounts]] \ No newline at end of file From be933bdc54f0b9707a61886c858fa05f0f44fd96 Mon Sep 17 00:00:00 2001 From: Muzaffer Kadir YILMAZ <34358176+muzafferkadir@users.noreply.github.com> Date: Mon, 15 Dec 2025 08:27:26 +0300 Subject: [PATCH 50/91] feat: add Mumble voice chat server template (#572) * feat: add Mumble voice chat server template - Add Mumble VoIP server blueprint with docker-compose.yml - Configure environment variables for superuser password, welcome text, and max users - Add template.toml with auto-generated secure password - Follow Dokploy best practices (no container_name, proper port format) - Add Mumble metadata to meta.json with proper tags - Support for TCP and UDP on port 64738 * Update template.toml * fix: correct JSON formatting in meta.json for Mumble template entry --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Co-authored-by: Mauricio Siu --- blueprints/mumble/docker-compose.yml | 16 ++++++++++++++++ blueprints/mumble/mumble.png | Bin 0 -> 2388 bytes blueprints/mumble/template.toml | 12 ++++++++++++ meta.json | 18 ++++++++++++++++++ 4 files changed, 46 insertions(+) create mode 100644 blueprints/mumble/docker-compose.yml create mode 100644 blueprints/mumble/mumble.png create mode 100644 blueprints/mumble/template.toml diff --git a/blueprints/mumble/docker-compose.yml b/blueprints/mumble/docker-compose.yml new file mode 100644 index 000000000..92580f1aa --- /dev/null +++ b/blueprints/mumble/docker-compose.yml @@ -0,0 +1,16 @@ +services: + mumble-server: + image: mumblevoip/mumble-server:latest + restart: unless-stopped + ports: + - 64738 + - 64738/udp + volumes: + - mumble-data:/data + environment: + - MUMBLE_SUPERUSER_PASSWORD=${SUPERUSER_PASSWORD} + - MUMBLE_CONFIG_WELCOMETEXT=${WELCOME_TEXT} + - MUMBLE_CONFIG_USERS=${MAX_USERS} + +volumes: + mumble-data: diff --git a/blueprints/mumble/mumble.png b/blueprints/mumble/mumble.png new file mode 100644 index 0000000000000000000000000000000000000000..dcd1084fcb496977578b68b792ab9638063399aa GIT binary patch literal 2388 zcmV-a39I&rP)x=sHmt>QBm;l@MUFX7byXm6es#)6=-PxOaDVl9G~dZ*RrL#eRN%CnqPGnwm#PM~aGyPft%478W8RA~`uZ z85tQiHa0*&K*q+#yu7>*4-X?9;F$mb2$4xdK~#90?Vam-qA(DK1@TaeiXvF?P-|<~ z-v13QB6Q2-#6UPoWv8Iv=`jDcrc~RYeLG?P?m8#08B$T{{l>##?T)nD`r-4va(RrpGr6elenB zx&lKS6yaq)4_*ahB*Q~--b?uYm=A!FG>Y(Ad=}1m;|Q~T2Ct5$<+Uz^&(+6sP6kE? zH*7^Zh1E4JgoE(PWMQ()Sjln+giCo+fq;-VZ(Q{P!xfYHNPMw1A)Cu*4=8*wbGnP9 zVC@M9%({rNH<1}F*XF7E2*)rVW0OVrx2;ca^bs9ZyN8PmS_wgYG4vvWnllEMwGzS+zLDW~ zSkLncuWKcQ)zrv3I2d&^vv0kGV6p3_4MU==o+q=`rm>w({6?Eu zT?JNFnAHWngrG;9Xv2^QZ1?p{IIX9q+{)4+(Bbz?2!@EiLCDN#9ARDZaj6xAFY?q> zgkX;72(`!&>LmoDU`MD$ju1yETJx^t2>X!U9^Xd7i6gAZMnYhsp?DALvrQ`rH}ceM zBm{Ou{~;mR;#btt`aYJGgo>7?PROolQzYEiucIvY-k9qY3Bh&6>R8>r^n zzspmr*J(RyDYW{PZ>H3ZN?2P2iiFVEM{3Fr7Mj?XmLegvmy#9=81Auqyc7xH{-SL2 z9c~Y@UnNNh{bwZBT4037cK9?283TS{ryqbXaz=P030d1y!LA=;JqtI?2Sf=u9tPau zH)Es9v$UW|$T1*3Fx%7V7xN{~BP2@5b*1sP>2O^dp@zxv!hR#>Y-?ow} z;o=QX$g87t2^DUIJPEuF!b22A|BPzP5^jQAuL7%9ETYIzX1(8SxBGczX=ao#D9lXX zM0UGlp69brA`|YaSw3#}XM=m)%M~ZFUs*%Ps9Xo3F;``5vfNy^$KgfM$8 z{+#o2<@YT9=IWyvpv1WOo3OBPdD{j3o9<0#_nzAG{BV;0%>R{oL2#X4fVMKj-(iu7 z=LgCCm;2Xe{n`l+Ck5U;vvCu`{5i_&>f|PWDBpzB$1iutT`0Y!i^5%rhak)~&En(c z7ze>Th#v~(X__U;T3zKB!UA&HQ(cY!@u3L2h)0;uqzTeNS=1Nt^_sr^yj& z%S)REVcs!n==)N0`2@@tm$nG0sl!?-%L+JVY z4(SW)C?!I#ORu<>Buj{paS&{*wK!EugkF1}y~q~Aua>DNl0-=ndTo8YPxcV9wp573 zZ6Iv?J)|K?hzM8rY$qY&ppXBb@ZC;AL@l-SJw&uL%2gVg zgoyso(jFf%rm=L?#&z%GYhx!N6Ao7_fwdlWLd52br88i}9KT6&*hH6A7H51iw=I8!8S?PO_*XBX1$vL?yeHzmRjF6&l!SYjuJb%~dT$q?ce z5m~VzKgbbwJO+93CPLFj+z38lnsZ72}x zJH?u3=0<=}XQU{SQIz~IouRPU+3>Fu`d7Vy_cqj`nfVjlkp(kS5$Sq4M}B-J6IIan zul=0Q9ngpj>G$#g9}4%aqI-7HH(@m!zlW^tGY;)^kB!jUQ5E5_Fk;Fxzw{nwcM%tX zb92Hc6NFX3^0J>LUhe;q#eaq~^-qzuw;riQlGBK|C2siMKSYu{}B zp*#=dnSZhX6)j$T#ydR!WTa*sml5@i1Y>WnjO8mKRY6y7e7>^{6||5d){UR0 zbB;F|4d?GQDbk zc#7$dR*+g`FT3Xg?-I@C|JCDja&mHVa&mHVa&mHVBCo&N2A0>Q<{9z;0000 Date: Mon, 15 Dec 2025 08:28:39 +0300 Subject: [PATCH 51/91] fix: update WireGuard Easy template for proper functionality (#573) * fix: update WireGuard Easy template for proper functionality - Changed to named volume (etc_wireguard) instead of host path mount - Added explicit port mappings (51820:51820/udp, 51821:51821/tcp) required for WireGuard - Updated environment variables to use correct WG_HOST and PASSWORD format - Added all required WireGuard environment variables: - WG_PORT, PORT, WG_MTU, WG_DEFAULT_DNS, WG_ALLOWED_IPS - WG_POST_UP/WG_POST_DOWN for iptables rules - Added NET_RAW capability for proper network operations - Simplified template.toml to use WIREGUARD_HOST and WIREGUARD_PASSWORD - Removed explicit networks config to enable Dokploy's isolated deployment - Template now works with Dokploy's automatic network isolation This configuration has been tested and confirmed working with isolated deployment enabled. * Update template.toml --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> --- blueprints/wg-easy/docker-compose.yml | 23 ++++++++++++++++++----- blueprints/wg-easy/template.toml | 18 +++--------------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/blueprints/wg-easy/docker-compose.yml b/blueprints/wg-easy/docker-compose.yml index b5d48fe17..1161bb123 100644 --- a/blueprints/wg-easy/docker-compose.yml +++ b/blueprints/wg-easy/docker-compose.yml @@ -1,17 +1,30 @@ -version: "3.8" +volumes: + etc_wireguard: + services: wg-easy: image: ghcr.io/wg-easy/wg-easy:15 + restart: unless-stopped + environment: + - WG_HOST=${WIREGUARD_HOST} + - PASSWORD=${WIREGUARD_PASSWORD} + - WG_PORT=51820 + - PORT=51821 + - WG_MTU=1280 + - WG_DEFAULT_DNS=1.1.1.1,8.8.8.8 + - WG_ALLOWED_IPS=0.0.0.0/0 + - WG_POST_UP=iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; + - WG_POST_DOWN=iptables -t nat -D POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE; iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; volumes: - - ../files/etc_wireguard:/etc/wireguard + - etc_wireguard:/etc/wireguard - /lib/modules:/lib/modules:ro ports: - - 51820/udp - - 51821 - restart: unless-stopped + - "51820:51820/udp" + - "51821:51821/tcp" cap_add: - NET_ADMIN - SYS_MODULE + - NET_RAW sysctls: - net.ipv4.ip_forward=1 - net.ipv4.conf.all.src_valid_mark=1 diff --git a/blueprints/wg-easy/template.toml b/blueprints/wg-easy/template.toml index 8b064ab73..b5d716b17 100644 --- a/blueprints/wg-easy/template.toml +++ b/blueprints/wg-easy/template.toml @@ -4,22 +4,10 @@ wg_password = "${password:32}" [config] [[config.domains]] -serviceName = "wg-easy" # Matches the service name in docker-compose.yml +serviceName = "wg-easy" port = 51821 host = "${main_domain}" [config.env] -# WG_HOST is required for the WG-Easy web interface to know the public hostname -WG_HOST = "${main_domain}" -# PASSWORD is used to secure the WG-Easy web interface -PASSWORD = "${wg_password}" -# Optional: PORT is set to match the exposed port -PORT = "51821" -# Optional: HOST ensures the service listens on all interfaces -HOST = "${main_domain}" -# Optional: INSECURE set to false for security -INSECURE = "false" - -[[config.mounts]] -filePath = "/etc/wireguard" -content = "" +WIREGUARD_HOST = "${main_domain}" +WIREGUARD_PASSWORD = "${wg_password}" From 54eccbe32bdb17f7a1f8bd63c99be9249f924e99 Mon Sep 17 00:00:00 2001 From: Jemg Date: Mon, 15 Dec 2025 00:30:03 -0500 Subject: [PATCH 52/91] add: restart policy to MinIO service (#576) restart: unless-stopped is a Docker restart policy that automatically restarts a container if it stops due to an error or Docker daemon restart --- blueprints/minio/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/minio/docker-compose.yml b/blueprints/minio/docker-compose.yml index 0e14a430c..a25e3aeec 100644 --- a/blueprints/minio/docker-compose.yml +++ b/blueprints/minio/docker-compose.yml @@ -1,10 +1,10 @@ -version: "3.8" services: minio: # after RELEASE.2025-04-22T22-12-26Z, minio removed most of the admin UI, if you want to use the admin UI, uncomment the line below # image: minio/minio:RELEASE.2025-04-22T22-12-26Z # if you uncommented the line above, comment the line below image: minio/minio + restart: unless-stopped volumes: # by default, the MinIO container will use a volume named minio-data # to store its data. This volume is created automatically by Docker. From 726a8c6bc91f934b908bd3ae8b91b9b789bdc14f Mon Sep 17 00:00:00 2001 From: Jainil Prajapati <86187588+jaainil@users.noreply.github.com> Date: Mon, 15 Dec 2025 11:49:57 +0530 Subject: [PATCH 53/91] Updating copilot instructions.md and AGENTS.md (#452) * docs: add copilot instructions for project development Added comprehensive documentation file (.github/copilot-instructions.md) that provides detailed guidance on the Dokploy Open Source Templates project structure, development workflow, and conventions. The document covers the project overview, key files and directories, development workflow for adding/updating templates, local development setup, CI/CD processes, and established conventions and patterns. This documentation will help onboard new contributors and ensure consistent development practices across the project. * docs(copilot-instructions): enhance project overview and development guide Updated copilot instructions to reflect expanded app capabilities (200+ apps, TypeScript integration, Fuse.js search) and detailed processes for templates, local dev, and CI/CD for improved clarity and accuracy. * refactor: remove frontend development instructions from AGENTS.md and update contributing guidelines - Removed frontend development commands from AGENTS.md to streamline the document. - Updated CONTRIBUTING.md by removing the recommendation to set `restart: unless-stopped` for services. --------- Co-authored-by: Mauricio Siu --- .github/copilot-instructions.md | 13 +- AGENTS.md | 212 ++++++++++++++++++++++++++++++++ CONTRIBUTING.md | 1 - 3 files changed, 214 insertions(+), 12 deletions(-) create mode 100644 AGENTS.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 597c33bf1..c0669f2fb 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -8,7 +8,6 @@ Key components: - **Blueprints**: Self-contained templates with `docker-compose.yml` (service definitions) and `template.toml` (Dokploy-specific configuration for domains, env vars, mounts). - **meta.json**: Centralized index of all templates, aggregated from blueprint metadata. Entries include `id`, `name`, `version`, `description`, `logo`, `links`, and `tags`. -- **app/**: Vite-based React frontend for local preview/development (runs at http://localhost:5173). Copies blueprints and meta.json to dist during build. - **Scripts**: Node.js tools in root and `build-scripts/` for maintaining `meta.json` (deduplication, sorting, validation). Data flow: New templates added to `blueprints/` → Metadata updated in `meta.json` → Processing scripts ensure consistency → App builds include static blueprints/meta for preview. @@ -22,7 +21,6 @@ The "why": Enables rapid, standardized deployment of 200+ OSS apps on Dokploy wi - `docker-compose.yml`: Standard Docker Compose v3.8. Avoid `ports`, `container_name`, `networks`—Dokploy handles isolation via internal networks. - `template.toml`: Defines variables (e.g., `${domain}`), domains (service:port → host), env vars, and mounts. Use helpers like `${password:32}`, `${uuid}`, `${jwt:secret_var}`. - `logo.svg/png`: Service icon, referenced in `meta.json`. -- `app/vite.config.ts`: Configures build to copy `blueprints/*` and `meta.json` to dist root for static serving. - `dedupe-and-sort-meta.js`: Standalone script—reads `meta.json`, removes duplicate `id`s (keeps first), sorts by `id` (case-insensitive), creates timestamped backup. - `build-scripts/process-meta.js`: Advanced processor with CLI options (`--verbose`, `--no-backup`, `--input`/`--output`), JSON schema validation (required: `id`, `name`, `version`, `description`, `links.github`, `logo`, `tags` array). @@ -39,14 +37,7 @@ Exemplary blueprint: `blueprints/ghost/`—`docker-compose.yml` exposes port 236 - Run `node dedupe-and-sort-meta.js --backup` to validate/sort. - Commit; PR triggers Dokploy preview (base64 import for testing). -2. **Local Development**: - - - App: `cd app && pnpm install && pnpm dev` (Vite dev server). - - Meta processing: `npm run process-meta` or `make process-meta` (uses Makefile targets: `validate`, `check`, `build`). - - Build app: `cd app && pnpm build`—copies blueprints/meta to `dist/` for static hosting. - - Test template: Use PR preview URL or local Dokploy instance; import base64 from template card. - -3. **CI/CD**: +2. **CI/CD**: - `.github/workflows/validate-meta.yml` (if present): Runs validation on push/PR—fails on duplicates, invalid JSON, missing fields. - Integrate processing: Add `npm run process-meta` to build steps; use `--no-backup` in CI. @@ -67,7 +58,7 @@ No tests in repo—focus on manual validation via scripts and Dokploy deploys. D - **Versions**: Pin images to specific versions in `docker-compose.yml` (e.g., `ghost:5.82.0-alpine`); match in `meta.json.version`. - **Logos**: SVG preferred; size ~128x128; file name in `meta.json.logo` (e.g., "ghost.svg"). -Cross-component: No runtime communication—templates independent. App consumes static blueprints/meta for UI rendering (e.g., search, cards via React components in `app/src/`). +Cross-component: Templates are independent and ship as static blueprints/meta. ## Integration Points diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..c34d6beb4 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,212 @@ +# AGENTS.md: AI Collaboration Guide + +This document provides essential context for AI models interacting with this project. Adhering to these guidelines will ensure consistency and maintain code quality. + +## 1. Project Overview & Purpose + +- **Primary Goal:** This is the official repository for Dokploy Open Source Templates, maintaining Docker Compose templates for deploying 200+ open-source applications via Dokploy (a self-hosted PaaS alternative to Heroku). The project enables rapid, standardized deployment of OSS applications without manual configuration. +- **Business Domain:** Self-hosted Platform as a Service (PaaS), DevOps tooling, containerized application deployment, and open-source software distribution. + +## 2. Core Technologies & Stack + +- **Languages:** JavaScript (Node.js), TypeScript (React frontend), YAML (Docker Compose), TOML (template configuration), JSON (metadata), Shell (build scripts). +- **Frameworks & Runtimes:** Node.js runtime, Vite 5.x (for React 19.x frontend), Docker Compose v3.8+, React Router 7.x. +- **Databases:** No direct database usage in core project (templates may include various databases like PostgreSQL, MySQL, Redis, etc.). +- **Key Libraries/Dependencies:** + - **Core:** `nodemon` for development, custom Node.js scripts for meta processing. + - **Frontend:** `fuse.js` (fuzzy search), `zustand` (state management), `@iarna/toml` (TOML parsing), `shadcn/ui` components, `@radix-ui` primitives, `@codemirror` (code editor), `react-router-dom`, `tailwindcss`. + - **Build:** `vite-plugin-static-copy` for copying blueprints to dist. +- **Platforms:** Linux (primary), Docker containers, Dokploy PaaS, Web browsers (for frontend preview), Cloudflare Pages (for PR previews). +- **Package Manager:** npm for root project, pnpm for frontend (`app/` directory). + +## 3. Architectural Patterns + +- **Overall Architecture:** Template collection and management system with decentralized blueprint architecture. Each blueprint is a self-contained Docker Compose application with Dokploy-specific configuration. +- **Directory Structure Philosophy:** + - `/blueprints`: Contains all deployable application templates, each as a subdirectory with `docker-compose.yml`, `template.toml`, and logo files. Each blueprint is independent with no shared state. + - `/app`: Vite + React + TypeScript frontend for local preview and development. + - `/src/components`: React components (UI components in `/ui` subdirectory using shadcn/ui). + - `/src/hooks`: Custom React hooks (e.g., `useFuseSearch.ts` for fuzzy search). + - `/src/store`: Zustand state management store. + - `/src/lib`: Utility functions and helpers. + - `/build-scripts`: Advanced meta.json processing tools with CLI options and JSON schema validation. + - `/.github/workflows`: CI/CD workflows for validation, preview builds, and deployments. + - Root level: Core processing scripts (`dedupe-and-sort-meta.js`), metadata index (`meta.json`), build configuration (`Makefile`, `package.json`). +- **Module Organization:** Node.js scripts for metadata processing, React components for frontend UI, Docker Compose files for service definitions, TOML files for Dokploy configuration. Frontend uses component-based architecture with hooks for business logic and Zustand for global state. + +## 4. Coding Conventions & Style Guide + +- **Formatting:** JavaScript follows standard conventions with 2-space indentation. TypeScript in frontend uses ESLint configuration. YAML uses 2-space indentation. TOML files use standard formatting. +- **Naming Conventions:** + - Variables, functions: camelCase (`myVariable`, `myFunction`) + - React components: PascalCase (`TemplateGrid`, `SearchBar`) + - Constants: SCREAMING_SNAKE_CASE (`MAX_BUFFER_SIZE`) + - Template IDs: lowercase, kebab-case (`activepieces`, `ghost`) - **MUST** be unique across repository + - Files: snake_case for scripts (`dedupe-and-sort-meta.js`), PascalCase for React components (`TemplateGrid.tsx`), kebab-case for directories (`build-scripts`) + - Docker services: **MUST** match blueprint folder name exactly (e.g., `ghost` service in `blueprints/ghost/`) +- **API Design:** + - **Backend:** Procedural scripting approach with CLI interfaces. Template system uses declarative configuration over imperative code. + - **Frontend:** Component-based React architecture with hooks for logic separation. Zustand for centralized state management. Custom hooks encapsulate complex logic (e.g., `useFuseSearch` for search functionality). +- **Common Patterns & Idioms:** + - **Metaprogramming:** Minimal use of advanced JavaScript features in scripts, focusing on simple, maintainable code. + - **Memory Management:** Relies on Node.js garbage collection and React's automatic memory management. + - **Polymorphism:** Uses JavaScript prototype-based objects and functional programming patterns. React components use composition over inheritance. + - **Type Safety:** + - Backend scripts: JavaScript with JSDoc comments for documentation. + - Frontend: TypeScript with strict type checking enabled. + - **Concurrency:** + - Backend: Synchronous processing model for meta.json operations. + - Frontend: React's concurrent rendering, `useDeferredValue` for debouncing, `useEffect` for async operations. + - **State Management:** Zustand store pattern with selectors for optimal re-renders. URL params synced with search state via React Router. +- **Error Handling:** + - Backend: Node.js error-first callback pattern and try-catch blocks. Scripts validate JSON structure and fail fast on errors. + - Frontend: Error boundaries for React components, try-catch for async operations, user-friendly error messages via toast notifications. + +## 5. Key Files & Entrypoints + +- **Main Entrypoints:** + - **Backend:** `dedupe-and-sort-meta.js` - Primary script for processing meta.json file. + - **Frontend:** `app/src/main.tsx` - React application entry point. +- **Configuration:** + - `package.json` - Root Node.js project configuration and npm scripts. + - `app/package.json` - Frontend dependencies and pnpm scripts. + - `meta.json` - Centralized template registry (200+ entries). + - `Makefile` - Build automation with targets for processing, validation, and cleanup. + - `app/vite.config.ts` - Vite build configuration with static copy plugin. + - `app/tsconfig.json` - TypeScript compiler configuration. +- **CI/CD Pipeline:** + - `.github/workflows/validate-meta.yml` - Validates meta.json structure, duplicates, and sort order. + - `.github/workflows/build-preview.yml` - Builds preview deployments for PRs. + - `.github/workflows/deploy-preview.yml` - Deploys previews to Cloudflare Pages. + +## 6. Development & Testing Workflow + +- **Local Development Environment:** + - **Install dependencies** + + ```bash + # Root project + npm install + + # Frontend (uses pnpm) + cd app && pnpm install + ``` + + - **Process meta.json** (CRITICAL: Run after ANY meta.json edits) + ```bash + npm run process-meta + # or + make process-meta + # or + node dedupe-and-sort-meta.js + ``` + - **Validate without modifying** + ```bash + npm run validate-meta + # or + make validate + ``` + - **Quick check for duplicates/sort status** + ```bash + make check + ``` + - **Clean backup files** + ```bash + make clean + ``` +- **Task Configuration:** + - **NPM Scripts:** Run `npm run` to list available scripts. Key scripts: + - `process-meta`: Remove duplicates and sort meta.json + - `process-meta-verbose`: Process with detailed output + - `validate-meta`: Validate structure without changes + - **Makefile Targets:** Run `make help` to list targets. Key targets: + - `process-meta`: Process meta.json + - `validate`: Validate without modifying + - `check`: Quick duplicate/sort check + - `build`: Full build process + - `clean`: Remove backup files +- **Testing:** No formal unit testing framework. Validation occurs through: + - Script-based validation of meta.json structure and schema + - Manual testing via Dokploy preview deployments (import base64 from PR preview) + - JSON schema validation in `build-scripts/process-meta.js` + - TypeScript compilation errors caught during build + - ESLint for code quality in frontend +- **CI/CD Process:** + - **On meta.json changes:** Validates structure, checks for duplicates, verifies sort order, compares processed vs original. + - **On PR creation:** Builds preview deployment, generates base64 import for testing in Dokploy. + - **Preview testing:** Use PR description link → Search template → Copy base64 → Import in Dokploy instance. + +## 7. Specific Instructions for AI Collaboration + +- **Contribution Guidelines:** + - Follow existing Dokploy template structure strictly. Each blueprint must be independent with no shared state. + - **CRITICAL:** Always run `node dedupe-and-sort-meta.js` or `npm run process-meta` after ANY meta.json edits. + - Test templates in Dokploy preview before submitting PRs (use base64 import from PR preview). + - Add logo file (SVG preferred, ~128x128px) to blueprint folder. + - Ensure template `id` in meta.json exactly matches blueprint folder name (lowercase kebab-case). +- **Docker Compose Conventions (CRITICAL):** + + - **Version:** MUST be `3.8` + - **NEVER include:** `ports` (use `expose` only), `container_name`, `networks` (Dokploy handles isolation) + - **ALWAYS include:** `restart: unless-stopped` or `restart: always`, persistent volumes + - **Service naming:** MUST match blueprint folder name exactly + - **Example:** + ```yaml + version: "3.8" + services: + ghost: + image: ghost:6-alpine + restart: always + volumes: + - ghost:/var/lib/ghost/content + volumes: + ghost: + ``` + +- **template.toml Conventions:** + + - **Variables:** Define in `[variables]` section, use helpers for secrets + - **Domains:** `[[config.domains]]` with `serviceName`, `port`, `host` (path optional) + - **Env:** Array of strings: `env = ["KEY=VALUE", "DB_PASSWORD=${db_pass}"]` + - **Available helpers:** `${domain}`, `${password:length}`, `${base64:length}`, `${hash:length}`, `${uuid}`, `${randomPort}`, `${email}`, `${username}`, `${timestamp}`, `${timestamps:datetime}`, `${timestampms:datetime}`, `${jwt:secret_var:payload_var}` + - **JWT helper example:** `${jwt:mysecret:mypayload}` with payload containing `exp: ${timestamps:2030-01-01T00:00:00Z}` + +- **meta.json Requirements:** + + - **Required fields:** `id`, `name`, `version`, `description`, `links` (with `github`), `logo`, `tags` (array) + - **Tags:** Lowercase strings (e.g., `["monitoring", "database"]`) + - **Version:** MUST match Docker image version in docker-compose.yml + - **Logo:** Filename only (e.g., `"ghost.jpeg"`), file must exist in blueprint folder + +- **Frontend Development:** + + - Use TypeScript with strict type checking + - Follow React hooks patterns, avoid class components + - Use Zustand selectors for state access to optimize re-renders + - Fuse.js searches across `name`, `description`, `tags`, `id` fields + - Use shadcn/ui components for consistency + - Sync URL params with search state via React Router + +- **Security:** + + - Be mindful of security when handling template configurations + - NEVER hardcode secrets in templates - use Dokploy's variable system with helpers + - Pin Docker images to specific versions to avoid supply chain attacks + - Validate user input in frontend before processing + +- **Dependencies:** + + - When adding new templates, ensure Docker images are pinned to specific versions + - Update meta.json with exact version matching Docker Compose image version + - For frontend dependencies: Use `pnpm add ` in `app/` directory + - For root dependencies: Use `npm install ` in root directory + +- **Commit Messages:** Follow conventional commit patterns (e.g., `feat:`, `fix:`, `docs:`, `chore:`). + +- **Common Pitfalls to Avoid:** + 1. Forgetting to process meta.json after editing (CI will fail) + 2. Template ID mismatch between meta.json and folder name + 3. Including `ports`, `container_name`, or `networks` in docker-compose.yml + 4. Using object syntax for env vars in template.toml (must be array of strings) + 5. Logo file missing or filename mismatch in meta.json + 6. Version mismatch between meta.json and docker-compose.yml image tag diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 657ce29d0..2671d5af3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -148,7 +148,6 @@ Use these in `${}` for dynamic values: - Omit explicit ports; let Dokploy handle exposure. - Use persistent volumes for data (e.g., databases). - - Set `restart: unless-stopped` for services. - **Template.toml**: From 76049e3628babb2d304f154832c3311423bd8106 Mon Sep 17 00:00:00 2001 From: Ye Liu Date: Tue, 16 Dec 2025 12:32:11 -0500 Subject: [PATCH 54/91] Add trailbase template (#590) * Add trailbase * Add comment on mounting local directory * Update meta.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- blueprints/trailbase/docker-compose.yml | 24 +++ blueprints/trailbase/logo.svg | 194 ++++++++++++++++++++++++ blueprints/trailbase/template.toml | 8 + meta.json | 17 +++ 4 files changed, 243 insertions(+) create mode 100644 blueprints/trailbase/docker-compose.yml create mode 100644 blueprints/trailbase/logo.svg create mode 100644 blueprints/trailbase/template.toml diff --git a/blueprints/trailbase/docker-compose.yml b/blueprints/trailbase/docker-compose.yml new file mode 100644 index 000000000..a8067d86b --- /dev/null +++ b/blueprints/trailbase/docker-compose.yml @@ -0,0 +1,24 @@ +# IMPORTANT: The initial admin credentials will be printed in the logs after the container starts +# Access TrailBase Admin UI at: https://your-domain.com/_/admin (replace with your configured domain) + +version: "3.8" + +services: + trailbase: + image: trailbase/trailbase:latest + restart: unless-stopped + volumes: + - trailbase-data:/app/traildepot + # If you want to use a local directory instead, uncomment the line below and specify the path to your local + # directory. Make sure this directory is writable by the trailbase user (UID 1000) and the group (GID 1000) i.e. + # chown -R 1000:1000 /path/to/your/local/directory + # - /path/to/your/local/directory:/app/traildepot + healthcheck: + test: ["CMD", "curl", "--fail", "http://localhost:4000/api/healthcheck"] + interval: 30s + timeout: 10s + retries: 3 + +volumes: + # comment the line below if you specified a local directory in the volumes section of the trailbase service + trailbase-data: {} diff --git a/blueprints/trailbase/logo.svg b/blueprints/trailbase/logo.svg new file mode 100644 index 000000000..d2e0b8b0b --- /dev/null +++ b/blueprints/trailbase/logo.svg @@ -0,0 +1,194 @@ + + + + diff --git a/blueprints/trailbase/template.toml b/blueprints/trailbase/template.toml new file mode 100644 index 000000000..dbfe2a65f --- /dev/null +++ b/blueprints/trailbase/template.toml @@ -0,0 +1,8 @@ +[variables] +main_domain = "${domain}" + +[config] +[[config.domains]] +serviceName = "trailbase" +port = 4000 +host = "${main_domain}" diff --git a/meta.json b/meta.json index ee73ec654..8122e183e 100644 --- a/meta.json +++ b/meta.json @@ -5640,6 +5640,23 @@ "tor" ] }, + { + "id": "trailbase", + "name": "TrailBase", + "version": "latest", + "description": "TrailBase is a blazingly fast, open-source application server with type-safe APIs, built-in WebAssembly runtime, realtime, auth, and admin UI built on Rust, SQLite & Wasmtime.", + "logo": "logo.svg", + "links": { + "github": "https://github.com/trailbase/trailbase", + "website": "https://trailbase.io/", + "docs": "https://trailbase.io/getting-started/install" + }, + "tags": [ + "backend", + "database", + "api" + ] + }, { "id": "triggerdotdev", "name": "Trigger.dev", From 37dc848e71141c8aae1c5cccb4c90adfe15868c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Gerhard=20Lindesv=C3=A4rd?= <1987198+lindesvard@users.noreply.github.com> Date: Wed, 17 Dec 2025 04:14:42 +0100 Subject: [PATCH 55/91] fix: openpanel and migrate to v2 (#594) --- blueprints/openpanel/docker-compose.yml | 103 +++++++++-------------- blueprints/openpanel/template.toml | 107 +++++++++++++----------- 2 files changed, 97 insertions(+), 113 deletions(-) diff --git a/blueprints/openpanel/docker-compose.yml b/blueprints/openpanel/docker-compose.yml index 566299d0f..f63935437 100644 --- a/blueprints/openpanel/docker-compose.yml +++ b/blueprints/openpanel/docker-compose.yml @@ -1,8 +1,12 @@ -x-database: &x-database - DATABASE_URL: postgres://${SERVICE_USER_POSTGRES}:${SERVICE_PASSWORD_POSTGRES}@op-db:5432/${OPENPANEL_POSTGRES_DB}?schema=public - DATABASE_URL_DIRECT: postgres://${SERVICE_USER_POSTGRES}:${SERVICE_PASSWORD_POSTGRES}@op-db:5432/${OPENPANEL_POSTGRES_DB}?schema=public - REDIS_URL: redis://default:${SERVICE_PASSWORD_REDIS}@op-kv:6379 - CLICKHOUSE_URL: ${OPENPANEL_CLICKHOUSE_URL:-http://op-ch:8123/openpanel} +x-common: &x-common + NODE_ENV: production + SELF_HOSTED: "true" + API_URL: ${API_URL} + DASHBOARD_URL: ${DASHBOARD_URL} + DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@op-db:5432/${POSTGRES_DB}?schema=public + DATABASE_URL_DIRECT: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@op-db:5432/${POSTGRES_DB}?schema=public + REDIS_URL: redis://default:${REDIS_PASSWORD}@op-kv:6379 + CLICKHOUSE_URL: http://op-ch:8123/openpanel services: op-db: @@ -11,44 +15,46 @@ services: volumes: - op-db-data:/var/lib/postgresql/data healthcheck: - test: [ 'CMD-SHELL', 'pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}' ] + test: ['CMD-SHELL', 'pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}'] interval: 10s timeout: 5s retries: 5 environment: - - POSTGRES_DB=${OPENPANEL_POSTGRES_DB} - - POSTGRES_USER=${SERVICE_USER_POSTGRES} - - POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRES} + - POSTGRES_DB=${POSTGRES_DB} + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} op-kv: image: redis:7.2.5-alpine restart: always volumes: - op-kv-data:/data - command: redis-server --requirepass ${SERVICE_PASSWORD_REDIS} --maxmemory-policy noeviction + command: redis-server --requirepass ${REDIS_PASSWORD} --maxmemory-policy noeviction healthcheck: - test: [CMD, redis-cli, -a, "${SERVICE_PASSWORD_REDIS}", ping] + test: ['CMD', 'redis-cli', '-a', '${REDIS_PASSWORD}', 'ping'] interval: 10s timeout: 5s retries: 5 op-ch: - image: clickhouse/clickhouse-server:24.3.2-alpine + image: clickhouse/clickhouse-server:25.10.2.65 restart: always volumes: - op-ch-data:/var/lib/clickhouse - op-ch-logs:/var/log/clickhouse-server - - ../files/clickhouse/clickhouse-config.xml:/etc/clickhouse-server/config.d/op-config.xml:ro - - ../files/clickhouse/clickhouse-user-config.xml:/etc/clickhouse-server/users.d/op-user-config.xml:ro - - ../files/clickhouse/init-db.sql:/docker-entrypoint-initdb.d/1_init-db.sql:ro + - ../files/clickhouse_config:/etc/clickhouse-server/config.d + - ../files/clickhouse_users:/etc/clickhouse-server/users.d + - ../files/clickhouse_init:/docker-entrypoint-initdb.d + environment: + - CLICKHOUSE_SKIP_USER_SETUP=1 healthcheck: - test: [CMD-SHELL, 'clickhouse-client --query "SELECT 1" -d openpanel'] + test: ['CMD-SHELL', 'clickhouse-client --query "SELECT 1" -d openpanel'] interval: 10s timeout: 5s retries: 5 op-api: - image: lindesvard/openpanel-api:${OP_API_VERSION:-latest} + image: lindesvard/openpanel-api:2.0.0 restart: always command: > sh -c " @@ -57,39 +63,28 @@ services: sleep 1 done echo 'PostgreSQL is ready' - + echo 'Waiting for ClickHouse to be ready...' while ! nc -z op-ch 8123; do sleep 1 done echo 'ClickHouse is ready' - + echo 'Running migrations...' - - echo '$DATABASE_URL' - + CI=true pnpm -r run migrate:deploy - + pnpm start " environment: - # Common - NODE_ENV: production - NEXT_PUBLIC_SELF_HOSTED: true - # URLs - SERVICE_FQDN_OPAPI: /api - # Set coolify FQDN domain - NEXT_PUBLIC_API_URL: $SERVICE_FQDN_OPAPI - NEXT_PUBLIC_DASHBOARD_URL: $SERVICE_FQDN_OPDASHBOARD - # Others - COOKIE_SECRET: ${SERVICE_BASE64_COOKIESECRET} - ALLOW_REGISTRATION: ${OPENPANEL_ALLOW_REGISTRATION:-false} - ALLOW_INVITATION: ${OPENPANEL_ALLOW_INVITATION:-true} - EMAIL_SENDER: ${OPENPANEL_EMAIL_SENDER} + COOKIE_SECRET: ${COOKIE_SECRET} + ALLOW_REGISTRATION: ${ALLOW_REGISTRATION} + ALLOW_INVITATION: ${ALLOW_INVITATION} + EMAIL_SENDER: ${EMAIL_SENDER} RESEND_API_KEY: ${RESEND_API_KEY} - <<: *x-database + <<: *x-common healthcheck: - test: [ "CMD-SHELL", "curl -f http://localhost:3000/healthcheck || exit 1" ] + test: ['CMD-SHELL', 'curl -f http://localhost:3000/healthcheck || exit 1'] interval: 10s timeout: 5s retries: 5 @@ -102,55 +97,35 @@ services: condition: service_healthy op-dashboard: - image: lindesvard/openpanel-dashboard:${OP_DASHBOARD_VERSION:-latest} + image: lindesvard/openpanel-dashboard:2.0.0 restart: always depends_on: op-api: condition: service_healthy environment: - # Common - NODE_ENV: production - NEXT_PUBLIC_SELF_HOSTED: true - # URLs - SERVICE_FQDN_OPDASHBOARD: - # Set coolify FQDN domain - NEXT_PUBLIC_API_URL: $SERVICE_FQDN_OPAPI - NEXT_PUBLIC_DASHBOARD_URL: $SERVICE_FQDN_OPDASHBOARD - <<: *x-database + <<: *x-common healthcheck: - test: [ 'CMD-SHELL', 'curl -f http://localhost:3000/api/healthcheck || exit 1' ] + test: ['CMD-SHELL', 'curl -f http://localhost:3000/api/healthcheck || exit 1'] interval: 10s timeout: 5s retries: 5 op-worker: - image: lindesvard/openpanel-worker:${OP_WORKER_VERSION:-latest} + image: lindesvard/openpanel-worker:2.0.0 restart: always depends_on: op-api: condition: service_healthy environment: - # FQDN - SERVICE_FQDN_OPBULLBOARD: - # Common - NODE_ENV=production: - NEXT_PUBLIC_SELF_HOSTED: true - # Set coolify FQDN domain - NEXT_PUBLIC_API_URL: $SERVICE_FQDN_OPAPI - <<: *x-database + <<: *x-common healthcheck: - test: [ 'CMD-SHELL', 'curl -f http://localhost:3000/healthcheck || exit 1' ] + test: ['CMD-SHELL', 'curl -f http://localhost:3000/healthcheck || exit 1'] interval: 10s timeout: 5s retries: 5 - deploy: - mode: replicated - replicas: $OP_WORKER_REPLICAS volumes: op-db-data: op-kv-data: op-ch-data: op-ch-logs: - op-proxy-data: - op-proxy-config: diff --git a/blueprints/openpanel/template.toml b/blueprints/openpanel/template.toml index 557735087..2a93c33c7 100644 --- a/blueprints/openpanel/template.toml +++ b/blueprints/openpanel/template.toml @@ -1,82 +1,91 @@ [variables] main_domain = "${domain}" -api_domain = "${domain}" -db_password = "${password}" +db_password = "${password:32}" cookie_secret = "${base64:32}" -redis_password = "${password}" +redis_password = "${password:32}" [config] +# ClickHouse config files - mounted as directories [[config.mounts]] -filePath = "clickhouse/clickhouse-config.xml" +filePath = "./clickhouse_config/op-config.xml" content = """ - - - warning - true - - 10 - - - - - - - - - - 0.0.0.0 - 0.0.0.0 - opch - - 0 - - - 1 - replica1 - openpanel_cluster - - + + + warning + true + + 10 + + + + + + + + + + 0.0.0.0 + 0.0.0.0 + opch + + 0 + + + 1 + replica1 + openpanel_cluster + + """ [[config.mounts]] -filePath = "clickhouse/clickhouse-user-config.xml" +filePath = "./clickhouse_users/op-user-config.xml" content = """ - - + + 0 0 - + """ [[config.mounts]] -filePath = "clickhouse/init-db.sql" +filePath = "./clickhouse_init/1_init-db.sql" content = """ CREATE DATABASE IF NOT EXISTS openpanel; """ [[config.domains]] serviceName = "op-dashboard" -port = 3_000 +port = 3000 host = "${main_domain}" [[config.domains]] serviceName = "op-api" -port = 3_000 -host = "${api_domain}" +port = 3000 +host = "${main_domain}" +path = "/api" +stripPath = true [config.env] -SERVICE_FQDN_OPDASHBOARD = "http://${main_domain}" -SERVICE_FQDN_OPAPI = "http://${api_domain}" -OPENPANEL_POSTGRES_DB = "openpanel-db" -SERVICE_USER_POSTGRES = "openpanel" -SERVICE_PASSWORD_POSTGRES = "${db_password}" -SERVICE_PASSWORD_REDIS = "${redis_password}" -SERVICE_BASE64_COOKIESECRET = "${cookie_secret}" -OP_WORKER_REPLICAS = "1" +DASHBOARD_URL = "http://${main_domain}" +API_URL = "http://${main_domain}/api" + +# Database configuration +POSTGRES_DB = "openpanel" +POSTGRES_USER = "openpanel" +POSTGRES_PASSWORD = "${db_password}" +REDIS_PASSWORD = "${redis_password}" + +# Security +COOKIE_SECRET = "${cookie_secret}" + +# Registration settings +ALLOW_REGISTRATION = "true" +ALLOW_INVITATION = "true" +# Email configuration (optional - configure for email notifications) +EMAIL_SENDER = "" RESEND_API_KEY = "" -OPENPANEL_ALLOW_REGISTRATION = "true" -OPENPANEL_ALLOW_INVITATION = "true" From 7528f7326509e8c6ceba8cc7228db93303b38e27 Mon Sep 17 00:00:00 2001 From: Jemg Date: Thu, 18 Dec 2025 00:45:28 -0500 Subject: [PATCH 56/91] refactor: update docker-compose.yml to include version and restart policy (#599) --- blueprints/excalidraw/docker-compose.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/blueprints/excalidraw/docker-compose.yml b/blueprints/excalidraw/docker-compose.yml index d68065c0e..9f2c30913 100644 --- a/blueprints/excalidraw/docker-compose.yml +++ b/blueprints/excalidraw/docker-compose.yml @@ -1,5 +1,8 @@ +version: "3.8" + services: excalidraw: + restart: unless-stopped image: excalidraw/excalidraw:latest healthcheck: test: From 0438165d43ae5d3e491284e9bed308f6f876b982 Mon Sep 17 00:00:00 2001 From: Khiet Tam Nguyen <86177399+nktnet1@users.noreply.github.com> Date: Fri, 19 Dec 2025 15:39:16 +1100 Subject: [PATCH 57/91] feat(blueprint): mage ai template (#601) * feat(blueprint): template for mage-ai * fix: add healthcheck for mage-ai --- blueprints/mage-ai/docker-compose.yml | 26 ++++++++++++++++++ blueprints/mage-ai/mage-ai.svg | 38 +++++++++++++++++++++++++++ blueprints/mage-ai/template.toml | 11 ++++++++ meta.json | 18 +++++++++++++ 4 files changed, 93 insertions(+) create mode 100644 blueprints/mage-ai/docker-compose.yml create mode 100644 blueprints/mage-ai/mage-ai.svg create mode 100644 blueprints/mage-ai/template.toml diff --git a/blueprints/mage-ai/docker-compose.yml b/blueprints/mage-ai/docker-compose.yml new file mode 100644 index 000000000..7c9bbc231 --- /dev/null +++ b/blueprints/mage-ai/docker-compose.yml @@ -0,0 +1,26 @@ +# https://docs.mage.ai/getting-started/setup#docker-compose +# +# The default credentials are: +# USERNAME: admin@admin.com +# PASSWORD: admin + +services: + mage-ai: + image: mageai/mageai:0.9.78 + command: mage start ${PROJECT_NAME} + environment: + USER_CODE_PATH: /home/src/${PROJECT_NAME} + ENV: ${ENV} + expose: + - 6789 + volumes: + - mageai_data:/home/src/ + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-s", "-f", "-o", "/dev/null", "http://localhost:6789"] + interval: 30s + timeout: 10s + retries: 5 + +volumes: + mageai_data: diff --git a/blueprints/mage-ai/mage-ai.svg b/blueprints/mage-ai/mage-ai.svg new file mode 100644 index 000000000..d45a7d1b2 --- /dev/null +++ b/blueprints/mage-ai/mage-ai.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/blueprints/mage-ai/template.toml b/blueprints/mage-ai/template.toml new file mode 100644 index 000000000..12cd84e68 --- /dev/null +++ b/blueprints/mage-ai/template.toml @@ -0,0 +1,11 @@ +[variables] +main_domain = "${domain}" + +[[config.domains]] +serviceName = "mage-ai" +port = 6789 +host = "${main_domain}" + +[config.env] +PROJECT_NAME = "mage-ai" +ENV = "production" diff --git a/meta.json b/meta.json index 8122e183e..fccd74475 100644 --- a/meta.json +++ b/meta.json @@ -3596,6 +3596,24 @@ "os" ] }, + { + "id": "mage-ai", + "name": "Mage AI", + "version": "0.9.78", + "description": "Build, run, and manage data pipelines for integrating and transforming data.", + "logo": "mage-ai.svg", + "links": { + "github": "https://github.com/mage-ai/mage-ai", + "website": "https://mage.ai", + "docs": "https://docs.mage.ai" + }, + "tags": [ + "data", + "dbt", + "etl", + "pipelines" + ] + }, { "id": "mailpit", "name": "Mailpit", From 4757c59da38887b40438781048f4d296feb23676 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Fri, 19 Dec 2025 15:45:08 -0600 Subject: [PATCH 58/91] docs: update copilot instructions to emphasize version pinning in docker-compose.yml Clarified the importance of pinning image versions in docker-compose.yml and explicitly stated to avoid using the `latest` tag to prevent potential issues with template functionality when upstream images change. --- .github/copilot-instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index c0669f2fb..671a3fef6 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -55,7 +55,7 @@ No tests in repo—focus on manual validation via scripts and Dokploy deploys. D - JWT helper: `${jwt:secret_var:payload_var}` for auth tokens; payload as JSON string with `exp: ${timestamps:YYYY-MM-DDTHH:mm:ssZ}`. - **Meta.json**: Entries as JSON objects; tags array of lowercase strings (e.g., ["monitoring", "database"]); links object with `github`, `website`, `docs`. - **No Networks**: Rely on Dokploy's isolated deployments—avoid explicit `networks:`. -- **Versions**: Pin images to specific versions in `docker-compose.yml` (e.g., `ghost:5.82.0-alpine`); match in `meta.json.version`. +- **Versions**: Pin images to specific versions in `docker-compose.yml` (e.g., `ghost:5.82.0-alpine`); match in `meta.json.version`. **NEVER use `latest` tag**—it can break templates when upstream images change unexpectedly. - **Logos**: SVG preferred; size ~128x128; file name in `meta.json.logo` (e.g., "ghost.svg"). Cross-component: Templates are independent and ship as static blueprints/meta. From dd8ef5b3c8cc6f7c6360fa5536a692f10fd9f274 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Fri, 19 Dec 2025 15:54:36 -0600 Subject: [PATCH 59/91] refactor: enhance Docker Compose validation workflow Updated the GitHub Actions workflow for validating Docker Compose files by streamlining the detection of changed blueprints and improving the validation process. Removed redundant steps and consolidated the validation of docker-compose.yml and template.toml files into a more efficient structure. Added clearer output messages for validation results and ensured that best practices are checked for each blueprint. This refactor aims to improve maintainability and clarity in the CI/CD process. --- .github/workflows/validate-docker-compose.yml | 290 +++--------------- 1 file changed, 47 insertions(+), 243 deletions(-) diff --git a/.github/workflows/validate-docker-compose.yml b/.github/workflows/validate-docker-compose.yml index ddd16aaba..25d4eaf3f 100644 --- a/.github/workflows/validate-docker-compose.yml +++ b/.github/workflows/validate-docker-compose.yml @@ -17,13 +17,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: - fetch-depth: 0 # Necesitamos el historial completo para comparar con base + fetch-depth: 0 - name: Set up Docker Compose - run: | - echo "🐳 Setting up Docker Compose..." - # Docker Compose V2 viene preinstalado en ubuntu-latest - docker compose version + run: docker compose version - name: Set up Node.js uses: actions/setup-node@v4 @@ -36,282 +33,89 @@ jobs: version: 8 - name: Install dependencies - run: | - echo "📦 Installing Node.js dependencies..." - cd build-scripts && pnpm install + run: cd build-scripts && pnpm install - - name: Get changed files - id: changed-files + - name: Detect changed blueprints + id: changed run: | - echo "🔍 Detecting changed files..." - - # Obtener la rama base BASE_SHA=$(git merge-base HEAD origin/${{ github.base_ref }}) - # Encontrar todos los archivos docker-compose.yml y template.toml modificados/agregados - CHANGED_COMPOSE=$(git diff --name-only --diff-filter=ACMRT $BASE_SHA HEAD | grep -E 'blueprints/.*/docker-compose\.yml$' || true) - CHANGED_TOML=$(git diff --name-only --diff-filter=ACMRT $BASE_SHA HEAD | grep -E 'blueprints/.*/template\.toml$' || true) - - # Crear lista de directorios únicos que tienen cambios - CHANGED_DIRS=$(echo -e "$CHANGED_COMPOSE\n$CHANGED_TOML" | sed 's|blueprints/\([^/]*\)/.*|\1|' | sort -u) - - echo "Changed compose files:" - echo "$CHANGED_COMPOSE" | while read file; do [ -n "$file" ] && echo " - $file"; done - - echo "Changed TOML files:" - echo "$CHANGED_TOML" | while read file; do [ -n "$file" ] && echo " - $file"; done - - echo "Changed directories:" - echo "$CHANGED_DIRS" | while read dir; do [ -n "$dir" ] && echo " - $dir"; done + # Obtener todos los blueprints que tienen cambios (en docker-compose.yml o template.toml) + CHANGED_BLUEPRINTS=$(git diff --name-only --diff-filter=ACMRT $BASE_SHA HEAD | \ + grep -E 'blueprints/[^/]+/(docker-compose\.yml|template\.toml)$' | \ + sed 's|blueprints/\([^/]*\)/.*|\1|' | \ + sort -u) - # Guardar para usar en siguientes pasos - echo "compose_files<> $GITHUB_OUTPUT - echo "$CHANGED_COMPOSE" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - - echo "toml_files<> $GITHUB_OUTPUT - echo "$CHANGED_TOML" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT + echo "Changed blueprints:" + echo "$CHANGED_BLUEPRINTS" | while read blueprint; do + [ -n "$blueprint" ] && echo " - $blueprint" + done - echo "directories<> $GITHUB_OUTPUT - echo "$CHANGED_DIRS" >> $GITHUB_OUTPUT + # Guardar lista de blueprints (una por línea) + echo "blueprints<> $GITHUB_OUTPUT + echo "$CHANGED_BLUEPRINTS" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT - - name: Validate Docker Compose files syntax - id: validate-compose-syntax + - name: Validate blueprints run: | - echo "🔍 Validating Docker Compose files syntax..." + BLUEPRINTS="${{ steps.changed.outputs.blueprints }}" - ERROR=0 - COMPOSE_FILES="${{ steps.changed-files.outputs.compose_files }}" - - if [ -z "$COMPOSE_FILES" ]; then - echo "ℹ️ No docker-compose.yml files changed, skipping validation" + if [ -z "$BLUEPRINTS" ] || [ "$BLUEPRINTS" = "" ]; then + echo "ℹ️ No blueprints changed, skipping validation" exit 0 fi - echo "$COMPOSE_FILES" | while read -r compose_file; do - if [ -z "$compose_file" ]; then - continue - fi - - TEMPLATE_DIR=$(dirname "$compose_file") - TEMPLATE_NAME=$(basename "$TEMPLATE_DIR") - - echo "" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "📦 Validating syntax: $TEMPLATE_NAME" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - - # Validar sintaxis de docker-compose.yml usando docker compose - echo "🔍 Validating docker-compose.yml syntax..." - if ! docker compose -f "$compose_file" config > /dev/null 2>&1; then - echo "❌ ERROR: docker-compose.yml syntax is invalid in $TEMPLATE_NAME" - echo "Running docker compose config to show errors:" - docker compose -f "$compose_file" config 2>&1 || true - ERROR=1 - else - echo "✅ docker-compose.yml syntax is valid" - fi - - # Obtener lista de servicios del compose - SERVICES=$(docker compose -f "$compose_file" config --services 2>/dev/null || echo "") - echo "📋 Services found in docker-compose.yml:" - echo "$SERVICES" | while read service; do - [ -n "$service" ] && echo " - $service" - done - - # Guardar servicios para validación posterior - echo "$SERVICES" > "/tmp/${TEMPLATE_NAME}_services.txt" - done - - if [ $ERROR -eq 1 ]; then - echo "" - echo "❌ Docker Compose syntax validation failed" - exit 1 - else - echo "" - echo "✅ All Docker Compose files have valid syntax" - fi - - - name: Validate Docker Compose best practices - id: validate-compose-practices - run: | - echo "🔍 Validating Docker Compose best practices..." - ERROR=0 - COMPOSE_FILES="${{ steps.changed-files.outputs.compose_files }}" - - if [ -z "$COMPOSE_FILES" ]; then - echo "ℹ️ No docker-compose.yml files changed, skipping validation" - exit 0 - fi - # Convert to array to avoid subshell issues with pipe - # This ensures ERROR=1 inside the loop propagates to the parent shell - mapfile -t COMPOSE_ARRAY <<< "$COMPOSE_FILES" + # Convertir a array para evitar problemas con subshells + mapfile -t BLUEPRINT_ARRAY <<< "$BLUEPRINTS" - for compose_file in "${COMPOSE_ARRAY[@]}"; do - if [ -z "$compose_file" ]; then - continue - fi + # Iterar sobre cada blueprint + for blueprint in "${BLUEPRINT_ARRAY[@]}"; do + [ -z "$blueprint" ] && continue - TEMPLATE_DIR=$(dirname "$compose_file") - TEMPLATE_NAME=$(basename "$TEMPLATE_DIR") + BLUEPRINT_PATH="blueprints/$blueprint" + COMPOSE_FILE="$BLUEPRINT_PATH/docker-compose.yml" + TOML_FILE="$BLUEPRINT_PATH/template.toml" echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "📦 Validating best practices: $TEMPLATE_NAME" + echo "📦 Validating: $blueprint" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - # Validar usando el script de TypeScript - if ! (cd build-scripts && pnpm exec tsx validate-docker-compose.ts --file "../$compose_file" --verbose); then + # 1. Validar best practices de docker-compose.yml + if [ ! -f "$COMPOSE_FILE" ]; then + echo "⚠️ WARNING: docker-compose.yml not found" ERROR=1 - fi - done - - if [ $ERROR -eq 1 ]; then - echo "" - echo "❌ Docker Compose best practices validation failed" - exit 1 - else - echo "" - echo "✅ All Docker Compose files follow best practices" - fi - - - name: Validate template.toml files - id: validate-toml - run: | - echo "🔍 Validating template.toml files..." - - ERROR=0 - DIRECTORIES="${{ steps.changed-files.outputs.directories }}" - - if [ -z "$DIRECTORIES" ]; then - echo "ℹ️ No template directories changed, skipping TOML validation" - exit 0 - fi - - # Convert to array to avoid subshell issues with pipe - # This ensures ERROR=1 inside the loop propagates to the parent shell - mapfile -t DIRS_ARRAY <<< "$DIRECTORIES" - - for template_dir in "${DIRS_ARRAY[@]}"; do - if [ -z "$template_dir" ]; then continue fi - - TEMPLATE_PATH="blueprints/$template_dir" - TOML_FILE="$TEMPLATE_PATH/template.toml" - - if [ ! -f "$TOML_FILE" ]; then - echo "⚠️ WARNING: template.toml not found in $template_dir (might be deleted)" - continue - fi - - echo "" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "📝 Validating: $template_dir/template.toml" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - - # Validar usando el script de TypeScript con tsx - # Ejecutar desde build-scripts para tener acceso a node_modules - if ! (cd build-scripts && pnpm exec tsx validate-template.ts --dir "../$TEMPLATE_PATH" --verbose); then + echo "🔍 Validating docker-compose.yml best practices..." + if ! (cd build-scripts && pnpm exec tsx validate-docker-compose.ts --file "../$COMPOSE_FILE" --verbose); then ERROR=1 - fi - done - - if [ $ERROR -eq 1 ]; then - echo "" - echo "❌ template.toml validation failed" - exit 1 - else - echo "" - echo "✅ All template.toml files are valid" - fi - - - name: Test Docker Compose (dry-run) - id: test-compose - run: | - echo "🧪 Testing Docker Compose files (dry-run)..." - - ERROR=0 - DIRECTORIES="${{ steps.changed-files.outputs.directories }}" - - if [ -z "$DIRECTORIES" ]; then - echo "ℹ️ No template directories changed, skipping dry-run test" - exit 0 - fi - - echo "$DIRECTORIES" | while read -r template_dir; do - if [ -z "$template_dir" ]; then continue fi - COMPOSE_FILE="blueprints/$template_dir/docker-compose.yml" - - if [ ! -f "$COMPOSE_FILE" ]; then - continue - fi - - echo "" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "🧪 Testing: $template_dir" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - - # Cambiar al directorio del template para resolver rutas relativas - cd "blueprints/$template_dir" - - # Validar que docker-compose puede parsear el archivo completamente - echo "🔍 Running docker compose config (full validation)..." - if docker compose config > /dev/null 2>&1; then - echo "✅ Docker Compose file is fully valid and can be processed" - - # Mostrar información útil - echo "📋 Configuration summary:" - docker compose config --services | while read service; do - [ -n "$service" ] && echo " Service: $service" - done + # 3. Validar template.toml + if [ -f "$TOML_FILE" ]; then + echo "🔍 Validating template.toml..." + if ! (cd build-scripts && pnpm exec tsx validate-template.ts --dir "../$BLUEPRINT_PATH" --verbose); then + ERROR=1 + continue + fi else - echo "❌ ERROR: Docker Compose file failed full validation" - docker compose config 2>&1 || true + echo "⚠️ WARNING: template.toml not found" ERROR=1 + continue fi - cd - > /dev/null + echo "✅ All validations passed for $blueprint" done if [ $ERROR -eq 1 ]; then echo "" - echo "❌ Docker Compose dry-run test failed" + echo "❌ Validation failed for one or more blueprints" exit 1 else echo "" - echo "✅ All Docker Compose files passed dry-run test" + echo "✅ All blueprints validated successfully" fi - - - name: Summary - if: always() - run: | - echo "" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "📊 Validation Summary" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - - if [ "${{ steps.validate-compose-syntax.outcome }}" == "success" ] && \ - [ "${{ steps.validate-compose-practices.outcome }}" == "success" ] && \ - [ "${{ steps.validate-toml.outcome }}" == "success" ] && \ - [ "${{ steps.test-compose.outcome }}" == "success" ]; then - echo "✅ All validations passed!" - echo "" - echo "Your Docker Compose and template.toml files are valid and ready to merge." - else - echo "❌ Some validations failed. Please review the errors above." - echo "" - echo "Common issues to check:" - echo " - docker-compose.yml syntax errors" - echo " - template.toml syntax errors" - echo " - serviceName in template.toml must match service names in docker-compose.yml" - echo " - Avoid using container_name, explicit networks, or port mappings" - fi - From e4ffe271e043c6c2869340334f80dc2e1d32066b Mon Sep 17 00:00:00 2001 From: Huy Pham Date: Sat, 20 Dec 2025 04:57:48 +0700 Subject: [PATCH 60/91] fix: update zitadel default instance features (#544) --- blueprints/zitadel/docker-compose.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/blueprints/zitadel/docker-compose.yml b/blueprints/zitadel/docker-compose.yml index 387466144..aaa457c35 100644 --- a/blueprints/zitadel/docker-compose.yml +++ b/blueprints/zitadel/docker-compose.yml @@ -33,6 +33,9 @@ services: ZITADEL_FIRSTINSTANCE_ORG_HUMAN_EMAIL_ADDRESS: "${ZITADEL_FIRSTINSTANCE_ORG_HUMAN_EMAIL_ADDRESS}" ZITADEL_FIRSTINSTANCE_ORG_HUMAN_FIRSTNAME: "${ZITADEL_FIRSTINSTANCE_ORG_HUMAN_FIRSTNAME}" ZITADEL_FIRSTINSTANCE_ORG_HUMAN_LASTNAME: "${ZITADEL_FIRSTINSTANCE_ORG_HUMAN_LASTNAME}" + + # Default Instance Features + ZITADEL_DEFAULTINSTANCE_FEATURES_LOGINV2_REQUIRED: false depends_on: db: From f5552b557c952b4f725e3d3580b936413c69ad43 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 19 Dec 2025 16:03:32 -0600 Subject: [PATCH 61/91] Add Reactive Resume template (#603) * Initial plan * Add Reactive Resume template with docker-compose, template.toml, and meta.json entry Co-authored-by: Siumauricio <47042324+Siumauricio@users.noreply.github.com> * Fix MinIO storage path alignment between template.toml and docker-compose.yml Co-authored-by: Siumauricio <47042324+Siumauricio@users.noreply.github.com> * Update docker-compose.yml --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Siumauricio <47042324+Siumauricio@users.noreply.github.com> --- blueprints/reactive-resume/docker-compose.yml | 67 +++++++++++++++++++ blueprints/reactive-resume/logo.svg | 20 ++++++ blueprints/reactive-resume/template.toml | 33 +++++++++ meta.json | 18 +++++ 4 files changed, 138 insertions(+) create mode 100644 blueprints/reactive-resume/docker-compose.yml create mode 100644 blueprints/reactive-resume/logo.svg create mode 100644 blueprints/reactive-resume/template.toml diff --git a/blueprints/reactive-resume/docker-compose.yml b/blueprints/reactive-resume/docker-compose.yml new file mode 100644 index 000000000..216830224 --- /dev/null +++ b/blueprints/reactive-resume/docker-compose.yml @@ -0,0 +1,67 @@ +version: "3.8" + +services: + postgres: + image: postgres:16-alpine + volumes: + - postgres_data:/var/lib/postgresql/data + environment: + POSTGRES_DB: postgres + POSTGRES_USER: postgres + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"] + interval: 10s + timeout: 5s + retries: 5 + + minio: + image: minio/minio:latest + command: server /data + volumes: + - minio_data:/data + environment: + MINIO_ROOT_USER: ${MINIO_ROOT_USER} + MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD} + + chrome: + image: ghcr.io/browserless/chromium:v2.18.0 + extra_hosts: + - "host.docker.internal:host-gateway" + environment: + TIMEOUT: 10000 + CONCURRENT: 10 + TOKEN: ${CHROME_TOKEN} + EXIT_ON_HEALTH_FAILURE: "true" + PRE_REQUEST_HEALTH_CHECK: "true" + + app: + image: amruthpillai/reactive-resume:latest + depends_on: + - postgres + - minio + - chrome + environment: + PORT: 3000 + NODE_ENV: production + PUBLIC_URL: https://${APP_DOMAIN} + STORAGE_URL: https://${APP_DOMAIN}/default + CHROME_TOKEN: ${CHROME_TOKEN} + CHROME_URL: ws://chrome:3000 + DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres + ACCESS_TOKEN_SECRET: ${ACCESS_TOKEN_SECRET} + REFRESH_TOKEN_SECRET: ${REFRESH_TOKEN_SECRET} + MAIL_FROM: ${MAIL_FROM} + STORAGE_ENDPOINT: minio + STORAGE_PORT: 9000 + STORAGE_REGION: us-east-1 + STORAGE_BUCKET: default + STORAGE_ACCESS_KEY: ${MINIO_ROOT_USER} + STORAGE_SECRET_KEY: ${MINIO_ROOT_PASSWORD} + STORAGE_USE_SSL: "false" + STORAGE_SKIP_BUCKET_CHECK: "false" + + +volumes: + minio_data: + postgres_data: diff --git a/blueprints/reactive-resume/logo.svg b/blueprints/reactive-resume/logo.svg new file mode 100644 index 000000000..8e9ccb297 --- /dev/null +++ b/blueprints/reactive-resume/logo.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/blueprints/reactive-resume/template.toml b/blueprints/reactive-resume/template.toml new file mode 100644 index 000000000..e6452ee1b --- /dev/null +++ b/blueprints/reactive-resume/template.toml @@ -0,0 +1,33 @@ +[variables] +main_domain = "${domain}" +postgres_password = "${password:32}" +minio_user = "minioadmin" +minio_password = "${password:32}" +chrome_token = "${password:32}" +access_token_secret = "${password:64}" +refresh_token_secret = "${password:64}" +mail_from = "noreply@${main_domain}" + +[config] +env = [ + "APP_DOMAIN=${main_domain}", + "POSTGRES_PASSWORD=${postgres_password}", + "MINIO_ROOT_USER=${minio_user}", + "MINIO_ROOT_PASSWORD=${minio_password}", + "CHROME_TOKEN=${chrome_token}", + "ACCESS_TOKEN_SECRET=${access_token_secret}", + "REFRESH_TOKEN_SECRET=${refresh_token_secret}", + "MAIL_FROM=${mail_from}", +] +mounts = [] + +[[config.domains]] +serviceName = "app" +port = 3000 +host = "${main_domain}" + +[[config.domains]] +serviceName = "minio" +port = 9000 +host = "${main_domain}" +path = "/default" diff --git a/meta.json b/meta.json index fccd74475..f54e741ee 100644 --- a/meta.json +++ b/meta.json @@ -5047,6 +5047,24 @@ "rabbitmq" ] }, + { + "id": "reactive-resume", + "name": "Reactive Resume", + "version": "latest", + "description": "A free and open-source resume builder that simplifies the process of creating, updating, and sharing your resume.", + "logo": "logo.svg", + "links": { + "github": "https://github.com/AmruthPillai/Reactive-Resume", + "website": "https://rxresu.me/", + "docs": "https://docs.rxresu.me/" + }, + "tags": [ + "resume", + "cv", + "productivity", + "document" + ] + }, { "id": "registry", "name": "Docker Registry", From be3c7f2d1d22d08b2943107bacea1a7629e22aea Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 19 Dec 2025 16:06:36 -0600 Subject: [PATCH 62/91] [WIP] Fix mounted prometheus.yml file not working (#605) * Initial plan * Fix Prometheus mounted prometheus.yml file by adding volume mount Co-authored-by: Siumauricio <47042324+Siumauricio@users.noreply.github.com> * Update blueprints/prometheus/template.toml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Siumauricio <47042324+Siumauricio@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- blueprints/prometheus/docker-compose.yml | 1 + blueprints/prometheus/template.toml | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/blueprints/prometheus/docker-compose.yml b/blueprints/prometheus/docker-compose.yml index e10167776..1896c8761 100644 --- a/blueprints/prometheus/docker-compose.yml +++ b/blueprints/prometheus/docker-compose.yml @@ -9,6 +9,7 @@ services: - "--web.console.templates=/usr/share/prometheus/consoles" - "--web.enable-lifecycle" volumes: + - ../files/prometheus.yml:/etc/prometheus/prometheus.yml - prometheus-data:/prometheus volumes: prometheus-data: {} diff --git a/blueprints/prometheus/template.toml b/blueprints/prometheus/template.toml index 535663dc0..2415f3fed 100644 --- a/blueprints/prometheus/template.toml +++ b/blueprints/prometheus/template.toml @@ -10,7 +10,10 @@ port = 9_090 host = "${main_domain}" [[config.mounts]] -filePath = "/etc/prometheus/prometheus.yml" +# Note: this relative path is resolved by Dokploy to the file mounted from +# ../files/prometheus.yml in docker-compose, and mapped inside the container +# to /etc/prometheus/prometheus.yml. +filePath = "prometheus.yml" serviceName = "prometheus" content = """ # Prometheus Configuration From 211c2aeb6019c213fb083e9ed5cd39d1249fd21c Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Fri, 19 Dec 2025 17:52:07 -0600 Subject: [PATCH 63/91] docs: update copilot instructions to enforce open source requirement Added a clear requirement stating that all services must be open source and only applications with open-source licenses (e.g., MIT, Apache, GPL, AGPL) are allowed. This update aims to ensure compliance and maintain the integrity of the project. --- .github/copilot-instructions.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 671a3fef6..fc54887c9 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -30,6 +30,7 @@ Exemplary blueprint: `blueprints/ghost/`—`docker-compose.yml` exposes port 236 1. **Add/Update Template**: + - **REQUIREMENT**: Service **MUST** be open source. Only add templates for applications with an open-source license (e.g., MIT, Apache, GPL, AGPL). Proprietary or closed-source services are not allowed. - Create `blueprints//` (e.g., `ghost`). - Implement `docker-compose.yml` (single service typical; use volumes for persistence). - Configure `template.toml`—reference vars in `[config.domains]`, `[config.env]`, `[config.mounts]`. @@ -45,8 +46,9 @@ No tests in repo—focus on manual validation via scripts and Dokploy deploys. D ## Conventions and Patterns +- **Open Source Requirement**: **ALL services MUST be open source**. Only applications with open-source licenses (MIT, Apache, GPL, AGPL, etc.) are allowed. Proprietary or closed-source services are strictly prohibited. - **Template IDs**: Lowercase, kebab-case (e.g., `active-pieces`); unique across repo—enforced by dedupe script. -- **Docker Compose**: Minimal—omit `ports` (Dokploy proxies), `restart: unless-stopped`, persistent volumes (e.g., `- db-data:/var/lib/postgresql/data`). Services named after folder (e.g., `ghost` service). +- **Docker Compose**: Minimal—omit `ports` (Dokploy proxies), persistent volumes (e.g., `- db-data:/var/lib/postgresql/data`). Services named after folder (e.g., `ghost` service). - **template.toml**: - Variables: `[variables] main_domain = "${domain}"`; use helpers for secrets (`${password:64}`, `${base64:32}`). - Domains: `[[config.domains]] serviceName = "" port = 80 host = "${main_domain}"` (path="/" optional). From 7b02385416cec53095f93132f90f7948ea3c70ed Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 19 Dec 2025 17:54:45 -0600 Subject: [PATCH 64/91] Fix Cap.so template MinIO deployment failure (#604) * Initial plan * Fix Cap.so MinIO image to use official quay.io image Co-authored-by: Siumauricio <47042324+Siumauricio@users.noreply.github.com> * Fix MinIO healthcheck to use curl instead of mc Co-authored-by: Siumauricio <47042324+Siumauricio@users.noreply.github.com> * Update .gitignore --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Siumauricio <47042324+Siumauricio@users.noreply.github.com> --- .gitignore | 4 +- blueprints/capso/docker-compose.yml | 13 +- build-scripts/package-lock.json | 615 ++++++++++++++++++++++++++++ 3 files changed, 628 insertions(+), 4 deletions(-) create mode 100644 build-scripts/package-lock.json diff --git a/.gitignore b/.gitignore index b512c09d4..2448babff 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -node_modules \ No newline at end of file +node_modules +meta.json.backup.* + diff --git a/blueprints/capso/docker-compose.yml b/blueprints/capso/docker-compose.yml index 637b1ded7..4714ef0c3 100644 --- a/blueprints/capso/docker-compose.yml +++ b/blueprints/capso/docker-compose.yml @@ -17,6 +17,9 @@ services: S3_INTERNAL_ENDPOINT: "http://minio:3902" expose: - 3000 + depends_on: + - ps-mysql + - minio ps-mysql: image: mysql:8.0 @@ -34,11 +37,10 @@ services: - ps-mysql:/var/lib/mysql minio: - image: bitnami/minio:latest + image: quay.io/minio/minio:RELEASE.2025-05-24T17-08-30Z restart: unless-stopped + command: server /bitnami/minio/data --address ":3902" --console-address ":3903" environment: - MINIO_API_PORT_NUMBER: 3902 - MINIO_CONSOLE_PORT_NUMBER: 3903 MINIO_ROOT_USER: ${MINIO_ROOT_USER} MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD} expose: @@ -47,6 +49,11 @@ services: volumes: - minio-data:/bitnami/minio/data - minio-certs:/certs + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3902/minio/health/ready"] + interval: 30s + timeout: 20s + retries: 3 volumes: ps-mysql: diff --git a/build-scripts/package-lock.json b/build-scripts/package-lock.json new file mode 100644 index 000000000..bb11b4e01 --- /dev/null +++ b/build-scripts/package-lock.json @@ -0,0 +1,615 @@ +{ + "name": "dokploy-templates-build-scripts", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "dokploy-templates-build-scripts", + "version": "1.0.0", + "dependencies": { + "toml": "^3.0.0", + "yaml": "2.7.1" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "tsx": "^4.7.0", + "typescript": "^5.3.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/node": { + "version": "20.19.27", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.27.tgz", + "integrity": "sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/esbuild": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/toml": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", + "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==", + "license": "MIT" + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/yaml": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", + "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + } + } +} From 97159bd1b4143d791abde8016a39969bada3ac97 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 19 Dec 2025 17:56:22 -0600 Subject: [PATCH 65/91] Fix Discourse template manifest error - update to bitnamilegacy/discourse:3.5.0 (#606) * Initial plan * Fix Discourse template by updating to bitnamilegacy/discourse:3.5.0 Co-authored-by: Siumauricio <47042324+Siumauricio@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Siumauricio <47042324+Siumauricio@users.noreply.github.com> --- blueprints/discourse/docker-compose.yml | 4 ++-- meta.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/blueprints/discourse/docker-compose.yml b/blueprints/discourse/docker-compose.yml index 2b938b855..caafab5bb 100644 --- a/blueprints/discourse/docker-compose.yml +++ b/blueprints/discourse/docker-compose.yml @@ -32,7 +32,7 @@ services: restart: unless-stopped discourse-app: - image: docker.io/bitnami/discourse:3.3.2 + image: docker.io/bitnamilegacy/discourse:3.5.0 volumes: - discourse-data:/bitnami/discourse @@ -59,7 +59,7 @@ services: restart: unless-stopped discourse-sidekiq: - image: docker.io/bitnami/discourse:3.3.2 + image: docker.io/bitnamilegacy/discourse:3.5.0 volumes: - discourse-sidekiq-data:/bitnami/discourse diff --git a/meta.json b/meta.json index f54e741ee..5b41cd314 100644 --- a/meta.json +++ b/meta.json @@ -1748,7 +1748,7 @@ { "id": "discourse", "name": "Discourse", - "version": "3.3.2", + "version": "3.5.0", "description": "Discourse is a modern forum software for your community. Use it as a mailing list, discussion forum, or long-form chat room.", "logo": "discourse.svg", "links": { From 9433dce128928a74097f514a257ae758d76d4e5e Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Fri, 19 Dec 2025 18:00:36 -0600 Subject: [PATCH 66/91] Remove pull request trigger from deploy-preview workflow --- .github/workflows/deploy-preview.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/deploy-preview.yml index a342ac7a4..d04171e64 100644 --- a/.github/workflows/deploy-preview.yml +++ b/.github/workflows/deploy-preview.yml @@ -5,9 +5,6 @@ on: workflows: [Build Preview Deployment] types: - completed - pull_request: - branches: - - canary permissions: actions: read deployments: write From ee0e6fba92d67715252d2b90592a7e75c0312724 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Fri, 19 Dec 2025 18:05:58 -0600 Subject: [PATCH 67/91] Enhance copilot instructions by adding verification steps for Docker images and clarifying URL variable usage. Emphasize the importance of verifying image existence before committing to prevent deployment failures. --- .github/copilot-instructions.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index fc54887c9..e17f19640 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -31,6 +31,7 @@ Exemplary blueprint: `blueprints/ghost/`—`docker-compose.yml` exposes port 236 1. **Add/Update Template**: - **REQUIREMENT**: Service **MUST** be open source. Only add templates for applications with an open-source license (e.g., MIT, Apache, GPL, AGPL). Proprietary or closed-source services are not allowed. + - **Verify Docker Images**: Before using any Docker image in `docker-compose.yml`, verify it exists using `docker manifest inspect ` (e.g., `docker manifest inspect docker.io/bitnami/discourse:3.5.0`). This ensures the image is available and prevents deployment failures. - Create `blueprints//` (e.g., `ghost`). - Implement `docker-compose.yml` (single service typical; use volumes for persistence). - Configure `template.toml`—reference vars in `[config.domains]`, `[config.env]`, `[config.mounts]`. @@ -53,11 +54,12 @@ No tests in repo—focus on manual validation via scripts and Dokploy deploys. D - Variables: `[variables] main_domain = "${domain}"`; use helpers for secrets (`${password:64}`, `${base64:32}`). - Domains: `[[config.domains]] serviceName = "" port = 80 host = "${main_domain}"` (path="/" optional). - Env: `[[config.env]]` array of "KEY=VALUE" strings, interpolating vars (e.g., "DB_PASSWORD=${db_pass}"). + - **URL Variables**: When environment variables require URLs (e.g., `WEB_URL`, `NEXTAUTH_URL`, `PUBLIC_URL`), **always use HTTP by default** (e.g., `"http://${main_domain}"`). HTTPS should only be used if explicitly required by the application or when using a reverse proxy with SSL termination. - Mounts: `[[config.mounts]] filePath = "/etc/config" content = """multi-line\ncontent"""`. - JWT helper: `${jwt:secret_var:payload_var}` for auth tokens; payload as JSON string with `exp: ${timestamps:YYYY-MM-DDTHH:mm:ssZ}`. - **Meta.json**: Entries as JSON objects; tags array of lowercase strings (e.g., ["monitoring", "database"]); links object with `github`, `website`, `docs`. - **No Networks**: Rely on Dokploy's isolated deployments—avoid explicit `networks:`. -- **Versions**: Pin images to specific versions in `docker-compose.yml` (e.g., `ghost:5.82.0-alpine`); match in `meta.json.version`. **NEVER use `latest` tag**—it can break templates when upstream images change unexpectedly. +- **Versions**: Pin images to specific versions in `docker-compose.yml` (e.g., `ghost:5.82.0-alpine`); match in `meta.json.version`. **NEVER use `latest` tag**—it can break templates when upstream images change unexpectedly. **Always verify image exists** using `docker manifest inspect ` before committing. - **Logos**: SVG preferred; size ~128x128; file name in `meta.json.logo` (e.g., "ghost.svg"). Cross-component: Templates are independent and ship as static blueprints/meta. From 9501d5f974d03058c7fadd199317bac5840c92c6 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 19 Dec 2025 18:16:25 -0600 Subject: [PATCH 68/91] Add Easy!Appointments template (#608) * Initial plan * Add Easyappointments template with docker-compose, template.toml, and meta.json entry Co-authored-by: Siumauricio <47042324+Siumauricio@users.noreply.github.com> * Update blueprints/easyappointments/template.toml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update template.toml --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Siumauricio <47042324+Siumauricio@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .gitignore | 1 + .../easyappointments/docker-compose.yml | 29 ++++++++++++++++++ blueprints/easyappointments/logo.png | Bin 0 -> 5535 bytes blueprints/easyappointments/template.toml | 13 ++++++++ meta.json | 17 ++++++++++ 5 files changed, 60 insertions(+) create mode 100644 blueprints/easyappointments/docker-compose.yml create mode 100644 blueprints/easyappointments/logo.png create mode 100644 blueprints/easyappointments/template.toml diff --git a/.gitignore b/.gitignore index 2448babff..c0cadd7c4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules +package-lock.json meta.json.backup.* diff --git a/blueprints/easyappointments/docker-compose.yml b/blueprints/easyappointments/docker-compose.yml new file mode 100644 index 000000000..ee2e1edfe --- /dev/null +++ b/blueprints/easyappointments/docker-compose.yml @@ -0,0 +1,29 @@ +version: "3.8" + +services: + easyappointments: + image: alextselegidis/easyappointments:1.5.0 + restart: unless-stopped + environment: + - BASE_URL=http://${DOMAIN} + - DB_HOST=mysql + - DB_NAME=easyappointments + - DB_USERNAME=root + - DB_PASSWORD=${DB_PASSWORD} + volumes: + - easyappointments:/var/www/html + depends_on: + - mysql + + mysql: + image: mysql:8.0 + restart: unless-stopped + environment: + - MYSQL_ROOT_PASSWORD=${DB_PASSWORD} + - MYSQL_DATABASE=easyappointments + volumes: + - mysql:/var/lib/mysql + +volumes: + easyappointments: + mysql: diff --git a/blueprints/easyappointments/logo.png b/blueprints/easyappointments/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..422670b98f375aeaefd0394f980c2ba044f0c538 GIT binary patch literal 5535 zcmV;Q6=3R#P)*@8nHAPI4R1?F*>M+}EhKp^oDhJ)MN$4vg%?cJH#?w;=1*}d2Ps`h4YrswN# z`uojv_t)QkLqPF^HxXb``XsrADGG6`Igpe<1oMdL4C?J7l2dlhGLyEsE5Wq*-ncqpq_4r2s zPCE|oBu4v2KWS7z{*k`VLs~zoQ9yse(hC#_ zFw7C?Zv}FR>atSQju?rcuG?5|!bDGBA1?j=79vJmC}VV486Fuw9@V9#05qGNTYbIw z&83UDc>9)6MoA!uxl^a%fid?WsOh$2z40gx{NDxaJpVhy1!FJ*g1M8YV!@P$P*qZD zI~FtIIDF+2{_DtoTS2| z`c*8NIzuSqi*PUgdizFP@9njmD{ki@*##5@0vI!LgljoX*D$WUoO}GSx;l&TIWP$x zyRQzq#%{`)^^;tU4*(C>-h*j1wajDn6;)U;5M%ULlb2~ZXaNqaIz z6or`kgSwV{?7JhR2T)mD!aP=ARV}Fr@a0N&*#~RJLL+WWOkhUMSn2wJAxZ$v^|rN? zLFPhTH;qfx1{pY`-G_nR=^3K`;rm_i5iylB6*(X%0v ztS%~YSZFzY5UV=3B4!w=*!_R&dJn&x^#cC1HkG!Kd&8;fB!W^G5DJBaeru<)x~?+` zK-URmWKi(R2Lb`6@py7{UFRCkd2KTOcU~Plw46GKl^w@o z7ZEf~%aB8jpP7o+1WbZEu?XgDZ^pLSFX7L}PPEDKLnjtIrHKm!{K+myjhih9AwsgL z%JSO(i{vz)+>fRYHYbcP5Pv5Y!JPJHZ2MN41b*qnLLnX?4`@EQA5HIXfJJEKbqdN7+(fv{z%~26&#DjxF6XyY#_a!&-=*Aq5UKsQ#e0< z%8`||?nGzW_$ELscX`H^0ZM}RFQ4<}+E`HT%xNHp6}%@Z+0eE3qv~t<~ULt8k;a9RU=y6v4D#jPpwWezyn)Ym# z>+@X(gI3&%hB13biz^9KuVcUkg!ETLkQX@XS#x|JR_)#l6NXeD?_PoZ-&@fzzTNQ? zTnnpZ1Q)?q0G0A6D(z%+2-3?FJOpyc+Joo84-ez(nvpBFL{YgNX!d zjwSDW=R98pyl#LY>*USPJXF9&P9lQ*Dl<@sBtmo7eynER`GP55mMYbctjbP+1^pTk zBvWP}lVI)feQ4adQO(JdgwQ7-5Lc`RFu%_I-2t-5%Bi2ty17Ws=XY*n@AzHCFkw-tz}-Xj7*<^PBgT9Z6PL=%fExb z4Os_-$TqwisGJQ)Kf)`&+=Sj(G}UnPA9xt+<}Sr3hBL@ZK#1FP(@nOs6bhmB;J&o+C3Nrj z-D%AG*;@Rfp%Ep?F#!!dbG!51U=1v>8UI*k!uUkez3anZP$wGJKd~6gCqI&*4^MY5 z@H`Eq(LAh=tTL?!_n~R?2FId_yO*rv?CFdr0g#yk!0T`3t+HB=??=;)jey_oU7lwU zN#p-T+W1y|{JK7hehUa`J-81|n>Kh@G%=Ii0h* z__;*C5TNbAhiKZkf!*gTpyTXm%-fI~5~PfuC-*Lpyz^(~&-j3cF@SX~pEqZw11i=G zpzXki$vYqHKGE4{I{Q=yC(NN)~IFlr@|x7K>%1h)Lb~hUqCVBB4a` zk8O5v8qfZG9qu?@+~m}YhMDnT7zUb;>_gM`jr_;Hn{po9yE@U(HggG<-_JI_jPr_K zpDL6H;Edw2D!>Ab_XB{pyN;ppEl!^=aDvEU*SRxT@Ky`%Mk2y;qPT*!hxcM-yT$kt z12#2e5=4iF)~_!@!{p4Vo|g!pehUb3iwOGBwzs`6%19DqSGPO~oLGB!FJ69ki`t!^ zC2@74p>^gWEMpj-r>Cn*xeN)D4VZN93rC|kc_sM@Hs0Oj)6`0q1Xip)ycet6H|O8I zYcYPl16U~Xk|L%k5Wt9{BFPG@TW~3h^ZQh#%B@>IL3VYY!E+m1g(O&eXfGPydDD0IuILG(6Ai83ScHcA z```FL4g*4*$6uen5EH6La|(;L-U+h1`!tsXYYu&YRof)*e4hq`3F9xuvH@azAjc_U ztjEWemt*(Ke}{?HH6AK**$S|$`wX7jWG6xEkxs0BXA^vn-W5DSbfTf<$wdRo_&}ah z#Eh?~M91paP+vXT$HAbWKTd9~BxpObA5Fi?zk9dk$wg>j%=7yjIS&YNVthp<+8bX% zU6o5%lTU`A(E8M3G)xg2KUY8hc7qZDRI-DJ&Y4hM zjrQ-ZMtv1$Odtaf1UYr>Glrh-PY|zq>4YR6`&07w*w5*21KrZUI~$j`9-wI&>Z+=- zZN;iocaZJWDpQ&C)^G2fjD9-N(DKw`G)#4W?DLj%RgIbRbn%1`l$4Yp7!0Oz_|qsG z`l0^^gFzGt=KjH85cQ*Lu%q#n1PMfNnOZ49V!);X7IAfGXq~kL&rknj*L8fiEGe{2 z-Q;-rWia4z5J-(%TwILe;$jpP71=(aP)I6`jIQgbA6TV+|!t-M}3 z(a<(~Y0CJbz!}-#85ucVQ|%0h3?btyD$()1*X&b7KkrmOyBL8_fEs`Kv`0M%?jH;Y z0sv_eOp^5z_)L&&pCE(L_&^q}U{P8Zm9!<+R3~35ABA_DUcscQBnf3%P;$z;fuM8f#X(BLq>%1w}M+8P--+;db4$K!G76frb(jHkTwm{rETqbkt8 zVl^hp`U!I2y_4x;bfTf{>FkV8%QIC_Fus?10iRwP1@Ij~ymTyXkwqyIR3=H_)4RRp?mpYI=$&;k}cAPBb(>vjj^Y@D|49H6KB)1_gt` zREikdK+Z~KGFQ+XLL&dQYzB<_(KYB;@hYBb{tukHe%12=kxn$U&27N48R9-)rjc1M zjq)Okrmb<4mDdz8R4JM-$Qlx64PlZ}z!6gBRnC3M?jSWVRCVua8rr_yfQE^1d_|?51R_`n5zt7=_`?lDiU2^81TQ5?AmiTEG_=lJJ}iw7c+3^d zOB6vt2QO~#@)N!Eh(6X>`TLx3M#hXAS&khqtj4U?H*oI8Ray$H+`CTW%Q&WF9=o!U zIx@0kUyA9aD^Oky42Q$1cL7;Lz^tEmJdS8IiiqHsxv^Lb(P-2bLT3F~Wm#V?#nQgXT=_$6Kl593efBBFl~-W){nIhNBKwyv z^j8pv2!>%Oy+&7w05bl183U=x@B*uxkRHI?X%AuUw1)t&UD5q*Ouw#M6?sV!lM5L# z=x?JA2IM70j8_?chnE$QiM*wV$yJ$r#VHO|kFWaMOv0T&P=3gQ4V8;&j;JHgw} zl0nP!0zz2(W!5R&_RGvdc-P=pY8Nm5{hS7b5JFH~T%7u0C(n678&ecLMDq>FEC$io z0G>|4U@+ySbPFyKdC6!UZUnNH<&~iUAu>-jk$j=3S`*@`HCTp26`OpIUc)9014`!2AaQh#Ll;X?_FK?-_@no*Y$M2cL|As0jep z5h|~|?Oet3yb-X}x$INZ`-$TD_VT9iTvfd}?IJkW7>G&djjzOJvsmw`wv|)zdEf|GeR1GF(4QTkc7)D&v$h8<@5Ul5Pu&~?GgdOIRNWW htS!agy#5IQ{67tn=*fF`V|f4o002ovPDHLkV1j7XUNHaw literal 0 HcmV?d00001 diff --git a/blueprints/easyappointments/template.toml b/blueprints/easyappointments/template.toml new file mode 100644 index 000000000..997ee0e1d --- /dev/null +++ b/blueprints/easyappointments/template.toml @@ -0,0 +1,13 @@ +[variables] +main_domain = "${domain}" +db_password = "${password:32}" + +[config] +[[config.domains]] +serviceName = "easyappointments" +port = 80 +host = "${main_domain}" + +[config.env] +DOMAIN = "${main_domain}" +DB_PASSWORD = "${db_password}" diff --git a/meta.json b/meta.json index 5b41cd314..358c0902b 100644 --- a/meta.json +++ b/meta.json @@ -2033,6 +2033,23 @@ "simple" ] }, + { + "id": "easyappointments", + "name": "Easy!Appointments", + "version": "1.5.0", + "description": "Easy!Appointments is a highly customizable web application that allows customers to book appointments with you via a sophisticated web interface.", + "logo": "logo.png", + "links": { + "github": "https://github.com/alextselegidis/easyappointments", + "website": "https://easyappointments.org", + "docs": "https://easyappointments.org/docs" + }, + "tags": [ + "scheduling", + "appointments", + "booking" + ] + }, { "id": "elastic-search", "name": "Elasticsearch", From b3e62b447d3da54640a6e7449cc92d743ae88b58 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 19 Dec 2025 22:29:05 -0600 Subject: [PATCH 69/91] Add Dolibarr ERP & CRM template (#610) * Initial plan * Add Dolibarr ERP & CRM template Co-authored-by: Siumauricio <47042324+Siumauricio@users.noreply.github.com> * Update template.toml * Update blueprints/dolibarr/docker-compose.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Siumauricio <47042324+Siumauricio@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- blueprints/dolibarr/docker-compose.yml | 40 ++++++++++++++++++++++++++ blueprints/dolibarr/logo.svg | 1 + blueprints/dolibarr/template.toml | 23 +++++++++++++++ meta.json | 19 ++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 blueprints/dolibarr/docker-compose.yml create mode 100644 blueprints/dolibarr/logo.svg create mode 100644 blueprints/dolibarr/template.toml diff --git a/blueprints/dolibarr/docker-compose.yml b/blueprints/dolibarr/docker-compose.yml new file mode 100644 index 000000000..9e364c465 --- /dev/null +++ b/blueprints/dolibarr/docker-compose.yml @@ -0,0 +1,40 @@ +services: + dolibarr: + image: dolibarr/dolibarr:21.0.0 + restart: always + environment: + DOLI_DB_HOST: db + DOLI_DB_NAME: $DB_NAME + DOLI_DB_USER: $DB_USER + DOLI_DB_PASSWORD: $DB_PASSWORD + DOLI_URL_ROOT: ${DOLIBARR_HOST} + DOLI_ADMIN_LOGIN: admin + DOLI_ADMIN_PASSWORD: $ADMIN_PASSWORD + volumes: + - dolibarr_documents:/var/www/documents + - dolibarr_html:/var/www/html + depends_on: + db: + condition: service_healthy + + db: + image: mariadb:10.11 + restart: always + environment: + MYSQL_ROOT_PASSWORD: $DB_ROOT_PASSWORD + MYSQL_DATABASE: $DB_NAME + MYSQL_USER: $DB_USER + MYSQL_PASSWORD: $DB_PASSWORD + volumes: + - db_data:/var/lib/mysql + healthcheck: + test: ["CMD-SHELL", "healthcheck.sh --connect --innodb_initialized"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + +volumes: + dolibarr_documents: + dolibarr_html: + db_data: diff --git a/blueprints/dolibarr/logo.svg b/blueprints/dolibarr/logo.svg new file mode 100644 index 000000000..94b7c1a65 --- /dev/null +++ b/blueprints/dolibarr/logo.svg @@ -0,0 +1 @@ + diff --git a/blueprints/dolibarr/template.toml b/blueprints/dolibarr/template.toml new file mode 100644 index 000000000..923b0de77 --- /dev/null +++ b/blueprints/dolibarr/template.toml @@ -0,0 +1,23 @@ +[variables] +main_domain = "${domain}" +db_name = "dolibarr" +db_user = "dolibarr" +db_password = "${password:32}" +db_root_password = "${password:32}" +admin_password = "${password:32}" + +[config] +env = [ + "DOLIBARR_HOST=${main_domain}", + "DB_NAME=${db_name}", + "DB_USER=${db_user}", + "DB_PASSWORD=${db_password}", + "DB_ROOT_PASSWORD=${db_root_password}", + "ADMIN_PASSWORD=${admin_password}" +] +mounts = [] + +[[config.domains]] +serviceName = "dolibarr" +port = 80 +host = "${main_domain}" diff --git a/meta.json b/meta.json index 358c0902b..7fc8b6b70 100644 --- a/meta.json +++ b/meta.json @@ -1850,6 +1850,25 @@ "grafana" ] }, + { + "id": "dolibarr", + "name": "Dolibarr", + "version": "21.0.0", + "description": "Dolibarr ERP & CRM is a modern software package that helps manage your organization's activities (contacts, quotes, invoices, orders, stocks, agenda, human resources, ecm, manufacturing).", + "logo": "logo.svg", + "links": { + "github": "https://github.com/Dolibarr/dolibarr", + "website": "https://www.dolibarr.org/", + "docs": "https://wiki.dolibarr.org/" + }, + "tags": [ + "erp", + "crm", + "business", + "management", + "invoicing" + ] + }, { "id": "domain-locker", "name": "Domain Locker", From 8b579fbb60dd34dfcc4101290b72d9127bb6f9e3 Mon Sep 17 00:00:00 2001 From: Louan Fontenele Date: Tue, 6 Jan 2026 01:49:26 -0300 Subject: [PATCH 70/91] feat: add Syncthing Template (#636) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adiciona template do Syncthing com arquivos de configuração e ícone * Remove versão do Docker Compose do template do Syncthing --- blueprints/syncthing/docker-compose.yml | 19 +++++++++ blueprints/syncthing/syncthing.svg | 54 +++++++++++++++++++++++++ blueprints/syncthing/template.toml | 9 +++++ meta.json | 17 ++++++++ 4 files changed, 99 insertions(+) create mode 100644 blueprints/syncthing/docker-compose.yml create mode 100644 blueprints/syncthing/syncthing.svg create mode 100644 blueprints/syncthing/template.toml diff --git a/blueprints/syncthing/docker-compose.yml b/blueprints/syncthing/docker-compose.yml new file mode 100644 index 000000000..e6083a7bd --- /dev/null +++ b/blueprints/syncthing/docker-compose.yml @@ -0,0 +1,19 @@ +services: + syncthing: + image: lscr.io/linuxserver/syncthing:latest + restart: unless-stopped + expose: + - 8384 + - 22000 + - 21027/udp + volumes: + - syncthing_config:/config + - syncthing_data:/var/syncthing/Sync + environment: + - PUID=1000 + - PGID=1000 + - TZ=${timezone} + +volumes: + syncthing_config: + syncthing_data: diff --git a/blueprints/syncthing/syncthing.svg b/blueprints/syncthing/syncthing.svg new file mode 100644 index 000000000..de8850f53 --- /dev/null +++ b/blueprints/syncthing/syncthing.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/blueprints/syncthing/template.toml b/blueprints/syncthing/template.toml new file mode 100644 index 000000000..eda476e47 --- /dev/null +++ b/blueprints/syncthing/template.toml @@ -0,0 +1,9 @@ +[variables] +main_domain = "${domain}" +timezone = "America/Sao_Paulo" + +[config] +[[config.domains]] +serviceName = "syncthing" +port = 8384 +host = "${main_domain}" diff --git a/meta.json b/meta.json index 7fc8b6b70..e3b7201ee 100644 --- a/meta.json +++ b/meta.json @@ -5608,6 +5608,23 @@ "surrealdb" ] }, + { + "id": "syncthing", + "name": "Syncthing", + "version": "latest", + "description": "Syncthing is a continuous file synchronization program that synchronizes files between two or more computers in real time.", + "logo": "syncthing.svg", + "links": { + "github": "https://github.com/syncthing/syncthing", + "website": "https://syncthing.net/", + "docs": "https://docs.syncthing.net/" + }, + "tags": [ + "file-sync", + "synchronization", + "backup" + ] + }, { "id": "tailscale-exitnode", "name": "Tailscale Exit nodes", From b8984b9e4faef989588319372db9621519879557 Mon Sep 17 00:00:00 2001 From: Alexey Fedorov Date: Wed, 7 Jan 2026 19:06:58 +0100 Subject: [PATCH 71/91] Update Umami to version v2.20.2 (#641) --- blueprints/umami/docker-compose.yml | 2 +- meta.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/blueprints/umami/docker-compose.yml b/blueprints/umami/docker-compose.yml index b20524a73..416dc8e13 100644 --- a/blueprints/umami/docker-compose.yml +++ b/blueprints/umami/docker-compose.yml @@ -1,6 +1,6 @@ services: umami: - image: ghcr.io/umami-software/umami:postgresql-v2.19.0 + image: ghcr.io/umami-software/umami:postgresql-v2.20.2 restart: always healthcheck: test: ["CMD-SHELL", "curl http://localhost:3000/api/heartbeat"] diff --git a/meta.json b/meta.json index e3b7201ee..4ecd479f8 100644 --- a/meta.json +++ b/meta.json @@ -5863,7 +5863,7 @@ { "id": "umami", "name": "Umami", - "version": "v2.19.0", + "version": "v2.20.2", "description": "Umami is a simple, fast, privacy-focused alternative to Google Analytics.", "logo": "umami.png", "links": { From 294421a380836ecdbe70fb454105d0a33c772fbc Mon Sep 17 00:00:00 2001 From: Harikrishnan Dhanasekaran Date: Wed, 7 Jan 2026 23:50:45 +0530 Subject: [PATCH 72/91] Feat : Add InstantDB template (#224) (#530) * feat: add InstantDB template * added the server service also * removed the external network * Update docker-compose.yml * Update blueprints/instantdb/docker-compose.yml * Update blueprints/instantdb/docker-compose.yml * Update docker-compose.yml * Update blueprints/instantdb/template.toml --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> --- blueprints/instantdb/docker-compose.yml | 64 +++++++++++++++++++++++++ blueprints/instantdb/instant.svg | 4 ++ blueprints/instantdb/template.toml | 29 +++++++++++ meta.json | 17 +++++++ 4 files changed, 114 insertions(+) create mode 100644 blueprints/instantdb/docker-compose.yml create mode 100644 blueprints/instantdb/instant.svg create mode 100644 blueprints/instantdb/template.toml diff --git a/blueprints/instantdb/docker-compose.yml b/blueprints/instantdb/docker-compose.yml new file mode 100644 index 000000000..b40e043dd --- /dev/null +++ b/blueprints/instantdb/docker-compose.yml @@ -0,0 +1,64 @@ +services: + postgres: + image: ghcr.io/instantdb/postgresql:postgresql-16-pg-hint-plan + restart: unless-stopped + environment: + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_DB: ${POSTGRES_DB} + volumes: + - backend-db:/var/lib/postgresql/data + healthcheck: + test: ["CMD", "pg_isready", "-U", "${POSTGRES_USER}"] + interval: 10s + timeout: 5s + retries: 5 + command: + - "postgres" + - "-c" + - "wal_level=logical" + - "-c" + - "max_replication_slots=4" + - "-c" + - "max_wal_senders=4" + - "-c" + - "shared_preload_libraries=pg_hint_plan" + - "-c" + - "random_page_cost=1.1" + server: + depends_on: + postgres: + condition: service_healthy + image: hari1367709/instantdb-server:latest + restart: unless-stopped + working_dir: /app + environment: + DATABASE_URL: "postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}" + NREPL_BIND_ADDRESS: "0.0.0.0" + PORT: "8888" + HOST: "0.0.0.0" + # AWS Credentials for KMS (required for InstantDB) + AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} + AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} + AWS_REGION: ${AWS_REGION:-us-east-1} + # Force unbuffered output for logs + PYTHONUNBUFFERED: "1" + NODE_ENV: "production" + command: ["java", "-Djava.awt.headless=true", "-server", "-jar", "target/instant-standalone.jar"] + ports: + - "8888" + - "6005" + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:8888/health || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s +volumes: + backend-db: + diff --git a/blueprints/instantdb/instant.svg b/blueprints/instantdb/instant.svg new file mode 100644 index 000000000..4e2dc24c7 --- /dev/null +++ b/blueprints/instantdb/instant.svg @@ -0,0 +1,4 @@ + + + + diff --git a/blueprints/instantdb/template.toml b/blueprints/instantdb/template.toml new file mode 100644 index 000000000..571e6b7a7 --- /dev/null +++ b/blueprints/instantdb/template.toml @@ -0,0 +1,29 @@ +[variables] +main_domain = "${domain}" +postgres_password = "${password:32}" +postgres_user = "instant" +postgres_db = "instant" +aws_access_key_id = "FILL-YOUR-CREDENTIALS" +aws_secret_access_key = "FILL-YOUR-CREDENTIALS" +aws_region = "us-east-1" + +[config] +env = [ + "POSTGRES_PASSWORD=${postgres_password}", + "POSTGRES_USER=${postgres_user}", + "POSTGRES_DB=${postgres_db}", + "DATABASE_URL=postgresql://${postgres_user}:${postgres_password}@postgres:5432/${postgres_db}", + "NREPL_BIND_ADDRESS=0.0.0.0", + "PORT=8888", + "HOST=0.0.0.0", + "AWS_ACCESS_KEY_ID=${aws_access_key_id}", + "AWS_SECRET_ACCESS_KEY=${aws_secret_access_key}", + "AWS_REGION=${aws_region}", +] +mounts = [] + +[[config.domains]] +serviceName = "server" +port = 8888 +host = "${main_domain}" + diff --git a/meta.json b/meta.json index 4ecd479f8..7b624bfa6 100644 --- a/meta.json +++ b/meta.json @@ -3116,6 +3116,23 @@ "events" ] }, + { + "id": "instantdb", + "name": "InstantDB", + "version": "latest", + "description": "InstantDB is a real-time database server that provides instant data synchronization and real-time updates for applications.", + "logo": "instant.svg", + "links": { + "github": "https://github.com/instantdb/instant/tree/main/server", + "website": "https://github.com/instantdb/instant", + "docs": "https://github.com/instantdb/instant" + }, + "tags": [ + "database", + "real-time", + "self-hosted" + ] + }, { "id": "invoiceshelf", "name": "InvoiceShelf", From 2199d1d15211c35b3ec765236b87d550484b11fd Mon Sep 17 00:00:00 2001 From: 0zul <26915998+0zul@users.noreply.github.com> Date: Wed, 7 Jan 2026 23:54:55 +0530 Subject: [PATCH 73/91] [New Template] Added Lavalink template for dokploy (#535) * Add initial Lavalink configuration template * Added lavalink.svg logo * Create docker-compose.yml for Lavalink setup Add Docker Compose configuration for Lavalink service. * Modify healthcheck interval in docker-compose.yml Increased healthcheck interval from 10s to 100s. * Add Lavalink metadata to meta.json Added metadata for Lavalink audio sending node. * Update server_port variable to use ${port} * Change server port variable to randomPort * Change port variable to randomPort in template.toml * Change port variable in template.toml * Update server port in template.toml * Fix formatting in template.toml for server config * style: Reformat lavalink entry in meta.json. * Update docker-compose.yml --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> --- blueprints/lavalink/docker-compose.yml | 41 +++++++++ blueprints/lavalink/lavalink.svg | 50 ++++++++++ blueprints/lavalink/template.toml | 123 +++++++++++++++++++++++++ meta.json | 16 ++++ 4 files changed, 230 insertions(+) create mode 100644 blueprints/lavalink/docker-compose.yml create mode 100644 blueprints/lavalink/lavalink.svg create mode 100644 blueprints/lavalink/template.toml diff --git a/blueprints/lavalink/docker-compose.yml b/blueprints/lavalink/docker-compose.yml new file mode 100644 index 000000000..1f4e91cf7 --- /dev/null +++ b/blueprints/lavalink/docker-compose.yml @@ -0,0 +1,41 @@ +services: + fix-perms: + image: busybox:1.36 + command: > + sh -c "mkdir -p /opt/Lavalink/plugins && + chmod -R 0777 /opt/Lavalink/plugins || true && + chown -R 1000:1000 /opt/Lavalink/plugins || true && + echo perms-fixed && sleep 1" + volumes: + - "../files/plugins/:/opt/Lavalink/plugins/" + - "../files/application.yml:/opt/Lavalink/application.yml" + restart: "no" + lavalink: + image: ghcr.io/lavalink-devs/lavalink:4 + depends_on: + - fix-perms + restart: unless-stopped + environment: + _JAVA_OPTIONS: "${_JAVA_OPTIONS:--Xmx6G}" + LAVALINK_SERVER_PASSWORD: "${LAVALINK_SERVER_PASSWORD:-youshallnotpass}" + SERVER_PORT: "${SERVER_PORT:-2333}" + volumes: + - "../files/application.yml:/opt/Lavalink/application.yml:rw" + - "../files/plugins/:/opt/Lavalink/plugins/:rw" + ports: + - ${SERVER_PORT} + + healthcheck: + test: > + sh -c 'wget --header="Authorization: ${LAVALINK_SERVER_PASSWORD}" -qO- http://127.0.0.1:${SERVER_PORT}/v4/info >/dev/null 2>&1 || exit 1' + interval: 100s + timeout: 5s + retries: 5 + start_period: 30s + + entrypoint: > + sh -c 'until [ -w /opt/Lavalink/plugins ] ; do + echo "waiting for /opt/Lavalink/plugins to be writable"; + sleep 1; + done; + exec java -jar /opt/Lavalink/Lavalink.jar' diff --git a/blueprints/lavalink/lavalink.svg b/blueprints/lavalink/lavalink.svg new file mode 100644 index 000000000..0ade923fd --- /dev/null +++ b/blueprints/lavalink/lavalink.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + diff --git a/blueprints/lavalink/template.toml b/blueprints/lavalink/template.toml new file mode 100644 index 000000000..6c9a92cd2 --- /dev/null +++ b/blueprints/lavalink/template.toml @@ -0,0 +1,123 @@ +[variables] +main_domain = "${domain}" +server_port = "2333" +lavalink_server_password = "${password}" + +[config] +[[config.mounts]] +filePath = "./application.yml" +content = """ +server: # REST and WS server + port: 2333 + address: 0.0.0.0 + http2: + enabled: false +plugins: +# name: # Name of the plugin +# some_key: some_value # Some key-value pair for the plugin +# another_key: another_value +lavalink: + plugins: + # - dependency: "com.github.username.pluginName:pluginName-plugin:x.y.z" + # snapshot: false + # pluginsDir: "/opt/Lavalink/plugins" + # defaultPluginRepository: "https://maven.lavalink.dev/releases" + # defaultPluginSnapshotRepository: "https://maven.lavalink.dev/snapshots" + server: + password: "youshallnotpass" + sources: + # The default Youtube source is now deprecated and won't receive further updates. Please use https://github.com/lavalink-devs/youtube-source#plugin instead. + youtube: true + bandcamp: true + soundcloud: true + twitch: true + vimeo: true + nico: true + http: true + local: false + filters: + volume: true + equalizer: true + karaoke: true + timescale: true + tremolo: true + vibrato: true + distortion: true + rotation: true + channelMix: true + lowPass: true + nonAllocatingFrameBuffer: false + bufferDurationMs: 400 + frameBufferDurationMs: 5000 + opusEncodingQuality: 10 + resamplingQuality: LOW + trackStuckThresholdMs: 10000 + useSeekGhosting: true + youtubePlaylistLoadLimit: 6 + playerUpdateInterval: 5 + youtubeSearchEnabled: true + soundcloudSearchEnabled: true + gc-warnings: true + #ratelimit: + #ipBlocks: ["1.0.0.0/8", "..."] # list of ip blocks + #excludedIps: ["...", "..."] # ips which should be explicit excluded from usage by lavalink + #strategy: "RotateOnBan" # RotateOnBan | LoadBalance | NanoSwitch | RotatingNanoSwitch + #searchTriggersFail: true # Whether a search 429 should trigger marking the ip as failing + #retryLimit: -1 # -1 = use default lavaplayer value | 0 = infinity | >0 = retry will happen this numbers times + #youtubeConfig: # Required for avoiding all age restrictions by YouTube, some restricted videos still can be played without. + #email: "" # Email of Google account + #password: "" # Password of Google account + #httpConfig: # Useful for blocking bad-actors from ip-grabbing your music node and attacking it, this way only the http proxy will be attacked + #proxyHost: "localhost" # Hostname of the proxy, (ip or domain) + #proxyPort: 3128 # Proxy port, 3128 is the default for squidProxy + #proxyUser: "" # Optional user for basic authentication fields, leave blank if you don't use basic auth + #proxyPassword: "" # Password for basic authentication + timeouts: + connectTimeoutMs: 3000 + connectionRequestTimeoutMs: 3000 + socketTimeoutMs: 3000 + +metrics: + prometheus: + enabled: false + endpoint: /metrics + +sentry: + dsn: "" + environment: "" +# tags: +# some_key: some_value +# another_key: another_value + +logging: + file: + path: ./logs/ + + level: + root: INFO + lavalink: INFO + + request: + enabled: true + includeClientInfo: true + includeHeaders: false + includeQueryString: true + includePayload: true + maxPayloadLength: 10000 + + + logback: + rollingpolicy: + max-file-size: 1GB + max-history: 30 +""" + +[[config.domains]] +serviceName = "lavalink" +port = 2_333 +host = "${main_domain}" + +[config.env] +_JAVA_OPTIONS = "-Xmx6G" +LAVALINK_SERVER_PASSWORD = "${lavalink_server_password}" +SERVER_PORT = "${server_port}" diff --git a/meta.json b/meta.json index 7b624bfa6..7dc8663c4 100644 --- a/meta.json +++ b/meta.json @@ -3390,6 +3390,22 @@ "ai" ] }, + { + "id": "lavalink", + "name": "Lavalink", + "version": "4.1.1", + "description": "Lavalink is an open source standalone audio sending node based on Lavaplayer.", + "logo": "lavalink.svg", + "links": { + "github": "https://github.com/lavalink-devs/Lavalink", + "website": "https://lavalink.dev/", + "docs": "https://lavalink.dev/getting-started/index.html" + }, + "tags": [ + "voice", + "discord" + ] + }, { "id": "letterfeed", "name": "Letterfeed", From 7417972335f279f7f517a11d6343a7adc62d6c86 Mon Sep 17 00:00:00 2001 From: Pascal Oberbeck <35005267+poberbeck@users.noreply.github.com> Date: Wed, 7 Jan 2026 20:00:31 +0100 Subject: [PATCH 74/91] chore(blueprint): update Appwrite services to v1.8 (#383) * Update Appwrite version docker images and environment variables * Add separate domains for functions and sites, update environment variables, replace insecure default passwords * Update Appwrite version from 1.6.1 to 1.7.4 in `meta.json` * Fix envs and update domains * Add missing volume mounts for sites * Fix config file syntax * Update runtimes network configuration in template.toml * Update Appwrite to 1.8 * Update appwrite image version to 1.8.0 * Add logging configuration to docker-compose * New Templates (#586) * feat(librechat): add LibreChat blueprint with compose, toml, metadata, links and tags * fix: rename templates to template.toml * fix(librechat): rename api service to librechat in docker-compose.yml * Update blueprints/librechat/template.toml * Update blueprints/librechat/template.toml * fix(librechat): add version under [config] and remove stray [config.mounts] header * fix(librechat): remove predefined persistent volume mounts from template.toml * docs(librechat): add authentication reference link to docker-compose.yml * feat: add Rote template - Add Rote deployment template with frontend, backend, and PostgreSQL services - Configure domain routing for frontend (port 80) and backend (port 3000) - Set up automatic password generation and environment variables - Use latest image tag by default - Add logo and metadata to meta.json * fix: process meta.json to fix formatting and sorting * Update GitHub workflows to target 'canary' branch for meta validation * Update pnpm-lock.yaml to upgrade various dependencies, including '@codemirror/autocomplete', '@radix-ui/react-dialog', and React packages to their latest versions. This includes updates to '@types/react' and '@types/react-dom' for improved compatibility and performance. * Enhance GitHub workflows: add production deployment configuration and target 'canary' branch for pull requests. * Refactor GitHub workflow: comment out build preview steps for clarity and future modifications. * Remove unnecessary blank line in deploy-preview.yml for improved readability. * Refactor GitHub workflow: uncomment build preview steps for improved deployment process and clarity. * Update template.toml (#555) * Update template.toml * Update template.toml * Update template.toml * fix: change VITE_API_BASE to http:// for traefik.me compatibility * changed image from sknnr/enshrouded-dedicated-server to mornedhels/enshrouded-server for autoupdate and easier config * Add Openinary Template (#567) * feat: add Openinary template * feat: update Openinary configuration to support ALLOWED_ORIGIN and refactor domain variable * fix: correct DEFAULT_DOMAIN environment variable reference in docker-compose.yml (#562) * add rustfs template (#568) * feat: add pull request template for improved contribution guidelines * fix: update pull request template to clarify issue closing keywords * feat: add validation scripts and configuration for Docker Compose and template files - Introduced a GitHub Actions workflow to validate Docker Compose files and template.toml on pull requests. - Added helper functions for generating random values and processing variables in templates. - Implemented validation scripts for checking the structure, syntax, and best practices of Docker Compose and template files. - Created necessary TypeScript types and configuration files for the build scripts. * Add Passbolt template blueprint to Dokploy templates (#376) * feat(templates): add Passbolt blueprint for Dokploy - Add docker-compose.yml defining services for Passbolt and MariaDB - Create template.toml with configurable domain, email, and database credentials - Add meta.json with metadata, tags, and link to logo * fix(meta): sort meta.json entries * fix: passbolt template had several issues that broke deployment - env variables were using old array format, changed to new table format - mariadb healthcheck was broken (wrong command for mariadb 11) - missing volume mounts for gpg keys, jwt tokens, and database - setup instructions weren't visible to users, moved to docker-compose - email config had circular references causing warnings - tested admin user creation and confirmed working everything works now, fully tested * Update blueprints/passbolt/template.toml --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> * feat: Add Kokoro TTS FastAPI template (#353) (#403) * feat: Add Kokoro TTS FastAPI template (#353) - Add CPU-optimized docker-compose.yml with source build - Add GPU-optimized docker-compose-gpu.yml for NVIDIA support - Add comprehensive template.toml with OpenAI-compatible API docs - Add kokoro-tts.svg logo and meta.json entry - Support streaming audio, timestamps, and multi-language TTS - Resolves #353 * updated the meta.json for the build errors * removed the docker-compose-gpu.yml file * Update docker-compose.yml --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> * chore: remove package-lock.json file from the app directory * chore: update Tolgee to latest version and fix SMTP config typo (#432) * chore: update Tolgee to latest version and fix SMTP config typo * Update docker-compose.yml * Update docker-compose.yml --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> * fix: improve Docker Compose validation workflow to handle subshell issues - Converted the handling of COMPOSE_FILES from a pipe to an array to ensure error propagation in the parent shell. - Updated the loop to iterate over the array for better reliability in the validation process. * refactor: enhance Docker Compose validation workflow to improve error handling - Replaced the pipe with an array to handle directory names, ensuring that errors within the loop propagate correctly to the parent shell. - Updated the loop structure for better reliability in processing the directories. * Feat: Add parseable (#460) * Add parseable * Update docker-compose.yml * Update docker-compose.yml * Update blueprints/parseable/template.toml --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Co-authored-by: Mauricio Siu * feat: add ChirpStack LoRaWAN Network Server template (#486) * feat: add ChirpStack LoRaWAN Network Server template Add complete ChirpStack v4 template with: - Main ChirpStack server with web UI - UDP and Basics Station gateway bridges - REST API interface - PostgreSQL database with PostGIS extensions - Redis cache - Mosquitto MQTT broker Default configuration for EU868 region with secure random credentials. Supports all LoRaWAN frequency bands globally. * fix(chirpstack): use original configurations from chirpstack-docker repo Update template.toml to use exact configuration files from the chirpstack-docker repository instead of simplified versions: - Use original chirpstack.toml with all 15 enabled regions - Use original gateway bridge configuration with documentation links - Use complete Basics Station EU868 config with frequency plans - Keep original Mosquitto and PostgreSQL initialization scripts Template size increased from 131 to 219 lines (4.7KB) to include comprehensive default configurations that match the official setup. * feat: add all 38 region configuration files * fix(chirpstack): add volume mounts to expose config files to containers * fix(chirpstack): remove read-only flag * fix(chirpstack): correct file paths for configuration mounts in docker-compose and template files * fix: update volume paths to be on correct directory level * fix: configure template for dokploy-network with proper DNS resolution - Add dokploy-network configuration to docker-compose.yml - Replace environment variable placeholders with actual service hostnames - Change PostgreSQL DSN from $POSTGRESQL_HOST to postgres - Change Redis server from $REDIS_HOST to redis - Replace $MQTT_BROKER_HOST with mosquitto in all 39 region configurations These changes ensure Docker DNS resolution works correctly by: - Using dokploy-network (overlay) instead of bridge network - Using service names directly in TOML config files (TOML doesn't expand env vars) - Enabling proper service discovery between containers This resolves DNS resolution failures that caused ChirpStack to fail connecting to PostgreSQL and MQTT services during deployment. * fix: add missing network configurations for all services in docker-compose * feat: add internal services to config.domains for proper network configuration * Update docker-compose.yml * fix: enhance domain validation in template validator - Updated the TemplateValidator to ensure that if the 'host' field is provided, it must be a valid string. - Added comments to clarify that 'host' is optional for internal services. * refactor: remove redundant host validation in template validator - Removed the validation for the 'host' field in the TemplateValidator, as it is optional for internal services and does not require a type check if not provided. * refactor: remove internal service domain configurations from template - Eliminated the domain configurations for internal services (Postgres, Redis, Mosquitto) from the template.toml file, streamlining the configuration for better clarity and maintainability. --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Co-authored-by: Mauricio Siu * Update section title from 'Suggestions' to 'Requirements' * Feat : Add MCSManager template support (#521) (#522) * feat: Add MCSManager template support (#521) * Update docker-compose.yml --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> * feat: Add MediaCMS template (#524) * Feat : Add Quant-Ux template -#173 (#525) * Feat : Add Quant-Ux template -#173 * Remove extra newline in docker-compose.yml * Update blueprints/quant-ux/docker-compose.yml * Update blueprints/quant-ux/docker-compose.yml * Update blueprints/quant-ux/docker-compose.yml * Update blueprints/quant-ux/docker-compose.yml --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> * fix(rustdesk): use explicit ports, use port 21118 on hbbs instead of hbbr (#526) * fix: use explicit ports, use port 21118 on hbbs instead of hbbr * fix: whitespace character in rustdesk * feat: Add anytype template (#527) * add anytype template * sort * Update name field for Anytype in meta.json * Update meta.json * Update docker-compose.yml * Update blueprints/anytype/docker-compose.yml --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> * refactor: remove dokploy-network configurations from multiple docker-compose files - Removed the external dokploy-network configuration from various services' docker-compose.yml files to streamline network management. - This change simplifies the setup and ensures consistency across blueprints. * chore: upgrade Infisical from v0.90.1 to v0.135.0 (#529) * chore: upgrade Infisical from v0.90.1 to v0.135.0 * Update docker-compose.yml --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> * fix: update pull request template link for clarity - Changed the link in the pull request template from 'general suggestions' to 'general requirements' to better reflect the content and ensure users follow the correct guidelines when creating templates. * chore: add section for screenshots or videos in pull request template - Introduced a new section in the pull request template to encourage contributors to include screenshots or videos, enhancing the clarity and context of their submissions. * Feat : Add MuleSoft ESB Runtime Template (#498) * added the mulesoft esb template * updated the compose and the meta.json * feat(mulesoft-esb): update image and add dynamic env configuration - Updated image to hari1367709/mule-esb:latest - Added dynamic HTTP_PORT for runtime port configuration - Added MULE_VERSION environment variable for Mule ESB version selection * updated the meta.json to use the version as latest * added a comment line to the template file * updated the mule runtime image * fix(mulesoft-esb): update ports configuration to follow guidelines * updated the port to use the env(HTTP_PORT) * Update docker-compose.yml * Update docker-compose.yml * Update blueprints/mulesoft-esb/docker-compose.yml --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> * feat(blueprint): update trmnl-byos-laravel template (#533) * feat(blueprint): update trmnl-byos-laravel template * Update docker-compose.yml --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> * feat(blueprint): peerdb template (#579) * feat(blueprint): initial attempt at peerdb template * fix: entrypoint and healthcheck * fix: entrypoint * fix: temporarily remove network * fix: temporal port * chore: remove 36987 for minio * fix: remove peerdb 9900 port exposure * fix: port for console * fix: minio env fix * fix: expose peerdb and minio to dokploy network * fix(peerdb): add defaults * fix: remove extra hosts * fix: remove network entries * fix: use consistent environment variables * feat: add Bluesky PDS template (#542) * feat: Bluesky PDS template * chore: add bluesky pds svg * chore: metadata for bluesky pds * yaml > yml * pnpm lock * fix: correct rotation key config * fix volumes * fix: volumes in the pds compose * define volumes in compose * fix: 32 bit rotation key * create pds.env correctly * some extra fixes * more extra fixes * a blank line * update pnpm lock * Add dokploy-prom-monitoring-extension template with comprehensive tests and documentation (#548) * Add dokploy-prom-monitoring-extension template with comprehensive tests and documentation * Fix METRICS_CONFIG environment variable: use single-line JSON format * Fix template.toml: use correct [config.env] syntax for environment variables * Fix docker-compose.yml: add env_file reference to load environment variables * Delete blueprints/dokploy-prom-monitoring-extension/README.md * Delete test-dokploy-prom-monitoring-extension.sh --------- Co-authored-by: Sanjeevi Subramani Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> * feat: improve RustDesk template configuration (#571) * feat: improve RustDesk template configuration - Add comprehensive environment variables for RustDesk server - Add RELAY_HOST, API_SERVER, ID_SERVER, and ENCRYPTION_KEY variables - Follow Dokploy best practices (no container_name, proper port format) - Use restart: unless-stopped policy - Add encryption key generation with password helper * fix: use explicit port mapping for RustDesk services RustDesk requires explicit port bindings (host:container format) to function properly. The service uses specific ports for: - 21115-21116 (TCP/UDP): hbbs service for ID and NAT traversal - 21117-21119 (TCP): hbbr relay service Without explicit port mapping, RustDesk clients cannot establish connections to the server. This is an exception to Dokploy's general port guidelines due to RustDesk's specific networking requirements. --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> * feat: add Mumble voice chat server template (#572) * feat: add Mumble voice chat server template - Add Mumble VoIP server blueprint with docker-compose.yml - Configure environment variables for superuser password, welcome text, and max users - Add template.toml with auto-generated secure password - Follow Dokploy best practices (no container_name, proper port format) - Add Mumble metadata to meta.json with proper tags - Support for TCP and UDP on port 64738 * Update template.toml * fix: correct JSON formatting in meta.json for Mumble template entry --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Co-authored-by: Mauricio Siu * fix: update WireGuard Easy template for proper functionality (#573) * fix: update WireGuard Easy template for proper functionality - Changed to named volume (etc_wireguard) instead of host path mount - Added explicit port mappings (51820:51820/udp, 51821:51821/tcp) required for WireGuard - Updated environment variables to use correct WG_HOST and PASSWORD format - Added all required WireGuard environment variables: - WG_PORT, PORT, WG_MTU, WG_DEFAULT_DNS, WG_ALLOWED_IPS - WG_POST_UP/WG_POST_DOWN for iptables rules - Added NET_RAW capability for proper network operations - Simplified template.toml to use WIREGUARD_HOST and WIREGUARD_PASSWORD - Removed explicit networks config to enable Dokploy's isolated deployment - Template now works with Dokploy's automatic network isolation This configuration has been tested and confirmed working with isolated deployment enabled. * Update template.toml --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> * add: restart policy to MinIO service (#576) restart: unless-stopped is a Docker restart policy that automatically restarts a container if it stops due to an error or Docker daemon restart --------- Co-authored-by: Sunil Shrestha Co-authored-by: Rabithua Co-authored-by: Mauricio Siu Co-authored-by: Scan <103391616+scanash00@users.noreply.github.com> Co-authored-by: Crackvignoule Co-authored-by: florianheysen <39408021+florianheysen@users.noreply.github.com> Co-authored-by: Thiago MadPin Co-authored-by: BlinkStrike <18644035+BlinkStrike@users.noreply.github.com> Co-authored-by: M Jupri Amin <127651222+Juupeee@users.noreply.github.com> Co-authored-by: Harikrishnan Dhanasekaran Co-authored-by: Kamil Dzieniszewski Co-authored-by: Nick Anderson Co-authored-by: lefolalan Co-authored-by: Chris <31969757+ChrisvanChip@users.noreply.github.com> Co-authored-by: kipavy <88386090+kipavy@users.noreply.github.com> Co-authored-by: Benjamin Nussbaum Co-authored-by: Khiet Tam Nguyen <86177399+nktnet1@users.noreply.github.com> Co-authored-by: Vidhya LKG for IT <24915474+VidhyaSanjeevi@users.noreply.github.com> Co-authored-by: Sanjeevi Subramani Co-authored-by: Muzaffer Kadir YILMAZ <34358176+muzafferkadir@users.noreply.github.com> Co-authored-by: Jemg * Remove `container_name` from all containers * Remove expliced networks * Update blueprints/appwrite/docker-compose.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update password variables to use 32-character length * Update Appwrite description and tags in meta.json * Fix JSON formatting in meta.json by adding missing closing bracket * Fix formatting in meta.json by removing trailing spaces in tags array * Add missing SMS and Backblaze storage configuration options in template.toml * Update docker-compose.yml --------- Co-authored-by: Pascal Oberbeck Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Co-authored-by: Sunil Shrestha Co-authored-by: Rabithua Co-authored-by: Mauricio Siu Co-authored-by: Scan <103391616+scanash00@users.noreply.github.com> Co-authored-by: Crackvignoule Co-authored-by: florianheysen <39408021+florianheysen@users.noreply.github.com> Co-authored-by: Thiago MadPin Co-authored-by: BlinkStrike <18644035+BlinkStrike@users.noreply.github.com> Co-authored-by: M Jupri Amin <127651222+Juupeee@users.noreply.github.com> Co-authored-by: Harikrishnan Dhanasekaran Co-authored-by: Kamil Dzieniszewski Co-authored-by: Nick Anderson Co-authored-by: lefolalan Co-authored-by: Chris <31969757+ChrisvanChip@users.noreply.github.com> Co-authored-by: kipavy <88386090+kipavy@users.noreply.github.com> Co-authored-by: Benjamin Nussbaum Co-authored-by: Khiet Tam Nguyen <86177399+nktnet1@users.noreply.github.com> Co-authored-by: Vidhya LKG for IT <24915474+VidhyaSanjeevi@users.noreply.github.com> Co-authored-by: Sanjeevi Subramani Co-authored-by: Muzaffer Kadir YILMAZ <34358176+muzafferkadir@users.noreply.github.com> Co-authored-by: Jemg Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Mauricio Siu --- blueprints/appwrite/docker-compose.yml | 342 ++++++++++--------------- blueprints/appwrite/template.toml | 54 +++- meta.json | 8 +- 3 files changed, 194 insertions(+), 210 deletions(-) diff --git a/blueprints/appwrite/docker-compose.yml b/blueprints/appwrite/docker-compose.yml index 130bb5d31..cb6222843 100644 --- a/blueprints/appwrite/docker-compose.yml +++ b/blueprints/appwrite/docker-compose.yml @@ -6,31 +6,32 @@ x-logging: &x-logging options: max-file: "5" max-size: "10m" - services: appwrite: - image: appwrite/appwrite:1.6.1 - container_name: appwrite + image: appwrite/appwrite:1.8.0 <<: *x-logging restart: unless-stopped - networks: - - dokploy-network labels: - traefik.enable=true - traefik.constraint-label-stack=appwrite volumes: - appwrite-uploads:/storage/uploads:rw + - appwrite-imports:/storage/imports:rw - appwrite-cache:/storage/cache:rw - appwrite-config:/storage/config:rw - appwrite-certificates:/storage/certificates:rw - appwrite-functions:/storage/functions:rw + - appwrite-sites:/storage/sites:rw + - appwrite-builds:/storage/builds:rw depends_on: - mariadb - redis + # - clamav environment: - _APP_ENV - _APP_WORKER_PER_CORE - _APP_LOCALE + - _APP_COMPRESSION_MIN_SIZE_BYTES - _APP_CONSOLE_WHITELIST_ROOT - _APP_CONSOLE_WHITELIST_EMAILS - _APP_CONSOLE_SESSION_ALERTS @@ -43,10 +44,14 @@ services: - _APP_OPTIONS_ABUSE - _APP_OPTIONS_ROUTER_PROTECTION - _APP_OPTIONS_FORCE_HTTPS - - _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS + - _APP_OPTIONS_ROUTER_FORCE_HTTPS - _APP_OPENSSL_KEY_V1 - _APP_DOMAIN - - _APP_DOMAIN_TARGET + - _APP_DOMAIN_TARGET_CNAME + - _APP_DOMAIN_TARGET_AAAA + - _APP_DOMAIN_TARGET_A + - _APP_DOMAIN_TARGET_CAA + - _APP_DNS - _APP_DOMAIN_FUNCTIONS - _APP_REDIS_HOST - _APP_REDIS_PORT @@ -73,6 +78,7 @@ services: - _APP_STORAGE_S3_SECRET - _APP_STORAGE_S3_REGION - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_S3_ENDPOINT - _APP_STORAGE_DO_SPACES_ACCESS_KEY - _APP_STORAGE_DO_SPACES_SECRET - _APP_STORAGE_DO_SPACES_REGION @@ -89,21 +95,26 @@ services: - _APP_STORAGE_WASABI_SECRET - _APP_STORAGE_WASABI_REGION - _APP_STORAGE_WASABI_BUCKET - - _APP_FUNCTIONS_SIZE_LIMIT + - _APP_COMPUTE_SIZE_LIMIT - _APP_FUNCTIONS_TIMEOUT - - _APP_FUNCTIONS_BUILD_TIMEOUT - - _APP_FUNCTIONS_CPUS - - _APP_FUNCTIONS_MEMORY + - _APP_SITES_TIMEOUT + - _APP_COMPUTE_BUILD_TIMEOUT + - _APP_COMPUTE_CPUS + - _APP_COMPUTE_MEMORY - _APP_FUNCTIONS_RUNTIMES + - _APP_SITES_RUNTIMES + - _APP_DOMAIN_SITES - _APP_EXECUTOR_SECRET - _APP_EXECUTOR_HOST - _APP_LOGGING_CONFIG - _APP_MAINTENANCE_INTERVAL - _APP_MAINTENANCE_DELAY + - _APP_MAINTENANCE_START_TIME - _APP_MAINTENANCE_RETENTION_EXECUTION - _APP_MAINTENANCE_RETENTION_CACHE - _APP_MAINTENANCE_RETENTION_ABUSE - _APP_MAINTENANCE_RETENTION_AUDIT + - _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE - _APP_MAINTENANCE_RETENTION_USAGE_HOURLY - _APP_MAINTENANCE_RETENTION_SCHEDULES - _APP_SMS_PROVIDER @@ -120,94 +131,25 @@ services: - _APP_MIGRATIONS_FIREBASE_CLIENT_ID - _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET - _APP_ASSISTANT_OPENAI_API_KEY - appwrite-console: - image: appwrite/console:5.0.12 - container_name: appwrite-console <<: *x-logging + image: appwrite/console:7.4.7 restart: unless-stopped - networks: - - dokploy-network labels: - "traefik.enable=true" - "traefik.constraint-label-stack=appwrite" - environment: - - _APP_ENV - - _APP_WORKER_PER_CORE - - _APP_LOCALE - - _APP_CONSOLE_WHITELIST_ROOT - - _APP_CONSOLE_WHITELIST_EMAILS - - _APP_CONSOLE_SESSION_ALERTS - - _APP_CONSOLE_WHITELIST_IPS - - _APP_CONSOLE_HOSTNAMES - - _APP_SYSTEM_EMAIL_NAME - - _APP_SYSTEM_EMAIL_ADDRESS - - _APP_EMAIL_SECURITY - - _APP_SYSTEM_RESPONSE_FORMAT - - _APP_OPTIONS_ABUSE - - _APP_OPTIONS_ROUTER_PROTECTION - - _APP_OPTIONS_FORCE_HTTPS - - _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS - - _APP_OPENSSL_KEY_V1 - - _APP_DOMAIN - - _APP_DOMAIN_TARGET - - _APP_DOMAIN_FUNCTIONS - - _APP_REDIS_HOST - - _APP_REDIS_PORT - - _APP_REDIS_USER - - _APP_REDIS_PASS - - _APP_DB_HOST - - _APP_DB_PORT - - _APP_DB_SCHEMA - - _APP_DB_USER - - _APP_DB_PASS - - _APP_SMTP_HOST - - _APP_SMTP_PORT - - _APP_SMTP_SECURE - - _APP_SMTP_USERNAME - - _APP_SMTP_PASSWORD - - _APP_USAGE_STATS - - _APP_STORAGE_LIMIT - - _APP_STORAGE_PREVIEW_LIMIT - - _APP_STORAGE_ANTIVIRUS - - _APP_STORAGE_ANTIVIRUS_HOST - - _APP_STORAGE_ANTIVIRUS_PORT - - _APP_STORAGE_DEVICE - - _APP_STORAGE_S3_ACCESS_KEY - - _APP_STORAGE_S3_SECRET - - _APP_STORAGE_S3_REGION - - _APP_STORAGE_S3_BUCKET - - _APP_STORAGE_DO_SPACES_ACCESS_KEY - - _APP_STORAGE_DO_SPACES_SECRET - - _APP_STORAGE_DO_SPACES_REGION - - _APP_STORAGE_DO_SPACES_BUCKET - - _APP_STORAGE_BACKBLAZE_ACCESS_KEY - - _APP_STORAGE_BACKBLAZE_SECRET - - _APP_STORAGE_BACKBLAZE_REGION - - _APP_STORAGE_BACKBLAZE_BUCKET - - _APP_STORAGE_LINODE_ACCESS_KEY - - _APP_STORAGE_LINODE_SECRET - - _APP_STORAGE_LINODE_REGION - - _APP_STORAGE_LINODE_BUCKET - - _APP_STORAGE_WASABI_ACCESS_KEY - - _APP_STORAGE_WASABI_SECRET - - _APP_STORAGE_WASABI_REGION - - _APP_STORAGE_WASABI_BUCKET appwrite-realtime: - image: appwrite/appwrite:1.6.1 + image: appwrite/appwrite:1.8.0 entrypoint: realtime - container_name: appwrite-realtime <<: *x-logging restart: unless-stopped - networks: - - dokploy-network - depends_on: - - mariadb - - redis labels: - "traefik.enable=true" - "traefik.constraint-label-stack=appwrite" + depends_on: + - mariadb + - redis environment: - _APP_ENV - _APP_WORKER_PER_CORE @@ -227,13 +169,10 @@ services: - _APP_LOGGING_CONFIG appwrite-worker-audits: - image: appwrite/appwrite:1.6.1 + image: appwrite/appwrite:1.8.0 entrypoint: worker-audits <<: *x-logging - container_name: appwrite-worker-audits restart: unless-stopped - networks: - - dokploy-network depends_on: - redis - mariadb @@ -253,13 +192,10 @@ services: - _APP_LOGGING_CONFIG appwrite-worker-webhooks: - image: appwrite/appwrite:1.6.1 + image: appwrite/appwrite:1.8.0 entrypoint: worker-webhooks <<: *x-logging - container_name: appwrite-worker-webhooks restart: unless-stopped - networks: - - dokploy-network depends_on: - redis - mariadb @@ -281,13 +217,10 @@ services: - _APP_LOGGING_CONFIG appwrite-worker-deletes: - image: appwrite/appwrite:1.6.1 + image: appwrite/appwrite:1.8.0 entrypoint: worker-deletes <<: *x-logging - container_name: appwrite-worker-deletes restart: unless-stopped - networks: - - dokploy-network depends_on: - redis - mariadb @@ -295,6 +228,7 @@ services: - appwrite-uploads:/storage/uploads:rw - appwrite-cache:/storage/cache:rw - appwrite-functions:/storage/functions:rw + - appwrite-sites:/storage/sites:rw - appwrite-builds:/storage/builds:rw - appwrite-certificates:/storage/certificates:rw environment: @@ -315,6 +249,7 @@ services: - _APP_STORAGE_S3_SECRET - _APP_STORAGE_S3_REGION - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_S3_ENDPOINT - _APP_STORAGE_DO_SPACES_ACCESS_KEY - _APP_STORAGE_DO_SPACES_SECRET - _APP_STORAGE_DO_SPACES_REGION @@ -336,16 +271,16 @@ services: - _APP_EXECUTOR_HOST - _APP_MAINTENANCE_RETENTION_ABUSE - _APP_MAINTENANCE_RETENTION_AUDIT + - _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE - _APP_MAINTENANCE_RETENTION_EXECUTION + - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS + - _APP_EMAIL_CERTIFICATES appwrite-worker-databases: - image: appwrite/appwrite:1.6.1 + image: appwrite/appwrite:1.8.0 entrypoint: worker-databases <<: *x-logging - container_name: appwrite-worker-databases restart: unless-stopped - networks: - - dokploy-network depends_on: - redis - mariadb @@ -365,19 +300,18 @@ services: - _APP_LOGGING_CONFIG appwrite-worker-builds: - image: appwrite/appwrite:1.6.1 + image: appwrite/appwrite:1.8.0 entrypoint: worker-builds <<: *x-logging - container_name: appwrite-worker-builds restart: unless-stopped - networks: - - dokploy-network depends_on: - redis - mariadb volumes: - appwrite-functions:/storage/functions:rw + - appwrite-sites:/storage/sites:rw - appwrite-builds:/storage/builds:rw + - appwrite-uploads:/storage/uploads:rw environment: - _APP_ENV - _APP_WORKER_PER_CORE @@ -398,18 +332,20 @@ services: - _APP_VCS_GITHUB_PRIVATE_KEY - _APP_VCS_GITHUB_APP_ID - _APP_FUNCTIONS_TIMEOUT - - _APP_FUNCTIONS_BUILD_TIMEOUT - - _APP_FUNCTIONS_CPUS - - _APP_FUNCTIONS_MEMORY - - _APP_FUNCTIONS_SIZE_LIMIT + - _APP_SITES_TIMEOUT + - _APP_COMPUTE_BUILD_TIMEOUT + - _APP_COMPUTE_CPUS + - _APP_COMPUTE_MEMORY + - _APP_COMPUTE_SIZE_LIMIT - _APP_OPTIONS_FORCE_HTTPS - - _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS + - _APP_OPTIONS_ROUTER_FORCE_HTTPS - _APP_DOMAIN - _APP_STORAGE_DEVICE - _APP_STORAGE_S3_ACCESS_KEY - _APP_STORAGE_S3_SECRET - _APP_STORAGE_S3_REGION - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_S3_ENDPOINT - _APP_STORAGE_DO_SPACES_ACCESS_KEY - _APP_STORAGE_DO_SPACES_SECRET - _APP_STORAGE_DO_SPACES_REGION @@ -426,15 +362,13 @@ services: - _APP_STORAGE_WASABI_SECRET - _APP_STORAGE_WASABI_REGION - _APP_STORAGE_WASABI_BUCKET + - _APP_DOMAIN_SITES appwrite-worker-certificates: - image: appwrite/appwrite:1.6.1 + image: appwrite/appwrite:1.8.0 entrypoint: worker-certificates <<: *x-logging - container_name: appwrite-worker-certificates restart: unless-stopped - networks: - - dokploy-network depends_on: - redis - mariadb @@ -446,7 +380,11 @@ services: - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - _APP_DOMAIN - - _APP_DOMAIN_TARGET + - _APP_DOMAIN_TARGET_CNAME + - _APP_DOMAIN_TARGET_AAAA + - _APP_DOMAIN_TARGET_A + - _APP_DOMAIN_TARGET_CAA + - _APP_DNS - _APP_DOMAIN_FUNCTIONS - _APP_EMAIL_CERTIFICATES - _APP_REDIS_HOST @@ -461,13 +399,10 @@ services: - _APP_LOGGING_CONFIG appwrite-worker-functions: - image: appwrite/appwrite:1.6.1 + image: appwrite/appwrite:1.8.0 entrypoint: worker-functions <<: *x-logging - container_name: appwrite-worker-functions restart: unless-stopped - networks: - - dokploy-network depends_on: - redis - mariadb @@ -488,9 +423,10 @@ services: - _APP_DB_USER - _APP_DB_PASS - _APP_FUNCTIONS_TIMEOUT - - _APP_FUNCTIONS_BUILD_TIMEOUT - - _APP_FUNCTIONS_CPUS - - _APP_FUNCTIONS_MEMORY + - _APP_SITES_TIMEOUT + - _APP_COMPUTE_BUILD_TIMEOUT + - _APP_COMPUTE_CPUS + - _APP_COMPUTE_MEMORY - _APP_EXECUTOR_SECRET - _APP_EXECUTOR_HOST - _APP_USAGE_STATS @@ -499,13 +435,10 @@ services: - _APP_LOGGING_CONFIG appwrite-worker-mails: - image: appwrite/appwrite:1.6.1 + image: appwrite/appwrite:1.8.0 entrypoint: worker-mails <<: *x-logging - container_name: appwrite-worker-mails restart: unless-stopped - networks: - - dokploy-network depends_on: - redis environment: @@ -529,15 +462,14 @@ services: - _APP_SMTP_USERNAME - _APP_SMTP_PASSWORD - _APP_LOGGING_CONFIG + - _APP_DOMAIN + - _APP_OPTIONS_FORCE_HTTPS appwrite-worker-messaging: - image: appwrite/appwrite:1.6.1 + image: appwrite/appwrite:1.8.0 entrypoint: worker-messaging - container_name: appwrite-worker-messaging <<: *x-logging restart: unless-stopped - networks: - - dokploy-network volumes: - appwrite-uploads:/storage/uploads:rw depends_on: @@ -563,6 +495,7 @@ services: - _APP_STORAGE_S3_SECRET - _APP_STORAGE_S3_REGION - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_S3_ENDPOINT - _APP_STORAGE_DO_SPACES_ACCESS_KEY - _APP_STORAGE_DO_SPACES_SECRET - _APP_STORAGE_DO_SPACES_REGION @@ -581,13 +514,12 @@ services: - _APP_STORAGE_WASABI_BUCKET appwrite-worker-migrations: - image: appwrite/appwrite:1.6.1 + image: appwrite/appwrite:1.8.0 entrypoint: worker-migrations <<: *x-logging - container_name: appwrite-worker-migrations restart: unless-stopped - networks: - - dokploy-network + volumes: + - appwrite-imports:/storage/imports:rw depends_on: - mariadb environment: @@ -595,7 +527,11 @@ services: - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - _APP_DOMAIN - - _APP_DOMAIN_TARGET + - _APP_DOMAIN_TARGET_CNAME + - _APP_DOMAIN_TARGET_AAAA + - _APP_DOMAIN_TARGET_A + - _APP_DOMAIN_TARGET_CAA + - _APP_DNS - _APP_EMAIL_SECURITY - _APP_REDIS_HOST - _APP_REDIS_PORT @@ -611,20 +547,21 @@ services: - _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET appwrite-task-maintenance: - image: appwrite/appwrite:1.6.1 + image: appwrite/appwrite:1.8.0 entrypoint: maintenance <<: *x-logging - container_name: appwrite-task-maintenance restart: unless-stopped - networks: - - dokploy-network depends_on: - redis environment: - _APP_ENV - _APP_WORKER_PER_CORE - _APP_DOMAIN - - _APP_DOMAIN_TARGET + - _APP_DOMAIN_TARGET_CNAME + - _APP_DOMAIN_TARGET_AAAA + - _APP_DOMAIN_TARGET_A + - _APP_DOMAIN_TARGET_CAA + - _APP_DNS - _APP_DOMAIN_FUNCTIONS - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST @@ -641,17 +578,15 @@ services: - _APP_MAINTENANCE_RETENTION_CACHE - _APP_MAINTENANCE_RETENTION_ABUSE - _APP_MAINTENANCE_RETENTION_AUDIT + - _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE - _APP_MAINTENANCE_RETENTION_USAGE_HOURLY - _APP_MAINTENANCE_RETENTION_SCHEDULES - appwrite-worker-usage: - image: appwrite/appwrite:1.6.1 - entrypoint: worker-usage - container_name: appwrite-worker-usage + appwrite-task-stats-resources: + image: appwrite/appwrite:1.8.0 + entrypoint: stats-resources <<: *x-logging restart: unless-stopped - networks: - - dokploy-network depends_on: - redis - mariadb @@ -670,15 +605,39 @@ services: - _APP_REDIS_PASS - _APP_USAGE_STATS - _APP_LOGGING_CONFIG - - _APP_USAGE_AGGREGATION_INTERVAL + - _APP_DATABASE_SHARED_TABLES + - _APP_STATS_RESOURCES_INTERVAL - appwrite-worker-usage-dump: - image: appwrite/appwrite:1.6.1 - entrypoint: worker-usage-dump - container_name: appwrite-worker-usage-dump + appwrite-worker-stats-resources: + image: appwrite/appwrite:1.8.0 + entrypoint: worker-stats-resources <<: *x-logging - networks: - - dokploy-network + restart: unless-stopped + depends_on: + - redis + - mariadb + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_USAGE_STATS + - _APP_LOGGING_CONFIG + - _APP_STATS_RESOURCES_INTERVAL + + appwrite-worker-stats-usage: + image: appwrite/appwrite:1.8.0 + entrypoint: worker-stats-usage + <<: *x-logging + restart: unless-stopped depends_on: - redis - mariadb @@ -700,13 +659,10 @@ services: - _APP_USAGE_AGGREGATION_INTERVAL appwrite-task-scheduler-functions: - image: appwrite/appwrite:1.6.1 + image: appwrite/appwrite:1.8.0 entrypoint: schedule-functions - container_name: appwrite-task-scheduler-functions <<: *x-logging restart: unless-stopped - networks: - - dokploy-network depends_on: - mariadb - redis @@ -725,13 +681,10 @@ services: - _APP_DB_PASS appwrite-task-scheduler-executions: - image: appwrite/appwrite:1.6.1 + image: appwrite/appwrite:1.8.0 entrypoint: schedule-executions - container_name: appwrite-task-scheduler-executions <<: *x-logging restart: unless-stopped - networks: - - dokploy-network depends_on: - mariadb - redis @@ -750,13 +703,10 @@ services: - _APP_DB_PASS appwrite-task-scheduler-messages: - image: appwrite/appwrite:1.6.1 + image: appwrite/appwrite:1.8.0 entrypoint: schedule-messages - container_name: appwrite-task-scheduler-messages <<: *x-logging restart: unless-stopped - networks: - - dokploy-network depends_on: - mariadb - redis @@ -775,44 +725,48 @@ services: - _APP_DB_PASS appwrite-assistant: - image: appwrite/assistant:0.4.0 - container_name: appwrite-assistant + image: appwrite/assistant:0.8.3 <<: *x-logging restart: unless-stopped - networks: - - dokploy-network environment: - _APP_ASSISTANT_OPENAI_API_KEY + appwrite-browser: + image: appwrite/browser:0.2.4 + <<: *x-logging + restart: unless-stopped + openruntimes-executor: - container_name: openruntimes-executor hostname: exc1 <<: *x-logging restart: unless-stopped stop_signal: SIGINT - image: openruntimes/executor:0.6.11 - networks: - - dokploy-network + image: openruntimes/executor:0.7.22 volumes: - /var/run/docker.sock:/var/run/docker.sock - appwrite-builds:/storage/builds:rw - appwrite-functions:/storage/functions:rw + - appwrite-sites:/storage/sites:rw + # Host mount necessary to share files between executor and runtimes. + # It's not possible to share mount file between 2 containers without host mount (copying is too slow) - /tmp:/tmp:rw environment: - - OPR_EXECUTOR_INACTIVE_TRESHOLD=$_APP_FUNCTIONS_INACTIVE_THRESHOLD - - OPR_EXECUTOR_MAINTENANCE_INTERVAL=$_APP_FUNCTIONS_MAINTENANCE_INTERVAL - - OPR_EXECUTOR_NETWORK=$_APP_FUNCTIONS_RUNTIMES_NETWORK + - OPR_EXECUTOR_INACTIVE_TRESHOLD=$_APP_COMPUTE_INACTIVE_THRESHOLD + - OPR_EXECUTOR_MAINTENANCE_INTERVAL=$_APP_COMPUTE_MAINTENANCE_INTERVAL + - OPR_EXECUTOR_NETWORK=$_APP_COMPUTE_RUNTIMES_NETWORK - OPR_EXECUTOR_DOCKER_HUB_USERNAME=$_APP_DOCKER_HUB_USERNAME - OPR_EXECUTOR_DOCKER_HUB_PASSWORD=$_APP_DOCKER_HUB_PASSWORD - OPR_EXECUTOR_ENV=$_APP_ENV - - OPR_EXECUTOR_RUNTIMES=$_APP_FUNCTIONS_RUNTIMES + - OPR_EXECUTOR_RUNTIMES=$_APP_FUNCTIONS_RUNTIMES,$_APP_SITES_RUNTIMES - OPR_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET + - OPR_EXECUTOR_RUNTIME_VERSIONS=v5 - OPR_EXECUTOR_LOGGING_CONFIG=$_APP_LOGGING_CONFIG - OPR_EXECUTOR_STORAGE_DEVICE=$_APP_STORAGE_DEVICE - OPR_EXECUTOR_STORAGE_S3_ACCESS_KEY=$_APP_STORAGE_S3_ACCESS_KEY - OPR_EXECUTOR_STORAGE_S3_SECRET=$_APP_STORAGE_S3_SECRET - OPR_EXECUTOR_STORAGE_S3_REGION=$_APP_STORAGE_S3_REGION - OPR_EXECUTOR_STORAGE_S3_BUCKET=$_APP_STORAGE_S3_BUCKET + - OPR_EXECUTOR_STORAGE_S3_ENDPOINT=$_APP_STORAGE_S3_ENDPOINT - OPR_EXECUTOR_STORAGE_DO_SPACES_ACCESS_KEY=$_APP_STORAGE_DO_SPACES_ACCESS_KEY - OPR_EXECUTOR_STORAGE_DO_SPACES_SECRET=$_APP_STORAGE_DO_SPACES_SECRET - OPR_EXECUTOR_STORAGE_DO_SPACES_REGION=$_APP_STORAGE_DO_SPACES_REGION @@ -831,12 +785,9 @@ services: - OPR_EXECUTOR_STORAGE_WASABI_BUCKET=$_APP_STORAGE_WASABI_BUCKET mariadb: - image: mariadb:10.11 - container_name: appwrite-mariadb + image: mariadb:10.11 # fix issues when upgrading using: mysql_upgrade -u root -p <<: *x-logging restart: unless-stopped - networks: - - dokploy-network volumes: - appwrite-mariadb:/var/lib/mysql:rw environment: @@ -849,39 +800,30 @@ services: redis: image: redis:7.2.4-alpine - container_name: appwrite-redis <<: *x-logging restart: unless-stopped command: > redis-server - --maxmemory 512mb - --maxmemory-policy allkeys-lru - --maxmemory-samples 5 - networks: - - dokploy-network + --maxmemory 512mb + --maxmemory-policy allkeys-lru + --maxmemory-samples 5 volumes: - appwrite-redis:/data:rw -# Uncomment and configure if ClamAV is needed -# clamav: -# image: appwrite/clamav:1.2.0 -# container_name: appwrite-clamav -# restart: unless-stopped -# networks: -# - dokploy-network -# volumes: -# - appwrite-uploads:/storage/uploads + # clamav: + # image: appwrite/clamav:1.2.0 + # restart: unless-stopped + # volumes: + # - appwrite-uploads:/storage/uploads volumes: appwrite-mariadb: appwrite-redis: appwrite-cache: appwrite-uploads: + appwrite-imports: appwrite-certificates: appwrite-functions: + appwrite-sites: appwrite-builds: appwrite-config: - -networks: - dokploy-network: - external: true diff --git a/blueprints/appwrite/template.toml b/blueprints/appwrite/template.toml index 3188ea17b..cca2d43d6 100644 --- a/blueprints/appwrite/template.toml +++ b/blueprints/appwrite/template.toml @@ -1,5 +1,11 @@ [variables] main_domain = "${domain}" +functions_domain = "functions.${main_domain}" +sites_domain = "sites.${main_domain}" +openssl_key = "${password:32}" +db_root_pw = "${password:32}" +db_user_pw = "${password:32}" +executor_secret = "${password:32}" [config] env = [ @@ -8,11 +14,19 @@ env = [ "_APP_OPTIONS_ABUSE=enabled", "_APP_OPTIONS_FORCE_HTTPS=disabled", "_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled", + "_APP_OPTIONS_ROUTER_FORCE_HTTPS=disabled", "_APP_OPTIONS_ROUTER_PROTECTION=disabled", - "_APP_OPENSSL_KEY_V1=your-secret-key", + "_APP_OPENSSL_KEY_V1=${openssl_key}", "_APP_DOMAIN=${main_domain}", - "_APP_DOMAIN_FUNCTIONS=${main_domain}", - "_APP_DOMAIN_TARGET=${main_domain}", + "_APP_CUSTOM_DOMAIN_DENY_LIST=example.com,test.com,app.example.com", + "_APP_DOMAIN_FUNCTIONS=${functions_domain}", + "_APP_DOMAIN_SITES=${sites_domain}", + "_APP_DOMAIN_TARGET=localhost", + "_APP_DOMAIN_TARGET_CNAME=localhost", + "_APP_DOMAIN_TARGET_AAAA=::1", + "_APP_DOMAIN_TARGET_A=127.0.0.1", + "_APP_DOMAIN_TARGET_CAA=", + "_APP_DNS=8.8.8.8", "_APP_CONSOLE_WHITELIST_ROOT=enabled", "_APP_CONSOLE_WHITELIST_EMAILS=", "_APP_CONSOLE_WHITELIST_IPS=", @@ -32,6 +46,8 @@ env = [ "_APP_USAGE_DATABASE_INTERVAL=900", "_APP_WORKER_PER_CORE=6", "_APP_CONSOLE_SESSION_ALERTS=disabled", + "_APP_COMPRESSION_ENABLED=enabled", + "_APP_COMPRESSION_MIN_SIZE_BYTES=1024", "_APP_REDIS_HOST=redis", "_APP_REDIS_PORT=6379", "_APP_REDIS_USER=", @@ -40,8 +56,8 @@ env = [ "_APP_DB_PORT=3306", "_APP_DB_SCHEMA=appwrite", "_APP_DB_USER=user", - "_APP_DB_PASS=password", - "_APP_DB_ROOT_PASS=rootsecretpassword", + "_APP_DB_PASS=${db_user_pw}", + "_APP_DB_ROOT_PASS=${db_root_pw}", "_APP_INFLUXDB_HOST=influxdb", "_APP_INFLUXDB_PORT=8086", "_APP_STATSD_HOST=telegraf", @@ -63,6 +79,7 @@ env = [ "_APP_STORAGE_S3_SECRET=", "_APP_STORAGE_S3_REGION=us-east-1", "_APP_STORAGE_S3_BUCKET=", + "_APP_STORAGE_S3_ENDPOINT=", "_APP_STORAGE_DO_SPACES_ACCESS_KEY=", "_APP_STORAGE_DO_SPACES_SECRET=", "_APP_STORAGE_DO_SPACES_REGION=us-east-1", @@ -80,27 +97,36 @@ env = [ "_APP_STORAGE_WASABI_REGION=eu-central-1", "_APP_STORAGE_WASABI_BUCKET=", "_APP_FUNCTIONS_SIZE_LIMIT=30000000", + "_APP_COMPUTE_SIZE_LIMIT=30000000", "_APP_FUNCTIONS_BUILD_SIZE_LIMIT=2000000000", "_APP_FUNCTIONS_TIMEOUT=900", "_APP_FUNCTIONS_BUILD_TIMEOUT=900", + "_APP_COMPUTE_BUILD_TIMEOUT=900", "_APP_FUNCTIONS_CONTAINERS=10", "_APP_FUNCTIONS_CPUS=0", + "_APP_COMPUTE_CPUS=0", "_APP_FUNCTIONS_MEMORY=0", + "_APP_COMPUTE_MEMORY=0", "_APP_FUNCTIONS_MEMORY_SWAP=0", "_APP_FUNCTIONS_RUNTIMES=node-16.0,php-8.0,python-3.9,ruby-3.0", - "_APP_EXECUTOR_SECRET=your-secret-key", + "_APP_EXECUTOR_SECRET=${executor_secret}", "_APP_EXECUTOR_HOST=http://exc1/v1", "_APP_EXECUTOR_RUNTIME_NETWORK=appwrite_runtimes", "_APP_FUNCTIONS_ENVS=node-16.0,php-7.4,python-3.9,ruby-3.0", "_APP_FUNCTIONS_INACTIVE_THRESHOLD=60", + "_APP_COMPUTE_INACTIVE_THRESHOLD=60", "DOCKERHUB_PULL_USERNAME=", "DOCKERHUB_PULL_PASSWORD=", "DOCKERHUB_PULL_EMAIL=", "OPEN_RUNTIMES_NETWORK=appwrite_runtimes", - "_APP_FUNCTIONS_RUNTIMES_NETWORK=runtimes", + "_APP_FUNCTIONS_RUNTIMES_NETWORK=dokploy-network", + "_APP_COMPUTE_RUNTIMES_NETWORK=dokploy-network", "_APP_DOCKER_HUB_USERNAME=", "_APP_DOCKER_HUB_PASSWORD=", "_APP_FUNCTIONS_MAINTENANCE_INTERVAL=3600", + "_APP_COMPUTE_MAINTENANCE_INTERVAL=3600", + "_APP_SITES_TIMEOUT=900", + "_APP_SITES_RUNTIMES=static-1,node-22,flutter-3.29", "_APP_VCS_GITHUB_APP_NAME=", "_APP_VCS_GITHUB_PRIVATE_KEY=", "_APP_VCS_GITHUB_APP_ID=", @@ -109,9 +135,11 @@ env = [ "_APP_VCS_GITHUB_WEBHOOK_SECRET=", "_APP_MAINTENANCE_INTERVAL=86400", "_APP_MAINTENANCE_DELAY=0", + "_APP_MAINTENANCE_START_TIME=00:00", "_APP_MAINTENANCE_RETENTION_CACHE=2592000", "_APP_MAINTENANCE_RETENTION_EXECUTION=1209600", "_APP_MAINTENANCE_RETENTION_AUDIT=1209600", + "_APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE=15778800", "_APP_MAINTENANCE_RETENTION_ABUSE=86400", "_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000", "_APP_MAINTENANCE_RETENTION_SCHEDULES=86400", @@ -130,6 +158,18 @@ port = 80 host = "${main_domain}" path = "/" +[[config.domains]] +serviceName = "appwrite" +port = 80 +host = "${sites_domain}" +path = "/" + +[[config.domains]] +serviceName = "appwrite" +port = 80 +host = "${functions_domain}" +path = "/" + [[config.domains]] serviceName = "appwrite-console" port = 80 diff --git a/meta.json b/meta.json index 7dc8663c4..9689043e1 100644 --- a/meta.json +++ b/meta.json @@ -365,8 +365,8 @@ { "id": "appwrite", "name": "Appwrite", - "version": "1.6.1", - "description": "Appwrite is an end-to-end backend server for Web, Mobile, Native, or Backend apps. Appwrite abstracts the complexity and repetitiveness required to build a modern backend API from scratch and allows you to build secure apps faster.\nUsing Appwrite, you can easily integrate your app with user authentication and multiple sign-in methods, a database for storing and querying users and team data, storage and file management, image manipulation, Cloud Functions, messaging, and more services.", + "version": "1.8.0", + "description": "Appwrite is an end-to-end platform for building Web, Mobile, Native, or Backend apps, packaged as a set of Docker microservices. It includes both a backend server and a fully integrated hosting solution for deploying static and server-side rendered frontends. Appwrite abstracts the complexity and repetitiveness required to build modern apps from scratch and allows you to build secure, full-stack applications faster.\nUsing Appwrite, you can easily integrate your app with user authentication and multiple sign-in methods, a database for storing and querying users and team data, storage and file management, image manipulation, Cloud Functions, messaging, and more services.", "links": { "github": "https://github.com/appwrite/appwrite", "website": "https://appwrite.io/", @@ -376,7 +376,9 @@ "tags": [ "database", "firebase", - "postgres" + "mariadb", + "hosting", + "self-hosted" ] }, { From 7c2f0d4f8e533496f3814f58e688c1f9649d8cf8 Mon Sep 17 00:00:00 2001 From: Khiet Tam Nguyen <86177399+nktnet1@users.noreply.github.com> Date: Thu, 8 Jan 2026 06:02:50 +1100 Subject: [PATCH 75/91] feat: emqx template (MQTT Broker) (#556) * feat: template for emqx * chore: process meta * fix: use websocket on port 443 instead of 8084 * docs: improve comments for emqx template * fix: use emqx service name * fix: use dummy domain instead of traefik * fix: explicitly list dokplok-network * fix(emqx): restart unless stopped * fix(emqx): add healthcheck --- blueprints/emqx/docker-compose.yml | 58 ++++++++++++++++++++++++++++++ blueprints/emqx/emqx.svg | 6 ++++ blueprints/emqx/template.toml | 11 ++++++ meta.json | 17 +++++++++ 4 files changed, 92 insertions(+) create mode 100644 blueprints/emqx/docker-compose.yml create mode 100644 blueprints/emqx/emqx.svg create mode 100644 blueprints/emqx/template.toml diff --git a/blueprints/emqx/docker-compose.yml b/blueprints/emqx/docker-compose.yml new file mode 100644 index 000000000..4b11f0109 --- /dev/null +++ b/blueprints/emqx/docker-compose.yml @@ -0,0 +1,58 @@ +# This templates requires additional traefik port mapping and entry point +# for port 8883 (mqtts over TCP) +# +# For the full instructions, refer to: +# - https://github.com/Dokploy/dokploy/discussions/3126 +# +# The initial login credentials are: +# - USERNAME: admin +# - PASSWORD: public + +services: + emqx: + image: emqx/emqx-enterprise:6.0.1 + hostname: node1.emqx.com + environment: + EMQX_NODE_NAME: emqx@node1.emqx.com + expose: + - 1883 # MQTT + - 8083 # WS + - 18083 # Dashboard + volumes: + - emqx_data:/opt/emqx/data + - emqx_log:/opt/emqx/log + networks: + dokploy-network: + aliases: + - emqx-service + restart: unless-stopped + healthcheck: + test: ["CMD", "/opt/emqx/bin/emqx_ctl", "status"] + interval: 30s + timeout: 5s + retries: 3 + labels: + # MQTT over TLS + - "traefik.tcp.routers.emqx-mqtts.entrypoints=mqtts" + - "traefik.tcp.routers.emqx-mqtts.rule=HostSNI(`broker.yourdomain.com`)" # Change domain + - "traefik.tcp.routers.emqx-mqtts.tls.certresolver=letsencrypt" + - "traefik.tcp.routers.emqx-mqtts.service=emqx-service" + - "traefik.tcp.services.emqx-service.loadbalancer.server.port=1883" + + # + # Optional Web UI: + # - https://github.com/emqx/MQTTX/tree/main/web + # + # mqttx-web: + # image: emqx/mqttx-web:latest + # expose: + # - 80 + # + +volumes: + emqx_data: + emqx_log: + +networks: + dokploy-network: + external: true diff --git a/blueprints/emqx/emqx.svg b/blueprints/emqx/emqx.svg new file mode 100644 index 000000000..ce0c1ec9e --- /dev/null +++ b/blueprints/emqx/emqx.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/blueprints/emqx/template.toml b/blueprints/emqx/template.toml new file mode 100644 index 000000000..5fc03e5f2 --- /dev/null +++ b/blueprints/emqx/template.toml @@ -0,0 +1,11 @@ +[variables] + +[[config.domains]] +serviceName = "emqx" +port = 18083 +host = "emqx.yourdomain.com" + +[[config.domains]] +serviceName = "emqx" +port = 8083 +host = "broker.yourdomain.com" diff --git a/meta.json b/meta.json index 9689043e1..753d3f875 100644 --- a/meta.json +++ b/meta.json @@ -2103,6 +2103,23 @@ "media system" ] }, + { + "id": "emqx", + "name": "EMQX", + "version": "6.0.1", + "description": "A scalable and reliable MQTT broker for AI, IoT, IIoT and connected vehicles", + "logo": "emqx.svg", + "links": { + "github": "https://github.com/emqx/emqx", + "website": "https://www.emqx.com", + "docs": "https://docs.emqx.com" + }, + "tags": [ + "broker", + "iot", + "mqtt" + ] + }, { "id": "enshrouded", "name": "Enshrouded", From 1ead7d06a4d3b2fe1c7a1e8ab11147b602f6d118 Mon Sep 17 00:00:00 2001 From: Christus Vincent Date: Thu, 8 Jan 2026 03:11:03 +0800 Subject: [PATCH 76/91] Dokploy Deployment for Mautic 5 (#564) * first draft * second * try 3 * 4 * Update docker-compose.yml * Update template.toml * Enhance healthchecks and service dependencies in Docker Compose Updated healthcheck configurations for Mautic and MySQL services to improve service reliability. Added conditions to ensure services wait for dependencies to be healthy before starting. * Update Mautic docker-compose with health checks and roles --- blueprints/mautic/docker-compose.yml | 131 +++++++++++++++++++++++++++ blueprints/mautic/mautic.svg | 76 ++++++++++++++++ blueprints/mautic/template.toml | 52 +++++++++++ meta.json | 18 ++++ 4 files changed, 277 insertions(+) create mode 100644 blueprints/mautic/docker-compose.yml create mode 100644 blueprints/mautic/mautic.svg create mode 100644 blueprints/mautic/template.toml diff --git a/blueprints/mautic/docker-compose.yml b/blueprints/mautic/docker-compose.yml new file mode 100644 index 000000000..223ea7ee9 --- /dev/null +++ b/blueprints/mautic/docker-compose.yml @@ -0,0 +1,131 @@ +version: "3.8" + +services: + # ------------------------------------------------------------------------- + # Service 1: Database + # ------------------------------------------------------------------------- + mysql: + image: mysql:8.0 + command: --default-authentication-plugin=mysql_native_password + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} + MYSQL_DATABASE: ${MAUTIC_DB_DATABASE} + MYSQL_USER: ${MAUTIC_DB_USER} + MYSQL_PASSWORD: ${MAUTIC_DB_PASSWORD} + volumes: + - mysql_data:/var/lib/mysql + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] + interval: 10s + timeout: 5s + retries: 5 + + # ------------------------------------------------------------------------- + # Service 2: Mautic Web (The Leader) + # ------------------------------------------------------------------------- + mautic: + image: mautic/mautic:5.1.1-apache + restart: unless-stopped + depends_on: + mysql: + condition: service_healthy + ports: + - 80 + environment: + - DOCKER_MAUTIC_ROLE=mautic_web + - DOCKER_MAUTIC_RUN_MIGRATIONS=true + - MAUTIC_DB_HOST=${MAUTIC_DB_HOST} + - MAUTIC_DB_PORT=${MAUTIC_DB_PORT} + - MAUTIC_DB_DATABASE=${MAUTIC_DB_DATABASE} + - MAUTIC_DB_USER=${MAUTIC_DB_USER} + - MAUTIC_DB_PASSWORD=${MAUTIC_DB_PASSWORD} + - MAUTIC_URL=${MAUTIC_URL} + - MAUTIC_TRUSTED_PROXIES=${MAUTIC_TRUSTED_PROXIES} + - MAUTIC_MESSENGER_DSN_EMAIL=${MAUTIC_MESSENGER_DSN_EMAIL} + - MAUTIC_MESSENGER_DSN_HIT=${MAUTIC_MESSENGER_DSN_HIT} + - PHP_INI_DATE_TIMEZONE=${PHP_INI_DATE_TIMEZONE} + - PHP_MEMORY_LIMIT=${PHP_MEMORY_LIMIT} + volumes: + - mautic_data:/var/www/html + # AUTOMATION FIX 1: Force permissions to be correct on every start + entrypoint: ["/bin/sh", "-c", "chown -R www-data:www-data /var/www/html && /entrypoint.sh apache2-foreground"] + # AUTOMATION FIX 2: Check if the CONFIG FILE exists. If not, report 'unhealthy'. + # This signals the other containers to keep waiting. + healthcheck: + test: ["CMD-SHELL", "test -f /var/www/html/config/local.php || exit 1"] + interval: 10s + timeout: 5s + retries: 3 + start_period: 300s # Give you 5 mins to run the installer before marking failed + + # ------------------------------------------------------------------------- + # Service 3: Mautic Cron (Waits for Install) + # ------------------------------------------------------------------------- + mautic-cron: + image: mautic/mautic:5.1.1-apache + restart: unless-stopped + depends_on: + mautic: + condition: service_healthy # AUTOMATION FIX 3: Do not start until config file exists + environment: + - DOCKER_MAUTIC_ROLE=mautic_cron + - MAUTIC_DB_HOST=${MAUTIC_DB_HOST} + - MAUTIC_DB_PORT=${MAUTIC_DB_PORT} + - MAUTIC_DB_DATABASE=${MAUTIC_DB_DATABASE} + - MAUTIC_DB_USER=${MAUTIC_DB_USER} + - MAUTIC_DB_PASSWORD=${MAUTIC_DB_PASSWORD} + - MAUTIC_URL=${MAUTIC_URL} + - PHP_INI_DATE_TIMEZONE=${PHP_INI_DATE_TIMEZONE} + volumes: + - mautic_data:/var/www/html + + # ------------------------------------------------------------------------- + # Service 4: Mautic Worker (Waits for Install) + # ------------------------------------------------------------------------- + mautic-worker: + image: mautic/mautic:5.1.1-apache + restart: unless-stopped + depends_on: + mautic: + condition: service_healthy # AUTOMATION FIX 3: Do not start until config file exists + deploy: + resources: + limits: + memory: 512M + environment: + - DOCKER_MAUTIC_ROLE=mautic_worker + - DOCKER_MAUTIC_WORKERS_CONSUME_EMAIL=2 + - DOCKER_MAUTIC_WORKERS_CONSUME_HIT=2 + - DOCKER_MAUTIC_WORKERS_CONSUME_FAILED=2 + - MAUTIC_DB_HOST=${MAUTIC_DB_HOST} + - MAUTIC_DB_PORT=${MAUTIC_DB_PORT} + - MAUTIC_DB_DATABASE=${MAUTIC_DB_DATABASE} + - MAUTIC_DB_USER=${MAUTIC_DB_USER} + - MAUTIC_DB_PASSWORD=${MAUTIC_DB_PASSWORD} + - MAUTIC_URL=${MAUTIC_URL} + - MAUTIC_MESSENGER_DSN_EMAIL=${MAUTIC_MESSENGER_DSN_EMAIL} + - MAUTIC_MESSENGER_DSN_HIT=${MAUTIC_MESSENGER_DSN_HIT} + - PHP_INI_DATE_TIMEZONE=${PHP_INI_DATE_TIMEZONE} + volumes: + - mautic_data:/var/www/html + + # ------------------------------------------------------------------------- + # Service 5: phpMyAdmin + # ------------------------------------------------------------------------- + phpmyadmin: + image: phpmyadmin/phpmyadmin + restart: unless-stopped + depends_on: + mysql: + condition: service_healthy + environment: + PMA_HOST: mysql + PMA_PORT: 3306 + UPLOAD_LIMIT: 64M + ports: + - 80 + +volumes: + mysql_data: + mautic_data: diff --git a/blueprints/mautic/mautic.svg b/blueprints/mautic/mautic.svg new file mode 100644 index 000000000..3f5229ae6 --- /dev/null +++ b/blueprints/mautic/mautic.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/blueprints/mautic/template.toml b/blueprints/mautic/template.toml new file mode 100644 index 000000000..eeb79eef1 --- /dev/null +++ b/blueprints/mautic/template.toml @@ -0,0 +1,52 @@ +[variables] +# Domain 1: For the main Mautic Application +mautic_domain = "${domain}" + +# Domain 2: For phpMyAdmin (Database Manager) +pma_domain = "${domain}" + +# Security: Random passwords +db_password = "${password:32}" +root_password = "${password:32}" + +[config] + +# --- Service 1: Mautic Web --- +[[config.domains]] +serviceName = "mautic" +port = 80 +host = "${mautic_domain}" +path = "/" + +# --- Service 2: phpMyAdmin --- +[[config.domains]] +serviceName = "phpmyadmin" +port = 80 +host = "${pma_domain}" +path = "/" + +# --- Shared Environment Variables --- +[config.env] + +# URL Configuration +MAUTIC_URL = "https://${mautic_domain}" + +# Database Connections +MAUTIC_DB_HOST = "mysql" +MAUTIC_DB_PORT = "3306" +MAUTIC_DB_DATABASE = "mautic" +MAUTIC_DB_USER = "mautic" +MAUTIC_DB_PASSWORD = "${db_password}" +MYSQL_ROOT_PASSWORD = "${root_password}" + +# Security & Proxy (JSON ARRAY FIXED) +# We use single quotes '...' so TOML treats the inner [...] as a string +MAUTIC_TRUSTED_PROXIES = '["0.0.0.0/0"]' + +# Queue Settings +MAUTIC_MESSENGER_DSN_EMAIL = "doctrine://default" +MAUTIC_MESSENGER_DSN_HIT = "doctrine://default" + +# PHP Settings +PHP_INI_DATE_TIMEZONE = "UTC" +PHP_MEMORY_LIMIT = "512M" \ No newline at end of file diff --git a/meta.json b/meta.json index 753d3f875..ffff0e0ba 100644 --- a/meta.json +++ b/meta.json @@ -3734,6 +3734,24 @@ "self-hosted" ] }, + { + "id": "mautic", + "name": "Mautic", + "version": "5.1.1", + "description": "Mautic is the world's largest open-source marketing automation project. It allows you to automate the process of finding and nurturing contacts through landing pages and forms, sending email, text messages, web notifications, and tracking your contacts.", + "logo": "mautic.svg", + "links": { + "github": "https://github.com/mautic/mautic", + "website": "https://www.mautic.org/", + "docs": "https://docs.mautic.org/en" + }, + "tags": [ + "marketing", + "automation", + "email", + "crm" + ] + }, { "id": "maybe", "name": "Maybe", From bce7326959afc5503bece324edca6e76f99b15e3 Mon Sep 17 00:00:00 2001 From: Huba Tuba <57007485+floxay@users.noreply.github.com> Date: Wed, 28 Jan 2026 07:28:22 +0100 Subject: [PATCH 77/91] fix: searxng template (#651) --- blueprints/searxng/docker-compose.yml | 10 ++++------ blueprints/searxng/template.toml | 6 +++++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/blueprints/searxng/docker-compose.yml b/blueprints/searxng/docker-compose.yml index 856490c68..721c077ae 100644 --- a/blueprints/searxng/docker-compose.yml +++ b/blueprints/searxng/docker-compose.yml @@ -1,20 +1,18 @@ -version: "3.8" services: - redis: + valkey: image: valkey/valkey:8-alpine command: valkey-server --save 30 1 --loglevel warning restart: unless-stopped volumes: - - redis-data:/data + - valkey-data:/data searxng: image: searxng/searxng:latest restart: unless-stopped volumes: - - searxng-config:/etc/searxng + - ../files/searxng:/etc/searxng - searxng-data:/var/cache/searxng volumes: - redis-data: {} - searxng-config: {} + valkey-data: {} searxng-data: {} diff --git a/blueprints/searxng/template.toml b/blueprints/searxng/template.toml index 78f7bc685..092aca39e 100644 --- a/blueprints/searxng/template.toml +++ b/blueprints/searxng/template.toml @@ -12,11 +12,15 @@ env = [ ] [[config.mounts]] -filePath = "/etc/searxng/settings.yml" +filePath = "/searxng/settings.yml" content = """ use_default_settings: true + server: secret_key: \"${secret_key}\" limiter: false image_proxy: false + +valkey: + url: valkey://valkey:6379/0 """ \ No newline at end of file From 51750f2b1bc6d340852105c8c751e762d540e372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Gerhard=20Lindesv=C3=A4rd?= <1987198+lindesvard@users.noreply.github.com> Date: Wed, 28 Jan 2026 07:29:15 +0100 Subject: [PATCH 78/91] fix: use major version for openpanel (#680) --- blueprints/openpanel/docker-compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/blueprints/openpanel/docker-compose.yml b/blueprints/openpanel/docker-compose.yml index f63935437..53c95bd29 100644 --- a/blueprints/openpanel/docker-compose.yml +++ b/blueprints/openpanel/docker-compose.yml @@ -54,7 +54,7 @@ services: retries: 5 op-api: - image: lindesvard/openpanel-api:2.0.0 + image: lindesvard/openpanel-api:2 restart: always command: > sh -c " @@ -97,7 +97,7 @@ services: condition: service_healthy op-dashboard: - image: lindesvard/openpanel-dashboard:2.0.0 + image: lindesvard/openpanel-dashboard:2 restart: always depends_on: op-api: @@ -111,7 +111,7 @@ services: retries: 5 op-worker: - image: lindesvard/openpanel-worker:2.0.0 + image: lindesvard/openpanel-worker:2 restart: always depends_on: op-api: From b2e91749d39cb9397e8ec45ceab19023399aea90 Mon Sep 17 00:00:00 2001 From: Pablo Moraga Sandoval <72164630+Ketbome@users.noreply.github.com> Date: Wed, 28 Jan 2026 03:30:45 -0300 Subject: [PATCH 79/91] Feat/minepanel template (#657) * feat: add Minepanel template * fix: pin images to v1.7.1 and use http for URLs * fix: remove explicit ports, Dokploy handles proxying * chore: process and sort meta.json * fix: consolidate domain variables to main_domain * fix: add version field to docker-compose.yml --- blueprints/minepanel/docker-compose.yml | 29 ++++++++++++++++++++++++ blueprints/minepanel/minepanel.webp | Bin 0 -> 10978 bytes blueprints/minepanel/template.toml | 28 +++++++++++++++++++++++ meta.json | 18 +++++++++++++++ 4 files changed, 75 insertions(+) create mode 100644 blueprints/minepanel/docker-compose.yml create mode 100644 blueprints/minepanel/minepanel.webp create mode 100644 blueprints/minepanel/template.toml diff --git a/blueprints/minepanel/docker-compose.yml b/blueprints/minepanel/docker-compose.yml new file mode 100644 index 000000000..e69b75f96 --- /dev/null +++ b/blueprints/minepanel/docker-compose.yml @@ -0,0 +1,29 @@ +version: "3.8" +services: + backend: + image: ketbom/minepanel-backend:1.7.1 + restart: unless-stopped + environment: + - NODE_ENV=production + - FRONTEND_URL=${FRONTEND_URL} + - JWT_SECRET=${JWT_SECRET} + - CLIENT_PASSWORD=${CLIENT_PASSWORD} + - CLIENT_USERNAME=${CLIENT_USERNAME} + - BASE_DIR=${BASE_DIR} + volumes: + - minepanel-servers:/app/servers + - minepanel-data:/app/data + - /var/run/docker.sock:/var/run/docker.sock + + frontend: + image: ketbom/minepanel-frontend:1.7.1 + restart: unless-stopped + environment: + - NEXT_PUBLIC_BACKEND_URL=${NEXT_PUBLIC_BACKEND_URL} + - NEXT_PUBLIC_DEFAULT_LANGUAGE=${NEXT_PUBLIC_DEFAULT_LANGUAGE} + depends_on: + - backend + +volumes: + minepanel-servers: + minepanel-data: diff --git a/blueprints/minepanel/minepanel.webp b/blueprints/minepanel/minepanel.webp new file mode 100644 index 0000000000000000000000000000000000000000..21a6f7a46d95120bb863d602ff856ae371e0ef7f GIT binary patch literal 10978 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 Date: Wed, 28 Jan 2026 14:38:29 +0800 Subject: [PATCH 80/91] Add Komari Monitor template with Docker Compose, icon, and metadata (#XXX) (#623) * Introduce Komari Monitor, a self-hosted server monitoring tool, with its corresponding Docker Compose configuration, icon, and metadata entry. * Add template.toml for configuration and environment variables. * Include necessary links for GitHub and documentation. --- blueprints/komari-monitor/docker-compose.yml | 13 +++++++++++++ blueprints/komari-monitor/komari-monitor.ico | Bin 0 -> 40832 bytes blueprints/komari-monitor/template.toml | 15 +++++++++++++++ meta.json | 16 ++++++++++++++++ 4 files changed, 44 insertions(+) create mode 100644 blueprints/komari-monitor/docker-compose.yml create mode 100644 blueprints/komari-monitor/komari-monitor.ico create mode 100644 blueprints/komari-monitor/template.toml diff --git a/blueprints/komari-monitor/docker-compose.yml b/blueprints/komari-monitor/docker-compose.yml new file mode 100644 index 000000000..149999b08 --- /dev/null +++ b/blueprints/komari-monitor/docker-compose.yml @@ -0,0 +1,13 @@ +version: "3.8" +services: + komari: + image: ghcr.io/komari-monitor/komari:latest + restart: unless-stopped + volumes: + - komari-data:/app/data + environment: + - ADMIN_USERNAME + - ADMIN_PASSWORD + +volumes: + komari-data: {} diff --git a/blueprints/komari-monitor/komari-monitor.ico b/blueprints/komari-monitor/komari-monitor.ico new file mode 100644 index 0000000000000000000000000000000000000000..f314b8f2ac230d50ed23c5d54dea5f7c90531121 GIT binary patch literal 40832 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 Date: Wed, 28 Jan 2026 14:40:43 +0800 Subject: [PATCH 81/91] Add Misaka Danmu Server template with Docker Compose, icon, and metadata (#624) * Introduce Misaka Danmu Server, a self-hosted danmaku server for live streaming, with its corresponding Docker Compose configuration, icon, and metadata entry. * Add template.toml for configuration and environment variables. * Include necessary links for GitHub and documentation. --- .../misaka-danmu-server/docker-compose.yml | 43 ++++++++++++++++++ .../misaka-danmu-server.png | Bin 0 -> 13821 bytes blueprints/misaka-danmu-server/template.toml | 30 ++++++++++++ meta.json | 17 +++++++ 4 files changed, 90 insertions(+) create mode 100644 blueprints/misaka-danmu-server/docker-compose.yml create mode 100644 blueprints/misaka-danmu-server/misaka-danmu-server.png create mode 100644 blueprints/misaka-danmu-server/template.toml diff --git a/blueprints/misaka-danmu-server/docker-compose.yml b/blueprints/misaka-danmu-server/docker-compose.yml new file mode 100644 index 000000000..42252c25f --- /dev/null +++ b/blueprints/misaka-danmu-server/docker-compose.yml @@ -0,0 +1,43 @@ +version: "3.8" +services: + postgres: + image: postgres:18 + restart: unless-stopped + environment: + - POSTGRES_PASSWORD + - POSTGRES_USER + - POSTGRES_DB + - TZ=Asia/Shanghai + volumes: + - postgres-data:/var/lib/postgresql + healthcheck: + test: ["CMD-SHELL", "pg_isready -U danmuapi -d danmuapi"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 60s + + danmu-app: + image: l429609201/misaka_danmu_server:latest + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + environment: + - PUID=1000 + - PGID=1000 + - UMASK=0022 + - TZ=Asia/Shanghai + - DANMUAPI_DATABASE__TYPE + - DANMUAPI_DATABASE__HOST + - DANMUAPI_DATABASE__PORT + - DANMUAPI_DATABASE__NAME + - DANMUAPI_DATABASE__USER + - DANMUAPI_DATABASE__PASSWORD + - DANMUAPI_ADMIN__INITIAL_USER + volumes: + - danmu-config:/app/config + +volumes: + postgres-data: {} + danmu-config: {} diff --git a/blueprints/misaka-danmu-server/misaka-danmu-server.png b/blueprints/misaka-danmu-server/misaka-danmu-server.png new file mode 100644 index 0000000000000000000000000000000000000000..3099061113b4d8200e33bc9d7d786eeaa5684242 GIT binary patch literal 13821 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 ji3An)|K}mM^B(28&3%(3 Date: Tue, 27 Jan 2026 22:42:41 -0800 Subject: [PATCH 82/91] fix: pyrodactyl no longer uses main tag for it's latest release (#626) * fix: pyrodactyl no longer uses main tag for it's latest release * Remove custom network settings from docker-compose Removed custom network configuration from docker-compose. * Update MariaDB and Pyrodactyl images in Docker Compose --- blueprints/pyrodactyl/docker-compose.yml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/blueprints/pyrodactyl/docker-compose.yml b/blueprints/pyrodactyl/docker-compose.yml index ad0e42b74..2fde33d73 100644 --- a/blueprints/pyrodactyl/docker-compose.yml +++ b/blueprints/pyrodactyl/docker-compose.yml @@ -1,6 +1,6 @@ services: database: - image: mariadb:10.5 + image: mariadb:11 restart: always command: --default-authentication-plugin=mysql_native_password volumes: @@ -14,7 +14,7 @@ services: image: redis:alpine restart: always panel: - image: ghcr.io/pyrohost/pyrodactyl:main + image: ghcr.io/pyrodactyl-oss/pyrodactyl:latest restart: always links: - database @@ -35,15 +35,8 @@ services: DB_HOST: DB_PORT: DB_PASSWORD: ${MYSQL_PASSWORD} - RECAPTCHA_ENABLED: DB_CONNECTION: "mariadb" -networks: - default: - ipam: - config: - - subnet: 172.20.0.0/16 - volumes: pterodb: pterovar: From 26fcbe302d7e66b472d92ac4bd72990577cd9168 Mon Sep 17 00:00:00 2001 From: Jemg Date: Wed, 28 Jan 2026 01:45:19 -0500 Subject: [PATCH 83/91] Add Jenkins blueprint template configuration (#634) - Created a new TOML configuration file for Jenkins blueprint. - Defined main domain variable and service configuration for Jenkins. - Set serviceName to "jenkins" with port 8080 and host as the main domain. --- blueprints/jenkins/docker-compose.yml | 13 +++++++++++++ blueprints/jenkins/jenkins.svg | 1 + blueprints/jenkins/template.toml | 12 ++++++++++++ meta.json | 19 +++++++++++++++++++ 4 files changed, 45 insertions(+) create mode 100644 blueprints/jenkins/docker-compose.yml create mode 100644 blueprints/jenkins/jenkins.svg create mode 100644 blueprints/jenkins/template.toml diff --git a/blueprints/jenkins/docker-compose.yml b/blueprints/jenkins/docker-compose.yml new file mode 100644 index 000000000..f2b8ad3cb --- /dev/null +++ b/blueprints/jenkins/docker-compose.yml @@ -0,0 +1,13 @@ +version: "3.8" + +services: + jenkins: + image: jenkins/jenkins:latest + volumes: + - jenkins-home:/var/jenkins_home + - /var/run/docker.sock:/var/run/docker.sock + ports: + - 8080 + +volumes: + jenkins-home: {} \ No newline at end of file diff --git a/blueprints/jenkins/jenkins.svg b/blueprints/jenkins/jenkins.svg new file mode 100644 index 000000000..b511310dd --- /dev/null +++ b/blueprints/jenkins/jenkins.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/blueprints/jenkins/template.toml b/blueprints/jenkins/template.toml new file mode 100644 index 000000000..46a84921a --- /dev/null +++ b/blueprints/jenkins/template.toml @@ -0,0 +1,12 @@ +[variables] +main_domain = "${domain}" + +[config] +env = {} +mounts = [] + +[[config.domains]] +serviceName = "jenkins" +port = 8080 +host = "${main_domain}" + diff --git a/meta.json b/meta.json index e769a1f6b..542d9bc9d 100644 --- a/meta.json +++ b/meta.json @@ -3218,6 +3218,25 @@ "media system" ] }, + { + "id": "jenkins", + "name": "jenkins", + "version": "latest", + "description": "Jenkins is a free, open-source automation server that helps developers build, test, and deploy software by automating repetitive tasks in the software delivery pipeline.", + "logo": "jenkins.svg", + "links": { + "github": "https://github.com/jenkinsci/jenkins", + "website": "https://www.jenkins.io/", + "docs": "https://www.jenkins.io/doc/" + }, + "tags": [ + "ci-cd", + "devops", + "automation", + "pipelines", + "open-source" + ] + }, { "id": "kaneo", "name": "Kaneo", From 3395913afb1631c33183cfac44c9ac67cfcfeed7 Mon Sep 17 00:00:00 2001 From: Vitaliy Kukharik <37010174+vitabaks@users.noreply.github.com> Date: Wed, 28 Jan 2026 11:46:57 +0500 Subject: [PATCH 84/91] Update Autobase to version 2.5 (#647) * Update Autobase to version 2.5 Bump the autobase-console Docker image from version 2.2.0 to 2.5.2 in docker-compose.yml to use the latest features and fixes. * Update docker-compose.yml * Update docker-compose.yml * Update meta.json * Update Autobase description in meta.json Revised the Autobase package description to clarify it as a self-hosted DBaaS alternative to cloud-managed databases. --- blueprints/autobase/docker-compose.yml | 2 +- meta.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/blueprints/autobase/docker-compose.yml b/blueprints/autobase/docker-compose.yml index 07b99485a..13b23d575 100644 --- a/blueprints/autobase/docker-compose.yml +++ b/blueprints/autobase/docker-compose.yml @@ -1,6 +1,6 @@ services: autobase-console: - image: autobase/console:2.3.0 + image: autobase/console:2.5.2 restart: unless-stopped ports: - "80" diff --git a/meta.json b/meta.json index 542d9bc9d..6b5ba634a 100644 --- a/meta.json +++ b/meta.json @@ -510,8 +510,8 @@ { "id": "autobase", "name": "Autobase", - "version": "2.3.0", - "description": "Autobase for PostgreSQL® is an open-source alternative to cloud-managed databases (DBaaS) such as Amazon RDS, Google Cloud SQL, Azure Database, and more.", + "version": "2.5.2", + "description": "Autobase for PostgreSQL® is an open-source alternative to cloud-managed databases (self-hosted DBaaS).", "links": { "github": "https://github.com/vitabaks/autobase", "website": "https://autobase.tech/", From ebcaed8eeaa4f59733df51e1d8912903fd290c61 Mon Sep 17 00:00:00 2001 From: Firefly <77047823+Yuri-NagaSaki@users.noreply.github.com> Date: Wed, 28 Jan 2026 14:49:21 +0800 Subject: [PATCH 85/91] Add Cloudreve template (#649) Self-hosted file management and sharing system with multi-cloud storage support. Includes PostgreSQL and Redis for database and caching. --- blueprints/cloudreve/cloudreve.png | Bin 0 -> 6938 bytes blueprints/cloudreve/docker-compose.yml | 48 ++++++++++++++++++++++++ blueprints/cloudreve/template.toml | 16 ++++++++ meta.json | 18 +++++++++ 4 files changed, 82 insertions(+) create mode 100644 blueprints/cloudreve/cloudreve.png create mode 100644 blueprints/cloudreve/docker-compose.yml create mode 100644 blueprints/cloudreve/template.toml diff --git a/blueprints/cloudreve/cloudreve.png b/blueprints/cloudreve/cloudreve.png new file mode 100644 index 0000000000000000000000000000000000000000..28020b06c9b4d150b9253d4cb4ee6b5fe4b25c04 GIT binary patch literal 6938 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 literal 0 HcmV?d00001 diff --git a/blueprints/cloudreve/docker-compose.yml b/blueprints/cloudreve/docker-compose.yml new file mode 100644 index 000000000..f90f3685b --- /dev/null +++ b/blueprints/cloudreve/docker-compose.yml @@ -0,0 +1,48 @@ +version: "3.8" + +services: + cloudreve: + image: cloudreve/cloudreve:4.10.1 + depends_on: + postgresql: + condition: service_healthy + redis: + condition: service_started + restart: unless-stopped + environment: + - CR_CONF_Database.Type=postgres + - CR_CONF_Database.Host=postgresql + - CR_CONF_Database.User=${POSTGRES_USER} + - CR_CONF_Database.Password=${POSTGRES_PASSWORD} + - CR_CONF_Database.Name=${POSTGRES_DB} + - CR_CONF_Database.Port=5432 + - CR_CONF_Redis.Server=redis:6379 + volumes: + - cloudreve_data:/cloudreve/data + + postgresql: + image: postgres:17 + restart: unless-stopped + environment: + - POSTGRES_USER + - POSTGRES_PASSWORD + - POSTGRES_DB + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + + redis: + image: redis:8.4.0 + restart: unless-stopped + volumes: + - redis_data:/data + +volumes: + cloudreve_data: + postgres_data: + redis_data: diff --git a/blueprints/cloudreve/template.toml b/blueprints/cloudreve/template.toml new file mode 100644 index 000000000..a5acf6b4e --- /dev/null +++ b/blueprints/cloudreve/template.toml @@ -0,0 +1,16 @@ +[variables] +main_domain = "${domain}" +db_password = "${password:32}" +db_user = "cloudreve" +db_name = "cloudreve" + +[config] +[[config.domains]] +serviceName = "cloudreve" +port = 5212 +host = "${main_domain}" + +[config.env] +POSTGRES_PASSWORD = "${db_password}" +POSTGRES_USER = "${db_user}" +POSTGRES_DB = "${db_name}" diff --git a/meta.json b/meta.json index 6b5ba634a..208fb03de 100644 --- a/meta.json +++ b/meta.json @@ -1317,6 +1317,24 @@ "tunnel" ] }, + { + "id": "cloudreve", + "name": "Cloudreve", + "version": "4.10.1", + "description": "Self-hosted file management and sharing system with multi-cloud storage support. Compatible with local, OneDrive, S3, and various cloud providers.", + "logo": "cloudreve.png", + "links": { + "github": "https://github.com/cloudreve/Cloudreve", + "website": "https://cloudreve.org", + "docs": "https://docs.cloudreve.org" + }, + "tags": [ + "storage", + "file-sharing", + "cloud", + "self-hosted" + ] + }, { "id": "cockpit", "name": "Cockpit", From bba70339f37cad2e4cdb0ee3599a955bb04abf8d Mon Sep 17 00:00:00 2001 From: aurorarissime Date: Wed, 28 Jan 2026 07:53:26 +0100 Subject: [PATCH 86/91] Update to Umami 3.0.3 (#668) * Update Umami version from v2.20.2 to v3.0.3 * Update Umami Docker image to version 3.0.3 --- blueprints/umami/docker-compose.yml | 2 +- meta.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/blueprints/umami/docker-compose.yml b/blueprints/umami/docker-compose.yml index 416dc8e13..6cd51601d 100644 --- a/blueprints/umami/docker-compose.yml +++ b/blueprints/umami/docker-compose.yml @@ -1,6 +1,6 @@ services: umami: - image: ghcr.io/umami-software/umami:postgresql-v2.20.2 + image: ghcr.io/umami-software/umami:3.0.3 restart: always healthcheck: test: ["CMD-SHELL", "curl http://localhost:3000/api/heartbeat"] diff --git a/meta.json b/meta.json index 208fb03de..92ea3de26 100644 --- a/meta.json +++ b/meta.json @@ -6021,7 +6021,7 @@ { "id": "umami", "name": "Umami", - "version": "v2.20.2", + "version": "v3.0.3", "description": "Umami is a simple, fast, privacy-focused alternative to Google Analytics.", "logo": "umami.png", "links": { From c2510418799c54d2c3430d5961035ffee50debc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D1=84=D1=8B=D1=80=D0=B0=D1=82=20=D1=91=D0=B7=D0=B4=D1=8D?= =?UTF-8?q?=D0=BD?= <31664778+fir4tozden@users.noreply.github.com> Date: Wed, 28 Jan 2026 09:53:37 +0300 Subject: [PATCH 87/91] fix: tailscale exitnode authkey env (#669) --- blueprints/tailscale-exitnode/template.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/tailscale-exitnode/template.toml b/blueprints/tailscale-exitnode/template.toml index eac3e68eb..cc03571f7 100644 --- a/blueprints/tailscale-exitnode/template.toml +++ b/blueprints/tailscale-exitnode/template.toml @@ -1,4 +1,4 @@ [config.env] TAILSCALE_HOSTNAME = "" -TAILSCALE_APIKEY = "" \ No newline at end of file +TAILSCALE_AUTHKEY = "" \ No newline at end of file From f8952fb79eced19320311a4a55de2e663212a296 Mon Sep 17 00:00:00 2001 From: Firefly <77047823+Yuri-NagaSaki@users.noreply.github.com> Date: Wed, 28 Jan 2026 14:54:53 +0800 Subject: [PATCH 88/91] Add qbitwebui (#675) * Add qBittorrent Web UI template * Fix healthcheck bug --- blueprints/qbitwebui/docker-compose.yml | 39 ++++++++++++++++++++++++ blueprints/qbitwebui/qbitwebui.png | Bin 0 -> 440710 bytes blueprints/qbitwebui/template.toml | 16 ++++++++++ meta.json | 18 +++++++++++ 4 files changed, 73 insertions(+) create mode 100644 blueprints/qbitwebui/docker-compose.yml create mode 100644 blueprints/qbitwebui/qbitwebui.png create mode 100644 blueprints/qbitwebui/template.toml diff --git a/blueprints/qbitwebui/docker-compose.yml b/blueprints/qbitwebui/docker-compose.yml new file mode 100644 index 000000000..3e805c3c2 --- /dev/null +++ b/blueprints/qbitwebui/docker-compose.yml @@ -0,0 +1,39 @@ +version: "3.8" + +services: + qbitwebui: + image: ghcr.io/maciejonos/qbitwebui:latest + restart: unless-stopped + environment: + # Required: Encryption key for storing credentials (min 32 chars) + - ENCRYPTION_KEY=${ENCRYPTION_KEY} + # Optional: Server port (default: 3000) + - PORT=${PORT:-3000} + # Optional: Database location (default: ./data/qbitwebui.db) + - DATABASE_PATH=${DATABASE_PATH:-/data/qbitwebui.db} + # Optional: Salt file location (default: ./data/.salt) + - SALT_PATH=${SALT_PATH:-/data/.salt} + # Optional: Allow self-signed certificates for qBittorrent instances (default: false) + # - ALLOW_SELF_SIGNED_CERTS=${ALLOW_SELF_SIGNED_CERTS:-false} + # Optional: Disable authentication/login (single-user mode) (default: false) + # - DISABLE_AUTH=${DISABLE_AUTH:-false} + # Optional: Disable new registrations, creates default admin account (default: false) + # - DISABLE_REGISTRATION=${DISABLE_REGISTRATION:-false} + # Optional: Enable file browser by setting downloads path + # - DOWNLOADS_PATH=/downloads + volumes: + - qbitwebui_data:/data + # Optional: Mount downloads directory for file browser feature + # Read-only mount (browse & download only): + # - /path/to/your/downloads:/downloads:ro + # Or read-write mount (enables delete/move/copy/rename): + # - /path/to/your/downloads:/downloads + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 10s + +volumes: + qbitwebui_data: diff --git a/blueprints/qbitwebui/qbitwebui.png b/blueprints/qbitwebui/qbitwebui.png new file mode 100644 index 0000000000000000000000000000000000000000..48052dffba50a4dc2b560669abaff278d28af9d4 GIT binary patch literal 440710 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