mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-06 06:41:56 +00:00
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
156 lines
4.6 KiB
TypeScript
156 lines
4.6 KiB
TypeScript
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
import { getTelegramWebApp, isTelegramEnvironment } from './useTelegram'
|
|
import type { AuthSource } from './useAuth'
|
|
|
|
const ACCESS_TOKEN_PREFIX = 'hapi_access_token::'
|
|
|
|
function getTelegramInitData(): string | null {
|
|
const tg = getTelegramWebApp()
|
|
if (tg?.initData) {
|
|
return tg.initData
|
|
}
|
|
|
|
// Fallback: check URL parameters (for testing or alternative flows)
|
|
const query = new URLSearchParams(window.location.search)
|
|
const tgWebAppData = query.get('tgWebAppData')
|
|
if (tgWebAppData) {
|
|
return tgWebAppData
|
|
}
|
|
|
|
const initData = query.get('initData')
|
|
return initData || null
|
|
}
|
|
|
|
function getTokenFromUrlParams(): string | null {
|
|
if (typeof window === 'undefined') return null
|
|
const query = new URLSearchParams(window.location.search)
|
|
return query.get('token')
|
|
}
|
|
|
|
function getAccessTokenKey(baseUrl: string): string {
|
|
return `${ACCESS_TOKEN_PREFIX}${baseUrl}`
|
|
}
|
|
|
|
function getStoredAccessToken(key: string): string | null {
|
|
try {
|
|
return localStorage.getItem(key)
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
function storeAccessToken(key: string, token: string): void {
|
|
try {
|
|
localStorage.setItem(key, token)
|
|
} catch {
|
|
// Ignore storage errors
|
|
}
|
|
}
|
|
|
|
function clearStoredAccessToken(key: string): void {
|
|
try {
|
|
localStorage.removeItem(key)
|
|
} catch {
|
|
// Ignore storage errors
|
|
}
|
|
}
|
|
|
|
export function useAuthSource(baseUrl: string): {
|
|
authSource: AuthSource | null
|
|
isLoading: boolean
|
|
isTelegram: boolean
|
|
setAccessToken: (token: string) => void
|
|
clearAuth: () => void
|
|
} {
|
|
const [authSource, setAuthSource] = useState<AuthSource | null>(null)
|
|
const [isLoading, setIsLoading] = useState(true)
|
|
const [isTelegram, setIsTelegram] = useState(false)
|
|
const retryCountRef = useRef(0)
|
|
const accessTokenKey = useMemo(() => getAccessTokenKey(baseUrl), [baseUrl])
|
|
|
|
// Initialize auth source on mount, with retry for delayed Telegram initData
|
|
useEffect(() => {
|
|
retryCountRef.current = 0
|
|
setAuthSource(null)
|
|
setIsTelegram(false)
|
|
setIsLoading(true)
|
|
|
|
const telegramInitData = getTelegramInitData()
|
|
|
|
if (telegramInitData) {
|
|
// Telegram Mini App environment
|
|
setAuthSource({ type: 'telegram', initData: telegramInitData })
|
|
setIsTelegram(true)
|
|
setIsLoading(false)
|
|
return
|
|
}
|
|
|
|
// Check for URL token parameter (for direct access links)
|
|
const urlToken = getTokenFromUrlParams()
|
|
if (urlToken) {
|
|
storeAccessToken(accessTokenKey, urlToken) // Save to localStorage for refresh
|
|
setAuthSource({ type: 'accessToken', token: urlToken })
|
|
setIsLoading(false)
|
|
return
|
|
}
|
|
|
|
// Check for stored access token as fallback
|
|
const storedToken = getStoredAccessToken(accessTokenKey)
|
|
if (storedToken) {
|
|
setAuthSource({ type: 'accessToken', token: storedToken })
|
|
setIsLoading(false)
|
|
return
|
|
}
|
|
|
|
// Check if we're in a Telegram environment before polling
|
|
if (!isTelegramEnvironment()) {
|
|
// Plain browser - show login prompt immediately
|
|
setIsLoading(false)
|
|
return
|
|
}
|
|
|
|
// Telegram environment detected - poll for delayed initData
|
|
// Telegram WebApp SDK may initialize slightly after page mount
|
|
const maxRetries = 20
|
|
const retryInterval = 250 // ms
|
|
|
|
const interval = setInterval(() => {
|
|
retryCountRef.current += 1
|
|
const initData = getTelegramInitData()
|
|
|
|
if (initData) {
|
|
setAuthSource({ type: 'telegram', initData })
|
|
setIsTelegram(true)
|
|
setIsLoading(false)
|
|
clearInterval(interval)
|
|
} else if (retryCountRef.current >= maxRetries) {
|
|
// Give up - show login prompt for browser access
|
|
setIsLoading(false)
|
|
clearInterval(interval)
|
|
}
|
|
}, retryInterval)
|
|
|
|
return () => {
|
|
clearInterval(interval)
|
|
}
|
|
}, [accessTokenKey])
|
|
|
|
const setAccessToken = useCallback((token: string) => {
|
|
storeAccessToken(accessTokenKey, token)
|
|
setAuthSource({ type: 'accessToken', token })
|
|
}, [accessTokenKey])
|
|
|
|
const clearAuth = useCallback(() => {
|
|
clearStoredAccessToken(accessTokenKey)
|
|
setAuthSource(null)
|
|
}, [accessTokenKey])
|
|
|
|
return {
|
|
authSource,
|
|
isLoading,
|
|
isTelegram,
|
|
setAccessToken,
|
|
clearAuth
|
|
}
|
|
}
|