Files
acc-server-manager/local/service/service_control.go

279 lines
7.3 KiB
Go
Executable File

package service
import (
"acc-server-manager/local/middleware"
"acc-server-manager/local/model"
"acc-server-manager/local/platform"
"acc-server-manager/local/repository"
"context"
"errors"
"time"
"github.com/gofiber/fiber/v2"
)
type ServiceControlService struct {
repository *repository.ServiceControlRepository
serverRepository *repository.ServerRepository
serverService *ServerService
activityLog *ActivityLogService
statusCache *model.ServerStatusCache
runtime platform.ServerRuntime
}
func NewServiceControlService(
repository *repository.ServiceControlRepository,
serverRepository *repository.ServerRepository,
runtime platform.ServerRuntime,
activityLog *ActivityLogService,
) *ServiceControlService {
return &ServiceControlService{
repository: repository,
serverRepository: serverRepository,
runtime: runtime,
activityLog: activityLog,
statusCache: model.NewServerStatusCache(model.CacheConfig{
ExpirationTime: 30 * time.Second,
ThrottleTime: 5 * time.Second,
DefaultStatus: model.StatusRunning,
}),
}
}
// getServerFromCtx resolves the server from ctx locals (serverId or service name).
func (as *ServiceControlService) getServerFromCtx(ctx *fiber.Ctx) (*model.Server, error) {
serviceName, ok := ctx.Locals("service").(string)
if !ok || serviceName == "" {
serverId, ok2 := ctx.Locals("serverId").(string)
if !ok2 || serverId == "" {
return nil, errors.New("service name missing")
}
return as.serverRepository.GetByID(ctx.UserContext(), serverId)
}
return as.serverRepository.GetFirstByServiceName(ctx.UserContext(), serviceName)
}
func extractActor(ctx *fiber.Ctx) (actorID, actorUsername string) {
userInfo, _ := ctx.Locals("userInfo").(*middleware.CachedUserInfo)
if userInfo != nil {
return userInfo.UserID, userInfo.Username
}
return "", "system"
}
func (as *ServiceControlService) SetServerService(serverService *ServerService) {
as.serverService = serverService
}
func (as *ServiceControlService) GetStatus(ctx *fiber.Ctx) (string, error) {
serviceName, err := as.GetServiceName(ctx)
if err != nil {
return "", err
}
if status, shouldCheck := as.statusCache.GetStatus(serviceName); !shouldCheck {
return status.String(), nil
}
statusStr, err := as.StatusServer(serviceName)
if err != nil {
return "", err
}
status := model.ParseServiceStatus(statusStr)
as.statusCache.UpdateStatus(serviceName, status)
return status.String(), nil
}
func (as *ServiceControlService) ServiceControlStartServer(ctx *fiber.Ctx) (string, error) {
server, err := as.getServerFromCtx(ctx)
if err != nil {
return "", err
}
serviceName := server.ServiceName
as.statusCache.UpdateStatus(serviceName, model.StatusStarting)
_, err = as.StartServer(serviceName)
if err != nil {
return "", err
}
statusStr, err := as.StatusServer(serviceName)
if err != nil {
return "", err
}
status := model.ParseServiceStatus(statusStr)
as.statusCache.UpdateStatus(serviceName, status)
actorID, actorUsername := extractActor(ctx)
go func() {
_ = as.activityLog.Log(context.Background(), &model.ActivityLog{
ServerID: server.ID,
UserID: actorID,
Username: actorUsername,
Action: model.ActionServerStart,
Details: `{"service":"` + serviceName + `"}`,
})
}()
return status.String(), nil
}
func (as *ServiceControlService) ServiceControlStopServer(ctx *fiber.Ctx) (string, error) {
server, err := as.getServerFromCtx(ctx)
if err != nil {
return "", err
}
serviceName := server.ServiceName
as.statusCache.UpdateStatus(serviceName, model.StatusStopping)
_, err = as.StopServer(serviceName)
if err != nil {
return "", err
}
statusStr, err := as.StatusServer(serviceName)
if err != nil {
return "", err
}
status := model.ParseServiceStatus(statusStr)
as.statusCache.UpdateStatus(serviceName, status)
actorID, actorUsername := extractActor(ctx)
go func() {
_ = as.activityLog.Log(context.Background(), &model.ActivityLog{
ServerID: server.ID,
UserID: actorID,
Username: actorUsername,
Action: model.ActionServerStop,
Details: `{"service":"` + serviceName + `"}`,
})
}()
return status.String(), nil
}
func (as *ServiceControlService) ServiceControlRestartServer(ctx *fiber.Ctx) (string, error) {
server, err := as.getServerFromCtx(ctx)
if err != nil {
return "", err
}
serviceName := server.ServiceName
as.statusCache.UpdateStatus(serviceName, model.StatusRestarting)
_, err = as.RestartServer(serviceName)
if err != nil {
return "", err
}
statusStr, err := as.StatusServer(serviceName)
if err != nil {
return "", err
}
status := model.ParseServiceStatus(statusStr)
as.statusCache.UpdateStatus(serviceName, status)
actorID, actorUsername := extractActor(ctx)
go func() {
_ = as.activityLog.Log(context.Background(), &model.ActivityLog{
ServerID: server.ID,
UserID: actorID,
Username: actorUsername,
Action: model.ActionServerRestart,
Details: `{"service":"` + serviceName + `"}`,
})
}()
return status.String(), nil
}
func (as *ServiceControlService) StatusServer(serviceName string) (string, error) {
server, err := as.serverRepository.GetFirstByServiceName(context.Background(), serviceName)
if err != nil {
return "", err
}
return as.runtime.Status(context.Background(), server.ID)
}
func (as *ServiceControlService) GetCachedStatus(serviceName string) (string, error) {
if status, shouldCheck := as.statusCache.GetStatus(serviceName); !shouldCheck {
return status.String(), nil
}
statusStr, err := as.StatusServer(serviceName)
if err != nil {
return "", err
}
status := model.ParseServiceStatus(statusStr)
as.statusCache.UpdateStatus(serviceName, status)
return status.String(), nil
}
func (as *ServiceControlService) StartServer(serviceName string) (string, error) {
server, err := as.serverRepository.GetFirstByServiceName(context.Background(), serviceName)
if err != nil {
return "", err
}
status, err := as.runtime.Start(context.Background(), server.ID)
if err != nil {
return "", err
}
as.serverService.StartAccServerRuntime(server)
return status, err
}
func (as *ServiceControlService) StopServer(serviceName string) (string, error) {
server, err := as.serverRepository.GetFirstByServiceName(context.Background(), serviceName)
if err != nil {
return "", err
}
status, err := as.runtime.Stop(context.Background(), server.ID)
if err != nil {
return "", err
}
as.serverService.instances.Delete(server.ID)
return status, err
}
func (as *ServiceControlService) RestartServer(serviceName string) (string, error) {
server, err := as.serverRepository.GetFirstByServiceName(context.Background(), serviceName)
if err != nil {
return "", err
}
status, err := as.runtime.Restart(context.Background(), server.ID)
if err != nil {
return "", err
}
as.serverService.StartAccServerRuntime(server)
return status, err
}
func (as *ServiceControlService) GetServiceName(ctx *fiber.Ctx) (string, error) {
var server *model.Server
var err error
serviceName, ok := ctx.Locals("service").(string)
if !ok || serviceName == "" {
serverId, ok2 := ctx.Locals("serverId").(string)
if !ok2 || serverId == "" {
return "", errors.New("service name missing")
}
server, err = as.serverRepository.GetByID(ctx.UserContext(), serverId)
} else {
server, err = as.serverRepository.GetFirstByServiceName(ctx.UserContext(), serviceName)
}
if err != nil {
return "", err
}
return server.ServiceName, nil
}