package cmd import ( "context" "fmt" "log/slog" "net" "net/http" _ "github.com/mattn/go-sqlite3" "github.com/spf13/cobra" "github.com/spf13/viper" "gitrepo.ru/neonxp/gorum/contextlib" "gitrepo.ru/neonxp/gorum/controllers" appmiddleware "gitrepo.ru/neonxp/gorum/middleware" "gitrepo.ru/neonxp/gorum/repository" "gitrepo.ru/neonxp/gorum/views" "gitrepo.ru/neonxp/gorum/views/assets" "go.etcd.io/bbolt" "go.neonxp.ru/mux" "go.neonxp.ru/mux/middleware" "go.neonxp.ru/mux/middleware/session" ) var ( theme string listen string sessionSecret string serverCmd = &cobra.Command{ Use: "serve", Short: "Run server", Long: `Run forum server`, RunE: func(cmd *cobra.Command, args []string) error { return serve(cmd.Context()) }, } ) func init() { serverCmd.PersistentFlags().StringVar(&theme, "theme", "default", "theme to use") serverCmd.PersistentFlags().StringVar(&listen, "listen", ":8000", "bind address to listen") serverCmd.PersistentFlags().StringVar(&sessionSecret, "session_secret", "s3cr3t", "sessions secret") viper.BindPFlag("theme", serverCmd.Flags().Lookup("theme")) viper.BindPFlag("listen", serverCmd.Flags().Lookup("listen")) viper.BindPFlag("session_secret", serverCmd.Flags().Lookup("session_secret")) } func serve(ctx context.Context) error { slog.Debug( "params", slog.String("listen", listen), slog.String("theme", theme), slog.String("session_secret", sessionSecret), ) ctx = context.WithValue(ctx, contextlib.ThemeKey, theme) orm, err := bbolt.Open(dbFile, 0600, nil) if err != nil { return err } defer orm.Close() userRepo := repository.NewUser(orm) nodeRepo := repository.NewPost(orm) topicRepo := repository.NewTopic(orm) if err := topicRepo.Init(); err != nil { return err } if err := userRepo.Init(); err != nil { return err } r := controllers.NewRouter(userRepo, nodeRepo, topicRepo) e := http.NewServeMux() mux.DefaultErrorHandler = func(err error) mux.Renderer { return views.ErrorPage(err) } e.HandleFunc("GET /register", r.Register) e.HandleFunc("POST /register", r.Register) e.HandleFunc("GET /login", r.Login) e.HandleFunc("POST /login", r.Login) e.HandleFunc("POST /logout", r.Logout) e.HandleFunc("GET /{$}", r.Index) mux.Group(e, "/t", func(sm *http.ServeMux) { sm.HandleFunc("GET /{id}", r.Topic) sm.HandleFunc("GET /new", r.NewTopic) sm.HandleFunc("POST /new", r.NewTopic) }) e.HandleFunc("POST /p/new", r.NewPost) e.Handle("/assets/", http.StripPrefix("/assets", http.FileServerFS(assets.FS))) sessionManager := session.New(session.NewBoltStore(orm, []byte("sessions"))) mh := mux.Use(e, middleware.Logger(slog.Default()), // middleware.Recover(slog.Default()), middleware.RequestID, appmiddleware.UserMiddleware(), sessionManager.Middleware(), ) slog.InfoContext(ctx, "started gorum", slog.String("bind", listen)) server := http.Server{ Addr: listen, Handler: mh, ErrorLog: slog.NewLogLogger(slog.Default().Handler(), slog.LevelError), ConnContext: func(cctx context.Context, c net.Conn) context.Context { return ctx }, } if err := server.ListenAndServe(); err != http.ErrServerClosed { return fmt.Errorf("server failed: %w", err) } return nil }