mirror of
https://github.com/seriousm4x/UpSnap.git
synced 2026-03-31 06:24:09 -04:00
added settings page
This commit is contained in:
@@ -1,7 +1,13 @@
|
||||
# Django Wake on LAN
|
||||
|
||||
<div align="center" width="100%">
|
||||
<img src="app/wol/static/img/favicon.png" width="128" />
|
||||
</div>
|
||||
|
||||
A simple wake on lan app written with Django, Django-Channels (websockets), Celery and Redis.
|
||||
|
||||
<a target="_blank" href="https://github.com/seriousm4x/django-wake-on-lan"><img src="https://img.shields.io/github/stars/seriousm4x/django-wake-on-lan" /></a> <a target="_blank" href="https://hub.docker.com/r/seriousm4x/django-wol"><img src="https://img.shields.io/docker/pulls/seriousm4x/django-wol" /></a> <a target="_blank" href="https://hub.docker.com/r/seriousm4x/django-wol"><img src="https://img.shields.io/docker/v/seriousm4x/django-wol/latest?label=docker%20image%20ver." /></a> <a target="_blank" href="https://github.com/seriousm4x/django-wake-on-lan"><img src="https://img.shields.io/github/last-commit/seriousm4x/django-wake-on-lan" /></a>
|
||||
|
||||
| Dark | Light |
|
||||
| -------------------- | --------------------- |
|
||||
|  |  |
|
||||
@@ -35,6 +41,9 @@ You can use the example [docker-compose.yml](docker-compose.yml) file and just r
|
||||
| POSTGRES_USER | Str | Database user |
|
||||
| POSTGRES_PASSWORD | Str | Database password |
|
||||
| POSTGRES_DB | Str | Database name |
|
||||
| PING_INTERVAL | Int | Time between pings |
|
||||
| ENABLE_NOTIFICATIONS | Bool | Show notifications in the bottom right corner |
|
||||
|
||||
|
||||
## Manage devices
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import os
|
||||
|
||||
from celery import Celery
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_wol.settings")
|
||||
@@ -8,11 +9,11 @@ app = Celery("dango_wol")
|
||||
app.config_from_object("django.conf:settings", namespace="CELERY")
|
||||
|
||||
app.conf.beat_schedule = {
|
||||
"ping_devices_5s": {
|
||||
"ping_devices": {
|
||||
"task": "wol.tasks.status",
|
||||
"schedule": 5
|
||||
"schedule": int(os.getenv("PING_INTERVAL"))
|
||||
},
|
||||
"scheduled_wakes_1s": {
|
||||
"scheduled_wakes": {
|
||||
"task": "wol.tasks.scheduled_wakes",
|
||||
"schedule": 1
|
||||
}
|
||||
|
||||
@@ -141,3 +141,5 @@ USE_TZ = True
|
||||
STATIC_URL = '/static/'
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
|
||||
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
|
||||
|
||||
VERSION = "v1.2.0"
|
||||
@@ -12,7 +12,11 @@ fi
|
||||
# set visitors to 0
|
||||
python -u manage.py shell -c "from wol.models import Websocket; [i.delete() for i in Websocket.objects.all()]; Websocket.objects.create(visitors=0)"
|
||||
|
||||
# set notifications
|
||||
if [ -n "$ENABLE_NOTIFICATIONS" ]; then
|
||||
python -u manage.py shell -c "from wol.models import Settings; Settings.objects.update_or_create(id=1, defaults={'enable_notifications': '$ENABLE_NOTIFICATIONS'})"
|
||||
fi
|
||||
|
||||
celery -A django_wol worker &
|
||||
celery -A django_wol beat &
|
||||
gunicorn --bind 0.0.0.0:"$DJANGO_PORT" --workers 4 django_wol.asgi:application -k uvicorn.workers.UvicornWorker
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from wol.models import Device
|
||||
from wol.models import Device, Settings
|
||||
|
||||
|
||||
class DeviceAdmin(admin.ModelAdmin):
|
||||
@@ -8,4 +8,14 @@ class DeviceAdmin(admin.ModelAdmin):
|
||||
search_fields = ["name", "ip", "mac"]
|
||||
list_filter = ["name", "ip", "mac"]
|
||||
|
||||
|
||||
class SettingsAdmin(admin.ModelAdmin):
|
||||
list_display = ["enable_notifications",
|
||||
"enable_console_logging", "sort_by"]
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = "Settings"
|
||||
|
||||
|
||||
admin.site.register(Device, DeviceAdmin)
|
||||
admin.site.register(Settings, SettingsAdmin)
|
||||
|
||||
12
app/wol/forms.py
Normal file
12
app/wol/forms.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from django import forms
|
||||
|
||||
from .models import Settings
|
||||
|
||||
|
||||
class SettingsForm(forms.Form):
|
||||
class Meta:
|
||||
model = Settings
|
||||
fields = []
|
||||
notifications = forms.CharField(widget=forms.RadioSelect, label="notifications")
|
||||
console = forms.CharField(widget=forms.RadioSelect, label="console")
|
||||
sort = forms.ChoiceField(choices=[("name", "name"), ("ip", "ip")], widget=forms.RadioSelect, label="sort")
|
||||
23
app/wol/migrations/0008_settings.py
Normal file
23
app/wol/migrations/0008_settings.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 3.2.7 on 2021-09-27 16:17
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wol', '0007_device_scheduled_wake'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Settings',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('ping_interval', models.PositiveSmallIntegerField(default=5)),
|
||||
('enable_notifications', models.BooleanField(default=True)),
|
||||
('enable_console_logging', models.BooleanField(default=True)),
|
||||
('sort_by', models.SlugField(default='name')),
|
||||
],
|
||||
),
|
||||
]
|
||||
17
app/wol/migrations/0009_remove_settings_ping_interval.py
Normal file
17
app/wol/migrations/0009_remove_settings_ping_interval.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# Generated by Django 3.2.7 on 2021-09-27 19:05
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wol', '0008_settings'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='settings',
|
||||
name='ping_interval',
|
||||
),
|
||||
]
|
||||
@@ -11,3 +11,8 @@ class Device(models.Model):
|
||||
|
||||
class Websocket(models.Model):
|
||||
visitors = models.PositiveSmallIntegerField(blank=False, null=False, default=0)
|
||||
|
||||
class Settings(models.Model):
|
||||
enable_notifications = models.BooleanField(default=True)
|
||||
enable_console_logging = models.BooleanField(default=False)
|
||||
sort_by = models.SlugField(default="name")
|
||||
|
||||
@@ -71,13 +71,14 @@
|
||||
flex-shrink: unset;
|
||||
}
|
||||
|
||||
.section.settings {
|
||||
padding: 0;
|
||||
.checkbox, .radio {
|
||||
display: table-header-group;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.box {
|
||||
background-color: var(--box-bg);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.button.is-static {
|
||||
@@ -92,6 +93,10 @@
|
||||
.modal-background {
|
||||
background-color: rgba(0, 0, 0, 0.86);
|
||||
}
|
||||
|
||||
.navbar-menu.is-active {
|
||||
box-shadow: 0 5px 5px rgba(0, 0, 0, 0.2)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes green-pulse {
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 8.5 KiB |
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
|
||||
// Modals
|
||||
|
||||
var rootEl = document.documentElement;
|
||||
@@ -47,6 +48,30 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
}
|
||||
});
|
||||
|
||||
// Navbar
|
||||
|
||||
// Get all "navbar-burger" elements
|
||||
const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
|
||||
|
||||
// Check if there are any navbar burgers
|
||||
if ($navbarBurgers.length > 0) {
|
||||
|
||||
// Add a click event on each of them
|
||||
$navbarBurgers.forEach(el => {
|
||||
el.addEventListener('click', () => {
|
||||
|
||||
// Get the target from the "data-target" attribute
|
||||
const target = el.dataset.target;
|
||||
const $target = document.getElementById(target);
|
||||
|
||||
// Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
|
||||
el.classList.toggle('is-active');
|
||||
$target.classList.toggle('is-active');
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Utils
|
||||
|
||||
function getAll(selector) {
|
||||
2
app/wol/static/js/jquery-3.6.0.min.js
vendored
2
app/wol/static/js/jquery-3.6.0.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -122,7 +122,7 @@ function setDeviceUp(device) {
|
||||
var scheduleModalButton = document.getElementById(device.id + "-btn-schedule");
|
||||
|
||||
// check if device was down before
|
||||
if (statusDot.classList.contains("dot-down")) {
|
||||
if (statusDot.classList.contains("dot-down") && enableNotifications) {
|
||||
notif.show("Device now up!", device.name + " is now up.", "is-success", 5000);
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ function setDeviceDown(device) {
|
||||
var scheduleModalButton = document.getElementById(device.id + "-btn-schedule");
|
||||
|
||||
// check if device was up before
|
||||
if (statusDot.classList.contains("dot-up")) {
|
||||
if (statusDot.classList.contains("dot-up") && enableNotifications ) {
|
||||
notif.show("Device now down!", device.name + " is now down.", "is-danger", 5000);
|
||||
}
|
||||
|
||||
@@ -215,7 +215,6 @@ function addSchedule(id, name, datetime) {
|
||||
if (!(datetime)) {
|
||||
return;
|
||||
}
|
||||
console.log(datetime);
|
||||
socket.send(JSON.stringify({
|
||||
"message": "add_schedule",
|
||||
"id": id,
|
||||
@@ -239,7 +238,9 @@ function deleteSchedule(id, name) {
|
||||
var socket = new WebSocket("ws://" + location.host + "/wol/");
|
||||
socket.onmessage = function (event) {
|
||||
var message = JSON.parse(event.data);
|
||||
console.log(message);
|
||||
if (enableConsoleLogging) {
|
||||
console.log(message);
|
||||
}
|
||||
|
||||
// set devices up or down
|
||||
if ("device" in message) {
|
||||
@@ -256,35 +257,47 @@ socket.onmessage = function (event) {
|
||||
document.getElementById("visitors").innerHTML = message.visitors + ' visitor';
|
||||
} else {
|
||||
document.getElementById("visitors").innerHTML = message.visitors + ' visitors';
|
||||
notif.show("Visitors updated", "There are currently " + message.visitors + " visitors", "is-info", 5000);
|
||||
if (enableNotifications) {
|
||||
notif.show("Visitors updated", "There are currently " + message.visitors + " visitors", "is-info", 5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set wake by client
|
||||
if ("wake" in message) {
|
||||
document.getElementById(message.wake.id + "-btn-wake").classList.add("is-loading");
|
||||
notif.show("Wake started", message.wake.name + " has been started.", "is-info", 5000);
|
||||
if (enableNotifications) {
|
||||
notif.show("Wake started", message.wake.name + " has been started.", "is-info", 5000);
|
||||
}
|
||||
}
|
||||
|
||||
// set wake by schedule
|
||||
if ("wake_schedule" in message) {
|
||||
document.getElementById(message.wake_schedule.id + "-btn-wake").classList.add("is-loading");
|
||||
document.getElementById(message.wake_schedule.id + "-schedule-notice").innerHTML = "";
|
||||
notif.show("Scheduled wake started", message.wake_schedule.name + " has been started.", "is-info", 5000);
|
||||
if (enableNotifications) {
|
||||
notif.show("Scheduled wake started", message.wake_schedule.name + " has been started.", "is-info", 5000);
|
||||
}
|
||||
}
|
||||
|
||||
// add schedule
|
||||
if ("add_schedule" in message) {
|
||||
document.getElementById(message.add_schedule.id + "-schedule-notice").innerHTML = `<p>Scheduled wake set:<br>${message.add_schedule.datetime}</p>`;
|
||||
notif.show("Schedule added", "A wake up event has been scheduled for " + message.add_schedule.name, "is-info", 5000);
|
||||
if (enableNotifications) {
|
||||
notif.show("Schedule added", "A wake up event has been scheduled for " + message.add_schedule.name, "is-info", 5000);
|
||||
}
|
||||
}
|
||||
|
||||
// delete schedule
|
||||
if ("delete_schedule" in message) {
|
||||
document.getElementById(message.delete_schedule.id + "-schedule-notice").innerHTML = "";
|
||||
notif.show("Schedule deleted", "A wake up event has been deleted for " + message.delete_schedule.name, "is-info", 5000);
|
||||
if (enableNotifications) {
|
||||
notif.show("Schedule deleted", "A wake up event has been deleted for " + message.delete_schedule.name, "is-info", 5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
socket.onclose = function (event) {
|
||||
notif.show("Connection closed", "Websocket connection has closed", "is-danger", 5000);
|
||||
if (enableNotifications) {
|
||||
notif.show("Connection closed", "Websocket connection has closed", "is-danger", 5000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Wake on LAN</title>
|
||||
<title>wake on lan</title>
|
||||
<link rel="icon" type="image/png" href="{% static 'img/favicon.png' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/bulma.min.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/bulma-prefers-dark.min.css' %}">
|
||||
@@ -15,150 +15,159 @@
|
||||
<link rel="stylesheet" href="{% static 'css/bulma-notifications.css' %}">
|
||||
</head>
|
||||
|
||||
<body class="px-4">
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<div class="columns is-vcentered">
|
||||
<div class="column is-one-third">
|
||||
<h1 class="title">
|
||||
Wake on LAN
|
||||
</h1>
|
||||
<p class="subtitle">
|
||||
Wake your devices with a simple click!
|
||||
</p>
|
||||
</div>
|
||||
<div class="column is-one-third">
|
||||
<div class="field is-grouped">
|
||||
<p class="control">
|
||||
<a class="button is-light" href="/settings">
|
||||
<span>Settings</span>
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-cogs"></i>
|
||||
</span>
|
||||
</a>
|
||||
</p>
|
||||
<p class="control">
|
||||
<a class="button is-light" href="/admin">
|
||||
<span>Admin</span>
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-user-cog"></i>
|
||||
</span>
|
||||
</a>
|
||||
</p>
|
||||
<p class="control">
|
||||
<a class="button is-static is-light">
|
||||
<span id="visitors">{{ visitors }} visitors</span>
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-users"></i>
|
||||
</span>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<body>
|
||||
<nav class="navbar" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="/">
|
||||
<img class="ml-2" src="{% static 'img/favicon.png' %}" width="28" height="28">
|
||||
</a>
|
||||
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
<div id="navbarBasicExample" class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
<a class="navbar-item" href="/">
|
||||
Devices
|
||||
</a>
|
||||
<a class="navbar-item" href="/settings/">
|
||||
Settings
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div class="columns is-fullhd is-multiline">
|
||||
{% for dev in devices %}
|
||||
<div class="column is-half-fullhd is-full-widescreen is-full-desktop is-full-tablet is-full-mobile">
|
||||
<div class="box mb-3 box-waiting" id="{{ dev.id }}-container">
|
||||
<div class="columns is-centered">
|
||||
<div class="column">
|
||||
<div class="container">
|
||||
<h1 class="title">
|
||||
{{ dev.name }}
|
||||
</h1>
|
||||
<p><strong>ip</strong>: {{ dev.ip }}</p>
|
||||
<p><strong>mac</strong>: {{ dev.mac }}</p>
|
||||
<p><strong>netmask</strong>: {{ dev.netmask }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="container">
|
||||
<h1 class="title">
|
||||
Status
|
||||
</h1>
|
||||
<p id="{{ dev.id }}-status">
|
||||
<i id="{{ dev.id }}-dot" class="fas fa-circle fa-2x dot-waiting"></i>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="container">
|
||||
<h1 class="title">
|
||||
Ports
|
||||
</h1>
|
||||
<div id="{{ dev.id }}-ports">
|
||||
<ul>
|
||||
<li><i class="fas fa-circle fa-fw dot-waiting"></i> VNC (5900)</li>
|
||||
<li><i class="fas fa-circle fa-fw dot-waiting"></i> RDP (3389)</li>
|
||||
<li><i class="fas fa-circle fa-fw dot-waiting"></i> SSH (22)</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="container">
|
||||
<section class="section">
|
||||
<h1 class="title is-size-2">
|
||||
Devices <span id="visitors" class="tag is-primary is-light">{{ visitors }} visitors</span>
|
||||
</h1>
|
||||
<p class="subtitle">
|
||||
Wake on LAN
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<div class="columns is-fullhd is-multiline">
|
||||
{% for dev in devices %}
|
||||
<div class="column is-half-fullhd is-full-widescreen is-full-desktop is-full-tablet is-full-mobile">
|
||||
<div class="box mb-3 box-waiting" id="{{ dev.id }}-container">
|
||||
<div class="columns is-centered">
|
||||
<div class="column">
|
||||
<div class="container">
|
||||
<h1 class="title">
|
||||
{{ dev.name }}
|
||||
</h1>
|
||||
<p><strong>ip</strong>: {{ dev.ip }}</p>
|
||||
<p><strong>mac</strong>: {{ dev.mac }}</p>
|
||||
<p><strong>netmask</strong>: {{ dev.netmask }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="container">
|
||||
<h1 class="title">
|
||||
Actions
|
||||
</h1>
|
||||
<div class="buttons">
|
||||
<button class="button is-success" id="{{ dev.id }}-btn-wake" name="wake"
|
||||
aria-label="wake" onclick="wakeDevice({{ dev.id }})" disabled>
|
||||
<span>Wake</span>
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-play"></i>
|
||||
</span>
|
||||
</button>
|
||||
<button id="{{ dev.id }}-btn-schedule" class="button is-light modal-button" data-target="{{ dev.id }}-modal" aria-haspopup="true" disabled>
|
||||
<span class="icon">
|
||||
<i class="fas fa-clock"></i>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="{{ dev.id }}-schedule-notice">
|
||||
{% if dev.scheduled_wake %}
|
||||
<p>
|
||||
Scheduled wake set:<br>{{ dev.scheduled_wake|date:"c" }}
|
||||
<div class="column">
|
||||
<div class="container">
|
||||
<h1 class="title">
|
||||
Status
|
||||
</h1>
|
||||
<p id="{{ dev.id }}-status">
|
||||
<i id="{{ dev.id }}-dot" class="fas fa-circle fa-2x dot-waiting"></i>
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="container">
|
||||
<h1 class="title">
|
||||
Ports
|
||||
</h1>
|
||||
<div id="{{ dev.id }}-ports">
|
||||
<ul>
|
||||
<li><i class="fas fa-circle fa-fw dot-waiting"></i> VNC (5900)</li>
|
||||
<li><i class="fas fa-circle fa-fw dot-waiting"></i> RDP (3389)</li>
|
||||
<li><i class="fas fa-circle fa-fw dot-waiting"></i> SSH (22)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="container">
|
||||
<h1 class="title">
|
||||
Actions
|
||||
</h1>
|
||||
<div class="buttons">
|
||||
<button class="button is-success" id="{{ dev.id }}-btn-wake" name="wake"
|
||||
aria-label="wake" onclick="wakeDevice({{ dev.id }})" disabled>
|
||||
<span>Wake</span>
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-play"></i>
|
||||
</span>
|
||||
</button>
|
||||
<button id="{{ dev.id }}-btn-schedule" class="button is-light modal-button" data-target="{{ dev.id }}-modal" aria-haspopup="true" disabled>
|
||||
<span class="icon">
|
||||
<i class="fas fa-clock"></i>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="{{ dev.id }}-schedule-notice">
|
||||
{% if dev.scheduled_wake %}
|
||||
<p>
|
||||
Scheduled wake:<br>{{ dev.scheduled_wake|date:"c" }}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<p></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="{{ dev.id }}-modal" class="modal">
|
||||
<div class="modal-background"></div>
|
||||
<div class="modal-content">
|
||||
<div class="modal-card">
|
||||
<header class="modal-card-head">
|
||||
<p class="modal-card-title">Schedule wake up for {{ dev.name }}</p>
|
||||
<button class="delete" aria-label="close"></button>
|
||||
</header>
|
||||
<section class="modal-card-body">
|
||||
<label for="{{ dev.id }}-input">Choose date and time:</label>
|
||||
<input type="datetime-local" id="{{ dev.id }}-input" class="input">
|
||||
</section>
|
||||
<footer class="modal-card-foot">
|
||||
<button class="button is-success" id="{{ dev.id }}-btn-schedule-save"
|
||||
onclick="addSchedule({{ dev.id }}, '{{ dev.name }}', document.getElementById('{{ dev.id }}-input').value)">Save changes</button>
|
||||
<button class="button is-light-dark">Cancel</button>
|
||||
<button class="button is-danger is-pulled-right" onclick="deleteSchedule({{ dev.id }}, '{{ dev.name }}')">Delete</button>
|
||||
</footer>
|
||||
<div id="{{ dev.id }}-modal" class="modal">
|
||||
<div class="modal-background"></div>
|
||||
<div class="modal-content">
|
||||
<div class="modal-card">
|
||||
<header class="modal-card-head">
|
||||
<p class="modal-card-title">Schedule wake up for {{ dev.name }}</p>
|
||||
<button class="delete" aria-label="close"></button>
|
||||
</header>
|
||||
<section class="modal-card-body">
|
||||
<label for="{{ dev.id }}-input">Choose date and time:</label>
|
||||
<input type="datetime-local" id="{{ dev.id }}-input" class="input">
|
||||
</section>
|
||||
<footer class="modal-card-foot">
|
||||
<button class="button is-success" id="{{ dev.id }}-btn-schedule-save"
|
||||
onclick="addSchedule({{ dev.id }}, '{{ dev.name }}', document.getElementById('{{ dev.id }}-input').value)">Save changes</button>
|
||||
<button class="button is-light-dark">Cancel</button>
|
||||
<button class="button is-danger is-pulled-right" onclick="deleteSchedule({{ dev.id }}, '{{ dev.name }}')">Delete</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<button class="modal-close is-large" aria-label="close"></button>
|
||||
</div>
|
||||
<button class="modal-close is-large" aria-label="close"></button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<footer>
|
||||
<script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>
|
||||
{% if settings.enable_notifications %}
|
||||
<script>
|
||||
const enableNotifications = true
|
||||
</script>
|
||||
{% else %}
|
||||
<script>
|
||||
const enableNotifications = false
|
||||
</script>
|
||||
{% endif %}
|
||||
{% if settings.enable_console_logging %}
|
||||
<script>
|
||||
const enableConsoleLogging = true
|
||||
</script>
|
||||
{% else %}
|
||||
<script>
|
||||
const enableConsoleLogging = false
|
||||
</script>
|
||||
{% endif %}
|
||||
<script src="{% static 'js/bulma.js' %}"></script>
|
||||
<script src="{% static 'js/main.js' %}"></script>
|
||||
<script src="{% static 'js/modal.js' %}"></script>
|
||||
</footer>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -16,115 +16,203 @@
|
||||
<link rel="stylesheet" href="{% static 'css/slider.css' %}">
|
||||
</head>
|
||||
|
||||
<body class="px-4">
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<div class="columns is-vcentered">
|
||||
<div class="column is-one-third">
|
||||
<h1 class="title">
|
||||
Wake on LAN
|
||||
</h1>
|
||||
<p class="subtitle">
|
||||
Settings
|
||||
</p>
|
||||
<body>
|
||||
<nav class="navbar" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="/">
|
||||
<img class="ml-2" src="{% static 'img/favicon.png' %}" width="28" height="28">
|
||||
</a>
|
||||
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
<div id="navbarBasicExample" class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
<a class="navbar-item" href="/">
|
||||
Devices
|
||||
</a>
|
||||
<a class="navbar-item" href="/settings/">
|
||||
Settings
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container">
|
||||
<section class="section">
|
||||
<h1 class="title is-size-2">
|
||||
Settings
|
||||
</h1>
|
||||
<p class="subtitle">
|
||||
Wake on LAN
|
||||
</p>
|
||||
</section>
|
||||
<section class="section pt-4 pb-3">
|
||||
<article class="message is-info">
|
||||
<div class="message-header">
|
||||
<p>Adding devices</p>
|
||||
</div>
|
||||
<div class="column is-one-third">
|
||||
<div class="field is-grouped">
|
||||
<p class="control">
|
||||
<a class="button is-light" href="/">
|
||||
<span>Devices</span>
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-desktop"></i>
|
||||
</span>
|
||||
</a>
|
||||
</p>
|
||||
<p class="control">
|
||||
<a class="button is-light" href="/admin">
|
||||
<span>Admin</span>
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-user-cog"></i>
|
||||
</span>
|
||||
</a>
|
||||
</p>
|
||||
<p class="control">
|
||||
<a class="button is-static is-light">
|
||||
<span id="visitors">{{ visitors }} visitors</span>
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-users"></i>
|
||||
</span>
|
||||
</a>
|
||||
</p>
|
||||
<div class="message-body">
|
||||
Adding devices is (for now) only possible via the <a href="/admin">/admin</a> backend. A frontend solution will be implemented in the future.
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
<section class="section py-3">
|
||||
<form action="/save_settings/" method="post">
|
||||
{% csrf_token %}
|
||||
<div class="box">
|
||||
<h2 class="title is-size-3 mb-4">General</h2>
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-half-mobile is-one-thirds-tablet is-one-quarter-desktop is-one-fifth-widescreen">
|
||||
<span class="mr-2"><strong>Notifications:</strong></span>
|
||||
</div>
|
||||
<div class="column is-half-mobile is-two-thirds-tablet is-three-quarters-desktop is-four-fifths-widescreen">
|
||||
<div class="control">
|
||||
<label class="radio">
|
||||
<input id="noti-on" type="radio" name="notifications" value="on" {% if settings.enable_notifications %}checked{% endif %}>
|
||||
Enabled
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input id="noti-off" type="radio" name="notifications" value="off" {% if not settings.enable_notifications %}checked{% endif %}>
|
||||
Disabled
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-half-mobile is-one-thirds-tablet is-one-quarter-desktop is-one-fifth-widescreen">
|
||||
<span class="mr-2"><strong>Console logging:</strong></span>
|
||||
</div>
|
||||
<div class="column is-half-mobile is-two-thirds-tablet is-three-quarters-desktop is-four-fifths-widescreen">
|
||||
<div class="control">
|
||||
<label class="radio">
|
||||
<input id="console-on" type="radio" name="console" value="on" {% if settings.enable_console_logging %}checked{% endif %}>
|
||||
Enabled
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input id="console-off" type="radio" name="console" value="off" {% if not settings.enable_console_logging %}checked{% endif %}>
|
||||
Disabled
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-half-mobile is-one-thirds-tablet is-one-quarter-desktop is-one-fifth-widescreen">
|
||||
<span class="mr-2"><strong>Sort devices by:</strong></span>
|
||||
</div>
|
||||
<div class="column is-half-mobile is-two-thirds-tablet is-three-quarters-desktop is-four-fifths-widescreen">
|
||||
<div class="control">
|
||||
<label class="radio">
|
||||
<input id="sort-name" type="radio" name="sort" value="name" {% if settings.sort_by == "name" %}checked{% endif %}>
|
||||
Name
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input id="sort-ip" type="radio" name="sort" value="ip" {% if settings.sort_by == "ip" %}checked{% endif %}>
|
||||
IP
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="button is-primary" type="submit" value="Submit">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
<section class="section py-3">
|
||||
<div class="box">
|
||||
<h2 class="title is-size-3 mb-4">App info</h2>
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-half-mobile is-one-thirds-tablet is-one-quarter-desktop is-one-fifth-widescreen">
|
||||
<span class="mr-2"><strong>App version:</strong></span>
|
||||
</div>
|
||||
<div class="column is-half-mobile is-two-thirds-tablet is-three-quarters-desktop is-four-fifths-widescreen">
|
||||
<p>{{ app_version }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-half-mobile is-one-thirds-tablet is-one-quarter-desktop is-one-fifth-widescreen">
|
||||
<span class="mr-2"><strong>Ping interval:</strong></span>
|
||||
</div>
|
||||
<div class="column is-half-mobile is-two-thirds-tablet is-three-quarters-desktop is-four-fifths-widescreen">
|
||||
<p>{{ ping_interval }} seconds</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-half-mobile is-one-thirds-tablet is-one-quarter-desktop is-one-fifth-widescreen">
|
||||
<span class="mr-2"><strong>Devices in database:</strong></span>
|
||||
</div>
|
||||
<div class="column is-half-mobile is-two-thirds-tablet is-three-quarters-desktop is-four-fifths-widescreen">
|
||||
<p>{{ devices_count }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-half-mobile is-one-thirds-tablet is-one-quarter-desktop is-one-fifth-widescreen">
|
||||
<span class="mr-2"><strong>Current visitors:</strong></span>
|
||||
</div>
|
||||
<div class="column is-half-mobile is-two-thirds-tablet is-three-quarters-desktop is-four-fifths-widescreen">
|
||||
<p>{{ visitors }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-half-mobile is-one-thirds-tablet is-one-quarter-desktop is-one-fifth-widescreen">
|
||||
<span class="mr-2"><strong>Debug mode:</strong></span>
|
||||
</div>
|
||||
<div class="column is-half-mobile is-two-thirds-tablet is-three-quarters-desktop is-four-fifths-widescreen">
|
||||
<p>{{ debug }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div class="container">
|
||||
<div class="box">
|
||||
<section class="section settings">
|
||||
<h2 class="is-size-3 mb-4">General</h2>
|
||||
<div class="columns">
|
||||
<div class="column is-one-fifth">
|
||||
<label for="ping-interval">Ping interval:</label>
|
||||
</section>
|
||||
<section class="section py-3">
|
||||
<div class="box">
|
||||
<h2 class="title is-size-3 mb-4">System info</h2>
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-half-mobile is-one-thirds-tablet is-one-quarter-desktop is-one-fifth-widescreen">
|
||||
<span class="mr-2"><strong>System:</strong></span>
|
||||
</div>
|
||||
<div class="column">
|
||||
<input id="ping-interval" class="input is-inline is-primary w-25" type="text" placeholder="5">
|
||||
<div class="column is-half-mobile is-two-thirds-tablet is-three-quarters-desktop is-four-fifths-widescreen">
|
||||
<p>{{ platform.system }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column is-one-fifth">
|
||||
<span class="mr-2">Enable notifications:</span>
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-half-mobile is-one-thirds-tablet is-one-quarter-desktop is-one-fifth-widescreen">
|
||||
<span class="mr-2"><strong>Node:</strong></span>
|
||||
</div>
|
||||
<div class="column">
|
||||
<label class="switch">
|
||||
<input type="checkbox">
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
<div class="column is-half-mobile is-two-thirds-tablet is-three-quarters-desktop is-four-fifths-widescreen">
|
||||
<p>{{ platform.node }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column is-one-fifth">
|
||||
<span class="mr-2">Debug mode:</span>
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-half-mobile is-one-thirds-tablet is-one-quarter-desktop is-one-fifth-widescreen">
|
||||
<span class="mr-2"><strong>Release:</strong></span>
|
||||
</div>
|
||||
<div class="column">
|
||||
<label class="switch">
|
||||
<input type="checkbox">
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
<div class="column is-half-mobile is-two-thirds-tablet is-three-quarters-desktop is-four-fifths-widescreen">
|
||||
<p>{{ platform.release }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="box">
|
||||
<section class="section settings">
|
||||
<h2 class="is-size-3 mb-4">Database</h2>
|
||||
<div class="columns">
|
||||
<div class="column is-one-fifth">
|
||||
<label for="ping-interval">Ping interval:</label>
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-half-mobile is-one-thirds-tablet is-one-quarter-desktop is-one-fifth-widescreen">
|
||||
<span class="mr-2"><strong>Version:</strong></span>
|
||||
</div>
|
||||
<div class="column">
|
||||
<input id="ping-interval" class="input is-inline is-primary w-25" type="text" placeholder="5">
|
||||
<div class="column is-half-mobile is-two-thirds-tablet is-three-quarters-desktop is-four-fifths-widescreen">
|
||||
<p>{{ platform.version }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column is-one-fifth">
|
||||
<span class="mr-2">Enable notifications:</span>
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-half-mobile is-one-thirds-tablet is-one-quarter-desktop is-one-fifth-widescreen">
|
||||
<span class="mr-2"><strong>Machine:</strong></span>
|
||||
</div>
|
||||
<div class="column">
|
||||
<label class="switch">
|
||||
<input type="checkbox">
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
<div class="column is-half-mobile is-two-thirds-tablet is-three-quarters-desktop is-four-fifths-widescreen">
|
||||
<p>{{ platform.machine }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<footer>
|
||||
<script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>
|
||||
<script src="{% static 'js/modal.js' %}"></script>
|
||||
<script src="{% static 'js/bulma.js' %}"></script>
|
||||
</footer>
|
||||
|
||||
</html>
|
||||
@@ -4,5 +4,6 @@ from wol import views
|
||||
|
||||
urlpatterns = [
|
||||
path("", views.index, name="index"),
|
||||
path("settings/", views.settings, name="settings")
|
||||
path("settings/", views.settings, name="settings"),
|
||||
path("save_settings/", views.save_settings, name="save_settings")
|
||||
]
|
||||
|
||||
@@ -1,23 +1,57 @@
|
||||
from django.shortcuts import render
|
||||
import os
|
||||
import platform
|
||||
|
||||
from wol.models import Device, Websocket
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import render
|
||||
from django_wol.settings import VERSION as app_version
|
||||
|
||||
from .forms import SettingsForm
|
||||
from .models import Device, Settings, Websocket
|
||||
|
||||
|
||||
def index(request):
|
||||
devices = Device.objects.all().order_by("name")
|
||||
conf = Settings.objects.first()
|
||||
devices = Device.objects.all().order_by(conf.sort_by)
|
||||
visitors = Websocket.objects.first().visitors
|
||||
|
||||
context = {
|
||||
"devices": devices,
|
||||
"visitors": visitors
|
||||
"visitors": visitors,
|
||||
"settings": conf
|
||||
}
|
||||
|
||||
return render(request, "wol/index.html", context)
|
||||
|
||||
|
||||
def settings(request):
|
||||
|
||||
conf = Settings.objects.first()
|
||||
devices_count = Device.objects.all().count()
|
||||
visitors = Websocket.objects.first().visitors
|
||||
|
||||
|
||||
context = {
|
||||
"test": "ja lol ey"
|
||||
"settings": conf,
|
||||
"ping_interval": os.getenv("PING_INTERVAL"),
|
||||
"devices_count": devices_count,
|
||||
"platform": platform.uname(),
|
||||
"app_version": app_version,
|
||||
"debug": os.getenv("DJANGO_DEBUG"),
|
||||
"visitors": visitors
|
||||
}
|
||||
|
||||
return render(request, "wol/settings.html", context)
|
||||
return render(request, "wol/settings.html", context)
|
||||
|
||||
|
||||
def save_settings(request):
|
||||
if request.method == "POST":
|
||||
form = SettingsForm(request.POST)
|
||||
if form.is_valid():
|
||||
Settings.objects.update_or_create(
|
||||
id = 1,
|
||||
defaults = {
|
||||
"enable_notifications": True if form.cleaned_data["notifications"] == "on" else False,
|
||||
"enable_console_logging": True if form.cleaned_data["console"] == "on" else False,
|
||||
"sort_by": form.cleaned_data["sort"]
|
||||
}
|
||||
)
|
||||
return HttpResponseRedirect('/settings/')
|
||||
|
||||
@@ -2,7 +2,8 @@ version: "3"
|
||||
services:
|
||||
wol_django:
|
||||
container_name: wol_django
|
||||
image: seriousm4x/django-wol:latest
|
||||
#image: seriousm4x/django-wol:latest
|
||||
build: .
|
||||
network_mode: host
|
||||
depends_on:
|
||||
- wol_redis
|
||||
@@ -16,6 +17,8 @@ services:
|
||||
- DJANGO_LANGUAGE_CODE=de
|
||||
- DJANGO_TIME_ZONE=Europe/Berlin
|
||||
- DJANGO_PORT=8000
|
||||
- PING_INTERVAL=5
|
||||
- ENABLE_NOTIFICATIONS=True
|
||||
wol_redis:
|
||||
container_name: wol_redis
|
||||
image: redis:6
|
||||
|
||||
Reference in New Issue
Block a user