diff --git a/api/api.go b/api/api.go index c5909eff..09814f70 100644 --- a/api/api.go +++ b/api/api.go @@ -3,6 +3,8 @@ package api import ( "github.com/gin-gonic/gin" "github.com/netless-io/flat-server/api/middleware" + + apiV1 "github.com/netless-io/flat-server/api/v1" ) func New(env string) *gin.Engine { @@ -18,5 +20,7 @@ func New(env string) *gin.Engine { app.Use(middleware.Logger()) + apiV1.RegisterAPIv1Routes(app) + return app } diff --git a/api/middleware/logger.go b/api/middleware/logger.go index 615ebbc3..ed810a64 100644 --- a/api/middleware/logger.go +++ b/api/middleware/logger.go @@ -32,9 +32,7 @@ func Logger() gin.HandlerFunc { path := c.Request.URL.Path requestID := strings.ReplaceAll(uuid.New().String(), "-", "") - log := logger.NewTraceLog(requestID) - - c.Set("logger", log) + c.Set("request_id", requestID) // Process request c.Next() @@ -61,7 +59,7 @@ func Logger() gin.HandlerFunc { param := make(map[string]PayLoad) param[path] = payload - log.Infow("router info", zap.Any("payload", param)) + logger.Infow("router info", zap.String("request_id", requestID), zap.Any("payload", param)) } } diff --git a/api/v1/agora/agora.go b/api/v1/agora/agora.go new file mode 100644 index 00000000..c9da0477 --- /dev/null +++ b/api/v1/agora/agora.go @@ -0,0 +1,22 @@ +package agora + +import ( + "github.com/gin-gonic/gin" +) + +func RegisterRoutes(agoraGroup *gin.RouterGroup) { + + agoraRoute := agoraGroup.Group("/agora") + { + agoraRoute.POST("/token/generate/rtc", HandleGenRTCToken) + agoraRoute.POST("/token/generate/rtm", HandleGenRTMToken) + } +} + +func HandleGenRTCToken(gCtx *gin.Context) { + // TODO code +} + +func HandleGenRTMToken(gCtx *gin.Context) { + // TODO code +} diff --git a/api/v1/cloud_storage/alibaba_cloud.go b/api/v1/cloud_storage/alibaba_cloud.go new file mode 100644 index 00000000..b6603ef1 --- /dev/null +++ b/api/v1/cloud_storage/alibaba_cloud.go @@ -0,0 +1,20 @@ +package cloudstorage + +import ( + "github.com/gin-gonic/gin" +) + +func HandleAlibabaCloudRemove(gCtx *gin.Context) { + // TODO code +} +func HandleAlibabaCloudRename(gCtx *gin.Context) { + // TODO code +} + +func HandleAlibabaCloudUploadStart(gCtx *gin.Context) { + // TODO code +} + +func HandleAlibabaCloudUploadFinish(gCtx *gin.Context) { + // TODO code +} diff --git a/api/v1/cloud_storage/cloud_storage.go b/api/v1/cloud_storage/cloud_storage.go new file mode 100644 index 00000000..42de34a5 --- /dev/null +++ b/api/v1/cloud_storage/cloud_storage.go @@ -0,0 +1,26 @@ +package cloudstorage + +import "github.com/gin-gonic/gin" + +func RegisterRoutes(storageGroup *gin.RouterGroup) { + + storageRoute := storageGroup.Group("/cloud-storage") + { + storageRoute.POST("/url-cloud/add", HandleUrlCloudAdd) + storageRoute.POST("/url-cloud/remove", HandleUrlCloudRemove) + storageRoute.POST("/url-cloud/rename", HandleUrlCloudRename) + + storageRoute.POST("/upload/cancel", HandleUploadCancel) + storageRoute.POST("/list", HandleList) + + storageRoute.POST("/convert/start", handleConvertStart) + storageRoute.POST("/convert/finish", handleConvertFinish) + + storageRoute.POST("/alibaba-cloud/upload/start", HandleAlibabaCloudUploadStart) + storageRoute.POST("/alibaba-cloud/upload/finish", HandleAlibabaCloudUploadFinish) + + storageRoute.POST("/alibaba-cloud/remove", HandleAlibabaCloudRemove) + storageRoute.POST("/alibaba-cloud/rename", HandleAlibabaCloudRename) + + } +} diff --git a/api/v1/cloud_storage/convert.go b/api/v1/cloud_storage/convert.go new file mode 100644 index 00000000..b3ae4bcf --- /dev/null +++ b/api/v1/cloud_storage/convert.go @@ -0,0 +1,13 @@ +package cloudstorage + +import ( + "github.com/gin-gonic/gin" +) + +func handleConvertStart(gCtx *gin.Context) { + // TODO code +} + +func handleConvertFinish(gCtx *gin.Context) { + // TODO code +} diff --git a/api/v1/cloud_storage/list.go b/api/v1/cloud_storage/list.go new file mode 100644 index 00000000..2e5ac1d6 --- /dev/null +++ b/api/v1/cloud_storage/list.go @@ -0,0 +1,27 @@ +package cloudstorage + +import ( + "github.com/gin-gonic/gin" + transportV1 "github.com/netless-io/flat-server/api/v1/transport" + "github.com/netless-io/flat-server/services" +) + +func HandleList(gCtx *gin.Context) { + + resp := transportV1.NewResp(gCtx) + defer resp.JSON() + + // userUUID:=resp.GetUserUUID() + ctx := resp.GetContext() + + // TODO test + userUUID := gCtx.Query("user_id") + + result, err := services.CloudStorageUserFileList(ctx, userUUID, 1, 50) + if err != nil { + resp.Err = err + return + } + + resp.Data = result +} diff --git a/api/v1/cloud_storage/upload_cancel.go b/api/v1/cloud_storage/upload_cancel.go new file mode 100644 index 00000000..8e0a0a01 --- /dev/null +++ b/api/v1/cloud_storage/upload_cancel.go @@ -0,0 +1,7 @@ +package cloudstorage + +import "github.com/gin-gonic/gin" + +func HandleUploadCancel(gCtx *gin.Context) { + // TODO code +} diff --git a/api/v1/cloud_storage/url_cloud.go b/api/v1/cloud_storage/url_cloud.go new file mode 100644 index 00000000..674776a4 --- /dev/null +++ b/api/v1/cloud_storage/url_cloud.go @@ -0,0 +1,15 @@ +package cloudstorage + +import "github.com/gin-gonic/gin" + +func HandleUrlCloudAdd(gCtx *gin.Context) { + // TODO code +} + +func HandleUrlCloudRemove(gCtx *gin.Context) { + // TODO code +} + +func HandleUrlCloudRename(gCtx *gin.Context) { + // TODO code +} diff --git a/api/v1/login/github.go b/api/v1/login/github.go new file mode 100644 index 00000000..68c32868 --- /dev/null +++ b/api/v1/login/github.go @@ -0,0 +1,7 @@ +package login + +import "github.com/gin-gonic/gin" + +func HandleGithubCallback(gCtx *gin.Context) { + // TODO code +} diff --git a/api/v1/login/login.go b/api/v1/login/login.go new file mode 100644 index 00000000..0401d1b0 --- /dev/null +++ b/api/v1/login/login.go @@ -0,0 +1,13 @@ +package login + +import "github.com/gin-gonic/gin" + +func RegisterRoutes(loginGroup *gin.RouterGroup) { + loginRoutes := loginGroup.Group("/login") + { + loginRoutes.GET("/github/callback", HandleGithubCallback) + + loginRoutes.GET("/weChat/web/callback", HandleWechatWebCallback) + loginRoutes.GET("/weChat/mobile/callback", HandleWechatMobileCallback) + } +} diff --git a/api/v1/login/wechat.go b/api/v1/login/wechat.go new file mode 100644 index 00000000..7e6f4590 --- /dev/null +++ b/api/v1/login/wechat.go @@ -0,0 +1,13 @@ +package login + +import ( + "github.com/gin-gonic/gin" +) + +func HandleWechatMobileCallback(gCtx *gin.Context) { + +} + +func HandleWechatWebCallback(gCtx *gin.Context) { + +} diff --git a/api/v1/room/room.go b/api/v1/room/room.go new file mode 100644 index 00000000..828937d8 --- /dev/null +++ b/api/v1/room/room.go @@ -0,0 +1,28 @@ +package room + +import "github.com/gin-gonic/gin" + +func RegisterRoutes(roomGroup *gin.RouterGroup) { + // TODO register route. handle func + roomRoute := roomGroup.Group("/room") + { + roomRoute.POST("/cancel/history") + roomRoute.POST("/cancel/ordinary") + roomRoute.POST("/cancel/periodic") + roomRoute.POST("/cancel/periodic-sub-room") + + roomRoute.POST("/create/ordinary") + roomRoute.POST("/create/periodic") + + roomRoute.POST("/info/ordinary") + roomRoute.POST("/info/periodic") + roomRoute.POST("/info/periodic-sub-room") + roomRoute.POST("/info/users") + + roomRoute.POST("/join") + roomRoute.POST("/list/:type") + + roomRoute.POST("/record/info") + + } +} diff --git a/api/v1/transport/transport.go b/api/v1/transport/transport.go new file mode 100644 index 00000000..372f63dc --- /dev/null +++ b/api/v1/transport/transport.go @@ -0,0 +1,119 @@ +package transport + +import ( + "context" + "net/http" + + "github.com/gin-gonic/gin" + "github.com/netless-io/flat-server/api/middleware" + "github.com/netless-io/flat-server/errors" +) + +type Status int + +const ( + NoLogin Status = -1 + Success = iota - 1 + Failed + Process + AuthFailed + NotMethod + NoRoute +) + +type Response struct { + gCtx *gin.Context + httpCode int + userUUID string + loginSource string + + Err error `json:"-"` + Status int `json:"status"` + Code int `json:"code,omitempty"` + Data interface{} `json:"data,omitempty"` +} + +func HandleNoMethod(gCtx *gin.Context) { + resp := new(Response) + resp.Status = NotMethod + defer resp.JSONWithHTTPCode(http.StatusMethodNotAllowed) +} + +func HandleNoRoute(gCtx *gin.Context) { + resp := new(Response) + resp.Status = NoRoute + defer resp.JSONWithHTTPCode(http.StatusNotFound) +} + +func NewResp(gCtx *gin.Context) *Response { + userUUID, loginSource := getUserIDWithLoginSource(gCtx) + + return &Response{ + gCtx: gCtx, + userUUID: userUUID, + loginSource: loginSource, + httpCode: http.StatusOK, + } +} + +func (resp *Response) GetUserUUID() string { + return resp.userUUID +} + +func (resp *Response) GetLoginSource() string { + return resp.loginSource +} + +func (resp *Response) GetContext() context.Context { + + ctx := context.Background() + + requestID, exists := resp.gCtx.Get("request_id") + if exists { + ctx = context.WithValue(ctx, "request_id", requestID) + return ctx + } + + return ctx +} + +func getUserIDWithLoginSource(gCtx *gin.Context) (string, string) { + userAuth, exists := gCtx.Get("user_auth") + if !exists { + return "", "" + } + + if userPayLoad, ok := userAuth.(middleware.UserPayLoad); ok { + return userPayLoad.UserID, userPayLoad.LoginSource + } + + return "", "" + +} + +func (resp *Response) JSONWithHTTPCode(httpCode int) { + resp.httpCode = httpCode + resp.JSON() +} + +func (resp *Response) JSON() { + defer resp.gCtx.JSON(resp.httpCode, resp) + + if resp.Err == nil { + return + } + + errCode, ok := resp.Err.(errors.Code) + if !ok { + resp.httpCode = http.StatusInternalServerError + errCode = errors.ServerFail + resp.Status = int(Failed) + resp.Data = nil + return + } + + if resp.Code = errCode.Code(); resp.Code != 0 { + resp.Data = nil + } + +} diff --git a/api/v1/v1.go b/api/v1/v1.go new file mode 100644 index 00000000..c13a8676 --- /dev/null +++ b/api/v1/v1.go @@ -0,0 +1,23 @@ +package v1 + +import ( + "github.com/gin-gonic/gin" + "github.com/netless-io/flat-server/api/v1/agora" + cloudstorage "github.com/netless-io/flat-server/api/v1/cloud_storage" + "github.com/netless-io/flat-server/api/v1/login" + "github.com/netless-io/flat-server/api/v1/room" + transportV1 "github.com/netless-io/flat-server/api/v1/transport" +) + +func RegisterAPIv1Routes(v1Group *gin.Engine) { + v1Group.NoMethod(transportV1.HandleNoMethod) + v1Group.NoRoute(transportV1.HandleNoRoute) + + apiV1 := v1Group.Group("/v1") + { + agora.RegisterRoutes(apiV1) + cloudstorage.RegisterRoutes(apiV1) + room.RegisterRoutes(apiV1) + login.RegisterRoutes(apiV1) + } +} diff --git a/dao/cloud_storage_configs.go b/dao/cloud_storage_configs.go new file mode 100644 index 00000000..fb4b9300 --- /dev/null +++ b/dao/cloud_storage_configs.go @@ -0,0 +1,33 @@ +package dao + +import ( + "context" + + "github.com/netless-io/flat-server/model" + "gorm.io/gorm" +) + +type CloudStorageConfigMgr struct { + db *gorm.DB +} + +func NewCloudStorageConfigMgr(db *gorm.DB) *CloudStorageConfigMgr { + + return &CloudStorageConfigMgr{db: db} +} + +func (c *CloudStorageConfigMgr) GetTableName() string { + return "cloud_storage_configs" +} + +func (c *CloudStorageConfigMgr) FindOne(ctx context.Context, userUUID string) (result model.CloudStorageConfigs, err error) { + err = c.db.WithContext(ctx).Model(&model.CloudStorageConfigs{}). + Where( + &model.CloudStorageConfigs{ + UserUUID: userUUID, + IsDelete: 0, + }, + ). + Find(&result).Error + return +} diff --git a/dao/cloud_storage_user_files.go b/dao/cloud_storage_user_files.go new file mode 100644 index 00000000..67747601 --- /dev/null +++ b/dao/cloud_storage_user_files.go @@ -0,0 +1,51 @@ +package dao + +import ( + "context" + + "github.com/netless-io/flat-server/model" + "gorm.io/gorm" +) + +type CloudStorageUserFilesMgr struct { + db *gorm.DB +} + +func NewCloudStorageUserFilesMgr(db *gorm.DB) *CloudStorageUserFilesMgr { + + return &CloudStorageUserFilesMgr{db: db} +} + +func (c *CloudStorageUserFilesMgr) FindUserStorageFiles(ctx context.Context, userUUID string, limit, offset int) (result []model.CloudStorageFiles, err error) { + err = c.db.WithContext(ctx).Model(&model.CloudStorageUserFiles{}). + Where( + &model.CloudStorageUserFiles{ + UserUUID: userUUID, + IsDelete: 0, + }, + ).Joins(" INNER JOIN cloud_storage_files csf ON cloud_storage_user_files.file_uuid = csf.file_uuid"). + Offset(offset). + Limit(limit). + Scan(&result).Error + return +} + +func FindUserStorageUserFile(ctx context.Context, userUUID string, limit, offset int) ([]model.CloudStorageFiles, int, error) { + + userStorageInfo, err := cloudStorageConfigModel.FindOne(ctx, userUUID) + if err != nil { + return nil, 0, err + } + + if userStorageInfo.TotalUsage == 0 { + return nil, 0, nil + } + + cloudStorageConfigs, err := cloudStorageUserFilesModel.FindUserStorageFiles(ctx, userUUID, limit, offset) + if err != nil { + return nil, 0, err + } + + return cloudStorageConfigs, int(userStorageInfo.TotalUsage), nil + +} diff --git a/dao/dao.go b/dao/dao.go index 07a0cc0f..2e223ca8 100644 --- a/dao/dao.go +++ b/dao/dao.go @@ -1 +1,26 @@ package dao + +import ( + "github.com/netless-io/flat-server/conf" + "github.com/netless-io/flat-server/model" +) + +var ( + cloudStorageUserFilesModel *CloudStorageUserFilesMgr + cloudStorageConfigModel *CloudStorageConfigMgr +) + +func RegistryModel(sqlConf conf.MySQLConf) error { + + dbConn, err := model.OpenDBConn(sqlConf) + if err != nil { + return err + } + + cloudStorageUserFilesModel = NewCloudStorageUserFilesMgr(dbConn) + cloudStorageConfigModel = NewCloudStorageConfigMgr(dbConn) + + // TODO register other model ... + return nil + +} diff --git a/errors/code.go b/errors/code.go new file mode 100644 index 00000000..2550b86f --- /dev/null +++ b/errors/code.go @@ -0,0 +1,53 @@ +package errors + +const ( + ParamsCheckFailed Code = iota + 100000 // parameter verification failed + ServerFail // server fail (retry) + CurrentProcessFailed // current processing failed + NotPermission // insufficient permissions + NeedLoginAgain // user need login in again + UnsupportedPlatform // Unsupported login platform + JWTSignFailed // jwt sign failed +) + +const ( + RoomNotFound Code = iota + 200000 // room not found + RoomIsEnded // room has been ended + RoomIsRunning // room status is running + RoomNotIsRunning // room not is running + RoomNotIsEnded // room not is stopped + RoomNotIsIdle // room not is idle +) + +const ( + PeriodicNotFound Code = iota + 300000 // room not found + PeriodicIsEnded // room has been ended + PeriodicSubRoomHasRunning // periodic sub room has running +) + +const ( + UserNotFound Code = 400000 // user not found + + RecordNotFound Code = 500000 // record info not found +) + +const ( + UploadConcurrentLimit Code = iota + 700000 + NotEnoughTotalUsage // not enough total usage + FileSizeTooBig // single file size too big + FileNotFound // file info not found + FileExists // file already exists +) + +const ( + FileIsConverted Code = 800000 + FileConvertFailed // file convert failed + FileIsConverting // file is converting + FileIsConvertWaiting // file convert is in waiting status +) + +const ( + LoginGithubSuspended Code = iota + 900000 // https://docs.github.com/en/developers/apps/troubleshooting-authorization-request-errors + LoginGithubURLMismatch + LoginGithubAccessDenied +) diff --git a/errors/errors.go b/errors/errors.go index 04b32187..95d9b486 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -1 +1,14 @@ package errors + +import "strconv" + +type Code int + +func (e Code) Code() int { + return int(e) +} + +func (e Code) Error() string { + + return strconv.Itoa(int(e)) +} diff --git a/logger/db_logger.go b/logger/db_logger.go new file mode 100644 index 00000000..48547f63 --- /dev/null +++ b/logger/db_logger.go @@ -0,0 +1,52 @@ +package logger + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/netless-io/flat-server/pkg/utils" + "go.uber.org/zap" + "gorm.io/gorm" + ormLogger "gorm.io/gorm/logger" +) + +type DBLogger struct { +} + +func NewDBLogger() ormLogger.Interface { + + return new(DBLogger) +} + +func (d *DBLogger) LogMode(level ormLogger.LogLevel) ormLogger.Interface { + + return d +} + +func (d *DBLogger) Info(ctx context.Context, k string, v ...interface{}) { + logger.Infow(fmt.Sprintf(k, v...), utils.GetCtxField(ctx)) +} + +func (d *DBLogger) Warn(ctx context.Context, k string, v ...interface{}) { + logger.Warnw(fmt.Sprintf(k, v...), utils.GetCtxField(ctx)) +} + +func (d *DBLogger) Error(ctx context.Context, k string, v ...interface{}) { + logger.Errorw(fmt.Sprintf(k, v...), utils.GetCtxField(ctx)) +} + +func (d *DBLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) { + sql, rowsAffected := fc() + + elapsed := time.Since(begin) + + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + logger.Errorw(err.Error(), utils.GetCtxField(ctx), zap.String("sql", sql), zap.String("expenditure", elapsed.String()), zap.Int64("rowsAffected", rowsAffected)) + return + } + + logger.Debugw("success", utils.GetCtxField(ctx), zap.String("sql", sql), zap.String("expenditure", elapsed.String()), zap.Int64("rowsAffected", rowsAffected)) + +} diff --git a/logger/trace_log.go b/logger/trace_log.go deleted file mode 100644 index e6a79971..00000000 --- a/logger/trace_log.go +++ /dev/null @@ -1,73 +0,0 @@ -package logger - -import ( - "go.uber.org/zap" -) - -type TraceLog struct { - requestID string - l *zap.SugaredLogger -} - -func NewTraceLog(requestID string) *TraceLog { - - tlog := &TraceLog{ - requestID: requestID, - } - - // copy new logger with key - tlog.l = logger.With(zap.String("request_id", tlog.requestID)) - return tlog -} - -func (t *TraceLog) Debug(args ...interface{}) { - t.l.Debug(args...) -} - -func (t *TraceLog) Debugf(template string, args ...interface{}) { - t.l.Debugf(template, args...) - -} - -func (t *TraceLog) Debugw(template string, args ...interface{}) { - t.l.Debugw(template, args...) - -} - -func (t *TraceLog) Info(args ...interface{}) { - t.l.Info(args...) -} - -func (t *TraceLog) Infof(template string, args ...interface{}) { - t.l.Infof(template, args...) - -} - -func (t *TraceLog) Infow(template string, args ...interface{}) { - t.l.Infow(template, args...) - -} - -func (t *TraceLog) Warn(args ...interface{}) { - t.l.Warn(args...) -} - -func (t *TraceLog) Warnf(template string, args ...interface{}) { - t.l.Warnf(template, args...) -} - -func (t *TraceLog) Warnw(template string, args ...interface{}) { - t.l.Warnw(template, args...) -} - -func (t *TraceLog) Error(args ...interface{}) { - t.l.Error(args...) -} - -func (t *TraceLog) Errorf(template string, args ...interface{}) { - t.l.Errorf(template, args...) -} - -func (t *TraceLog) Errorw(template string, args ...interface{}) { - t.l.Errorw(template, args...) -} diff --git a/main.go b/main.go index 3b536530..09ca80cd 100644 --- a/main.go +++ b/main.go @@ -9,6 +9,7 @@ import ( "github.com/netless-io/flat-server/api" "github.com/netless-io/flat-server/conf" + "github.com/netless-io/flat-server/dao" "github.com/netless-io/flat-server/internal" "github.com/netless-io/flat-server/logger" ) @@ -34,6 +35,12 @@ func main() { log.Fatal(err) } + err := dao.RegistryModel(conf.Mysql()) + if err != nil { + logger.Error(err) + return + } + app := api.New(internal.ENV) port := strconv.Itoa(conf.ServerPort()) diff --git a/model/cloud_storage_configs.go b/model/cloud_storage_configs.go index 7647de24..89372db0 100644 --- a/model/cloud_storage_configs.go +++ b/model/cloud_storage_configs.go @@ -25,7 +25,7 @@ var CloudStorageConfigsColumns = struct { // CloudStorageConfigs [...] type CloudStorageConfigs struct { - ID int64 `gorm:"primaryKey;column:id;type:bigint(20);not null"` + ID int64 `gorm:"primaryKey;column:id;type:bigint(20);not null" ` CreatedAt time.Time `gorm:"column:created_at;type:datetime(3);not null;default:CURRENT_TIMESTAMP(3)"` UpdatedAt time.Time `gorm:"column:updated_at;type:datetime(3);not null;default:CURRENT_TIMESTAMP(3)"` Version int `gorm:"column:version;type:int(11);not null"` diff --git a/model/cloud_storage_files.go b/model/cloud_storage_files.go index 4aae261e..041b7780 100644 --- a/model/cloud_storage_files.go +++ b/model/cloud_storage_files.go @@ -37,19 +37,19 @@ var CloudStorageFilesColumns = struct { // CloudStorageFiles [...] type CloudStorageFiles struct { - ID int64 `gorm:"primaryKey;column:id;type:bigint(20);not null"` - CreatedAt time.Time `gorm:"column:created_at;type:datetime(3);not null;default:CURRENT_TIMESTAMP(3)"` - UpdatedAt time.Time `gorm:"column:updated_at;type:datetime(3);not null;default:CURRENT_TIMESTAMP(3)"` - Version int `gorm:"column:version;type:int(11);not null"` - FileUUID string `gorm:"unique;column:file_uuid;type:varchar(40);not null"` - FileName string `gorm:"column:file_name;type:varchar(128);not null"` // file name - FileSize uint32 `gorm:"column:file_size;type:int(10) unsigned;not null"` // file size (bytes) - FileURL string `gorm:"column:file_url;type:varchar(256);not null"` // file url - ConvertStep string `gorm:"column:convert_step;type:enum('None','Converting','Done','Failed');not null;default:None"` - TaskUUID string `gorm:"column:task_uuid;type:varchar(40);not null;default:''"` // netless conversion task uuid v1 - TaskToken string `gorm:"column:task_token;type:varchar(256);not null;default:''"` // generated from sdk token and task uuid - IsDelete int8 `gorm:"index:cloud_storage_files_is_delete_index;column:is_delete;type:tinyint(4);not null;default:0"` - Region string `gorm:"column:region;type:enum('cn-hz','us-sv','sg','in-mum','gb-lon','none');not null"` + ID int64 `gorm:"primaryKey;column:id;type:bigint(20);not null" json:"-"` + CreatedAt time.Time `gorm:"column:created_at;type:datetime(3);not null;default:CURRENT_TIMESTAMP(3)" json:"createdAt"` + UpdatedAt time.Time `gorm:"column:updated_at;type:datetime(3);not null;default:CURRENT_TIMESTAMP(3)" json:"-"` + Version int `gorm:"column:version;type:int(11);not null" json:"-"` + FileUUID string `gorm:"unique;column:file_uuid;type:varchar(40);not null" json:"fileUUID"` + FileName string `gorm:"column:file_name;type:varchar(128);not null" json:"fileName"` // file name + FileSize uint32 `gorm:"column:file_size;type:int(10) unsigned;not null" json:"fileSize"` // file size (bytes) + FileURL string `gorm:"column:file_url;type:varchar(256);not null" json:"fileURL"` // file url + ConvertStep string `gorm:"column:convert_step;type:enum('None','Converting','Done','Failed');not null;default:None" json:"convertStep"` + TaskUUID string `gorm:"column:task_uuid;type:varchar(40);not null;default:''" json:"taskUUID"` // netless conversion task uuid v1 + TaskToken string `gorm:"column:task_token;type:varchar(256);not null;default:''" json:"taskToken"` // generated from sdk token and task uuid + IsDelete int8 `gorm:"index:cloud_storage_files_is_delete_index;column:is_delete;type:tinyint(4);not null;default:0" json:"-"` + Region string `gorm:"column:region;type:enum('cn-hz','us-sv','sg','in-mum','gb-lon','none');not null" json:"region"` } // TableName get sql table name. diff --git a/model/model.go b/model/model.go index 49758df5..8fce2d43 100644 --- a/model/model.go +++ b/model/model.go @@ -5,6 +5,7 @@ import ( "time" "github.com/netless-io/flat-server/conf" + "github.com/netless-io/flat-server/logger" "gorm.io/driver/mysql" "gorm.io/gorm" ) @@ -15,18 +16,14 @@ const ( defaultMaxIdleConns = 10 ) -var ( - dbClient *gorm.DB -) - -func OpenDBConn(conf conf.MySQLConf) error { +func OpenDBConn(conf conf.MySQLConf) (*gorm.DB, error) { var ( err error ) dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?parseTime=true&charset=utf8mb4", conf.Username, conf.Password, conf.Host, conf.Port, conf.Name) - dbClient, err = gorm.Open(mysql.New(mysql.Config{ + dbConn, err := gorm.Open(mysql.New(mysql.Config{ // data source name, refer https://github.com/go-sql-driver/mysql#dsn-data-source-name DSN: dsn, @@ -42,16 +39,18 @@ func OpenDBConn(conf conf.MySQLConf) error { // smart configure based on used version SkipInitializeWithVersion: false, - }), &gorm.Config{}) + }), &gorm.Config{ + Logger: logger.NewDBLogger(), + }) if err != nil { - return err + return nil, err } // see https://github.com/go-sql-driver/mysql - sqlConn, err := dbClient.DB() + sqlConn, err := dbConn.DB() if err != nil { - return err + return nil, err } var ( @@ -74,5 +73,5 @@ func OpenDBConn(conf conf.MySQLConf) error { sqlConn.SetMaxOpenConns(maxOpenConns) sqlConn.SetMaxIdleConns(maxIdleConns) - return nil + return dbConn, nil } diff --git a/model/model_test.go b/model/model_test.go index 634fd078..c1e555d2 100644 --- a/model/model_test.go +++ b/model/model_test.go @@ -4,6 +4,11 @@ import ( "testing" "github.com/netless-io/flat-server/conf" + "gorm.io/gorm" +) + +var ( + dbConn *gorm.DB ) func init() { @@ -12,7 +17,7 @@ func init() { panic(err) } - err = OpenDBConn(conf.Mysql()) + dbConn, err = OpenDBConn(conf.Mysql()) if err != nil { panic(err) } @@ -21,7 +26,7 @@ func init() { func TestSelectUserLimit(t *testing.T) { var users []Users - err := dbClient.Find(&users).Limit(10).Offset(1).Error + err := dbConn.Find(&users).Limit(10).Offset(1).Error if err != nil { t.Fatal(err) } diff --git a/pkg/cache/redis/redis.go b/pkg/cache/redis/redis.go index 136a44dc..4eba0529 100644 --- a/pkg/cache/redis/redis.go +++ b/pkg/cache/redis/redis.go @@ -25,29 +25,27 @@ func OpenConn(conf conf.RedisConf) error { return redisClient.Ping(context.TODO()).Err() } -func Set(key, value string, expire time.Duration) error { +func Set(ctx context.Context, key, value string, expire time.Duration) error { if expire <= 0 { expire = -1 } - return redisClient.Set(context.TODO(), key, value, expire).Err() + return redisClient.Set(ctx, key, value, expire).Err() } -func Get(key string) (string, error) { - return redisClient.Get(context.TODO(), key).Result() +func Get(ctx context.Context, key string) (string, error) { + return redisClient.Get(ctx, key).Result() } -func Del(keys ...string) error { - return redisClient.Del(context.TODO(), keys...).Err() +func Del(ctx context.Context, keys ...string) error { + return redisClient.Del(ctx, keys...).Err() } -func HMSet(key string, value map[string]string, expire time.Duration) error { +func HMSet(ctx context.Context, key string, value map[string]string, expire time.Duration) error { if expire <= 0 { expire = -1 } - ctx := context.TODO() - err := redisClient.HMSet(ctx, key, value).Err() if err != nil { return err @@ -56,16 +54,15 @@ func HMSet(key string, value map[string]string, expire time.Duration) error { return redisClient.Expire(ctx, key, expire).Err() } -func HMGetWithField(key string, field ...string) (interface{}, error) { - return redisClient.HMGet(context.TODO(), key, field...).Result() +func HMGetWithField(ctx context.Context, key string, field ...string) (interface{}, error) { + return redisClient.HMGet(ctx, key, field...).Result() } -func MGet(keys ...string) (interface{}, error) { - return redisClient.MGet(context.TODO(), keys...).Result() +func MGet(ctx context.Context, keys ...string) (interface{}, error) { + return redisClient.MGet(ctx, keys...).Result() } -func Scan(match string, count int64) ([]string, error) { - ctx := context.TODO() +func Scan(ctx context.Context, match string, count int64) ([]string, error) { result := make([]string, 0) @@ -81,8 +78,8 @@ func Scan(match string, count int64) ([]string, error) { return result, nil } -func VacantKey(keys ...string) ([]string, error) { - sliceCmd := redisClient.MGet(context.TODO(), keys...) +func VacantKey(ctx context.Context, keys ...string) ([]string, error) { + sliceCmd := redisClient.MGet(ctx, keys...) if err := sliceCmd.Err(); err != nil { if err == redis.Nil { return keys, nil diff --git a/pkg/cache/redis/redis_test.go b/pkg/cache/redis/redis_test.go index 5a30bd38..3c535d52 100644 --- a/pkg/cache/redis/redis_test.go +++ b/pkg/cache/redis/redis_test.go @@ -1,6 +1,7 @@ package redis import ( + "context" "strings" "testing" @@ -20,7 +21,7 @@ func init() { } func TestScan(t *testing.T) { - res, err := Scan("agora:rtm:userUUID*", 100) + res, err := Scan(context.TODO(), "agora:rtm:userUUID*", 100) if err != nil { t.Fatal(err) } @@ -29,7 +30,7 @@ func TestScan(t *testing.T) { func TestVacantKey(t *testing.T) { k := []string{"room:invite:123123", "room:invite:123123-null", "agora:rtc:room:uuid:uid:123123", "agora:rtc:room:uuid:uid:123123-null"} - res, err := VacantKey(k...) + res, err := VacantKey(context.TODO(), k...) if err != nil { t.Fatal(err) } diff --git a/pkg/utils/context.go b/pkg/utils/context.go new file mode 100644 index 00000000..e3cd25a2 --- /dev/null +++ b/pkg/utils/context.go @@ -0,0 +1,12 @@ +package utils + +import ( + "context" + + "go.uber.org/zap" +) + +func GetCtxField(ctx context.Context) zap.Field { + requestID := ctx.Value("request_id").(string) + return zap.String("request_id", requestID) +} diff --git a/pkg/utils/units.go b/pkg/utils/units.go index f0e79293..feb8bff0 100644 --- a/pkg/utils/units.go +++ b/pkg/utils/units.go @@ -3,8 +3,11 @@ package utils // Binary size units // See: http://en.wikipedia.org/wiki/Binary_prefix const ( - KiB = 1024 - MiB = 1024 * KiB - GiB = 1024 * MiB - TiB = 1024 * GiB + _ = iota // ignore first value by assigning to blank identifier + KiB = 1 << (10 * iota) // 1 << (10*1) + MiB // 1 << (10*2) + GiB // 1 << (10*3) + TiB // 1 << (10*4) + PiB // 1 << (10*5) + ) diff --git a/services/cloud_storage_configs.go b/services/cloud_storage_configs.go new file mode 100644 index 00000000..546f657e --- /dev/null +++ b/services/cloud_storage_configs.go @@ -0,0 +1,32 @@ +package services + +import ( + "context" + + "github.com/netless-io/flat-server/dao" + errCode "github.com/netless-io/flat-server/errors" + "github.com/netless-io/flat-server/logger" + "github.com/netless-io/flat-server/model" + "github.com/netless-io/flat-server/pkg/utils" +) + +type ListStorageConfig struct { + TotalUsage int `json:"totalUsage"` + Files []model.CloudStorageFiles `json:"files,omitempty"` +} + +func CloudStorageUserFileList(ctx context.Context, userUUID string, limit, offset int) (ListStorageConfig, error) { + + logger.Infow("list cloud storage file", utils.GetCtxField(ctx)) + + files, totalUsage, err := dao.FindUserStorageUserFile(ctx, userUUID, limit, offset) + if err != nil { + logger.Errorw(err.Error(), utils.GetCtxField(ctx)) + return ListStorageConfig{}, errCode.ServerFail + } + + return ListStorageConfig{ + TotalUsage: totalUsage, + Files: files, + }, nil +}