feat: enhance evobgp with new command-line tools for bundle management, including pull, verify, and apply functionalities. Update go.mod to include necessary dependencies and complete todos in architecture plan for improved observability and deployment practices.
CI / changes (push) Successful in 4s
CI / go (push) Failing after 6s
CI / bird2 (push) Has been skipped
CI / openapi (push) Has been skipped

This commit is contained in:
Denozordec
2026-04-05 14:07:45 +07:00
parent 272542b92a
commit bf52b21150
131 changed files with 9222 additions and 17 deletions
+47
View File
@@ -0,0 +1,47 @@
import { browser } from '$app/environment';
export const TOKEN_STORAGE_KEY = 'evobgp_api_token';
export type Problem = {
title?: string;
status?: number;
detail?: string;
};
function mergeHeaders(init?: RequestInit): Headers {
const h = new Headers(init?.headers);
if (!h.has('Accept')) {
h.set('Accept', 'application/json');
}
if (browser) {
const t = localStorage.getItem(TOKEN_STORAGE_KEY);
if (t && !h.has('Authorization')) {
h.set('Authorization', `Bearer ${t}`);
}
}
return h;
}
export async function apiFetch(path: string, init?: RequestInit): Promise<Response> {
if (!browser) {
throw new Error('API is only available in the browser');
}
return fetch(path, { ...init, headers: mergeHeaders(init) });
}
export async function apiJSON<T>(path: string, init?: RequestInit): Promise<T> {
const res = await apiFetch(path, init);
const text = await res.text();
if (!res.ok) {
let detail = text;
try {
const j = JSON.parse(text) as Problem;
if (j?.detail) detail = j.detail;
} catch {
/* plain text */
}
throw new Error(detail || `HTTP ${res.status}`);
}
if (!text) return undefined as T;
return JSON.parse(text) as T;
}
+77
View File
@@ -0,0 +1,77 @@
export type ModuleRow = {
id: string;
type: string;
name: string;
enabled: boolean;
priority: number;
refresh_interval_sec: number;
cron_expr: string;
default_community_id: string | null;
doh_profile_id: string | null;
};
export type ModulesResponse = {
items: ModuleRow[];
next_cursor: string | null;
has_more: boolean;
};
export type RevisionRow = {
id: string;
content_hash: string;
created_at: string;
parent_revision_id: string | null;
materialized_prefix_count: number;
module_id: string | null;
};
export type RevisionsResponse = {
items: RevisionRow[];
next_cursor: string | null;
has_more: boolean;
};
export type PeerRow = {
id: string;
name: string;
neighbor: string;
session_state: string;
bgp_speaker_id: string | null;
};
export type PeersResponse = {
items: PeerRow[];
next_cursor: string | null;
has_more: boolean;
};
export type SpeakerRow = {
id: string;
role: string;
endpoint: string;
last_applied_revision_id: string | null;
};
export type SpeakersResponse = {
items: SpeakerRow[];
next_cursor: string | null;
has_more: boolean;
};
export type JobRow = {
job_id: string;
kind: string;
status: string;
idempotency_key?: string | null;
created_at?: string;
started_at?: string | null;
finished_at?: string | null;
error?: string | null;
meta?: Record<string, unknown>;
};
export type JobsResponse = {
items: JobRow[];
next_cursor: string | null;
has_more: boolean;
};