Revert "feat: add native fnOS gateway socket and base path support"
CI / verify (push) Canceled after 0s

This reverts commit 419c205103.
This commit is contained in:
2026-10-02 15:17:05 +08:00
parent 419c205103
commit a7e47e950a
7 changed files with 9 additions and 69 deletions
+1 -48
View File
@@ -2,16 +2,13 @@ package app
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"io" "io"
"io/fs" "io/fs"
"log/slog" "log/slog"
"net"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"time" "time"
"github.com/havline/havline/internal/acme" "github.com/havline/havline/internal/acme"
@@ -147,9 +144,6 @@ func New(cfg config.Config, staticFS fs.FS, migrationsDir string) (*App, error)
StaticFS: staticFS, StaticFS: staticFS,
StartedAt: startedAt, StartedAt: startedAt,
}) })
if prefix := strings.TrimSpace(cfg.GatewayPrefix); prefix != "" {
handler = stripPrefixIfPresent(prefix, handler)
}
server := &http.Server{ server := &http.Server{
Addr: cfg.ListenAddr, Addr: cfg.ListenAddr,
Handler: handler, Handler: handler,
@@ -241,33 +235,7 @@ func (a *App) bootstrapNginx(ctx context.Context, proxySvc *service.ProxyService
} }
func (a *App) Run() error { func (a *App) Run() error {
errCh := make(chan error, 2) return a.server.ListenAndServe()
go func() {
errCh <- a.server.ListenAndServe()
}()
if socket := strings.TrimSpace(a.cfg.GatewaySocket); socket != "" {
if err := os.Remove(socket); err != nil && !os.IsNotExist(err) {
return err
}
listener, err := net.Listen("unix", socket)
if err != nil {
return err
}
if err := os.Chmod(socket, 0o660); err != nil {
_ = listener.Close()
return err
}
go func() {
errCh <- a.server.Serve(listener)
}()
}
err := <-errCh
if errors.Is(err, http.ErrServerClosed) {
return nil
}
return err
} }
func (a *App) Shutdown(ctx context.Context) error { func (a *App) Shutdown(ctx context.Context) error {
@@ -278,9 +246,6 @@ func (a *App) Shutdown(ctx context.Context) error {
if err := a.server.Shutdown(ctx); err != nil { if err := a.server.Shutdown(ctx); err != nil {
return err return err
} }
if socket := strings.TrimSpace(a.cfg.GatewaySocket); socket != "" {
_ = os.Remove(socket)
}
if err := a.nginx.Stop(ctx); err != nil { if err := a.nginx.Stop(ctx); err != nil {
a.logger.Error("nginx stop failed", "module", "NGINX", "error", err.Error()) a.logger.Error("nginx stop failed", "module", "NGINX", "error", err.Error())
} }
@@ -295,18 +260,6 @@ func (a *App) Shutdown(ctx context.Context) error {
return nil return nil
} }
func stripPrefixIfPresent(prefix string, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.URL.Path, prefix) {
r.URL.Path = strings.TrimPrefix(r.URL.Path, prefix)
if r.URL.Path == "" {
r.URL.Path = "/"
}
}
next.ServeHTTP(w, r)
})
}
func ResolveMigrationsDir() (string, error) { func ResolveMigrationsDir() (string, error) {
candidates := []string{ candidates := []string{
"migrations", "migrations",
-4
View File
@@ -12,8 +12,6 @@ import (
type Config struct { type Config struct {
ListenAddr string ListenAddr string
GatewaySocket string
GatewayPrefix string
DataDir string DataDir string
SessionSecret string SessionSecret string
NginxBin string NginxBin string
@@ -33,8 +31,6 @@ func Load() Config {
} }
return Config{ return Config{
ListenAddr: envOr("HAVLINE_LISTEN", ":6893"), ListenAddr: envOr("HAVLINE_LISTEN", ":6893"),
GatewaySocket: envOr("HAVLINE_GATEWAY_SOCKET", ""),
GatewayPrefix: envOr("HAVLINE_GATEWAY_PREFIX", ""),
DataDir: dataDir, DataDir: dataDir,
SessionSecret: envOr("HAVLINE_SESSION_SECRET", ""), SessionSecret: envOr("HAVLINE_SESSION_SECRET", ""),
NginxBin: envOr("HAVLINE_NGINX_BIN", "nginx"), NginxBin: envOr("HAVLINE_NGINX_BIN", "nginx"),
-6
View File
@@ -1,6 +0,0 @@
export const APP_BASE = import.meta.env.BASE_URL.replace(/\/$/, '')
export function withBase(path: string): string {
if (!path.startsWith('/')) return path
return `${APP_BASE}${path}`
}
+6 -7
View File
@@ -51,7 +51,6 @@ import type {
CfPreflightResult, CfPreflightResult,
CfRouteTestResult, CfRouteTestResult,
} from './types' } from './types'
import { withBase } from './base'
export function asList<T>(value: T[] | null | undefined): T[] { export function asList<T>(value: T[] | null | undefined): T[] {
return Array.isArray(value) ? value : [] return Array.isArray(value) ? value : []
@@ -80,7 +79,7 @@ async function request<T>(path: string, init?: RequestInit, timeoutMs = REQUEST_
} }
async function requestWithSignal<T>(path: string, init?: RequestInit, signal?: AbortSignal): Promise<T> { async function requestWithSignal<T>(path: string, init?: RequestInit, signal?: AbortSignal): Promise<T> {
const response = await fetch(withBase(path), { const response = await fetch(path, {
credentials: 'include', credentials: 'include',
signal, signal,
headers: { headers: {
@@ -180,7 +179,7 @@ export const api = {
email: payload.email ?? '', email: payload.email ?? '',
}), }),
}), }),
certificateApplyStreamURL: (jobId: string) => withBase(`/api/certificates/jobs/${encodeURIComponent(jobId)}/stream`), certificateApplyStreamURL: (jobId: string) => `/api/certificates/jobs/${encodeURIComponent(jobId)}/stream`,
importCertificate: (payload: { importCertificate: (payload: {
certificate?: string certificate?: string
private_key?: string private_key?: string
@@ -193,7 +192,7 @@ export const api = {
}), }),
async downloadCertificate(domain: string, part: 'zip' | 'cert' | 'key' = 'zip') { async downloadCertificate(domain: string, part: 'zip' | 'cert' | 'key' = 'zip') {
const response = await fetch( const response = await fetch(
withBase(`/api/certificates/${encodeURIComponent(domain)}/download?part=${part}`), `/api/certificates/${encodeURIComponent(domain)}/download?part=${part}`,
{ credentials: 'include' }, { credentials: 'include' },
) )
if (!response.ok) { if (!response.ok) {
@@ -433,7 +432,7 @@ export const api = {
}, },
async exportBackup() { async exportBackup() {
const response = await fetch(withBase('/api/backup/export'), { credentials: 'include' }) const response = await fetch('/api/backup/export', { credentials: 'include' })
if (!response.ok) { if (!response.ok) {
throw new Error('导出备份失败') throw new Error('导出备份失败')
} }
@@ -455,7 +454,7 @@ export const api = {
deleteBackupArchive: (name: string) => deleteBackupArchive: (name: string) =>
request<{ ok: boolean }>(`/api/backup/archives/${encodeURIComponent(name)}`, { method: 'DELETE' }), request<{ ok: boolean }>(`/api/backup/archives/${encodeURIComponent(name)}`, { method: 'DELETE' }),
async downloadBackupArchive(name: string) { async downloadBackupArchive(name: string) {
const response = await fetch(withBase(`/api/backup/archives/${encodeURIComponent(name)}`), { credentials: 'include' }) const response = await fetch(`/api/backup/archives/${encodeURIComponent(name)}`, { credentials: 'include' })
if (!response.ok) { if (!response.ok) {
throw new Error('下载备份失败') throw new Error('下载备份失败')
} }
@@ -471,7 +470,7 @@ export const api = {
async restoreBackup(file: File) { async restoreBackup(file: File) {
const form = new FormData() const form = new FormData()
form.append('file', file) form.append('file', file)
const response = await fetch(withBase('/api/backup/restore'), { const response = await fetch('/api/backup/restore', {
method: 'POST', method: 'POST',
credentials: 'include', credentials: 'include',
body: form, body: form,
+1 -1
View File
@@ -17,7 +17,7 @@ import StatusPageView from '../views/StatusPageView.vue'
import AboutView from '../views/AboutView.vue' import AboutView from '../views/AboutView.vue'
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(),
routes: [ routes: [
{ path: '/setup', redirect: '/login' }, { path: '/setup', redirect: '/login' },
{ path: '/login', name: 'login', component: LoginView, meta: { public: true } }, { path: '/login', name: 'login', component: LoginView, meta: { public: true } },
+1 -2
View File
@@ -912,7 +912,6 @@ import {
WarningOutline, WarningOutline,
} from '@vicons/ionicons5' } from '@vicons/ionicons5'
import { api, asList } from '../api/client' import { api, asList } from '../api/client'
import { withBase } from '../api/base'
import type { CfTunnel, DiscoveredService, ProxyClientConn, ProxyRule, ProxySavePayload, ProxyTraffic } from '../api/types' import type { CfTunnel, DiscoveredService, ProxyClientConn, ProxyRule, ProxySavePayload, ProxyTraffic } from '../api/types'
import { useVisibilityPolling } from '../composables/useVisibilityPolling' import { useVisibilityPolling } from '../composables/useVisibilityPolling'
import EmptyState from '../components/EmptyState.vue' import EmptyState from '../components/EmptyState.vue'
@@ -1836,7 +1835,7 @@ function stopLogStream() {
function startLogStream() { function startLogStream() {
const rule = selectedRule.value const rule = selectedRule.value
if (!rule || logEventSource) return if (!rule || logEventSource) return
logEventSource = new EventSource(withBase(`/api/proxies/${rule.id}/logs/stream?tail=100`), { logEventSource = new EventSource(`/api/proxies/${rule.id}/logs/stream?tail=100`, {
withCredentials: true, withCredentials: true,
}) })
logEventSource.addEventListener('log', (event) => { logEventSource.addEventListener('log', (event) => {
-1
View File
@@ -5,7 +5,6 @@ import vue from '@vitejs/plugin-vue'
const pkg = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf-8')) as { version: string } const pkg = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf-8')) as { version: string }
export default defineConfig({ export default defineConfig({
base: process.env.VITE_BASE_PATH || '/',
plugins: [vue()], plugins: [vue()],
define: { define: {
__APP_VERSION__: JSON.stringify(process.env.VITE_APP_VERSION || pkg.version), __APP_VERSION__: JSON.stringify(process.env.VITE_APP_VERSION || pkg.version),