mirror of
https://github.com/seriousm4x/UpSnap.git
synced 2026-07-25 06:52:48 -04:00
rework objects/arrays, better modal, remove drf
This commit is contained in:
@@ -39,8 +39,7 @@ INSTALLED_APPS = [
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'wol',
|
||||
'channels',
|
||||
'rest_framework'
|
||||
'channels'
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
||||
@@ -14,9 +14,8 @@ Including another URLconf
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
from django.urls import path
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('', include('wol.urls')),
|
||||
path('admin/', admin.site.urls)
|
||||
]
|
||||
|
||||
@@ -16,9 +16,10 @@ class WSConsumer(AsyncWebsocketConsumer):
|
||||
await self.accept()
|
||||
await self.send(text_data=json.dumps({
|
||||
"type": "init",
|
||||
"message": await self.get_device_count()
|
||||
"message": await self.get_all_devices()
|
||||
}))
|
||||
await self.add_visitor()
|
||||
|
||||
await self.channel_layer.group_send(
|
||||
"wol", {
|
||||
"type": "send_group",
|
||||
@@ -36,7 +37,8 @@ class WSConsumer(AsyncWebsocketConsumer):
|
||||
"wol", {
|
||||
"type": "send_group",
|
||||
"message": {
|
||||
"visitors": await self.get_visitors()
|
||||
"type": "visitor",
|
||||
"message": await self.get_visitors()
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -89,13 +91,7 @@ class WSConsumer(AsyncWebsocketConsumer):
|
||||
}
|
||||
}
|
||||
)
|
||||
elif received["type"] == "get_ports":
|
||||
ports = await self.get_ports()
|
||||
await self.send(text_data=json.dumps({
|
||||
"type": "get_ports",
|
||||
"message": ports
|
||||
}))
|
||||
elif received["type"] == "delete":
|
||||
elif received["type"] == "delete_device":
|
||||
await self.delete_device(received["id"])
|
||||
await self.channel_layer.group_send(
|
||||
"wol", {
|
||||
@@ -106,8 +102,10 @@ class WSConsumer(AsyncWebsocketConsumer):
|
||||
}
|
||||
}
|
||||
)
|
||||
elif received["type"] == "update":
|
||||
elif received["type"] == "update_device":
|
||||
await self.update_device(received["data"])
|
||||
elif received["type"] == "update_port":
|
||||
await self.update_port(received["data"])
|
||||
|
||||
|
||||
async def send_group(self, event):
|
||||
@@ -130,9 +128,27 @@ class WSConsumer(AsyncWebsocketConsumer):
|
||||
return Websocket.objects.first().visitors
|
||||
|
||||
@database_sync_to_async
|
||||
def get_device_count(self):
|
||||
count = Device.objects.count()
|
||||
return count
|
||||
def get_all_devices(self):
|
||||
devices = Device.objects.all()
|
||||
d = []
|
||||
for dev in devices:
|
||||
data = {
|
||||
"id": dev.id,
|
||||
"name": dev.name,
|
||||
"ip": dev.ip,
|
||||
"mac": dev.mac,
|
||||
"netmask": dev.netmask,
|
||||
"ports": []
|
||||
}
|
||||
for p in Port.objects.all().order_by("number"):
|
||||
data["ports"].append({
|
||||
"number": p.number,
|
||||
"name": p.name,
|
||||
"checked": False,
|
||||
"open": False
|
||||
})
|
||||
d.append(data)
|
||||
return d
|
||||
|
||||
@database_sync_to_async
|
||||
def delete_device(self, id):
|
||||
@@ -150,14 +166,25 @@ class WSConsumer(AsyncWebsocketConsumer):
|
||||
"netmask": data["netmask"]
|
||||
}
|
||||
)
|
||||
for _, value in data["ports"].items():
|
||||
if value["checked"]:
|
||||
p, _ = Port.objects.get_or_create(number=value["number"], name=value["name"])
|
||||
for port in data["ports"]:
|
||||
if port["checked"]:
|
||||
p, _ = Port.objects.get_or_create(number=port["number"], name=port["name"])
|
||||
obj.port.add(p)
|
||||
else:
|
||||
p = Port.objects.get(number=value["number"])
|
||||
p = Port.objects.get(number=port["number"])
|
||||
obj.port.remove(p)
|
||||
|
||||
@database_sync_to_async
|
||||
def update_port(self, data):
|
||||
if data.get("name"):
|
||||
Port.objects.update_or_create(
|
||||
number=data["number"],
|
||||
defaults={
|
||||
"name": data["name"]
|
||||
}
|
||||
)
|
||||
else:
|
||||
Port.objects.filter(number=data["number"]).delete()
|
||||
|
||||
@database_sync_to_async
|
||||
def get_json_from_device_id(self, id):
|
||||
@@ -175,8 +202,3 @@ class WSConsumer(AsyncWebsocketConsumer):
|
||||
dev = Device.objects.filter(id=id).get()
|
||||
dev.scheduled_wake = None
|
||||
dev.save()
|
||||
|
||||
@database_sync_to_async
|
||||
def get_ports(self):
|
||||
ports = Port.objects.all().order_by("name")
|
||||
return serializers.serialize("python", ports)
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
from rest_framework import serializers, viewsets
|
||||
from wol.models import Settings, Device, Port
|
||||
|
||||
class SettingsSerializer(serializers.HyperlinkedModelSerializer):
|
||||
class Meta:
|
||||
model = Settings
|
||||
fields = "__all__"
|
||||
|
||||
class SettingsViewSet(viewsets.ModelViewSet):
|
||||
def get_queryset(self):
|
||||
queryset = Settings.objects.first()
|
||||
return queryset
|
||||
serializer_class = SettingsSerializer
|
||||
|
||||
class PortSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Port
|
||||
fields = ["number", "name"]
|
||||
|
||||
class DeviceSerializer(serializers.ModelSerializer):
|
||||
port = PortSerializer(many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Device
|
||||
fields = ["name", "ip", "mac", "netmask", "scheduled_wake", "port"]
|
||||
@@ -31,33 +31,32 @@ class WolDevice:
|
||||
|
||||
def start(self, dev):
|
||||
data = {
|
||||
"device": {
|
||||
"id": dev.id,
|
||||
"name": dev.name,
|
||||
"ip": dev.ip,
|
||||
"mac": dev.mac,
|
||||
"netmask": dev.netmask,
|
||||
"up": False,
|
||||
"ports": {}
|
||||
}
|
||||
"id": dev.id,
|
||||
"name": dev.name,
|
||||
"ip": dev.ip,
|
||||
"mac": dev.mac,
|
||||
"netmask": dev.netmask,
|
||||
"up": False,
|
||||
"ports": []
|
||||
}
|
||||
for p in Port.objects.all():
|
||||
data["device"]["ports"][str(p.number)] = {
|
||||
for p in Port.objects.all().order_by("number"):
|
||||
data["ports"].append({
|
||||
"number": p.number,
|
||||
"name": p.name,
|
||||
"checked": False,
|
||||
"checked": True if p in dev.port.all() else False,
|
||||
"open": False
|
||||
}
|
||||
})
|
||||
|
||||
if self.ping_device(dev.ip):
|
||||
data["device"]["up"] = True
|
||||
data["up"] = True
|
||||
for port in dev.port.all():
|
||||
index = next(i for i, d in enumerate(data["ports"]) if d["number"] == port.number)
|
||||
if self.check_port(dev.ip, port.number):
|
||||
data["device"]["ports"][str(port.number)]["checked"] = True
|
||||
data["device"]["ports"][str(port.number)]["open"] = True
|
||||
data["ports"][index]["checked"] = True
|
||||
data["ports"][index]["open"] = True
|
||||
else:
|
||||
data["device"]["ports"][str(port.number)]["checked"] = True
|
||||
data["device"]["ports"][str(port.number)]["open"] = False
|
||||
data["ports"][index]["checked"] = True
|
||||
data["ports"][index]["open"] = False
|
||||
|
||||
async_to_sync(channel_layer.group_send)(
|
||||
"wol", {"type": "send_group", "message": {
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
from django.urls import path, include
|
||||
from rest_framework import routers
|
||||
from wol.serializers import SettingsViewSet
|
||||
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
router.register(r'settings', SettingsViewSet, basename="settings")
|
||||
|
||||
urlpatterns = [
|
||||
path('api/', include(router.urls)),
|
||||
]
|
||||
@@ -1,3 +0,0 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
@@ -5,7 +5,7 @@
|
||||
"scripts": {
|
||||
"build": "rollup -c",
|
||||
"dev": "rollup -c -w",
|
||||
"start": "sirv public --no-clear"
|
||||
"start": "sirv public --no-clear --host"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^17.0.0",
|
||||
|
||||
@@ -3,35 +3,36 @@
|
||||
import store from './store.js';
|
||||
import Navbar from "./components/Navbar.svelte";
|
||||
import DeviceCard from "./components/DeviceCard.svelte"
|
||||
import DummyDeviceCard from "./components/DummyDeviceCard.svelte"
|
||||
|
||||
let visitors = 0;
|
||||
let devices = [];
|
||||
let dummyDevices = 0;
|
||||
let ports = [];
|
||||
|
||||
onMount(() => {
|
||||
store.subscribe(currentMessage => {
|
||||
if (currentMessage.type == "init") {
|
||||
// create placeholder devices
|
||||
dummyDevices = currentMessage.message;
|
||||
for (let index = 0; index < currentMessage.message.length; index++) {
|
||||
const element = currentMessage.message[index];
|
||||
devices.push(element);
|
||||
devices = devices;
|
||||
devices.sort(compare)
|
||||
}
|
||||
} else if (currentMessage.type == "status") {
|
||||
// set device array and sort
|
||||
dummyDevices -= 1;
|
||||
const index = devices.findIndex(x => x.id == currentMessage.message.device.id);
|
||||
const index = devices.findIndex(x => x.id == currentMessage.message.id);
|
||||
if (devices.length === 0 || index === -1) {
|
||||
devices.push(currentMessage.message.device);
|
||||
devices.push(currentMessage.message);
|
||||
devices = devices;
|
||||
} else {
|
||||
devices[index] = currentMessage.message.device;
|
||||
devices[index] = currentMessage.message;
|
||||
}
|
||||
devices.sort(compare)
|
||||
|
||||
// set device status
|
||||
if (currentMessage.message.device.up == true) {
|
||||
setUp(currentMessage.message.device.id);
|
||||
if (currentMessage.message.up == true) {
|
||||
setUp(currentMessage.message.id);
|
||||
} else {
|
||||
setDown(currentMessage.message.device.id);
|
||||
setDown(currentMessage.message.id);
|
||||
}
|
||||
} else if (currentMessage.type == "wake") {
|
||||
// set device waking
|
||||
@@ -40,9 +41,6 @@
|
||||
} else if (currentMessage.type == "visitor") {
|
||||
// update visitor count
|
||||
visitors = currentMessage.message;
|
||||
} else if (currentMessage.type == "get_ports") {
|
||||
// all available ports in db
|
||||
ports = [...currentMessage.message];
|
||||
} else if (currentMessage.type == "delete") {
|
||||
// delete device
|
||||
const devCol = document.querySelector(`#device-col-${currentMessage.message}`);
|
||||
@@ -115,15 +113,10 @@
|
||||
|
||||
<main>
|
||||
<Navbar visitors={visitors} />
|
||||
<div class="container">
|
||||
<div class="container mb-3">
|
||||
<div class="row">
|
||||
{#if dummyDevices >= 1}
|
||||
{#each Array(dummyDevices) as _, i}
|
||||
<DummyDeviceCard/>
|
||||
{/each}
|
||||
{/if}
|
||||
{#each devices as device}
|
||||
<DeviceCard device={device} ports={ports}/>
|
||||
<DeviceCard device={device}/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script>
|
||||
import store from '../store.js';
|
||||
export let device;
|
||||
export let ports;
|
||||
|
||||
let modalDevice = JSON.parse(JSON.stringify(device));
|
||||
let customPort = {}
|
||||
|
||||
function wake(id) {
|
||||
store.sendMessage({
|
||||
@@ -12,28 +12,60 @@
|
||||
})
|
||||
}
|
||||
|
||||
function openModal() {
|
||||
modalDevice = Object.assign({}, device)
|
||||
|
||||
function deleteDevice(id) {
|
||||
store.sendMessage({
|
||||
type: "get_ports"
|
||||
})
|
||||
}
|
||||
|
||||
function del(id) {
|
||||
store.sendMessage({
|
||||
type: "delete",
|
||||
type: "delete_device",
|
||||
id: id
|
||||
})
|
||||
}
|
||||
|
||||
function update(data) {
|
||||
function updateDevice(data) {
|
||||
device = data;
|
||||
store.sendMessage({
|
||||
type: "update",
|
||||
type: "update_device",
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
function updatePort() {
|
||||
if (!customPort.number) {
|
||||
return
|
||||
}
|
||||
const index = modalDevice.ports.findIndex(x => x.number == customPort.number);
|
||||
if (customPort.name) {
|
||||
// add port
|
||||
if (index === -1) {
|
||||
customPort.checked = true;
|
||||
modalDevice.ports.push(customPort);
|
||||
} else {
|
||||
customPort.checked = modalDevice.ports[index].checked;
|
||||
modalDevice.ports[index] = JSON.parse(JSON.stringify(customPort));
|
||||
}
|
||||
} else {
|
||||
// delete port
|
||||
if (index >= 0) {
|
||||
modalDevice.ports.splice(index, 1)
|
||||
}
|
||||
}
|
||||
modalDevice = modalDevice;
|
||||
// send to backend
|
||||
store.sendMessage({
|
||||
type: "update_port",
|
||||
data: customPort
|
||||
})
|
||||
}
|
||||
|
||||
function openModal() {
|
||||
modalDevice = Object.assign({}, device)
|
||||
}
|
||||
|
||||
function validatePort() {
|
||||
if (typeof customPort.number != "number") {
|
||||
customPort.number = 1
|
||||
} else if (customPort.number > 65535) {
|
||||
customPort.number = 65535
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="device-col-{device.id}" class="col-xs-12 col-sm-6 col-md-4 col-lg-3 g-4">
|
||||
@@ -43,7 +75,11 @@
|
||||
<div class="col-auto me-auto">
|
||||
<div id="spinner-{device.id}" class="spinner-border color-warning d-none" role="status"></div>
|
||||
<div class="hover" on:click="{() => wake(device.id)}" role="button">
|
||||
<i id="dot-{device.id}" class="fa-solid fa-power-off fa-2x {device.up ? 'success' : 'danger'}"></i>
|
||||
{#if device.up === true || device.up === false}
|
||||
<i id="dot-{device.id}" class="fa-solid fa-power-off fa-2x {device.up ? 'success' : 'danger'}"></i>
|
||||
{:else}
|
||||
<i id="dot-{device.id}" class="fa-solid fa-power-off fa-2x text-muted"></i>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto hover" data-bs-toggle="modal" data-bs-target="#device-modal-{device.id}" role="button" on:click="{() => openModal()}">
|
||||
@@ -53,7 +89,7 @@
|
||||
<h5 class="card-title fw-bold my-2">{device.name}</h5>
|
||||
<h6 class="card-subtitle mb-2 text-muted">{device.ip}</h6>
|
||||
<ul class="list-group">
|
||||
{#each Object.entries(device.ports) as [_, port]}
|
||||
{#each device.ports as port}
|
||||
{#if port.checked === true}
|
||||
<li class="list-group-item">
|
||||
<i class="fa-solid fa-circle align-middle {port.open ? 'success' : 'danger'}"></i>
|
||||
@@ -66,7 +102,7 @@
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="device-modal-{device.id}" tabindex="-1" aria-labelledby="device-modal-{device.id}-label" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-dialog modal-dialog-scrollable">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title fw-bold" id="device-modal-{modalDevice.id}-label">{modalDevice.name}</h5>
|
||||
@@ -75,9 +111,19 @@
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
<h5 class="fw-bold">General</h5>
|
||||
<div class="mb-3">
|
||||
<label for="inputName{modalDevice.id}" class="form-label">Device name</label>
|
||||
<input type="text" class="form-control" id="inputName{modalDevice.id}" bind:value="{modalDevice.name}">
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
<div class="mb-3">
|
||||
<label for="inputName{modalDevice.id}" class="form-label">Device name</label>
|
||||
<input type="text" class="form-control" id="inputName{modalDevice.id}" bind:value="{modalDevice.name}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<div class="mb-3">
|
||||
<label for="inputMac{modalDevice.id}" class="form-label">Mac address</label>
|
||||
<input type="text" class="form-control" id="inputMac{modalDevice.mac}" bind:value="{modalDevice.mac}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
@@ -94,17 +140,25 @@
|
||||
</div>
|
||||
</div>
|
||||
<h5 class="fw-bold">Ports</h5>
|
||||
{#each ports as port}
|
||||
{#each modalDevice.ports as port}
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" id="{device.id}-port-{port.fields.name}" bind:checked="{modalDevice.ports[port.fields.number]['checked']}" >
|
||||
<label class="form-check-label" for="{device.id}-port-{port.fields.name}">{port.fields.name}</label>
|
||||
<input type="checkbox" class="form-check-input" id="{device.id}-port-{port.name}" bind:checked="{port['checked']}">
|
||||
<label class="form-check-label" for="{device.id}-port-{port.name}">{port.name} <span class="text-muted">({port.number})</span></label>
|
||||
</div>
|
||||
{/each}
|
||||
<label class="form-label mt-3" for="{device.id}-custom-port">Custom port</label>
|
||||
<div class="input-group mb-2">
|
||||
<input type="text" id="{device.id}-custom-port" class="form-control" placeholder="Name" aria-label="Name" aria-describedby="button-addon2" bind:value={customPort.name} required>
|
||||
<input type="number" min="1" max="65535" class="form-control" placeholder="Port" aria-label="Port" aria-describedby="button-addon2" bind:value={customPort.number} on:input={validatePort} required>
|
||||
<button class="btn btn-outline-secondary" type="button" id="button-addon2" on:click="{updatePort}">Update Port</button>
|
||||
</div>
|
||||
<p>Port must be between 1 and 65535. Enter the same port with a differen name to change it. Leave name empty to delete port.</p>
|
||||
<h5 class="fw-bold">Scheduled wake</h5>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer justify-content-between">
|
||||
<button type="button" class="btn btn-outline-danger" data-bs-dismiss="modal" on:click="{() => del(modalDevice.id)}">Delete</button>
|
||||
<button type="button" class="btn btn-outline-success" on:click="{() => update(modalDevice)}">Save changes</button>
|
||||
<button type="button" class="btn btn-outline-danger" data-bs-dismiss="modal" on:click="{() => deleteDevice(modalDevice.id)}">Delete</button>
|
||||
<button type="button" class="btn btn-outline-success" data-bs-dismiss="modal" on:click="{() => updateDevice(modalDevice)}">Save changes</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-3 g-4">
|
||||
<div class="card border-0 p-3 pt-2" >
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-auto me-auto">
|
||||
<div>
|
||||
<i class="fa-solid fa-power-off fa-2x"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div>
|
||||
<i class="fa-solid fa-ellipsis-vertical fa-2x"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h5 class="card-title fw-bold my-2 placeholder-glow">
|
||||
<span class="placeholder col-7"></span>
|
||||
</h5>
|
||||
<h6 class="card-subtitle mb-2 text-muted placeholder-glow">
|
||||
<span class="placeholder col-4"></span>
|
||||
</h6>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../variables.scss";
|
||||
|
||||
.card {
|
||||
border-radius: 2em;
|
||||
}
|
||||
|
||||
.fa-2x {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.fa-circle {
|
||||
font-size: 0.8em;
|
||||
color: $success;
|
||||
}
|
||||
</style>
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
</script>
|
||||
|
||||
<nav class="navbar navbar-expand-lg bg-light">
|
||||
<nav class="navbar navbar-expand-sm bg-light">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="/">
|
||||
<img src="/favicon.png" alt="Logo" width="24" height="24">
|
||||
|
||||
@@ -2,7 +2,6 @@ celery[redis]
|
||||
channels
|
||||
channels-redis
|
||||
django
|
||||
django-rest-framework
|
||||
gunicorn
|
||||
mysqlclient
|
||||
psycopg2-binary
|
||||
|
||||
Reference in New Issue
Block a user