From f7758c1cbfe01fd8f2e928e427aa92232c8ef54d Mon Sep 17 00:00:00 2001 From: "Md. Ashikul Alam" Date: Mon, 8 Jul 2024 20:46:51 +0600 Subject: [PATCH 01/47] feat!: update search bar depending on country config variables --- src/api/application/application-config-default.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/api/application/application-config-default.ts b/src/api/application/application-config-default.ts index 9b2db665a..8891729b4 100644 --- a/src/api/application/application-config-default.ts +++ b/src/api/application/application-config-default.ts @@ -52,7 +52,15 @@ export const defaultApplicationConfig = { }, USER_NOTIFICATION_DELIVERY_METHOD: 'email', // or 'sms', or '' ... You can use 'sms' for WhatsApp INFORMANT_NOTIFICATION_DELIVERY_METHOD: 'email', // or 'sms', or '' ... You can use 'sms' for WhatsApp - SIGNATURE_REQUIRED_FOR_ROLES: ['LOCAL_REGISTRAR', 'NATIONAL_REGISTRAR'] + SIGNATURE_REQUIRED_FOR_ROLES: ['LOCAL_REGISTRAR', 'NATIONAL_REGISTRAR'], + SEARCH_DEFAULT_CRITERIA: [ + 'TRACKING_ID', + 'REGISTRATION_NUMBER', + 'NATIONAL_ID', + 'NAME', + 'PHONE_NUMBER', + 'EMAIL' + ] } export const COUNTRY_WIDE_CRUDE_DEATH_RATE = 10 From defe9ff8877fd9640eafb6899cfdc34aa79d2eca Mon Sep 17 00:00:00 2001 From: "Md. Ashikul Alam" Date: Tue, 9 Jul 2024 19:11:53 +0600 Subject: [PATCH 02/47] feat!: amend search default criteria in app config --- .../application/application-config-default.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/api/application/application-config-default.ts b/src/api/application/application-config-default.ts index 8891729b4..660bab084 100644 --- a/src/api/application/application-config-default.ts +++ b/src/api/application/application-config-default.ts @@ -38,7 +38,6 @@ export const defaultApplicationConfig = { }, PRINT_IN_ADVANCE: true }, - // Following constants aren't configurable via UI FIELD_AGENT_AUDIT_LOCATIONS: 'DISTRICT', DECLARATION_AUDIT_LOCATIONS: 'DISTRICT', FEATURES: { @@ -53,14 +52,16 @@ export const defaultApplicationConfig = { USER_NOTIFICATION_DELIVERY_METHOD: 'email', // or 'sms', or '' ... You can use 'sms' for WhatsApp INFORMANT_NOTIFICATION_DELIVERY_METHOD: 'email', // or 'sms', or '' ... You can use 'sms' for WhatsApp SIGNATURE_REQUIRED_FOR_ROLES: ['LOCAL_REGISTRAR', 'NATIONAL_REGISTRAR'], - SEARCH_DEFAULT_CRITERIA: [ - 'TRACKING_ID', - 'REGISTRATION_NUMBER', - 'NATIONAL_ID', - 'NAME', - 'PHONE_NUMBER', - 'EMAIL' - ] + SEARCH_DEFAULT_CRITERIA: 'TRACKING_ID' + /* + * SEARCH_DEFAULT_CRITERIA's value can be one of the following + * | 'TRACKING_ID', + * | 'REGISTRATION_NUMBER', + * | 'NATIONAL_ID', + * | 'NAME', + * | 'PHONE_NUMBER', + * | 'EMAIL' + */ } export const COUNTRY_WIDE_CRUDE_DEATH_RATE = 10 From 941d2948f06cebea29cff312cc52584acaed87cf Mon Sep 17 00:00:00 2001 From: Markus Date: Thu, 11 Jul 2024 07:33:21 +0300 Subject: [PATCH 03/47] take off filesystem metric check from system and related alert --- infrastructure/monitoring/beats/metricbeat.yml | 16 ++++++++-------- .../monitoring/elastalert/rules/alert.yaml | 6 +++--- infrastructure/monitoring/kibana/config.ndjson | 1 - 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/infrastructure/monitoring/beats/metricbeat.yml b/infrastructure/monitoring/beats/metricbeat.yml index cb4875457..520ed80bd 100644 --- a/infrastructure/monitoring/beats/metricbeat.yml +++ b/infrastructure/monitoring/beats/metricbeat.yml @@ -38,14 +38,14 @@ metricbeat.modules: cpu.metrics: ['percentages'] core.metrics: ['percentages'] - - module: system - period: 1m - metricsets: - - filesystem - - fsstat - processors: - - drop_event.when.regexp: - system.filesystem.mount_point: '^/(sys|cgroup|proc|dev|etc|host|lib)($|/)' + # - module: system + # period: 1m + # metricsets: + # - filesystem + # - fsstat + # processors: + # - drop_event.when.regexp: + # system.filesystem.mount_point: '^/(sys|cgroup|proc|dev|etc|host|lib)($|/)' - module: system period: 15m diff --git a/infrastructure/monitoring/elastalert/rules/alert.yaml b/infrastructure/monitoring/elastalert/rules/alert.yaml index d5bfbcb50..58a74d374 100644 --- a/infrastructure/monitoring/elastalert/rules/alert.yaml +++ b/infrastructure/monitoring/elastalert/rules/alert.yaml @@ -30,9 +30,9 @@ filter: - term: rule.name.keyword: value: 'CPU under heavy load' - - term: - rule.name.keyword: - value: 'Low on available disk space' + # - term: + # rule.name.keyword: + # value: 'Low on available disk space' minimum_should_match: 1 alert: post2 diff --git a/infrastructure/monitoring/kibana/config.ndjson b/infrastructure/monitoring/kibana/config.ndjson index a8f47219d..6c26fe491 100644 --- a/infrastructure/monitoring/kibana/config.ndjson +++ b/infrastructure/monitoring/kibana/config.ndjson @@ -5,6 +5,5 @@ {"attributes":{"anomalyThreshold":50,"description":"","fields":{"container":"container.id","host":"host.name","message":["message","@message"],"pod":"kubernetes.pod.uid","tiebreaker":"_doc","timestamp":"@timestamp"},"inventoryDefaultView":"0","logColumns":[{"timestampColumn":{"id":"5e7f964a-be8a-40d8-88d2-fbcfbdca0e2f"}},{"fieldColumn":{"field":"event.dataset","id":" eb9777a8-fcd3-420e-ba7d-172fff6da7a2"}},{"messageColumn":{"id":"b645d6da-824b-4723-9a2a-e8cece1645c0"}}],"logIndices":{"indexName":"logs-*,filebeat-*,kibana_sample_data_logs*,logstash*","type":"index_name"},"metricAlias":"metrics-*,metricbeat-*","metricsExplorerDefaultView":"0","name":"Default"},"coreMigrationVersion":"7.17.0","id":"default","migrationVersion":{"infrastructure-ui-source":"7.16.2"},"references":[],"sort":[1707273006619,217714],"type":"infrastructure-ui-source","updated_at":"2024-02-07T02:30:06.619Z","version":"WzQ5MzAyNywxOV0="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"query matched","params":{"documents":["{\"@timestamp\":\"2023-11-20T10:19:30.521Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":".es-query","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2023-11-20T09:12:19.237Z","createdBy":"elastic","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Successful SSH login","notifyWhen":"onActionGroupChange","params":{"esQuery":"{ \"query\": { \"bool\": { \"must\": [ \n { \"term\": { \"log.file.path\": \"/var/log/auth.log\" } },\n { \"term\": { \"event.outcome\": \"success\" }}\n ] } } }","index":["filebeat-*"],"size":100,"threshold":[1],"thresholdComparator":">=","timeField":"@timestamp","timeWindowSize":1,"timeWindowUnit":"m"},"schedule":{"interval":"1m"},"scheduledTaskId":null,"tags":[],"throttle":null,"updatedAt":"2024-02-07T02:27:19.537Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"e79aaa90-8784-11ee-b9ba-89bbe73df7ff","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294457367,232778],"type":"alert","updated_at":"2024-02-07T08:27:37.367Z","version":"WzQ5NTQ0MCwxOV0="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"metrics.inventory_threshold.fired","params":{"documents":["{\"@timestamp\":\"2022-06-20T06:16:33.414Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"metrics.alert.inventory.threshold","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2022-05-31T10:10:47.084Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"CPU under heavy load","notifyWhen":"onActionGroupChange","params":{"criteria":[{"comparator":">","customMetric":{"aggregation":"avg","field":"","id":"alert-custom-metric","type":"custom"},"metric":"cpu","threshold":[70],"timeSize":1,"timeUnit":"m"}],"nodeType":"host","sourceId":"default"},"schedule":{"interval":"1m"},"scheduledTaskId":null,"tags":["infra","opencrvs-builtin"],"throttle":null,"updatedAt":"2024-02-07T02:27:30.573Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"f022bee0-e0c9-11ec-99b8-dbfd54551fda","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294457362,232774],"type":"alert","updated_at":"2024-02-07T08:27:37.362Z","version":"WzQ5NTQzOCwxOV0="} -{"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"metrics.inventory_threshold.fired","params":{"documents":["{\"@timestamp\":\"2023-11-17T13:17:52.791Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"metrics.alert.inventory.threshold","apiKey":null,"apiKeyOwner":null,"consumer":"infrastructure","createdAt":"2022-05-31T10:10:47.080Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Low on available disk space","notifyWhen":"onActionGroupChange","params":{"alertOnNoData":true,"criteria":[{"comparator":">","customMetric":{"aggregation":"max","field":"system.filesystem.used.pct","id":"alert-custom-metric","label":"","type":"custom"},"metric":"custom","threshold":[0.7],"timeSize":1,"timeUnit":"h","warningComparator":">","warningThreshold":[0.5]}],"filterQuery":"{\"bool\":{\"should\":[{\"match_phrase\":{\"system.filesystem.device_name\":\"/dev/mapper/cryptfs\"}}],\"minimum_should_match\":1}}","filterQueryText":"system.filesystem.device_name : \"/dev/mapper/cryptfs\"","nodeType":"host","sourceId":"default"},"schedule":{"interval":"1h"},"scheduledTaskId":null,"tags":["infra","opencrvs-builtin"],"throttle":null,"updatedAt":"2024-02-07T02:27:20.542Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"f023d050-e0c9-11ec-99b8-dbfd54551fda","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294448366,232768],"type":"alert","updated_at":"2024-02-07T08:27:28.366Z","version":"WzQ5NTQzNSwxOV0="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"threshold_met","params":{"documents":["{\"@timestamp\":\"2023-06-15T07:57:35.954Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"],"indexOverride":"kibana-alert-history-services"}}],"alertTypeId":"apm.error_rate","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2022-05-31T10:10:47.069Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Error in service","notifyWhen":"onActionGroupChange","params":{"environment":"ENVIRONMENT_ALL","threshold":1,"windowSize":1,"windowUnit":"m"},"schedule":{"interval":"1m"},"scheduledTaskId":null,"tags":[],"throttle":null,"updatedAt":"2024-02-07T02:27:21.551Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"f02b4a60-e0c9-11ec-99b8-dbfd54551fda","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294457374,232780],"type":"alert","updated_at":"2024-02-07T08:27:37.374Z","version":"WzQ5NTQ0MSwxOV0="} {"excludedObjects":[],"excludedObjectsCount":0,"exportedCount":9,"missingRefCount":0,"missingReferences":[]} \ No newline at end of file From 8e7311a6b6ed3387c4c18442c522506ad7bef763 Mon Sep 17 00:00:00 2001 From: Markus Date: Thu, 11 Jul 2024 10:55:57 +0300 Subject: [PATCH 04/47] take up docker module --- .../monitoring/beats/metricbeat.yml | 37 ++----------------- 1 file changed, 4 insertions(+), 33 deletions(-) diff --git a/infrastructure/monitoring/beats/metricbeat.yml b/infrastructure/monitoring/beats/metricbeat.yml index 520ed80bd..98bf4ffdb 100644 --- a/infrastructure/monitoring/beats/metricbeat.yml +++ b/infrastructure/monitoring/beats/metricbeat.yml @@ -19,39 +19,10 @@ metricbeat.modules: #------------------------------- System Module ------------------------------- - module: system metricsets: - [ - 'cpu', - 'load', - 'memory', - 'network', - 'process', - 'process_summary', - 'core', - 'diskio', - 'socket' - ] - processes: ['.*'] - process.include_top_n: - by_cpu: 5 - by_memory: 5 - period: 10s - cpu.metrics: ['percentages'] - core.metrics: ['percentages'] - - # - module: system - # period: 1m - # metricsets: - # - filesystem - # - fsstat - # processors: - # - drop_event.when.regexp: - # system.filesystem.mount_point: '^/(sys|cgroup|proc|dev|etc|host|lib)($|/)' - - - module: system - period: 15m - metricsets: - - uptime - + - filesystem + processors: + - drop_event.when.regexp: + system.filesystem.mount_point: '^/(sys|cgroup|proc|dev|etc|host|lib)($|/)' #------------------------------- Docker Module ------------------------------- - module: docker metricsets: From 65ea4b0b43503173ee33b350ca75f3d052b3d8d4 Mon Sep 17 00:00:00 2001 From: Markus Date: Thu, 11 Jul 2024 16:37:10 +0300 Subject: [PATCH 05/47] Use kibana access token for connections --- infrastructure/docker-compose.deploy.yml | 6 ++++-- infrastructure/monitoring/kibana/kibana.yml | 2 +- infrastructure/monitoring/kibana/setup-config.sh | 15 ++++++++------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index 4d8a8473f..85ecfc2c5 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -77,6 +77,7 @@ services: - ELASTICSEARCH_HOST=elasticsearch:9200 - ELASTICSEARCH_USERNAME=elastic - ELASTICSEARCH_PASSWORD=${ELASTICSEARCH_SUPERUSER_PASSWORD} + - KIBANA_ACCESS_TOKEN=${KIBANA_ACCESS_TOKEN} - KIBANA_HOST=kibana:5601 command: ['--strict.perms=false'] deploy: @@ -127,8 +128,8 @@ services: entrypoint: [ 'curl', - '-u', - 'elastic:${ELASTICSEARCH_SUPERUSER_PASSWORD}', + '-H', + 'Authorization: Bearer ${KIBANA_ACCESS_TOKEN}', '-X', 'POST', 'http://kibana:5601/api/saved_objects/_import?overwrite=true', @@ -175,6 +176,7 @@ services: environment: - ELASTICSEARCH_USERNAME=elastic - ELASTICSEARCH_PASSWORD=${ELASTICSEARCH_SUPERUSER_PASSWORD} + - KIBANA_ACCESS_TOKEN=${KIBANA_ACCESS_TOKEN} configs: - source: kibana.{{ts}} target: /usr/share/kibana/config/kibana.yml diff --git a/infrastructure/monitoring/kibana/kibana.yml b/infrastructure/monitoring/kibana/kibana.yml index 90404ecab..1530fc336 100644 --- a/infrastructure/monitoring/kibana/kibana.yml +++ b/infrastructure/monitoring/kibana/kibana.yml @@ -64,7 +64,7 @@ xpack.infra.sources.default.logAlias: 'logs-*,filebeat-*,kibana_sample_data_logs # Kibana can also authenticate to Elasticsearch via "service account tokens". # Service account tokens are Bearer style tokens that replace the traditional username/password based configuration. # Use this token instead of a username/password. -# elasticsearch.serviceAccountToken: "my_token" +elasticsearch.serviceAccountToken: '{{KIBANA_ACCESS_TOKEN}}' # Time in milliseconds to wait for Elasticsearch to respond to pings. Defaults to the value of # the elasticsearch.requestTimeout setting. diff --git a/infrastructure/monitoring/kibana/setup-config.sh b/infrastructure/monitoring/kibana/setup-config.sh index a2f5cca78..34fc10bd3 100755 --- a/infrastructure/monitoring/kibana/setup-config.sh +++ b/infrastructure/monitoring/kibana/setup-config.sh @@ -15,7 +15,7 @@ kibana_alerting_api_url="http://kibana:5601/api/alerting/rules/_find?page=1&per_ docker_command="docker run --rm -v /opt/opencrvs/infrastructure/monitoring/kibana/config.ndjson:/config.ndjson --network=opencrvs_overlay_net curlimages/curl" # Initial API status check to ensure Kibana is ready -status_code=$($docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD -o /dev/null -w '%{http_code}' "$kibana_alerting_api_url") +status_code=$($docker_command --connect-timeout 60 -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" -o /dev/null -w '%{http_code}' "$kibana_alerting_api_url") if [ "$status_code" -ne 200 ]; then echo "Kibana is not ready. API returned status code: $status_code" @@ -23,15 +23,16 @@ if [ "$status_code" -ne 200 ]; then fi # Delete all alerts -$docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do - $docker_command --connect-timeout 60 -X DELETE -H 'kbn-xsrf: true' -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "http://kibana:5601/api/alerting/rule/$id" +$docker_command --connect-timeout 60 -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do + $docker_command --connect-timeout 60 -X DELETE -H 'kbn-xsrf: true' -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" "http://kibana:5601/api/alerting/rule/$id" done # Import configuration -$docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD -X POST "http://kibana:5601/api/saved_objects/_import?overwrite=true" -H 'kbn-xsrf: true' --form file=@/config.ndjson > /dev/null +$docker_command --connect-timeout 60 -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" -X POST "http://kibana:5601/api/saved_objects/_import?overwrite=true" -H 'kbn-xsrf: true' --form file=@/config.ndjson > /dev/null # Re-enable all alerts -$docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do - $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "http://kibana:5601/api/alerting/rule/$id/_disable" - $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "http://kibana:5601/api/alerting/rule/$id/_enable" +$docker_command --connect-timeout 60 -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do + $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" "http://kibana:5601/api/alerting/rule/$id/_disable" + $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" "http://kibana:5601/api/alerting/rule/$id/_enable" done + From fd200d0719b2a5a0ba397ffd161fb51d88fa60d7 Mon Sep 17 00:00:00 2001 From: Markus Date: Fri, 12 Jul 2024 10:08:28 +0300 Subject: [PATCH 06/47] use kibana_system account instead of elastic --- infrastructure/deployment/deploy.sh | 3 +++ infrastructure/docker-compose.deploy.yml | 11 +++++------ infrastructure/elasticsearch/setup-users.sh | 1 + infrastructure/monitoring/kibana/kibana.yml | 1 - infrastructure/monitoring/kibana/setup-config.sh | 15 +++++++-------- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/infrastructure/deployment/deploy.sh b/infrastructure/deployment/deploy.sh index 5d8725915..9fe757fad 100755 --- a/infrastructure/deployment/deploy.sh +++ b/infrastructure/deployment/deploy.sh @@ -331,6 +331,9 @@ export ROTATING_METRICBEAT_ELASTIC_PASSWORD=`generate_password` # Used by APM for writing data to ElasticSearch export ROTATING_APM_ELASTIC_PASSWORD=`generate_password` +# Used by Kibana for writing data to ElasticSearch +export ROTATING_KIBANA_ELASTIC_PASSWORD=`generate_password` + # Download core compose files to /tmp/ for compose_file in ${COMPOSE_FILES_DOWNLOADED_FROM_CORE[@]}; do if [ ! -f $compose_file ]; then diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index 85ecfc2c5..78580d51b 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -77,7 +77,6 @@ services: - ELASTICSEARCH_HOST=elasticsearch:9200 - ELASTICSEARCH_USERNAME=elastic - ELASTICSEARCH_PASSWORD=${ELASTICSEARCH_SUPERUSER_PASSWORD} - - KIBANA_ACCESS_TOKEN=${KIBANA_ACCESS_TOKEN} - KIBANA_HOST=kibana:5601 command: ['--strict.perms=false'] deploy: @@ -128,8 +127,8 @@ services: entrypoint: [ 'curl', - '-H', - 'Authorization: Bearer ${KIBANA_ACCESS_TOKEN}', + '-u', + 'kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD}', '-X', 'POST', 'http://kibana:5601/api/saved_objects/_import?overwrite=true', @@ -174,9 +173,8 @@ services: networks: - overlay_net environment: - - ELASTICSEARCH_USERNAME=elastic - - ELASTICSEARCH_PASSWORD=${ELASTICSEARCH_SUPERUSER_PASSWORD} - - KIBANA_ACCESS_TOKEN=${KIBANA_ACCESS_TOKEN} + - ELASTICSEARCH_USERNAME=kibana_system + - ELASTICSEARCH_PASSWORD=${ROTATING_KIBANA_ELASTIC_PASSWORD} configs: - source: kibana.{{ts}} target: /usr/share/kibana/config/kibana.yml @@ -367,6 +365,7 @@ services: - APM_ELASTIC_PASSWORD=${ROTATING_APM_ELASTIC_PASSWORD} - SEARCH_ELASTIC_USERNAME=search-user - SEARCH_ELASTIC_PASSWORD=${ROTATING_SEARCH_ELASTIC_PASSWORD} + - KIBANA_ELASTIC_PASSWORD=${ROTATING_KIBANA_ELASTIC_PASSWORD} - KIBANA_USERNAME=${KIBANA_USERNAME} - KIBANA_PASSWORD=${KIBANA_PASSWORD} volumes: diff --git a/infrastructure/elasticsearch/setup-users.sh b/infrastructure/elasticsearch/setup-users.sh index 366090da1..05f6aae0f 100755 --- a/infrastructure/elasticsearch/setup-users.sh +++ b/infrastructure/elasticsearch/setup-users.sh @@ -25,6 +25,7 @@ users_passwords=( [beats_system]="${METRICBEAT_ELASTIC_PASSWORD:-}" [apm_system]="${APM_ELASTIC_PASSWORD:-}" [$KIBANA_USERNAME]="${KIBANA_PASSWORD:-}" + [kibana_system]="${KIBANA_ELASTIC_PASSWORD:-}" ) # ------------------------------------- diff --git a/infrastructure/monitoring/kibana/kibana.yml b/infrastructure/monitoring/kibana/kibana.yml index 1530fc336..7b2f480c1 100644 --- a/infrastructure/monitoring/kibana/kibana.yml +++ b/infrastructure/monitoring/kibana/kibana.yml @@ -64,7 +64,6 @@ xpack.infra.sources.default.logAlias: 'logs-*,filebeat-*,kibana_sample_data_logs # Kibana can also authenticate to Elasticsearch via "service account tokens". # Service account tokens are Bearer style tokens that replace the traditional username/password based configuration. # Use this token instead of a username/password. -elasticsearch.serviceAccountToken: '{{KIBANA_ACCESS_TOKEN}}' # Time in milliseconds to wait for Elasticsearch to respond to pings. Defaults to the value of # the elasticsearch.requestTimeout setting. diff --git a/infrastructure/monitoring/kibana/setup-config.sh b/infrastructure/monitoring/kibana/setup-config.sh index 34fc10bd3..e6f8d864f 100755 --- a/infrastructure/monitoring/kibana/setup-config.sh +++ b/infrastructure/monitoring/kibana/setup-config.sh @@ -15,7 +15,7 @@ kibana_alerting_api_url="http://kibana:5601/api/alerting/rules/_find?page=1&per_ docker_command="docker run --rm -v /opt/opencrvs/infrastructure/monitoring/kibana/config.ndjson:/config.ndjson --network=opencrvs_overlay_net curlimages/curl" # Initial API status check to ensure Kibana is ready -status_code=$($docker_command --connect-timeout 60 -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" -o /dev/null -w '%{http_code}' "$kibana_alerting_api_url") +status_code=$($docker_command --connect-timeout 60 -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} -o /dev/null -w '%{http_code}' "$kibana_alerting_api_url") if [ "$status_code" -ne 200 ]; then echo "Kibana is not ready. API returned status code: $status_code" @@ -23,16 +23,15 @@ if [ "$status_code" -ne 200 ]; then fi # Delete all alerts -$docker_command --connect-timeout 60 -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do - $docker_command --connect-timeout 60 -X DELETE -H 'kbn-xsrf: true' -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" "http://kibana:5601/api/alerting/rule/$id" +$docker_command --connect-timeout 60 -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do + $docker_command --connect-timeout 60 -X DELETE -H 'kbn-xsrf: true' -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} "http://kibana:5601/api/alerting/rule/$id" done # Import configuration -$docker_command --connect-timeout 60 -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" -X POST "http://kibana:5601/api/saved_objects/_import?overwrite=true" -H 'kbn-xsrf: true' --form file=@/config.ndjson > /dev/null +$docker_command --connect-timeout 60 -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} -X POST "http://kibana:5601/api/saved_objects/_import?overwrite=true" -H 'kbn-xsrf: true' --form file=@/config.ndjson > /dev/null # Re-enable all alerts -$docker_command --connect-timeout 60 -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do - $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" "http://kibana:5601/api/alerting/rule/$id/_disable" - $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -H "Authorization: Bearer $KIBANA_ACCESS_TOKEN" "http://kibana:5601/api/alerting/rule/$id/_enable" +$docker_command --connect-timeout 60 -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do + $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} "http://kibana:5601/api/alerting/rule/$id/_disable" + $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} "http://kibana:5601/api/alerting/rule/$id/_enable" done - From 08908e1be64c405043dc12af5abe2dfab9f2c8bc Mon Sep 17 00:00:00 2001 From: Markus Date: Fri, 12 Jul 2024 11:29:08 +0300 Subject: [PATCH 07/47] add kibana_system_password to top level env --- infrastructure/deployment/deploy.sh | 2 -- infrastructure/docker-compose.deploy.yml | 6 ++--- infrastructure/elasticsearch/setup-users.sh | 2 +- .../environments/setup-environment.ts | 24 +++++++++++++++++++ .../monitoring/kibana/setup-config.sh | 14 +++++------ 5 files changed, 35 insertions(+), 13 deletions(-) diff --git a/infrastructure/deployment/deploy.sh b/infrastructure/deployment/deploy.sh index 9fe757fad..e1e58fe10 100755 --- a/infrastructure/deployment/deploy.sh +++ b/infrastructure/deployment/deploy.sh @@ -331,8 +331,6 @@ export ROTATING_METRICBEAT_ELASTIC_PASSWORD=`generate_password` # Used by APM for writing data to ElasticSearch export ROTATING_APM_ELASTIC_PASSWORD=`generate_password` -# Used by Kibana for writing data to ElasticSearch -export ROTATING_KIBANA_ELASTIC_PASSWORD=`generate_password` # Download core compose files to /tmp/ for compose_file in ${COMPOSE_FILES_DOWNLOADED_FROM_CORE[@]}; do diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index 78580d51b..ef35f1763 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -128,7 +128,7 @@ services: [ 'curl', '-u', - 'kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD}', + 'kibana_system:${KIBANA_SYSTEM_PASSWORD}', '-X', 'POST', 'http://kibana:5601/api/saved_objects/_import?overwrite=true', @@ -174,7 +174,7 @@ services: - overlay_net environment: - ELASTICSEARCH_USERNAME=kibana_system - - ELASTICSEARCH_PASSWORD=${ROTATING_KIBANA_ELASTIC_PASSWORD} + - ELASTICSEARCH_PASSWORD=${KIBANA_SYSTEM_PASSWORD} configs: - source: kibana.{{ts}} target: /usr/share/kibana/config/kibana.yml @@ -365,7 +365,7 @@ services: - APM_ELASTIC_PASSWORD=${ROTATING_APM_ELASTIC_PASSWORD} - SEARCH_ELASTIC_USERNAME=search-user - SEARCH_ELASTIC_PASSWORD=${ROTATING_SEARCH_ELASTIC_PASSWORD} - - KIBANA_ELASTIC_PASSWORD=${ROTATING_KIBANA_ELASTIC_PASSWORD} + - KIBANA_SYSTEM_PASSWORD=${KIBANA_SYSTEM_PASSWORD} - KIBANA_USERNAME=${KIBANA_USERNAME} - KIBANA_PASSWORD=${KIBANA_PASSWORD} volumes: diff --git a/infrastructure/elasticsearch/setup-users.sh b/infrastructure/elasticsearch/setup-users.sh index 05f6aae0f..16c7534dd 100755 --- a/infrastructure/elasticsearch/setup-users.sh +++ b/infrastructure/elasticsearch/setup-users.sh @@ -25,7 +25,7 @@ users_passwords=( [beats_system]="${METRICBEAT_ELASTIC_PASSWORD:-}" [apm_system]="${APM_ELASTIC_PASSWORD:-}" [$KIBANA_USERNAME]="${KIBANA_PASSWORD:-}" - [kibana_system]="${KIBANA_ELASTIC_PASSWORD:-}" + [kibana_system]="${KIBANA_SYSTEM_PASSWORD:-}" ) # ------------------------------------- diff --git a/infrastructure/environments/setup-environment.ts b/infrastructure/environments/setup-environment.ts index 83a0642bf..aa5d3dd41 100644 --- a/infrastructure/environments/setup-environment.ts +++ b/infrastructure/environments/setup-environment.ts @@ -653,6 +653,13 @@ const derivedVariables = [ type: 'disabled', scope: 'ENVIRONMENT' }, + { + name: 'KIBANA_SYSTEM_PASSWORD', + valueLabel: 'KIBANA_SYSTEM_PASSWORD', + valueType: 'SECRET', + type: 'disabled', + scope: 'ENVIRONMENT' + }, { name: 'MINIO_ROOT_USER', valueLabel: 'MINIO_ROOT_USER', @@ -1103,6 +1110,23 @@ const SPECIAL_NON_APPLICATION_ENVIRONMENTS = ['jump', 'backup'] ), scope: 'ENVIRONMENT' as const }, + { + name: 'KIBANA_SYSTEM_PASSWORD', + type: 'SECRET' as const, + didExist: findExistingValue( + 'KIBANA_SYSTEM_PASSWORD', + 'SECRET', + 'ENVIRONMENT', + existingValues + ), + value: findExistingOrDefine( + 'KIBANA_SYSTEM_PASSWORD', + 'SECRET', + 'ENVIRONMENT', + generateLongPassword() + ), + scope: 'ENVIRONMENT' as const + }, { name: 'MINIO_ROOT_USER', type: 'SECRET' as const, diff --git a/infrastructure/monitoring/kibana/setup-config.sh b/infrastructure/monitoring/kibana/setup-config.sh index e6f8d864f..e3876ae68 100755 --- a/infrastructure/monitoring/kibana/setup-config.sh +++ b/infrastructure/monitoring/kibana/setup-config.sh @@ -15,7 +15,7 @@ kibana_alerting_api_url="http://kibana:5601/api/alerting/rules/_find?page=1&per_ docker_command="docker run --rm -v /opt/opencrvs/infrastructure/monitoring/kibana/config.ndjson:/config.ndjson --network=opencrvs_overlay_net curlimages/curl" # Initial API status check to ensure Kibana is ready -status_code=$($docker_command --connect-timeout 60 -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} -o /dev/null -w '%{http_code}' "$kibana_alerting_api_url") +status_code=$($docker_command --connect-timeout 60 -u kibana_system:${KIBANA_SYSTEM_PASSWORD} -o /dev/null -w '%{http_code}' "$kibana_alerting_api_url") if [ "$status_code" -ne 200 ]; then echo "Kibana is not ready. API returned status code: $status_code" @@ -23,15 +23,15 @@ if [ "$status_code" -ne 200 ]; then fi # Delete all alerts -$docker_command --connect-timeout 60 -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do - $docker_command --connect-timeout 60 -X DELETE -H 'kbn-xsrf: true' -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} "http://kibana:5601/api/alerting/rule/$id" +$docker_command --connect-timeout 60 -u kibana_system:${KIBANA_SYSTEM_PASSWORD} "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do + $docker_command --connect-timeout 60 -X DELETE -H 'kbn-xsrf: true' -u kibana_system:${KIBANA_SYSTEM_PASSWORD} "http://kibana:5601/api/alerting/rule/$id" done # Import configuration -$docker_command --connect-timeout 60 -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} -X POST "http://kibana:5601/api/saved_objects/_import?overwrite=true" -H 'kbn-xsrf: true' --form file=@/config.ndjson > /dev/null +$docker_command --connect-timeout 60 -u kibana_system:${KIBANA_SYSTEM_PASSWORD} -X POST "http://kibana:5601/api/saved_objects/_import?overwrite=true" -H 'kbn-xsrf: true' --form file=@/config.ndjson > /dev/null # Re-enable all alerts -$docker_command --connect-timeout 60 -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do - $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} "http://kibana:5601/api/alerting/rule/$id/_disable" - $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u kibana_system:${ROTATING_KIBANA_ELASTIC_PASSWORD} "http://kibana:5601/api/alerting/rule/$id/_enable" +$docker_command --connect-timeout 60 -u kibana_system:${KIBANA_SYSTEM_PASSWORD} "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do + $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u kibana_system:${KIBANA_SYSTEM_PASSWORD} "http://kibana:5601/api/alerting/rule/$id/_disable" + $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u kibana_system:${KIBANA_SYSTEM_PASSWORD} "http://kibana:5601/api/alerting/rule/$id/_enable" done From 63755d29d3e169b617998b8048635db961629a37 Mon Sep 17 00:00:00 2001 From: Markus Date: Fri, 12 Jul 2024 11:39:37 +0300 Subject: [PATCH 08/47] remove reference to kibana_system from setup-users.sh --- infrastructure/elasticsearch/setup-users.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/infrastructure/elasticsearch/setup-users.sh b/infrastructure/elasticsearch/setup-users.sh index 16c7534dd..366090da1 100755 --- a/infrastructure/elasticsearch/setup-users.sh +++ b/infrastructure/elasticsearch/setup-users.sh @@ -25,7 +25,6 @@ users_passwords=( [beats_system]="${METRICBEAT_ELASTIC_PASSWORD:-}" [apm_system]="${APM_ELASTIC_PASSWORD:-}" [$KIBANA_USERNAME]="${KIBANA_PASSWORD:-}" - [kibana_system]="${KIBANA_SYSTEM_PASSWORD:-}" ) # ------------------------------------- From adfe12bad1c4e83ac5e2eb34aed49808ddaf5706 Mon Sep 17 00:00:00 2001 From: Markus Date: Fri, 12 Jul 2024 12:30:14 +0300 Subject: [PATCH 09/47] use elastic user on setup-config kibana_system does not have access to kibana --- infrastructure/monitoring/kibana/setup-config.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/infrastructure/monitoring/kibana/setup-config.sh b/infrastructure/monitoring/kibana/setup-config.sh index e3876ae68..b94cab21f 100755 --- a/infrastructure/monitoring/kibana/setup-config.sh +++ b/infrastructure/monitoring/kibana/setup-config.sh @@ -15,7 +15,7 @@ kibana_alerting_api_url="http://kibana:5601/api/alerting/rules/_find?page=1&per_ docker_command="docker run --rm -v /opt/opencrvs/infrastructure/monitoring/kibana/config.ndjson:/config.ndjson --network=opencrvs_overlay_net curlimages/curl" # Initial API status check to ensure Kibana is ready -status_code=$($docker_command --connect-timeout 60 -u kibana_system:${KIBANA_SYSTEM_PASSWORD} -o /dev/null -w '%{http_code}' "$kibana_alerting_api_url") +status_code=$($docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD -o /dev/null -w '%{http_code}' "$kibana_alerting_api_url") if [ "$status_code" -ne 200 ]; then echo "Kibana is not ready. API returned status code: $status_code" @@ -23,15 +23,15 @@ if [ "$status_code" -ne 200 ]; then fi # Delete all alerts -$docker_command --connect-timeout 60 -u kibana_system:${KIBANA_SYSTEM_PASSWORD} "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do - $docker_command --connect-timeout 60 -X DELETE -H 'kbn-xsrf: true' -u kibana_system:${KIBANA_SYSTEM_PASSWORD} "http://kibana:5601/api/alerting/rule/$id" +$docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do + $docker_command --connect-timeout 60 -X DELETE -H 'kbn-xsrf: true' -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "http://kibana:5601/api/alerting/rule/$id" done # Import configuration -$docker_command --connect-timeout 60 -u kibana_system:${KIBANA_SYSTEM_PASSWORD} -X POST "http://kibana:5601/api/saved_objects/_import?overwrite=true" -H 'kbn-xsrf: true' --form file=@/config.ndjson > /dev/null +$docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD -X POST "http://kibana:5601/api/saved_objects/_import?overwrite=true" -H 'kbn-xsrf: true' --form file=@/config.ndjson > /dev/null # Re-enable all alerts -$docker_command --connect-timeout 60 -u kibana_system:${KIBANA_SYSTEM_PASSWORD} "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do - $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u kibana_system:${KIBANA_SYSTEM_PASSWORD} "http://kibana:5601/api/alerting/rule/$id/_disable" - $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u kibana_system:${KIBANA_SYSTEM_PASSWORD} "http://kibana:5601/api/alerting/rule/$id/_enable" -done +$docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do + $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "http://kibana:5601/api/alerting/rule/$id/_disable" + $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "http://kibana:5601/api/alerting/rule/$id/_enable" +done \ No newline at end of file From c923aea406029a5c1e328dbbdb904b8b4b25d5a5 Mon Sep 17 00:00:00 2001 From: Markus Date: Fri, 12 Jul 2024 12:41:04 +0300 Subject: [PATCH 10/47] remove deprecated dependencies from elastic and kibana --- infrastructure/docker-compose.deploy.yml | 1 - infrastructure/monitoring/kibana/kibana.yml | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index ef35f1763..69d30bb7b 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -282,7 +282,6 @@ services: - path.repo=/data/backups/elasticsearch - cluster.name=docker-cluster - network.host=0.0.0.0 - - discovery.zen.minimum_master_nodes=1 - discovery.type=single-node - xpack.security.enabled=true - xpack.security.authc.api_key.enabled=true diff --git a/infrastructure/monitoring/kibana/kibana.yml b/infrastructure/monitoring/kibana/kibana.yml index 7b2f480c1..ba87306dc 100644 --- a/infrastructure/monitoring/kibana/kibana.yml +++ b/infrastructure/monitoring/kibana/kibana.yml @@ -53,7 +53,6 @@ monitoring.ui.container.elasticsearch.enabled: true xpack.encryptedSavedObjects.encryptionKey: '{{KIBANA_ENCRYPTION_KEY}}' xpack.reporting.encryptionKey: '{{KIBANA_ENCRYPTION_KEY}}' xpack.actions.preconfiguredAlertHistoryEsIndex: true -xpack.infra.sources.default.logAlias: 'logs-*,filebeat-*,kibana_sample_data_logs*,logstash*' # If your Elasticsearch is protected with basic authentication, these settings provide # the username and password that the Kibana server uses to perform maintenance on the Kibana # index at startup. Your Kibana users still need to authenticate with Elasticsearch, which @@ -103,7 +102,7 @@ xpack.infra.sources.default.logAlias: 'logs-*,filebeat-*,kibana_sample_data_logs # =================== System: Logging =================== # Set the value of this setting to off to suppress all logging output, or to debug to log everything. Defaults to 'info' -logging.root.level: error +logging.root.level: debug # Enables you to specify a file where Kibana stores log output. #logging.appenders.default: # type: file From cc410abc8c1be88700c5eb27a1ec0db5d8d9e799 Mon Sep 17 00:00:00 2001 From: Markus Date: Mon, 15 Jul 2024 13:36:45 +0300 Subject: [PATCH 11/47] upgrade kibana to 8.14.3 --- infrastructure/docker-compose.deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index 69d30bb7b..cde5be71d 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -156,7 +156,7 @@ services: gelf-address: 'udp://127.0.0.1:12201' tag: 'setup-kibana-config' kibana: - image: docker.elastic.co/kibana/kibana:7.17.0 + image: docker.elastic.co/kibana/kibana:8.14.3 restart: always deploy: labels: From 94e9fed45440072ce9d817e0ced4a435bc348f0d Mon Sep 17 00:00:00 2001 From: Markus Date: Mon, 15 Jul 2024 13:57:48 +0300 Subject: [PATCH 12/47] upgrade logstash to 8.14.3 --- infrastructure/docker-compose.deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index cde5be71d..152e48796 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -408,7 +408,7 @@ services: tag: 'elastalert' logstash: - image: logstash:7.17.0 + image: logstash:8.14.3 command: logstash -f /etc/logstash/logstash.conf --verbose ports: - '12201:12201' From 495a3020aa6001f0188e40bcc91e84232e49974a Mon Sep 17 00:00:00 2001 From: Markus Date: Mon, 15 Jul 2024 14:05:46 +0300 Subject: [PATCH 13/47] upgrade beats to 8.14.3 use monitoring instead of deprecated xpack.monitoring --- infrastructure/docker-compose.deploy.yml | 4 ++-- infrastructure/monitoring/beats/metricbeat.yml | 12 ++++++------ infrastructure/monitoring/filebeat/filebeat.yml | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index 152e48796..e72e52d63 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -60,7 +60,7 @@ services: - overlay_net filebeat: - image: docker.elastic.co/beats/filebeat:7.17.0 + image: docker.elastic.co/beats/filebeat:8.14.3 user: root networks: - overlay_net @@ -85,7 +85,7 @@ services: - 'traefik.enable=false' metricbeat: - image: docker.elastic.co/beats/metricbeat:7.17.13 + image: docker.elastic.co/beats/metricbeat:8.14.3 user: root cap_add: - SYS_PTRACE diff --git a/infrastructure/monitoring/beats/metricbeat.yml b/infrastructure/monitoring/beats/metricbeat.yml index 98bf4ffdb..bb0f9d01b 100644 --- a/infrastructure/monitoring/beats/metricbeat.yml +++ b/infrastructure/monitoring/beats/metricbeat.yml @@ -24,11 +24,11 @@ metricbeat.modules: - drop_event.when.regexp: system.filesystem.mount_point: '^/(sys|cgroup|proc|dev|etc|host|lib)($|/)' #------------------------------- Docker Module ------------------------------- - - module: docker - metricsets: - ['container', 'cpu', 'diskio', 'healthcheck', 'info', 'memory', 'network'] - hosts: ['unix:///var/run/docker.sock'] - period: 10s + # - module: docker + # metricsets: + # ['container', 'cpu', 'diskio', 'healthcheck', 'info', 'memory', 'network'] + # hosts: ['unix:///var/run/docker.sock'] + # period: 10s #================================ Processors =================================== processors: @@ -56,7 +56,7 @@ setup.kibana: password: ${KIBANA_PASSWORD} #============================== Xpack Monitoring =============================== -xpack.monitoring: +monitoring: enabled: true elasticsearch: username: ${BEATS_USERNAME} diff --git a/infrastructure/monitoring/filebeat/filebeat.yml b/infrastructure/monitoring/filebeat/filebeat.yml index d2ac41bcb..8f75ec4b9 100644 --- a/infrastructure/monitoring/filebeat/filebeat.yml +++ b/infrastructure/monitoring/filebeat/filebeat.yml @@ -60,7 +60,7 @@ setup.kibana: password: ${ELASTICSEARCH_PASSWORD} #============================== Xpack Monitoring =============================== -xpack.monitoring: +monitoring: enabled: true elasticsearch: From 3b67e8906fa0c3f9c3df3114019f52e3c4da7fa8 Mon Sep 17 00:00:00 2001 From: Markus Date: Mon, 15 Jul 2024 14:42:18 +0300 Subject: [PATCH 14/47] upgrade apm server to 7.17.22 --- infrastructure/docker-compose.deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index e72e52d63..fdd49d7f4 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -431,7 +431,7 @@ services: - 'traefik.enable=false' replicas: 1 apm-server: - image: docker.elastic.co/apm/apm-server:7.15.2 + image: docker.elastic.co/apm/apm-server:7.17.22 cap_add: ['CHOWN', 'DAC_OVERRIDE', 'SETGID', 'SETUID'] cap_drop: ['ALL'] restart: always From 8a76f4bbceefa9fa041f4013e87c5737b6963e43 Mon Sep 17 00:00:00 2001 From: Markus Date: Mon, 15 Jul 2024 15:16:03 +0300 Subject: [PATCH 15/47] revert log changes after debugging --- .../monitoring/beats/metricbeat.yml | 39 ++++++++++++++++--- infrastructure/monitoring/kibana/kibana.yml | 2 +- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/infrastructure/monitoring/beats/metricbeat.yml b/infrastructure/monitoring/beats/metricbeat.yml index bb0f9d01b..78bd940a7 100644 --- a/infrastructure/monitoring/beats/metricbeat.yml +++ b/infrastructure/monitoring/beats/metricbeat.yml @@ -18,17 +18,46 @@ metricbeat.autodiscover: metricbeat.modules: #------------------------------- System Module ------------------------------- - module: system + metricsets: + [ + 'cpu', + 'load', + 'memory', + 'network', + 'process', + 'process_summary', + 'core', + 'diskio', + 'socket' + ] + processes: ['.*'] + process.include_top_n: + by_cpu: 5 + by_memory: 5 + period: 10s + cpu.metrics: ['percentages'] + core.metrics: ['percentages'] + + - module: system + period: 1m metricsets: - filesystem + - fsstat processors: - drop_event.when.regexp: system.filesystem.mount_point: '^/(sys|cgroup|proc|dev|etc|host|lib)($|/)' + + - module: system + period: 15m + metricsets: + - uptime + #------------------------------- Docker Module ------------------------------- - # - module: docker - # metricsets: - # ['container', 'cpu', 'diskio', 'healthcheck', 'info', 'memory', 'network'] - # hosts: ['unix:///var/run/docker.sock'] - # period: 10s + - module: docker + metricsets: + ['container', 'cpu', 'diskio', 'healthcheck', 'info', 'memory', 'network'] + hosts: ['unix:///var/run/docker.sock'] + period: 10s #================================ Processors =================================== processors: diff --git a/infrastructure/monitoring/kibana/kibana.yml b/infrastructure/monitoring/kibana/kibana.yml index ba87306dc..2cfdd4c66 100644 --- a/infrastructure/monitoring/kibana/kibana.yml +++ b/infrastructure/monitoring/kibana/kibana.yml @@ -102,7 +102,7 @@ xpack.actions.preconfiguredAlertHistoryEsIndex: true # =================== System: Logging =================== # Set the value of this setting to off to suppress all logging output, or to debug to log everything. Defaults to 'info' -logging.root.level: debug +logging.root.level: error # Enables you to specify a file where Kibana stores log output. #logging.appenders.default: # type: file From 208f002e01ad0c87480503ac63f88302a942f35e Mon Sep 17 00:00:00 2001 From: Markus Date: Tue, 16 Jul 2024 16:20:39 +0300 Subject: [PATCH 16/47] widen search user privileges to accommodate reindex job --- infrastructure/elasticsearch/roles/search_user.json | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/infrastructure/elasticsearch/roles/search_user.json b/infrastructure/elasticsearch/roles/search_user.json index b7be198b5..da60cdeb8 100644 --- a/infrastructure/elasticsearch/roles/search_user.json +++ b/infrastructure/elasticsearch/roles/search_user.json @@ -1,8 +1,17 @@ { + "cluster": ["manage"], "indices": [ { - "names": ["ocrvs"], - "privileges": ["write", "create", "create_index", "delete", "delete_index", "read"] + "names": ["ocrvs", "ocrvs-*"], + "privileges": [ + "write", + "create", + "create_index", + "delete", + "delete_index", + "read", + "manage" + ] } ] } From b13f4cac9fda9018d9db21d203407d5138761ade Mon Sep 17 00:00:00 2001 From: Markus Date: Tue, 16 Jul 2024 16:21:01 +0300 Subject: [PATCH 17/47] upgrade elastalert to 2.19 2.4 onwards elastic8 is supported --- infrastructure/docker-compose.deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/docker-compose.deploy.yml b/infrastructure/docker-compose.deploy.yml index fdd49d7f4..3af57d9e6 100644 --- a/infrastructure/docker-compose.deploy.yml +++ b/infrastructure/docker-compose.deploy.yml @@ -384,7 +384,7 @@ services: gelf-address: 'udp://127.0.0.1:12201' tag: 'setup-elasticsearch-users' elastalert: - image: jertel/elastalert2:2.3.0 + image: jertel/elastalert2:2.19.0 restart: unless-stopped environment: - ES_USERNAME=elastic From dcf1c2a6c03acfd5ebc84bf805060f39eeeadd70 Mon Sep 17 00:00:00 2001 From: Markus Date: Tue, 16 Jul 2024 17:05:09 +0300 Subject: [PATCH 18/47] use minimal access for search user --- infrastructure/elasticsearch/roles/search_user.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/infrastructure/elasticsearch/roles/search_user.json b/infrastructure/elasticsearch/roles/search_user.json index da60cdeb8..1c84a4173 100644 --- a/infrastructure/elasticsearch/roles/search_user.json +++ b/infrastructure/elasticsearch/roles/search_user.json @@ -1,5 +1,5 @@ { - "cluster": ["manage"], + "cluster": ["monitoring"], "indices": [ { "names": ["ocrvs", "ocrvs-*"], @@ -9,8 +9,7 @@ "create_index", "delete", "delete_index", - "read", - "manage" + "read" ] } ] From fcd680b5b0d31ca9523afd1f9e0622e4e1a547d8 Mon Sep 17 00:00:00 2001 From: Markus Date: Tue, 16 Jul 2024 17:26:26 +0300 Subject: [PATCH 19/47] update index creation error check match against v8 error message https://github.com/elastic/elasticsearch/blob/e64aab1b08f03a0af8a2845ff0cef226bde363af/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataCreateIndexService.java#L186 --- infrastructure/elasticsearch/setup-helpers.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/elasticsearch/setup-helpers.sh b/infrastructure/elasticsearch/setup-helpers.sh index c6a25e874..38caab50a 100755 --- a/infrastructure/elasticsearch/setup-helpers.sh +++ b/infrastructure/elasticsearch/setup-helpers.sh @@ -253,7 +253,7 @@ function create_elastic_index { echo "${output}" - if [[ "${output: -3}" -eq 200 || $output == *"resource_already_exists"* ]]; then + if [[ "${output: -3}" -eq 200 || $output == *"already exists as alias"* ]]; then result=0 fi From 895fdc66ea7676893d7ddf7bd976ed6967dcfb21 Mon Sep 17 00:00:00 2001 From: Markus Date: Tue, 16 Jul 2024 17:36:25 +0300 Subject: [PATCH 20/47] add manage privilege back to search user --- infrastructure/elasticsearch/roles/search_user.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/infrastructure/elasticsearch/roles/search_user.json b/infrastructure/elasticsearch/roles/search_user.json index 1c84a4173..da60cdeb8 100644 --- a/infrastructure/elasticsearch/roles/search_user.json +++ b/infrastructure/elasticsearch/roles/search_user.json @@ -1,5 +1,5 @@ { - "cluster": ["monitoring"], + "cluster": ["manage"], "indices": [ { "names": ["ocrvs", "ocrvs-*"], @@ -9,7 +9,8 @@ "create_index", "delete", "delete_index", - "read" + "read", + "manage" ] } ] From 6709b96b974d5cba6503276ed4ca510d65fc5bb1 Mon Sep 17 00:00:00 2001 From: Markus Date: Wed, 17 Jul 2024 08:42:07 +0300 Subject: [PATCH 21/47] add comment for error message check --- infrastructure/elasticsearch/setup-helpers.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/infrastructure/elasticsearch/setup-helpers.sh b/infrastructure/elasticsearch/setup-helpers.sh index 38caab50a..5df149fbb 100755 --- a/infrastructure/elasticsearch/setup-helpers.sh +++ b/infrastructure/elasticsearch/setup-helpers.sh @@ -253,6 +253,8 @@ function create_elastic_index { echo "${output}" + # @TODO: Preferably check whether the index already exists before creating it. + # Error message might change in the future so we should not depend on it. if [[ "${output: -3}" -eq 200 || $output == *"already exists as alias"* ]]; then result=0 fi From 51d5fd31090a0b3ea1d301076ed898a90ba09e82 Mon Sep 17 00:00:00 2001 From: Markus Date: Wed, 17 Jul 2024 13:39:05 +0300 Subject: [PATCH 22/47] fix cherry-pick inconsistencies Changes were initially ran against test country config --- infrastructure/deployment/deploy.sh | 1 - infrastructure/monitoring/elastalert/rules/alert.yaml | 6 +++--- infrastructure/monitoring/kibana/config.ndjson | 1 + infrastructure/monitoring/kibana/kibana.yml | 1 + 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/infrastructure/deployment/deploy.sh b/infrastructure/deployment/deploy.sh index e1e58fe10..5d8725915 100755 --- a/infrastructure/deployment/deploy.sh +++ b/infrastructure/deployment/deploy.sh @@ -331,7 +331,6 @@ export ROTATING_METRICBEAT_ELASTIC_PASSWORD=`generate_password` # Used by APM for writing data to ElasticSearch export ROTATING_APM_ELASTIC_PASSWORD=`generate_password` - # Download core compose files to /tmp/ for compose_file in ${COMPOSE_FILES_DOWNLOADED_FROM_CORE[@]}; do if [ ! -f $compose_file ]; then diff --git a/infrastructure/monitoring/elastalert/rules/alert.yaml b/infrastructure/monitoring/elastalert/rules/alert.yaml index 58a74d374..d5bfbcb50 100644 --- a/infrastructure/monitoring/elastalert/rules/alert.yaml +++ b/infrastructure/monitoring/elastalert/rules/alert.yaml @@ -30,9 +30,9 @@ filter: - term: rule.name.keyword: value: 'CPU under heavy load' - # - term: - # rule.name.keyword: - # value: 'Low on available disk space' + - term: + rule.name.keyword: + value: 'Low on available disk space' minimum_should_match: 1 alert: post2 diff --git a/infrastructure/monitoring/kibana/config.ndjson b/infrastructure/monitoring/kibana/config.ndjson index 6c26fe491..a8f47219d 100644 --- a/infrastructure/monitoring/kibana/config.ndjson +++ b/infrastructure/monitoring/kibana/config.ndjson @@ -5,5 +5,6 @@ {"attributes":{"anomalyThreshold":50,"description":"","fields":{"container":"container.id","host":"host.name","message":["message","@message"],"pod":"kubernetes.pod.uid","tiebreaker":"_doc","timestamp":"@timestamp"},"inventoryDefaultView":"0","logColumns":[{"timestampColumn":{"id":"5e7f964a-be8a-40d8-88d2-fbcfbdca0e2f"}},{"fieldColumn":{"field":"event.dataset","id":" eb9777a8-fcd3-420e-ba7d-172fff6da7a2"}},{"messageColumn":{"id":"b645d6da-824b-4723-9a2a-e8cece1645c0"}}],"logIndices":{"indexName":"logs-*,filebeat-*,kibana_sample_data_logs*,logstash*","type":"index_name"},"metricAlias":"metrics-*,metricbeat-*","metricsExplorerDefaultView":"0","name":"Default"},"coreMigrationVersion":"7.17.0","id":"default","migrationVersion":{"infrastructure-ui-source":"7.16.2"},"references":[],"sort":[1707273006619,217714],"type":"infrastructure-ui-source","updated_at":"2024-02-07T02:30:06.619Z","version":"WzQ5MzAyNywxOV0="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"query matched","params":{"documents":["{\"@timestamp\":\"2023-11-20T10:19:30.521Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":".es-query","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2023-11-20T09:12:19.237Z","createdBy":"elastic","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Successful SSH login","notifyWhen":"onActionGroupChange","params":{"esQuery":"{ \"query\": { \"bool\": { \"must\": [ \n { \"term\": { \"log.file.path\": \"/var/log/auth.log\" } },\n { \"term\": { \"event.outcome\": \"success\" }}\n ] } } }","index":["filebeat-*"],"size":100,"threshold":[1],"thresholdComparator":">=","timeField":"@timestamp","timeWindowSize":1,"timeWindowUnit":"m"},"schedule":{"interval":"1m"},"scheduledTaskId":null,"tags":[],"throttle":null,"updatedAt":"2024-02-07T02:27:19.537Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"e79aaa90-8784-11ee-b9ba-89bbe73df7ff","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294457367,232778],"type":"alert","updated_at":"2024-02-07T08:27:37.367Z","version":"WzQ5NTQ0MCwxOV0="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"metrics.inventory_threshold.fired","params":{"documents":["{\"@timestamp\":\"2022-06-20T06:16:33.414Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"metrics.alert.inventory.threshold","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2022-05-31T10:10:47.084Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"CPU under heavy load","notifyWhen":"onActionGroupChange","params":{"criteria":[{"comparator":">","customMetric":{"aggregation":"avg","field":"","id":"alert-custom-metric","type":"custom"},"metric":"cpu","threshold":[70],"timeSize":1,"timeUnit":"m"}],"nodeType":"host","sourceId":"default"},"schedule":{"interval":"1m"},"scheduledTaskId":null,"tags":["infra","opencrvs-builtin"],"throttle":null,"updatedAt":"2024-02-07T02:27:30.573Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"f022bee0-e0c9-11ec-99b8-dbfd54551fda","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294457362,232774],"type":"alert","updated_at":"2024-02-07T08:27:37.362Z","version":"WzQ5NTQzOCwxOV0="} +{"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"metrics.inventory_threshold.fired","params":{"documents":["{\"@timestamp\":\"2023-11-17T13:17:52.791Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"metrics.alert.inventory.threshold","apiKey":null,"apiKeyOwner":null,"consumer":"infrastructure","createdAt":"2022-05-31T10:10:47.080Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Low on available disk space","notifyWhen":"onActionGroupChange","params":{"alertOnNoData":true,"criteria":[{"comparator":">","customMetric":{"aggregation":"max","field":"system.filesystem.used.pct","id":"alert-custom-metric","label":"","type":"custom"},"metric":"custom","threshold":[0.7],"timeSize":1,"timeUnit":"h","warningComparator":">","warningThreshold":[0.5]}],"filterQuery":"{\"bool\":{\"should\":[{\"match_phrase\":{\"system.filesystem.device_name\":\"/dev/mapper/cryptfs\"}}],\"minimum_should_match\":1}}","filterQueryText":"system.filesystem.device_name : \"/dev/mapper/cryptfs\"","nodeType":"host","sourceId":"default"},"schedule":{"interval":"1h"},"scheduledTaskId":null,"tags":["infra","opencrvs-builtin"],"throttle":null,"updatedAt":"2024-02-07T02:27:20.542Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"f023d050-e0c9-11ec-99b8-dbfd54551fda","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294448366,232768],"type":"alert","updated_at":"2024-02-07T08:27:28.366Z","version":"WzQ5NTQzNSwxOV0="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"threshold_met","params":{"documents":["{\"@timestamp\":\"2023-06-15T07:57:35.954Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"],"indexOverride":"kibana-alert-history-services"}}],"alertTypeId":"apm.error_rate","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2022-05-31T10:10:47.069Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Error in service","notifyWhen":"onActionGroupChange","params":{"environment":"ENVIRONMENT_ALL","threshold":1,"windowSize":1,"windowUnit":"m"},"schedule":{"interval":"1m"},"scheduledTaskId":null,"tags":[],"throttle":null,"updatedAt":"2024-02-07T02:27:21.551Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"f02b4a60-e0c9-11ec-99b8-dbfd54551fda","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294457374,232780],"type":"alert","updated_at":"2024-02-07T08:27:37.374Z","version":"WzQ5NTQ0MSwxOV0="} {"excludedObjects":[],"excludedObjectsCount":0,"exportedCount":9,"missingRefCount":0,"missingReferences":[]} \ No newline at end of file diff --git a/infrastructure/monitoring/kibana/kibana.yml b/infrastructure/monitoring/kibana/kibana.yml index 2cfdd4c66..a97e87780 100644 --- a/infrastructure/monitoring/kibana/kibana.yml +++ b/infrastructure/monitoring/kibana/kibana.yml @@ -63,6 +63,7 @@ xpack.actions.preconfiguredAlertHistoryEsIndex: true # Kibana can also authenticate to Elasticsearch via "service account tokens". # Service account tokens are Bearer style tokens that replace the traditional username/password based configuration. # Use this token instead of a username/password. +# elasticsearch.serviceAccountToken: "my_token" # Time in milliseconds to wait for Elasticsearch to respond to pings. Defaults to the value of # the elasticsearch.requestTimeout setting. From a8e055f90a4009a144b1083fc3c0dfc308beeb69 Mon Sep 17 00:00:00 2001 From: Markus Date: Thu, 18 Jul 2024 10:55:47 +0300 Subject: [PATCH 23/47] set kibana_system password on setup-users.sh --- infrastructure/elasticsearch/setup-users.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/infrastructure/elasticsearch/setup-users.sh b/infrastructure/elasticsearch/setup-users.sh index 366090da1..30d2f5923 100755 --- a/infrastructure/elasticsearch/setup-users.sh +++ b/infrastructure/elasticsearch/setup-users.sh @@ -24,6 +24,7 @@ users_passwords=( [$SEARCH_ELASTIC_USERNAME]="${SEARCH_ELASTIC_PASSWORD:-}" [beats_system]="${METRICBEAT_ELASTIC_PASSWORD:-}" [apm_system]="${APM_ELASTIC_PASSWORD:-}" + [kibana_system]="${KIBANA_SYSTEM_PASSWORD:-}" [$KIBANA_USERNAME]="${KIBANA_PASSWORD:-}" ) From 2cb1a6bffcf1f24782fd084b9d2a6dbeec4f9ddb Mon Sep 17 00:00:00 2001 From: Markus Date: Thu, 18 Jul 2024 17:04:00 +0300 Subject: [PATCH 24/47] Include first commit to range, render commits to body --- .github/workflows/auto-pr-to-release.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/auto-pr-to-release.yml b/.github/workflows/auto-pr-to-release.yml index 0be40b8fb..84e5e2634 100644 --- a/.github/workflows/auto-pr-to-release.yml +++ b/.github/workflows/auto-pr-to-release.yml @@ -120,7 +120,8 @@ jobs: echo "First commit: ${{ env.FIRST_COMMIT_SHA }}" echo "Latest commit: ${{ env.LATEST_COMMIT_SHA }}" - COMMIT_RANGE="${{ env.FIRST_COMMIT_SHA }}..${{ env.LATEST_COMMIT_SHA }}" + # NOTE: git cherry-pick range needs ~ to include the FIRST_COMMIT_SHA (https://stackoverflow.com/questions/1994463/how-to-cherry-pick-a-range-of-commits-and-merge-them-into-another-branch) + COMMIT_RANGE="${{ env.FIRST_COMMIT_SHA }}~..${{ env.LATEST_COMMIT_SHA }}" if [ "${{ env.FIRST_COMMIT_SHA }}" == "${{ env.LATEST_COMMIT_SHA }}" ]; then COMMIT_RANGE=${{ env.FIRST_COMMIT_SHA }} @@ -133,6 +134,9 @@ jobs: git cherry-pick --abort || true # If cherry-pick fails, create a placeholder commit echo "Cherry-pick failed. Creating placeholder commit." + + GIT_LOG_ONELINE_OUTPUT=$(git log --oneline --no-decorate $COMMIT_RANGE) + git reset --hard git commit --allow-empty -m "Placeholder commit for PR #${{ env.PR_ID }}" @@ -154,6 +158,12 @@ jobs: git reset --hard HEAD~1 # Remove placeholder commit git cherry-pick $COMMIT_RANGE \`\`\` + + + **Individual commits:** + \`\`\` + $GIT_LOG_ONELINE_OUTPUT + \`\`\` " } From d1280b54ba5623dab9afa562c22e49e7d0e5d395 Mon Sep 17 00:00:00 2001 From: Markus Date: Mon, 29 Jul 2024 11:28:14 +0300 Subject: [PATCH 25/47] allow defining internal ssh port --- infrastructure/server-setup/inventory/qa.yml | 2 ++ infrastructure/server-setup/tasks/ufw.yml | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/infrastructure/server-setup/inventory/qa.yml b/infrastructure/server-setup/inventory/qa.yml index 840662637..d1247ebc0 100644 --- a/infrastructure/server-setup/inventory/qa.yml +++ b/infrastructure/server-setup/inventory/qa.yml @@ -21,6 +21,8 @@ docker-manager-first: hosts: qa: # @todo set this to be the hostname of your target server ansible_host: '55.55.55.55' # @todo set this to be the IP address of your server + # ansible_port: '23' # @todo set this to be the SSH port if it's not 22 + # internal_ssh_port: '22' # @todo if you are port-forwarding and server SSH port is not the same as ansible_port, set it here data_label: data1 # for manager machines, this should always be "data1" # QA and staging servers are not configured to use workers. diff --git a/infrastructure/server-setup/tasks/ufw.yml b/infrastructure/server-setup/tasks/ufw.yml index 2d67cc6d6..867bf5248 100644 --- a/infrastructure/server-setup/tasks/ufw.yml +++ b/infrastructure/server-setup/tasks/ufw.yml @@ -3,6 +3,10 @@ name: ufw state: present +- name: Set default SSH port + set_fact: + ssh_port: '{{ internal_ssh_port | ansible_port | default(22) }}' + - name: Allow OpenSSH for IPv4 from specific addresses ufw: rule: allow @@ -12,10 +16,6 @@ loop: '{{ only_allow_access_from_addresses }}' when: only_allow_access_from_addresses is defined and only_allow_access_from_addresses | length > 0 -- name: Set default SSH port - set_fact: - ssh_port: '{{ ansible_port | default(22) }}' - - name: Remove general OpenSSH allow rule ufw: rule: allow From 04fc9f83e2719a60a2eafb5ec1f0f84f4ec4b868 Mon Sep 17 00:00:00 2001 From: Markus Date: Mon, 29 Jul 2024 11:28:36 +0300 Subject: [PATCH 26/47] Use dynamic ssh port for OpenSSH --- infrastructure/server-setup/tasks/ufw.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/server-setup/tasks/ufw.yml b/infrastructure/server-setup/tasks/ufw.yml index 867bf5248..43af63522 100644 --- a/infrastructure/server-setup/tasks/ufw.yml +++ b/infrastructure/server-setup/tasks/ufw.yml @@ -10,7 +10,7 @@ - name: Allow OpenSSH for IPv4 from specific addresses ufw: rule: allow - port: 22 + port: '{{ ssh_port }}' proto: tcp src: '{{ item }}' loop: '{{ only_allow_access_from_addresses }}' From 7c2eeb984048daacdf0041bd39fcf10c38ec586f Mon Sep 17 00:00:00 2001 From: Markus Date: Thu, 25 Jul 2024 16:11:49 +0300 Subject: [PATCH 27/47] stop creating ocrvs index as a part of elastic setup --- infrastructure/elasticsearch/setup-helpers.sh | 34 ------------------- .../elasticsearch/setup-settings.sh | 3 -- 2 files changed, 37 deletions(-) diff --git a/infrastructure/elasticsearch/setup-helpers.sh b/infrastructure/elasticsearch/setup-helpers.sh index 5df149fbb..e55e0a4ae 100755 --- a/infrastructure/elasticsearch/setup-helpers.sh +++ b/infrastructure/elasticsearch/setup-helpers.sh @@ -231,37 +231,3 @@ function ensure_settings { return $result } - -function create_elastic_index { - local index_name=$1 - local elasticsearch_host="${ELASTICSEARCH_HOST:-elasticsearch}" - - local -a args=( '-s' '-D-' '-m15' '-w' '%{http_code}' - "http://${elasticsearch_host}:9200/${index_name}" - '-X' 'PUT' - '-H' 'Content-Type: application/json' - ) - - if [[ -n "${ELASTIC_PASSWORD:-}" ]]; then - args+=( '-u' "elastic:${ELASTIC_PASSWORD}" ) - fi - - local -i result=1 - local output - - output="$(curl "${args[@]}")" - - echo "${output}" - - # @TODO: Preferably check whether the index already exists before creating it. - # Error message might change in the future so we should not depend on it. - if [[ "${output: -3}" -eq 200 || $output == *"already exists as alias"* ]]; then - result=0 - fi - - if ((result)); then - echo -e "\n${output::-3}\n" - fi - - return $result -} diff --git a/infrastructure/elasticsearch/setup-settings.sh b/infrastructure/elasticsearch/setup-settings.sh index eea61f90a..260e13a31 100644 --- a/infrastructure/elasticsearch/setup-settings.sh +++ b/infrastructure/elasticsearch/setup-settings.sh @@ -19,8 +19,5 @@ echo "-------- $(date) --------" log 'Waiting for availability of Elasticsearch' wait_for_elasticsearch -log "Creating index for Elasticsearch. Index: ocrvs" -create_elastic_index "ocrvs" - log "Updating replicas for Elasticsearch" ensure_settings "{\"index\":{\"number_of_replicas\":0}}" From 3830bfafec0bc43a6b998c7bb248e8e2da1139f0 Mon Sep 17 00:00:00 2001 From: Markus Date: Fri, 26 Jul 2024 10:12:43 +0300 Subject: [PATCH 28/47] stop creating ocrvs index before running migrations during cleanup --- infrastructure/run-migrations.sh | 8 -------- 1 file changed, 8 deletions(-) diff --git a/infrastructure/run-migrations.sh b/infrastructure/run-migrations.sh index d68ee546b..fbe00d8b0 100755 --- a/infrastructure/run-migrations.sh +++ b/infrastructure/run-migrations.sh @@ -25,13 +25,5 @@ elasticsearch_host() { fi } -create_elastic_index () { - local index_name=$1 - echo "Creating ElasticSearch Index: ${index_name}" - docker run --rm --network=opencrvs_overlay_net appropriate/curl curl -XPUT "http://$(elasticsearch_host)/$index_name" -v -} - -create_elastic_index "ocrvs" - # run migration by restarting migration service docker service update --force --update-parallelism 1 --update-delay 30s opencrvs_migration From d3c926cbfc206e281679cd488f255e4f4e63037a Mon Sep 17 00:00:00 2001 From: Markus Date: Mon, 29 Jul 2024 13:03:57 +0300 Subject: [PATCH 29/47] Restart and remove existing elastalert indices --- .../elasticsearch/setup-elastalert-indices.sh | 29 +++++++++++++++++ infrastructure/elasticsearch/setup-helpers.sh | 31 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100755 infrastructure/elasticsearch/setup-elastalert-indices.sh diff --git a/infrastructure/elasticsearch/setup-elastalert-indices.sh b/infrastructure/elasticsearch/setup-elastalert-indices.sh new file mode 100755 index 000000000..e53e4ce4a --- /dev/null +++ b/infrastructure/elasticsearch/setup-elastalert-indices.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +# OpenCRVS is also distributed under the terms of the Civil Registration +# & Healthcare Disclaimer located at http://opencrvs.org/license. +# +# Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. + +set -eu +set -o pipefail + +source "$(dirname "${BASH_SOURCE[0]}")/setup-helpers.sh" + +echo "-------- $(date) --------" + +log 'Waiting for availability of Elasticsearch' +wait_for_elasticsearch + + +log 'Scaling down Elastalert' +docker service scale opencrvs_elastalert=0 +log 'Deleting Elastalert indices' +delete_elastalert_indices +log 'Scaling up Elastalert' +docker service scale opencrvs_elastalert=1 + diff --git a/infrastructure/elasticsearch/setup-helpers.sh b/infrastructure/elasticsearch/setup-helpers.sh index e55e0a4ae..b058a2658 100755 --- a/infrastructure/elasticsearch/setup-helpers.sh +++ b/infrastructure/elasticsearch/setup-helpers.sh @@ -231,3 +231,34 @@ function ensure_settings { return $result } +# Upgrading from 7 to 8 requires deleting elastalert indices. https://elastalert2.readthedocs.io/en/latest/recipes/faq.html#does-elastalert-2-support-elasticsearch-8 +# Elastalert depends on kibana/beat indices, so we delete can elastalert indices during each deployment. +function delete_elastalert_indices { + # Opt for explicity over wildcard since we are deleting indices + local indices='elastalert_status,elastalert_status_error,elastalert_status_past,elastalert_status_silence,elastalert_status_status' + + local elasticsearch_host="${ELASTICSEARCH_HOST:-elasticsearch}" + + local -a args=( '-s' '-D-' '-m15' '-w' '%{http_code}' + "http://${elasticsearch_host}:9200/${indices}" + '-X' 'DELETE' + ) + + if [[ -n "${ELASTIC_PASSWORD:-}" ]]; then + args+=( '-u' "elastic:${ELASTIC_PASSWORD}" ) + fi + + local -i result=1 + local output + + output="$(curl "${args[@]}")" + if [[ "${output: -3}" -eq 200 ]]; then + result=0 + fi + + if ((result)); then + echo -e "\n${output::-3}\n" + fi + + return $result +} \ No newline at end of file From aa84a6c7e038c4730d0c555c136e1194e34ead7b Mon Sep 17 00:00:00 2001 From: Markus Date: Tue, 30 Jul 2024 07:47:26 +0300 Subject: [PATCH 30/47] move restart to deploy script --- infrastructure/deployment/deploy.sh | 9 +++++ .../elasticsearch/setup-elastalert-indices.sh | 35 +++++++++++++------ infrastructure/elasticsearch/setup-helpers.sh | 32 ----------------- 3 files changed, 34 insertions(+), 42 deletions(-) diff --git a/infrastructure/deployment/deploy.sh b/infrastructure/deployment/deploy.sh index 5d8725915..ba3eda77d 100755 --- a/infrastructure/deployment/deploy.sh +++ b/infrastructure/deployment/deploy.sh @@ -386,6 +386,15 @@ echo echo "Waiting 2 mins for mongo to deploy before working with data. Please note it can take up to 10 minutes for the entire stack to deploy in some scenarios." echo +echo 'Setting up elastalert indices' + +while true; do + if configured_ssh "/opt/opencrvs/infrastructure/elasticsearch/setup-elastalert-indices.sh"; then + break + fi + sleep 5 +done + echo "Setting up Kibana config & alerts" while true; do diff --git a/infrastructure/elasticsearch/setup-elastalert-indices.sh b/infrastructure/elasticsearch/setup-elastalert-indices.sh index e53e4ce4a..699ec03de 100755 --- a/infrastructure/elasticsearch/setup-elastalert-indices.sh +++ b/infrastructure/elasticsearch/setup-elastalert-indices.sh @@ -9,21 +9,36 @@ # # Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. -set -eu -set -o pipefail +# Upgrading from 7 to 8 requires deleting elastalert indices. https://elastalert2.readthedocs.io/en/latest/recipes/faq.html#does-elastalert-2-support-elasticsearch-8 -source "$(dirname "${BASH_SOURCE[0]}")/setup-helpers.sh" +set -e -echo "-------- $(date) --------" +docker_command="docker run --rm --network=opencrvs_overlay_net curlimages/curl" -log 'Waiting for availability of Elasticsearch' -wait_for_elasticsearch +echo 'Waiting for availability of Elasticsearch' +ping_status_code=$($docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD -o /dev/null -w '%{http_code}' "http://elasticsearch:9200") +if [ "$ping_status_code" -ne 200 ]; then + echo "Elasticsearch is not ready. API returned status code: $ping_status_code" + exit 1 +fi + + + +echo 'Scaling down Elastalert' -log 'Scaling down Elastalert' docker service scale opencrvs_elastalert=0 -log 'Deleting Elastalert indices' -delete_elastalert_indices -log 'Scaling up Elastalert' + +echo 'Deleting Elastalert indices' +indices='elastalert_status,elastalert_status_error,elastalert_status_past,elastalert_status_silence,elastalert_status_status' + +delete_status_code=$($docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD -o /dev/null -w '%{http_code}' "http://elasticsearch:9200/${indices}" -X DELETE) + +if [ "$delete_status_code" -ne 200 ]; then + echo "Could not delete indices. API returned status code: $delete_status_code" + exit 1 +fi + +echo 'Scaling up Elastalert' docker service scale opencrvs_elastalert=1 diff --git a/infrastructure/elasticsearch/setup-helpers.sh b/infrastructure/elasticsearch/setup-helpers.sh index b058a2658..1b3380f0f 100755 --- a/infrastructure/elasticsearch/setup-helpers.sh +++ b/infrastructure/elasticsearch/setup-helpers.sh @@ -230,35 +230,3 @@ function ensure_settings { return $result } - -# Upgrading from 7 to 8 requires deleting elastalert indices. https://elastalert2.readthedocs.io/en/latest/recipes/faq.html#does-elastalert-2-support-elasticsearch-8 -# Elastalert depends on kibana/beat indices, so we delete can elastalert indices during each deployment. -function delete_elastalert_indices { - # Opt for explicity over wildcard since we are deleting indices - local indices='elastalert_status,elastalert_status_error,elastalert_status_past,elastalert_status_silence,elastalert_status_status' - - local elasticsearch_host="${ELASTICSEARCH_HOST:-elasticsearch}" - - local -a args=( '-s' '-D-' '-m15' '-w' '%{http_code}' - "http://${elasticsearch_host}:9200/${indices}" - '-X' 'DELETE' - ) - - if [[ -n "${ELASTIC_PASSWORD:-}" ]]; then - args+=( '-u' "elastic:${ELASTIC_PASSWORD}" ) - fi - - local -i result=1 - local output - - output="$(curl "${args[@]}")" - if [[ "${output: -3}" -eq 200 ]]; then - result=0 - fi - - if ((result)); then - echo -e "\n${output::-3}\n" - fi - - return $result -} \ No newline at end of file From dba61a3adfee4c9b7a56894eb44bcb53abeabc02 Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Mon, 5 Aug 2024 11:28:54 +0300 Subject: [PATCH 31/47] Update Auto cherry-pick pipeline as per core PR https://github.com/opencrvs/opencrvs-core/pull/7398 --- .github/workflows/auto-pr-to-release.yml | 59 +++++++++--------------- 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/.github/workflows/auto-pr-to-release.yml b/.github/workflows/auto-pr-to-release.yml index 84e5e2634..b15e6e006 100644 --- a/.github/workflows/auto-pr-to-release.yml +++ b/.github/workflows/auto-pr-to-release.yml @@ -33,19 +33,17 @@ jobs: id: get_pr_details_dispatch run: | PR_NUMBER=${{ github.event.inputs.pr_number }} - PR_DATA=$(gh pr view $PR_NUMBER --json number,headRefName,baseRefName,mergedBy,mergeCommit,author,milestone,title --jq '{number: .number, headRefName: .headRefName, baseRefName: .baseRefName, merger: .mergedBy.login, author: .author.login, milestone: .milestone.title, title: .title}') - echo "PR_ID=$(echo $PR_DATA | jq -r '.number')" >> $GITHUB_ENV - echo "PR_AUTHOR=$(echo $PR_DATA | jq -r '.author')" >> $GITHUB_ENV - echo "PR_MERGER=$(echo $PR_DATA | jq -r '.merger')" >> $GITHUB_ENV - echo "MILESTONE=$(echo $PR_DATA | jq -r '.milestone')" >> $GITHUB_ENV - echo "BASE_BRANCH=$(echo $PR_DATA | jq -r '.baseRefName')" >> $GITHUB_ENV - echo "HEAD_BRANCH=$(echo $PR_DATA | jq -r '.headRefName')" >> $GITHUB_ENV - echo "PR_TITLE=$(echo $PR_DATA | jq -r '.title')" >> $GITHUB_ENV - - LATEST_COMMIT_SHA=$(gh pr view $PR_NUMBER --json commits --jq '.commits[-1].oid') - FIRST_COMMIT_SHA=$(gh pr view $PR_NUMBER --json commits --jq '.commits[0].oid') - echo "LATEST_COMMIT_SHA=${LATEST_COMMIT_SHA}" >> $GITHUB_ENV - echo "FIRST_COMMIT_SHA=${FIRST_COMMIT_SHA}" >> $GITHUB_ENV + PR_DATA=$(curl -s -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/opencrvs/opencrvs-core/pulls/$PR_NUMBER) + # printf escapes the newlines in the JSON, so we can use jq to parse output such as: + # "body": "![image](https://github.com/user-attachments/assets/8eee5bcf-7692-490f-a19f-576623e09961)\r\n", + echo "PR_ID=$(printf '%s' $PR_DATA | jq -r '.number')" >> $GITHUB_ENV + echo "PR_AUTHOR=$(printf '%s' $PR_DATA | jq -r '.user.login')" >> $GITHUB_ENV + echo "PR_MERGER=$(printf '%s' $PR_DATA | jq -r '.merged_by.login')" >> $GITHUB_ENV + echo "MILESTONE=$(printf '%s' $PR_DATA | jq -r '.milestone.title')" >> $GITHUB_ENV + echo "BASE_BRANCH=$(printf '%s' $PR_DATA | jq -r '.base.ref')" >> $GITHUB_ENV + echo "HEAD_BRANCH=$(printf '%s' $PR_DATA | jq -r '.head.ref')" >> $GITHUB_ENV + echo "PR_TITLE=$(printf '%s' $PR_DATA | jq -r '.title')" >> $GITHUB_ENV + echo "BASE_SHA=$(printf '%s' $PR_DATA | jq -r '.base.sha')" >> $GITHUB_ENV env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -60,11 +58,7 @@ jobs: echo "BASE_BRANCH=${{ github.event.pull_request.base.ref }}" >> $GITHUB_ENV echo "HEAD_BRANCH=${{ github.event.pull_request.head.ref }}" >> $GITHUB_ENV echo "PR_TITLE=${{ github.event.pull_request.title }}" >> $GITHUB_ENV - - LATEST_COMMIT_SHA=$(gh pr view $PR_NUMBER --json commits --jq '.commits[-1].oid') - FIRST_COMMIT_SHA=$(gh pr view $PR_NUMBER --json commits --jq '.commits[0].oid') - echo "LATEST_COMMIT_SHA=${LATEST_COMMIT_SHA}" >> $GITHUB_ENV - echo "FIRST_COMMIT_SHA=${FIRST_COMMIT_SHA}" >> $GITHUB_ENV + echo "BASE_SHA=${{ github.event.pull_request.base.sha }}" >> $GITHUB_ENV PR_DETAILS=$(gh pr view $PR_NUMBER --json mergedBy) MERGED_BY_LOGIN=$(echo "$PR_DETAILS" | jq -r '.mergedBy.login') @@ -111,32 +105,30 @@ jobs: git config advice.mergeConflict false # Fetch and checkout the release branch - git fetch --all + git fetch --all --unshallow git checkout ${{ env.RELEASE_BRANCH }} # Create a new branch for the PR NEW_BRANCH="auto-pr-${{ env.RELEASE_BRANCH }}-${{ env.PR_ID }}-$RANDOM" git checkout -b $NEW_BRANCH - echo "First commit: ${{ env.FIRST_COMMIT_SHA }}" - echo "Latest commit: ${{ env.LATEST_COMMIT_SHA }}" - # NOTE: git cherry-pick range needs ~ to include the FIRST_COMMIT_SHA (https://stackoverflow.com/questions/1994463/how-to-cherry-pick-a-range-of-commits-and-merge-them-into-another-branch) - COMMIT_RANGE="${{ env.FIRST_COMMIT_SHA }}~..${{ env.LATEST_COMMIT_SHA }}" + echo "HEAD_BRANCH: ${{ env.HEAD_BRANCH }}" + echo "BASE_SHA: ${{ env.BASE_SHA }}" - if [ "${{ env.FIRST_COMMIT_SHA }}" == "${{ env.LATEST_COMMIT_SHA }}" ]; then - COMMIT_RANGE=${{ env.FIRST_COMMIT_SHA }} - fi + COMMIT_RANGE="${{ env.BASE_SHA }}..origin/${{ env.HEAD_BRANCH }}" + + echo "Commit range: ${COMMIT_RANGE}" + + NON_MERGE_COMMITS=$(git log ${COMMIT_RANGE} --reverse --no-merges --pretty=format:"%h" -- | xargs) - echo "Commit range: $COMMIT_RANGE" + echo "Ordered non-merge commits: $NON_MERGE_COMMITS" # Attempt to cherry-pick the commits from the original PR - CHERRY_PICK_OUTPUT=$(git cherry-pick $COMMIT_RANGE 2>&1) || { + CHERRY_PICK_OUTPUT=$(git cherry-pick ${NON_MERGE_COMMITS} 2>&1) || { git cherry-pick --abort || true # If cherry-pick fails, create a placeholder commit echo "Cherry-pick failed. Creating placeholder commit." - GIT_LOG_ONELINE_OUTPUT=$(git log --oneline --no-decorate $COMMIT_RANGE) - git reset --hard git commit --allow-empty -m "Placeholder commit for PR #${{ env.PR_ID }}" @@ -156,14 +148,9 @@ jobs: git checkout $NEW_BRANCH git reset --hard HEAD~1 # Remove placeholder commit - git cherry-pick $COMMIT_RANGE + git cherry-pick $NON_MERGE_COMMITS \`\`\` - - **Individual commits:** - \`\`\` - $GIT_LOG_ONELINE_OUTPUT - \`\`\` " } From b69ac5c655d5d0e9a1e9477c85d85b6e0066b271 Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Mon, 5 Aug 2024 11:33:31 +0300 Subject: [PATCH 32/47] Fix environment creator asking user to resupply some Github variables and secrets on every run (#225) --- infrastructure/environments/github.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/infrastructure/environments/github.ts b/infrastructure/environments/github.ts index d95184430..6782f0473 100644 --- a/infrastructure/environments/github.ts +++ b/infrastructure/environments/github.ts @@ -244,9 +244,11 @@ export async function listEnvironmentSecrets( { owner: owner, repository_id: repositoryId, - environment_name: environmentName + environment_name: environmentName, + per_page: 100 } ) + return response.data.secrets.map((secret) => ({ ...secret, type: 'SECRET', @@ -263,7 +265,8 @@ export async function listRepositorySecrets( 'GET /repos/{owner}/{repo}/actions/secrets', { owner: owner, - repo: repositoryName + repo: repositoryName, + per_page: 100 } ) return response.data.secrets.map((secret) => ({ @@ -281,9 +284,9 @@ export async function listEnvironmentVariables( const response = await octokit.request( 'GET /repositories/{repository_id}/environments/{environment_name}/variables', { - per_page: 30, repository_id: repositoryId, environment_name: environmentName, + per_page: 100, headers: { 'X-GitHub-Api-Version': '2022-11-28' } From 174a5ec3db56663045104fcd3e1be672d06d75f8 Mon Sep 17 00:00:00 2001 From: Tameem Bin Haider Date: Tue, 6 Aug 2024 11:02:19 +0600 Subject: [PATCH 33/47] fix: remove overshadowing rect (#224) * fix: remove overshadowing rect * chore: remove unused bg image --- .../certificates/source/Farajaland-birth-certificate-v2.svg | 4 ---- .../certificates/source/Farajaland-death-certificate-v2.svg | 4 ---- .../source/Farajaland-marriage-certificate-v2.svg | 5 ----- 3 files changed, 13 deletions(-) diff --git a/src/data-seeding/certificates/source/Farajaland-birth-certificate-v2.svg b/src/data-seeding/certificates/source/Farajaland-birth-certificate-v2.svg index 6d66223f4..7aa67b024 100644 --- a/src/data-seeding/certificates/source/Farajaland-birth-certificate-v2.svg +++ b/src/data-seeding/certificates/source/Farajaland-birth-certificate-v2.svg @@ -47,7 +47,6 @@ REPUBLIC OF FARAJALAND / REPUBLIQUE DE FARAJALAND CERTIFICATE OF BIRTH / ACTE DE NAISSANCE - 1. Child’s full name /
Nom complet de l'enfant @@ -129,9 +128,6 @@ - - - diff --git a/src/data-seeding/certificates/source/Farajaland-death-certificate-v2.svg b/src/data-seeding/certificates/source/Farajaland-death-certificate-v2.svg index 5ed6a20cd..994b425a3 100644 --- a/src/data-seeding/certificates/source/Farajaland-death-certificate-v2.svg +++ b/src/data-seeding/certificates/source/Farajaland-death-certificate-v2.svg @@ -14,7 +14,6 @@ Registrar / L'Officier de l'État Civil {{registrar.name}} I certify that this certificate is a true copy of the civil registry and is issued by the mandated authority in pursuance of civil registration law / Je certifie que le présent certificat est une copie conforme du registre d'état civil et qu'il est délivré par l'autorité mandatée conformément à la loi sur l'état civil. - 1. Deceased full name /
Nom complet du défunt @@ -114,9 +113,6 @@ REPUBLIC OF FARAJALAND / REPUBLIQUE DE FARAJALAND CERTIFICATE OF DEATH / ACTE DE DEATH - - - diff --git a/src/data-seeding/certificates/source/Farajaland-marriage-certificate-v2.svg b/src/data-seeding/certificates/source/Farajaland-marriage-certificate-v2.svg index 3c0dca946..e84fb604d 100644 --- a/src/data-seeding/certificates/source/Farajaland-marriage-certificate-v2.svg +++ b/src/data-seeding/certificates/source/Farajaland-marriage-certificate-v2.svg @@ -36,7 +36,6 @@ REPUBLIC OF FARAJALAND / REPUBLIQUE DE FARAJALAND CERTIFICATE OF MARRIAGE / ACTE DE MARRIAGE - 1. Groom’s full name /
Nom complet du marié @@ -115,9 +114,6 @@ - - - @@ -167,6 +163,5 @@ {{#ifCond printInAdvance '!==' true}}{{/ifCond}} - From 844a46efa2bb471bc10bdaf3c5e4c2725e4fc40a Mon Sep 17 00:00:00 2001 From: Tameem Bin Haider Date: Tue, 6 Aug 2024 11:10:20 +0600 Subject: [PATCH 34/47] chore: remove unused props (#119) --- src/form/types/types.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/form/types/types.ts b/src/form/types/types.ts index 28a95b547..70e12b025 100644 --- a/src/form/types/types.ts +++ b/src/form/types/types.ts @@ -159,10 +159,6 @@ export enum IntegratingSystemType { Other = 'OTHER' } -export declare enum THEME_MODE { - DARK = 'dark' -} - export interface IPreviewGroup { id: string label: MessageDescriptor @@ -569,7 +565,6 @@ export interface IFormFieldBase { mapping?: IFormFieldMapping hideAsterisk?: boolean hideHeader?: boolean - mode?: THEME_MODE hidden?: boolean previewGroup?: string nestedFields?: { [key: string]: IFormField[] } @@ -593,7 +588,6 @@ export interface IFormFieldBase { ignoreFieldLabelOnErrorMessage?: boolean ignoreBottomMargin?: boolean customQuestionMappingId?: string - ignoreMediaQuery?: boolean } export interface Conditional { From 131b4b45e5c952fbf0e8ed9ee9de1eb44b0e4e6e Mon Sep 17 00:00:00 2001 From: Tameem Bin Haider Date: Tue, 6 Aug 2024 12:31:49 +0600 Subject: [PATCH 35/47] fix: add output for reset job (#159) * fix: add output for reset job * fix: add id to the reset data step --- .github/workflows/clear-environment.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/clear-environment.yml b/.github/workflows/clear-environment.yml index 25f360477..fea82bf7a 100644 --- a/.github/workflows/clear-environment.yml +++ b/.github/workflows/clear-environment.yml @@ -22,6 +22,8 @@ jobs: name: 'Reset data' environment: ${{ github.event.inputs.environment }} runs-on: ubuntu-22.04 + outputs: + outcome: ${{ steps.reset-data.outcome }} timeout-minutes: 60 steps: - name: Clone country config resource package @@ -45,6 +47,7 @@ jobs: known_hosts: ${{ env.KNOWN_HOSTS }} - name: Reset data + id: reset-data env: HOST: ${{ vars.DOMAIN }} ENV: ${{ vars.ENVIRONMENT_TYPE }} From 01ed4253fa4e27147b3441c72ddbda35cac8b462 Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Tue, 6 Aug 2024 09:33:01 +0300 Subject: [PATCH 36/47] add renovate json --- renovate.json | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 renovate.json diff --git a/renovate.json b/renovate.json new file mode 100644 index 000000000..0c150f3b2 --- /dev/null +++ b/renovate.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["config:recommended", "monorepo:lerna", ":semanticCommits"], + "lockFileMaintenance": { + "enabled": true + }, + "pruneStaleBranches": false, + "timezone": "Europe/London", + "schedule": [ + "after 5pm every weekday", + "before 3am every weekday", + "every weekend" + ], + "vulnerabilityAlerts": { + "enabled": true, + "labels": ["Security"] + }, + "osvVulnerabilityAlerts": true, + "packageRules": [ + { + "updateTypes": ["patch"], + "enabled": false + } + ] +} From 13f0f2a363b1aeacf03834d93728e4495f554bbd Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Wed, 7 Aug 2024 09:57:58 +0300 Subject: [PATCH 37/47] fix(alerts): use mount point filters for disk space alerts (#229) --- CHANGELOG.md | 6 ++++++ infrastructure/monitoring/kibana/config.ndjson | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bfa5bcef..e4c4e9256 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 1.7.0 (TBD) + +### Bug fixes + +- Kibana disk space alerts now work regardless of your disk device names. Alerts listen devices mounted both to `/` and `/data` (encrypted data partition) + ## 1.6.0 (TBD) ### Breaking changes diff --git a/infrastructure/monitoring/kibana/config.ndjson b/infrastructure/monitoring/kibana/config.ndjson index a8f47219d..d8ff821ed 100644 --- a/infrastructure/monitoring/kibana/config.ndjson +++ b/infrastructure/monitoring/kibana/config.ndjson @@ -1,10 +1,10 @@ -{"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"metrics.inventory_threshold.fired","params":{"documents":["{\"@timestamp\":\"2023-11-17T13:17:52.791Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"metrics.alert.inventory.threshold","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2023-11-17T12:01:46.420Z","createdBy":"elastic","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Available disk space in data partition","notifyWhen":"onActionGroupChange","params":{"criteria":[{"comparator":"<","customMetric":{"aggregation":"min","field":"system.filesystem.available","id":"alert-custom-metric","type":"custom"},"metric":"custom","threshold":[170000000000],"timeSize":1,"timeUnit":"h","warningComparator":"<","warningThreshold":[220000000000]}],"filterQuery":"{\"bool\":{\"should\":[{\"match_phrase\":{\"system.filesystem.device_name\":\"/dev/vda\"}}],\"minimum_should_match\":1}}","filterQueryText":"system.filesystem.device_name : \"/dev/vda\"","nodeType":"host","sourceId":"default"},"schedule":{"interval":"1h"},"scheduledTaskId":null,"tags":["infra","opencrvs-builtin"],"throttle":null,"updatedAt":"2024-02-07T02:27:29.567Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"14778650-8541-11ee-9002-2f37fdc4e5d5","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294424438,232754],"type":"alert","updated_at":"2024-02-07T08:27:04.438Z","version":"WzQ5NTQzMSwxOV0="} +{"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"metrics.inventory_threshold.fired","params":{"documents":["{\"@timestamp\":\"2024-08-06T07:57:35.644Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"metrics.alert.inventory.threshold","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2023-11-17T12:01:46.420Z","createdBy":"elastic","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-08-06T08:02:44.568Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.18"},"muteAll":false,"mutedInstanceIds":[],"name":"Available disk space in root file system","notifyWhen":"onActionGroupChange","params":{"alertOnNoData":true,"criteria":[{"comparator":">=","customMetric":{"aggregation":"max","field":"system.filesystem.used.pct","id":"alert-custom-metric","type":"custom"},"metric":"custom","threshold":[0.7],"timeSize":1,"timeUnit":"h","warningComparator":">=","warningThreshold":[0.5]}],"filterQuery":"{\"bool\":{\"should\":[{\"match_phrase\":{\"system.filesystem.mount_point\":\"/hostfs\"}}],\"minimum_should_match\":1}}","filterQueryText":"system.filesystem.mount_point : \"/hostfs\"","nodeType":"host","sourceId":"default"},"schedule":{"interval":"1h"},"scheduledTaskId":null,"tags":["infra","opencrvs-builtin"],"throttle":null,"updatedAt":"2024-08-06T08:01:56.542Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.18","id":"14778650-8541-11ee-9002-2f37fdc4e5d5","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1722931316554,643],"type":"alert","updated_at":"2024-08-06T08:01:56.554Z","version":"WzM5MywxXQ=="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"threshold_met","params":{"documents":["{\"@timestamp\":\"2022-04-18T07:05:33.819Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"apm.error_rate","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2022-06-01T11:30:27.033Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Error in service","notifyWhen":"onActionGroupChange","params":{"environment":"ENVIRONMENT_ALL","threshold":1,"windowSize":1,"windowUnit":"m"},"schedule":{"interval":"1m"},"scheduledTaskId":null,"tags":[],"throttle":null,"updatedAt":"2024-02-05T03:00:20.633Z","updatedBy":"opencrvs-admin"},"coreMigrationVersion":"7.17.0","id":"3b6722e0-e19e-11ec-ba8e-51649755648d","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707273006619,214975],"type":"alert","updated_at":"2024-02-07T02:30:06.619Z","version":"WzQ5MjAzNCwxOV0="} {"attributes":{"buildNum":46534,"defaultIndex":"metricbeat-*"},"coreMigrationVersion":"7.17.0","id":"7.17.0","migrationVersion":{"config":"7.13.0"},"references":[],"sort":[1707273006619,216009],"type":"config","updated_at":"2024-02-07T02:30:06.619Z","version":"WzQ5MjQyOCwxOV0="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"logs.threshold.fired","params":{"documents":["{\"@timestamp\":\"2023-11-22T08:25:47.329Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"logs.alert.document.count","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2023-11-22T08:32:38.272Z","createdBy":"elastic","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Error in backup logs","notifyWhen":"onActionGroupChange","params":{"count":{"comparator":"more than or equals","value":1},"criteria":[{"comparator":"matches","field":"message","value":"error"},{"comparator":"equals","field":"log.file.path","value":"/var/log/opencrvs-backup.log"}],"timeSize":1,"timeUnit":"h"},"schedule":{"interval":"1h"},"scheduledTaskId":null,"tags":[],"throttle":null,"updatedAt":"2024-02-07T02:27:22.558Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"b166fcb0-8911-11ee-8111-2f3be9e93efc","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294436464,232766],"type":"alert","updated_at":"2024-02-07T08:27:16.464Z","version":"WzQ5NTQzNCwxOV0="} {"attributes":{"anomalyThreshold":50,"description":"","fields":{"container":"container.id","host":"host.name","message":["message","@message"],"pod":"kubernetes.pod.uid","tiebreaker":"_doc","timestamp":"@timestamp"},"inventoryDefaultView":"0","logColumns":[{"timestampColumn":{"id":"5e7f964a-be8a-40d8-88d2-fbcfbdca0e2f"}},{"fieldColumn":{"field":"event.dataset","id":" eb9777a8-fcd3-420e-ba7d-172fff6da7a2"}},{"messageColumn":{"id":"b645d6da-824b-4723-9a2a-e8cece1645c0"}}],"logIndices":{"indexName":"logs-*,filebeat-*,kibana_sample_data_logs*,logstash*","type":"index_name"},"metricAlias":"metrics-*,metricbeat-*","metricsExplorerDefaultView":"0","name":"Default"},"coreMigrationVersion":"7.17.0","id":"default","migrationVersion":{"infrastructure-ui-source":"7.16.2"},"references":[],"sort":[1707273006619,217714],"type":"infrastructure-ui-source","updated_at":"2024-02-07T02:30:06.619Z","version":"WzQ5MzAyNywxOV0="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"query matched","params":{"documents":["{\"@timestamp\":\"2023-11-20T10:19:30.521Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":".es-query","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2023-11-20T09:12:19.237Z","createdBy":"elastic","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Successful SSH login","notifyWhen":"onActionGroupChange","params":{"esQuery":"{ \"query\": { \"bool\": { \"must\": [ \n { \"term\": { \"log.file.path\": \"/var/log/auth.log\" } },\n { \"term\": { \"event.outcome\": \"success\" }}\n ] } } }","index":["filebeat-*"],"size":100,"threshold":[1],"thresholdComparator":">=","timeField":"@timestamp","timeWindowSize":1,"timeWindowUnit":"m"},"schedule":{"interval":"1m"},"scheduledTaskId":null,"tags":[],"throttle":null,"updatedAt":"2024-02-07T02:27:19.537Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"e79aaa90-8784-11ee-b9ba-89bbe73df7ff","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294457367,232778],"type":"alert","updated_at":"2024-02-07T08:27:37.367Z","version":"WzQ5NTQ0MCwxOV0="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"metrics.inventory_threshold.fired","params":{"documents":["{\"@timestamp\":\"2022-06-20T06:16:33.414Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"metrics.alert.inventory.threshold","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2022-05-31T10:10:47.084Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"CPU under heavy load","notifyWhen":"onActionGroupChange","params":{"criteria":[{"comparator":">","customMetric":{"aggregation":"avg","field":"","id":"alert-custom-metric","type":"custom"},"metric":"cpu","threshold":[70],"timeSize":1,"timeUnit":"m"}],"nodeType":"host","sourceId":"default"},"schedule":{"interval":"1m"},"scheduledTaskId":null,"tags":["infra","opencrvs-builtin"],"throttle":null,"updatedAt":"2024-02-07T02:27:30.573Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"f022bee0-e0c9-11ec-99b8-dbfd54551fda","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294457362,232774],"type":"alert","updated_at":"2024-02-07T08:27:37.362Z","version":"WzQ5NTQzOCwxOV0="} -{"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"metrics.inventory_threshold.fired","params":{"documents":["{\"@timestamp\":\"2023-11-17T13:17:52.791Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"metrics.alert.inventory.threshold","apiKey":null,"apiKeyOwner":null,"consumer":"infrastructure","createdAt":"2022-05-31T10:10:47.080Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Low on available disk space","notifyWhen":"onActionGroupChange","params":{"alertOnNoData":true,"criteria":[{"comparator":">","customMetric":{"aggregation":"max","field":"system.filesystem.used.pct","id":"alert-custom-metric","label":"","type":"custom"},"metric":"custom","threshold":[0.7],"timeSize":1,"timeUnit":"h","warningComparator":">","warningThreshold":[0.5]}],"filterQuery":"{\"bool\":{\"should\":[{\"match_phrase\":{\"system.filesystem.device_name\":\"/dev/mapper/cryptfs\"}}],\"minimum_should_match\":1}}","filterQueryText":"system.filesystem.device_name : \"/dev/mapper/cryptfs\"","nodeType":"host","sourceId":"default"},"schedule":{"interval":"1h"},"scheduledTaskId":null,"tags":["infra","opencrvs-builtin"],"throttle":null,"updatedAt":"2024-02-07T02:27:20.542Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"f023d050-e0c9-11ec-99b8-dbfd54551fda","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294448366,232768],"type":"alert","updated_at":"2024-02-07T08:27:28.366Z","version":"WzQ5NTQzNSwxOV0="} +{"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"metrics.inventory_threshold.fired","params":{"documents":["{\"@timestamp\":\"2023-11-17T13:17:52.791Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"]}}],"alertTypeId":"metrics.alert.inventory.threshold","apiKey":null,"apiKeyOwner":null,"consumer":"infrastructure","createdAt":"2022-05-31T10:10:47.080Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Low on available disk space in data partition","notifyWhen":"onActionGroupChange","params":{"alertOnNoData":true,"criteria":[{"comparator":">=","customMetric":{"aggregation":"max","field":"system.filesystem.used.pct","id":"alert-custom-metric","label":"","type":"custom"},"metric":"custom","threshold":[0.7],"timeSize":1,"timeUnit":"h","warningComparator":">=","warningThreshold":[0.5]}],"filterQuery":"{\"bool\":{\"should\":[{\"match_phrase\":{\"system.filesystem.mount_point\":\"/hostfs/data\"}}],\"minimum_should_match\":1}}","filterQueryText":"system.filesystem.mount_point : \"/hostfs/data\"","nodeType":"host","sourceId":"default"},"schedule":{"interval":"1h"},"scheduledTaskId":null,"tags":["infra","opencrvs-builtin"],"throttle":null,"updatedAt":"2024-02-07T02:27:20.542Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"f023d050-e0c9-11ec-99b8-dbfd54551fda","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294448366,232768],"type":"alert","updated_at":"2024-02-07T08:27:28.366Z","version":"WzQ5NTQzNSwxOV0="} {"attributes":{"actions":[{"actionRef":"preconfigured:preconfigured-alert-history-es-index","actionTypeId":".index","group":"threshold_met","params":{"documents":["{\"@timestamp\":\"2023-06-15T07:57:35.954Z\",\"tags\":\"{{rule.tags}}\",\"rule\":{\"id\":\"{{rule.id}}\",\"name\":\"{{rule.name}}\",\"params\":{\"{{rule__type}}\":\"{{params}}\"},\"space\":\"{{rule.spaceId}}\",\"type\":\"{{rule.type}}\"},\"kibana\":{\"alert\":{\"id\":\"{{alert.id}}\",\"context\":{\"{{rule__type}}\":\"{{context}}\"},\"actionGroup\":\"{{alert.actionGroup}}\",\"actionGroupName\":\"{{alert.actionGroupName}}\"}},\"event\":{\"kind\":\"alert\"}}"],"indexOverride":"kibana-alert-history-services"}}],"alertTypeId":"apm.error_rate","apiKey":null,"apiKeyOwner":null,"consumer":"alerts","createdAt":"2022-05-31T10:10:47.069Z","createdBy":"opencrvs-admin","enabled":false,"executionStatus":{"error":null,"lastExecutionDate":"2024-02-07T08:28:08.400Z","status":"pending"},"legacyId":null,"meta":{"versionApiKeyLastmodified":"7.17.0"},"muteAll":false,"mutedInstanceIds":[],"name":"Error in service","notifyWhen":"onActionGroupChange","params":{"environment":"ENVIRONMENT_ALL","threshold":1,"windowSize":1,"windowUnit":"m"},"schedule":{"interval":"1m"},"scheduledTaskId":null,"tags":[],"throttle":null,"updatedAt":"2024-02-07T02:27:21.551Z","updatedBy":"elastic"},"coreMigrationVersion":"7.17.0","id":"f02b4a60-e0c9-11ec-99b8-dbfd54551fda","migrationVersion":{"alert":"7.16.0"},"references":[],"sort":[1707294457374,232780],"type":"alert","updated_at":"2024-02-07T08:27:37.374Z","version":"WzQ5NTQ0MSwxOV0="} {"excludedObjects":[],"excludedObjectsCount":0,"exportedCount":9,"missingRefCount":0,"missingReferences":[]} \ No newline at end of file From 1ffd52b7f95ecf0bb20440d1141a5987416e1e92 Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Wed, 7 Aug 2024 10:37:18 +0300 Subject: [PATCH 38/47] fix repo reference in auto pr pipeline --- .github/workflows/auto-pr-to-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/auto-pr-to-release.yml b/.github/workflows/auto-pr-to-release.yml index b15e6e006..4b2132549 100644 --- a/.github/workflows/auto-pr-to-release.yml +++ b/.github/workflows/auto-pr-to-release.yml @@ -33,7 +33,7 @@ jobs: id: get_pr_details_dispatch run: | PR_NUMBER=${{ github.event.inputs.pr_number }} - PR_DATA=$(curl -s -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/opencrvs/opencrvs-core/pulls/$PR_NUMBER) + PR_DATA=$(curl -s -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/opencrvs/opencrvs-countryconfig/pulls/$PR_NUMBER) # printf escapes the newlines in the JSON, so we can use jq to parse output such as: # "body": "![image](https://github.com/user-attachments/assets/8eee5bcf-7692-490f-a19f-576623e09961)\r\n", echo "PR_ID=$(printf '%s' $PR_DATA | jq -r '.number')" >> $GITHUB_ENV From 628e59b9be86f82ddeb841ab2fe89e67919e49b1 Mon Sep 17 00:00:00 2001 From: "Md. Ashikul Alam" <32668488+Nil20@users.noreply.github.com> Date: Wed, 7 Aug 2024 14:18:33 +0600 Subject: [PATCH 39/47] chore: rename application config file (#212) * chore: rename application config file * chore!: remove unused config messages --- ...onfig-default.ts => application-config.ts} | 2 +- src/api/application/handler.ts | 2 +- src/index.ts | 2 +- src/translations/client.csv | 111 ------------------ 4 files changed, 3 insertions(+), 114 deletions(-) rename src/api/application/{application-config-default.ts => application-config.ts} (97%) diff --git a/src/api/application/application-config-default.ts b/src/api/application/application-config.ts similarity index 97% rename from src/api/application/application-config-default.ts rename to src/api/application/application-config.ts index 660bab084..d5f38db73 100644 --- a/src/api/application/application-config-default.ts +++ b/src/api/application/application-config.ts @@ -1,6 +1,6 @@ import { countryLogo } from '@countryconfig/api/application/country-logo' -export const defaultApplicationConfig = { +export const applicationConfig = { APPLICATION_NAME: 'Farajaland CRS', BIRTH: { REGISTRATION_TARGET: 30, diff --git a/src/api/application/handler.ts b/src/api/application/handler.ts index 68e655f47..a7b028d30 100644 --- a/src/api/application/handler.ts +++ b/src/api/application/handler.ts @@ -10,7 +10,7 @@ */ import { Request, ResponseToolkit } from '@hapi/hapi' -import { defaultApplicationConfig as applicationConfig } from './application-config-default' +import { applicationConfig } from './application-config' export async function applicationConfigHandler(_: Request, h: ResponseToolkit) { const res = JSON.stringify(applicationConfig) diff --git a/src/index.ts b/src/index.ts index c7ea2459b..b6406fe55 100644 --- a/src/index.ts +++ b/src/index.ts @@ -54,7 +54,7 @@ import { usersHandler } from './data-seeding/employees/handler' import { applicationConfigHandler } from './api/application/handler' import { validatorsHandler } from './form/common/custom-validation-conditionals/validators-handler' import { conditionalsHandler } from './form/common/custom-validation-conditionals/conditionals-handler' -import { COUNTRY_WIDE_CRUDE_DEATH_RATE } from './api/application/application-config-default' +import { COUNTRY_WIDE_CRUDE_DEATH_RATE } from './api/application/application-config' import { handlebarsHandler } from './form/common/certificate/handlebars/handler' import { trackingIDHandler } from './api/tracking-id/handler' import { dashboardQueriesHandler } from './api/dashboards/handler' diff --git a/src/translations/client.csv b/src/translations/client.csv index 525186ad2..13e418047 100644 --- a/src/translations/client.csv +++ b/src/translations/client.csv @@ -156,134 +156,23 @@ changeEmail.validation.msg,Phone number validation message,Must be a valid email changePhone.validation.msg,Phone number validation message,Must be a valid 10 digit number that starts with 0,Doit être un numéro valide à {num} chiffres qui commence par {start}. config.advanced.search,This is used for the advanced search,Advanced Search,Recherche avancée config.advanced.search.instruction,This is used for the advanced search,Select the options to build an advanced search. A minimum of two search parameters is required.,Sélectionnez les options pour construire une recherche avancée. Un minimum de deux paramètres de recherche est requis. -config.application.applicationNameChangeNotification,Message for application name change notification,Name of application updated,Nom de l'application mise à jour -config.application.applicationNameLabel,Application name config label,Name of application,Nom de l'application -config.application.backgroundImageChangeNotification,Message for background image change notification,Background image updated,Mise à jour de l'image de fond -config.application.backgroundImageError,Error message for background image change,Unable to change image. Please try again.,Impossible de modifier l'image. Veuillez réessayer. -config.application.backgroundImageFileLimitError,Error message for large Background file,Background image file must be less than 2mb,Le fichier de l'image d'arrière-plan doit être inférieur à 2 Mo -config.application.birthDelayedDialogTitle,Delayed dialog title for brith,Delayed registration time period for birth registration,Délai d'enregistrement retardé pour l'enregistrement des naissances -config.application.birthDelayedFeeChangeNotification,Message for application birth delayed fee change notification,Birth delayed fee updated,Mise à jour de la pénalité de déclaration tardive des naissances -config.application.birthLateFeeChangeNotification,Message for application birth late fee change notification,Birth late fee updated,Mise à jour de la pénalité de déclaration tardive des naissances -config.application.birthLateRegTargetChangeNotification,Message for application birth late registration target change notification,Birth late registration target days updated,Mise à jour des jours cibles d'enregistrement tardif des naissances -config.application.birthLegallySpecifiedDialogTitle,Legally specified dialog title for brith,Legally specified time period for birth registration,Délai légal pour déclaration des naissances -config.application.birthOnTimeFeeChangeNotification,Message for application birth on time fee change notification,Birth on time fee updated,Mise à jour des frais de naissance à temps -config.application.birthRegTargetChangeNotification,Message for application birth registration target change notification,Birth registration target days updated,Mise à jour des jours cibles pour l'enregistrement des naissances config.application.birthTabTitle,The title for birth tab,Birth,Naissance config.application.birthTabTitleExport,The title for birth tab for VSExport,Births,Naissances -config.application.colourTabText,The title for colour tab text,Hex code,Code hexadécimal -config.application.colourTabTitle,The title for colour tab,Colour,Couleur -config.application.configChangeError,Error message for application config change,Unable to make change. Please try again,Impossible d'effectuer la modification. Veuillez réessayer -config.application.currencyChangeMessage,Message for application currency change modal,Select your currency for your CRVS system,Selectionnez la devise -config.application.currencyChangeNotification,Message for application currency change notification,Currency updated,Devise mise à jour -config.application.currencyLabel,Currency config label,Currency,Devise -config.application.deathDelayedFeeChangeNotification,Message for application death delayed fee change notification,Death delayed fee updated,Mise à jour de la pénalité de retard déclaration du décès -config.application.deathLegallySpecifiedDialogTitle,Legally specified dialog title for death,Legally specified time period for death registration,Délais légal de déclaration du décès -config.application.deathOnTimeFeeChangeNotification,Message for application death on time fee change notification,Death on time fee updated,Mise à jour des frais de déclaration de décès dans le délais legal -config.application.deathRegTargetChangeNotification,Message for application death registration target change notification,Death registration target days updated,Mise à jour des jours cibles de déclaration des décès config.application.deathTabTitle,The title for death tab,Death,Décès config.application.deathTabTitleExport,The title for death tab for VSExport,Deaths,Décès -config.application.delayedFeeDialogTitle,Delayed fee dialog title,Registration fees for delayed registrations,Frais pour les declarations tardives -config.application.delayedRegistrationLabel,Delayed registration config label,Delayed registration,Enregistrement retardé -config.application.delayedRegistrationValue,Delayed registration config value,After {lateTime} days,Après {lateTime} jours config.application.emptystate,Vital Statistics Export Empty State Text,The previous month's vital statistics data (based on vital event registrations occurring within that month) will become available for you to export as of the 1st of every month. Large CSV files cannot be opened in Excel and should therefore be opened in a statistical program such as {posit}.,Les données statistiques vitales du mois précédent (basées sur les enregistrements d'événements vitaux survenus au cours de ce mois) seront disponibles pour l'exportation à partir du 1er de chaque mois. Les grands fichiers CSV ne peuvent pas être ouverts dans Excel et doivent donc être ouverts dans un programme statistique tel que {posit}. -config.application.eventTargetInputLabel,The label for event target label,days,jours -config.application.example,Label for Example,Example,Exemple config.application.export,Download Export CSV,Export,Export -config.application.generalTabTitle,The title for general tab,General,Général -config.application.govermentLogoLabel,Government logo config label,Goverment logo,Logo du gouvernement -config.application.govtLogoChangeError,Error message for country logo change,Unable to change logo. Please try again.,Impossible de modifier le logo. Veuillez réessayer. -config.application.govtLogoChangeMessage,Message for government logo change modal,Upload a logo to be used on the login and declaration review screens,Téléchargez le logo du gouvernement qui sera utilisé sur le login et la décalcomanie du formulaire. Notez que le logo du certificat est téléchargé dans le cadre du modèle de certificat. -config.application.govtLogoChangeNotification,Message for government logo change notification,Government logo updated,Mise à jour du logo du gouvernement -config.application.govtLogoFileLimitError,Error message for large country logo file,Logo image file must be less than 2mb,Le fichier image du logo doit être inférieur à 2 Mo -config.application.imageTabTitle,The title for image tab,Image,Image -config.application.invalidExample,Label for Invalid example,Invalid,Invalide -config.application.lateFeeDialogTitle,Date fee dialog title,Registration fees for late registrations,Frais d'inscription pour les inscriptions tardives -config.application.lateRegistrationLabel,Late registration config label,Late registration,Déclaration tardive -config.application.lateRegistrationValue,Late registration config value,Between {onTime} days and {lateTime} days,Entre {onTime} jours et {lateTime} jours -config.application.legallySpecifiedLabel,Legally specified config label,Legally specified,Mention légale -config.application.legallySpecifiedValue,Legally specified config value,Within {onTime} days,Dans (ontime) jours -config.application.loginBackgroundLabel,Login Background config label,Login Background,Historique de la connexion -config.application.loginImageText,Login Image config label,Upload an image and set how you would like it to display in the background,Téléchargez une image et définissez comment vous souhaitez qu'elle s'affiche en arrière-plan. -config.application.marriageDelayedFeeChangeNotification,Message for application marriage delayed fee change notification,Marriage delayed fee updated,Mise à jour de la pénalité de retard déclaration du marriage -config.application.marriageLegallySpecifiedDialogTitle,Legally specified dialog title for marriage,Legally specified time period for marriage registration,Délais légal de déclaration du marriage -config.application.marriageOnTimeFeeChangeNotification,Message for application marriage on time fee change notification,Marriage on time fee updated,Mise à jour des frais de déclaration de marriage dans le délais legal -config.application.marriageRegTargetChangeNotification,Message for application marriage registration target change notification,Marriage registration target days updated,Mise à jour des jours cibles de déclaration des marriages -config.application.marriageTabTitle,The title for marriage tab,Marriage,Mariage -config.application.nameChangeMessage,Message for application name change modal,Choose a name for your CRVS system,Choisissez un nom pour votre système CRVS -config.application.nidPatternChangeError,Error message for invalid regular expression for NID number,Invalid regular expression for a National ID,Expression régulière invalide pour un identifiant national -config.application.nidPatternChangeMessage,Unique Identification Number (UIN) config message,Set the regex pattern for your national ID. For guidance please refer to www.regex101.com,Expression régulière invalide pour un identifiant national -config.application.nidPatternChangeNotification,Message for NID Pattern change modal,Unique Identification Number (UIN) regex pattern updated,Mise à jour du modèle regex pour le numéro d'identification unique (UIN) -config.application.nidPatternTitle,Unique Identification Number (UIN) config title,Unique Identification Number (UIN) e.g. National ID,"Numéro d'identification unique (UIN), par exemple la carte d'identité nationale." -config.application.onTimeFeeDialogTitle,On time fee dialog title,Registration fees within legally specified time,Droits d'inscription dans le délai légal -config.application.pattern,Label for Pattern,Pattern,Modèle -config.application.phoneNumberChangeError,Error message for invalid regular expression for phone number number,Invalid regular expression for a phone number,Expression régulière invalide pour un numéro de téléphone -config.application.phoneNumberChangeMessage,phone number config config message,Set the regex pattern for your country phone number. For guidance please refer to www.regex101.com,"Définissez le modèle regex pour le numéro de téléphone de votre pays. Pour obtenir des conseils, veuillez consulter le site www.regex101.com" -config.application.phoneNumberChangeNotification,Message for phone number Pattern change modal,Phone regex pattern updated,Mise à jour du modèle de regex téléphonique -config.application.phoneNumberExampleLabel,,example: {example},exemple: {example} -config.application.phoneNumberLabel,Phone number config label,Phone number,Numéro de téléphone -config.application.phoneNumberPatternLabel,,pattern: {pattern},motif: {pattern} -config.application.phoneNumberPatternTitle,Phone number config title,Phone number regex,Regex du numéro de téléphone -config.application.registrationFeesGroupTitle,The title for registration fee group,Registration fees,Droits d'inscription -config.application.registrationTimePeriodsGroupTitle,The title for registration time periods group,Registration time periods,Périodes d'enregistrement -config.application.settings,Link Text for Config Application Settings,Application,Application -config.application.testNumber,Label for test number,Test number,Numéro de test -config.application.updatingeMessage,Message for application config updated modal,Updating...,Mise à jour en cours -config.application.validExample,Label for valid example,Valid,Valable config.application.vitalStatistics,Vital Statistics Export,"Month-{month}-Farajaland-{event, select, birth{birth} death{death} other{birth}}-event-statistics.csv {fileSize}","Mois-{month}-Farajaland-{event, select, birth{birth} death{death} other{birth}}-événement-statistiques.csv {fileSize}" config.application.vsExportDownloadFailed,Vital Statistics Export Empty State Text,Sorry! Something went wrong,Désolé ! Quelque chose s'est mal passé config.application.vsexport,VS Export tab,Vital statistics,Statistiques vitales -config.application.withinLegallySpecifiedTimeLabel,Within legally specified time config label,Within legally specified time,Dans les délais prévus par la loi -config.birthDefaultTempDesc,Label for default birth certificate template,Default birth certificate template,Modèle d'acte de naissance par défaut -config.birthTemplate,Label for birth certificate template,Birth certificate,Certificat de naissance -config.birthUpdatedTempDesc,,Updated {birthLongDate},Mise à jour de {birthLongDate} config.certTemplate,Label for certificate templates,Certificate Template,Modèle de certificat -config.certificate.allowPrinting,To allow printing in advanced of issuance,Allow printing in advanced of issuance,Permettre l'impression à l'avance de l'émission -config.certificate.allowPrintingNotification,Message for allowing printing notification,Allow printing in advance of issuance updated,Permettre l'impression avant la mise à jour de l'émission -config.certificate.certificateUpdated,Certificate template change message on success,{eventName} certificate has been updated,Le certificat {eventName} a été mis à jour. -config.certificate.certificateUploading,Certificate template message when uploading SVG,Uploading and validating {eventName} certificate.,Téléchargement et validation du certificat {eventName}. -config.certificate.certificateValidationError,Certificate template error message on failed,Unable to read SVG. Please check,Impossible de lire le SVG. Veuillez vérifier config.certificate.options,Show options,Options,Options -config.certificate.printDescription,Allowing printing,Records printed off in advance of collections will be added to the ready to issue work-queue,Les documents imprimés avant les collectes seront ajoutés à la liste des documents prêts à être délivrés -config.certificate.template,Template for certificates,Template,Gabarit -config.certificate.uploadCertificateDialogCancel,Cancel new certificate template upload button,Cancel,Annuler -config.certificate.uploadCertificateDialogConfirm,Confirm new certificate template upload button,Upload,Télécharger -config.certificate.uploadCertificateDialogDescription,The description for the dialog when upload new certificate template,This will replace the current certificate template. We recommend downloading the existing certificate template as a reference.,Ceci remplacera le modèle de certificat actuel. Nous vous recommandons de télécharger le modèle de certificat existant comme référence. -config.certificate.uploadCertificateDialogTitle,Upload certificate template modal title,Upload new certificate?,Télécharger un nouveau certificat ? -config.certificateConfiguration,Link Text for Config Declaration Settings,Certificate configuration,Configuration du certificat -config.deathDefaultTempDesc,Label for default death certificate template,Default death certificate template,Modèle de certificat de décès par défaut -config.deathTemplate,Label for death certificate template,Death certificate,Acte de mariage -config.deathUpdatedTempDesc,,Updated {deathLongDate},Mise à jour de {deathLongDate} -config.downloadTemplate,Download action in certificate config action menu,Download,Télécharger config.emailAllUsers.modal.supportingCopy,Label for send email all users confirmation supporting copy,User will receive emails over the next 24 hours,L'utilisateur recevra des courriels au cours des prochaines 24 heures config.emailAllUsers.modal.title,Label for send email all users confirmation title,Send email to all users?,Envoyer un e-mail à tous les utilisateurs ? config.emailAllUsers.subtitle,Subtitle for email all users,This email will be sent to all users you are active. Emails will be sent over the next 24 hours. Only one email can be sent per day,Cet e-mail sera envoyé à tous les utilisateurs que vous activez. Les courriels seront envoyés au cours des prochaines 24 heures. Un seul courriel peut être envoyé par jour config.emailAllUsers.title,Title for email all users,Email all users,Envoyer un e-mail à tous les utilisateurs -config.eventUpdatedTempDesc,Label for updated birth certificate template,"Updated {lastModified, date, ::dd MMMM yyyy}","Mis à jour {lastModified, date, ::dd MMMM yyyy}" -config.form.settings.time,,Time input,Saisie de l'heure -config.form.tools.input.customSelectWithDynamicOptions,,Custom select with dynamic options,Sélection personnalisée avec options dynamiques -config.informantNotification.declarationSMS,Title for informant declarationSMS notification,Declaration sent for review,Déclaration envoyée pour examen -config.informantNotification.inProgressSMS,Title for informant inProgressSMS notification,Notification sent to Office,Notification envoyée au bureau -config.informantNotification.registrationSMS,Title for informant registrationSMS notification,Declaration registered,Déclaration enregistrée -config.informantNotification.rejectionSMS,Title for informant rejectionSMS notification,Declaration rejected,Déclaration rejetée -config.informantNotification.subtitle,Subtile for informant sms notification,Select the notifications to send to the informant to keep them informed of the progress to their declaration. Your system is configured to send {communicationType}.,Sélectionnez les notifications à envoyer à l'informateur pour le tenir informé de l'avancement de sa déclaration. Votre système est configuré pour envoyer {communicationType} -config.informantNotification.success,Notification for informant update success,Informant notifications updated,Mise à jour des notifications des informateurs -config.informantNotification.title,The title for Informant notifications,Informant notifications,Notifications d'informateurs -config.integrations,,Integrations,Intégrations -config.listDetails,Details for certificates templates list,To learn how to edit an SVG and upload a certificate to suite your country requirements please refer to this detailed guide. ,"Pour savoir comment modifier un SVG et télécharger un certificat en fonction des exigences de votre pays, veuillez consulter ce guide détaillé." -config.listDetailsQsn,Details question for certificates templates list,How to configure a certificate?,Comment configurer un certificat ? -config.listTitle,Title for certificates templates list,Certification,La certification -config.marriageDefaultTempDesc,Label for default marriage certificate template,Default marriage certificate template,Modèle de certificat de mariage par défaut -config.marriageTemplate,Label for marriage certificate template,Marriage certificate,Certificat de mariage -config.previewTemplate,,Preview,Prévisualiser -config.printTemplate,Print action in certificate config action menu,Print,Imprimer -config.uploadTemplate,Upload action in certificate config action menu,Upload,Télécharger config.userRoles.language,Language name,"{language, select, en {English} fr {French} other {{language}}}","{language, select, en {Anglais} fr {Français} other {{language}}}" -config.userRoles.role,ListViewSimplified header for role,ROLE,RÔLE config.userRoles.roleUpdateInstruction,Instruction for adding/updating role in role management modal,Add the roles to be assigned the system role of {systemRole},Ajoutez les rôles auxquels attribuer le rôle système de {systemRole} -config.userRoles.subtitle,Subtile for informant sms notification,Map user roles to each system role so that specific permissions and privileges are correctly assigned. To learn more about the different system roles see ... {link},"Associez les rôles d'utilisateur à chaque rôle système afin que les autorisations et les privilèges spécifiques soient correctement attribués. Pour en savoir plus sur les différents rôles système, voir ... {link}" -config.userRoles.systemRoleSuccessMsg,Label for System role updated success message,System role updated successfully,Rôle système mis à jour avec succès -config.userRoles.systemRoles,ListViewSimplified header for system roles,SYSTEM ROLES,RÔLES SYSTÈME -config.userRoles.title,The title for user roles,User roles,Rôles des utilisateurs conflicts.modal.assign.description,Description for modal when assign,Please note you will have sole access to this record. Please make any updates promptly otherwise unassign the record.,"Veuillez noter que vous aurez un accès exclusif à cet enregistrement. Veuillez effectuer rapidement les mises à jour éventuelles, sinon vous désassignez l'enregistrement." conflicts.modal.assign.title,Title for modal when assign,Assign record?,Attribuer un enregistrement ? conflicts.modal.assigned.description,Description for modal when record already assigned,{name} at {officeName} has sole editable access to this record,{name} à {officeName} a un accès unique et modifiable à cet enregistrement. From 2239b89a911a063c49c036cb5963c570b0961039 Mon Sep 17 00:00:00 2001 From: "Md. Ashikul Alam" <32668488+Nil20@users.noreply.github.com> Date: Wed, 7 Aug 2024 17:38:38 +0600 Subject: [PATCH 40/47] docs: update changelog(#6924) (#233) --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4c4e9256..935f14923 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,15 @@ - Remove `splitView` option from DOCUMENT_UPLOADER_WITH_OPTION field - New required sections preview & review added. Signature field definitions are now part of these two sections same as normal form fields. - Remove `inputFieldWidth` from Number type form field +- Application config file is renamed to `application-config.ts` +- Allow configuring the default search criteria for record search which can be done by adding or modifying a property named `SEARCH_DEFAULT_CRITERIA` in `application-config.ts` + Value of `SEARCH_DEFAULT_CRITERIA` can be one of the following + 1. 'TRACKING_ID', + 2. 'REGISTRATION_NUMBER', + 3. 'NATIONAL_ID', + 4. 'NAME', + 5. 'PHONE_NUMBER', + 6. 'EMAIL' ## 1.5.0 From 1d4b93fec5f40fc7c1b509b222540a9f1d9cddef Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Wed, 7 Aug 2024 14:57:06 +0300 Subject: [PATCH 41/47] fix(monitoring): fail alert setup if any command fails during (#232) --- .../monitoring/kibana/setup-config.sh | 79 ++++++++++++++++--- 1 file changed, 70 insertions(+), 9 deletions(-) diff --git a/infrastructure/monitoring/kibana/setup-config.sh b/infrastructure/monitoring/kibana/setup-config.sh index b94cab21f..68ea00223 100755 --- a/infrastructure/monitoring/kibana/setup-config.sh +++ b/infrastructure/monitoring/kibana/setup-config.sh @@ -9,29 +9,90 @@ #!/bin/bash set -e +set -o pipefail # Define common variables kibana_alerting_api_url="http://kibana:5601/api/alerting/rules/_find?page=1&per_page=100&default_search_operator=AND&sort_field=name&sort_order=asc" -docker_command="docker run --rm -v /opt/opencrvs/infrastructure/monitoring/kibana/config.ndjson:/config.ndjson --network=opencrvs_overlay_net curlimages/curl" + +docker pull ghcr.io/jqlang/jq +docker pull curlimages/curl + +http_status_from_curl_output() { + echo "$1" | tail -n1 +} + +response_text_from_curl_output() { + echo "$1" | head -n-1 +} + +curl_raw() { + docker run --rm -v /opt/opencrvs/infrastructure/monitoring/kibana/config.ndjson:/config.ndjson --network=opencrvs_overlay_net curlimages/curl -s -w "\n%{http_code}" "$@" +} + +parse_url_from_string() { + local input_string="$1" + + local url + url=$(echo "$input_string" | grep -oP '(http|https)://[^\s]+') + + echo "$url" +} + +parse_method_from_string() { + local input_string="$1" + + local method + method=$(echo "$input_string" | grep -oP '(?<=-X\s)[A-Z]+' || echo "GET") + + echo "$method" +} + + +curl() { + result=$(curl_raw "$@") + params="$@" + method=$(parse_method_from_string "$params") + request_url=$(parse_url_from_string "$params") + + http_status=$(http_status_from_curl_output "$result") + response=$(response_text_from_curl_output "$result") + + if [ "$http_status" -ge 200 ] && [ "$http_status" -lt 300 ]; then + if [ -z "$response" ]; then + echo "$method $request_url – $http_status" + else + echo $response + fi + else + echo "$method $request_url – $http_status" >&2 + echo "Error: HTTP request failed with status code $http_status" >&2 + exit 1 + fi +} + +jq() { + docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq "$@" +} # Initial API status check to ensure Kibana is ready -status_code=$($docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD -o /dev/null -w '%{http_code}' "$kibana_alerting_api_url") +response=$(curl_raw --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD -o /dev/null -w '%{http_code}' "$kibana_alerting_api_url") +status_code=$(http_status_from_curl_output $response) if [ "$status_code" -ne 200 ]; then - echo "Kibana is not ready. API returned status code: $status_code" + echo "Kibana is not ready yet! HTTP status $status_code" exit 1 fi # Delete all alerts -$docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do - $docker_command --connect-timeout 60 -X DELETE -H 'kbn-xsrf: true' -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "http://kibana:5601/api/alerting/rule/$id" +curl --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "$kibana_alerting_api_url" | jq -r '.data[].id' | while read -r id; do + curl --connect-timeout 60 -X DELETE -H 'kbn-xsrf: true' -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "http://kibana:5601/api/alerting/rule/$id" done # Import configuration -$docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD -X POST "http://kibana:5601/api/saved_objects/_import?overwrite=true" -H 'kbn-xsrf: true' --form file=@/config.ndjson > /dev/null +curl --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD -X POST "http://kibana:5601/api/saved_objects/_import?overwrite=true" -H 'kbn-xsrf: true' --form file=@/config.ndjson > /dev/null # Re-enable all alerts -$docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "$kibana_alerting_api_url" | docker run --rm -i --network=opencrvs_overlay_net ghcr.io/jqlang/jq -r '.data[].id' | while read -r id; do - $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "http://kibana:5601/api/alerting/rule/$id/_disable" - $docker_command --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "http://kibana:5601/api/alerting/rule/$id/_enable" +curl --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "$kibana_alerting_api_url" | jq -r '.data[].id' | while read -r id; do + curl --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "http://kibana:5601/api/alerting/rule/$id/_disable" + curl --connect-timeout 60 -X POST -H 'kbn-xsrf: true' -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD "http://kibana:5601/api/alerting/rule/$id/_enable" done \ No newline at end of file From b7ce93c4ea99d244e657dd2af59abfd4e41492b4 Mon Sep 17 00:00:00 2001 From: Markus Date: Thu, 8 Aug 2024 08:59:13 +0300 Subject: [PATCH 42/47] add default wrapper for variable on pipe --- infrastructure/server-setup/tasks/ufw.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/server-setup/tasks/ufw.yml b/infrastructure/server-setup/tasks/ufw.yml index 43af63522..915f787e5 100644 --- a/infrastructure/server-setup/tasks/ufw.yml +++ b/infrastructure/server-setup/tasks/ufw.yml @@ -5,7 +5,7 @@ - name: Set default SSH port set_fact: - ssh_port: '{{ internal_ssh_port | ansible_port | default(22) }}' + ssh_port: '{{ internal_ssh_port | default(ansible_port) | default(22) }}' - name: Allow OpenSSH for IPv4 from specific addresses ufw: From d7ccf97eea997fa3998c5d7d18ca535f390ad58a Mon Sep 17 00:00:00 2001 From: Markus Date: Wed, 14 Aug 2024 13:15:20 +0300 Subject: [PATCH 43/47] ignore unavailable indices upon delete --- infrastructure/elasticsearch/setup-elastalert-indices.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/elasticsearch/setup-elastalert-indices.sh b/infrastructure/elasticsearch/setup-elastalert-indices.sh index 699ec03de..d36fd8d10 100755 --- a/infrastructure/elasticsearch/setup-elastalert-indices.sh +++ b/infrastructure/elasticsearch/setup-elastalert-indices.sh @@ -32,7 +32,7 @@ docker service scale opencrvs_elastalert=0 echo 'Deleting Elastalert indices' indices='elastalert_status,elastalert_status_error,elastalert_status_past,elastalert_status_silence,elastalert_status_status' -delete_status_code=$($docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD -o /dev/null -w '%{http_code}' "http://elasticsearch:9200/${indices}" -X DELETE) +delete_status_code=$($docker_command --connect-timeout 60 -u elastic:$ELASTICSEARCH_SUPERUSER_PASSWORD -o /dev/null -w '%{http_code}' "http://elasticsearch:9200/${indices}?ignore_unavailable=true" -X DELETE) if [ "$delete_status_code" -ne 200 ]; then echo "Could not delete indices. API returned status code: $delete_status_code" From 04a560c984091019dea4270e593b366ab53d86c6 Mon Sep 17 00:00:00 2001 From: jamil314 Date: Thu, 15 Aug 2024 14:18:31 +0600 Subject: [PATCH 44/47] chore: add missing translations for reload modal --- src/translations/client.csv | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/translations/client.csv b/src/translations/client.csv index 13e418047..76b299e10 100644 --- a/src/translations/client.csv +++ b/src/translations/client.csv @@ -1742,6 +1742,9 @@ register.selectVitalEvent.registerNewEventHeading,The section heading on the pag register.selectVitalEvent.registerNewEventTitle,The title that appears on the select vital event page,New declaration,Nouvelle déclaration register.selectinformant.legalGuardian,,Legal guardian,Tuteur légal register.workQueue.declarations.banner,,Declarations to register in your area,Déclarations à enregistrer dans votre région +reloadmodal.body,Body of reload modal,There’s a new version of {app_name} available. Please update to continue.,Une nouvelle version de {app_name} est disponible. Veuillez effectuer la mise à jour pour continuer. +reloadmodal.button.update,Label of update button,Update,Mise à jour +reloadmodal.title,Title when update is available,Update available,Mise à jour disponible review.actions.desc.regConfComp,,"By clicking register, you confirm that the information entered is correct and the vital event can be registered.","En cliquant sur Enregistrer, vous confirmez que les informations sont correctes et ont été vérifiées par l'informateur. L'informateur comprend qu'elles seront utilisées pour enregistrer la naissance et à des fins de planification. En enregistrant cette naissance, un certificat de naissance sera généré avec votre signature pour être délivré." review.actions.desc.regConfInComp,,Please add mandatory information before registering.,Des informations obligatoires sont manquantes. Veuillez ajouter ces informations afin de pouvoir terminer le processus d'enregistrement. review.actions.description,,"By clicking register, you confirm that the information entered is correct and the vital event can be registered. ","En vous enregistrant, vous confirmez que vous avez examiné cette déclaration et que vous êtes convaincu qu'elle remplit les conditions requises pour l'enregistrement." From 6e1df5c38dcef26f18f5716b34b579b7d70c085c Mon Sep 17 00:00:00 2001 From: jamil314 Date: Thu, 15 Aug 2024 14:28:32 +0600 Subject: [PATCH 45/47] chore: add missing translations for advanced search --- src/translations/client.csv | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/translations/client.csv b/src/translations/client.csv index 76b299e10..9d8bc5b1e 100644 --- a/src/translations/client.csv +++ b/src/translations/client.csv @@ -18,8 +18,11 @@ advancedSearch.form.recordStatusInReview,Option for form field: status of record advancedSearch.form.recordStatusInprogress,Option for form field: status of record,In progress,En cours advancedSearch.form.recordStatusRegistered,Option for form field: status of record,Registered,Inscrit advancedSearch.form.recordStatusRequireUpdate,Option for form field: status of record,Requires updates,Nécessite des mises à jour +advancedSearch.form.recordStatusValidated,Option for form field: status of record,Validated,Validé advancedSearch.form.registrationDetails,The title of Registration details accordion,Registration details,Détails d'inscription advancedSearch.form.statusOfRecordLabel,Label for input Status of record,Status of record,Etat d'avancement du dossier +advancedSearch.form.timePeriodHelperText,Helper text for input Time period,Period of time since the record status changed,Période écoulée depuis le changement de statut de l'enregistrement +advancedSearch.form.timePeriodLabel,Label for input Time period,Time period,Période de temps advancedSearchResult.pill.childDoB,The label for child d.o.b in active advancedSearchParams,Child d.o.b,Date de naissance enfant advancedSearchResult.pill.childFirstName,The label for child firstname in active advancedSearchParams,Child firstname,Prénom de l'enfant advancedSearchResult.pill.childLastName,The label for child lastname in active advancedSearchParams,Child lastname,Nom de l'enfant @@ -43,6 +46,7 @@ advancedSearchResult.pill.regDate,The label for registration date in active adv advancedSearchResult.pill.regLocation,The label for event location in active advancedSearchParams,Location,Emplacement advancedSearchResult.pill.regNumber,The label for registration number in active advancedSearchParams,Registration number,Numéro d'enregistrement advancedSearchResult.pill.registationStatus,The label for registration Status param in active advancedSearchParams,Registration status,Statut d'enregistrement +advancedSearchResult.pill.timePeriod,The label for time period in active advancedSearchParams,Time period,Période de temps advancedSearchResult.pill.trackingId,The label for tracking id in active advancedSearchParams,Tracking ID,Identifiant de suivi advancedSearchResult.table.noResult,The label for no result in advancedSearchResult page,No result,Pas de résultat advancedSearchResult.table.searchResult,The label for search result header in advancedSearchResult page,Search results,Résultat de la recherche @@ -1241,6 +1245,10 @@ form.section.information.death.bullet2,,As the legal Informant it is important t form.section.information.death.bullet3,,Once the declaration is processed you will receive an email to tell you when to visit the office to collect the certificate - Take your ID with you.,"Une fois la déclaration traitée, vous recevrez un courriel vous indiquant quand vous rendre au bureau pour retirer le certificat - Munissez-vous d'une pièce d'identité." form.section.information.death.bullet4,,Make sure you collect the certificate. A death certificate is critical to support with inheritance claims and to resolve the affairs of the deceased e.g. closing bank accounts and setting loans.,"Veillez à récupérer le certificat. Le certificat de décès est essentiel pour les demandes d'héritage et pour régler les affaires de la personne décédée, par exemple la fermeture des comptes bancaires et la mise en place des prêts." form.section.information.name,,Information,Informations +form.section.label.timePeriodLast30Days,Label for option of time period select: last 30 days,last 30 days,les 30 derniers jours +form.section.label.timePeriodLast7Days,Label for option of time period select: last 7 days,last 7 days,les 7 derniers jours +form.section.label.timePeriodLast90Days,Label for option of time period select: last 90 days,last 90 days,les 90 derniers jours +form.section.label.timePeriodLastYear,Label for option of time period select: last year,last year,l'année dernière form.section.marriageEvent.date,,Date of marriage,Date du mariage form.section.marriageEvent.name,,Marriage event details,Détails de l'événement de mariage form.section.marriageEvent.title,,Marriage details,Détails du mariage From 9b716bb888fa90c4d0fc106b91217028db4f6d51 Mon Sep 17 00:00:00 2001 From: Tareq Date: Fri, 16 Aug 2024 12:26:16 +0600 Subject: [PATCH 46/47] Serve client manifest and icons from country-config --- .../images/icons/icon-128x128.png | Bin 0 -> 4533 bytes .../images/icons/icon-144x144.png | Bin 0 -> 5309 bytes .../images/icons/icon-152x152.png | Bin 0 -> 5683 bytes .../images/icons/icon-196x196.png | Bin 0 -> 7618 bytes .../images/icons/icon-256x256.png | Bin 0 -> 10779 bytes .../images/icons/icon-512x512.png | Bin 0 -> 26617 bytes src/client-static/images/icons/icon-72x72.png | Bin 0 -> 2039 bytes src/client-static/images/logo-90x90.svg | 22 ++++++++ src/client-static/manifest.json | 47 ++++++++++++++++++ src/index.ts | 18 +++++++ 10 files changed, 87 insertions(+) create mode 100644 src/client-static/images/icons/icon-128x128.png create mode 100644 src/client-static/images/icons/icon-144x144.png create mode 100644 src/client-static/images/icons/icon-152x152.png create mode 100644 src/client-static/images/icons/icon-196x196.png create mode 100644 src/client-static/images/icons/icon-256x256.png create mode 100644 src/client-static/images/icons/icon-512x512.png create mode 100644 src/client-static/images/icons/icon-72x72.png create mode 100644 src/client-static/images/logo-90x90.svg create mode 100644 src/client-static/manifest.json diff --git a/src/client-static/images/icons/icon-128x128.png b/src/client-static/images/icons/icon-128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..fbf19dc54e94521e13f53057f6bb7d7b2cf7e1c1 GIT binary patch literal 4533 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 R`^}Pd8OpTmBgcX`rSA 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 H*@pffTV~CP literal 0 HcmV?d00001 diff --git a/src/client-static/images/icons/icon-152x152.png b/src/client-static/images/icons/icon-152x152.png new file mode 100644 index 0000000000000000000000000000000000000000..4f27e38bd31234780337ed8cdb5451c0991a26f1 GIT binary patch literal 5683 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 WRT{t!IUky`01Xvg~gkk(eQs5}QBLD#5mmmNR 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 literal 0 HcmV?d00001 diff --git a/src/client-static/images/icons/icon-512x512.png b/src/client-static/images/icons/icon-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..4625da3fc6cc7cd52968d54395add81a83764f51 GIT binary patch literal 26617 zcmeFZXH-*5*fyMm5PI)TK&2>Mq(}{-qM(3<-bHFCQF={66${`I=_QH?f;6d0NdOV0 zOYc<#sR8Mbr9%d1*l)5W}^rmu`VT z5a3S;h#Cg`+Vt<+2Y!LQZs}YE6?O9e1c8J=*Dh(@@wZ;ipsKdI+#fZWkd~Gi55jb- zMog&c8vSI}$~Of`=&tLqT)M;j+Kl;9&O;%gt1`OJC-fEs-U^?Nh|f$LOCaxcrhl!A zh)?%gO9^QuZU6ctw5(n>Z{@WeT;|x3D(&^7JtbU*O&0w72V=>ya2Fge5mZwuY705~ zF%+@|PsRTIw=LwtXs<_+NkcEr|c0aAGj%`FtKY zlDzk%fX@5v&Xs*4A_{(nC{_RNz^Ws;L*^C)OowI_+Dlxf{uJlVKRI8{w@|aVWIi*c zQL}3~&0{0l4TdZRE$WPaOeB@8#)VXNZ#^Owcl(TU=~4Yul@|ukkH*pDoF-)7&^0|4 zwRBL8ja#8&vm2q`MY29g;jS;7ebexrvm0I<=egri(s`@ov*E%dn`4Lc8ij{m3Yt1k z=JNV(kGQ|b=_hHdLwS=O=?rc3^dX2ZAZL%TAFCyOC%bc74tYW^@?R3Rc!jMnx-LWs zteO3e)sJABPZ*8P&YCVK$DFto`L%}RYlMAne}rVP8s!xOdYg&ivZfX@il9TosvXva z6E8+dvaAS*KO^fGgIUt3&LZ*={Y9b&yvZvm%Y~a#ErMA9Cuh@Ry7y1 zL`_|p>6aPWn8IFgMssjR`Xpg42cq8l_UC9=`D>8OhUoTjY4AvDQ_vmi*N^wkW40;k zNgv*himiQnx0vEL{nl@)d8es8^e$a_XGDru<78pk2<%Y#6k<&`Bg;aevwVLiY{fYI zG46z;mv6(zq(t4RHKwLBRZxRoOHaI-^r5)gXbbfx4v84U7>p?KC!_ku@XghpaND_N zyQjh>qz5J69&}eZjapQ2L08Li%-eQ+63~=zov(Wz_PRD6ONVrHZ?U*(F1U(r;StC| zJv-z69@<{>rpf(mCvVgtfrBJnl)>5R!o^e)&y8;^WMwjERdz!t`*`xTLpk-~6a*`o z#_6O6o#&4z{E{L+r-#0pYJ8tax*->ZW!8g_KDgsmRbts)!1K`$BSO^}+hkkDVV99( zXP9M}-kN+t;_!qdjMGDP@}>r$vW=9zm_6o)MSBZjgHnPwqM|1w#3sWIY{H+-VjVcI zC)4&iMa89#<&JM+o||@SH)BGzD1tA*x`Vcs!Q?&RcXi$u4E86~SPgNbtJk^LJ9E{I z1?J=i?+XdvWT(9Urq~ohWQ1+fwi%l%nj+2&-E|lWkm6P;5r_SVs16U~eu#S!TOXe# zk2wjZ1NlWVvN@CG5Ni|UwKhUmOOuZG;hkhP_3CYfoNrImKI+V#&%G5+JmkHV{Iny= z-Zw03d5m+YfBMQnLzF0C(Rt{we5%MGphJT8d^Mu7m~?gxomk8+Q_{*ju_*0`vLpEn5) zS3;C^KS`P1aS)Clv>yH7YXS0(wJFMsr9h>x7^Hy7p^QZ#wAS1-g8ET$xlvj;r`t#) zN4YkcX6n>y<9=?t*!^>uJr+ay*5RtApH)U#24hCp$#hsibD4NN5@}-^AUEWwFl0Yo zVu{t;VNdKG!xebessvKdp;gWqS6CYf3U^!$WY6B^;0c)yN6zaJCX~4^XuNZ8_2FsO z8e23e^z*POt_3~Ftq*>{ELMMgstk(1rqr;v)5n=54s+yaM$b60i&wXw6h;yV?vhZ~ zw!2%u6gfd#_66;e-V+#`pmmv0=2d^N^`X$|D>pBdIUej^ZFWbc;1({PO#m5|Zy|6| zp(~Y^{Ubs9A^Cc6#1fS?H96$%$F)-INH%x(27Ys3io?|Y+K~6<-Oq`3To`po1p;Rt zBu|GnFT2W6YcLkd?({2L6raP?Bp}oHYc2Yd+9T7V=3&wyq1i1wj-M+mKR5+{NbL+C z-{be+9v_+S4UMsF-DIXJ*}hdWtV`(cxY+Yj9J;CM!}w&R%0*$K8-nHSLsaX3*tgf6 zpy{3J+G(_siH=a`-90!T{Yf-(IJA{Ogg%ib@h-oQ)r_L)R=o7=5!iHHIawt;G?new zy)PEIC8YP$e)*GIwE-6vb6lE;7X$klx!A2O4zEpC{_p&gyA+%?+M8dXd zuCWJyCMYTgVx_^CJt3L49U=wl*(iM6W8oB?kn^$p=~`>cRHn&vKhs4*`q<+wHGEmlLVT-;|697*eGxrTtVj`&bZgRLaV7vt83)(=5eU`XVOK+vQds7AIDY2>MTK3iYi%SYTp`b-v@^NhghpGm0w2kQpNoqU z!h!3WT7*tiSTqk_zg02fvsGnVW7q6O^;h4_#ieIxh_%uMk<;(#E{_Iw$CyrML^9`8ZvC^C=n+j~hGx?!qJh@2)6i~e- ziKpI4l<-q#USRhar_`lc-;UeJffd3h3*qc!N2;ePOJwL@(z0&iV?O1#A@i?Cllra~ zM%o|+`B5R%v517^q%PNBwa8!DXG&&Xl??79H}&mYu204!+hb4r*S{^IF$MKi>~WGd z_LDE~ObmTRu8tGpbf#ABPsg)#kobtx2ZQ5St=>M zG9>5fLEvrtPHS{Fyk$zxQWS8^EqeF4HAxBHfEUz{c$yeJ>&MW- zw6yUT{3(w_!;&fJG}@f*a-|7@iOkel%is|ry}T564dptmUKUJ6BOEfdGfo{^WivW` z#jHMjDPIc0`gMe|?AMz1`vP;7PiOURjj+f%J}_lfw&NOdnk5{>?;uy-W}4Nx_W6U=UGV~c50*Tmk3LVKdPGCqD8;gsss*b2o0q;+^c7%ocsbt z)4eZGk7Nb2nHKc0p@*MPS<5aBgE~ zitOUS3hFhV-jr#wvrwflr#OVRx`x358IYdA)M;5GL&Gv{5c#eSugmy*%C@(6JCLUD zvZ|A`x`V`tm~(pFN)P8}ZKShz%eMlQENKwe!I{?#DW(VG~(*CZV0X59DOKYbaFUW!oo8L3!$F35QoA z8?xd@et9xAx{w)j_s^K>XsOm~q@f3tIY8TZ1Ka0w0)o$y>U%VH*Yt4FexDopYsg&A zp&|CCA>R6pp?pm#2Y6cthH~n>gRG~N@QhU1B9p~%-)SPU=Z36LAhqkSDI7A})$tT~ zt65?CF#M%wSsjQjcYxb(p~~x*m+aw~Z2Z^TKM)Pj9~@w(6~3N_s`j=aJN!U^F3*Ay z8xwObU_(e^mQ-s`qcuA0=;DfdsGTRPVl@ZO;Xh0Ge|UY~bt}lk@_>vyxBN@Y|Nb54 z$Khn%G8cC6BZd7|QNjTci~CD#n9YzE&s*CD!NQ)t$C~s^33d_c*Ijnk6=WX-?+X8r zs_=BW!NsI?Yq3}J1UI(|C${Vk;vj%wyGo5Jh{VFV^GYYMeI(dLGQi;h>X~im!(fbvj0$w#}P$h)II-ui@H1 z&gv{@f1N(%yQ3#uSO`Pe2%N&kCHfjJms)?z8J3`@&Ft`g;#8>V(<@)n@}cAsDyUt> zzM#=~gr{VADqh^V%12u=G7J&Y&jh5gDy2sBmETggs$>`tWc9R-ApdJ39RqzFqBo*P z$k2z=)pPp7cB^jlRGK9KJuAvN{Q(v$gpwLXiw)h{ z6FM`JTKLb?L%wlK%hmCFw=^MIerD4P_5Ekh1a@Zf(TryBY?pPVa=0xOoP(BI zzL`T4l_GyJg=Vv|J2mU6N8p1WH1ppk!W_BzBXG_ zVJ|kx6Wo2@_ga?Z>4TrNJM|Dv@ztHdmm(()pWn?2l3YF+G8MJfz^fN{Mo4PCHR%M? zm!5#_*1z`R03p4GOm&4?3|`-Y}_^itCT+Zbt&iI;~A=G~F=C zk#bJ$&;ZDvg%`R^6rt?H?^4=Vmzq*+7=u2~x{0PKA*C{enZuQeL(aZF6gY>!NzRxy z>nz)M{2E@_a~Ii3D(OFK*Yj#Jp?Kt!E!Z$fF(u7#R%WN+OjT4}rFyE`L08Wm^T3!I zNPWc(iw<8WN0-QrrAI~N1M5f%Y4p#+28I;@`MI*?cD7z~yIebAta0@73wpLr&ToPS zAVLD8j4${#?-=0NAY^MkwoZwvC7iah>Y<@ax`~@e-nmb}5&`sF9t&7KiUmq$=z#DV?+9P2cz2?H%u~F`GYBnKn=;JY9375Js9X zz*BA@+HoFqXx`EoND}S5s~@H-3v5u&W=YmJGY+9R<9YaWIMz*GkN^nPqQ0Dg}r$nARmx;;I26T2d>QTPr*-rndvPNSuIr<4+iF^@(OJG`~^3jbC6^E*y=zkQD!wRm1YXB9kyf9 zvEA>M<$ptfugf|$|eIa0R-l*jxfma{i>o4n}kytp`w(lK+~ zONR*yVDfq1CQ^ic5TjqF>~(@M#TK`+##i?Y=rNo|)pK=L3nVProOz!ySN#6n?H6x^ z%qhF9HyDOo9aWEwQuuC*g{{ld6R@ zn&XrMytGnG^R;OkiS6VW{iS^3&a7${lhJ|F zv$r%qj2*#-0;`ecFSd885u=c=`MYO}K88Wrxr9vSTW;GmH&2%o+O9F!HDA^ik$Y7a zW;Eb)GqR4@JbQO*r{AElnF&u(Ox5t3qBMQHxafx3WYXX@3|U?DQ!CI|Y^mo2^-o(P zJwvCf2r~!5bl6#P{=J8n`dc$~Zx>X9q}GC6GLUCSJeHs)m9=2=+0I`L;wx04Dbw|X zQzK&2Be}NirxzwA4y9{5WNwNT-YsQqpoP_vf=X6Tx9{cNMlMp)DC zOexT(S8XwBh>=1cs@2DckJ1kyiAvE0q~0_VO0?tH?N7<1h8PE0AtMDLQ8-d}TqC z$5Iqq&nxC`3m2g~Lg^89b$(BiA;v3@tYfP*!-A}7a(Bn~-IV1+4cEG@i?^I6WeSY! zuV;h|%4IE+`vbGo27vm3vY@TwL+@}y-OoZhBU$*1f`latU$V0+HE2APVaTfvd9^iF zdlc5H?7uh>xWU@|OX^jc(#!xJMMfZhl}*RZ2qo|u-_4y`t-|42)#XPQ9oDwnK*Iy8 zBehV&NDZt$YG4KVW2J%U3Cwk>@9GOy2=7pf0YlKl9Lta+2hh11@{UB}4QtO^JTmKb z{U&e7y*VFoN&UXNW}y6u0Qu!v9Fem6XUf#H>Fa8;h1fyBSn)H)ly$_VhN=x_|L@gL zB>a3RA={ukFHbEa71f9ydDRnEwMO_sq|uc){3p(2H38U8BvC2KPCK00)F-CI3rpl+ zdiE$p9KocOKGz~;xf1#A)~Th36Hb%iENl1|aZP$_T3%ks>jk|*Q=ZixV=rM}^OmjplO!Ok*WpTG#+z{Z_V&L(x(g zN_X~c_xo^d$A;}p%&zbK6}9cR;*zSpEwu8#W;WGGUY3=qRhG@?zciCSbNkP@`>)m^ z?-ftzl@TxA!EGM|ea^Cq&JRz$>pGRSO0VNK?|FAXv<&nmc~?^~`cx71v(Ah?Wv9cD zB57^NBG>%@cnu6_<+T4SA?Z$Y_X9nBJH053&w8kxaUHh4Potzri2lc{Z>k?L#O^-T zhVhdO_Cy(P!?+DIXF;-+46FnDU43M}F#ohOe}9%8RepB3h3O6LrG;j`??zIf zbLU@ceA29DEecc%KD|QfVh+tP&cT>wf+<^XWSppEscc8T3~%Jj-%5%yii((n1i)b1 zQ5x!41FtWy28Su$Bt)5%1+7>b688`4)(PJ&2+Ln&XHf{Z6eAlj#58vgVG8avDZDITks@S z%~3`BV{*7KFDbB|U4*s71wqDdF=0X{uCrgxbDTG?8g#o34t%5?-_5PgQf^{%=BW)I zL$+vyZT;8VK)X8j=gPOb8}~l7hcZx%F?@}HZ$icbu79CjkWhtif`&__tM!tI_ji#% zPq~v>d>JbKgQFX)@{#tHt?_Qnly8;*%)5N_^_kZLD}($bkHraJcEnQqRO_Jp?Ke%| z&y18aS$&Msw)l?eCYmR9ql=X(3+KC@9{O>koUW%FIQ7T%<%Y5 z%9fcqK|DPJ6Z4*y%kBm|C%cS{ZZdyb33Ck|JC`S?;yBcMy{_a(G2dgV(oYJg@#47q z2Uy%T3wb@ui*n%U4P7DMW}*^ia%H~o+cmqc+trltE9DCIDnw0TD)b;pK5W%hLrVw- z>MSOAWPH-&Pfnw`gF*X#Pzz3<1--xI365uR|KPZD;PoZqM;r7y9(sN1IkX(Q)gxmU z0~USG0XV{wt9l@}0P}~*+YY~31Oyj|k|4`DyZzQEpp&GSo^oCwai3*#*@Fvl<%`u* znq;^Di(&9r�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 V;dDU2D_j5o002ovPDHLkV1ks#zIp%v literal 0 HcmV?d00001 diff --git a/src/client-static/images/logo-90x90.svg b/src/client-static/images/logo-90x90.svg new file mode 100644 index 000000000..7e24a831b --- /dev/null +++ b/src/client-static/images/logo-90x90.svg @@ -0,0 +1,22 @@ + + + + \ No newline at end of file diff --git a/src/client-static/manifest.json b/src/client-static/manifest.json new file mode 100644 index 000000000..195d48c73 --- /dev/null +++ b/src/client-static/manifest.json @@ -0,0 +1,47 @@ +{ + "name": "OpenCRVS", + "short_name": "OpenCRVS", + "theme_color": "#4c68c1", + "background_color": "#4c68c1", + "display": "standalone", + "scope": "/", + "start_url": "/", + "icons": [ + { + "src": "images/icons/icon-72x72.png", + "sizes": "72x72", + "type": "image/png" + }, + { + "src": "images/icons/icon-128x128.png", + "sizes": "128x128", + "type": "image/png" + }, + { + "src": "images/icons/icon-144x144.png", + "sizes": "144x144", + "type": "image/png" + }, + { + "src": "images/icons/icon-152x152.png", + "sizes": "152x152", + "type": "image/png" + }, + { + "src": "images/icons/icon-196x196.png", + "sizes": "196x196", + "type": "image/png" + }, + { + "src": "images/icons/icon-256x256.png", + "sizes": "256x256", + "type": "image/png" + }, + { + "src": "images/icons/icon-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "permissions": ["unlimitedStorage"] +} diff --git a/src/index.ts b/src/index.ts index b6406fe55..029ee5ecf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,6 +12,7 @@ require('app-module-path').addPath(require('path').join(__dirname)) require('dotenv').config() import fetch from 'node-fetch' +import path from 'path' import * as Hapi from '@hapi/hapi' import * as Pino from 'hapi-pino' import * as JWT from 'hapi-auth-jwt2' @@ -529,6 +530,23 @@ export async function createServer() { } }) + server.route({ + method: 'GET', + path: '/static/{param*}', + handler: { + directory: { + path: path.join(__dirname, 'client-static'), + redirectToSlash: true, + index: false + } + }, + options: { + auth: false, + tags: ['api', 'static'], + description: 'Server static files for client' + } + }) + server.ext({ type: 'onRequest', method(request: Hapi.Request & { sentryScope?: any }, h) { From 2bc51fd104da26ee60e52e8e06ca2bf432172746 Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Mon, 19 Aug 2024 10:58:04 +0300 Subject: [PATCH 47/47] add changelog reminder pipeline for PRs --- .github/workflows/check-changelog.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/workflows/check-changelog.yml diff --git a/.github/workflows/check-changelog.yml b/.github/workflows/check-changelog.yml new file mode 100644 index 000000000..2cb663718 --- /dev/null +++ b/.github/workflows/check-changelog.yml @@ -0,0 +1,19 @@ +name: Verify CHANGELOG.md is updated + +on: [pull_request] + +jobs: + check: + runs-on: ubuntu-22.04 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - uses: mskelton/changelog-reminder-action@v3 + with: + message: > + Oops! Looks like you forgot to update the changelog. + When updating CHANGELOG.md, please consider the following: + - Changelog is read by country implementors who might not always be familiar with all technical details of OpenCRVS. Keep language high-level, user friendly and avoid technical references to internals. + - Answer "What's new?", "Why was the change made?" and "Why should I care?" for each change. + - If it's a breaking change, include a migration guide answering "What do I need to do to upgrade?".