feat: RDP ACL exclusivity, mobile wall, nav buttons, resolution xrandr

- RDP сервис может быть назначен только одному пользователю в ACL
- Мобильная заглушка на dashboard при ширине < 1024px
- rdp-proxy: кнопки навигации, спиннер Ожидайте, реконнект
- session_wait_page: тёмная тема, CSS спиннер
- kiosk/universal-runtime manager.py: xrandr + cvt --newmode для resolution
- Dockerfiles: x11-xserver-utils, x11-utils
This commit is contained in:
2026-04-27 18:49:06 +00:00
parent 445d025de2
commit 419b495020
11 changed files with 356 additions and 71 deletions
+18 -2
View File
@@ -70,7 +70,7 @@
<div class="list-title">ACL выбранного пользователя</div>
<div class="acl-grid">
{% for s in services %}
<label><input type="checkbox" class="acl_service" value="{{s.id}}" /> {{s.name}} ({{s.slug}})</label>
<label><input type="checkbox" class="acl_service" value="{{s.id}}" data-stype="{{s.type.value}}" /> {{s.name}} ({{s.slug}})<span class="acl-owner"></span></label>
{% endfor %}
</div>
<button onclick="saveAclForSelectedUser()">Save ACL</button>
@@ -513,6 +513,8 @@
const csrf = "{{ csrf_token }}";
const aclMap = {{ acl | tojson }};
const serviceCategoryMap = {{ service_category_map | tojson }};
const rdpOccupiedBy = {{ rdp_occupied_by | tojson }};
const rdpOccupiedUsername = {{ rdp_occupied_username | tojson }};
const placeholderIcon = '/static/service-placeholder.svg';
let activeTab = 'users';
@@ -683,7 +685,21 @@
const userId = parseInt(document.getElementById('u_id').value || '0', 10);
const allowed = new Set((aclMap[userId] || []));
document.querySelectorAll('.acl_service').forEach((box) => {
box.checked = allowed.has(parseInt(box.value, 10));
const sid = parseInt(box.value, 10);
box.checked = allowed.has(sid);
const isRdp = box.dataset.stype === 'RDP';
const occupiedBy = rdpOccupiedBy[sid];
const currentUserHasIt = allowed.has(sid);
const ownerSpan = box.closest('label').querySelector('.acl-owner');
if (isRdp && occupiedBy && occupiedBy !== userId && !currentUserHasIt) {
box.disabled = true;
box.closest('label').style.opacity = '0.45';
if (ownerSpan) ownerSpan.textContent = ` (${rdpOccupiedUsername[sid]})`;
} else {
box.disabled = false;
box.closest('label').style.opacity = '';
if (ownerSpan) ownerSpan.textContent = '';
}
});
}