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