mirror of
https://github.com/MarginaliaSearch/MarginaliaSearch.git
synced 2025-02-24 05:18:58 +00:00
(control) GUI for triggering control-side actors
This commit is contained in:
parent
4155fbe94c
commit
67a1e1c874
@ -3,9 +3,9 @@ package nu.marginalia.control;
|
|||||||
import spark.ResponseTransformer;
|
import spark.ResponseTransformer;
|
||||||
|
|
||||||
public class Redirects {
|
public class Redirects {
|
||||||
public static final HtmlRedirect redirectToActors = new HtmlRedirect("/actors");
|
|
||||||
public static final HtmlRedirect redirectToApiKeys = new HtmlRedirect("/api-keys");
|
public static final HtmlRedirect redirectToApiKeys = new HtmlRedirect("/api-keys");
|
||||||
public static final HtmlRedirect redirectToStorage = new HtmlRedirect("/storage");
|
public static final HtmlRedirect redirectToStorage = new HtmlRedirect("/storage");
|
||||||
|
public static final HtmlRedirect redirectToOverview = new HtmlRedirect("/");
|
||||||
public static final HtmlRedirect redirectToBlacklist = new HtmlRedirect("/blacklist");
|
public static final HtmlRedirect redirectToBlacklist = new HtmlRedirect("/blacklist");
|
||||||
public static final HtmlRedirect redirectToComplaints = new HtmlRedirect("/complaints");
|
public static final HtmlRedirect redirectToComplaints = new HtmlRedirect("/complaints");
|
||||||
public static final HtmlRedirect redirectToMessageQueue = new HtmlRedirect("/message-queue");
|
public static final HtmlRedirect redirectToMessageQueue = new HtmlRedirect("/message-queue");
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
package nu.marginalia.control.sys.svc;
|
package nu.marginalia.control.sys.svc;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
import nu.marginalia.client.Context;
|
import nu.marginalia.client.Context;
|
||||||
import nu.marginalia.control.Redirects;
|
import nu.marginalia.control.Redirects;
|
||||||
|
import nu.marginalia.control.actor.ControlActor;
|
||||||
|
import nu.marginalia.control.actor.ControlActorService;
|
||||||
import nu.marginalia.db.DomainTypes;
|
import nu.marginalia.db.DomainTypes;
|
||||||
import nu.marginalia.executor.client.ExecutorClient;
|
import nu.marginalia.executor.client.ExecutorClient;
|
||||||
import nu.marginalia.mq.MessageQueueFactory;
|
import nu.marginalia.mq.MessageQueueFactory;
|
||||||
import nu.marginalia.mq.outbox.MqOutbox;
|
import nu.marginalia.mq.outbox.MqOutbox;
|
||||||
|
import nu.marginalia.renderer.RendererFactory;
|
||||||
import nu.marginalia.service.control.ServiceEventLog;
|
import nu.marginalia.service.control.ServiceEventLog;
|
||||||
import nu.marginalia.service.id.ServiceId;
|
import nu.marginalia.service.id.ServiceId;
|
||||||
import spark.Request;
|
import spark.Request;
|
||||||
@ -19,13 +23,23 @@ public class ControlSysActionsService {
|
|||||||
private final MqOutbox apiOutbox;
|
private final MqOutbox apiOutbox;
|
||||||
private final DomainTypes domainTypes;
|
private final DomainTypes domainTypes;
|
||||||
private final ServiceEventLog eventLog;
|
private final ServiceEventLog eventLog;
|
||||||
|
private final RendererFactory rendererFactory;
|
||||||
|
private final ControlActorService controlActorService;
|
||||||
private final ExecutorClient executorClient;
|
private final ExecutorClient executorClient;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ControlSysActionsService(MessageQueueFactory mqFactory, DomainTypes domainTypes, ServiceEventLog eventLog, ExecutorClient executorClient) {
|
public ControlSysActionsService(MessageQueueFactory mqFactory,
|
||||||
|
DomainTypes domainTypes,
|
||||||
|
ServiceEventLog eventLog,
|
||||||
|
RendererFactory rendererFactory,
|
||||||
|
ControlActorService controlActorService,
|
||||||
|
ExecutorClient executorClient)
|
||||||
|
{
|
||||||
this.apiOutbox = createApiOutbox(mqFactory);
|
this.apiOutbox = createApiOutbox(mqFactory);
|
||||||
this.eventLog = eventLog;
|
this.eventLog = eventLog;
|
||||||
this.domainTypes = domainTypes;
|
this.domainTypes = domainTypes;
|
||||||
|
this.rendererFactory = rendererFactory;
|
||||||
|
this.controlActorService = controlActorService;
|
||||||
this.executorClient = executorClient;
|
this.executorClient = executorClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,12 +52,17 @@ public class ControlSysActionsService {
|
|||||||
return mqFactory.createOutbox(inboxName, 0, outboxName, 0, UUID.randomUUID());
|
return mqFactory.createOutbox(inboxName, 0, outboxName, 0, UUID.randomUUID());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
public void register() {
|
public void register() {
|
||||||
Spark.post("/public/actions/flush-api-caches", this::flushApiCaches, Redirects.redirectToActors);
|
var actionsView = rendererFactory.renderer("control/sys/sys-actions");
|
||||||
Spark.post("/public/actions/reload-blogs-list", this::reloadBlogsList, Redirects.redirectToActors);
|
|
||||||
Spark.post("/public/actions/calculate-adjacencies", this::calculateAdjacencies, Redirects.redirectToActors);
|
Spark.get("/public/actions", (rq,rsp) -> new Object(), actionsView::render);
|
||||||
Spark.post("/public/actions/truncate-links-database", this::truncateLinkDatabase, Redirects.redirectToActors);
|
Spark.post("/public/actions/recalculate-adjacencies-graph", this::calculateAdjacencies, Redirects.redirectToOverview);
|
||||||
Spark.post("/public/actions/trigger-data-exports", this::triggerDataExports, Redirects.redirectToActors);
|
Spark.post("/public/actions/reindex-all", this::reindexAll, Redirects.redirectToOverview);
|
||||||
|
Spark.post("/public/actions/reprocess-all", this::reprocessAll, Redirects.redirectToOverview);
|
||||||
|
Spark.post("/public/actions/flush-api-caches", this::flushApiCaches, Redirects.redirectToOverview);
|
||||||
|
Spark.post("/public/actions/reload-blogs-list", this::reloadBlogsList, Redirects.redirectToOverview);
|
||||||
|
Spark.post("/public/actions/trigger-data-exports", this::triggerDataExports, Redirects.redirectToOverview);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object triggerDataExports(Request request, Response response) throws Exception {
|
public Object triggerDataExports(Request request, Response response) throws Exception {
|
||||||
@ -54,23 +73,6 @@ public class ControlSysActionsService {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object truncateLinkDatabase(Request request, Response response) throws Exception {
|
|
||||||
|
|
||||||
String footgunLicense = request.queryParams("footgun-license");
|
|
||||||
|
|
||||||
if (!"YES".equals(footgunLicense)) {
|
|
||||||
Spark.halt(403);
|
|
||||||
return "You must agree to the footgun license to truncate the link database";
|
|
||||||
}
|
|
||||||
|
|
||||||
eventLog.logEvent("USER-ACTION", "FLUSH-LINK-DATABASE");
|
|
||||||
|
|
||||||
// FIXME:
|
|
||||||
// actors.start(Actor.TRUNCATE_LINK_DATABASE);
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object reloadBlogsList(Request request, Response response) throws Exception {
|
public Object reloadBlogsList(Request request, Response response) throws Exception {
|
||||||
eventLog.logEvent("USER-ACTION", "RELOAD-BLOGS-LIST");
|
eventLog.logEvent("USER-ACTION", "RELOAD-BLOGS-LIST");
|
||||||
|
|
||||||
@ -89,10 +91,26 @@ public class ControlSysActionsService {
|
|||||||
public Object calculateAdjacencies(Request request, Response response) throws Exception {
|
public Object calculateAdjacencies(Request request, Response response) throws Exception {
|
||||||
eventLog.logEvent("USER-ACTION", "CALCULATE-ADJACENCIES");
|
eventLog.logEvent("USER-ACTION", "CALCULATE-ADJACENCIES");
|
||||||
|
|
||||||
// This is technically not a partitioned operation, but we execute it at node zero
|
// This is technically not a partitioned operation, but we execute it at node 1
|
||||||
// and let the effects be global :-)
|
// and let the effects be global :-)
|
||||||
|
|
||||||
executorClient.calculateAdjacencies(Context.fromRequest(request), 0);
|
executorClient.calculateAdjacencies(Context.fromRequest(request), 1);
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object reindexAll(Request request, Response response) throws Exception {
|
||||||
|
eventLog.logEvent("USER-ACTION", "REINDEX-ALL");
|
||||||
|
|
||||||
|
controlActorService.start(ControlActor.REINDEX_ALL);
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object reprocessAll(Request request, Response response) throws Exception {
|
||||||
|
eventLog.logEvent("USER-ACTION", "REPROCESS-ALL");
|
||||||
|
|
||||||
|
controlActorService.start(ControlActor.REPROCESS_ALL);
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,11 @@
|
|||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
|
||||||
{{#if nodes}}
|
{{#if nodes}}
|
||||||
|
<div class="my-3 p-3 border bg-light">
|
||||||
|
Index nodes are processing units. The search engine requires at least one, but more can be added
|
||||||
|
to spread the system load across multiple physical disks or even multiple servers.
|
||||||
|
</div>
|
||||||
|
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Node ID</th>
|
<th>Node ID</th>
|
||||||
@ -28,14 +33,6 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div class="m-5 p-5 border bg-light">
|
|
||||||
<h2 class="my-3">Index Nodes</h2>
|
|
||||||
<p>
|
|
||||||
Index nodes are processing units. The search engine requires at least one, but more can be added
|
|
||||||
to spread the system load across multiple physical disks or even multiple servers.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a href="#" class="nav-link dropdown-toggle" data-bs-toggle="dropdown" role="button" aria-expanded="false">System</a>
|
<a href="#" class="nav-link dropdown-toggle" data-bs-toggle="dropdown" role="button" aria-expanded="false">System</a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
|
<li><a class="dropdown-item" href="/actions" title="System actions">Actions</a></li>
|
||||||
<li><a class="dropdown-item" href="/nodes" title="View and configure index nodes">Nodes</a></li>
|
<li><a class="dropdown-item" href="/nodes" title="View and configure index nodes">Nodes</a></li>
|
||||||
<li><a class="dropdown-item" href="/datasets" title="View and update the data sets">Datasets</a></li>
|
<li><a class="dropdown-item" href="/datasets" title="View and update the data sets">Datasets</a></li>
|
||||||
<li><a class="dropdown-item" href="/events" title="View the event log">Events</a></li>
|
<li><a class="dropdown-item" href="/events" title="View the event log">Events</a></li>
|
||||||
|
@ -0,0 +1,102 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
{{> control/partials/head-includes }}
|
||||||
|
<head><title>Control Service: Actions</title></head>
|
||||||
|
<body>
|
||||||
|
{{> control/partials/nav}}
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<h1 class="my-3">System Actions</h1>
|
||||||
|
|
||||||
|
<div class="my-3 border p-3 bg-light">
|
||||||
|
These are actions that perform system-wide operations,
|
||||||
|
on each node in the system. Nodes are included if they are
|
||||||
|
configured to be part of the <em>precession</em> in the Node Configuration.
|
||||||
|
For a single-node system, most of these actions may be triggered from the
|
||||||
|
single node's actions tab.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="accordion mt-3" id="accordionActions">
|
||||||
|
|
||||||
|
<div class="accordion-item">
|
||||||
|
<h2 class="accordion-header">
|
||||||
|
<button class="accordion-button collapsed"
|
||||||
|
type="button"
|
||||||
|
data-bs-toggle="collapse"
|
||||||
|
data-bs-target="#collapseCalculateAdjacencies"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-controls="collapseCalculateAdjacencies">
|
||||||
|
Calculate Adjacencies
|
||||||
|
</button>
|
||||||
|
</h2>
|
||||||
|
<div id="collapseCalculateAdjacencies" class="accordion-collapse collapse p-3" data-bs-parent="#accordionActions">
|
||||||
|
<div class="mb-3">
|
||||||
|
This will re-calculate the adjacencies graph. The adjacencies graph is used to calculate the domain ranking.
|
||||||
|
</div>
|
||||||
|
<form method="post" action="actions/recalculate-adjacencies-graph">
|
||||||
|
<button
|
||||||
|
class="btn btn-primary me-md-2"
|
||||||
|
onclick="return confirm('Confirm recalculation of adjacencies graph');"
|
||||||
|
type="submit">
|
||||||
|
Recalculate Graph</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="accordion-item">
|
||||||
|
<h2 class="accordion-header">
|
||||||
|
<button class="accordion-button collapsed"
|
||||||
|
type="button"
|
||||||
|
data-bs-toggle="collapse"
|
||||||
|
data-bs-target="#collapseReindexAll"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-controls="collapseReindexAll">
|
||||||
|
Recalculate Rankings
|
||||||
|
</button>
|
||||||
|
</h2>
|
||||||
|
<div id="collapseReindexAll" class="accordion-collapse collapse p-3" data-bs-parent="#accordionActions">
|
||||||
|
<div class="mb-3">
|
||||||
|
This will successively re-calculate the domain rankings for all nodes configured to be part in the precession,
|
||||||
|
and then re-construct the indexes from existing journal data.
|
||||||
|
</div>
|
||||||
|
<form method="post" action="actions/reindex-all">
|
||||||
|
<button
|
||||||
|
class="btn btn-primary me-md-2"
|
||||||
|
onclick="return confirm('Confirm reindexing of all nodes');"
|
||||||
|
type="submit">
|
||||||
|
Reconstruct Indexes</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="accordion-item">
|
||||||
|
<h2 class="accordion-header">
|
||||||
|
<button class="accordion-button collapsed"
|
||||||
|
type="button"
|
||||||
|
data-bs-toggle="collapse"
|
||||||
|
data-bs-target="#collapseReprocessAll"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-controls="collapseReprocessAll">
|
||||||
|
Reprocess Data
|
||||||
|
</button>
|
||||||
|
</h2>
|
||||||
|
<div id="collapseReprocessAll" class="accordion-collapse collapse p-3" data-bs-parent="#accordionActions">
|
||||||
|
<div class="mb-3">
|
||||||
|
This will reprocess the crawl data on each node configured to be part in the precession, based on
|
||||||
|
the currently ACTIVE crawl data. If no crawl data is ACTIVE, the node will be omitted.
|
||||||
|
</div>
|
||||||
|
<form method="post" action="actions/reprocess-all">
|
||||||
|
<button
|
||||||
|
class="btn btn-primary me-md-2"
|
||||||
|
onclick="return confirm('Confirm reprocessing');"
|
||||||
|
type="submit">
|
||||||
|
Reprocess Crawl Data</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
{{> control/partials/foot-includes }}
|
||||||
|
</html>
|
@ -69,7 +69,7 @@ public class ProcessingService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Object startAdjacencyCalculation(Request request, Response response) throws Exception {
|
public Object startAdjacencyCalculation(Request request, Response response) throws Exception {
|
||||||
actorControlService.start(ExecutorActor.ADJACENCY_CALCULATION);
|
actorControlService.startFrom(ExecutorActor.ADJACENCY_CALCULATION, new TriggerAdjacencyCalculationActor.Run());
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user