Initial commit
This commit is contained in:
commit
c1028ae6b1
|
@ -0,0 +1,3 @@
|
||||||
|
PURGE_TOKEN=my-super-token
|
||||||
|
TARGET_HOST=http://localhost:8080 # Do not include the trailing /
|
||||||
|
OUTGOING_PORT=8081
|
|
@ -0,0 +1,14 @@
|
||||||
|
# dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# logs
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# environment variables
|
||||||
|
.env
|
||||||
|
.env.production
|
||||||
|
|
||||||
|
cache
|
|
@ -0,0 +1,10 @@
|
||||||
|
# middleware.accords-library.com
|
||||||
|
|
||||||
|
This middleware handles caching of the website.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```
|
||||||
|
bun install
|
||||||
|
bun run dev
|
||||||
|
```
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"start": "bun run --watch src/index.ts"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"hono": "^3.5.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"bun-types": "^0.6.2"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
import { BunFile } from "bun";
|
||||||
|
import fs from "fs/promises";
|
||||||
|
|
||||||
|
type SerializableResponse = {
|
||||||
|
body: string;
|
||||||
|
headers: Record<string, string>;
|
||||||
|
status: number;
|
||||||
|
statusText: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const purgeCache = async (request: Request): Promise<void> => {
|
||||||
|
const url = new URL(request.url);
|
||||||
|
if (url.pathname.endsWith("/*")) {
|
||||||
|
const pathnameWithoutWildcard = url.pathname.slice(0, -2);
|
||||||
|
await fs.rm(getFolderPath(pathnameWithoutWildcard), {
|
||||||
|
recursive: true,
|
||||||
|
force: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await fs.rm(getFilePath(url.pathname), { force: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getResponse = async (request: Request): Promise<Response> => {
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const file = Bun.file(getFilePath(url.pathname));
|
||||||
|
let serializableResponse;
|
||||||
|
if (fileExists(file)) {
|
||||||
|
serializableResponse = await file.json<SerializableResponse>();
|
||||||
|
console.log("🟢 Retrieved response from cache for", request.url);
|
||||||
|
} else {
|
||||||
|
serializableResponse = await createCache(request);
|
||||||
|
console.log("🟠 Generated response for", request.url);
|
||||||
|
}
|
||||||
|
const { body, headers, status, statusText } = serializableResponse;
|
||||||
|
return new Response(body, { headers, status, statusText });
|
||||||
|
};
|
||||||
|
|
||||||
|
const createCache = async (request: Request): Promise<SerializableResponse> => {
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const response = await fetch(
|
||||||
|
`${Bun.env.TARGET_HOST}${url.pathname}${url.search}`
|
||||||
|
);
|
||||||
|
const serializableReponse: SerializableResponse = {
|
||||||
|
body: await response.text(),
|
||||||
|
headers: Object.fromEntries(response.headers.entries()),
|
||||||
|
status: response.status,
|
||||||
|
statusText: response.statusText,
|
||||||
|
};
|
||||||
|
// Only save cache is status is ok
|
||||||
|
if (response.ok) {
|
||||||
|
await fs.mkdir(getFolderPath(url.pathname), { recursive: true });
|
||||||
|
await Bun.write(
|
||||||
|
getFilePath(url.pathname),
|
||||||
|
JSON.stringify(serializableReponse)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return serializableReponse;
|
||||||
|
};
|
||||||
|
|
||||||
|
const fileExists = (file: BunFile): boolean => file.size > 0;
|
||||||
|
|
||||||
|
const getFolderPath = (pathname: string): string => `./cache${pathname}`;
|
||||||
|
|
||||||
|
const getFilePath = (pathname: string): string =>
|
||||||
|
`${getFolderPath(pathname)}/index.res`;
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { Hono } from "hono";
|
||||||
|
import { getResponse, purgeCache } from "./cache";
|
||||||
|
|
||||||
|
const app = new Hono();
|
||||||
|
|
||||||
|
app.on("PURGE", "*", async ({ req }) => {
|
||||||
|
if (req.headers.get("Authorization") !== `Bearer ${Bun.env.PURGE_TOKEN}`) {
|
||||||
|
console.log("🛑 Purge request rejected for", req.url);
|
||||||
|
return new Response(null, { status: 403 });
|
||||||
|
}
|
||||||
|
console.log("🔥 Purge request accepted for", req.url);
|
||||||
|
await purgeCache(req.raw);
|
||||||
|
return new Response(null, { status: 200 });
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get(async ({ req }) => await getResponse(req.raw));
|
||||||
|
|
||||||
|
const server = Bun.serve({
|
||||||
|
port: Bun.env.OUTGOING_PORT,
|
||||||
|
fetch: app.fetch,
|
||||||
|
});
|
||||||
|
console.log(`👂 Listening on http://${server.hostname}:${server.port}`);
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"strict": true,
|
||||||
|
"types": ["bun-types"]
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue