2022-10-22 21:36:37 +02:00
|
|
|
package io.gitlab.jfronny.gitea.helpdesk.web;
|
|
|
|
|
|
|
|
import io.gitlab.jfronny.commons.StringFormatter;
|
|
|
|
import io.gitlab.jfronny.gitea.helpdesk.Config;
|
|
|
|
import io.gitlab.jfronny.gitea.helpdesk.Main;
|
|
|
|
import io.gitlab.jfronny.gitea.helpdesk.db.DBInterface;
|
|
|
|
import io.gitlab.jfronny.gitea.helpdesk.gitea.GiteaInterface;
|
|
|
|
import net.freeutils.httpserver.HTTPServer;
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.UncheckedIOException;
|
|
|
|
import java.net.URISyntaxException;
|
|
|
|
import java.nio.charset.StandardCharsets;
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
public class WebInterface implements AutoCloseable {
|
2022-10-22 23:29:07 +02:00
|
|
|
public static final String THEME = Main.getResource("/web/theme.css");
|
|
|
|
private static final String TEMPLATE = Main.getResource("/web/template.html");
|
|
|
|
private static final String ISSUE_MISSING_REPO = Main.getResource("/web/issue/missing_repo.html");
|
|
|
|
private static final String ISSUE_SUCCESS = Main.getResource("/web/issue/success.html");
|
2022-10-25 17:39:13 +02:00
|
|
|
private static final String REPLY_MISSING_REPO = Main.getResource("/web/reply/missing_repo.html");
|
|
|
|
private static final String REPLY_SUCCESS = Main.getResource("/web/reply/success.html");
|
2022-10-22 23:29:07 +02:00
|
|
|
private static final String UNSUBSCRIBE_MISSING_ID = Main.getResource("/web/unsubscribe/missing_id.html");
|
|
|
|
private static final String UNSUBSCRIBE_FAILURE = Main.getResource("/web/unsubscribe/failure.html");
|
|
|
|
private static final String UNSUBSCRIBE_SUCCESS = Main.getResource("/web/unsubscribe/success.html");
|
|
|
|
private static final String CLOSE_MISSING_ID = Main.getResource("/web/close/missing_id.html");
|
|
|
|
private static final String CLOSE_FAILURE = Main.getResource("/web/close/failure.html");
|
|
|
|
private static final String CLOSE_NOT_CREATOR = Main.getResource("/web/close/not_creator.html");
|
|
|
|
private static final String CLOSE_SUCCESS = Main.getResource("/web/close/success.html");
|
|
|
|
|
|
|
|
private String template(String body) {
|
|
|
|
return TEMPLATE.replace("<%template-content%>", body);
|
2022-10-22 21:36:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private final HTTPServer server;
|
|
|
|
private final Config.Web web;
|
|
|
|
|
|
|
|
public WebInterface(Config.Web web, String address, DBInterface db, GiteaInterface gitea) throws IOException {
|
|
|
|
server = new HTTPServer(web.port);
|
|
|
|
this.web = web;
|
|
|
|
HTTPServer.VirtualHost host = server.getVirtualHost(null);
|
|
|
|
host.addContext("/unsubscribe", (req, resp) -> {
|
|
|
|
String id = req.getParams().get("id");
|
|
|
|
if (id == null) {
|
2022-10-22 23:29:07 +02:00
|
|
|
resp.send(404, template(UNSUBSCRIBE_MISSING_ID));
|
2022-10-22 21:36:37 +02:00
|
|
|
} else {
|
|
|
|
try {
|
|
|
|
db.removeSubscription(id);
|
2022-10-22 23:29:07 +02:00
|
|
|
resp.send(202, template(UNSUBSCRIBE_SUCCESS));
|
2022-10-22 21:36:37 +02:00
|
|
|
} catch (Throwable e) {
|
2022-10-22 23:29:07 +02:00
|
|
|
resp.send(500, template(UNSUBSCRIBE_FAILURE.formatted(escapeHTML(StringFormatter.toString(e)))));
|
2022-10-22 21:36:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
});
|
|
|
|
host.addContext("/close", ((req, resp) -> {
|
|
|
|
String id = req.getParams().get("id");
|
|
|
|
if (id == null) {
|
2022-10-22 23:29:07 +02:00
|
|
|
resp.send(404, template(CLOSE_MISSING_ID));
|
2022-10-22 21:36:37 +02:00
|
|
|
} else {
|
|
|
|
try {
|
|
|
|
db.getSubscription(id).ifPresentOrElse(sub -> {
|
|
|
|
try {
|
2022-10-22 23:29:07 +02:00
|
|
|
if (sub.creator()) {
|
|
|
|
gitea.closeIssue(sub.repoOwner(), sub.repo(), sub.issue());
|
|
|
|
resp.send(202, template(CLOSE_SUCCESS));
|
|
|
|
} else {
|
|
|
|
resp.send(403, template(CLOSE_NOT_CREATOR));
|
|
|
|
}
|
2022-10-22 21:36:37 +02:00
|
|
|
} catch (IOException e) {
|
|
|
|
throw new UncheckedIOException(e);
|
|
|
|
} catch (URISyntaxException e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
|
|
|
}, () -> {
|
|
|
|
try {
|
2022-10-22 23:29:07 +02:00
|
|
|
resp.send(404, template(CLOSE_MISSING_ID));
|
2022-10-22 21:36:37 +02:00
|
|
|
} catch (IOException e) {
|
|
|
|
throw new UncheckedIOException(e);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} catch (Throwable e) {
|
2022-10-22 23:29:07 +02:00
|
|
|
resp.send(500, template(CLOSE_FAILURE.formatted(escapeHTML(StringFormatter.toString(e)))));
|
2022-10-22 21:36:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}));
|
|
|
|
String[] addr = address.split("@");
|
|
|
|
host.addContext("/issue", (req, resp) -> {
|
|
|
|
Map<String, String> params = req.getParams();
|
|
|
|
String owner = params.get("owner");
|
|
|
|
String repo = params.get("repo");
|
2022-10-22 23:29:07 +02:00
|
|
|
if (owner == null || repo == null || !Main.PATH_SEGMENT_PATTERN.matcher(owner).matches() || !Main.PATH_SEGMENT_PATTERN.matcher(repo).matches()) {
|
|
|
|
resp.send(404, template(ISSUE_MISSING_REPO));
|
2022-10-22 21:36:37 +02:00
|
|
|
} else {
|
2022-10-22 23:29:07 +02:00
|
|
|
String mail = "mailto:" + addr[0] + "+create+" + owner + '+' + repo + '@' + addr[1];
|
|
|
|
resp.send(200, template(ISSUE_SUCCESS.formatted(escapeHTML(mail))));
|
2022-10-22 21:36:37 +02:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
});
|
2022-10-25 17:39:13 +02:00
|
|
|
host.addContext("/comment", (req, resp) -> {
|
|
|
|
Map<String, String> params = req.getParams();
|
|
|
|
String owner = params.get("owner");
|
|
|
|
String repo = params.get("repo");
|
|
|
|
String issueId = params.get("id");
|
|
|
|
if (owner == null || repo == null || !Main.PATH_SEGMENT_PATTERN.matcher(owner).matches() || !Main.PATH_SEGMENT_PATTERN.matcher(repo).matches() || !Main.NUMBER_PATTERN.matcher(issueId).matches()) {
|
|
|
|
resp.send(404, template(REPLY_MISSING_REPO));
|
|
|
|
} else {
|
|
|
|
String mail = "mailto:" + addr[0] + "+comment+" + owner + '+' + repo + '+' + issueId + '@' + addr[1];
|
|
|
|
resp.send(200, template(REPLY_SUCCESS.formatted(escapeHTML(mail))));
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
});
|
2022-10-22 21:36:37 +02:00
|
|
|
host.addContext("/theme.css", (req, resp) -> {
|
|
|
|
byte[] content = THEME.getBytes(StandardCharsets.UTF_8);
|
|
|
|
resp.sendHeaders(200, content.length, -1, "W/\"" + Integer.toHexString(THEME.hashCode()) + "\"", "text/css; charset=utf-8", null);
|
|
|
|
resp.getBody().write(content);
|
|
|
|
return 0;
|
|
|
|
});
|
|
|
|
server.start();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void close() {
|
|
|
|
server.stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
public static String escapeHTML(String s) {
|
|
|
|
StringBuilder out = new StringBuilder();
|
|
|
|
for (int i = 0; i < s.length(); i++) {
|
|
|
|
char c = s.charAt(i);
|
|
|
|
if (c > 127 || c == '"' || c == '\'' || c == '<' || c == '>' || c == '&') {
|
|
|
|
out.append("&#");
|
|
|
|
out.append((int) c);
|
|
|
|
out.append(';');
|
|
|
|
} else if (c == '\n') {
|
|
|
|
out.append("<br>\n");
|
|
|
|
} else {
|
|
|
|
out.append(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return out.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getAddress() {
|
|
|
|
return web.publicAddress;
|
|
|
|
}
|
|
|
|
}
|