148 lines
4.1 KiB
TypeScript
148 lines
4.1 KiB
TypeScript
import { NoopTinybird, Tinybird } from "@chronark/zod-bird";
|
|
import { newId } from "@aigxion/id";
|
|
import { auditLogSchemaV1, unkeyAuditLogEvents } from "@aigxion/schema/src/auditlog";
|
|
import { ratelimitSchemaV1 } from "@aigxion/schema/src/ratelimit-tinybird";
|
|
import { z } from "zod";
|
|
import { MaybeArray } from "./types/maybe";
|
|
// const datetimeToUnixMilli = z.string().transform((t) => new Date(t).getTime());
|
|
|
|
/**
|
|
* `t` has the format `2021-01-01 00:00:00`
|
|
*
|
|
* If we transform it as is, we get `1609459200000` which is `2021-01-01 01:00:00` due to fun timezone stuff.
|
|
* So we split the string at the space and take the date part, and then parse that.
|
|
*/
|
|
const dateToUnixMilli = z.string().transform((t) => new Date(t.split(" ").at(0) ?? t).getTime());
|
|
|
|
export class Analytics {
|
|
public readonly client: Tinybird | NoopTinybird;
|
|
|
|
constructor(token?: string) {
|
|
this.client = token ? new Tinybird({ token }) : new NoopTinybird();
|
|
}
|
|
|
|
public get ingestSdkTelemetry() {
|
|
return this.client.buildIngestEndpoint({
|
|
datasource: "sdk_telemetry__v1",
|
|
event: z.object({
|
|
runtime: z.string(),
|
|
platform: z.string(),
|
|
versions: z.array(z.string()),
|
|
requestId: z.string(),
|
|
time: z.number(),
|
|
}),
|
|
});
|
|
}
|
|
|
|
public ingestAuditLogs(
|
|
logs: MaybeArray<{
|
|
workspaceId: string;
|
|
event: z.infer<typeof unkeyAuditLogEvents>;
|
|
description: string;
|
|
actor: {
|
|
type: "user" | "key";
|
|
name?: string;
|
|
id: string;
|
|
};
|
|
resources: Array<{
|
|
type:
|
|
| "key"
|
|
| "api"
|
|
| "workspace"
|
|
| "role"
|
|
| "permission"
|
|
| "keyAuth"
|
|
| "vercelBinding"
|
|
| "vercelIntegration";
|
|
id: string;
|
|
meta?: Record<string, string | number | boolean>;
|
|
}>;
|
|
context: {
|
|
userAgent?: string;
|
|
location: string;
|
|
};
|
|
}>,
|
|
) {
|
|
return this.client.buildIngestEndpoint({
|
|
datasource: "audit_logs__v2",
|
|
event: auditLogSchemaV1
|
|
.merge(
|
|
z.object({
|
|
event: unkeyAuditLogEvents,
|
|
auditLogId: z.string().default(newId("auditLog")),
|
|
bucket: z.string().default("unkey_mutations"),
|
|
time: z.number().default(Date.now()),
|
|
}),
|
|
)
|
|
.transform((l) => ({
|
|
...l,
|
|
actor: {
|
|
...l.actor,
|
|
meta: l.actor.meta ? JSON.stringify(l.actor.meta) : undefined,
|
|
},
|
|
resources: JSON.stringify(l.resources),
|
|
})),
|
|
})(logs);
|
|
}
|
|
|
|
public get ingestRatelimit() {
|
|
return this.client.buildIngestEndpoint({
|
|
datasource: "ratelimits__v1",
|
|
event: ratelimitSchemaV1.transform((l) => ({
|
|
...l,
|
|
resources: JSON.stringify(l.resources),
|
|
})),
|
|
});
|
|
}
|
|
|
|
public get ingestKeyVerification() {
|
|
return this.client.buildIngestEndpoint({
|
|
datasource: "key_verifications__v2",
|
|
event: z.object({
|
|
workspaceId: z.string(),
|
|
apiId: z.string(),
|
|
keyId: z.string(),
|
|
deniedReason: z
|
|
.enum([
|
|
"RATE_LIMITED",
|
|
"USAGE_EXCEEDED",
|
|
"FORBIDDEN",
|
|
"UNAUTHORIZED",
|
|
"DISABLED",
|
|
"INSUFFICIENT_PERMISSIONS",
|
|
])
|
|
.optional(),
|
|
time: z.number(),
|
|
ipAddress: z.string().default(""),
|
|
userAgent: z.string().default(""),
|
|
requestedResource: z.string().default(""),
|
|
edgeRegion: z.string().default(""),
|
|
region: z.string(),
|
|
// deprecated, use deniedReason
|
|
ratelimited: z.boolean().default(false),
|
|
// deprecated, use deniedReason
|
|
usageExceeded: z.boolean().default(false),
|
|
}),
|
|
});
|
|
}
|
|
|
|
public get getVerificationsDaily() {
|
|
return this.client.buildPipe({
|
|
pipe: "get_verifications_daily__v1",
|
|
parameters: z.object({
|
|
workspaceId: z.string(),
|
|
apiId: z.string(),
|
|
keyId: z.string().optional(),
|
|
start: z.number().optional(),
|
|
end: z.number().optional(),
|
|
}),
|
|
data: z.object({
|
|
time: dateToUnixMilli,
|
|
success: z.number(),
|
|
rateLimited: z.number(),
|
|
usageExceeded: z.number(),
|
|
}),
|
|
});
|
|
}
|
|
}
|