diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/config/config.go | 25 | ||||
-rw-r--r-- | pkg/db/config.go | 5 | ||||
-rw-r--r-- | pkg/db/db.go | 18 | ||||
-rw-r--r-- | pkg/handler/add.go | 60 | ||||
-rw-r--r-- | pkg/handler/admin.go | 57 | ||||
-rw-r--r-- | pkg/handler/handler.go | 7 | ||||
-rw-r--r-- | pkg/handler/index.go | 32 | ||||
-rw-r--r-- | pkg/handler/quote.go | 27 | ||||
-rw-r--r-- | pkg/handler/random.go | 18 | ||||
-rw-r--r-- | pkg/model/quote.go | 23 | ||||
-rw-r--r-- | pkg/tpl/add.templ | 45 | ||||
-rw-r--r-- | pkg/tpl/add_success.templ | 12 | ||||
-rw-r--r-- | pkg/tpl/add_success_templ.go | 58 | ||||
-rw-r--r-- | pkg/tpl/add_templ.go | 141 | ||||
-rw-r--r-- | pkg/tpl/index.templ | 72 | ||||
-rw-r--r-- | pkg/tpl/index_templ.go | 206 | ||||
-rw-r--r-- | pkg/tpl/layout.templ | 51 | ||||
-rw-r--r-- | pkg/tpl/layout_templ.go | 93 | ||||
-rw-r--r-- | pkg/tpl/quote.templ | 30 | ||||
-rw-r--r-- | pkg/tpl/quote_admin.templ | 36 | ||||
-rw-r--r-- | pkg/tpl/quote_admin_templ.go | 150 | ||||
-rw-r--r-- | pkg/tpl/quote_templ.go | 154 | ||||
-rw-r--r-- | pkg/tpl/random.templ | 18 | ||||
-rw-r--r-- | pkg/tpl/random_templ.go | 81 |
24 files changed, 1419 insertions, 0 deletions
diff --git a/pkg/config/config.go b/pkg/config/config.go new file mode 100644 index 0000000..342012e --- /dev/null +++ b/pkg/config/config.go @@ -0,0 +1,25 @@ +package config + +import ( + "os" + + "gopkg.in/yaml.v3" + "sh.org.ru/pkg/db" +) + +type Config struct { + Debug bool `yaml:"debug"` + DB *db.Config `yaml:"db"` + Listen string `yaml:"listen"` + Admins map[string]string `yaml:"admins"` +} + +func New(file string) (*Config, error) { + cfg := new(Config) + fp, err := os.Open(file) + if err != nil { + return nil, err + } + + return cfg, yaml.NewDecoder(fp).Decode(cfg) +} diff --git a/pkg/db/config.go b/pkg/db/config.go new file mode 100644 index 0000000..4df38f0 --- /dev/null +++ b/pkg/db/config.go @@ -0,0 +1,5 @@ +package db + +type Config struct { + DSN string `yaml:"dsn"` +} diff --git a/pkg/db/db.go b/pkg/db/db.go new file mode 100644 index 0000000..8b7c47c --- /dev/null +++ b/pkg/db/db.go @@ -0,0 +1,18 @@ +package db + +import ( + "database/sql" + + "github.com/uptrace/bun" + "github.com/uptrace/bun/dialect/pgdialect" + "github.com/uptrace/bun/driver/pgdriver" +) + +// dsn := "postgres://postgres:@localhost:5432/test?sslmode=disable" +// dsn := "unix://user:pass@dbname/var/run/postgresql/.s.PGSQL.5432" + +func New(config *Config) *bun.DB { + sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(config.DSN))) + + return bun.NewDB(sqldb, pgdialect.New()) +} diff --git a/pkg/handler/add.go b/pkg/handler/add.go new file mode 100644 index 0000000..a6a5ced --- /dev/null +++ b/pkg/handler/add.go @@ -0,0 +1,60 @@ +package handler + +import ( + "net/http" + + "github.com/labstack/echo/v4" + "github.com/ssoda/captcha" + "sh.org.ru/pkg/model" + "sh.org.ru/pkg/tpl" +) + +func (h *Handler) AddQuote(c echo.Context) error { + cid := captcha.New() + form := &tpl.AddQuoteForm{ + CaptchaID: cid, + } + return tpl. + AddQuotePage(form, ""). + Render(c.Request().Context(), c.Response()) +} + +func (h *Handler) AddQuotePost(c echo.Context) error { + form := &tpl.AddQuoteForm{} + if err := c.Bind(form); err != nil { + return err + } + if form.CaptchaValue == "" { + return formError(form, c, "Неверный код") + } + if !captcha.VerifyString(form.CaptchaID, form.CaptchaValue) { + return formError(form, c, "Неверный код") + } + + if len(form.Quote) < 10 { + return formError(form, c, "Цитата слишком короткая") + } + + q := &model.Quote{ + Quote: form.Quote, + Approved: false, + Archive: false, + } + if _, err := h.DB.NewInsert().Model(q).Exec(c.Request().Context()); err != nil { + return err + } + + return c.Redirect(http.StatusFound, "/add/success") +} + +func (h *Handler) AddQuoteSuccess(c echo.Context) error { + return tpl.AddQuoteSuccessPage().Render(c.Request().Context(), c.Response()) +} + +func formError(form *tpl.AddQuoteForm, c echo.Context, err string) error { + form.CaptchaID = captcha.New() + form.CaptchaValue = "" + return tpl. + AddQuotePage(form, err). + Render(c.Request().Context(), c.Response()) +} diff --git a/pkg/handler/admin.go b/pkg/handler/admin.go new file mode 100644 index 0000000..8531011 --- /dev/null +++ b/pkg/handler/admin.go @@ -0,0 +1,57 @@ +package handler + +import ( + "net/http" + + "github.com/labstack/echo/v4" + "sh.org.ru/pkg/model" + "sh.org.ru/pkg/tpl" +) + +func (h *Handler) Admin(c echo.Context) error { + quotes := make([]model.Quote, 0, 20) + count, err := h.DB.NewSelect(). + Model((*model.Quote)(nil)). + Order("id ASC"). + Where("approved = ?", false). + ScanAndCount(c.Request().Context(), "es) + if err != nil { + return err + } + + return tpl.Admin(quotes, count).Render(c.Request().Context(), c.Response()) +} + +func (h *Handler) AdminAction(c echo.Context) error { + form := new(tpl.AdminForm) + if err := c.Bind(form); err != nil { + return err + } + + switch form.Action { + case "approve": + _, err := h.DB.NewUpdate(). + Model(&model.Quote{ + ID: int64(form.ID), + Approved: true, + }). + Column("approved"). + WherePK("id"). + Exec(c.Request().Context()) + if err != nil { + return err + } + case "decline": + _, err := h.DB.NewDelete(). + Model(&model.Quote{ + ID: int64(form.ID), + }). + WherePK("id"). + Exec(c.Request().Context()) + if err != nil { + return err + } + } + + return c.Redirect(http.StatusFound, "/admin/") +} diff --git a/pkg/handler/handler.go b/pkg/handler/handler.go new file mode 100644 index 0000000..5ba3966 --- /dev/null +++ b/pkg/handler/handler.go @@ -0,0 +1,7 @@ +package handler + +import "github.com/uptrace/bun" + +type Handler struct { + DB *bun.DB +} diff --git a/pkg/handler/index.go b/pkg/handler/index.go new file mode 100644 index 0000000..611a544 --- /dev/null +++ b/pkg/handler/index.go @@ -0,0 +1,32 @@ +package handler + +import ( + "github.com/labstack/echo/v4" + "sh.org.ru/pkg/model" + "sh.org.ru/pkg/tpl" +) + +func (h *Handler) Index(c echo.Context) error { + p := &Pagination{} + if err := c.Bind(p); err != nil { + return err + } + + quotes := make([]model.Quote, 0, 20) + count, err := h.DB.NewSelect(). + Model((*model.Quote)(nil)). + Order("id DESC"). + Where("approved = ?", true). + Limit(20). + Offset(p.Page*20). + ScanAndCount(c.Request().Context(), "es) + if err != nil { + return err + } + + return tpl.Index(quotes, p.Page, count).Render(c.Request().Context(), c.Response()) +} + +type Pagination struct { + Page int `query:"page" default:"0"` +} diff --git a/pkg/handler/quote.go b/pkg/handler/quote.go new file mode 100644 index 0000000..af9fd82 --- /dev/null +++ b/pkg/handler/quote.go @@ -0,0 +1,27 @@ +package handler + +import ( + "strconv" + + "github.com/labstack/echo/v4" + "sh.org.ru/pkg/model" + "sh.org.ru/pkg/tpl" +) + +func (h *Handler) Quote(c echo.Context) error { + sid := c.Param("id") + id, err := strconv.Atoi(sid) + if err != nil { + return err + } + + quote := new(model.Quote) + err = h.DB.NewSelect(). + Model(quote). + Where("id = ?", id).Scan(c.Request().Context(), quote) + if err != nil { + return err + } + + return tpl.QuotePage(quote).Render(c.Request().Context(), c.Response()) +} diff --git a/pkg/handler/random.go b/pkg/handler/random.go new file mode 100644 index 0000000..091058f --- /dev/null +++ b/pkg/handler/random.go @@ -0,0 +1,18 @@ +package handler + +import ( + "github.com/labstack/echo/v4" + "sh.org.ru/pkg/model" + "sh.org.ru/pkg/tpl" +) + +func (h *Handler) Random(c echo.Context) error { + quotes := make([]model.Quote, 0, 20) + err := h.DB.NewRaw(`select q.* from quotes q where q.approved = true order by random() limit 20`). + Scan(c.Request().Context(), "es) + if err != nil { + return err + } + + return tpl.Random(quotes).Render(c.Request().Context(), c.Response()) +} diff --git a/pkg/model/quote.go b/pkg/model/quote.go new file mode 100644 index 0000000..0ad89cf --- /dev/null +++ b/pkg/model/quote.go @@ -0,0 +1,23 @@ +package model + +import ( + "strings" + "time" + + "github.com/uptrace/bun" +) + +type Quote struct { + bun.BaseModel `bun:"table:quotes,alias:q"` + + ID int64 `bun:",pk,autoincrement"` + Quote string `bun:",notnull"` + Approved bool + Archive bool + CreatedAt time.Time `bun:",nullzero,notnull,default:current_timestamp"` + DeletedAt time.Time `bun:",soft_delete,nullzero"` +} + +func (q *Quote) Text() string { + return strings.ReplaceAll(q.Quote, "\n", "<br />") +} diff --git a/pkg/tpl/add.templ b/pkg/tpl/add.templ new file mode 100644 index 0000000..0611d1b --- /dev/null +++ b/pkg/tpl/add.templ @@ -0,0 +1,45 @@ +package tpl + +import "fmt" + +templ AddQuotePage(form *AddQuoteForm, err string) { + @Layout(HeaderParams{}) { + <h2>Добавление цитаты</h2> + if err != "" { + <article> + <header>Ошибка</header> + { err } + </article> + } + <form method="post"> + <textarea rows="5" name="quote" placeholder="Текст цитаты">{ form.Quote }</textarea> + <input type="hidden" name="captcha_id" value={ form.CaptchaID }/> + <div role="group"> + <img class="captcha" src={ fmt.Sprintf("/captcha/download/%s.png", form.CaptchaID) }/> + <audio id="audiocaptcha" src={ fmt.Sprintf("/captcha/download/%s.wav?lang=ru", form.CaptchaID) }></audio> + <a role="button" onclick="togglePlay()">Прослушать</a> + <input type="text" name="captcha_value" placeholder="Код с картинки"/> + </div> + <input type="submit" value="Отправить на модерацию"/> + </form> + <script> + var myAudio = document.getElementById("audiocaptcha"); + var isPlaying = false; + function togglePlay() { + isPlaying ? myAudio.pause() : myAudio.play(); + }; + myAudio.onplaying = function() { + isPlaying = true; + }; + myAudio.onpause = function() { + isPlaying = false; + }; + </script> + } +} + +type AddQuoteForm struct { + Quote string `form:"quote"` + CaptchaID string `form:"captcha_id"` + CaptchaValue string `form:"captcha_value"` +} diff --git a/pkg/tpl/add_success.templ b/pkg/tpl/add_success.templ new file mode 100644 index 0000000..25caa7c --- /dev/null +++ b/pkg/tpl/add_success.templ @@ -0,0 +1,12 @@ +package tpl + +templ AddQuoteSuccessPage() { + @Layout(HeaderParams{}) { + <h2>Добавление цитаты</h2> + <article> + <header>Успешно!</header> + Ваша цитата отправлена на модерацию. В случае успешной проверки, цитата скоро будет опубликована. + </article> + <a href="/" role="button">На главную страницу</a> + } +} diff --git a/pkg/tpl/add_success_templ.go b/pkg/tpl/add_success_templ.go new file mode 100644 index 0000000..c036c68 --- /dev/null +++ b/pkg/tpl/add_success_templ.go @@ -0,0 +1,58 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.2.778 +package tpl + +//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" + +func AddQuoteSuccessPage() 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 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + 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("<h2>Добавление цитаты</h2><article><header>Успешно!</header>Ваша цитата отправлена на модерацию. В случае успешной проверки, цитата скоро будет опубликована.</article><a href=\"/\" role=\"button\">На главную страницу</a>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = Layout(HeaderParams{}).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/pkg/tpl/add_templ.go b/pkg/tpl/add_templ.go new file mode 100644 index 0000000..04fb615 --- /dev/null +++ b/pkg/tpl/add_templ.go @@ -0,0 +1,141 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.2.778 +package tpl + +//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" + +func AddQuotePage(form *AddQuoteForm, err string) 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 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + 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("<h2>Добавление цитаты</h2>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if err != "" { + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<article><header>Ошибка</header>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var3 string + templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(err) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/tpl/add.templ`, Line: 11, Col: 9} + } + _, 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("</article>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <form method=\"post\"><textarea rows=\"5\" name=\"quote\" placeholder=\"Текст цитаты\">") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var4 string + templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(form.Quote) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/tpl/add.templ`, Line: 15, Col: 85} + } + _, 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("</textarea> <input type=\"hidden\" name=\"captcha_id\" value=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var5 string + templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(form.CaptchaID) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/tpl/add.templ`, Line: 16, Col: 64} + } + _, 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("\"><div role=\"group\"><img class=\"captcha\" src=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var6 string + templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/captcha/download/%s.png", form.CaptchaID)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/tpl/add.templ`, Line: 18, Col: 86} + } + _, 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("\"> <audio id=\"audiocaptcha\" src=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var7 string + templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/captcha/download/%s.wav?lang=ru", form.CaptchaID)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/tpl/add.templ`, Line: 19, Col: 98} + } + _, 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("\"></audio> <a role=\"button\" onclick=\"togglePlay()\">Прослушать</a> <input type=\"text\" name=\"captcha_value\" placeholder=\"Код с картинки\"></div><input type=\"submit\" value=\"Отправить на модерацию\"></form><script>\n\t\t\tvar myAudio = document.getElementById(\"audiocaptcha\");\n\t\t\tvar isPlaying = false;\n\t\t\tfunction togglePlay() {\n\t\t\t\tisPlaying ? myAudio.pause() : myAudio.play();\n\t\t\t};\n\t\t\tmyAudio.onplaying = function() {\n\t\t\t\tisPlaying = true;\n\t\t\t};\n\t\t\tmyAudio.onpause = function() {\n\t\t\t\tisPlaying = false;\n\t\t\t};\n\t\t</script>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = Layout(HeaderParams{}).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +type AddQuoteForm struct { + Quote string `form:"quote"` + CaptchaID string `form:"captcha_id"` + CaptchaValue string `form:"captcha_value"` +} + +var _ = templruntime.GeneratedTemplate diff --git a/pkg/tpl/index.templ b/pkg/tpl/index.templ new file mode 100644 index 0000000..b405f3e --- /dev/null +++ b/pkg/tpl/index.templ @@ -0,0 +1,72 @@ +package tpl + +import ( + "fmt" + "sh.org.ru/pkg/model" + "strconv" +) + +templ Index(quotes []model.Quote, page, count int) { + @Layout(HeaderParams{ + Title: "Цитатник Рунета", + Description: "Новый цитатник Рунета", + URL: "https://sh.org.ru/", + }) { + for _, q := range quotes { + @Quote(&q) + } + <nav> + <ul> + if page > 0 { + <li><a href={ templ.URL(fmt.Sprintf("/?page=%d", page-1)) }>←</a></li> + } + for _, p := range generatePagination(page, count/20) { + if p == "..." { + <li>...</li> + } else if p == strconv.Itoa(page) { + <li>[{ p }]</li> + } else { + <li><a href={ templ.URL(fmt.Sprintf("/?page=%s", p)) }>{ p }</a></li> + } + } + + if page < count/20 { + <li><a href={ templ.URL(fmt.Sprintf("/?page=%d", page+1)) }>→</a></li> + } + </ul> + </nav> + Всего { strconv.Itoa(count) } цитат. + } +} + +func generatePagination(currentPage, totalPages int) []string { + pagination := make([]string, 0, 11) + + if currentPage <= 3 { + for i := 0; i <= currentPage+3; i++ { + pagination = append(pagination, strconv.Itoa(i)) + } + pagination = append(pagination, "...") + pagination = append(pagination, strconv.Itoa(totalPages-2)) + pagination = append(pagination, strconv.Itoa(totalPages-1)) + pagination = append(pagination, strconv.Itoa(totalPages)) + } else if currentPage >= totalPages-3 { + pagination = append(pagination, "0") + pagination = append(pagination, "1") + pagination = append(pagination, "2") + pagination = append(pagination, "...") + for i := currentPage - 3; i <= totalPages; i++ { + pagination = append(pagination, strconv.Itoa(i)) + } + } else { + pagination = append(pagination, "0") + pagination = append(pagination, "...") + for i := currentPage - 2; i <= currentPage+2; i++ { + pagination = append(pagination, strconv.Itoa(i)) + } + pagination = append(pagination, "...") + pagination = append(pagination, strconv.Itoa(totalPages)) + } + + return pagination +} diff --git a/pkg/tpl/index_templ.go b/pkg/tpl/index_templ.go new file mode 100644 index 0000000..4d249ab --- /dev/null +++ b/pkg/tpl/index_templ.go @@ -0,0 +1,206 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.2.778 +package tpl + +//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" + "sh.org.ru/pkg/model" + "strconv" +) + +func Index(quotes []model.Quote, page, count int) 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 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + 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) + for _, q := range quotes { + templ_7745c5c3_Err = Quote(&q).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <nav><ul>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if page > 0 { + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li><a href=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var3 templ.SafeURL = templ.URL(fmt.Sprintf("/?page=%d", page-1)) + _, 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></li>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + for _, p := range generatePagination(page, count/20) { + if p == "..." { + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li>...</li>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } else if p == strconv.Itoa(page) { + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li>[") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var4 string + templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(p) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/tpl/index.templ`, Line: 27, Col: 14} + } + _, 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("]</li>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } else { + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li><a href=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var5 templ.SafeURL = templ.URL(fmt.Sprintf("/?page=%s", p)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var5))) + 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_Var6 string + templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(p) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/tpl/index.templ`, Line: 29, Col: 64} + } + _, 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("</a></li>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + } + if page < count/20 { + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li><a href=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var7 templ.SafeURL = templ.URL(fmt.Sprintf("/?page=%d", page+1)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var7))) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">→</a></li>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</ul></nav>Всего ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var8 string + templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(count)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/tpl/index.templ`, Line: 38, 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(" цитат.") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = Layout(HeaderParams{ + Title: "Цитатник Рунета", + Description: "Новый цитатник Рунета", + URL: "https://sh.org.ru/", + }).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +func generatePagination(currentPage, totalPages int) []string { + pagination := make([]string, 0, 11) + + if currentPage <= 3 { + for i := 0; i <= currentPage+3; i++ { + pagination = append(pagination, strconv.Itoa(i)) + } + pagination = append(pagination, "...") + pagination = append(pagination, strconv.Itoa(totalPages-2)) + pagination = append(pagination, strconv.Itoa(totalPages-1)) + pagination = append(pagination, strconv.Itoa(totalPages)) + } else if currentPage >= totalPages-3 { + pagination = append(pagination, "0") + pagination = append(pagination, "1") + pagination = append(pagination, "2") + pagination = append(pagination, "...") + for i := currentPage - 3; i <= totalPages; i++ { + pagination = append(pagination, strconv.Itoa(i)) + } + } else { + pagination = append(pagination, "0") + pagination = append(pagination, "...") + for i := currentPage - 2; i <= currentPage+2; i++ { + pagination = append(pagination, strconv.Itoa(i)) + } + pagination = append(pagination, "...") + pagination = append(pagination, strconv.Itoa(totalPages)) + } + + return pagination +} + +var _ = templruntime.GeneratedTemplate diff --git a/pkg/tpl/layout.templ b/pkg/tpl/layout.templ new file mode 100644 index 0000000..fc9314e --- /dev/null +++ b/pkg/tpl/layout.templ @@ -0,0 +1,51 @@ +package tpl + +templ Layout(params HeaderParams) { + <!DOCTYPE html> + <html lang="en"> + <head> + <meta charset="utf-8"/> + <meta name="viewport" content="width=device-width, initial-scale=1"/> + <meta name="color-scheme" content="light dark"/> + <link rel="stylesheet" href="/css/pico.css"/> + <link rel="stylesheet" href="/css/style.css"/> + <link rel="stylesheet" href="/css/fork-awesome.min.css"/> + <meta property="og:title" content={ params.Title }/> + <meta property="og:url" content={ params.URL }/> + <meta property="og:description" content={ params.Description }/> + <title>ШОргРу</title> + </head> + <body> + <main class="container"> + <nav> + <ul> + <li><a href="/"><strong>ШОргРу</strong></a></li> + </ul> + <ul> + <li><a href="/add">Добавить цитату</a></li> + <li><a href="/random">Случайные</a></li> + </ul> + </nav> + { children... } + </main> + </body> + <footer> + <main class="container"> + <nav> + <ul> + <li>Сделал <a href="https://neonxp.ru/">NeonXP</a> в 2024 году.</li> + </ul> + <ul> + <a href="https://gitrepo.ru/NeonXP/ShOrgRu">Исходный код</a> + </ul> + </nav> + </main> + </footer> + </html> +} + +type HeaderParams struct { + Title string + URL string + Description string +} diff --git a/pkg/tpl/layout_templ.go b/pkg/tpl/layout_templ.go new file mode 100644 index 0000000..133beed --- /dev/null +++ b/pkg/tpl/layout_templ.go @@ -0,0 +1,93 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.2.778 +package tpl + +//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" + +func Layout(params HeaderParams) 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 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + 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_Err = templ_7745c5c3_Buffer.WriteString("<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta name=\"color-scheme\" content=\"light dark\"><link rel=\"stylesheet\" href=\"/css/pico.css\"><link rel=\"stylesheet\" href=\"/css/style.css\"><link rel=\"stylesheet\" href=\"/css/fork-awesome.min.css\"><meta property=\"og:title\" content=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var2 string + templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(params.Title) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/tpl/layout.templ`, Line: 13, Col: 51} + } + _, 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("\"><meta property=\"og:url\" content=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var3 string + templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(params.URL) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/tpl/layout.templ`, Line: 14, Col: 47} + } + _, 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("\"><meta property=\"og:description\" content=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var4 string + templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(params.Description) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/tpl/layout.templ`, Line: 15, Col: 63} + } + _, 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("\"><title>ШОргРу</title></head><body><main class=\"container\"><nav><ul><li><a href=\"/\"><strong>ШОргРу</strong></a></li></ul><ul><li><a href=\"/add\">Добавить цитату</a></li><li><a href=\"/random\">Случайные</a></li></ul></nav>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templ_7745c5c3_Var1.Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</main></body><footer><main class=\"container\"><nav><ul><li>Сделал <a href=\"https://neonxp.ru/\">NeonXP</a> в 2024 году.</li></ul><ul><a href=\"https://gitrepo.ru/NeonXP/ShOrgRu\">Исходный код</a></ul></nav></main></footer></html>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +type HeaderParams struct { + Title string + URL string + Description string +} + +var _ = templruntime.GeneratedTemplate diff --git a/pkg/tpl/quote.templ b/pkg/tpl/quote.templ new file mode 100644 index 0000000..459a873 --- /dev/null +++ b/pkg/tpl/quote.templ @@ -0,0 +1,30 @@ +package tpl + +import ( + "fmt" + "sh.org.ru/pkg/model" + "strconv" +) + +templ Quote(quote *model.Quote) { + <article> + <header><a href={ templ.URL(fmt.Sprintf("/quote/%d", quote.ID)) }>#{ strconv.Itoa(int(quote.ID)) }</a></header> + @templ.Raw(quote.Text()) + <footer> + Поделиться: + <a target="_blank" href={ templ.URL(fmt.Sprintf("https://t.me/share/url?url=https://sh.org.ru/%d&text=%s", quote.ID, quote.Quote)) }><i class="fa fa-telegram" aria-hidden="true"></i></a> + <a target="_blank" href={ templ.URL(fmt.Sprintf("https://vk.com/share.php?url=https://sh.org.ru/%d", quote.ID)) }><i class="fa fa-vk" aria-hidden="true"></i></a> + <a target="_blank" href={ templ.URL(fmt.Sprintf("https://connect.ok.ru/offer?url=https://sh.org.ru/%d", quote.ID)) }><i class="fa fa-odnoklassniki-square" aria-hidden="true"></i></a> + </footer> + </article> +} + +templ QuotePage(quote *model.Quote) { + @Layout(HeaderParams{ + Title: "Цитата #" + strconv.Itoa(int(quote.ID)), + URL: fmt.Sprintf("https://sh.org.ru/quote/%d", quote.ID), + Description: templ.EscapeString(quote.Quote), + }) { + @Quote(quote) + } +} diff --git a/pkg/tpl/quote_admin.templ b/pkg/tpl/quote_admin.templ new file mode 100644 index 0000000..80bafad --- /dev/null +++ b/pkg/tpl/quote_admin.templ @@ -0,0 +1,36 @@ +package tpl + +import ( + "sh.org.ru/pkg/model" + "strconv" +) + +templ Admin(quotes []model.Quote, count int) { + @Layout(HeaderParams{}) { + for _, q := range quotes { + @QuoteAdmin(&q) + } + Всего { strconv.Itoa(count) } цитат. + } +} + +templ QuoteAdmin(quote *model.Quote) { + <article> + <header>#{ strconv.Itoa(int(quote.ID)) }</header> + @templ.Raw(quote.Text()) + <footer> + <form method="post" action="/admin/action"> + <input type="hidden" name="id" value={ strconv.Itoa(int(quote.ID)) }/> + <div role="group"> + <input class="primary" type="submit" name="action" value="approve"/> + <input class="secondary" type="submit" name="action" value="decline"/> + </div> + </form> + </footer> + </article> +} + +type AdminForm struct { + ID int `form:"id"` + Action string `form:"action"` +} diff --git a/pkg/tpl/quote_admin_templ.go b/pkg/tpl/quote_admin_templ.go new file mode 100644 index 0000000..ab8ceeb --- /dev/null +++ b/pkg/tpl/quote_admin_templ.go @@ -0,0 +1,150 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.2.778 +package tpl + +//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 ( + "sh.org.ru/pkg/model" + "strconv" +) + +func Admin(quotes []model.Quote, count int) 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 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + 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) + for _, q := range quotes { + templ_7745c5c3_Err = QuoteAdmin(&q).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 + } + var templ_7745c5c3_Var3 string + templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(count)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/tpl/quote_admin.templ`, Line: 13, Col: 34} + } + _, 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(" цитат.") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = Layout(HeaderParams{}).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +func QuoteAdmin(quote *model.Quote) 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 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + 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_Var4 := templ.GetChildren(ctx) + if templ_7745c5c3_Var4 == nil { + templ_7745c5c3_Var4 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<article><header>#") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var5 string + templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(int(quote.ID))) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/tpl/quote_admin.templ`, Line: 19, Col: 40} + } + _, 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("</header>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templ.Raw(quote.Text()).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<footer><form method=\"post\" action=\"/admin/action\"><input type=\"hidden\" name=\"id\" value=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var6 string + templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(int(quote.ID))) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/tpl/quote_admin.templ`, Line: 23, Col: 70} + } + _, 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("\"><div role=\"group\"><input class=\"primary\" type=\"submit\" name=\"action\" value=\"approve\"> <input class=\"secondary\" type=\"submit\" name=\"action\" value=\"decline\"></div></form></footer></article>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +type AdminForm struct { + ID int `form:"id"` + Action string `form:"action"` +} + +var _ = templruntime.GeneratedTemplate diff --git a/pkg/tpl/quote_templ.go b/pkg/tpl/quote_templ.go new file mode 100644 index 0000000..e374b88 --- /dev/null +++ b/pkg/tpl/quote_templ.go @@ -0,0 +1,154 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.2.778 +package tpl + +//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" + "sh.org.ru/pkg/model" + "strconv" +) + +func Quote(quote *model.Quote) 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 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + 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_Err = templ_7745c5c3_Buffer.WriteString("<article><header><a href=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var2 templ.SafeURL = templ.URL(fmt.Sprintf("/quote/%d", quote.ID)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var2))) + 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_Var3 string + templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(int(quote.ID))) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/tpl/quote.templ`, Line: 11, Col: 98} + } + _, 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("</a></header>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templ.Raw(quote.Text()).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<footer>Поделиться: <a target=\"_blank\" href=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var4 templ.SafeURL = templ.URL(fmt.Sprintf("https://t.me/share/url?url=https://sh.org.ru/%d&text=%s", quote.ID, quote.Quote)) + _, 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("\"><i class=\"fa fa-telegram\" aria-hidden=\"true\"></i></a> <a target=\"_blank\" href=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var5 templ.SafeURL = templ.URL(fmt.Sprintf("https://vk.com/share.php?url=https://sh.org.ru/%d", quote.ID)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var5))) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><i class=\"fa fa-vk\" aria-hidden=\"true\"></i></a> <a target=\"_blank\" href=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var6 templ.SafeURL = templ.URL(fmt.Sprintf("https://connect.ok.ru/offer?url=https://sh.org.ru/%d", quote.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("\"><i class=\"fa fa-odnoklassniki-square\" aria-hidden=\"true\"></i></a></footer></article>") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +func QuotePage(quote *model.Quote) 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 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + 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_Var7 := templ.GetChildren(ctx) + if templ_7745c5c3_Var7 == nil { + templ_7745c5c3_Var7 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Var8 := 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 = Quote(quote).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = Layout(HeaderParams{ + Title: "Цитата #" + strconv.Itoa(int(quote.ID)), + URL: fmt.Sprintf("https://sh.org.ru/quote/%d", quote.ID), + Description: templ.EscapeString(quote.Quote), + }).Render(templ.WithChildren(ctx, templ_7745c5c3_Var8), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/pkg/tpl/random.templ b/pkg/tpl/random.templ new file mode 100644 index 0000000..63e7c03 --- /dev/null +++ b/pkg/tpl/random.templ @@ -0,0 +1,18 @@ +package tpl + +import "sh.org.ru/pkg/model" +import "math/rand" +import "fmt" + +templ Random(quotes []model.Quote) { + @Layout(HeaderParams{ + Title: "Цитатник Рунета -- случайные", + Description: "Новый цитатник Рунета", + URL: "https://sh.org.ru/random", + }) { + for _, q := range quotes { + @Quote(&q) + } + <a role="button" href={templ.URL(fmt.Sprintf("/random?%d", rand.Int()))}>Обновить</a> + } +} diff --git a/pkg/tpl/random_templ.go b/pkg/tpl/random_templ.go new file mode 100644 index 0000000..19a7035 --- /dev/null +++ b/pkg/tpl/random_templ.go @@ -0,0 +1,81 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.2.778 +package tpl + +//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 "sh.org.ru/pkg/model" +import "math/rand" +import "fmt" + +func Random(quotes []model.Quote) 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 + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + 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) + for _, q := range quotes { + templ_7745c5c3_Err = Quote(&q).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <a role=\"button\" href=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var3 templ.SafeURL = templ.URL(fmt.Sprintf("/random?%d", rand.Int())) + _, 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 + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = Layout(HeaderParams{ + Title: "Цитатник Рунета -- случайные", + Description: "Новый цитатник Рунета", + URL: "https://sh.org.ru/random", + }).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +var _ = templruntime.GeneratedTemplate |