diff --git a/html/config.go b/html/config.go new file mode 100644 index 00000000..ee6a63a7 --- /dev/null +++ b/html/config.go @@ -0,0 +1,15 @@ +package html + +import ( + "github.com/Gleipnir-Technology/nidus-sync/config" +) + +type ContentConfig struct { + IsProductionEnvironment bool +} + +func NewContentConfig() ContentConfig { + return ContentConfig{ + IsProductionEnvironment: config.IsProductionEnvironment(), + } +} diff --git a/html/content.go b/html/content.go new file mode 100644 index 00000000..5aafd5b0 --- /dev/null +++ b/html/content.go @@ -0,0 +1,7 @@ +package html + +type Content[T any] struct { + C T + Config ContentConfig + URL ContentURL +} diff --git a/html/handler.go b/html/handler.go new file mode 100644 index 00000000..a469fb84 --- /dev/null +++ b/html/handler.go @@ -0,0 +1,26 @@ +package html + +import ( + "context" + "net/http" + + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" + "github.com/rs/zerolog/log" +) + +type handlerFunctionGet[T any] func(context.Context, *http.Request) (*Response[T], *nhttp.ErrorWithStatus) + +func MakeGet[T any](f handlerFunctionGet[T]) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + resp, e := f(ctx, r) + if e != nil { + log.Warn().Int("status", e.Status) + http.Error(w, e.Error(), e.Status) + return + } + RenderOrError(w, resp.Template, Content[T]{ + C: resp.Content, + }) + } +} diff --git a/html/response.go b/html/response.go index 33e1d386..d67a9bbc 100644 --- a/html/response.go +++ b/html/response.go @@ -38,3 +38,15 @@ func RespondError(w http.ResponseWriter, m string, e error, s int) { log.Warn().Int("status", s).Err(e).Str("user message", m).Msg("Responding with an error") http.Error(w, m, s) } + +type Response[T any] struct { + Content T + Template string +} + +func NewResponse[T any](template string, content T) *Response[T] { + return &Response[T]{ + Content: content, + Template: template, + } +} diff --git a/sync/url.go b/html/url.go similarity index 97% rename from sync/url.go rename to html/url.go index bfbd13d1..3e7b4f1d 100644 --- a/sync/url.go +++ b/html/url.go @@ -1,4 +1,4 @@ -package sync +package html import ( "strconv" @@ -6,7 +6,7 @@ import ( "github.com/Gleipnir-Technology/nidus-sync/config" ) -type contentURL struct { +type ContentURL struct { Configuration contentURLConfiguration OAuthRefreshArcGIS string Root string @@ -16,8 +16,8 @@ type contentURL struct { Upload contentURLUpload } -func newContentURL() contentURL { - return contentURL{ +func NewContentURL() ContentURL { + return ContentURL{ Configuration: newContentURLConfiguration(), OAuthRefreshArcGIS: config.MakeURLNidus("/arcgis/oauth/begin"), Root: config.MakeURLNidus("/"), diff --git a/http/error_with_status.go b/http/error_with_status.go new file mode 100644 index 00000000..a23acbdd --- /dev/null +++ b/http/error_with_status.go @@ -0,0 +1,32 @@ +package http + +import ( + "fmt" + "net/http" +) + +type ErrorWithStatus struct { + Message string + Status int +} + +func (e *ErrorWithStatus) Error() string { + return e.Message +} +func NewError(mesg_format string, args ...any) *ErrorWithStatus { + return NewErrorStatus(http.StatusInternalServerError, mesg_format, args...) +} +func NewErrorMaybe(mesg_format string, err error, args ...any) *ErrorWithStatus { + if err == nil { + return nil + } + allArgs := append([]any{err}, args...) + return NewErrorStatus(http.StatusInternalServerError, mesg_format, allArgs...) +} +func NewErrorStatus(status int, mesg_format string, args ...any) *ErrorWithStatus { + w := fmt.Errorf(mesg_format, args...) + return &ErrorWithStatus{ + Message: w.Error(), + Status: status, + } +} diff --git a/rmo/mailer.go b/rmo/mailer.go index 9e9a09e1..3444509a 100644 --- a/rmo/mailer.go +++ b/rmo/mailer.go @@ -1,12 +1,67 @@ package rmo import ( + "context" "net/http" + + "github.com/Gleipnir-Technology/bob" + "github.com/Gleipnir-Technology/bob/dialect/psql" + "github.com/Gleipnir-Technology/bob/dialect/psql/sm" + "github.com/Gleipnir-Technology/nidus-sync/db" + "github.com/Gleipnir-Technology/nidus-sync/html" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" + "github.com/go-chi/chi/v5" + "github.com/stephenafamo/scan" //"github.com/Gleipnir-Technology/nidus-sync/config" ) -type contentMailer struct{} +type address struct { + Number int32 `db:"number_"` + Street string `db:"street"` + Locality string `db:"locality"` + PostalCode string `db:"postal_code"` + Country string `db:"country"` +} +type contentMailer struct { + Address address +} -func getMailer(w http.ResponseWriter, r *http.Request) { +func getMailer(ctx context.Context, r *http.Request) (*html.Response[contentMailer], *nhttp.ErrorWithStatus) { + public_id := chi.URLParam(r, "public_id") + if public_id == "" { + return nil, nhttp.NewErrorStatus(http.StatusBadRequest, "No 'public_id' in the url params") + } + + /* + compliance_request, err := models.ComplianceReportRequests.Query( + models.Preload.ComplianceReportRequest.Site(), + models.SelectWhere.ComplianceReportRequests.PublicID.EQ(public_id), + ).One(ctx, db.PGInstance.BobDB) + if err != nil { + respondError(w, "failed to get compliance request", err, http.StatusBadRequest) + } + site := compliance_request. + */ + report, err := bob.One(ctx, db.PGInstance.BobDB, psql.Select( + sm.Columns( + "address.number_", + "address.street", + "address.locality", + "address.postal_code", + "address.country", + ), + sm.From("compliance_report_request").As("crr"), + sm.InnerJoin("site").OnEQ(psql.Raw("crr.site_id"), psql.Raw("site.id")), + sm.InnerJoin("address").OnEQ(psql.Raw("site.address_id"), psql.Raw("address.id")), + sm.Where(psql.Raw("crr.public_id").EQ(psql.Arg(public_id))), + ), scan.StructMapper[address]()) + if err != nil { + return nil, nhttp.NewErrorStatus(http.StatusNotFound, "No compliance report with that public ID") + } + return html.NewResponse( + "rmo/mailer.html", contentMailer{ + Address: report, + }, + ), nil } diff --git a/rmo/routes.go b/rmo/routes.go index f727fbbe..ba5e40a5 100644 --- a/rmo/routes.go +++ b/rmo/routes.go @@ -32,7 +32,7 @@ func Router() chi.Router { r.Get("/email/unsubscribe", getEmailUnsubscribe) r.Get("/email/unsubscribe/report/{report_id}", getEmailReportUnsubscribe) r.Get("/image/{uuid}", getImageByUUID) - r.Get("/mailer/{public_id}", getMailer) + r.Get("/mailer/{public_id}", html.MakeGet(getMailer)) r.Route("/mock", addMockRoutes) r.Post("/register-notifications", postRegisterNotifications) r.Get("/register-notifications-complete", getRegisterNotificationsComplete) diff --git a/sync/admin.go b/sync/admin.go index bd30a135..7edf4d36 100644 --- a/sync/admin.go +++ b/sync/admin.go @@ -5,11 +5,13 @@ import ( "net/http" "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/Gleipnir-Technology/nidus-sync/html" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" ) type contentAdminDash struct{} -func getAdminDash(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentAdminDash], *errorWithStatus) { +func getAdminDash(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentAdminDash], *nhttp.ErrorWithStatus) { content := contentAdminDash{} - return newResponse("sync/admin-dash.html", content), nil + return html.NewResponse("sync/admin-dash.html", content), nil } diff --git a/sync/cell.go b/sync/cell.go index 4fc96b5a..eb9069e0 100644 --- a/sync/cell.go +++ b/sync/cell.go @@ -6,6 +6,8 @@ import ( "github.com/Gleipnir-Technology/nidus-sync/db/models" "github.com/Gleipnir-Technology/nidus-sync/h3utils" + "github.com/Gleipnir-Technology/nidus-sync/html" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" "github.com/go-chi/chi/v5" "github.com/uber/h3-go/v4" ) @@ -19,46 +21,46 @@ type contentCell struct { Treatments []Treatment } -func getCellDetails(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentCell], *errorWithStatus) { +func getCellDetails(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentCell], *nhttp.ErrorWithStatus) { cell_str := chi.URLParam(r, "cell") if cell_str == "" { - return nil, newErrorStatus(http.StatusBadRequest, "There should always be a cell") + return nil, nhttp.NewErrorStatus(http.StatusBadRequest, "There should always be a cell") } c, err := HexToInt64(cell_str) if err != nil { - return nil, newErrorStatus(http.StatusBadRequest, "Cannot convert provided cell to uint64") + return nil, nhttp.NewErrorStatus(http.StatusBadRequest, "Cannot convert provided cell to uint64") } center, err := h3.Cell(c).LatLng() if err != nil { - return nil, newError("Failed to get center: %w", err) + return nil, nhttp.NewError("Failed to get center: %w", err) } boundary, err := h3.Cell(c).Boundary() if err != nil { - return nil, newError("Failed to get boundary: %w", err) + return nil, nhttp.NewError("Failed to get boundary: %w", err) } inspections, err := inspectionsByCell(ctx, org, h3.Cell(c)) if err != nil { - return nil, newError("Failed to get inspections by cell: %w", err) + return nil, nhttp.NewError("Failed to get inspections by cell: %w", err) } geojson, err := h3utils.H3ToGeoJSON([]h3.Cell{h3.Cell(c)}) if err != nil { - return nil, newError("Failed to get boundaries: %w", err) + return nil, nhttp.NewError("Failed to get boundaries: %w", err) } resolution := h3.Cell(c).Resolution() sources, err := breedingSourcesByCell(ctx, org, h3.Cell(c)) if err != nil { - return nil, newError("Failed to get sources: %w", err) + return nil, nhttp.NewError("Failed to get sources: %w", err) } traps, err := trapsByCell(ctx, org, h3.Cell(c)) if err != nil { - return nil, newError("Failed to get traps: %w", err) + return nil, nhttp.NewError("Failed to get traps: %w", err) } treatments, err := treatmentsByCell(ctx, org, h3.Cell(c)) if err != nil { - return nil, newError("Failed to get treatments: %w", err) + return nil, nhttp.NewError("Failed to get treatments: %w", err) } - return newResponse("sync/cell.html", contentCell{ + return html.NewResponse("sync/cell.html", contentCell{ BreedingSources: sources, CellBoundary: boundary, Inspections: inspections, diff --git a/sync/communication.go b/sync/communication.go index bed9a5d4..32bc035b 100644 --- a/sync/communication.go +++ b/sync/communication.go @@ -5,10 +5,12 @@ import ( "net/http" "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/Gleipnir-Technology/nidus-sync/html" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" ) type contentCommunicationRoot struct{} -func getCommunicationRoot(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentCommunicationRoot], *errorWithStatus) { - return newResponse("sync/communication-root.html", contentCommunicationRoot{}), nil +func getCommunicationRoot(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentCommunicationRoot], *nhttp.ErrorWithStatus) { + return html.NewResponse("sync/communication-root.html", contentCommunicationRoot{}), nil } diff --git a/sync/configuration.go b/sync/configuration.go index 4df3abef..03b8be9a 100644 --- a/sync/configuration.go +++ b/sync/configuration.go @@ -5,26 +5,17 @@ import ( "net/http" "github.com/Gleipnir-Technology/nidus-sync/arcgis" - "github.com/Gleipnir-Technology/nidus-sync/config" "github.com/Gleipnir-Technology/nidus-sync/db" "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/Gleipnir-Technology/nidus-sync/html" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" //"github.com/rs/zerolog/log" ) -type contentConfig struct { - IsProductionEnvironment bool -} - -func newContentConfig() contentConfig { - return contentConfig{ - IsProductionEnvironment: config.IsProductionEnvironment(), - } -} - type contentConfigurationRoot struct{} -func getConfigurationRoot(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentConfigurationRoot], *errorWithStatus) { - return newResponse("sync/configuration/root.html", contentConfigurationRoot{}), nil +func getConfigurationRoot(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentConfigurationRoot], *nhttp.ErrorWithStatus) { + return html.NewResponse("sync/configuration/root.html", contentConfigurationRoot{}), nil } type contentSettingOrganization struct { @@ -35,10 +26,10 @@ type contentSettingIntegration struct { ArcGISOAuth *models.ArcgisOauthToken } -func getConfigurationOrganization(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*response[contentSettingOrganization], *errorWithStatus) { +func getConfigurationOrganization(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*html.Response[contentSettingOrganization], *nhttp.ErrorWithStatus) { org, err := u.Organization().One(ctx, db.PGInstance.BobDB) if err != nil { - return nil, newError("get organization: %w", err) + return nil, nhttp.NewError("get organization: %w", err) } /* var district contentDistrict @@ -74,44 +65,44 @@ func getConfigurationOrganization(ctx context.Context, r *http.Request, org *mod data := contentSettingOrganization{ Organization: org, } - return newResponse("sync/configuration/organization.html", data), nil + return html.NewResponse("sync/configuration/organization.html", data), nil } -func getConfigurationIntegration(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*response[contentSettingIntegration], *errorWithStatus) { +func getConfigurationIntegration(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*html.Response[contentSettingIntegration], *nhttp.ErrorWithStatus) { oauth, err := arcgis.GetOAuthForUser(ctx, u) if err != nil { - return nil, newError("Failed to get oauth: %w", err) + return nil, nhttp.NewError("Failed to get oauth: %w", err) } data := contentSettingIntegration{ ArcGISOAuth: oauth, } - return newResponse("sync/configuration/integration.html", data), nil + return html.NewResponse("sync/configuration/integration.html", data), nil } -func getConfigurationIntegrationArcgis(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*response[contentSettingIntegration], *errorWithStatus) { +func getConfigurationIntegrationArcgis(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*html.Response[contentSettingIntegration], *nhttp.ErrorWithStatus) { oauth, err := arcgis.GetOAuthForUser(ctx, u) if err != nil { - return nil, newError("Failed to get oauth: %w", err) + return nil, nhttp.NewError("Failed to get oauth: %w", err) } data := contentSettingIntegration{ ArcGISOAuth: oauth, } - return newResponse("sync/configuration/integration-arcgis.html", data), nil + return html.NewResponse("sync/configuration/integration-arcgis.html", data), nil } type contentSettingPlaceholder struct{} -func getConfigurationPesticide(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentSettingPlaceholder], *errorWithStatus) { +func getConfigurationPesticide(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentSettingPlaceholder], *nhttp.ErrorWithStatus) { content := contentSettingPlaceholder{} - return newResponse("sync/configuration/pesticide.html", content), nil + return html.NewResponse("sync/configuration/pesticide.html", content), nil } -func getConfigurationPesticideAdd(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentSettingPlaceholder], *errorWithStatus) { +func getConfigurationPesticideAdd(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentSettingPlaceholder], *nhttp.ErrorWithStatus) { content := contentSettingPlaceholder{} - return newResponse("sync/configuration/pesticide-add.html", content), nil + return html.NewResponse("sync/configuration/pesticide-add.html", content), nil } -func getConfigurationUserAdd(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentSettingPlaceholder], *errorWithStatus) { +func getConfigurationUserAdd(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentSettingPlaceholder], *nhttp.ErrorWithStatus) { content := contentSettingPlaceholder{} - return newResponse("sync/configuration/user-add.html", content), nil + return html.NewResponse("sync/configuration/user-add.html", content), nil } -func getConfigurationUserList(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentSettingPlaceholder], *errorWithStatus) { +func getConfigurationUserList(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentSettingPlaceholder], *nhttp.ErrorWithStatus) { content := contentSettingPlaceholder{} - return newResponse("sync/configuration/user-list.html", content), nil + return html.NewResponse("sync/configuration/user-list.html", content), nil } diff --git a/sync/dash.go b/sync/dash.go index 979c1998..73ffe07f 100644 --- a/sync/dash.go +++ b/sync/dash.go @@ -14,6 +14,7 @@ import ( "github.com/Gleipnir-Technology/nidus-sync/db" "github.com/Gleipnir-Technology/nidus-sync/db/models" "github.com/Gleipnir-Technology/nidus-sync/html" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" "github.com/go-chi/chi/v5" "github.com/google/uuid" ) @@ -61,8 +62,8 @@ func getDistrict(w http.ResponseWriter, r *http.Request) { html.RenderOrError(w, "sync/district.html", &context) } -func getLayoutTest(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentLayoutTest], *errorWithStatus) { - return newResponse("sync/layout-test.html", contentLayoutTest{}), nil +func getLayoutTest(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentLayoutTest], *nhttp.ErrorWithStatus) { + return html.NewResponse("sync/layout-test.html", contentLayoutTest{}), nil } func getRoot(w http.ResponseWriter, r *http.Request) { @@ -93,40 +94,40 @@ func getRoot(w http.ResponseWriter, r *http.Request) { } } -func getSource(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentSource], *errorWithStatus) { +func getSource(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentSource], *nhttp.ErrorWithStatus) { globalid_s := chi.URLParam(r, "globalid") if globalid_s == "" { - return nil, newError("No globalid provided: %w", nil) + return nil, nhttp.NewError("No globalid provided: %w", nil) } globalid, err := uuid.Parse(globalid_s) if err != nil { - return nil, newError("globalid is not a UUID: %w", nil) + return nil, nhttp.NewError("globalid is not a UUID: %w", nil) } userContent, err := contentForUser(r.Context(), user) if err != nil { - return nil, newError("Failed to get user content: %w", err) + return nil, nhttp.NewError("Failed to get user content: %w", err) } s, err := sourceByGlobalId(r.Context(), org, globalid) if err != nil { - return nil, newError("Failed to get source: %w", err) + return nil, nhttp.NewError("Failed to get source: %w", err) } inspections, err := inspectionsBySource(r.Context(), org, globalid) if err != nil { - return nil, newError("Failed to get inspections: %w", err) + return nil, nhttp.NewError("Failed to get inspections: %w", err) } traps, err := trapsBySource(r.Context(), org, globalid) if err != nil { - return nil, newError("Failed to get traps: %w", err) + return nil, nhttp.NewError("Failed to get traps: %w", err) } treatments, err := treatmentsBySource(r.Context(), org, globalid) if err != nil { - return nil, newError("Failed to get treatments: %w", err) + return nil, nhttp.NewError("Failed to get treatments: %w", err) } treatment_models := modelTreatment(treatments) latlng, err := s.H3Cell.LatLng() if err != nil { - return nil, newError("Failed to get latlng: %w", err) + return nil, nhttp.NewError("Failed to get latlng: %w", err) } data := contentSource{ Inspections: inspections, @@ -148,40 +149,40 @@ func getSource(ctx context.Context, r *http.Request, org *models.Organization, u User: userContent, } - return newResponse("sync/source.html", data), nil + return html.NewResponse("sync/source.html", data), nil } -func getStadia(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*response[contentDashboard], *errorWithStatus) { +func getStadia(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*html.Response[contentDashboard], *nhttp.ErrorWithStatus) { data := contentDashboard{ MapData: ComponentMap{ MapboxToken: config.MapboxToken, }, } - return newResponse("sync/stadia.html", data), nil + return html.NewResponse("sync/stadia.html", data), nil } func getTemplateTest(w http.ResponseWriter, r *http.Request) { html.RenderOrError(w, "sync/template-test.html", nil) } -func getTrap(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentTrap], *errorWithStatus) { +func getTrap(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentTrap], *nhttp.ErrorWithStatus) { globalid_s := chi.URLParam(r, "globalid") if globalid_s == "" { - return nil, newError("No globalid provided: %w", nil) + return nil, nhttp.NewError("No globalid provided: %w", nil) } globalid, err := uuid.Parse(globalid_s) if err != nil { - return nil, newError("globalid is not a UUID: %w", nil) + return nil, nhttp.NewError("globalid is not a UUID: %w", nil) } userContent, err := contentForUser(r.Context(), user) if err != nil { - return nil, newError("Failed to get user content: %w", err) + return nil, nhttp.NewError("Failed to get user content: %w", err) } t, err := trapByGlobalId(r.Context(), org, globalid) if err != nil { - return nil, newError("Failed to get trap: %w", err) + return nil, nhttp.NewError("Failed to get trap: %w", err) } latlng, err := t.H3Cell.LatLng() if err != nil { - return nil, newError("Failed to get latlng: %w", err) + return nil, nhttp.NewError("Failed to get latlng: %w", err) } data := contentTrap{ MapData: ComponentMap{ @@ -198,7 +199,7 @@ func getTrap(ctx context.Context, r *http.Request, org *models.Organization, use Trap: t, User: userContent, } - return newResponse("sync/trap.html", data), nil + return html.NewResponse("sync/trap.html", data), nil } func dashboard(ctx context.Context, w http.ResponseWriter, org *models.Organization, user *models.User) { @@ -260,7 +261,7 @@ func dashboard(ctx context.Context, w http.ResponseWriter, org *models.Organizat } html.RenderOrError(w, "sync/dashboard.html", contentAuthenticated[contentDashboard]{ C: content, - URL: newContentURL(), + URL: html.NewContentURL(), User: userContent, }) } diff --git a/sync/download.go b/sync/download.go index 4a151b4e..2217a5b3 100644 --- a/sync/download.go +++ b/sync/download.go @@ -5,11 +5,13 @@ import ( "net/http" "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/Gleipnir-Technology/nidus-sync/html" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" ) type contentDownloadPlaceholder struct{} -func getDownloadList(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentDownloadPlaceholder], *errorWithStatus) { +func getDownloadList(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentDownloadPlaceholder], *nhttp.ErrorWithStatus) { content := contentDownloadPlaceholder{} - return newResponse("sync/download-list.html", content), nil + return html.NewResponse("sync/download-list.html", content), nil } diff --git a/sync/handler.go b/sync/handler.go new file mode 100644 index 00000000..4937456f --- /dev/null +++ b/sync/handler.go @@ -0,0 +1,119 @@ +package sync + +import ( + "context" + "net/http" + + "github.com/Gleipnir-Technology/nidus-sync/auth" + "github.com/Gleipnir-Technology/nidus-sync/db" + "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/Gleipnir-Technology/nidus-sync/html" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" + "github.com/rs/zerolog/log" +) + +type handlerFunctionGet[T any] func(context.Context, *http.Request, *models.Organization, *models.User) (*html.Response[T], *nhttp.ErrorWithStatus) +type wrappedHandler func(http.ResponseWriter, *http.Request) +type contentAuthenticated[T any] struct { + C T + Config html.ContentConfig + Organization *models.Organization + URL html.ContentURL + User User +} + +// w http.ResponseWriter, r *http.Request, u *models.User) { +func authenticatedHandler[T any](f handlerFunctionGet[T]) http.Handler { + return auth.NewEnsureAuth(func(w http.ResponseWriter, r *http.Request, u *models.User) { + ctx := r.Context() + userContent, err := contentForUser(ctx, u) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + org, err := u.Organization().One(ctx, db.PGInstance.BobDB) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if org == nil { + http.Error(w, "nil org", http.StatusInternalServerError) + return + } + resp, e := f(ctx, r, org, u) + //log.Info().Str("template", template).Err(e).Msg("handler done") + if e != nil { + log.Warn().Int("status", e.Status).Err(e).Str("user message", e.Message).Msg("Responding with an error from sync pages") + http.Error(w, e.Error(), e.Status) + return + } + html.RenderOrError(w, resp.Template, contentAuthenticated[T]{ + C: resp.Content, + Config: html.NewContentConfig(), + Organization: org, + URL: html.NewContentURL(), + User: userContent, + }) + }) +} + +type handlerFunctionPost[T any] func(context.Context, *http.Request, *models.Organization, *models.User, T) (string, *nhttp.ErrorWithStatus) + +func authenticatedHandlerPost[T any](f handlerFunctionPost[T]) http.Handler { + return auth.NewEnsureAuth(func(w http.ResponseWriter, r *http.Request, u *models.User) { + err := r.ParseForm() + if err != nil { + respondError(w, "Failed to parse form", err, http.StatusBadRequest) + return + } + + var content T + + err = decoder.Decode(&content, r.PostForm) + if err != nil { + respondError(w, "Failed to decode form", err, http.StatusBadRequest) + return + } + ctx := r.Context() + org, err := u.Organization().One(ctx, db.PGInstance.BobDB) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + path, e := f(ctx, r, org, u, content) + if e != nil { + http.Error(w, e.Error(), e.Status) + return + } + http.Redirect(w, r, path, http.StatusFound) + }) +} +func authenticatedHandlerPostMultipart[T any](f handlerFunctionPost[T]) http.Handler { + return auth.NewEnsureAuth(func(w http.ResponseWriter, r *http.Request, u *models.User) { + err := r.ParseMultipartForm(32 << 10) // 32 MB buffer + if err != nil { + respondError(w, "Failed to parse form", err, http.StatusBadRequest) + return + } + + var content T + + err = decoder.Decode(&content, r.PostForm) + if err != nil { + respondError(w, "Failed to decode form", err, http.StatusBadRequest) + return + } + ctx := r.Context() + org, err := u.Organization().One(ctx, db.PGInstance.BobDB) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + path, e := f(ctx, r, org, u, content) + if e != nil { + http.Error(w, e.Error(), e.Status) + return + } + http.Redirect(w, r, path, http.StatusFound) + }) +} diff --git a/sync/intelligence.go b/sync/intelligence.go index b8c1037c..13f45635 100644 --- a/sync/intelligence.go +++ b/sync/intelligence.go @@ -5,10 +5,12 @@ import ( "net/http" "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/Gleipnir-Technology/nidus-sync/html" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" ) type contentIntelligenceRoot struct{} -func getIntelligenceRoot(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentIntelligenceRoot], *errorWithStatus) { - return newResponse("sync/intelligence-root.html", contentIntelligenceRoot{}), nil +func getIntelligenceRoot(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentIntelligenceRoot], *nhttp.ErrorWithStatus) { + return html.NewResponse("sync/intelligence-root.html", contentIntelligenceRoot{}), nil } diff --git a/sync/mailer.go b/sync/mailer.go index f791fe39..41cc2707 100644 --- a/sync/mailer.go +++ b/sync/mailer.go @@ -16,7 +16,7 @@ import ( ) type contentMailer struct { - Config contentConfig + Config html.ContentConfig DocumentID string LogoURL string Organization *models.Organization @@ -71,7 +71,7 @@ func getMailerPreview(w http.ResponseWriter, r *http.Request) { } doc_id := uuid.New() html.RenderOrError(w, "sync/mailer.html", contentMailer{ - Config: newContentConfig(), + Config: html.NewContentConfig(), DocumentID: doc_id.String(), LogoURL: config.MakeURLNidus("/api/district/%s/logo", org.Slug.GetOr("unset")), Organization: org, diff --git a/sync/messages.go b/sync/messages.go index 6dc06422..5eae4108 100644 --- a/sync/messages.go +++ b/sync/messages.go @@ -5,11 +5,13 @@ import ( "net/http" "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/Gleipnir-Technology/nidus-sync/html" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" ) type contentMessageList struct{} -func getMessageList(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentMessageList], *errorWithStatus) { +func getMessageList(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentMessageList], *nhttp.ErrorWithStatus) { content := contentMessageList{} - return newResponse("sync/message-list.html", content), nil + return html.NewResponse("sync/message-list.html", content), nil } diff --git a/sync/mock.go b/sync/mock.go index 17c2d955..3c8c87b9 100644 --- a/sync/mock.go +++ b/sync/mock.go @@ -54,7 +54,7 @@ func addMock(r chi.Router, path string, template string) { } type contentMock struct { - Config contentConfig + Config html.ContentConfig DistrictName string URLs ContentMockURLs } @@ -66,7 +66,7 @@ func renderMock(template_name string) http.HandlerFunc { code = "abc-123" } data := contentMock{ - Config: newContentConfig(), + Config: html.NewContentConfig(), DistrictName: "Delta MVCD", URLs: ContentMockURLs{ Dispatch: "/mock/dispatch", @@ -91,13 +91,13 @@ func renderMock(template_name string) http.HandlerFunc { } type contentMockList struct { - Config contentConfig + Config html.ContentConfig Mocks []mock } func renderMockList(w http.ResponseWriter, r *http.Request) { data := contentMockList{ - Config: newContentConfig(), + Config: html.NewContentConfig(), Mocks: mocks, } html.RenderOrError(w, "sync/mock/root.html", data) diff --git a/sync/notification.go b/sync/notification.go index 54fc808b..6f85c32d 100644 --- a/sync/notification.go +++ b/sync/notification.go @@ -8,6 +8,8 @@ import ( //"time" "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/Gleipnir-Technology/nidus-sync/html" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" "github.com/Gleipnir-Technology/nidus-sync/notification" //"github.com/Gleipnir-Technology/bob" //"github.com/Gleipnir-Technology/bob/dialect/psql" @@ -22,12 +24,12 @@ type contentNotificationList struct { Notifications []notification.Notification } -func getNotificationList(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*response[contentNotificationList], *errorWithStatus) { +func getNotificationList(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*html.Response[contentNotificationList], *nhttp.ErrorWithStatus) { notifications, err := notification.ForUser(ctx, u) if err != nil { - return nil, newError("Failed to get notifications: %w", err) + return nil, nhttp.NewError("Failed to get notifications: %w", err) } - return newResponse("sync/notification-list.html", contentNotificationList{ + return html.NewResponse("sync/notification-list.html", contentNotificationList{ Notifications: notifications, }), nil } diff --git a/sync/oauth.go b/sync/oauth.go index 35722b4f..2451f388 100644 --- a/sync/oauth.go +++ b/sync/oauth.go @@ -10,6 +10,8 @@ import ( "github.com/Gleipnir-Technology/nidus-sync/background" "github.com/Gleipnir-Technology/nidus-sync/config" "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/Gleipnir-Technology/nidus-sync/html" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" "github.com/rs/zerolog/log" ) @@ -65,7 +67,7 @@ func getArcgisOauthCallback(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, config.MakeURLNidus("/"), http.StatusFound) } -func getOAuthRefresh(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentOauthPrompt], *errorWithStatus) { +func getOAuthRefresh(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentOauthPrompt], *nhttp.ErrorWithStatus) { data := contentOauthPrompt{} - return newResponse("sync/oauth-prompt.html", data), nil + return html.NewResponse("sync/oauth-prompt.html", data), nil } diff --git a/sync/operations.go b/sync/operations.go index 1930a6cb..d56100d2 100644 --- a/sync/operations.go +++ b/sync/operations.go @@ -5,10 +5,12 @@ import ( "net/http" "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/Gleipnir-Technology/nidus-sync/html" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" ) type contentOperationsRoot struct{} -func getOperationsRoot(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentOperationsRoot], *errorWithStatus) { - return newResponse("sync/operations-root.html", contentOperationsRoot{}), nil +func getOperationsRoot(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentOperationsRoot], *nhttp.ErrorWithStatus) { + return html.NewResponse("sync/operations-root.html", contentOperationsRoot{}), nil } diff --git a/sync/parcel.go b/sync/parcel.go index d3329cca..1f7a7d32 100644 --- a/sync/parcel.go +++ b/sync/parcel.go @@ -5,10 +5,12 @@ import ( "net/http" "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/Gleipnir-Technology/nidus-sync/html" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" ) type contentParcel struct{} -func getParcel(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentParcel], *errorWithStatus) { - return newResponse("sync/parcel.html", contentParcel{}), nil +func getParcel(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentParcel], *nhttp.ErrorWithStatus) { + return html.NewResponse("sync/parcel.html", contentParcel{}), nil } diff --git a/sync/planning.go b/sync/planning.go index bca944ce..b55df1ea 100644 --- a/sync/planning.go +++ b/sync/planning.go @@ -5,10 +5,12 @@ import ( "net/http" "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/Gleipnir-Technology/nidus-sync/html" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" ) type contentPlanningRoot struct{} -func getPlanningRoot(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentPlanningRoot], *errorWithStatus) { - return newResponse("sync/planning-root.html", contentPlanningRoot{}), nil +func getPlanningRoot(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentPlanningRoot], *nhttp.ErrorWithStatus) { + return html.NewResponse("sync/planning-root.html", contentPlanningRoot{}), nil } diff --git a/sync/pool.go b/sync/pool.go index b5c8ec56..a2c4395d 100644 --- a/sync/pool.go +++ b/sync/pool.go @@ -5,16 +5,18 @@ import ( "net/http" "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/Gleipnir-Technology/nidus-sync/html" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" ) type contentPoolList struct{} -func getPoolList(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentPoolList], *errorWithStatus) { - return newResponse("sync/pool-list.html", contentPoolList{}), nil +func getPoolList(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentPoolList], *nhttp.ErrorWithStatus) { + return html.NewResponse("sync/pool-list.html", contentPoolList{}), nil } -func getPoolCreate(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentPoolList], *errorWithStatus) { - return newResponse("sync/pool-upload.html", contentPoolList{}), nil +func getPoolCreate(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentPoolList], *nhttp.ErrorWithStatus) { + return html.NewResponse("sync/pool-upload.html", contentPoolList{}), nil } -func getPoolByID(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentPoolList], *errorWithStatus) { - return newResponse("sync/pool-by-id.html", contentPoolList{}), nil +func getPoolByID(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentPoolList], *nhttp.ErrorWithStatus) { + return html.NewResponse("sync/pool-by-id.html", contentPoolList{}), nil } diff --git a/sync/radar.go b/sync/radar.go index c2d74ea2..d70452e4 100644 --- a/sync/radar.go +++ b/sync/radar.go @@ -6,19 +6,21 @@ import ( "github.com/Gleipnir-Technology/nidus-sync/db" "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/Gleipnir-Technology/nidus-sync/html" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" ) type contentRadar struct { Organization *models.Organization } -func getRadar(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentRadar], *errorWithStatus) { +func getRadar(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentRadar], *nhttp.ErrorWithStatus) { org, err := user.Organization().One(ctx, db.PGInstance.BobDB) if err != nil { - return nil, newError("get org: %w", err) + return nil, nhttp.NewError("get org: %w", err) } data := contentRadar{ Organization: org, } - return newResponse("sync/radar.html", data), nil + return html.NewResponse("sync/radar.html", data), nil } diff --git a/sync/review.go b/sync/review.go index 996cbeca..12facd2b 100644 --- a/sync/review.go +++ b/sync/review.go @@ -5,10 +5,12 @@ import ( "net/http" "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/Gleipnir-Technology/nidus-sync/html" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" ) type contentReviewRoot struct{} -func getReviewRoot(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentReviewRoot], *errorWithStatus) { - return newResponse("sync/review-root.html", contentReviewRoot{}), nil +func getReviewRoot(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentReviewRoot], *nhttp.ErrorWithStatus) { + return html.NewResponse("sync/review-root.html", contentReviewRoot{}), nil } diff --git a/sync/routes.go b/sync/routes.go index 6a54ffc8..b8ad0325 100644 --- a/sync/routes.go +++ b/sync/routes.go @@ -1,17 +1,10 @@ package sync import ( - "context" - "fmt" - "net/http" - "github.com/Gleipnir-Technology/nidus-sync/api" "github.com/Gleipnir-Technology/nidus-sync/auth" - "github.com/Gleipnir-Technology/nidus-sync/db" - "github.com/Gleipnir-Technology/nidus-sync/db/models" "github.com/Gleipnir-Technology/nidus-sync/html" "github.com/go-chi/chi/v5" - "github.com/rs/zerolog/log" ) func Router() chi.Router { @@ -98,147 +91,3 @@ func Router() chi.Router { html.AddStaticRoute(r, "/static") return r } - -type errorWithStatus struct { - Message string - Status int -} - -func (e *errorWithStatus) Error() string { - return e.Message -} -func newError(mesg_format string, args ...interface{}) *errorWithStatus { - return newErrorStatus(http.StatusInternalServerError, mesg_format, args...) -} -func newErrorMaybe(mesg_format string, err error, args ...interface{}) *errorWithStatus { - if err == nil { - return nil - } - allArgs := append([]interface{}{err}, args...) - return newErrorStatus(http.StatusInternalServerError, mesg_format, allArgs...) -} -func newErrorStatus(status int, mesg_format string, args ...interface{}) *errorWithStatus { - w := fmt.Errorf(mesg_format, args...) - return &errorWithStatus{ - Message: w.Error(), - Status: status, - } -} - -type response[T any] struct { - content T - template string -} - -func newResponse[T any](template string, content T) *response[T] { - return &response[T]{ - content: content, - template: template, - } -} - -type handlerFunctionGet[T any] func(context.Context, *http.Request, *models.Organization, *models.User) (*response[T], *errorWithStatus) -type wrappedHandler func(http.ResponseWriter, *http.Request) -type contentAuthenticated[T any] struct { - C T - Config contentConfig - Organization *models.Organization - URL contentURL - User User -} - -// w http.ResponseWriter, r *http.Request, u *models.User) { -func authenticatedHandler[T any](f handlerFunctionGet[T]) http.Handler { - return auth.NewEnsureAuth(func(w http.ResponseWriter, r *http.Request, u *models.User) { - ctx := r.Context() - userContent, err := contentForUser(ctx, u) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - org, err := u.Organization().One(ctx, db.PGInstance.BobDB) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - resp, e := f(ctx, r, org, u) - //log.Info().Str("template", template).Err(e).Msg("handler done") - if e != nil { - log.Warn().Int("status", e.Status).Err(e).Str("user message", e.Message).Msg("Responding with an error from sync pages") - http.Error(w, e.Error(), e.Status) - return - } - if org == nil { - http.Error(w, "nil org", http.StatusInternalServerError) - return - } - html.RenderOrError(w, resp.template, contentAuthenticated[T]{ - C: resp.content, - Config: newContentConfig(), - Organization: org, - URL: newContentURL(), - User: userContent, - }) - }) -} - -type handlerFunctionPost[T any] func(context.Context, *http.Request, *models.Organization, *models.User, T) (string, *errorWithStatus) - -func authenticatedHandlerPost[T any](f handlerFunctionPost[T]) http.Handler { - return auth.NewEnsureAuth(func(w http.ResponseWriter, r *http.Request, u *models.User) { - err := r.ParseForm() - if err != nil { - respondError(w, "Failed to parse form", err, http.StatusBadRequest) - return - } - - var content T - - err = decoder.Decode(&content, r.PostForm) - if err != nil { - respondError(w, "Failed to decode form", err, http.StatusBadRequest) - return - } - ctx := r.Context() - org, err := u.Organization().One(ctx, db.PGInstance.BobDB) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - path, e := f(ctx, r, org, u, content) - if e != nil { - http.Error(w, e.Error(), e.Status) - return - } - http.Redirect(w, r, path, http.StatusFound) - }) -} -func authenticatedHandlerPostMultipart[T any](f handlerFunctionPost[T]) http.Handler { - return auth.NewEnsureAuth(func(w http.ResponseWriter, r *http.Request, u *models.User) { - err := r.ParseMultipartForm(32 << 10) // 32 MB buffer - if err != nil { - respondError(w, "Failed to parse form", err, http.StatusBadRequest) - return - } - - var content T - - err = decoder.Decode(&content, r.PostForm) - if err != nil { - respondError(w, "Failed to decode form", err, http.StatusBadRequest) - return - } - ctx := r.Context() - org, err := u.Organization().One(ctx, db.PGInstance.BobDB) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - path, e := f(ctx, r, org, u, content) - if e != nil { - http.Error(w, e.Error(), e.Status) - return - } - http.Redirect(w, r, path, http.StatusFound) - }) -} diff --git a/sync/service-request.go b/sync/service-request.go index 48b56944..2035dd6a 100644 --- a/sync/service-request.go +++ b/sync/service-request.go @@ -7,6 +7,8 @@ import ( "github.com/Gleipnir-Technology/nidus-sync/config" "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/Gleipnir-Technology/nidus-sync/html" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" ) type contentActiveServiceRequest struct { @@ -32,11 +34,11 @@ type contentServiceRequestList struct { ClosedRequests []contentClosedServiceRequest } -func getServiceRequestDetail(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentServiceRequestDetail], *errorWithStatus) { +func getServiceRequestDetail(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentServiceRequestDetail], *nhttp.ErrorWithStatus) { content := contentServiceRequestDetail{} - return newResponse("sync/service-request-detail.html", content), nil + return html.NewResponse("sync/service-request-detail.html", content), nil } -func getServiceRequestList(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentServiceRequestList], *errorWithStatus) { +func getServiceRequestList(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentServiceRequestList], *nhttp.ErrorWithStatus) { now := time.Now() content := contentServiceRequestList{ ActiveRequests: []contentActiveServiceRequest{ @@ -112,5 +114,5 @@ func getServiceRequestList(ctx context.Context, r *http.Request, org *models.Org }, }, } - return newResponse("sync/service-request-list.html", content), nil + return html.NewResponse("sync/service-request-list.html", content), nil } diff --git a/sync/signin.go b/sync/signin.go index 479a310e..cdb6c4aa 100644 --- a/sync/signin.go +++ b/sync/signin.go @@ -98,8 +98,8 @@ func postSignup(w http.ResponseWriter, r *http.Request) { type contentUnauthenticated[T any] struct { C T - Config contentConfig - URL contentURL + Config html.ContentConfig + URL html.ContentURL } func signin(w http.ResponseWriter, errorCode string, next string) { @@ -111,8 +111,8 @@ func signin(w http.ResponseWriter, errorCode string, next string) { InvalidCredentials: errorCode == "invalid-credentials", Next: next, }, - Config: newContentConfig(), - URL: newContentURL(), + Config: html.NewContentConfig(), + URL: html.NewContentURL(), } html.RenderOrError(w, "sync/signin.html", data) } diff --git a/sync/sudo.go b/sync/sudo.go index 2e485ac6..a28efe83 100644 --- a/sync/sudo.go +++ b/sync/sudo.go @@ -10,6 +10,8 @@ import ( "github.com/Gleipnir-Technology/nidus-sync/config" "github.com/Gleipnir-Technology/nidus-sync/db/enums" "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/Gleipnir-Technology/nidus-sync/html" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" "github.com/gorilla/schema" "github.com/rs/zerolog/log" ) @@ -19,9 +21,9 @@ type contentSudo struct { ForwardEmailNidusAddress string } -func getSudo(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentSudo], *errorWithStatus) { +func getSudo(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentSudo], *nhttp.ErrorWithStatus) { if user.Role != enums.UserroleRoot { - return nil, &errorWithStatus{ + return nil, &nhttp.ErrorWithStatus{ Message: "You have to be a root user to access this", Status: http.StatusForbidden, } @@ -30,7 +32,7 @@ func getSudo(ctx context.Context, r *http.Request, org *models.Organization, use ForwardEmailRMOAddress: config.ForwardEmailRMOAddress, ForwardEmailNidusAddress: config.ForwardEmailNidusAddress, } - return newResponse("sync/sudo.html", content), nil + return html.NewResponse("sync/sudo.html", content), nil } var decoder = schema.NewDecoder() @@ -42,9 +44,9 @@ type FormEmail struct { To string `schema:"emailTo"` } -func postSudoEmail(ctx context.Context, r *http.Request, org *models.Organization, u *models.User, e FormEmail) (string, *errorWithStatus) { +func postSudoEmail(ctx context.Context, r *http.Request, org *models.Organization, u *models.User, e FormEmail) (string, *nhttp.ErrorWithStatus) { if u.Role != enums.UserroleRoot { - return "", &errorWithStatus{ + return "", &nhttp.ErrorWithStatus{ Message: "You must have sudo powers to do this", Status: http.StatusForbidden, } @@ -71,9 +73,9 @@ type FormSMS struct { Phone string `schema:"smsPhone"` } -func postSudoSMS(ctx context.Context, r *http.Request, org *models.Organization, u *models.User, sms FormSMS) (string, *errorWithStatus) { +func postSudoSMS(ctx context.Context, r *http.Request, org *models.Organization, u *models.User, sms FormSMS) (string, *nhttp.ErrorWithStatus) { if u.Role != enums.UserroleRoot { - return "", &errorWithStatus{ + return "", &nhttp.ErrorWithStatus{ Message: "You must have sudo powers to do this", Status: http.StatusForbidden, } diff --git a/sync/text.go b/sync/text.go index 6ddc774d..1d22f077 100644 --- a/sync/text.go +++ b/sync/text.go @@ -5,11 +5,13 @@ import ( "net/http" "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/Gleipnir-Technology/nidus-sync/html" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" ) type contentTextMessages struct{} -func getTextMessages(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*response[contentTextMessages], *errorWithStatus) { +func getTextMessages(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*html.Response[contentTextMessages], *nhttp.ErrorWithStatus) { content := contentTextMessages{} - return newResponse("sync/text-messages.html", content), nil + return html.NewResponse("sync/text-messages.html", content), nil } diff --git a/sync/upload.go b/sync/upload.go index 47cd7cad..4e5c0388 100644 --- a/sync/upload.go +++ b/sync/upload.go @@ -8,10 +8,12 @@ import ( "github.com/Gleipnir-Technology/nidus-sync/db/enums" "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/Gleipnir-Technology/nidus-sync/html" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" "github.com/Gleipnir-Technology/nidus-sync/platform" "github.com/Gleipnir-Technology/nidus-sync/userfile" "github.com/go-chi/chi/v5" - "github.com/rs/zerolog/log" + //"github.com/rs/zerolog/log" ) type contentUploadList struct { @@ -19,11 +21,11 @@ type contentUploadList struct { } type contentUploadPlaceholder struct{} -func getUploadList(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*response[contentUploadList], *errorWithStatus) { +func getUploadList(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*html.Response[contentUploadList], *nhttp.ErrorWithStatus) { rows, err := platform.UploadSummaryList(ctx, org) - return newResponse("sync/upload-list.html", contentUploadList{ + return html.NewResponse("sync/upload-list.html", contentUploadList{ RecentUploads: rows, - }), newErrorMaybe("get upload list: %w", err) + }), nhttp.NewErrorMaybe("get upload list: %w", err) } type contentUploadDetail struct { @@ -36,110 +38,108 @@ type contentUploadPoolList struct { } type contentUploadPool struct{} -func getUploadPool(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*response[contentUploadPool], *errorWithStatus) { +func getUploadPool(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*html.Response[contentUploadPool], *nhttp.ErrorWithStatus) { data := contentUploadPool{} - return newResponse("sync/upload-csv-pool.html", data), nil + return html.NewResponse("sync/upload-csv-pool.html", data), nil } type contentUploadPoolFlyoverCreate struct{} -func getUploadPoolFlyoverCreate(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*response[contentUploadPoolFlyoverCreate], *errorWithStatus) { +func getUploadPoolFlyoverCreate(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*html.Response[contentUploadPoolFlyoverCreate], *nhttp.ErrorWithStatus) { data := contentUploadPoolFlyoverCreate{} - return newResponse("sync/upload-csv-pool-flyover.html", data), nil + return html.NewResponse("sync/upload-csv-pool-flyover.html", data), nil } type contentUploadPoolCustomCreate struct{} -func getUploadPoolCustomCreate(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*response[contentUploadPoolCustomCreate], *errorWithStatus) { +func getUploadPoolCustomCreate(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*html.Response[contentUploadPoolCustomCreate], *nhttp.ErrorWithStatus) { data := contentUploadPoolCustomCreate{} - return newResponse("sync/upload-csv-pool-custom.html", data), nil + return html.NewResponse("sync/upload-csv-pool-custom.html", data), nil } -func getUploadByID(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*response[contentUploadDetail], *errorWithStatus) { - test := newContentURLUpload() - log.Info().Str("output", test.Discard(123)).Send() +func getUploadByID(ctx context.Context, r *http.Request, org *models.Organization, u *models.User) (*html.Response[contentUploadDetail], *nhttp.ErrorWithStatus) { file_id_str := chi.URLParam(r, "id") file_id_, err := strconv.ParseInt(file_id_str, 10, 32) if err != nil { - return nil, newError("Failed to parse file_id: %w", err) + return nil, nhttp.NewError("Failed to parse file_id: %w", err) } file_id := int32(file_id_) detail, err := platform.GetUploadPoolDetail(ctx, u.OrganizationID, file_id) if err != nil { - return nil, newError("Failed to get pool: %w", err) + return nil, nhttp.NewError("Failed to get pool: %w", err) } data := contentUploadDetail{ CSVFileID: file_id, Organization: org, Upload: detail, } - return newResponse("sync/upload-by-id.html", data), nil + return html.NewResponse("sync/upload-by-id.html", data), nil } type FormUploadCommit struct{} -func postUploadCommit(ctx context.Context, r *http.Request, org *models.Organization, u *models.User, f FormUploadCommit) (string, *errorWithStatus) { +func postUploadCommit(ctx context.Context, r *http.Request, org *models.Organization, u *models.User, f FormUploadCommit) (string, *nhttp.ErrorWithStatus) { file_id_str := chi.URLParam(r, "id") file_id_, err := strconv.ParseInt(file_id_str, 10, 32) if err != nil { - return "", newError("Failed to parse file_id: %w", err) + return "", nhttp.NewError("Failed to parse file_id: %w", err) } err = platform.UploadCommit(ctx, org, int32(file_id_)) if err != nil { - return "", newError("Failed to mark discarded: %w", err) + return "", nhttp.NewError("Failed to mark discarded: %w", err) } return "/configuration/upload", nil } type FormUploadDiscard struct{} -func postUploadDiscard(ctx context.Context, r *http.Request, org *models.Organization, u *models.User, f FormUploadDiscard) (string, *errorWithStatus) { +func postUploadDiscard(ctx context.Context, r *http.Request, org *models.Organization, u *models.User, f FormUploadDiscard) (string, *nhttp.ErrorWithStatus) { file_id_str := chi.URLParam(r, "id") file_id_, err := strconv.ParseInt(file_id_str, 10, 32) if err != nil { - return "", newError("Failed to parse file_id: %w", err) + return "", nhttp.NewError("Failed to parse file_id: %w", err) } err = platform.UploadDiscard(ctx, org, int32(file_id_)) if err != nil { - return "", newError("Failed to mark discarded: %w", err) + return "", nhttp.NewError("Failed to mark discarded: %w", err) } return "/configuration/upload", nil } type FormUploadPool struct{} -func postUploadPoolFlyoverCreate(ctx context.Context, r *http.Request, org *models.Organization, u *models.User, f FormUploadPool) (string, *errorWithStatus) { +func postUploadPoolFlyoverCreate(ctx context.Context, r *http.Request, org *models.Organization, u *models.User, f FormUploadPool) (string, *nhttp.ErrorWithStatus) { uploads, err := userfile.SaveFileUpload(r, "csvfile", userfile.CollectionCSV) if err != nil { - return "", newError("Failed to extract image uploads: %s", err) + return "", nhttp.NewError("Failed to extract image uploads: %s", err) } if len(uploads) == 0 { - return "", newErrorStatus(http.StatusBadRequest, "No upload found") + return "", nhttp.NewErrorStatus(http.StatusBadRequest, "No upload found") } if len(uploads) != 1 { - return "", newErrorStatus(http.StatusBadRequest, "You must only submit one file at a time") + return "", nhttp.NewErrorStatus(http.StatusBadRequest, "You must only submit one file at a time") } upload := uploads[0] saved_upload, err := platform.NewUpload(r.Context(), u, upload, enums.FileuploadCsvtypeFlyover) if err != nil { - return "", newError("Failed to create new pool: %w", err) + return "", nhttp.NewError("Failed to create new pool: %w", err) } return fmt.Sprintf("/configuration/upload/%d", saved_upload.ID), nil } -func postUploadPoolCustomCreate(ctx context.Context, r *http.Request, org *models.Organization, u *models.User, f FormUploadPool) (string, *errorWithStatus) { +func postUploadPoolCustomCreate(ctx context.Context, r *http.Request, org *models.Organization, u *models.User, f FormUploadPool) (string, *nhttp.ErrorWithStatus) { uploads, err := userfile.SaveFileUpload(r, "csvfile", userfile.CollectionCSV) if err != nil { - return "", newError("Failed to extract image uploads: %s", err) + return "", nhttp.NewError("Failed to extract image uploads: %s", err) } if len(uploads) == 0 { - return "", newErrorStatus(http.StatusBadRequest, "No upload found") + return "", nhttp.NewErrorStatus(http.StatusBadRequest, "No upload found") } if len(uploads) != 1 { - return "", newErrorStatus(http.StatusBadRequest, "You must only submit one file at a time") + return "", nhttp.NewErrorStatus(http.StatusBadRequest, "You must only submit one file at a time") } upload := uploads[0] pool_upload, err := platform.NewUpload(r.Context(), u, upload, enums.FileuploadCsvtypePoollist) if err != nil { - return "", newError("Failed to create new pool: %w", err) + return "", nhttp.NewError("Failed to create new pool: %w", err) } return fmt.Sprintf("/configuration/upload/%d", pool_upload.ID), nil }