add filers for create in update donate page

This commit is contained in:
harold 2025-02-28 14:01:53 +05:00
parent 86e8341f59
commit 447dd1d6a9
11 changed files with 194 additions and 35 deletions

View File

@ -53,7 +53,13 @@ func main() {
// SERVICES // SERVICES
widgetService := WidgetService.New(widgetRepo, authClient) widgetService := WidgetService.New(widgetRepo, authClient)
donatService := DonatService.New(donatRepo, widgetRepo, paymentClient, authClient) donatService := DonatService.New(
donatRepo,
widgetRepo,
paymentClient,
authClient,
storage,
)
targetService := TargetService.New(targetRepo, authClient) targetService := TargetService.New(targetRepo, authClient)
http.NewApp( http.NewApp(

View File

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"donat-widget/internal/model" "donat-widget/internal/model"
"io" "io"
"mime/multipart"
"os" "os"
"path/filepath" "path/filepath"
) )
@ -28,16 +29,18 @@ func NewLocalStorage(basePath string) *LocalStorage {
} }
func (ls *LocalStorage) Upload( func (ls *LocalStorage) Upload(
file model.UploadFile, file *multipart.FileHeader,
filename string, filename string,
size int64, size int64,
collection string, collection string,
) (model.FileID, error) { ) (model.FileID, error) {
// Создаем путь для коллекции, если он не существует
collectionPath := filepath.Join(ls.basePath, collection) collectionPath := filepath.Join(ls.basePath, collection)
if err := os.MkdirAll(collectionPath, os.ModePerm); err != nil { if err := os.MkdirAll(collectionPath, os.ModePerm); err != nil {
return "error", err return "error", err
} }
// Создаем путь для файла
filePath := filepath.Join(collectionPath, filename) filePath := filepath.Join(collectionPath, filename)
f, err := os.Create(filePath) f, err := os.Create(filePath)
if err != nil { if err != nil {
@ -45,17 +48,26 @@ func (ls *LocalStorage) Upload(
} }
defer f.Close() defer f.Close()
_, err = io.Copy(f, *file) // Открываем исходный файл для чтения
srcFile, err := file.Open()
if err != nil {
return "error opening file", err
}
defer srcFile.Close()
// Копируем содержимое файла
_, err = io.Copy(f, srcFile)
if err != nil { if err != nil {
return "error copy", err return "error copy", err
} }
// Возвращаем идентификатор файла
return model.FileID(filename), nil return model.FileID(filename), nil
} }
func (ls *LocalStorage) Download( func (ls *LocalStorage) Download(
FileID model.FileID, FileID model.FileID,
) (model.DownloadFile, error) { ) ([]byte, error) {
filePath := string(FileID) filePath := string(FileID)
f, err := os.Open(filePath) f, err := os.Open(filePath)
if err != nil { if err != nil {
@ -73,20 +85,31 @@ func (ls *LocalStorage) Download(
} }
func (ls *LocalStorage) Update( func (ls *LocalStorage) Update(
file model.UploadFile, file *multipart.FileHeader,
fileID model.FileID, fileID model.FileID,
filename string, filename string,
size int64, size int64,
collection string, collection string,
) error { ) error {
// Получаем путь для хранения файла
filePath := string(fileID) filePath := string(fileID)
// Создаем новый файл
f, err := os.Create(filePath) f, err := os.Create(filePath)
if err != nil { if err != nil {
return err return err
} }
defer f.Close() defer f.Close()
_, err = io.Copy(f, *file) // Открываем файл из multipart.FileHeader
srcFile, err := file.Open()
if err != nil {
return err
}
defer srcFile.Close()
// Копируем содержимое из исходного файла в новый файл
_, err = io.Copy(f, srcFile)
if err != nil { if err != nil {
return err return err
} }

View File

@ -3,8 +3,8 @@ package donat
import ( import (
"context" "context"
"donat-widget/internal/model" "donat-widget/internal/model"
"donat-widget/pkg/validator"
"donat-widget/pkg/custom_response" "donat-widget/pkg/custom_response"
"donat-widget/pkg/validator"
"fmt" "fmt"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
@ -125,6 +125,7 @@ func MarkDonatView(donatService model.DonatService) echo.HandlerFunc {
// @Tags Donate // @Tags Donate
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Security BearerAuth
// @Success 200 {object} model.InnerDonatePageResponse "Current donate page state" // @Success 200 {object} model.InnerDonatePageResponse "Current donate page state"
// @Failure 400 {object} echo.HTTPError "Bad request" // @Failure 400 {object} echo.HTTPError "Bad request"
// @Failure 401 {object} echo.HTTPError "Unauthorized or expired token" // @Failure 401 {object} echo.HTTPError "Unauthorized or expired token"
@ -159,7 +160,7 @@ func GetInnerDonatePage(donatService model.DonatService) echo.HandlerFunc {
// @Failure 400 {object} echo.HTTPError "Bad request" // @Failure 400 {object} echo.HTTPError "Bad request"
// @Failure 401 {object} echo.HTTPError "Unauthorized or expired token" // @Failure 401 {object} echo.HTTPError "Unauthorized or expired token"
// @Failure 422 {object} echo.HTTPError "Validation error" // @Failure 422 {object} echo.HTTPError "Validation error"
// @Router /outer-donate-page/:streamer-login [get] // @Router /outer-donate-page/{streamer-login} [get]
func GetOuterDonatePage(donatService model.DonatService) echo.HandlerFunc { func GetOuterDonatePage(donatService model.DonatService) echo.HandlerFunc {
return func(request echo.Context) error { return func(request echo.Context) error {
ctx := context.Background() ctx := context.Background()
@ -181,8 +182,10 @@ func GetOuterDonatePage(donatService model.DonatService) echo.HandlerFunc {
// @Tags Donate // @Tags Donate
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Security BearerAuth
// @Param request body model.UpdateDonatPage true "Update fields" // @Param request body model.UpdateDonatPage true "Update fields"
// @Param background formData file false "Background image" // @Param background formData file false "Background image"
// @Param head-img formData file false "Head image"
// @Success 200 {string} string "Donat page updated successfully" // @Success 200 {string} string "Donat page updated successfully"
// @Failure 400 {object} echo.HTTPError "Bad request" // @Failure 400 {object} echo.HTTPError "Bad request"
// @Failure 401 {object} echo.HTTPError "Unauthorized or expired token" // @Failure 401 {object} echo.HTTPError "Unauthorized or expired token"
@ -190,31 +193,41 @@ func GetOuterDonatePage(donatService model.DonatService) echo.HandlerFunc {
// @Router /donat-page [patch] // @Router /donat-page [patch]
func UpdateDonatePage(donatService model.DonatService) echo.HandlerFunc { func UpdateDonatePage(donatService model.DonatService) echo.HandlerFunc {
return func(request echo.Context) error { return func(request echo.Context) error {
ctx := context.Background()
var body model.UpdateDonatPage var body model.UpdateDonatPage
err := validator.ParseAndValidate(&body, request) err := validator.ParseAndValidate(&body, request)
if err != nil { if err != nil {
slog.Error(err.Error()) slog.Error(err.Error())
return echo.NewHTTPError(http.StatusUnprocessableEntity, "Unprocessable Entity") return echo.NewHTTPError(http.StatusUnprocessableEntity, "Unprocessable Entity")
} }
form, err := request.MultipartForm()
authData, err := donatService.CheckToken(request)
//if err != nil {
// slog.Error(err.Error())
// return echo.NewHTTPError(http.StatusUnauthorized, custom_response.Unauthorized)
//}
background, err := request.FormFile("background")
if err != nil { if err != nil {
slog.Error(err.Error()) background = nil
return echo.NewHTTPError(http.StatusUnprocessableEntity, "Unprocessable Entity") }
headImg, err := request.FormFile("head_img")
if err != nil {
headImg = nil
} }
if form != nil { err = donatService.UpdateDonatePage(
if backgroundFiles, ok := form.File["background"]; ok && len(backgroundFiles) > 0 { ctx,
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to save background") model.StreamerID(authData.AccountID),
} body,
background,
headImg,
)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, custom_response.InternalError)
} }
return request.JSON(http.StatusOK, "Success")
// Проверяем и сохраняем avatar, если он передан
if avatarFiles, ok := form.File["avatar"]; ok && len(avatarFiles) > 0 {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to save avatar")
}
return nil
} }
} }

View File

@ -74,6 +74,8 @@ func IncludeDonatHandlers(
server.POST(PREFIX+"/donat/create", CreateDonat(donatService)) server.POST(PREFIX+"/donat/create", CreateDonat(donatService))
server.GET(PREFIX+"/inner-donate-page", GetInnerDonatePage(donatService)) server.GET(PREFIX+"/inner-donate-page", GetInnerDonatePage(donatService))
server.GET(PREFIX+"/outer-donate-page/:streamer-login", GetOuterDonatePage(donatService))
server.PATCH(PREFIX+"/donat-page", UpdateDonatePage(donatService))
server.GET(PREFIX+"/donat/get/:streamerID", GetDonat(donatService)) server.GET(PREFIX+"/donat/get/:streamerID", GetDonat(donatService))

View File

@ -17,6 +17,11 @@ const docTemplate = `{
"paths": { "paths": {
"/donat-page": { "/donat-page": {
"patch": { "patch": {
"security": [
{
"BearerAuth": []
}
],
"description": "Update personal streamer donate page.", "description": "Update personal streamer donate page.",
"consumes": [ "consumes": [
"application/json" "application/json"
@ -43,6 +48,12 @@ const docTemplate = `{
"description": "Background image", "description": "Background image",
"name": "background", "name": "background",
"in": "formData" "in": "formData"
},
{
"type": "file",
"description": "Head image",
"name": "head-img",
"in": "formData"
} }
], ],
"responses": { "responses": {
@ -172,6 +183,11 @@ const docTemplate = `{
}, },
"/inner-donate-page": { "/inner-donate-page": {
"get": { "get": {
"security": [
{
"BearerAuth": []
}
],
"description": "Get inner donate page info", "description": "Get inner donate page info",
"consumes": [ "consumes": [
"application/json" "application/json"
@ -291,7 +307,7 @@ const docTemplate = `{
} }
} }
}, },
"/outer-donate-page": { "/outer-donate-page/{streamer-login}": {
"get": { "get": {
"description": "Get outer donate page info", "description": "Get outer donate page info",
"consumes": [ "consumes": [
@ -304,6 +320,15 @@ const docTemplate = `{
"Donate" "Donate"
], ],
"summary": "Get outer donate page info", "summary": "Get outer donate page info",
"parameters": [
{
"type": "string",
"description": "Login стримера",
"name": "streamer-login",
"in": "path",
"required": true
}
],
"responses": { "responses": {
"200": { "200": {
"description": "Current donate page state", "description": "Current donate page state",
@ -457,7 +482,7 @@ const docTemplate = `{
"description": { "description": {
"type": "string" "type": "string"
}, },
"pageBackground": { "headImg": {
"type": "string" "type": "string"
}, },
"textAfterDonat": { "textAfterDonat": {
@ -479,9 +504,18 @@ const docTemplate = `{
"donat-widget_internal_model.OuterDonatePageResponse": { "donat-widget_internal_model.OuterDonatePageResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
"avatar_url": {
"type": "string"
},
"background_url": {
"type": "string"
},
"description": { "description": {
"type": "string" "type": "string"
}, },
"headImg": {
"type": "string"
},
"login": { "login": {
"type": "string" "type": "string"
}, },

View File

@ -10,6 +10,11 @@
"paths": { "paths": {
"/donat-page": { "/donat-page": {
"patch": { "patch": {
"security": [
{
"BearerAuth": []
}
],
"description": "Update personal streamer donate page.", "description": "Update personal streamer donate page.",
"consumes": [ "consumes": [
"application/json" "application/json"
@ -36,6 +41,12 @@
"description": "Background image", "description": "Background image",
"name": "background", "name": "background",
"in": "formData" "in": "formData"
},
{
"type": "file",
"description": "Head image",
"name": "head-img",
"in": "formData"
} }
], ],
"responses": { "responses": {
@ -165,6 +176,11 @@
}, },
"/inner-donate-page": { "/inner-donate-page": {
"get": { "get": {
"security": [
{
"BearerAuth": []
}
],
"description": "Get inner donate page info", "description": "Get inner donate page info",
"consumes": [ "consumes": [
"application/json" "application/json"
@ -284,7 +300,7 @@
} }
} }
}, },
"/outer-donate-page": { "/outer-donate-page/{streamer-login}": {
"get": { "get": {
"description": "Get outer donate page info", "description": "Get outer donate page info",
"consumes": [ "consumes": [
@ -297,6 +313,15 @@
"Donate" "Donate"
], ],
"summary": "Get outer donate page info", "summary": "Get outer donate page info",
"parameters": [
{
"type": "string",
"description": "Login стримера",
"name": "streamer-login",
"in": "path",
"required": true
}
],
"responses": { "responses": {
"200": { "200": {
"description": "Current donate page state", "description": "Current donate page state",
@ -450,7 +475,7 @@
"description": { "description": {
"type": "string" "type": "string"
}, },
"pageBackground": { "headImg": {
"type": "string" "type": "string"
}, },
"textAfterDonat": { "textAfterDonat": {
@ -472,9 +497,18 @@
"donat-widget_internal_model.OuterDonatePageResponse": { "donat-widget_internal_model.OuterDonatePageResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
"avatar_url": {
"type": "string"
},
"background_url": {
"type": "string"
},
"description": { "description": {
"type": "string" "type": "string"
}, },
"headImg": {
"type": "string"
},
"login": { "login": {
"type": "string" "type": "string"
}, },

View File

@ -17,7 +17,7 @@ definitions:
type: string type: string
description: description:
type: string type: string
pageBackground: headImg:
type: string type: string
textAfterDonat: textAfterDonat:
type: string type: string
@ -31,8 +31,14 @@ definitions:
type: object type: object
donat-widget_internal_model.OuterDonatePageResponse: donat-widget_internal_model.OuterDonatePageResponse:
properties: properties:
avatar_url:
type: string
background_url:
type: string
description: description:
type: string type: string
headImg:
type: string
login: login:
type: string type: string
online: online:
@ -119,6 +125,10 @@ paths:
in: formData in: formData
name: background name: background
type: file type: file
- description: Head image
in: formData
name: head-img
type: file
produces: produces:
- application/json - application/json
responses: responses:
@ -138,6 +148,8 @@ paths:
description: Validation error description: Validation error
schema: schema:
$ref: '#/definitions/echo.HTTPError' $ref: '#/definitions/echo.HTTPError'
security:
- BearerAuth: []
summary: Update personal streamer donate page. summary: Update personal streamer donate page.
tags: tags:
- Donate - Donate
@ -229,6 +241,8 @@ paths:
description: Validation error description: Validation error
schema: schema:
$ref: '#/definitions/echo.HTTPError' $ref: '#/definitions/echo.HTTPError'
security:
- BearerAuth: []
summary: Get inner donate page info summary: Get inner donate page info
tags: tags:
- Donate - Donate
@ -285,11 +299,17 @@ paths:
summary: Update donat moderation settings summary: Update donat moderation settings
tags: tags:
- Donate - Donate
/outer-donate-page: /outer-donate-page/{streamer-login}:
get: get:
consumes: consumes:
- application/json - application/json
description: Get outer donate page info description: Get outer donate page info
parameters:
- description: Login стримера
in: path
name: streamer-login
required: true
type: string
produces: produces:
- application/json - application/json
responses: responses:

View File

@ -5,6 +5,7 @@ import (
"donat-widget/internal/model/api" "donat-widget/internal/model/api"
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"mime/multipart"
) )
type WidgetService interface { type WidgetService interface {
@ -46,8 +47,13 @@ type DonatService interface {
GetInnerDonatPage(ctx context.Context, streamerID StreamerID) (InnerDonatePageResponse, error) GetInnerDonatPage(ctx context.Context, streamerID StreamerID) (InnerDonatePageResponse, error)
GetOuterDonatPage(ctx context.Context, streamerLogin string) (OuterDonatePageResponse, error) GetOuterDonatPage(ctx context.Context, streamerLogin string) (OuterDonatePageResponse, error)
UpdateDonatePage(ctx context.Context, streamerID StreamerID, updateModel UpdateDonatPage) error UpdateDonatePage(
ctx context.Context,
streamerID StreamerID,
updateModel UpdateDonatPage,
background *multipart.FileHeader,
headImg *multipart.FileHeader,
) error
GetVoiceSettings(ctx context.Context, streamerID StreamerID) (VoiceSettingsResponse, error) GetVoiceSettings(ctx context.Context, streamerID StreamerID) (VoiceSettingsResponse, error)
UpdateVoiceSettings(ctx context.Context, streamerID StreamerID, updateModel UpdateVoiceSettings) error UpdateVoiceSettings(ctx context.Context, streamerID StreamerID, updateModel UpdateVoiceSettings) error
@ -90,9 +96,9 @@ type Error interface {
} }
type Storage interface { type Storage interface {
Upload(file UploadFile, filename string, size int64, collection string) (FileID, error) Upload(file *multipart.FileHeader, filename string, size int64, collection string) (FileID, error)
Download(FileID FileID) (DownloadFile, error) Download(FileID FileID) ([]byte, error)
Update(file UploadFile, fileID FileID, filename string, size int64, collection string) error Update(file *multipart.FileHeader, fileID FileID, filename string, size int64, collection string) error
} }
type PaymentClient interface { type PaymentClient interface {

View File

@ -18,7 +18,7 @@ type Widgets []*Widget
type WidgetHTML string type WidgetHTML string
type WeedData *goseaweedfs.FilePart type WeedData *goseaweedfs.FilePart
type UploadFile *multipart.File type UploadFile *multipart.FileHeader
type DownloadFile []byte type DownloadFile []byte
type FileID string type FileID string

View File

@ -6,6 +6,7 @@ import (
"donat-widget/internal/model/api" "donat-widget/internal/model/api"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"log/slog" "log/slog"
"mime/multipart"
"net/http" "net/http"
) )
@ -14,6 +15,7 @@ type ServiceDonat struct {
widgetRepo model.WidgetRepo widgetRepo model.WidgetRepo
authClient model.AuthClient authClient model.AuthClient
paymentClient model.PaymentClient paymentClient model.PaymentClient
storage model.Storage
} }
func New( func New(
@ -21,12 +23,14 @@ func New(
widgetRepo model.WidgetRepo, widgetRepo model.WidgetRepo,
paymentClient model.PaymentClient, paymentClient model.PaymentClient,
authClient model.AuthClient, authClient model.AuthClient,
storage model.Storage,
) *ServiceDonat { ) *ServiceDonat {
return &ServiceDonat{ return &ServiceDonat{
donatRepo: donatRepo, donatRepo: donatRepo,
widgetRepo: widgetRepo, widgetRepo: widgetRepo,
paymentClient: paymentClient, paymentClient: paymentClient,
authClient: authClient, authClient: authClient,
storage: storage,
} }
} }
@ -193,8 +197,24 @@ func (donatService *ServiceDonat) UpdateDonatePage(
ctx context.Context, ctx context.Context,
streamerID model.StreamerID, streamerID model.StreamerID,
updateModel model.UpdateDonatPage, updateModel model.UpdateDonatPage,
background *multipart.FileHeader,
headImg *multipart.FileHeader,
) error { ) error {
if background != nil {
err := donatService.storage.Update(
background, // ❌ Было &background → ✅ Просто background
model.FileID("some-id"),
"test-filename",
999999,
"hello world",
)
if err != nil {
slog.Error(err.Error())
}
}
return nil return nil
} }
func (donatService *ServiceDonat) GetVoiceSettings( func (donatService *ServiceDonat) GetVoiceSettings(

View File

@ -12,6 +12,7 @@ const (
AccountNotFound = "Account not found" AccountNotFound = "Account not found"
NotVerified2FA = "2FA not verified" NotVerified2FA = "2FA not verified"
NotExists2FA = "2FA does not exist" NotExists2FA = "2FA does not exist"
Unauthorized = "Unauthorized"
) )
// Success response // Success response