11 changed files with 469 additions and 43 deletions
@ -0,0 +1,83 @@
@@ -0,0 +1,83 @@
|
||||
/** |
||||
* Tor Hidden Service management |
||||
* Detects and provides .onion addresses for the server |
||||
*/ |
||||
|
||||
import { readFile, access } from 'fs/promises'; |
||||
import { constants } from 'fs'; |
||||
import { join } from 'path'; |
||||
import logger from '../logger.js'; |
||||
import { TOR_ENABLED } from '../../config.js'; |
||||
|
||||
/** |
||||
* Common locations for Tor hidden service hostname files |
||||
*/ |
||||
const TOR_HOSTNAME_PATHS = [ |
||||
'/var/lib/tor/hidden_service/hostname', |
||||
'/var/lib/tor/gitrepublic/hostname', |
||||
'/usr/local/var/lib/tor/hidden_service/hostname', |
||||
'/home/.tor/hidden_service/hostname', |
||||
process.env.TOR_HOSTNAME_FILE || '' |
||||
].filter(Boolean); |
||||
|
||||
/** |
||||
* Get the Tor hidden service .onion address |
||||
* Returns null if Tor is not enabled or .onion address cannot be found |
||||
*/ |
||||
export async function getTorOnionAddress(): Promise<string | null> { |
||||
if (!TOR_ENABLED) { |
||||
return null; |
||||
} |
||||
|
||||
// First, check if explicitly set via environment variable
|
||||
if (typeof process !== 'undefined' && process.env?.TOR_ONION_ADDRESS) { |
||||
const onion = process.env.TOR_ONION_ADDRESS.trim(); |
||||
if (onion.endsWith('.onion')) { |
||||
logger.info({ onion }, 'Using Tor .onion address from environment variable'); |
||||
return onion; |
||||
} |
||||
} |
||||
|
||||
// Try to read from Tor hidden service hostname file
|
||||
for (const hostnamePath of TOR_HOSTNAME_PATHS) { |
||||
if (!hostnamePath) continue; |
||||
|
||||
try { |
||||
await access(hostnamePath, constants.R_OK); |
||||
const hostname = await readFile(hostnamePath, 'utf-8'); |
||||
const onion = hostname.trim().split('\n')[0].trim(); |
||||
|
||||
if (onion.endsWith('.onion')) { |
||||
logger.info({ onion, path: hostnamePath }, 'Found Tor .onion address from hostname file'); |
||||
return onion; |
||||
} |
||||
} catch { |
||||
// File doesn't exist or can't be read, try next path
|
||||
continue; |
||||
} |
||||
} |
||||
|
||||
logger.warn('Tor is enabled but .onion address not found. Set TOR_ONION_ADDRESS env var or configure Tor hidden service.'); |
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* Get the full git URL with Tor .onion address for a repository |
||||
*/ |
||||
export async function getTorGitUrl(npub: string, repoName: string): Promise<string | null> { |
||||
const onion = await getTorOnionAddress(); |
||||
if (!onion) { |
||||
return null; |
||||
} |
||||
|
||||
// Use HTTP for .onion addresses (HTTPS doesn't work with .onion)
|
||||
return `http://${onion}/${npub}/${repoName}.git`; |
||||
} |
||||
|
||||
/** |
||||
* Check if Tor hidden service is available |
||||
*/ |
||||
export async function isTorHiddenServiceAvailable(): Promise<boolean> { |
||||
const onion = await getTorOnionAddress(); |
||||
return onion !== null; |
||||
} |
||||
@ -0,0 +1,38 @@
@@ -0,0 +1,38 @@
|
||||
/** |
||||
* Tor utility functions for detecting and handling .onion addresses |
||||
*/ |
||||
|
||||
import { isOnionAddress, TOR_ENABLED, parseTorProxy } from '../config.js'; |
||||
|
||||
/** |
||||
* Check if a URL should use Tor proxy |
||||
*/ |
||||
export function shouldUseTor(url: string): boolean { |
||||
return TOR_ENABLED && isOnionAddress(url); |
||||
} |
||||
|
||||
/** |
||||
* Get Tor SOCKS proxy configuration |
||||
*/ |
||||
export function getTorProxy(): { host: string; port: number } | null { |
||||
return parseTorProxy(); |
||||
} |
||||
|
||||
/** |
||||
* Format git URL with Tor proxy configuration |
||||
* Returns the original URL if Tor is not needed or not available |
||||
*/ |
||||
export function formatGitUrlWithTor(url: string): string { |
||||
if (!shouldUseTor(url)) { |
||||
return url; |
||||
} |
||||
|
||||
const proxy = getTorProxy(); |
||||
if (!proxy) { |
||||
return url; |
||||
} |
||||
|
||||
// Git can use Tor via GIT_PROXY_COMMAND or http.proxy
|
||||
// For now, return the original URL - git will be configured separately
|
||||
return url; |
||||
} |
||||
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
/** |
||||
* API endpoint to get the Tor .onion address for the server |
||||
*/ |
||||
|
||||
import { json } from '@sveltejs/kit'; |
||||
import type { RequestHandler } from './$types'; |
||||
import { getTorOnionAddress } from '$lib/services/tor/hidden-service.js'; |
||||
|
||||
export const GET: RequestHandler = async () => { |
||||
const onion = await getTorOnionAddress(); |
||||
return json({ onion, available: onion !== null }); |
||||
}; |
||||
Loading…
Reference in new issue