Move toju-app into own its folder

This commit is contained in:
2026-03-29 23:55:24 +02:00
parent 0467a7b612
commit 8162e0444a
287 changed files with 42 additions and 34 deletions
@@ -0,0 +1,100 @@
/* eslint-disable @typescript-eslint/member-ordering */
import {
Injectable,
signal,
computed
} from '@angular/core';
/** Default timeout (ms) for the NTP-style HTTP sync request. */
const DEFAULT_SYNC_TIMEOUT_MS = 5000;
/**
* Maintains a clock-offset between the local system time and the
* remote signaling server.
*
* The offset is estimated using a simple NTP-style round-trip
* measurement and is stored as a reactive Angular signal so that
* any dependent computed value auto-updates when a new sync occurs.
*/
@Injectable({ providedIn: 'root' })
export class TimeSyncService {
/**
* Internal offset signal:
* `serverTime = Date.now() + offset`.
*/
private readonly _offset = signal<number>(0);
/** Epoch timestamp of the most recent successful sync. */
private lastSyncTimestamp = 0;
/** Reactive read-only offset (milliseconds). */
readonly offset = computed(() => this._offset());
/**
* Return a server-adjusted "now" timestamp.
*
* @returns Epoch milliseconds aligned to the server clock.
*/
now(): number {
return Date.now() + this._offset();
}
/**
* Set the offset from a known server timestamp.
*
* @param serverTime - Epoch timestamp reported by the server.
* @param receiveTimestamp - Local epoch timestamp when the server time was
* observed. Defaults to `Date.now()` if omitted.
*/
setFromServerTime(serverTime: number, receiveTimestamp?: number): void {
const observedAt = receiveTimestamp ?? Date.now();
this._offset.set(serverTime - observedAt);
this.lastSyncTimestamp = Date.now();
}
/**
* Perform an HTTP-based clock synchronisation using a simple
* NTP-style round-trip.
*
* 1. Record `clientSendTime` (`t0`).
* 2. Fetch `GET {baseApiUrl}/time`.
* 3. Record `clientReceiveTime` (`t2`).
* 4. Estimate one-way latency as `(t2 t0) / 2`.
* 5. Compute offset: `serverNow midpoint(t0, t2)`.
*
* Any network or parsing error is silently ignored so that the
* last known offset (or zero) is retained.
*
* @param baseApiUrl - API base URL (e.g. `http://host:3001/api`).
* @param timeoutMs - Maximum time to wait for the response.
*/
async syncWithEndpoint(
baseApiUrl: string,
timeoutMs: number = DEFAULT_SYNC_TIMEOUT_MS
): Promise<void> {
try {
const controller = new AbortController();
const clientSendTime = Date.now();
const timer = setTimeout(() => controller.abort(), timeoutMs);
const response = await fetch(`${baseApiUrl}/time`, {
signal: controller.signal
});
const clientReceiveTime = Date.now();
clearTimeout(timer);
if (!response.ok)
return;
const data = await response.json();
const serverNow = Number(data?.now) || Date.now();
const midpoint = (clientSendTime + clientReceiveTime) / 2;
this._offset.set(serverNow - midpoint);
this.lastSyncTimestamp = Date.now();
} catch {
// Sync failure is non-fatal; retain the previous offset.
}
}
}