move server to server directory.

This commit is contained in:
Miroslav Šedivý
2024-06-23 17:48:14 +02:00
parent da45f62ca8
commit 5b98344205
211 changed files with 18 additions and 10 deletions

18
server/cmd/neko/main.go Normal file
View File

@ -0,0 +1,18 @@
package main
import (
"fmt"
"github.com/rs/zerolog/log"
"github.com/demodesk/neko"
"github.com/demodesk/neko/cmd"
"github.com/demodesk/neko/pkg/utils"
)
func main() {
fmt.Print(utils.Colorf(neko.Header, "server", neko.Version))
if err := cmd.Execute(); err != nil {
log.Panic().Err(err).Msg("failed to execute command")
}
}

49
server/cmd/plugins.go Normal file
View File

@ -0,0 +1,49 @@
package cmd
import (
"encoding/json"
"os"
"github.com/demodesk/neko/internal/config"
"github.com/demodesk/neko/internal/plugins"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)
func init() {
command := &cobra.Command{
Use: "plugins [directory]",
Short: "load, verify and list plugins",
Long: `load, verify and list plugins`,
Run: pluginsCmd,
Args: cobra.MaximumNArgs(1),
}
root.AddCommand(command)
}
func pluginsCmd(cmd *cobra.Command, args []string) {
pluginDir := "/etc/neko/plugins"
if len(args) > 0 {
pluginDir = args[0]
}
log.Info().Str("dir", pluginDir).Msg("plugins directory")
plugs := plugins.New(&config.Plugins{
Enabled: true,
Required: true,
Dir: pluginDir,
})
meta := plugs.Metadata()
if len(meta) == 0 {
log.Fatal().Msg("no plugins found")
}
// marshal indent to stdout
dec := json.NewEncoder(os.Stdout)
dec.SetIndent("", " ")
err := dec.Encode(meta)
if err != nil {
log.Fatal().Err(err).Msg("unable to marshal metadata")
}
}

165
server/cmd/root.go Normal file
View File

@ -0,0 +1,165 @@
package cmd
import (
"fmt"
"io"
"os"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/rs/zerolog"
"github.com/rs/zerolog/diode"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/demodesk/neko"
"github.com/demodesk/neko/internal/config"
)
func Execute() error {
// properly log unhandled panics
defer func() {
panicVal := recover()
if panicVal != nil {
log.Panic().Msgf("%v", panicVal)
}
}()
return root.Execute()
}
var root = &cobra.Command{
Use: "neko",
Short: "neko streaming server",
Long: `neko streaming server`,
Version: neko.Version.String(),
}
func init() {
rootConfig := config.Root{}
cobra.OnInitialize(func() {
//////
// configs
//////
config := viper.GetString("config") // Use config file from the flag.
if config == "" {
config = os.Getenv("NEKO_CONFIG") // Use config file from the environment variable.
}
if config != "" {
viper.SetConfigFile(config)
} else {
if runtime.GOOS == "linux" {
viper.AddConfigPath("/etc/neko/")
}
viper.AddConfigPath(".")
viper.SetConfigName("neko")
}
viper.SetEnvPrefix("NEKO")
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
viper.AutomaticEnv() // read in environment variables that match
// read config values
err := viper.ReadInConfig()
if err != nil {
_, notFound := err.(viper.ConfigFileNotFoundError)
if !notFound {
log.Fatal().Err(err).Msg("unable to read config file")
}
}
// get full config file path
config = viper.ConfigFileUsed()
// set root config values
rootConfig.Set()
//////
// logs
//////
var logWriter io.Writer
// log to a directory instead of stderr
if rootConfig.LogDir != "" {
if _, err := os.Stat(rootConfig.LogDir); os.IsNotExist(err) {
_ = os.Mkdir(rootConfig.LogDir, os.ModePerm)
}
latest := filepath.Join(rootConfig.LogDir, "neko-latest.log")
if _, err := os.Stat(latest); err == nil {
err = os.Rename(latest, filepath.Join(rootConfig.LogDir, "neko."+time.Now().Format("2006-01-02T15-04-05Z07-00")+".log"))
if err != nil {
log.Fatal().Err(err).Msg("failed to rotate log file")
}
}
logf, err := os.OpenFile(latest, os.O_RDWR|os.O_CREATE, 0666)
if err != nil {
log.Fatal().Err(err).Msg("failed to open log file")
}
logWriter = diode.NewWriter(logf, 1000, 10*time.Millisecond, func(missed int) {
fmt.Printf("logger dropped %d messages", missed)
})
} else {
logWriter = os.Stderr
}
// log console output instead of json
if !rootConfig.LogJson {
logWriter = zerolog.ConsoleWriter{
Out: logWriter,
NoColor: rootConfig.LogNocolor,
}
}
// save new logger output
log.Logger = log.Output(logWriter)
// set custom log level
if rootConfig.LogLevel != zerolog.NoLevel {
zerolog.SetGlobalLevel(rootConfig.LogLevel)
}
// set custom log tiem format
if rootConfig.LogTime != "" {
zerolog.TimeFieldFormat = rootConfig.LogTime
}
timeFormat := rootConfig.LogTime
if rootConfig.LogTime == zerolog.TimeFormatUnix {
timeFormat = "UNIX"
}
logger := log.With().
Str("config", config).
Str("log-level", zerolog.GlobalLevel().String()).
Bool("log-json", rootConfig.LogJson).
Str("log-time", timeFormat).
Str("log-dir", rootConfig.LogDir).
Logger()
if config == "" {
logger.Warn().Msg("preflight complete without config file")
} else {
if _, err := os.Stat(config); os.IsNotExist(err) {
logger.Err(err).Msg("preflight complete with nonexistent config file")
} else {
logger.Info().Msg("preflight complete with config file")
}
}
})
if err := rootConfig.Init(root); err != nil {
log.Panic().Err(err).Msg("unable to run root command")
}
root.SetVersionTemplate(neko.Version.Details())
}

212
server/cmd/serve.go Normal file
View File

@ -0,0 +1,212 @@
package cmd
import (
"os"
"os/signal"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/demodesk/neko/internal/api"
"github.com/demodesk/neko/internal/capture"
"github.com/demodesk/neko/internal/config"
"github.com/demodesk/neko/internal/desktop"
"github.com/demodesk/neko/internal/http"
"github.com/demodesk/neko/internal/member"
"github.com/demodesk/neko/internal/plugins"
"github.com/demodesk/neko/internal/session"
"github.com/demodesk/neko/internal/webrtc"
"github.com/demodesk/neko/internal/websocket"
)
func init() {
service := serve{}
command := &cobra.Command{
Use: "serve",
Short: "serve neko streaming server",
Long: `serve neko streaming server`,
PreRun: service.PreRun,
Run: service.Run,
}
if err := service.Init(command); err != nil {
log.Panic().Err(err).Msg("unable to initialize configuration")
}
root.AddCommand(command)
}
type serve struct {
logger zerolog.Logger
configs struct {
Desktop config.Desktop
Capture config.Capture
WebRTC config.WebRTC
Member config.Member
Session config.Session
Plugins config.Plugins
Server config.Server
}
managers struct {
desktop *desktop.DesktopManagerCtx
capture *capture.CaptureManagerCtx
webRTC *webrtc.WebRTCManagerCtx
member *member.MemberManagerCtx
session *session.SessionManagerCtx
webSocket *websocket.WebSocketManagerCtx
plugins *plugins.ManagerCtx
api *api.ApiManagerCtx
http *http.HttpManagerCtx
}
}
func (c *serve) Init(cmd *cobra.Command) error {
if err := c.configs.Desktop.Init(cmd); err != nil {
return err
}
if err := c.configs.Capture.Init(cmd); err != nil {
return err
}
if err := c.configs.WebRTC.Init(cmd); err != nil {
return err
}
if err := c.configs.Member.Init(cmd); err != nil {
return err
}
if err := c.configs.Session.Init(cmd); err != nil {
return err
}
if err := c.configs.Plugins.Init(cmd); err != nil {
return err
}
if err := c.configs.Server.Init(cmd); err != nil {
return err
}
return nil
}
func (c *serve) PreRun(cmd *cobra.Command, args []string) {
c.logger = log.With().Str("service", "neko").Logger()
c.configs.Desktop.Set()
c.configs.Capture.Set()
c.configs.WebRTC.Set()
c.configs.Member.Set()
c.configs.Session.Set()
c.configs.Plugins.Set()
c.configs.Server.Set()
}
func (c *serve) Start(cmd *cobra.Command) {
c.managers.session = session.New(
&c.configs.Session,
)
c.managers.member = member.New(
c.managers.session,
&c.configs.Member,
)
if err := c.managers.member.Connect(); err != nil {
c.logger.Panic().Err(err).Msg("unable to connect to member manager")
}
c.managers.desktop = desktop.New(
&c.configs.Desktop,
)
c.managers.desktop.Start()
c.managers.capture = capture.New(
c.managers.desktop,
&c.configs.Capture,
)
c.managers.capture.Start()
c.managers.webRTC = webrtc.New(
c.managers.desktop,
c.managers.capture,
&c.configs.WebRTC,
)
c.managers.webRTC.Start()
c.managers.webSocket = websocket.New(
c.managers.session,
c.managers.desktop,
c.managers.capture,
c.managers.webRTC,
)
c.managers.webSocket.Start()
c.managers.api = api.New(
c.managers.session,
c.managers.member,
c.managers.desktop,
c.managers.capture,
)
c.managers.plugins = plugins.New(
&c.configs.Plugins,
)
// init and set configuration now
// this means it won't be in --help
c.managers.plugins.InitConfigs(cmd)
c.managers.plugins.SetConfigs()
c.managers.plugins.Start(
c.managers.session,
c.managers.webSocket,
c.managers.api,
)
c.managers.http = http.New(
c.managers.webSocket,
c.managers.api,
&c.configs.Server,
)
c.managers.http.Start()
}
func (c *serve) Shutdown() {
var err error
err = c.managers.http.Shutdown()
c.logger.Err(err).Msg("http manager shutdown")
err = c.managers.plugins.Shutdown()
c.logger.Err(err).Msg("plugins manager shutdown")
err = c.managers.webSocket.Shutdown()
c.logger.Err(err).Msg("websocket manager shutdown")
err = c.managers.webRTC.Shutdown()
c.logger.Err(err).Msg("webrtc manager shutdown")
err = c.managers.capture.Shutdown()
c.logger.Err(err).Msg("capture manager shutdown")
err = c.managers.desktop.Shutdown()
c.logger.Err(err).Msg("desktop manager shutdown")
err = c.managers.member.Disconnect()
c.logger.Err(err).Msg("member manager disconnect")
}
func (c *serve) Run(cmd *cobra.Command, args []string) {
c.logger.Info().Msg("starting neko server")
c.Start(cmd)
c.logger.Info().Msg("neko ready")
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
sig := <-quit
c.logger.Warn().Msgf("received %s, attempting graceful shutdown", sig)
c.Shutdown()
c.logger.Info().Msg("shutdown complete")
}