From 0512fa41f644e41c4373f275cb595a4b01c0237a Mon Sep 17 00:00:00 2001 From: Ian Gulliver Date: Tue, 26 Nov 2024 13:26:26 -0600 Subject: [PATCH] Use JSON for errors and page response --- error.go | 15 +++++++++++++++ go.mod | 6 +++++- go.sum | 4 ++++ json.go | 14 ++++++++++++++ main.go | 23 ++++++++++++----------- response.go | 14 ++++++++++++++ static/index.html | 9 +++++---- 7 files changed, 69 insertions(+), 16 deletions(-) create mode 100644 error.go create mode 100644 json.go create mode 100644 response.go diff --git a/error.go b/error.go new file mode 100644 index 0000000..2de889d --- /dev/null +++ b/error.go @@ -0,0 +1,15 @@ +package main + +import ( + "fmt" + "net/http" +) + +type Error struct { + Message string `json:"message"` +} + +func sendError(w http.ResponseWriter, code int, msg string, args ...any) { + w.WriteHeader(code) + sendJSON(w, Error{Message: fmt.Sprintf(msg, args...)}) +} diff --git a/go.mod b/go.mod index cb10bb1..8f93bd5 100644 --- a/go.mod +++ b/go.mod @@ -2,11 +2,15 @@ module github.com/gopatchy/p go 1.22 -require github.com/openai/openai-go v0.1.0-alpha.38 +require ( + github.com/openai/openai-go v0.1.0-alpha.38 + github.com/samber/lo v1.47.0 +) require ( github.com/tidwall/gjson v1.14.4 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect + golang.org/x/text v0.16.0 // indirect ) diff --git a/go.sum b/go.sum index e1dd97e..a525c66 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/openai/openai-go v0.1.0-alpha.38 h1:j/rL0aEIHWnWaPgA8/AXYKCI79ZoW44NTIpn7qfMEXQ= github.com/openai/openai-go v0.1.0-alpha.38/go.mod h1:3SdE6BffOX9HPEQv8IL/fi3LYZ5TUpRYaqGQZbyk11A= +github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc= +github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -10,3 +12,5 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= diff --git a/json.go b/json.go new file mode 100644 index 0000000..cfe3eae --- /dev/null +++ b/json.go @@ -0,0 +1,14 @@ +package main + +import ( + "encoding/json" + "net/http" +) + +func sendJSON(w http.ResponseWriter, v any) { + w.Header().Set("Content-Type", "application/json") + + enc := json.NewEncoder(w) + enc.SetEscapeHTML(false) + _ = enc.Encode(v) +} diff --git a/main.go b/main.go index 21f1e41..6befed5 100644 --- a/main.go +++ b/main.go @@ -63,7 +63,7 @@ func (ph *PHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (ph *PHandler) serveRoot(w http.ResponseWriter, r *http.Request) { err := r.ParseForm() if err != nil { - http.Error(w, fmt.Sprintf("invalid form: %s\n", err), http.StatusBadRequest) + sendError(w, http.StatusBadRequest, "Parse form: %s", err) return } @@ -73,7 +73,7 @@ func (ph *PHandler) serveRoot(w http.ResponseWriter, r *http.Request) { if m == "" { err = ph.tmpl.Execute(w, ph.envs()) if err != nil { - http.Error(w, fmt.Sprintf("execute %s: %s\n", ph.tmpl.Name(), err), http.StatusBadRequest) + sendError(w, http.StatusInternalServerError, "Execute template %s: %s", ph.tmpl.Name(), err) return } @@ -92,13 +92,13 @@ func (ph *PHandler) serveRoot(w http.ResponseWriter, r *http.Request) { }) if err != nil { - http.Error(w, fmt.Sprintf("failed to create PD request: %s\n", err), http.StatusBadRequest) + sendError(w, http.StatusInternalServerError, "Create PD request: %s", err) return } req, err := http.NewRequest("POST", "https://events.pagerduty.com/v2/enqueue", buf) if err != nil { - http.Error(w, fmt.Sprintf("failed to create HTTP request: %s\n", err), http.StatusBadRequest) + sendError(w, http.StatusInternalServerError, "Create HTTP request: %s", err) return } @@ -106,7 +106,7 @@ func (ph *PHandler) serveRoot(w http.ResponseWriter, r *http.Request) { res, err := c.Do(req) if err != nil { - http.Error(w, fmt.Sprintf("error from PD: %s\n", err), http.StatusBadRequest) + sendError(w, http.StatusInternalServerError, "Call PagerDuty: %s", err) return } @@ -114,17 +114,17 @@ func (ph *PHandler) serveRoot(w http.ResponseWriter, r *http.Request) { _ = res.Body.Close() if res.StatusCode != 202 { - http.Error(w, fmt.Sprintf("error from PD: %s", string(body)), http.StatusBadRequest) + sendError(w, http.StatusInternalServerError, "PagerDuty error: %s", string(body)) return } - _, _ = w.Write([]byte("page sent\n")) + sendResponse(w, "Page sent") } func (ph *PHandler) serveSuggest(w http.ResponseWriter, r *http.Request) { err := r.ParseForm() if err != nil { - http.Error(w, fmt.Sprintf("invalid form: %s\n", err), http.StatusBadRequest) + sendError(w, http.StatusBadRequest, "Parse form: %s", err) return } @@ -132,21 +132,22 @@ func (ph *PHandler) serveSuggest(w http.ResponseWriter, r *http.Request) { m := r.Form.Get("m") if m == "" { - http.Error(w, "m param required", http.StatusBadRequest) + sendError(w, http.StatusBadRequest, "m= param required") + return } c := openai.NewClient() comp, err := c.Chat.Completions.New(r.Context(), openai.ChatCompletionNewParams{ Messages: openai.F([]openai.ChatCompletionMessageParamUnion{ - openai.SystemMessage(`You are an assistant helping users to write good text to include in an urgent page sent to a person. Good page text contains a very brief description of the problem (e.g. "down" or "slow"), the systems it affects (acronyms for system names are fine), the identity of the sender (first names are fine), and how to contact them (e.g. a phone number or incident Slack channel). The request will consist of just the user's proposed page text. Respond with just a very brief message suggesting improvements that the sender might make or saying "Looks good, send it!". Remember that the user is likely in an urgent, stressful situation, so make your response brief and err on the side of assuming that the message is sufficient if the text might be OK. Assume that the recipient already knows the message is urgent so the sender doesn't have to specify urgency.`), + openai.SystemMessage(`You are an assistant helping users to write good text to include in an urgent page sent to a person. Good page text contains a very brief description of the problem (e.g. "down" or "slow"), the systems it affects (acronyms for system names are fine), the identity of the sender (first names are fine), and how to contact them (e.g. a phone number or incident Slack channel). The request will consist of just the user's proposed page text. Respond with just a very brief message suggesting improvements that the sender might make or saying "Looks good -- send it!". Remember that the user is likely in an urgent, stressful situation, so make your response brief and err on the side of assuming that the message is sufficient if the text might be OK. Assume that the recipient already knows the message is urgent so the sender doesn't have to specify urgency.`), openai.UserMessage(m), }), Model: openai.F(openai.ChatModelGPT4o), }) if err != nil { - http.Error(w, fmt.Sprintf("error from openai: %s", err), http.StatusInternalServerError) + sendError(w, http.StatusInternalServerError, "OpenAI error: %s", err) return } diff --git a/response.go b/response.go new file mode 100644 index 0000000..8f77317 --- /dev/null +++ b/response.go @@ -0,0 +1,14 @@ +package main + +import ( + "fmt" + "net/http" +) + +type Response struct { + Message string `json:"message"` +} + +func sendResponse(w http.ResponseWriter, msg string, args ...any) { + sendJSON(w, Response{Message: fmt.Sprintf(msg, args...)}) +} diff --git a/static/index.html b/static/index.html index 9a7035b..a6c849c 100644 --- a/static/index.html +++ b/static/index.html @@ -100,10 +100,11 @@ async function page() { ) if (!resp.ok) { - error('Failed to send page', await resp.text()); + error('Failed to send page', (await resp.json()).message); return; } + document.getElementById('sent-msg').innerText = (await resp.json()).message; document.getElementById('sent').show(); } @@ -112,7 +113,7 @@ async function suggestLater() { clearTimeout(suggestTimer); } - suggestTimer = setTimeout(suggestNow, 3000); + suggestTimer = setTimeout(suggestNow, 2000); } async function suggestNow() { @@ -182,7 +183,7 @@ document.addEventListener('DOMContentLoaded', () => {
- + @@ -191,7 +192,7 @@ document.addEventListener('DOMContentLoaded', () => { - Page sent +