forked from wrenn/wrenn
Added basic frontend (#1)
Reviewed-on: wrenn/sandbox#1 Co-authored-by: pptx704 <rafeed@omukk.dev> Co-committed-by: pptx704 <rafeed@omukk.dev>
This commit is contained in:
37
frontend/src/lib/api/auth.ts
Normal file
37
frontend/src/lib/api/auth.ts
Normal file
@ -0,0 +1,37 @@
|
||||
export type AuthResponse = {
|
||||
token: string;
|
||||
user_id: string;
|
||||
team_id: string;
|
||||
email: string;
|
||||
};
|
||||
|
||||
export type AuthResult = { ok: true; data: AuthResponse } | { ok: false; error: string };
|
||||
|
||||
export async function apiLogin(email: string, password: string): Promise<AuthResult> {
|
||||
return authFetch('/api/v1/auth/login', { email, password });
|
||||
}
|
||||
|
||||
export async function apiSignup(email: string, password: string): Promise<AuthResult> {
|
||||
return authFetch('/api/v1/auth/signup', { email, password });
|
||||
}
|
||||
|
||||
async function authFetch(url: string, body: Record<string, string>): Promise<AuthResult> {
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body)
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
if (!res.ok) {
|
||||
const message = data?.error?.message ?? 'Something went wrong';
|
||||
return { ok: false, error: message };
|
||||
}
|
||||
|
||||
return { ok: true, data: data as AuthResponse };
|
||||
} catch {
|
||||
return { ok: false, error: 'Unable to connect to the server' };
|
||||
}
|
||||
}
|
||||
66
frontend/src/lib/api/capsules.ts
Normal file
66
frontend/src/lib/api/capsules.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { apiFetch, type ApiResult } from '$lib/api/client';
|
||||
|
||||
export type Capsule = {
|
||||
id: string;
|
||||
status: string;
|
||||
template: string;
|
||||
vcpus: number;
|
||||
memory_mb: number;
|
||||
timeout_sec: number;
|
||||
guest_ip?: string;
|
||||
host_ip?: string;
|
||||
created_at: string;
|
||||
started_at?: string;
|
||||
last_active_at?: string;
|
||||
last_updated: string;
|
||||
};
|
||||
|
||||
|
||||
export async function listCapsules(): Promise<ApiResult<Capsule[]>> {
|
||||
return apiFetch('GET', '/api/v1/sandboxes');
|
||||
}
|
||||
|
||||
export type CreateCapsuleParams = {
|
||||
template?: string;
|
||||
vcpus?: number;
|
||||
memory_mb?: number;
|
||||
timeout_sec?: number;
|
||||
};
|
||||
|
||||
export async function createCapsule(params: CreateCapsuleParams): Promise<ApiResult<Capsule>> {
|
||||
return apiFetch('POST', '/api/v1/sandboxes', params);
|
||||
}
|
||||
|
||||
export async function pauseCapsule(id: string): Promise<ApiResult<Capsule>> {
|
||||
return apiFetch('POST', `/api/v1/sandboxes/${id}/pause`);
|
||||
}
|
||||
|
||||
export async function resumeCapsule(id: string): Promise<ApiResult<Capsule>> {
|
||||
return apiFetch('POST', `/api/v1/sandboxes/${id}/resume`);
|
||||
}
|
||||
|
||||
export async function destroyCapsule(id: string): Promise<ApiResult<void>> {
|
||||
return apiFetch('DELETE', `/api/v1/sandboxes/${id}`);
|
||||
}
|
||||
|
||||
export type Snapshot = {
|
||||
name: string;
|
||||
type: string;
|
||||
vcpus?: number;
|
||||
memory_mb?: number;
|
||||
size_bytes: number;
|
||||
created_at: string;
|
||||
};
|
||||
|
||||
export async function createSnapshot(sandboxId: string, name?: string): Promise<ApiResult<Snapshot>> {
|
||||
return apiFetch('POST', '/api/v1/snapshots', { sandbox_id: sandboxId, name });
|
||||
}
|
||||
|
||||
export async function listSnapshots(typeFilter?: string): Promise<ApiResult<Snapshot[]>> {
|
||||
const url = typeFilter ? `/api/v1/snapshots?type=${typeFilter}` : '/api/v1/snapshots';
|
||||
return apiFetch('GET', url);
|
||||
}
|
||||
|
||||
export async function deleteSnapshot(name: string): Promise<ApiResult<void>> {
|
||||
return apiFetch('DELETE', `/api/v1/snapshots/${name}`);
|
||||
}
|
||||
24
frontend/src/lib/api/client.ts
Normal file
24
frontend/src/lib/api/client.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { auth } from '$lib/auth.svelte';
|
||||
|
||||
export type ApiResult<T> = { ok: true; data: T } | { ok: false; error: string };
|
||||
|
||||
export async function apiFetch<T>(method: string, path: string, body?: unknown): Promise<ApiResult<T>> {
|
||||
try {
|
||||
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
|
||||
if (auth.token) headers['Authorization'] = `Bearer ${auth.token}`;
|
||||
|
||||
const res = await fetch(path, {
|
||||
method,
|
||||
headers,
|
||||
body: body ? JSON.stringify(body) : undefined
|
||||
});
|
||||
|
||||
if (res.status === 204) return { ok: true, data: undefined as T };
|
||||
|
||||
const data = await res.json();
|
||||
if (!res.ok) return { ok: false, error: data?.error?.message ?? 'Something went wrong' };
|
||||
return { ok: true, data: data as T };
|
||||
} catch {
|
||||
return { ok: false, error: 'Unable to connect to the server' };
|
||||
}
|
||||
}
|
||||
26
frontend/src/lib/api/keys.ts
Normal file
26
frontend/src/lib/api/keys.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { apiFetch, type ApiResult } from '$lib/api/client';
|
||||
|
||||
export type APIKey = {
|
||||
id: string;
|
||||
team_id: string;
|
||||
name: string;
|
||||
key_prefix: string;
|
||||
created_by: string;
|
||||
creator_email?: string;
|
||||
created_at: string;
|
||||
last_used?: string;
|
||||
key?: string; // only present immediately after creation
|
||||
};
|
||||
|
||||
|
||||
export async function listKeys(): Promise<ApiResult<APIKey[]>> {
|
||||
return apiFetch('GET', '/api/v1/api-keys');
|
||||
}
|
||||
|
||||
export async function createKey(name: string): Promise<ApiResult<APIKey>> {
|
||||
return apiFetch('POST', '/api/v1/api-keys', { name });
|
||||
}
|
||||
|
||||
export async function revokeKey(id: string): Promise<ApiResult<void>> {
|
||||
return apiFetch('DELETE', `/api/v1/api-keys/${id}`);
|
||||
}
|
||||
Reference in New Issue
Block a user