feat: implement Windows service for parental control application

This commit is contained in:
2026-08-02 16:24:53 +07:00
parent 32d2553380
commit 229a02b543
10 changed files with 1111 additions and 272 deletions
+27 -10
View File
@@ -5,6 +5,11 @@ async function api(path, body) {
? { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }
: {};
const res = await fetch(path, init);
// сессия истекла — иначе открытая вкладка молча перестала бы обновляться
if (res.status === 401) {
location.href = '/login';
throw new Error('нужен вход');
}
if (!res.ok) throw new Error(`${path}: ${res.status}`);
return res.json();
}
@@ -89,25 +94,32 @@ const UpdateBar = {
props: { info: { type: Object, required: true } },
emits: ['check'],
setup(props) {
const busy = computed(() => ['checking', 'downloading', 'installing'].includes(props.info.phase));
const status = computed(() => {
const i = props.info;
const phases = { checking: 'проверка…', downloading: 'загрузка…', installing: 'установка…' };
if (phases[i.phase]) return phases[i.phase];
if (i.error) return 'не удалось проверить обновления';
if (i.available && i.latest && i.latest !== i.current) return 'доступна версия ' + i.latest;
if (i.phase === 'checking') return 'проверка…';
if (i.phase === 'installing') return `устанавливается ${i.latest}, панель перезапустится`;
if (i.error) return i.error;
if (i.available) return `доступна версия ${i.latest}`;
return 'установлена последняя версия';
});
return { busy, status };
return {
status,
checking: computed(() => props.info.phase === 'checking'),
bad: computed(() => Boolean(props.info.error)),
};
},
template: `
<section class="card">
<div class="row" style="justify-content:space-between">
<div>
<h2 style="margin:0">Версия {{ info.current || '—' }}</h2>
<span :class="info.error ? 'warn' : 'muted'">{{ status }}</span>
<span :class="bad ? 'warn' : 'muted'">{{ status }}</span>
</div>
<button :disabled="busy" @click="$emit('check')">Проверить обновления</button>
<button :disabled="checking" @click="$emit('check')">Проверить обновления</button>
</div>
<div v-if="info.installer" class="muted" style="margin-top:8px">
Установщик, {{ info.installer.time }}:
<span :class="{ warn: !info.installer.ok }">{{ info.installer.message }}</span>
</div>
</section>`,
};
@@ -243,7 +255,9 @@ const App = {
const { info: update, load: loadUpdate, check: checkUpdate } = useUpdate();
onMounted(loadRules);
usePolling(() => { loadProcesses(); loadLog(); loadUpdate(); }, 3000);
usePolling(() => { loadProcesses(); loadLog(); }, 3000);
// версия меняется раз в сутки в лучшем случае — частить незачем
usePolling(loadUpdate, 30000);
// в whitelist «заблокирован» означает отсутствие в allowed — действия зеркальные
const toggle = async (g) => {
@@ -265,7 +279,10 @@ const App = {
return { rules, groups, lines, update, whitelist, save, remove, toggle, kill, scheduleRandom, checkUpdate };
},
template: `
<h1>Родительский контроль</h1>
<div class="row" style="justify-content:space-between">
<h1>Родительский контроль</h1>
<form method="post" action="/logout"><button>Выйти</button></form>
</div>
<update-bar :info="update" @check="checkUpdate" />
<rules-panel :rules="rules" @update="save" @remove="remove" />
<random-kill @schedule="scheduleRandom" />