From 28cd1fceee2ab67916fed9e665f0ff7da4538967 Mon Sep 17 00:00:00 2001 From: Ralph Slooten Date: Sat, 9 Dec 2023 23:24:42 +1300 Subject: [PATCH] API: Allow ID "latest" for message summary, headers, raw version & HTML/link checks --- internal/storage/database.go | 17 ++++++++++ server/apiv1/api.go | 64 ++++++++++++++++++++++++++++++++--- server/apiv1/swagger.go | 10 +++--- server/handlers/messages.go | 48 ++++---------------------- server/ui/api/v1/swagger.json | 16 ++++----- 5 files changed, 95 insertions(+), 60 deletions(-) diff --git a/internal/storage/database.go b/internal/storage/database.go index bcfb9f6..f9035da 100644 --- a/internal/storage/database.go +++ b/internal/storage/database.go @@ -468,6 +468,23 @@ func GetAttachmentPart(id, partID string) (*enmime.Part, error) { return nil, errors.New("attachment not found") } +// LatestID returns the latest message ID +func LatestID() (string, error) { + messages := []MessageSummary{} + var err error + + messages, err = List(0, 1) + if err != nil { + return "", err + } + + if len(messages) == 0 { + return "", errors.New("Message not found") + } + + return messages[0].ID, nil +} + // MarkRead will mark a message as read func MarkRead(id string) error { if !IsUnread(id) { diff --git a/server/apiv1/api.go b/server/apiv1/api.go index 1afd703..5f0c9ae 100644 --- a/server/apiv1/api.go +++ b/server/apiv1/api.go @@ -189,6 +189,8 @@ func GetMessage(w http.ResponseWriter, r *http.Request) { // // Returns the summary of a message, marking the message as read. // + // The ID can be set to `latest` to return the latest message. + // // Produces: // - application/json // @@ -197,7 +199,7 @@ func GetMessage(w http.ResponseWriter, r *http.Request) { // Parameters: // + name: ID // in: path - // description: Message database ID + // description: Message database ID or "latest" // required: true // type: string // @@ -209,6 +211,16 @@ func GetMessage(w http.ResponseWriter, r *http.Request) { id := vars["id"] + if id == "latest" { + var err error + id, err = storage.LatestID() + if err != nil { + w.WriteHeader(404) + fmt.Fprint(w, err.Error()) + return + } + } + msg, err := storage.GetMessage(id) if err != nil { fourOFour(w) @@ -279,6 +291,8 @@ func GetHeaders(w http.ResponseWriter, r *http.Request) { // // Returns the message headers as an array. // + // The ID can be set to `latest` to return the latest message headers. + // // Produces: // - application/json // @@ -287,7 +301,7 @@ func GetHeaders(w http.ResponseWriter, r *http.Request) { // Parameters: // + name: ID // in: path - // description: Database ID + // description: Message database ID or "latest" // required: true // type: string // @@ -299,6 +313,16 @@ func GetHeaders(w http.ResponseWriter, r *http.Request) { id := vars["id"] + if id == "latest" { + var err error + id, err = storage.LatestID() + if err != nil { + w.WriteHeader(404) + fmt.Fprint(w, err.Error()) + return + } + } + data, err := storage.GetMessageRaw(id) if err != nil { fourOFour(w) @@ -326,6 +350,8 @@ func DownloadRaw(w http.ResponseWriter, r *http.Request) { // // Returns the full email source as plain text. // + // The ID can be set to `latest` to return the latest message source. + // // Produces: // - text/plain // @@ -334,7 +360,7 @@ func DownloadRaw(w http.ResponseWriter, r *http.Request) { // Parameters: // + name: ID // in: path - // description: Database ID + // description: Message database ID or "latest" // required: true // type: string // @@ -345,9 +371,18 @@ func DownloadRaw(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id := vars["id"] - dl := r.FormValue("dl") + if id == "latest" { + var err error + id, err = storage.LatestID() + if err != nil { + w.WriteHeader(404) + fmt.Fprint(w, err.Error()) + return + } + } + data, err := storage.GetMessageRaw(id) if err != nil { fourOFour(w) @@ -554,7 +589,6 @@ func SetTags(w http.ResponseWriter, r *http.Request) { } // ReleaseMessage (method: POST) will release a message via a pre-configured external SMTP server. -// If no IDs are provided then all messages are updated. func ReleaseMessage(w http.ResponseWriter, r *http.Request) { // swagger:route POST /api/v1/message/{ID}/release message ReleaseMessage // @@ -702,6 +736,16 @@ func HTMLCheck(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id := vars["id"] + if id == "latest" { + var err error + id, err = storage.LatestID() + if err != nil { + w.WriteHeader(404) + fmt.Fprint(w, err.Error()) + return + } + } + msg, err := storage.GetMessage(id) if err != nil { fourOFour(w) @@ -747,6 +791,16 @@ func LinkCheck(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id := vars["id"] + if id == "latest" { + var err error + id, err = storage.LatestID() + if err != nil { + w.WriteHeader(404) + fmt.Fprint(w, err.Error()) + return + } + } + msg, err := storage.GetMessage(id) if err != nil { fourOFour(w) diff --git a/server/apiv1/swagger.go b/server/apiv1/swagger.go index dc15baf..4e1df61 100644 --- a/server/apiv1/swagger.go +++ b/server/apiv1/swagger.go @@ -1,6 +1,6 @@ package apiv1 -// These structs are for the purpose of defining swagger HTTP responses +// These structs are for the purpose of defining swagger HTTP parameters & responses // Application information // swagger:response InfoResponse @@ -118,20 +118,20 @@ type releaseMessageRequestBody struct { // swagger:parameters HTMLCheck type htmlCheckParams struct { - // Message database ID + // Message database ID or "latest" // // in: path - // description: Message database ID + // description: Message database ID or "latest" // required: true ID string } // swagger:parameters LinkCheck type linkCheckParams struct { - // Message database ID + // Message database ID or "latest" // // in: path - // description: Message database ID + // description: Message database ID or "latest" // required: true ID string diff --git a/server/handlers/messages.go b/server/handlers/messages.go index 94abc9a..ba93d37 100644 --- a/server/handlers/messages.go +++ b/server/handlers/messages.go @@ -78,31 +78,13 @@ func GetMessageHTML(w http.ResponseWriter, r *http.Request) { id := vars["id"] if id == "latest" { - messages := []storage.MessageSummary{} var err error - - search := strings.TrimSpace(r.URL.Query().Get("query")) - if search != "" { - messages, _, err = storage.Search(search, 0, 1) - if err != nil { - httpError(w, err.Error()) - return - } - } else { - messages, err = storage.List(0, 1) - if err != nil { - httpError(w, err.Error()) - return - } - } - - if len(messages) == 0 { + id, err = storage.LatestID() + if err != nil { w.WriteHeader(404) - fmt.Fprint(w, "Message not found") + fmt.Fprint(w, err.Error()) return } - - id = messages[0].ID } msg, err := storage.GetMessage(id) @@ -153,31 +135,13 @@ func GetMessageText(w http.ResponseWriter, r *http.Request) { id := vars["id"] if id == "latest" { - messages := []storage.MessageSummary{} var err error - - search := strings.TrimSpace(r.URL.Query().Get("query")) - if search != "" { - messages, _, err = storage.Search(search, 0, 1) - if err != nil { - httpError(w, err.Error()) - return - } - } else { - messages, err = storage.List(0, 1) - if err != nil { - httpError(w, err.Error()) - return - } - } - - if len(messages) == 0 { + id, err = storage.LatestID() + if err != nil { w.WriteHeader(404) - fmt.Fprint(w, "Message not found") + fmt.Fprint(w, err.Error()) return } - - id = messages[0].ID } msg, err := storage.GetMessage(id) diff --git a/server/ui/api/v1/swagger.json b/server/ui/api/v1/swagger.json index 5f4526d..360ce57 100644 --- a/server/ui/api/v1/swagger.json +++ b/server/ui/api/v1/swagger.json @@ -50,7 +50,7 @@ }, "/api/v1/message/{ID}": { "get": { - "description": "Returns the summary of a message, marking the message as read.", + "description": "Returns the summary of a message, marking the message as read.\n\nThe ID can be set to `latest` to return the latest message.", "produces": [ "application/json" ], @@ -66,7 +66,7 @@ "parameters": [ { "type": "string", - "description": "Message database ID", + "description": "Message database ID or \"latest\"", "name": "ID", "in": "path", "required": true @@ -87,7 +87,7 @@ }, "/api/v1/message/{ID}/headers": { "get": { - "description": "Returns the message headers as an array.", + "description": "Returns the message headers as an array.\n\nThe ID can be set to `latest` to return the latest message headers.", "produces": [ "application/json" ], @@ -103,7 +103,7 @@ "parameters": [ { "type": "string", - "description": "Database ID", + "description": "Message database ID or \"latest\"", "name": "ID", "in": "path", "required": true @@ -140,7 +140,7 @@ "parameters": [ { "type": "string", - "description": "Message database ID", + "description": "Message database ID or \"latest\"", "name": "ID", "in": "path", "required": true @@ -177,7 +177,7 @@ "parameters": [ { "type": "string", - "description": "Message database ID", + "description": "Message database ID or \"latest\"", "name": "ID", "in": "path", "required": true @@ -290,7 +290,7 @@ }, "/api/v1/message/{ID}/raw": { "get": { - "description": "Returns the full email source as plain text.", + "description": "Returns the full email source as plain text.\n\nThe ID can be set to `latest` to return the latest message source.", "produces": [ "text/plain" ], @@ -306,7 +306,7 @@ "parameters": [ { "type": "string", - "description": "Database ID", + "description": "Message database ID or \"latest\"", "name": "ID", "in": "path", "required": true