feat: performance tracker
This commit is contained in:
parent
1cf0d91af5
commit
59ddcb64a6
132
fresh.gen.ts
132
fresh.gen.ts
@ -5,38 +5,40 @@
|
||||
import * as $0 from "./routes/_404.tsx";
|
||||
import * as $1 from "./routes/_app.tsx";
|
||||
import * as $2 from "./routes/_middleware.ts";
|
||||
import * as $3 from "./routes/api/articles/[name].ts";
|
||||
import * as $4 from "./routes/api/articles/create/index.ts";
|
||||
import * as $5 from "./routes/api/articles/index.ts";
|
||||
import * as $6 from "./routes/api/auth/callback.ts";
|
||||
import * as $7 from "./routes/api/auth/login.ts";
|
||||
import * as $8 from "./routes/api/auth/logout.ts";
|
||||
import * as $9 from "./routes/api/cache/index.ts";
|
||||
import * as $10 from "./routes/api/images/index.ts";
|
||||
import * as $11 from "./routes/api/index.ts";
|
||||
import * as $12 from "./routes/api/movies/[name].ts";
|
||||
import * as $13 from "./routes/api/movies/enhance/[name].ts";
|
||||
import * as $14 from "./routes/api/movies/index.ts";
|
||||
import * as $15 from "./routes/api/query/index.ts";
|
||||
import * as $16 from "./routes/api/query/sync.ts";
|
||||
import * as $17 from "./routes/api/recipes/[name].ts";
|
||||
import * as $18 from "./routes/api/recipes/index.ts";
|
||||
import * as $19 from "./routes/api/resources.ts";
|
||||
import * as $20 from "./routes/api/series/[name].ts";
|
||||
import * as $21 from "./routes/api/series/enhance/[name].ts";
|
||||
import * as $22 from "./routes/api/series/index.ts";
|
||||
import * as $23 from "./routes/api/tmdb/[id].ts";
|
||||
import * as $24 from "./routes/api/tmdb/credits/[id].ts";
|
||||
import * as $25 from "./routes/api/tmdb/query.ts";
|
||||
import * as $26 from "./routes/articles/[name].tsx";
|
||||
import * as $27 from "./routes/articles/index.tsx";
|
||||
import * as $28 from "./routes/index.tsx";
|
||||
import * as $29 from "./routes/movies/[name].tsx";
|
||||
import * as $30 from "./routes/movies/index.tsx";
|
||||
import * as $31 from "./routes/recipes/[name].tsx";
|
||||
import * as $32 from "./routes/recipes/index.tsx";
|
||||
import * as $33 from "./routes/series/[name].tsx";
|
||||
import * as $34 from "./routes/series/index.tsx";
|
||||
import * as $3 from "./routes/admin/performance/api.ts";
|
||||
import * as $4 from "./routes/admin/performance/index.tsx";
|
||||
import * as $5 from "./routes/api/articles/[name].ts";
|
||||
import * as $6 from "./routes/api/articles/create/index.ts";
|
||||
import * as $7 from "./routes/api/articles/index.ts";
|
||||
import * as $8 from "./routes/api/auth/callback.ts";
|
||||
import * as $9 from "./routes/api/auth/login.ts";
|
||||
import * as $10 from "./routes/api/auth/logout.ts";
|
||||
import * as $11 from "./routes/api/cache/index.ts";
|
||||
import * as $12 from "./routes/api/images/index.ts";
|
||||
import * as $13 from "./routes/api/index.ts";
|
||||
import * as $14 from "./routes/api/movies/[name].ts";
|
||||
import * as $15 from "./routes/api/movies/enhance/[name].ts";
|
||||
import * as $16 from "./routes/api/movies/index.ts";
|
||||
import * as $17 from "./routes/api/query/index.ts";
|
||||
import * as $18 from "./routes/api/query/sync.ts";
|
||||
import * as $19 from "./routes/api/recipes/[name].ts";
|
||||
import * as $20 from "./routes/api/recipes/index.ts";
|
||||
import * as $21 from "./routes/api/resources.ts";
|
||||
import * as $22 from "./routes/api/series/[name].ts";
|
||||
import * as $23 from "./routes/api/series/enhance/[name].ts";
|
||||
import * as $24 from "./routes/api/series/index.ts";
|
||||
import * as $25 from "./routes/api/tmdb/[id].ts";
|
||||
import * as $26 from "./routes/api/tmdb/credits/[id].ts";
|
||||
import * as $27 from "./routes/api/tmdb/query.ts";
|
||||
import * as $28 from "./routes/articles/[name].tsx";
|
||||
import * as $29 from "./routes/articles/index.tsx";
|
||||
import * as $30 from "./routes/index.tsx";
|
||||
import * as $31 from "./routes/movies/[name].tsx";
|
||||
import * as $32 from "./routes/movies/index.tsx";
|
||||
import * as $33 from "./routes/recipes/[name].tsx";
|
||||
import * as $34 from "./routes/recipes/index.tsx";
|
||||
import * as $35 from "./routes/series/[name].tsx";
|
||||
import * as $36 from "./routes/series/index.tsx";
|
||||
import * as $$0 from "./islands/Counter.tsx";
|
||||
import * as $$1 from "./islands/IngredientsList.tsx";
|
||||
import * as $$2 from "./islands/KMenu.tsx";
|
||||
@ -54,38 +56,40 @@ const manifest = {
|
||||
"./routes/_404.tsx": $0,
|
||||
"./routes/_app.tsx": $1,
|
||||
"./routes/_middleware.ts": $2,
|
||||
"./routes/api/articles/[name].ts": $3,
|
||||
"./routes/api/articles/create/index.ts": $4,
|
||||
"./routes/api/articles/index.ts": $5,
|
||||
"./routes/api/auth/callback.ts": $6,
|
||||
"./routes/api/auth/login.ts": $7,
|
||||
"./routes/api/auth/logout.ts": $8,
|
||||
"./routes/api/cache/index.ts": $9,
|
||||
"./routes/api/images/index.ts": $10,
|
||||
"./routes/api/index.ts": $11,
|
||||
"./routes/api/movies/[name].ts": $12,
|
||||
"./routes/api/movies/enhance/[name].ts": $13,
|
||||
"./routes/api/movies/index.ts": $14,
|
||||
"./routes/api/query/index.ts": $15,
|
||||
"./routes/api/query/sync.ts": $16,
|
||||
"./routes/api/recipes/[name].ts": $17,
|
||||
"./routes/api/recipes/index.ts": $18,
|
||||
"./routes/api/resources.ts": $19,
|
||||
"./routes/api/series/[name].ts": $20,
|
||||
"./routes/api/series/enhance/[name].ts": $21,
|
||||
"./routes/api/series/index.ts": $22,
|
||||
"./routes/api/tmdb/[id].ts": $23,
|
||||
"./routes/api/tmdb/credits/[id].ts": $24,
|
||||
"./routes/api/tmdb/query.ts": $25,
|
||||
"./routes/articles/[name].tsx": $26,
|
||||
"./routes/articles/index.tsx": $27,
|
||||
"./routes/index.tsx": $28,
|
||||
"./routes/movies/[name].tsx": $29,
|
||||
"./routes/movies/index.tsx": $30,
|
||||
"./routes/recipes/[name].tsx": $31,
|
||||
"./routes/recipes/index.tsx": $32,
|
||||
"./routes/series/[name].tsx": $33,
|
||||
"./routes/series/index.tsx": $34,
|
||||
"./routes/admin/performance/api.ts": $3,
|
||||
"./routes/admin/performance/index.tsx": $4,
|
||||
"./routes/api/articles/[name].ts": $5,
|
||||
"./routes/api/articles/create/index.ts": $6,
|
||||
"./routes/api/articles/index.ts": $7,
|
||||
"./routes/api/auth/callback.ts": $8,
|
||||
"./routes/api/auth/login.ts": $9,
|
||||
"./routes/api/auth/logout.ts": $10,
|
||||
"./routes/api/cache/index.ts": $11,
|
||||
"./routes/api/images/index.ts": $12,
|
||||
"./routes/api/index.ts": $13,
|
||||
"./routes/api/movies/[name].ts": $14,
|
||||
"./routes/api/movies/enhance/[name].ts": $15,
|
||||
"./routes/api/movies/index.ts": $16,
|
||||
"./routes/api/query/index.ts": $17,
|
||||
"./routes/api/query/sync.ts": $18,
|
||||
"./routes/api/recipes/[name].ts": $19,
|
||||
"./routes/api/recipes/index.ts": $20,
|
||||
"./routes/api/resources.ts": $21,
|
||||
"./routes/api/series/[name].ts": $22,
|
||||
"./routes/api/series/enhance/[name].ts": $23,
|
||||
"./routes/api/series/index.ts": $24,
|
||||
"./routes/api/tmdb/[id].ts": $25,
|
||||
"./routes/api/tmdb/credits/[id].ts": $26,
|
||||
"./routes/api/tmdb/query.ts": $27,
|
||||
"./routes/articles/[name].tsx": $28,
|
||||
"./routes/articles/index.tsx": $29,
|
||||
"./routes/index.tsx": $30,
|
||||
"./routes/movies/[name].tsx": $31,
|
||||
"./routes/movies/index.tsx": $32,
|
||||
"./routes/recipes/[name].tsx": $33,
|
||||
"./routes/recipes/index.tsx": $34,
|
||||
"./routes/series/[name].tsx": $35,
|
||||
"./routes/series/index.tsx": $36,
|
||||
},
|
||||
islands: {
|
||||
"./islands/Counter.tsx": $$0,
|
||||
|
86
lib/cache/performance.ts
vendored
Normal file
86
lib/cache/performance.ts
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
import * as cache from "@lib/cache/cache.ts";
|
||||
|
||||
export type PerformancePoint = {
|
||||
path: string;
|
||||
search: string;
|
||||
time: number;
|
||||
date: Date;
|
||||
};
|
||||
|
||||
export type PerformanceRes = {
|
||||
max: number;
|
||||
res: {
|
||||
url: string;
|
||||
data: readonly [number, number, number, number];
|
||||
}[];
|
||||
};
|
||||
|
||||
export const savePerformance = (url: string, milliseconds: number) => {
|
||||
const d = new Date();
|
||||
const year = d.getFullYear();
|
||||
const month = d.getMonth().toString().padStart(2, "0");
|
||||
const day = d.getDay().toString().padStart(2, "0");
|
||||
const hour = d.getHours().toString().padStart(2, "0");
|
||||
const minute = d.getMinutes().toString().padStart(2, "0");
|
||||
const seconds = d.getSeconds().toString().padStart(2, "0");
|
||||
|
||||
const u = new URL(url);
|
||||
if (u.pathname.includes("_frsh/")) return;
|
||||
u.searchParams.delete("__frsh_c");
|
||||
|
||||
cache.set(
|
||||
`performance:${year}:${month}:${day}:${hour}:${minute}:${seconds}`,
|
||||
JSON.stringify({
|
||||
path: decodeURIComponent(u.pathname),
|
||||
search: u.search,
|
||||
time: Math.floor(milliseconds * 1000),
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
export async function getPerformances(): Promise<PerformanceRes> {
|
||||
const d = new Date();
|
||||
const year = d.getFullYear();
|
||||
const month = d.getMonth().toString().padStart(2, "0");
|
||||
const day = d.getDay().toString().padStart(2, "0");
|
||||
|
||||
const keys = await cache.keys(
|
||||
`performance:${year}:${month}:${day}:*`,
|
||||
);
|
||||
|
||||
const performances = await Promise.all(
|
||||
keys.map(async (key) =>
|
||||
JSON.parse(await cache.get<string>(key)) as PerformancePoint
|
||||
),
|
||||
);
|
||||
|
||||
let maximum = 0;
|
||||
|
||||
const res = new Map<string, number[]>();
|
||||
for (const p of performances) {
|
||||
const id = p.path;
|
||||
const timings = res.get(id) || [];
|
||||
if (p.time > maximum) maximum = p.time;
|
||||
timings.push(p.time);
|
||||
res.set(id, timings);
|
||||
}
|
||||
|
||||
return {
|
||||
max: maximum,
|
||||
res: [...res.entries()].map(([url, timings]) => {
|
||||
let total = 0;
|
||||
let min = Infinity;
|
||||
let max = -Infinity;
|
||||
for (const t of timings) {
|
||||
total += t;
|
||||
if (t < min) min = t;
|
||||
if (t > max) max = t;
|
||||
}
|
||||
|
||||
return {
|
||||
url,
|
||||
data: [timings.length, min, total / timings.length, max] as const,
|
||||
};
|
||||
}).sort((a, b) => a.data[2] < b.data[2] ? 1 : -1),
|
||||
};
|
||||
}
|
@ -71,8 +71,6 @@ export async function searchResource(
|
||||
}
|
||||
}
|
||||
|
||||
console.log({ q, query_by, filter_by });
|
||||
|
||||
return await typesenseClient.collections("resources")
|
||||
.documents().search({
|
||||
q,
|
||||
|
@ -36,6 +36,7 @@ export default function App({ Component }: AppProps) {
|
||||
:root {
|
||||
--background: rgb(43, 41, 48);
|
||||
--foreground: rgb(129, 129, 129);
|
||||
--light: #2B2930;
|
||||
}
|
||||
.custom-grid {
|
||||
grid-template-columns: repeat(auto-fit, minmax(37vw, 1fr)) ;
|
||||
|
@ -2,17 +2,17 @@
|
||||
import { MiddlewareHandlerContext } from "$fresh/server.ts";
|
||||
import { DomainError } from "@lib/errors.ts";
|
||||
import { getCookies } from "https://deno.land/std@0.197.0/http/cookie.ts";
|
||||
import { decode, verify } from "https://deno.land/x/djwt@v2.2/mod.ts";
|
||||
import { sessionDB, userDB } from "@lib/db.ts";
|
||||
import { verify } from "https://deno.land/x/djwt@v2.2/mod.ts";
|
||||
import * as cache from "@lib/cache/performance.ts";
|
||||
import { JWT_SECRET } from "@lib/env.ts";
|
||||
|
||||
export async function handler(
|
||||
_req: Request,
|
||||
req: Request,
|
||||
ctx: MiddlewareHandlerContext,
|
||||
) {
|
||||
try {
|
||||
ctx.state.flag = true;
|
||||
const allCookies = getCookies(_req.headers);
|
||||
performance.mark("a");
|
||||
const allCookies = getCookies(req.headers);
|
||||
const sessionCookie = allCookies["session_cookie"];
|
||||
if (!ctx.state.session && sessionCookie && JWT_SECRET) {
|
||||
try {
|
||||
@ -22,9 +22,15 @@ export async function handler(
|
||||
}
|
||||
} catch (_err) {
|
||||
//
|
||||
console.log({ _err });
|
||||
}
|
||||
}
|
||||
return await ctx.next();
|
||||
|
||||
const resp = await ctx.next();
|
||||
performance.mark("b");
|
||||
const b = performance.measure("a->b", "a", "b");
|
||||
cache.savePerformance(req.url, b.duration);
|
||||
return resp;
|
||||
} catch (error) {
|
||||
console.error("Error", error);
|
||||
|
||||
|
9
routes/admin/performance/api.ts
Normal file
9
routes/admin/performance/api.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { Handlers } from "$fresh/server.ts";
|
||||
import { getPerformances } from "@lib/cache/performance.ts";
|
||||
import { json } from "@lib/helpers.ts";
|
||||
|
||||
export const handler: Handlers = {
|
||||
async GET() {
|
||||
return json(await getPerformances());
|
||||
},
|
||||
};
|
93
routes/admin/performance/index.tsx
Normal file
93
routes/admin/performance/index.tsx
Normal file
@ -0,0 +1,93 @@
|
||||
import { MainLayout } from "@components/layouts/main.tsx";
|
||||
import { Handlers, PageProps } from "$fresh/server.ts";
|
||||
import {
|
||||
getPerformances,
|
||||
PerformancePoint,
|
||||
PerformanceRes,
|
||||
} from "@lib/cache/performance.ts";
|
||||
import { AccessDeniedError } from "@lib/errors.ts";
|
||||
|
||||
export const handler: Handlers = {
|
||||
async GET(_, ctx) {
|
||||
const performances = await getPerformances();
|
||||
if (!("session" in ctx.state)) {
|
||||
throw new AccessDeniedError();
|
||||
}
|
||||
|
||||
return ctx.render({ performances });
|
||||
},
|
||||
};
|
||||
|
||||
function PerformanceLine(
|
||||
{ maximum, data: [amount, min, average, max], url }: {
|
||||
maximum: number;
|
||||
url: string;
|
||||
data: readonly [number, number, number, number];
|
||||
},
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
class="mt-10 flex flex-col gap-2 bg-gray-900 px-4 py-2 rounded-2xl"
|
||||
style={{ background: "var(--light)" }}
|
||||
>
|
||||
<div style={{ color: "var(--foreground)" }}>
|
||||
{url}
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<span class="bg-gray-600 p-1 text-xs rounded-xl text-white">
|
||||
{Math.floor(average / 1000)}ms
|
||||
</span>
|
||||
<span class="bg-gray-600 p-1 text-xs rounded-xl text-white">
|
||||
{amount}x
|
||||
</span>
|
||||
</div>
|
||||
<div class="" style={{ maxWidth: "75vw" }}>
|
||||
<div
|
||||
class="h-2 bg-red-200 rounded"
|
||||
style={{
|
||||
width: `${Math.floor((max / maximum) * 100)}%`,
|
||||
borderRadius: "1rem 1rem 1rem 0px",
|
||||
minWidth: "5px",
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
class="h-2 bg-green-200 rounded"
|
||||
style={{
|
||||
width: `${Math.floor((average / maximum) * 100)}%`,
|
||||
borderStartEndRadius: "0px",
|
||||
borderRadius: "0px 0px 1rem 0px",
|
||||
minWidth: "5px",
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
class="h-2 bg-yellow-200 rounded"
|
||||
style={{
|
||||
width: `${Math.floor((min / maximum) * 100)}%`,
|
||||
borderRadius: "0px 0px 1rem 1rem",
|
||||
minWidth: "5px",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Greet(
|
||||
{ data: { performances }, url }: PageProps<{ performances: PerformanceRes }>,
|
||||
) {
|
||||
return (
|
||||
<MainLayout url={url}>
|
||||
<h1 class="text-white text-4xl ">Performance</h1>
|
||||
|
||||
{performances.res.map((r) => {
|
||||
return (
|
||||
<PerformanceLine
|
||||
maximum={performances.max}
|
||||
url={r.url}
|
||||
data={r.data}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</MainLayout>
|
||||
);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user