feat(visualizer): add layers + fast timeframe switching
This commit is contained in:
@@ -47,6 +47,7 @@ export async function fetchChart(params: {
|
||||
source?: string;
|
||||
tf: string;
|
||||
limit: number;
|
||||
signal?: AbortSignal;
|
||||
}): Promise<{ candles: Candle[]; indicators: ChartIndicators; meta: { tf: string; bucketSeconds: number } }> {
|
||||
const base = getApiBaseUrl();
|
||||
const u = new URL(base, window.location.origin);
|
||||
@@ -56,7 +57,7 @@ export async function fetchChart(params: {
|
||||
u.searchParams.set('limit', String(params.limit));
|
||||
if (params.source && params.source.trim()) u.searchParams.set('source', params.source.trim());
|
||||
|
||||
const res = await fetch(u.toString());
|
||||
const res = await fetch(u.toString(), { signal: params.signal });
|
||||
const text = await res.text();
|
||||
if (!res.ok) throw new Error(`API HTTP ${res.status}: ${text}`);
|
||||
const json = JSON.parse(text) as ChartResponse;
|
||||
@@ -79,8 +80,16 @@ export async function fetchChart(params: {
|
||||
flat: Number((c as any).flow.flat),
|
||||
}
|
||||
: undefined,
|
||||
flowRows: Array.isArray((c as any)?.flowRows) ? (c as any).flowRows.map((x: any) => Number(x)) : undefined,
|
||||
flowMoves: Array.isArray((c as any)?.flowMoves) ? (c as any).flowMoves.map((x: any) => Number(x)) : undefined,
|
||||
flowRows: Array.isArray((c as any)?.flowRows)
|
||||
? (c as any).flowRows.map((x: any) => Number(x))
|
||||
: Array.isArray((c as any)?.flow_rows)
|
||||
? (c as any).flow_rows.map((x: any) => Number(x))
|
||||
: undefined,
|
||||
flowMoves: Array.isArray((c as any)?.flowMoves)
|
||||
? (c as any).flowMoves.map((x: any) => Number(x))
|
||||
: Array.isArray((c as any)?.flow_moves)
|
||||
? (c as any).flow_moves.map((x: any) => Number(x))
|
||||
: undefined,
|
||||
})),
|
||||
indicators: json.indicators || {},
|
||||
meta: { tf: String(json.tf || params.tf), bucketSeconds: Number(json.bucketSeconds || 0) },
|
||||
|
||||
@@ -43,13 +43,30 @@ function resolveGraphqlWsUrl(): string {
|
||||
}
|
||||
|
||||
function resolveAuthHeaders(): HeadersMap | undefined {
|
||||
const token = envString('VITE_HASURA_AUTH_TOKEN');
|
||||
if (token) return { authorization: `Bearer ${token}` };
|
||||
const rawToken = envString('VITE_HASURA_AUTH_TOKEN');
|
||||
if (rawToken) {
|
||||
const bearer = normalizeBearerToken(rawToken);
|
||||
if (bearer) return { authorization: `Bearer ${bearer}` };
|
||||
}
|
||||
const secret = envString('VITE_HASURA_ADMIN_SECRET');
|
||||
if (secret) return { 'x-hasura-admin-secret': secret };
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function normalizeBearerToken(raw: string): string | undefined {
|
||||
const trimmed = String(raw || '').trim();
|
||||
if (!trimmed) return undefined;
|
||||
const m = trimmed.match(/^Bearer\s+(.+)$/i);
|
||||
const token = (m ? m[1] : trimmed).trim();
|
||||
if (!token) return undefined;
|
||||
const parts = token.split(/\s+/).filter(Boolean);
|
||||
if (!parts.length) return undefined;
|
||||
if (parts.length > 1) {
|
||||
console.warn('VITE_HASURA_AUTH_TOKEN contains whitespace; using the first segment only.');
|
||||
}
|
||||
return parts[0];
|
||||
}
|
||||
|
||||
type WsMessage =
|
||||
| { type: 'connection_ack' | 'ka' | 'complete' }
|
||||
| { type: 'connection_error'; payload?: any }
|
||||
|
||||
@@ -23,7 +23,18 @@ function getHasuraUrl(): string {
|
||||
|
||||
function getAuthToken(): string | undefined {
|
||||
const v = (import.meta as any).env?.VITE_HASURA_AUTH_TOKEN;
|
||||
return v ? String(v) : undefined;
|
||||
const raw = v ? String(v) : '';
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return undefined;
|
||||
const m = trimmed.match(/^Bearer\s+(.+)$/i);
|
||||
const token = (m ? m[1] : trimmed).trim();
|
||||
if (!token) return undefined;
|
||||
const parts = token.split(/\s+/).filter(Boolean);
|
||||
if (!parts.length) return undefined;
|
||||
if (parts.length > 1) {
|
||||
console.warn('VITE_HASURA_AUTH_TOKEN contains whitespace; using the first segment only.');
|
||||
}
|
||||
return parts[0];
|
||||
}
|
||||
|
||||
function getAdminSecret(): string | undefined {
|
||||
|
||||
Reference in New Issue
Block a user