737 lines
30 KiB
YAML
737 lines
30 KiB
YAML
---
|
||
- name: Развернуть сервер AWatch-rus
|
||
hosts: aw_server
|
||
become: true
|
||
gather_facts: true
|
||
|
||
vars:
|
||
aw_release_root: "/opt/activitywatch/releases"
|
||
aw_release_dir: "{{ aw_release_root }}/{{ aw_server_version }}"
|
||
aw_archive_path: "/tmp/activitywatch-{{ aw_server_version }}.zip"
|
||
aw_bootstrap_dir: "/tmp/aw-rus-bootstrap"
|
||
aw_release_install_dir: "{{ aw_release_root }}/aw-server-rust-{{ aw_server_version }}"
|
||
aw_ru_patch_cache_bust: "{{ lookup('file', aw_repo_root + '/aw-server/aw-ru-patch.js') | hash('sha1') | truncate(12, true, '') }}"
|
||
aw_sw_cleanup_cache_bust: "{{ lookup('file', aw_repo_root + '/aw-server/aw-sw-cleanup.js') | hash('sha1') | truncate(12, true, '') }}"
|
||
aw_worktime_classes: "{{ lookup('file', aw_repo_root + '/aw-server/settings/classes-worktime.json') | from_json }}"
|
||
aw_default_views: "{{ lookup('file', aw_repo_root + '/aw-server/settings/views-default.json') | from_json }}"
|
||
|
||
tasks:
|
||
- name: Установить базовые пакеты
|
||
ansible.builtin.apt:
|
||
name:
|
||
- curl
|
||
- rsync
|
||
- unzip
|
||
state: present
|
||
update_cache: true
|
||
|
||
- name: Создать системную группу сервиса
|
||
ansible.builtin.group:
|
||
name: "{{ aw_server_group }}"
|
||
system: true
|
||
state: present
|
||
|
||
- name: Создать системную учётную запись сервиса
|
||
ansible.builtin.user:
|
||
name: "{{ aw_server_user }}"
|
||
group: "{{ aw_server_group }}"
|
||
home: "{{ aw_server_data_dir }}"
|
||
shell: /usr/sbin/nologin
|
||
system: true
|
||
create_home: false
|
||
|
||
- name: Создать обязательные каталоги
|
||
ansible.builtin.file:
|
||
path: "{{ item }}"
|
||
state: directory
|
||
mode: "0755"
|
||
loop:
|
||
- "{{ aw_release_root }}"
|
||
- "{{ aw_release_dir }}"
|
||
- "{{ aw_release_install_dir }}"
|
||
- /opt/activitywatch
|
||
- /opt/activitywatch/bin
|
||
- "{{ aw_server_webui_dir }}"
|
||
- "{{ aw_server_webui_dir }}/js"
|
||
- "{{ aw_server_data_dir }}"
|
||
- "{{ aw_server_db_path | dirname }}"
|
||
- "{{ aw_server_data_dir }}/.config"
|
||
- "{{ aw_server_data_dir }}/.config/activitywatch"
|
||
- "{{ aw_server_data_dir }}/.config/activitywatch/aw-server-rust"
|
||
- "{{ aw_server_data_dir }}/backups"
|
||
- "{{ aw_server_log_dir }}"
|
||
- /etc/activitywatch
|
||
- "{{ aw_bootstrap_dir }}"
|
||
|
||
- name: Настроить каталоги ActivityWatch с владельцем сервиса
|
||
ansible.builtin.file:
|
||
path: "{{ item }}"
|
||
state: directory
|
||
owner: "{{ aw_server_user }}"
|
||
group: "{{ aw_server_group }}"
|
||
mode: "0755"
|
||
loop:
|
||
- /opt/activitywatch
|
||
- /opt/activitywatch/bin
|
||
- "{{ aw_release_root }}"
|
||
- "{{ aw_release_dir }}"
|
||
- "{{ aw_release_install_dir }}"
|
||
- "{{ aw_server_webui_dir }}"
|
||
- "{{ aw_server_webui_dir }}/js"
|
||
- "{{ aw_server_data_dir }}"
|
||
- "{{ aw_server_db_path | dirname }}"
|
||
- "{{ aw_server_data_dir }}/.config"
|
||
- "{{ aw_server_data_dir }}/.config/activitywatch"
|
||
- "{{ aw_server_data_dir }}/.config/activitywatch/aw-server-rust"
|
||
- "{{ aw_server_data_dir }}/backups"
|
||
- "{{ aw_server_log_dir }}"
|
||
|
||
- name: (Check mode) Пропустить установку релиза ActivityWatch
|
||
ansible.builtin.debug:
|
||
msg: "ansible_check_mode=true: download/unarchive/install of ActivityWatch release is skipped."
|
||
when: ansible_check_mode
|
||
|
||
- name: Установить релиз ActivityWatch (download/unarchive/install)
|
||
when: not ansible_check_mode
|
||
block:
|
||
- name: Скачать архив релиза ActivityWatch
|
||
ansible.builtin.get_url:
|
||
url: "{{ aw_server_download_url }}"
|
||
dest: "{{ aw_archive_path }}"
|
||
mode: "0644"
|
||
|
||
- name: Распаковать релиз ActivityWatch
|
||
ansible.builtin.unarchive:
|
||
src: "{{ aw_archive_path }}"
|
||
dest: "{{ aw_release_dir }}"
|
||
remote_src: true
|
||
extra_opts: ["-o"]
|
||
|
||
- name: Найти распакованный каталог ActivityWatch
|
||
ansible.builtin.find:
|
||
paths: "{{ aw_release_dir }}"
|
||
recurse: true
|
||
file_type: directory
|
||
patterns: "activitywatch*"
|
||
register: aw_release_find
|
||
|
||
- name: Найти бинарный файл AW server
|
||
ansible.builtin.find:
|
||
paths: "{{ aw_release_dir }}"
|
||
recurse: true
|
||
file_type: file
|
||
patterns:
|
||
- aw-server-rust
|
||
- aw-server
|
||
register: aw_server_binary_find
|
||
|
||
- name: Найти index.html WebUI
|
||
ansible.builtin.find:
|
||
paths: "{{ aw_release_dir }}"
|
||
recurse: true
|
||
file_type: file
|
||
patterns:
|
||
- index.html
|
||
register: aw_webui_index_find
|
||
|
||
- name: Сохранить пути распакованного релиза (binary + webui index)
|
||
ansible.builtin.set_fact:
|
||
aw_release_extracted: "{{ (aw_release_find.files | default([]) | sort(attribute='path') | map(attribute='path') | list | first) | default('') }}"
|
||
aw_server_binary_path: >-
|
||
{{
|
||
(
|
||
(
|
||
(aw_server_binary_find.files | default([]) | sort(attribute='path') | map(attribute='path') | list)
|
||
| select('match', '.*/aw-server-rust$') | list | first
|
||
)
|
||
| default(
|
||
(
|
||
(aw_server_binary_find.files | default([]) | sort(attribute='path') | map(attribute='path') | list | first)
|
||
),
|
||
true
|
||
)
|
||
) | default('')
|
||
}}
|
||
aw_webui_index_path: >-
|
||
{{
|
||
(
|
||
(
|
||
(aw_webui_index_find.files | default([]) | sort(attribute='path') | map(attribute='path') | list)
|
||
| select('search', '/static/index\\.html$') | list | first
|
||
)
|
||
| default(
|
||
(
|
||
(aw_webui_index_find.files | default([]) | sort(attribute='path') | map(attribute='path') | list | first)
|
||
),
|
||
true
|
||
)
|
||
) | default('')
|
||
}}
|
||
|
||
- name: Сохранить каталог WebUI (dirname index.html)
|
||
ansible.builtin.set_fact:
|
||
aw_webui_source_path: "{{ aw_webui_index_path | dirname }}"
|
||
|
||
- name: Проверить, что компоненты релиза найдены
|
||
ansible.builtin.assert:
|
||
that:
|
||
- aw_release_extracted is defined
|
||
- aw_release_extracted | length > 0
|
||
- aw_server_binary_path is defined
|
||
- aw_server_binary_path | length > 0
|
||
- aw_webui_source_path is defined
|
||
- aw_webui_source_path | length > 0
|
||
fail_msg: "Не удалось найти бинарный файл или WebUI в распакованном релизе ActivityWatch."
|
||
|
||
- name: Создать каталог установленного релиза
|
||
ansible.builtin.file:
|
||
path: "{{ aw_release_install_dir }}"
|
||
state: directory
|
||
owner: "{{ aw_server_user }}"
|
||
group: "{{ aw_server_group }}"
|
||
mode: "0755"
|
||
|
||
- name: Установить бинарный файл AW server
|
||
ansible.builtin.copy:
|
||
remote_src: true
|
||
src: "{{ aw_server_binary_path }}"
|
||
dest: "{{ aw_release_install_dir }}/aw-server-rust"
|
||
owner: "{{ aw_server_user }}"
|
||
group: "{{ aw_server_group }}"
|
||
mode: "0755"
|
||
|
||
- name: Создать ссылку на активный бинарный файл AW server
|
||
ansible.builtin.file:
|
||
src: "{{ aw_release_install_dir }}/aw-server-rust"
|
||
dest: /opt/activitywatch/bin/aw-server-rust
|
||
owner: "{{ aw_server_user }}"
|
||
group: "{{ aw_server_group }}"
|
||
state: link
|
||
force: true
|
||
|
||
- name: Синхронизировать WebUI в RU каталог
|
||
ansible.builtin.command:
|
||
cmd: "rsync -a {{ aw_webui_source_path }}/ {{ aw_server_webui_dir }}/"
|
||
|
||
- name: Настроить владельца файлов /opt/activitywatch
|
||
ansible.builtin.file:
|
||
path: /opt/activitywatch
|
||
state: directory
|
||
owner: "{{ aw_server_user }}"
|
||
group: "{{ aw_server_group }}"
|
||
recurse: true
|
||
|
||
- name: Установить systemd service из шаблона репозитория
|
||
ansible.builtin.copy:
|
||
dest: /etc/systemd/system/activitywatch-server.service
|
||
mode: "0644"
|
||
content: >-
|
||
{{
|
||
lookup('file', aw_repo_root + '/aw-server/activitywatch-server.service')
|
||
| replace('__AW_SERVER_USER__', aw_server_user)
|
||
| replace('__AW_SERVER_GROUP__', aw_server_group)
|
||
| replace('__AW_SERVER_DATA_DIR__', aw_server_data_dir)
|
||
}}
|
||
notify:
|
||
- Перезагрузить systemd
|
||
- Перезапустить activitywatch
|
||
|
||
- name: (Check mode) Пропустить WebUI patch и запуск сервиса
|
||
ansible.builtin.debug:
|
||
msg: "ansible_check_mode=true: WebUI patch + service start + API checks are skipped."
|
||
when: ansible_check_mode
|
||
|
||
- name: Применить WebUI RU patch и запустить сервис
|
||
when: not ansible_check_mode
|
||
block:
|
||
- name: Скопировать RU patch файлы WebUI из репозитория
|
||
ansible.builtin.copy:
|
||
src: "{{ item.src }}"
|
||
dest: "{{ item.dest }}"
|
||
mode: "{{ item.mode }}"
|
||
owner: "{{ aw_server_user }}"
|
||
group: "{{ aw_server_group }}"
|
||
loop:
|
||
- { src: "{{ aw_repo_root }}/aw-server/aw-ru-patch.js", dest: "{{ aw_server_webui_dir }}/js/ru-patch-v5.js", mode: "0644" }
|
||
- { src: "{{ aw_repo_root }}/aw-server/aw-sw-cleanup.js", dest: "{{ aw_server_webui_dir }}/js/sw-cleanup.js", mode: "0644" }
|
||
- { src: "{{ aw_repo_root }}/aw-server/aw-worktime-panel.js", dest: "{{ aw_server_webui_dir }}/js/aw-worktime-panel.js", mode: "0644" }
|
||
- { src: "{{ aw_repo_root }}/aw-server/aw-host-groups.json", dest: "{{ aw_server_webui_dir }}/js/aw-host-groups.json", mode: "0644" }
|
||
|
||
- name: Создать каталог /root/bootstrap для apply_webui_ru_patch.sh
|
||
ansible.builtin.file:
|
||
path: /root/bootstrap
|
||
state: directory
|
||
mode: "0755"
|
||
|
||
- name: Скопировать RU patch файлы для apply_webui_ru_patch.sh (хотфиксы compiled JS чанков)
|
||
ansible.builtin.copy:
|
||
src: "{{ item.src }}"
|
||
dest: "{{ item.dest }}"
|
||
mode: "{{ item.mode }}"
|
||
loop:
|
||
- { src: "{{ aw_repo_root }}/aw-server/aw-ru-patch.js", dest: "/root/bootstrap/aw-ru-patch.js", mode: "0644" }
|
||
- { src: "{{ aw_repo_root }}/aw-server/aw-sw-cleanup.js", dest: "/root/bootstrap/aw-sw-cleanup.js", mode: "0644" }
|
||
- { src: "{{ aw_repo_root }}/aw-server/aw-worktime-panel.js", dest: "/root/bootstrap/aw-worktime-panel.js", mode: "0644" }
|
||
- { src: "{{ aw_repo_root }}/aw-server/aw-host-groups.json", dest: "/root/bootstrap/aw-host-groups.json", mode: "0644" }
|
||
|
||
- name: Скопировать apply_webui_ru_patch.sh скрипт
|
||
ansible.builtin.copy:
|
||
src: "{{ aw_repo_root }}/aw-server/apply_webui_ru_patch.sh"
|
||
dest: /opt/activitywatch/aw-server/apply_webui_ru_patch.sh
|
||
mode: "0755"
|
||
|
||
- name: Записать /etc/activitywatch/aw-server.env перед хотфиксами
|
||
ansible.builtin.copy:
|
||
dest: /etc/activitywatch/aw-server.env
|
||
mode: "0640"
|
||
owner: root
|
||
group: root
|
||
content: |
|
||
AW_SERVER_BIND_HOST={{ aw_server_bind_host }}
|
||
AW_SERVER_PORT={{ aw_server_port }}
|
||
AW_SERVER_DATA_DIR={{ aw_server_data_dir }}
|
||
AW_SERVER_DB_PATH={{ aw_server_db_path }}
|
||
AW_SERVER_LOG_DIR={{ aw_server_log_dir }}
|
||
AW_SERVER_WEBUI_DIR={{ aw_server_webui_dir }}
|
||
AW_SERVER_USER={{ aw_server_user }}
|
||
AW_SERVER_GROUP={{ aw_server_group }}
|
||
AW_WORKTIME_REPORT_BASE={{ aw_worktime_report_base }}
|
||
AW_WORKTIME_TZ={{ aw_worktime_timezone }}
|
||
XDG_DATA_HOME={{ aw_server_data_dir }}/.local/share
|
||
XDG_CONFIG_HOME={{ aw_server_data_dir }}/.config
|
||
|
||
- name: Установить скрипт AW worktime API
|
||
ansible.builtin.copy:
|
||
src: "{{ aw_repo_root }}/aw-server/aw-worktime-api.py"
|
||
dest: /usr/local/bin/aw-worktime-api.py
|
||
owner: root
|
||
group: root
|
||
mode: "0755"
|
||
|
||
- name: Установить systemd unit AW worktime API
|
||
ansible.builtin.copy:
|
||
src: "{{ aw_repo_root }}/aw-server/aw-worktime-api.service"
|
||
dest: /etc/systemd/system/aw-worktime-api.service
|
||
owner: root
|
||
group: root
|
||
mode: "0644"
|
||
|
||
- name: Установить скрипт AW worktime UI bridge
|
||
ansible.builtin.copy:
|
||
src: "{{ aw_repo_root }}/aw-server/aw-worktime-ui-bridge.py"
|
||
dest: /usr/local/bin/aw-worktime-ui-bridge.py
|
||
owner: root
|
||
group: root
|
||
mode: "0755"
|
||
|
||
- name: Установить systemd unit AW worktime UI bridge
|
||
ansible.builtin.copy:
|
||
src: "{{ aw_repo_root }}/aw-server/aw-worktime-ui-bridge.service"
|
||
dest: /etc/systemd/system/aw-worktime-ui-bridge.service
|
||
owner: root
|
||
group: root
|
||
mode: "0644"
|
||
|
||
- name: Установить systemd timer AW worktime UI bridge
|
||
ansible.builtin.copy:
|
||
src: "{{ aw_repo_root }}/aw-server/aw-worktime-ui-bridge.timer"
|
||
dest: /etc/systemd/system/aw-worktime-ui-bridge.timer
|
||
owner: root
|
||
group: root
|
||
mode: "0644"
|
||
|
||
- name: Перезагрузить systemd после установки AW worktime API
|
||
ansible.builtin.systemd:
|
||
daemon_reload: true
|
||
|
||
- name: Включить и перезапустить AW worktime API
|
||
ansible.builtin.systemd:
|
||
name: aw-worktime-api.service
|
||
enabled: true
|
||
state: restarted
|
||
|
||
- name: Отключить legacy timer aw-worktime-afk-bridge (если есть)
|
||
ansible.builtin.systemd:
|
||
name: aw-worktime-afk-bridge.timer
|
||
enabled: false
|
||
state: stopped
|
||
failed_when: false
|
||
|
||
- name: Включить и перезапустить AW worktime UI bridge timer
|
||
ansible.builtin.systemd:
|
||
name: aw-worktime-ui-bridge.timer
|
||
enabled: true
|
||
state: restarted
|
||
|
||
- name: Выполнить разовый прогон AW worktime UI bridge
|
||
ansible.builtin.systemd:
|
||
name: aw-worktime-ui-bridge.service
|
||
state: started
|
||
failed_when: false
|
||
|
||
- name: Применить хотфиксы compiled JS чанков (Trends, Timespiral, Category helper)
|
||
ansible.builtin.command:
|
||
cmd: "/opt/activitywatch/aw-server/apply_webui_ru_patch.sh"
|
||
register: apply_ru_patch_result
|
||
failed_when: false
|
||
|
||
- name: Вывести результат применения хотфиксов
|
||
ansible.builtin.debug:
|
||
msg: "apply_webui_ru_patch.sh: {{ apply_ru_patch_result.stdout }}"
|
||
|
||
- name: Проверить наличие index.html после копирования
|
||
ansible.builtin.stat:
|
||
path: "{{ aw_server_webui_dir }}/index.html"
|
||
register: aw_webui_ru_index
|
||
|
||
- name: Проверить, что index.html доступен для RU patch
|
||
ansible.builtin.assert:
|
||
that:
|
||
- aw_webui_ru_index.stat.exists
|
||
fail_msg: "Не найден index.html WebUI для применения RU patch."
|
||
|
||
- name: Удалить старые теги RU patch из index.html
|
||
ansible.builtin.replace:
|
||
path: "{{ aw_server_webui_dir }}/index.html"
|
||
regexp: '<script[^>]+(?:ru-patch-v5\.js|sw-cleanup\.js|aw-ru-patch\.js|aw-sw-cleanup\.js)[^>]*></script>'
|
||
replace: ''
|
||
|
||
- name: Добавить cleanup script RU patch в index.html
|
||
ansible.builtin.replace:
|
||
path: "{{ aw_server_webui_dir }}/index.html"
|
||
regexp: '</head>'
|
||
replace: '<script src="/js/sw-cleanup.js?v={{ aw_sw_cleanup_cache_bust }}"></script></head>'
|
||
|
||
- name: Добавить загрузчик RU patch перед закрытием body
|
||
ansible.builtin.replace:
|
||
path: "{{ aw_server_webui_dir }}/index.html"
|
||
regexp: '</body>'
|
||
replace: '<script defer="defer" src="/js/ru-patch-v5.js?v={{ aw_ru_patch_cache_bust }}"></script></body>'
|
||
|
||
- name: Скопировать merge script AW DB на сервер
|
||
ansible.builtin.copy:
|
||
src: "{{ aw_repo_root }}/scripts/merge_aw_server_dbs.py"
|
||
dest: /usr/local/bin/merge_aw_server_dbs.py
|
||
owner: root
|
||
group: root
|
||
mode: "0755"
|
||
|
||
- name: Проверить наличие legacy root DB
|
||
ansible.builtin.stat:
|
||
path: /root/.local/share/activitywatch/aw-server-rust/sqlite.db
|
||
register: aw_legacy_root_db
|
||
|
||
- name: Проверить наличие target DB
|
||
ansible.builtin.stat:
|
||
path: "{{ aw_server_db_path }}"
|
||
register: aw_target_db
|
||
|
||
- name: Остановить сервис перед merge server DB
|
||
ansible.builtin.systemd:
|
||
name: activitywatch-server.service
|
||
state: stopped
|
||
when: aw_legacy_root_db.stat.exists | default(false)
|
||
|
||
- name: Создать backup каталоги server DB
|
||
ansible.builtin.file:
|
||
path: "{{ aw_server_data_dir }}/backups/db"
|
||
state: directory
|
||
owner: "{{ aw_server_user }}"
|
||
group: "{{ aw_server_group }}"
|
||
mode: "0755"
|
||
when: aw_legacy_root_db.stat.exists | default(false)
|
||
|
||
- name: Backup target DB перед merge
|
||
ansible.builtin.copy:
|
||
remote_src: true
|
||
src: "{{ aw_server_db_path }}"
|
||
dest: "{{ aw_server_data_dir }}/backups/db/target-before-merge-{{ ansible_date_time.iso8601_basic_short }}.sqlite.db"
|
||
owner: "{{ aw_server_user }}"
|
||
group: "{{ aw_server_group }}"
|
||
mode: "0644"
|
||
when:
|
||
- aw_legacy_root_db.stat.exists | default(false)
|
||
- aw_target_db.stat.exists | default(false)
|
||
|
||
- name: Backup legacy root DB перед merge
|
||
ansible.builtin.copy:
|
||
remote_src: true
|
||
src: /root/.local/share/activitywatch/aw-server-rust/sqlite.db
|
||
dest: "{{ aw_server_data_dir }}/backups/db/legacy-root-{{ ansible_date_time.iso8601_basic_short }}.sqlite.db"
|
||
owner: "{{ aw_server_user }}"
|
||
group: "{{ aw_server_group }}"
|
||
mode: "0644"
|
||
when: aw_legacy_root_db.stat.exists | default(false)
|
||
|
||
- name: Merge legacy root DB в target DB
|
||
ansible.builtin.command:
|
||
argv:
|
||
- python3
|
||
- /usr/local/bin/merge_aw_server_dbs.py
|
||
- --base
|
||
- /root/.local/share/activitywatch/aw-server-rust/sqlite.db
|
||
- --overlay
|
||
- "{{ aw_server_db_path }}"
|
||
- --output
|
||
- "{{ aw_server_db_path }}.merged"
|
||
when:
|
||
- aw_legacy_root_db.stat.exists | default(false)
|
||
- aw_target_db.stat.exists | default(false)
|
||
|
||
- name: Install merged DB as active target DB
|
||
ansible.builtin.copy:
|
||
remote_src: true
|
||
src: "{{ aw_server_db_path }}.merged"
|
||
dest: "{{ aw_server_db_path }}"
|
||
owner: "{{ aw_server_user }}"
|
||
group: "{{ aw_server_group }}"
|
||
mode: "0644"
|
||
when:
|
||
- aw_legacy_root_db.stat.exists | default(false)
|
||
- aw_target_db.stat.exists | default(false)
|
||
|
||
- name: Скопировать legacy root DB в target DB если target ещё не существует
|
||
ansible.builtin.copy:
|
||
remote_src: true
|
||
src: /root/.local/share/activitywatch/aw-server-rust/sqlite.db
|
||
dest: "{{ aw_server_db_path }}"
|
||
owner: "{{ aw_server_user }}"
|
||
group: "{{ aw_server_group }}"
|
||
mode: "0644"
|
||
when:
|
||
- aw_legacy_root_db.stat.exists | default(false)
|
||
- not (aw_target_db.stat.exists | default(false))
|
||
|
||
- name: Записать aw-server-rust config.toml с разрешёнными CORS origin
|
||
ansible.builtin.copy:
|
||
dest: "{{ aw_server_data_dir }}/.config/activitywatch/aw-server-rust/config.toml"
|
||
owner: "{{ aw_server_user }}"
|
||
group: "{{ aw_server_group }}"
|
||
mode: "0644"
|
||
content: |
|
||
cors = [
|
||
{% for origin in aw_server_cors_origins | default([]) %}
|
||
"{{ origin }}"{% if not loop.last %},{% endif %}
|
||
{% endfor %}
|
||
]
|
||
|
||
- name: Включить и запустить сервис
|
||
ansible.builtin.systemd:
|
||
name: activitywatch-server.service
|
||
enabled: true
|
||
state: restarted
|
||
daemon_reload: true
|
||
|
||
- name: Дождаться ответа API
|
||
ansible.builtin.uri:
|
||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/info"
|
||
method: GET
|
||
status_code: 200
|
||
register: aw_api
|
||
retries: 10
|
||
delay: 3
|
||
until: aw_api.status == 200
|
||
|
||
- name: Считать текущие server-side settings
|
||
ansible.builtin.uri:
|
||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/"
|
||
method: GET
|
||
status_code: 200
|
||
register: aw_settings_current
|
||
when: aw_apply_worktime_settings | default(false) | bool
|
||
|
||
- name: Считать текущие server-side views
|
||
ansible.builtin.uri:
|
||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/views"
|
||
method: GET
|
||
status_code: 200
|
||
register: aw_views_current
|
||
when: aw_apply_worktime_settings | default(false) | bool
|
||
|
||
- name: Считать текущие server-side classes
|
||
ansible.builtin.uri:
|
||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/classes"
|
||
method: GET
|
||
status_code: 200
|
||
register: aw_classes_current
|
||
when: aw_apply_worktime_settings | default(false) | bool
|
||
|
||
- name: Создать backup текущих server-side settings/views/classes
|
||
ansible.builtin.copy:
|
||
dest: "{{ aw_server_data_dir }}/backups/{{ item.name }}-{{ ansible_date_time.iso8601_basic_short }}.json"
|
||
owner: "{{ aw_server_user }}"
|
||
group: "{{ aw_server_group }}"
|
||
mode: "0644"
|
||
content: "{{ item.payload | to_nice_json }}"
|
||
loop:
|
||
- name: settings
|
||
payload: "{{ aw_settings_current.json | default({}) }}"
|
||
- name: views
|
||
payload: "{{ aw_views_current.json | default(none) }}"
|
||
- name: classes
|
||
payload: "{{ aw_classes_current.json | default(none) }}"
|
||
when: aw_apply_worktime_settings | default(false) | bool
|
||
|
||
- name: Настроить DLP Aggregator (Phase 2)
|
||
block:
|
||
- name: Создать каталог для скриптов
|
||
ansible.builtin.file:
|
||
path: "/opt/activitywatch/scripts"
|
||
state: directory
|
||
owner: root
|
||
group: root
|
||
mode: "0755"
|
||
|
||
- name: Скопировать агрегатор событий DLP
|
||
ansible.builtin.copy:
|
||
src: "{{ aw_repo_root }}/scripts/aggregate_dlp_events.py"
|
||
dest: "/opt/activitywatch/scripts/aggregate_dlp_events.py"
|
||
owner: root
|
||
group: root
|
||
mode: "0755"
|
||
|
||
- name: Установить systemd unit для агрегатора
|
||
ansible.builtin.copy:
|
||
dest: /etc/systemd/system/activitywatch-dlp-aggregator.service
|
||
content: |
|
||
[Unit]
|
||
Description=ActivityWatch DLP Event Aggregator
|
||
After=activitywatch-server.service
|
||
|
||
[Service]
|
||
Type=oneshot
|
||
User={{ aw_server_user }}
|
||
WorkingDirectory={{ aw_server_data_dir }}
|
||
ExecStart=/usr/bin/python3 /opt/activitywatch/scripts/aggregate_dlp_events.py \
|
||
--aw-url http://127.0.0.1:{{ aw_server_port }}/api/0 \
|
||
--sqlite-path {{ aw_server_data_dir }}/dlp_warehouse.sqlite \
|
||
--state-path {{ aw_server_data_dir }}/dlp-aggregator-state.json
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
|
||
- name: Установить systemd timer для агрегатора
|
||
ansible.builtin.copy:
|
||
dest: /etc/systemd/system/activitywatch-dlp-aggregator.timer
|
||
content: |
|
||
[Unit]
|
||
Description=Run ActivityWatch DLP Aggregator every 5 minutes
|
||
|
||
[Timer]
|
||
OnBootSec=1min
|
||
OnUnitActiveSec=5min
|
||
AccuracySec=1s
|
||
|
||
[Install]
|
||
WantedBy=timers.target
|
||
|
||
- name: Включить и запустить таймер агрегатора
|
||
ansible.builtin.systemd:
|
||
name: activitywatch-dlp-aggregator.timer
|
||
enabled: true
|
||
state: started
|
||
daemon_reload: true
|
||
|
||
- name: Применить базовые worktime settings (classes)
|
||
ansible.builtin.uri:
|
||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/classes"
|
||
method: POST
|
||
body: "{{ aw_worktime_classes }}"
|
||
body_format: json
|
||
status_code: [200, 201]
|
||
when: aw_apply_worktime_settings | default(false) | bool
|
||
|
||
- name: Применить базовые views для DLP и worktime
|
||
ansible.builtin.uri:
|
||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/views"
|
||
method: POST
|
||
body: "{{ aw_default_views }}"
|
||
body_format: json
|
||
status_code: [200, 201]
|
||
when: aw_apply_worktime_settings | default(false) | bool
|
||
|
||
- name: Вычислить worktime durationDefault из aw_worktime_from/to
|
||
ansible.builtin.set_fact:
|
||
aw_worktime_duration_default_derived: >-
|
||
{{
|
||
(
|
||
(
|
||
(((aw_worktime_to | default('17:00')).split(':')[0] | int) * 60 + ((aw_worktime_to | default('17:00')).split(':')[1] | int)) -
|
||
(((aw_worktime_from | default('08:00')).split(':')[0] | int) * 60 + ((aw_worktime_from | default('08:00')).split(':')[1] | int))
|
||
) * 60
|
||
)
|
||
}}
|
||
when: aw_apply_worktime_settings | default(false) | bool
|
||
|
||
- name: Нормализовать durationDefault для ночных смен
|
||
ansible.builtin.set_fact:
|
||
aw_worktime_duration_default_effective: >-
|
||
{{
|
||
(aw_worktime_duration_default_derived | int)
|
||
if (aw_worktime_duration_default_derived | int) > 0
|
||
else ((aw_worktime_duration_default_derived | int) + 86400)
|
||
}}
|
||
when: aw_apply_worktime_settings | default(false) | bool
|
||
|
||
- name: Проверить корректность durationDefault
|
||
ansible.builtin.assert:
|
||
that:
|
||
- aw_worktime_duration_default_effective | int > 0
|
||
- aw_worktime_duration_default_effective | int <= 86400
|
||
fail_msg: "Некорректный интервал рабочего времени: {{ aw_worktime_from }}..{{ aw_worktime_to }}"
|
||
when: aw_apply_worktime_settings | default(false) | bool
|
||
|
||
- name: Применить базовый период worktime (startOfDay)
|
||
ansible.builtin.uri:
|
||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/startOfDay"
|
||
method: POST
|
||
body: "\"{{ aw_worktime_start_of_day }}\""
|
||
headers:
|
||
Content-Type: application/json
|
||
status_code: [200, 201]
|
||
when: aw_apply_worktime_settings | default(false) | bool
|
||
|
||
- name: Применить базовый период worktime (durationDefault seconds)
|
||
ansible.builtin.uri:
|
||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/durationDefault"
|
||
method: POST
|
||
body: "{{ aw_worktime_duration_default_effective | string }}"
|
||
headers:
|
||
Content-Type: application/json
|
||
status_code: [200, 201]
|
||
when: aw_apply_worktime_settings | default(false) | bool
|
||
|
||
- name: Применить always_active_pattern для fallback без AFK
|
||
ansible.builtin.uri:
|
||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/always_active_pattern"
|
||
method: POST
|
||
body: "\"{{ aw_server_always_active_pattern }}\""
|
||
headers:
|
||
Content-Type: application/json
|
||
status_code: [200, 201]
|
||
when:
|
||
- aw_apply_worktime_settings | default(false) | bool
|
||
- (aw_server_always_active_pattern | default('') | string | length) > 0
|
||
|
||
- name: Применить landingpage профиля
|
||
ansible.builtin.uri:
|
||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/landingpage"
|
||
method: POST
|
||
body: "\"{{ aw_server_landingpage }}\""
|
||
headers:
|
||
Content-Type: application/json
|
||
status_code: [200, 201]
|
||
when:
|
||
- aw_apply_worktime_settings | default(false) | bool
|
||
- (aw_server_landingpage | default('') | string | length) > 0
|
||
|
||
handlers:
|
||
- name: Перезагрузить systemd
|
||
ansible.builtin.systemd:
|
||
daemon_reload: true
|
||
|
||
- name: Перезапустить activitywatch
|
||
ansible.builtin.systemd:
|
||
name: activitywatch-server.service
|
||
state: restarted
|