problem: github rest api has rate limits

solution: parse problem url before publish merit request

additional: parse github pull url
This commit is contained in:
Bob
2024-07-27 15:54:07 +08:00
parent 3f90852fb5
commit 6ac2176b2c
5 changed files with 52 additions and 59 deletions

View File

@@ -14,6 +14,7 @@
import Todo from './Todo.svelte'; import Todo from './Todo.svelte';
import { isValidUrl } from '@/event_helpers/rockets'; import { isValidUrl } from '@/event_helpers/rockets';
import CalculateSats from './CalculateSats.svelte'; import CalculateSats from './CalculateSats.svelte';
import { isGitHubUrl, parseProblem } from '@/helpers';
export let rocketEvent: NDKEvent; export let rocketEvent: NDKEvent;
@@ -48,6 +49,14 @@
} }
} }
$: if (isGitHubUrl(problem)) {
parseProblem(problem).then((title) => {
if (title) {
problem = `${title}\n\n${problem}`;
}
});
}
function validateSolution(solution: string) { function validateSolution(solution: string) {
if (solution.length > 0) { if (solution.length > 0) {
return isValidUrl(solution); return isValidUrl(solution);

View File

@@ -28,11 +28,7 @@
<Breadcrumb.Separator /> <Breadcrumb.Separator />
<Breadcrumb.Item> <Breadcrumb.Item>
<Breadcrumb.Page> <Breadcrumb.Page>
{#await parseProblem(merit.Problem())} {merit.Problem().substring(0, 16)}{#if merit.Problem().length > 16}...{/if}
{merit.Problem().substring(0, 16)}{#if merit.Problem().length > 16}...{/if}
{:then parsed}
{parsed.substring(0, 16)}{#if parsed.length > 16}...{/if}
{/await}
</Breadcrumb.Page> </Breadcrumb.Page>
</Breadcrumb.Item> </Breadcrumb.Item>
</Breadcrumb.List> </Breadcrumb.List>

View File

@@ -5,7 +5,7 @@
import * as Table from '@/components/ui/table'; import * as Table from '@/components/ui/table';
import { MapOfMeritResult, MeritRequest } from '@/event_helpers/merits'; import { MapOfMeritResult, MeritRequest } from '@/event_helpers/merits';
import { Rocket, RocketATagFilter } from '@/event_helpers/rockets'; import { Rocket, RocketATagFilter } from '@/event_helpers/rockets';
import { parseProblem, unixToRelativeTime } from '@/helpers'; import { unixToRelativeTime } from '@/helpers';
import { ndk } from '@/ndk'; import { ndk } from '@/ndk';
import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk'; import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk';
import { Avatar, Name } from '@nostr-dev-kit/ndk-svelte-components'; import { Avatar, Name } from '@nostr-dev-kit/ndk-svelte-components';
@@ -115,11 +115,7 @@
</div> </div>
</Table.Cell> </Table.Cell>
<Table.Cell class="hidden text-left md:table-cell"> <Table.Cell class="hidden text-left md:table-cell">
{#await parseProblem(merit.Problem())} {merit.Problem().split('\n')[0]}
{merit.Problem()}
{:then parsed}
{parsed}
{/await}
</Table.Cell> </Table.Cell>
<Table.Cell class="table-cell">{merit.Sats}</Table.Cell> <Table.Cell class="table-cell">{merit.Sats}</Table.Cell>
<Table.Cell class="table-cell">{merit.Merits}</Table.Cell> <Table.Cell class="table-cell">{merit.Merits}</Table.Cell>

View File

@@ -11,13 +11,7 @@
import { Separator } from '$lib/components/ui/separator/index.js'; import { Separator } from '$lib/components/ui/separator/index.js';
import * as Table from '@/components/ui/table'; import * as Table from '@/components/ui/table';
import { Rocket, RocketATagFilter } from '@/event_helpers/rockets'; import { Rocket, RocketATagFilter } from '@/event_helpers/rockets';
import { import { formatReferenceTime, getCuckPrice, getRocketURL, unixToRelativeTime } from '@/helpers';
formatReferenceTime,
getCuckPrice,
getRocketURL,
parseProblem,
unixToRelativeTime
} from '@/helpers';
import { derived } from 'svelte/store'; import { derived } from 'svelte/store';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
@@ -123,11 +117,7 @@
<Card.Header class="pb-3"> <Card.Header class="pb-3">
<div class="flex flex-nowrap justify-between"> <div class="flex flex-nowrap justify-between">
<Card.Title> <Card.Title>
{#await parseProblem(merit.Problem())} {merit.Problem().split('\n')[0]}
Problem: {merit.Problem().substring(0, 20)}
{:then parsed}
{parsed}
{/await}
</Card.Title>{#if merit.Solution()}<a </Card.Title>{#if merit.Solution()}<a
class="flex flex-nowrap text-orange-500 underline decoration-orange-500" class="flex flex-nowrap text-orange-500 underline decoration-orange-500"
href={merit.Solution()}>View Solution <ExternalLink size={18} class="m-1" /></a href={merit.Solution()}>View Solution <ExternalLink size={18} class="m-1" /></a

View File

@@ -90,51 +90,53 @@ export async function getCuckPrice(): Promise<number | Error> {
} }
export async function parseProblem(problem: string) { export async function parseProblem(problem: string) {
try { if (!isGitHubUrl(problem)) {
if (isGitHubIssueUrl(problem)) { return;
const apiURL = convertToGitHubApiUrl(problem);
if (!apiURL) {
return problem;
}
const response = await fetch(apiURL);
const json = await response.json();
return json.title;
} else {
return problem;
}
} catch (error) {
console.error('Get title error:', error);
return problem;
} }
const apiURL = convertToGitHubApiUrl(problem);
if (!apiURL) {
return;
}
const response = await fetch(apiURL);
if (!response.ok) {
return;
}
const { title } = await response.json();
return title;
} }
function isGitHubIssueUrl(url: string): boolean { export function isGitHubUrl(str: string): boolean {
let url;
try { try {
const parsedUrl: URL = new URL(url); url = new URL(str);
if (parsedUrl.hostname !== 'github.com') { } catch {
return false;
}
const pathParts: string[] = parsedUrl.pathname.split('/').filter((part) => part !== '');
if (pathParts.length !== 4 || pathParts[2] !== 'issues' || !/^[1-9]\d*$/.test(pathParts[3])) {
return false;
}
return true;
} catch (error) {
return false; return false;
} }
const pathParts = url.pathname.split('/').filter(Boolean);
if (url.hostname !== 'github.com') {
return false;
}
if (pathParts.length !== 4) {
return false;
}
if (!['issues', 'pull'].includes(pathParts[2])) {
return false;
}
if (!/^[1-9]\d*$/.test(pathParts[3])) {
return false;
}
return true;
} }
function convertToGitHubApiUrl(issueUrl: URL | string): URL | null { function convertToGitHubApiUrl(issueUrl: string): URL | null {
const url = new URL(issueUrl);
const [owner, repo, , issueNumber] = url.pathname.split('/').filter(Boolean);
try { try {
const url = new URL(issueUrl); // Whether it's `issues` or `pull`, the API uses `issues`
if (url.hostname !== 'github.com') {
throw new Error('Not a valid GitHub URL');
}
const pathParts = url.pathname.split('/').filter((part) => part !== '');
if (pathParts.length !== 4 || pathParts[2] !== 'issues') {
throw new Error('Not a valid GitHub issue URL');
}
const [owner, repo, , issueNumber] = pathParts;
return new URL(`https://api.github.com/repos/${owner}/${repo}/issues/${issueNumber}`); return new URL(`https://api.github.com/repos/${owner}/${repo}/issues/${issueNumber}`);
} catch (error) { } catch (error) {
console.error('URL conversion error:', error); console.error('URL conversion error:', error);