init
This commit is contained in:
45
src/app/core/services/time-sync.service.ts
Normal file
45
src/app/core/services/time-sync.service.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { Injectable, signal, computed } from '@angular/core';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class TimeSyncService {
|
||||
// serverTime - clientTime offset (milliseconds)
|
||||
private readonly _offset = signal<number>(0);
|
||||
private _lastSyncAt = 0;
|
||||
|
||||
readonly offset = computed(() => this._offset());
|
||||
|
||||
// Returns a server-adjusted now() using the current offset
|
||||
now(): number {
|
||||
return Date.now() + this._offset();
|
||||
}
|
||||
|
||||
// Set offset based on a serverTime observed at approximately receiveAt
|
||||
setFromServerTime(serverTime: number, receiveAt?: number): void {
|
||||
const observedAt = receiveAt ?? Date.now();
|
||||
const offset = serverTime - observedAt;
|
||||
this._offset.set(offset);
|
||||
this._lastSyncAt = Date.now();
|
||||
}
|
||||
|
||||
// Perform an HTTP-based sync using a simple NTP-style roundtrip
|
||||
async syncWithEndpoint(baseApiUrl: string, timeoutMs = 5000): Promise<void> {
|
||||
try {
|
||||
const controller = new AbortController();
|
||||
const t0 = Date.now();
|
||||
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
||||
const resp = await fetch(`${baseApiUrl}/time`, { signal: controller.signal });
|
||||
const t2 = Date.now();
|
||||
clearTimeout(timer);
|
||||
if (!resp.ok) return;
|
||||
const data = await resp.json();
|
||||
// Estimate one-way latency and offset
|
||||
const serverNow = Number(data?.now) || Date.now();
|
||||
const midpoint = (t0 + t2) / 2;
|
||||
const offset = serverNow - midpoint;
|
||||
this._offset.set(offset);
|
||||
this._lastSyncAt = Date.now();
|
||||
} catch {
|
||||
// ignore sync failures; retain last offset
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user