mirror of
https://github.com/seriousm4x/UpSnap.git
synced 2026-08-04 11:15:15 -04:00
feat: add optional confirmation dialog for wake/shutdown #260
This commit is contained in:
66
backend/migrations/1700555328_updated_devices.go
Normal file
66
backend/migrations/1700555328_updated_devices.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/daos"
|
||||
m "github.com/pocketbase/pocketbase/migrations"
|
||||
"github.com/pocketbase/pocketbase/models/schema"
|
||||
)
|
||||
|
||||
func init() {
|
||||
m.Register(func(db dbx.Builder) error {
|
||||
dao := daos.New(db);
|
||||
|
||||
collection, err := dao.FindCollectionByNameOrId("z5lghx2r3tm45n1")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// add
|
||||
new_wake_confirm := &schema.SchemaField{}
|
||||
json.Unmarshal([]byte(`{
|
||||
"system": false,
|
||||
"id": "chtoblvl",
|
||||
"name": "wake_confirm",
|
||||
"type": "bool",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {}
|
||||
}`), new_wake_confirm)
|
||||
collection.Schema.AddField(new_wake_confirm)
|
||||
|
||||
// add
|
||||
new_shutdown_confirm := &schema.SchemaField{}
|
||||
json.Unmarshal([]byte(`{
|
||||
"system": false,
|
||||
"id": "muqy3axq",
|
||||
"name": "shutdown_confirm",
|
||||
"type": "bool",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {}
|
||||
}`), new_shutdown_confirm)
|
||||
collection.Schema.AddField(new_shutdown_confirm)
|
||||
|
||||
return dao.SaveCollection(collection)
|
||||
}, func(db dbx.Builder) error {
|
||||
dao := daos.New(db);
|
||||
|
||||
collection, err := dao.FindCollectionByNameOrId("z5lghx2r3tm45n1")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// remove
|
||||
collection.Schema.RemoveField("chtoblvl")
|
||||
|
||||
// remove
|
||||
collection.Schema.RemoveField("muqy3axq")
|
||||
|
||||
return dao.SaveCollection(collection)
|
||||
})
|
||||
}
|
||||
2465
frontend/pnpm-lock.yaml
generated
2465
frontend/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,8 @@
|
||||
let disabled = false;
|
||||
let timeout = 120;
|
||||
var interval: number;
|
||||
let modalWake: HTMLDialogElement;
|
||||
let modalShutdown: HTMLDialogElement;
|
||||
|
||||
$: if (device.status === 'pending' && !interval) {
|
||||
countdown(Date.parse(device.updated));
|
||||
@@ -86,9 +88,17 @@
|
||||
|
||||
function handleClick() {
|
||||
if (device.status === 'offline') {
|
||||
wake();
|
||||
device.wake_confirm ? askConfirmation('wake') : wake();
|
||||
} else if (device.status === 'online') {
|
||||
shutdown();
|
||||
device.shutdown_confirm ? askConfirmation('shutdown') : shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
function askConfirmation(action: string) {
|
||||
if (action === 'wake') {
|
||||
modalWake.showModal();
|
||||
} else {
|
||||
modalShutdown.showModal();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -141,6 +151,36 @@
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<dialog class="modal" bind:this={modalWake}>
|
||||
<div class="modal-box">
|
||||
<h3 class="font-bold text-lg">
|
||||
{$LL.device.modal_confirm_wake_title({ device: device.name })}
|
||||
</h3>
|
||||
<p class="py-4">{$LL.device.modal_confirm_wake_desc({ device: device.name })}</p>
|
||||
<div class="modal-action">
|
||||
<form method="dialog" class="flex flex-row flex-wrap gap-2">
|
||||
<button class="btn btn-neutral">{$LL.buttons.cancel()}</button>
|
||||
<button class="btn btn-success" on:click={wake}>{$LL.buttons.confirm()}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<dialog class="modal" bind:this={modalShutdown}>
|
||||
<div class="modal-box">
|
||||
<h3 class="font-bold text-lg">
|
||||
{$LL.device.modal_confirm_shutdown_title({ device: device.name })}
|
||||
</h3>
|
||||
<p class="py-4">{$LL.device.modal_confirm_shutdown_desc({ device: device.name })}</p>
|
||||
<div class="modal-action">
|
||||
<form method="dialog" class="flex flex-row flex-wrap gap-2">
|
||||
<button class="btn btn-neutral">{$LL.buttons.cancel()}</button>
|
||||
<button class="btn btn-success" on:click={shutdown}>{$LL.buttons.confirm()}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<style>
|
||||
:global(.menu li.disabled) {
|
||||
color: inherit;
|
||||
|
||||
@@ -284,6 +284,19 @@
|
||||
<!-- eslint-disable svelte/no-at-html-tags -->
|
||||
{@html $LL.settings.ping_interval_desc2()}
|
||||
</p>
|
||||
<div class="form-control flex flex-row flex-wrap gap-4">
|
||||
<div class="flex flex-row gap-2 items-center">
|
||||
<input
|
||||
id="wake-confirm"
|
||||
type="checkbox"
|
||||
class="toggle toggle-success"
|
||||
bind:checked={device.wake_confirm}
|
||||
/>
|
||||
<label class="label cursor-pointer" for="wake-confirm">
|
||||
<span class="label-text">{$LL.device.require_confirmation()}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-control flex flex-row flex-wrap gap-4">
|
||||
<div class="w-full max-w-xs">
|
||||
<label class="label" for="wake-cron">
|
||||
@@ -460,6 +473,19 @@
|
||||
bind:value={device.shutdown_cmd}
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control flex flex-row flex-wrap gap-4">
|
||||
<div class="flex flex-row gap-2 items-center">
|
||||
<input
|
||||
id="shutdown-confirm"
|
||||
type="checkbox"
|
||||
class="toggle toggle-success"
|
||||
bind:checked={device.shutdown_confirm}
|
||||
/>
|
||||
<label class="label cursor-pointer" for="shutdown-confirm">
|
||||
<span class="label-text">{$LL.device.require_confirmation()}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<p class="my-2">
|
||||
{$LL.device.shutdown_cron_desc()}
|
||||
</p>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { pocketbase } from '$lib/stores/pocketbase';
|
||||
import type { Device, Port } from '$lib/types/device';
|
||||
import { faTrash } from '@fortawesome/free-solid-svg-icons';
|
||||
import type { Record } from 'pocketbase';
|
||||
import type { RecordModel } from 'pocketbase';
|
||||
import Fa from 'svelte-fa';
|
||||
import toast from 'svelte-french-toast';
|
||||
import { scale } from 'svelte/transition';
|
||||
@@ -11,7 +11,7 @@
|
||||
export let device: Device;
|
||||
export let index: number;
|
||||
|
||||
function deletePort(port: Port | Record) {
|
||||
function deletePort(port: Port | RecordModel) {
|
||||
if (port.id !== undefined) {
|
||||
$pocketbase
|
||||
.collection('ports')
|
||||
|
||||
@@ -45,6 +45,10 @@ const de = {
|
||||
card_nic_tooltip_shutdown_no_permission: 'Keine Berechtigung zum ausschalten',
|
||||
card_nic_tooltip_power: 'Einschalten',
|
||||
card_nic_tooltip_power_no_permission: 'Keine Berechtigung zum einschalten',
|
||||
modal_confirm_wake_title: '{device} einschalten?',
|
||||
modal_confirm_wake_desc: 'Bitte bestätigen, um {device} einzuschalten.',
|
||||
modal_confirm_shutdown_title: '{device} herunterfahren?',
|
||||
modal_confirm_shutdown_desc: 'Bitte bestätigen, um {device} herunterzufahren.',
|
||||
general: 'Allgemein',
|
||||
general_name: 'Name',
|
||||
general_name_placeholder: 'Gerätename',
|
||||
@@ -109,7 +113,8 @@ const de = {
|
||||
network_scan_add_all: 'Alle Geräte hinzufügen',
|
||||
network_scan_replace_netmask: 'Netmask für alle Geräte ändern?',
|
||||
network_scan_new_netmask: 'Neue Netmask',
|
||||
network_scan_include_unknown: 'Schließe Geräte ein, bei denen der Name "Unknown" ist.'
|
||||
network_scan_include_unknown: 'Schließe Geräte ein, bei denen der Name "Unknown" ist.',
|
||||
require_confirmation: 'Bestätigung erforderlich'
|
||||
},
|
||||
login: {
|
||||
welcome: 'Willkommen',
|
||||
@@ -161,7 +166,8 @@ const de = {
|
||||
change: 'Ändern',
|
||||
reset: 'Zurücksetzen',
|
||||
cancel: 'Abbrechen',
|
||||
add: 'Hinzufügen'
|
||||
add: 'Hinzufügen',
|
||||
confirm: 'Bestätigen'
|
||||
},
|
||||
welcome: {
|
||||
step1_page_title: 'Willkommen bei UpSnap 🥳',
|
||||
|
||||
@@ -43,6 +43,10 @@ const en = {
|
||||
card_nic_tooltip_shutdown_no_permission: 'No permission to shut down this device',
|
||||
card_nic_tooltip_power: 'Power on',
|
||||
card_nic_tooltip_power_no_permission: 'No permission to power on this device',
|
||||
modal_confirm_wake_title: 'Wake {device}?',
|
||||
modal_confirm_wake_desc: 'Please confirm to wake {device}.',
|
||||
modal_confirm_shutdown_title: 'Shutdown {device}?',
|
||||
modal_confirm_shutdown_desc: 'Please confirm to shutdown {device}.',
|
||||
general: 'General',
|
||||
general_name: 'Name',
|
||||
general_name_placeholder: 'Device name',
|
||||
@@ -106,7 +110,8 @@ const en = {
|
||||
network_scan_add_all: 'Add all devices',
|
||||
network_scan_replace_netmask: 'Replace netmask for all devices?',
|
||||
network_scan_new_netmask: 'New netmask',
|
||||
network_scan_include_unknown: 'Include devices where name is "Unknown"'
|
||||
network_scan_include_unknown: 'Include devices where name is "Unknown"',
|
||||
require_confirmation: 'Require Confirmation'
|
||||
},
|
||||
login: {
|
||||
welcome: 'Welcome',
|
||||
@@ -157,7 +162,8 @@ const en = {
|
||||
change: 'Change',
|
||||
reset: 'Reset',
|
||||
cancel: 'Cancel',
|
||||
add: 'Add'
|
||||
add: 'Add',
|
||||
confirm: 'Confirm'
|
||||
},
|
||||
welcome: {
|
||||
step1_page_title: 'Welcome to UpSnap 🥳',
|
||||
|
||||
@@ -46,6 +46,10 @@ const fr = {
|
||||
"Vous n'avez pas la permission pour éteindre cet appareil",
|
||||
card_nic_tooltip_power: 'Allumer',
|
||||
card_nic_tooltip_power_no_permission: "Vous n'avez pas la permission pour allumer cet appareil",
|
||||
modal_confirm_wake_title: 'Réveiller {device} ?',
|
||||
modal_confirm_wake_desc: 'Veuillez confirmer le réveil de {device}.',
|
||||
modal_confirm_shutdown_title: 'Arrêter {device} ?',
|
||||
modal_confirm_shutdown_desc: "Veuillez confirmer l'arrêt du {device}.",
|
||||
general: 'Général',
|
||||
general_name: 'Nom',
|
||||
general_name_placeholder: "Nom de l'appareil",
|
||||
@@ -110,7 +114,8 @@ const fr = {
|
||||
network_scan_add_all: 'Ajouter tous les appareils',
|
||||
network_scan_replace_netmask: 'Remplacer le masque réseau pour tous les appareils ?',
|
||||
network_scan_new_netmask: 'Nouveau masque réseau',
|
||||
network_scan_include_unknown: 'Inclure les appareils dont nom est « Inconnu »'
|
||||
network_scan_include_unknown: 'Inclure les appareils dont nom est « Inconnu »',
|
||||
require_confirmation: 'Demande de confirmation'
|
||||
},
|
||||
login: {
|
||||
welcome: 'Bienvenue',
|
||||
@@ -163,7 +168,8 @@ const fr = {
|
||||
change: 'Modifier',
|
||||
reset: 'Réinitialiser',
|
||||
cancel: 'Annuler',
|
||||
add: 'Ajouter'
|
||||
add: 'Ajouter',
|
||||
confirm: 'Confirmer'
|
||||
},
|
||||
welcome: {
|
||||
step1_page_title: 'Bienvenue sur UpSnap 🥳',
|
||||
|
||||
@@ -181,6 +181,26 @@ type RootTranslation = {
|
||||
* No permission to power on this device
|
||||
*/
|
||||
card_nic_tooltip_power_no_permission: string;
|
||||
/**
|
||||
* Wake {device}?
|
||||
* @param {unknown} device
|
||||
*/
|
||||
modal_confirm_wake_title: RequiredParams<'device'>;
|
||||
/**
|
||||
* Please confirm to wake {device}.
|
||||
* @param {unknown} device
|
||||
*/
|
||||
modal_confirm_wake_desc: RequiredParams<'device'>;
|
||||
/**
|
||||
* Shutdown {device}?
|
||||
* @param {unknown} device
|
||||
*/
|
||||
modal_confirm_shutdown_title: RequiredParams<'device'>;
|
||||
/**
|
||||
* Please confirm to shutdown {device}.
|
||||
* @param {unknown} device
|
||||
*/
|
||||
modal_confirm_shutdown_desc: RequiredParams<'device'>;
|
||||
/**
|
||||
* General
|
||||
*/
|
||||
@@ -405,6 +425,10 @@ type RootTranslation = {
|
||||
* Include devices where name is "Unknown"
|
||||
*/
|
||||
network_scan_include_unknown: string;
|
||||
/**
|
||||
* Require Confirmation
|
||||
*/
|
||||
require_confirmation: string;
|
||||
};
|
||||
login: {
|
||||
/**
|
||||
@@ -575,6 +599,10 @@ type RootTranslation = {
|
||||
* Add
|
||||
*/
|
||||
add: string;
|
||||
/**
|
||||
* Confirm
|
||||
*/
|
||||
confirm: string;
|
||||
};
|
||||
welcome: {
|
||||
/**
|
||||
@@ -912,6 +940,22 @@ export type TranslationFunctions = {
|
||||
* No permission to power on this device
|
||||
*/
|
||||
card_nic_tooltip_power_no_permission: () => LocalizedString;
|
||||
/**
|
||||
* Wake {device}?
|
||||
*/
|
||||
modal_confirm_wake_title: (arg: { device: unknown }) => LocalizedString;
|
||||
/**
|
||||
* Please confirm to wake {device}.
|
||||
*/
|
||||
modal_confirm_wake_desc: (arg: { device: unknown }) => LocalizedString;
|
||||
/**
|
||||
* Shutdown {device}?
|
||||
*/
|
||||
modal_confirm_shutdown_title: (arg: { device: unknown }) => LocalizedString;
|
||||
/**
|
||||
* Please confirm to shutdown {device}.
|
||||
*/
|
||||
modal_confirm_shutdown_desc: (arg: { device: unknown }) => LocalizedString;
|
||||
/**
|
||||
* General
|
||||
*/
|
||||
@@ -1136,6 +1180,10 @@ export type TranslationFunctions = {
|
||||
* Include devices where name is "Unknown"
|
||||
*/
|
||||
network_scan_include_unknown: () => LocalizedString;
|
||||
/**
|
||||
* Require Confirmation
|
||||
*/
|
||||
require_confirmation: () => LocalizedString;
|
||||
};
|
||||
login: {
|
||||
/**
|
||||
@@ -1304,6 +1352,10 @@ export type TranslationFunctions = {
|
||||
* Add
|
||||
*/
|
||||
add: () => LocalizedString;
|
||||
/**
|
||||
* Confirm
|
||||
*/
|
||||
confirm: () => LocalizedString;
|
||||
};
|
||||
welcome: {
|
||||
/**
|
||||
|
||||
@@ -45,6 +45,10 @@ const pt = {
|
||||
card_nic_tooltip_shutdown_no_permission: 'Não tem permissão para desligar este dispositivo',
|
||||
card_nic_tooltip_power: 'Ligar',
|
||||
card_nic_tooltip_power_no_permission: 'Não tem permissão para ligar este dispositivo',
|
||||
modal_confirm_wake_title: 'Despertar {device}?',
|
||||
modal_confirm_wake_desc: 'Confirme para ativar {device}.',
|
||||
modal_confirm_shutdown_title: 'Desligar {device}?',
|
||||
modal_confirm_shutdown_desc: 'Confirme o encerramento do {device}.',
|
||||
general: 'Geral',
|
||||
general_name: 'Nome',
|
||||
general_name_placeholder: 'Nome do dispositivo',
|
||||
@@ -108,7 +112,8 @@ const pt = {
|
||||
network_scan_add_all: 'Adicionar todos os dispositivos',
|
||||
network_scan_replace_netmask: 'Substituir máscara de rede para todos os dispositivos?',
|
||||
network_scan_new_netmask: 'Nova máscara de rede',
|
||||
network_scan_include_unknown: 'Incluir dispositivos em que o nome é "Unknown"'
|
||||
network_scan_include_unknown: 'Incluir dispositivos em que o nome é "Unknown"',
|
||||
require_confirmation: 'Exigir confirmação'
|
||||
},
|
||||
login: {
|
||||
welcome: 'Bem-vindo',
|
||||
@@ -160,7 +165,8 @@ const pt = {
|
||||
change: 'Alterar',
|
||||
reset: 'Repor',
|
||||
cancel: 'Cancelar',
|
||||
add: 'Adicionar'
|
||||
add: 'Adicionar',
|
||||
confirm: 'Confirmar'
|
||||
},
|
||||
welcome: {
|
||||
step1_page_title: 'Bem-vindo ao UpSnap 🥳',
|
||||
|
||||
@@ -42,6 +42,10 @@ const zh = {
|
||||
card_nic_tooltip_shutdown_no_permission: '没有关闭此设备的权限',
|
||||
card_nic_tooltip_power: '启动',
|
||||
card_nic_tooltip_power_no_permission: '没有启动此设备的权限',
|
||||
modal_confirm_wake_title: '唤醒 {device}?',
|
||||
modal_confirm_wake_desc: '请确认唤醒 {device}.',
|
||||
modal_confirm_shutdown_title: '关闭 {device}?',
|
||||
modal_confirm_shutdown_desc: '请确认关闭 {device}.',
|
||||
general: '通用',
|
||||
general_name: '名称',
|
||||
general_name_placeholder: '设备名',
|
||||
@@ -102,7 +106,8 @@ const zh = {
|
||||
network_scan_add_all: '添加全部设备',
|
||||
network_scan_replace_netmask: '更换所有设备的子网掩码?',
|
||||
network_scan_new_netmask: '新子网掩码',
|
||||
network_scan_include_unknown: '包括名称为 “Unknown” 的设备'
|
||||
network_scan_include_unknown: '包括名称为 “Unknown” 的设备',
|
||||
require_confirmation: '要求确认'
|
||||
},
|
||||
login: {
|
||||
welcome: '欢迎使用',
|
||||
@@ -151,7 +156,8 @@ const zh = {
|
||||
change: '更改',
|
||||
reset: '重置',
|
||||
cancel: '取消',
|
||||
add: '添加'
|
||||
add: '添加',
|
||||
confirm: '确认'
|
||||
},
|
||||
welcome: {
|
||||
step1_page_title: '欢迎使用 UpSnap 🥳',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Record } from 'pocketbase';
|
||||
import type { RecordModel } from 'pocketbase';
|
||||
|
||||
export type Device = Record & {
|
||||
export type Device = RecordModel & {
|
||||
name: string;
|
||||
ip: string;
|
||||
mac: string;
|
||||
@@ -10,9 +10,11 @@ export type Device = Record & {
|
||||
link: URL;
|
||||
wake_cron: string;
|
||||
wake_cron_enabled: boolean;
|
||||
wake_confirm: boolean;
|
||||
shutdown_cron: string;
|
||||
shutdown_cron_enabled: boolean;
|
||||
shutdown_cmd: string;
|
||||
shutdown_confirm: boolean;
|
||||
password: string;
|
||||
groups: string[];
|
||||
expand: {
|
||||
@@ -27,11 +29,11 @@ export type Device = Record & {
|
||||
sol_port: number;
|
||||
};
|
||||
|
||||
export type Port = Record & {
|
||||
export type Port = RecordModel & {
|
||||
name: string;
|
||||
number: number;
|
||||
};
|
||||
|
||||
export type Group = Record & {
|
||||
export type Group = RecordModel & {
|
||||
name: string;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Record } from 'pocketbase';
|
||||
import type { RecordModel } from 'pocketbase';
|
||||
|
||||
export type Permission = Record & {
|
||||
export type Permission = RecordModel & {
|
||||
user: string;
|
||||
create: boolean;
|
||||
read: string[];
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import type { Record } from 'pocketbase';
|
||||
import type { RecordModel } from 'pocketbase';
|
||||
|
||||
export type SettingsPublic = Record & {
|
||||
export type SettingsPublic = RecordModel & {
|
||||
collectionId: string;
|
||||
favicon: string;
|
||||
setup_completed: boolean;
|
||||
website_title: string;
|
||||
};
|
||||
|
||||
export type SettingsPrivate = Record & {
|
||||
export type SettingsPrivate = RecordModel & {
|
||||
interval: number;
|
||||
lazy_ping: boolean;
|
||||
scan_range: string;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Record } from 'pocketbase';
|
||||
import type { RecordModel } from 'pocketbase';
|
||||
|
||||
export type User = Record & {
|
||||
export type User = RecordModel & {
|
||||
username: string;
|
||||
email: string;
|
||||
avatar: number;
|
||||
|
||||
Reference in New Issue
Block a user