From 0d68bbead0d57a4649a336f80485b30de32881ed Mon Sep 17 00:00:00 2001 From: Tigor Hutasuhut Date: Fri, 3 May 2024 19:44:25 +0700 Subject: [PATCH] subreddit: implemented add page --- api/reddit/check_subreddit.go | 11 ++ server/routes/routes.go | 1 + server/routes/subreddit_create.go | 116 ++++++++++++++++++ server/routes/subreddit_validate_schedule.go | 2 +- views/subredditsview/addview/addview.templ | 21 ++-- views/subredditsview/addview/countback.templ | 73 +++++++++++ ...{subreddit_name_input.templ => name.templ} | 101 +++++++-------- ...it_schedule_input.templ => schedule.templ} | 61 +++++++-- .../addview/subreddit_type_input.templ | 27 ---- views/subredditsview/addview/type.templ | 52 ++++++++ 10 files changed, 366 insertions(+), 99 deletions(-) create mode 100644 views/subredditsview/addview/countback.templ rename views/subredditsview/addview/{subreddit_name_input.templ => name.templ} (50%) rename views/subredditsview/addview/{subreddit_schedule_input.templ => schedule.templ} (50%) delete mode 100644 views/subredditsview/addview/subreddit_type_input.templ create mode 100644 views/subredditsview/addview/type.templ diff --git a/api/reddit/check_subreddit.go b/api/reddit/check_subreddit.go index 10ad0f7..949a14a 100644 --- a/api/reddit/check_subreddit.go +++ b/api/reddit/check_subreddit.go @@ -49,6 +49,17 @@ func (reddit *Reddit) CheckSubreddit(ctx context.Context, params CheckSubredditP return actual, errs.Wrapw(err, msg, "url", url, "params", params).Code(http.StatusNotFound) } + if resp.StatusCode == http.StatusForbidden { + var msg string + if params.SubredditType == SubredditTypeUser { + msg = "user has set their profile to private" + } + if params.SubredditType == SubredditTypeSub { + msg = "subreddit is private" + } + return actual, errs.Wrapw(err, msg, "url", url, "params", params).Code(http.StatusForbidden) + } + if params.SubredditType == SubredditTypeUser && resp.StatusCode == http.StatusOK { return params.Subreddit, nil } diff --git a/server/routes/routes.go b/server/routes/routes.go index ac2bdef..9d40ddb 100644 --- a/server/routes/routes.go +++ b/server/routes/routes.go @@ -58,6 +58,7 @@ func (routes *Routes) registerHTMXRoutes(router chi.Router) { router.Use(chimiddleware.RequestLogger(middleware.ChiLogger{})) router.Use(chimiddleware.SetHeader("Content-Type", "text/html; charset=utf-8")) + router.Post("/subreddits/add", routes.SubredditsCreateHTMX) router.Post("/subreddits/start", routes.SubredditStartDownloadHTMX) router.Post("/subreddits/check", routes.SubredditCheckHTMX) router.Get("/subreddits/validate/schedule", routes.SubredditValidateScheduleHTMX) diff --git a/server/routes/subreddit_create.go b/server/routes/subreddit_create.go index f0c74f5..91857de 100644 --- a/server/routes/subreddit_create.go +++ b/server/routes/subreddit_create.go @@ -5,13 +5,17 @@ import ( "errors" "fmt" "net/http" + "strconv" + "github.com/a-h/templ" "github.com/robfig/cron/v3" "github.com/tigorlazuardi/redmage/api" "github.com/tigorlazuardi/redmage/api/reddit" "github.com/tigorlazuardi/redmage/models" "github.com/tigorlazuardi/redmage/pkg/errs" "github.com/tigorlazuardi/redmage/pkg/log" + "github.com/tigorlazuardi/redmage/views/components" + "github.com/tigorlazuardi/redmage/views/subredditsview/addview" ) func (routes *Routes) SubredditsCreateAPI(rw http.ResponseWriter, req *http.Request) { @@ -64,6 +68,118 @@ func (routes *Routes) SubredditsCreateAPI(rw http.ResponseWriter, req *http.Requ } } +func (routes *Routes) SubredditsCreateHTMX(rw http.ResponseWriter, r *http.Request) { + ctx, span := tracer.Start(r.Context(), "*Routes.SubredditsCreateHTMX") + defer span.End() + + sub, errComponents := subredditsDataFromRequest(r) + if len(errComponents) > 0 { + rw.WriteHeader(http.StatusBadRequest) + for _, err := range errComponents { + if e := err.Render(ctx, rw); e != nil { + log.New(ctx).Err(e).Error("failed to render error") + } + } + return + } + actual, err := routes.API.SubredditCheck(ctx, api.SubredditCheckParam{ + Subreddit: sub.Name, + SubredditType: reddit.SubredditType(sub.Subtype), + }) + if err != nil { + rw.WriteHeader(http.StatusBadRequest) + log.New(ctx).Err(err).Error("subreddit check returns error") + renderer := addview.SubredditInputForm(addview.SubredditInputData{ + Value: sub.Name, + Error: err.Error(), + Type: reddit.SubredditType(sub.Subtype), + HXSwapOOB: "true", + }) + if err := renderer.Render(ctx, rw); err != nil { + log.New(ctx).Err(err).Error("failed to render error") + } + return + } + sub.Name = actual + + _, err = routes.API.SubredditsCreate(ctx, sub) + if err != nil { + log.New(ctx).Err(err).Error("failed to create subreddit") + code, message := errs.HTTPMessage(err) + rw.Header().Set("HX-Retarget", components.NotificationContainerID) + rw.WriteHeader(code) + if err := components.ErrorNotication(message).Render(ctx, rw); err != nil { + log.New(ctx).Err(err).Error("failed to render error") + } + return + } + rw.Header().Set("HX-Redirect", "/subreddits") + rw.WriteHeader(http.StatusCreated) + _, _ = rw.Write([]byte("Subreddit created")) +} + +func subredditsDataFromRequest(r *http.Request) (sub *models.Subreddit, errs []templ.Component) { + sub = &models.Subreddit{} + + var t reddit.SubredditType + err := t.Parse(r.FormValue("type")) + if err != nil { + errs = append(errs, addview.SubredditTypeInput(addview.SubredditTypeData{ + Value: strconv.Itoa(int(t)), + Error: err.Error(), + HXSwapOOB: "true", + })) + + return nil, errs + } + sub.Subtype = int32(t) + + sub.Name = r.FormValue("name") + if sub.Name == "" { + errs = append(errs, addview.SubredditInputForm(addview.SubredditInputData{ + Value: sub.Name, + Error: "name is required", + Type: t, + HXSwapOOB: "true", + })) + return nil, errs + } + + enableSchedule, _ := strconv.Atoi(r.FormValue("enable_schedule")) + sub.EnableSchedule = int32(enableSchedule) + if sub.EnableSchedule > 1 { + sub.EnableSchedule = 1 + } else if sub.EnableSchedule < 0 { + sub.EnableSchedule = 0 + } + if sub.EnableSchedule == 0 { + sub.Schedule = "@daily" + } + + if sub.EnableSchedule == 1 { + sub.Schedule = r.FormValue("schedule") + _, err = cronParser.Parse(sub.Schedule) + if err != nil { + errs = append(errs, addview.ScheduleInput(addview.ScheduleInputData{ + Value: sub.Schedule, + Error: fmt.Sprintf("invalid cron schedule: %s", err), + HXSwapOOB: "true", + })) + } + } + + countback, _ := strconv.Atoi(r.FormValue("countback")) + sub.Countback = int32(countback) + if sub.Countback < 1 { + errs = append(errs, addview.CountbackInput(addview.CountbackInputData{ + Value: int64(sub.Countback), + Error: "countback must be 1 or higher", + })) + } + + return sub, errs +} + var cronParser = cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor) func validateSubredditsCreate(body *models.Subreddit) error { diff --git a/server/routes/subreddit_validate_schedule.go b/server/routes/subreddit_validate_schedule.go index 7703658..91e873d 100644 --- a/server/routes/subreddit_validate_schedule.go +++ b/server/routes/subreddit_validate_schedule.go @@ -45,7 +45,7 @@ func (routes *Routes) SubredditValidateScheduleHTMX(rw http.ResponseWriter, r *h next := scheduler.Next(time.Now()) - data.Valid = fmt.Sprintf("Schedule is valid. Next run at: %s", next.Format("Monday, _2 January 2006 15:04:05 MST")) + data.Valid = fmt.Sprintf("Syntax is valid. Next run at: %s", next.Format("Monday, _2 January 2006 15:04 MST")) if err := addview.ScheduleInput(data).Render(ctx, rw); err != nil { log.New(ctx).Err(err).Error("failed to render schedule input") diff --git a/views/subredditsview/addview/addview.templ b/views/subredditsview/addview/addview.templ index dbcc63e..600964c 100644 --- a/views/subredditsview/addview/addview.templ +++ b/views/subredditsview/addview/addview.templ @@ -19,19 +19,24 @@ templ AddviewContent(c *views.Context) {

Add Subreddit

-