Scert/backend/internal/cli/application_services.go

90 lines
2.1 KiB
Go

package cli
import (
"context"
"errors"
"fmt"
"net/http"
"git.pcux.cn/xuans/Scert/backend/internal/localreset"
)
type applicationHTTP interface {
ListenAndServe() error
Listening() <-chan struct{}
}
type applicationControl interface {
Serve(context.Context, localreset.Factory) error
Close() error
}
// runApplicationServices 将本机通道绑定到正式主服务生命周期,并在数据库关闭前等待重置处理退出。
func runApplicationServices(
ctx context.Context,
web applicationHTTP,
startRenewal func(context.Context),
factory localreset.Factory,
openControl func() (applicationControl, error),
) (_ bool, resultErr error) {
if ctx.Err() != nil {
return false, nil
}
control, err := openControl()
if err != nil {
return false, fmt.Errorf("启动本机密码重置通道失败:%w", err)
}
controlCtx, cancel := context.WithCancel(ctx)
controlDone := make(chan error, 1)
go func() { controlDone <- control.Serve(controlCtx, factory) }()
controlFinished := false
defer func() {
cancel()
resultErr = errors.Join(resultErr, control.Close())
if !controlFinished {
resultErr = errors.Join(resultErr, <-controlDone)
}
}()
httpDone := make(chan error, 1)
go func() { httpDone <- web.ListenAndServe() }()
select {
case <-ctx.Done():
return false, nil
case err := <-controlDone:
controlFinished = true
return false, applicationControlExit(ctx, err)
case err := <-httpDone:
return false, applicationHTTPExit(err)
case <-web.Listening():
}
if ctx.Err() != nil {
return false, nil
}
if startRenewal != nil {
startRenewal(ctx)
}
select {
case <-ctx.Done():
return false, nil
case err := <-controlDone:
controlFinished = true
return false, applicationControlExit(ctx, err)
case err := <-httpDone:
return false, applicationHTTPExit(err)
}
}
func applicationControlExit(ctx context.Context, err error) error {
if ctx.Err() != nil {
return err
}
return fmt.Errorf("本机密码重置通道意外退出:%w", errors.Join(localreset.ErrUnavailable, err))
}
func applicationHTTPExit(err error) error {
if errors.Is(err, http.ErrServerClosed) {
return nil
}
return err
}