Add developer API, Prometheus metrics, and community links
This commit is contained in:
71
shared/developer-api.ts
Normal file
71
shared/developer-api.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const developerScopes = [
|
||||
"maps:read",
|
||||
"account:read",
|
||||
"account:maps:read",
|
||||
"account:games:read",
|
||||
] as const;
|
||||
export type DeveloperScope = (typeof developerScopes)[number];
|
||||
export type DeveloperKey = {
|
||||
id: string;
|
||||
name: string;
|
||||
prefix: string;
|
||||
scopes: DeveloperScope[];
|
||||
createdAt: string;
|
||||
expiresAt: string;
|
||||
revokedAt: string | null;
|
||||
lastUsedAt: string | null;
|
||||
};
|
||||
export const developerKeyInput = z
|
||||
.object({
|
||||
name: z.string().trim().min(1).max(60),
|
||||
scopes: z
|
||||
.array(z.enum(developerScopes))
|
||||
.min(1)
|
||||
.max(4)
|
||||
.refine(
|
||||
(values) => new Set(values).size === values.length,
|
||||
"Duplicate permissions",
|
||||
),
|
||||
expiresDays: z.number().int().min(1).max(365).default(90),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export function developerPagination(url: URL, search = false) {
|
||||
const allowed = new Set(
|
||||
search ? ["limit", "offset", "q"] : ["limit", "offset"],
|
||||
);
|
||||
for (const key of url.searchParams.keys()) {
|
||||
if (!allowed.has(key) || url.searchParams.getAll(key).length !== 1)
|
||||
throw new Error("Unsupported or duplicate query parameter");
|
||||
}
|
||||
const integer = (key: string, fallback: number, min: number, max: number) => {
|
||||
const raw = url.searchParams.get(key);
|
||||
if (raw === null) return fallback;
|
||||
if (!/^\d+$/.test(raw)) throw new Error(`Invalid ${key}`);
|
||||
const value = Number(raw);
|
||||
if (!Number.isSafeInteger(value) || value < min || value > max)
|
||||
throw new Error(`Invalid ${key}`);
|
||||
return value;
|
||||
};
|
||||
const q = (url.searchParams.get("q") ?? "").trim();
|
||||
if (q.length > 80) throw new Error("Search must be 80 characters or fewer");
|
||||
return {
|
||||
limit: integer("limit", 20, 1, 50),
|
||||
offset: integer("offset", 0, 0, 10000),
|
||||
q,
|
||||
};
|
||||
}
|
||||
export type DeveloperPagination = ReturnType<typeof developerPagination>;
|
||||
export function developerPage(total: number, page: DeveloperPagination) {
|
||||
return {
|
||||
limit: page.limit,
|
||||
offset: page.offset,
|
||||
total,
|
||||
nextOffset:
|
||||
page.offset + page.limit < total && page.offset + page.limit <= 10000
|
||||
? page.offset + page.limit
|
||||
: null,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user