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

/**
* 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
);