add nodes list
- add NodeController - add getNodes to DeviceRegistryService - add WebClient deviceRegistryNodes to DeviceRegisterWebClientCalls.kt - add node list.html - add node to main menu and cleanup in layout.html - add node to AdminPaths.kt
This commit is contained in:
37
src/main/kotlin/ltd/hlaeja/controller/NodeController.kt
Normal file
37
src/main/kotlin/ltd/hlaeja/controller/NodeController.kt
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package ltd.hlaeja.controller
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.Max
|
||||||
|
import jakarta.validation.constraints.Min
|
||||||
|
import ltd.hlaeja.service.DeviceRegistryService
|
||||||
|
import ltd.hlaeja.util.Pagination
|
||||||
|
import ltd.hlaeja.util.Pagination.Companion.DEFAULT_PAGE
|
||||||
|
import ltd.hlaeja.util.Pagination.Companion.DEFAULT_SIZE
|
||||||
|
import ltd.hlaeja.util.Pagination.Companion.MAX
|
||||||
|
import ltd.hlaeja.util.Pagination.Companion.MIN
|
||||||
|
import org.springframework.stereotype.Controller
|
||||||
|
import org.springframework.ui.Model
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
|
import reactor.core.publisher.Mono
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
class NodeController(
|
||||||
|
private val deviceRegistryService: DeviceRegistryService,
|
||||||
|
) {
|
||||||
|
@GetMapping(
|
||||||
|
"/node",
|
||||||
|
"/node/page-{page}",
|
||||||
|
"/node/page-{page}/show-{show}",
|
||||||
|
)
|
||||||
|
fun getNodes(
|
||||||
|
@PathVariable(required = false) @Min(MIN) page: Int = DEFAULT_PAGE,
|
||||||
|
@PathVariable(required = false) @Min(MIN) @Max(MAX) show: Int = DEFAULT_SIZE,
|
||||||
|
model: Model,
|
||||||
|
) = deviceRegistryService.getNodes(page, show)
|
||||||
|
.collectList()
|
||||||
|
.doOnNext { items ->
|
||||||
|
model.addAttribute("items", items)
|
||||||
|
model.addAttribute("pagination", Pagination(page, show, items.size, DEFAULT_SIZE))
|
||||||
|
}
|
||||||
|
.then(Mono.just("node/list"))
|
||||||
|
}
|
||||||
@@ -6,4 +6,5 @@ fun AuthorizeExchangeSpec.adminPaths(): AuthorizeExchangeSpec.Access = pathMatch
|
|||||||
"/account/**",
|
"/account/**",
|
||||||
"/type/**",
|
"/type/**",
|
||||||
"/device/**",
|
"/device/**",
|
||||||
|
"/node/**",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ package ltd.hlaeja.service
|
|||||||
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import ltd.hlaeja.library.deviceRegistry.Devices
|
import ltd.hlaeja.library.deviceRegistry.Devices
|
||||||
|
import ltd.hlaeja.library.deviceRegistry.Nodes
|
||||||
import ltd.hlaeja.library.deviceRegistry.Type
|
import ltd.hlaeja.library.deviceRegistry.Type
|
||||||
import ltd.hlaeja.library.deviceRegistry.Types
|
import ltd.hlaeja.library.deviceRegistry.Types
|
||||||
import ltd.hlaeja.property.DeviceRegistryProperty
|
import ltd.hlaeja.property.DeviceRegistryProperty
|
||||||
import ltd.hlaeja.util.deviceRegistryDevices
|
import ltd.hlaeja.util.deviceRegistryDevices
|
||||||
|
import ltd.hlaeja.util.deviceRegistryNodes
|
||||||
import ltd.hlaeja.util.deviceRegistryType
|
import ltd.hlaeja.util.deviceRegistryType
|
||||||
import ltd.hlaeja.util.deviceRegistryTypes
|
import ltd.hlaeja.util.deviceRegistryTypes
|
||||||
import ltd.hlaeja.util.deviceRegistryTypesCreate
|
import ltd.hlaeja.util.deviceRegistryTypesCreate
|
||||||
@@ -48,4 +50,9 @@ class DeviceRegistryService(
|
|||||||
page: Int,
|
page: Int,
|
||||||
show: Int,
|
show: Int,
|
||||||
): Flux<Devices.Response> = webClient.deviceRegistryDevices(page, show, property)
|
): Flux<Devices.Response> = webClient.deviceRegistryDevices(page, show, property)
|
||||||
|
|
||||||
|
fun getNodes(
|
||||||
|
page: Int,
|
||||||
|
show: Int,
|
||||||
|
): Flux<Nodes.Response> = webClient.deviceRegistryNodes(page, show, property)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import ltd.hlaeja.exception.NoChangeException
|
|||||||
import ltd.hlaeja.exception.NotFoundException
|
import ltd.hlaeja.exception.NotFoundException
|
||||||
import ltd.hlaeja.exception.TypeNameDuplicateException
|
import ltd.hlaeja.exception.TypeNameDuplicateException
|
||||||
import ltd.hlaeja.library.deviceRegistry.Devices
|
import ltd.hlaeja.library.deviceRegistry.Devices
|
||||||
|
import ltd.hlaeja.library.deviceRegistry.Nodes
|
||||||
import ltd.hlaeja.library.deviceRegistry.Type
|
import ltd.hlaeja.library.deviceRegistry.Type
|
||||||
import ltd.hlaeja.library.deviceRegistry.Types
|
import ltd.hlaeja.library.deviceRegistry.Types
|
||||||
import ltd.hlaeja.property.DeviceRegistryProperty
|
import ltd.hlaeja.property.DeviceRegistryProperty
|
||||||
@@ -69,3 +70,12 @@ fun WebClient.deviceRegistryDevices(
|
|||||||
.uri("${property.url}/devices/page-$page/show-$size".also(::logCall))
|
.uri("${property.url}/devices/page-$page/show-$size".also(::logCall))
|
||||||
.retrieve()
|
.retrieve()
|
||||||
.bodyToFlux(Devices.Response::class.java)
|
.bodyToFlux(Devices.Response::class.java)
|
||||||
|
|
||||||
|
fun WebClient.deviceRegistryNodes(
|
||||||
|
page: Int,
|
||||||
|
size: Int,
|
||||||
|
property: DeviceRegistryProperty,
|
||||||
|
): Flux<Nodes.Response> = get()
|
||||||
|
.uri("${property.url}/nodes/page-$page/show-$size".also(::logCall))
|
||||||
|
.retrieve()
|
||||||
|
.bodyToFlux(Nodes.Response::class.java)
|
||||||
|
|||||||
@@ -33,13 +33,14 @@
|
|||||||
<div id="dropdown-menu" class="hidden absolute right-0 mt-2 w-48 bg-gray-800 border border-green-900 shadow-lg z-10">
|
<div id="dropdown-menu" class="hidden absolute right-0 mt-2 w-48 bg-gray-800 border border-green-900 shadow-lg z-10">
|
||||||
<th:block th:if="${remoteUser.hasRole('admin')}">
|
<th:block th:if="${remoteUser.hasRole('admin')}">
|
||||||
<a href="/account" class="block px-4 py-2 text-sm hover:bg-gray-700 hover:text-green-300 transition-colors">Account</a>
|
<a href="/account" class="block px-4 py-2 text-sm hover:bg-gray-700 hover:text-green-300 transition-colors">Account</a>
|
||||||
<a href="/type" class="block px-4 py-2 text-sm hover:bg-gray-700 hover:text-green-300 transition-colors">Device Type</a>
|
<hr class="dropdown-divider">
|
||||||
|
<a href="/type" class="block px-4 py-2 text-sm hover:bg-gray-700 hover:text-green-300 transition-colors">Type</a>
|
||||||
<a href="/device" class="block px-4 py-2 text-sm hover:bg-gray-700 hover:text-green-300 transition-colors">Device</a>
|
<a href="/device" class="block px-4 py-2 text-sm hover:bg-gray-700 hover:text-green-300 transition-colors">Device</a>
|
||||||
<a href="#" class="block px-4 py-2 text-sm hover:bg-gray-700 hover:text-green-300 transition-colors">$ Device</a>
|
<a href="/node" class="block px-4 py-2 text-sm hover:bg-gray-700 hover:text-green-300 transition-colors">Node</a>
|
||||||
<hr class="dropdown-divider">
|
<hr class="dropdown-divider">
|
||||||
</th:block>
|
</th:block>
|
||||||
<th:block th:if="${remoteUser.authenticated}">
|
<th:block th:if="${remoteUser.authenticated}">
|
||||||
<a href="#" class="block px-4 py-2 text-sm hover:bg-gray-700 hover:text-green-300 transition-colors">$ Node</a>
|
<a href="#" class="block px-4 py-2 text-sm hover:bg-gray-700 hover:text-green-300 transition-colors">$ User Node</a>
|
||||||
<hr class="dropdown-divider">
|
<hr class="dropdown-divider">
|
||||||
</th:block>
|
</th:block>
|
||||||
<th:block th:if="${remoteUser.authenticated}">
|
<th:block th:if="${remoteUser.authenticated}">
|
||||||
|
|||||||
57
src/main/resources/templates/node/list.html
Normal file
57
src/main/resources/templates/node/list.html
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head>
|
||||||
|
<title>Hlæja Management</title>
|
||||||
|
<th:block th:replace="~{layout.html :: metadata}"/>
|
||||||
|
</head>
|
||||||
|
<body class="bg-gray-900 text-green-400 min-h-screen flex flex-col">
|
||||||
|
<th:block th:replace="~{layout.html :: header}"/>
|
||||||
|
<main class="container mx-auto p-4 flex-grow">
|
||||||
|
<div class="bg-gray-800 p-6 rounded-lg border border-green-900">
|
||||||
|
<h1 class="text-lg sm:text-xl mb-4 terminal-glow">Nodes</h1>
|
||||||
|
<hr class="border-green-900 mb-4">
|
||||||
|
<div class="flex justify-between items-center mb-4">
|
||||||
|
<div th:if="${pagination.start > pagination.size}" class="text-sm">
|
||||||
|
Show page <span th:text="${pagination.page}"/> items 0 - 0
|
||||||
|
</div>
|
||||||
|
<div th:unless="${pagination.start > pagination.size}" class="text-sm">
|
||||||
|
Show page <span th:text="${pagination.page}"/>
|
||||||
|
items <span th:text="${pagination.start}"/> -
|
||||||
|
<span th:text="${pagination.size}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="overflow-x-auto">
|
||||||
|
<table class="w-full text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr class="border-b border-green-900">
|
||||||
|
<th class="py-2 px-4 text-left">Name</th>
|
||||||
|
<th class="py-2 px-4 text-left">Time</th>
|
||||||
|
<th class="py-2 px-4 text-left">ID <span>▲</span></th>
|
||||||
|
<th class="py-2 px-4 text-left">Client ID</th>
|
||||||
|
<th class="py-2 px-4 text-left">Device ID</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr th:if="${items.isEmpty()}">
|
||||||
|
<td colspan="4" class="py-2 px-4 text-center text-green-600">No nodes found</td>
|
||||||
|
</tr>
|
||||||
|
<tr th:each="item : ${items}" class="border-b border-gray-700 hover:bg-gray-700">
|
||||||
|
<td th:text="${item.name}" class="py-2 px-4"></td>
|
||||||
|
<td th:data-timestamp="${item.timestamp}" class="py-2 px-4 utcTimestamp"></td>
|
||||||
|
<td th:text="${item.id}" class="py-2 px-4"></td>
|
||||||
|
<td th:text="${item.client}" class="py-2 px-4"></td>
|
||||||
|
<td th:text="${item.device}" class="py-2 px-4"></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<th:block th:replace="~{pagination :: pagination('/node', ${pagination})}"/>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<th:block th:replace="~{layout.html :: footer}"/>
|
||||||
|
<th:block th:replace="~{layout.html :: script}"/>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', () => makeLocalTime(document.querySelectorAll('.utcTimestamp')));
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user