fix(backend): properly shutdown SSH server on SIGINT/SIGTERM

This commit is contained in:
pycook
2025-08-17 15:45:56 +08:00
parent 1e4306dc69
commit 0e23064067
4 changed files with 83 additions and 8 deletions

View File

@@ -13,6 +13,7 @@ import (
"github.com/veops/oneterm/internal/model"
"github.com/veops/oneterm/internal/service"
fileservice "github.com/veops/oneterm/internal/service/file"
webproxy "github.com/veops/oneterm/internal/service/web_proxy"
gsession "github.com/veops/oneterm/internal/session"
"github.com/veops/oneterm/pkg/config"
"github.com/veops/oneterm/pkg/db"
@@ -114,4 +115,10 @@ func StopApi() {
if err := srv.Shutdown(ctx); err != nil {
logger.L().Error("Stop HTTP server failed", zap.Error(err))
}
// Stop storage service background tasks
service.StopStorageService()
// Stop web proxy session cleanup routine
webproxy.StopSessionCleanupRoutine()
}

View File

@@ -8,6 +8,7 @@ import (
"sort"
"strconv"
"strings"
"sync"
"time"
"github.com/gin-gonic/gin"
@@ -542,6 +543,13 @@ func (s *storageService) GetAvailableProvider(ctx context.Context) (storage.Prov
// Global storage service instance
var DefaultStorageService StorageService
// Global context for health monitoring
var (
healthMonitoringCtx context.Context
healthMonitoringCancel context.CancelFunc
healthMonitoringWg sync.WaitGroup
)
// InitStorageService initializes the global storage service with database configurations
func InitStorageService() {
if DefaultStorageService == nil {
@@ -711,15 +719,25 @@ func verifyPrimaryProvider(ctx context.Context, s *storageService) error {
func init() {
DefaultStorageService = NewStorageService()
// Initialize health monitoring context
healthMonitoringCtx, healthMonitoringCancel = context.WithCancel(context.Background())
// Start background storage health monitoring
healthMonitoringWg.Add(1)
go func() {
defer healthMonitoringWg.Done()
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()
for {
<-ticker.C
if DefaultStorageService != nil {
performHealthMonitoring()
select {
case <-healthMonitoringCtx.Done():
logger.L().Info("Storage health monitoring stopped")
return
case <-ticker.C:
if DefaultStorageService != nil {
performHealthMonitoring()
}
}
}
}()
@@ -745,6 +763,15 @@ func init() {
// }()
}
// StopStorageService stops all background tasks for storage service
func StopStorageService() {
if healthMonitoringCancel != nil {
healthMonitoringCancel()
healthMonitoringWg.Wait()
logger.L().Info("Storage service background tasks stopped")
}
}
// performHealthMonitoring performs periodic health checks on all storage providers
func performHealthMonitoring() {
ctx := context.Background()

View File

@@ -2,6 +2,7 @@ package web_proxy
import (
"context"
"sync"
"time"
"go.uber.org/zap"
@@ -15,6 +16,13 @@ import (
// Global session storage
var webProxySessions = make(map[string]*WebProxySession)
// Global cleanup context
var (
cleanupCtx context.Context
cleanupCancel context.CancelFunc
cleanupWg sync.WaitGroup
)
// WebProxySession represents an active web proxy session
type WebProxySession struct {
SessionId string
@@ -73,17 +81,39 @@ func cleanupExpiredSessions(maxInactiveTime time.Duration) {
// StartSessionCleanupRoutine starts background cleanup routine for web sessions
func StartSessionCleanupRoutine() {
// Initialize cleanup context
cleanupCtx, cleanupCancel = context.WithCancel(context.Background())
// More frequent cleanup - every 30 seconds to catch closed browser tabs quickly
ticker := time.NewTicker(30 * time.Second)
cleanupWg.Add(1)
go func() {
for range ticker.C {
// Use system configured timeout (same as other protocols)
systemTimeout := time.Duration(model.GlobalConfig.Load().Timeout) * time.Second
cleanupExpiredSessions(systemTimeout)
defer cleanupWg.Done()
defer ticker.Stop()
for {
select {
case <-cleanupCtx.Done():
logger.L().Info("Web proxy session cleanup stopped")
return
case <-ticker.C:
// Use system configured timeout (same as other protocols)
systemTimeout := time.Duration(model.GlobalConfig.Load().Timeout) * time.Second
cleanupExpiredSessions(systemTimeout)
}
}
}()
}
// StopSessionCleanupRoutine stops the background cleanup routine
func StopSessionCleanupRoutine() {
if cleanupCancel != nil {
cleanupCancel()
cleanupWg.Wait()
logger.L().Info("Web proxy session cleanup routine stopped")
}
}
// GetSession retrieves a session by ID
func GetSession(sessionID string) (*WebProxySession, bool) {
session, exists := webProxySessions[sessionID]

View File

@@ -3,6 +3,7 @@ package sshsrv
import (
"context"
"fmt"
"time"
"github.com/gliderlabs/ssh"
gossh "golang.org/x/crypto/ssh"
@@ -40,5 +41,15 @@ func RunSsh() error {
}
func StopSsh() {
defer cancel()
if server != nil {
// Use a fresh context for shutdown with timeout
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second)
defer shutdownCancel()
if err := server.Shutdown(shutdownCtx); err != nil {
// If graceful shutdown fails, force close
server.Close()
}
}
cancel()
}