diff --git a/roles/podman_cleaninsights/defaults/main.yml b/roles/podman_cleaninsights/defaults/main.yml deleted file mode 100644 index eeff222..0000000 --- a/roles/podman_cleaninsights/defaults/main.yml +++ /dev/null @@ -1,8 +0,0 @@ -podman_cleaninsights_podman_rootless_user: cleaninsights -podman_cleaninsights_web_hostname: "{{ inventory_hostname }}" -podman_cleaninsights_mysql_database: matomo -# podman_cleaninsights_mysql_password: -# podman_cleaninsights_mysql_root_password: -podman_cleaninsights_php_memory_limit: 2048M -podman_cleaninsights_mysql_user: matomo -podman_cleaninsights_matomo_token: "" \ No newline at end of file diff --git a/roles/podman_cleaninsights/handlers/main.yml b/roles/podman_cleaninsights/handlers/main.yml deleted file mode 100644 index e0735da..0000000 --- a/roles/podman_cleaninsights/handlers/main.yml +++ /dev/null @@ -1,26 +0,0 @@ -- name: restart mysql - ansible.builtin.systemd_service: - name: mysql - state: restarted - scope: user - daemon_reload: true - become: true - become_user: "{{ podman_cleaninsights_podman_rootless_user }}" - -- name: restart matomo - ansible.builtin.systemd_service: - name: matomo - state: restarted - scope: user - daemon_reload: true - become: true - become_user: "{{ podman_cleaninsights_podman_rootless_user }}" - -- name: restart redis - ansible.builtin.systemd_service: - name: redis - state: restarted - scope: user - daemon_reload: true - become: true - become_user: "{{ podman_cleaninsights_podman_rootless_user }}" diff --git a/roles/podman_cleaninsights/tasks/main.yml b/roles/podman_cleaninsights/tasks/main.yml deleted file mode 100644 index 7550975..0000000 --- a/roles/podman_cleaninsights/tasks/main.yml +++ /dev/null @@ -1,113 +0,0 @@ ---- -- name: create service configuration directories - ansible.builtin.file: - path: "/home/{{ podman_cleaninsights_podman_rootless_user }}/{{ item }}" - state: directory - owner: "{{ podman_cleaninsights_podman_rootless_user }}" - group: "{{ podman_cleaninsights_podman_rootless_user }}" - mode: "0755" - become: true - with_items: - - mysql - - matomo - - -- name: install cleaninsights script - ansible.builtin.copy: - src: templates/cleaninsights.php - dest: "/home/{{ podman_cleaninsights_podman_rootless_user }}/matomo/cleaninsights.php" - owner: "{{ podman_cleaninsights_podman_rootless_user }}" - group: "{{ podman_cleaninsights_podman_rootless_user }}" - mode: "0755" - become: true - -- name: install cleaninsights configuration file - ansible.builtin.template: - src: "cleaninsights.ini.j2" - dest: "/home/{{ podman_cleaninsights_podman_rootless_user }}/matomo/cleaninsights.ini" - owner: "{{ podman_cleaninsights_podman_rootless_user }}" - group: "{{ podman_cleaninsights_podman_rootless_user }}" - mode: "0755" - become: true - -- name: install podman quadlet for rootless podman user - ansible.builtin.template: - src: "{{ item }}" - dest: "/home/{{ podman_cleaninsights_podman_rootless_user }}/.config/containers/systemd/{{ item }}" - owner: "{{ podman_cleaninsights_podman_rootless_user }}" - mode: "0400" - with_items: - - matomo.container - - mysql.container - - redis.container - notify: - - "restart {{ item | split('.') | first }}" - become: true - - -- name: install network quadlets for rootless podman user - ansible.builtin.template: - src: "{{ item }}" - dest: "/home/{{ podman_cleaninsights_podman_rootless_user }}/.config/containers/systemd/{{ item }}" - owner: "{{ podman_cleaninsights_podman_rootless_user }}" - mode: "0400" - with_items: - - frontend.network - - cleaninsights.network - become: true - -- name: verify quadlets are correctly defined - ansible.builtin.command: /usr/libexec/podman/quadlet -dryrun -user - register: podman_cleaninsights_quadlet_result - ignore_errors: true - changed_when: false - become: true - become_user: "{{ podman_cleaninsights_podman_rootless_user }}" - -- name: assert that the quadlet verification succeeded - ansible.builtin.assert: - that: - - podman_cleaninsights_quadlet_result.rc == 0 - fail_msg: "'/usr/libexec/podman/quadlet -dryrun -user' failed! Output withheld to prevent leaking secrets." - -- name: set up nginx - ansible.builtin.include_role: - name: irl.wip.podman_nginx - vars: - podman_nginx_frontend_network: frontend - podman_nginx_podman_rootless_user: "{{ podman_cleaninsights_podman_rootless_user }}" - podman_nginx_primary_hostname: "{{ podman_cleaninsights_web_hostname }}" - podman_nginx_systemd_service_slice: cleaninsights.slice - podman_nginx_systemd_service_requires: ["matomo"] - podman_nginx_additional_volumes: - - src: "/home/{{ podman_cleaninsights_podman_rootless_user }}/matomo" - dest: "/var/www/html" - options: "ro" - -- name: create nginx configuration file - ansible.builtin.template: - src: nginx.conf - dest: "/home/{{ podman_cleaninsights_podman_rootless_user }}/nginx/nginx.conf" - owner: "{{ podman_cleaninsights_podman_rootless_user }}" - group: "{{ podman_cleaninsights_podman_rootless_user }}" - mode: "0644" - become: true - -- name: install services slice for rootless podman user - ansible.builtin.template: - src: "cleaninsights.slice" - dest: "/home/{{ podman_cleaninsights_podman_rootless_user }}/.config/systemd/user/cleaninsights.slice" - owner: "{{ podman_cleaninsights_podman_rootless_user }}" - group: "{{ podman_cleaninsights_podman_rootless_user }}" - mode: "0655" - become: true - -- name: make sure services are started on boot - ansible.builtin.systemd_service: - name: "cleaninsights.slice" - enabled: true - state: started - daemon_reload: true - scope: user - become: true - become_user: "{{ podman_cleaninsights_podman_rootless_user }}" diff --git a/roles/podman_cleaninsights/templates/cleaninsights.ini.j2 b/roles/podman_cleaninsights/templates/cleaninsights.ini.j2 deleted file mode 100644 index 01ea4b4..0000000 --- a/roles/podman_cleaninsights/templates/cleaninsights.ini.j2 +++ /dev/null @@ -1,22 +0,0 @@ -; A Matomo token for authentication to enable retrograde setting of timestamps. -; Needs to be set for CIMP to work. See https://matomo.org/faq/general/faq_114/ -token_auth = '{{ podman_cleaninsights_matomo_token }}' - -; Allowed CORS scheme/domain/port tuples. Defaults to none allowed. -; cors[] = 'https://example.com:3000' - -; The base URL of your Matomo instance. Only needs to be set if CIMP is -; not installed inside the Matomo directory.ca -matomo_base_url = 'http://nginx' - -; Number of created tracking requests to send to Matomo at once. - chunk_size = 1000 - -; Number of seconds to wait before retry, if Matomo responded with an error. -delay_after_failure = 2 - -; Number of maximum retries on error. -max_attempts = 3 - -; Set to true to have a debug output in 'ciapi.log' in the same directory. -debug = true \ No newline at end of file diff --git a/roles/podman_cleaninsights/templates/cleaninsights.network b/roles/podman_cleaninsights/templates/cleaninsights.network deleted file mode 100644 index f95a29b..0000000 --- a/roles/podman_cleaninsights/templates/cleaninsights.network +++ /dev/null @@ -1,2 +0,0 @@ -[Network] -NetworkName=cleaninsights diff --git a/roles/podman_cleaninsights/templates/cleaninsights.php b/roles/podman_cleaninsights/templates/cleaninsights.php deleted file mode 100644 index d72a67a..0000000 --- a/roles/podman_cleaninsights/templates/cleaninsights.php +++ /dev/null @@ -1,341 +0,0 @@ -idsite < 1) { - debug('Malformed body. Last JSON error:' . json_last_error() . ' ' . json_last_error_msg()); - http_response_code(400); - exit(1); -} - - -// Build output for Matomo Tracking API and send in chunks. - -$output = []; - -$reporting_start = time(); -$reporting_end = 0; -$count = 0; -$result = false; - -if (property_exists($data, 'visits') && gettype($data->visits) === 'array') { - foreach ($data->visits as $visit) { - $count += renderVisit($visit); - } -} - -if (property_exists($data, 'events') && gettype($data->events) === 'array') { - foreach ($data->events as $event) { - $count += renderEvent($event); - } -} - -// Send the rest. -send(false); - -debug('result = ' . ($result ? 'success': 'failure') . ", count = $count"); - -if ($result === false) { - debug('There were no events and visits or the Matomo Tracking API refused all requests.'); - http_response_code(400); - exit(1); -} - - -// Instruct Matomo to re-analyse the complete period of this request -// on the next auto-archiving run. - -$start = new DateTime(); -$start->setTimestamp($reporting_start); - -$end = new DateTime(); -$end->setTimestamp($reporting_end); - -$query = http_build_query([ - 'module' => 'API', - 'method' => 'CoreAdminHome.invalidateArchivedReports', - 'idSites' => $data->idsite, - 'period' => 'range', - // Need to use `dates[]` instead of `dates`. Otherwise, some automagic happens in Matomo and breaks the call. - 'dates[]' => $start->format('Y-m-d') . ',' . $end->format('Y-m-d'), - 'token_auth' => $TOKEN_AUTH, -]); - -$result = callMatomo(null, $query); - -if ($result === false) { - debug('Matomo could not be triggered to reprocess data!'); -} -else { - debug('Matomo was triggered to reprocess data!'); -} - -http_response_code(204); - -exit(); - - -// Helper functions. - -function renderVisit(stdClass $visit): int { - $action_name = $visit->action_name; - - return render($visit->period_start, $visit->period_end, $visit->times, - function() use ($action_name) { - return ['action_name' => $action_name]; - }); -} - -function renderEvent(stdClass $event): int { - return render($event->period_start, $event->period_end, $event->times, - function() use ($event) { - $e = ['e_c' => $event->category, 'e_a' => $event->action]; - - if (property_exists($event, 'name') && $event->name) { - $e['e_n'] = $event->name; - } - - if (property_exists($event, 'value') && $event->value) { - $e['e_v'] = $event->value; - } - - return $e; - }); -} - -function render(int $period_start, int $period_end, int $times, callable $callback): int { - global $reporting_start, $reporting_end, $output; - - $reporting_start = min($reporting_start, $period_start); - $reporting_end = max($reporting_end, $period_end); - - $interval = ($period_end - $period_start) / $times; - - // Offset the first record by half an interval, so records are neatly "centered" in the period. - $period_start += $interval / 2; - - for ($i = 0; $i < $times; $i++) { - $request = $callback(); - - $request['cdt'] = $period_start + $interval * $i; - - $output[] = buildRequest($request); - - send(); - } - - return $times; -} - -function buildRequest(array &$request): string { - global $data; - - $request['idsite'] = $data->idsite; - $request['rec'] = 1; - if (property_exists($data, 'lang') && $data->lang) $request['lang'] = $data->lang; - if (property_exists($data, 'ua') && $data->ua) $request['ua'] = $data->ua; - - return '?' . http_build_query($request); -} - -function callMatomo(string $path = null, string $query = null, string $body = null): string { - global $MATOMO_BASE_URL, $MAX_ATTEMPTS, $DELAY_AFTER_FAILURE; - - $options = [ - 'method' => 'POST', - 'header' => 'Content-Type: application/json; charset=UTF-8']; - - if (isset($body)) { - $options['content'] = $body; - } - - $context = stream_context_create(['http' => $options]); - - $url = "$MATOMO_BASE_URL/"; - - if (isset($path)) { - $url .= $path; - } - - if (isset($query)) { - $url .= "?$query"; - } - - $counter = 1; - - debug("Call Matomo at $url"); - - do { - $result = file_get_contents($url, false, $context); - - $counter++; - - if ($result !== false || $counter > $MAX_ATTEMPTS) { - break; - } - - debug("Request error. Sleep $DELAY_AFTER_FAILURE seconds and retry!"); - - sleep($DELAY_AFTER_FAILURE); - - } while (true); - - return $result; -} - -function sendToMatomo(array &$output): bool { - global $TOKEN_AUTH; - - $body = json_encode([ - 'token_auth' => $TOKEN_AUTH, - 'requests' => $output]); - - if ($body === false) { - return false; - } - - $result = callMatomo("matomo.php", null, $body); - - debug("Called tracking API " . (!$result ? 'un' : '') . "successfully with " - . count($output) . " lines of data: " . substr($body, 0, 256)); - - return !!$result; -} - -function send(bool $onlyIfChunkSizeReached = true) { - global $output, $CHUNK_SIZE, $result; - - if (!$onlyIfChunkSizeReached || count($output) >= $CHUNK_SIZE) { - if (sendToMatomo($output)) { - // If at least one request succeeded, we consider this good. - // Otherwise, the client would re-send already processed data. - $result = true; - } - - // Remove already sent requests from memory again. - $output = []; - } -} - -function debug($message) { - global $DEBUG; - if (!$DEBUG) return; - - error_log(print_r($message, true)); -} \ No newline at end of file diff --git a/roles/podman_cleaninsights/templates/cleaninsights.slice b/roles/podman_cleaninsights/templates/cleaninsights.slice deleted file mode 100644 index 71c7a82..0000000 --- a/roles/podman_cleaninsights/templates/cleaninsights.slice +++ /dev/null @@ -1,8 +0,0 @@ -[Unit] -Description=Cleaninsights Slice -Before=slices.target -Requires=matomo.service -Requires=nginx.service - -[Install] -WantedBy=default.target diff --git a/roles/podman_cleaninsights/templates/frontend.network b/roles/podman_cleaninsights/templates/frontend.network deleted file mode 100644 index 379c059..0000000 --- a/roles/podman_cleaninsights/templates/frontend.network +++ /dev/null @@ -1,2 +0,0 @@ -[Network] -NetworkName=frontend diff --git a/roles/podman_cleaninsights/templates/matomo.container b/roles/podman_cleaninsights/templates/matomo.container deleted file mode 100644 index fd9d168..0000000 --- a/roles/podman_cleaninsights/templates/matomo.container +++ /dev/null @@ -1,23 +0,0 @@ -[Unit] -Requires=mysql.service -After=mysql.service -Requires=redis.service -After=redis.service - -[Container] -ContainerName=matomo -Environment=MATOMO_DATABASE_HOST=mysql -Environment=PHP_MEMORY_LIMIT={{ podman_cleaninsights_php_memory_limit }} -Environment=MATOMO_DATABASE_DBNAME={{ podman_cleaninsights_mysql_database }} -Environment=MATOMO_DATABASE_PASSWORD={{ podman_cleaninsights_mysql_password }} -Environment=MATOMO_DATABASE_USERNAME={{ podman_cleaninsights_mysql_user }} -Image=docker.io/matomo:5-fpm -Volume=/home/{{ podman_cleaninsights_podman_rootless_user }}/matomo:/var/www/html -Volume=/home/{{ podman_cleaninsights_podman_rootless_user }}/matomo/cleaninsights.php:/var/www/html/cleaninsights.php:ro -Volume=/home/{{ podman_cleaninsights_podman_rootless_user }}/matomo/cleaninsights.ini:/var/www/html/cleaninsights.ini:ro -Network=cleaninsights.network -Network=frontend.network - -[Service] -Restart=always -Slice=cleaninsights.slice diff --git a/roles/podman_cleaninsights/templates/mysql.container b/roles/podman_cleaninsights/templates/mysql.container deleted file mode 100644 index 71e6dd6..0000000 --- a/roles/podman_cleaninsights/templates/mysql.container +++ /dev/null @@ -1,15 +0,0 @@ -[Container] -ContainerName=mysql -Environment=MYSQL_ROOT_PASSWORD={{ podman_cleaninsights_mysql_root_password }} -Environment=MYSQL_DATABASE={{ podman_cleaninsights_mysql_database }} -Environment=MYSQL_USER={{ podman_cleaninsights_mysql_user }} -Environment=MYSQL_PASSWORD={{ podman_cleaninsights_mysql_password }} -Image=docker.io/mysql:9 -PublishPort=127.0.0.1:3306:3306 -Volume=/home/{{ podman_cleaninsights_podman_rootless_user }}/mysql:/var/lib/mysql -Network=cleaninsights.network - -[Service] -Restart=always -Slice=cleaninsights.slice - diff --git a/roles/podman_cleaninsights/templates/nginx.conf b/roles/podman_cleaninsights/templates/nginx.conf deleted file mode 100644 index 3c20147..0000000 --- a/roles/podman_cleaninsights/templates/nginx.conf +++ /dev/null @@ -1,103 +0,0 @@ -# {{ ansible_managed }} - -upstream php-handler { - server matomo:9000; -} - -server { - listen 80; - listen [::]:80; - - server_name {{ podman_cleaninsights_web_hostname }}; - server_tokens off; - - location /.well-known/acme-challenge/ { - root /var/www/certbot; - } - - location / { - return 301 https://{{ podman_cleaninsights_web_hostname }}$request_uri; - } -} - -server { - listen 443 ssl; - listen [::]:443 ssl; - http2 on; - - server_name matomo {{ podman_cleaninsights_web_hostname }}; - server_tokens off; - - ssl_certificate /etc/letsencrypt/live/{{ podman_cleaninsights_web_hostname }}/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/{{ podman_cleaninsights_web_hostname }}/privkey.pem; - - add_header Strict-Transport-Security max-age=15768000 always; - add_header Referrer-Policy origin always; # make sure outgoing links don't show the URL to the Matomo instance - add_header X-Content-Type-Options "nosniff" always; - add_header X-XSS-Protection "1; mode=block" always; - - root /var/www/html/; # this is the default matomo image path - index index.php; - - location ~ ^/(index|matomo|cleaninsights|piwik|js/index|plugins/HeatmapSessionRecording/configs).php { - # regex to split $uri to $fastcgi_script_name and $fastcgi_path - try_files $fastcgi_script_name =404; - fastcgi_split_path_info ^(.+\.php)(/.+)$; - # Check that the PHP script exists before passing it - #try_files $fastcgi_script_name =404; - - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_param PATH_INFO $fastcgi_path_info; - fastcgi_param HTTP_PROXY ""; # prohibit httpoxy: https://httpoxy.org/ - include fastcgi_params; - fastcgi_pass php-handler; - fastcgi_read_timeout 60s; - } - - # deny access to all other .php files - location ~* ^.+\.php$ { - return 403; - } - - location / { - try_files $uri $uri/ =404; - } - - # deny access to all cleaninsights.ini file - location ~ cleaninsights.ini { - return 403; - } - - # disable all access to the following directories - location ~ ^/(config|tmp|core|lang) { - return 403; # replace with 404 to not show these directories exist - } - - location ~ /\.ht { - return 403; - } - - location ~ js/container_.*_preview\.js$ { - expires off; - add_header Cache-Control 'private, no-cache, no-store'; - } - - location ~ \.(gif|ico|jpg|png|svg|js|css|htm|html|mp3|mp4|wav|ogg|avi|ttf|eot|woff|woff2)$ { - allow all; - # Cache images,CSS,JS and webfonts for an hour - # Increasing the duration may improve the load-time, but may cause old files to show after an Matomo upgrade - expires 1h; - add_header Pragma public; - add_header Cache-Control "public"; - } - - location ~ ^/(libs|vendor|plugins|misc|node_modules) { - deny all; - return 403; - } - - # properly display textfiles in root directory - location ~/(.*\.md|LEGALNOTICE|LICENSE) { - default_type text/plain; - } -} \ No newline at end of file diff --git a/roles/podman_cleaninsights/templates/redis.container b/roles/podman_cleaninsights/templates/redis.container deleted file mode 100644 index aa85fb3..0000000 --- a/roles/podman_cleaninsights/templates/redis.container +++ /dev/null @@ -1,9 +0,0 @@ -[Container] -ContainerName=redis -ExposeHostPort=6379 -Image=docker.io/redis:7.2-rc1-alpine -Network=cleaninsights.network - -[Service] -Restart=always -Slice=cleaninsights.slice