diff --git a/src/api/api.go b/src/api/api.go index c5d205d..30a08ca 100644 --- a/src/api/api.go +++ b/src/api/api.go @@ -10,12 +10,18 @@ import ( "os/exec" "strings" "time" + "path/filepath" + "net/http" + "strconv" + "io/ioutil" "github.com/gin-gonic/gin" uuid "github.com/gofrs/uuid" "github.com/h2non/filetype" log "github.com/sirupsen/logrus" qrcode "github.com/skip2/go-qrcode" + "github.com/gabriel-vasile/mimetype" + "github.com/cyphar/filepath-securejoin" ) const groupPrefix = "group." @@ -618,7 +624,7 @@ func (a *Api) DeleteGroup(c *gin.Context) { // @Summary Link device and generate QR code. // @Tags Devices -// @Description test +// @Description Link device and generate QR code // @Produce json // @Success 200 {string} string "Image" // @Failure 400 {object} Error @@ -658,3 +664,98 @@ func (a *Api) GetQrCodeLink(c *gin.Context) { c.Data(200, "image/png", png) } + +// @Summary List all attachments. +// @Tags Attachments +// @Description List all downloaded attachments +// @Produce json +// @Success 200 {object} []string +// @Failure 400 {object} Error +// @Router /v1/attachments [get] +func (a *Api) GetAttachments(c *gin.Context) { + files := []string{} + err := filepath.Walk(a.signalCliConfig + "/attachments/", func(path string, info os.FileInfo, err error) error { + if info.IsDir() { + return nil + } + files = append(files, filepath.Base(path)) + return nil + }) + + if err != nil { + c.JSON(500, Error{Msg: "Couldn't get list of attachments: " + err.Error()}) + return + } + + c.JSON(200, files) +} + +// @Summary Remove attachment. +// @Tags Attachments +// @Description Remove the attachment with the given id from filesystem. +// @Produce json +// @Success 200 {string} OK +// @Failure 400 {object} Error +// @Param attachment path string true "Attachment ID" +// @Router /v1/attachments/{attachment} [delete] +func (a *Api) RemoveAttachment(c *gin.Context) { + attachment := c.Param("attachment") + path, err := securejoin.SecureJoin(a.signalCliConfig + "/attachments/", attachment) + if err != nil { + c.JSON(400, Error{Msg: "Please provide a valid attachment name"}) + return + } + + if _, err := os.Stat(path); os.IsNotExist(err) { + c.JSON(404, Error{Msg: "No attachment with that name found"}) + return + } + err = os.Remove(path) + if err != nil { + c.JSON(500, Error{Msg: "Couldn't delete attachment - please try again later"}) + return + } + c.Status(http.StatusNoContent) +} + +// @Summary Serve Attachment. +// @Tags Attachments +// @Description Serve the attachment with the given id +// @Produce json +// @Success 200 {string} OK +// @Failure 400 {object} Error +// @Param attachment path string true "Attachment ID" +// @Router /v1/attachments/{attachment} [get] +func (a *Api) ServeAttachment(c *gin.Context) { + attachment := c.Param("attachment") + path, err := securejoin.SecureJoin(a.signalCliConfig + "/attachments/", attachment) + if err != nil { + c.JSON(400, Error{Msg: "Please provide a valid attachment name"}) + return + } + + if _, err := os.Stat(path); os.IsNotExist(err) { + c.JSON(404, Error{Msg: "No attachment with that name found"}) + return + } + + imgBytes, err := ioutil.ReadFile(path) + if err != nil { + c.JSON(500, Error{Msg: "Couldn't read attachment - please try again later"}) + return + } + + mime, err := mimetype.DetectReader(bytes.NewReader(imgBytes)) + if err != nil { + c.JSON(500, Error{Msg: "Couldn't detect MIME type for attachment"}) + return + } + + c.Writer.Header().Set("Content-Type", mime.String()) + c.Writer.Header().Set("Content-Length", strconv.Itoa(len(imgBytes))) + _, err = c.Writer.Write(imgBytes) + if err != nil { + c.JSON(500, Error{Msg: "Couldn't serve attachment - please try again later"}) + return + } +} diff --git a/src/docs/docs.go b/src/docs/docs.go index 7d3d95f..3028120 100644 --- a/src/docs/docs.go +++ b/src/docs/docs.go @@ -45,6 +45,103 @@ var doc = `{ } } }, + "/v1/attachments": { + "get": { + "description": "List all downloaded attachments", + "produces": [ + "application/json" + ], + "tags": [ + "Attachments" + ], + "summary": "List all attachments.", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, + "/v1/attachments/{attachment}": { + "get": { + "description": "Serve the attachment with the given id", + "produces": [ + "application/json" + ], + "tags": [ + "Attachments" + ], + "summary": "Serve Attachment.", + "parameters": [ + { + "type": "string", + "description": "Attachment ID", + "name": "attachment", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + }, + "delete": { + "description": "Remove the attachment with the given id from filesystem.", + "produces": [ + "application/json" + ], + "tags": [ + "Attachments" + ], + "summary": "Remove attachment.", + "parameters": [ + { + "type": "string", + "description": "Attachment ID", + "name": "attachment", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, "/v1/groups/{number}": { "get": { "description": "List all Signal Groups.", @@ -169,7 +266,7 @@ var doc = `{ }, "/v1/qrcodelink": { "get": { - "description": "test", + "description": "Link device and generate QR code", "produces": [ "application/json" ], @@ -537,6 +634,10 @@ var doc = `{ { "description": "Send and Receive Signal Messages.", "name": "Messages" + }, + { + "description": "List and Delete Attachments.", + "name": "Attachments" } ] }` diff --git a/src/docs/swagger.json b/src/docs/swagger.json index 9199551..2f1c557 100644 --- a/src/docs/swagger.json +++ b/src/docs/swagger.json @@ -30,6 +30,103 @@ } } }, + "/v1/attachments": { + "get": { + "description": "List all downloaded attachments", + "produces": [ + "application/json" + ], + "tags": [ + "Attachments" + ], + "summary": "List all attachments.", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, + "/v1/attachments/{attachment}": { + "get": { + "description": "Serve the attachment with the given id", + "produces": [ + "application/json" + ], + "tags": [ + "Attachments" + ], + "summary": "Serve Attachment.", + "parameters": [ + { + "type": "string", + "description": "Attachment ID", + "name": "attachment", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + }, + "delete": { + "description": "Remove the attachment with the given id from filesystem.", + "produces": [ + "application/json" + ], + "tags": [ + "Attachments" + ], + "summary": "Remove attachment.", + "parameters": [ + { + "type": "string", + "description": "Attachment ID", + "name": "attachment", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, "/v1/groups/{number}": { "get": { "description": "List all Signal Groups.", @@ -154,7 +251,7 @@ }, "/v1/qrcodelink": { "get": { - "description": "test", + "description": "Link device and generate QR code", "produces": [ "application/json" ], @@ -522,6 +619,10 @@ { "description": "Send and Receive Signal Messages.", "name": "Messages" + }, + { + "description": "List and Delete Attachments.", + "name": "Attachments" } ] } \ No newline at end of file diff --git a/src/docs/swagger.yaml b/src/docs/swagger.yaml index e94efb9..05184cf 100644 --- a/src/docs/swagger.yaml +++ b/src/docs/swagger.yaml @@ -92,6 +92,70 @@ paths: summary: Lists general information about the API tags: - General + /v1/attachments: + get: + description: List all downloaded attachments + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + type: string + type: array + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.Error' + summary: List all attachments. + tags: + - Attachments + /v1/attachments/{attachment}: + delete: + description: Remove the attachment with the given id from filesystem. + parameters: + - description: Attachment ID + in: path + name: attachment + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.Error' + summary: Remove attachment. + tags: + - Attachments + get: + description: Serve the attachment with the given id + parameters: + - description: Attachment ID + in: path + name: attachment + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.Error' + summary: Serve Attachment. + tags: + - Attachments /v1/groups/{number}: get: consumes: @@ -175,7 +239,7 @@ paths: - Groups /v1/qrcodelink: get: - description: test + description: Link device and generate QR code produces: - application/json responses: @@ -342,3 +406,5 @@ tags: name: Groups - description: Send and Receive Signal Messages. name: Messages +- description: List and Delete Attachments. + name: Attachments diff --git a/src/main.go b/src/main.go index 0f4b494..2d7c0ed 100644 --- a/src/main.go +++ b/src/main.go @@ -31,6 +31,9 @@ import ( // @tag.name Messages // @tag.description Send and Receive Signal Messages. +// @tag.name Attachments +// @tag.description List and Delete Attachments. + // @host 127.0.0.1:8080 // @BasePath / func main() { @@ -76,6 +79,13 @@ func main() { { link.GET("", api.GetQrCodeLink) } + + attachments := v1.Group("attachments") + { + attachments.GET("", api.GetAttachments) + attachments.DELETE(":attachment", api.RemoveAttachment) + attachments.GET(":attachment", api.ServeAttachment) + } } v2 := router.Group("/v2")