Revert "feat: add native fnOS gateway socket and base path support"
CI / verify (push) Canceled after 0s
CI / verify (push) Canceled after 0s
This reverts commit 419c205103.
This commit is contained in:
+1
-48
@@ -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",
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
|||||||
@@ -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}`
|
|
||||||
}
|
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 } },
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
Reference in New Issue
Block a user