Files
hysteria2/roles/hysteria2/tasks/export_global.yml
T
Sergey Antropoff 0783f35e2d Вкладки серверов и страница статистики в output/index.html
По умолчанию — Статистика: доступность TCP, IP, пользователи,
матрица присутствия. Серверы открываются отдельными вкладками.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-23 09:20:50 +03:00

165 lines
5.5 KiB
YAML

---
- name: Ensure output root directory exists
ansible.builtin.file:
path: "{{ hysteria2_output_dir }}"
state: directory
mode: "0700"
become: false
- name: Find server-info.yml in output directories
ansible.builtin.find:
paths: "{{ hysteria2_output_dir }}"
patterns: server-info.yml
recurse: true
register: _hysteria2_server_info_files
become: false
- name: Load server metadata from output
ansible.builtin.slurp:
src: "{{ item.path }}"
loop: "{{ _hysteria2_server_info_files.files | default([]) }}"
register: _hysteria2_server_info_raw
when: _hysteria2_server_info_files.matched | default(0) | int > 0
become: false
- name: Build global servers list
ansible.builtin.set_fact:
hysteria2_global_servers: "{{ hysteria2_global_servers | default([]) + [ _entry ] }}"
loop: "{{ _hysteria2_server_info_raw.results | default([]) }}"
when: not item.skipped | default(false)
vars:
_info: "{{ item.content | b64decode | from_yaml }}"
_server_name: "{{ _info.server }}"
_entry:
name: "{{ _server_name }}"
domain: "{{ _info.domain }}"
port: "{{ _info.port }}"
mode: "{{ _info.mode | default('masquerade') }}"
host_ip: >-
{{
_info.host_ip
| default(hostvars[_server_name].ansible_host | default(''), true)
}}
dir: "{{ item.item.path | dirname | basename }}"
users: "{{ _info.users }}"
become: false
- name: Sort global servers by name
ansible.builtin.set_fact:
hysteria2_global_servers: "{{ hysteria2_global_servers | sort(attribute='name') }}"
when: hysteria2_global_servers | default([]) | length > 0
become: false
- name: Probe TCP reachability of exported servers
ansible.builtin.wait_for:
host: "{{ item.host_ip if (item.host_ip | default('') | length > 0) else item.domain }}"
port: "{{ item.port | int }}"
timeout: 3
state: started
loop: "{{ hysteria2_global_servers | default([]) }}"
loop_control:
label: "{{ item.name }}"
register: _hysteria2_probe
failed_when: false
changed_when: false
become: false
when: hysteria2_global_servers | default([]) | length > 0
- name: Merge probe result for each server
ansible.builtin.set_fact:
hysteria2_global_servers_enriched: >-
{{
(hysteria2_global_servers_enriched | default([]))
+ [
hysteria2_global_servers[item] | combine({
'reachable': not ((_hysteria2_probe.results | default([]))[item].failed | default(true)),
'probe_seconds': (_hysteria2_probe.results | default([]))[item].elapsed | default(none)
})
]
}}
loop: "{{ range(0, hysteria2_global_servers | length) | list }}"
when: hysteria2_global_servers | default([]) | length > 0
become: false
- name: Replace servers list with enriched data
ansible.builtin.set_fact:
hysteria2_global_servers: "{{ hysteria2_global_servers_enriched }}"
when: hysteria2_global_servers_enriched is defined
become: false
- name: Compute unique users across servers
ansible.builtin.set_fact:
hysteria2_unique_users: >-
{{
hysteria2_global_servers
| map(attribute='users')
| map('map', attribute='name')
| flatten
| unique
| sort
}}
hysteria2_reachable_count: >-
{{
hysteria2_global_servers
| selectattr('reachable')
| list
| length
}}
when: hysteria2_global_servers | default([]) | length > 0
become: false
- name: Generate global HTML index
ansible.builtin.template:
src: export/global-index.html.j2
dest: "{{ hysteria2_output_dir }}/index.html"
mode: "0644"
vars:
generated_at: "{{ now(utc=false, fmt='%Y-%m-%d %H:%M:%S') }}"
total_users: "{{ hysteria2_global_servers | map(attribute='users') | map('length') | sum }}"
unique_users: "{{ hysteria2_unique_users | default([]) }}"
reachable_count: "{{ hysteria2_reachable_count | default(0) }}"
when: hysteria2_global_servers | default([]) | length > 0
become: false
- name: Remove global HTML index when no servers remain
ansible.builtin.file:
path: "{{ hysteria2_output_dir }}/index.html"
state: absent
when: hysteria2_global_servers | default([]) | length == 0
become: false
- name: Open global index in default browser (macOS)
ansible.builtin.command:
cmd: open "{{ hysteria2_output_dir }}/index.html"
when:
- hysteria2_open_browser | bool
- hysteria2_global_servers | default([]) | length > 0
- ansible_facts['system'] == 'Darwin'
changed_when: false
become: false
- name: Open global index in default browser (Linux)
ansible.builtin.command:
cmd: xdg-open "{{ hysteria2_output_dir }}/index.html"
when:
- hysteria2_open_browser | bool
- hysteria2_global_servers | default([]) | length > 0
- ansible_facts['system'] == 'Linux'
changed_when: false
become: false
failed_when: false
- name: Show global index location
ansible.builtin.debug:
msg: >-
Общий каталог: {{ hysteria2_output_dir }}/index.html
({% if hysteria2_open_browser | bool and hysteria2_global_servers | default([]) | length > 0 %}открыт в браузере{% else %}откройте вручную{% endif %})
when: hysteria2_global_servers | default([]) | length > 0
become: false
- name: Skip global index when no servers exported
ansible.builtin.debug:
msg: "Глобальный {{ hysteria2_output_dir }}/index.html удалён — нет server-info.yml в output/"
when: hysteria2_global_servers | default([]) | length == 0
become: false