diff --git a/src/components/Menu.svelte b/src/components/Menu.svelte index 42c73e2..07e81f2 100644 --- a/src/components/Menu.svelte +++ b/src/components/Menu.svelte @@ -4,16 +4,8 @@ import { Badge } from '@/components/ui/badge'; import Separator from '@/components/ui/separator/separator.svelte'; import { currentUser, devmode } from '@/stores/session'; - import { - Code, - GitBranch, - HelpCircle, - Mail, - Package, - Pyramid, - Rocket, - Users - } from 'lucide-svelte'; + import { commitInfo } from '@/stores/github'; + import { Code, HelpCircle, Mail, Package, Pyramid, Rocket } from 'lucide-svelte'; import { GitAltBrand, TelegramBrand } from 'svelte-awesome-icons'; import NotifyMe from './NotifyMe.svelte'; @@ -85,6 +77,8 @@ {#if $devmode} RELEASE NAME: - shippable intermediary + shippable intermediary + Commit: {$commitInfo.hash} + Height: {$commitInfo.count} + {/if} diff --git a/src/lib/helpers.ts b/src/lib/helpers.ts index 8b5542b..1a3a36c 100644 --- a/src/lib/helpers.ts +++ b/src/lib/helpers.ts @@ -102,11 +102,6 @@ export async function getCuckPrice(): Promise { }); } -interface CommitInfo { - count: number; - hash: string; -} - interface GitHubUrlParts { owner: string; repo: string; @@ -114,7 +109,7 @@ interface GitHubUrlParts { number?: string; } -class GitHubApiError extends Error { +export class GitHubApiError extends Error { constructor( message: string, public status?: number @@ -124,7 +119,7 @@ class GitHubApiError extends Error { } } -function parseGitHubUrl(url: URL): GitHubUrlParts { +export function parseGitHubUrl(url: URL): GitHubUrlParts { const parts = url.pathname.split('/').filter(Boolean); if (parts.length < 2) { throw new Error('Invalid GitHub URL'); @@ -137,38 +132,6 @@ function parseGitHubUrl(url: URL): GitHubUrlParts { }; } -async function fetchGitHubApi(apiUrl: URL): Promise { - const response = await fetch(apiUrl); - if (!response.ok) { - throw new GitHubApiError(`HTTP error! status: ${response.status}`, response.status); - } - return response.json(); -} - -export async function getCommit(url: URL): Promise { - try { - const { owner, repo } = parseGitHubUrl(url); - const apiURL = new URL(`https://api.github.com/repos/${owner}/${repo}/commits`); - const json = await fetchGitHubApi(apiURL); - - if (!json[0]?.sha) { - throw new GitHubApiError('Failed to fetch commit info: API returned unexpected data'); - } - - return { - count: json.length, - hash: json[0].sha - }; - } catch (error) { - if (error instanceof GitHubApiError) { - throw error; - } - throw new Error( - `Failed to fetch commit info: ${error instanceof Error ? error.message : String(error)}` - ); - } -} - export async function parseProblem(problem: string): Promise { if (!isGitHubIssuesOrPullUrl(problem)) { return undefined; @@ -176,9 +139,15 @@ export async function parseProblem(problem: string): Promise try { const { owner, repo, number } = parseGitHubUrl(new URL(problem)); + const apiURL = new URL(`https://api.github.com/repos/${owner}/${repo}/issues/${number}`); - const { title } = await fetchGitHubApi(apiURL); - return title; + const response = await fetch(apiURL); + if (!response.ok) { + throw new GitHubApiError(`HTTP error! status: ${response.status}`, response.status); + } + + const json = await response.json(); + return json.title; } catch (error) { console.error('Failed to parse problem:', error); return undefined; diff --git a/src/lib/stores/github.ts b/src/lib/stores/github.ts new file mode 100644 index 0000000..446314d --- /dev/null +++ b/src/lib/stores/github.ts @@ -0,0 +1,48 @@ +import { GitHubApiError, parseGitHubUrl } from '@/helpers'; +import { writable } from 'svelte/store'; + +interface CommitInfo { + count: number; + hash: string; +} + +let _c: CommitInfo = { hash: '', count: 0 }; +export const commitInfo = writable(_c); + +export async function getCommitInfo(url: URL): Promise { + try { + const { owner, repo } = parseGitHubUrl(url); + const apiURL = new URL(`https://api.github.com/repos/${owner}/${repo}/commits?per_page=1`); + + const response = await fetch(apiURL); + if (!response.ok) { + throw new GitHubApiError(`HTTP error! status: ${response.status}`, response.status); + } + const json = await response.json(); + + if (!json[0]?.sha) { + throw new GitHubApiError('Failed to fetch commit info: API returned unexpected data'); + } + const totalCommits = parseLinkHeader(response.headers.get('Link')); + + let r: CommitInfo = { + count: totalCommits, + hash: json[0].sha + }; + commitInfo.set(r); + return r; + } catch (error) { + if (error instanceof GitHubApiError) { + throw error; + } + throw new Error( + `Failed to fetch commit info: ${error instanceof Error ? error.message : String(error)}` + ); + } +} + +function parseLinkHeader(header: string | null): number { + if (!header) return 0; + const matches = header.match(/page=(\d+)>; rel="last"/); + return matches ? parseInt(matches[1], 10) : 0; +} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 9a6445a..9dc5ee7 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -7,6 +7,7 @@ import { onMount } from 'svelte'; import '../app.css'; import SidePanelLayout from '../layouts/SidePanelLayout.svelte'; + import { getCommitInfo } from '@/stores/github'; let sessionStarted = false; let connected = false; @@ -29,6 +30,7 @@ onMount(() => { getBitcoinTip(); + getCommitInfo(new URL('https://github.com/nostrocket/hypergolic/')); }); setInterval(