mirror of
https://github.com/MarginaliaSearch/MarginaliaSearch.git
synced 2025-02-23 13:09:00 +00:00
(assistant) Migrate to Jooby framework
This commit is contained in:
parent
f076d05595
commit
f553701224
@ -15,6 +15,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
import org.slf4j.Marker;
|
import org.slf4j.Marker;
|
||||||
import org.slf4j.MarkerFactory;
|
import org.slf4j.MarkerFactory;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -106,9 +107,12 @@ public class JoobyService {
|
|||||||
config.externalAddress());
|
config.externalAddress());
|
||||||
|
|
||||||
// FIXME: This won't work outside of docker, may need to submit a PR to jooby to allow classpaths here
|
// FIXME: This won't work outside of docker, may need to submit a PR to jooby to allow classpaths here
|
||||||
jooby.install(new JteModule(Path.of("/app/resources/jte"), Path.of("/app/classes/jte-precompiled")));
|
if (Files.exists(Path.of("/app/resources/jte")) || Files.exists(Path.of("/app/classes/jte-precompiled"))) {
|
||||||
jooby.assets("/*", Paths.get("/app/resources/static"));
|
jooby.install(new JteModule(Path.of("/app/resources/jte"), Path.of("/app/classes/jte-precompiled")));
|
||||||
|
}
|
||||||
|
if (Files.exists(Path.of("/app/resources/static"))) {
|
||||||
|
jooby.assets("/*", Paths.get("/app/resources/static"));
|
||||||
|
}
|
||||||
var options = new ServerOptions();
|
var options = new ServerOptions();
|
||||||
options.setHost(config.bindAddress());
|
options.setHost(config.bindAddress());
|
||||||
options.setPort(restEndpoint.port());
|
options.setPort(restEndpoint.port());
|
||||||
|
@ -37,8 +37,6 @@ dependencies {
|
|||||||
implementation project(':code:functions:domain-info')
|
implementation project(':code:functions:domain-info')
|
||||||
implementation project(':code:functions:domain-info:api')
|
implementation project(':code:functions:domain-info:api')
|
||||||
|
|
||||||
implementation project(':code:features-search:screenshots')
|
|
||||||
|
|
||||||
implementation project(':code:libraries:geo-ip')
|
implementation project(':code:libraries:geo-ip')
|
||||||
implementation project(':code:libraries:language-processing')
|
implementation project(':code:libraries:language-processing')
|
||||||
implementation project(':code:libraries:term-frequency-dict')
|
implementation project(':code:libraries:term-frequency-dict')
|
||||||
@ -48,6 +46,7 @@ dependencies {
|
|||||||
|
|
||||||
implementation libs.bundles.slf4j
|
implementation libs.bundles.slf4j
|
||||||
implementation libs.prometheus
|
implementation libs.prometheus
|
||||||
|
implementation libs.commons.io
|
||||||
implementation libs.guava
|
implementation libs.guava
|
||||||
libs.bundles.grpc.get().each {
|
libs.bundles.grpc.get().each {
|
||||||
implementation dependencies.create(it) {
|
implementation dependencies.create(it) {
|
||||||
@ -61,9 +60,7 @@ dependencies {
|
|||||||
implementation dependencies.create(libs.guice.get()) {
|
implementation dependencies.create(libs.guice.get()) {
|
||||||
exclude group: 'com.google.guava'
|
exclude group: 'com.google.guava'
|
||||||
}
|
}
|
||||||
implementation dependencies.create(libs.spark.get()) {
|
implementation libs.bundles.jooby
|
||||||
exclude group: 'org.eclipse.jetty'
|
|
||||||
}
|
|
||||||
implementation libs.bundles.jetty
|
implementation libs.bundles.jetty
|
||||||
implementation libs.opencsv
|
implementation libs.opencsv
|
||||||
implementation libs.trove
|
implementation libs.trove
|
||||||
|
@ -3,6 +3,8 @@ package nu.marginalia.assistant;
|
|||||||
import com.google.inject.Guice;
|
import com.google.inject.Guice;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Injector;
|
import com.google.inject.Injector;
|
||||||
|
import io.jooby.ExecutionMode;
|
||||||
|
import io.jooby.Jooby;
|
||||||
import nu.marginalia.livecapture.LivecaptureModule;
|
import nu.marginalia.livecapture.LivecaptureModule;
|
||||||
import nu.marginalia.service.MainClass;
|
import nu.marginalia.service.MainClass;
|
||||||
import nu.marginalia.service.ServiceId;
|
import nu.marginalia.service.ServiceId;
|
||||||
@ -38,8 +40,17 @@ public class AssistantMain extends MainClass {
|
|||||||
var configuration = injector.getInstance(ServiceConfiguration.class);
|
var configuration = injector.getInstance(ServiceConfiguration.class);
|
||||||
orchestrateBoot(registry, configuration);
|
orchestrateBoot(registry, configuration);
|
||||||
|
|
||||||
injector.getInstance(AssistantMain.class);
|
var main = injector.getInstance(AssistantMain.class);
|
||||||
injector.getInstance(Initialization.class).setReady();
|
injector.getInstance(Initialization.class).setReady();
|
||||||
|
|
||||||
|
Jooby.runApp(new String[] { "application.env=prod" }, ExecutionMode.WORKER, () -> new Jooby() {
|
||||||
|
{
|
||||||
|
main.start(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start(Jooby jooby) {
|
||||||
|
service.startJooby(jooby);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,27 +2,27 @@ package nu.marginalia.assistant;
|
|||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
import io.jooby.Context;
|
||||||
|
import io.jooby.Jooby;
|
||||||
import nu.marginalia.assistant.suggest.Suggestions;
|
import nu.marginalia.assistant.suggest.Suggestions;
|
||||||
import nu.marginalia.functions.domains.DomainInfoGrpcService;
|
import nu.marginalia.functions.domains.DomainInfoGrpcService;
|
||||||
import nu.marginalia.functions.math.MathGrpcService;
|
import nu.marginalia.functions.math.MathGrpcService;
|
||||||
import nu.marginalia.livecapture.LiveCaptureGrpcService;
|
import nu.marginalia.livecapture.LiveCaptureGrpcService;
|
||||||
import nu.marginalia.model.gson.GsonFactory;
|
import nu.marginalia.model.gson.GsonFactory;
|
||||||
import nu.marginalia.rss.svc.FeedsGrpcService;
|
import nu.marginalia.rss.svc.FeedsGrpcService;
|
||||||
import nu.marginalia.screenshot.ScreenshotService;
|
|
||||||
import nu.marginalia.service.discovery.property.ServicePartition;
|
import nu.marginalia.service.discovery.property.ServicePartition;
|
||||||
import nu.marginalia.service.server.BaseServiceParams;
|
import nu.marginalia.service.server.BaseServiceParams;
|
||||||
import nu.marginalia.service.server.SparkService;
|
import nu.marginalia.service.server.JoobyService;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import spark.Request;
|
|
||||||
import spark.Response;
|
|
||||||
import spark.Spark;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class AssistantService extends SparkService {
|
public class AssistantService extends JoobyService {
|
||||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||||
private final Gson gson = GsonFactory.get();
|
private final Gson gson = GsonFactory.get();
|
||||||
|
@org.jetbrains.annotations.NotNull
|
||||||
|
private final ScreenshotService screenshotService;
|
||||||
private final Suggestions suggestions;
|
private final Suggestions suggestions;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@ -39,30 +39,30 @@ public class AssistantService extends SparkService {
|
|||||||
List.of(domainInfoGrpcService,
|
List.of(domainInfoGrpcService,
|
||||||
mathGrpcService,
|
mathGrpcService,
|
||||||
liveCaptureGrpcService,
|
liveCaptureGrpcService,
|
||||||
feedsGrpcService));
|
feedsGrpcService),
|
||||||
|
List.of());
|
||||||
|
this.screenshotService = screenshotService;
|
||||||
|
|
||||||
this.suggestions = suggestions;
|
this.suggestions = suggestions;
|
||||||
|
|
||||||
Spark.staticFiles.expireTime(600);
|
|
||||||
|
|
||||||
Spark.get("/screenshot/:id", screenshotService::serveScreenshotRequest);
|
|
||||||
Spark.get("/suggest/", this::getSuggestions, this::convertToJson);
|
|
||||||
|
|
||||||
Spark.awaitInitialization();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object getSuggestions(Request request, Response response) {
|
public void startJooby(Jooby jooby) {
|
||||||
response.type("application/json");
|
super.startJooby(jooby);
|
||||||
var param = request.queryParams("partial");
|
|
||||||
if (param == null) {
|
jooby.get("/suggest", this::getSuggestions);
|
||||||
|
jooby.get("/screenshot/{id}", screenshotService::serveScreenshotRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSuggestions(Context context) {
|
||||||
|
context.setResponseType("application/json");
|
||||||
|
var param = context.query("partial");
|
||||||
|
if (param.isMissing()) {
|
||||||
logger.warn("Bad parameter, partial is null");
|
logger.warn("Bad parameter, partial is null");
|
||||||
Spark.halt(500);
|
context.setResponseCode(500);
|
||||||
|
return "{}";
|
||||||
}
|
}
|
||||||
return suggestions.getSuggestions(10, param);
|
return gson.toJson(suggestions.getSuggestions(10, param.value()));
|
||||||
}
|
|
||||||
|
|
||||||
private String convertToJson(Object o) {
|
|
||||||
return gson.toJson(o);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,116 @@
|
|||||||
|
package nu.marginalia.assistant;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.zaxxer.hikari.HikariDataSource;
|
||||||
|
import io.jooby.Context;
|
||||||
|
import nu.marginalia.db.DbDomainQueries;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
public class ScreenshotService {
|
||||||
|
|
||||||
|
private final DbDomainQueries domainQueries;
|
||||||
|
private final HikariDataSource dataSource;
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ScreenshotService(DbDomainQueries dbDomainQueries, HikariDataSource dataSource) {
|
||||||
|
this.domainQueries = dbDomainQueries;
|
||||||
|
this.dataSource = dataSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasScreenshot(int domainId) {
|
||||||
|
try (var conn = dataSource.getConnection();
|
||||||
|
var ps = conn.prepareStatement("""
|
||||||
|
SELECT TRUE
|
||||||
|
FROM DATA_DOMAIN_SCREENSHOT
|
||||||
|
INNER JOIN EC_DOMAIN ON EC_DOMAIN.DOMAIN_NAME=DATA_DOMAIN_SCREENSHOT.DOMAIN_NAME
|
||||||
|
WHERE EC_DOMAIN.ID=?
|
||||||
|
""")) {
|
||||||
|
ps.setInt(1, domainId);
|
||||||
|
var rs = ps.executeQuery();
|
||||||
|
if (rs.next()) {
|
||||||
|
return rs.getBoolean(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (SQLException ex) {
|
||||||
|
logger.warn("SQL error", ex);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object serveScreenshotRequest(Context context) {
|
||||||
|
if (Strings.isNullOrEmpty(context.path("id").value(""))) {
|
||||||
|
context.setResponseCode(404);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
int id = context.path("id").intValue();
|
||||||
|
|
||||||
|
try (var conn = dataSource.getConnection();
|
||||||
|
var ps = conn.prepareStatement("""
|
||||||
|
SELECT CONTENT_TYPE, DATA
|
||||||
|
FROM DATA_DOMAIN_SCREENSHOT
|
||||||
|
INNER JOIN EC_DOMAIN ON EC_DOMAIN.DOMAIN_NAME=DATA_DOMAIN_SCREENSHOT.DOMAIN_NAME
|
||||||
|
WHERE EC_DOMAIN.ID=?
|
||||||
|
""")) {
|
||||||
|
ps.setInt(1, id);
|
||||||
|
var rsp = ps.executeQuery();
|
||||||
|
if (rsp.next()) {
|
||||||
|
context.setResponseType(rsp.getString(1));
|
||||||
|
context.setResponseCode(200);
|
||||||
|
context.setResponseHeader("Cache-control", "public,max-age=3600");
|
||||||
|
|
||||||
|
IOUtils.copy(rsp.getBlob(2).getBinaryStream(), context.responseStream());
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
logger.warn("IO error", ex);
|
||||||
|
}
|
||||||
|
catch (SQLException ex) {
|
||||||
|
logger.warn("SQL error", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.setResponseType("image/svg+xml");
|
||||||
|
|
||||||
|
var name = domainQueries.getDomain(id).map(Object::toString)
|
||||||
|
.orElse("[Screenshot Not Yet Captured]");
|
||||||
|
|
||||||
|
return """
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="640px"
|
||||||
|
height="480px"
|
||||||
|
viewBox="0 0 640 480"
|
||||||
|
version="1.1">
|
||||||
|
<g>
|
||||||
|
<rect
|
||||||
|
style="fill:#808080"
|
||||||
|
id="rect288"
|
||||||
|
width="595.41992"
|
||||||
|
height="430.01825"
|
||||||
|
x="23.034981"
|
||||||
|
y="27.850344" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:100px;fill:#909090;font-family:sans-serif;"
|
||||||
|
x="20"
|
||||||
|
y="120">Placeholder</text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;fill:#000000;font-family:monospace;"
|
||||||
|
x="320" y="240" dominant-baseline="middle" text-anchor="middle">%s</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
""".formatted(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user