feat: standardize request ids across operator flows
This commit is contained in:
parent
ffa60fcfb7
commit
d99cb94d19
@ -98,7 +98,7 @@ pnpm lint
|
|||||||
|
|
||||||
## Request Tracing
|
## Request Tracing
|
||||||
|
|
||||||
- the main web and mobile API paths now attach `x-request-id`
|
- the main web and mobile API paths, operator actions, and lifecycle fetches now attach `x-request-id`
|
||||||
- backend HTTP responses echo `x-request-id` so browser/app logs can be correlated with backend logs
|
- backend HTTP responses echo `x-request-id` so browser/app logs can be correlated with backend logs
|
||||||
- during incident review, treat `x-request-id` as the primary request correlation key across client and backend traces
|
- during incident review, treat `x-request-id` as the primary request correlation key across client and backend traces
|
||||||
|
|
||||||
@ -228,6 +228,6 @@ Manual mobile release smoke is still required before broad rollout:
|
|||||||
- web now uses platform-session handling end to end; the remaining auth cleanup is removing dormant compatibility stubs and aligning profile bootstrap contracts fully with backend-owned product APIs
|
- web now uses platform-session handling end to end; the remaining auth cleanup is removing dormant compatibility stubs and aligning profile bootstrap contracts fully with backend-owned product APIs
|
||||||
- root `pnpm verify` is green again after aligning the web Vitest harness with platform-session storage and current API contracts
|
- root `pnpm verify` is green again after aligning the web Vitest harness with platform-session storage and current API contracts
|
||||||
- mobile does not yet include push notification infrastructure
|
- mobile does not yet include push notification infrastructure
|
||||||
- feature-flag ownership and correlation-ID propagation are not fully standardized yet
|
- feature-flag ownership and exchange/order-level correlation-ID propagation are not fully standardized yet
|
||||||
|
|
||||||
These are follow-up items, not hidden defects. They should remain tracked in `docs/ROADMAP.md`.
|
These are follow-up items, not hidden defects. They should remain tracked in `docs/ROADMAP.md`.
|
||||||
|
|||||||
@ -40,7 +40,7 @@ It assumes:
|
|||||||
- [x] Backend risk and PnL aggregate reads now flow through repository abstractions instead of direct legacy service calls
|
- [x] Backend risk and PnL aggregate reads now flow through repository abstractions instead of direct legacy service calls
|
||||||
- [x] Web history, profile, marketplace, config, and manual-entry flows now run through backend APIs instead of browser-side table access
|
- [x] Web history, profile, marketplace, config, and manual-entry flows now run through backend APIs instead of browser-side table access
|
||||||
- [x] Release smoke coverage now exists for web auth and product accessibility flows, with a tracked mobile release smoke checklist in operations
|
- [x] Release smoke coverage now exists for web auth and product accessibility flows, with a tracked mobile release smoke checklist in operations
|
||||||
- [x] Request ID propagation is now standardized across the main web/mobile API paths and echoed by backend HTTP responses
|
- [x] Request ID propagation is now standardized across the main web/mobile API paths, operator actions, lifecycle fetches, and backend HTTP responses
|
||||||
- [x] Backtest feature access now reads from an explicit backend feature-flags contract instead of scraping generic runtime config
|
- [x] Backtest feature access now reads from an explicit backend feature-flags contract instead of scraping generic runtime config
|
||||||
- [x] Trading user profiles and marketplace presets now have Cosmos-backed authority paths
|
- [x] Trading user profiles and marketplace presets now have Cosmos-backed authority paths
|
||||||
- [x] Runtime order, trade-history, manual-entry, order-activity, and reconciliation-audit repositories now use Cosmos-backed trading-record storage instead of the legacy service layer
|
- [x] Runtime order, trade-history, manual-entry, order-activity, and reconciliation-audit repositories now use Cosmos-backed trading-record storage instead of the legacy service layer
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { mobileRuntime } from '@/lib/runtime';
|
|||||||
import { mobileTelemetry, trackMobileError } from '@/lib/telemetry';
|
import { mobileTelemetry, trackMobileError } from '@/lib/telemetry';
|
||||||
import { useMobileAuth } from '@/providers/MobileAuthProvider';
|
import { useMobileAuth } from '@/providers/MobileAuthProvider';
|
||||||
import { buildTradingSocketOptions, isUnauthorizedSocketError } from '../../shared/realtime.js';
|
import { buildTradingSocketOptions, isUnauthorizedSocketError } from '../../shared/realtime.js';
|
||||||
|
import { createRequestId } from '../../shared/request-id.js';
|
||||||
|
|
||||||
type HealthSnapshot = {
|
type HealthSnapshot = {
|
||||||
tradingControl?: {
|
tradingControl?: {
|
||||||
@ -165,6 +166,7 @@ export function TradingDataProvider({ children }: { children: ReactNode }) {
|
|||||||
const response = await fetch(`${mobileRuntime.tradingApiUrl}/state`, {
|
const response = await fetch(`${mobileRuntime.tradingApiUrl}/state`, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
|
'x-request-id': createRequestId('mobile-state'),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -335,6 +337,7 @@ export function TradingDataProvider({ children }: { children: ReactNode }) {
|
|||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
|
'x-request-id': createRequestId('mobile-control'),
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
reason: reason || 'Requested from mobile trading surface',
|
reason: reason || 'Requested from mobile trading surface',
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { useState, useRef, useEffect, useMemo } from 'react';
|
|||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
import { tradingRuntime } from '../lib/runtime';
|
import { tradingRuntime } from '../lib/runtime';
|
||||||
import { getPlatformAccessToken } from '../lib/authSession';
|
import { getPlatformAccessToken } from '../lib/authSession';
|
||||||
|
import { createRequestId } from '../../../shared/request-id.js';
|
||||||
import {
|
import {
|
||||||
Send, X, Bot, User,
|
Send, X, Bot, User,
|
||||||
Check, Loader2,
|
Check, Loader2,
|
||||||
@ -204,7 +205,8 @@ export const ChatControl = ({ profiles, onApplyProfile }: ChatControlProps) => {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': `Bearer ${accessToken}`
|
'Authorization': `Bearer ${accessToken}`,
|
||||||
|
'x-request-id': createRequestId('web-chat')
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
message: msg,
|
message: msg,
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { useAuth } from '../components/AuthContext';
|
|||||||
import { tradingRuntime } from '../lib/runtime';
|
import { tradingRuntime } from '../lib/runtime';
|
||||||
import { createManualEntry, updateManualEntry } from '../lib/manualEntriesApi';
|
import { createManualEntry, updateManualEntry } from '../lib/manualEntriesApi';
|
||||||
import { getPlatformAccessToken } from '../lib/authSession';
|
import { getPlatformAccessToken } from '../lib/authSession';
|
||||||
|
import { createRequestId } from '../../../shared/request-id.js';
|
||||||
|
|
||||||
interface EntryFormProps {
|
interface EntryFormProps {
|
||||||
onSuccess: () => void;
|
onSuccess: () => void;
|
||||||
@ -129,7 +130,8 @@ export function EntryForm({ onSuccess, initialData }: EntryFormProps) {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': `Bearer ${accessToken}`
|
'Authorization': `Bearer ${accessToken}`,
|
||||||
|
'x-request-id': createRequestId('web-entry')
|
||||||
},
|
},
|
||||||
body: JSON.stringify(apiPayload)
|
body: JSON.stringify(apiPayload)
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { tradingRuntime } from './runtime';
|
import { tradingRuntime } from './runtime';
|
||||||
|
import { createRequestId } from '../../../shared/request-id.js';
|
||||||
|
|
||||||
export type CanonicalLifecycleState = 'OPEN' | 'PARTIAL_EXIT' | 'CLOSED' | 'ORPHAN_EXIT';
|
export type CanonicalLifecycleState = 'OPEN' | 'PARTIAL_EXIT' | 'CLOSED' | 'ORPHAN_EXIT';
|
||||||
export type CanonicalSide = 'BUY' | 'SELL';
|
export type CanonicalSide = 'BUY' | 'SELL';
|
||||||
@ -145,7 +146,8 @@ export const fetchCanonicalLifecycleSnapshot = async (args: {
|
|||||||
|
|
||||||
const response = await fetch(endpoint, {
|
const response = await fetch(endpoint, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${token}`
|
Authorization: `Bearer ${token}`,
|
||||||
|
'x-request-id': createRequestId('web-lifecycle')
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import { useAuth } from '../components/AuthContext';
|
|||||||
import { tradingRuntime } from '../lib/runtime';
|
import { tradingRuntime } from '../lib/runtime';
|
||||||
import { fetchDynamicConfigItems, upsertDynamicConfigItems } from '../lib/dynamicConfigApi';
|
import { fetchDynamicConfigItems, upsertDynamicConfigItems } from '../lib/dynamicConfigApi';
|
||||||
import { getPlatformAccessToken } from '../lib/authSession';
|
import { getPlatformAccessToken } from '../lib/authSession';
|
||||||
|
import { createRequestId } from '../../../shared/request-id.js';
|
||||||
|
|
||||||
interface AdminTabProps {
|
interface AdminTabProps {
|
||||||
botState: BotState;
|
botState: BotState;
|
||||||
@ -158,7 +159,8 @@ export const AdminTab = ({ botState }: AdminTabProps) => {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${accessToken}`,
|
'Authorization': `Bearer ${accessToken}`,
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
|
'x-request-id': createRequestId('web-admin')
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ reason: 'Admin pause from dashboard' })
|
body: JSON.stringify({ reason: 'Admin pause from dashboard' })
|
||||||
});
|
});
|
||||||
@ -183,7 +185,8 @@ export const AdminTab = ({ botState }: AdminTabProps) => {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${accessToken}`,
|
'Authorization': `Bearer ${accessToken}`,
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
|
'x-request-id': createRequestId('web-admin')
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ reason: 'Admin resume from dashboard' })
|
body: JSON.stringify({ reason: 'Admin resume from dashboard' })
|
||||||
});
|
});
|
||||||
@ -208,7 +211,8 @@ export const AdminTab = ({ botState }: AdminTabProps) => {
|
|||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${accessToken}`,
|
'Authorization': `Bearer ${accessToken}`,
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
|
'x-request-id': createRequestId('web-admin')
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
@ -230,7 +234,8 @@ export const AdminTab = ({ botState }: AdminTabProps) => {
|
|||||||
if (!accessToken) return;
|
if (!accessToken) return;
|
||||||
const res = await fetch(`${apiUrl}/api/config`, {
|
const res = await fetch(`${apiUrl}/api/config`, {
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${accessToken}`
|
'Authorization': `Bearer ${accessToken}`,
|
||||||
|
'x-request-id': createRequestId('web-admin')
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import type { BotState } from '../hooks/useWebSocket';
|
|||||||
import { getPlatformAccessToken } from '../lib/authSession';
|
import { getPlatformAccessToken } from '../lib/authSession';
|
||||||
import { tradingRuntime } from '../lib/runtime';
|
import { tradingRuntime } from '../lib/runtime';
|
||||||
import { useAuth } from '../components/AuthContext';
|
import { useAuth } from '../components/AuthContext';
|
||||||
|
import { createRequestId } from '../../../shared/request-id.js';
|
||||||
import { Layers, ListFilter, Link2, GitBranch, AlertTriangle, Lock, RefreshCw, CheckCircle, XCircle } from 'lucide-react';
|
import { Layers, ListFilter, Link2, GitBranch, AlertTriangle, Lock, RefreshCw, CheckCircle, XCircle } from 'lucide-react';
|
||||||
import { useCanonicalLifecycle } from '../hooks/useCanonicalLifecycle';
|
import { useCanonicalLifecycle } from '../hooks/useCanonicalLifecycle';
|
||||||
import { fetchPositionsBootstrap } from '../lib/positionsApi';
|
import { fetchPositionsBootstrap } from '../lib/positionsApi';
|
||||||
@ -1370,9 +1371,10 @@ export const PositionsTab = ({ botState }: PositionsTabProps) => {
|
|||||||
const response = await fetch(`${tradingRuntime.tradingApiUrl}/api/close`, {
|
const response = await fetch(`${tradingRuntime.tradingApiUrl}/api/close`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': `Bearer ${accessToken}`
|
'Authorization': `Bearer ${accessToken}`,
|
||||||
},
|
'x-request-id': createRequestId('web-close')
|
||||||
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
profile_id: pos.profileId,
|
profile_id: pos.profileId,
|
||||||
symbol: pos.symbol
|
symbol: pos.symbol
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import React from 'react';
|
|||||||
import { AlertTriangle, Clock3, RefreshCcw, Search, ShieldCheck, Undo2 } from 'lucide-react';
|
import { AlertTriangle, Clock3, RefreshCcw, Search, ShieldCheck, Undo2 } from 'lucide-react';
|
||||||
import { tradingRuntime } from '../lib/runtime';
|
import { tradingRuntime } from '../lib/runtime';
|
||||||
import { getPlatformAccessToken } from '../lib/authSession';
|
import { getPlatformAccessToken } from '../lib/authSession';
|
||||||
|
import { createRequestId } from '../../../shared/request-id.js';
|
||||||
|
|
||||||
interface ReconciliationBackfillAuditRow {
|
interface ReconciliationBackfillAuditRow {
|
||||||
id: number;
|
id: number;
|
||||||
@ -167,13 +168,22 @@ export const ReconciliationAuditPanel = () => {
|
|||||||
|
|
||||||
const [auditResponse, batchResponse, manualReviewResponse] = await Promise.all([
|
const [auditResponse, batchResponse, manualReviewResponse] = await Promise.all([
|
||||||
fetch(`${apiUrl}/api/reconciliation/backfill/audit?${auditParams.toString()}`, {
|
fetch(`${apiUrl}/api/reconciliation/backfill/audit?${auditParams.toString()}`, {
|
||||||
headers: { Authorization: `Bearer ${accessToken}` }
|
headers: {
|
||||||
|
Authorization: `Bearer ${accessToken}`,
|
||||||
|
'x-request-id': createRequestId('web-recon')
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
fetch(`${apiUrl}/api/reconciliation/backfill/batches?${batchParams.toString()}`, {
|
fetch(`${apiUrl}/api/reconciliation/backfill/batches?${batchParams.toString()}`, {
|
||||||
headers: { Authorization: `Bearer ${accessToken}` }
|
headers: {
|
||||||
|
Authorization: `Bearer ${accessToken}`,
|
||||||
|
'x-request-id': createRequestId('web-recon')
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
fetch(`${apiUrl}/api/reconciliation/backfill/audit?${manualReviewParams.toString()}`, {
|
fetch(`${apiUrl}/api/reconciliation/backfill/audit?${manualReviewParams.toString()}`, {
|
||||||
headers: { Authorization: `Bearer ${accessToken}` }
|
headers: {
|
||||||
|
Authorization: `Bearer ${accessToken}`,
|
||||||
|
'x-request-id': createRequestId('web-recon')
|
||||||
|
}
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -266,7 +276,8 @@ export const ReconciliationAuditPanel = () => {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
Authorization: `Bearer ${accessToken}`
|
Authorization: `Bearer ${accessToken}`,
|
||||||
|
'x-request-id': createRequestId('web-recon')
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ batchId })
|
body: JSON.stringify({ batchId })
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user