Add empty server implementation

Change-Id: Ieeee07a18d91d0c429a15c5eb0f1f96aed9393c6
diff --git a/server/cmd/commands/root.go b/server/cmd/commands/root.go
new file mode 100644
index 0000000..088e7ff
--- /dev/null
+++ b/server/cmd/commands/root.go
@@ -0,0 +1,62 @@
+package commands
+
+import (
+	"log/slog"
+	"os"
+	"os/signal"
+	"syscall"
+
+	"github.com/iomodo/staff/server"
+	"github.com/spf13/cobra"
+)
+
+// Command is an abstraction of the cobra Command
+type Command = cobra.Command
+
+// Run function starts the application
+func Run(args []string) error {
+	rootCmd.SetArgs(args)
+	return rootCmd.Execute()
+}
+
+// rootCmd is a command to run the server.
+var rootCmd = &cobra.Command{
+	Use:   "server",
+	Short: "Runs a server",
+	Long:  `Runs a server. Killing the process will stop the server`,
+	RunE:  serverCmdF,
+}
+
+func serverCmdF(_ *cobra.Command, _ []string) error {
+	srv, err := runServer()
+	if err != nil {
+		return err
+	}
+	defer srv.Shutdown()
+
+	// wait for kill signal before attempting to gracefully shutdown
+	// the running service
+	interruptChan := make(chan os.Signal, 1)
+	signal.Notify(interruptChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
+	<-interruptChan
+	return nil
+}
+
+func runServer() (*server.Server, error) {
+	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
+		Level: slog.LevelDebug,
+	}))
+
+	srv, err := server.NewServer(logger)
+	if err != nil {
+		logger.Error(err.Error())
+		return nil, err
+	}
+
+	serverErr := srv.Start()
+	if serverErr != nil {
+		logger.Error(serverErr.Error())
+		return nil, serverErr
+	}
+	return srv, nil
+}
diff --git a/server/cmd/main.go b/server/cmd/main.go
new file mode 100644
index 0000000..4316dfd
--- /dev/null
+++ b/server/cmd/main.go
@@ -0,0 +1,13 @@
+package main
+
+import (
+	"os"
+
+	"github.com/iomodo/staff/cmd/commands"
+)
+
+func main() {
+	if err := commands.Run(os.Args[1:]); err != nil {
+		os.Exit(1)
+	}
+}