mirror of
https://github.com/wu736139669/hapi.git
synced 2026-10-27 22:51:43 +00:00
Adds support for hosting the web UI separately from the hapi server on static hosts (GitHub Pages, Cloudflare Pages). Users can now set a custom server origin via a dialog on the login screen, with the ability to return to same-origin behavior. Changes include: - New useServerUrl hook for managing server URL configuration and storage - Updated API client to support baseUrl parameter for all requests - Enhanced login UI with server picker dialog (top-right button) - Auth system now keys tokens per baseUrl to support multiple servers - SSE connection updated to use configured baseUrl - Documentation updates for standalone hosting setup
95 lines
2.5 KiB
TypeScript
95 lines
2.5 KiB
TypeScript
import { useCallback, useMemo, useState } from 'react'
|
|
|
|
const SERVER_URL_KEY = 'hapi_server_url'
|
|
|
|
export type ServerUrlResult =
|
|
| { ok: true; value: string }
|
|
| { ok: false; error: string }
|
|
|
|
export function normalizeServerUrl(input: string): ServerUrlResult {
|
|
const trimmed = input.trim()
|
|
if (!trimmed) {
|
|
return { ok: false, error: 'Enter a server URL like https://example.com' }
|
|
}
|
|
|
|
let parsed: URL
|
|
try {
|
|
parsed = new URL(trimmed)
|
|
} catch {
|
|
return { ok: false, error: 'Enter a valid URL including http:// or https://' }
|
|
}
|
|
|
|
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
return { ok: false, error: 'Server URL must start with http:// or https://' }
|
|
}
|
|
|
|
return { ok: true, value: parsed.origin }
|
|
}
|
|
|
|
function readStoredServerUrl(): string | null {
|
|
try {
|
|
const stored = localStorage.getItem(SERVER_URL_KEY)
|
|
if (!stored) {
|
|
return null
|
|
}
|
|
const normalized = normalizeServerUrl(stored)
|
|
if (!normalized.ok) {
|
|
localStorage.removeItem(SERVER_URL_KEY)
|
|
return null
|
|
}
|
|
return normalized.value
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
function writeStoredServerUrl(value: string): void {
|
|
try {
|
|
localStorage.setItem(SERVER_URL_KEY, value)
|
|
} catch {
|
|
// Ignore storage errors
|
|
}
|
|
}
|
|
|
|
function clearStoredServerUrl(): void {
|
|
try {
|
|
localStorage.removeItem(SERVER_URL_KEY)
|
|
} catch {
|
|
// Ignore storage errors
|
|
}
|
|
}
|
|
|
|
export function useServerUrl(): {
|
|
serverUrl: string | null
|
|
baseUrl: string
|
|
setServerUrl: (input: string) => ServerUrlResult
|
|
clearServerUrl: () => void
|
|
} {
|
|
const [serverUrl, setServerUrlState] = useState<string | null>(() => readStoredServerUrl())
|
|
|
|
const fallbackOrigin = typeof window !== 'undefined' ? window.location.origin : ''
|
|
const baseUrl = useMemo(() => serverUrl ?? fallbackOrigin, [serverUrl, fallbackOrigin])
|
|
|
|
const setServerUrl = useCallback((input: string): ServerUrlResult => {
|
|
const normalized = normalizeServerUrl(input)
|
|
if (!normalized.ok) {
|
|
return normalized
|
|
}
|
|
writeStoredServerUrl(normalized.value)
|
|
setServerUrlState(normalized.value)
|
|
return normalized
|
|
}, [])
|
|
|
|
const clearServerUrl = useCallback(() => {
|
|
clearStoredServerUrl()
|
|
setServerUrlState(null)
|
|
}, [])
|
|
|
|
return {
|
|
serverUrl,
|
|
baseUrl,
|
|
setServerUrl,
|
|
clearServerUrl
|
|
}
|
|
}
|