mirror of
https://github.com/lukaszraczylo/ghtracker.git
synced 2026-10-14 00:58:36 +00:00
Alerts that match a configured action get a button labelled from the config. A confirm dialog opens first when the action defines confirm text. After a press the UI posts the alert, reloads the state through the existing poll path and shows the reported status as a chip, with an anchor when the status carries a link. The alert dialog comes from reka-ui through the usual shadcn-vue wrappers.
54 lines
2.4 KiB
TypeScript
54 lines
2.4 KiB
TypeScript
import type { ActionStatus, RepoRefreshResult, TrackerState } from './types'
|
|
|
|
export async function fetchState(signal?: AbortSignal): Promise<TrackerState> {
|
|
const res = await fetch('/api/state', { signal, headers: { Accept: 'application/json' } })
|
|
if (!res.ok) throw new Error(`state request failed: HTTP ${res.status}`)
|
|
return (await res.json()) as TrackerState
|
|
}
|
|
|
|
/** Thrown when the server refuses a rescan because one ran recently. */
|
|
export class RescanLimitedError extends Error {}
|
|
|
|
export async function requestRefresh(): Promise<void> {
|
|
const res = await fetch('/refresh', { method: 'POST' })
|
|
if (res.status === 429) throw new RescanLimitedError('Rescan is limited to once every 5 minutes')
|
|
if (!res.ok) throw new Error(`rescan request failed: HTTP ${res.status}`)
|
|
}
|
|
|
|
/** Thrown when one repository was refreshed too recently or a scan is running. */
|
|
export class RepoRefreshRefusedError extends Error {}
|
|
|
|
const REPO_COOLDOWN_FALLBACK_SECONDS = 60
|
|
|
|
/** Refreshes one repository and returns the full state after it, plus the repo name. */
|
|
export async function requestRepoRefresh(fullName: string): Promise<RepoRefreshResult> {
|
|
const res = await fetch(`/refresh?repo=${encodeURIComponent(fullName)}`, { method: 'POST' })
|
|
if (res.status === 429) {
|
|
const wait = Number(res.headers.get('Retry-After')) || REPO_COOLDOWN_FALLBACK_SECONDS
|
|
throw new RepoRefreshRefusedError(`${fullName} was refreshed recently. Try again in ${wait}s`)
|
|
}
|
|
if (res.status === 409)
|
|
throw new RepoRefreshRefusedError('A refresh is already running. Try again shortly')
|
|
if (!res.ok) throw new Error(`refresh of ${fullName} failed: HTTP ${res.status}`)
|
|
return (await res.json()) as RepoRefreshResult
|
|
}
|
|
|
|
/** Sends an alert to the webhook behind an action; returns the status the webhook reported, if any. */
|
|
export async function requestAction(
|
|
id: string,
|
|
repo: string,
|
|
url: string,
|
|
): Promise<ActionStatus | null> {
|
|
const res = await fetch(`/api/actions/${encodeURIComponent(id)}`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ repo, url }),
|
|
})
|
|
if (!res.ok) {
|
|
const body = (await res.json().catch(() => null)) as { error?: string } | null
|
|
throw new Error(body?.error ?? `action ${id} failed: HTTP ${res.status}`)
|
|
}
|
|
const status = (await res.json().catch(() => null)) as ActionStatus | null
|
|
return status && typeof status.state === 'string' ? status : null
|
|
}
|