Browse Source

improved codebase

main
Silberengel 4 weeks ago
parent
commit
cdd71a9c04
  1. 25
      src/lib/components/PRDetail.svelte
  2. 8
      src/lib/services/git/file-manager.ts
  3. 1
      src/lib/types/nostr.ts
  4. 53
      src/lib/utils/npub-utils.ts
  5. 24
      src/routes/api/git/[...path]/+server.ts
  6. 29
      src/routes/api/repos/[npub]/[repo]/branch-protection/+server.ts
  7. 19
      src/routes/api/repos/[npub]/[repo]/branches/+server.ts
  8. 8
      src/routes/api/repos/[npub]/[repo]/download/+server.ts
  9. 29
      src/routes/api/repos/[npub]/[repo]/file/+server.ts
  10. 27
      src/routes/api/repos/[npub]/[repo]/fork/+server.ts
  11. 18
      src/routes/api/repos/[npub]/[repo]/highlights/+server.ts
  12. 7
      src/routes/api/repos/[npub]/[repo]/issues/+server.ts
  13. 20
      src/routes/api/repos/[npub]/[repo]/maintainers/+server.ts
  14. 8
      src/routes/api/repos/[npub]/[repo]/prs/+server.ts
  15. 8
      src/routes/api/repos/[npub]/[repo]/raw/+server.ts
  16. 8
      src/routes/api/repos/[npub]/[repo]/readme/+server.ts
  17. 35
      src/routes/api/repos/[npub]/[repo]/settings/+server.ts
  18. 19
      src/routes/api/repos/[npub]/[repo]/tags/+server.ts
  19. 15
      src/routes/api/repos/[npub]/[repo]/transfer/+server.ts
  20. 8
      src/routes/api/repos/[npub]/[repo]/tree/+server.ts
  21. 12
      src/routes/api/repos/[npub]/[repo]/verify/+server.ts
  22. 58
      src/routes/api/search/+server.ts
  23. 5
      src/routes/docs/+page.svelte
  24. 5
      src/routes/docs/nip34/+page.svelte
  25. 5
      src/routes/docs/nip34/spec/+page.svelte
  26. 9
      src/routes/repos/[npub]/[repo]/+page.svelte
  27. 16
      src/routes/search/+page.svelte
  28. 82
      src/routes/signup/+page.svelte

25
src/lib/components/PRDetail.svelte

@ -27,8 +27,27 @@ @@ -27,8 +27,27 @@
let { pr, npub, repo, repoOwnerPubkey }: Props = $props();
let highlights = $state<Array<any>>([]);
let comments = $state<Array<any>>([]);
let highlights = $state<Array<{
id: string;
content: string;
pubkey: string;
created_at: number;
comments?: Array<{
id: string;
content: string;
pubkey: string;
created_at: number;
[key: string]: unknown;
}>;
[key: string]: unknown;
}>>([]);
let comments = $state<Array<{
id: string;
content: string;
pubkey: string;
created_at: number;
[key: string]: unknown;
}>>([]);
let loading = $state(false);
let error = $state<string | null>(null);
let userPubkey = $state<string | null>(null);
@ -105,7 +124,7 @@ @@ -105,7 +124,7 @@
if (response.ok) {
const data = await response.json();
// Combine all file diffs
prDiff = data.map((d: any) =>
prDiff = data.map((d: { file: string; diff: string }) =>
`--- ${d.file}\n+++ ${d.file}\n${d.diff}`
).join('\n\n');
}

8
src/lib/services/git/file-manager.ts

@ -671,7 +671,11 @@ export class FileManager { @@ -671,7 +671,11 @@ export class FileManager {
const git: SimpleGit = simpleGit(repoPath);
try {
const logOptions: any = {
const logOptions: {
maxCount: number;
from: string;
file?: string;
} = {
maxCount: limit,
from: branch
};
@ -687,7 +691,7 @@ export class FileManager { @@ -687,7 +691,7 @@ export class FileManager {
message: commit.message,
author: `${commit.author_name} <${commit.author_email}>`,
date: commit.date,
files: commit.diff?.files?.map((f: any) => f.file) || []
files: commit.diff?.files?.map((f: { file: string }) => f.file) || []
}));
} catch (error) {
logger.error({ error, repoPath, branch, limit }, 'Error getting commit history');

1
src/lib/types/nostr.ts

@ -23,6 +23,7 @@ export interface NostrFilter { @@ -23,6 +23,7 @@ export interface NostrFilter {
since?: number;
until?: number;
limit?: number;
search?: string; // NIP-50: Search capability
}
export const KIND = {

53
src/lib/utils/npub-utils.ts

@ -0,0 +1,53 @@ @@ -0,0 +1,53 @@
/**
* Utility functions for working with npub (Nostr public key) encoding/decoding
*/
import { nip19 } from 'nostr-tools';
/**
* Decode an npub to a hex pubkey
* @param npub - The npub string to decode
* @returns The hex pubkey, or null if invalid
*/
export function decodeNpubToHex(npub: string): string | null {
try {
const decoded = nip19.decode(npub);
if (decoded.type === 'npub') {
return decoded.data as string;
}
return null;
} catch {
return null;
}
}
/**
* Decode an npub and return both the type and data
* @param npub - The npub string to decode
* @returns Object with type and hex pubkey, or null if invalid
*/
export function decodeNpub(npub: string): { type: 'npub'; data: string } | null {
try {
const decoded = nip19.decode(npub);
if (decoded.type === 'npub') {
return { type: 'npub', data: decoded.data as string };
}
return null;
} catch {
return null;
}
}
/**
* Validate and decode npub, throwing an error if invalid
* @param npub - The npub string to decode
* @returns The hex pubkey
* @throws Error if npub is invalid
*/
export function requireNpubHex(npub: string): string {
const decoded = decodeNpub(npub);
if (!decoded) {
throw new Error('Invalid npub format');
}
return decoded.data;
}

24
src/routes/api/git/[...path]/+server.ts

@ -7,6 +7,7 @@ import { error } from '@sveltejs/kit'; @@ -7,6 +7,7 @@ import { error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { RepoManager } from '$lib/services/git/repo-manager.js';
import { nip19 } from 'nostr-tools';
import { requireNpubHex } from '$lib/utils/npub-utils.js';
import { spawn, execSync } from 'child_process';
import { existsSync } from 'fs';
import { join, resolve } from 'path';
@ -69,11 +70,7 @@ function findGitHttpBackend(): string | null { @@ -69,11 +70,7 @@ function findGitHttpBackend(): string | null {
*/
async function getRepoAnnouncement(npub: string, repoName: string): Promise<NostrEvent | null> {
try {
const decoded = nip19.decode(npub);
if (decoded.type !== 'npub') {
return null;
}
const pubkey = decoded.data as string;
const pubkey = requireNpubHex(npub);
const events = await nostrClient.fetchEvents([
{
@ -127,10 +124,7 @@ export const GET: RequestHandler = async ({ params, url, request }) => { @@ -127,10 +124,7 @@ export const GET: RequestHandler = async ({ params, url, request }) => {
// Validate npub format
try {
const decoded = nip19.decode(npub);
if (decoded.type !== 'npub') {
return error(400, 'Invalid npub format');
}
pubkey = requireNpubHex(npub);
} catch {
return error(400, 'Invalid npub format');
}
@ -150,11 +144,7 @@ export const GET: RequestHandler = async ({ params, url, request }) => { @@ -150,11 +144,7 @@ export const GET: RequestHandler = async ({ params, url, request }) => {
// Check repository privacy for clone/fetch operations
let originalOwnerPubkey: string;
try {
const decoded = nip19.decode(npub);
if (decoded.type !== 'npub') {
return error(400, 'Invalid npub format');
}
originalOwnerPubkey = decoded.data as string;
originalOwnerPubkey = requireNpubHex(npub);
} catch {
return error(400, 'Invalid npub format');
}
@ -351,11 +341,7 @@ export const POST: RequestHandler = async ({ params, url, request }) => { @@ -351,11 +341,7 @@ export const POST: RequestHandler = async ({ params, url, request }) => {
// Validate npub format and decode to get pubkey
let originalOwnerPubkey: string;
try {
const decoded = nip19.decode(npub);
if (decoded.type !== 'npub') {
return error(400, 'Invalid npub format');
}
originalOwnerPubkey = decoded.data as string;
originalOwnerPubkey = requireNpubHex(npub);
} catch {
return error(400, 'Invalid npub format');
}

29
src/routes/api/repos/[npub]/[repo]/branch-protection/+server.ts

@ -13,6 +13,7 @@ import { NostrClient } from '$lib/services/nostr/nostr-client.js'; @@ -13,6 +13,7 @@ import { NostrClient } from '$lib/services/nostr/nostr-client.js';
import { nip19 } from 'nostr-tools';
import type { BranchProtectionRule } from '$lib/services/nostr/branch-protection-service.js';
import logger from '$lib/services/logger.js';
import { requireNpubHex, decodeNpubToHex } from '$lib/utils/npub-utils.js';
const branchProtectionService = new BranchProtectionService(DEFAULT_NOSTR_RELAYS);
const ownershipTransferService = new OwnershipTransferService(DEFAULT_NOSTR_RELAYS);
@ -32,12 +33,7 @@ export const GET: RequestHandler = async ({ params }: { params: { npub?: string; @@ -32,12 +33,7 @@ export const GET: RequestHandler = async ({ params }: { params: { npub?: string;
// Decode npub to get pubkey
let ownerPubkey: string;
try {
const decoded = nip19.decode(npub);
if (decoded.type === 'npub' && typeof decoded.data === 'string') {
ownerPubkey = decoded.data;
} else {
return error(400, 'Invalid npub format');
}
ownerPubkey = requireNpubHex(npub);
} catch {
return error(400, 'Invalid npub format');
}
@ -82,27 +78,12 @@ export const POST: RequestHandler = async ({ params, request }: { params: { npub @@ -82,27 +78,12 @@ export const POST: RequestHandler = async ({ params, request }: { params: { npub
// Decode npub to get pubkey
let ownerPubkey: string;
try {
const decoded = nip19.decode(npub);
if (decoded.type === 'npub' && typeof decoded.data === 'string') {
ownerPubkey = decoded.data;
} else {
return error(400, 'Invalid npub format');
}
ownerPubkey = requireNpubHex(npub);
} catch {
return error(400, 'Invalid npub format');
}
let userPubkeyHex: string = userPubkey;
try {
const userDecoded = nip19.decode(userPubkey) as { type: string; data: unknown };
// Type guard: check if it's an npub
if (userDecoded.type === 'npub' && typeof userDecoded.data === 'string') {
userPubkeyHex = userDecoded.data;
}
// If not npub, assume it's already hex
} catch {
// Assume it's already hex
}
const userPubkeyHex = decodeNpubToHex(userPubkey) || userPubkey;
// Check if user is owner
const currentOwner = await ownershipTransferService.getCurrentOwner(ownerPubkey, repo);
@ -111,7 +92,7 @@ export const POST: RequestHandler = async ({ params, request }: { params: { npub @@ -111,7 +92,7 @@ export const POST: RequestHandler = async ({ params, request }: { params: { npub
}
// Validate rules
const validatedRules: BranchProtectionRule[] = rules.map((rule: any) => ({
const validatedRules: BranchProtectionRule[] = rules.map((rule: { branch: string; requirePullRequest?: boolean; requireReviewers?: string[]; allowForcePush?: boolean; requireStatusChecks?: string[] }) => ({
branch: rule.branch,
requirePullRequest: rule.requirePullRequest || false,
requireReviewers: rule.requireReviewers || [],

19
src/routes/api/repos/[npub]/[repo]/branches/+server.ts

@ -9,6 +9,7 @@ import { FileManager } from '$lib/services/git/file-manager.js'; @@ -9,6 +9,7 @@ import { FileManager } from '$lib/services/git/file-manager.js';
import { MaintainerService } from '$lib/services/nostr/maintainer-service.js';
import { DEFAULT_NOSTR_RELAYS } from '$lib/config.js';
import { nip19 } from 'nostr-tools';
import { requireNpubHex, decodeNpubToHex } from '$lib/utils/npub-utils.js';
import logger from '$lib/services/logger.js';
const repoRoot = process.env.GIT_REPO_ROOT || '/repos';
@ -64,27 +65,13 @@ export const POST: RequestHandler = async ({ params, request }: { params: { npub @@ -64,27 +65,13 @@ export const POST: RequestHandler = async ({ params, request }: { params: { npub
// Check if user is a maintainer
let repoOwnerPubkey: string;
try {
const decoded = nip19.decode(npub);
if (decoded.type === 'npub') {
repoOwnerPubkey = decoded.data as string;
} else {
return error(400, 'Invalid npub format');
}
repoOwnerPubkey = requireNpubHex(npub);
} catch {
return error(400, 'Invalid npub format');
}
// Convert userPubkey to hex if needed
let userPubkeyHex = userPubkey;
try {
const userDecoded = nip19.decode(userPubkey);
// @ts-ignore - nip19 types are incomplete, but we know npub returns string
if (userDecoded.type === 'npub') {
userPubkeyHex = userDecoded.data as unknown as string;
}
} catch {
// Assume it's already a hex pubkey
}
const userPubkeyHex = decodeNpubToHex(userPubkey) || userPubkey;
const isMaintainer = await maintainerService.isMaintainer(userPubkeyHex, repoOwnerPubkey, repo);
if (!isMaintainer) {

8
src/routes/api/repos/[npub]/[repo]/download/+server.ts

@ -8,6 +8,7 @@ import { FileManager } from '$lib/services/git/file-manager.js'; @@ -8,6 +8,7 @@ import { FileManager } from '$lib/services/git/file-manager.js';
import { MaintainerService } from '$lib/services/nostr/maintainer-service.js';
import { DEFAULT_NOSTR_RELAYS } from '$lib/config.js';
import { nip19 } from 'nostr-tools';
import { requireNpubHex } from '$lib/utils/npub-utils.js';
import { exec } from 'child_process';
import { promisify } from 'util';
import { existsSync, readFileSync } from 'fs';
@ -37,12 +38,7 @@ export const GET: RequestHandler = async ({ params, url, request }) => { @@ -37,12 +38,7 @@ export const GET: RequestHandler = async ({ params, url, request }) => {
// Check repository privacy
let repoOwnerPubkey: string;
try {
const decoded = nip19.decode(npub);
if (decoded.type === 'npub') {
repoOwnerPubkey = decoded.data as string;
} else {
return error(400, 'Invalid npub format');
}
repoOwnerPubkey = requireNpubHex(npub);
} catch {
return error(400, 'Invalid npub format');
}

29
src/routes/api/repos/[npub]/[repo]/file/+server.ts

@ -12,6 +12,8 @@ import { nip19 } from 'nostr-tools'; @@ -12,6 +12,8 @@ import { nip19 } from 'nostr-tools';
import { verifyNIP98Auth } from '$lib/services/nostr/nip98-auth.js';
import { auditLogger } from '$lib/services/security/audit-logger.js';
import logger from '$lib/services/logger.js';
import type { NostrEvent } from '$lib/types/nostr.js';
import { requireNpubHex, decodeNpubToHex } from '$lib/utils/npub-utils.js';
const repoRoot = process.env.GIT_REPO_ROOT || '/repos';
const fileManager = new FileManager(repoRoot);
@ -35,12 +37,7 @@ export const GET: RequestHandler = async ({ params, url, request }: { params: { @@ -35,12 +37,7 @@ export const GET: RequestHandler = async ({ params, url, request }: { params: {
// Check repository privacy
let repoOwnerPubkey: string;
try {
const decoded = nip19.decode(npub);
if (decoded.type === 'npub') {
repoOwnerPubkey = decoded.data as string;
} else {
return error(400, 'Invalid npub format');
}
repoOwnerPubkey = requireNpubHex(npub);
} catch {
return error(400, 'Invalid npub format');
}
@ -131,27 +128,13 @@ export const POST: RequestHandler = async ({ params, url, request }: { params: { @@ -131,27 +128,13 @@ export const POST: RequestHandler = async ({ params, url, request }: { params: {
// Check if user is a maintainer
let repoOwnerPubkey: string;
try {
const decoded = nip19.decode(npub);
if (decoded.type === 'npub') {
repoOwnerPubkey = decoded.data as string;
} else {
return error(400, 'Invalid npub format');
}
repoOwnerPubkey = requireNpubHex(npub);
} catch {
return error(400, 'Invalid npub format');
}
// Convert userPubkey to hex if needed
let userPubkeyHex = userPubkey;
try {
const userDecoded = nip19.decode(userPubkey);
// @ts-ignore - nip19 types are incomplete, but we know npub returns string
if (userDecoded.type === 'npub') {
userPubkeyHex = userDecoded.data as unknown as string;
}
} catch {
// Assume it's already a hex pubkey
}
const userPubkeyHex = decodeNpubToHex(userPubkey) || userPubkey;
const isMaintainer = await maintainerService.isMaintainer(userPubkeyHex, repoOwnerPubkey, repo);
if (!isMaintainer) {
@ -164,7 +147,7 @@ export const POST: RequestHandler = async ({ params, url, request }: { params: { @@ -164,7 +147,7 @@ export const POST: RequestHandler = async ({ params, url, request }: { params: {
// nsecKey is only for server-side use via environment variables.
const signingOptions: {
useNIP07?: boolean;
nip98Event?: any;
nip98Event?: NostrEvent;
nsecKey?: string;
} = {};

27
src/routes/api/repos/[npub]/[repo]/fork/+server.ts

@ -11,6 +11,7 @@ import { NostrClient } from '$lib/services/nostr/nostr-client.js'; @@ -11,6 +11,7 @@ import { NostrClient } from '$lib/services/nostr/nostr-client.js';
import { KIND, type NostrEvent } from '$lib/types/nostr.js';
import { nip19 } from 'nostr-tools';
import { signEventWithNIP07 } from '$lib/services/nostr/nip07-signer.js';
import { requireNpubHex, decodeNpubToHex } from '$lib/utils/npub-utils.js';
import { OwnershipTransferService } from '$lib/services/nostr/ownership-transfer-service.js';
import { exec } from 'child_process';
import { promisify } from 'util';
@ -92,28 +93,13 @@ export const POST: RequestHandler = async ({ params, request }) => { @@ -92,28 +93,13 @@ export const POST: RequestHandler = async ({ params, request }) => {
// Decode original repo owner npub
let originalOwnerPubkey: string;
try {
const decoded = nip19.decode(npub);
if (decoded.type === 'npub') {
originalOwnerPubkey = decoded.data as string;
} else {
return error(400, 'Invalid npub format');
}
originalOwnerPubkey = requireNpubHex(npub);
} catch {
return error(400, 'Invalid npub format');
}
// Decode user pubkey if needed (must be done before using it)
let userPubkeyHex = userPubkey;
try {
const userDecoded = nip19.decode(userPubkey) as { type: string; data: unknown };
// Type guard: check if it's an npub
if (userDecoded.type === 'npub' && typeof userDecoded.data === 'string') {
userPubkeyHex = userDecoded.data;
}
// If not npub, assume it's already hex
} catch {
// Assume it's already hex
}
const userPubkeyHex = decodeNpubToHex(userPubkey) || userPubkey;
// Convert to npub for resource check and path construction
const userNpub = nip19.npubEncode(userPubkeyHex);
@ -394,12 +380,7 @@ export const GET: RequestHandler = async ({ params }) => { @@ -394,12 +380,7 @@ export const GET: RequestHandler = async ({ params }) => {
// Decode repo owner npub
let ownerPubkey: string;
try {
const decoded = nip19.decode(npub);
if (decoded.type === 'npub') {
ownerPubkey = decoded.data as string;
} else {
return error(400, 'Invalid npub format');
}
ownerPubkey = requireNpubHex(npub);
} catch {
return error(400, 'Invalid npub format');
}

18
src/routes/api/repos/[npub]/[repo]/highlights/+server.ts

@ -7,6 +7,7 @@ import type { RequestHandler } from './$types'; @@ -7,6 +7,7 @@ import type { RequestHandler } from './$types';
import { HighlightsService } from '$lib/services/nostr/highlights-service.js';
import { DEFAULT_NOSTR_RELAYS } from '$lib/config.js';
import { nip19 } from 'nostr-tools';
import { requireNpubHex, decodeNpubToHex } from '$lib/utils/npub-utils.js';
import { verifyEvent } from 'nostr-tools';
import type { NostrEvent } from '$lib/types/nostr.js';
import { combineRelays } from '$lib/config.js';
@ -38,26 +39,13 @@ export const GET: RequestHandler = async ({ params, url }) => { @@ -38,26 +39,13 @@ export const GET: RequestHandler = async ({ params, url }) => {
// Decode npub to get pubkey
let repoOwnerPubkey: string;
try {
const decoded = nip19.decode(npub);
if (decoded.type === 'npub') {
repoOwnerPubkey = decoded.data as string;
} else {
return error(400, 'Invalid npub format');
}
repoOwnerPubkey = requireNpubHex(npub);
} catch {
return error(400, 'Invalid npub format');
}
// Decode prAuthor if it's an npub
let prAuthorPubkey = prAuthor;
try {
const decoded = nip19.decode(prAuthor);
if (decoded.type === 'npub') {
prAuthorPubkey = decoded.data as string;
}
} catch {
// Assume it's already hex
}
const prAuthorPubkey = decodeNpubToHex(prAuthor) || prAuthor;
// Get highlights for the PR
const highlights = await highlightsService.getHighlightsForPR(

7
src/routes/api/repos/[npub]/[repo]/issues/+server.ts

@ -19,11 +19,12 @@ export const GET: RequestHandler = async ({ params, url, request }) => { @@ -19,11 +19,12 @@ export const GET: RequestHandler = async ({ params, url, request }) => {
try {
// Convert npub to pubkey
const decoded = nip19.decode(npub);
if (decoded.type !== 'npub') {
let repoOwnerPubkey: string;
try {
repoOwnerPubkey = requireNpubHex(npub);
} catch {
return error(400, 'Invalid npub format');
}
const repoOwnerPubkey = decoded.data as string;
// Check repository privacy
const { checkRepoAccess } = await import('$lib/utils/repo-privacy.js');

20
src/routes/api/repos/[npub]/[repo]/maintainers/+server.ts

@ -8,6 +8,7 @@ import type { RequestHandler } from './$types'; @@ -8,6 +8,7 @@ import type { RequestHandler } from './$types';
import { MaintainerService } from '$lib/services/nostr/maintainer-service.js';
import { DEFAULT_NOSTR_RELAYS } from '$lib/config.js';
import { nip19 } from 'nostr-tools';
import { requireNpubHex, decodeNpubToHex } from '$lib/utils/npub-utils.js';
import logger from '$lib/services/logger.js';
const maintainerService = new MaintainerService(DEFAULT_NOSTR_RELAYS);
@ -24,12 +25,7 @@ export const GET: RequestHandler = async ({ params, url }: { params: { npub?: st @@ -24,12 +25,7 @@ export const GET: RequestHandler = async ({ params, url }: { params: { npub?: st
// Convert npub to pubkey
let repoOwnerPubkey: string;
try {
const decoded = nip19.decode(npub);
if (decoded.type === 'npub') {
repoOwnerPubkey = decoded.data as string;
} else {
return error(400, 'Invalid npub format');
}
repoOwnerPubkey = requireNpubHex(npub);
} catch {
return error(400, 'Invalid npub format');
}
@ -38,17 +34,7 @@ export const GET: RequestHandler = async ({ params, url }: { params: { npub?: st @@ -38,17 +34,7 @@ export const GET: RequestHandler = async ({ params, url }: { params: { npub?: st
// If userPubkey provided, check if they're a maintainer
if (userPubkey) {
let userPubkeyHex = userPubkey;
try {
// Try to decode if it's an npub
const userDecoded = nip19.decode(userPubkey);
// @ts-ignore - nip19 types are incomplete, but we know npub returns string
if (userDecoded.type === 'npub') {
userPubkeyHex = userDecoded.data as unknown as string;
}
} catch {
// Assume it's already a hex pubkey
}
const userPubkeyHex = decodeNpubToHex(userPubkey) || userPubkey;
const isMaintainer = maintainers.includes(userPubkeyHex);
return json({

8
src/routes/api/repos/[npub]/[repo]/prs/+server.ts

@ -8,6 +8,7 @@ import type { RequestHandler } from './$types'; @@ -8,6 +8,7 @@ import type { RequestHandler } from './$types';
import { PRsService } from '$lib/services/nostr/prs-service.js';
import { DEFAULT_NOSTR_RELAYS } from '$lib/config.js';
import { nip19 } from 'nostr-tools';
import { requireNpubHex } from '$lib/utils/npub-utils.js';
import logger from '$lib/services/logger.js';
export const GET: RequestHandler = async ({ params, url, request }: { params: { npub?: string; repo?: string }; url: URL; request: Request }) => {
@ -22,12 +23,7 @@ export const GET: RequestHandler = async ({ params, url, request }: { params: { @@ -22,12 +23,7 @@ export const GET: RequestHandler = async ({ params, url, request }: { params: {
// Convert npub to pubkey
let repoOwnerPubkey: string;
try {
const decoded = nip19.decode(npub);
if (decoded.type === 'npub') {
repoOwnerPubkey = decoded.data as string;
} else {
return error(400, 'Invalid npub format');
}
repoOwnerPubkey = requireNpubHex(npub);
} catch {
return error(400, 'Invalid npub format');
}

8
src/routes/api/repos/[npub]/[repo]/raw/+server.ts

@ -8,6 +8,7 @@ import { FileManager } from '$lib/services/git/file-manager.js'; @@ -8,6 +8,7 @@ import { FileManager } from '$lib/services/git/file-manager.js';
import { MaintainerService } from '$lib/services/nostr/maintainer-service.js';
import { DEFAULT_NOSTR_RELAYS } from '$lib/config.js';
import { nip19 } from 'nostr-tools';
import { requireNpubHex } from '$lib/utils/npub-utils.js';
import logger from '$lib/services/logger.js';
const repoRoot = process.env.GIT_REPO_ROOT || '/repos';
@ -32,12 +33,7 @@ export const GET: RequestHandler = async ({ params, url, request }) => { @@ -32,12 +33,7 @@ export const GET: RequestHandler = async ({ params, url, request }) => {
// Check repository privacy
let repoOwnerPubkey: string;
try {
const decoded = nip19.decode(npub);
if (decoded.type === 'npub') {
repoOwnerPubkey = decoded.data as string;
} else {
return error(400, 'Invalid npub format');
}
repoOwnerPubkey = requireNpubHex(npub);
} catch {
return error(400, 'Invalid npub format');
}

8
src/routes/api/repos/[npub]/[repo]/readme/+server.ts

@ -8,6 +8,7 @@ import { FileManager } from '$lib/services/git/file-manager.js'; @@ -8,6 +8,7 @@ import { FileManager } from '$lib/services/git/file-manager.js';
import { MaintainerService } from '$lib/services/nostr/maintainer-service.js';
import { DEFAULT_NOSTR_RELAYS } from '$lib/config.js';
import { nip19 } from 'nostr-tools';
import { requireNpubHex } from '$lib/utils/npub-utils.js';
import logger from '$lib/services/logger.js';
const repoRoot = process.env.GIT_REPO_ROOT || '/repos';
@ -42,12 +43,7 @@ export const GET: RequestHandler = async ({ params, url, request }) => { @@ -42,12 +43,7 @@ export const GET: RequestHandler = async ({ params, url, request }) => {
// Check repository privacy
let repoOwnerPubkey: string;
try {
const decoded = nip19.decode(npub);
if (decoded.type === 'npub') {
repoOwnerPubkey = decoded.data as string;
} else {
return error(400, 'Invalid npub format');
}
repoOwnerPubkey = requireNpubHex(npub);
} catch {
return error(400, 'Invalid npub format');
}

35
src/routes/api/repos/[npub]/[repo]/settings/+server.ts

@ -9,6 +9,7 @@ import { DEFAULT_NOSTR_RELAYS, combineRelays, getGitUrl } from '$lib/config.js'; @@ -9,6 +9,7 @@ import { DEFAULT_NOSTR_RELAYS, combineRelays, getGitUrl } from '$lib/config.js';
import { getUserRelays } from '$lib/services/nostr/user-relays.js';
import { KIND } from '$lib/types/nostr.js';
import { nip19 } from 'nostr-tools';
import { requireNpubHex, decodeNpubToHex } from '$lib/utils/npub-utils.js';
import { signEventWithNIP07 } from '$lib/services/nostr/nip07-signer.js';
import { MaintainerService } from '$lib/services/nostr/maintainer-service.js';
import logger from '$lib/services/logger.js';
@ -33,12 +34,7 @@ export const GET: RequestHandler = async ({ params, url, request }) => { @@ -33,12 +34,7 @@ export const GET: RequestHandler = async ({ params, url, request }) => {
// Decode npub to get pubkey
let repoOwnerPubkey: string;
try {
const decoded = nip19.decode(npub) as { type: string; data: unknown };
if (decoded.type === 'npub' && typeof decoded.data === 'string') {
repoOwnerPubkey = decoded.data;
} else {
return error(400, 'Invalid npub format');
}
repoOwnerPubkey = requireNpubHex(npub);
} catch {
return error(400, 'Invalid npub format');
}
@ -48,15 +44,7 @@ export const GET: RequestHandler = async ({ params, url, request }) => { @@ -48,15 +44,7 @@ export const GET: RequestHandler = async ({ params, url, request }) => {
return error(401, 'Authentication required');
}
let userPubkeyHex = userPubkey;
try {
const userDecoded = nip19.decode(userPubkey) as { type: string; data: unknown };
if (userDecoded.type === 'npub' && typeof userDecoded.data === 'string') {
userPubkeyHex = userDecoded.data;
}
} catch {
// Assume it's already hex
}
const userPubkeyHex = decodeNpubToHex(userPubkey) || userPubkey;
const currentOwner = await ownershipTransferService.getCurrentOwner(repoOwnerPubkey, repo);
if (userPubkeyHex !== currentOwner) {
@ -127,25 +115,12 @@ export const POST: RequestHandler = async ({ params, request }) => { @@ -127,25 +115,12 @@ export const POST: RequestHandler = async ({ params, request }) => {
// Decode npub to get pubkey
let repoOwnerPubkey: string;
try {
const decoded = nip19.decode(npub) as { type: string; data: unknown };
if (decoded.type === 'npub' && typeof decoded.data === 'string') {
repoOwnerPubkey = decoded.data;
} else {
return error(400, 'Invalid npub format');
}
repoOwnerPubkey = requireNpubHex(npub);
} catch {
return error(400, 'Invalid npub format');
}
let userPubkeyHex = userPubkey;
try {
const userDecoded = nip19.decode(userPubkey) as { type: string; data: unknown };
if (userDecoded.type === 'npub' && typeof userDecoded.data === 'string') {
userPubkeyHex = userDecoded.data;
}
} catch {
// Assume it's already hex
}
const userPubkeyHex = decodeNpubToHex(userPubkey) || userPubkey;
// Check if user is owner
const currentOwner = await ownershipTransferService.getCurrentOwner(repoOwnerPubkey, repo);

19
src/routes/api/repos/[npub]/[repo]/tags/+server.ts

@ -9,6 +9,7 @@ import { FileManager } from '$lib/services/git/file-manager.js'; @@ -9,6 +9,7 @@ import { FileManager } from '$lib/services/git/file-manager.js';
import { MaintainerService } from '$lib/services/nostr/maintainer-service.js';
import { DEFAULT_NOSTR_RELAYS } from '$lib/config.js';
import { nip19 } from 'nostr-tools';
import { requireNpubHex, decodeNpubToHex } from '$lib/utils/npub-utils.js';
import logger from '$lib/services/logger.js';
const repoRoot = process.env.GIT_REPO_ROOT || '/repos';
@ -73,27 +74,13 @@ export const POST: RequestHandler = async ({ params, request }: { params: { npub @@ -73,27 +74,13 @@ export const POST: RequestHandler = async ({ params, request }: { params: { npub
// Check if user is a maintainer
let repoOwnerPubkey: string;
try {
const decoded = nip19.decode(npub);
if (decoded.type === 'npub') {
repoOwnerPubkey = decoded.data as string;
} else {
return error(400, 'Invalid npub format');
}
repoOwnerPubkey = requireNpubHex(npub);
} catch {
return error(400, 'Invalid npub format');
}
// Convert userPubkey to hex if needed
let userPubkeyHex = userPubkey;
try {
const userDecoded = nip19.decode(userPubkey);
// @ts-ignore - nip19 types are incomplete, but we know npub returns string
if (userDecoded.type === 'npub') {
userPubkeyHex = userDecoded.data as unknown as string;
}
} catch {
// Assume it's already a hex pubkey
}
const userPubkeyHex = decodeNpubToHex(userPubkey) || userPubkey;
const isMaintainer = await maintainerService.isMaintainer(userPubkeyHex, repoOwnerPubkey, repo);
if (!isMaintainer) {

15
src/routes/api/repos/[npub]/[repo]/transfer/+server.ts

@ -9,6 +9,7 @@ import { NostrClient } from '$lib/services/nostr/nostr-client.js'; @@ -9,6 +9,7 @@ import { NostrClient } from '$lib/services/nostr/nostr-client.js';
import { DEFAULT_NOSTR_RELAYS, combineRelays } from '$lib/config.js';
import { KIND } from '$lib/types/nostr.js';
import { nip19 } from 'nostr-tools';
import { requireNpubHex, decodeNpubToHex } from '$lib/utils/npub-utils.js';
import { verifyEvent } from 'nostr-tools';
import type { NostrEvent } from '$lib/types/nostr.js';
import { getUserRelays } from '$lib/services/nostr/user-relays.js';
@ -31,12 +32,7 @@ export const GET: RequestHandler = async ({ params }) => { @@ -31,12 +32,7 @@ export const GET: RequestHandler = async ({ params }) => {
// Decode npub to get pubkey
let originalOwnerPubkey: string;
try {
const decoded = nip19.decode(npub);
if (decoded.type === 'npub') {
originalOwnerPubkey = decoded.data as string;
} else {
return error(400, 'Invalid npub format');
}
originalOwnerPubkey = requireNpubHex(npub);
} catch {
return error(400, 'Invalid npub format');
}
@ -109,12 +105,7 @@ export const POST: RequestHandler = async ({ params, request }) => { @@ -109,12 +105,7 @@ export const POST: RequestHandler = async ({ params, request }) => {
// Decode npub to get original owner pubkey
let originalOwnerPubkey: string;
try {
const decoded = nip19.decode(npub);
if (decoded.type === 'npub') {
originalOwnerPubkey = decoded.data as string;
} else {
return error(400, 'Invalid npub format');
}
originalOwnerPubkey = requireNpubHex(npub);
} catch {
return error(400, 'Invalid npub format');
}

8
src/routes/api/repos/[npub]/[repo]/tree/+server.ts

@ -8,6 +8,7 @@ import { FileManager } from '$lib/services/git/file-manager.js'; @@ -8,6 +8,7 @@ import { FileManager } from '$lib/services/git/file-manager.js';
import { MaintainerService } from '$lib/services/nostr/maintainer-service.js';
import { DEFAULT_NOSTR_RELAYS } from '$lib/config.js';
import { nip19 } from 'nostr-tools';
import { requireNpubHex } from '$lib/utils/npub-utils.js';
import logger from '$lib/services/logger.js';
const repoRoot = process.env.GIT_REPO_ROOT || '/repos';
@ -32,12 +33,7 @@ export const GET: RequestHandler = async ({ params, url, request }) => { @@ -32,12 +33,7 @@ export const GET: RequestHandler = async ({ params, url, request }) => {
// Check repository privacy
let repoOwnerPubkey: string;
try {
const decoded = nip19.decode(npub);
if (decoded.type === 'npub') {
repoOwnerPubkey = decoded.data as string;
} else {
return error(400, 'Invalid npub format');
}
repoOwnerPubkey = requireNpubHex(npub);
} catch {
return error(400, 'Invalid npub format');
}

12
src/routes/api/repos/[npub]/[repo]/verify/+server.ts

@ -32,12 +32,7 @@ export const GET: RequestHandler = async ({ params }: { params: { npub?: string; @@ -32,12 +32,7 @@ export const GET: RequestHandler = async ({ params }: { params: { npub?: string;
// Decode npub to get pubkey
let ownerPubkey: string;
try {
const decoded = nip19.decode(npub) as { type: string; data: unknown };
if (decoded.type === 'npub' && typeof decoded.data === 'string') {
ownerPubkey = decoded.data;
} else {
return error(400, 'Invalid npub format');
}
ownerPubkey = requireNpubHex(npub);
} catch {
return error(400, 'Invalid npub format');
}
@ -87,10 +82,7 @@ export const GET: RequestHandler = async ({ params }: { params: { npub?: string; @@ -87,10 +82,7 @@ export const GET: RequestHandler = async ({ params }: { params: { npub?: string;
// Decode npub if needed
if (toPubkey) {
try {
const decoded = nip19.decode(toPubkey) as { type: string; data: unknown };
if (decoded.type === 'npub' && typeof decoded.data === 'string') {
toPubkey = decoded.data;
}
toPubkey = decodeNpubToHex(toPubkey) || toPubkey;
} catch {
// Assume it's already hex
}

58
src/routes/api/search/+server.ts

@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
import { json, error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { NostrClient } from '$lib/services/nostr/nostr-client.js';
import { DEFAULT_NOSTR_RELAYS } from '$lib/config.js';
import { DEFAULT_NOSTR_RELAYS, DEFAULT_NOSTR_SEARCH_RELAYS } from '$lib/config.js';
import { KIND } from '$lib/types/nostr.js';
import { FileManager } from '$lib/services/git/file-manager.js';
import { nip19 } from 'nostr-tools';
@ -30,8 +30,8 @@ export const GET: RequestHandler = async ({ url }) => { @@ -30,8 +30,8 @@ export const GET: RequestHandler = async ({ url }) => {
}
try {
// Create a new client instance for each search to ensure fresh connections
const nostrClient = new NostrClient(DEFAULT_NOSTR_RELAYS);
// Use search relays which are more likely to support NIP-50
const nostrClient = new NostrClient(DEFAULT_NOSTR_SEARCH_RELAYS);
const results: {
repos: Array<{ id: string; name: string; description: string; owner: string; npub: string }>;
@ -41,27 +41,53 @@ export const GET: RequestHandler = async ({ url }) => { @@ -41,27 +41,53 @@ export const GET: RequestHandler = async ({ url }) => {
code: []
};
// Search repositories
// Search repositories using NIP-50
if (type === 'repos' || type === 'all') {
const events = await nostrClient.fetchEvents([
let events: Array<{ id: string; pubkey: string; tags: string[][]; content: string; created_at: number }> = [];
try {
// Try NIP-50 search first (relays that support it will return results sorted by relevance)
events = await nostrClient.fetchEvents([
{
kinds: [KIND.REPO_ANNOUNCEMENT],
search: query, // NIP-50: Search field
limit: limit * 2 // Get more results to account for different relay implementations
}
]);
logger.info({ query, eventCount: events.length }, 'NIP-50 search results');
} catch (nip50Error) {
// Fallback to manual filtering if NIP-50 fails or isn't supported
logger.warn({ error: nip50Error, query }, 'NIP-50 search failed, falling back to manual filtering');
const allEvents = await nostrClient.fetchEvents([
{
kinds: [KIND.REPO_ANNOUNCEMENT],
limit: 100
limit: 500 // Get more events for manual filtering
}
]);
const searchLower = query.toLowerCase();
events = allEvents.filter(event => {
const name = event.tags.find(t => t[0] === 'name')?.[1] || '';
const description = event.tags.find(t => t[0] === 'description')?.[1] || '';
const repoId = event.tags.find(t => t[0] === 'd')?.[1] || '';
const content = event.content || '';
return name.toLowerCase().includes(searchLower) ||
description.toLowerCase().includes(searchLower) ||
repoId.toLowerCase().includes(searchLower) ||
content.toLowerCase().includes(searchLower);
});
}
// Process events into results
const searchLower = query.toLowerCase();
for (const event of events) {
const name = event.tags.find(t => t[0] === 'name')?.[1] || '';
const description = event.tags.find(t => t[0] === 'description')?.[1] || '';
const repoId = event.tags.find(t => t[0] === 'd')?.[1] || '';
const nameMatch = name.toLowerCase().includes(searchLower);
const descMatch = description.toLowerCase().includes(searchLower);
const repoMatch = repoId.toLowerCase().includes(searchLower);
if (nameMatch || descMatch || repoMatch) {
try {
const npub = nip19.npubEncode(event.pubkey);
results.repos.push({
@ -75,14 +101,20 @@ export const GET: RequestHandler = async ({ url }) => { @@ -75,14 +101,20 @@ export const GET: RequestHandler = async ({ url }) => {
// Skip if npub encoding fails
}
}
}
// Sort by relevance (name matches first)
// Sort by relevance (name matches first, then description)
// Note: NIP-50 compliant relays should already return results sorted by relevance
results.repos.sort((a, b) => {
const aNameMatch = a.name.toLowerCase().includes(searchLower);
const bNameMatch = b.name.toLowerCase().includes(searchLower);
if (aNameMatch && !bNameMatch) return -1;
if (!aNameMatch && bNameMatch) return 1;
const aDescMatch = a.description.toLowerCase().includes(searchLower);
const bDescMatch = b.description.toLowerCase().includes(searchLower);
if (aDescMatch && !bDescMatch) return -1;
if (!aDescMatch && bDescMatch) return 1;
return 0;
});

5
src/routes/docs/+page.svelte

@ -21,7 +21,10 @@ @@ -21,7 +21,10 @@
return '<pre class="hljs"><code>' +
hljs.highlight(str, { language: lang }).value +
'</code></pre>';
} catch (__) {}
} catch (err) {
// Fallback to escaped HTML if highlighting fails
// This is expected for unsupported languages
}
}
return '<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>';
}

5
src/routes/docs/nip34/+page.svelte

@ -21,7 +21,10 @@ @@ -21,7 +21,10 @@
return '<pre class="hljs"><code>' +
hljs.highlight(str, { language: lang }).value +
'</code></pre>';
} catch (__) {}
} catch (err) {
// Fallback to escaped HTML if highlighting fails
// This is expected for unsupported languages
}
}
return '<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>';
}

5
src/routes/docs/nip34/spec/+page.svelte

@ -21,7 +21,10 @@ @@ -21,7 +21,10 @@
return '<pre class="hljs"><code>' +
hljs.highlight(str, { language: lang }).value +
'</code></pre>';
} catch (__) {}
} catch (err) {
// Fallback to escaped HTML if highlighting fails
// This is expected for unsupported languages
}
}
return '<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>';
}

9
src/routes/repos/[npub]/[repo]/+page.svelte

@ -135,7 +135,10 @@ @@ -135,7 +135,10 @@
return '<pre class="hljs"><code>' +
hljs.highlight(str, { language: lang }).value +
'</code></pre>';
} catch (__) {}
} catch (err) {
// Fallback to escaped HTML if highlighting fails
// This is expected for unsupported languages
}
}
return '<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>';
}
@ -881,7 +884,7 @@ @@ -881,7 +884,7 @@
const response = await fetch(`/api/repos/${npub}/${repo}/issues`);
if (response.ok) {
const data = await response.json();
issues = data.map((issue: any) => ({
issues = data.map((issue: { id: string; tags: string[][]; content: string; status?: string; pubkey: string; created_at: number }) => ({
id: issue.id,
subject: issue.tags.find((t: string[]) => t[0] === 'subject')?.[1] || 'Untitled',
content: issue.content,
@ -955,7 +958,7 @@ @@ -955,7 +958,7 @@
const response = await fetch(`/api/repos/${npub}/${repo}/prs`);
if (response.ok) {
const data = await response.json();
prs = data.map((pr: any) => ({
prs = data.map((pr: { id: string; tags: string[][]; content: string; status?: string; pubkey: string; created_at: number; commitId?: string }) => ({
id: pr.id,
subject: pr.tags.find((t: string[]) => t[0] === 'subject')?.[1] || 'Untitled',
content: pr.content,

16
src/routes/search/+page.svelte

@ -52,7 +52,7 @@ @@ -52,7 +52,7 @@
<input
type="text"
bind:value={query}
placeholder="Search repositories or code..."
placeholder="Search repositories or code... (NIP-50 search)"
class="search-input"
/>
<div class="search-controls">
@ -65,6 +65,9 @@ @@ -65,6 +65,9 @@
{loading ? 'Searching...' : 'Search'}
</button>
</div>
<div class="search-info">
<small>Using NIP-50 search across multiple relays for better results</small>
</div>
</form>
{#if error}
@ -146,3 +149,14 @@ @@ -146,3 +149,14 @@
</main>
</div>
<style>
.search-info {
margin-top: 0.5rem;
color: var(--text-secondary, #666);
font-size: 0.875rem;
}
.search-info small {
color: inherit;
}
</style>

82
src/routes/signup/+page.svelte

@ -45,7 +45,8 @@ @@ -45,7 +45,8 @@
// Lookup state
let lookupLoading = $state<{ [key: string]: boolean }>({});
let lookupError = $state<{ [key: string]: string | null }>({});
let lookupResults = $state<{ [key: string]: any }>({});
type ProfileData = { pubkey: string; npub: string; name?: string; about?: string; picture?: string };
let lookupResults = $state<{ [key: string]: Array<ProfileData | NostrEvent> | null }>({});
import { DEFAULT_NOSTR_RELAYS, DEFAULT_NOSTR_SEARCH_RELAYS, combineRelays } from '../../lib/config.js';
@ -434,7 +435,13 @@ @@ -434,7 +435,13 @@
}
]);
let profileData: any = {
let profileData: {
pubkey: string;
npub: string;
name?: string;
about?: string;
picture?: string;
} = {
pubkey,
npub: query.startsWith('npub') ? query : nip19.npubEncode(pubkey)
};
@ -1212,22 +1219,24 @@ @@ -1212,22 +1219,24 @@
<img src="/icons/x.svg" alt="Clear" class="icon-small" />
</button>
</div>
{#each lookupResults['repo-existingRepoRef'] as result}
{@const nameTag = result.tags.find((t: string[]) => t[0] === 'name')?.[1]}
{@const dTag = result.tags.find((t: string[]) => t[0] === 'd')?.[1]}
{@const descTag = result.tags.find((t: string[]) => t[0] === 'description')?.[1]}
{@const imageTag = result.tags.find((t: string[]) => t[0] === 'image')?.[1]}
{@const ownerNpub = nip19.npubEncode(result.pubkey)}
{@const tags = result.tags.filter((t: string[]) => t[0] === 't' && t[1] && t[1] !== 'private' && t[1] !== 'fork').map((t: string[]) => t[1])}
{#each (lookupResults['repo-existingRepoRef'] || []) as result}
{#if 'tags' in result}
{@const event = result as NostrEvent}
{@const nameTag = event.tags.find((t: string[]) => t[0] === 'name')?.[1]}
{@const dTag = event.tags.find((t: string[]) => t[0] === 'd')?.[1]}
{@const descTag = event.tags.find((t: string[]) => t[0] === 'description')?.[1]}
{@const imageTag = event.tags.find((t: string[]) => t[0] === 'image')?.[1]}
{@const ownerNpub = nip19.npubEncode(event.pubkey)}
{@const tags = event.tags.filter((t: string[]) => t[0] === 't' && t[1] && t[1] !== 'private' && t[1] !== 'fork').map((t: string[]) => t[1])}
<div
class="lookup-result-item repo-result"
role="button"
tabindex="0"
onclick={() => selectRepoResult(result, 'existingRepoRef')}
onclick={() => selectRepoResult(event, 'existingRepoRef')}
onkeydown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
selectRepoResult(result, 'existingRepoRef');
selectRepoResult(event, 'existingRepoRef');
}
}}
>
@ -1245,7 +1254,7 @@ @@ -1245,7 +1254,7 @@
{/if}
<div class="result-meta">
<small>Owner: {ownerNpub.slice(0, 16)}...</small>
<small>Event: {result.id.slice(0, 16)}...</small>
<small>Event: {event.id.slice(0, 16)}...</small>
</div>
{#if tags.length > 0}
<div class="result-tags">
@ -1257,6 +1266,7 @@ @@ -1257,6 +1266,7 @@
</div>
</div>
</div>
{/if}
{/each}
</div>
{/if}
@ -1435,7 +1445,7 @@ @@ -1435,7 +1445,7 @@
{#if lookupResults[`npub-maintainers-${index}`]}
<div class="lookup-results">
<div class="lookup-results-header">
<span>Found {lookupResults[`npub-maintainers-${index}`].length} profile(s):</span>
<span>Found {(lookupResults[`npub-maintainers-${index}`] || []).length} profile(s):</span>
<button
type="button"
onclick={() => clearLookupResults(`npub-maintainers-${index}`)}
@ -1445,36 +1455,39 @@ @@ -1445,36 +1455,39 @@
<img src="/icons/x.svg" alt="Clear" class="icon-small" />
</button>
</div>
{#each lookupResults[`npub-maintainers-${index}`] as result}
{#each (lookupResults[`npub-maintainers-${index}`] || []) as result}
{#if 'npub' in result}
{@const profile = result as ProfileData}
<div
class="lookup-result-item profile-result"
role="button"
tabindex="0"
onclick={() => selectNpubResult(result, 'maintainers', index)}
onclick={() => selectNpubResult(profile, 'maintainers', index)}
onkeydown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
selectNpubResult(result, 'maintainers', index);
selectNpubResult(profile, 'maintainers', index);
}
}}
>
<div class="result-header">
{#if result.picture}
<img src={result.picture} alt="" class="result-avatar" />
{#if profile.picture}
<img src={profile.picture} alt="" class="result-avatar" />
{:else}
<div class="result-avatar-placeholder">
{(result.name || result.npub).slice(0, 2).toUpperCase()}
{(profile.name || profile.npub).slice(0, 2).toUpperCase()}
</div>
{/if}
<div class="result-info">
<strong>{result.name || 'Unknown'}</strong>
{#if result.about}
<p class="result-description">{result.about}</p>
<strong>{profile.name || 'Unknown'}</strong>
{#if profile.about}
<p class="result-description">{profile.about}</p>
{/if}
<small class="npub-display">{result.npub}</small>
<small class="npub-display">{profile.npub}</small>
</div>
</div>
</div>
{/if}
{/each}
</div>
{/if}
@ -1750,22 +1763,24 @@ @@ -1750,22 +1763,24 @@
<img src="/icons/x.svg" alt="Clear" class="icon-small" />
</button>
</div>
{#each lookupResults['repo-forkOriginalRepo'] as result}
{@const nameTag = result.tags.find((t: string[]) => t[0] === 'name')?.[1]}
{@const dTag = result.tags.find((t: string[]) => t[0] === 'd')?.[1]}
{@const descTag = result.tags.find((t: string[]) => t[0] === 'description')?.[1]}
{@const imageTag = result.tags.find((t: string[]) => t[0] === 'image')?.[1]}
{@const ownerNpub = nip19.npubEncode(result.pubkey)}
{@const tags = result.tags.filter((t: string[]) => t[0] === 't' && t[1] && t[1] !== 'private' && t[1] !== 'fork').map((t: string[]) => t[1])}
{#each (lookupResults['repo-forkOriginalRepo'] || []) as result}
{#if 'tags' in result}
{@const event = result as NostrEvent}
{@const nameTag = event.tags.find((t: string[]) => t[0] === 'name')?.[1]}
{@const dTag = event.tags.find((t: string[]) => t[0] === 'd')?.[1]}
{@const descTag = event.tags.find((t: string[]) => t[0] === 'description')?.[1]}
{@const imageTag = event.tags.find((t: string[]) => t[0] === 'image')?.[1]}
{@const ownerNpub = nip19.npubEncode(event.pubkey)}
{@const tags = event.tags.filter((t: string[]) => t[0] === 't' && t[1] && t[1] !== 'private' && t[1] !== 'fork').map((t: string[]) => t[1])}
<div
class="lookup-result-item repo-result"
role="button"
tabindex="0"
onclick={() => selectRepoResult(result, 'forkOriginalRepo')}
onclick={() => selectRepoResult(event, 'forkOriginalRepo')}
onkeydown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
selectRepoResult(result, 'forkOriginalRepo');
selectRepoResult(event, 'forkOriginalRepo');
}
}}
>
@ -1783,7 +1798,7 @@ @@ -1783,7 +1798,7 @@
{/if}
<div class="result-meta">
<small>Owner: {ownerNpub.slice(0, 16)}...</small>
<small>Event: {result.id.slice(0, 16)}...</small>
<small>Event: {event.id.slice(0, 16)}...</small>
</div>
{#if tags.length > 0}
<div class="result-tags">
@ -1795,6 +1810,7 @@ @@ -1795,6 +1810,7 @@
</div>
</div>
</div>
{/if}
{/each}
</div>
{/if}

Loading…
Cancel
Save