22
Dockerfile
|
@ -1,22 +1,24 @@
|
|||
FROM node:lts as build
|
||||
FROM node:lts as web
|
||||
# Reduce npm log spam and colour during install within Docker
|
||||
ENV NPM_CONFIG_LOGLEVEL=warn
|
||||
ENV NPM_CONFIG_COLOR=false
|
||||
|
||||
WORKDIR /home/node/app
|
||||
|
||||
COPY --chown=node:node . /home/node/app/
|
||||
COPY --chown=node:node web /home/node/app/
|
||||
|
||||
RUN npm install
|
||||
RUN npm run typecheck
|
||||
RUN npm run build
|
||||
|
||||
FROM nginx:stable as production
|
||||
WORKDIR /home/node/app
|
||||
# Copy nginx config
|
||||
COPY --chown=nobody:nogroup nginx.conf /etc/nginx/conf.d/default.conf
|
||||
# Copy what we've installed/built from production
|
||||
COPY --chown=nobody:nogroup --from=build /home/node/app/build /usr/share/nginx/html/
|
||||
FROM denoland/deno:1.42.3 as build-server
|
||||
|
||||
# Caching directory
|
||||
RUN mkdir -p /var/run/nginx-cache && chown nobody:nogroup /var/run/nginx-cache
|
||||
WORKDIR /app
|
||||
|
||||
COPY server /app
|
||||
COPY --from=web /home/node/app/build /app/web
|
||||
|
||||
RUN deno cache server.ts
|
||||
|
||||
USER deno
|
||||
CMD ["run", "--unstable-kv", "--allow-net", "--allow-read", "server.ts"]
|
||||
|
|
5
justfile
Normal file
|
@ -0,0 +1,5 @@
|
|||
build:
|
||||
docker build -t code.tjo.space/tjo-space/tjo.space:latest .
|
||||
|
||||
run:
|
||||
docker run -p 8080:8080 code.tjo.space/tjo-space/tjo.space:latest
|
55
nginx.conf
|
@ -1,55 +0,0 @@
|
|||
proxy_cache_path /var/run/nginx-cache/jscache levels=1:2 keys_zone=jscache:100m inactive=30d use_temp_path=off max_size=100m;
|
||||
|
||||
server {
|
||||
listen 80 default_server;
|
||||
server_name _;
|
||||
resolver 9.9.9.9 1.1.1.1 8.8.8.8;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
|
||||
location /.well-known/ {
|
||||
root /usr/share/nginx/html;
|
||||
default_type application/json;
|
||||
add_header Access-Control-Allow-Origin *;
|
||||
}
|
||||
|
||||
# Plausible Analytics
|
||||
# https://plausible.io/docs/proxy/guides/nginx
|
||||
set $plausible_script_url https://plausible.io/js/script.js; # Change this if you use a different variant of the script
|
||||
set $plausible_event_url https://plausible.io/api/event;
|
||||
location = /js/script.js {
|
||||
proxy_pass $plausible_script_url;
|
||||
proxy_set_header Host plausible.io;
|
||||
|
||||
# Tiny, negligible performance improvement. Very optional.
|
||||
proxy_buffering on;
|
||||
|
||||
# Cache the script for 6 hours, as long as plausible.io returns a valid response
|
||||
proxy_cache jscache;
|
||||
proxy_cache_valid 200 6h;
|
||||
proxy_cache_use_stale updating error timeout invalid_header http_500;
|
||||
|
||||
# Optional. Adds a header to tell if you got a cache hit or miss
|
||||
add_header X-Cache $upstream_cache_status;
|
||||
}
|
||||
location = /api/event {
|
||||
proxy_pass $plausible_event_url;
|
||||
proxy_set_header Host plausible.io;
|
||||
proxy_buffering on;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
}
|
||||
|
||||
# redirect server error pages to the static page /50x.html
|
||||
#
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
92
server/server.ts
Normal file
|
@ -0,0 +1,92 @@
|
|||
import { Hono } from "https://deno.land/x/hono@v4.2.5/mod.ts";
|
||||
import { serveStatic } from "https://deno.land/x/hono@v4.2.5/middleware.ts";
|
||||
|
||||
const cache = await Deno.openKv();
|
||||
const app = new Hono();
|
||||
|
||||
// .well-known for matrix
|
||||
app.get("/.well-known/matrix/client", (c) =>
|
||||
c.json({
|
||||
"m.homeserver": {
|
||||
base_url: "https://matrix.chat.tjo.space",
|
||||
},
|
||||
}),
|
||||
);
|
||||
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) => {
|
||||
console.log(c.req.query);
|
||||
|
||||
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: [
|
||||
{
|
||||
rel: "http://schemas.openid.net/specs/connect/1.0/issuer",
|
||||
href: `https://id.tjo.space/application/o/tailscalecom/`,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
// Serve plausible analytics
|
||||
app.get("/js/script.js", async (c) => {
|
||||
c.header("Content-Type", "text/javascript");
|
||||
|
||||
// Check if we have the script cached
|
||||
const cachedScript = await cache.get<string>(["plausible", "script"]);
|
||||
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) {
|
||||
await cache.set(["plausible", "script"], script, {
|
||||
expireIn: 1000 * 60 * 60 * 24,
|
||||
});
|
||||
}
|
||||
|
||||
return c.body(script);
|
||||
});
|
||||
app.get("/api/event", (c) => {
|
||||
const request = new Request(c.req.raw);
|
||||
request.headers.delete("Cookie");
|
||||
request.headers.set("X-Forwarded-For", c.req.header("host") || "");
|
||||
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);
|
0
.gitignore → web/.gitignore
vendored
0
package-lock.json → web/package-lock.json
generated
|
@ -12,8 +12,7 @@
|
|||
"serve": "docusaurus serve",
|
||||
"write-translations": "docusaurus write-translations",
|
||||
"write-heading-ids": "docusaurus write-heading-ids",
|
||||
"typecheck": "tsc",
|
||||
"build:docker": "docker build -t tjo-space ."
|
||||
"typecheck": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "2.4.1",
|
0
web/static/.nojekyll
Normal file
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.8 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.4 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |