diff --git a/__tests__/apps.test.ts b/__tests__/apps.test.ts index 4fd30c9..0f92ed1 100644 --- a/__tests__/apps.test.ts +++ b/__tests__/apps.test.ts @@ -3,6 +3,7 @@ import { appInfoSchema, dynamicComposeSchema } from '@runtipi/common/schemas' import { fromError } from 'zod-validation-error'; import fs from 'node:fs' import path from 'node:path' +import YAML from 'yaml' const getApps = async () => { const appsDir = await fs.promises.readdir(path.join(process.cwd(), 'apps')) @@ -29,7 +30,7 @@ describe("each app should have the required files", async () => { const apps = await getApps() for (const app of apps) { - const files = ['config.json', 'docker-compose.json', 'metadata/logo.jpg', 'metadata/description.md'] + const files = ['config.json', 'metadata/logo.jpg', 'metadata/description.md'] for (const file of files) { test(`app ${app} should have ${file}`, async () => { @@ -37,6 +38,12 @@ describe("each app should have the required files", async () => { expect(fileContent).not.toBeNull() }) } + + test(`app ${app} should have a compose file`, async () => { + const legacyCompose = await getFile(app, 'docker-compose.json') + const modernCompose = await getFile(app, 'docker-compose.yml') + expect(legacyCompose || modernCompose).not.toBeNull() + }) } }) @@ -58,17 +65,38 @@ describe("each app should have a valid config.json", async () => { } }) -describe("each app should have a valid docker-compose.json", async () => { +describe("modern compose files preserve runtime semantics", () => { + test("n8n-sandbox keeps its one-shot certificate service", async () => { + const fileContent = await getFile('n8n-sandbox', 'docker-compose.yml') + expect(fileContent).not.toBeNull() + + const parsed = YAML.parse(fileContent || '') + expect(parsed['x-runtipi']?.schema_version).toBe(2) + expect(parsed.services?.['sandbox-certs']?.restart).toBe('no') + expect(parsed.services?.['sandbox-api']?.['x-runtipi']?.is_main).toBe(true) + }) +}) + +describe("each app should have a valid compose file", async () => { const apps = await getApps() for (const app of apps) { - test(`app ${app} should have a valid docker-compose.json`, async () => { - const fileContent = await getFile(app, 'docker-compose.json') - const parsed = dynamicComposeSchema.safeParse(JSON.parse(fileContent || '{}')) + test(`app ${app} should have a valid compose file`, async () => { + const legacyCompose = await getFile(app, 'docker-compose.json') + const modernCompose = await getFile(app, 'docker-compose.yml') + + if (modernCompose) { + const parsed = YAML.parse(modernCompose) + expect(parsed['x-runtipi']?.schema_version).toBeTypeOf('number') + expect(parsed.services).toBeTypeOf('object') + return + } + + const parsed = dynamicComposeSchema.safeParse(JSON.parse(legacyCompose || '{}')) if (!parsed.success) { const validationError = fromError(parsed.error); - console.error(`Error parsing docker-compose.json for app ${app}:`, validationError.toString()); + console.error(`Error parsing compose file for app ${app}:`, validationError.toString()); } expect(parsed.success).toBe(true) diff --git a/apps/mediamtx/docker-compose.json b/apps/mediamtx/docker-compose.json index ce9814a..c038ecf 100644 --- a/apps/mediamtx/docker-compose.json +++ b/apps/mediamtx/docker-compose.json @@ -7,64 +7,22 @@ "hostname": "mediamtx", "isMain": true, "internalPort": "8889", - "environment": [ - { - "key": "MTX_PROTOCOLS", - "value": "tcp" - }, - { - "key": "MTX_LOGLEVEL", - "value": "info" - }, - { - "key": "MTX_LOGDESTINATIONS", - "value": "stdout" - }, - { - "key": "MTX_RTSPADDRESS", - "value": ":8554" - }, - { - "key": "MTX_RTMPADDRESS", - "value": ":1935" - }, - { - "key": "MTX_HLSADDRESS", - "value": ":8888" - }, - { - "key": "MTX_WEBRTCADDRESS", - "value": ":8889" - }, - { - "key": "MTX_SRTADDRESS", - "value": ":8890" - }, - { - "key": "MTX_APIADDRESS", - "value": ":9997" - }, - { - "key": "MTX_METRICSADDRESS", - "value": ":9998" - }, - { - "key": "MTX_AUTHINTERNALUSERS", - "value": "${MTX_API_USERNAME:+${MTX_API_USERNAME}:${MTX_API_PASSWORD}}" - }, - { - "key": "MTX_PATHDEFAULTS_RECORD", - "value": "${MTX_RECORD_ENABLED:-false}" - }, - { - "key": "MTX_PATHDEFAULTS_RECORDPATH", - "value": "./recordings/%path/%Y-%m-%d_%H-%M-%S-%f" - }, - { - "key": "MTX_PATHDEFAULTS_RECORDFORMAT", - "value": "fmp4" - } - ], + "environment": { + "MTX_PROTOCOLS": "tcp", + "MTX_LOGLEVEL": "info", + "MTX_LOGDESTINATIONS": "stdout", + "MTX_RTSPADDRESS": ":8554", + "MTX_RTMPADDRESS": ":1935", + "MTX_HLSADDRESS": ":8888", + "MTX_WEBRTCADDRESS": ":8889", + "MTX_SRTADDRESS": ":8890", + "MTX_APIADDRESS": ":9997", + "MTX_METRICSADDRESS": ":9998", + "MTX_AUTHINTERNALUSERS": "${MTX_API_USERNAME:+${MTX_API_USERNAME}:${MTX_API_PASSWORD}}", + "MTX_PATHDEFAULTS_RECORD": "${MTX_RECORD_ENABLED:-false}", + "MTX_PATHDEFAULTS_RECORDPATH": "./recordings/%path/%Y-%m-%d_%H-%M-%S-%f", + "MTX_PATHDEFAULTS_RECORDFORMAT": "fmp4" + }, "addPorts": [ { "containerPort": 8554, diff --git a/apps/config.json b/apps/n8n-sandbox/config.json similarity index 100% rename from apps/config.json rename to apps/n8n-sandbox/config.json diff --git a/apps/docker-compose.yml b/apps/n8n-sandbox/docker-compose.yml similarity index 100% rename from apps/docker-compose.yml rename to apps/n8n-sandbox/docker-compose.yml diff --git a/apps/description.md b/apps/n8n-sandbox/metadata/description.md similarity index 100% rename from apps/description.md rename to apps/n8n-sandbox/metadata/description.md diff --git a/apps/logo.jpg b/apps/n8n-sandbox/metadata/logo.jpg similarity index 100% rename from apps/logo.jpg rename to apps/n8n-sandbox/metadata/logo.jpg diff --git a/apps/nginx/docker-compose.json b/apps/nginx/docker-compose.json index 6cd866a..a0dfed5 100644 --- a/apps/nginx/docker-compose.json +++ b/apps/nginx/docker-compose.json @@ -29,16 +29,10 @@ "readOnly": true } ], - "environment": [ - { - "key": "NGINX_HOST", - "value": "${NGINX_SERVER_NAME:-localhost}" - }, - { - "key": "TZ", - "value": "${TZ:-Europe/Paris}" - } - ], + "environment": { + "NGINX_HOST": "${NGINX_SERVER_NAME:-localhost}", + "TZ": "${TZ:-Europe/Paris}" + }, "healthCheck": { "test": "curl --fail http://localhost:80 || exit 1", "interval": "30s", diff --git a/apps/wazuh-runtipi/docker-compose.json b/apps/wazuh-runtipi/docker-compose.json index 5da3f56..25cc73d 100644 --- a/apps/wazuh-runtipi/docker-compose.json +++ b/apps/wazuh-runtipi/docker-compose.json @@ -9,12 +9,9 @@ "sh", "/scripts/init-certs.sh" ], - "environment": [ - { - "key": "CERT_TOOL_VERSION", - "value": "4.14" - } - ], + "environment": { + "CERT_TOOL_VERSION": "4.14" + }, "volumes": [ { "hostPath": "${APP_DATA_DIR}/data/config", @@ -74,48 +71,18 @@ "condition": "service_healthy" } }, - "environment": [ - { - "key": "OPENSEARCH_JAVA_OPTS", - "value": "-Xms1g -Xmx1g -Dlog4j2.formatMsgNoLookups=true" - }, - { - "key": "DISABLE_INSTALL_DEMO_CONFIG", - "value": "true" - }, - { - "key": "bootstrap.memory_lock", - "value": "true" - }, - { - "key": "network.host", - "value": "wazuh.indexer" - }, - { - "key": "node.name", - "value": "wazuh.indexer" - }, - { - "key": "cluster.initial_cluster_manager_nodes", - "value": "wazuh.indexer" - }, - { - "key": "node.max_local_storage_nodes", - "value": "1" - }, - { - "key": "plugins.security.allow_default_init_securityindex", - "value": "true" - }, - { - "key": "NODES_DN", - "value": "CN=wazuh.indexer,OU=Wazuh,O=Wazuh,L=California,C=US" - }, - { - "key": "plugins.security.ssl.http.clientauth_mode", - "value": "OPTIONAL" - } - ], + "environment": { + "OPENSEARCH_JAVA_OPTS": "-Xms1g -Xmx1g -Dlog4j2.formatMsgNoLookups=true", + "DISABLE_INSTALL_DEMO_CONFIG": "true", + "bootstrap.memory_lock": "true", + "network.host": "wazuh.indexer", + "node.name": "wazuh.indexer", + "cluster.initial_cluster_manager_nodes": "wazuh.indexer", + "node.max_local_storage_nodes": "1", + "plugins.security.allow_default_init_securityindex": "true", + "NODES_DN": "CN=wazuh.indexer,OU=Wazuh,O=Wazuh,L=California,C=US", + "plugins.security.ssl.http.clientauth_mode": "OPTIONAL" + }, "ulimits": { "memlock": { "soft": -1, @@ -209,64 +176,22 @@ "condition": "service_healthy" } }, - "environment": [ - { - "key": "WAZUH_INDEXER_HOSTS", - "value": "wazuh.indexer:9200" - }, - { - "key": "WAZUH_NODE_NAME", - "value": "manager" - }, - { - "key": "WAZUH_NODE_TYPE", - "value": "master" - }, - { - "key": "WAZUH_CLUSTER_NODES", - "value": "wazuh.manager" - }, - { - "key": "WAZUH_CLUSTER_BIND_ADDR", - "value": "wazuh.manager" - }, - { - "key": "INDEXER_URL", - "value": "https://wazuh.indexer:9200" - }, - { - "key": "INDEXER_USERNAME", - "value": "${INDEXER_USERNAME:-admin}" - }, - { - "key": "INDEXER_PASSWORD", - "value": "${INDEXER_PASSWORD:-admin}" - }, - { - "key": "FILEBEAT_SSL_VERIFICATION_MODE", - "value": "full" - }, - { - "key": "SSL_CERTIFICATE_AUTHORITIES", - "value": "/var/ossec/etc/certs/root-ca.pem" - }, - { - "key": "SSL_CERTIFICATE", - "value": "/var/ossec/etc/certs/server.pem" - }, - { - "key": "SSL_KEY", - "value": "/var/ossec/etc/certs/server-key.pem" - }, - { - "key": "API_USERNAME", - "value": "wazuh-wui" - }, - { - "key": "API_PASSWORD", - "value": "${API_PASSWORD:-MyS3cr37P450r.*-}" - } - ], + "environment": { + "WAZUH_INDEXER_HOSTS": "wazuh.indexer:9200", + "WAZUH_NODE_NAME": "manager", + "WAZUH_NODE_TYPE": "master", + "WAZUH_CLUSTER_NODES": "wazuh.manager", + "WAZUH_CLUSTER_BIND_ADDR": "wazuh.manager", + "INDEXER_URL": "https://wazuh.indexer:9200", + "INDEXER_USERNAME": "${INDEXER_USERNAME:-admin}", + "INDEXER_PASSWORD": "${INDEXER_PASSWORD:-admin}", + "FILEBEAT_SSL_VERIFICATION_MODE": "full", + "SSL_CERTIFICATE_AUTHORITIES": "/var/ossec/etc/certs/root-ca.pem", + "SSL_CERTIFICATE": "/var/ossec/etc/certs/server.pem", + "SSL_KEY": "/var/ossec/etc/certs/server-key.pem", + "API_USERNAME": "wazuh-wui", + "API_PASSWORD": "${API_PASSWORD:-MyS3cr37P450r.*-}" + }, "ulimits": { "memlock": { "soft": -1, @@ -368,60 +293,21 @@ "condition": "service_healthy" } }, - "environment": [ - { - "key": "SERVER_HOST", - "value": "0.0.0.0" - }, - { - "key": "OPENSEARCH_HOSTS", - "value": "https://wazuh.indexer:9200" - }, - { - "key": "SERVER_SSL_ENABLED", - "value": "true" - }, - { - "key": "INDEXER_USERNAME", - "value": "${INDEXER_USERNAME:-admin}" - }, - { - "key": "INDEXER_PASSWORD", - "value": "${INDEXER_PASSWORD:-admin}" - }, - { - "key": "WAZUH_API_URL", - "value": "https://wazuh.manager" - }, - { - "key": "DASHBOARD_USERNAME", - "value": "${DASHBOARD_USERNAME:-kibanaserver}" - }, - { - "key": "DASHBOARD_PASSWORD", - "value": "${DASHBOARD_PASSWORD:-kibanaserver}" - }, - { - "key": "API_USERNAME", - "value": "wazuh-wui" - }, - { - "key": "API_PASSWORD", - "value": "${API_PASSWORD:-MyS3cr37P450r.*-}" - }, - { - "key": "SERVER_SSL_CERTIFICATE", - "value": "/usr/share/wazuh-dashboard/config/certs/dashboard.pem" - }, - { - "key": "SERVER_SSL_KEY", - "value": "/usr/share/wazuh-dashboard/config/certs/dashboard-key.pem" - }, - { - "key": "OPENSEARCH_SSL_CERTIFICATE_AUTHORITIES", - "value": "/usr/share/wazuh-dashboard/config/certs/root-ca.pem" - } - ], + "environment": { + "SERVER_HOST": "0.0.0.0", + "OPENSEARCH_HOSTS": "https://wazuh.indexer:9200", + "SERVER_SSL_ENABLED": "true", + "INDEXER_USERNAME": "${INDEXER_USERNAME:-admin}", + "INDEXER_PASSWORD": "${INDEXER_PASSWORD:-admin}", + "WAZUH_API_URL": "https://wazuh.manager", + "DASHBOARD_USERNAME": "${DASHBOARD_USERNAME:-kibanaserver}", + "DASHBOARD_PASSWORD": "${DASHBOARD_PASSWORD:-kibanaserver}", + "API_USERNAME": "wazuh-wui", + "API_PASSWORD": "${API_PASSWORD:-MyS3cr37P450r.*-}", + "SERVER_SSL_CERTIFICATE": "/usr/share/wazuh-dashboard/config/certs/dashboard.pem", + "SERVER_SSL_KEY": "/usr/share/wazuh-dashboard/config/certs/dashboard-key.pem", + "OPENSEARCH_SSL_CERTIFICATE_AUTHORITIES": "/usr/share/wazuh-dashboard/config/certs/root-ca.pem" + }, "volumes": [ { "hostPath": "${APP_DATA_DIR}/data/config/wazuh_ssl_certs", @@ -462,4 +348,4 @@ } } ] -} \ No newline at end of file +} diff --git a/bun.lockb b/bun.lockb index a285984..f5a23fe 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 925fe34..8dbf660 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "license": "ISC", "devDependencies": { "@types/bun": "latest", - "@types/node": "^22.14.1" + "@types/node": "^22.14.1", + "yaml": "2" }, "dependencies": { "@runtipi/common": "^0.8.0",