diff options
author | Alexander NeonXP Kiryukhin <i@neonxp.ru> | 2024-07-29 02:47:35 +0300 |
---|---|---|
committer | Alexander NeonXP Kiryukhin <i@neonxp.ru> | 2024-07-29 02:47:35 +0300 |
commit | 96e2ce2e9d363a6296f9411ecb00168520258874 (patch) | |
tree | 09aa7fffe10eab84ae0edd39e570355984ba0148 | |
parent | 12ed72e4e1da181a6c87319a50d3b4142788b4c0 (diff) |
Отказ от echo
46 files changed, 1298 insertions, 1405 deletions
@@ -1,18 +1,39 @@ # syntax=docker/dockerfile:1 -FROM golang:1.22.5-alpine3.20 AS builder +FROM --platform=$BUILDPLATFORM golang:1.22.5-alpine3.20 AS builder + +ARG TARGETARCH + +ARG TARGETOS + +ENV CGO_ENABLED 0 + +ENV GOOS linux + +RUN apk update --no-cache && apk add --no-cache tzdata ca-certificates WORKDIR /app COPY go.mod go.sum ./ + RUN go mod download COPY . . -RUN CGO_ENABLED=0 GOOS=linux go build -o /app/gorum +RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -ldflags="-s -w" -o /app/gorum + +FROM --platform=$BUILDPLATFORM scratch + +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ + +COPY --from=builder /usr/share/zoneinfo/Europe/Moscow /usr/share/zoneinfo/Europe/Moscow + +ENV TZ Europe/Moscow + +WORKDIR /app -FROM alpine:3.20 +COPY --from=builder /app/gorum /app/gorum -COPY --from=builder /app/gorum . +EXPOSE 8000 -ENTRYPOINT ["/gorum"]
\ No newline at end of file +ENTRYPOINT ["/app/gorum"]
\ No newline at end of file diff --git a/app/cmd/migrate.go b/app/cmd/migrate.go deleted file mode 100644 index 95a1dc3..0000000 --- a/app/cmd/migrate.go +++ /dev/null @@ -1,99 +0,0 @@ -package cmd - -import ( - "database/sql" - "fmt" - - "github.com/golang-migrate/migrate/v4" - "github.com/golang-migrate/migrate/v4/database/sqlite" - "github.com/golang-migrate/migrate/v4/source/iofs" - "github.com/spf13/cobra" - "gitrepo.ru/neonxp/gorum/migrations" -) - -var migrateCmd = &cobra.Command{ - Use: "migrate", - Short: "Migrate db", - Long: `Up and down migrations`, -} - -func init() { - migrateCmd.AddCommand( - &cobra.Command{ - Use: "up", - Short: "Migrate up", - Long: `Up migrations`, - RunE: up, - }, - &cobra.Command{ - Use: "down", - Short: "Migrate down", - Long: `Down migrations`, - RunE: down, - }, - &cobra.Command{ - Use: "drop", - Short: "Drop db", - Long: `Deletes everything in the database`, - RunE: drop, - }, - ) -} - -func up(_ *cobra.Command, _ []string) error { - m, err := initMigrate() - if err != nil { - return fmt.Errorf("open migration failed: %w", err) - } - defer m.Close() - if err := m.Up(); err != nil && err != migrate.ErrNoChange { - return fmt.Errorf("do migration failed: %w", err) - } - - return nil -} - -func down(_ *cobra.Command, _ []string) error { - m, err := initMigrate() - if err != nil { - return fmt.Errorf("open migration failed: %w", err) - } - defer m.Close() - if err := m.Down(); err != nil && err != migrate.ErrNoChange { - return fmt.Errorf("do migration failed: %w", err) - } - - return nil -} - -func drop(_ *cobra.Command, _ []string) error { - m, err := initMigrate() - if err != nil { - return fmt.Errorf("open migration failed: %w", err) - } - defer m.Close() - if err := m.Drop(); err != nil && err != migrate.ErrNoChange { - return fmt.Errorf("do migration failed: %w", err) - } - - return nil -} - -func initMigrate() (*migrate.Migrate, error) { - db, err := sql.Open("sqlite3", dbFile) - if err != nil { - return nil, fmt.Errorf("open db failed: %w", err) - } - defer db.Close() - - driver, err := sqlite.WithInstance(db, &sqlite.Config{}) - if err != nil { - return nil, fmt.Errorf("failed create migration driver: %w", err) - } - sourceDriver, err := iofs.New(migrations.FS, ".") - if err != nil { - return nil, fmt.Errorf("failed open migrations: %w", err) - } - - return migrate.NewWithInstance("fs", sourceDriver, "sqlite3", driver) -} diff --git a/app/cmd/root.go b/app/cmd/root.go index 5803166..7d1c731 100644 --- a/app/cmd/root.go +++ b/app/cmd/root.go @@ -25,7 +25,6 @@ func init() { viper.BindPFlag("db", serverCmd.Flags().Lookup("db")) rootCmd.AddCommand(serverCmd) - rootCmd.AddCommand(migrateCmd) rootCmd.AddCommand(userCmd) } diff --git a/app/cmd/serve.go b/app/cmd/serve.go index e2d2843..578de1e 100644 --- a/app/cmd/serve.go +++ b/app/cmd/serve.go @@ -7,21 +7,19 @@ import ( "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" "gitrepo.ru/neonxp/gorum/contextlib" - "gitrepo.ru/neonxp/gorum/db" - "gitrepo.ru/neonxp/gorum/middleware" + "gitrepo.ru/neonxp/gorum/controllers" + appmiddleware "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" + "go.etcd.io/bbolt" + "go.neonxp.ru/mux" + "go.neonxp.ru/mux/middleware" + "go.neonxp.ru/mux/middleware/session" ) var ( @@ -57,7 +55,7 @@ func serve(ctx context.Context) error { ) ctx = context.WithValue(ctx, contextlib.ThemeKey, theme) - orm, err := db.GetDB(dbFile) + orm, err := bbolt.Open(dbFile, 0600, nil) if err != nil { return err } @@ -65,59 +63,51 @@ func serve(ctx context.Context) error { defer orm.Close() userRepo := repository.NewUser(orm) - nodeRepo := repository.NewNode(orm) + nodeRepo := repository.NewPost(orm) + topicRepo := repository.NewTopic(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)) + if err := topicRepo.Init(); err != nil { + return err } - - sessionStore, err := sqlitestore.NewSqliteStoreFromConnection(orm.DB, "sessions", "", 0, []byte(sessionSecret)) - if err != nil { - return fmt.Errorf("failed init session store: %w", err) + if err := userRepo.Init(); err != nil { + return 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(), - ) + r := controllers.NewRouter(userRepo, nodeRepo, topicRepo) - 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 := http.NewServeMux() - e.GET("/", r.Node) - e.GET("/p/:id", r.Node) - e.GET("/p/:id/new", r.NewPost) - e.POST("/p/:id/new", r.NewPost) - e.GET("/t/:id", r.Node) - e.GET("/t/:id/new", r.NewTopic) - e.POST("/t/:id/new", r.NewTopic) + mux.DefaultErrorHandler = func(err error) mux.Renderer { + return views.ErrorPage(err) + } - e.StaticFS("/assets", assets.FS) + 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) + e.HandleFunc("GET /t/{id}", r.Topic) + e.HandleFunc("GET /t/new", r.NewTopic) + e.HandleFunc("POST /t/new", r.NewTopic) + e.HandleFunc("POST /p/new", r.NewPost) + + e.Handle("/assets/", http.StripPrefix("/assets", http.FileServerFS(assets.FS))) + + mh := mux.Use(e, + middleware.Logger(slog.Default()), + middleware.Recover(slog.Default()), + middleware.RequestID, + appmiddleware.UserMiddleware(), + session.Middleware(session.DefaultConfig, session.NewBoltStore(orm, []byte("sessions"))), + ) slog.InfoContext(ctx, "started gorum", slog.String("bind", listen)) server := http.Server{ Addr: listen, - Handler: e, + Handler: mh, ErrorLog: slog.NewLogLogger(slog.Default().Handler(), slog.LevelError), ConnContext: func(cctx context.Context, c net.Conn) context.Context { return ctx diff --git a/app/cmd/user.go b/app/cmd/user.go index 673c7b8..1f88c6f 100644 --- a/app/cmd/user.go +++ b/app/cmd/user.go @@ -6,9 +6,9 @@ import ( "os" "github.com/spf13/cobra" - "gitrepo.ru/neonxp/gorum/db" "gitrepo.ru/neonxp/gorum/models" "gitrepo.ru/neonxp/gorum/repository" + "go.etcd.io/bbolt" ) var userCmd = &cobra.Command{ @@ -21,7 +21,7 @@ var createUserCmd = &cobra.Command{ Args: cobra.ExactArgs(3), ArgAliases: []string{"username", "email", "role"}, RunE: func(cmd *cobra.Command, args []string) error { - orm, err := db.GetDB(dbFile) + orm, err := bbolt.Open(dbFile, 0600, nil) if err != nil { return fmt.Errorf("failed init db: %w", err) } @@ -40,12 +40,11 @@ var createUserCmd = &cobra.Command{ password, _ := reader.ReadString('\n') ur := repository.NewUser(orm) - id, err := ur.Create(cmd.Context(), email, password, username, iRole) - if err != nil { + if err := ur.Create(email, password, username, iRole); err != nil { return fmt.Errorf("failed create user: %w", err) } - fmt.Printf("Created user %s (id=%d, email=%s, role_id=%d)\n", username, id, email, iRole) + fmt.Printf("Created user %s (email=%s, role_id=%d)\n", username, email, iRole) return nil }, @@ -54,20 +53,20 @@ var createUserCmd = &cobra.Command{ var listUserCmd = &cobra.Command{ Use: "list", RunE: func(cmd *cobra.Command, args []string) error { - orm, err := db.GetDB(dbFile) + orm, err := bbolt.Open(dbFile, 0600, nil) if err != nil { return fmt.Errorf("failed init db: %w", err) } ur := repository.NewUser(orm) - users, err := ur.List(cmd.Context()) + users, err := ur.List() if err != nil { return err } - fmt.Printf("ID\tUsername\tEmail\tRole\n") + fmt.Printf("Username\tEmail\tRole\n") for _, u := range users { - fmt.Printf("%d\t%s\t%s\t%d\n", u.ID, u.Username, u.Email, u.Role) + fmt.Printf("%s\t%s\t%d\n", u.Username, u.Email, u.Role) } return nil diff --git a/build-docker.sh b/build-docker.sh new file mode 100755 index 0000000..dfcc96d --- /dev/null +++ b/build-docker.sh @@ -0,0 +1,8 @@ +#! /bin/sh +buildah manifest rm gorum-manifest || true +buildah manifest create gorum-manifest +for PLATFORM in linux/amd64 linux/arm64/v8 # linux/386 linux/arm/v5 linux/arm/v6 linux/arm/v7 linux/arm/v8 linux/s390x linux/ppc64le +do + buildah bud --manifest gorum-manifest --platform ${PLATFORM} +done +buildah manifest push --all --format v2s2 gorum-manifest docker://gitrepo.ru/neonxp/gorum:latest
\ No newline at end of file diff --git a/controllers/post.go b/controllers/post.go new file mode 100644 index 0000000..aee7c97 --- /dev/null +++ b/controllers/post.go @@ -0,0 +1,42 @@ +package controllers + +import ( + "errors" + "fmt" + "net/http" + + "gitrepo.ru/neonxp/gorum/contextlib" + "gitrepo.ru/neonxp/gorum/views" + "go.neonxp.ru/mux" +) + +func (rt *Router) NewPost(w http.ResponseWriter, r *http.Request) { + req := new(postRequest) + if err := mux.Bind(r, req); err != nil { + mux.Render(w, r, mux.DefaultErrorHandler(err)) + return + } + user := contextlib.GetUser(r.Context()) + if user == nil { + mux.Render(w, r, mux.DefaultErrorHandler(errors.New("403"))) + return + } + + topic, err := rt.topicRepo.Get(uint64(req.Parent)) + if err != nil { + mux.Render(w, r, mux.DefaultErrorHandler(err)) + return + } + + if r.Method == http.MethodPost { + postID, err := rt.postRepo.CreatePost(req.Text, user.Email, uint64(req.Parent)) + if err != nil { + mux.Render(w, r, mux.DefaultErrorHandler(err)) + return + } + mux.Redirect(w, 302, fmt.Sprintf("/t/%d#post%d", req.Parent, postID)) + return + } + + mux.Render(w, r, views.NewPost(topic)) +} diff --git a/routes/requests.go b/controllers/requests.go index a0bc13f..487b7cc 100644 --- a/routes/requests.go +++ b/controllers/requests.go @@ -1,11 +1,9 @@ -package routes - -import "gitrepo.ru/neonxp/gorum/models" +package controllers type loginRequest struct { Email string `form:"email"` Password string `form:"password"` - Remember string `form:"remember"` + Remember bool `form:"remember"` } type registerRequest struct { @@ -15,7 +13,13 @@ type registerRequest struct { Password2 string `form:"password2"` } -type nodeRequest struct { - Type models.NodeType `form:"type"` - Text string `form:"text"` +type topicRequest struct { + Parent int `form:"parent"` + Topic string `form:"topic"` + Text string `form:"text"` +} + +type postRequest struct { + Parent int `form:"parent"` + Text string `form:"text"` } diff --git a/controllers/routes.go b/controllers/routes.go new file mode 100644 index 0000000..6e2ab9b --- /dev/null +++ b/controllers/routes.go @@ -0,0 +1,19 @@ +package controllers + +import ( + "gitrepo.ru/neonxp/gorum/repository" +) + +type Router struct { + userRepo *repository.User + postRepo *repository.Post + topicRepo *repository.Topic +} + +func NewRouter(userRepo *repository.User, nodeRepo *repository.Post, topicRepo *repository.Topic) *Router { + return &Router{ + userRepo: userRepo, + postRepo: nodeRepo, + topicRepo: topicRepo, + } +} diff --git a/controllers/topic.go b/controllers/topic.go new file mode 100644 index 0000000..50f65e7 --- /dev/null +++ b/controllers/topic.go @@ -0,0 +1,103 @@ +package controllers + +import ( + "errors" + "fmt" + "net/http" + "strconv" + + "gitrepo.ru/neonxp/gorum/contextlib" + "gitrepo.ru/neonxp/gorum/models" + "gitrepo.ru/neonxp/gorum/views" + "go.neonxp.ru/mux" +) + +func (rt *Router) Index(w http.ResponseWriter, r *http.Request) { + topics, err := rt.topicRepo.List(0) + if err != nil { + mux.Render(w, r, mux.DefaultErrorHandler(err)) + return + } + + mux.Render(w, r, views.Topics(topics)) +} + +func (rt *Router) Topic(w http.ResponseWriter, r *http.Request) { + sParentID := r.PathValue("id") + parentID, err := strconv.Atoi(sParentID) + if err != nil { + mux.Render(w, r, mux.DefaultErrorHandler(err)) + return + } + topic, err := rt.topicRepo.Get(uint64(parentID)) + if err != nil { + mux.Render(w, r, mux.DefaultErrorHandler(err)) + return + } + + topics, err := rt.topicRepo.List(uint64(parentID)) + if err != nil { + mux.Render(w, r, mux.DefaultErrorHandler(err)) + return + } + posts, err := rt.postRepo.List(uint64(parentID)) + if err != nil { + mux.Render(w, r, mux.DefaultErrorHandler(err)) + return + } + emailToUser := map[string]*models.User{} + for _, p := range posts { + u, ok := emailToUser[p.AuthorID] + if !ok { + u, err = rt.userRepo.Get(p.AuthorID) + if err != nil { + u = &models.User{Username: "user-not-found"} + } + } + emailToUser[p.AuthorID] = u + p.Author = u + } + + mux.Render(w, r, views.Posts(topic, topics, posts)) +} + +func (rt *Router) NewTopic(w http.ResponseWriter, r *http.Request) { + req := new(topicRequest) + if err := mux.Bind(r, req); err != nil { + mux.Render(w, r, mux.DefaultErrorHandler(err)) + return + } + user := contextlib.GetUser(r.Context()) + if user == nil { + mux.Render(w, r, mux.DefaultErrorHandler(errors.New("403"))) + return + } + + if r.Method == http.MethodPost { + topic, err := rt.topicRepo.Create(req.Topic, req.Text, user.Email, uint64(req.Parent)) + if err != nil { + mux.Render(w, r, mux.DefaultErrorHandler(err)) + return + } + mux.Redirect(w, 302, fmt.Sprintf("/t/%d", topic.ID)) + return + } + + topic := &models.Topic{ + ID: 0, + Topic: "Корень", + } + + sTopicID := r.PathValue("topic") + if sTopicID != "" { + if topicID, err := strconv.Atoi(sTopicID); err == nil { + topic, err = rt.topicRepo.Get(uint64(topicID)) + if err != nil { + mux.Render(w, r, mux.DefaultErrorHandler(err)) + return + } + } + } + + mux.Render(w, r, views.NewTopic(topic)) +} diff --git a/controllers/user.go b/controllers/user.go new file mode 100644 index 0000000..a605c25 --- /dev/null +++ b/controllers/user.go @@ -0,0 +1,59 @@ +package controllers + +import ( + "net/http" + + "gitrepo.ru/neonxp/gorum/models" + "gitrepo.ru/neonxp/gorum/views" + "go.neonxp.ru/mux" + "go.neonxp.ru/mux/middleware/session" +) + +func (rt *Router) Login(w http.ResponseWriter, r *http.Request) { + req := new(loginRequest) + + if r.Method == http.MethodPost { + if err := mux.Bind(r, req); err != nil { + mux.Render(w, r, mux.DefaultErrorHandler(err)) + return + } + u, err := rt.userRepo.Login(req.Email, req.Password) + if err != nil { + mux.Render(w, r, mux.DefaultErrorHandler(err)) + return + } + sess := session.FromRequest(r) + + (*sess)["user"] = u + + mux.Redirect(w, 302, "/") + return + } + + mux.Render(w, r, views.Login(req.Email)) +} + +func (rt *Router) Logout(w http.ResponseWriter, r *http.Request) { + session.Clear(w, r) + + mux.Redirect(w, 302, "/") +} + +func (rt *Router) Register(w http.ResponseWriter, r *http.Request) { + req := new(registerRequest) + + if err := mux.Bind(r, req); err != nil { + mux.Render(w, r, mux.DefaultErrorHandler(err)) + return + } + if r.Method == http.MethodPost { + if err := rt.userRepo.Create(req.Email, req.Password, req.Username, models.RoleUser); err != nil { + mux.Render(w, r, mux.DefaultErrorHandler(err)) + return + } + mux.Redirect(w, 302, "/login") + return + } + + mux.Render(w, r, views.Register(req.Username, req.Email)) +} @@ -4,6 +4,8 @@ go 1.22.5 require ( github.com/a-h/templ v0.2.747 + github.com/go-session/echo-session v3.0.0+incompatible + github.com/go-session/session v2.4.0+incompatible github.com/gomarkdown/markdown v0.0.0-20240626202925-2eda941fd024 github.com/gorilla/sessions v1.3.0 github.com/labstack/echo-contrib v0.17.1 @@ -11,63 +13,63 @@ require ( ) require ( + github.com/bytedance/gopkg v0.0.0-20221122125632-68358b8ecec6 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/google/uuid v1.4.0 // indirect github.com/gorilla/context v1.1.2 // indirect github.com/gorilla/securecookie v1.1.2 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect - github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/labstack/echo v3.3.10+incompatible // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect - github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/satori/go.uuid v1.2.0 // indirect + github.com/smartystreets/goconvey v1.8.1 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.6.0 // indirect + github.com/tidwall/btree v1.4.2 // indirect + github.com/tidwall/buntdb v1.3.1 // indirect + github.com/tidwall/gjson v1.14.3 // indirect + github.com/tidwall/grect v0.1.4 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + github.com/tidwall/rtred v0.1.2 // indirect + github.com/tidwall/tinyqueue v0.1.1 // indirect github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - go.uber.org/atomic v1.11.0 // indirect + go.etcd.io/bbolt v1.3.10 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/tools v0.13.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - lukechampine.com/uint128 v1.2.0 // indirect - modernc.org/cc/v3 v3.36.3 // indirect - modernc.org/ccgo/v3 v3.16.9 // indirect - modernc.org/libc v1.17.1 // indirect - modernc.org/mathutil v1.5.0 // indirect - modernc.org/memory v1.2.1 // indirect - modernc.org/opt v0.1.3 // indirect - modernc.org/sqlite v1.18.1 // indirect - modernc.org/strutil v1.1.3 // indirect - modernc.org/token v1.0.0 // indirect ) require ( + github.com/dimuska139/go-email-normalizer/v2 v2.0.1 + github.com/go-session/session/v3 v3.2.1 github.com/golang-jwt/jwt v3.2.2+incompatible // indirect - github.com/golang-migrate/migrate/v4 v4.17.1 github.com/labstack/gommon v0.4.2 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-sqlite3 v1.14.22 - github.com/michaeljs1990/sqlitestore v0.0.0-20210507162135-8585425bc864 github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 github.com/uptrace/bun v1.2.1 github.com/uptrace/bun/dialect/sqlitedialect v1.2.1 github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect + github.com/wagslane/go-password-validator v0.3.0 + go.neonxp.ru/objectid v0.0.2 golang.org/x/crypto v0.22.0 golang.org/x/net v0.24.0 // indirect golang.org/x/sys v0.21.0 // indirect @@ -1,75 +1,79 @@ github.com/a-h/templ v0.2.747 h1:D0dQ2lxC3W7Dxl6fxQ/1zZHBQslSkTSvl5FxP/CfdKg= github.com/a-h/templ v0.2.747/go.mod h1:69ObQIbrcuwPCU32ohNaWce3Cb7qM5GMiqN1K+2yop4= +github.com/bytedance/gopkg v0.0.0-20221122125632-68358b8ecec6 h1:FCLDGi1EmB7JzjVVYNZiqc/zAJj2BQ5M0lfkVOxbfs8= +github.com/bytedance/gopkg v0.0.0-20221122125632-68358b8ecec6/go.mod h1:5FoAH5xUHHCMDvQPy1rnj8moqLkLHFaDVBjHhcFwEi0= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dimuska139/go-email-normalizer/v2 v2.0.1 h1:axEFQ9XgXQjZKUwMT52WhJEbCuZXcTAPBqbgW+EPQJw= +github.com/dimuska139/go-email-normalizer/v2 v2.0.1/go.mod h1:2Gil1j/rfUKJ5BHc/uxxyRiuk3YTg6/C3D7dz7jVQfw= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/go-session/echo-session v3.0.0+incompatible h1:yJejVUrmpOF+sZSRfhO2fC/5TNCa3BkgQIVhMykwY44= +github.com/go-session/echo-session v3.0.0+incompatible/go.mod h1:bUiKI2tqw16QA15uld4e6WV8BnPYROuzqm3+fECvrII= +github.com/go-session/session v2.4.0+incompatible h1:lSgyN6s0GJO1P5/EIad5N2jwI1dszDT8aES24KHl2vM= +github.com/go-session/session v2.4.0+incompatible/go.mod h1:8B3iivBQjrz/JtC68Np2T1yBBLxTan3mn/3OM0CyRt0= +github.com/go-session/session/v3 v3.2.1 h1:APQf5JFW84+bhbqRjEZO8J+IppSgT1jMQTFI/XVyIFY= +github.com/go-session/session/v3 v3.2.1/go.mod h1:RftEBbyuzqkNCAxIrCLJe+rfBqB/4G11qxq9KYKrx4M= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-migrate/migrate/v4 v4.17.1 h1:4zQ6iqL6t6AiItphxJctQb3cFqWiSpMnX7wLTPnnYO4= -github.com/golang-migrate/migrate/v4 v4.17.1/go.mod h1:m8hinFyWBn0SA4QKHuKh175Pm9wjmxj3S2Mia7dbXzM= github.com/gomarkdown/markdown v0.0.0-20240626202925-2eda941fd024 h1:saBP362Qm7zDdDXqv61kI4rzhmLFq3Z1gx34xpl6cWE= github.com/gomarkdown/markdown v0.0.0-20240626202925-2eda941fd024/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= +github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o= github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM= github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/gorilla/sessions v1.3.0 h1:XYlkq7KcpOB2ZhHBPv5WpjMIxrQosiZanfoy1HLZFzg= github.com/gorilla/sessions v1.3.0/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg= +github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= github.com/labstack/echo-contrib v0.17.1 h1:7I/he7ylVKsDUieaGRZ9XxxTYOjfQwVzHzUYrNykfCU= github.com/labstack/echo-contrib v0.17.1/go.mod h1:SnsCZtwHBAZm5uBSAtQtXQHI3wqEA73hvTn0bYMKnZA= github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= -github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/michaeljs1990/sqlitestore v0.0.0-20210507162135-8585425bc864 h1:NkqeBeGMAmwEr0CibX80gHlrX7hSQSmdKpTaPex5n9c= -github.com/michaeljs1990/sqlitestore v0.0.0-20210507162135-8585425bc864/go.mod h1:N6aiMetO+sSN0h4VC8RjkwiljKaZmgPsWzZG+mk6oec= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= @@ -77,8 +81,6 @@ github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -86,6 +88,12 @@ github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6ke github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY= +github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= +github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY= +github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= @@ -102,6 +110,9 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= @@ -109,6 +120,27 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/tidwall/assert v0.1.0 h1:aWcKyRBUAdLoVebxo95N7+YZVTFF/ASTr7BN4sLP6XI= +github.com/tidwall/assert v0.1.0/go.mod h1:QLYtGyeqse53vuELQheYl9dngGCJQ+mTtlxcktb+Kj8= +github.com/tidwall/btree v1.4.2 h1:PpkaieETJMUxYNADsjgtNRcERX7mGc/GP2zp/r5FM3g= +github.com/tidwall/btree v1.4.2/go.mod h1:LGm8L/DZjPLmeWGjv5kFrY8dL4uVhMmzmmLYmsObdKE= +github.com/tidwall/buntdb v1.3.1 h1:HKoDF01/aBhl9RjYtbaLnvX9/OuenwvQiC3OP1CcL4o= +github.com/tidwall/buntdb v1.3.1/go.mod h1:lZZrZUWzlyDJKlLQ6DKAy53LnG7m5kHyrEHvvcDmBpU= +github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw= +github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/grect v0.1.4 h1:dA3oIgNgWdSspFzn1kS4S/RDpZFLrIxAZOdJKjYapOg= +github.com/tidwall/grect v0.1.4/go.mod h1:9FBsaYRaR0Tcy4UwefBX/UDcDcDy9V5jUcxHzv2jd5Q= +github.com/tidwall/lotsa v1.0.2 h1:dNVBH5MErdaQ/xd9s769R31/n2dXavsQ0Yf4TMEHHw8= +github.com/tidwall/lotsa v1.0.2/go.mod h1:X6NiU+4yHA3fE3Puvpnn1XMDrFZrE9JO2/w+UMuqgR8= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/rtred v0.1.2 h1:exmoQtOLvDoO8ud++6LwVsAMTu0KPzLTUrMln8u1yu8= +github.com/tidwall/rtred v0.1.2/go.mod h1:hd69WNXQ5RP9vHd7dqekAz+RIdtfBogmglkZSRxCHFQ= +github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaymYE= +github.com/tidwall/tinyqueue v0.1.1/go.mod h1:O/QNHwrnjqr6IHItYrzoHAKYhBkLI67Q096fQP5zMYw= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= github.com/uptrace/bun v1.2.1 h1:2ENAcfeCfaY5+2e7z5pXrzFKy3vS8VXvkCag6N2Yzfk= @@ -123,54 +155,30 @@ github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IU github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= -go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +github.com/wagslane/go-password-validator v0.3.0 h1:vfxOPzGHkz5S146HDpavl0cw1DSVP061Ry2PX0/ON6I= +github.com/wagslane/go-password-validator v0.3.0/go.mod h1:TI1XJ6T5fRdRnHqHt14pvy1tNVnrwe7m3/f1f2fDphQ= +go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0= +go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ= +go.neonxp.ru/objectid v0.0.2 h1:Z/G6zvBxmUq0NTq681oGH8pTbBWwi6VA22YOYludIPs= +go.neonxp.ru/objectid v0.0.2/go.mod h1:s0dRi//oe1liiKcor1KmWx09WzkD6Wtww8ZaIv+VLBs= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -179,39 +187,3 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= -lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.3 h1:uISP3F66UlixxWEcKuIWERa4TwrZENHSL8tWxZz8bHg= -modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/ccgo/v3 v3.16.9 h1:AXquSwg7GuMk11pIdw7fmO1Y/ybgazVkMhsZWCV0mHM= -modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= -modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= -modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= -modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= -modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= -modernc.org/libc v1.17.1 h1:Q8/Cpi36V/QBfuQaFVeisEBs3WqoGAJprZzmf7TfEYI= -modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= -modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= -modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.1 h1:dkRh86wgmq/bJu2cAS2oqBCz/KsMZU7TUM4CibQ7eBs= -modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= -modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.18.1 h1:ko32eKt3jf7eqIkCgPAeHMBXw3riNSLhl2f3loEF7o8= -modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= -modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= -modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= -modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= -modernc.org/tcl v1.13.1 h1:npxzTwFTZYM8ghWicVIX1cRWzj7Nd8i6AqqX2p+IYao= -modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= -modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk= -modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.5.1 h1:RTNHdsrOpeoSeOF4FbzTo8gBYByaJ5xT7NgZ9ZqRiJM= -modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= diff --git a/middleware/session/store.go b/middleware/session/store.go deleted file mode 100644 index f22c64d..0000000 --- a/middleware/session/store.go +++ /dev/null @@ -1,30 +0,0 @@ -package session - -import ( - "net/http" - - "github.com/gorilla/sessions" - "github.com/uptrace/bun" -) - -type SessionStore struct { - orm *bun.DB -} - -// Get should return a cached session. -func (s *SessionStore) Get(r *http.Request, name string) (*sessions.Session, error) { - panic("not implemented") // TODO: Implement -} - -// New should create and return a new session. -// -// Note that New should never return a nil session, even in the case of -// an error if using the Registry infrastructure to cache the session. -func (s *SessionStore) New(r *http.Request, name string) (*sessions.Session, error) { - panic("not implemented") // TODO: Implement -} - -// Save should persist session to the underlying store implementation. -func (s *SessionStore) Save(r *http.Request, w http.ResponseWriter, ss *sessions.Session) error { - panic("not implemented") // TODO: Implement -} diff --git a/middleware/user.go b/middleware/user.go index eaca6e9..f4eb12a 100644 --- a/middleware/user.go +++ b/middleware/user.go @@ -2,33 +2,25 @@ package middleware import ( "context" + "net/http" - "github.com/labstack/echo-contrib/session" - "github.com/labstack/echo/v4" "gitrepo.ru/neonxp/gorum/contextlib" - "gitrepo.ru/neonxp/gorum/models" + "go.neonxp.ru/mux" + "go.neonxp.ru/mux/middleware/session" ) -func UserMiddleware() echo.MiddlewareFunc { - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - sess, err := session.Get("session", c) - if err != nil { - return err +func UserMiddleware() mux.Middleware { + return func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + session := session.FromRequest(r) + user := (*session)["user"] + if user == nil { + h.ServeHTTP(w, r) + return } - u, okSess := sess.Values["user"] - if !okSess { - return next(c) - } - user, okUser := u.(models.User) - if !okUser { - return next(c) - } - ctx := context.WithValue(c.Request().Context(), contextlib.UserKey, user) - req := c.Request().WithContext(ctx) - c.SetRequest(req) - - return next(c) - } + h.ServeHTTP(w, r.WithContext( + context.WithValue(r.Context(), contextlib.UserKey, user), + )) + }) } } diff --git a/migrations/1_schema.down.sql b/migrations/1_schema.down.sql deleted file mode 100644 index c0a3f9d..0000000 --- a/migrations/1_schema.down.sql +++ /dev/null @@ -1,2 +0,0 @@ -DROP TABLE "nodes"; --- DROP TABLE "users"; diff --git a/migrations/1_schema.up.sql b/migrations/1_schema.up.sql deleted file mode 100644 index 2f8bc04..0000000 --- a/migrations/1_schema.up.sql +++ /dev/null @@ -1,27 +0,0 @@ -CREATE TABLE IF NOT EXISTS "users" ( - "id" INTEGER, - "email" TEXT NOT NULL, - "password" TEXT NOT NULL, - "username" TEXT NOT NULL, - "photo" TEXT, - "role" INTEGER DEFAULT(0), - PRIMARY KEY ("id" AUTOINCREMENT), - UNIQUE ("email" COLLATE NOCASE), - UNIQUE ("username" COLLATE NOCASE) -); - -CREATE TABLE IF NOT EXISTS "nodes" ( - "id" INTEGER, - "type" INTEGER, - "text" TEXT, - "author_id" INTEGER NOT NULL, - "parent_id" INTEGER, - "created_at" INTEGER NOT NULL, - "updated_at" INTEGER NOT NULL, - "deleted_at" INTEGER, - "permission" INTEGER, - PRIMARY KEY ("id" AUTOINCREMENT), - FOREIGN KEY ("author_id") REFERENCES "users" ("id"), - FOREIGN KEY ("parent_id") REFERENCES "posts" ("id") -); - diff --git a/migrations/fs.go b/migrations/fs.go deleted file mode 100644 index 91cca1c..0000000 --- a/migrations/fs.go +++ /dev/null @@ -1,6 +0,0 @@ -package migrations - -import "embed" - -//go:embed *.sql -var FS embed.FS diff --git a/models/errors.go b/models/errors.go index 660fe85..9b514e9 100644 --- a/models/errors.go +++ b/models/errors.go @@ -6,4 +6,8 @@ var ( ErrInvalidUserOrPassword = errors.New("invalid user or password") ErrUserAlreadyExists = errors.New("user already exists") ErrInvalidPassword = errors.New("invalid password") + ErrTopicNotFound = errors.New("topic not found") + ErrPostNotFound = errors.New("post not found") + ErrUserNotFound = errors.New("user not found") + ErrDBNotInitialized = errors.New("db is not initialized") ) diff --git a/models/node.go b/models/node.go index 98a8d4e..7d3f719 100644 --- a/models/node.go +++ b/models/node.go @@ -1,49 +1,36 @@ package models import ( - "context" "time" - - "github.com/uptrace/bun" ) -type Node struct { - bun.BaseModel `bun:"table:nodes,alias:n"` - - ID int `bun:"id,pk,autoincrement"` - Type NodeType - Text string - AuthorID int - Author *User `bun:"rel:belongs-to,join:author_id=id"` - ParentID int - Parent *Node `bun:"rel:belongs-to,join:parent_id=id"` - Permission int - CreatedAt int64 `bun:",nullzero,notnull,default:current_timestamp"` - UpdatedAt int64 `bun:",nullzero,notnull,default:current_timestamp"` - DeletedAt int64 - Children []*Node `bun:"rel:has-many,join:id=parent_id"` +type Topic struct { + ID uint64 `json:"id"` + Topic string `json:"topic"` + Text string `json:"text"` + AuthorID string `json:"author_id"` + Author *User `json:"-"` + ParentID uint64 `json:"parent_id"` + Parent *Topic `json:"-"` + Permission int `json:"permission"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + DeletedAt *time.Time `json:"deleted_at"` } -var _ bun.BeforeAppendModelHook = (*Node)(nil) - -func (m *Node) BeforeAppendModel(ctx context.Context, query bun.Query) error { - switch query.(type) { - case *bun.InsertQuery: - m.CreatedAt = time.Now().Unix() - m.UpdatedAt = time.Now().Unix() - case *bun.UpdateQuery: - m.UpdatedAt = time.Now().Unix() - } - return nil +type Post struct { + ID uint64 `json:"id"` + Text string `json:"text"` + AuthorID string `json:"author_id"` + Author *User `json:"-"` + ParentID uint64 `json:"parent_id"` + Parent *Post `json:"-"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + DeletedAt *time.Time `json:"deleted_at"` + Children []*Post `json:"-"` } -type NodeType int - -const ( - TopicType NodeType = iota - PostType -) - type Permission int const ( diff --git a/models/user.go b/models/user.go index 73358fe..e8ae3cc 100644 --- a/models/user.go +++ b/models/user.go @@ -2,8 +2,6 @@ package models import ( "encoding/gob" - - "github.com/uptrace/bun" ) func init() { @@ -11,14 +9,11 @@ func init() { } type User struct { - bun.BaseModel `bun:"table:users,alias:u"` - - ID int `bun:"id,pk,autoincrement"` - Email string - Password string - Username string - Photo *string - Role UserRole + Email string `json:"email,omitempty"` + Password string `json:"password,omitempty"` + Username string `json:"username,omitempty"` + Photo *string `json:"photo,omitempty"` + Role UserRole `json:"role,omitempty"` } type UserRole int diff --git a/repository/node.go b/repository/node.go deleted file mode 100644 index 27282af..0000000 --- a/repository/node.go +++ /dev/null @@ -1,58 +0,0 @@ -package repository - -import ( - "context" - - "github.com/uptrace/bun" - "gitrepo.ru/neonxp/gorum/models" -) - -type Node struct { - db *bun.DB -} - -func NewNode(db *bun.DB) *Node { - return &Node{ - db: db, - } -} - -func (t *Node) Create( - ctx context.Context, - ntype models.NodeType, - text string, - authorID int, - parentID int, -) (int, error) { - post := &models.Node{ - Type: ntype, - Text: text, - AuthorID: authorID, - ParentID: parentID, - } - _, err := t.db.NewInsert().Model(post).Returning("id").Exec(ctx) - - return post.ID, err -} - -func (t *Node) Get(ctx context.Context, topicID int) (*models.Node, error) { - node := new(models.Node) - - return node, t.db.NewSelect(). - Model(node). - Where(`n.id = ?`, topicID). - Relation("Author"). - Relation("Parent"). - Scan(ctx) -} - -func (t *Node) List(ctx context.Context, topicID int) ([]*models.Node, error) { - posts := make([]*models.Node, 0) - - return posts, t.db.NewSelect(). - Model(&posts). - Where(`parent_id = ?`, topicID). - Relation("Author"). - Relation("Children"). - Scan(ctx) -} diff --git a/repository/post.go b/repository/post.go new file mode 100644 index 0000000..1b0fef6 --- /dev/null +++ b/repository/post.go @@ -0,0 +1,70 @@ +package repository + +import ( + "encoding/json" + "fmt" + "strconv" + "time" + + "gitrepo.ru/neonxp/gorum/models" + "go.etcd.io/bbolt" +) + +type Post struct { + db *bbolt.DB +} + +func NewPost(db *bbolt.DB) *Post { + return &Post{ + db: db, + } +} + +func (t *Post) CreatePost( + text string, + authorID string, + topicID uint64, +) (uint64, error) { + post := &models.Post{ + Text: text, + AuthorID: authorID, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + } + + return post.ID, t.db.Update(func(tx *bbolt.Tx) error { + bucket, err := tx.CreateBucketIfNotExists([]byte(fmt.Sprintf("posts_%d", topicID))) + if err != nil { + return err + } + id, err := bucket.NextSequence() + if err != nil { + return err + } + post.ID = id + pb, err := json.Marshal(post) + if err != nil { + return err + } + + return bucket.Put([]byte(strconv.Itoa(int(id))), pb) + }) +} + +func (t *Post) List(topicID uint64) ([]*models.Post, error) { + posts := make([]*models.Post, 0) + + return posts, t.db.View(func(tx *bbolt.Tx) error { + return tx.Bucket([]byte(fmt.Sprintf("posts_%d", topicID))).ForEach(func(k, v []byte) error { + post := new(models.Post) + + if err := json.Unmarshal(v, post); err != nil { + return err + } + + posts = append(posts, post) + + return nil + }) + }) +} diff --git a/repository/topic.go b/repository/topic.go new file mode 100644 index 0000000..4c34643 --- /dev/null +++ b/repository/topic.go @@ -0,0 +1,101 @@ +package repository + +import ( + "encoding/json" + "fmt" + "strconv" + "time" + + "gitrepo.ru/neonxp/gorum/models" + "go.etcd.io/bbolt" +) + +type Topic struct { + db *bbolt.DB +} + +func NewTopic(db *bbolt.DB) *Topic { + return &Topic{ + db: db, + } +} + +func (t *Topic) Init() error { + return t.db.Update(func(tx *bbolt.Tx) error { + _, err := tx.CreateBucketIfNotExists([]byte("topics")) + return err + }) +} + +func (t *Topic) Create(title, text, authorID string, parentID uint64) (*models.Topic, error) { + topic := &models.Topic{ + Topic: title, + Text: text, + AuthorID: authorID, + ParentID: parentID, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + } + + return topic, t.db.Update(func(tx *bbolt.Tx) error { + bucket, err := tx.CreateBucketIfNotExists([]byte("topics")) + if err != nil { + return err + } + id, err := bucket.NextSequence() + if err != nil { + return err + } + topic.ID = id + tb, err := json.Marshal(topic) + if err != nil { + return err + } + + if _, err := tx.CreateBucketIfNotExists([]byte(fmt.Sprintf("posts_%d", topic.ID))); err != nil { + return err + } + + return bucket.Put([]byte(strconv.Itoa(int(topic.ID))), tb) + }) +} + +func (t *Topic) List(parentID uint64) ([]*models.Topic, error) { + topics := make([]*models.Topic, 0) + + return topics, t.db.View(func(tx *bbolt.Tx) error { + bucket := tx.Bucket([]byte("topics")) + if bucket == nil { + return models.ErrDBNotInitialized + } + + return bucket.ForEach(func(k, v []byte) error { + t := new(models.Topic) + if err := json.Unmarshal(v, t); err != nil { + return err + } + if t.ParentID == parentID { + topics = append(topics, t) + } + + return nil + }) + }) +} + +func (t *Topic) Get(topicID uint64) (*models.Topic, error) { + topic := new(models.Topic) + + return topic, t.db.View(func(tx *bbolt.Tx) error { + bucket := tx.Bucket([]byte("topics")) + if bucket == nil { + return models.ErrDBNotInitialized + } + tb := bucket.Get([]byte(strconv.Itoa(int(topicID)))) + if tb == nil { + return models.ErrTopicNotFound + } + + return json.Unmarshal(tb, topic) + }) +} diff --git a/repository/user.go b/repository/user.go index 5c3cce6..b01eaee 100644 --- a/repository/user.go +++ b/repository/user.go @@ -1,31 +1,44 @@ package repository import ( - "context" + "encoding/json" "fmt" - "github.com/uptrace/bun" + normalizer "github.com/dimuska139/go-email-normalizer/v2" "gitrepo.ru/neonxp/gorum/models" + "go.etcd.io/bbolt" "golang.org/x/crypto/bcrypt" ) type User struct { - db *bun.DB + db *bbolt.DB } -func NewUser(db *bun.DB) *User { +func NewUser(db *bbolt.DB) *User { return &User{ db: db, } } -func (u *User) Create(ctx context.Context, email, password, username string, role models.UserRole) (int, error) { +func (t *User) Init() error { + return t.db.Update(func(tx *bbolt.Tx) error { + _, err := tx.CreateBucketIfNotExists([]byte("users")) + return err + }) +} +func (u *User) Create(email, password, username string, role models.UserRole) error { + + if len(password) < 8 { + return models.ErrInvalidPassword + } hpassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { - return 0, models.ErrInvalidPassword + return models.ErrInvalidPassword } + email = normalizer.NewNormalizer().Normalize(email) + user := &models.User{ Email: email, Password: string(hpassword), @@ -33,29 +46,74 @@ func (u *User) Create(ctx context.Context, email, password, username string, rol Role: role, } - if _, err := u.db.NewInsert().Model(user).Returning("id").Exec(ctx); err != nil { - return 0, models.ErrUserAlreadyExists - } + u.db.Update(func(tx *bbolt.Tx) error { + bucket, err := tx.CreateBucketIfNotExists([]byte("users")) + if err != nil { + return err + } + ub := bucket.Get([]byte(email)) + if ub != nil { + return models.ErrUserAlreadyExists + } + + ub, err = json.Marshal(user) + if err != nil { + return err + } + + return bucket.Put([]byte(email), ub) + }) - return user.ID, nil + return nil } -func (u *User) Login(ctx context.Context, email, password string) (*models.User, error) { +func (u *User) Login(email, password string) (*models.User, error) { user := new(models.User) + email = normalizer.NewNormalizer().Normalize(email) - if err := u.db.NewSelect().Model(user).Where("email = ?", email).Scan(ctx); err != nil { - return nil, fmt.Errorf("user not found: %w", models.ErrInvalidUserOrPassword) - } + return user, u.db.View(func(tx *bbolt.Tx) error { + ub := tx.Bucket([]byte("users")).Get([]byte(email)) + if ub == nil { + return models.ErrInvalidUserOrPassword + } + if err := json.Unmarshal(ub, user); err != nil { + return err + } - if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil { - return nil, fmt.Errorf("invalid password: %w", models.ErrInvalidUserOrPassword) - } + if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil { + return fmt.Errorf("invalid password: %w", models.ErrInvalidUserOrPassword) + } + + return nil + }) +} + +func (u *User) Get(email string) (*models.User, error) { + user := new(models.User) + email = normalizer.NewNormalizer().Normalize(email) + + return user, u.db.View(func(tx *bbolt.Tx) error { + ub := tx.Bucket([]byte("users")).Get([]byte(email)) + if ub == nil { + return models.ErrInvalidUserOrPassword + } - return user, nil + return json.Unmarshal(ub, user) + }) } -func (u *User) List(ctx context.Context) ([]*models.User, error) { +func (u *User) List() ([]*models.User, error) { ret := make([]*models.User, 0) - return ret, u.db.NewSelect().Model(&ret).Scan(ctx) + return ret, u.db.View(func(tx *bbolt.Tx) error { + bucket := tx.Bucket([]byte("users")) + return bucket.ForEach(func(k, v []byte) error { + u := new(models.User) + if err := json.Unmarshal(v, u); err != nil { + return err + } + ret = append(ret, u) + return nil + }) + }) } diff --git a/routes/node.go b/routes/node.go deleted file mode 100644 index e8aa455..0000000 --- a/routes/node.go +++ /dev/null @@ -1,129 +0,0 @@ -package routes - -import ( - "fmt" - "net/http" - "strconv" - - "github.com/labstack/echo/v4" - "gitrepo.ru/neonxp/gorum/contextlib" - "gitrepo.ru/neonxp/gorum/models" - "gitrepo.ru/neonxp/gorum/utils" - "gitrepo.ru/neonxp/gorum/views" -) - -func (r *Router) Node(c echo.Context) error { - sParentID := c.Param("id") - parentID := 0 - var err error - if sParentID != "" { - parentID, err = strconv.Atoi(sParentID) - if err != nil { - return err - } - } - - node := &models.Node{ - ID: 0, - Text: "Gorum", - } - if parentID > 0 { - node, err = r.nodeRepo.Get(c.Request().Context(), parentID) - if err != nil { - return err - } - } - - nodes, err := r.nodeRepo.List(c.Request().Context(), parentID) - if err != nil { - return err - } - - topics := make([]*models.Node, 0, len(nodes)) - posts := make([]*models.Node, 0, len(nodes)) - for _, n := range nodes { - switch n.Type { - case models.PostType: - posts = append(posts, n) - case models.TopicType: - topics = append(topics, n) - } - } - - return utils.Render(c, views.Node(node, topics, posts)) -} - -func (r *Router) NewPost(c echo.Context) error { - req := new(nodeRequest) - if err := c.Bind(req); err != nil { - return err - } - user := contextlib.GetUser(c.Request().Context()) - if user == nil { - return echo.ErrForbidden - } - sParentID := c.Param("id") - parentID, err := strconv.Atoi(sParentID) - if err != nil { - return err - } - - if c.Request().Method == http.MethodPost { - postID, err := r.nodeRepo.Create(c.Request().Context(), req.Type, req.Text, user.ID, parentID) - if err != nil { - return err - } - - return c.Redirect(302, fmt.Sprintf("/t/%d#post%d", parentID, postID)) - } - - node := &models.Node{ - ID: 0, - Text: "Gorum", - } - if parentID > 0 { - node, err = r.nodeRepo.Get(c.Request().Context(), parentID) - if err != nil { - return err - } - } - - return utils.Render(c, views.NewPost(node)) -} -func (r *Router) NewTopic(c echo.Context) error { - req := new(nodeRequest) - if err := c.Bind(req); err != nil { - return err - } - user := contextlib.GetUser(c.Request().Context()) - if user == nil { - return echo.ErrForbidden - } - sParentID := c.Param("id") - parentID, err := strconv.Atoi(sParentID) - if err != nil { - return err - } - - if c.Request().Method == http.MethodPost { - postID, err := r.nodeRepo.Create(c.Request().Context(), req.Type, req.Text, user.ID, parentID) - if err != nil { - return err - } - - return c.Redirect(302, fmt.Sprintf("/t/%d", postID)) - } - - node := &models.Node{ - ID: 0, - Text: "Gorum", - } - if parentID > 0 { - node, err = r.nodeRepo.Get(c.Request().Context(), parentID) - if err != nil { - return err - } - } - - return utils.Render(c, views.NewTopic(node)) -} diff --git a/routes/routes.go b/routes/routes.go deleted file mode 100644 index 835991a..0000000 --- a/routes/routes.go +++ /dev/null @@ -1,17 +0,0 @@ -package routes - -import ( - "gitrepo.ru/neonxp/gorum/repository" -) - -type Router struct { - userRepo *repository.User - nodeRepo *repository.Node -} - -func NewRouter(userRepo *repository.User, nodeRepo *repository.Node) *Router { - return &Router{ - userRepo: userRepo, - nodeRepo: nodeRepo, - } -} diff --git a/routes/user.go b/routes/user.go deleted file mode 100644 index dd5bd61..0000000 --- a/routes/user.go +++ /dev/null @@ -1,86 +0,0 @@ -package routes - -import ( - "log" - "net/http" - - "github.com/gorilla/sessions" - "github.com/labstack/echo-contrib/session" - "github.com/labstack/echo/v4" - "gitrepo.ru/neonxp/gorum/models" - "gitrepo.ru/neonxp/gorum/utils" - "gitrepo.ru/neonxp/gorum/views" -) - -func (r *Router) Login(c echo.Context) error { - req := new(loginRequest) - if err := c.Bind(req); err != nil { - return err - } - if c.Request().Method == http.MethodPost { - u, err := r.userRepo.Login(c.Request().Context(), req.Email, req.Password) - if err != nil { - return err - } - sess, err := session.Get("session", c) - if err != nil { - return err - } - maxAge := 0 - if req.Remember == "on" { - maxAge = 86400 * 14 - } - sess.Options = &sessions.Options{ - Path: "/", - MaxAge: maxAge, - HttpOnly: true, - } - sess.Values["user"] = *u - if err := sess.Save(c.Request(), c.Response()); err != nil { - return err - } - - return c.Redirect(302, "/") - } - - return utils.Render(c, views.Login(req.Email)) -} - -func (r *Router) Logout(c echo.Context) error { - sess, err := session.Get("session", c) - if err != nil { - return err - } - sess.Options = &sessions.Options{ - Path: "/", - MaxAge: -1, - HttpOnly: true, - } - sess.Values["user"] = nil - if err := sess.Save(c.Request(), c.Response()); err != nil { - return err - } - if err := sess.Save(c.Request(), c.Response()); err != nil { - return err - } - - return c.Redirect(302, "/") -} - -func (r *Router) Register(c echo.Context) error { - req := new(registerRequest) - if err := c.Bind(req); err != nil { - return err - } - if c.Request().Method == http.MethodPost { - uid, err := r.userRepo.Create(c.Request().Context(), req.Email, req.Password, req.Username, models.RoleUser) - if err != nil { - return err - } - log.Println(uid) - - return c.Redirect(302, "/login") - } - - return utils.Render(c, views.Register(req.Username, req.Email)) -} diff --git a/utils/date.go b/utils/date.go deleted file mode 100644 index 3a6197d..0000000 --- a/utils/date.go +++ /dev/null @@ -1,7 +0,0 @@ -package utils - -import "time" - -func FormatDate(ts int64) string { - return time.Unix(ts, 0).Format("15:04 02.01.2006") -} diff --git a/utils/middleware.go b/utils/middleware.go new file mode 100644 index 0000000..c297d65 --- /dev/null +++ b/utils/middleware.go @@ -0,0 +1,8 @@ +package utils + +import "net/http" + +// Middleware in itself simply takes a http.Handler as the first parameter, wraps +// it and returns a new http.Handler for the server to call (wraps handlers with +// closures according to the principle of Russian doll). +type Middleware func(http.Handler) http.Handler diff --git a/views/error.templ b/views/error.templ index 3e857ef..27d0b55 100644 --- a/views/error.templ +++ b/views/error.templ @@ -1,7 +1,7 @@ package views templ ErrorPage(err error) { - @Layout(nil) { + @Layout() { <h1>Ошибка</h1> { err.Error() } } diff --git a/views/error_templ.go b/views/error_templ.go index 8513d1d..acf3743 100644 --- a/views/error_templ.go +++ b/views/error_templ.go @@ -53,7 +53,7 @@ func ErrorPage(err error) templ.Component { } return templ_7745c5c3_Err }) - templ_7745c5c3_Err = Layout(nil).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = Layout().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/views/layouts.templ b/views/layouts.templ index f1c2deb..768a571 100644 --- a/views/layouts.templ +++ b/views/layouts.templ @@ -2,12 +2,11 @@ package views import ( "context" - "fmt" "gitrepo.ru/neonxp/gorum/contextlib" "gitrepo.ru/neonxp/gorum/models" ) -templ Layout(parent *models.Node) { +templ Layout() { <!DOCTYPE html> <html lang="ru"> <head> @@ -24,27 +23,15 @@ templ Layout(parent *models.Node) { <li> <strong>Gorum BBS</strong> </li> - if parent != nil { - <li> - <a href="/">Список тем</a> - </li> - <li> - switch parent.Type { - case models.PostType: - <a href={ templ.URL(fmt.Sprintf("/p/%d", parent.ID)) }>На уровень выше</a> - case models.TopicType: - <a href={ templ.URL(fmt.Sprintf("/t/%d", parent.ID)) }>К предыдущей теме</a> - } - </li> - } else { - <li> - <a href="/">Список тем</a> - </li> + <li> + <a href="/">Список тем</a> + </li> + if isAuthorized(ctx) { + <li><a href="/t/new">Новая тема</a></li> } </ul> <ul> if isAuthorized(ctx) { - // <li><a href="/topic/new">Новая тема</a></li> <li>{ getUser(ctx).Username }</li> <li><form action="/logout" method="POST"><input type="submit" value="Выход"/></form></li> } else { @@ -53,7 +40,7 @@ templ Layout(parent *models.Node) { } </ul> </nav> - <main class="container"> + <main class="container-fluid"> { children... } </main> <footer class="container-fluid"> diff --git a/views/layouts_templ.go b/views/layouts_templ.go index 6191925..4f563a5 100644 --- a/views/layouts_templ.go +++ b/views/layouts_templ.go @@ -10,12 +10,11 @@ import templruntime "github.com/a-h/templ/runtime" import ( "context" - "fmt" "gitrepo.ru/neonxp/gorum/contextlib" "gitrepo.ru/neonxp/gorum/models" ) -func Layout(parent *models.Node) templ.Component { +func Layout() templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) @@ -40,57 +39,18 @@ func Layout(parent *models.Node) templ.Component { var templ_7745c5c3_Var2 string templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs("/assets/css/pico." + ctx.Value(contextlib.ThemeKey).(string) + ".min.css") if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/layouts.templ`, Line: 17, Col: 107} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/layouts.templ`, Line: 16, Col: 107} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><link rel=\"stylesheet\" href=\"/assets/css/style.css\"><title>Gorum</title></head><body><nav class=\"container-fluid\"><ul><li><strong>Gorum BBS</strong></li>") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><link rel=\"stylesheet\" href=\"/assets/css/style.css\"><title>Gorum</title></head><body><nav class=\"container-fluid\"><ul><li><strong>Gorum BBS</strong></li><li><a href=\"/\">Список тем</a></li>") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - if parent != nil { - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li><a href=\"/\">Список тем</a></li><li>") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - switch parent.Type { - case models.PostType: - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<a href=\"") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var3 templ.SafeURL = templ.URL(fmt.Sprintf("/p/%d", parent.ID)) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var3))) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">На уровень выше</a>") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - case models.TopicType: - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<a href=\"") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var4 templ.SafeURL = templ.URL(fmt.Sprintf("/t/%d", parent.ID)) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var4))) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">К предыдущей теме</a>") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</li>") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } else { - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li><a href=\"/\">Список тем</a></li>") + if isAuthorized(ctx) { + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li><a href=\"/t/new\">Новая тема</a></li>") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -100,16 +60,16 @@ func Layout(parent *models.Node) templ.Component { return templ_7745c5c3_Err } if isAuthorized(ctx) { - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <li>") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li>") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var5 string - templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(getUser(ctx).Username) + var templ_7745c5c3_Var3 string + templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(getUser(ctx).Username) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/layouts.templ`, Line: 48, Col: 33} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/layouts.templ`, Line: 35, Col: 33} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -123,7 +83,7 @@ func Layout(parent *models.Node) templ.Component { return templ_7745c5c3_Err } } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</ul></nav><main class=\"container\">") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</ul></nav><main class=\"container-fluid\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/views/login.templ b/views/login.templ index 6c897c6..7f3a385 100644 --- a/views/login.templ +++ b/views/login.templ @@ -1,7 +1,7 @@ package views templ Login(email string) { - @Layout(nil) { + @Layout() { <h1>Вход</h1> <form method="post"> <label for="email">Электропочта:</label> diff --git a/views/login_templ.go b/views/login_templ.go index 034133f..2ae400f 100644 --- a/views/login_templ.go +++ b/views/login_templ.go @@ -57,7 +57,7 @@ func Login(email string) templ.Component { } return templ_7745c5c3_Err }) - templ_7745c5c3_Err = Layout(nil).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = Layout().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/views/new.templ b/views/new.templ index c83a4a9..ef84d74 100644 --- a/views/new.templ +++ b/views/new.templ @@ -1,51 +1,54 @@ package views import ( - "fmt" "gitrepo.ru/neonxp/gorum/models" - "gitrepo.ru/neonxp/gorum/utils" "strconv" ) -templ NewPost(parent *models.Node) { - @Layout(parent.Parent) { - <article> - <header class="post-header"> - <span> - { parent.Author.Username } - </span> - <span> - { utils.FormatDate(parent.CreatedAt) } - </span> - </header> - @templ.Raw(utils.MarkdownToHTML(parent.Text)) - </article> - @NewPostForm(parent) +templ NewPost(parent *models.Topic) { + @Layout() { + // <article> + // <header class="post-header"> + // <span> + // { parent.Author.Username } + // </span> + // <span> + // { utils.FormatDate(parent.CreatedAt) } + // </span> + // </header> + // @templ.Raw(utils.MarkdownToHTML(parent.Text)) + // </article> + if parent != nil { + @NewPostForm(parent.ID) + } } } -templ NewTopic(parent *models.Node) { - @Layout(parent.Parent) { - <h1>{parent.Text}</h1> - @NewTopicForm(parent) + +templ NewTopic(parent *models.Topic) { + @Layout() { + <h1>{ parent.Topic }</h1> + @NewTopicForm(parent.ID) } } -templ NewPostForm(parent *models.Node) { - <form method="post" action={ templ.URL(fmt.Sprintf("/p/%d/new", parent.ID)) }> +templ NewPostForm(parentID uint64) { + <form method="post" action="/p/new"> @CSRF() - <input type="hidden" name="type" value={ strconv.Itoa(int(models.PostType)) }/> + <input type="hidden" name="parent" value={ strconv.Itoa(int(parentID)) }/> <label for="text"><strong>Ответ</strong></label> <textarea name="text" id="text" placeholder="текст..." rows="5"></textarea> <input type="submit" value="Создать"/> </form> } -templ NewTopicForm(parent *models.Node) { - <form method="post" action={ templ.URL(fmt.Sprintf("/t/%d/new", parent.ID)) }> +templ NewTopicForm(parentID uint64) { + <form method="post" action="/t/new"> @CSRF() - <input type="hidden" name="type" value={ strconv.Itoa(int(models.TopicType)) }/> - <label for="text"><strong>Новая тема</strong></label> - <input type="text" name="text" id="text" placeholder="название темы..." /> + <input type="hidden" name="parent" value={ strconv.Itoa(int(parentID)) }/> + <label for="topic"><strong>Новая тема</strong></label> + <input type="text" name="topic" id="topic" placeholder="название темы..."/> + <label for="text"><strong>Описание</strong></label> + <textarea name="text" id="text" placeholder="текст..." rows="5"></textarea> <input type="submit" value="Создать"/> </form> } diff --git a/views/new_templ.go b/views/new_templ.go index 53f60ed..677b7c6 100644 --- a/views/new_templ.go +++ b/views/new_templ.go @@ -9,13 +9,11 @@ import "github.com/a-h/templ" import templruntime "github.com/a-h/templ/runtime" import ( - "fmt" "gitrepo.ru/neonxp/gorum/models" - "gitrepo.ru/neonxp/gorum/utils" "strconv" ) -func NewPost(parent *models.Node) templ.Component { +func NewPost(parent *models.Topic) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) @@ -45,51 +43,19 @@ func NewPost(parent *models.Node) templ.Component { }() } ctx = templ.InitializeContext(ctx) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<article><header class=\"post-header\"><span>") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var3 string - templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(parent.Author.Username) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/new.templ`, Line: 15, Col: 29} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span> <span>") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var4 string - templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(utils.FormatDate(parent.CreatedAt)) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/new.templ`, Line: 18, Col: 41} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span></header>") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templ.Raw(utils.MarkdownToHTML(parent.Text)).Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</article>") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = NewPostForm(parent).Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err + if parent != nil { + templ_7745c5c3_Err = NewPostForm(parent.ID).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } } return templ_7745c5c3_Err }) - templ_7745c5c3_Err = Layout(parent.Parent).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = Layout().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -97,7 +63,7 @@ func NewPost(parent *models.Node) templ.Component { }) } -func NewTopic(parent *models.Node) templ.Component { +func NewTopic(parent *models.Topic) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) @@ -110,12 +76,12 @@ func NewTopic(parent *models.Node) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var5 := templ.GetChildren(ctx) - if templ_7745c5c3_Var5 == nil { - templ_7745c5c3_Var5 = templ.NopComponent + templ_7745c5c3_Var3 := templ.GetChildren(ctx) + if templ_7745c5c3_Var3 == nil { + templ_7745c5c3_Var3 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Var6 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_Var4 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) if !templ_7745c5c3_IsBuffer { @@ -131,12 +97,12 @@ func NewTopic(parent *models.Node) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var7 string - templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(parent.Text) + var templ_7745c5c3_Var5 string + templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(parent.Topic) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/new.templ`, Line: 28, Col: 18} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/new.templ`, Line: 29, Col: 20} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -144,13 +110,13 @@ func NewTopic(parent *models.Node) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = NewTopicForm(parent).Render(ctx, templ_7745c5c3_Buffer) + templ_7745c5c3_Err = NewTopicForm(parent.ID).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } return templ_7745c5c3_Err }) - templ_7745c5c3_Err = Layout(parent.Parent).Render(templ.WithChildren(ctx, templ_7745c5c3_Var6), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = Layout().Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -158,7 +124,7 @@ func NewTopic(parent *models.Node) templ.Component { }) } -func NewPostForm(parent *models.Node) templ.Component { +func NewPostForm(parentID uint64) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) @@ -171,21 +137,12 @@ func NewPostForm(parent *models.Node) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var8 := templ.GetChildren(ctx) - if templ_7745c5c3_Var8 == nil { - templ_7745c5c3_Var8 = templ.NopComponent + templ_7745c5c3_Var6 := templ.GetChildren(ctx) + if templ_7745c5c3_Var6 == nil { + templ_7745c5c3_Var6 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<form method=\"post\" action=\"") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var9 templ.SafeURL = templ.URL(fmt.Sprintf("/p/%d/new", parent.ID)) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var9))) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<form method=\"post\" action=\"/p/new\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -193,16 +150,16 @@ func NewPostForm(parent *models.Node) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<input type=\"hidden\" name=\"type\" value=\"") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<input type=\"hidden\" name=\"parent\" value=\"") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var10 string - templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(int(models.PostType))) + var templ_7745c5c3_Var7 string + templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(int(parentID))) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/new.templ`, Line: 36, Col: 77} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/new.templ`, Line: 37, Col: 72} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -214,7 +171,7 @@ func NewPostForm(parent *models.Node) templ.Component { }) } -func NewTopicForm(parent *models.Node) templ.Component { +func NewTopicForm(parentID uint64) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) @@ -227,21 +184,12 @@ func NewTopicForm(parent *models.Node) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var11 := templ.GetChildren(ctx) - if templ_7745c5c3_Var11 == nil { - templ_7745c5c3_Var11 = templ.NopComponent + templ_7745c5c3_Var8 := templ.GetChildren(ctx) + if templ_7745c5c3_Var8 == nil { + templ_7745c5c3_Var8 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<form method=\"post\" action=\"") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var12 templ.SafeURL = templ.URL(fmt.Sprintf("/t/%d/new", parent.ID)) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var12))) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<form method=\"post\" action=\"/t/new\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -249,20 +197,20 @@ func NewTopicForm(parent *models.Node) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<input type=\"hidden\" name=\"type\" value=\"") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<input type=\"hidden\" name=\"parent\" value=\"") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var13 string - templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(int(models.TopicType))) + var templ_7745c5c3_Var9 string + templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(int(parentID))) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/new.templ`, Line: 46, Col: 78} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/new.templ`, Line: 47, Col: 72} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"> <label for=\"text\"><strong>Новая тема</strong></label> <input type=\"text\" name=\"text\" id=\"text\" placeholder=\"название темы...\"> <input type=\"submit\" value=\"Создать\"></form>") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"> <label for=\"topic\"><strong>Новая тема</strong></label> <input type=\"text\" name=\"topic\" id=\"topic\" placeholder=\"название темы...\"> <label for=\"text\"><strong>Описание</strong></label> <textarea name=\"text\" id=\"text\" placeholder=\"текст...\" rows=\"5\"></textarea> <input type=\"submit\" value=\"Создать\"></form>") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/views/nodes.templ b/views/nodes.templ deleted file mode 100644 index e6a120c..0000000 --- a/views/nodes.templ +++ /dev/null @@ -1,99 +0,0 @@ -package views - -import ( - "fmt" - "gitrepo.ru/neonxp/gorum/models" - "gitrepo.ru/neonxp/gorum/utils" - "strconv" -) - -templ Node(node *models.Node, topics []*models.Node, nodes []*models.Node) { - @Layout(node.Parent) { - switch node.Type { - case models.TopicType: - <h1>{ node.Text }</h1> - <div> - <a href={ templ.URL(fmt.Sprintf("/t/%d/new", node.ID)) }>Новая подтема</a> - </div> - case models.PostType: - <h1>Пост</h1> - @Post(node, 0, false) - } - if len(topics) != 0 { - <table> - <thead> - <tr> - <th>Тема</th> - <th>Тем/Ответов</th> - <th>Дата</th> - <th>Автор</th> - </tr> - </thead> - <tbody> - for _, n := range topics { - @Topic(n) - } - </tbody> - </table> - } - if len(nodes) == 0 { - <strong>Постов нет</strong> - } - for _, n := range nodes { - @Post(n, level(node), true) - } - if isAuthorized(ctx) { - @NewPostForm(node) - } else { - <a href="/login">Войдите</a> чтобы ответить в тему. - } - } -} - -templ Topic(n *models.Node) { - <tr> - <td> - <a href={ templ.URL(fmt.Sprintf("/t/%d", n.ID)) }>{ n.Text }</a> - </td> - <td> - { strconv.Itoa(len(n.Children)) } - </td> - <td> - { utils.FormatDate(n.CreatedAt) } - </td> - <td> - { n.Author.Username } - </td> - </tr> -} - -css levelStyle(level int) { - margin-left: { fmt.Sprintf("%dem", level) }; -} - -templ Post(n *models.Node, level int, withFooter bool) { - <article id={ fmt.Sprintf("post%d", n.ID) } class={ levelStyle(level) }> - <header class="post-header"> - <span> - { n.Author.Username } - </span> - <span> - { utils.FormatDate(n.CreatedAt) } - </span> - </header> - @templ.Raw(utils.MarkdownToHTML(n.Text)) - if withFooter { - <footer class="post-header"> - <a href={ templ.URL(fmt.Sprintf("/p/%d/new", n.ID)) }>Ответить</a> - <a href={ templ.URL(fmt.Sprintf("/p/%d", n.ID)) }>Ответов: { strconv.Itoa(len(n.Children)) }</a> - </footer> - } - </article> -} - -func level(node *models.Node) int { - if node.Type == models.PostType { - return 1 - } - return 0 -} diff --git a/views/nodes_templ.go b/views/nodes_templ.go deleted file mode 100644 index fc23258..0000000 --- a/views/nodes_templ.go +++ /dev/null @@ -1,377 +0,0 @@ -// Code generated by templ - DO NOT EDIT. - -// templ: version: v0.2.747 -package views - -//lint:file-ignore SA4006 This context is only used if a nested component is present. - -import "github.com/a-h/templ" -import templruntime "github.com/a-h/templ/runtime" - -import ( - "fmt" - "gitrepo.ru/neonxp/gorum/models" - "gitrepo.ru/neonxp/gorum/utils" - "strconv" -) - -func Node(node *models.Node, topics []*models.Node, nodes []*models.Node) templ.Component { - return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var1 := templ.GetChildren(ctx) - if templ_7745c5c3_Var1 == nil { - templ_7745c5c3_Var1 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - switch node.Type { - case models.TopicType: - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h1>") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var3 string - templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(node.Text) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/nodes.templ`, Line: 14, Col: 19} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h1><div><a href=\"") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var4 templ.SafeURL = templ.URL(fmt.Sprintf("/t/%d/new", node.ID)) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var4))) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">Новая подтема</a></div>") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - case models.PostType: - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h1>Пост</h1>") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = Post(node, 0, false).Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - if len(topics) != 0 { - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<table><thead><tr><th>Тема</th><th>Тем/Ответов</th><th>Дата</th><th>Автор</th></tr></thead> <tbody>") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - for _, n := range topics { - templ_7745c5c3_Err = Topic(n).Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</tbody></table>") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - if len(nodes) == 0 { - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<strong>Постов нет</strong> ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - for _, n := range nodes { - templ_7745c5c3_Err = Post(n, level(node), true).Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - if isAuthorized(ctx) { - templ_7745c5c3_Err = NewPostForm(node).Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } else { - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<a href=\"/login\">Войдите</a> чтобы ответить в тему.") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - return templ_7745c5c3_Err - }) - templ_7745c5c3_Err = Layout(node.Parent).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) -} - -func Topic(n *models.Node) templ.Component { - return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var5 := templ.GetChildren(ctx) - if templ_7745c5c3_Var5 == nil { - templ_7745c5c3_Var5 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<tr><td><a href=\"") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var6 templ.SafeURL = templ.URL(fmt.Sprintf("/t/%d", n.ID)) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var6))) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var7 string - templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(n.Text) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/nodes.templ`, Line: 56, Col: 61} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></td><td>") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var8 string - templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(len(n.Children))) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/nodes.templ`, Line: 59, Col: 34} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td>") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var9 string - templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(utils.FormatDate(n.CreatedAt)) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/nodes.templ`, Line: 62, Col: 34} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td>") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var10 string - templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(n.Author.Username) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/nodes.templ`, Line: 65, Col: 22} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td></tr>") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) -} - -func levelStyle(level int) templ.CSSClass { - templ_7745c5c3_CSSBuilder := templruntime.GetBuilder() - templ_7745c5c3_CSSBuilder.WriteString(string(templ.SanitizeCSS(`margin-left`, fmt.Sprintf("%dem", level)))) - templ_7745c5c3_CSSID := templ.CSSID(`levelStyle`, templ_7745c5c3_CSSBuilder.String()) - return templ.ComponentCSSClass{ - ID: templ_7745c5c3_CSSID, - Class: templ.SafeCSS(`.` + templ_7745c5c3_CSSID + `{` + templ_7745c5c3_CSSBuilder.String() + `}`), - } -} - -func Post(n *models.Node, level int, withFooter bool) templ.Component { - return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var11 := templ.GetChildren(ctx) - if templ_7745c5c3_Var11 == nil { - templ_7745c5c3_Var11 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - var templ_7745c5c3_Var12 = []any{levelStyle(level)} - templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var12...) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<article id=\"") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var13 string - templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("post%d", n.ID)) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/nodes.templ`, Line: 75, Col: 42} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" class=\"") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var14 string - templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var12).String()) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/nodes.templ`, Line: 1, Col: 0} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><header class=\"post-header\"><span>") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var15 string - templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(n.Author.Username) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/nodes.templ`, Line: 78, Col: 23} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span> <span>") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var16 string - templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(utils.FormatDate(n.CreatedAt)) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/nodes.templ`, Line: 81, Col: 35} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span></header>") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templ.Raw(utils.MarkdownToHTML(n.Text)).Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - if withFooter { - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<footer class=\"post-header\"><a href=\"") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var17 templ.SafeURL = templ.URL(fmt.Sprintf("/p/%d/new", n.ID)) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var17))) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">Ответить</a> <a href=\"") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var18 templ.SafeURL = templ.URL(fmt.Sprintf("/p/%d", n.ID)) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var18))) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">Ответов: ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var19 string - templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(len(n.Children))) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/nodes.templ`, Line: 88, Col: 101} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></footer>") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</article>") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) -} - -func level(node *models.Node) int { - if node.Type == models.PostType { - return 1 - } - return 0 -} diff --git a/views/posts.templ b/views/posts.templ new file mode 100644 index 0000000..2a89a78 --- /dev/null +++ b/views/posts.templ @@ -0,0 +1,72 @@ +package views + +import ( + "fmt" + "gitrepo.ru/neonxp/gorum/models" + "gitrepo.ru/neonxp/gorum/utils" +) + +templ Posts(topic *models.Topic, topics []*models.Topic, nodes []*models.Post) { + @Layout() { + <h1>{ topic.Topic }</h1> + if len(topics) != 0 { + <table> + <thead> + <tr> + <th>Тема</th> + <th>Тем/Ответов</th> + <th>Дата</th> + <th>Автор</th> + </tr> + </thead> + <tbody> + for _, n := range topics { + @Topic(n) + } + </tbody> + </table> + } + if topic.Text != "" { + <article> + <header class="post-header"> + <span> + if topic.Author != nil { + { topic.Author.Username } + } + </span> + <span> + { topic.CreatedAt.Format("15:04 02.01.2006") } + </span> + </header> + @templ.Raw(utils.MarkdownToHTML(topic.Text)) + </article> + } + <hr/> + if len(nodes) == 0 { + <strong>Ответов нет</strong> + } + for _, n := range nodes { + @Post(n) + } + <hr/> + if isAuthorized(ctx) { + @NewPostForm(topic.ID) + } else { + <a href="/login">Войдите</a> чтобы ответить в тему. + } + } +} + +templ Post(n *models.Post) { + <article id={ fmt.Sprintf("post%d", n.ID) }> + <header class="post-header"> + <span> + { n.Author.Username } + </span> + <span> + { n.CreatedAt.Format("15:04 02.01.2006") } + </span> + </header> + @templ.Raw(utils.MarkdownToHTML(n.Text)) + </article> +} diff --git a/views/posts_templ.go b/views/posts_templ.go new file mode 100644 index 0000000..ca91d94 --- /dev/null +++ b/views/posts_templ.go @@ -0,0 +1,238 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.2.747 +package views + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +import ( + "fmt" + "gitrepo.ru/neonxp/gorum/models" + "gitrepo.ru/neonxp/gorum/utils" +) + +func Posts(topic *models.Topic, topics []*models.Topic, nodes []*models.Post) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var1 := templ.GetChildren(ctx) + if templ_7745c5c3_Var1 == nil { + templ_7745c5c3_Var1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h1>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var3 string + templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(topic.Topic) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/posts.templ`, Line: 11, Col: 19} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h1>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if len(topics) != 0 { + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<table><thead><tr><th>Тема</th><th>Тем/Ответов</th><th>Дата</th><th>Автор</th></tr></thead> <tbody>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + for _, n := range topics { + templ_7745c5c3_Err = Topic(n).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</tbody></table>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if topic.Text != "" { + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<article><header class=\"post-header\"><span>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if topic.Author != nil { + var templ_7745c5c3_Var4 string + templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(topic.Author.Username) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/posts.templ`, Line: 34, Col: 30} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span> <span>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var5 string + templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(topic.CreatedAt.Format("15:04 02.01.2006")) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/posts.templ`, Line: 38, Col: 50} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span></header>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templ.Raw(utils.MarkdownToHTML(topic.Text)).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</article>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <hr>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if len(nodes) == 0 { + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<strong>Ответов нет</strong> ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + for _, n := range nodes { + templ_7745c5c3_Err = Post(n).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <hr>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if isAuthorized(ctx) { + templ_7745c5c3_Err = NewPostForm(topic.ID).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } else { + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<a href=\"/login\">Войдите</a> чтобы ответить в тему.") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = Layout().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +func Post(n *models.Post) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var6 := templ.GetChildren(ctx) + if templ_7745c5c3_Var6 == nil { + templ_7745c5c3_Var6 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<article id=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var7 string + templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("post%d", n.ID)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/posts.templ`, Line: 61, Col: 42} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><header class=\"post-header\"><span>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var8 string + templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(n.Author.Username) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/posts.templ`, Line: 64, Col: 23} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span> <span>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var9 string + templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(n.CreatedAt.Format("15:04 02.01.2006")) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/posts.templ`, Line: 67, Col: 44} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span></header>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templ.Raw(utils.MarkdownToHTML(n.Text)).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</article>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} diff --git a/views/register.templ b/views/register.templ index 0a4384e..9974110 100644 --- a/views/register.templ +++ b/views/register.templ @@ -1,7 +1,7 @@ package views templ Register(username, email string) { - @Layout(nil) { + @Layout() { <h1>Регистрация</h1> <form method="post"> <label for="username">Имя пользователя:</label> diff --git a/views/register_templ.go b/views/register_templ.go index 5008775..71a53ba 100644 --- a/views/register_templ.go +++ b/views/register_templ.go @@ -70,7 +70,7 @@ func Register(username, email string) templ.Component { } return templ_7745c5c3_Err }) - templ_7745c5c3_Err = Layout(nil).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = Layout().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/views/topics.templ b/views/topics.templ new file mode 100644 index 0000000..52011e5 --- /dev/null +++ b/views/topics.templ @@ -0,0 +1,45 @@ +package views + +import ( + "fmt" + "gitrepo.ru/neonxp/gorum/models" +) + +templ Topics(topics []*models.Topic) { + @Layout() { + <table> + <thead> + <tr> + <th>Тема</th> + <th>Тем/Ответов</th> + <th>Дата</th> + <th>Автор</th> + </tr> + </thead> + <tbody> + for _, n := range topics { + @Topic(n) + } + </tbody> + </table> + } +} + +templ Topic(n *models.Topic) { + <tr> + <td> + <a href={ templ.URL(fmt.Sprintf("/t/%d", n.ID)) }>{ n.Topic }</a> + </td> + <td> + // { strconv.Itoa(len(n.Children)) } + </td> + <td> + { n.CreatedAt.Format("15:04 02.01.2006") } + </td> + <td> + if n.Author != nil { + { n.Author.Username } + } + </td> + </tr> +}
\ No newline at end of file diff --git a/views/topics_templ.go b/views/topics_templ.go new file mode 100644 index 0000000..f0f7e36 --- /dev/null +++ b/views/topics_templ.go @@ -0,0 +1,144 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.2.747 +package views + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +import ( + "fmt" + "gitrepo.ru/neonxp/gorum/models" +) + +func Topics(topics []*models.Topic) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var1 := templ.GetChildren(ctx) + if templ_7745c5c3_Var1 == nil { + templ_7745c5c3_Var1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<table><thead><tr><th>Тема</th><th>Тем/Ответов</th><th>Дата</th><th>Автор</th></tr></thead> <tbody>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + for _, n := range topics { + templ_7745c5c3_Err = Topic(n).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</tbody></table>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = Layout().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +func Topic(n *models.Topic) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var3 := templ.GetChildren(ctx) + if templ_7745c5c3_Var3 == nil { + templ_7745c5c3_Var3 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<tr><td><a href=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var4 templ.SafeURL = templ.URL(fmt.Sprintf("/t/%d", n.ID)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var4))) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var5 string + templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(n.Topic) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/topics.templ`, Line: 31, Col: 62} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></td><td></td><td>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var6 string + templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(n.CreatedAt.Format("15:04 02.01.2006")) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/topics.templ`, Line: 37, Col: 43} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if n.Author != nil { + var templ_7745c5c3_Var7 string + templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(n.Author.Username) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/topics.templ`, Line: 41, Col: 23} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td></tr>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} |