Files
oneterm/backend/internal/api/controller/share.go

175 lines
5.1 KiB
Go

package controller
import (
"context"
"fmt"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/samber/lo"
"github.com/spf13/cast"
"gorm.io/gorm"
"github.com/veops/oneterm/internal/acl"
"github.com/veops/oneterm/internal/model"
dbpkg "github.com/veops/oneterm/pkg/db"
)
// CreateShare godoc
//
// @Tags share
// @Param share body []model.Share true "share"
// @Success 200 {object} HttpResponse{data=ListData{list=[]string}}
// @Router /share [post]
func (c *Controller) CreateShare(ctx *gin.Context) {
currentUser, _ := acl.GetSessionFromCtx(ctx)
shares := make([]*model.Share, 0)
if err := ctx.ShouldBindBodyWithJSON(&shares); err != nil {
ctx.AbortWithError(http.StatusBadRequest, &ApiError{Code: ErrInvalidArgument, Data: map[string]any{"err": err}})
return
}
for _, s := range shares {
if !hasPermShare(ctx, s, acl.GRANT) {
ctx.AbortWithError(http.StatusForbidden, &ApiError{Code: ErrNoPerm, Data: map[string]any{"perm": acl.GRANT}})
return
}
s.CreatorId = currentUser.GetUid()
s.UpdaterId = currentUser.GetUid()
}
uuids := lo.Map(shares, func(s *model.Share, _ int) string {
s.Uuid = uuid.New().String()
return s.Uuid
})
if err := dbpkg.DB.Create(&shares).Error; err != nil {
ctx.AbortWithError(http.StatusInternalServerError, &ApiError{Code: ErrInternal, Data: map[string]any{"err": err}})
return
}
ctx.JSON(http.StatusOK, toListData(uuids))
}
// DeleteShare godoc
//
// @Tags share
// @Param id path int true "share id"
// @Success 200 {object} HttpResponse
// @Router /share/:id [delete]
func (c *Controller) DeleteShare(ctx *gin.Context) {
share := &model.Share{
Id: cast.ToInt(ctx.Param("id")),
}
if err := dbpkg.DB.Model(share).Where("id=?", share.Id).First(share); err != nil {
ctx.AbortWithError(http.StatusBadRequest, &ApiError{Code: ErrInvalidArgument, Data: map[string]any{"err": err}})
return
}
if !hasPermShare(ctx, share, acl.GRANT) {
ctx.AbortWithError(http.StatusForbidden, &ApiError{Code: ErrNoPerm, Data: map[string]any{"perm": acl.GRANT}})
return
}
doDelete(ctx, false, &model.Share{}, "")
}
// GetShare godoc
//
// @Tags share
// @Param page_index query int true "page_index"
// @Param page_size query int true "page_size"
// @Param search query string false "name or ip"
// @Param start query string false "start, RFC3339"
// @Param end query string false "end, RFC3339"
// @Param asset_id query string false "asset id"
// @Param account_id query string false "account id"
// @Success 200 {object} HttpResponse{data=ListData{list=[]model.Share}}
// @Router /share [get]
func (c *Controller) GetShare(ctx *gin.Context) {
currentUser, _ := acl.GetSessionFromCtx(ctx)
db := dbpkg.DB.Model(&model.Share{})
db = filterSearch(ctx, db)
db, err := filterStartEnd(ctx, db)
if err != nil {
return
}
db = filterEqual(ctx, db, "asset_id", "account_id")
if !acl.IsAdmin(currentUser) {
_, assetIds, accountIds, err := getNodeAssetAccoutIdsByAction(ctx, acl.GRANT)
if err != nil {
return
}
db = db.Where("asset_id IN (?) OR account_id IN (?)", assetIds, accountIds)
}
doGet[*model.Share](ctx, false, db, "")
}
// ConnectShare godoc
//
// @Tags share
// @Success 200 {object} HttpResponse
// @Param w query int false "width"
// @Param h query int false "height"
// @Param dpi query int false "dpi"
// @Success 200 {object} HttpResponse{}
// @Router /share/connect/:uuid [get]
func (c *Controller) ConnectShare(ctx *gin.Context) {
share := &model.Share{}
if err := dbpkg.DB.Transaction(func(tx *gorm.DB) (err error) {
if err = tx.Where("uuid=?", ctx.Param("uuid")).First(share).Error; err != nil {
return
}
now := time.Now()
if now.Before(share.Start) || now.After(share.End) {
err = fmt.Errorf("share expired or not started")
return
}
if share.NoLimit {
return
}
db := tx.Model(share).Where("uuid=? AND times>0", share.Uuid).Update("times", gorm.Expr("times-?", 1))
if db.Error != nil {
return
}
if db.RowsAffected != 1 {
err = fmt.Errorf("no times left")
return
}
return
}); err != nil {
ctx.Set("shareErr", &ApiError{Code: ErrInvalidArgument, Data: map[string]any{"err": err}})
}
ctx.Params = lo.Filter(ctx.Params, func(p gin.Param, _ int) bool {
return !lo.Contains([]string{"account_id", "asset_id", "protocol"}, p.Key)
})
ctx.Params = append(ctx.Params, gin.Param{Key: "account_id", Value: cast.ToString(share.AccountId)})
ctx.Params = append(ctx.Params, gin.Param{Key: "asset_id", Value: cast.ToString(share.AssetId)})
ctx.Params = append(ctx.Params, gin.Param{Key: "protocol", Value: cast.ToString(share.Protocol)})
ctx.Set("shareId", share.Id)
ctx.Set("session", &acl.Session{})
ctx.Set("shareEnd", share.End)
c.Connect(ctx)
}
func hasPermShare(ctx context.Context, share *model.Share, action string) (ok bool) {
currentUser, _ := acl.GetSessionFromCtx(ctx)
if ok = acl.IsAdmin(currentUser); ok {
return true
}
_, assetIds, accountIds, err := getNodeAssetAccoutIdsByAction(ctx, action)
if err != nil {
return
}
return lo.Contains(assetIds, share.AssetId) || lo.Contains(accountIds, share.AccountId)
}