-
Notifications
You must be signed in to change notification settings - Fork 134
/
Copy path_middleware.ts
87 lines (82 loc) · 2.93 KB
/
_middleware.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
// Copyright 2024 the JSR authors. All rights reserved. MIT license.
import type { Middleware } from "fresh";
import { deleteCookie, getCookies } from "@std/http/cookie";
import { State } from "../util.ts";
import { API, path } from "../utils/api.ts";
import { FullUser } from "../utils/api_types.ts";
import { Tracer } from "../utils/tracing.ts";
import { define } from "../util.ts";
export const API_ROOT = Deno.env.get("API_ROOT") ?? "http://api.jsr.test";
export const tracer = new Tracer();
const tracing = define.middleware(async (ctx) => {
ctx.state.span = tracer.spanForRequest(ctx.req);
const attributes: Record<string, string | bigint> = {
"http.url": ctx.url.href,
"http.method": ctx.req.method,
"http.host": ctx.url.host,
};
const start = new Date();
try {
const resp = await ctx.next();
resp.headers.set("x-deno-ray", ctx.state.span.traceId);
attributes["http.status_code"] = BigInt(resp.status);
return resp;
} finally {
const end = new Date();
ctx.state.span.record(ctx.url.pathname, start, end, attributes);
}
});
const auth = define.middleware(async (ctx) => {
const pathname = ctx.url.pathname;
const interactive = !pathname.startsWith("/_fresh") &&
!pathname.startsWith("/api") &&
!ctx.url.searchParams.has("__frsh_c");
const { token, sudo } = getCookies(ctx.req.headers);
if (interactive) {
ctx.state.sudo = sudo === "1";
ctx.state.api = new API(API_ROOT, {
token,
sudo: ctx.state.sudo,
span: ctx.state.span,
});
if (ctx.state.api.hasToken()) {
ctx.state.userPromise = (async () => {
const userResp = await ctx.state.api.get<FullUser>(path`/user`);
if (userResp.ok) {
return userResp.data;
} else if (!userResp.ok && userResp.code === "invalidBearerToken") {
// The token is invalid, so delete it.
ctx.state.api = new API(API_ROOT, {
span: ctx.state.span,
token: null,
});
const redirectTarget = `${ctx.url.pathname}${ctx.url.search}`;
const loginUrl = `/login?redirect=${
encodeURIComponent(redirectTarget)
}`;
const resp = new Response("Re-authenticating, token expired", {
status: 303,
headers: { Location: loginUrl },
});
deleteCookie(resp.headers, "token", { path: "/" });
return resp;
} else {
throw userResp;
}
})();
ctx.state.userPromise.catch(() => {}); // don't trigger unhandled rejection
} else {
ctx.state.userPromise = Promise.resolve(null);
}
Object.defineProperty(ctx.state, "user", {
get() {
throw new Error(
"'ctx.state.user' may only be used during rendering - use ctx.state.userPromise to get the user object in handlers.",
);
},
configurable: true,
});
}
return await ctx.next();
});
export const handler: Middleware<State> = [tracing, auth];