mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
feat: add wireguard relay integration for public server access with End-to-End Encryption
Integrates tunwg (WireGuard tunnel) to enable optional public access to the hapi server. Tunnel is disabled by default and enabled via --relay flag or HAPI_TUNNEL=true environment variable. Users can now run 'hapi server --relay' and get a direct link like: https://app.hapi.run/?server=https://xxx.relay.hapi.run&token=xxx
This commit is contained in:
@@ -68,6 +68,7 @@ ${chalk.bold('Usage:')}
|
||||
hapi connect (not available in direct-connect mode)
|
||||
hapi notify (not available in direct-connect mode)
|
||||
hapi server Start the API + web server
|
||||
hapi server --relay Start with public relay
|
||||
hapi daemon Manage background service that allows
|
||||
to spawn new sessions away from your computer
|
||||
hapi doctor System diagnostics & troubleshooting
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import chalk from 'chalk'
|
||||
import type { CommandDefinition, CommandContext } from './types'
|
||||
|
||||
function parseServerArgs(args: string[]): { host?: string; port?: string } {
|
||||
const result: { host?: string; port?: string } = {}
|
||||
function parseServerArgs(args: string[]): { host?: string; port?: string; relay?: boolean } {
|
||||
const result: { host?: string; port?: string; relay?: boolean } = {}
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i]
|
||||
@@ -14,6 +14,10 @@ function parseServerArgs(args: string[]): { host?: string; port?: string } {
|
||||
result.host = arg.slice('--host='.length)
|
||||
} else if (arg.startsWith('--port=')) {
|
||||
result.port = arg.slice('--port='.length)
|
||||
} else if (arg === '--relay') {
|
||||
result.relay = true
|
||||
} else if (arg === '--no-relay') {
|
||||
result.relay = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +29,7 @@ export const serverCommand: CommandDefinition = {
|
||||
requiresRuntimeAssets: false,
|
||||
run: async (context: CommandContext) => {
|
||||
try {
|
||||
const { host, port } = parseServerArgs(context.commandArgs)
|
||||
const { host, port, relay } = parseServerArgs(context.commandArgs)
|
||||
|
||||
if (host) {
|
||||
process.env.WEBAPP_HOST = host
|
||||
@@ -33,6 +37,9 @@ export const serverCommand: CommandDefinition = {
|
||||
if (port) {
|
||||
process.env.WEBAPP_PORT = port
|
||||
}
|
||||
if (relay !== undefined) {
|
||||
process.env.HAPI_RELAY = relay ? 'true' : 'false'
|
||||
}
|
||||
|
||||
await import('../../../server/src/index')
|
||||
} catch (error) {
|
||||
|
||||
Executable → Regular
@@ -67,6 +67,24 @@ function areToolsUnpacked(unpackedPath: string): boolean {
|
||||
return expectedFiles.every((file) => existsSync(file));
|
||||
}
|
||||
|
||||
function isTunwgReady(runtimeRoot: string): boolean {
|
||||
const isWin = platform() === 'win32';
|
||||
const tunwgBinary = isWin ? 'tunwg.exe' : 'tunwg';
|
||||
const tunwgPath = join(runtimeRoot, 'tools', 'tunwg', tunwgBinary);
|
||||
return existsSync(tunwgPath);
|
||||
}
|
||||
|
||||
function ensureTunwgExecutable(runtimeRoot: string): void {
|
||||
if (platform() === 'win32') {
|
||||
return;
|
||||
}
|
||||
|
||||
const tunwgPath = join(runtimeRoot, 'tools', 'tunwg', 'tunwg');
|
||||
if (existsSync(tunwgPath)) {
|
||||
chmodSync(tunwgPath, 0o755);
|
||||
}
|
||||
}
|
||||
|
||||
function unpackTools(runtimeRoot: string): void {
|
||||
const platformDir = getPlatformDir();
|
||||
const toolsDir = join(runtimeRoot, 'tools');
|
||||
@@ -114,7 +132,7 @@ function unpackTools(runtimeRoot: string): void {
|
||||
}
|
||||
|
||||
function runtimeAssetsReady(runtimeRoot: string): boolean {
|
||||
return areToolsUnpacked(join(runtimeRoot, 'tools', 'unpacked'));
|
||||
return areToolsUnpacked(join(runtimeRoot, 'tools', 'unpacked')) && isTunwgReady(runtimeRoot);
|
||||
}
|
||||
|
||||
export async function ensureRuntimeAssets(): Promise<void> {
|
||||
@@ -142,5 +160,20 @@ export async function ensureRuntimeAssets(): Promise<void> {
|
||||
}
|
||||
|
||||
unpackTools(runtimeRoot);
|
||||
ensureTunwgExecutable(runtimeRoot);
|
||||
writeFileSync(markerPath, packageJson.version, 'utf-8');
|
||||
}
|
||||
|
||||
export function getTunwgPath(): string {
|
||||
const isWin = platform() === 'win32';
|
||||
const tunwgBinary = isWin ? 'tunwg.exe' : 'tunwg';
|
||||
|
||||
if (isBunCompiled()) {
|
||||
return join(runtimePath(), 'tools', 'tunwg', tunwgBinary);
|
||||
}
|
||||
|
||||
// Development mode: use downloaded binary from server/tools/tunwg
|
||||
const platformDir = getPlatformDir();
|
||||
const devBinaryName = isWin ? `tunwg-${platformDir}.exe` : `tunwg-${platformDir}`;
|
||||
return join(__dirname, '..', '..', '..', 'server', 'tools', 'tunwg', devBinaryName);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import difftasticArchiveLicense from '../../tools/archives/difftastic-LICENSE' a
|
||||
import ripgrepArchiveLicense from '../../tools/archives/ripgrep-LICENSE' assert { type: 'file' };
|
||||
import difftasticLicense from '../../tools/licenses/difftastic-LICENSE' assert { type: 'file' };
|
||||
import ripgrepLicense from '../../tools/licenses/ripgrep-LICENSE' assert { type: 'file' };
|
||||
import tunwgLicense from '../../../server/tools/tunwg/LICENSE' assert { type: 'file' };
|
||||
|
||||
export interface EmbeddedAsset {
|
||||
relativePath: string;
|
||||
@@ -21,67 +22,98 @@ const COMMON_ASSETS: EmbeddedAsset[] = [
|
||||
asset('tools/archives/difftastic-LICENSE', difftasticArchiveLicense),
|
||||
asset('tools/archives/ripgrep-LICENSE', ripgrepArchiveLicense),
|
||||
asset('tools/licenses/difftastic-LICENSE', difftasticLicense),
|
||||
asset('tools/licenses/ripgrep-LICENSE', ripgrepLicense)
|
||||
asset('tools/licenses/ripgrep-LICENSE', ripgrepLicense),
|
||||
asset('tools/tunwg/LICENSE', tunwgLicense)
|
||||
];
|
||||
|
||||
async function selectEmbeddedAssets(): Promise<EmbeddedAsset[]> {
|
||||
if (feature('HAPI_TARGET_DARWIN_ARM64')) {
|
||||
const [{ default: difftasticArm64Darwin }, { default: ripgrepArm64Darwin }] = await Promise.all([
|
||||
const [
|
||||
{ default: difftasticArm64Darwin },
|
||||
{ default: ripgrepArm64Darwin },
|
||||
{ default: tunwgArm64Darwin }
|
||||
] = await Promise.all([
|
||||
import('../../tools/archives/difftastic-arm64-darwin.tar.gz', { assert: { type: 'file' } }),
|
||||
import('../../tools/archives/ripgrep-arm64-darwin.tar.gz', { assert: { type: 'file' } })
|
||||
import('../../tools/archives/ripgrep-arm64-darwin.tar.gz', { assert: { type: 'file' } }),
|
||||
import('../../../server/tools/tunwg/tunwg-arm64-darwin', { assert: { type: 'file' } })
|
||||
]);
|
||||
return [
|
||||
...COMMON_ASSETS,
|
||||
asset('tools/archives/difftastic-arm64-darwin.tar.gz', difftasticArm64Darwin),
|
||||
asset('tools/archives/ripgrep-arm64-darwin.tar.gz', ripgrepArm64Darwin)
|
||||
asset('tools/archives/ripgrep-arm64-darwin.tar.gz', ripgrepArm64Darwin),
|
||||
asset('tools/tunwg/tunwg', tunwgArm64Darwin)
|
||||
];
|
||||
}
|
||||
|
||||
if (feature('HAPI_TARGET_DARWIN_X64')) {
|
||||
const [{ default: difftasticX64Darwin }, { default: ripgrepX64Darwin }] = await Promise.all([
|
||||
const [
|
||||
{ default: difftasticX64Darwin },
|
||||
{ default: ripgrepX64Darwin },
|
||||
{ default: tunwgX64Darwin }
|
||||
] = await Promise.all([
|
||||
import('../../tools/archives/difftastic-x64-darwin.tar.gz', { assert: { type: 'file' } }),
|
||||
import('../../tools/archives/ripgrep-x64-darwin.tar.gz', { assert: { type: 'file' } })
|
||||
import('../../tools/archives/ripgrep-x64-darwin.tar.gz', { assert: { type: 'file' } }),
|
||||
import('../../../server/tools/tunwg/tunwg-x64-darwin', { assert: { type: 'file' } })
|
||||
]);
|
||||
return [
|
||||
...COMMON_ASSETS,
|
||||
asset('tools/archives/difftastic-x64-darwin.tar.gz', difftasticX64Darwin),
|
||||
asset('tools/archives/ripgrep-x64-darwin.tar.gz', ripgrepX64Darwin)
|
||||
asset('tools/archives/ripgrep-x64-darwin.tar.gz', ripgrepX64Darwin),
|
||||
asset('tools/tunwg/tunwg', tunwgX64Darwin)
|
||||
];
|
||||
}
|
||||
|
||||
if (feature('HAPI_TARGET_LINUX_ARM64')) {
|
||||
const [{ default: difftasticArm64Linux }, { default: ripgrepArm64Linux }] = await Promise.all([
|
||||
const [
|
||||
{ default: difftasticArm64Linux },
|
||||
{ default: ripgrepArm64Linux },
|
||||
{ default: tunwgArm64Linux }
|
||||
] = await Promise.all([
|
||||
import('../../tools/archives/difftastic-arm64-linux.tar.gz', { assert: { type: 'file' } }),
|
||||
import('../../tools/archives/ripgrep-arm64-linux.tar.gz', { assert: { type: 'file' } })
|
||||
import('../../tools/archives/ripgrep-arm64-linux.tar.gz', { assert: { type: 'file' } }),
|
||||
import('../../../server/tools/tunwg/tunwg-arm64-linux', { assert: { type: 'file' } })
|
||||
]);
|
||||
return [
|
||||
...COMMON_ASSETS,
|
||||
asset('tools/archives/difftastic-arm64-linux.tar.gz', difftasticArm64Linux),
|
||||
asset('tools/archives/ripgrep-arm64-linux.tar.gz', ripgrepArm64Linux)
|
||||
asset('tools/archives/ripgrep-arm64-linux.tar.gz', ripgrepArm64Linux),
|
||||
asset('tools/tunwg/tunwg', tunwgArm64Linux)
|
||||
];
|
||||
}
|
||||
|
||||
if (feature('HAPI_TARGET_LINUX_X64')) {
|
||||
const [{ default: difftasticX64Linux }, { default: ripgrepX64Linux }] = await Promise.all([
|
||||
const [
|
||||
{ default: difftasticX64Linux },
|
||||
{ default: ripgrepX64Linux },
|
||||
{ default: tunwgX64Linux }
|
||||
] = await Promise.all([
|
||||
import('../../tools/archives/difftastic-x64-linux.tar.gz', { assert: { type: 'file' } }),
|
||||
import('../../tools/archives/ripgrep-x64-linux.tar.gz', { assert: { type: 'file' } })
|
||||
import('../../tools/archives/ripgrep-x64-linux.tar.gz', { assert: { type: 'file' } }),
|
||||
import('../../../server/tools/tunwg/tunwg-x64-linux', { assert: { type: 'file' } })
|
||||
]);
|
||||
return [
|
||||
...COMMON_ASSETS,
|
||||
asset('tools/archives/difftastic-x64-linux.tar.gz', difftasticX64Linux),
|
||||
asset('tools/archives/ripgrep-x64-linux.tar.gz', ripgrepX64Linux)
|
||||
asset('tools/archives/ripgrep-x64-linux.tar.gz', ripgrepX64Linux),
|
||||
asset('tools/tunwg/tunwg', tunwgX64Linux)
|
||||
];
|
||||
}
|
||||
|
||||
if (feature('HAPI_TARGET_WIN32_X64')) {
|
||||
const [{ default: difftasticX64Win32 }, { default: ripgrepX64Win32 }] = await Promise.all([
|
||||
const [
|
||||
{ default: difftasticX64Win32 },
|
||||
{ default: ripgrepX64Win32 },
|
||||
{ default: tunwgX64Win32 }
|
||||
] = await Promise.all([
|
||||
import('../../tools/archives/difftastic-x64-win32.tar.gz', { assert: { type: 'file' } }),
|
||||
import('../../tools/archives/ripgrep-x64-win32.tar.gz', { assert: { type: 'file' } })
|
||||
import('../../tools/archives/ripgrep-x64-win32.tar.gz', { assert: { type: 'file' } }),
|
||||
import('../../../server/tools/tunwg/tunwg-x64-win32.exe', { assert: { type: 'file' } })
|
||||
]);
|
||||
return [
|
||||
...COMMON_ASSETS,
|
||||
asset('tools/archives/difftastic-x64-win32.tar.gz', difftasticX64Win32),
|
||||
asset('tools/archives/ripgrep-x64-win32.tar.gz', ripgrepX64Win32)
|
||||
asset('tools/archives/ripgrep-x64-win32.tar.gz', ripgrepX64Win32),
|
||||
asset('tools/tunwg/tunwg.exe', tunwgX64Win32)
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
Vendored
+30
@@ -12,3 +12,33 @@ declare module '*-LICENSE' {
|
||||
const path: string;
|
||||
export default path;
|
||||
}
|
||||
|
||||
declare module '*/tunwg/LICENSE' {
|
||||
const path: string;
|
||||
export default path;
|
||||
}
|
||||
|
||||
declare module '*/tunwg/tunwg-arm64-darwin' {
|
||||
const path: string;
|
||||
export default path;
|
||||
}
|
||||
|
||||
declare module '*/tunwg/tunwg-x64-darwin' {
|
||||
const path: string;
|
||||
export default path;
|
||||
}
|
||||
|
||||
declare module '*/tunwg/tunwg-arm64-linux' {
|
||||
const path: string;
|
||||
export default path;
|
||||
}
|
||||
|
||||
declare module '*/tunwg/tunwg-x64-linux' {
|
||||
const path: string;
|
||||
export default path;
|
||||
}
|
||||
|
||||
declare module '*/tunwg/tunwg-x64-win32.exe' {
|
||||
const path: string;
|
||||
export default path;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user