mirror of
https://github.com/spacebarchat/server.git
synced 2024-11-05 18:32:33 +01:00
Refactored testclient
This commit is contained in:
parent
ff2043e07e
commit
a786c2317c
@ -122,6 +122,7 @@
|
|||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- inline plugin marker -->
|
||||||
<!-- preload plugin marker -->
|
<!-- preload plugin marker -->
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
0
assets/inline-plugins/.gitkeep
Normal file
0
assets/inline-plugins/.gitkeep
Normal file
0
assets/preload-plugins/.gitkeep
Normal file
0
assets/preload-plugins/.gitkeep
Normal file
@ -1,106 +1,53 @@
|
|||||||
import express, { Request, Response, Application } from "express";
|
import express, { Application } from "express";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import fetch, { Response as FetchResponse } from "node-fetch";
|
import fetch, { Response as FetchResponse, Headers } from "node-fetch";
|
||||||
import ProxyAgent from "proxy-agent";
|
import ProxyAgent from "proxy-agent";
|
||||||
import { Config } from "@fosscord/util";
|
import { Config } from "@fosscord/util";
|
||||||
|
|
||||||
const ASSET_FOLDER_PATH = path.join(__dirname, "..", "..", "..", "assets");
|
const ASSET_FOLDER_PATH = path.join(__dirname, "..", "..", "..", "assets");
|
||||||
|
|
||||||
let hasWarnedAboutCache = false;
|
|
||||||
|
|
||||||
export default function TestClient(app: Application) {
|
export default function TestClient(app: Application) {
|
||||||
const agent = new ProxyAgent();
|
|
||||||
const assetCache = new Map<
|
|
||||||
string,
|
|
||||||
{ response: FetchResponse; buffer: Buffer; }
|
|
||||||
>();
|
|
||||||
const indexHTML = fs.readFileSync(
|
|
||||||
path.join(ASSET_FOLDER_PATH, "client_test", "index.html"),
|
|
||||||
{ encoding: "utf8" },
|
|
||||||
);
|
|
||||||
|
|
||||||
var html = indexHTML;
|
|
||||||
const CDN_ENDPOINT = (
|
|
||||||
Config.get().cdn.endpointClient ||
|
|
||||||
Config.get()?.cdn.endpointPublic ||
|
|
||||||
process.env.CDN ||
|
|
||||||
""
|
|
||||||
).replace(/(https?)?(:\/\/?)/g, "");
|
|
||||||
const GATEWAY_ENDPOINT =
|
|
||||||
Config.get().gateway.endpointClient ||
|
|
||||||
Config.get()?.gateway.endpointPublic ||
|
|
||||||
process.env.GATEWAY ||
|
|
||||||
"";
|
|
||||||
|
|
||||||
if (CDN_ENDPOINT) {
|
|
||||||
html = html.replace(/CDN_HOST: .+/, `CDN_HOST: \`${CDN_ENDPOINT}\`,`);
|
|
||||||
}
|
|
||||||
if (GATEWAY_ENDPOINT) {
|
|
||||||
html = html.replace(
|
|
||||||
/GATEWAY_ENDPOINT: .+/,
|
|
||||||
`GATEWAY_ENDPOINT: \`${GATEWAY_ENDPOINT}\`,`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// inline plugins
|
|
||||||
var files = fs.readdirSync(path.join(ASSET_FOLDER_PATH, "preload-plugins"));
|
|
||||||
var plugins = "";
|
|
||||||
files.forEach((x) => {
|
|
||||||
if (x.endsWith(".js"))
|
|
||||||
plugins += `<script>${fs.readFileSync(
|
|
||||||
path.join(ASSET_FOLDER_PATH, "preload-plugins", x),
|
|
||||||
)}</script>\n`;
|
|
||||||
});
|
|
||||||
html = html.replaceAll("<!-- preload plugin marker -->", plugins);
|
|
||||||
|
|
||||||
// plugins
|
|
||||||
files = fs.readdirSync(path.join(ASSET_FOLDER_PATH, "plugins"));
|
|
||||||
plugins = "";
|
|
||||||
files.forEach((x) => {
|
|
||||||
if (x.endsWith(".js"))
|
|
||||||
plugins += `<script src='/assets/plugins/${x}'></script>\n`;
|
|
||||||
});
|
|
||||||
html = html.replaceAll("<!-- plugin marker -->", plugins);
|
|
||||||
//preload plugins
|
|
||||||
files = fs.readdirSync(path.join(ASSET_FOLDER_PATH, "preload-plugins"));
|
|
||||||
plugins = "";
|
|
||||||
files.forEach((x) => {
|
|
||||||
if (x.endsWith(".js"))
|
|
||||||
plugins += `<script>${fs.readFileSync(
|
|
||||||
path.join(ASSET_FOLDER_PATH, "preload-plugins", x),
|
|
||||||
)}</script>\n`;
|
|
||||||
});
|
|
||||||
html = html.replaceAll("<!-- preload plugin marker -->", plugins);
|
|
||||||
|
|
||||||
app.use("/assets", express.static(path.join(ASSET_FOLDER_PATH, "public")));
|
app.use("/assets", express.static(path.join(ASSET_FOLDER_PATH, "public")));
|
||||||
app.use("/assets", express.static(path.join(ASSET_FOLDER_PATH, "cache")));
|
app.use("/assets", express.static(path.join(ASSET_FOLDER_PATH, "cache")));
|
||||||
|
|
||||||
app.get("/assets/:file", async (req: Request, res: Response) => {
|
// Test client is disabled, so don't need to run any more. Above should probably be moved somewhere?
|
||||||
if (req.params.file.includes(".map")) return res.status(404);
|
if (!Config.get().client.useTestClient) return;
|
||||||
|
|
||||||
|
const agent = new ProxyAgent();
|
||||||
|
|
||||||
|
let html = fs.readFileSync(path.join(ASSET_FOLDER_PATH, "client_test", "index.html"), { encoding: "utf-8" });
|
||||||
|
|
||||||
|
html = applyEnv(html); // update window.GLOBAL_ENV according to config
|
||||||
|
|
||||||
|
html = applyPlugins(html); // inject our plugins
|
||||||
|
app.use("/assets/plugins", express.static(path.join(ASSET_FOLDER_PATH, "plugins")));
|
||||||
|
app.use("/assets/inline-plugins", express.static(path.join(ASSET_FOLDER_PATH, "inline-plugins")));
|
||||||
|
|
||||||
|
// Asset memory cache
|
||||||
|
const assetCache = new Map<string, { response: FetchResponse; buffer: Buffer; }>();
|
||||||
|
|
||||||
|
// Fetches uncached ( on disk ) assets from discord.com and stores them in memory cache.
|
||||||
|
app.get("/assets/:file", async (req, res) => {
|
||||||
delete req.headers.host;
|
delete req.headers.host;
|
||||||
var response: FetchResponse;
|
|
||||||
var buffer: Buffer;
|
if (req.params.file.endsWith(".map")) return res.status(404);
|
||||||
|
|
||||||
|
let response: FetchResponse;
|
||||||
|
let buffer: Buffer;
|
||||||
const cache = assetCache.get(req.params.file);
|
const cache = assetCache.get(req.params.file);
|
||||||
if (!cache) {
|
if (!cache) {
|
||||||
response = await fetch(
|
response = await fetch(`https://discord.com/assets/${req.params.file}`, {
|
||||||
`https://discord.com/assets/${req.params.file}`,
|
|
||||||
{
|
|
||||||
agent,
|
agent,
|
||||||
// @ts-ignore
|
headers: { ...req.headers as { [key: string]: string; } },
|
||||||
headers: {
|
});
|
||||||
...req.headers,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
buffer = await response.buffer();
|
buffer = await response.buffer();
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
response = cache.response;
|
response = cache.response;
|
||||||
buffer = cache.buffer;
|
buffer = cache.buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
response.headers.forEach((value, name) => {
|
|
||||||
if (
|
|
||||||
[
|
[
|
||||||
"content-length",
|
"content-length",
|
||||||
"content-security-policy",
|
"content-security-policy",
|
||||||
@ -109,61 +56,84 @@ export default function TestClient(app: Application) {
|
|||||||
"transfer-encoding",
|
"transfer-encoding",
|
||||||
"expect-ct",
|
"expect-ct",
|
||||||
"access-control-allow-origin",
|
"access-control-allow-origin",
|
||||||
"content-encoding",
|
"content-encoding"
|
||||||
].includes(name.toLowerCase())
|
].forEach(headerName => {
|
||||||
) {
|
response.headers.delete(headerName);
|
||||||
return;
|
|
||||||
}
|
|
||||||
res.set(name, value);
|
|
||||||
});
|
});
|
||||||
|
response.headers.forEach((value, name) => res.set(name, value));
|
||||||
|
|
||||||
assetCache.set(req.params.file, { buffer, response });
|
assetCache.set(req.params.file, { buffer, response });
|
||||||
|
|
||||||
|
// TODO: I don't like this. Figure out a way to get client cacher to download *all* assets.
|
||||||
if (response.status == 200) {
|
if (response.status == 200) {
|
||||||
// if (!hasWarnedAboutCache) {
|
console.warn(`[TestClient] Cache miss for file ${req.params.file}! Use 'npm run generate:client' to cache and patch.`);
|
||||||
hasWarnedAboutCache = true;
|
|
||||||
console.warn(
|
|
||||||
`[TestClient] Cache miss for file ${req.params.file}! Use 'npm run generate:client' to cache and patch.`,
|
|
||||||
);
|
|
||||||
await fs.promises.appendFile(path.join(ASSET_FOLDER_PATH, "cacheMisses"), req.params.file + "\n");
|
await fs.promises.appendFile(path.join(ASSET_FOLDER_PATH, "cacheMisses"), req.params.file + "\n");
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.send(buffer);
|
return res.send(buffer);
|
||||||
});
|
});
|
||||||
app.get("/developers*", (req: Request, res: Response) => {
|
|
||||||
const { useTestClient } = Config.get().client;
|
// Instead of our generated html, send developers.html for developers endpoint
|
||||||
res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24);
|
app.get("/developers*", (req, res) => {
|
||||||
|
res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24); // 24 hours
|
||||||
res.set("content-type", "text/html");
|
res.set("content-type", "text/html");
|
||||||
|
res.send(fs.readFileSync(path.join(ASSET_FOLDER_PATH, "client_test", "developers.html"), { encoding: "utf-8" }));
|
||||||
if (!useTestClient)
|
|
||||||
return res.send(
|
|
||||||
"Test client is disabled on this instance. Use a stand-alone client to connect this instance.",
|
|
||||||
);
|
|
||||||
|
|
||||||
res.send(
|
|
||||||
fs.readFileSync(
|
|
||||||
path.join(ASSET_FOLDER_PATH, "client_test", "developers.html"),
|
|
||||||
{ encoding: "utf8" },
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
app.get("*", (req: Request, res: Response) => {
|
|
||||||
const { useTestClient } = Config.get().client;
|
// Send our generated index.html for all routes.
|
||||||
res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24);
|
app.get("*", (req, res) => {
|
||||||
|
res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24); // 24 hours
|
||||||
res.set("content-type", "text/html");
|
res.set("content-type", "text/html");
|
||||||
|
|
||||||
if (req.url.startsWith("/api") || req.url.startsWith("/__development"))
|
if (req.url.startsWith("/api") || req.url.startsWith("/__development")) return;
|
||||||
return;
|
|
||||||
|
|
||||||
if (!useTestClient)
|
return res.send(html);
|
||||||
return res.send(
|
|
||||||
"Test client is disabled on this instance. Use a stand-alone client to connect this instance.",
|
|
||||||
);
|
|
||||||
if (req.url.startsWith("/invite"))
|
|
||||||
return res.send(
|
|
||||||
html.replace("9b2b7f0632acd0c5e781", "9f24f709a3de09b67c49"),
|
|
||||||
);
|
|
||||||
|
|
||||||
res.send(html);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply gateway/cdn endpoint values from config to index.html.
|
||||||
|
const applyEnv = (html: string): string => {
|
||||||
|
const config = Config.get();
|
||||||
|
|
||||||
|
const cdn = (config.cdn.endpointClient || config.cdn.endpointPublic || process.env.CDN || "")
|
||||||
|
.replace(/(https?)?(:\/\/?)/g, "");
|
||||||
|
|
||||||
|
const gateway = (config.gateway.endpointClient || config.gateway.endpointPublic || process.env.GATEWAY || "");
|
||||||
|
|
||||||
|
if (cdn)
|
||||||
|
html = html.replace(/CDN_HOST: .+/, `CDN_HOST: \`${cdn}\`,`);
|
||||||
|
|
||||||
|
if (gateway)
|
||||||
|
html = html.replace(/GATEWAY_ENDPOINT: .+/, `GATEWAY_ENDPOINT: \`${gateway}\`,`);
|
||||||
|
|
||||||
|
return html;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Injects inline, preload, and standard plugins into index.html.
|
||||||
|
const applyPlugins = (html: string): string => {
|
||||||
|
// Inline plugins. Injected as <script src="/assets/inline-plugins/name.js"> into head.
|
||||||
|
const inlineFiles = fs.readdirSync(path.join(ASSET_FOLDER_PATH, "inline-plugins"));
|
||||||
|
const inline = inlineFiles
|
||||||
|
.filter(x => x.endsWith(".js"))
|
||||||
|
.map(x => `<script src="/assets/inline-plugins/${x}"></script>`)
|
||||||
|
.join("\n");
|
||||||
|
html = html.replace("<!-- inline plugin marker -->", inline);
|
||||||
|
|
||||||
|
// Preload plugins. Text content of each plugin is injected into head.
|
||||||
|
const preloadFiles = fs.readdirSync(path.join(ASSET_FOLDER_PATH, "preload-plugins"));
|
||||||
|
const preload = preloadFiles
|
||||||
|
.filter(x => x.endsWith(".js"))
|
||||||
|
.map(x => `<script>${fs.readFileSync(path.join(ASSET_FOLDER_PATH, "preload-plugins", x))}</script>`)
|
||||||
|
.join("\n");
|
||||||
|
html = html.replace("<!-- preload plugin marker -->", preload);
|
||||||
|
|
||||||
|
// Normal plugins. Injected as <script src="/assets/plugins/name.js"> into body.
|
||||||
|
const pluginFiles = fs.readdirSync(path.join(ASSET_FOLDER_PATH, "plugins"));
|
||||||
|
const plugins = pluginFiles
|
||||||
|
.filter(x => x.endsWith(".js"))
|
||||||
|
.map(x => `<script src="/assets/plugins/${x}"></script>`)
|
||||||
|
.join("\n");
|
||||||
|
html = html.replace("<!-- plugin marker -->", plugins);
|
||||||
|
|
||||||
|
return html;
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user