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.
131 lines
4.8 KiB
131 lines
4.8 KiB
/** |
|
* Page metadata and OpenGraph tags for repository pages |
|
*/ |
|
|
|
import type { PageLoad } from './$types'; |
|
import { NostrClient } from '$lib/services/nostr/nostr-client.js'; |
|
import { MaintainerService } from '$lib/services/nostr/maintainer-service.js'; |
|
import { DEFAULT_NOSTR_RELAYS } from '$lib/config.js'; |
|
import { KIND } from '$lib/types/nostr.js'; |
|
import { nip19 } from 'nostr-tools'; |
|
import { extractRequestContext } from '$lib/utils/api-context.js'; |
|
|
|
export const load: PageLoad = async ({ params, url, parent }) => { |
|
const { npub, repo } = params; |
|
|
|
if (!npub || !repo) { |
|
return { |
|
title: 'Repository Not Found', |
|
description: 'Repository not found' |
|
}; |
|
} |
|
|
|
try { |
|
// Decode npub to get pubkey |
|
const decoded = nip19.decode(npub); |
|
if (decoded.type !== 'npub') { |
|
return { |
|
title: 'Invalid Repository', |
|
description: 'Invalid repository identifier' |
|
}; |
|
} |
|
|
|
const repoOwnerPubkey = decoded.data as string; |
|
|
|
// Fetch repository announcement |
|
const nostrClient = new NostrClient(DEFAULT_NOSTR_RELAYS); |
|
const events = await nostrClient.fetchEvents([ |
|
{ |
|
kinds: [KIND.REPO_ANNOUNCEMENT], |
|
authors: [repoOwnerPubkey], |
|
'#d': [repo], |
|
limit: 1 |
|
} |
|
]); |
|
|
|
if (events.length === 0) { |
|
return { |
|
title: `${repo} - Repository Not Found`, |
|
description: 'Repository announcement not found' |
|
}; |
|
} |
|
|
|
const announcement = events[0]; |
|
|
|
// Check privacy - for private repos, we'll let the API endpoints handle access control |
|
// The page load function runs server-side but doesn't have access to client auth headers |
|
// So we'll mark it as private and let the frontend handle access denial |
|
const maintainerService = new MaintainerService(DEFAULT_NOSTR_RELAYS); |
|
const isPrivate = announcement.tags.some(t => |
|
(t[0] === 'private' && t[1] === 'true') || |
|
(t[0] === 't' && t[1] === 'private') |
|
); |
|
|
|
// For private repos, we can't check access here (no user context in page load) |
|
// The frontend will need to check access via API and show appropriate error |
|
// We still expose basic metadata (name) but the API will enforce access |
|
|
|
const name = announcement.tags.find(t => t[0] === 'name')?.[1] || repo; |
|
const description = announcement.tags.find(t => t[0] === 'description')?.[1] || ''; |
|
const image = announcement.tags.find(t => t[0] === 'image')?.[1]; |
|
const banner = announcement.tags.find(t => t[0] === 'banner')?.[1]; |
|
|
|
// Debug: log image and banner tags if found |
|
if (image) console.log('[Page Load] Found image tag:', image); |
|
if (banner) console.log('[Page Load] Found banner tag:', banner); |
|
if (!image && !banner) { |
|
console.log('[Page Load] No image or banner tags found. Available tags:', |
|
announcement.tags.filter(t => t[0] === 'image' || t[0] === 'banner').map(t => t[0])); |
|
} |
|
const cloneUrls = announcement.tags |
|
.filter(t => t[0] === 'clone') |
|
.flatMap(t => t.slice(1)) |
|
.filter(url => url && typeof url === 'string') as string[]; |
|
const maintainers = announcement.tags |
|
.filter(t => t[0] === 'maintainers') |
|
.flatMap(t => t.slice(1)) |
|
.filter(m => m && typeof m === 'string') as string[]; |
|
// Owner is the author of the announcement event |
|
const ownerPubkey = announcement.pubkey; |
|
const language = announcement.tags.find(t => t[0] === 'language')?.[1]; |
|
const topics = announcement.tags |
|
.filter(t => t[0] === 't' && t[1] !== 'private') |
|
.map(t => t[1]) |
|
.filter(t => t && typeof t === 'string') as string[]; |
|
const website = announcement.tags.find(t => t[0] === 'website')?.[1]; |
|
const isPrivate = announcement.tags.some(t => |
|
(t[0] === 'private' && t[1] === 'true') || |
|
(t[0] === 't' && t[1] === 'private') |
|
); |
|
|
|
// Get git domain for constructing URLs |
|
const layoutData = await parent(); |
|
const gitDomain = (layoutData as { gitDomain?: string }).gitDomain || url.host || 'localhost:6543'; |
|
const protocol = gitDomain.startsWith('localhost') ? 'http' : 'https'; |
|
const repoUrl = `${protocol}://${gitDomain}/repos/${npub}/${repo}`; |
|
|
|
return { |
|
title: `${name} - ${repo}`, |
|
description: description || `Repository: ${name}`, |
|
image: image || banner || undefined, |
|
banner: banner || image || undefined, |
|
repoName: name, |
|
repoDescription: description, |
|
repoUrl, |
|
repoCloneUrls: cloneUrls, |
|
repoMaintainers: maintainers, |
|
repoOwnerPubkey: ownerPubkey, |
|
repoLanguage: language, |
|
repoTopics: topics, |
|
repoWebsite: website, |
|
repoIsPrivate: isPrivate, |
|
ogType: 'website' |
|
}; |
|
} catch (error) { |
|
console.error('Error loading repository metadata:', error); |
|
return { |
|
title: `${repo} - Repository`, |
|
description: 'Repository' |
|
}; |
|
} |
|
};
|
|
|