diff --git a/internal/api/http/handlers/files/files.go b/internal/api/http/handlers/files/files.go index c1d86ce..6c8a1a7 100644 --- a/internal/api/http/handlers/files/files.go +++ b/internal/api/http/handlers/files/files.go @@ -81,3 +81,45 @@ func GetFile(fileService model.FileService) echo.HandlerFunc { return request.Blob(http.StatusOK, fileType, file) } } + +// GetWidgetFiles godoc +// @Summary Get all widget files +// @Description Retrieve all widget files, filtered by type if specified +// @Tags Files +// @Accept json +// @Produce json +// @Security BearerAuth +// @Param type query string false "File type (audio or image)" Enums(audio, image) +// @Success 200 {array} model.DataFile "List of files" +// @Failure 400 {object} echo.HTTPError "Bad request" +// @Failure 401 {object} echo.HTTPError "Unauthorized or expired token" +// @Router /files/widgets [get] +func GetWidgetFiles(fileService model.FileService) echo.HandlerFunc { + return func(request echo.Context) error { + ctx := context.Background() + fileType := request.QueryParam("type") + + if fileType != "" && fileType != "audio" && fileType != "image" { + return echo.NewHTTPError(http.StatusUnprocessableEntity, "Invalid file type") + } + + authData, err := fileService.CheckToken(request) + if err != nil { + slog.Error(err.Error()) + return echo.NewHTTPError(http.StatusUnauthorized, err.Error()) + } + + widgetFiles, err := fileService.WidgetsFiles( + ctx, + fileType, + authData.AccountID, + ) + + if err != nil { + slog.Error(err.Error()) + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to retrieve files") + } + + return request.JSON(http.StatusOK, widgetFiles) + } +} diff --git a/internal/app/http/app.go b/internal/app/http/app.go index 2e50aa1..2e79efa 100644 --- a/internal/app/http/app.go +++ b/internal/app/http/app.go @@ -103,4 +103,5 @@ func IncludeFileHandlers( ) { server.POST(PREFIX+"/files", files.AddNewFile(fileService)) server.GET(PREFIX+"/files/:file_id", files.GetFile(fileService)) + server.GET(PREFIX+"/files/widgets", files.GetWidgetFiles(fileService)) } diff --git a/internal/docs/docs.go b/internal/docs/docs.go index 8bc834d..08bbdf4 100644 --- a/internal/docs/docs.go +++ b/internal/docs/docs.go @@ -138,6 +138,61 @@ const docTemplate = `{ } } }, + "/files/widgets": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve all widget files, filtered by type if specified", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Files" + ], + "summary": "Get all widget files", + "parameters": [ + { + "enum": [ + "audio", + "image" + ], + "type": "string", + "description": "File type (audio or image)", + "name": "type", + "in": "query" + } + ], + "responses": { + "200": { + "description": "List of files", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/donat-widget_internal_model.DataFile" + } + } + }, + "400": { + "description": "Bad request", + "schema": { + "$ref": "#/definitions/echo.HTTPError" + } + }, + "401": { + "description": "Unauthorized or expired token", + "schema": { + "$ref": "#/definitions/echo.HTTPError" + } + } + } + } + }, "/files/{file_id}": { "get": { "description": "Retrieve a file by its ID", @@ -708,6 +763,32 @@ const docTemplate = `{ } } }, + "donat-widget_internal_model.DataFile": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "extension": { + "type": "string" + }, + "file_link": { + "type": "string" + }, + "file_name": { + "type": "string" + }, + "file_type": { + "type": "string" + }, + "id": { + "type": "string" + }, + "streamer_id": { + "type": "integer" + } + } + }, "donat-widget_internal_model.FilterSettingResponse": { "type": "object", "properties": { diff --git a/internal/docs/swagger.json b/internal/docs/swagger.json index cdd8de0..238d85f 100644 --- a/internal/docs/swagger.json +++ b/internal/docs/swagger.json @@ -131,6 +131,61 @@ } } }, + "/files/widgets": { + "get": { + "security": [ + { + "BearerAuth": [] + } + ], + "description": "Retrieve all widget files, filtered by type if specified", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Files" + ], + "summary": "Get all widget files", + "parameters": [ + { + "enum": [ + "audio", + "image" + ], + "type": "string", + "description": "File type (audio or image)", + "name": "type", + "in": "query" + } + ], + "responses": { + "200": { + "description": "List of files", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/donat-widget_internal_model.DataFile" + } + } + }, + "400": { + "description": "Bad request", + "schema": { + "$ref": "#/definitions/echo.HTTPError" + } + }, + "401": { + "description": "Unauthorized or expired token", + "schema": { + "$ref": "#/definitions/echo.HTTPError" + } + } + } + } + }, "/files/{file_id}": { "get": { "description": "Retrieve a file by its ID", @@ -701,6 +756,32 @@ } } }, + "donat-widget_internal_model.DataFile": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "extension": { + "type": "string" + }, + "file_link": { + "type": "string" + }, + "file_name": { + "type": "string" + }, + "file_type": { + "type": "string" + }, + "id": { + "type": "string" + }, + "streamer_id": { + "type": "integer" + } + } + }, "donat-widget_internal_model.FilterSettingResponse": { "type": "object", "properties": { diff --git a/internal/docs/swagger.yaml b/internal/docs/swagger.yaml index ca7e51a..461b3f5 100644 --- a/internal/docs/swagger.yaml +++ b/internal/docs/swagger.yaml @@ -34,6 +34,23 @@ definitions: - name - template_id type: object + donat-widget_internal_model.DataFile: + properties: + created_at: + type: string + extension: + type: string + file_link: + type: string + file_name: + type: string + file_type: + type: string + id: + type: string + streamer_id: + type: integer + type: object donat-widget_internal_model.FilterSettingResponse: properties: enable_links: @@ -324,6 +341,41 @@ paths: summary: Get a file tags: - Files + /files/widgets: + get: + consumes: + - application/json + description: Retrieve all widget files, filtered by type if specified + parameters: + - description: File type (audio or image) + enum: + - audio + - image + in: query + name: type + type: string + produces: + - application/json + responses: + "200": + description: List of files + schema: + items: + $ref: '#/definitions/donat-widget_internal_model.DataFile' + type: array + "400": + description: Bad request + schema: + $ref: '#/definitions/echo.HTTPError' + "401": + description: Unauthorized or expired token + schema: + $ref: '#/definitions/echo.HTTPError' + security: + - BearerAuth: [] + summary: Get all widget files + tags: + - Files /filters-settings: get: consumes: diff --git a/internal/model/interfaces.go b/internal/model/interfaces.go index 764a896..6e74993 100644 --- a/internal/model/interfaces.go +++ b/internal/model/interfaces.go @@ -46,7 +46,6 @@ type WidgetRepo interface { name string, ) (WidgetID, error) CheckWidgetName(ctx context.Context, streamerID int, name string) (bool, error) - GetWidgetsByStreamerID(ctx context.Context, streamerID int) ([]*GetWidgetDb, error) //GetWidgetByID(ctx context.Context, widgetID WidgetID) ([]*GetWidgetDb, error) @@ -152,6 +151,7 @@ type FileRepo interface { extension, fileType string, ) (string, error) GetByID(ctx context.Context, fileID string) ([]byte, string, error) + WidgetsFiles(ctx context.Context, fileType string, streamerID int) ([]*DataFile, error) } type FileService interface { @@ -163,4 +163,5 @@ type FileService interface { streamerID int, ) (string, error) GetByID(ctx context.Context, fileID string) ([]byte, string, error) + WidgetsFiles(ctx context.Context, fileType string, streamerID int) ([]*DataFile, error) } diff --git a/internal/model/models.go b/internal/model/models.go index ecce281..0c91c32 100644 --- a/internal/model/models.go +++ b/internal/model/models.go @@ -5,6 +5,17 @@ import ( "time" ) +type DataFile struct { + ID uuid.UUID `db:"id" json:"id"` + FileType string `db:"file_type" json:"file_type"` + FileName string `db:"file_name" json:"file_name"` + Extension string `db:"extension" json:"extension"` + StreamerID int `db:"streamer_id" json:"streamer_id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + Size float32 `db:"size" json:"size"` + FileLink string `json:"file_link"` +} + type GetWidgetDb struct { ID int `db:"id" json:"id" example:"1" format:"int64" description:"Unique identifier of the widget"` StreamerID int `db:"streamer_id" json:"streamer_id" example:"1001" format:"int64" description:"ID of the streamer"` diff --git a/internal/model/sql/model.go b/internal/model/sql/model.go index b532162..f65d83f 100644 --- a/internal/model/sql/model.go +++ b/internal/model/sql/model.go @@ -35,6 +35,7 @@ CREATE TABLE IF NOT EXISTS donats ( widget_id INTEGER NOT NULL, order_id TEXT NOT NULL, target_id INTEGER NOT NULL, + size FLOAT NOT NULL, text TEXT NOT NULL, amount INTEGER NOT NULL, diff --git a/internal/model/sql/query.go b/internal/model/sql/query.go index 157b6eb..05a5921 100644 --- a/internal/model/sql/query.go +++ b/internal/model/sql/query.go @@ -125,3 +125,17 @@ var GetWidgetByName = `SELECT id FROM widgets WHERE ( ` var FileById = `SELECT file_name, streamer_id, file_type FROM files WHERE id = (@id);` + +var AudioFilesWidgets = ` +SELECT f.* +FROM files AS f +JOIN widgets AS w ON w.audio = f.id +WHERE f.streamer_id = (@streamer_id); +` + +var ImageFilesWidgets = ` +SELECT f.* +FROM files AS f +JOIN widgets AS w ON w.image = f.id +WHERE f.streamer_id = (@streamer_id); +` diff --git a/internal/repository/file/file.go b/internal/repository/file/file.go index 37fc7da..c5bf3c6 100644 --- a/internal/repository/file/file.go +++ b/internal/repository/file/file.go @@ -5,6 +5,7 @@ import ( "donat-widget/internal/model" "donat-widget/internal/model/sql" "fmt" + "github.com/georgysavva/scany/v2/pgxscan" "github.com/google/uuid" "github.com/jackc/pgx/v5" "mime/multipart" @@ -37,11 +38,13 @@ func (fileRepo *RepoFile) AddNew( return "", err } + sizeInKB := float64(file.Size) / 1024 args := pgx.NamedArgs{ "streamer_id": streamerID, "file_name": fileName, "file_type": fileType, "extension": extension, + "size": sizeInKB, } fileIDRaw, err := fileRepo.db.Insert(ctx, sql.AddNewFile, args) @@ -87,3 +90,36 @@ func (fileRepo *RepoFile) GetByID( return fileContent, fileType, nil } + +func (fileRepo *RepoFile) WidgetsFiles( + ctx context.Context, + fileType string, + streamerID int, +) ([]*model.DataFile, error) { + args := pgx.NamedArgs{ + "streamer_id": streamerID, + } + + var stmt string + if fileType == "audio" { + stmt = sql.AudioFilesWidgets + } else { + stmt = sql.ImageFilesWidgets + } + + rows, err := fileRepo.db.Select(ctx, stmt, args) + if err != nil { + return nil, fmt.Errorf("error retrieving file info: %v", err) + } + defer rows.Close() + + var files []*model.DataFile + + err = pgxscan.ScanAll(&files, rows) + + if len(files) == 0 { + return nil, nil + } + + return files, nil +} diff --git a/internal/service/file/file.go b/internal/service/file/file.go index 179905a..cd35b19 100644 --- a/internal/service/file/file.go +++ b/internal/service/file/file.go @@ -70,6 +70,32 @@ func (fileService *ServiceFile) GetByID( return fileBytes, fileType, nil } +func (fileService *ServiceFile) WidgetsFiles( + ctx context.Context, + fileType string, + streamerID int, +) ([]*model.DataFile, error) { + files, err := fileService.fileRepo.WidgetsFiles( + ctx, + fileType, + streamerID, + ) + + if err != nil { + return nil, err + } + + if files == nil { + return []*model.DataFile{}, nil + } + + for _, file := range files { + file.FileLink = "http://localhost/file/" + file.ID.String() + } + + return files, nil +} + func (fileService *ServiceFile) CheckToken( request echo.Context, ) (api.CheckTokenResponse, error) {