Commit 159feb40 authored by HotaruYS's avatar HotaruYS Committed by GitHub
Browse files

Properly handle static assets for announcements (#891)

parent 5b3c05a9
{ {
"t": "{{SYSTEM_TIME}}",
"list": [ "list": [
{ {
"ann_id": 1, "ann_id": 1,
"title": "<strong>Welcome to Grasscutter!</strong>", "title": "<strong>Welcome to Grasscutter!</strong>",
"subtitle": "Welcome!", "subtitle": "Welcome!",
"banner": "https://uploadstatic-sea.mihoyo.com/announcement/2020/09/17/f4aa42d505822805eebf4a55d72a78d8_2755691727027973637.jpg", "banner": "{{DISPATCH_PUBLIC}}/hk4e/announcement/assets/banner/1.jpg",
"content": "<p>Hi there!</p><p>First of all, welcome to Grasscutter. If you have any issues, please let us know so that Lawnmower can help you!</p><br><p><strong>〓Discord〓</strong></p><a href=\"https://discord.gg/T5vZU6UyeG\">https://discord.gg/T5vZU6UyeG</a><br><br><p><strong>〓GitHub〓</strong><a href=\"https://github.com/Grasscutters/Grasscutter\">https://github.com/Grasscutters/Grasscutter</a>", "content": "<p>Hi there!</p><p>First of all, welcome to Grasscutter. If you have any issues, please let us know so that Lawnmower can help you!</p><br><p><strong>〓Discord〓</strong></p><a href=\"https://discord.gg/T5vZU6UyeG\">https://discord.gg/T5vZU6UyeG</a><br><br><p><strong>〓GitHub〓</strong><a href=\"https://github.com/Grasscutters/Grasscutter\">https://github.com/Grasscutters/Grasscutter</a>",
"lang": "en-US" "lang": "en-US"
}, },
{ {
"ann_id": 2, "ann_id": 2,
"title": "<strong>How to use announcements</strong>", "title": "<strong>How to use announcements</strong>",
"subtitle": "How to use announcements", "subtitle": "How to use announcements",
"banner": "https://uploadstatic-sea.mihoyo.com/announcement/2020/09/17/f4aa42d505822805eebf4a55d72a78d8_2755691727027973637.jpg", "banner": "{{DISPATCH_PUBLIC}}/hk4e/announcement/assets/banner/2.jpg",
"content": "<p>Announcement content uses HTML. The specific content of the announcement is stored in the program directory <code>GameAnnouncement.json</code>, while <code>GameAnnouncementList.json</code> stores the announcement list data.</p><h2><code>GameAnnouncement</code></h2><table><tr><th>Parameter</th><th>Description</th></tr><tr><td>ann_id</td><td>Unique ID</td></tr><tr><td>title</td><td>Title shown at the top of the content</td></tr><tr><td>subtitle</td><td>Short title shown on the left</td></tr><tr><td>banner</td><td>Image to display between content and title</td></tr><tr><td>content</td><td>Content body in HTML</td></tr><tr><td>lang</td><td>Language code for this entry</td></tr></table><h2><code>GameAnnouncementList</code></h2><p>If you want to add an announcement, please add the list data in the announcement type corresponding to <code>GameAnnouncementList</code>, and finally add the announcement content in <code>GameAnnouncement</code>.</p>", "content": "<p>Announcement content uses HTML. The specific content of the announcement is stored in the program directory <code>GameAnnouncement.json</code>, while <code>GameAnnouncementList.json</code> stores the announcement list data.</p><h2><code>GameAnnouncement</code></h2><table><tr><th>Parameter</th><th>Description</th></tr><tr><td>ann_id</td><td>Unique ID</td></tr><tr><td>title</td><td>Title shown at the top of the content</td></tr><tr><td>subtitle</td><td>Short title shown on the left</td></tr><tr><td>banner</td><td>Image to display between content and title</td></tr><tr><td>content</td><td>Content body in HTML</td></tr><tr><td>lang</td><td>Language code for this entry</td></tr></table><h2><code>GameAnnouncementList</code></h2><p>If you want to add an announcement, please add the list data in the announcement type corresponding to <code>GameAnnouncementList</code>, and finally add the announcement content in <code>GameAnnouncement</code>.</p>",
"lang": "en-US" "lang": "en-US"
} }
......
{ {
"t": "System.currentTimeMillis()", "t": "{{SYSTEM_TIME}}",
"list": [ "list": [
{ {
"list": [ "list": [
{ {
"ann_id": 1, "ann_id": 1,
"title": "<strong>Welcome to Grasscutter!</strong>", "title": "<strong>Welcome to Grasscutter!</strong>",
"subtitle": "Welcome!", "subtitle": "Welcome!",
"banner": "https://uploadstatic-sea.mihoyo.com/announcement/2020/09/22/7d85f19b152d218e73224d7c138a0fd0_5818585260283672899.jpg", "banner": "{{DISPATCH_PUBLIC}}/hk4e/announcement/assets/banner/1.jpg",
"tag_icon": "https://uploadstatic-sea.mihoyo.com/announcement/2020/03/05/a2588f1a51faee9fa8dfe9aead649dd6_7237021399135895303.png", "tag_icon": "{{DISPATCH_PUBLIC}}/hk4e/announcement/assets/tag_icon.png",
"type": 2, "type": 2,
"type_label": "System", "type_label": "System",
"lang": "en-US", "lang": "en-US",
...@@ -22,8 +21,8 @@ ...@@ -22,8 +21,8 @@
"ann_id": 2, "ann_id": 2,
"title": "<strong>How to use announcements</strong>", "title": "<strong>How to use announcements</strong>",
"subtitle": "How to use announcements", "subtitle": "How to use announcements",
"banner": "https://uploadstatic-sea.mihoyo.com/announcement/2020/09/22/7d85f19b152d218e73224d7c138a0fd0_5818585260283672899.jpg", "banner": "{{DISPATCH_PUBLIC}}/hk4e/announcement/assets/banner/2.jpg",
"tag_icon": "https://uploadstatic-sea.mihoyo.com/announcement/2020/03/05/a2588f1a51faee9fa8dfe9aead649dd6_7237021399135895303.png", "tag_icon": "{{DISPATCH_PUBLIC}}/hk4e/announcement/assets/tag_icon.png",
"type": 2, "type": 2,
"type_label": "System", "type_label": "System",
"lang": "en-US", "lang": "en-US",
...@@ -59,11 +58,5 @@ ...@@ -59,11 +58,5 @@
], ],
"timezone": -5, "timezone": -5,
"alert": false, "alert": false,
"alert_id": 0, "alert_id": 0
"pic_list": [],
"pic_total": 0,
"pic_type_list": [],
"pic_alert": false,
"pic_alert_id": 0,
"static_sign": ""
} }
\ No newline at end of file
...@@ -6,6 +6,7 @@ import emu.grasscutter.server.http.Router; ...@@ -6,6 +6,7 @@ import emu.grasscutter.server.http.Router;
import emu.grasscutter.utils.FileUtils; import emu.grasscutter.utils.FileUtils;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
import express.Express; import express.Express;
import express.http.MediaType;
import express.http.Request; import express.http.Request;
import express.http.Response; import express.http.Response;
import io.javalin.Javalin; import io.javalin.Javalin;
...@@ -13,27 +14,15 @@ import io.javalin.Javalin; ...@@ -13,27 +14,15 @@ import io.javalin.Javalin;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Paths; import java.nio.charset.StandardCharsets;
import java.util.Objects; import java.util.Objects;
import static emu.grasscutter.Configuration.DATA; import static emu.grasscutter.Configuration.*;
/** /**
* Handles requests related to the announcements page. * Handles requests related to the announcements page.
*/ */
public final class AnnouncementsHandler implements Router { public final class AnnouncementsHandler implements Router {
private static String template, swjs, vue;
public AnnouncementsHandler() {
var templateFile = new File(Utils.toFilePath(DATA("/hk4e/announcement/index.html")));
var swjsFile = new File(Utils.toFilePath(DATA("/hk4e/announcement/sw.js")));
var vueFile = new File(Utils.toFilePath(DATA("/hk4e/announcement/vue.min.js")));
template = templateFile.exists() ? new String(FileUtils.read(template)) : null;
swjs = swjsFile.exists() ? new String(FileUtils.read(swjs)) : null;
vue = vueFile.exists() ? new String(FileUtils.read(vueFile)) : null;
}
@Override public void applyRoutes(Express express, Javalin handle) { @Override public void applyRoutes(Express express, Javalin handle) {
// hk4e-api-os.hoyoverse.com // hk4e-api-os.hoyoverse.com
express.all("/common/hk4e_global/announcement/api/getAlertPic", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"total\":0,\"list\":[]}}")); express.all("/common/hk4e_global/announcement/api/getAlertPic", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"total\":0,\"list\":[]}}"));
...@@ -47,58 +36,57 @@ public final class AnnouncementsHandler implements Router { ...@@ -47,58 +36,57 @@ public final class AnnouncementsHandler implements Router {
express.all("/hk4e_global/mdk/shopwindow/shopwindow/listPriceTier", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"suggest_currency\":\"USD\",\"tiers\":[]}}")); express.all("/hk4e_global/mdk/shopwindow/shopwindow/listPriceTier", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"suggest_currency\":\"USD\",\"tiers\":[]}}"));
express.get("/hk4e/announcement/*", AnnouncementsHandler::getPageResources); express.get("/hk4e/announcement/*", AnnouncementsHandler::getPageResources);
express.get("/sw.js", AnnouncementsHandler::getPageResources);
express.get("/dora/lib/vue/2.6.11/vue.min.js", AnnouncementsHandler::getPageResources);
} }
private static void getAnnouncement(Request request, Response response) { private static void getAnnouncement(Request request, Response response) {
String data = "";
if (Objects.equals(request.baseUrl(), "/common/hk4e_global/announcement/api/getAnnContent")) { if (Objects.equals(request.baseUrl(), "/common/hk4e_global/announcement/api/getAnnContent")) {
String data = readToString(Paths.get(DATA("GameAnnouncement.json")).toFile()); data = readToString(new File(Utils.toFilePath(DATA("GameAnnouncement.json"))));
response.send("{\"retcode\":0,\"message\":\"OK\",\"data\":" + data + "}");
} else if (Objects.equals(request.baseUrl(), "/common/hk4e_global/announcement/api/getAnnList")) { } else if (Objects.equals(request.baseUrl(), "/common/hk4e_global/announcement/api/getAnnList")) {
String data = readToString(Paths.get(DATA("GameAnnouncementList.json")).toFile()) data = readToString(new File(Utils.toFilePath(DATA("GameAnnouncementList.json"))));
.replace("System.currentTimeMillis()", String.valueOf(System.currentTimeMillis())); } else {
response.send("{\"retcode\":0,\"message\":\"OK\",\"data\": " + data + "}"); response.send("{\"retcode\":404,\"message\":\"Unknown request path\"}");
} }
if (data.isEmpty()) {
response.send("{\"retcode\":500,\"message\":\"Unable to fetch requsted content\"}");
return;
} }
private static void getPageResources(Request request, Response response) { String dispatchDomain = "http" + (HTTP_ENCRYPTION.useInRouting ? "s" : "") + "://"
var path = request.path(); + lr(HTTP_INFO.accessAddress, HTTP_INFO.bindAddress) + ":"
switch(path) { + lr(HTTP_INFO.accessPort, HTTP_INFO.bindPort);
case "/sw.js" -> response.send(swjs);
case "/hk4e/announcement/index.html" -> response.send(template);
case "/dora/lib/vue/2.6.11/vue.min.js" -> response.send(vue);
default -> { data = data
File renderFile = new File(Utils.toFilePath(DATA(path))); .replace("{{DISPATCH_PUBLIC}}", dispatchDomain)
if(!renderFile.exists()) { .replace("{{SYSTEM_TIME}}", String.valueOf(System.currentTimeMillis()));
Grasscutter.getLogger().info("File not exist: " + path); response.send("{\"retcode\":0,\"message\":\"OK\",\"data\": " + data + "}");
return;
} }
String ext = path.substring(path.lastIndexOf(".") + 1); private static void getPageResources(Request request, Response response) {
if ("css".equals(ext)) { String filename = Utils.toFilePath(DATA(request.path()));
response.type("text/css"); File file = new File(filename);
response.send(FileUtils.read(renderFile)); if (file.exists() && file.isFile()) {
MediaType fromExtension = MediaType.getByExtension(filename.substring(filename.lastIndexOf(".") + 1));
response.type((fromExtension != null) ? fromExtension.getMIME() : "application/octet-stream");
response.send(FileUtils.read(file));
} else { } else {
response.send(FileUtils.read(renderFile)); Grasscutter.getLogger().warn("File does not exist: " + file);
} response.status(404);
}
} }
} }
@SuppressWarnings("ResultOfMethodCallIgnored") @SuppressWarnings("ResultOfMethodCallIgnored")
private static String readToString(File file) { private static String readToString(File file) {
long length = file.length(); byte[] content = new byte[(int) file.length()];
byte[] content = new byte[(int) length];
try { try {
FileInputStream in = new FileInputStream(file); FileInputStream in = new FileInputStream(file);
in.read(content); in.close(); in.read(content); in.close();
} catch (IOException ignored) { } catch (IOException ignored) {
Grasscutter.getLogger().warn("File not found: " + file.getAbsolutePath()); Grasscutter.getLogger().warn("File does not exist: " + file);
} }
return new String(content); return new String(content, StandardCharsets.UTF_8);
} }
} }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment