mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-07 03:00:29 +00:00
feat(web): build unified auth sessions page
Render /profile/sessions from the new GET /api/v1/auth/sessions API. The page shows the current browser session and active CLI sessions in one list, with revoke buttons gated by the backend-supplied revocable field. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5da2f3d455
commit
d2623662e0
13 changed files with 571 additions and 1 deletions
|
|
@ -3,6 +3,7 @@ import type {
|
|||
ApiQuestion,
|
||||
AuthConfigResponse,
|
||||
AuthMeResponse,
|
||||
AuthSessionsResponse,
|
||||
CommandLogResponse,
|
||||
EventEnvelope,
|
||||
PaginatedBoardRunList,
|
||||
|
|
@ -75,6 +76,13 @@ export function useAuthMe() {
|
|||
);
|
||||
}
|
||||
|
||||
export function useAuthSessions() {
|
||||
return useSWR<AuthSessionsResponse>(
|
||||
queryKeys.auth.sessions(),
|
||||
() => apiData(() => authApi.listAuthSessions()),
|
||||
);
|
||||
}
|
||||
|
||||
export function useSystemInfo() {
|
||||
return useSWR<SystemInfoResponse>(
|
||||
queryKeys.system.info(),
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ export const queryKeys = {
|
|||
auth: {
|
||||
config: () => ["auth", "config"] as const,
|
||||
me: () => ["auth", "me"] as const,
|
||||
sessions: () => ["auth", "sessions"] as const,
|
||||
loginDevToken: () => ["auth", "login-dev-token"] as const,
|
||||
},
|
||||
demo: {
|
||||
|
|
|
|||
182
apps/fabro-web/app/routes/profile-sessions.test.tsx
Normal file
182
apps/fabro-web/app/routes/profile-sessions.test.tsx
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
import { afterEach, describe, expect, mock, test } from "bun:test";
|
||||
import TestRenderer, { act } from "react-test-renderer";
|
||||
|
||||
import type {
|
||||
AuthSession,
|
||||
AuthSessionsResponse,
|
||||
} from "@qltysh/fabro-api-client";
|
||||
|
||||
let currentResponse: AuthSessionsResponse | undefined;
|
||||
|
||||
const deleteAuthSessionMock = mock((_id: string) => Promise.resolve({ data: undefined }));
|
||||
const mutateMock = mock((..._args: unknown[]) => Promise.resolve(undefined));
|
||||
|
||||
mock.module("../lib/queries", () => ({
|
||||
useAuthSessions: () => ({ data: currentResponse, error: undefined }),
|
||||
}));
|
||||
|
||||
mock.module("../lib/api-client", () => ({
|
||||
apiData: async function apiData<T>(
|
||||
call: () => Promise<{ data: T }>,
|
||||
): Promise<T> {
|
||||
const response = await call();
|
||||
return response.data;
|
||||
},
|
||||
authApi: {
|
||||
deleteAuthSession: (id: string) => deleteAuthSessionMock(id),
|
||||
},
|
||||
ApiError: class ApiError extends Error {
|
||||
readonly status: number;
|
||||
readonly requestId: string | null;
|
||||
readonly body: unknown;
|
||||
|
||||
constructor({
|
||||
status,
|
||||
message,
|
||||
requestId,
|
||||
body,
|
||||
}: {
|
||||
status: number;
|
||||
message: string;
|
||||
requestId: string | null;
|
||||
body: unknown;
|
||||
}) {
|
||||
super(message);
|
||||
this.name = "ApiError";
|
||||
this.status = status;
|
||||
this.requestId = requestId;
|
||||
this.body = body;
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
mock.module("swr", () => ({
|
||||
useSWRConfig: () => ({ mutate: mutateMock }),
|
||||
}));
|
||||
|
||||
const { default: ProfileSessions } = await import("./profile-sessions");
|
||||
|
||||
const browserSession: AuthSession = {
|
||||
id: "browser:current",
|
||||
kind: "browser",
|
||||
current: true,
|
||||
provider: "github",
|
||||
login: "alice",
|
||||
label: "This browser",
|
||||
createdAt: "2026-05-10T10:00:00Z",
|
||||
lastSeenAt: "2026-05-10T12:00:00Z",
|
||||
expiresAt: "2026-05-17T10:00:00Z",
|
||||
revocable: false,
|
||||
};
|
||||
|
||||
const cliSession: AuthSession = {
|
||||
id: "cli:abcd-1234",
|
||||
kind: "cli",
|
||||
current: false,
|
||||
provider: "github",
|
||||
login: "alice",
|
||||
label: "Fabro CLI",
|
||||
userAgent: "fabro/0.1.0 Darwin",
|
||||
createdAt: "2026-05-09T08:00:00Z",
|
||||
lastSeenAt: "2026-05-10T11:30:00Z",
|
||||
expiresAt: "2026-06-09T08:00:00Z",
|
||||
revocable: true,
|
||||
};
|
||||
|
||||
function textFromNode(
|
||||
node: ReturnType<TestRenderer.ReactTestRenderer["toJSON"]>,
|
||||
): string {
|
||||
if (!node) return "";
|
||||
if (typeof node === "string") return node;
|
||||
if (Array.isArray(node)) return node.map(textFromNode).join(" ");
|
||||
return (node.children ?? []).map(textFromNode).join(" ");
|
||||
}
|
||||
|
||||
function render(): TestRenderer.ReactTestRenderer {
|
||||
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
||||
let renderer!: TestRenderer.ReactTestRenderer;
|
||||
act(() => {
|
||||
renderer = TestRenderer.create(<ProfileSessions />);
|
||||
});
|
||||
return renderer;
|
||||
}
|
||||
|
||||
const mountedRenderers: TestRenderer.ReactTestRenderer[] = [];
|
||||
|
||||
function renderAndTrack(): TestRenderer.ReactTestRenderer {
|
||||
const renderer = render();
|
||||
mountedRenderers.push(renderer);
|
||||
return renderer;
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
for (const renderer of mountedRenderers.splice(0)) {
|
||||
act(() => renderer.unmount());
|
||||
}
|
||||
currentResponse = undefined;
|
||||
deleteAuthSessionMock.mockClear();
|
||||
mutateMock.mockClear();
|
||||
delete (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT;
|
||||
});
|
||||
|
||||
describe("ProfileSessions", () => {
|
||||
test("renders a profile-style skeleton while loading", () => {
|
||||
currentResponse = undefined;
|
||||
const renderer = renderAndTrack();
|
||||
const text = textFromNode(renderer.toJSON());
|
||||
|
||||
// Skeleton has no real content, just placeholder bars; verify session
|
||||
// labels haven't rendered yet.
|
||||
expect(text).not.toContain("This browser");
|
||||
expect(text).not.toContain("Fabro CLI");
|
||||
});
|
||||
|
||||
test("renders browser and CLI sessions from a unified response", () => {
|
||||
currentResponse = { sessions: [cliSession, browserSession] };
|
||||
const renderer = renderAndTrack();
|
||||
const text = textFromNode(renderer.toJSON());
|
||||
|
||||
expect(text).toContain("This browser");
|
||||
expect(text).toContain("Fabro CLI");
|
||||
expect(text).toContain("browser");
|
||||
expect(text).toContain("cli");
|
||||
expect(text).toContain("alice");
|
||||
expect(text).toContain("fabro/0.1.0 Darwin");
|
||||
});
|
||||
|
||||
test("does not show a revoke button for non-revocable browser sessions", () => {
|
||||
currentResponse = { sessions: [browserSession] };
|
||||
const renderer = renderAndTrack();
|
||||
|
||||
const buttons = renderer.root.findAllByType("button");
|
||||
expect(buttons).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("shows a revoke button for revocable CLI sessions", () => {
|
||||
currentResponse = { sessions: [cliSession] };
|
||||
const renderer = renderAndTrack();
|
||||
|
||||
const buttons = renderer.root.findAllByType("button");
|
||||
expect(buttons).toHaveLength(1);
|
||||
expect(buttons[0].props["aria-label"]).toBe("Revoke Fabro CLI");
|
||||
});
|
||||
|
||||
test("clicking revoke calls the delete endpoint and refreshes the sessions query", async () => {
|
||||
currentResponse = { sessions: [cliSession] };
|
||||
const renderer = renderAndTrack();
|
||||
|
||||
const button = renderer.root.findByType("button");
|
||||
|
||||
await act(async () => {
|
||||
await button.props.onClick();
|
||||
});
|
||||
|
||||
expect(deleteAuthSessionMock).toHaveBeenCalledTimes(1);
|
||||
expect(deleteAuthSessionMock.mock.calls[0]?.[0]).toBe("cli:abcd-1234");
|
||||
|
||||
expect(mutateMock).toHaveBeenCalledTimes(1);
|
||||
const mutateKey = mutateMock.mock.calls[0]?.[0] as readonly unknown[];
|
||||
expect(Array.isArray(mutateKey)).toBe(true);
|
||||
expect(mutateKey).toEqual(["auth", "sessions"]);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,3 +1,162 @@
|
|||
import { useState } from "react";
|
||||
import { useSWRConfig } from "swr";
|
||||
import type { AuthSession } from "@qltysh/fabro-api-client";
|
||||
|
||||
import { ApiError, apiData, authApi } from "../lib/api-client";
|
||||
import { useAuthSessions } from "../lib/queries";
|
||||
import { queryKeys } from "../lib/query-keys";
|
||||
import {
|
||||
Badge,
|
||||
Mono,
|
||||
Muted,
|
||||
Panel,
|
||||
PanelSkeleton,
|
||||
} from "../components/settings-panel";
|
||||
import { formatAbsoluteTs, formatRelativeTime } from "../lib/format";
|
||||
|
||||
export default function ProfileSessions() {
|
||||
return <div />;
|
||||
const { data, error } = useAuthSessions();
|
||||
const { mutate } = useSWRConfig();
|
||||
const [revokingId, setRevokingId] = useState<string | null>(null);
|
||||
const [revokeError, setRevokeError] = useState<string | null>(null);
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<Panel title="Sessions">
|
||||
<div className="px-4 py-6 text-sm text-fg-2">
|
||||
Couldn't load sessions. Please try again.
|
||||
</div>
|
||||
</Panel>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<PanelSkeleton />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const sessions = sortSessions(data.sessions);
|
||||
|
||||
async function revoke(id: string) {
|
||||
setRevokeError(null);
|
||||
setRevokingId(id);
|
||||
try {
|
||||
await apiData(() => authApi.deleteAuthSession(id));
|
||||
await mutate(queryKeys.auth.sessions());
|
||||
} catch (e) {
|
||||
const message =
|
||||
e instanceof ApiError && e.message
|
||||
? e.message
|
||||
: "Couldn't revoke this session. Please try again.";
|
||||
setRevokeError(message);
|
||||
} finally {
|
||||
setRevokingId(null);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<Panel title="Sessions">
|
||||
{sessions.length === 0 ? (
|
||||
<div className="px-4 py-6 text-sm text-fg-muted">No sessions.</div>
|
||||
) : (
|
||||
sessions.map((session) => (
|
||||
<SessionRow
|
||||
key={session.id}
|
||||
session={session}
|
||||
onRevoke={revoke}
|
||||
pending={revokingId === session.id}
|
||||
disabled={revokingId !== null}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</Panel>
|
||||
{revokeError ? (
|
||||
<div
|
||||
role="alert"
|
||||
className="text-sm text-rose-400"
|
||||
data-testid="revoke-error"
|
||||
>
|
||||
{revokeError}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function sortSessions(sessions: AuthSession[]): AuthSession[] {
|
||||
return [...sessions].sort((a, b) => {
|
||||
if (a.current !== b.current) return a.current ? -1 : 1;
|
||||
return Date.parse(b.lastSeenAt) - Date.parse(a.lastSeenAt);
|
||||
});
|
||||
}
|
||||
|
||||
function SessionRow({
|
||||
session,
|
||||
onRevoke,
|
||||
pending,
|
||||
disabled,
|
||||
}: {
|
||||
session: AuthSession;
|
||||
onRevoke: (id: string) => void;
|
||||
pending: boolean;
|
||||
disabled: boolean;
|
||||
}) {
|
||||
return (
|
||||
<div className="grid grid-cols-[minmax(0,1fr)_auto] items-start gap-4 px-4 py-3.5">
|
||||
<div className="min-w-0 space-y-1">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<span className="text-sm text-fg">{session.label}</span>
|
||||
<Badge>{session.kind}</Badge>
|
||||
{session.current ? <Badge>current</Badge> : null}
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-fg-muted">
|
||||
<span>
|
||||
<Muted>Provider</Muted> <Mono>{session.provider}</Mono>
|
||||
</span>
|
||||
<span>
|
||||
<Muted>Login</Muted> <Mono>{session.login}</Mono>
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-fg-muted">
|
||||
<span>
|
||||
<Muted>Last active</Muted>{" "}
|
||||
<span title={formatAbsoluteTs(session.lastSeenAt)}>
|
||||
{formatRelativeTime(session.lastSeenAt)}
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
<Muted>Expires</Muted>{" "}
|
||||
<span title={formatAbsoluteTs(session.expiresAt)}>
|
||||
{formatAbsoluteTs(session.expiresAt)}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
{session.userAgent ? (
|
||||
<div className="truncate text-xs text-fg-muted" title={session.userAgent}>
|
||||
<Muted>User agent</Muted>{" "}
|
||||
<span className="font-mono text-fg-3">{session.userAgent}</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center">
|
||||
{session.revocable ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onRevoke(session.id)}
|
||||
disabled={disabled}
|
||||
aria-label={`Revoke ${session.label}`}
|
||||
className="rounded-md border border-line bg-overlay px-2.5 py-1 text-xs text-fg-2 transition-colors hover:bg-overlay-strong hover:text-fg disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
{pending ? "Revoking…" : "Revoke"}
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ models/auth-config-response.ts
|
|||
models/auth-me-response.ts
|
||||
models/auth-method.ts
|
||||
models/auth-session-user.ts
|
||||
models/auth-session.ts
|
||||
models/auth-sessions-response.ts
|
||||
models/billed-token-counts.ts
|
||||
models/billing-by-model.ts
|
||||
models/billing-model-ref.ts
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ import type { AuthConfigResponse } from '../models';
|
|||
// @ts-ignore
|
||||
import type { AuthMeResponse } from '../models';
|
||||
// @ts-ignore
|
||||
import type { AuthSessionsResponse } from '../models';
|
||||
// @ts-ignore
|
||||
import type { DemoToggleRequest } from '../models';
|
||||
// @ts-ignore
|
||||
import type { DemoToggleResponse } from '../models';
|
||||
|
|
@ -40,6 +42,46 @@ import type { ErrorResponse } from '../models';
|
|||
*/
|
||||
export const AuthApiAxiosParamCreator = function (configuration?: Configuration) {
|
||||
return {
|
||||
/**
|
||||
* Revokes an active CLI session chain. Browser sessions are not revocable in this API version.
|
||||
* @summary Revoke an authenticated session
|
||||
* @param {string} id
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
deleteAuthSession: async (id: string, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'id' is not null or undefined
|
||||
assertParamExists('deleteAuthSession', 'id', id)
|
||||
const localVarPath = `/api/v1/auth/sessions/{id}`
|
||||
.replace(`{${"id"}}`, encodeURIComponent(String(id)));
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication SessionCookie required
|
||||
|
||||
// authentication BearerAuth required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
localVarHeaderParameter['Accept'] = 'application/json';
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Returns the browser login methods enabled for this server.
|
||||
* @summary Retrieve auth configuration
|
||||
|
|
@ -106,6 +148,42 @@ export const AuthApiAxiosParamCreator = function (configuration?: Configuration)
|
|||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Returns the current browser session and active CLI session chains for the authenticated user.
|
||||
* @summary List authenticated sessions
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
listAuthSessions: async (options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/api/v1/auth/sessions`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication SessionCookie required
|
||||
|
||||
// authentication BearerAuth required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
localVarHeaderParameter['Accept'] = 'application/json';
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Creates a browser session from an enabled development token.
|
||||
* @summary Login with development token
|
||||
|
|
@ -191,6 +269,19 @@ export const AuthApiAxiosParamCreator = function (configuration?: Configuration)
|
|||
export const AuthApiFp = function(configuration?: Configuration) {
|
||||
const localVarAxiosParamCreator = AuthApiAxiosParamCreator(configuration)
|
||||
return {
|
||||
/**
|
||||
* Revokes an active CLI session chain. Browser sessions are not revocable in this API version.
|
||||
* @summary Revoke an authenticated session
|
||||
* @param {string} id
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async deleteAuthSession(id: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.deleteAuthSession(id, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['AuthApi.deleteAuthSession']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
* Returns the browser login methods enabled for this server.
|
||||
* @summary Retrieve auth configuration
|
||||
|
|
@ -215,6 +306,18 @@ export const AuthApiFp = function(configuration?: Configuration) {
|
|||
const localVarOperationServerBasePath = operationServerMap['AuthApi.getAuthMe']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
* Returns the current browser session and active CLI session chains for the authenticated user.
|
||||
* @summary List authenticated sessions
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async listAuthSessions(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AuthSessionsResponse>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listAuthSessions(options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['AuthApi.listAuthSessions']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
* Creates a browser session from an enabled development token.
|
||||
* @summary Login with development token
|
||||
|
|
@ -250,6 +353,16 @@ export const AuthApiFp = function(configuration?: Configuration) {
|
|||
export const AuthApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
|
||||
const localVarFp = AuthApiFp(configuration)
|
||||
return {
|
||||
/**
|
||||
* Revokes an active CLI session chain. Browser sessions are not revocable in this API version.
|
||||
* @summary Revoke an authenticated session
|
||||
* @param {string} id
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
deleteAuthSession(id: string, options?: RawAxiosRequestConfig): AxiosPromise<void> {
|
||||
return localVarFp.deleteAuthSession(id, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* Returns the browser login methods enabled for this server.
|
||||
* @summary Retrieve auth configuration
|
||||
|
|
@ -268,6 +381,15 @@ export const AuthApiFactory = function (configuration?: Configuration, basePath?
|
|||
getAuthMe(options?: RawAxiosRequestConfig): AxiosPromise<AuthMeResponse> {
|
||||
return localVarFp.getAuthMe(options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* Returns the current browser session and active CLI session chains for the authenticated user.
|
||||
* @summary List authenticated sessions
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
listAuthSessions(options?: RawAxiosRequestConfig): AxiosPromise<AuthSessionsResponse> {
|
||||
return localVarFp.listAuthSessions(options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* Creates a browser session from an enabled development token.
|
||||
* @summary Login with development token
|
||||
|
|
@ -295,6 +417,17 @@ export const AuthApiFactory = function (configuration?: Configuration, basePath?
|
|||
* AuthApi - object-oriented interface
|
||||
*/
|
||||
export class AuthApi extends BaseAPI {
|
||||
/**
|
||||
* Revokes an active CLI session chain. Browser sessions are not revocable in this API version.
|
||||
* @summary Revoke an authenticated session
|
||||
* @param {string} id
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
public deleteAuthSession(id: string, options?: RawAxiosRequestConfig) {
|
||||
return AuthApiFp(this.configuration).deleteAuthSession(id, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the browser login methods enabled for this server.
|
||||
* @summary Retrieve auth configuration
|
||||
|
|
@ -315,6 +448,16 @@ export class AuthApi extends BaseAPI {
|
|||
return AuthApiFp(this.configuration).getAuthMe(options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current browser session and active CLI session chains for the authenticated user.
|
||||
* @summary List authenticated sessions
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
public listAuthSessions(options?: RawAxiosRequestConfig) {
|
||||
return AuthApiFp(this.configuration).listAuthSessions(options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a browser session from an enabled development token.
|
||||
* @summary Login with development token
|
||||
|
|
|
|||
38
lib/packages/fabro-api-client/src/models/auth-session.ts
Normal file
38
lib/packages/fabro-api-client/src/models/auth-session.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* HTTP API for managing Fabro workflow run executions.
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
export interface AuthSession {
|
||||
'id': string;
|
||||
'kind': AuthSessionKindEnum;
|
||||
'current': boolean;
|
||||
'provider': string;
|
||||
'login': string;
|
||||
'label': string;
|
||||
'userAgent'?: string;
|
||||
'createdAt': string;
|
||||
'lastSeenAt': string;
|
||||
'expiresAt': string;
|
||||
'revocable': boolean;
|
||||
}
|
||||
|
||||
export const AuthSessionKindEnum = {
|
||||
BROWSER: 'browser',
|
||||
CLI: 'cli'
|
||||
} as const;
|
||||
|
||||
export type AuthSessionKindEnum = typeof AuthSessionKindEnum[keyof typeof AuthSessionKindEnum];
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Fabro Run API
|
||||
* HTTP API for managing Fabro workflow run executions.
|
||||
*
|
||||
* The version of the OpenAPI document: 0.1.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
// May contain unused imports in some cases
|
||||
// @ts-ignore
|
||||
import type { AuthSession } from './auth-session';
|
||||
|
||||
export interface AuthSessionsResponse {
|
||||
'sessions': Array<AuthSession>;
|
||||
}
|
||||
|
||||
|
|
@ -21,3 +21,6 @@ export interface DeleteRunSandbox {
|
|||
'provider': SandboxProvider;
|
||||
'id': string;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@ export * from './artifacts-settings';
|
|||
export * from './auth-config-response';
|
||||
export * from './auth-me-response';
|
||||
export * from './auth-method';
|
||||
export * from './auth-session';
|
||||
export * from './auth-session-user';
|
||||
export * from './auth-sessions-response';
|
||||
export * from './billed-token-counts';
|
||||
export * from './billing-by-model';
|
||||
export * from './billing-model-ref';
|
||||
|
|
|
|||
|
|
@ -32,3 +32,6 @@ export interface RunSandboxSettings {
|
|||
'docker': DockerSettings | null;
|
||||
'daytona': DaytonaSettings | null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -47,3 +47,6 @@ export interface RunSandbox {
|
|||
'clone_branch'?: string | null;
|
||||
'resources'?: SandboxResources | null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -25,3 +25,6 @@ export const SandboxProvider = {
|
|||
} as const;
|
||||
|
||||
export type SandboxProvider = typeof SandboxProvider[keyof typeof SandboxProvider];
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue