edit login & otp

This commit is contained in:
2026-05-31 14:12:18 +03:30
parent 2f485914b4
commit 27c44710be
34 changed files with 448 additions and 67 deletions
+4
View File
@@ -1,3 +1,5 @@
import { logApiServerTiming } from './server-timing';
const baseUrl = import.meta.env.VITE_API_URL ?? 'http://localhost:3000';
export class ApiError extends Error {
@@ -36,6 +38,8 @@ export async function api<T>(path: string, options: ApiOptions = {}): Promise<T>
headers,
});
logApiServerTiming(path, res);
if (!res.ok) {
const body = (await res.json().catch(() => null)) as
| { message?: string | string[] }
+60
View File
@@ -0,0 +1,60 @@
import { reactive } from 'vue';
export const apiMetrics = reactive({
inFlight: 0,
lastDurationMs: null as number | null,
lastServerMs: null as number | null,
lastMethod: 'GET',
lastPath: '',
lastOk: true,
lastRequestId: null as string | null,
});
export function parseTimingFromResponse(res: Response): {
serverMs: number | null;
requestId: string | null;
} {
let serverMs: number | null = null;
const headerMs = res.headers.get('X-Response-Time-Ms');
if (headerMs) {
const n = Number(headerMs);
if (Number.isFinite(n)) {
serverMs = n;
}
}
if (serverMs == null) {
const serverTiming = res.headers.get('Server-Timing');
const match = serverTiming?.match(/dur=(\d+(?:\.\d+)?)/);
if (match) {
serverMs = Math.round(Number(match[1]));
}
}
return {
serverMs,
requestId: res.headers.get('X-Request-Id'),
};
}
export function trackApiRequest(
method: string,
path: string,
): (ok: boolean, res?: Response) => void {
apiMetrics.inFlight += 1;
const start = performance.now();
return (ok: boolean, res?: Response) => {
apiMetrics.inFlight = Math.max(0, apiMetrics.inFlight - 1);
apiMetrics.lastDurationMs = Math.round(performance.now() - start);
apiMetrics.lastMethod = method.toUpperCase();
apiMetrics.lastPath = path.split('?')[0] ?? path;
apiMetrics.lastOk = ok;
if (res) {
const timing = parseTimingFromResponse(res);
apiMetrics.lastServerMs = timing.serverMs;
apiMetrics.lastRequestId = timing.requestId;
} else {
apiMetrics.lastServerMs = null;
apiMetrics.lastRequestId = null;
}
};
}
+15
View File
@@ -0,0 +1,15 @@
/** Log Server-Timing from API responses (DevTools Network tab also shows the header). */
export function logApiServerTiming(path: string, res: Response): void {
const timing = res.headers.get('server-timing');
if (!timing) {
return;
}
const enabled =
import.meta.env.DEV || import.meta.env.VITE_LOG_API_TIMING === 'true';
if (!enabled) {
return;
}
const match = /total;dur=(\d+)/i.exec(timing);
const ms = match?.[1] ?? timing;
console.info(`[API] ${path} — server-timing: total;dur=${ms}`);
}