✨ Pay for upload
This commit is contained in:
		| @@ -32,8 +32,8 @@ func createAttachmentDirectly(c *fiber.Ctx) error { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if !user.HasPermNode("CreateAttachments", file.Size) { | ||||
| 		return fiber.NewError(fiber.StatusForbidden, "you are not permitted to create attachments like this large") | ||||
| 	if !user.HasPermNode("CreateAttachments", true) { | ||||
| 		return fiber.NewError(fiber.StatusForbidden, "you are not permitted to create attachments") | ||||
| 	} else if pool.Config.Data().MaxFileSize != nil && file.Size > *pool.Config.Data().MaxFileSize { | ||||
| 		return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("attachment pool %s doesn't allow file larger than %d", pool.Alias, *pool.Config.Data().MaxFileSize)) | ||||
| 	} | ||||
| @@ -62,6 +62,13 @@ func createAttachmentDirectly(c *fiber.Ctx) error { | ||||
| 		return fiber.NewError(fiber.StatusBadRequest, err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	// If pool has no belongs to, it means it is shared pool, apply shared attachment discount | ||||
| 	withDiscount := pool.AccountID == nil | ||||
| 	if err := services.PlaceOrder(user.ID, file.Size, withDiscount); err != nil { | ||||
| 		tx.Rollback() | ||||
| 		return fiber.NewError(fiber.StatusPaymentRequired, err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	tx.Commit() | ||||
|  | ||||
| 	metadata.Pool = &pool | ||||
|   | ||||
| @@ -41,13 +41,15 @@ func createAttachmentFragment(c *fiber.Ctx) error { | ||||
| 		return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("unable to get attachment pool info: %v", err)) | ||||
| 	} | ||||
|  | ||||
| 	if !user.HasPermNode("CreateAttachments", data.Size) { | ||||
| 		return fiber.NewError(fiber.StatusForbidden, "you are not permitted to create attachments like this large") | ||||
| 	if !user.HasPermNode("CreateAttachments", true) { | ||||
| 		return fiber.NewError(fiber.StatusForbidden, "you are not permitted to create attachments") | ||||
| 	} else if pool.Config.Data().MaxFileSize != nil && *pool.Config.Data().MaxFileSize > data.Size { | ||||
| 		return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("attachment pool %s doesn't allow file larger than %d", pool.Alias, *pool.Config.Data().MaxFileSize)) | ||||
| 	} | ||||
|  | ||||
| 	metadata, err := services.NewAttachmentFragment(database.C, user, models.AttachmentFragment{ | ||||
| 	tx := database.C.Begin() | ||||
|  | ||||
| 	metadata, err := services.NewAttachmentFragment(tx, user, models.AttachmentFragment{ | ||||
| 		Name:        data.FileName, | ||||
| 		Size:        data.Size, | ||||
| 		Alternative: data.Alternative, | ||||
| @@ -63,6 +65,15 @@ func createAttachmentFragment(c *fiber.Ctx) error { | ||||
| 		metadata.FileChunksMissing = services.FindFragmentMissingChunks(metadata) | ||||
| 	} | ||||
|  | ||||
| 	// If pool has no belongs to, it means it is shared pool, apply shared attachment discount | ||||
| 	withDiscount := pool.AccountID == nil | ||||
| 	if err := services.PlaceOrder(user.ID, data.Size, withDiscount); err != nil { | ||||
| 		tx.Rollback() | ||||
| 		return fiber.NewError(fiber.StatusPaymentRequired, err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	tx.Commit() | ||||
|  | ||||
| 	return c.JSON(fiber.Map{ | ||||
| 		"chunk_size":  viper.GetInt64("performance.file_chunk_size"), | ||||
| 		"chunk_count": len(metadata.FileChunks), | ||||
|   | ||||
							
								
								
									
										60
									
								
								pkg/internal/services/payment.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								pkg/internal/services/payment.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| package services | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"time" | ||||
|  | ||||
| 	"git.solsynth.dev/hypernet/paperclip/pkg/internal/gap" | ||||
| 	wproto "git.solsynth.dev/hypernet/wallet/pkg/proto" | ||||
| 	"github.com/rs/zerolog/log" | ||||
| 	"github.com/samber/lo" | ||||
| ) | ||||
|  | ||||
| const DiscountFileSize = 52428800 // 50 MiB | ||||
|  | ||||
| // PlaceOrder create a transaction if needed for user | ||||
| // Pricing according here: https://kb.solsynth.dev/solar-network/wallet#file-uploads | ||||
| func PlaceOrder(user uint, filesize int64, withDiscount bool) error { | ||||
| 	if filesize <= DiscountFileSize && withDiscount { | ||||
| 		// Discount included | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	var amount float64 | ||||
| 	if withDiscount { | ||||
| 		billableSize := filesize - DiscountFileSize | ||||
| 		amount = float64(billableSize) / 1024 / 1024 * 1 | ||||
| 	} else if filesize > DiscountFileSize { | ||||
| 		amount = 50 + float64(filesize-DiscountFileSize)/1024/1024*5 | ||||
| 	} else { | ||||
| 		amount = float64(filesize) / 1024 / 1024 * 1 | ||||
| 	} | ||||
|  | ||||
| 	if !withDiscount { | ||||
| 		amount += 10 // Service fee | ||||
| 	} | ||||
|  | ||||
| 	conn, err := gap.Nx.GetClientGrpcConn("wa") | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to connect wallet: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	wc := wproto.NewPaymentServiceClient(conn) | ||||
| 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) | ||||
| 	defer cancel() | ||||
| 	resp, err := wc.MakeTransactionWithAccount(ctx, &wproto.MakeTransactionWithAccountRequest{ | ||||
| 		PayerAccountId: lo.ToPtr(uint64(user)), | ||||
| 		Amount:         amount, | ||||
| 		Remark:         "File Uploading Fee", | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	log.Info(). | ||||
| 		Uint64("transaction", resp.Id).Float64("amount", amount).Bool("discount", withDiscount). | ||||
| 		Msg("Order placed for charge file uploading fee...") | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
		Reference in New Issue
	
	Block a user