From c05e7c3aea2ea4045f5b69bd94d88bcb8eb8ed4a Mon Sep 17 00:00:00 2001 From: James Tombleson Date: Sun, 2 Jun 2024 17:27:36 -0700 Subject: [PATCH] updated handlers to define if the response was an error --- docs/docs.go | 148 +++++++-- docs/swagger.json | 148 +++++++-- docs/swagger.yaml | 118 +++++-- domain/responses.go | 1 + internal/handler/v1/articles.go | 66 ++-- internal/handler/v1/discordwebhooks.go | 259 +++++++++------ internal/handler/v1/handler.go | 54 ++-- internal/handler/v1/sources.go | 422 +++++++++++++++---------- internal/handler/v1/users.go | 194 ++++++++---- 9 files changed, 914 insertions(+), 496 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index 1151667..2862d26 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -225,13 +225,13 @@ const docTemplate = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.DiscordWebhookResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.DiscordWebhookResponse" } } } @@ -277,13 +277,13 @@ const docTemplate = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.DiscordWebhookResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.DiscordWebhookResponse" } } } @@ -333,13 +333,13 @@ const docTemplate = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.DiscordWebhookResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.DiscordWebhookResponse" } } } @@ -347,6 +347,11 @@ const docTemplate = `{ }, "/v1/discord/webhooks/{ID}": { "delete": { + "security": [ + { + "Bearer": [] + } + ], "tags": [ "DiscordWebhook" ], @@ -370,13 +375,13 @@ const docTemplate = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.DiscordWebhookResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.DiscordWebhookResponse" } } } @@ -412,13 +417,13 @@ const docTemplate = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.DiscordWebhookResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.DiscordWebhookResponse" } } } @@ -444,7 +449,26 @@ const docTemplate = `{ "required": true } ], - "responses": {} + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/domain.DiscordWebhookResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/domain.DiscordWebhookResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/domain.DiscordWebhookResponse" + } + } + } } }, "/v1/discord/webhooks/{id}": { @@ -480,13 +504,13 @@ const docTemplate = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.DiscordWebhookResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.DiscordWebhookResponse" } } } @@ -524,7 +548,7 @@ const docTemplate = `{ "400": { "description": "Unable to reach SQL or Data problems", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } } } @@ -569,13 +593,13 @@ const docTemplate = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } } } @@ -670,13 +694,13 @@ const docTemplate = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } } } @@ -719,13 +743,13 @@ const docTemplate = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } } } @@ -751,7 +775,26 @@ const docTemplate = `{ "required": true } ], - "responses": {} + "responses": { + "200": { + "description": "ok", + "schema": { + "$ref": "#/definitions/domain.SourcesResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/domain.SourcesResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/domain.SourcesResponse" + } + } + } } }, "/v1/sources/new/youtube": { @@ -781,7 +824,26 @@ const docTemplate = `{ "required": true } ], - "responses": {} + "responses": { + "200": { + "description": "ok", + "schema": { + "$ref": "#/definitions/domain.SourcesResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/domain.SourcesResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/domain.SourcesResponse" + } + } + } } }, "/v1/sources/{id}": { @@ -817,13 +879,13 @@ const docTemplate = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } } } @@ -857,13 +919,13 @@ const docTemplate = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } } } @@ -899,13 +961,13 @@ const docTemplate = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } } } @@ -941,13 +1003,13 @@ const docTemplate = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } } } @@ -984,13 +1046,13 @@ const docTemplate = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.LoginResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.LoginResponse" } } } @@ -1101,8 +1163,8 @@ const docTemplate = `{ } ], "responses": { - "200": { - "description": "OK", + "201": { + "description": "Created", "schema": { "$ref": "#/definitions/domain.BaseResponse" } @@ -1238,6 +1300,9 @@ const docTemplate = `{ "domain.ArticleDetailedResponse": { "type": "object", "properties": { + "isError": { + "type": "boolean" + }, "message": { "type": "string" }, @@ -1287,6 +1352,9 @@ const docTemplate = `{ "domain.ArticleResponse": { "type": "object", "properties": { + "isError": { + "type": "boolean" + }, "message": { "type": "string" }, @@ -1301,6 +1369,9 @@ const docTemplate = `{ "domain.BaseResponse": { "type": "object", "properties": { + "isError": { + "type": "boolean" + }, "message": { "type": "string" } @@ -1330,6 +1401,9 @@ const docTemplate = `{ "domain.DiscordWebhookResponse": { "type": "object", "properties": { + "isError": { + "type": "boolean" + }, "message": { "type": "string" }, @@ -1344,6 +1418,9 @@ const docTemplate = `{ "domain.LoginResponse": { "type": "object", "properties": { + "isError": { + "type": "boolean" + }, "message": { "type": "string" }, @@ -1395,6 +1472,9 @@ const docTemplate = `{ "domain.SourcesResponse": { "type": "object", "properties": { + "isError": { + "type": "boolean" + }, "message": { "type": "string" }, diff --git a/docs/swagger.json b/docs/swagger.json index 2f5f8ae..10d542c 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -216,13 +216,13 @@ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.DiscordWebhookResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.DiscordWebhookResponse" } } } @@ -268,13 +268,13 @@ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.DiscordWebhookResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.DiscordWebhookResponse" } } } @@ -324,13 +324,13 @@ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.DiscordWebhookResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.DiscordWebhookResponse" } } } @@ -338,6 +338,11 @@ }, "/v1/discord/webhooks/{ID}": { "delete": { + "security": [ + { + "Bearer": [] + } + ], "tags": [ "DiscordWebhook" ], @@ -361,13 +366,13 @@ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.DiscordWebhookResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.DiscordWebhookResponse" } } } @@ -403,13 +408,13 @@ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.DiscordWebhookResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.DiscordWebhookResponse" } } } @@ -435,7 +440,26 @@ "required": true } ], - "responses": {} + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/domain.DiscordWebhookResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/domain.DiscordWebhookResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/domain.DiscordWebhookResponse" + } + } + } } }, "/v1/discord/webhooks/{id}": { @@ -471,13 +495,13 @@ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.DiscordWebhookResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.DiscordWebhookResponse" } } } @@ -515,7 +539,7 @@ "400": { "description": "Unable to reach SQL or Data problems", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } } } @@ -560,13 +584,13 @@ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } } } @@ -661,13 +685,13 @@ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } } } @@ -710,13 +734,13 @@ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } } } @@ -742,7 +766,26 @@ "required": true } ], - "responses": {} + "responses": { + "200": { + "description": "ok", + "schema": { + "$ref": "#/definitions/domain.SourcesResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/domain.SourcesResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/domain.SourcesResponse" + } + } + } } }, "/v1/sources/new/youtube": { @@ -772,7 +815,26 @@ "required": true } ], - "responses": {} + "responses": { + "200": { + "description": "ok", + "schema": { + "$ref": "#/definitions/domain.SourcesResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/domain.SourcesResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/domain.SourcesResponse" + } + } + } } }, "/v1/sources/{id}": { @@ -808,13 +870,13 @@ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } } } @@ -848,13 +910,13 @@ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } } } @@ -890,13 +952,13 @@ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } } } @@ -932,13 +994,13 @@ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.SourcesResponse" } } } @@ -975,13 +1037,13 @@ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.LoginResponse" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/domain.BaseResponse" + "$ref": "#/definitions/domain.LoginResponse" } } } @@ -1092,8 +1154,8 @@ } ], "responses": { - "200": { - "description": "OK", + "201": { + "description": "Created", "schema": { "$ref": "#/definitions/domain.BaseResponse" } @@ -1229,6 +1291,9 @@ "domain.ArticleDetailedResponse": { "type": "object", "properties": { + "isError": { + "type": "boolean" + }, "message": { "type": "string" }, @@ -1278,6 +1343,9 @@ "domain.ArticleResponse": { "type": "object", "properties": { + "isError": { + "type": "boolean" + }, "message": { "type": "string" }, @@ -1292,6 +1360,9 @@ "domain.BaseResponse": { "type": "object", "properties": { + "isError": { + "type": "boolean" + }, "message": { "type": "string" } @@ -1321,6 +1392,9 @@ "domain.DiscordWebhookResponse": { "type": "object", "properties": { + "isError": { + "type": "boolean" + }, "message": { "type": "string" }, @@ -1335,6 +1409,9 @@ "domain.LoginResponse": { "type": "object", "properties": { + "isError": { + "type": "boolean" + }, "message": { "type": "string" }, @@ -1386,6 +1463,9 @@ "domain.SourcesResponse": { "type": "object", "properties": { + "isError": { + "type": "boolean" + }, "message": { "type": "string" }, diff --git a/docs/swagger.yaml b/docs/swagger.yaml index f96541c..77354da 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -9,6 +9,8 @@ definitions: type: object domain.ArticleDetailedResponse: properties: + isError: + type: boolean message: type: string payload: @@ -41,6 +43,8 @@ definitions: type: object domain.ArticleResponse: properties: + isError: + type: boolean message: type: string payload: @@ -50,6 +54,8 @@ definitions: type: object domain.BaseResponse: properties: + isError: + type: boolean message: type: string type: object @@ -71,6 +77,8 @@ definitions: type: object domain.DiscordWebhookResponse: properties: + isError: + type: boolean message: type: string payload: @@ -80,6 +88,8 @@ definitions: type: object domain.LoginResponse: properties: + isError: + type: boolean message: type: string refreshToken: @@ -113,6 +123,8 @@ definitions: type: object domain.SourcesResponse: properties: + isError: + type: boolean message: type: string payload: @@ -264,11 +276,11 @@ paths: "400": description: Bad Request schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.DiscordWebhookResponse' "500": description: Internal Server Error schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.DiscordWebhookResponse' security: - Bearer: [] summary: Returns the top 100 @@ -290,11 +302,13 @@ paths: "400": description: Bad Request schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.DiscordWebhookResponse' "500": description: Internal Server Error schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.DiscordWebhookResponse' + security: + - Bearer: [] summary: Deletes a record by ID. tags: - DiscordWebhook @@ -314,11 +328,11 @@ paths: "400": description: Bad Request schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.DiscordWebhookResponse' "500": description: Internal Server Error schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.DiscordWebhookResponse' security: - Bearer: [] summary: Disables a Webhook from being used. @@ -332,7 +346,19 @@ paths: name: id required: true type: integer - responses: {} + responses: + "200": + description: OK + schema: + $ref: '#/definitions/domain.DiscordWebhookResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/domain.DiscordWebhookResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/domain.DiscordWebhookResponse' security: - Bearer: [] summary: Enables a source to continue processing. @@ -356,11 +382,11 @@ paths: "400": description: Bad Request schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.DiscordWebhookResponse' "500": description: Internal Server Error schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.DiscordWebhookResponse' security: - Bearer: [] summary: Returns the top 100 entries from the queue to be processed. @@ -389,11 +415,11 @@ paths: "400": description: Bad Request schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.DiscordWebhookResponse' "500": description: Internal Server Error schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.DiscordWebhookResponse' security: - Bearer: [] summary: Returns all the known web hooks based on the Server and Channel given. @@ -425,11 +451,11 @@ paths: "400": description: Bad Request schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.DiscordWebhookResponse' "500": description: Internal Server Error schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.DiscordWebhookResponse' security: - Bearer: [] summary: Creates a new record for a discord web hook to post data to. @@ -452,7 +478,7 @@ paths: "400": description: Unable to reach SQL or Data problems schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.SourcesResponse' security: - Bearer: [] summary: Lists the top 50 records @@ -476,11 +502,11 @@ paths: "400": description: Bad Request schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.SourcesResponse' "500": description: Internal Server Error schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.SourcesResponse' security: - Bearer: [] summary: Returns a single entity by ID @@ -501,11 +527,11 @@ paths: "400": description: Bad Request schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.SourcesResponse' "500": description: Internal Server Error schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.SourcesResponse' security: - Bearer: [] summary: Marks a source as deleted based on its ID value. @@ -527,11 +553,11 @@ paths: "400": description: Bad Request schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.SourcesResponse' "500": description: Internal Server Error schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.SourcesResponse' security: - Bearer: [] summary: Disables a source from processing. @@ -553,11 +579,11 @@ paths: "400": description: Bad Request schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.SourcesResponse' "500": description: Internal Server Error schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.SourcesResponse' security: - Bearer: [] summary: Enables a source to continue processing. @@ -585,11 +611,11 @@ paths: "400": description: Bad Request schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.SourcesResponse' "500": description: Internal Server Error schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.SourcesResponse' security: - Bearer: [] summary: 'Lists the top 50 records based on the name given. Example: reddit' @@ -649,11 +675,11 @@ paths: "400": description: Bad Request schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.SourcesResponse' "500": description: Internal Server Error schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.SourcesResponse' security: - Bearer: [] summary: Creates a new reddit source to monitor. @@ -680,11 +706,11 @@ paths: "400": description: Bad Request schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.SourcesResponse' "500": description: Internal Server Error schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.SourcesResponse' security: - Bearer: [] summary: Creates a new rss source to monitor. @@ -698,7 +724,19 @@ paths: name: name required: true type: string - responses: {} + responses: + "200": + description: ok + schema: + $ref: '#/definitions/domain.SourcesResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/domain.SourcesResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/domain.SourcesResponse' security: - Bearer: [] summary: Creates a new twitch source to monitor. @@ -717,7 +755,19 @@ paths: name: url required: true type: string - responses: {} + responses: + "200": + description: ok + schema: + $ref: '#/definitions/domain.SourcesResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/domain.SourcesResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/domain.SourcesResponse' security: - Bearer: [] summary: Creates a new youtube source to monitor. @@ -742,11 +792,11 @@ paths: "400": description: Bad Request schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.LoginResponse' "500": description: Internal Server Error schema: - $ref: '#/definitions/domain.BaseResponse' + $ref: '#/definitions/domain.LoginResponse' summary: Logs into the API and returns a bearer token if successful tags: - Users @@ -813,8 +863,8 @@ paths: produces: - application/json responses: - "200": - description: OK + "201": + description: Created schema: $ref: '#/definitions/domain.BaseResponse' "400": diff --git a/domain/responses.go b/domain/responses.go index 6fe048e..689e949 100644 --- a/domain/responses.go +++ b/domain/responses.go @@ -2,6 +2,7 @@ package domain type BaseResponse struct { Message string `json:"message"` + IsError bool `json:"isError"` } type LoginResponse struct { diff --git a/internal/handler/v1/articles.go b/internal/handler/v1/articles.go index 4b3ce5f..9f086cd 100644 --- a/internal/handler/v1/articles.go +++ b/internal/handler/v1/articles.go @@ -20,17 +20,18 @@ import ( // @Failure 500 {object} domain.BaseResponse // @Security Bearer func (s *Handler) listArticles(c echo.Context) error { - _, err := s.ValidateJwtToken(c, domain.ScopeArticleRead) - if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) - } - resp := domain.ArticleResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, + IsError: true, }, } + _, err := s.ValidateJwtToken(c, domain.ScopeArticleRead) + if err != nil { + return c.JSON(http.StatusUnauthorized, resp) + } + page, err := strconv.Atoi(c.QueryParam("page")) if err != nil { page = 0 @@ -38,10 +39,11 @@ func (s *Handler) listArticles(c echo.Context) error { res, err := s.repo.Articles.ListByPage(c.Request().Context(), page, 25) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + return c.JSON(http.StatusInternalServerError, resp) } resp.Payload = dtoconv.ArticlesToDto(res) + resp.BaseResponse.IsError = false return c.JSON(http.StatusOK, resp) } @@ -56,32 +58,34 @@ func (s *Handler) listArticles(c echo.Context) error { // @Failure 500 {object} domain.BaseResponse // @Security Bearer func (s *Handler) getArticle(c echo.Context) error { - _, err := s.ValidateJwtToken(c, domain.ScopeArticleRead) - if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) - } - p := domain.ArticleResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, + IsError: true, }, } + _, err := s.ValidateJwtToken(c, domain.ScopeArticleRead) + if err != nil { + return c.JSON(http.StatusUnauthorized, p) + } + id := c.Param("ID") idNumber, err := strconv.Atoi(id) if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) + return c.JSON(http.StatusBadRequest, p) } item, err := s.repo.Articles.GetById(c.Request().Context(), int64(idNumber)) if err != nil { - return c.JSON(http.StatusInternalServerError, err) + return c.JSON(http.StatusBadRequest, p) } var dtos []domain.ArticleDto dtos = append(dtos, dtoconv.ArticleToDto(item)) p.Payload = dtos + p.BaseResponse.IsError = false return c.JSON(http.StatusOK, p) } @@ -96,36 +100,37 @@ func (s *Handler) getArticle(c echo.Context) error { // @Failure 500 {object} domain.BaseResponse // @Security Bearer func (s *Handler) getArticleDetails(c echo.Context) error { - _, err := s.ValidateJwtToken(c, domain.ScopeArticleRead) - if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) - } - p := domain.ArticleDetailedResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, + IsError: true, }, Payload: domain.ArticleAndSourceModel{}, } + _, err := s.ValidateJwtToken(c, domain.ScopeArticleRead) + if err != nil { + return c.JSON(http.StatusUnauthorized, p) + } + id, err := strconv.Atoi(c.Param("ID")) if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) + return c.JSON(http.StatusBadRequest, p) } article, err := s.repo.Articles.GetById(c.Request().Context(), int64(id)) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + return c.JSON(http.StatusBadRequest, p) } source, err := s.repo.Sources.GetById(c.Request().Context(), article.SourceID) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + return c.JSON(http.StatusBadRequest, p) } p.Payload.Article = dtoconv.ArticleToDto(article) p.Payload.Source = dtoconv.SourceToDto(source) - + p.BaseResponse.IsError = false return c.JSON(http.StatusOK, p) } @@ -141,20 +146,21 @@ func (s *Handler) getArticleDetails(c echo.Context) error { // @Failure 500 {object} domain.BaseResponse // @Security Bearer func (s *Handler) ListArticlesBySourceId(c echo.Context) error { - _, err := s.ValidateJwtToken(c, domain.ScopeArticleRead) - if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) - } - p := domain.ArticleResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, + IsError: true, }, } + _, err := s.ValidateJwtToken(c, domain.ScopeArticleRead) + if err != nil { + return c.JSON(http.StatusUnauthorized, p) + } + id, err := strconv.Atoi(c.QueryParam("id")) if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) + return c.JSON(http.StatusBadRequest, p) } // if the page number is missing, default to 0 @@ -165,9 +171,11 @@ func (s *Handler) ListArticlesBySourceId(c echo.Context) error { items, err := s.repo.Articles.ListBySource(c.Request().Context(), _page, 25, id, "") if err != nil { - return c.JSON(http.StatusInternalServerError, err) + return c.JSON(http.StatusBadRequest, p) } p.Payload = dtoconv.ArticlesToDto(items) + p.BaseResponse.IsError = false + return c.JSON(http.StatusOK, p) } diff --git a/internal/handler/v1/discordwebhooks.go b/internal/handler/v1/discordwebhooks.go index 944616b..33abda2 100644 --- a/internal/handler/v1/discordwebhooks.go +++ b/internal/handler/v1/discordwebhooks.go @@ -16,26 +16,29 @@ import ( // @Tags DiscordWebhook // @Router /v1/discord/webhooks [get] // @Success 200 {object} domain.DiscordWebhookResponse -// @Failure 400 {object} domain.BaseResponse -// @Failure 500 {object} domain.BaseResponse +// @Failure 400 {object} domain.DiscordWebhookResponse +// @Failure 500 {object} domain.DiscordWebhookResponse // @Security Bearer func (s *Handler) ListDiscordWebHooks(c echo.Context) error { - _, err := s.ValidateJwtToken(c, domain.ScopeDiscordWebhookRead) - if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) - } - p := domain.DiscordWebhookResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, + IsError: true, }, } + _, err := s.ValidateJwtToken(c, domain.ScopeDiscordWebhookRead) + if err != nil { + return c.JSON(http.StatusUnauthorized, p) + } + res, err := s.repo.DiscordWebHooks.ListByServerName(c.Request().Context(), "") if err != nil { - return c.JSON(http.StatusInternalServerError, err) + return c.JSON(http.StatusInternalServerError, p) } + p.Payload = dtoconv.DiscordWebhooksToDto(res) + p.BaseResponse.IsError = false return c.JSON(http.StatusOK, p) } @@ -46,33 +49,36 @@ func (s *Handler) ListDiscordWebHooks(c echo.Context) error { // @Tags DiscordWebhook // @Router /v1/discord/webhooks/{id} [get] // @Success 200 {object} domain.DiscordWebhookResponse "OK" -// @Failure 400 {object} domain.BaseResponse -// @Failure 500 {object} domain.BaseResponse +// @Failure 400 {object} domain.DiscordWebhookResponse +// @Failure 500 {object} domain.DiscordWebhookResponse // @Security Bearer func (s *Handler) GetDiscordWebHooksById(c echo.Context) error { - _, err := s.ValidateJwtToken(c, domain.ScopeDiscordWebhookRead) - if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) - } - p := domain.DiscordWebhookResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, + IsError: true, }, } + _, err := s.ValidateJwtToken(c, domain.ScopeDiscordWebhookRead) + if err != nil { + return c.JSON(http.StatusUnauthorized, p) + } + id, err := strconv.Atoi(c.Param("ID")) if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) + return c.JSON(http.StatusBadRequest, p) } res, err := s.repo.DiscordWebHooks.GetById(c.Request().Context(), int64(id)) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + return c.JSON(http.StatusInternalServerError, p) } + var dtos []domain.DiscordWebHookDto dtos = append(dtos, dtoconv.DiscordWebhookToDto(res)) p.Payload = dtos + p.BaseResponse.IsError = false return c.JSON(http.StatusOK, p) } @@ -84,37 +90,43 @@ func (s *Handler) GetDiscordWebHooksById(c echo.Context) error { // @Tags DiscordWebhook // @Router /v1/discord/webhooks/by/serverAndChannel [get] // @Success 200 {object} domain.DiscordWebhookResponse "OK" -// @Failure 400 {object} domain.BaseResponse -// @Failure 500 {object} domain.BaseResponse +// @Failure 400 {object} domain.DiscordWebhookResponse +// @Failure 500 {object} domain.DiscordWebhookResponse // @Security Bearer func (s *Handler) GetDiscordWebHooksByServerAndChannel(c echo.Context) error { - _, err := s.ValidateJwtToken(c, domain.ScopeDiscordWebhookRead) - if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) - } - p := domain.DiscordWebhookResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, + IsError: true, }, } + _, err := s.ValidateJwtToken(c, domain.ScopeDiscordWebhookRead) + if err != nil { + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusUnauthorized, p) + } + _server := c.QueryParam("server") if _server == "" { - return s.WriteMessage(c, "server was not defined", http.StatusBadRequest) + p.BaseResponse.Message = "server was not defined" + return c.JSON(http.StatusBadRequest, p) } _channel := c.QueryParam("channel") if _channel == "" { - return s.WriteMessage(c, "channel was not defined", http.StatusBadRequest) + p.BaseResponse.Message = "channel was not defined" + return c.JSON(http.StatusBadRequest, p) } res, err := s.repo.DiscordWebHooks.ListByServerAndChannel(c.Request().Context(), _server, _channel) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } p.Payload = dtoconv.DiscordWebhooksToDto(res) + p.IsError = false return c.JSON(http.StatusOK, p) } @@ -126,13 +138,21 @@ func (s *Handler) GetDiscordWebHooksByServerAndChannel(c echo.Context) error { // @Tags DiscordWebhook // @Router /v1/discord/webhooks/new [post] // @Success 200 {object} domain.DiscordWebhookResponse "OK" -// @Failure 400 {object} domain.BaseResponse -// @Failure 500 {object} domain.BaseResponse +// @Failure 400 {object} domain.DiscordWebhookResponse +// @Failure 500 {object} domain.DiscordWebhookResponse // @Security Bearer func (s *Handler) NewDiscordWebHook(c echo.Context) error { + p := domain.DiscordWebhookResponse{ + BaseResponse: domain.BaseResponse{ + Message: ResponseMessageSuccess, + IsError: true, + }, + } + token, err := s.ValidateJwtToken(c, domain.ScopeDiscordWebHookCreate) if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusUnauthorized, p) } _url := c.QueryParam("url") @@ -140,54 +160,51 @@ func (s *Handler) NewDiscordWebHook(c echo.Context) error { _channel := c.QueryParam("channel") if _url == "" { - return c.JSON(http.StatusBadRequest, domain.BaseResponse{ - Message: "url is missing a value", - }) + p.Message = "url is missing a value" + return c.JSON(http.StatusBadRequest, p) } if !strings.Contains(_url, "discord.com/api/webhooks") { - return c.JSON(http.StatusBadRequest, domain.BaseResponse{ - Message: "invalid url", - }) + p.Message = "invalid url" + return c.JSON(http.StatusBadRequest, p) } if _server == "" { - return c.JSON(http.StatusBadRequest, domain.BaseResponse{ - Message: "server is missing", - }) + p.Message = "server is missing" + return c.JSON(http.StatusBadRequest, p) } if _channel == "" { - return c.JSON(http.StatusBadRequest, domain.BaseResponse{ - Message: "channel is missing", - }) + p.Message = "channel is missing" + return c.JSON(http.StatusBadRequest, p) } user, err := s.repo.Users.GetUser(c.Request().Context(), token.UserName) if err != nil { - return s.WriteMessage(c, ErrUserUnknown, http.StatusBadRequest) + p.Message = err.Error() + return c.JSON(http.StatusBadRequest, p) } rows, err := s.repo.DiscordWebHooks.Create(c.Request().Context(), user.ID, _url, _server, _channel, true) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + p.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } if rows != 1 { - return s.WriteMessage(c, "data was not written to database", http.StatusInternalServerError) + p.Message = "data was not written to database" + return c.JSON(http.StatusInternalServerError, p) } item, err := s.repo.DiscordWebHooks.GetByUrl(c.Request().Context(), _url) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + p.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } var dtos []domain.DiscordWebHookDto dtos = append(dtos, dtoconv.DiscordWebhookToDto(item)) - return c.JSON(http.StatusOK, domain.DiscordWebhookResponse{ - BaseResponse: domain.BaseResponse{ - Message: ResponseMessageSuccess, - }, - Payload: dtos, - }) + p.Payload = dtos + p.IsError = false + return c.JSON(http.StatusOK, p) } // DisableDiscordWebHooks @@ -196,56 +213,66 @@ func (s *Handler) NewDiscordWebHook(c echo.Context) error { // @Tags DiscordWebhook // @Router /v1/discord/webhooks/{ID}/disable [post] // @Success 200 {object} domain.DiscordWebhookResponse "OK" -// @Failure 400 {object} domain.BaseResponse -// @Failure 500 {object} domain.BaseResponse +// @Failure 400 {object} domain.DiscordWebhookResponse +// @Failure 500 {object} domain.DiscordWebhookResponse // @Security Bearer func (s *Handler) disableDiscordWebHook(c echo.Context) error { + p := domain.DiscordWebhookResponse{ + BaseResponse: domain.BaseResponse{ + Message: ResponseMessageSuccess, + IsError: true, + }, + } + _, err := s.ValidateJwtToken(c, domain.ScopeDiscordWebHookCreate) if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusUnauthorized, p) } id, err := strconv.Atoi(c.Param("ID")) if err != nil { - return c.JSON(http.StatusBadRequest, domain.BaseResponse{ - Message: err.Error(), - }) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusBadRequest, p) } // Check to make sure we can find the record record, err := s.repo.DiscordWebHooks.GetById(c.Request().Context(), int64(id)) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } if record.UserID != s.GetUserIdFromJwtToken(c) { - return s.WriteMessage(c, ErrYouDontOwnTheRecord, http.StatusBadRequest) + p.BaseResponse.Message = ErrYouDontOwnTheRecord + return c.JSON(http.StatusBadRequest, p) } // flip the it updated, err := s.repo.DiscordWebHooks.Disable(c.Request().Context(), int64(id)) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } // make sure we got a row updated if updated != 1 { - return s.WriteMessage(c, "unexpected number of updates found", http.StatusInternalServerError) + p.BaseResponse.Message = "unexpected number of updates found" + return c.JSON(http.StatusInternalServerError, p) } item, err := s.repo.DiscordWebHooks.GetById(c.Request().Context(), int64(id)) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } var dtos []domain.DiscordWebHookDto dtos = append(dtos, dtoconv.DiscordWebhookToDto(item)) - return c.JSON(http.StatusOK, domain.DiscordWebhookResponse{ - BaseResponse: domain.BaseResponse{ - Message: ResponseMessageSuccess, - }, - Payload: dtos, - }) + + p.Payload = dtos + p.IsError = false + return c.JSON(http.StatusOK, p) } // EnableDiscordWebHook @@ -253,104 +280,132 @@ func (s *Handler) disableDiscordWebHook(c echo.Context) error { // @Param id path int true "id" // @Tags DiscordWebhook // @Router /v1/discord/webhooks/{ID}/enable [post] +// @Success 200 {object} domain.DiscordWebhookResponse "OK" +// @Failure 400 {object} domain.DiscordWebhookResponse +// @Failure 500 {object} domain.DiscordWebhookResponse // @Security Bearer func (s *Handler) enableDiscordWebHook(c echo.Context) error { + p := domain.DiscordWebhookResponse{ + BaseResponse: domain.BaseResponse{ + Message: ResponseMessageSuccess, + IsError: true, + }, + } + _, err := s.ValidateJwtToken(c, domain.ScopeDiscordWebHookCreate) if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusUnauthorized, p) } id, err := strconv.Atoi(c.Param("ID")) if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusBadRequest, p) } // Check to make sure we can find the record record, err := s.repo.DiscordWebHooks.GetById(c.Request().Context(), int64(id)) if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusBadRequest, p) } if record.UserID != s.GetUserIdFromJwtToken(c) { - return s.WriteMessage(c, ErrYouDontOwnTheRecord, http.StatusBadRequest) + p.BaseResponse.Message = ErrYouDontOwnTheRecord + return c.JSON(http.StatusBadRequest, p) } updated, err := s.repo.DiscordWebHooks.Enable(c.Request().Context(), int64(id)) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } if updated != 1 { - return s.WriteMessage(c, ErrFailedToUpdateRecord, http.StatusInternalServerError) + p.BaseResponse.Message = ErrFailedToUpdateRecord + return c.JSON(http.StatusInternalServerError, p) } item, err := s.repo.DiscordWebHooks.GetById(c.Request().Context(), int64(id)) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } var dtos []domain.DiscordWebHookDto dtos = append(dtos, dtoconv.DiscordWebhookToDto(item)) - return c.JSON(http.StatusOK, domain.DiscordWebhookResponse{ - BaseResponse: domain.BaseResponse{ - Message: ResponseMessageSuccess, - }, - Payload: dtos, - }) + + p.Payload = dtos + p.IsError = false + return c.JSON(http.StatusOK, p) } // DeleteDiscordWebHook -// @Summary Deletes a record by ID. -// @Param id path string true "id" -// @Tags DiscordWebhook -// @Router /v1/discord/webhooks/{ID} [delete] -// @Success 200 {object} domain.DiscordWebhookResponse "OK" -// @Failure 400 {object} domain.BaseResponse -// @Failure 500 {object} domain.BaseResponse +// @Summary Deletes a record by ID. +// @Param id path string true "id" +// @Tags DiscordWebhook +// @Router /v1/discord/webhooks/{ID} [delete] +// @Success 200 {object} domain.DiscordWebhookResponse "OK" +// @Failure 400 {object} domain.DiscordWebhookResponse +// @Failure 500 {object} domain.DiscordWebhookResponse +// @Security Bearer func (s *Handler) deleteDiscordWebHook(c echo.Context) error { + p := domain.DiscordWebhookResponse{ + BaseResponse: domain.BaseResponse{ + Message: ResponseMessageSuccess, + IsError: true, + }, + } + _, err := s.ValidateJwtToken(c, domain.ScopeDiscordWebHookCreate) if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusUnauthorized, p) } id, err := strconv.Atoi(c.Param("ID")) if err != nil { - return c.JSON(http.StatusBadRequest, err.Error()) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusBadRequest, p) } // Check to make sure we can find the record record, err := s.repo.DiscordWebHooks.GetById(c.Request().Context(), int64(id)) if err != nil { - return c.JSON(http.StatusInternalServerError, err.Error()) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusBadRequest, p) } if record.UserID != s.GetUserIdFromJwtToken(c) { - return s.WriteMessage(c, ErrYouDontOwnTheRecord, http.StatusBadRequest) + p.BaseResponse.Message = ErrYouDontOwnTheRecord + return c.JSON(http.StatusBadRequest, p) } // Soft delete the record updated, err := s.repo.DiscordWebHooks.SoftDelete(c.Request().Context(), int64(id)) if err != nil { - return c.JSON(http.StatusInternalServerError, err.Error()) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } if updated != 1 { - return s.WriteMessage(c, ErrFailedToUpdateRecord, http.StatusInternalServerError) + p.BaseResponse.Message = ErrFailedToUpdateRecord + return c.JSON(http.StatusInternalServerError, p) } item, err := s.repo.DiscordWebHooks.GetById(c.Request().Context(), int64(id)) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } var dtos []domain.DiscordWebHookDto dtos = append(dtos, dtoconv.DiscordWebhookToDto(item)) - return c.JSON(http.StatusOK, domain.DiscordWebhookResponse{ - BaseResponse: domain.BaseResponse{ - Message: ResponseMessageSuccess, - }, - Payload: dtos, - }) + + p.Payload = dtos + p.BaseResponse.IsError = false + return c.JSON(http.StatusOK, p) } // UpdateDiscordWebHook diff --git a/internal/handler/v1/handler.go b/internal/handler/v1/handler.go index 0c86f7a..1e27f62 100644 --- a/internal/handler/v1/handler.go +++ b/internal/handler/v1/handler.go @@ -4,7 +4,6 @@ import ( "context" "database/sql" "errors" - "net/http" "github.com/golang-jwt/jwt/v5" echojwt "github.com/labstack/echo-jwt/v4" @@ -13,7 +12,6 @@ import ( swagger "github.com/swaggo/echo-swagger" _ "git.jamestombleson.com/jtom38/newsbot-api/docs" - "git.jamestombleson.com/jtom38/newsbot-api/domain" "git.jamestombleson.com/jtom38/newsbot-api/internal/services" ) @@ -48,8 +46,6 @@ var ( func NewServer(ctx context.Context, configs services.Configs, conn *sql.DB) *Handler { s := &Handler{ - //Db: db, - //dto: dto.NewDtoClient(db), config: configs, repo: services.NewRepositoryService(conn), } @@ -126,29 +122,30 @@ func NewServer(ctx context.Context, configs services.Configs, conn *sql.DB) *Han // *ApiStatusModel //} -func (s *Handler) WriteError(c echo.Context, errMessage error, HttpStatusCode int) error { - return c.JSON(HttpStatusCode, domain.BaseResponse{ - Message: errMessage.Error(), - }) -} +//func (s *Handler) WriteError(c echo.Context, errMessage error, HttpStatusCode int) error { +// return c.JSON(HttpStatusCode, domain.BaseResponse{ +// Message: errMessage.Error(), +// IsError: true, +// }) +//} -func (s *Handler) WriteMessage(c echo.Context, msg string, HttpStatusCode int) error { - return c.JSON(HttpStatusCode, domain.BaseResponse{ - Message: msg, - }) -} +//func (s *Handler) WriteMessage(c echo.Context, msg string, HttpStatusCode int) error { +// return c.JSON(HttpStatusCode, domain.BaseResponse{ +// Message: msg, +// }) +//} -func (s *Handler) InternalServerErrorResponse(c echo.Context, msg string) error { - return c.JSON(http.StatusInternalServerError, domain.BaseResponse{ - Message: msg, - }) -} +//func (s *Handler) InternalServerErrorResponse(c echo.Context, msg string) error { +// return c.JSON(http.StatusInternalServerError, domain.BaseResponse{ +// Message: msg, +// }) +//} -func (s *Handler) UnauthorizedResponse(c echo.Context, msg string) error { - return c.JSON(http.StatusUnauthorized, domain.BaseResponse{ - Message: msg, - }) -} +//func (s *Handler) UnauthorizedResponse(c echo.Context, msg string) error { +// return c.JSON(http.StatusUnauthorized, domain.BaseResponse{ +// Message: msg, +// }) +//} // If the token is not valid then an json error will be returned. // If the token has the wrong scope, a json error will be returned. @@ -156,7 +153,7 @@ func (s *Handler) UnauthorizedResponse(c echo.Context, msg string) error { func (s *Handler) ValidateJwtToken(c echo.Context, requiredScope string) (JwtToken, error) { token, err := s.getJwtTokenFromContext(c) if err != nil { - s.WriteMessage(c, ErrJwtMissing, http.StatusUnauthorized) + return JwtToken{}, errors.New(ErrJwtMissing) } err = token.hasExpired() @@ -173,6 +170,11 @@ func (s *Handler) ValidateJwtToken(c echo.Context, requiredScope string) (JwtTok return JwtToken{}, errors.New(ErrJwtInvalidIssuer) } + // If you are the built in admin account, skip the username and session token check + if token.UserName == "admin" { + return token, nil + } + user, err := s.repo.Users.GetUser(c.Request().Context(), token.UserName) if err != nil { return JwtToken{}, errors.New("user record not found") @@ -188,7 +190,7 @@ func (s *Handler) ValidateJwtToken(c echo.Context, requiredScope string) (JwtTok func (s *Handler) GetUserIdFromJwtToken(c echo.Context) int64 { token, err := s.getJwtTokenFromContext(c) if err != nil { - s.WriteMessage(c, ErrJwtMissing, http.StatusUnauthorized) + return -1 } return token.GetUserId() diff --git a/internal/handler/v1/sources.go b/internal/handler/v1/sources.go index ce66b22..e7d7c68 100644 --- a/internal/handler/v1/sources.go +++ b/internal/handler/v1/sources.go @@ -18,20 +18,22 @@ import ( // @Tags Source // @Router /v1/sources [get] // @Success 200 {object} domain.SourcesResponse "ok" -// @Failure 400 {object} domain.BaseResponse "Unable to reach SQL or Data problems" +// @Failure 400 {object} domain.SourcesResponse "Unable to reach SQL or Data problems" // @Security Bearer func (s *Handler) listSources(c echo.Context) error { - _, err := s.ValidateJwtToken(c, domain.ScopeSourceRead) - if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) - } - - resp := domain.SourcesResponse{ + p := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, + IsError: true, }, } + _, err := s.ValidateJwtToken(c, domain.ScopeSourceRead) + if err != nil { + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusUnauthorized, p) + } + page, err := strconv.Atoi(c.QueryParam("page")) if err != nil { page = 0 @@ -40,11 +42,13 @@ func (s *Handler) listSources(c echo.Context) error { // Default way of showing all sources items, err := s.repo.Sources.List(c.Request().Context(), page, 25) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } - resp.Payload = dtoconv.SourcesToDto(items) - return c.JSON(http.StatusOK, resp) + p.Payload = dtoconv.SourcesToDto(items) + p.BaseResponse.IsError = false + return c.JSON(http.StatusOK, p) } // ListSourcesBySource @@ -55,24 +59,27 @@ func (s *Handler) listSources(c echo.Context) error { // @Tags Source // @Router /v1/sources/by/source [get] // @Success 200 {object} domain.SourcesResponse "ok" -// @Failure 400 {object} domain.BaseResponse -// @Failure 500 {object} domain.BaseResponse +// @Failure 400 {object} domain.SourcesResponse +// @Failure 500 {object} domain.SourcesResponse // @Security Bearer func (s *Handler) listSourcesBySource(c echo.Context) error { - _, err := s.ValidateJwtToken(c, domain.ScopeSourceRead) - if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) - } - - resp := domain.SourcesResponse{ + p := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, + IsError: true, }, } + _, err := s.ValidateJwtToken(c, domain.ScopeSourceRead) + if err != nil { + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusUnauthorized, p) + } + source := c.QueryParam("source") if source == "" { - return s.WriteMessage(c, fmt.Sprintf("%s source", ErrParameterMissing), http.StatusBadRequest) + p.BaseResponse.Message = fmt.Sprintf("%s source", ErrParameterMissing) + return c.JSON(http.StatusBadRequest, p) } page, err := strconv.Atoi(c.QueryParam("page")) @@ -83,13 +90,13 @@ func (s *Handler) listSourcesBySource(c echo.Context) error { // Shows the list by Sources.source items, err := s.repo.Sources.ListBySource(c.Request().Context(), page, 25, source) if err != nil { - return c.JSON(http.StatusInternalServerError, domain.BaseResponse{ - Message: err.Error(), - }) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } - resp.Payload = dtoconv.SourcesToDto(items) - return c.JSON(http.StatusOK, resp) + p.Payload = dtoconv.SourcesToDto(items) + p.BaseResponse.IsError = false + return c.JSON(http.StatusOK, p) } // GetSource @@ -99,37 +106,39 @@ func (s *Handler) listSourcesBySource(c echo.Context) error { // @Tags Source // @Router /v1/sources/{id} [get] // @Success 200 {object} domain.SourcesResponse "ok" -// @Failure 400 {object} domain.BaseResponse -// @Failure 500 {object} domain.BaseResponse +// @Failure 400 {object} domain.SourcesResponse +// @Failure 500 {object} domain.SourcesResponse // @Security Bearer func (s *Handler) getSource(c echo.Context) error { - _, err := s.ValidateJwtToken(c, domain.ScopeSourceRead) - if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) - } - - resp := domain.SourcesResponse{ + p := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, }, } + _, err := s.ValidateJwtToken(c, domain.ScopeSourceRead) + if err != nil { + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusUnauthorized, p) + } + id, err := strconv.Atoi(c.Param("ID")) if err != nil { - return c.JSON(http.StatusBadRequest, domain.BaseResponse{ - Message: ErrUnableToParseId, - }) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusBadRequest, p) } item, err := s.repo.Sources.GetById(c.Request().Context(), int64(id)) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } var dto []domain.SourceDto dto = append(dto, dtoconv.SourceToDto(item)) - resp.Payload = dto - return c.JSON(http.StatusOK, resp) + p.Payload = dto + p.BaseResponse.IsError = false + return c.JSON(http.StatusOK, p) } // GetSourceByNameAndSource @@ -144,34 +153,37 @@ func (s *Handler) getSource(c echo.Context) error { // @Failure 500 {object} domain.BaseResponse // @Security Bearer func (s *Handler) GetSourceBySourceAndName(c echo.Context) error { - _, err := s.ValidateJwtToken(c, domain.ScopeSourceRead) - if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) - } - - resp := domain.SourcesResponse{ + p := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, + IsError: true, }, } + _, err := s.ValidateJwtToken(c, domain.ScopeSourceRead) + if err != nil { + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusUnauthorized, p) + } + var param domain.GetSourceBySourceAndNameParamRequest err = c.Bind(¶m) if err != nil { - return c.JSON(http.StatusBadRequest, domain.BaseResponse{ - Message: err.Error(), - }) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusBadRequest, p) } item, err := s.repo.Sources.GetBySourceAndName(c.Request().Context(), param.Source, param.Name) if err != nil { - return c.JSON(http.StatusInternalServerError, err.Error()) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } var dto []domain.SourceDto dto = append(dto, dtoconv.SourceToDto(item)) - resp.Payload = dto - return c.JSON(http.StatusOK, resp) + p.Payload = dto + p.BaseResponse.IsError = false + return c.JSON(http.StatusOK, p) } // NewRedditSource @@ -181,52 +193,71 @@ func (s *Handler) GetSourceBySourceAndName(c echo.Context) error { // @Tags Source // @Router /v1/sources/new/reddit [post] // @Success 200 {object} domain.SourcesResponse "ok" -// @Failure 400 {object} domain.BaseResponse -// @Failure 500 {object} domain.BaseResponse +// @Failure 400 {object} domain.SourcesResponse +// @Failure 500 {object} domain.SourcesResponse // @Security Bearer func (s *Handler) newRedditSource(c echo.Context) error { - _, err := s.ValidateJwtToken(c, domain.ScopeSourceCreate) - if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) - } - - resp := domain.SourcesResponse{ + p := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, + IsError: true, }, } + _, err := s.ValidateJwtToken(c, domain.ScopeSourceCreate) + if err != nil { + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusUnauthorized, p) + } + var param domain.NewSourceParamRequest err = c.Bind(¶m) if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusBadRequest, p) } if param.Url == "" { - return s.WriteMessage(c, "url is missing", http.StatusBadRequest) + p.BaseResponse.Message = "url is missing" + return c.JSON(http.StatusBadRequest, p) } if !strings.Contains(param.Url, "reddit.com") { - return s.WriteMessage(c, "invalid url", http.StatusBadRequest) + p.BaseResponse.Message = "invalid url" + return c.JSON(http.StatusBadRequest, p) + } + + // Check to see if we already have this record, if we do, return it. + item, err := s.repo.Sources.GetBySourceAndName(c.Request().Context(), domain.SourceCollectorReddit, param.Name) + if err == nil { + var dto []domain.SourceDto + dto = append(dto, dtoconv.SourceToDto(item)) + p.Payload = dto + p.BaseResponse.IsError = false + return c.JSON(http.StatusOK, p) } tags := fmt.Sprintf("twitch, %v, %s", param.Name, param.Tags) rows, err := s.repo.Sources.Create(c.Request().Context(), domain.SourceCollectorReddit, param.Name, param.Url, tags, true) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } if rows != 1 { - return s.WriteMessage(c, ErrFailedToCreateRecord, http.StatusInternalServerError) + p.BaseResponse.Message = ErrFailedToCreateRecord + return c.JSON(http.StatusInternalServerError, p) } - item, err := s.repo.Sources.GetBySourceAndName(c.Request().Context(), domain.SourceCollectorReddit, param.Name) + item, err = s.repo.Sources.GetBySourceAndName(c.Request().Context(), domain.SourceCollectorReddit, param.Name) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } var dto []domain.SourceDto dto = append(dto, dtoconv.SourceToDto(item)) - resp.Payload = dto - return c.JSON(http.StatusOK, resp) + p.Payload = dto + p.BaseResponse.IsError = false + return c.JSON(http.StatusOK, p) } // NewYoutubeSource @@ -235,59 +266,71 @@ func (s *Handler) newRedditSource(c echo.Context) error { // @Param url query string true "url" // @Tags Source // @Router /v1/sources/new/youtube [post] +// @Success 200 {object} domain.SourcesResponse "ok" +// @Failure 400 {object} domain.SourcesResponse +// @Failure 500 {object} domain.SourcesResponse // @Security Bearer func (s *Handler) newYoutubeSource(c echo.Context) error { + p := domain.SourcesResponse{ + BaseResponse: domain.BaseResponse{ + Message: ResponseMessageSuccess, + }, + } + // Validate the jwt _, err := s.ValidateJwtToken(c, domain.ScopeSourceCreate) if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusUnauthorized, p) } var param domain.NewSourceParamRequest err = c.Bind(¶m) if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusBadRequest, p) } if param.Url == "" { - return s.WriteMessage(c, "url is missing a value", http.StatusBadRequest) + p.BaseResponse.Message = "url is missing a value" + return c.JSON(http.StatusBadRequest, p) } if !strings.Contains(param.Url, "youtube.com") { - return s.WriteMessage(c, "invalid url", http.StatusBadRequest) - } - - resp := domain.SourcesResponse{ - BaseResponse: domain.BaseResponse{ - Message: ResponseMessageSuccess, - }, + p.BaseResponse.Message = "invalid url" + return c.JSON(http.StatusBadRequest, p) } item, err := s.repo.Sources.GetBySourceAndName(c.Request().Context(), domain.SourceCollectorYoutube, param.Name) if err == nil { var dto []domain.SourceDto dto = append(dto, dtoconv.SourceToDto(item)) - resp.Payload = dto - return c.JSON(http.StatusOK, resp) + p.Payload = dto + p.BaseResponse.IsError = false + return c.JSON(http.StatusOK, p) } tags := fmt.Sprintf("twitch, %v", param.Name) rows, err := s.repo.Sources.Create(c.Request().Context(), domain.SourceCollectorYoutube, param.Name, param.Url, tags, true) if err != nil { - return c.JSON(http.StatusInternalServerError, err.Error()) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } if rows != 1 { - return s.WriteMessage(c, ErrFailedToCreateRecord, http.StatusInternalServerError) + p.BaseResponse.Message = ErrFailedToCreateRecord + return c.JSON(http.StatusInternalServerError, p) } item, err = s.repo.Sources.GetBySourceAndName(c.Request().Context(), domain.SourceCollectorYoutube, param.Name) - if err == nil { - var dto []domain.SourceDto - dto = append(dto, dtoconv.SourceToDto(item)) - resp.Payload = dto - return c.JSON(http.StatusOK, resp) + if err != nil { + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } - return c.JSON(http.StatusOK, resp) + var dto []domain.SourceDto + dto = append(dto, dtoconv.SourceToDto(item)) + p.Payload = dto + p.BaseResponse.IsError = false + return c.JSON(http.StatusOK, p) } // NewTwitchSource @@ -295,25 +338,29 @@ func (s *Handler) newYoutubeSource(c echo.Context) error { // @Param name query string true "name" // @Tags Source // @Router /v1/sources/new/twitch [post] +// @Success 200 {object} domain.SourcesResponse "ok" +// @Failure 400 {object} domain.SourcesResponse +// @Failure 500 {object} domain.SourcesResponse // @Security Bearer func (s *Handler) newTwitchSource(c echo.Context) error { + p := domain.SourcesResponse{ + BaseResponse: domain.BaseResponse{ + Message: ResponseMessageSuccess, + IsError: true, + }, + } + _, err := s.ValidateJwtToken(c, domain.ScopeSourceCreate) if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusUnauthorized, p) } var param domain.NewSourceParamRequest err = c.Bind(¶m) if err != nil { - return c.JSON(http.StatusBadRequest, domain.BaseResponse{ - Message: err.Error(), - }) - } - - resp := domain.SourcesResponse{ - BaseResponse: domain.BaseResponse{ - Message: ResponseMessageSuccess, - }, + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusBadRequest, p) } tags := fmt.Sprintf("twitch, %v", param.Name) @@ -324,27 +371,28 @@ func (s *Handler) newTwitchSource(c echo.Context) error { if err == nil { var dto []domain.SourceDto dto = append(dto, dtoconv.SourceToDto(item)) - resp.Payload = dto - return c.JSON(http.StatusOK, resp) + p.Payload = dto + p.BaseResponse.IsError = false + return c.JSON(http.StatusOK, p) } rows, err := s.repo.Sources.Create(c.Request().Context(), domain.SourceCollectorTwitch, param.Name, url, tags, true) if err != nil { - return c.JSON(http.StatusInternalServerError, domain.BaseResponse{ - Message: err.Error(), - }) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } if rows != 1 { - return s.WriteMessage(c, ErrFailedToCreateRecord, http.StatusInternalServerError) + p.BaseResponse.Message = ErrFailedToCreateRecord + return c.JSON(http.StatusInternalServerError, p) } item, _ = s.repo.Sources.GetBySourceAndName(c.Request().Context(), domain.SourceCollectorTwitch, param.Name) var dto []domain.SourceDto dto = append(dto, dtoconv.SourceToDto(item)) - resp.Payload = dto - - return c.JSON(http.StatusOK, resp) + p.Payload = dto + p.BaseResponse.IsError = false + return c.JSON(http.StatusOK, p) } // NewRssSource @@ -354,54 +402,68 @@ func (s *Handler) newTwitchSource(c echo.Context) error { // @Tags Source // @Router /v1/sources/new/rss [post] // @Success 200 {object} domain.SourcesResponse "ok" -// @Failure 400 {object} domain.BaseResponse -// @Failure 500 {object} domain.BaseResponse +// @Failure 400 {object} domain.SourcesResponse +// @Failure 500 {object} domain.SourcesResponse // @Security Bearer func (s *Handler) newRssSource(c echo.Context) error { - _, err := s.ValidateJwtToken(c, domain.ScopeSourceCreate) - if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) - } - - resp := domain.SourcesResponse{ + p := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, + IsError: true, }, } + _, err := s.ValidateJwtToken(c, domain.ScopeSourceCreate) + if err != nil { + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusUnauthorized, p) + } + var param domain.NewSourceParamRequest err = c.Bind(¶m) if err != nil { - return c.JSON(http.StatusBadRequest, domain.BaseResponse{ - Message: err.Error(), - }) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusBadRequest, p) } if param.Url == "" { - return c.JSON(http.StatusBadRequest, domain.BaseResponse{ - Message: "Url is missing a value", - }) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusBadRequest, p) + } + + // Check if the record already exists + item, err := s.repo.Sources.GetBySourceAndName(c.Request().Context(), domain.SourceCollectorRss, param.Name) + if err == nil { + var dto []domain.SourceDto + dto = append(dto, dtoconv.SourceToDto(item)) + p.Payload = dto + p.BaseResponse.IsError = false + return c.JSON(http.StatusOK, p) } tags := fmt.Sprintf("rss, %v, %s", param.Name, param.Tags) rows, err := s.repo.Sources.Create(c.Request().Context(), domain.SourceCollectorRss, param.Name, param.Url, tags, true) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } if rows != 1 { - return s.WriteMessage(c, ErrFailedToCreateRecord, http.StatusInternalServerError) + p.BaseResponse.Message = ErrFailedToCreateRecord + return c.JSON(http.StatusInternalServerError, p) } - item, err := s.repo.Sources.GetBySourceAndName(c.Request().Context(), domain.SourceCollectorRss, param.Name) + item, err = s.repo.Sources.GetBySourceAndName(c.Request().Context(), domain.SourceCollectorRss, param.Name) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } var dto []domain.SourceDto dto = append(dto, dtoconv.SourceToDto(item)) - resp.Payload = dto - return c.JSON(http.StatusOK, resp) + p.Payload = dto + p.BaseResponse.IsError = false + return c.JSON(http.StatusOK, p) } // DeleteSource @@ -410,50 +472,58 @@ func (s *Handler) newRssSource(c echo.Context) error { // @Tags Source // @Router /v1/sources/{id} [POST] // @Success 200 {object} domain.SourcesResponse "ok" -// @Failure 400 {object} domain.BaseResponse -// @Failure 500 {object} domain.BaseResponse +// @Failure 400 {object} domain.SourcesResponse +// @Failure 500 {object} domain.SourcesResponse // @Security Bearer func (s *Handler) deleteSources(c echo.Context) error { + p := domain.SourcesResponse{ + BaseResponse: domain.BaseResponse{ + Message: ResponseMessageSuccess, + IsError: true, + }, + } _, err := s.ValidateJwtToken(c, domain.ScopeAll) if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusUnauthorized, p) } id, err := strconv.Atoi(c.Param("ID")) if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusBadRequest, p) } // Check to make sure we can find the record _, err = s.repo.Sources.GetById(c.Request().Context(), int64(id)) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } // Delete the record rows, err := s.repo.Sources.SoftDelete(c.Request().Context(), int64(id)) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } if rows != 1 { - return s.WriteMessage(c, ErrFailedToUpdateRecord, http.StatusInternalServerError) + p.BaseResponse.Message = ErrFailedToUpdateRecord + return c.JSON(http.StatusInternalServerError, p) } // pull the record with its updated value item, err := s.repo.Sources.GetById(c.Request().Context(), int64(id)) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } var items []domain.SourceDto items = append(items, dtoconv.SourceToDto(item)) - - return c.JSON(http.StatusOK, domain.SourcesResponse{ - BaseResponse: domain.BaseResponse{ - Message: "OK", - }, - Payload: items, - }) + p.Payload = items + p.IsError = false + return c.JSON(http.StatusOK, p) } // DisableSource @@ -462,46 +532,53 @@ func (s *Handler) deleteSources(c echo.Context) error { // @Tags Source // @Router /v1/sources/{id}/disable [post] // @Success 200 {object} domain.SourcesResponse "ok" -// @Failure 400 {object} domain.BaseResponse -// @Failure 500 {object} domain.BaseResponse +// @Failure 400 {object} domain.SourcesResponse +// @Failure 500 {object} domain.SourcesResponse // @Security Bearer func (s *Handler) disableSource(c echo.Context) error { - _, err := s.ValidateJwtToken(c, domain.ScopeAll) - if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) - } - - resp := domain.SourcesResponse{ + p := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, + IsError: true, }, } + _, err := s.ValidateJwtToken(c, domain.ScopeAll) + if err != nil { + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusUnauthorized, p) + } + id, err := strconv.Atoi(c.Param("ID")) if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusBadRequest, p) } // Check to make sure we can find the record _, err = s.repo.Sources.GetById(c.Request().Context(), int64(id)) if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusBadRequest, p) } _, err = s.repo.Sources.Disable(c.Request().Context(), int64(id)) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } item, err := s.repo.Sources.GetById(c.Request().Context(), int64(id)) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } var dto []domain.SourceDto dto = append(dto, dtoconv.SourceToDto(item)) - resp.Payload = dto - return c.JSON(http.StatusOK, resp) + p.Payload = dto + p.BaseResponse.IsError = false + return c.JSON(http.StatusOK, p) } // EnableSource @@ -510,44 +587,51 @@ func (s *Handler) disableSource(c echo.Context) error { // @Tags Source // @Router /v1/sources/{id}/enable [post] // @Success 200 {object} domain.SourcesResponse "ok" -// @Failure 400 {object} domain.BaseResponse -// @Failure 500 {object} domain.BaseResponse +// @Failure 400 {object} domain.SourcesResponse +// @Failure 500 {object} domain.SourcesResponse // @Security Bearer func (s *Handler) enableSource(c echo.Context) error { - _, err := s.ValidateJwtToken(c, domain.ScopeAll) - if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) - } - - resp := domain.SourcesResponse{ + p := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, + IsError: true, }, } + _, err := s.ValidateJwtToken(c, domain.ScopeAll) + if err != nil { + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusUnauthorized, p) + } + id, err := strconv.Atoi(c.Param("ID")) if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusBadRequest, p) } // Check to make sure we can find the record _, err = s.repo.Sources.GetById(c.Request().Context(), int64(id)) if err != nil { - return s.WriteError(c, err, http.StatusBadRequest) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusBadRequest, p) } _, err = s.repo.Sources.Enable(c.Request().Context(), int64(id)) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } item, err := s.repo.Sources.GetById(c.Request().Context(), int64(id)) if err != nil { - return s.WriteError(c, err, http.StatusInternalServerError) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } var dto []domain.SourceDto dto = append(dto, dtoconv.SourceToDto(item)) - resp.Payload = dto - return c.JSON(http.StatusOK, resp) + p.Payload = dto + p.BaseResponse.IsError = false + return c.JSON(http.StatusOK, p) } diff --git a/internal/handler/v1/users.go b/internal/handler/v1/users.go index 2ab3154..44d0437 100644 --- a/internal/handler/v1/users.go +++ b/internal/handler/v1/users.go @@ -23,10 +23,15 @@ const ( // @Accepts x-www-form-urlencoded // @Produce json // @Tags Users -// @Success 200 {object} domain.BaseResponse +// @Success 201 {object} domain.BaseResponse // @Failure 400 {object} domain.BaseResponse // @Failure 500 {object} domain.BaseResponse func (h *Handler) AuthRegister(c echo.Context) error { + p := domain.BaseResponse{ + Message: ResponseMessageSuccess, + IsError: true, + } + username := c.FormValue("username") password := c.FormValue("password") @@ -36,27 +41,29 @@ func (h *Handler) AuthRegister(c echo.Context) error { // if we have an err, validate that if its not user not found. // if the user is not found, we can use that name if err.Error() != repository.ErrUserNotFound { - return h.WriteError(c, err, http.StatusBadRequest) + p.Message = err.Error() + return c.JSON(http.StatusBadRequest, p) } } if exists.Username == username { - return h.InternalServerErrorResponse(c, ErrUsernameAlreadyExists) + p.Message = ErrUsernameAlreadyExists + return c.JSON(http.StatusInternalServerError, p) } //password := c.QueryParam("password") err = h.repo.Users.CheckPasswordForRequirements(password) if err != nil { - return h.WriteError(c, err, http.StatusInternalServerError) + p.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } _, err = h.repo.Users.Create(c.Request().Context(), username, password, domain.ScopeArticleRead) if err != nil { - return h.InternalServerErrorResponse(c, err.Error()) + p.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } - return c.JSON(http.StatusCreated, domain.BaseResponse{ - Message: "OK", - }) + return c.JSON(http.StatusCreated, p) } // @Summary Logs into the API and returns a bearer token if successful @@ -66,9 +73,19 @@ func (h *Handler) AuthRegister(c echo.Context) error { // @Produce json // @Tags Users // @Success 200 {object} domain.LoginResponse -// @Failure 400 {object} domain.BaseResponse -// @Failure 500 {object} domain.BaseResponse +// @Failure 400 {object} domain.LoginResponse +// @Failure 500 {object} domain.LoginResponse func (h *Handler) AuthLogin(c echo.Context) error { + p := domain.LoginResponse{ + BaseResponse: domain.BaseResponse{ + Message: ResponseMessageSuccess, + IsError: true, + }, + //Token: jwt, + Type: "Bearer", + //RefreshToken: refresh, + } + username := c.FormValue("username") password := c.FormValue("password") @@ -80,13 +97,15 @@ func (h *Handler) AuthLogin(c echo.Context) error { // check if the user exists user, err := h.repo.Users.GetUser(c.Request().Context(), username) if err != nil { - return h.InternalServerErrorResponse(c, err.Error()) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } // make sure the hash matches err = h.repo.Users.DoesPasswordMatchHash(c.Request().Context(), username, password) if err != nil { - return h.InternalServerErrorResponse(c, err.Error()) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } // TODO think about moving this down some? @@ -95,53 +114,60 @@ func (h *Handler) AuthLogin(c echo.Context) error { jwt, err := h.generateJwtWithExp(username, h.config.ServerAddress, user.SessionToken, userScopes, user.ID, expiresAt) if err != nil { - return h.InternalServerErrorResponse(c, err.Error()) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } refresh, err := h.repo.RefreshTokens.Create(c.Request().Context(), username) if err != nil { - return h.InternalServerErrorResponse(c, err.Error()) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } - return c.JSON(http.StatusOK, domain.LoginResponse{ - BaseResponse: domain.BaseResponse{ - Message: "OK", - }, - Token: jwt, - Type: "Bearer", - RefreshToken: refresh, - }) + p.Token = jwt + p.RefreshToken = refresh + p.BaseResponse.IsError = false + return c.JSON(http.StatusOK, p) } func (h *Handler) createAdminToken(c echo.Context, password string) error { + p := domain.LoginResponse{ + BaseResponse: domain.BaseResponse{ + Message: ResponseMessageSuccess, + IsError: true, + }, + //Token: token, + Type: "Bearer", + } + // if the admin token is blank, then the admin wanted this disabled. // this will fail right away and not progress. if h.config.AdminSecret == "" { - return h.InternalServerErrorResponse(c, ErrUserNotFound) + p.BaseResponse.Message = ErrUserNotFound + return c.JSON(http.StatusBadRequest, p) } if h.config.AdminSecret != password { - return h.UnauthorizedResponse(c, ErrUserNotFound) + p.BaseResponse.Message = ErrUserNotFound + return c.JSON(http.StatusBadRequest, p) } var userScopes []string userScopes = append(userScopes, domain.ScopeAll) sessionToken, err := uuid.NewV7() if err != nil { - return h.InternalServerErrorResponse(c, err.Error()) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } token, err := h.generateJwt("admin", h.config.ServerAddress, sessionToken.String(), userScopes, -1) if err != nil { - return h.InternalServerErrorResponse(c, err.Error()) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } - return c.JSON(http.StatusOK, domain.LoginResponse{ - BaseResponse: domain.BaseResponse{ - Message: "OK", - }, - Token: token, - Type: "Bearer", - }) + p.Token = token + p.BaseResponse.IsError = false + return c.JSON(http.StatusOK, p) } // This will take collect some information about the requested refresh, validate and then return a new jwt token if approved. @@ -155,47 +181,58 @@ func (h *Handler) createAdminToken(c echo.Context, password string) error { // @Failure 500 {object} domain.BaseResponse // @Security Bearer func (h *Handler) RefreshJwtToken(c echo.Context) error { + p := domain.LoginResponse{ + BaseResponse: domain.BaseResponse{ + Message: "OK", + }, + //Token: jwt, + Type: "Bearer", + //RefreshToken: newRefreshToken, + } + _, err := h.ValidateJwtToken(c, domain.ScopeDiscordWebHookCreate) if err != nil { - return h.WriteError(c, err, http.StatusBadRequest) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusUnauthorized, p) } // Check the context for the refresh token var request domain.RefreshTokenRequest err = (&echo.DefaultBinder{}).BindBody(c, &request) if err != nil { - return h.InternalServerErrorResponse(c, err.Error()) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } err = h.repo.RefreshTokens.IsRequestValid(c.Request().Context(), request.Username, request.RefreshToken) if err != nil { - return h.InternalServerErrorResponse(c, err.Error()) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } user, err := h.repo.Users.GetUser(c.Request().Context(), request.Username) if err != nil { - return h.InternalServerErrorResponse(c, err.Error()) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } userScopes := strings.Split(user.Scopes, ",") jwt, err := h.generateJwtWithExp(request.Username, h.config.ServerAddress, user.SessionToken, userScopes, user.ID, time.Now().Add(time.Hour*48)) if err != nil { - return h.InternalServerErrorResponse(c, err.Error()) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } newRefreshToken, err := h.repo.RefreshTokens.Create(c.Request().Context(), request.Username) if err != nil { - return h.InternalServerErrorResponse(c, err.Error()) + p.BaseResponse.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } - return c.JSON(http.StatusOK, domain.LoginResponse{ - BaseResponse: domain.BaseResponse{ - Message: "OK", - }, - Token: jwt, - Type: "Bearer", - RefreshToken: newRefreshToken, - }) + p.Token = jwt + p.RefreshToken = newRefreshToken + p.BaseResponse.IsError = false + return c.JSON(http.StatusOK, p) } // @Summary Adds a new scope to a user account @@ -209,25 +246,32 @@ func (h *Handler) RefreshJwtToken(c echo.Context) error { // @Failure 500 {object} domain.BaseResponse // @Security Bearer func (h *Handler) AddScopes(c echo.Context) error { + p := domain.BaseResponse{ + Message: ResponseMessageSuccess, + IsError: true, + } + _, err := h.ValidateJwtToken(c, domain.ScopeAll) if err != nil { - return h.WriteError(c, err, http.StatusBadRequest) + p.Message = err.Error() + return c.JSON(http.StatusUnauthorized, p) } request := domain.UpdateScopesRequest{} err = (&echo.DefaultBinder{}).BindBody(c, &request) if err != nil { - return h.WriteError(c, err, http.StatusBadRequest) + p.Message = err.Error() + return c.JSON(http.StatusBadRequest, p) } err = h.repo.Users.AddScopes(c.Request().Context(), request.Username, request.Scopes) if err != nil { - return h.InternalServerErrorResponse(c, err.Error()) + p.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } - return c.JSON(http.StatusOK, domain.BaseResponse{ - Message: "OK", - }) + p.IsError = false + return c.JSON(http.StatusOK, p) } // @Summary Adds a new scope to a user account @@ -237,34 +281,42 @@ func (h *Handler) AddScopes(c echo.Context) error { // @Accept json // @Produce json // @Success 200 {object} domain.BaseResponse -// @Failure 400 {object} domain.BaseResponse -// @Failure 500 {object} domain.BaseResponse +// @Failure 400 {object} domain.BaseResponse +// @Failure 500 {object} domain.BaseResponse // @Security Bearer func (h *Handler) RemoveScopes(c echo.Context) error { + p := domain.BaseResponse{ + Message: ResponseMessageSuccess, + IsError: true, + } + token, err := h.getJwtTokenFromContext(c) if err != nil { - return h.WriteError(c, err, http.StatusUnauthorized) + p.Message = err.Error() + return c.JSON(http.StatusUnauthorized, p) } err = token.IsValid(domain.ScopeAll) if err != nil { - return h.WriteError(c, err, http.StatusUnauthorized) + p.Message = err.Error() + return c.JSON(http.StatusBadRequest, p) } request := domain.UpdateScopesRequest{} err = (&echo.DefaultBinder{}).BindBody(c, &request) if err != nil { - h.WriteError(c, err, http.StatusBadRequest) + p.Message = err.Error() + return c.JSON(http.StatusBadRequest, p) } err = h.repo.Users.RemoveScopes(c.Request().Context(), request.Username, request.Scopes) if err != nil { - return h.InternalServerErrorResponse(c, err.Error()) + p.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } - return c.JSON(http.StatusOK, domain.BaseResponse{ - Message: "OK", - }) + p.IsError = false + return c.JSON(http.StatusOK, p) } // @Summary Revokes the current session token and replaces it with a new one. @@ -272,22 +324,28 @@ func (h *Handler) RemoveScopes(c echo.Context) error { // @Tags Users // @Accept json // @Produce json -// @Success 200 {object} domain.BaseResponse +// @Success 200 {object} domain.BaseResponse // @Failure 400 {object} domain.BaseResponse // @Failure 500 {object} domain.BaseResponse // @Security Bearer func (h *Handler) NewSessionToken(c echo.Context) error { + p := domain.BaseResponse{ + Message: ResponseMessageSuccess, + IsError: true, + } + token, err := h.getJwtTokenFromContext(c) if err != nil { - return h.WriteError(c, err, http.StatusUnauthorized) + p.Message = err.Error() + return c.JSON(http.StatusUnauthorized, p) } _, err = h.repo.Users.NewSessionToken(c.Request().Context(), token.UserName) if err != nil { - return h.WriteError(c, err, http.StatusInternalServerError) + p.Message = err.Error() + return c.JSON(http.StatusInternalServerError, p) } - return c.JSON(http.StatusInternalServerError, domain.BaseResponse{ - Message: "OK", - }) + p.IsError = false + return c.JSON(http.StatusInternalServerError, p) }