[TSM.ID].[11031972] xcu-client v0.2.0 — API key auth, leaveRoom, billing types
JUMPA App CI / build (push) Has started running

This commit is contained in:
pxe_gitea
2026-06-02 12:31:44 +07:00
parent 4fec4b089b
commit b931e5cacd
+58 -9
View File
@@ -1,7 +1,16 @@
// [TSM.ID].[11031972] — XCU Client Library for JUMPA.ID
// Connects to api.xcomu.id Rust engine
// [TSM.ID].[11031972] — XCU Client Library for JUMPA.ID v0.2.0
// Connects to api.xcomu.id Rust engine (PostgreSQL-backed)
const XCU_API_URL = process.env.NEXT_PUBLIC_XCU_API_URL || 'https://api.xcomu.id';
const XCU_API_KEY = process.env.XCU_API_KEY || process.env.NEXT_PUBLIC_XCU_API_KEY || 'xcu_live_jumpa_2026';
function headers(extra?: Record<string, string>): Record<string, string> {
return {
'Content-Type': 'application/json',
'X-XCU-API-Key': XCU_API_KEY,
...extra,
};
}
export interface Room {
id: string;
@@ -23,6 +32,7 @@ export interface Participant {
role: string;
transport: string;
joined_at: string;
is_active: boolean;
}
export interface TokenResponse {
@@ -43,34 +53,65 @@ export interface HealthResponse {
uptime_secs: number;
timestamp: string;
watermark: string;
db_connected: boolean;
}
export interface BillingResponse {
client_id: string;
total_rooms: number;
total_participants: number;
active_rooms: number;
video_minutes: number;
period: string;
}
export interface AuthResponse {
valid: boolean;
tenant_id: string | null;
tenant_name: string | null;
plan: string | null;
}
export const xcuClient = {
async health(): Promise<HealthResponse> {
const res = await fetch(`${XCU_API_URL}/health`);
const res = await fetch(`${XCU_API_URL}/health`, { headers: headers() });
return res.json();
},
async validateAuth(): Promise<AuthResponse> {
const res = await fetch(`${XCU_API_URL}/v1/auth/validate`, {
method: 'POST',
headers: headers(),
body: JSON.stringify({ api_key: XCU_API_KEY }),
});
return res.json();
},
async createRoom(displayName: string, maxParticipants = 100): Promise<Room> {
const res = await fetch(`${XCU_API_URL}/v1/rooms`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
headers: headers(),
body: JSON.stringify({
display_name: displayName,
max_participants: maxParticipants,
codec_preference: 'h265',
}),
});
if (!res.ok) {
const err = await res.text();
throw new Error(`Create room failed: ${err}`);
}
return res.json();
},
async listRooms(): Promise<Room[]> {
const res = await fetch(`${XCU_API_URL}/v1/rooms`);
const res = await fetch(`${XCU_API_URL}/v1/rooms`, { headers: headers() });
if (!res.ok) return [];
return res.json();
},
async getRoom(code: string): Promise<Room> {
const res = await fetch(`${XCU_API_URL}/v1/rooms/${code}`);
const res = await fetch(`${XCU_API_URL}/v1/rooms/${code}`, { headers: headers() });
if (!res.ok) throw new Error(`Room ${code} not found`);
return res.json();
},
@@ -78,7 +119,7 @@ export const xcuClient = {
async joinRoom(code: string, userId: string, displayName: string, role = 'participant'): Promise<TokenResponse> {
const res = await fetch(`${XCU_API_URL}/v1/rooms/${code}/join`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
headers: headers(),
body: JSON.stringify({
external_user_id: userId,
display_name: displayName,
@@ -89,8 +130,16 @@ export const xcuClient = {
return res.json();
},
async getBilling() {
const res = await fetch(`${XCU_API_URL}/v1/billing`);
async leaveRoom(code: string): Promise<{ success: boolean }> {
const res = await fetch(`${XCU_API_URL}/v1/rooms/${code}/leave`, {
method: 'POST',
headers: headers(),
});
return res.json();
},
async getBilling(): Promise<BillingResponse> {
const res = await fetch(`${XCU_API_URL}/v1/billing`, { headers: headers() });
return res.json();
},
};