Skip to content

Commit

Permalink
refactor: get version patch path
Browse files Browse the repository at this point in the history
  • Loading branch information
dongwlin committed Jan 17, 2025
1 parent c56e1fc commit fdb30f2
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 103 deletions.
159 changes: 75 additions & 84 deletions internal/handler/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"time"

Expand All @@ -19,7 +18,6 @@ import (
"github.com/MirrorChyan/resource-backend/internal/handler/response"
"github.com/MirrorChyan/resource-backend/internal/logic"
. "github.com/MirrorChyan/resource-backend/internal/model"
"github.com/MirrorChyan/resource-backend/internal/patcher"
"github.com/gofiber/fiber/v2"
"github.com/segmentio/ksuid"
"go.uber.org/zap"
Expand Down Expand Up @@ -333,8 +331,7 @@ func (h *VersionHandler) ValidateCDK(cdk, spId, source string) (bool, error) {
}

func (h *VersionHandler) GetLatest(c *fiber.Ctx) error {
// equal resId
resourceName := c.Params(resourceKey)
resID := c.Params(resourceKey)

req := &GetLatestVersionRequest{}
if err := c.QueryParser(req); err != nil {
Expand All @@ -347,7 +344,7 @@ func (h *VersionHandler) GetLatest(c *fiber.Ctx) error {

var ctx = c.UserContext()

latest, err := h.versionLogic.GetLatest(ctx, resourceName)
latest, err := h.versionLogic.GetLatest(ctx, resID)

if err != nil {

Expand All @@ -363,16 +360,17 @@ func (h *VersionHandler) GetLatest(c *fiber.Ctx) error {
JSON(response.UnexpectedError())
}

resp := QueryLatestResponseData{
data := QueryLatestResponseData{
VersionName: latest.Name,
VersionNumber: latest.Number,
}

if req.CDK == "" {
return c.Status(fiber.StatusOK).JSON(response.Success(resp, "current resource latest version is "+latest.Name))
resp := response.Success(data, "current resource latest version is "+latest.Name)
return c.Status(fiber.StatusOK).JSON(resp)
}

if isFirstBind, err := h.ValidateCDK(req.CDK, req.SpID, resourceName); err != nil {
if isFirstBind, err := h.ValidateCDK(req.CDK, req.SpID, resID); err != nil {
var e RemoteError
switch {
case errors.Is(err, CdkNotfound):
Expand All @@ -391,7 +389,7 @@ func (h *VersionHandler) GetLatest(c *fiber.Ctx) error {
} else if isFirstBind {
request := BillingCheckinRequest{
CDK: req.CDK,
Application: resourceName,
Application: resID,
UserAgent: req.UserAgent,
}
body, err := json.Marshal(request)
Expand All @@ -405,23 +403,55 @@ func (h *VersionHandler) GetLatest(c *fiber.Ctx) error {
}

if latest.Name == req.CurrentVersion {
resp := response.Success(resp, "current version is latest")
resp := response.Success(data, "current version is latest")
return c.Status(fiber.StatusOK).JSON(resp)
}

h.logger.Info("CDK validation success")

rk := ksuid.New().String()
var info TempDownloadInfo
isFull := req.CurrentVersion == ""

// if current version is not provided, we will download the full version
var current *ent.Version
if !isFull {
getVersionByNameParam := GetVersionByNameParam{
ResourceID: resID,
Name: req.CurrentVersion,
}
current, err = h.versionLogic.GetByName(ctx, getVersionByNameParam)
if err != nil {
if ent.IsNotFound(err) {
isFull = true
} else {
h.logger.Error("Failed to get current version",
zap.Error(err),
)
resp := response.UnexpectedError()
return c.Status(fiber.StatusInternalServerError).JSON(resp)
}
}
}

info := TempDownloadInfo{
ID: resourceName,
Full: req.CurrentVersion == "",
VersionID: latest.ID,
VersionName: latest.Name,
CurrentVersion: req.CurrentVersion,
FileHashes: latest.FileHashes,
if isFull {
info = TempDownloadInfo{
ResourceID: resID,
Full: isFull,
TargetVersionID: latest.ID,
}
} else {
info = TempDownloadInfo{
ResourceID: resID,
Full: isFull,
TargetVersionID: latest.ID,
TargetVersionFileHashes: latest.FileHashes,
CurrentVersionID: current.ID,
CurrentVersionFileHashes: current.FileHashes,
}
}

rk := ksuid.New().String()

if buf, err := json.Marshal(info); err != nil {
h.logger.Error("Failed to marshal JSON",
zap.Error(err),
Expand All @@ -431,14 +461,13 @@ func (h *VersionHandler) GetLatest(c *fiber.Ctx) error {
db.IRS.Set(ctx, fmt.Sprintf("RES:%v", rk), string(buf), 20*time.Minute)

url := strings.Join([]string{h.conf.Extra.DownloadPrefix, rk}, "/")
resp.Url = url
return c.Status(fiber.StatusOK).JSON(response.Success(resp, "success"))
data.Url = url
return c.Status(fiber.StatusOK).JSON(response.Success(data, "success"))
}

}

func (h *VersionHandler) Download(c *fiber.Ctx) error {

key := c.Params("key", "")

if key == "" {
Expand All @@ -451,83 +480,45 @@ func (h *VersionHandler) Download(c *fiber.Ctx) error {

var info TempDownloadInfo
if err != nil || val == "" || json.Unmarshal([]byte(val), &info) != nil {
h.logger.Error("invalid key or resource not found", zap.String("key", key))
return c.Status(fiber.StatusNotFound).JSON(response.BusinessError("invalid key or resource not found"))
}

var (
resourceID = info.ID
versionID = info.VersionID
versionName = info.VersionName

fileHashes = info.FileHashes
currentVersion = info.CurrentVersion
)

cwd, err := os.Getwd()
if err != nil {
h.logger.Error("Failed to get current directory",
zap.Error(err),
h.logger.Error("invalid key or resource not found",
zap.String("key", key),
)
resp := response.UnexpectedError()
return c.Status(fiber.StatusInternalServerError).JSON(resp)
resp := response.BusinessError("invalid key or resource not found")
return c.Status(fiber.StatusNotFound).JSON(resp)
}
dir := filepath.Join(cwd, "storage")
versionDir := filepath.Join(dir, resourceID, strconv.Itoa(versionID))

resArchivePath := filepath.Join(versionDir, "resource.zip")
if info.Full {
c.Set("X-Update-Type", "full")
return c.Status(fiber.StatusOK).Download(resArchivePath)
// full update
getResourcePathParam := GetResourcePathParam{
ResourceID: info.ResourceID,
VersionID: info.TargetVersionID,
}

param := GetVersionByNameParam{
ResourceID: resourceID,
Name: currentVersion,
}
current, err := h.versionLogic.GetByName(ctx, param)
if ent.IsNotFound(err) {
resArchivePath := h.versionLogic.GetResourcePath(getResourcePathParam)
if info.Full {
c.Set("X-Update-Type", "full")
return c.Status(fiber.StatusOK).Download(resArchivePath)

} else if err != nil {
h.logger.Error("Failed to get current version",
zap.String("version name", currentVersion),
zap.Error(err),
)
resp := response.UnexpectedError()
return c.Status(fiber.StatusInternalServerError).JSON(resp)
}

changes, err := patcher.CalculateDiff(fileHashes, current.FileHashes)
if err != nil {
h.logger.Error("Failed to calculate diff",
zap.String("resource ID", resourceID),
)
resp := response.UnexpectedError()
return c.Status(fiber.StatusInternalServerError).JSON(resp)
}

patchDir := filepath.Join(versionDir, "patch")
patchName := fmt.Sprintf("%s-%s", current.Name, versionName)
latestStorage, err := h.storageLogic.GetByVersionID(ctx, versionID)
if err != nil {
h.logger.Error("Failed to get storage",
zap.Error(err),
)
resp := response.UnexpectedError()
return c.Status(fiber.StatusInternalServerError).JSON(resp)
// incremental update
getPatchPathParam := GetVersionPatchParam{
ResourceID: info.ResourceID,
TargetVersionID: info.TargetVersionID,
TargetVersionFileHashes: info.TargetVersionFileHashes,
CurrentVersionID: info.CurrentVersionID,
CurrentVersionFileHashes: info.CurrentVersionFileHashes,
}
archiving, err := patcher.Generate(patchName, latestStorage.Directory, patchDir, changes)
patchPath, err := h.versionLogic.GetPatchPath(ctx, getPatchPathParam)
if err != nil {
h.logger.Error("Failed to generate patch package",
h.logger.Error("Failed to get patch",
zap.String("resource id", info.ResourceID),
zap.Int("target version id", info.TargetVersionID),
zap.Int("current version id", info.CurrentVersionID),
zap.Error(err),
)
resp := response.UnexpectedError()
return c.Status(fiber.StatusInternalServerError).JSON(resp)
resp := response.UnexpectedError
return c.Status(fiber.StatusInternalServerError).JSON(resp())
}

c.Set("X-New-Version-Available", "true")
c.Set("X-Update-Type", "incremental")
return c.Status(fiber.StatusOK).Download(filepath.Join(patchDir, archiving))
return c.Status(fiber.StatusOK).Download(patchPath, "ota.zip")
}
70 changes: 65 additions & 5 deletions internal/logic/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,27 @@ import (
"github.com/MirrorChyan/resource-backend/internal/ent/resource"
"github.com/MirrorChyan/resource-backend/internal/ent/version"
. "github.com/MirrorChyan/resource-backend/internal/model"
"github.com/MirrorChyan/resource-backend/internal/patcher"
"github.com/MirrorChyan/resource-backend/internal/pkg/archive"
"github.com/MirrorChyan/resource-backend/internal/pkg/filehash"
"github.com/MirrorChyan/resource-backend/internal/pkg/fileops"
"github.com/MirrorChyan/resource-backend/internal/pkg/stg"
"go.uber.org/zap"
)

type VersionLogic struct {
logger *zap.Logger
db *ent.Client
logger *zap.Logger
db *ent.Client
storage *stg.Storage
storageLogic *StorageLogic
}

func NewVersionLogic(logger *zap.Logger, db *ent.Client) *VersionLogic {
func NewVersionLogic(logger *zap.Logger, db *ent.Client, storage *stg.Storage, storageLogic *StorageLogic) *VersionLogic {
return &VersionLogic{
logger: logger,
db: db,
logger: logger,
db: db,
storage: storage,
storageLogic: storageLogic,
}
}

Expand Down Expand Up @@ -230,3 +236,57 @@ func (l *VersionLogic) Delete(ctx context.Context, id int) error {
DeleteOneID(id).
Exec(ctx)
}

func (l *VersionLogic) GetResourcePath(param GetResourcePathParam) string {
return l.storage.ResourcePath(param.ResourceID, param.VersionID)
}

func (l *VersionLogic) GetPatchPath(ctx context.Context, param GetVersionPatchParam) (string, error) {
changes, err := patcher.CalculateDiff(param.TargetVersionFileHashes, param.CurrentVersionFileHashes)
if err != nil {
l.logger.Error("Failed to calculate diff",
zap.String("resource ID", param.ResourceID),
zap.Int("target version ID", param.TargetVersionID),
zap.Int("current version ID", param.CurrentVersionID),
zap.Error(err),
)
return "", err
}

exists, err := l.storage.PatchExists(param.ResourceID, param.TargetVersionID, param.CurrentVersionID)
if err != nil {
l.logger.Error("Failed to check patch file exists",
zap.String("resource ID", param.ResourceID),
zap.Int("target version ID", param.TargetVersionID),
zap.Int("current version ID", param.CurrentVersionID),
zap.Error(err),
)
return "", err
}

if exists {
patchPath := l.storage.PatchPath(param.ResourceID, param.TargetVersionID, param.CurrentVersionID)
return patchPath, nil
}

patchDir := l.storage.PatchDir(param.ResourceID, param.TargetVersionID)
latestStorage, err := l.storageLogic.GetByVersionID(ctx, param.TargetVersionID)
if err != nil {
l.logger.Error("Failed to get storage",
zap.Error(err),
)
return "", err

}
patchName, err := patcher.Generate(strconv.Itoa(param.CurrentVersionID), latestStorage.Directory, patchDir, changes)
if err != nil {
l.logger.Error("Failed to generate patch package",
zap.Error(err),
)
return "", err

}

patchPath := filepath.Join(patchDir, patchName)
return patchPath, nil
}
25 changes: 19 additions & 6 deletions internal/model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,12 @@ type CreateResourceResponseData struct {
}

type TempDownloadInfo struct {
ID string `json:"id"`
Full bool `json:"full"`
VersionID int `json:"version_id"`
VersionName string `json:"version_name"`
CurrentVersion string `json:"current_version"`
FileHashes map[string]string `json:"file_hashes"`
ResourceID string `json:"resource_id"`
Full bool `json:"full"`
TargetVersionID int `json:"target_version_id"`
TargetVersionFileHashes map[string]string `json:"target_version_file_hashes"`
CurrentVersionID int `json:"current_version_id"`
CurrentVersionFileHashes map[string]string `json:"current_version_file_hashes"`
}

type BillingCheckinRequest struct {
Expand All @@ -107,3 +107,16 @@ type BillingCheckinRequest struct {
Module string `json:"module"`
UserAgent string `json:"user_agent"`
}

type GetResourcePathParam struct {
ResourceID string
VersionID int
}

type GetVersionPatchParam struct {
ResourceID string
CurrentVersionID int
CurrentVersionFileHashes map[string]string
TargetVersionID int
TargetVersionFileHashes map[string]string
}
Loading

0 comments on commit fdb30f2

Please sign in to comment.