(control) New list and form for index nodes.

This commit is contained in:
Viktor Lofgren 2023-10-14 21:46:52 +02:00
parent 108b4cb648
commit eacbf87979
4 changed files with 96 additions and 5 deletions

View File

@ -215,10 +215,10 @@ public class FileStorageService {
return null;
}
public FileStorageBase createStorageBase(String name, Path path, FileStorageBaseType type) throws SQLException, FileNotFoundException {
return createStorageBase(name, path, node, type);
}
if (!Files.exists(path)) {
throw new FileNotFoundException("Storage base path does not exist: " + path);
}
public FileStorageBase createStorageBase(String name, Path path, int node, FileStorageBaseType type) throws SQLException, FileNotFoundException {
try (var conn = dataSource.getConnection();
var stmt = conn.prepareStatement("""
@ -238,7 +238,6 @@ public class FileStorageService {
return getStorageBase(type);
}
@SneakyThrows
private Path allocateDirectory(Path basePath, String prefix) throws IOException {
LocalDateTime now = LocalDateTime.now();

View File

@ -25,6 +25,7 @@ import spark.Request;
import spark.Response;
import spark.Spark;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@ -64,6 +65,7 @@ public class ControlNodeService {
}
public void register() throws IOException {
var nodeListRenderer = rendererFactory.renderer("control/node/nodes-list");
var overviewRenderer = rendererFactory.renderer("control/node/node-overview");
var actionsRenderer = rendererFactory.renderer("control/node/node-actions");
var actorsRenderer = rendererFactory.renderer("control/node/node-actors");
@ -74,6 +76,8 @@ public class ControlNodeService {
var newSpecsFormRenderer = rendererFactory.renderer("control/node/node-new-specs-form");
Spark.get("/public/nodes", this::nodeListModel, nodeListRenderer::render);
Spark.post("/public/nodes", this::createNode);
Spark.get("/public/nodes/:id", this::nodeOverviewModel, overviewRenderer::render);
Spark.get("/public/nodes/:id/", this::nodeOverviewModel, overviewRenderer::render);
Spark.get("/public/nodes/:id/actors", this::nodeActorsModel, actorsRenderer::render);
@ -102,6 +106,27 @@ public class ControlNodeService {
}
private Object createNode(Request request, Response response) throws SQLException, FileNotFoundException {
var newConfig = nodeConfigurationService.create(request.queryParams("description"), "on".equalsIgnoreCase(request.queryParams("acceptQueries")));
int id = newConfig.node();
fileStorageService.createStorageBase("Index Data", Path.of("/idx"), id, FileStorageBaseType.CURRENT);
fileStorageService.createStorageBase("Index Backups", Path.of("/backup"), id, FileStorageBaseType.BACKUP);
fileStorageService.createStorageBase("Crawl Data", Path.of("/storage"), id, FileStorageBaseType.STORAGE);
fileStorageService.createStorageBase("Work Area", Path.of("/work"), id, FileStorageBaseType.WORK);
return redirectToOverview(id);
}
private Object nodeListModel(Request request, Response response) throws SQLException {
var configs = nodeConfigurationService.getAll();
int nextId = configs.stream().mapToInt(NodeConfiguration::node).map(i -> i+1).max().orElse(1);
return Map.of("nodes", nodeConfigurationService.getAll(),
"nextNodeId", nextId);
}
private Object triggerCrawl(Request request, Response response) throws Exception {
int nodeId = Integer.parseInt(request.params("id"));
@ -117,10 +142,14 @@ public class ControlNodeService {
return redirectToOverview(request);
}
@SneakyThrows
public String redirectToOverview(int nodeId) {
return new Redirects.HtmlRedirect("/nodes/"+nodeId).render(null);
}
@SneakyThrows
public String redirectToOverview(Request request) {
return new Redirects.HtmlRedirect("/nodes/"+request.params("id")).render(null);
return redirectToOverview(Integer.parseInt(request.params("id")));
}
private Object createNewSpecsAction(Request request, Response response) {

View File

@ -0,0 +1,62 @@
<!doctype html>
<html>
{{> control/partials/head-includes }}
<head><title>Control Service</title></head>
<body>
{{> control/partials/nav}}
<div class="container">
<h1 class="my-5">Index Nodes</h1>
{{#unless nodes}}
It appears no nodes have been configured! This is necessary before any index or executor services
can be started. At least a single node needs to be configured to serve search queries.
{{/unless}}
{{#if nodes}}
<table class="table">
<tr>
<th>Node ID</th>
<th>Description</th>
<th>Accept Queries</th>
</tr>
{{#each nodes}}
<tr>
<td><a href="/nodes/{{node}}">node-{{node}}</a></td>
<td>{{description}}</td>
<td>{{acceptQueries}}</td>
</tr>
{{/each}}
{{/if}}
</table>
<div class="m-3 p-3 border">
<h2>Add Node</h2>
<form method="post">
<div class="mb-3">
<label for="name" class="form-label">ID</label>
<input class="form-control" type="text" name="id" id="id" value="{{nextNodeId}}" disabled aria-disabled="true" />
</div>
<div class="mb-3">
<label for="name" class="form-label">Description</label>
<input class="form-control" type="text" name="description" id="description" />
</div>
<div class="form-check form-switch mb-3">
<input class="form-check-input" type="checkbox" role="switch" name="acceptQueries">
<label class="form-check-label" for="acceptQueries">Accept queries</label>
<div class="form-text">Sets whether queries will be routed to this node. This can be modified later.</div>
</div>
<button type="submit" class="btn btn-primary">Create</button>
</form>
</div>
</table>
</div>
</body>
{{> control/partials/foot-includes }}
</html>

View File

@ -21,6 +21,7 @@
<li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" data-bs-toggle="dropdown" role="button" aria-expanded="false">System</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="/nodes" title="View and configure index nodes">Nodes</a></li>
<li><a class="dropdown-item" href="/events" title="View the event log">Events</a></li>
<li><a class="dropdown-item" href="/message-queue" title="View or manipulate the system message queue">Message Queue</a></li>
</ul>