diff options
Diffstat (limited to 'app/cmd/serve.go')
-rw-r--r-- | app/cmd/serve.go | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/app/cmd/serve.go b/app/cmd/serve.go new file mode 100644 index 0000000..5593c32 --- /dev/null +++ b/app/cmd/serve.go @@ -0,0 +1,134 @@ +package cmd + +import ( + "context" + "database/sql" + "fmt" + "log/slog" + "net" + "net/http" + + "github.com/labstack/echo-contrib/session" + "github.com/labstack/echo/v4" + echomiddleware "github.com/labstack/echo/v4/middleware" + _ "github.com/mattn/go-sqlite3" + "github.com/michaeljs1990/sqlitestore" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/uptrace/bun" + "github.com/uptrace/bun/dialect/sqlitedialect" + "github.com/uptrace/bun/extra/bundebug" + "gitrepo.ru/neonxp/gorum/contextlib" + "gitrepo.ru/neonxp/gorum/middleware" + "gitrepo.ru/neonxp/gorum/repository" + "gitrepo.ru/neonxp/gorum/routes" + "gitrepo.ru/neonxp/gorum/utils" + "gitrepo.ru/neonxp/gorum/views" + "gitrepo.ru/neonxp/gorum/views/assets" +) + +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 (default is 'default')") + serverCmd.PersistentFlags().StringVar(&listen, "listen", ":8000", "bind address to listen (default is ':8000')") + serverCmd.PersistentFlags().StringVar(&sessionSecret, "session_secret", "s3cr3t", "sessions secret (default is 's3cr3t')") + 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) + + db, err := sql.Open("sqlite3", dbFile) + if err != nil { + return fmt.Errorf("open db failed: %w", err) + } + defer db.Close() + + orm := bun.NewDB(db, sqlitedialect.New()) + orm.AddQueryHook(bundebug.NewQueryHook(bundebug.WithVerbose(true))) + + userRepo := repository.NewUser(orm) + nodeRepo := repository.NewNode(orm) + + r := routes.NewRouter(userRepo, nodeRepo) + + e := echo.New() + + e.HideBanner = true + + e.HTTPErrorHandler = func(err error, c echo.Context) { + _ = utils.Render(c, views.ErrorPage(err)) + } + + e.Server.BaseContext = func(l net.Listener) context.Context { + return ctx + } + + sessionStore, err := sqlitestore.NewSqliteStoreFromConnection(db, "sessions", "", 0, []byte(sessionSecret)) + if err != nil { + return fmt.Errorf("failed init session store: %w", err) + } + + e.Use( + echomiddleware.Recover(), + echomiddleware.Gzip(), + echomiddleware.CSRFWithConfig(echomiddleware.CSRFConfig{ + Skipper: echomiddleware.DefaultSkipper, + TokenLength: 32, + TokenLookup: "form:" + echo.HeaderXCSRFToken, + ContextKey: "csrf", + CookieName: "_csrf", + CookieMaxAge: 86400, + }), + session.Middleware(sessionStore), + middleware.UserMiddleware(), + ) + + e.GET("/register", r.Register) + e.POST("/register", r.Register) + e.GET("/login", r.Login) + e.POST("/login", r.Login) + e.POST("/logout", r.Logout) + + e.GET("/", r.Node) + e.GET("/n/:id", r.Node) + e.GET("/n/:id/new", r.NewPost) + e.POST("/n/:id/new", r.NewPost) + + e.StaticFS("/assets", assets.FS) + + slog.InfoContext(ctx, "started gorum", slog.String("bind", listen)) + + server := http.Server{ + Addr: listen, + Handler: e, + ErrorLog: slog.NewLogLogger(slog.Default().Handler(), slog.LevelError), + } + if err := server.ListenAndServe(); err != http.ErrServerClosed { + return fmt.Errorf("server failed: %w", err) + } + + return nil +} |