Initialer Import des AssetManagers

This commit is contained in:
2026-08-02 21:28:50 +00:00
commit ed72ce8821
256 changed files with 26361 additions and 0 deletions
+341
View File
@@ -0,0 +1,341 @@
{% extends 'base.html' %}
{% block content %}
<div class="software-settings-page">
<div class="toolbar software-settings-header">
<div>
<h1>{{ t('software.settings.title') }}</h1>
<p class="muted">{{ t('software.settings.description') }}</p>
</div>
<a class="button button-secondary" href="/software">{{ t('software.settings.back_to_software') }}</a>
</div>
<form method="post" action="/settings/software" class="software-settings-form">
<section class="panel software-settings-section">
<div class="software-settings-section-heading">
<div>
<h2>{{ t('software.settings.dispatch_title') }}</h2>
<p class="muted">{{ t('software.settings.dispatch_help') }}</p>
</div>
<span class="software-settings-badge">{{ t('software.settings.dispatch_badge') }}</span>
</div>
<div class="software-settings-grid">
<label>
{{ t('software.settings.dispatch_delay') }}
<input type="number" name="dispatch_delay_seconds" min="0" max="60" step="1" value="{{ software_settings.get('dispatch_delay_seconds', 2) }}">
<small>{{ t('software.settings.dispatch_delay_help') }}</small>
</label>
<div class="software-callback-preview">
<span>{{ t('software.settings.timeout_check') }}</span>
<code>{{ t('software.settings.timeout_check_value') }}</code>
</div>
</div>
</section>
<section class="panel software-settings-section">
<div class="software-settings-section-heading">
<div>
<h2>{{ t('software.settings.retry_title') }}</h2>
<p class="muted">{{ t('software.settings.retry_help') }}</p>
</div>
<span class="software-settings-badge">{{ t('software.settings.retry_badge') }}</span>
</div>
<div class="software-settings-grid">
<label class="software-toggle-option software-settings-wide">
<input type="checkbox" name="automatic_retry_enabled" {% if software_settings.get('automatic_retry_enabled', true) %}checked{% endif %}>
<span class="software-toggle-track"><span class="software-toggle-thumb"></span></span>
<span>
<strong>{{ t('software.settings.retry_enabled') }}</strong>
<small>{{ t('software.settings.retry_enabled_help') }}</small>
</span>
</label>
<label>
{{ t('software.settings.retry_max_age') }}
<input type="number" name="automatic_retry_max_age_hours" min="1" max="168" step="1" value="{{ software_settings.get('automatic_retry_max_age_hours', 12) }}">
<small>{{ t('software.settings.retry_max_age_help') }}</small>
</label>
<label>
{{ t('software.settings.retry_interval') }}
<input type="number" name="automatic_retry_interval_seconds" min="10" max="86400" step="1" value="{{ software_settings.get('automatic_retry_interval_seconds', 60) }}">
<small>{{ t('software.settings.retry_interval_help') }}</small>
</label>
<label>
{{ t('software.settings.retry_max_retries') }}
<input type="number" name="automatic_retry_max_retries" min="1" max="10" step="1" value="{{ software_settings.get('automatic_retry_max_retries', 3) }}">
<small>{{ t('software.settings.retry_max_retries_help') }}</small>
</label>
<div class="software-callback-preview">
<span>{{ t('software.settings.retry_statuses') }}</span>
<code>{{ t('software.settings.retry_statuses_value') }}</code>
</div>
</div>
</section>
<section class="panel software-settings-section">
<div class="software-settings-section-heading">
<div>
<h2>{{ t('software.settings.callback_title') }}</h2>
<p class="muted">{{ t('software.settings.callback_help') }}</p>
</div>
<span class="software-settings-badge">{{ t('software.settings.callback_badge') }}</span>
</div>
<div class="software-settings-grid">
<label class="software-settings-wide">
{{ t('software.settings.callback_base_url') }}
<input
type="url"
name="callback_base_url"
value="{{ software_settings.get('callback_base_url', '') }}"
placeholder="https://assetmanager.example.org"
autocomplete="url"
>
<small>{{ t('software.settings.callback_base_url_help') }}</small>
</label>
<label>
{{ t('software.settings.callback_timeout') }}
<input type="number" name="callback_timeout_minutes" min="5" max="240" value="{{ software_settings.get('callback_timeout_minutes', 30) }}">
<small>{{ t('software.settings.callback_timeout_help') }}</small>
</label>
<label>
{{ t('software.settings.callback_workers') }}
<input type="number" name="callback_worker_count" min="1" max="8" value="{{ software_settings.get('callback_worker_count', 4) }}">
<small>{{ t('software.settings.callback_workers_help') }}</small>
</label>
<label>
{{ t('software.settings.test_timeout') }}
<input type="number" name="callback_test_timeout_seconds" min="2" max="60" value="{{ software_settings.get('callback_test_timeout_seconds', 10) }}">
<small>{{ t('software.settings.test_timeout_help') }}</small>
</label>
<label class="software-toggle-option">
<input type="checkbox" name="callback_test_verify_tls" {% if software_settings.get('callback_test_verify_tls', true) %}checked{% endif %}>
<span class="software-toggle-track"><span class="software-toggle-thumb"></span></span>
<span>
<strong>{{ t('software.settings.verify_tls') }}</strong>
<small>{{ t('software.settings.verify_tls_help') }}</small>
</span>
</label>
<div class="software-callback-preview software-settings-wide">
<span>{{ t('software.settings.health_url') }}</span>
<code id="callback-health-preview">{{ callback_health_url or t('software.settings.automatic_url') }}</code>
</div>
</div>
</section>
<section class="panel software-settings-section">
<div class="software-settings-section-heading">
<div>
<h2>{{ t('software.settings.debug_title') }}</h2>
<p class="muted">{{ t('software.settings.debug_help') }}</p>
</div>
</div>
<div class="software-settings-grid">
<label class="software-toggle-option">
<input type="checkbox" name="debug_logging" {% if software_settings.get('debug_logging', true) %}checked{% endif %}>
<span class="software-toggle-track"><span class="software-toggle-thumb"></span></span>
<span>
<strong>{{ t('software.settings.debug_logging') }}</strong>
<small>{{ t('software.settings.debug_logging_help') }}</small>
</span>
</label>
<label>
{{ t('software.settings.debug_max_lines') }}
<input type="number" name="debug_log_max_lines" min="100" max="10000" value="{{ software_settings.get('debug_log_max_lines', 1000) }}">
<small>{{ t('software.settings.debug_max_lines_help') }}</small>
</label>
<div class="software-debug-path software-settings-wide">
<span>{{ t('software.settings.debug_log_path') }}</span>
<code>{{ debug_log_path }}</code>
</div>
</div>
</section>
<section class="panel software-settings-section">
<div class="software-settings-section-heading">
<div>
<h2>{{ t('software.settings.exclusions_title') }}</h2>
<p class="muted">{{ t('software.settings.exclusions_help') }}</p>
</div>
<span class="software-settings-badge">{{ t('software.settings.active') }}</span>
</div>
<div class="software-exclusion-list" id="software-exclusion-list">
{% for rule in software_settings.get('inventory_exclusion_rules', []) %}
<div class="software-exclusion-row">
<label>
<span>{{ t('software.settings.exclude_platform') }}</span>
<select name="inventory_exclusion_platform">
{% for platform in ['all', 'windows', 'linux', 'macos', 'unknown'] %}
<option value="{{ platform }}" {% if rule.get('platform', 'all') == platform %}selected{% endif %}>{{ t('software.platform.' ~ platform, platform) }}</option>
{% endfor %}
</select>
</label>
<label class="software-exclusion-name">
<span>{{ t('software.settings.exclude_name_contains') }}</span>
<input type="text" name="inventory_exclusion_name" maxlength="300" value="{{ rule.get('name_contains', '') }}" placeholder="{{ t('software.settings.exclude_name_placeholder') }}">
</label>
<button type="button" class="button button-secondary software-exclusion-remove" title="{{ t('software.settings.exclude_remove') }}" aria-label="{{ t('software.settings.exclude_remove') }}">{{ t('common.delete') }}</button>
</div>
{% endfor %}
</div>
<template id="software-exclusion-row-template">
<div class="software-exclusion-row">
<label>
<span>{{ t('software.settings.exclude_platform') }}</span>
<select name="inventory_exclusion_platform">
<option value="all">{{ t('software.platform.all', 'All platforms') }}</option>
<option value="windows">{{ t('software.platform.windows', 'Windows') }}</option>
<option value="linux">{{ t('software.platform.linux', 'Linux') }}</option>
<option value="macos">{{ t('software.platform.macos', 'macOS') }}</option>
<option value="unknown">{{ t('software.platform.unknown', 'Unknown') }}</option>
</select>
</label>
<label class="software-exclusion-name">
<span>{{ t('software.settings.exclude_name_contains') }}</span>
<input type="text" name="inventory_exclusion_name" maxlength="300" placeholder="{{ t('software.settings.exclude_name_placeholder') }}">
</label>
<button type="button" class="button button-secondary software-exclusion-remove" title="{{ t('software.settings.exclude_remove') }}" aria-label="{{ t('software.settings.exclude_remove') }}">{{ t('common.delete') }}</button>
</div>
</template>
<div class="software-exclusion-actions">
<button type="button" class="button button-secondary" id="software-exclusion-add">{{ t('software.settings.exclude_add') }}</button>
<small class="muted">{{ t('software.settings.exclude_match_help') }}</small>
</div>
</section>
<div class="form-actions software-settings-actions">
<button type="submit" class="button button-save" data-save-shortcut>{{ t('common.save') }}</button>
</div>
</form>
<section class="panel software-settings-section software-test-section">
<div class="software-settings-section-heading">
<div>
<h2>{{ t('software.settings.test_title') }}</h2>
<p class="muted">{{ t('software.settings.test_help') }}</p>
</div>
<form method="post" action="/settings/software/test-callback" class="inline-form">
<button type="submit" class="button">{{ t('software.settings.run_test') }}</button>
</form>
</div>
<div class="software-debug-toolbar">
<span class="muted">{{ t('software.settings.log_auto_refresh') }}</span>
<div class="software-debug-actions">
<button type="button" class="button button-secondary" id="copy-software-debug-log">{{ t('software.copy_log') }}</button>
<form method="post" action="/settings/software/debug-log/clear" class="inline-form">
<button type="submit" class="button button-secondary">{{ t('software.settings.clear_log') }}</button>
</form>
</div>
</div>
<pre class="sync-log software-debug-log" id="software-debug-log" data-localize-log>{{ debug_log or t('software.settings.no_log') }}</pre>
</section>
<section class="panel software-settings-section software-future-section">
<h2>{{ t('software.settings.future_title') }}</h2>
<p class="muted">{{ t('software.settings.future_help') }}</p>
<div class="software-future-grid">
<span>{{ t('software.settings.future_inventory') }}</span>
<span>{{ t('software.settings.future_retention') }}</span>
<span>{{ t('software.settings.future_packages') }}</span>
<span>{{ t('software.settings.future_security') }}</span>
</div>
</section>
</div>
<script>
async function copyTextRobust(text) {
const value = String(text || '');
if (!value) return false;
try {
if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard.writeText(value);
return true;
}
} catch (_) {}
const area = document.createElement('textarea');
area.value = value;
area.setAttribute('readonly', '');
area.style.position = 'fixed';
area.style.left = '-9999px';
area.style.top = '0';
area.style.opacity = '0';
document.body.appendChild(area);
area.focus();
area.select();
area.setSelectionRange(0, area.value.length);
let copied = false;
try {
copied = document.execCommand('copy');
} catch (_) {
copied = false;
}
area.remove();
return copied;
}
(() => {
const baseInput = document.querySelector('input[name="callback_base_url"]');
const preview = document.getElementById('callback-health-preview');
const automaticLabel = {{ t('software.settings.automatic_url')|tojson }};
function updatePreview() {
const base = (baseInput?.value || '').trim().replace(/\/+$/, '');
preview.textContent = base ? base + '/api/software-callback/health' : automaticLabel;
}
baseInput?.addEventListener('input', updatePreview);
const exclusionList = document.getElementById('software-exclusion-list');
const exclusionTemplate = document.getElementById('software-exclusion-row-template');
function bindExclusionRow(row) {
row?.querySelector('.software-exclusion-remove')?.addEventListener('click', () => row.remove());
}
function addExclusionRow() {
if (!exclusionList || !exclusionTemplate) return;
const fragment = exclusionTemplate.content.cloneNode(true);
const row = fragment.querySelector('.software-exclusion-row');
bindExclusionRow(row);
exclusionList.appendChild(fragment);
row?.querySelector('input[name="inventory_exclusion_name"]')?.focus();
}
exclusionList?.querySelectorAll('.software-exclusion-row').forEach(bindExclusionRow);
document.getElementById('software-exclusion-add')?.addEventListener('click', addExclusionRow);
if (exclusionList && exclusionList.children.length === 0) addExclusionRow();
const log = document.getElementById('software-debug-log');
document.getElementById('copy-software-debug-log')?.addEventListener('click', async event => {
const ok = await copyTextRobust(log?.textContent || '');
event.currentTarget.textContent = ok ? {{ t('software.copied')|tojson }} : {{ t('software.copy_failed')|tojson }};
window.setTimeout(() => event.currentTarget.textContent = {{ t('software.copy_log')|tojson }}, 1600);
});
async function refreshLog() {
try {
const response = await fetch('/settings/software/debug-log', {cache: 'no-store'});
if (response.ok) {
const text = await response.text();
log.textContent = text || {{ t('software.settings.no_log')|tojson }}; window.AssetManagerTime?.scan(log);
log.scrollTop = log.scrollHeight;
}
} catch (_) {}
}
log.scrollTop = log.scrollHeight;
window.setInterval(refreshLog, 3000);
})();
</script>
{% endblock %}