2024-04-20 18:18:38 +00:00
|
|
|
import { Hono } from "https://deno.land/x/hono@v4.2.5/mod.ts";
|
2024-04-20 18:32:52 +00:00
|
|
|
import {
|
|
|
|
serveStatic,
|
|
|
|
logger,
|
2024-06-11 15:57:04 +00:00
|
|
|
cors,
|
|
|
|
etag,
|
|
|
|
secureHeaders,
|
|
|
|
cache,
|
2024-04-20 18:32:52 +00:00
|
|
|
} from "https://deno.land/x/hono@v4.2.5/middleware.ts";
|
2024-04-20 18:18:38 +00:00
|
|
|
|
|
|
|
const app = new Hono();
|
2024-04-20 18:32:52 +00:00
|
|
|
app.use(logger());
|
2024-06-11 15:57:04 +00:00
|
|
|
app.use(etag());
|
|
|
|
app.use(secureHeaders());
|
|
|
|
app.use(
|
|
|
|
cache({
|
|
|
|
cacheName: "tjo-space",
|
|
|
|
cacheControl: "public, max-age=3600",
|
|
|
|
wait: true,
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Allow CORS for tjo.space
|
|
|
|
app.use(cors({ origin: "https://tjo.space" }));
|
|
|
|
// But, /.well-known requests from anywhere.
|
|
|
|
app.use("/.well-known/*", cors({ origin: "*" }));
|
2024-04-20 18:18:38 +00:00
|
|
|
|
|
|
|
// .well-known for matrix
|
|
|
|
app.get("/.well-known/matrix/client", (c) =>
|
|
|
|
c.json({
|
|
|
|
"m.homeserver": {
|
|
|
|
base_url: "https://matrix.chat.tjo.space",
|
2024-06-11 15:57:04 +00:00
|
|
|
server_name: "tjo.space",
|
2024-04-20 18:18:38 +00:00
|
|
|
},
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
app.get("/.well-known/matrix/server", (c) =>
|
|
|
|
c.json({
|
|
|
|
"m.server": "matrix.chat.tjo.space:443",
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
app.get("/.well-known/matrix/support", (c) =>
|
|
|
|
c.json({
|
|
|
|
contacts: [
|
|
|
|
{
|
|
|
|
matrix_id: "@tine:tjo.space",
|
|
|
|
email_address: "tine@tjo.space",
|
|
|
|
role: "m.role.admin",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
|
|
|
|
// .well-known for webfinger - tailscale
|
|
|
|
app.get("/.well-known/webfinger", (c) => {
|
|
|
|
const resource = c.req.query("resource");
|
|
|
|
if (!resource) {
|
|
|
|
c.status(400);
|
|
|
|
return c.json({ error: "Missing resource query parameter" });
|
|
|
|
}
|
|
|
|
|
|
|
|
const [scheme] = resource.split(":");
|
|
|
|
if (scheme !== "acct") {
|
|
|
|
c.status(400);
|
|
|
|
return c.json({ error: "Only acct scheme is supported" });
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.json({
|
|
|
|
subject: resource,
|
|
|
|
links: [
|
|
|
|
{
|
2024-04-20 18:43:14 +00:00
|
|
|
rel: "http://openid.net/specs/connect/1.0/issuer",
|
2024-04-20 18:18:38 +00:00
|
|
|
href: `https://id.tjo.space/application/o/tailscalecom/`,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2024-06-11 15:57:04 +00:00
|
|
|
const kvCache = await Deno.openKv();
|
2024-04-20 18:18:38 +00:00
|
|
|
// Serve plausible analytics
|
|
|
|
app.get("/js/script.js", async (c) => {
|
|
|
|
c.header("Content-Type", "text/javascript");
|
|
|
|
|
|
|
|
// Check if we have the script cached
|
2024-06-11 15:57:04 +00:00
|
|
|
const cachedScript = await kvCache.get<string>(["plausible", "script"]);
|
2024-04-20 18:18:38 +00:00
|
|
|
if (cachedScript.value) {
|
|
|
|
return c.body(cachedScript.value);
|
|
|
|
}
|
|
|
|
|
|
|
|
const response = await fetch("https://plausible.io/js/plausible.js");
|
|
|
|
const script = await response.text();
|
|
|
|
|
|
|
|
// Cache for 24 hours if we got the script
|
|
|
|
if (response.status === 200) {
|
2024-06-11 15:57:04 +00:00
|
|
|
await kvCache.set(["plausible", "script"], script, {
|
2024-04-20 18:18:38 +00:00
|
|
|
expireIn: 1000 * 60 * 60 * 24,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.body(script);
|
|
|
|
});
|
2024-04-22 17:39:04 +00:00
|
|
|
app.post("/api/event", (c) => {
|
2024-04-20 18:18:38 +00:00
|
|
|
const request = new Request(c.req.raw);
|
|
|
|
request.headers.delete("Cookie");
|
2024-04-22 19:05:50 +00:00
|
|
|
request.headers.set(
|
|
|
|
"X-Forwarded-For",
|
|
|
|
c.req.header("X-Forwarded-For") || "unknown",
|
|
|
|
);
|
2024-04-20 18:18:38 +00:00
|
|
|
return fetch("https://plausible.io/api/event", request);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Serve website
|
|
|
|
app.use("*", serveStatic({ root: "./web" }));
|
|
|
|
|
|
|
|
// Start server
|
|
|
|
Deno.serve({ port: 8080, hostname: "0.0.0.0" }, app.fetch);
|