diff --git a/src/routes/api/repos/[npub]/[repo]/fork/+server.ts b/src/routes/api/repos/[npub]/[repo]/fork/+server.ts index a9ef6e5..9376ebe 100644 --- a/src/routes/api/repos/[npub]/[repo]/fork/+server.ts +++ b/src/routes/api/repos/[npub]/[repo]/fork/+server.ts @@ -179,14 +179,25 @@ export const POST: RequestHandler = async ({ params, request }) => { // Create fork announcement const gitDomain = process.env.GIT_DOMAIN || 'localhost:6543'; - const protocol = gitDomain.startsWith('localhost') ? 'http' : 'https'; + const isLocalhost = gitDomain.startsWith('localhost') || gitDomain.startsWith('127.0.0.1'); + const protocol = isLocalhost ? 'http' : 'https'; const forkGitUrl = `${protocol}://${gitDomain}/${userNpub}/${forkRepoName}.git`; + // Get Tor .onion URL if available + const { getTorGitUrl } = await import('$lib/services/tor/hidden-service.js'); + const torOnionUrl = await getTorGitUrl(userNpub, forkRepoName); + // Extract original clone URLs and earliest unique commit const originalCloneUrls = originalAnnouncement.tags .filter(t => t[0] === 'clone') .flatMap(t => t.slice(1)) - .filter(url => url && typeof url === 'string') as string[]; + .filter(url => url && typeof url === 'string') + .filter(url => { + // Exclude our domain and .onion URLs (we'll add our own if available) + if (url.includes(gitDomain)) return false; + if (url.includes('.onion')) return false; + return true; + }) as string[]; const earliestCommitTag = originalAnnouncement.tags.find(t => t[0] === 'r' && t[2] === 'euc'); const earliestCommit = earliestCommitTag?.[1]; @@ -195,12 +206,33 @@ export const POST: RequestHandler = async ({ params, request }) => { const originalName = originalAnnouncement.tags.find(t => t[0] === 'name')?.[1] || repo; const originalDescription = originalAnnouncement.tags.find(t => t[0] === 'description')?.[1] || ''; + // Build clone URLs for fork - NEVER include localhost, only include public domain or Tor .onion + const forkCloneUrls: string[] = []; + + // Add our domain URL only if it's NOT localhost (explicitly check the URL) + if (!isLocalhost && !forkGitUrl.includes('localhost') && !forkGitUrl.includes('127.0.0.1')) { + forkCloneUrls.push(forkGitUrl); + } + + // Add Tor .onion URL if available + if (torOnionUrl) { + forkCloneUrls.push(torOnionUrl); + } + + // Add original clone URLs + forkCloneUrls.push(...originalCloneUrls); + + // Validate: If using localhost, require either Tor .onion URL or at least one other clone URL + if (isLocalhost && !torOnionUrl && originalCloneUrls.length === 0) { + return error(400, 'Cannot create fork with only localhost. The original repository must have at least one public clone URL, or you need to configure a Tor .onion address.'); + } + // Build fork announcement tags const tags: string[][] = [ ['d', forkRepoName], ['name', `${originalName} (fork)`], ['description', `Fork of ${originalName}${originalDescription ? `: ${originalDescription}` : ''}`], - ['clone', forkGitUrl, ...originalCloneUrls.filter(url => !url.includes(gitDomain))], + ['clone', ...forkCloneUrls], ['relays', ...DEFAULT_NOSTR_RELAYS], ['t', 'fork'], // Mark as fork ['a', `${KIND.REPO_ANNOUNCEMENT}:${originalOwnerPubkey}:${repo}`], // Reference to original repo diff --git a/src/routes/api/repos/[npub]/[repo]/settings/+server.ts b/src/routes/api/repos/[npub]/[repo]/settings/+server.ts index 0dcac2a..15c6f07 100644 --- a/src/routes/api/repos/[npub]/[repo]/settings/+server.ts +++ b/src/routes/api/repos/[npub]/[repo]/settings/+server.ts @@ -171,19 +171,43 @@ export const POST: RequestHandler = async ({ params, request }) => { // Build updated tags const gitDomain = process.env.GIT_DOMAIN || 'localhost:6543'; - const protocol = gitDomain.startsWith('localhost') ? 'http' : 'https'; + const isLocalhost = gitDomain.startsWith('localhost') || gitDomain.startsWith('127.0.0.1'); + const protocol = isLocalhost ? 'http' : 'https'; const gitUrl = `${protocol}://${gitDomain}/${npub}/${repo}.git`; // Get Tor .onion URL if available const { getTorGitUrl } = await import('$lib/services/tor/hidden-service.js'); const torOnionUrl = await getTorGitUrl(npub, repo); - // Build clone URLs - include regular domain and Tor .onion if available - const cloneUrlList = [ - gitUrl, - ...(torOnionUrl ? [torOnionUrl] : []), - ...(cloneUrls || []).filter((url: string) => url && !url.includes(gitDomain) && !url.includes('.onion')) - ]; + // Filter user-provided clone URLs (exclude localhost and .onion duplicates) + const userCloneUrls = (cloneUrls || []).filter((url: string) => { + if (!url || !url.trim()) return false; + // Exclude if it's our domain or already a .onion + if (url.includes(gitDomain)) return false; + if (url.includes('.onion')) return false; + return true; + }); + + // Build clone URLs - NEVER include localhost, only include public domain or Tor .onion + const cloneUrlList: string[] = []; + + // Add our domain URL only if it's NOT localhost (explicitly check the URL) + if (!isLocalhost && !gitUrl.includes('localhost') && !gitUrl.includes('127.0.0.1')) { + cloneUrlList.push(gitUrl); + } + + // Add Tor .onion URL if available (always useful, even with localhost) + if (torOnionUrl) { + cloneUrlList.push(torOnionUrl); + } + + // Add user-provided clone URLs + cloneUrlList.push(...userCloneUrls); + + // Validate: If using localhost, require either Tor .onion URL or at least one other clone URL + if (isLocalhost && !torOnionUrl && userCloneUrls.length === 0) { + return error(400, 'Cannot update with only localhost. You need either a Tor .onion address or at least one other clone URL.'); + } const tags: string[][] = [ ['d', repo], diff --git a/src/routes/signup/+page.svelte b/src/routes/signup/+page.svelte index 372d53f..31c967a 100644 --- a/src/routes/signup/+page.svelte +++ b/src/routes/signup/+page.svelte @@ -879,7 +879,8 @@ // Get git domain from layout data const gitDomain = $page.data.gitDomain || 'localhost:6543'; - const protocol = gitDomain.startsWith('localhost') ? 'http' : 'https'; + const isLocalhost = gitDomain.startsWith('localhost') || gitDomain.startsWith('127.0.0.1'); + const protocol = isLocalhost ? 'http' : 'https'; const gitUrl = `${protocol}://${gitDomain}/${npub}/${dTag}.git`; // Try to get Tor .onion address and add it to clone URLs @@ -896,12 +897,40 @@ // Tor not available, continue without it } - // Build clone URLs - always include our domain, and Tor .onion if available - const allCloneUrls = [ - gitUrl, - ...(torOnionUrl ? [torOnionUrl] : []), // Add Tor .onion URL if available - ...cloneUrls.filter(url => url.trim() && !url.includes(gitDomain) && !url.includes('.onion')) - ]; + // Filter user-provided clone URLs (exclude localhost and .onion duplicates) + const userCloneUrls = cloneUrls.filter(url => { + const trimmed = url.trim(); + if (!trimmed) return false; + // Exclude if it's our domain or already a .onion + if (trimmed.includes(gitDomain)) return false; + if (trimmed.includes('.onion')) return false; + return true; + }); + + // Validate: If using localhost, require either Tor .onion URL or at least one other clone URL + if (isLocalhost && !torOnionUrl && userCloneUrls.length === 0) { + error = 'Cannot publish with only localhost. You need either:\n' + + '• A Tor .onion address (configure Tor hidden service and set TOR_ONION_ADDRESS)\n' + + '• At least one other clone URL (e.g., GitHub, GitLab, or another GitRepublic instance)'; + loading = false; + return; + } + + // Build clone URLs - NEVER include localhost, only include public domain or Tor .onion + const allCloneUrls: string[] = []; + + // Add our domain URL only if it's NOT localhost (explicitly check the URL) + if (!isLocalhost && !gitUrl.includes('localhost') && !gitUrl.includes('127.0.0.1')) { + allCloneUrls.push(gitUrl); + } + + // Add Tor .onion URL if available (always useful, even with localhost) + if (torOnionUrl) { + allCloneUrls.push(torOnionUrl); + } + + // Add user-provided clone URLs + allCloneUrls.push(...userCloneUrls); // Build web URLs const allWebUrls = webUrls.filter(url => url.trim()); @@ -1264,7 +1293,13 @@