diff --git a/.env.example b/.env.example index b1780f9..c4cacd6 100644 --- a/.env.example +++ b/.env.example @@ -9,6 +9,7 @@ POSTGRES_HOST=ecea_db POSTGRES_PORT=5432 PGDATA=/data/postgres ADMINER_PORT=8080 +ADMINER_DESIGN=pappu687 MAIL_HOST=smtp.gmail.com MAIL_PORT=465 MAIL_USER=mail@gmail.com diff --git a/config/database.go b/config/database.go index bb5a8dd..d0f48cb 100644 --- a/config/database.go +++ b/config/database.go @@ -47,6 +47,8 @@ func MigrateDB() { &schemas.Team{}, &schemas.Role{}, &schemas.Member{}, + &schemas.PodcastType{}, + &schemas.Podcast{}, } { if err := db.AutoMigrate(&schema); err != nil { panic(err) diff --git a/controllers/app_controller.go b/controllers/app_controller.go index 2f99e31..6ed73ce 100644 --- a/controllers/app_controller.go +++ b/controllers/app_controller.go @@ -8,9 +8,10 @@ import ( ) type AppController struct { - User interface{ UserController } - Team interface{ TeamController } - Seed interface{ SeedController } + User interface{ UserController } + Team interface{ TeamController } + Podcast interface{ PodcastController } + Seed interface{ SeedController } } type seedController struct { @@ -41,6 +42,12 @@ func (s *seedController) SeedDB() error { log.Panic(err) return err } + err = s.seed.PodcastTypesSeeder() + if err != nil { + log.Panic(err) + return err + } + log.Println(color.GreenString("Seeded Successfully")) return nil } diff --git a/controllers/podcast_controller.go b/controllers/podcast_controller.go new file mode 100644 index 0000000..222cbba --- /dev/null +++ b/controllers/podcast_controller.go @@ -0,0 +1,291 @@ +package controllers + +import ( + "log" + "net/http" + "strconv" + + "github.com/ecea-nitt/ecea-server/middlewares" + "github.com/ecea-nitt/ecea-server/models" + "github.com/ecea-nitt/ecea-server/services" + "github.com/ecea-nitt/ecea-server/utils" + "github.com/fatih/color" + "github.com/labstack/echo/v4" +) + +type podcastController struct { + ps services.PodcastService +} + +type PodcastController interface { + CreatePodcast(c echo.Context) error + EditThumbnail(c echo.Context) error + EditURL(c echo.Context) error + EditDescription(c echo.Context) error + DeletePodcast(c echo.Context) error + GetPodcast(c echo.Context) error + GetAllPodcasts(c echo.Context) error + GetPodcastByType(c echo.Context) error +} + +func NewPodcastController(ps services.PodcastService) PodcastController { + return &podcastController{ps} +} + +// CreatePodcast godoc +// +// @Summary Create Podcast +// @Description Adds a new podcast to the database +// @Tags Podcast +// @Accept multipart/form-data +// @Produce json +// @Param name formData string true "Enter name" +// @Param episodeNo formData uint true "Enter episode number" +// @Param type formData models.PodcastType true "Choose a type" +// @Param description formData string true "Enter description" +// @Param mediaURL formData string true "Enter Media URL" +// @Param image formData file true "Upload Thumbnail" +// @Success 200 {object} string +// @Failure 400 {object} models.Error +// +// @Security ApiKeyAuth +// @Router /v1/podcast/create [post] +func (pc *podcastController) CreatePodcast(c echo.Context) error { + request := new(models.PodcastRequest) + if err := c.Bind(request); err != nil { + log.Println(color.RedString(err.Error())) + return middlewares.Responder(c, http.StatusBadRequest, http.StatusText(http.StatusBadRequest)) + } + + file, err := c.FormFile("image") + if err != nil { + log.Println(color.RedString(err.Error())) + return err + } + + err = pc.ps.CreatePodcast(*request, file) + if err != nil { + log.Println(color.RedString(err.Error())) + return middlewares.Responder(c, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } + + return middlewares.Responder(c, http.StatusOK, http.StatusText(http.StatusOK)) +} + +// EditThumbnail godoc +// +// @Summary Edit Thumbnail +// @Description Edits the thumbnail of a podcast +// @Tags Podcast +// @Accept multipart/form-data +// @Produce json +// @Param episodeNo formData uint true "Enter episode number" +// +// @Param type formData models.PodcastType true "Choose a type" +// +// @Param image formData file true "Upload Thumbnail" +// @Success 200 {object} string +// @Failure 400 {object} models.Error +// +// @Security ApiKeyAuth +// @Router /v1/podcast/edit/thumbnail [put] +func (pc *podcastController) EditThumbnail(c echo.Context) error { + request := new(models.PodcastRequest) + if err := c.Bind(request); err != nil { + log.Println(color.RedString(err.Error())) + return middlewares.Responder(c, http.StatusBadRequest, http.StatusText(http.StatusBadRequest)) + } + + file, err := c.FormFile("image") + if err != nil { + log.Println(color.RedString(err.Error())) + return err + } + + err = pc.ps.EditThumbnail(*request, file) + if err != nil { + log.Println(color.RedString(err.Error())) + return middlewares.Responder(c, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } + + return middlewares.Responder(c, http.StatusOK, http.StatusText(http.StatusOK)) +} + +// EditURL godoc +// +// @Summary Edit URL +// @Description Edits the media url of a podcast +// @Tags Podcast +// @Accept multipart/form-data +// @Produce json +// @Param episodeNo formData uint true "Enter episode number" +// +// @Param type formData models.PodcastType true "Choose a type" +// +// @Param mediaURL formData string true "Enter Media URL" +// @Success 200 {object} string +// @Failure 400 {object} models.Error +// +// @Security ApiKeyAuth +// @Router /v1/podcast/edit/url [put] +func (pc *podcastController) EditURL(c echo.Context) error { + request := new(models.PodcastRequest) + if err := c.Bind(request); err != nil { + log.Println(color.RedString(err.Error())) + return middlewares.Responder(c, http.StatusBadRequest, http.StatusText(http.StatusBadRequest)) + } + + err := pc.ps.EditURL(*request) + if err != nil { + log.Println(color.RedString(err.Error())) + return middlewares.Responder(c, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } + + return middlewares.Responder(c, http.StatusOK, http.StatusText(http.StatusOK)) +} + +// EditDescription godoc +// +// @Summary Edit Description +// @Description Edits the description of a podcast +// @Tags Podcast +// @Accept multipart/form-data +// @Produce json +// @Param episodeNo formData uint true "Enter episode number" +// +// @Param type formData models.PodcastType true "Choose a type" +// +// @Param description formData string true "Enter description" +// @Success 200 {object} string +// @Failure 400 {object} models.Error +// +// @Security ApiKeyAuth +// @Router /v1/podcast/edit/description [put] +func (pc *podcastController) EditDescription(c echo.Context) error { + request := new(models.PodcastRequest) + if err := c.Bind(request); err != nil { + log.Println(color.RedString(err.Error())) + return middlewares.Responder(c, http.StatusBadRequest, http.StatusText(http.StatusBadRequest)) + } + + err := pc.ps.EditDescription(*request) + if err != nil { + log.Println(color.RedString(err.Error())) + return middlewares.Responder(c, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } + + return middlewares.Responder(c, http.StatusOK, http.StatusText(http.StatusOK)) +} + +// DeletePodcast godoc +// +// @Summary Delete Podcast +// @Description Deletes a podcast +// @Tags Podcast +// @Accept multipart/form-data +// @Produce json +// @Param episodeNo formData uint true "Enter episode number" +// +// @Param type formData models.PodcastType true "Choose a type" +// +// @Success 200 {object} string +// @Failure 400 {object} models.Error +// +// @Security ApiKeyAuth +// @Router /v1/podcast/delete [delete] +func (pc *podcastController) DeletePodcast(c echo.Context) error { + request := new(models.PodcastRequest) + + if err := c.Bind(request); err != nil { + log.Println(color.RedString(err.Error())) + return middlewares.Responder(c, http.StatusBadRequest, http.StatusText(http.StatusBadRequest)) + } + + err := pc.ps.DeletePodcast(*request) + + if err != nil { + log.Println(color.RedString(err.Error())) + return middlewares.Responder(c, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } + + return middlewares.Responder(c, http.StatusOK, http.StatusText(http.StatusOK)) +} + +// GetPodcastByEpisodeNumberAndType godoc +// @Summary Get Podcast By Episode Number And Type +// @Description Gets a podcast by episode number and type +// @Tags Podcast +// @Accept json +// @Produce json +// @Param episodeNo path uint true "Enter episode number" +// @Param type path models.PodcastType true "Choose a type" +// @Success 200 {object} models.Podcasts +// @Failure 400 {object} models.Error +// @Failure 500 {object} models.Error +// @Router /v1/podcast/get/{episodeNo}/{type} [get] +func (pc *podcastController) GetPodcast(c echo.Context) error { + podcastType, err := utils.PodcastTypeValidator(c.Param("type")) + if err != nil { + return middlewares.Responder(c, http.StatusBadRequest, http.StatusText(http.StatusBadRequest)) + } + + episodeNo, err := strconv.ParseUint(c.Param("episodeNo"), 10, 64) + if err != nil { + return middlewares.Responder(c, http.StatusBadRequest, http.StatusText(http.StatusBadRequest)) + } + + podcast, err := pc.ps.GetPodcastByEpisodeNumberAndType(uint(episodeNo), podcastType) + if err != nil { + log.Println(color.RedString(err.Error())) + return middlewares.Responder(c, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } + + return middlewares.Responder(c, http.StatusOK, podcast) +} + +// GetAllPodcasts godoc +// @Summary Get All Podcasts +// @Description Gets all podcasts +// @Tags Podcast +// @Accept json +// @Produce json +// @Success 200 {object} []models.Podcasts +// @Failure 400 {object} models.Error +// @Failure 500 {object} models.Error +// @Router /v1/podcast/get/all [get] +func (pc *podcastController) GetAllPodcasts(c echo.Context) error { + podcasts, err := pc.ps.GetAllPodcasts() + if err != nil { + log.Println(color.RedString(err.Error())) + return middlewares.Responder(c, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } + + return middlewares.Responder(c, http.StatusOK, podcasts) +} + +// GetPodcastByType godoc +// @Summary Get Podcast By Type +// @Description Gets a podcast by type +// @Tags Podcast +// @Accept json +// @Produce json +// @Param type path models.PodcastType true "Enter type" +// @Success 200 {object} []models.Podcasts +// @Failure 400 {object} models.Error +// @Failure 500 {object} models.Error +// @Router /v1/podcast/getall/{type} [get] +func (pc *podcastController) GetPodcastByType(c echo.Context) error { + podcastType, err := utils.PodcastTypeValidator(c.Param("type")) + if err != nil { + log.Println(color.RedString(err.Error())) + return middlewares.Responder(c, http.StatusBadRequest, http.StatusText(http.StatusBadRequest)) + } + + podcasts, err := pc.ps.GetPodcastByType(podcastType) + if err != nil { + log.Println(color.RedString(err.Error())) + return middlewares.Responder(c, http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + } + + return middlewares.Responder(c, http.StatusOK, podcasts) +} diff --git a/docker-compose.yml b/docker-compose.yml index 1c09ed0..4c44b8d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -32,7 +32,10 @@ services: volumes: - ./database:/data/postgres -networks: - default: - external: - name: thirumathikart_network + adminer: + image: adminer + env_file: + - .env + restart: unless-stopped + ports: + - ${ADMINER_PORT}:${ADMINER_PORT} \ No newline at end of file diff --git a/docs/docs.go b/docs/docs.go index 2f0243d..80b3f10 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -24,6 +24,469 @@ const docTemplate = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { + "/v1/podcast/create": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Adds a new podcast to the database", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Podcast" + ], + "summary": "Create Podcast", + "parameters": [ + { + "type": "string", + "description": "Enter name", + "name": "name", + "in": "formData", + "required": true + }, + { + "type": "integer", + "description": "Enter episode number", + "name": "episodeNo", + "in": "formData", + "required": true + }, + { + "enum": [ + "GUEST_LECTURE", + "CAREER_PATH" + ], + "type": "string", + "description": "Choose a type", + "name": "type", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Enter description", + "name": "description", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Enter Media URL", + "name": "mediaURL", + "in": "formData", + "required": true + }, + { + "type": "file", + "description": "Upload Thumbnail", + "name": "image", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Error" + } + } + } + } + }, + "/v1/podcast/delete": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Deletes a podcast", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Podcast" + ], + "summary": "Delete Podcast", + "parameters": [ + { + "type": "integer", + "description": "Enter episode number", + "name": "episodeNo", + "in": "formData", + "required": true + }, + { + "enum": [ + "GUEST_LECTURE", + "CAREER_PATH" + ], + "type": "string", + "description": "Choose a type", + "name": "type", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Error" + } + } + } + } + }, + "/v1/podcast/edit/description": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Edits the description of a podcast", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Podcast" + ], + "summary": "Edit Description", + "parameters": [ + { + "type": "integer", + "description": "Enter episode number", + "name": "episodeNo", + "in": "formData", + "required": true + }, + { + "enum": [ + "GUEST_LECTURE", + "CAREER_PATH" + ], + "type": "string", + "description": "Choose a type", + "name": "type", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Enter description", + "name": "description", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Error" + } + } + } + } + }, + "/v1/podcast/edit/thumbnail": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Edits the thumbnail of a podcast", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Podcast" + ], + "summary": "Edit Thumbnail", + "parameters": [ + { + "type": "integer", + "description": "Enter episode number", + "name": "episodeNo", + "in": "formData", + "required": true + }, + { + "enum": [ + "GUEST_LECTURE", + "CAREER_PATH" + ], + "type": "string", + "description": "Choose a type", + "name": "type", + "in": "formData", + "required": true + }, + { + "type": "file", + "description": "Upload Thumbnail", + "name": "image", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Error" + } + } + } + } + }, + "/v1/podcast/edit/url": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Edits the media url of a podcast", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Podcast" + ], + "summary": "Edit URL", + "parameters": [ + { + "type": "integer", + "description": "Enter episode number", + "name": "episodeNo", + "in": "formData", + "required": true + }, + { + "enum": [ + "GUEST_LECTURE", + "CAREER_PATH" + ], + "type": "string", + "description": "Choose a type", + "name": "type", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Enter Media URL", + "name": "mediaURL", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Error" + } + } + } + } + }, + "/v1/podcast/get/all": { + "get": { + "description": "Gets all podcasts", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Podcast" + ], + "summary": "Get All Podcasts", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Podcasts" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.Error" + } + } + } + } + }, + "/v1/podcast/get/{episodeNo}/{type}": { + "get": { + "description": "Gets a podcast by episode number and type", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Podcast" + ], + "summary": "Get Podcast By Episode Number And Type", + "parameters": [ + { + "type": "integer", + "description": "Enter episode number", + "name": "episodeNo", + "in": "path", + "required": true + }, + { + "enum": [ + "GUEST_LECTURE", + "CAREER_PATH" + ], + "type": "string", + "description": "Choose a type", + "name": "type", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.Podcasts" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.Error" + } + } + } + } + }, + "/v1/podcast/getall/{type}": { + "get": { + "description": "Gets a podcast by type", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Podcast" + ], + "summary": "Get Podcast By Type", + "parameters": [ + { + "enum": [ + "GUEST_LECTURE", + "CAREER_PATH" + ], + "type": "string", + "description": "Enter type", + "name": "type", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Podcasts" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.Error" + } + } + } + } + }, "/v1/team/add": { "post": { "security": [ @@ -583,6 +1046,37 @@ const docTemplate = `{ } } }, + "models.PodcastType": { + "type": "string", + "enum": [ + "GUEST_LECTURE", + "CAREER_PATH" + ], + "x-enum-varnames": [ + "Guestlecture", + "CareerPath" + ] + }, + "models.Podcasts": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "image_url": { + "type": "string" + }, + "mediaUrl": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/models.PodcastType" + } + } + }, "models.RegisterRequest": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index b74456a..879df79 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -15,6 +15,469 @@ "version": "1.0" }, "paths": { + "/v1/podcast/create": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Adds a new podcast to the database", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Podcast" + ], + "summary": "Create Podcast", + "parameters": [ + { + "type": "string", + "description": "Enter name", + "name": "name", + "in": "formData", + "required": true + }, + { + "type": "integer", + "description": "Enter episode number", + "name": "episodeNo", + "in": "formData", + "required": true + }, + { + "enum": [ + "GUEST_LECTURE", + "CAREER_PATH" + ], + "type": "string", + "description": "Choose a type", + "name": "type", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Enter description", + "name": "description", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Enter Media URL", + "name": "mediaURL", + "in": "formData", + "required": true + }, + { + "type": "file", + "description": "Upload Thumbnail", + "name": "image", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Error" + } + } + } + } + }, + "/v1/podcast/delete": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Deletes a podcast", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Podcast" + ], + "summary": "Delete Podcast", + "parameters": [ + { + "type": "integer", + "description": "Enter episode number", + "name": "episodeNo", + "in": "formData", + "required": true + }, + { + "enum": [ + "GUEST_LECTURE", + "CAREER_PATH" + ], + "type": "string", + "description": "Choose a type", + "name": "type", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Error" + } + } + } + } + }, + "/v1/podcast/edit/description": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Edits the description of a podcast", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Podcast" + ], + "summary": "Edit Description", + "parameters": [ + { + "type": "integer", + "description": "Enter episode number", + "name": "episodeNo", + "in": "formData", + "required": true + }, + { + "enum": [ + "GUEST_LECTURE", + "CAREER_PATH" + ], + "type": "string", + "description": "Choose a type", + "name": "type", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Enter description", + "name": "description", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Error" + } + } + } + } + }, + "/v1/podcast/edit/thumbnail": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Edits the thumbnail of a podcast", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Podcast" + ], + "summary": "Edit Thumbnail", + "parameters": [ + { + "type": "integer", + "description": "Enter episode number", + "name": "episodeNo", + "in": "formData", + "required": true + }, + { + "enum": [ + "GUEST_LECTURE", + "CAREER_PATH" + ], + "type": "string", + "description": "Choose a type", + "name": "type", + "in": "formData", + "required": true + }, + { + "type": "file", + "description": "Upload Thumbnail", + "name": "image", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Error" + } + } + } + } + }, + "/v1/podcast/edit/url": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Edits the media url of a podcast", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Podcast" + ], + "summary": "Edit URL", + "parameters": [ + { + "type": "integer", + "description": "Enter episode number", + "name": "episodeNo", + "in": "formData", + "required": true + }, + { + "enum": [ + "GUEST_LECTURE", + "CAREER_PATH" + ], + "type": "string", + "description": "Choose a type", + "name": "type", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Enter Media URL", + "name": "mediaURL", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Error" + } + } + } + } + }, + "/v1/podcast/get/all": { + "get": { + "description": "Gets all podcasts", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Podcast" + ], + "summary": "Get All Podcasts", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Podcasts" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.Error" + } + } + } + } + }, + "/v1/podcast/get/{episodeNo}/{type}": { + "get": { + "description": "Gets a podcast by episode number and type", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Podcast" + ], + "summary": "Get Podcast By Episode Number And Type", + "parameters": [ + { + "type": "integer", + "description": "Enter episode number", + "name": "episodeNo", + "in": "path", + "required": true + }, + { + "enum": [ + "GUEST_LECTURE", + "CAREER_PATH" + ], + "type": "string", + "description": "Choose a type", + "name": "type", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.Podcasts" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.Error" + } + } + } + } + }, + "/v1/podcast/getall/{type}": { + "get": { + "description": "Gets a podcast by type", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Podcast" + ], + "summary": "Get Podcast By Type", + "parameters": [ + { + "enum": [ + "GUEST_LECTURE", + "CAREER_PATH" + ], + "type": "string", + "description": "Enter type", + "name": "type", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Podcasts" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.Error" + } + } + } + } + }, "/v1/team/add": { "post": { "security": [ @@ -574,6 +1037,37 @@ } } }, + "models.PodcastType": { + "type": "string", + "enum": [ + "GUEST_LECTURE", + "CAREER_PATH" + ], + "x-enum-varnames": [ + "Guestlecture", + "CareerPath" + ] + }, + "models.Podcasts": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "image_url": { + "type": "string" + }, + "mediaUrl": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/models.PodcastType" + } + } + }, "models.RegisterRequest": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 4f5429a..83ccf6f 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -49,6 +49,27 @@ definitions: team: $ref: '#/definitions/models.MemberTeams' type: object + models.PodcastType: + enum: + - GUEST_LECTURE + - CAREER_PATH + type: string + x-enum-varnames: + - Guestlecture + - CareerPath + models.Podcasts: + properties: + description: + type: string + image_url: + type: string + mediaUrl: + type: string + name: + type: string + type: + $ref: '#/definitions/models.PodcastType' + type: object models.RegisterRequest: properties: email: @@ -75,6 +96,312 @@ info: title: Probe Admin version: "1.0" paths: + /v1/podcast/create: + post: + consumes: + - multipart/form-data + description: Adds a new podcast to the database + parameters: + - description: Enter name + in: formData + name: name + required: true + type: string + - description: Enter episode number + in: formData + name: episodeNo + required: true + type: integer + - description: Choose a type + enum: + - GUEST_LECTURE + - CAREER_PATH + in: formData + name: type + required: true + type: string + - description: Enter description + in: formData + name: description + required: true + type: string + - description: Enter Media URL + in: formData + name: mediaURL + required: true + type: string + - description: Upload Thumbnail + in: formData + name: image + required: true + type: file + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.Error' + security: + - ApiKeyAuth: [] + summary: Create Podcast + tags: + - Podcast + /v1/podcast/delete: + delete: + consumes: + - multipart/form-data + description: Deletes a podcast + parameters: + - description: Enter episode number + in: formData + name: episodeNo + required: true + type: integer + - description: Choose a type + enum: + - GUEST_LECTURE + - CAREER_PATH + in: formData + name: type + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.Error' + security: + - ApiKeyAuth: [] + summary: Delete Podcast + tags: + - Podcast + /v1/podcast/edit/description: + put: + consumes: + - multipart/form-data + description: Edits the description of a podcast + parameters: + - description: Enter episode number + in: formData + name: episodeNo + required: true + type: integer + - description: Choose a type + enum: + - GUEST_LECTURE + - CAREER_PATH + in: formData + name: type + required: true + type: string + - description: Enter description + in: formData + name: description + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.Error' + security: + - ApiKeyAuth: [] + summary: Edit Description + tags: + - Podcast + /v1/podcast/edit/thumbnail: + put: + consumes: + - multipart/form-data + description: Edits the thumbnail of a podcast + parameters: + - description: Enter episode number + in: formData + name: episodeNo + required: true + type: integer + - description: Choose a type + enum: + - GUEST_LECTURE + - CAREER_PATH + in: formData + name: type + required: true + type: string + - description: Upload Thumbnail + in: formData + name: image + required: true + type: file + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.Error' + security: + - ApiKeyAuth: [] + summary: Edit Thumbnail + tags: + - Podcast + /v1/podcast/edit/url: + put: + consumes: + - multipart/form-data + description: Edits the media url of a podcast + parameters: + - description: Enter episode number + in: formData + name: episodeNo + required: true + type: integer + - description: Choose a type + enum: + - GUEST_LECTURE + - CAREER_PATH + in: formData + name: type + required: true + type: string + - description: Enter Media URL + in: formData + name: mediaURL + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.Error' + security: + - ApiKeyAuth: [] + summary: Edit URL + tags: + - Podcast + /v1/podcast/get/{episodeNo}/{type}: + get: + consumes: + - application/json + description: Gets a podcast by episode number and type + parameters: + - description: Enter episode number + in: path + name: episodeNo + required: true + type: integer + - description: Choose a type + enum: + - GUEST_LECTURE + - CAREER_PATH + in: path + name: type + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.Podcasts' + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.Error' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.Error' + summary: Get Podcast By Episode Number And Type + tags: + - Podcast + /v1/podcast/get/all: + get: + consumes: + - application/json + description: Gets all podcasts + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/models.Podcasts' + type: array + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.Error' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.Error' + summary: Get All Podcasts + tags: + - Podcast + /v1/podcast/getall/{type}: + get: + consumes: + - application/json + description: Gets a podcast by type + parameters: + - description: Enter type + enum: + - GUEST_LECTURE + - CAREER_PATH + in: path + name: type + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/models.Podcasts' + type: array + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.Error' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.Error' + summary: Get Podcast By Type + tags: + - Podcast /v1/team/add: post: consumes: diff --git a/helpers/asset.go b/helpers/asset.go new file mode 100644 index 0000000..8c649e2 --- /dev/null +++ b/helpers/asset.go @@ -0,0 +1,53 @@ +package helpers + +import ( + "log" + "mime/multipart" + + "github.com/ecea-nitt/ecea-server/repositories" + "github.com/ecea-nitt/ecea-server/utils" +) + +func UploadAndFetchAssetID( + channel chan int, + image *multipart.FileHeader, + repo interface{}, + repoType string) { + + switch repoType { + case "podcast": + repo := repo.(repositories.PodcastRepository) + fileName, err := utils.UploadImage(image) + if err != nil { + log.Println(err) + channel <- -1 + return + } + + id, err := repo.InsertAsset(fileName) + if err != nil { + log.Println(err) + channel <- -1 + return + } + + channel <- int(id) + case "team": + repo := repo.(repositories.TeamRepository) + fileName, err := utils.UploadImage(image) + if err != nil { + log.Println(err) + channel <- -1 + return + } + + id, err := repo.InsertAsset(fileName) + if err != nil { + log.Println(err) + channel <- -1 + return + } + + channel <- int(id) + } +} diff --git a/helpers/podcast.go b/helpers/podcast.go new file mode 100644 index 0000000..4893a43 --- /dev/null +++ b/helpers/podcast.go @@ -0,0 +1,22 @@ +package helpers + +import ( + "log" + + "github.com/ecea-nitt/ecea-server/repositories" +) + +func FetchPodcastTypeID( + channel chan int, + podcastTypeName string, + repo repositories.PodcastRepository) { + + podcast, err := repo.FindPodcastTypeByName(podcastTypeName) + if err != nil { + log.Println(err) + channel <- -1 + return + } + channel <- int(podcast.ID) + +} diff --git a/helpers/team.go b/helpers/team.go index f3e347b..af1f430 100644 --- a/helpers/team.go +++ b/helpers/team.go @@ -38,26 +38,6 @@ func FetchRoleID( } -func UploadAndFetchAssetID( - channel chan int, - image *multipart.FileHeader, - repo repositories.TeamRepository) { - - fileName, err := utils.UploadImage(image) - if err != nil { - log.Println(err) - channel <- -1 - return - } - id, err := repo.InsertAsset(fileName) - if err != nil { - log.Println(err) - channel <- -1 - return - } - channel <- int(id) -} - func UpdateAndFetchTeamID( dbID uint, dbName string, diff --git a/models/podcast.go b/models/podcast.go new file mode 100644 index 0000000..569a205 --- /dev/null +++ b/models/podcast.go @@ -0,0 +1,25 @@ +package models + +type PodcastRequest struct { + Name string `json:"name" form:"name"` + Description string `json:"description" form:"description"` + MediaURL string `json:"mediaUrl" form:"mediaUrl"` + EpisodeNo uint `json:"episodeNo" form:"episodeNo" query:"episodeNo"` + Type PodcastType `json:"type" form:"type" query:"type"` + ImageURL string `json:"image_url"` +} + +type Podcasts struct { + Name string `json:"name"` + Description string `json:"description"` + MediaURL string `json:"mediaUrl"` + Type PodcastType `json:"type"` + ImageURL string `json:"image_url"` +} + +type PodcastType string + +const ( + Guestlecture PodcastType = "GUEST_LECTURE" + CareerPath PodcastType = "CAREER_PATH" +) diff --git a/registry/podcast_registry.go b/registry/podcast_registry.go new file mode 100644 index 0000000..ca900ba --- /dev/null +++ b/registry/podcast_registry.go @@ -0,0 +1,19 @@ +package registry + +import ( + "github.com/ecea-nitt/ecea-server/controllers" + "github.com/ecea-nitt/ecea-server/repositories" + "github.com/ecea-nitt/ecea-server/services" +) + +func (r *registry) NewPodcastController() controllers.PodcastController { + return controllers.NewPodcastController(r.NewPodcastService()) +} + +func (r *registry) NewPodcastService() services.PodcastService { + return services.NewPodcastService(r.NewPodcastRepository()) +} + +func (r *registry) NewPodcastRepository() repositories.PodcastRepository { + return repositories.NewPodcastRepository(r.db) +} diff --git a/registry/registry.go b/registry/registry.go index 1142ec8..2fb485b 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -19,8 +19,9 @@ func NewRegistry(db *gorm.DB) Registry { func (r *registry) NewAppController() controllers.AppController { return controllers.AppController{ - User: r.NewUserController(), - Team: r.NewTeamController(), - Seed: r.NewSeedController(), + User: r.NewUserController(), + Team: r.NewTeamController(), + Podcast: r.NewPodcastController(), + Seed: r.NewSeedController(), } } diff --git a/repositories/podcast_repository.go b/repositories/podcast_repository.go new file mode 100644 index 0000000..8e744aa --- /dev/null +++ b/repositories/podcast_repository.go @@ -0,0 +1,160 @@ +package repositories + +import ( + "github.com/ecea-nitt/ecea-server/config" + "github.com/ecea-nitt/ecea-server/models" + "github.com/ecea-nitt/ecea-server/schemas" + "gorm.io/gorm" +) + +type podcastRepository struct { + db *gorm.DB +} + +type PodcastRepository interface { + InsertPodcast(podcast schemas.Podcast) error + InsertAsset(name string) (uint, error) + FindPodcastByName(name string) (schemas.Podcast, error) + FindPodcastTypeByName(name string) (schemas.PodcastType, error) + FindPodcastByEpisodeNoAndType(episodeNo uint, podcastType string) (schemas.Podcast, error) + FetchThumbnail(podcastID uint) (schemas.Asset, error) + UpdatePodcastThumbnail(podcastID uint, assetID uint) error + UpdatePodcastURL(podcastID uint, url string) error + UpdatePodcastDescription(podcastID uint, description string) error + DeletePodcast(podcastID uint) error + GetAllPodcasts() ([]models.PodcastRequest, error) + GetPodcastByType(podcastType string) ([]models.PodcastRequest, error) + GetPodcastByEpisodeNoAndType(episodeNo uint, podcastType string) (models.PodcastRequest, error) +} + +func NewPodcastRepository(db *gorm.DB) PodcastRepository { + return &podcastRepository{db} +} + +func (pr *podcastRepository) InsertPodcast(podcast schemas.Podcast) error { + return pr.db.Create(&podcast).Error +} + +func (pr *podcastRepository) InsertAsset(name string) (uint, error) { + var assetType schemas.AssetType + if err := pr.db.Where("name = ?", schemas.Image).First(&assetType).Error; err != nil { + return 0, err + } + asset := schemas.Asset{ + Name: name, + AssetTypeID: assetType.ID, + } + if err := pr.db.Create(&asset).Error; err != nil { + return 0, err + } + return asset.ID, nil +} + +func (pr *podcastRepository) FindPodcastByName(name string) (schemas.Podcast, error) { + var podcast schemas.Podcast + if err := pr.db.Where("name = ?", name).First(&podcast).Error; err != nil { + return schemas.Podcast{}, err + } + + return podcast, nil +} + +func (pr *podcastRepository) FindPodcastByEpisodeNoAndType(episodeNo uint, podcastType string) (schemas.Podcast, error) { + var podcastTypeSchema schemas.PodcastType + if err := pr.db.Where("name = ?", podcastType).First(&podcastTypeSchema).Error; err != nil { + return schemas.Podcast{}, err + } + var podcast schemas.Podcast + if err := pr.db.Where("episode_no = ? AND type_id = ?", episodeNo, podcastTypeSchema.ID).First(&podcast).Error; err != nil { + return schemas.Podcast{}, err + } + return podcast, nil +} + +func (pr *podcastRepository) FindPodcastTypeByName(name string) (schemas.PodcastType, error) { + var podcastType schemas.PodcastType + if err := pr.db.Where("name = ?", name).First(&podcastType).Error; err != nil { + return schemas.PodcastType{}, err + } + + return podcastType, nil +} + +func (pr *podcastRepository) FetchThumbnail(podcastID uint) (schemas.Asset, error) { + var podcast schemas.Podcast + if err := pr.db.Preload("Thumbnail").Where("id = ?", podcastID).First(&podcast).Error; err != nil { + return schemas.Asset{}, err + } + return podcast.Thumbnail, nil +} + +func (pr *podcastRepository) UpdatePodcastThumbnail(podcastID uint, assetID uint) error { + return pr.db.Model(&schemas.Podcast{}).Where("id = ?", podcastID).Update("thumbnail_id", assetID).Error +} + +func (pr *podcastRepository) UpdatePodcastURL(podcastID uint, url string) error { + return pr.db.Model(&schemas.Podcast{}).Where("id = ?", podcastID).Update("media_url", url).Error +} + +func (pr *podcastRepository) UpdatePodcastDescription(podcastID uint, description string) error { + return pr.db.Model(&schemas.Podcast{}).Where("id = ?", podcastID).Update("description", description).Error +} + +func (pr *podcastRepository) DeletePodcast(podcastID uint) error { + return pr.db.Unscoped().Delete(&schemas.Podcast{}, podcastID).Error +} + +func (pr *podcastRepository) GetAllPodcasts() ([]models.PodcastRequest, error) { + var podcasts []models.PodcastRequest + + if err := pr.db.Table("podcasts").Select( + `podcasts.name,podcasts.description,podcasts.episode_no,podcasts.media_url,podcast_types.name as type, + CONCAT(?::text,'/static/images','/',assets.name) as image_url`, config.Origin, + ).Joins( + "JOIN assets on assets.id = podcasts.thumbnail_id").Joins( + "JOIN podcast_types on podcast_types.id = podcasts.type_id").Scan( + &podcasts).Error; err != nil { + return nil, err + } + + return podcasts, nil +} + +func (pr *podcastRepository) GetPodcastByType(podcastType string) ([]models.PodcastRequest, error) { + var podcasts []models.PodcastRequest + if err := pr.db.Table("podcasts").Select( + `podcasts.name,podcasts.description,podcasts.episode_no,podcasts.media_url,podcast_types.name as type, + CONCAT(?::text,'/static/images','/',assets.name) as image_url`, config.Origin, + ).Where("podcast_types.name = ?", podcastType).Joins( + "JOIN assets on assets.id = podcasts.thumbnail_id").Joins( + "JOIN podcast_types on podcast_types.id = podcasts.type_id").Scan( + &podcasts).Error; err != nil { + return nil, err + } + + return podcasts, nil +} + +func (pr *podcastRepository) GetPodcastByEpisodeNoAndType(episodeNo uint, podcastType string) (models.PodcastRequest, error) { + var podcasts models.PodcastRequest + if err := pr.db.Table("podcasts").Select( + `podcasts.name,podcasts.description,podcasts.episode_no,podcasts.media_url,podcast_types.name as type, + CONCAT(?::text,'/static/images','/',assets.name) as image_url`, config.Origin, + ).Where("podcast_types.name = ? AND episode_no = ?", podcastType, episodeNo).Joins( + "JOIN assets on assets.id = podcasts.thumbnail_id").Joins( + "JOIN podcast_types on podcast_types.id = podcasts.type_id").Scan( + &podcasts).Error; err != nil { + return models.PodcastRequest{}, err + } + + return podcasts, nil + // var podcastTypeSchema schemas.PodcastType + // if err := pr.db.Where("name = ?", podcastType).First(&podcastTypeSchema).Error; err != nil { + // return schemas.Podcast{}, err + // } + // var podcast schemas.Podcast + // if err := pr.db.Where("episode_no = ? AND type_id = ?", episodeNo, podcastTypeSchema.ID).First(&podcast).Error; err != nil { + // return schemas.Podcast{}, err + // } + // return podcast, nil +} diff --git a/repositories/seeder_repository.go b/repositories/seeder_repository.go index f71db45..3f0441e 100644 --- a/repositories/seeder_repository.go +++ b/repositories/seeder_repository.go @@ -13,6 +13,7 @@ type SeedRepository interface { InsertTeams(teams []schemas.Team) error InsertRoles(roles []schemas.Role) error InsertAssetTypes(types []schemas.AssetType) error + InsertPodcastTypes(types []schemas.PodcastType) error } func NewSeedRepository(db *gorm.DB) SeedRepository { @@ -31,3 +32,7 @@ func (sr *seedRepository) InsertRoles(roles []schemas.Role) error { func (sr *seedRepository) InsertAssetTypes(types []schemas.AssetType) error { return sr.db.Create(&types).Error } + +func (sr *seedRepository) InsertPodcastTypes(types []schemas.PodcastType) error { + return sr.db.Create(&types).Error +} diff --git a/router/podcast.go b/router/podcast.go new file mode 100644 index 0000000..923149f --- /dev/null +++ b/router/podcast.go @@ -0,0 +1,27 @@ +package router + +import ( + "github.com/ecea-nitt/ecea-server/controllers" + "github.com/ecea-nitt/ecea-server/middlewares" + "github.com/labstack/echo/v4" +) + +func PodcastRoutes(e *echo.Group, c controllers.PodcastController) { + podcast := e.Group("/podcast") + + // Create + podcast.POST("/create", middlewares.Authorizer(c.CreatePodcast)) + + // Read + podcast.GET("/get/all", c.GetAllPodcasts) + podcast.GET("/getall/:type", c.GetPodcastByType) + podcast.GET("/get/:episodeNo/:type", c.GetPodcast) + + // Update + podcast.PUT("/edit/thumbnail", middlewares.Authorizer(c.EditThumbnail)) + podcast.PUT("/edit/url", middlewares.Authorizer(c.EditURL)) + podcast.PUT("/edit/description", middlewares.Authorizer(c.EditDescription)) + + // Delete + podcast.DELETE("/delete", middlewares.Authorizer(c.DeletePodcast)) +} diff --git a/router/router.go b/router/router.go index 20a3ac4..13a9be7 100644 --- a/router/router.go +++ b/router/router.go @@ -16,5 +16,6 @@ func NewRouter(e *echo.Echo, c controllers.AppController) { UserRoutes(api, c.User) TeamRoutes(api, c.Team) + PodcastRoutes(api, c.Podcast) SwaggerRoutes(api) } diff --git a/router/swagger.go b/router/swagger.go index c0631d3..c7993ae 100644 --- a/router/swagger.go +++ b/router/swagger.go @@ -10,6 +10,6 @@ func SwaggerRoutes(e *echo.Group) { origin := config.Origin url := echoSwagger.URL(origin + "/v1/admin/doc.json") e.GET("/admin/*", echoSwagger.EchoWrapHandler(url)) - // e.File("/admin/index.html", "templates/html/swagger.html") - // e.File("/admin/swagger-ui.css", "templates/css/swagger-ui.css") + e.File("/admin/index.html", "templates/html/swagger.html") + //e.File("/admin/swagger-ui.css", "templates/css/swagger-ui.css") } diff --git a/schemas/asset.go b/schemas/asset.go index 7ab5f23..5d05b0f 100644 --- a/schemas/asset.go +++ b/schemas/asset.go @@ -17,6 +17,7 @@ type AssetType struct { type AssetTypes string const ( - Image AssetTypes = "IMAGE" - Document AssetTypes = "DOCUMENT" + Image AssetTypes = "IMAGE" + Document AssetTypes = "DOCUMENT" + ExternalAudio AssetTypes = "EXTERNAL_AUDIO" ) diff --git a/schemas/podcast.go b/schemas/podcast.go new file mode 100644 index 0000000..a14cabe --- /dev/null +++ b/schemas/podcast.go @@ -0,0 +1,32 @@ +package schemas + +import ( + "gorm.io/gorm" +) + +type Podcast struct { + gorm.Model + Name string `gorm:"varchar(50);not null;unique"` + Description string `gorm:"varchar(800);not null"` + EpisodeNo uint `gorm:"not null;uniqueIndex:epno_type"` + MediaURL string `gorm:"not null"` + + // Relations + TypeID uint `gorm:"not null;uniqueIndex:epno_type"` + Type PodcastType `gorm:"foreignKey:TypeID;"` + ThumbnailID uint `gorm:"not null;"` + Thumbnail Asset `gorm:"foreignKey:ThumbnailID;"` +} + +type PodcastType struct { + ID uint `gorm:"primarykey"` + //gorm.Model + Name string `gorm:"not null;unique"` +} + +type PodcastTypes string + +const ( + GuestLecture PodcastTypes = "GUEST_LECTURE" + CareerPath PodcastTypes = "CAREER_PATH" +) diff --git a/services/podcast_service.go b/services/podcast_service.go new file mode 100644 index 0000000..6372d98 --- /dev/null +++ b/services/podcast_service.go @@ -0,0 +1,188 @@ +package services + +import ( + "errors" + "mime/multipart" + + "github.com/ecea-nitt/ecea-server/helpers" + "github.com/ecea-nitt/ecea-server/models" + "github.com/ecea-nitt/ecea-server/repositories" + "github.com/ecea-nitt/ecea-server/schemas" + "github.com/ecea-nitt/ecea-server/utils" +) + +type podcastService struct { + repo repositories.PodcastRepository +} + +type PodcastService interface { + CreatePodcast( + podcastDetails models.PodcastRequest, + podcastImage *multipart.FileHeader) error + EditThumbnail( + podcastDetails models.PodcastRequest, + podcastImage *multipart.FileHeader) error + + EditURL(podcastDetails models.PodcastRequest) error + EditDescription(podcastDetails models.PodcastRequest) error + DeletePodcast(podcastDetails models.PodcastRequest) error + GetPodcastByEpisodeNumberAndType(episode uint, podcastType string) (models.PodcastRequest, error) + GetAllPodcasts() ([]models.PodcastRequest, error) + GetPodcastByType(podcastType string) ([]models.PodcastRequest, error) +} + +func NewPodcastService(repo repositories.PodcastRepository) PodcastService { + return &podcastService{repo} +} + +func (ps *podcastService) CreatePodcast( + podcast models.PodcastRequest, + podcastImage *multipart.FileHeader) error { + + name, err := utils.NameValidator(podcast.Name) + if err != nil { + return err + } + + mediaURL, err := utils.URLValidator(podcast.MediaURL) + if err != nil { + return err + } + + assetChannel := make(chan int) + typeChannel := make(chan int) + + go helpers.FetchPodcastTypeID(typeChannel, string(podcast.Type), ps.repo) + + go helpers.UploadAndFetchAssetID(assetChannel, podcastImage, ps.repo, "podcast") + + assetID := <-assetChannel + podcastTypeID := <-typeChannel + + if assetID == -1 || podcastTypeID == -1 { + return errors.New("error uploading asset") + } + + newPodcast := schemas.Podcast{ + Name: name, + Description: podcast.Description, + MediaURL: mediaURL, + EpisodeNo: podcast.EpisodeNo, + ThumbnailID: uint(assetID), + TypeID: uint(podcastTypeID), + } + + return ps.repo.InsertPodcast(newPodcast) +} + +func (ps *podcastService) EditThumbnail( + podcast models.PodcastRequest, + podcastImage *multipart.FileHeader) error { + + dbPodcast, err := ps.repo.FindPodcastByEpisodeNoAndType(podcast.EpisodeNo, string(podcast.Type)) + if err != nil { + return err + } + + thumbnail, err := ps.repo.FetchThumbnail(dbPodcast.ID) + if err != nil { + return err + } + + err = utils.DeleteImage(thumbnail.Name) + if err != nil { + return err + } + + assetChannel := make(chan int) + + go helpers.UploadAndFetchAssetID(assetChannel, podcastImage, ps.repo, "podcast") + + assetID := <-assetChannel + + if assetID == -1 { + return errors.New("error uploading asset") + } + + return ps.repo.UpdatePodcastThumbnail(dbPodcast.ID, uint(assetID)) +} + +func (ps *podcastService) EditURL(podcast models.PodcastRequest) error { + mediaURL, err := utils.URLValidator(podcast.MediaURL) + if err != nil { + return err + } + + dbPodcast, err := ps.repo.FindPodcastByEpisodeNoAndType(podcast.EpisodeNo, string(podcast.Type)) + if err != nil { + return err + } + + return ps.repo.UpdatePodcastURL(dbPodcast.ID, mediaURL) +} + +func (ps *podcastService) EditDescription(podcast models.PodcastRequest) error { + dbPodcast, err := ps.repo.FindPodcastByEpisodeNoAndType(podcast.EpisodeNo, string(podcast.Type)) + if err != nil { + return err + } + + return ps.repo.UpdatePodcastDescription(dbPodcast.ID, podcast.Description) +} + +func (ps *podcastService) DeletePodcast(podcast models.PodcastRequest) error { + dbPodcast, err := ps.repo.FindPodcastByEpisodeNoAndType(podcast.EpisodeNo, string(podcast.Type)) + if err != nil { + return err + } + + thumbnail, err := ps.repo.FetchThumbnail(dbPodcast.ID) + if err != nil { + return err + } + + err = utils.DeleteImage(thumbnail.Name) + if err != nil { + return err + } + + return ps.repo.DeletePodcast(dbPodcast.ID) +} + +func (ps *podcastService) GetPodcastByEpisodeNumberAndType(episode uint, podcastType string) (models.PodcastRequest, error) { + dbPodcast, err := ps.repo.GetPodcastByEpisodeNoAndType(episode, podcastType) + if err != nil { + return models.PodcastRequest{}, err + } + return dbPodcast, nil +} + +func (ps *podcastService) GetPodcastByName(podcastName string) (schemas.Podcast, error) { + name, err := utils.NameValidator(podcastName) + if err != nil { + return schemas.Podcast{}, err + } + + dbPodcast, err := ps.repo.FindPodcastByName(name) + if err != nil { + return schemas.Podcast{}, err + } + return dbPodcast, nil +} + +func (ps *podcastService) GetAllPodcasts() ([]models.PodcastRequest, error) { + dbPodcast, err := ps.repo.GetAllPodcasts() + if err != nil { + return []models.PodcastRequest{}, err + } + return dbPodcast, nil + +} + +func (ps *podcastService) GetPodcastByType(podcastType string) ([]models.PodcastRequest, error) { + dbPodcast, err := ps.repo.GetPodcastByType(podcastType) + if err != nil { + return []models.PodcastRequest{}, err + } + return dbPodcast, nil +} diff --git a/services/seeder_service.go b/services/seeder_service.go index 9837b41..3a9001e 100644 --- a/services/seeder_service.go +++ b/services/seeder_service.go @@ -14,6 +14,7 @@ type SeederService interface { AssetTypesSeeder() error TeamsSeeder() error RolesSeeder() error + PodcastTypesSeeder() error } var teams = []schemas.Team{ @@ -61,6 +62,15 @@ var assetTypes = []schemas.AssetType{ }, } +var podcastTypes = []schemas.PodcastType{ + { + Name: string(schemas.CareerPath), + }, + { + Name: string(schemas.GuestLecture), + }, +} + func NewSeeder(repo repositories.SeedRepository) SeederService { return &seederService{repo} } @@ -76,3 +86,7 @@ func (s *seederService) RolesSeeder() error { func (s *seederService) AssetTypesSeeder() error { return s.repo.InsertAssetTypes(assetTypes) } + +func (s *seederService) PodcastTypesSeeder() error { + return s.repo.InsertPodcastTypes(podcastTypes) +} diff --git a/services/team_service.go b/services/team_service.go index 084336b..16e7cd2 100644 --- a/services/team_service.go +++ b/services/team_service.go @@ -64,7 +64,7 @@ func (ts *teamService) CreateTeamMember( go helpers.FetchRoleID(roleChannel, string(memberDetails.Role), ts.repo) - go helpers.UploadAndFetchAssetID(assetChannel, memberImage, ts.repo) + go helpers.UploadAndFetchAssetID(assetChannel, memberImage, ts.repo, "team") teamID := <-teamChannel roleID := <-roleChannel diff --git a/templates/html/swagger.html b/templates/html/swagger.html index 77ac384..c3240f7 100644 --- a/templates/html/swagger.html +++ b/templates/html/swagger.html @@ -4,7 +4,7 @@