mirror of
https://github.com/Nezumi-2711/uptime-monitoring.git
synced 2026-09-22 13:48:31 +00:00
fix: optimize pricing for deploying
This commit is contained in:
@@ -40,13 +40,13 @@ export type PublicStatus = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function getStatus(signal?: AbortSignal) {
|
export function getStatus(signal?: AbortSignal) {
|
||||||
return getJson<PublicStatus>('/api/status', { signal });
|
return getJson<PublicStatus>('/api/status', { signal, cache: 'no-store' });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getIncidentHistory(signal?: AbortSignal) {
|
export function getIncidentHistory(signal?: AbortSignal) {
|
||||||
return getJson<{ incidents: PublicIncident[] }>('/api/status/incidents', { signal });
|
return getJson<{ incidents: PublicIncident[] }>('/api/status/incidents', { signal, cache: 'no-store' });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPublicIncident(id: number, signal?: AbortSignal) {
|
export function getPublicIncident(id: number, signal?: AbortSignal) {
|
||||||
return getJson<{ incident: PublicIncident }>(`/api/status/incidents/${id}`, { signal });
|
return getJson<{ incident: PublicIncident }>(`/api/status/incidents/${id}`, { signal, cache: 'no-store' });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export function useStatusQuery() {
|
|||||||
queryKey: statusKeys.all,
|
queryKey: statusKeys.all,
|
||||||
queryFn: ({ signal }) => getStatus(signal),
|
queryFn: ({ signal }) => getStatus(signal),
|
||||||
refetchInterval: 60_000,
|
refetchInterval: 60_000,
|
||||||
refetchIntervalInBackground: true,
|
refetchIntervalInBackground: false,
|
||||||
refetchOnWindowFocus: true,
|
refetchOnWindowFocus: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -21,8 +21,9 @@ export function useIncidentHistoryQuery() {
|
|||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: statusKeys.history,
|
queryKey: statusKeys.history,
|
||||||
queryFn: ({ signal }) => getIncidentHistory(signal),
|
queryFn: ({ signal }) => getIncidentHistory(signal),
|
||||||
refetchInterval: 120_000,
|
refetchInterval: 300_000,
|
||||||
refetchIntervalInBackground: false,
|
refetchIntervalInBackground: false,
|
||||||
|
refetchOnWindowFocus: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,11 @@ import { resolveFavicon } from './monitors';
|
|||||||
const DAY_MS = 24 * 60 * 60 * 1000;
|
const DAY_MS = 24 * 60 * 60 * 1000;
|
||||||
const FAVICON_CACHE_SECONDS = 86_400;
|
const FAVICON_CACHE_SECONDS = 86_400;
|
||||||
const STATUS_CACHE_TIME_HEADER = 'X-Upwatch-Status-Cache-Time';
|
const STATUS_CACHE_TIME_HEADER = 'X-Upwatch-Status-Cache-Time';
|
||||||
|
const STATUS_NO_STORE_HEADERS = {
|
||||||
|
'Cache-Control': 'no-store',
|
||||||
|
'CDN-Cache-Control': 'no-store',
|
||||||
|
'Cloudflare-CDN-Cache-Control': 'no-store',
|
||||||
|
};
|
||||||
type ServiceStatus = 'up' | 'degraded' | 'down' | 'unknown' | 'maintenance';
|
type ServiceStatus = 'up' | 'degraded' | 'down' | 'unknown' | 'maintenance';
|
||||||
type OverallStatus = 'operational' | 'degraded' | 'down';
|
type OverallStatus = 'operational' | 'degraded' | 'down';
|
||||||
type DailyAggregate = { monitorId: number; day: Date; totalChecks: number; upChecks: number };
|
type DailyAggregate = { monitorId: number; day: Date; totalChecks: number; upChecks: number };
|
||||||
@@ -143,7 +148,7 @@ async function cachedStatusResponse(context: Context<{ Bindings: Env }>): Promis
|
|||||||
const cached = await cache.match(statusCacheKey(context));
|
const cached = await cache.match(statusCacheKey(context));
|
||||||
if (!cached) return undefined;
|
if (!cached) return undefined;
|
||||||
if (!isStatusCacheFresh(cached, maxAgeSeconds)) return undefined;
|
if (!isStatusCacheFresh(cached, maxAgeSeconds)) return undefined;
|
||||||
return cached;
|
return statusResponse(cached);
|
||||||
} catch {
|
} catch {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -154,10 +159,21 @@ export function isStatusCacheFresh(response: Response, maxAgeSeconds: number, no
|
|||||||
return Number.isFinite(cachedAt) && now - cachedAt < maxAgeSeconds * 1_000;
|
return Number.isFinite(cachedAt) && now - cachedAt < maxAgeSeconds * 1_000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function statusResponse(response: Response): Response {
|
||||||
|
const headers = new Headers(response.headers);
|
||||||
|
for (const [name, value] of Object.entries(STATUS_NO_STORE_HEADERS)) headers.set(name, value);
|
||||||
|
headers.delete(STATUS_CACHE_TIME_HEADER);
|
||||||
|
return new Response(response.body, {
|
||||||
|
status: response.status,
|
||||||
|
statusText: response.statusText,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function jsonWithEdgeCache(context: Context<{ Bindings: Env }>, body: unknown): Response {
|
function jsonWithEdgeCache(context: Context<{ Bindings: Env }>, body: unknown): Response {
|
||||||
const seconds = resolveStatusCacheSeconds(context.env);
|
const seconds = resolveStatusCacheSeconds(context.env);
|
||||||
if (seconds <= 0) return Response.json(body);
|
if (seconds <= 0) return Response.json(body, { headers: STATUS_NO_STORE_HEADERS });
|
||||||
const response = Response.json(body, {
|
const cachedResponse = Response.json(body, {
|
||||||
headers: {
|
headers: {
|
||||||
'Cache-Control': `public, max-age=${seconds}`,
|
'Cache-Control': `public, max-age=${seconds}`,
|
||||||
[STATUS_CACHE_TIME_HEADER]: String(Date.now()),
|
[STATUS_CACHE_TIME_HEADER]: String(Date.now()),
|
||||||
@@ -166,12 +182,12 @@ function jsonWithEdgeCache(context: Context<{ Bindings: Env }>, body: unknown):
|
|||||||
const cache = edgeCache();
|
const cache = edgeCache();
|
||||||
if (cache) {
|
if (cache) {
|
||||||
try {
|
try {
|
||||||
context.executionCtx.waitUntil(cache.put(statusCacheKey(context), response.clone()).catch(() => undefined));
|
context.executionCtx.waitUntil(cache.put(statusCacheKey(context), cachedResponse).catch(() => undefined));
|
||||||
} catch {
|
} catch {
|
||||||
// No ExecutionContext available (e.g. unit tests): serve without populating the edge cache.
|
// No ExecutionContext available (e.g. unit tests): serve without populating the edge cache.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return response;
|
return Response.json(body, { headers: STATUS_NO_STORE_HEADERS });
|
||||||
}
|
}
|
||||||
|
|
||||||
const statusRoutes = new Hono<{ Bindings: Env }>();
|
const statusRoutes = new Hono<{ Bindings: Env }>();
|
||||||
|
|||||||
@@ -119,6 +119,10 @@ describe('public status API', () => {
|
|||||||
const body = await response.json<PublicStatusResponse>();
|
const body = await response.json<PublicStatusResponse>();
|
||||||
|
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
|
expect(response.headers.get('Cache-Control')).toBe('no-store');
|
||||||
|
expect(response.headers.get('CDN-Cache-Control')).toBe('no-store');
|
||||||
|
expect(response.headers.get('Cloudflare-CDN-Cache-Control')).toBe('no-store');
|
||||||
|
expect(response.headers.get('X-Upwatch-Status-Cache-Time')).toBeNull();
|
||||||
expect(body.overall).toBe('operational');
|
expect(body.overall).toBe('operational');
|
||||||
expect(body.updatedAt).toEqual(expect.any(Number));
|
expect(body.updatedAt).toEqual(expect.any(Number));
|
||||||
expect(body.services).toHaveLength(1);
|
expect(body.services).toHaveLength(1);
|
||||||
|
|||||||
Reference in New Issue
Block a user