You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
72 lines
3.7 KiB
72 lines
3.7 KiB
/** |
|
* RESTful Pull Requests Collection Endpoint |
|
* |
|
* GET /api/repos/{npub}/{repo}/pull-requests # List pull requests |
|
* POST /api/repos/{npub}/{repo}/pull-requests # Create pull request |
|
*/ |
|
|
|
import { json } from '@sveltejs/kit'; |
|
// @ts-ignore - SvelteKit generates this type |
|
import type { RequestHandler } from './$types'; |
|
import { prsService, nostrClient } from '$lib/services/service-registry.js'; |
|
import { createRepoGetHandler, withRepoValidation } from '$lib/utils/api-handlers.js'; |
|
import type { RepoRequestContext, RequestEvent } from '$lib/utils/api-context.js'; |
|
import { handleValidationError, handleApiError } from '$lib/utils/error-handler.js'; |
|
import { DEFAULT_NOSTR_RELAYS } from '$lib/config.js'; |
|
import { forwardEventIfEnabled } from '$lib/services/messaging/event-forwarder.js'; |
|
import logger from '$lib/services/logger.js'; |
|
import { getRelaysForEventPublishing } from '$lib/utils/repo-visibility.js'; |
|
import { fetchRepoAnnouncementsWithCache, findRepoAnnouncement } from '$lib/utils/nostr-utils.js'; |
|
import { eventCache } from '$lib/services/nostr/event-cache.js'; |
|
|
|
export const GET: RequestHandler = createRepoGetHandler( |
|
async (context: RepoRequestContext) => { |
|
const prs = await prsService.getPullRequests(context.repoOwnerPubkey, context.repo); |
|
return json(prs); |
|
}, |
|
{ operation: 'getPullRequests', requireRepoExists: false, requireRepoAccess: false } // PRs are stored in Nostr, don't require local repo |
|
); |
|
|
|
export const POST: RequestHandler = withRepoValidation( |
|
async ({ repoContext, requestContext, event }) => { |
|
const body = await event.request.json(); |
|
const { event: prEvent } = body; |
|
|
|
if (!prEvent) { |
|
throw handleValidationError('Missing event in request body', { operation: 'createPullRequest', npub: repoContext.npub, repo: repoContext.repo }); |
|
} |
|
|
|
// Verify the event is properly signed |
|
if (!prEvent.sig || !prEvent.id) { |
|
throw handleValidationError('Invalid event: missing signature or ID', { operation: 'createPullRequest', npub: repoContext.npub, repo: repoContext.repo }); |
|
} |
|
|
|
// Get repository announcement to determine visibility and relay publishing |
|
const allEvents = await fetchRepoAnnouncementsWithCache(nostrClient, repoContext.repoOwnerPubkey, eventCache); |
|
const announcement = findRepoAnnouncement(allEvents, repoContext.repo); |
|
|
|
// Determine which relays to publish to based on visibility |
|
const relaysToPublish = announcement ? getRelaysForEventPublishing(announcement) : DEFAULT_NOSTR_RELAYS; |
|
|
|
// Publish the event to relays (empty array means no relay publishing, but event is still saved to repo) |
|
const result = relaysToPublish.length > 0 |
|
? await nostrClient.publishEvent(prEvent, relaysToPublish) |
|
: { success: [], failed: [] }; |
|
|
|
if (result.failed.length > 0 && result.success.length === 0) { |
|
throw handleApiError(new Error('Failed to publish pull request to all relays'), { operation: 'createPullRequest', npub: repoContext.npub, repo: repoContext.repo }, 'Failed to publish pull request to all relays'); |
|
} |
|
|
|
// Forward to messaging platforms if user has unlimited access and preferences configured |
|
if (requestContext.userPubkeyHex && result.success.length > 0) { |
|
forwardEventIfEnabled(prEvent, requestContext.userPubkeyHex) |
|
.catch(err => { |
|
// Log but don't fail the request - forwarding is optional |
|
logger.warn({ error: err, npub: repoContext.npub, repo: repoContext.repo }, 'Failed to forward event to messaging platforms'); |
|
}); |
|
} |
|
|
|
return json({ success: true, event: prEvent, published: result }); |
|
}, |
|
{ operation: 'createPullRequest', requireRepoAccess: false } // PRs can be created by anyone with access |
|
);
|
|
|