From 7759006a76bf9d847b69861c3c1de47bfa8d2857 Mon Sep 17 00:00:00 2001 From: ogzhanolguncu Date: Thu, 26 Sep 2024 15:57:12 +0300 Subject: [PATCH 01/37] feat: add basic components --- apps/dashboard/app/(app)/layout.tsx | 10 +- apps/dashboard/app/(app)/logs/chart.tsx | 168 + apps/dashboard/app/(app)/logs/data.ts | 729 + apps/dashboard/app/(app)/logs/page.tsx | 103 +- apps/dashboard/components/ui/chart.tsx | 365 + apps/dashboard/components/ui/group-button.tsx | 53 + apps/dashboard/components/ui/input.tsx | 44 +- apps/dashboard/package.json | 1 + apps/dashboard/styles/tailwind/base.css | 12 + internal/clickhouse-zod/src/client.ts | 17 +- packages/api/src/openapi.d.ts | 1116 +- pnpm-lock.yaml | 32147 +++++++++------- 12 files changed, 19414 insertions(+), 15351 deletions(-) create mode 100644 apps/dashboard/app/(app)/logs/chart.tsx create mode 100644 apps/dashboard/app/(app)/logs/data.ts create mode 100644 apps/dashboard/components/ui/chart.tsx create mode 100644 apps/dashboard/components/ui/group-button.tsx diff --git a/apps/dashboard/app/(app)/layout.tsx b/apps/dashboard/app/(app)/layout.tsx index 4a4bb69d2f..00628fe3cb 100644 --- a/apps/dashboard/app/(app)/layout.tsx +++ b/apps/dashboard/app/(app)/layout.tsx @@ -40,11 +40,13 @@ export default async function Layout({ children, breadcrumb }: LayoutProps) { />
-
+
{workspace.enabled ? ( <> {/* Hacky way to make the breadcrumbs line up with the Teamswitcher on the left, because that also has h12 */} - {breadcrumb &&
{breadcrumb}
} + {breadcrumb && ( +
{breadcrumb}
+ )} {children} ) : ( @@ -53,7 +55,9 @@ export default async function Layout({ children, breadcrumb }: LayoutProps) { - This workspace is disabled + + This workspace is disabled + Contact{" "} ("desktop"); + + return ( + + + + { + const date = new Date(value); + return date.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + }); + }} + /> + { + return new Date(value).toLocaleDateString("en-US", { + month: "short", + day: "numeric", + year: "numeric", + }); + }} + /> + } + /> + + + + ); +} diff --git a/apps/dashboard/app/(app)/logs/data.ts b/apps/dashboard/app/(app)/logs/data.ts new file mode 100644 index 0000000000..bd76193544 --- /dev/null +++ b/apps/dashboard/app/(app)/logs/data.ts @@ -0,0 +1,729 @@ +export type Log = { + request_id: string; + time: number; + workspace_id: string; + host: string; + method: string; + path: string; + request_headers: string[]; + request_body: string; + response_status: number; + response_headers: string[]; + response_body: string; + error: string; + service_latency: number; +}; +export type ResponseBody = { + keyId: string; + valid: boolean; + meta: Record; + enabled: boolean; + permissions: string[]; + code: + | "VALID" + | "RATE_LIMITED" + | "EXPIRED" + | "USAGE_EXCEEDED" + | "DISABLED" + | "FORBIDDEN" + | "INSUFFICIENT_PERMISSIONS"; +}; + +export const sampleLogs: Log[] = [ + { + request_id: "req_3BNIWyIBih2CvE5HYSC0OdvOznXk", + time: 1727169245831, + workspace_id: "ws_37lf7kSNFoM5qlBoL4fZLXJjttC", + host: "api.example.com", + method: "POST", + path: "/v1/keys.verifyKey", + request_headers: [ + "accept: */*", + "accept-encoding: gzip, deflate", + "content-length: 48", + "content-type: application/json", + "host: api.example.com", + "user-agent: curl/7.68.0", + ], + request_body: '{\n "key": ""\n }', + response_status: 429, + response_headers: [ + "access-control-allow-origin: *", + "content-type: application/json; charset=UTF-8", + "unkey-latency: service=78ms", + "unkey-request-id: req_3BNIWyIBih2CvE5HYSC0OdvOznXk", + "unkey-version: 1.2", + ], + response_body: + '{"keyId":"key_37pjcIKRsTGflXYeRTqZMAnrjyKV","valid":false,"meta":{},"enabled":true,"permissions":[],"code":"RATE_LIMITED"}', + error: "", + service_latency: 89, + }, + { + request_id: "req_4COJXzJCji3DwF6IZTD1PewPznYl", + time: 1727170310922, + workspace_id: "ws_48mg8lTOGpN6rmCpM5gAMYKkujD", + host: "api.unkey.dev", + method: "POST", + path: "/v1/keys.verifyKey", + request_headers: [ + "accept: application/json", + "accept-encoding: gzip", + "content-length: 50", + "content-type: application/json", + "host: api.unkey.dev", + "user-agent: node-fetch/3.3.0", + ], + request_body: '{\n "key": ""\n }', + response_status: 403, + response_headers: [ + "access-control-allow-origin: *", + "content-type: application/json; charset=UTF-8", + "unkey-latency: service=62ms", + "unkey-request-id: req_4COJXzJCji3DwF6IZTD1PewPznYl", + "unkey-version: 1.3", + ], + response_body: + '{"keyId":"key_48qkdJLSuUHgmYZfSUrANBoukzLW","valid":false,"meta":{},"enabled":false,"permissions":[],"code":"FORBIDDEN"}', + error: "", + service_latency: 73, + }, + { + request_id: "req_5DPKYzKDkj4ExG7JAUE2QfxQaoZm", + time: 1727171375013, + workspace_id: "ws_59nh9mUPHqO7snDqN6hBNZLlvkE", + host: "api.keys.example.org", + method: "POST", + path: "/v1/keys.verifyKey", + request_headers: [ + "accept: */*", + "accept-encoding: gzip, deflate, br", + "content-length: 51", + "content-type: application/json", + "host: api.keys.example.org", + "user-agent: axios/0.21.1", + ], + request_body: '{\n "key": ""\n }', + response_status: 200, + response_headers: [ + "access-control-allow-origin: *", + "content-type: application/json; charset=UTF-8", + "unkey-latency: service=45ms", + "unkey-request-id: req_5DPKYzKDkj4ExG7JAUE2QfxQaoZm", + "unkey-version: 1.4", + ], + response_body: + '{"keyId":"key_59rleKMTvVIhnZAgTVsBOCpvlaMX","valid":true,"meta":{},"enabled":true,"permissions":["read","write"],"code":"valid"}', + error: "", + service_latency: 58, + }, + { + request_id: "req_3BNIWyIBih2CvE5HYSC0OdvOznXk", + time: 1727169245831, + workspace_id: "ws_37lf7kSNFoM5qlBoL4fZLXJjttC", + host: "api.example.com", + method: "POST", + path: "/v1/keys.verifyKey", + request_headers: [ + "accept: */*", + "accept-encoding: gzip, deflate", + "content-length: 48", + "content-type: application/json", + "host: api.example.com", + "user-agent: curl/7.68.0", + ], + request_body: '{\n "key": ""\n }', + response_status: 429, + response_headers: [ + "access-control-allow-origin: *", + "content-type: application/json; charset=UTF-8", + "unkey-latency: service=78ms", + "unkey-request-id: req_3BNIWyIBih2CvE5HYSC0OdvOznXk", + "unkey-version: 1.2", + ], + response_body: + '{"keyId":"key_37pjcIKRsTGflXYeRTqZMAnrjyKV","valid":false,"meta":{},"enabled":true,"permissions":[],"code":"RATE_LIMITED"}', + error: "", + service_latency: 89, + }, + { + request_id: "req_4COJXzJCji3DwF6IZTD1PewPznYl", + time: 1727170310922, + workspace_id: "ws_48mg8lTOGpN6rmCpM5gAMYKkujD", + host: "api.unkey.dev", + method: "POST", + path: "/v1/keys.verifyKey", + request_headers: [ + "accept: application/json", + "accept-encoding: gzip", + "content-length: 50", + "content-type: application/json", + "host: api.unkey.dev", + "user-agent: node-fetch/3.3.0", + ], + request_body: '{\n "key": ""\n }', + response_status: 403, + response_headers: [ + "access-control-allow-origin: *", + "content-type: application/json; charset=UTF-8", + "unkey-latency: service=62ms", + "unkey-request-id: req_4COJXzJCji3DwF6IZTD1PewPznYl", + "unkey-version: 1.3", + ], + response_body: + '{"keyId":"key_48qkdJLSuUHgmYZfSUrANBoukzLW","valid":false,"meta":{},"enabled":false,"permissions":[],"code":"FORBIDDEN"}', + error: "", + service_latency: 73, + }, + { + request_id: "req_5DPKYzKDkj4ExG7JAUE2QfxQaoZm", + time: 1727171375013, + workspace_id: "ws_59nh9mUPHqO7snDqN6hBNZLlvkE", + host: "api.keys.example.org", + method: "POST", + path: "/v1/keys.verifyKey", + request_headers: [ + "accept: */*", + "accept-encoding: gzip, deflate, br", + "content-length: 51", + "content-type: application/json", + "host: api.keys.example.org", + "user-agent: axios/0.21.1", + ], + request_body: '{\n "key": ""\n }', + response_status: 200, + response_headers: [ + "access-control-allow-origin: *", + "content-type: application/json; charset=UTF-8", + "unkey-latency: service=45ms", + "unkey-request-id: req_5DPKYzKDkj4ExG7JAUE2QfxQaoZm", + "unkey-version: 1.4", + ], + response_body: + '{"keyId":"key_59rleKMTvVIhnZAgTVsBOCpvlaMX","valid":true,"meta":{},"enabled":true,"permissions":["read","write"],"code":"valid"}', + error: "", + service_latency: 58, + }, + { + request_id: "req_3BNIWyIBih2CvE5HYSC0OdvOznXk", + time: 1727169245831, + workspace_id: "ws_37lf7kSNFoM5qlBoL4fZLXJjttC", + host: "api.example.com", + method: "POST", + path: "/v1/keys.verifyKey", + request_headers: [ + "accept: */*", + "accept-encoding: gzip, deflate", + "content-length: 48", + "content-type: application/json", + "host: api.example.com", + "user-agent: curl/7.68.0", + ], + request_body: '{\n "key": ""\n }', + response_status: 429, + response_headers: [ + "access-control-allow-origin: *", + "content-type: application/json; charset=UTF-8", + "unkey-latency: service=78ms", + "unkey-request-id: req_3BNIWyIBih2CvE5HYSC0OdvOznXk", + "unkey-version: 1.2", + ], + response_body: + '{"keyId":"key_37pjcIKRsTGflXYeRTqZMAnrjyKV","valid":false,"meta":{},"enabled":true,"permissions":[],"code":"RATE_LIMITED"}', + error: "", + service_latency: 89, + }, + { + request_id: "req_4COJXzJCji3DwF6IZTD1PewPznYl", + time: 1727170310922, + workspace_id: "ws_48mg8lTOGpN6rmCpM5gAMYKkujD", + host: "api.unkey.dev", + method: "POST", + path: "/v1/keys.verifyKey", + request_headers: [ + "accept: application/json", + "accept-encoding: gzip", + "content-length: 50", + "content-type: application/json", + "host: api.unkey.dev", + "user-agent: node-fetch/3.3.0", + ], + request_body: '{\n "key": ""\n }', + response_status: 403, + response_headers: [ + "access-control-allow-origin: *", + "content-type: application/json; charset=UTF-8", + "unkey-latency: service=62ms", + "unkey-request-id: req_4COJXzJCji3DwF6IZTD1PewPznYl", + "unkey-version: 1.3", + ], + response_body: + '{"keyId":"key_48qkdJLSuUHgmYZfSUrANBoukzLW","valid":false,"meta":{},"enabled":false,"permissions":[],"code":"FORBIDDEN"}', + error: "", + service_latency: 73, + }, + { + request_id: "req_5DPKYzKDkj4ExG7JAUE2QfxQaoZm", + time: 1727171375013, + workspace_id: "ws_59nh9mUPHqO7snDqN6hBNZLlvkE", + host: "api.keys.example.org", + method: "POST", + path: "/v1/keys.verifyKey", + request_headers: [ + "accept: */*", + "accept-encoding: gzip, deflate, br", + "content-length: 51", + "content-type: application/json", + "host: api.keys.example.org", + "user-agent: axios/0.21.1", + ], + request_body: '{\n "key": ""\n }', + response_status: 200, + response_headers: [ + "access-control-allow-origin: *", + "content-type: application/json; charset=UTF-8", + "unkey-latency: service=45ms", + "unkey-request-id: req_5DPKYzKDkj4ExG7JAUE2QfxQaoZm", + "unkey-version: 1.4", + ], + response_body: + '{"keyId":"key_59rleKMTvVIhnZAgTVsBOCpvlaMX","valid":true,"meta":{},"enabled":true,"permissions":["read","write"],"code":"valid"}', + error: "", + service_latency: 58, + }, + { + request_id: "req_3BNIWyIBih2CvE5HYSC0OdvOznXk", + time: 1727169245831, + workspace_id: "ws_37lf7kSNFoM5qlBoL4fZLXJjttC", + host: "api.example.com", + method: "POST", + path: "/v1/keys.verifyKey", + request_headers: [ + "accept: */*", + "accept-encoding: gzip, deflate", + "content-length: 48", + "content-type: application/json", + "host: api.example.com", + "user-agent: curl/7.68.0", + ], + request_body: '{\n "key": ""\n }', + response_status: 429, + response_headers: [ + "access-control-allow-origin: *", + "content-type: application/json; charset=UTF-8", + "unkey-latency: service=78ms", + "unkey-request-id: req_3BNIWyIBih2CvE5HYSC0OdvOznXk", + "unkey-version: 1.2", + ], + response_body: + '{"keyId":"key_37pjcIKRsTGflXYeRTqZMAnrjyKV","valid":false,"meta":{},"enabled":true,"permissions":[],"code":"RATE_LIMITED"}', + error: "", + service_latency: 89, + }, + { + request_id: "req_4COJXzJCji3DwF6IZTD1PewPznYl", + time: 1727170310922, + workspace_id: "ws_48mg8lTOGpN6rmCpM5gAMYKkujD", + host: "api.unkey.dev", + method: "POST", + path: "/v1/keys.verifyKey", + request_headers: [ + "accept: application/json", + "accept-encoding: gzip", + "content-length: 50", + "content-type: application/json", + "host: api.unkey.dev", + "user-agent: node-fetch/3.3.0", + ], + request_body: '{\n "key": ""\n }', + response_status: 403, + response_headers: [ + "access-control-allow-origin: *", + "content-type: application/json; charset=UTF-8", + "unkey-latency: service=62ms", + "unkey-request-id: req_4COJXzJCji3DwF6IZTD1PewPznYl", + "unkey-version: 1.3", + ], + response_body: + '{"keyId":"key_48qkdJLSuUHgmYZfSUrANBoukzLW","valid":false,"meta":{},"enabled":false,"permissions":[],"code":"FORBIDDEN"}', + error: "", + service_latency: 73, + }, + { + request_id: "req_5DPKYzKDkj4ExG7JAUE2QfxQaoZm", + time: 1727171375013, + workspace_id: "ws_59nh9mUPHqO7snDqN6hBNZLlvkE", + host: "api.keys.example.org", + method: "POST", + path: "/v1/keys.verifyKey", + request_headers: [ + "accept: */*", + "accept-encoding: gzip, deflate, br", + "content-length: 51", + "content-type: application/json", + "host: api.keys.example.org", + "user-agent: axios/0.21.1", + ], + request_body: '{\n "key": ""\n }', + response_status: 200, + response_headers: [ + "access-control-allow-origin: *", + "content-type: application/json; charset=UTF-8", + "unkey-latency: service=45ms", + "unkey-request-id: req_5DPKYzKDkj4ExG7JAUE2QfxQaoZm", + "unkey-version: 1.4", + ], + response_body: + '{"keyId":"key_59rleKMTvVIhnZAgTVsBOCpvlaMX","valid":true,"meta":{},"enabled":true,"permissions":["read","write"],"code":"valid"}', + error: "", + service_latency: 58, + }, + { + request_id: "req_3BNIWyIBih2CvE5HYSC0OdvOznXk", + time: 1727169245831, + workspace_id: "ws_37lf7kSNFoM5qlBoL4fZLXJjttC", + host: "api.example.com", + method: "POST", + path: "/v1/keys.verifyKey", + request_headers: [ + "accept: */*", + "accept-encoding: gzip, deflate", + "content-length: 48", + "content-type: application/json", + "host: api.example.com", + "user-agent: curl/7.68.0", + ], + request_body: '{\n "key": ""\n }', + response_status: 429, + response_headers: [ + "access-control-allow-origin: *", + "content-type: application/json; charset=UTF-8", + "unkey-latency: service=78ms", + "unkey-request-id: req_3BNIWyIBih2CvE5HYSC0OdvOznXk", + "unkey-version: 1.2", + ], + response_body: + '{"keyId":"key_37pjcIKRsTGflXYeRTqZMAnrjyKV","valid":false,"meta":{},"enabled":true,"permissions":[],"code":"RATE_LIMITED"}', + error: "", + service_latency: 89, + }, + { + request_id: "req_4COJXzJCji3DwF6IZTD1PewPznYl", + time: 1727170310922, + workspace_id: "ws_48mg8lTOGpN6rmCpM5gAMYKkujD", + host: "api.unkey.dev", + method: "POST", + path: "/v1/keys.verifyKey", + request_headers: [ + "accept: application/json", + "accept-encoding: gzip", + "content-length: 50", + "content-type: application/json", + "host: api.unkey.dev", + "user-agent: node-fetch/3.3.0", + ], + request_body: '{\n "key": ""\n }', + response_status: 403, + response_headers: [ + "access-control-allow-origin: *", + "content-type: application/json; charset=UTF-8", + "unkey-latency: service=62ms", + "unkey-request-id: req_4COJXzJCji3DwF6IZTD1PewPznYl", + "unkey-version: 1.3", + ], + response_body: + '{"keyId":"key_48qkdJLSuUHgmYZfSUrANBoukzLW","valid":false,"meta":{},"enabled":false,"permissions":[],"code":"FORBIDDEN"}', + error: "", + service_latency: 73, + }, + { + request_id: "req_5DPKYzKDkj4ExG7JAUE2QfxQaoZm", + time: 1727171375013, + workspace_id: "ws_59nh9mUPHqO7snDqN6hBNZLlvkE", + host: "api.keys.example.org", + method: "POST", + path: "/v1/keys.verifyKey", + request_headers: [ + "accept: */*", + "accept-encoding: gzip, deflate, br", + "content-length: 51", + "content-type: application/json", + "host: api.keys.example.org", + "user-agent: axios/0.21.1", + ], + request_body: '{\n "key": ""\n }', + response_status: 200, + response_headers: [ + "access-control-allow-origin: *", + "content-type: application/json; charset=UTF-8", + "unkey-latency: service=45ms", + "unkey-request-id: req_5DPKYzKDkj4ExG7JAUE2QfxQaoZm", + "unkey-version: 1.4", + ], + response_body: + '{"keyId":"key_59rleKMTvVIhnZAgTVsBOCpvlaMX","valid":true,"meta":{},"enabled":true,"permissions":["read","write"],"code":"valid"}', + error: "", + service_latency: 58, + }, + { + request_id: "req_3BNIWyIBih2CvE5HYSC0OdvOznXk", + time: 1727169245831, + workspace_id: "ws_37lf7kSNFoM5qlBoL4fZLXJjttC", + host: "api.example.com", + method: "POST", + path: "/v1/keys.verifyKey", + request_headers: [ + "accept: */*", + "accept-encoding: gzip, deflate", + "content-length: 48", + "content-type: application/json", + "host: api.example.com", + "user-agent: curl/7.68.0", + ], + request_body: '{\n "key": ""\n }', + response_status: 429, + response_headers: [ + "access-control-allow-origin: *", + "content-type: application/json; charset=UTF-8", + "unkey-latency: service=78ms", + "unkey-request-id: req_3BNIWyIBih2CvE5HYSC0OdvOznXk", + "unkey-version: 1.2", + ], + response_body: + '{"keyId":"key_37pjcIKRsTGflXYeRTqZMAnrjyKV","valid":false,"meta":{},"enabled":true,"permissions":[],"code":"RATE_LIMITED"}', + error: "", + service_latency: 89, + }, + { + request_id: "req_4COJXzJCji3DwF6IZTD1PewPznYl", + time: 1727170310922, + workspace_id: "ws_48mg8lTOGpN6rmCpM5gAMYKkujD", + host: "api.unkey.dev", + method: "POST", + path: "/v1/keys.verifyKey", + request_headers: [ + "accept: application/json", + "accept-encoding: gzip", + "content-length: 50", + "content-type: application/json", + "host: api.unkey.dev", + "user-agent: node-fetch/3.3.0", + ], + request_body: '{\n "key": ""\n }', + response_status: 403, + response_headers: [ + "access-control-allow-origin: *", + "content-type: application/json; charset=UTF-8", + "unkey-latency: service=62ms", + "unkey-request-id: req_4COJXzJCji3DwF6IZTD1PewPznYl", + "unkey-version: 1.3", + ], + response_body: + '{"keyId":"key_48qkdJLSuUHgmYZfSUrANBoukzLW","valid":false,"meta":{},"enabled":false,"permissions":[],"code":"FORBIDDEN"}', + error: "", + service_latency: 73, + }, + { + request_id: "req_5DPKYzKDkj4ExG7JAUE2QfxQaoZm", + time: 1727171375013, + workspace_id: "ws_59nh9mUPHqO7snDqN6hBNZLlvkE", + host: "api.keys.example.org", + method: "POST", + path: "/v1/keys.verifyKey", + request_headers: [ + "accept: */*", + "accept-encoding: gzip, deflate, br", + "content-length: 51", + "content-type: application/json", + "host: api.keys.example.org", + "user-agent: axios/0.21.1", + ], + request_body: '{\n "key": ""\n }', + response_status: 200, + response_headers: [ + "access-control-allow-origin: *", + "content-type: application/json; charset=UTF-8", + "unkey-latency: service=45ms", + "unkey-request-id: req_5DPKYzKDkj4ExG7JAUE2QfxQaoZm", + "unkey-version: 1.4", + ], + response_body: + '{"keyId":"key_59rleKMTvVIhnZAgTVsBOCpvlaMX","valid":true,"meta":{},"enabled":true,"permissions":["read","write"],"code":"valid"}', + error: "", + service_latency: 58, + }, + { + request_id: "req_3BNIWyIBih2CvE5HYSC0OdvOznXk", + time: 1727169245831, + workspace_id: "ws_37lf7kSNFoM5qlBoL4fZLXJjttC", + host: "api.example.com", + method: "POST", + path: "/v1/keys.verifyKey", + request_headers: [ + "accept: */*", + "accept-encoding: gzip, deflate", + "content-length: 48", + "content-type: application/json", + "host: api.example.com", + "user-agent: curl/7.68.0", + ], + request_body: '{\n "key": ""\n }', + response_status: 429, + response_headers: [ + "access-control-allow-origin: *", + "content-type: application/json; charset=UTF-8", + "unkey-latency: service=78ms", + "unkey-request-id: req_3BNIWyIBih2CvE5HYSC0OdvOznXk", + "unkey-version: 1.2", + ], + response_body: + '{"keyId":"key_37pjcIKRsTGflXYeRTqZMAnrjyKV","valid":false,"meta":{},"enabled":true,"permissions":[],"code":"RATE_LIMITED"}', + error: "", + service_latency: 89, + }, + { + request_id: "req_4COJXzJCji3DwF6IZTD1PewPznYl", + time: 1727170310922, + workspace_id: "ws_48mg8lTOGpN6rmCpM5gAMYKkujD", + host: "api.unkey.dev", + method: "POST", + path: "/v1/keys.verifyKey", + request_headers: [ + "accept: application/json", + "accept-encoding: gzip", + "content-length: 50", + "content-type: application/json", + "host: api.unkey.dev", + "user-agent: node-fetch/3.3.0", + ], + request_body: '{\n "key": ""\n }', + response_status: 403, + response_headers: [ + "access-control-allow-origin: *", + "content-type: application/json; charset=UTF-8", + "unkey-latency: service=62ms", + "unkey-request-id: req_4COJXzJCji3DwF6IZTD1PewPznYl", + "unkey-version: 1.3", + ], + response_body: + '{"keyId":"key_48qkdJLSuUHgmYZfSUrANBoukzLW","valid":false,"meta":{},"enabled":false,"permissions":[],"code":"FORBIDDEN"}', + error: "", + service_latency: 73, + }, + { + request_id: "req_5DPKYzKDkj4ExG7JAUE2QfxQaoZm", + time: 1727171375013, + workspace_id: "ws_59nh9mUPHqO7snDqN6hBNZLlvkE", + host: "api.keys.example.org", + method: "POST", + path: "/v1/keys.verifyKey", + request_headers: [ + "accept: */*", + "accept-encoding: gzip, deflate, br", + "content-length: 51", + "content-type: application/json", + "host: api.keys.example.org", + "user-agent: axios/0.21.1", + ], + request_body: '{\n "key": ""\n }', + response_status: 200, + response_headers: [ + "access-control-allow-origin: *", + "content-type: application/json; charset=UTF-8", + "unkey-latency: service=45ms", + "unkey-request-id: req_5DPKYzKDkj4ExG7JAUE2QfxQaoZm", + "unkey-version: 1.4", + ], + response_body: + '{"keyId":"key_59rleKMTvVIhnZAgTVsBOCpvlaMX","valid":true,"meta":{},"enabled":true,"permissions":["read","write"],"code":"valid"}', + error: "", + service_latency: 58, + }, + { + request_id: "req_3BNIWyIBih2CvE5HYSC0OdvOznXk", + time: 1727169245831, + workspace_id: "ws_37lf7kSNFoM5qlBoL4fZLXJjttC", + host: "api.example.com", + method: "POST", + path: "/v1/keys.verifyKey", + request_headers: [ + "accept: */*", + "accept-encoding: gzip, deflate", + "content-length: 48", + "content-type: application/json", + "host: api.example.com", + "user-agent: curl/7.68.0", + ], + request_body: '{\n "key": ""\n }', + response_status: 429, + response_headers: [ + "access-control-allow-origin: *", + "content-type: application/json; charset=UTF-8", + "unkey-latency: service=78ms", + "unkey-request-id: req_3BNIWyIBih2CvE5HYSC0OdvOznXk", + "unkey-version: 1.2", + ], + response_body: + '{"keyId":"key_37pjcIKRsTGflXYeRTqZMAnrjyKV","valid":false,"meta":{},"enabled":true,"permissions":[],"code":"RATE_LIMITED"}', + error: "", + service_latency: 89, + }, + { + request_id: "req_4COJXzJCji3DwF6IZTD1PewPznYl", + time: 1727170310922, + workspace_id: "ws_48mg8lTOGpN6rmCpM5gAMYKkujD", + host: "api.unkey.dev", + method: "POST", + path: "/v1/keys.verifyKey", + request_headers: [ + "accept: application/json", + "accept-encoding: gzip", + "content-length: 50", + "content-type: application/json", + "host: api.unkey.dev", + "user-agent: node-fetch/3.3.0", + ], + request_body: '{\n "key": ""\n }', + response_status: 403, + response_headers: [ + "access-control-allow-origin: *", + "content-type: application/json; charset=UTF-8", + "unkey-latency: service=62ms", + "unkey-request-id: req_4COJXzJCji3DwF6IZTD1PewPznYl", + "unkey-version: 1.3", + ], + response_body: + '{"keyId":"key_48qkdJLSuUHgmYZfSUrANBoukzLW","valid":false,"meta":{},"enabled":false,"permissions":[],"code":"FORBIDDEN"}', + error: "", + service_latency: 73, + }, + { + request_id: "req_5DPKYzKDkj4ExG7JAUE2QfxQaoZm", + time: 1727171375013, + workspace_id: "ws_59nh9mUPHqO7snDqN6hBNZLlvkE", + host: "api.keys.example.org", + method: "POST", + path: "/v1/keys.verifyKey", + request_headers: [ + "accept: */*", + "accept-encoding: gzip, deflate, br", + "content-length: 51", + "content-type: application/json", + "host: api.keys.example.org", + "user-agent: axios/0.21.1", + ], + request_body: '{\n "key": ""\n }', + response_status: 200, + response_headers: [ + "access-control-allow-origin: *", + "content-type: application/json; charset=UTF-8", + "unkey-latency: service=45ms", + "unkey-request-id: req_5DPKYzKDkj4ExG7JAUE2QfxQaoZm", + "unkey-version: 1.4", + ], + response_body: + '{"keyId":"key_59rleKMTvVIhnZAgTVsBOCpvlaMX","valid":true,"meta":{},"enabled":true,"permissions":["read","write"],"code":"valid"}', + error: "", + service_latency: 58, + }, +]; diff --git a/apps/dashboard/app/(app)/logs/page.tsx b/apps/dashboard/app/(app)/logs/page.tsx index a8a40fbe32..0db4fc4ab7 100644 --- a/apps/dashboard/app/(app)/logs/page.tsx +++ b/apps/dashboard/app/(app)/logs/page.tsx @@ -1,28 +1,93 @@ -import { PageHeader } from "@/components/dashboard/page-header"; -import { getTenantId } from "@/lib/auth"; -import { getLogs } from "@/lib/clickhouse"; -import { db } from "@/lib/db"; +import { cn } from "@/lib/utils"; +import { format } from "date-fns"; +import { Log, ResponseBody, sampleLogs } from "./data"; +import { Badge } from "@/components/ui/badge"; +import { Input } from "@/components/ui/input"; +import { Calendar, Clock, RefreshCcw, Search } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { ButtonGroup } from "@/components/ui/group-button"; +import { ChartsComp } from "./chart"; export const revalidate = 0; -export default async function Page() { - const tenantId = getTenantId(); - - const workspace = await db.query.workspaces.findFirst({ - where: (table, { and, eq, isNull }) => - and(eq(table.tenantId, tenantId), isNull(table.deletedAt)), - }); - if (!workspace) { - return
Workspace with tenantId: {tenantId} not found
; - } - - const logs = await getLogs({ workspaceId: workspace.id, limit: 10 }); +const YELLOW_STATES = ["RATE_LIMITED", "EXPIRED", "USAGE_EXCEEDED"]; +const RED_STATES = ["DISABLED", "FORBIDDEN", "INSUFFICIENT_PERMISSIONS"]; +export default async function Page() { return ( -
- +
+ {/* Filter Section */} +
+
+ +
+ + + + + -
{JSON.stringify(logs, null, 2)}
+ + + + + + +
+ + {/* Logs section */} +
+
+
Time
+
Status
+
Host
+
Request
+
Message
+
+
+ {sampleLogs.map((log, index) => ( +
+
+ {format(log.time, "MMM dd HH:mm:ss.SS")} +
+
+ {log.response_status} +
+
{log.host}
+
{log.path}
+
+ {log.response_body} +
+
+ ))} +
); } + +//TODO: parsing might fail add check +const getOutcome = (log: Log) => { + return (JSON.parse(log.response_body) as ResponseBody).code; +}; diff --git a/apps/dashboard/components/ui/chart.tsx b/apps/dashboard/components/ui/chart.tsx new file mode 100644 index 0000000000..8620baa3b3 --- /dev/null +++ b/apps/dashboard/components/ui/chart.tsx @@ -0,0 +1,365 @@ +"use client" + +import * as React from "react" +import * as RechartsPrimitive from "recharts" + +import { cn } from "@/lib/utils" + +// Format: { THEME_NAME: CSS_SELECTOR } +const THEMES = { light: "", dark: ".dark" } as const + +export type ChartConfig = { + [k in string]: { + label?: React.ReactNode + icon?: React.ComponentType + } & ( + | { color?: string; theme?: never } + | { color?: never; theme: Record } + ) +} + +type ChartContextProps = { + config: ChartConfig +} + +const ChartContext = React.createContext(null) + +function useChart() { + const context = React.useContext(ChartContext) + + if (!context) { + throw new Error("useChart must be used within a ") + } + + return context +} + +const ChartContainer = React.forwardRef< + HTMLDivElement, + React.ComponentProps<"div"> & { + config: ChartConfig + children: React.ComponentProps< + typeof RechartsPrimitive.ResponsiveContainer + >["children"] + } +>(({ id, className, children, config, ...props }, ref) => { + const uniqueId = React.useId() + const chartId = `chart-${id || uniqueId.replace(/:/g, "")}` + + return ( + +
+ + + {children} + +
+
+ ) +}) +ChartContainer.displayName = "Chart" + +const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { + const colorConfig = Object.entries(config).filter( + ([_, config]) => config.theme || config.color + ) + + if (!colorConfig.length) { + return null + } + + return ( +