Merge remote-tracking branch 'origin/main' into feature/mailbox-fair-use-allowance

This commit is contained in:
Matthew Meszaros
2026-09-04 21:25:53 -07:00
70 changed files with 11208 additions and 18 deletions
@@ -0,0 +1,22 @@
// /auth/cli/* — a signed-in member reviews the code a CLI is showing and
// authorizes it into one of their workspaces, which mints the API key.
import Request from "@/lib/api/client/Request";
import type { CLIAuthCode } from "@/lib/api/models/app/cliauth/CLIAuth";
export async function describeCLIAuthCode(code: string): Promise<CLIAuthCode> {
return await Request<CLIAuthCode>({ method: "GET", url: `/auth/cli/codes/${encodeURIComponent(code)}`, authorization: true });
}
export async function approveCLIAuthCode(code: string, organizationId: string): Promise<CLIAuthCode> {
return await Request<CLIAuthCode>({
method: "POST",
url: `/auth/cli/codes/${encodeURIComponent(code)}/approve`,
data: { organization_id: organizationId },
authorization: true,
});
}
export async function denyCLIAuthCode(code: string): Promise<void> {
await Request<void>({ method: "POST", url: `/auth/cli/codes/${encodeURIComponent(code)}/deny`, authorization: true });
}
@@ -0,0 +1,28 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { approveCLIAuthCode, denyCLIAuthCode, describeCLIAuthCode } from "@/lib/api/client/app/cliauth/cliAuth";
export const CLI_AUTH_KEY = ["cli-auth"];
export function useCLIAuthCode(code: string) {
return useQuery({
queryKey: [...CLI_AUTH_KEY, "code", code],
queryFn: () => describeCLIAuthCode(code),
enabled: code.length === 9,
retry: false,
staleTime: 0,
});
}
export function useApproveCLIAuthCode() {
const qc = useQueryClient();
return useMutation({
mutationFn: ({ code, organizationId }: { code: string; organizationId: string }) => approveCLIAuthCode(code, organizationId),
// The approval mints a key, so the API keys list is stale everywhere.
onSuccess: () => void qc.invalidateQueries({ queryKey: ["api-keys"] }),
});
}
export function useDenyCLIAuthCode() {
return useMutation({ mutationFn: (code: string) => denyCLIAuthCode(code) });
}
@@ -0,0 +1,18 @@
// /auth/cli/* — the browser half of `warmbly auth login`.
export type CLIAuthCodeStatus = "pending" | "approved" | "claimed" | "denied";
export interface CLIAuthCode {
id: string;
user_code: string;
client_name: string;
hostname: string;
cli_version: string;
scopes: number;
scope_names: string[];
status: CLIAuthCodeStatus;
organization_id?: string;
api_key_id?: string;
expires_at: string;
created_at: string;
}