fix: use Date header from health/version instead of health.getTime()

health.getTime() requires health.read scope which regular console users
don't have. Instead, parse the Date header from the existing
health/version fetch that already happens on console load.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Damodar Lohani
2026-03-29 07:06:34 +00:00
co-authored by Claude Opus 4.6
parent e8938c3113
commit 2fbb953232
4 changed files with 17 additions and 26 deletions
+6 -14
View File
@@ -7,23 +7,15 @@ const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
let serverTimeCache: { serverSecs: number; fetchedAtMs: number } | null = null;
/**
* Fetch and cache the server's clock so fingerprint timestamps always align
* with the backend's clock, regardless of local clock drift.
* Cache the server's clock so fingerprint timestamps always align with the
* backend's clock, regardless of local clock drift.
*
* @param getServerTime - callback that returns the server's unix timestamp in seconds
* (e.g. `health.getTime()` → `response.localTime`)
* @param serverTimeSecs - the server's unix timestamp in seconds
* (e.g. parsed from a response Date header)
*/
export async function syncServerTime(
getServerTime: () => Promise<number>
): Promise<void> {
export function syncServerTime(serverTimeSecs: number): void {
if (serverTimeCache) return;
try {
const fetchedAtMs = Date.now();
const serverSecs = await getServerTime();
serverTimeCache = { serverSecs, fetchedAtMs };
} catch {
console.warn('Failed to sync server time for fingerprint');
}
serverTimeCache = { serverSecs: serverTimeSecs, fetchedAtMs: Date.now() };
}
function getServerTimestamp(): number {
+8 -1
View File
@@ -6,6 +6,7 @@ import { Platform, Query } from '@appwrite.io/console';
import { makePlansMap } from '$lib/helpers/billing';
import { plansInfo as plansInfoStore } from '$lib/stores/billing';
import { normalizeConsoleVariables } from '$lib/helpers/domains';
import { syncServerTime } from '$lib/helpers/fingerprint';
export const load: LayoutLoad = async ({ depends, parent }) => {
const { organizations, plansInfo } = await parent();
@@ -28,7 +29,13 @@ export const load: LayoutLoad = async ({ depends, parent }) => {
plansArrayPromise,
fetch(`${endpoint}/health/version`, {
headers: { 'X-Appwrite-Project': project as string }
}).then((response) => response.json() as { version?: string }),
}).then((response) => {
const dateHeader = response.headers.get('Date');
if (dateHeader) {
syncServerTime(Math.floor(new Date(dateHeader).getTime() / 1000));
}
return response.json() as { version?: string };
}),
sdk.forConsole.console.variables()
]);
@@ -11,7 +11,7 @@ import { loadAvailableRegions } from '$routes/(console)/regions';
import { type Models, Platform } from '@appwrite.io/console';
import { redirect } from '@sveltejs/kit';
import { resolve } from '$app/paths';
import { generateFingerprintToken, syncServerTime } from '$lib/helpers/fingerprint';
import { generateFingerprintToken } from '$lib/helpers/fingerprint';
import { normalizeConsoleVariables } from '$lib/helpers/domains';
import { browser } from '$app/environment';
@@ -107,11 +107,7 @@ export const load: LayoutLoad = async ({ params, depends, parent }) => {
// Track console access for cloud projects (fire-and-forget, backend has 6-day cooldown).
// Skip if paused — user must explicitly resume via the paused project modal.
if (isCloud && browser && project.status !== 'paused') {
syncServerTime(async () => {
const { localTime } = await sdk.forConsole.health.getTime();
return localTime;
})
.then(() => generateFingerprintToken())
generateFingerprintToken()
.then((fingerprint) => {
sdk.forConsole.client.headers['X-Appwrite-Console-Fingerprint'] = fingerprint;
return sdk.forConsole.projects.updateConsoleAccess({
@@ -6,7 +6,7 @@
import { Dependencies } from '$lib/constants';
import { addNotification } from '$lib/stores/notifications';
import { Submit, trackError } from '$lib/actions/analytics';
import { generateFingerprintToken, syncServerTime } from '$lib/helpers/fingerprint';
import { generateFingerprintToken } from '$lib/helpers/fingerprint';
import { Alert, Layout, Modal, Typography } from '@appwrite.io/pink-svelte';
import { Status } from '@appwrite.io/console';
@@ -28,10 +28,6 @@
error = null;
try {
await syncServerTime(async () => {
const { localTime } = await sdk.forConsole.health.getTime();
return localTime;
});
const fingerprint = await generateFingerprintToken();
sdk.forConsole.client.headers['X-Appwrite-Console-Fingerprint'] = fingerprint;