From 8bdd18649d154b9c6631b8e484da238724f2943c Mon Sep 17 00:00:00 2001 From: Eli Ribble Date: Tue, 28 Apr 2026 06:36:55 +0000 Subject: [PATCH] Separate out a public and non-public halves to publicreport APIs This prevents us from leaking text messaging details on public endpoints. --- api/publicreport.go | 2 +- api/routes.go | 99 +++++++++++++++-------------- platform/publicreport.go | 30 ++++----- platform/publicreport/log.go | 26 ++++---- platform/publicreport/report.go | 28 ++++---- platform/signal.go | 2 +- resource/communication.go | 2 +- resource/publicreport.go | 37 ++++++++++- resource/publicreport_compliance.go | 13 ++-- resource/publicreport_nuisance.go | 7 +- resource/publicreport_water.go | 32 ++++++---- ts/rmo/view/StatusByID.vue | 27 +------- 12 files changed, 169 insertions(+), 136 deletions(-) diff --git a/api/publicreport.go b/api/publicreport.go index 195c1a41..02e362d1 100644 --- a/api/publicreport.go +++ b/api/publicreport.go @@ -26,7 +26,7 @@ type formPublicreportInvalid struct { } func postPublicreportInvalid(ctx context.Context, r *http.Request, user platform.User, req formPublicreportSignal) (string, *nhttp.ErrorWithStatus) { - err := platform.PublicreportInvalid(ctx, user, req.ReportID) + err := platform.PublicReportInvalid(ctx, user, req.ReportID) if err != nil { return "", nhttp.NewError("create signal: %w", err) } diff --git a/api/routes.go b/api/routes.go index cc89c38a..e817f07d 100644 --- a/api/routes.go +++ b/api/routes.go @@ -9,12 +9,59 @@ import ( func AddRoutes(r *mux.Router) { router := resource.NewRouter(r) + + compliance_request := resource.ComplianceRequest(router) + district := resource.District(router) + geocode := resource.Geocode(router) + lob_hook := resource.LobHook(router) + nuisance := resource.Nuisance(router) + pr_compliance := resource.PublicReportCompliance(router) + publicreport := resource.Publicreport(router) + publicreport_notification := resource.PublicreportNotification(router) + qrcode := resource.QRCode(router) + service_request := resource.ServiceRequest(router) + water := resource.Water(router) + //r.Use(render.SetContentType(render.ContentTypeJSON)) // Unauthenticated endpoints r.HandleFunc("", handlerJSON(getRoot)) + r.HandleFunc("/compliance-request/image/pool/{public_id}", compliance_request.ImagePoolGet).Methods("GET").Name("compliance-request.image.pool.ByIDGet") + r.Handle("/district", handlerJSONSlice(district.List)).Methods("GET") + r.Handle("/district/{id}", handlerJSON(district.GetByID)).Methods("GET").Name("district.ByIDGet") + r.HandleFunc("/district/{slug}/logo", apiGetDistrictLogo).Methods("GET").Name("district.logo.BySlug") + r.Handle("/geocode/by-gid/{id:.*}", handlerJSON(geocode.ByGID)).Methods("GET") + r.Handle("/geocode/reverse", handlerJSONPost(geocode.Reverse)).Methods("POST") + r.Handle("/geocode/reverse/closest", handlerJSONPost(geocode.ReverseClosest)).Methods("POST") + r.Handle("/geocode/suggestion", handlerJSONSlice(geocode.SuggestionList)).Methods("GET") + r.Handle("/lob/event", handlerBasic(lob_hook.Event)).Methods("POST") + + r.Handle("/publicreport-notification", handlerJSONPost(publicreport_notification.Create)).Methods("POST") + r.Handle("/qr-code/mailer/{code}", handlerBasic(qrcode.Mailer)).Methods("GET") + r.Handle("/qr-code/marketing", handlerBasic(qrcode.Marketing)).Methods("GET") + r.Handle("/qr-code/report/{code}", handlerBasic(qrcode.Report)).Methods("GET") + r.HandleFunc("/rmo/compliance", handlerJSONPost(pr_compliance.Create)).Methods("POST") + r.HandleFunc("/rmo/nuisance", handlerFormPost(nuisance.Create)).Methods("POST") + r.Handle("/rmo/publicreport/{id}", handlerBasic(publicreport.ByIDPublic)).Methods("GET").Name("publicreport.ByIDGetPublic") + r.Handle("/rmo/publicreport/compliance/{id}/image", handlerFormPost(publicreport.ImageCreate)).Methods("POST") + r.Handle("/rmo/publicreport/compliance/{id}", handlerJSON(pr_compliance.ByIDPublic)).Methods("GET").Name("publicreport.compliance.ByIDGetPublic") + r.Handle("/rmo/publicreport/compliance/{id}", handlerJSONPut(pr_compliance.Update)).Methods("PUT") + r.Handle("/rmo/publicreport/nuisance/{id}", handlerJSON(nuisance.ByIDPublic)).Methods("GET").Name("publicreport.nuisance.ByIDGetPublic") + r.Handle("/rmo/publicreport/water/{id}", handlerJSON(water.ByIDPublic)).Methods("GET").Name("publicreport.water.ByIDGet") + r.Handle("/rmo/publicreport/{id}", handlerBasic(publicreport.ByIDPublic)).Methods("GET").Name("publicreport.ByIDGetPublicPublic") + r.HandleFunc("/rmo/water", handlerFormPost(water.Create)).Methods("POST") r.HandleFunc("/signin", handlerJSONPost(postSignin)) r.Handle("/signout", authenticatedHandlerBasic(postSignout)) r.HandleFunc("/signup", handlerJSONPost(postSignup)) + r.HandleFunc("/twilio/call", twilioCallPost).Methods("POST") + r.HandleFunc("/twilio/call/status", twilioCallStatusPost).Methods("POST") + r.HandleFunc("/twilio/message", twilioMessagePost).Methods("POST") + r.HandleFunc("/twilio/text", twilioTextPost).Methods("POST") + r.HandleFunc("/twilio/text/status", twilioTextStatusPost).Methods("POST") + r.HandleFunc("/voipms/text", voipmsTextGet).Methods("GET") + r.HandleFunc("/voipms/text", voipmsTextPost).Methods("POST") + r.HandleFunc("/webhook/fieldseeker", webhookFieldseeker).Methods("GET") + r.HandleFunc("/webhook/fieldseeker", webhookFieldseeker).Methods("POST") + // Authenticated endpoints r.Handle("/audio/{uuid}", auth.NewEnsureAuth(apiAudioPost)).Methods("POST") r.Handle("/audio/{uuid}/content", auth.NewEnsureAuth(apiAudioContentPost)).Methods("POST") @@ -24,9 +71,7 @@ func AddRoutes(r *mux.Router) { r.Handle("/client/ios", auth.NewEnsureAuth(handleClientIos)).Methods("GET") communication := resource.Communication(router) r.Handle("/communication", authenticatedHandlerJSON(communication.List)).Methods("GET") - compliance_request := resource.ComplianceRequest(router) r.Handle("/compliance-request/mailer", authenticatedHandlerJSONPost(compliance_request.CreateMailer)).Methods("POST") - r.HandleFunc("/compliance-request/image/pool/{public_id}", compliance_request.ImagePoolGet).Methods("GET").Name("compliance-request.image.pool.ByIDGet") //r.HandleFunc("/compliance-request/image/pool/{public_id}", getComplianceRequestImagePool).Methods("GET") r.Handle("/configuration/integration/arcgis", authenticatedHandlerJSONPost(postConfigurationIntegrationArcgis)).Methods("POST") r.Handle("/events", auth.NewEnsureAuth(streamEvents)).Methods("GET") @@ -39,32 +84,24 @@ func AddRoutes(r *mux.Router) { lead := resource.Lead(r) r.Handle("/leads", authenticatedHandlerJSON(lead.List)).Methods("GET") r.Handle("/leads", authenticatedHandlerJSONPost(lead.Create)).Methods("POST") - lob_hook := resource.LobHook(router) - r.Handle("/lob/event", handlerBasic(lob_hook.Event)).Methods("POST") mailer := resource.Mailer(router) r.Handle("/mailer", authenticatedHandlerJSONSlice(mailer.List)).Methods("GET") r.Handle("/mailer/{id}", authenticatedHandlerJSONPost(mailer.ByIDGet)).Methods("GET").Name("mailer.ByIDGet") r.Handle("/mosquito-source", auth.NewEnsureAuth(apiMosquitoSource)).Methods("GET") - qrcode := resource.QRCode(router) - r.Handle("/qr-code/mailer/{code}", handlerBasic(qrcode.Mailer)).Methods("GET") - r.Handle("/qr-code/marketing", handlerBasic(qrcode.Marketing)).Methods("GET") - r.Handle("/qr-code/report/{code}", handlerBasic(qrcode.Report)).Methods("GET") - r.Handle("/publicreport/invalid", authenticatedHandlerJSONPost(postPublicreportInvalid)).Methods("POST") r.Handle("/publicreport/signal", authenticatedHandlerJSONPost(postPublicreportSignal)).Methods("POST") r.Handle("/publicreport/message", authenticatedHandlerJSONPost(postPublicreportMessage)).Methods("POST") + r.Handle("/publicreport/{id}", authenticatedHandlerBasic(publicreport.ByID)).Methods("GET").Name("publicreport.ByIDGetPublic") + r.Handle("/publicreport/compliance/{id}", authenticatedHandlerJSON(pr_compliance.ByID)).Methods("GET").Name("publicreport.compliance.ByIDGet") + r.Handle("/publicreport/nuisance/{id}", authenticatedHandlerJSON(nuisance.ByID)).Methods("GET").Name("publicreport.nuisance.ByIDGet") + r.Handle("/publicreport/water/{id}", authenticatedHandlerJSON(water.ByID)).Methods("GET").Name("publicreport.water.ByIDGet") + + r.Handle("/publicreport-notification", handlerJSONPost(publicreport_notification.Create)).Methods("POST") r.Handle("/review/pool", authenticatedHandlerJSONPost(postReviewPool)).Methods("POST") review_task := resource.ReviewTask(r) r.Handle("/review-task", authenticatedHandlerJSON(review_task.List)).Methods("GET") - pr_compliance := resource.PublicReportCompliance(router) - r.HandleFunc("/rmo/compliance", handlerJSONPost(pr_compliance.Create)).Methods("POST") - nuisance := resource.Nuisance(router) - r.HandleFunc("/rmo/nuisance", handlerFormPost(nuisance.Create)).Methods("POST") - water := resource.Water(router) - r.HandleFunc("/rmo/water", handlerFormPost(water.Create)).Methods("POST") - service_request := resource.ServiceRequest(router) r.Handle("/service-request", authenticatedHandlerJSONSlice(service_request.List)).Methods("GET") session := resource.Session(router) r.Handle("/session", authenticatedHandlerJSON(session.Get)).Methods("GET").Name("session.get") @@ -96,34 +133,4 @@ func AddRoutes(r *mux.Router) { r.Handle("/user/{id}", authenticatedHandlerJSONPut(user.ByIDPut)).Methods("PUT") // Unauthenticated endpoints - district := resource.District(router) - r.Handle("/district", handlerJSONSlice(district.List)).Methods("GET") - r.Handle("/district/{id}", handlerJSON(district.GetByID)).Methods("GET").Name("district.ByIDGet") - geocode := resource.Geocode(router) - r.Handle("/geocode/by-gid/{id:.*}", handlerJSON(geocode.ByGID)).Methods("GET") - r.Handle("/geocode/reverse", handlerJSONPost(geocode.Reverse)).Methods("POST") - r.Handle("/geocode/reverse/closest", handlerJSONPost(geocode.ReverseClosest)).Methods("POST") - r.Handle("/geocode/suggestion", handlerJSONSlice(geocode.SuggestionList)).Methods("GET") - publicreport := resource.Publicreport(router) - r.Handle("/publicreport/{id}", handlerBasic(publicreport.ByID)).Methods("GET").Name("publicreport.ByIDGet") - r.Handle("/publicreport/compliance/{id}/image", handlerFormPost(publicreport.ImageCreate)).Methods("POST") - r.Handle("/publicreport/compliance/{id}", handlerJSON(pr_compliance.ByID)).Methods("GET").Name("publicreport.compliance.ByIDGet") - r.Handle("/publicreport/compliance/{id}", handlerJSONPut(pr_compliance.Update)).Methods("PUT") - r.Handle("/publicreport/nuisance/{id}", handlerJSON(nuisance.ByID)).Methods("GET").Name("publicreport.nuisance.ByIDGet") - r.Handle("/publicreport/water/{id}", handlerJSON(water.ByID)).Methods("GET").Name("publicreport.water.ByIDGet") - - publicreport_notification := resource.PublicreportNotification(router) - r.Handle("/publicreport-notification", handlerJSONPost(publicreport_notification.Create)).Methods("POST") - - //r.HandleFunc("/district", apiGetDistrict).Methods("GET") - r.HandleFunc("/district/{slug}/logo", apiGetDistrictLogo).Methods("GET").Name("district.logo.BySlug") - r.HandleFunc("/twilio/call", twilioCallPost).Methods("POST") - r.HandleFunc("/twilio/call/status", twilioCallStatusPost).Methods("POST") - r.HandleFunc("/twilio/message", twilioMessagePost).Methods("POST") - r.HandleFunc("/twilio/text", twilioTextPost).Methods("POST") - r.HandleFunc("/twilio/text/status", twilioTextStatusPost).Methods("POST") - r.HandleFunc("/voipms/text", voipmsTextGet).Methods("GET") - r.HandleFunc("/voipms/text", voipmsTextPost).Methods("POST") - r.HandleFunc("/webhook/fieldseeker", webhookFieldseeker).Methods("GET") - r.HandleFunc("/webhook/fieldseeker", webhookFieldseeker).Methods("POST") } diff --git a/platform/publicreport.go b/platform/publicreport.go index 939cdb7e..f81a4fca 100644 --- a/platform/publicreport.go +++ b/platform/publicreport.go @@ -53,11 +53,11 @@ func GenerateReportID() (string, error) { return builder.String(), nil } -func PublicreportByID(ctx context.Context, report_id string) (*types.PublicReport, error) { - return publicreport.ByID(ctx, report_id) +func PublicReportByID(ctx context.Context, report_id string, is_public bool) (*types.PublicReport, error) { + return publicreport.ByID(ctx, report_id, is_public) } -func PublicreportByIDCompliance(ctx context.Context, report_id string) (*types.PublicReportCompliance, error) { - result, err := publicreport.ByIDCompliance(ctx, report_id) +func PublicReportByIDCompliance(ctx context.Context, report_id string, is_public bool) (*types.PublicReportCompliance, error) { + result, err := publicreport.ByIDCompliance(ctx, report_id, is_public) if err != nil { return nil, fmt.Errorf("byidcompliance: %w", err) } @@ -75,14 +75,14 @@ func PublicreportByIDCompliance(ctx context.Context, report_id string) (*types.P } return result, nil } -func PublicreportByIDNuisance(ctx context.Context, report_id string) (*types.PublicReportNuisance, error) { - return publicreport.ByIDNuisance(ctx, report_id) +func PublicReportByIDNuisance(ctx context.Context, report_id string, is_public bool) (*types.PublicReportNuisance, error) { + return publicreport.ByIDNuisance(ctx, report_id, is_public) } -func PublicreportByIDWater(ctx context.Context, report_id string) (*types.PublicReportWater, error) { - return publicreport.ByIDWater(ctx, report_id) +func PublicReportByIDWater(ctx context.Context, report_id string, is_public bool) (*types.PublicReportWater, error) { + return publicreport.ByIDWater(ctx, report_id, is_public) } -func PublicreportComplianceSubmit(ctx context.Context, report_id string) (*types.PublicReportCompliance, error) { - report, err := publicreport.ByIDCompliance(ctx, report_id) +func PublicReportComplianceSubmit(ctx context.Context, report_id string, is_public bool) (*types.PublicReportCompliance, error) { + report, err := publicreport.ByIDCompliance(ctx, report_id, is_public) if err != nil { return nil, fmt.Errorf("byidcompliance: %w", err) } @@ -94,9 +94,9 @@ func PublicreportComplianceSubmit(ctx context.Context, report_id string) (*types if err != nil { return nil, fmt.Errorf("update report submitted: %w", err) } - return publicreport.ByIDCompliance(ctx, report_id) + return publicreport.ByIDCompliance(ctx, report_id, is_public) } -func PublicreportInvalid(ctx context.Context, user User, public_id string) error { +func PublicReportInvalid(ctx context.Context, user User, public_id string) error { report, err := publicReportFromID(ctx, public_id) if err != nil { return fmt.Errorf("query report existence: %w", err) @@ -206,13 +206,13 @@ func PublicReportUpdateCompliance(ctx context.Context, public_id string, report_ } } txn.Commit(ctx) - return publicreport.ByIDCompliance(ctx, public_id) + return publicreport.ByIDCompliance(ctx, public_id, false) } func PublicReportReporterUpdated(ctx context.Context, org_id int32, report_id string) { event.Updated(event.TypeRMOPublicReport, org_id, report_id) } -func PublicReportsForOrganization(ctx context.Context, org_id int32) ([]*types.PublicReport, error) { - return publicreport.ReportsForOrganization(ctx, org_id) +func PublicReportsForOrganization(ctx context.Context, org_id int32, is_public bool) ([]*types.PublicReport, error) { + return publicreport.ReportsForOrganization(ctx, org_id, is_public) } func PublicReportComplianceCreate(ctx context.Context, setter_report models.PublicreportReportSetter, setter_compliance models.PublicreportComplianceSetter, org_id int32) (*models.PublicreportReport, error) { return publicReportCreate(ctx, setter_report, nil, nil, nil, org_id, func(ctx context.Context, txn bob.Executor, report_id int32) error { diff --git a/platform/publicreport/log.go b/platform/publicreport/log.go index 73b5d501..873cfd83 100644 --- a/platform/publicreport/log.go +++ b/platform/publicreport/log.go @@ -15,7 +15,7 @@ import ( "github.com/stephenafamo/scan" ) -func logEntriesByReportID(ctx context.Context, report_ids []int32) (map[int32][]*types.LogEntry, error) { +func logEntriesByReportID(ctx context.Context, report_ids []int32, is_public bool) (map[int32][]*types.LogEntry, error) { results := make(map[int32][]*types.LogEntry, len(report_ids)) for _, report_id := range report_ids { results[report_id] = make([]*types.LogEntry, 0) @@ -49,19 +49,21 @@ func logEntriesByReportID(ctx context.Context, report_ids []int32) (map[int32][] for _, row := range rows { results[row.ReportID] = append(results[row.ReportID], &row) } - logs_from_texts, err := logEntriesFromTexts(ctx, report_ids) - if err != nil { - return results, fmt.Errorf("log from texts: %w", err) - } - for report_id, logs := range logs_from_texts { - cur_logs, ok := results[report_id] - if !ok { - return results, fmt.Errorf("no text logs for %d", report_id) + if !is_public { + logs_from_texts, err := logEntriesFromTexts(ctx, report_ids) + if err != nil { + return results, fmt.Errorf("log from texts: %w", err) } - for _, l := range logs { - cur_logs = append(cur_logs, l) + for report_id, logs := range logs_from_texts { + cur_logs, ok := results[report_id] + if !ok { + return results, fmt.Errorf("no text logs for %d", report_id) + } + for _, l := range logs { + cur_logs = append(cur_logs, l) + } + results[report_id] = cur_logs } - results[report_id] = cur_logs } return results, nil } diff --git a/platform/publicreport/report.go b/platform/publicreport/report.go index 9598768c..c3c2f35b 100644 --- a/platform/publicreport/report.go +++ b/platform/publicreport/report.go @@ -15,12 +15,12 @@ import ( "github.com/stephenafamo/scan" ) -func ByID(ctx context.Context, public_id string) (*types.PublicReport, error) { +func ByID(ctx context.Context, public_id string, is_public bool) (*types.PublicReport, error) { query := reportQuery() query.Apply( sm.Where(psql.Quote("r", "public_id").EQ(psql.Arg(public_id))), ) - reports, err := reportQueryToRows(ctx, query) + reports, err := reportQueryToRows(ctx, query, is_public) if err != nil { return nil, fmt.Errorf("query to rows: %w", err) } @@ -30,36 +30,36 @@ func ByID(ctx context.Context, public_id string) (*types.PublicReport, error) { } return reports[0], nil } -func ByIDCompliance(ctx context.Context, public_id string) (*types.PublicReportCompliance, error) { - report, err := ByID(ctx, public_id) +func ByIDCompliance(ctx context.Context, public_id string, is_public bool) (*types.PublicReportCompliance, error) { + report, err := ByID(ctx, public_id, is_public) if err != nil { return nil, fmt.Errorf("base report byid: %w", err) } return compliance(ctx, public_id, report) } -func ByIDNuisance(ctx context.Context, public_id string) (*types.PublicReportNuisance, error) { - report, err := ByID(ctx, public_id) +func ByIDNuisance(ctx context.Context, public_id string, is_public bool) (*types.PublicReportNuisance, error) { + report, err := ByID(ctx, public_id, is_public) if err != nil { return nil, fmt.Errorf("base report byid: %w", err) } return nuisance(ctx, public_id, report) } -func ByIDWater(ctx context.Context, public_id string) (*types.PublicReportWater, error) { - report, err := ByID(ctx, public_id) +func ByIDWater(ctx context.Context, public_id string, is_public bool) (*types.PublicReportWater, error) { + report, err := ByID(ctx, public_id, is_public) if err != nil { return nil, fmt.Errorf("base report byid: %w", err) } return water(ctx, public_id, report) } -func ReportsForOrganization(ctx context.Context, org_id int32) ([]*types.PublicReport, error) { +func ReportsForOrganization(ctx context.Context, org_id int32, is_public bool) ([]*types.PublicReport, error) { query := reportQuery() query.Apply( sm.Where(psql.Quote("r", "organization_id").EQ(psql.Arg(org_id))), sm.Where(psql.Quote("r", "reviewed").IsNull()), ) - return reportQueryToRows(ctx, query) + return reportQueryToRows(ctx, query, is_public) } -func reportQueryToRows(ctx context.Context, query bob.BaseQuery[*dialect.SelectQuery]) ([]*types.PublicReport, error) { +func reportQueryToRows(ctx context.Context, query bob.BaseQuery[*dialect.SelectQuery], is_public bool) ([]*types.PublicReport, error) { rows, err := bob.All(ctx, db.PGInstance.BobDB, query, scan.StructMapper[types.PublicReport]()) if err != nil { @@ -73,7 +73,7 @@ func reportQueryToRows(ctx context.Context, query bob.BaseQuery[*dialect.SelectQ if err != nil { return nil, fmt.Errorf("images for report: %w", err) } - logs_by_report_id, err := logEntriesByReportID(ctx, report_ids) + logs_by_report_id, err := logEntriesByReportID(ctx, report_ids, is_public) if err != nil { return nil, fmt.Errorf("log entries for reports: %w", err) } @@ -94,13 +94,13 @@ func reportQueryToRows(ctx context.Context, query bob.BaseQuery[*dialect.SelectQ } return results, nil } -func Reports(ctx context.Context, org_id int32, ids []int32) ([]*types.PublicReport, error) { +func Reports(ctx context.Context, org_id int32, ids []int32, is_public bool) ([]*types.PublicReport, error) { query := reportQuery() query.Apply( sm.Where(psql.Quote("r", "organization_id").EQ(psql.Arg(org_id))), sm.Where(psql.Quote("r", "id").EQ(psql.Any(ids))), ) - return reportQueryToRows(ctx, query) + return reportQueryToRows(ctx, query, is_public) } func ReportsForOrganizationCount(ctx context.Context, org_id int32) (uint, error) { type _Row struct { diff --git a/platform/signal.go b/platform/signal.go index ef516105..9811a4d1 100644 --- a/platform/signal.go +++ b/platform/signal.go @@ -275,7 +275,7 @@ func SignalList(ctx context.Context, user User, limit int) ([]*Signal, error) { if err != nil { return nil, fmt.Errorf("getting pools by ID: %w", err) } - reports, err := publicreport.Reports(ctx, org_id, report_ids) + reports, err := publicreport.Reports(ctx, org_id, report_ids, false) if err != nil { return nil, fmt.Errorf("getting reports by ID: %w", err) } diff --git a/resource/communication.go b/resource/communication.go index 76989a14..c0b5494d 100644 --- a/resource/communication.go +++ b/resource/communication.go @@ -47,7 +47,7 @@ func toImageURLs(m map[string][]uuid.UUID, id string) []string { return urls } func (res *communicationR) List(ctx context.Context, r *http.Request, user platform.User, query QueryParams) (*communicationList, *nhttp.ErrorWithStatus) { - reports, err := platform.PublicReportsForOrganization(ctx, user.Organization.ID) + reports, err := platform.PublicReportsForOrganization(ctx, user.Organization.ID, false) if err != nil { return nil, nhttp.NewError("nuisance report query: %w", err) } diff --git a/resource/publicreport.go b/resource/publicreport.go index 42c90fc5..516998c0 100644 --- a/resource/publicreport.go +++ b/resource/publicreport.go @@ -23,7 +23,7 @@ func Publicreport(r *router) *publicreportR { } } -func (res *publicreportR) ByID(ctx context.Context, w http.ResponseWriter, r *http.Request) *nhttp.ErrorWithStatus { +func (res *publicreportR) ByID(ctx context.Context, w http.ResponseWriter, r *http.Request, u platform.User) *nhttp.ErrorWithStatus { vars := mux.Vars(r) public_id := vars["id"] if public_id == "" { @@ -40,6 +40,23 @@ func (res *publicreportR) ByID(ctx context.Context, w http.ResponseWriter, r *ht http.Redirect(w, r, path, http.StatusFound) return nil } +func (res *publicreportR) ByIDPublic(ctx context.Context, w http.ResponseWriter, r *http.Request) *nhttp.ErrorWithStatus { + vars := mux.Vars(r) + public_id := vars["id"] + if public_id == "" { + return nhttp.NewBadRequest("You must provide an ID") + } + report_type, err := platform.PublicReportTypeByID(ctx, public_id) + if err != nil { + return nhttp.NewError("get report '%s': %w", public_id, err) + } + path, err := reportURIPublic(res.router, report_type, public_id) + if err != nil { + return nhttp.NewError("get uri '%s': %w", public_id, err) + } + http.Redirect(w, r, path, http.StatusFound) + return nil +} type image struct { Status string `json:"status"` @@ -100,3 +117,21 @@ func reportURI(r *router, report_type string, public_id string) (string, error) } return uri, nil } +func reportURIPublic(r *router, report_type string, public_id string) (string, error) { + var route_name string + switch report_type { + case "compliance": + route_name = "publicreport.compliance.ByIDGetPublic" + case "nuisance": + route_name = "publicreport.nuisance.ByIDGetPublic" + case "water": + route_name = "publicreport.water.ByIDGetPublic" + default: + return "", fmt.Errorf("Unrecognized report type '%s'", report_type) + } + uri, err := r.IDStrToURI(route_name, public_id) + if err != nil { + return "", fmt.Errorf("id str to uri '%s' '%s': %w", route_name, public_id, err) + } + return uri, nil +} diff --git a/resource/publicreport_compliance.go b/resource/publicreport_compliance.go index 1f2f8bf7..4b4857df 100644 --- a/resource/publicreport_compliance.go +++ b/resource/publicreport_compliance.go @@ -45,13 +45,16 @@ type publicreportComplianceForm struct { WantsScheduled omitnull.Val[bool] `schema:"wants_scheduled" json:"wants_scheduled"` } -func (res *complianceR) ByID(ctx context.Context, r *http.Request, query QueryParams) (*types.PublicReportCompliance, *nhttp.ErrorWithStatus) { +func (res *complianceR) ByID(ctx context.Context, r *http.Request, u platform.User, query QueryParams) (*types.PublicReportCompliance, *nhttp.ErrorWithStatus) { + return res.ByIDPublic(ctx, r, query) +} +func (res *complianceR) ByIDPublic(ctx context.Context, r *http.Request, query QueryParams) (*types.PublicReportCompliance, *nhttp.ErrorWithStatus) { vars := mux.Vars(r) public_id := vars["id"] if public_id == "" { return nil, nhttp.NewBadRequest("You must provid an ID") } - report, err := platform.PublicreportByIDCompliance(ctx, public_id) + report, err := platform.PublicReportByIDCompliance(ctx, public_id, true) if err != nil { return nil, nhttp.NewError("get report: %w", err) } @@ -132,7 +135,7 @@ func (res *complianceR) Create(ctx context.Context, r *http.Request, n publicrep return nil, nhttp.NewError("create compliance report: %w", err) } // Return a fully-fleshed-out report object, even though it's a bit more expensive - result, err := platform.PublicreportByIDCompliance(ctx, report.PublicID) + result, err := platform.PublicReportByIDCompliance(ctx, report.PublicID, true) if err != nil { return nil, nhttp.NewError("get report after creation: %w", err) } @@ -206,7 +209,7 @@ func (res *complianceR) Update(ctx context.Context, r *http.Request, prf publicr return nil, nhttp.NewError("platform update report compliance: %w", err) } // Return a fully-fleshed-out report object, even though it's a bit more expensive - report, err = platform.PublicreportByIDCompliance(ctx, public_id) + report, err = platform.PublicReportByIDCompliance(ctx, public_id, true) if err != nil { return nil, nhttp.NewError("get report after update: %w", err) } @@ -223,7 +226,7 @@ func (res *complianceR) Submit(ctx context.Context, r *http.Request, prf publicr if public_id == "" { return nil, nhttp.NewBadRequest("You must provide an ID") } - report, err := platform.PublicreportComplianceSubmit(ctx, public_id) + report, err := platform.PublicReportComplianceSubmit(ctx, public_id, true) if err != nil { return nil, nhttp.NewError("submit report: %w", err) } diff --git a/resource/publicreport_nuisance.go b/resource/publicreport_nuisance.go index 06fcec12..5fc2090d 100644 --- a/resource/publicreport_nuisance.go +++ b/resource/publicreport_nuisance.go @@ -51,13 +51,16 @@ type nuisanceForm struct { TODNight bool `schema:"tod-night"` } -func (res *nuisanceR) ByID(ctx context.Context, r *http.Request, query QueryParams) (*types.PublicReportNuisance, *nhttp.ErrorWithStatus) { +func (res *nuisanceR) ByID(ctx context.Context, r *http.Request, u platform.User, query QueryParams) (*types.PublicReportNuisance, *nhttp.ErrorWithStatus) { + return res.ByIDPublic(ctx, r, query) +} +func (res *nuisanceR) ByIDPublic(ctx context.Context, r *http.Request, query QueryParams) (*types.PublicReportNuisance, *nhttp.ErrorWithStatus) { vars := mux.Vars(r) public_id := vars["id"] if public_id == "" { return nil, nhttp.NewBadRequest("You must provid an ID") } - report, err := platform.PublicreportByIDNuisance(ctx, public_id) + report, err := platform.PublicReportByIDNuisance(ctx, public_id, true) if err != nil { return nil, nhttp.NewError("get report: %w", err) } diff --git a/resource/publicreport_water.go b/resource/publicreport_water.go index 43fd1a94..dcf97309 100644 --- a/resource/publicreport_water.go +++ b/resource/publicreport_water.go @@ -56,19 +56,11 @@ type waterForm struct { OwnerPhone string `schema:"owner-phone"` } -func (res *waterR) ByID(ctx context.Context, r *http.Request, query QueryParams) (*types.PublicReportWater, *nhttp.ErrorWithStatus) { - vars := mux.Vars(r) - public_id := vars["id"] - if public_id == "" { - return nil, nhttp.NewBadRequest("You must provid an ID") - } - report, err := platform.PublicreportByIDWater(ctx, public_id) - if err != nil { - return nil, nhttp.NewError("get report: %w", err) - } - populateDistrictURI(&report.PublicReport, res.router) - populateReportURI(&report.PublicReport, res.router) - return report, nil +func (res *waterR) ByID(ctx context.Context, r *http.Request, u platform.User, query QueryParams) (*types.PublicReportWater, *nhttp.ErrorWithStatus) { + return res.byID(ctx, r, false) +} +func (res *waterR) ByIDPublic(ctx context.Context, r *http.Request, query QueryParams) (*types.PublicReportWater, *nhttp.ErrorWithStatus) { + return res.byID(ctx, r, true) } func (res *waterR) Create(ctx context.Context, r *http.Request, w waterForm) (*water, *nhttp.ErrorWithStatus) { @@ -146,3 +138,17 @@ func (res *waterR) Create(ctx context.Context, r *http.Request, w waterForm) (*w URI: uri, }, nil } +func (res *waterR) byID(ctx context.Context, r *http.Request, is_public bool) (*types.PublicReportWater, *nhttp.ErrorWithStatus) { + vars := mux.Vars(r) + public_id := vars["id"] + if public_id == "" { + return nil, nhttp.NewBadRequest("You must provid an ID") + } + report, err := platform.PublicReportByIDWater(ctx, public_id, is_public) + if err != nil { + return nil, nhttp.NewError("get report: %w", err) + } + populateDistrictURI(&report.PublicReport, res.router) + populateReportURI(&report.PublicReport, res.router) + return report, nil +} diff --git a/ts/rmo/view/StatusByID.vue b/ts/rmo/view/StatusByID.vue index 9e6a3531..8b54846b 100644 --- a/ts/rmo/view/StatusByID.vue +++ b/ts/rmo/view/StatusByID.vue @@ -146,7 +146,7 @@